Details
-
Type:
Bug
-
Status:
Resolved
-
Priority:
Major
-
Resolution: Fixed
-
Affects Version/s: JRuby 1.7.0.pre1
-
Fix Version/s: JRuby 1.7.0.pre2
-
Component/s: Core Classes/Modules
-
Labels:None
-
Environment:I'm running Microsoft Windows Vista with Java Client VM 1.6.0_14, but I think the problem is likely to independent of the platform.
-
Number of attachments :
Description
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()));
}
}
Your fix looks quite appropriate. Will try it out.