Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: jruby/jruby
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 1cc067457756
Choose a base ref
...
head repository: jruby/jruby
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 2e783d706676
Choose a head ref
  • 3 commits
  • 5 files changed
  • 1 contributor

Commits on Apr 17, 2018

  1. Copy the full SHA
    65bee1f View commit details
  2. Copy the full SHA
    33ff5a8 View commit details
  3. Copy the full SHA
    2e783d7 View commit details
63 changes: 47 additions & 16 deletions core/src/main/java/org/jruby/ext/bigdecimal/RubyBigDecimal.java
Original file line number Diff line number Diff line change
@@ -652,10 +652,11 @@ else if ( lastSign > s ) {
try {
decimal = new BigDecimal(str, s, e - s + 1, mathContext);
}
catch (ArithmeticException ex) {
return checkOverUnderFlow(context.runtime, ex, false);
}
catch (NumberFormatException ex) {
if (isOverflowExceptionMode(context.runtime)) throw context.runtime.newFloatDomainError("exponent overflow");

decimal = BigDecimal.ZERO;
throw context.runtime.newArgumentError("invalid value for BigDecimal(): \"" + arg + "\"");
}

// MRI behavior: -0 and +0 are two different things
@@ -935,12 +936,12 @@ private RubyBigDecimal multImpl(final Ruby runtime, RubyBigDecimal val) {
result = value.multiply(val.value, mathContext);
}
catch (ArithmeticException ex) {
return checkOverUnderFlow(runtime, ex);
return checkOverUnderFlow(runtime, ex, false);
}
return new RubyBigDecimal(runtime, result).setResult(0);
}

private static RubyBigDecimal checkOverUnderFlow(final Ruby runtime, final ArithmeticException ex) {
private static RubyBigDecimal checkOverUnderFlow(final Ruby runtime, final ArithmeticException ex, boolean nullDefault) {
String message = ex.getMessage();
if (message == null) message = "";
message = message.toLowerCase(Locale.ENGLISH);
@@ -952,6 +953,7 @@ private static RubyBigDecimal checkOverUnderFlow(final Ruby runtime, final Arith
if (isOverflowExceptionMode(runtime)) throw runtime.newFloatDomainError(message);
return newInfinity(runtime, 1); // TODO sign?
}
if (nullDefault) return null;
throw runtime.newFloatDomainError(message);
}

@@ -1339,19 +1341,36 @@ private IRubyObject cmp(ThreadContext context, final IRubyObject arg, final char
final int e;
RubyBigDecimal rb = getVpValue(context, arg, false);
if (rb == null) {
IRubyObject cmp = callCoerced(context, sites(context).op_cmp, arg, false);
if ( cmp.isNil() ) { // arg.coerce failed
if (op == '*') return context.nil;
if (op == '=' || isNaN()) return context.fals;
throw context.runtime.newArgumentError("comparison of BigDecimal with "+ errMessageType(context, arg) +" failed");
String id = "!=";
switch (op) {
case '*':
if (falsyEqlCheck(context, arg)) return context.nil;
return callCoerced(context, sites(context).op_cmp, arg, false);
case '=': {
if (falsyEqlCheck(context, arg)) return context.fals;
IRubyObject res = callCoerced(context, sites(context).op_eql, arg, false);
return context.runtime.newBoolean(res != context.nil && res != context.fals);
}
case '!':
if (falsyEqlCheck(context, arg)) return context.tru;
/* id = "!="; */ break;
case 'G': id = ">="; break;
case 'L': id = "<="; break;
case '<': id = "<"; break;
case '>': id = ">"; break;
}
e = RubyNumeric.fix2int(cmp);
} else {
if (isNaN() || rb.isNaN()) return (op == '*') ? context.nil : context.fals;

e = infinitySign != 0 || rb.infinitySign != 0 ? infinitySign - rb.infinitySign : value.compareTo(rb.value);
IRubyObject cmp = callCoerced(context, id, arg);
if (cmp == context.nil) { // arg.coerce failed
throw context.runtime.newArgumentError("comparison of BigDecimal with "+ errMessageType(context, arg) +" failed");
}
return cmp;
}
switch(op) {

if (isNaN() || rb.isNaN()) return (op == '*') ? context.nil : context.fals;
e = infinitySign != 0 || rb.infinitySign != 0 ? infinitySign - rb.infinitySign : value.compareTo(rb.value);

switch (op) {
case '*': return context.runtime.newFixnum(e);
case '=': return context.runtime.newBoolean(e == 0);
case '!': return context.runtime.newBoolean(e != 0);
@@ -1363,18 +1382,30 @@ private IRubyObject cmp(ThreadContext context, final IRubyObject arg, final char
return context.nil;
}

// NOTE: otherwise `BD == nil` etc gets ___reeeaaally___ slow (due exception throwing)
private static boolean falsyEqlCheck(final ThreadContext context, final IRubyObject arg) {
return arg == context.nil || arg == context.fals || arg == context.tru;
}

@Override
@JRubyMethod(name = "<=>", required = 1)
public IRubyObject op_cmp(ThreadContext context, IRubyObject arg) {
return cmp(context, arg, '*');
}

// NOTE: do not use BigDecimal#equals since ZERO.equals(new BD('0.0')) -> false
@Override
@JRubyMethod(name = {"eql?", "==", "==="}, required = 1)
@JRubyMethod(name = {"eql?", "=="}, required = 1)
public IRubyObject eql_p(ThreadContext context, IRubyObject arg) {
return cmp(context, arg, '=');
}

@Override
@JRubyMethod(name = "===", required = 1) // same as == (eql?)
public IRubyObject op_eqq(ThreadContext context, IRubyObject arg) {
return cmp(context, arg, '=');
}

@JRubyMethod(name = "<", required = 1)
public IRubyObject op_lt(ThreadContext context, IRubyObject arg) {
return cmp(context, arg, '<');
1 change: 1 addition & 0 deletions core/src/main/java/org/jruby/runtime/JavaSites.java
Original file line number Diff line number Diff line change
@@ -369,6 +369,7 @@ public static class IRRuntimeHelpersSites {
public static class BigDecimalSites {
public final CallSite op_plus = new FunctionalCachingCallSite("+");
public final CallSite op_cmp = new FunctionalCachingCallSite("<=>");
public final CallSite op_eql = new FunctionalCachingCallSite("==");
public final CallSite divmod = new FunctionalCachingCallSite("divmod");
public final CallSite op_times = new FunctionalCachingCallSite("*");
public final CallSite div = new FunctionalCachingCallSite("div");
14 changes: 14 additions & 0 deletions test/jruby/test_array.rb
Original file line number Diff line number Diff line change
@@ -62,4 +62,18 @@ def test_collect_concurrency
end
end

# GH-5141
def test_concat_self
arr = [1]
arr.concat(arr)
arr.concat(arr)
arr.concat(arr)
assert_equal [1, 1, 1, 1, 1, 1, 1, 1], arr

arr = [1, 2]
arr.concat(arr)
arr.concat(arr)
assert_equal [1, 2, 1, 2, 1, 2, 1, 2], arr
end

end
11 changes: 11 additions & 0 deletions test/jruby/test_big_decimal.rb
Original file line number Diff line number Diff line change
@@ -478,6 +478,17 @@ def test_new # from MRI test_bigdecimal.rb
assert_positive_infinite(BigDecimal("1E1111111111111111111"))
end

def test_eqq_eql
assert_equal false, BigDecimal('1000.0') == nil
assert_equal true, BigDecimal('11000.0') != nil
assert_equal true, BigDecimal('12000.0') != true
assert_equal false, BigDecimal('1000.0').eql?(false)
assert_equal false, BigDecimal('0.0000') === nil
assert_raise(ArgumentError) { BigDecimal('1.001') >= true }
assert_raise(ArgumentError) { BigDecimal('2.200') < false }
assert_raise(ArgumentError) { BigDecimal('3.003') >= nil }
end

private

def assert_nan(x)
1 change: 0 additions & 1 deletion test/mri/excludes/TestBigDecimal.rb
Original file line number Diff line number Diff line change
@@ -4,7 +4,6 @@
exclude :test_BigMath_log_under_gc_stress, "needs investigation"
exclude :test_div, "does not pass due precision differences (ported to test/jruby/test_big_decimal.rb)"

exclude :test_global_new_with_invalid_string, "error change in 2.4?"
exclude :test_limit, "needs investigation"
exclude :test_marshal, "needs investigation"
exclude :test_new, "BigDecimal('_1_1_1') parses fine while in MRI raises"