This seems to be a problem in 1.7.0.preview1 and in 1.6.7; I haven't tried it in other JRuby versions, but I suspect it will be a problem in other JRuby versions.
In JIRB or run as a JRuby program:
v = 2**62 * -2 #=> -9_223_372_036_854_775_808
v = -(v + 2) #=> 9_223_372_036_854_775_806
v = v.succ #=> 9_223_372_036_854_775_807
v = v.succ #=> -9_223_372_036_854_775_808
v = v.succ #=> -9_223_372_036_854_775_807
Looking at this code in RubyInteger.java it seems clear that the issue is "succ" does not check for Java long integer overflow for "getLongValue() + 1L".
RubyInteger.java 1.7.0.preview1 line 243
@JRubyMethod(name = {"succ", "next"})
public IRubyObject succ(ThreadContext context) {
if (this instanceof RubyFixnum) {
return RubyFixnum.newFixnum(context.getRuntime(), getLongValue() + 1L);
} else {
return callMethod(context, "+", RubyFixnum.one(context.getRuntime()));
}
}
Looking at: RubyFixnum.java 1.7.0.preview1 line 353
public IRubyObject op_plus_one(ThreadContext context) {
long result = value + 1;
if (result == Long.MIN_VALUE) {
return addAsBignum(context, 1);
}
return newFixnum(context.getRuntime(), result);
}
suggests this possible patch:
public IRubyObject succ(ThreadContext context) {
if (this instanceof RubyFixnum) {
// replace "return RubyFixnum.newFixnum(..." by something like:
return this.op_plus_one(context); // will this work?
} else {
return callMethod(context, "+", RubyFixnum.one(context.getRuntime()));
} }
}
or maybe this if there are performance (or other) reasons for using
"return RubyFixnum.newFixnum(..."
public IRubyObject succ(ThreadContext context) {
if (this instanceof RubyFixnum) {
long fixnumvalue = getLongValue();
if (fixnumvalue != Long.MAX_VALUE) {
return RubyFixnum.newFixnum(context.getRuntime(),fixnumvalue + 1L);
}
return this.op_plus_one(context); // will this work?
} else {
return callMethod(context, "+", RubyFixnum.one(context.getRuntime()));
}
}
|