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:
def rr(rng)
puts
puts "demonstrating (" + rng.inspect + ").each problem:"
indx = -1 # use indx to prevent an almost infinite loop
rng.each do |v|
indx += 1
puts " each indx= #{indx.inspect}, v= #{v.inspect}"
break if indx >= 5
end
end
vv = 2**63
rr(vv - 3 ... vv + 0) # (1) at indx == 3 has integer overflow
rr(vv - 3 .. vv + 0) # (2) at indx == 3 has integer overflow
rr(vv - 3 ... vv - 1) # (3) prints out expected "each" values
rr(vv - 3 .. vv - 1) # (4) doesn't print out any "each" values
In JRUBY-6612 I reported an integer overflow problem in RubyFixnum.java in
public IRubyObject op_mul
and also gave examples of some strange behaviour of Range#each
with integer values near the maximum Fixnum value.
The reported integer overflow problem seems to have been fixed in
JRuby 1.7.0.preview1
but I still seem to be getting this sometimes strange behaviour of Range#each.
I've put below extracts from RubyRange.java: it seems that the problem may arise in "private void rangeEach".
I suspect that the problems of examples (1) and (2) above might be caused (at least partly) by a problem with Integer#succ which I've reported here:
JRUBY-6778 Possible long integer overflow bug in Integer#succ in RubyInteger.java
So fixing that Integer#succ problem may fix (1) and (2).
But it's not clear to me from the RubyRange.java code why the exclusive range in (3) works but the inclusive range in (4) doesn't work, and I don't have a sufficient understanding of the interaction between the underlying Java code for various JRuby classes to see what is the cause of the problem.
-
-
-
-
- extracts from RubyRange.java 1.7.0.preview1 from line 346
private IRubyObject rangeLt(ThreadContext context, IRubyObject a, IRubyObject b) {
IRubyObject result = invokedynamic(context, a, OP_CMP, b);
if (result.isNil()) return null;
return RubyComparable.cmpint(context, result, a, b) < 0 ? getRuntime().getTrue() : null;
}
private IRubyObject rangeLe(ThreadContext context, IRubyObject a, IRubyObject b) {
IRubyObject result = invokedynamic(context, a, OP_CMP, b);
if (result.isNil()) return null;
int c = RubyComparable.cmpint(context, result, a, b);
if (c == 0) return RubyFixnum.zero(getRuntime());
return c < 0 ? getRuntime().getTrue() : null;
}
private void rangeEach(ThreadContext context, RangeCallBack callback) {
IRubyObject v = begin;
if (isExclusive) {
while (rangeLt(context, v, end) != null) {
callback.call(context, v);
v = v.callMethod(context, "succ");
}
} else {
IRubyObject c;
while ((c = rangeLe(context, v, end)) != null && c.isTrue()) {
callback.call(context, v);
if (c == RubyFixnum.zero(getRuntime())) break;
v = v.callMethod(context, "succ");
}
}
}
|