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: bccf82ea0a68
Choose a base ref
...
head repository: jruby/jruby
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 4a2f300d6d2d
Choose a head ref
  • 7 commits
  • 3 files changed
  • 2 contributors

Commits on Jun 14, 2016

  1. Copy the full SHA
    46d85a2 View commit details
  2. remove wrong equality test

    monkstone authored and kares committed Jun 14, 2016
    Copy the full SHA
    8b1671a View commit details
  3. Copy the full SHA
    aa7f3ed View commit details
  4. Copy the full SHA
    9104740 View commit details
  5. add test for custom range

    monkstone authored and kares committed Jun 14, 2016
    Copy the full SHA
    cc36911 View commit details
  6. Copy the full SHA
    a6a8d24 View commit details
  7. Copy the full SHA
    4a2f300 View commit details
Showing with 292 additions and 129 deletions.
  1. +220 −124 core/src/main/java/org/jruby/RubyRange.java
  2. +71 −4 spec/ruby/core/range/eql_spec.rb
  3. +1 −1 spec/ruby/core/range/range_spec.rb
344 changes: 220 additions & 124 deletions core/src/main/java/org/jruby/RubyRange.java
Original file line number Diff line number Diff line change
@@ -21,18 +21,18 @@
* Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
* Copyright (C) 2005 Charles O Nutter <headius@headius.com>
* Copyright (C) 2006 Miguel Covarrubias <mlcovarrubias@gmail.com>
*
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the EPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the EPL, the GPL or the LGPL.
* either of the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), in
* which case the provisions of the GPL or the LGPL are applicable instead of
* those above. If you wish to allow use of your version of this file only under
* the terms of either the GPL or the LGPL, and not to allow others to use your
* version of this file under the terms of the EPL, indicate your decision by
* deleting the provisions above and replace them with the notice and other
* provisions required by the GPL or the LGPL. If you do not delete the
* provisions above, a recipient may use your version of this file under the
* terms of any one of the EPL, the GPL or the LGPL.
***** END LICENSE BLOCK *****/
package org.jruby;

@@ -76,8 +76,9 @@
/**
* @author jpetersen
*/
@JRubyClass(name="Range", include="Enumerable")
@JRubyClass(name = "Range", include = "Enumerable")
public class RubyRange extends RubyObject {

private IRubyObject begin;
private IRubyObject end;
private boolean isExclusive;
@@ -91,7 +92,7 @@ public static RubyClass createRangeClass(Ruby runtime) {
result.setReifiedClass(RubyRange.class);

result.kindOf = new RubyModule.JavaClassKindOf(RubyRange.class);

result.setMarshal(RANGE_MARSHAL);
result.includeModule(runtime.getEnumerable());

@@ -104,7 +105,7 @@ public static RubyClass createRangeClass(Ruby runtime) {
public IRubyObject allocate(Ruby runtime, RubyClass klass) {
return new RubyRange(runtime, klass);
}
};
};

private RubyRange(Ruby runtime, RubyClass klass) {
super(runtime, klass);
@@ -119,54 +120,66 @@ public static RubyRange newRange(ThreadContext context, IRubyObject begin, IRuby

@Override
public void copySpecialInstanceVariables(IRubyObject clone) {
RubyRange range = (RubyRange)clone;
RubyRange range = (RubyRange) clone;
range.begin = begin;
range.end = end;
range.isExclusive = isExclusive;
}

final boolean checkBegin(long length) {
long beg = RubyNumeric.num2long(this.begin);
if(beg < 0) {
if (beg < 0) {
beg += length;
if(beg < 0) {
if (beg < 0) {
return false;
}
} else if(length < beg) {
} else if (length < beg) {
return false;
}
return true;
}

final long[] begLen(long len, int err){
final long[] begLen(long len, int err) {
long beg = RubyNumeric.num2long(this.begin);
long end = RubyNumeric.num2long(this.end);

if (beg < 0) {
beg += len;
if (beg < 0) {
if (err != 0) throw getRuntime().newRangeError(beg + ".." + (isExclusive ? "." : "") + end + " out of range");
if (err != 0) {
throw getRuntime().newRangeError(beg + ".." + (isExclusive ? "." : "") + end + " out of range");
}
return null;
}
}

if (err == 0 || err == 2) {
if (beg > len) {
if (err != 0) throw getRuntime().newRangeError(beg + ".." + (isExclusive ? "." : "") + end + " out of range");
if (err != 0) {
throw getRuntime().newRangeError(beg + ".." + (isExclusive ? "." : "") + end + " out of range");
}
return null;
}
if (end > len) end = len;
if (end > len) {
end = len;
}
}

if (end < 0) end += len;
if (!isExclusive) end++;
if (end < 0) {
end += len;
}
if (!isExclusive) {
end++;
}
len = end - beg;
if (len < 0) len = 0;
if (len < 0) {
len = 0;
}

return new long[]{beg, len};
}

final long begLen0(long len){
final long begLen0(long len) {
long beg = RubyNumeric.num2long(this.begin);

if (beg < 0) {
@@ -175,45 +188,63 @@ final long begLen0(long len){
throw getRuntime().newRangeError(beg + ".." + (isExclusive ? "." : "") + end + " out of range");
}
}

return beg;
}

final long begLen1(long len, long beg){
final long begLen1(long len, long beg) {
long end = RubyNumeric.num2long(this.end);

if (end < 0) end += len;
if (!isExclusive) end++;
if (end < 0) {
end += len;
}
if (!isExclusive) {
end++;
}
len = end - beg;
if (len < 0) len = 0;
if (len < 0) {
len = 0;
}

return len;
}

final int[] begLenInt(int len, int err){
final int[] begLenInt(int len, int err) {
int beg = RubyNumeric.num2int(this.begin);
int end = RubyNumeric.num2int(this.end);

if (beg < 0) {
beg += len;
if (beg < 0) {
if (err != 0) throw getRuntime().newRangeError(beg + ".." + (isExclusive ? "." : "") + end + " out of range");
if (err != 0) {
throw getRuntime().newRangeError(beg + ".." + (isExclusive ? "." : "") + end + " out of range");
}
return null;
}
}

if (err == 0 || err == 2) {
if (beg > len) {
if (err != 0) throw getRuntime().newRangeError(beg + ".." + (isExclusive ? "." : "") + end + " out of range");
if (err != 0) {
throw getRuntime().newRangeError(beg + ".." + (isExclusive ? "." : "") + end + " out of range");
}
return null;
}
if (end > len) end = len;
if (end > len) {
end = len;
}
}

if (end < 0) end += len;
if (!isExclusive) end++;
if (end < 0) {
end += len;
}
if (!isExclusive) {
end++;
}
len = end - beg;
if (len < 0) len = 0;
if (len < 0) {
len = 0;
}

return new int[]{beg, len};
}
@@ -222,9 +253,11 @@ private void init(ThreadContext context, IRubyObject begin, IRubyObject end, boo
if (!(begin instanceof RubyFixnum && end instanceof RubyFixnum)) {
try {
IRubyObject result = invokedynamic(context, begin, MethodNames.OP_CMP, end);
if (result.isNil()) throw getRuntime().newArgumentError("bad value for range");
if (result.isNil()) {
throw context.runtime.newArgumentError("bad value for range");
}
} catch (RaiseException re) {
throw getRuntime().newArgumentError("bad value for range");
throw context.runtime.newArgumentError("bad value for range");
}
}

@@ -233,14 +266,14 @@ private void init(ThreadContext context, IRubyObject begin, IRubyObject end, boo
this.isExclusive = isExclusive;
this.isInited = true;
}

@JRubyMethod(required = 2, optional = 1, visibility = PRIVATE)
public IRubyObject initialize(ThreadContext context, IRubyObject[] args, Block unusedBlock) {
if (this.isInited) {
throw getRuntime().newNameError("`initialize' called twice", "initialize");
throw context.runtime.newNameError("`initialize' called twice", "initialize");
}
init(context, args[0], args[1], args.length > 2 && args[2].isTrue());
return getRuntime().getNil();
return context.nil;
}

@JRubyMethod(required = 1, visibility = PRIVATE)
@@ -251,26 +284,26 @@ public IRubyObject initialize_copy(ThreadContext context, IRubyObject original)

RubyRange other = (RubyRange) original;
init(context, other.begin, other.end, other.isExclusive);
return getRuntime().getNil();
return context.nil;
}

@JRubyMethod(name = "hash")
public RubyFixnum hash(ThreadContext context) {
long hash = isExclusive ? 1 : 0;
long h = hash;

long v = invokedynamic(context, begin, MethodNames.HASH).convertToInteger().getLongValue();
hash ^= v << 1;
v = invokedynamic(context, end, MethodNames.HASH).convertToInteger().getLongValue();
hash ^= v << 9;
hash ^= h << 24;
return getRuntime().newFixnum(hash);
return context.runtime.newFixnum(hash);
}

private IRubyObject inspectValue(final ThreadContext context, IRubyObject value) {
return getRuntime().execRecursiveOuter(new Ruby.RecursiveFunction() {
return context.runtime.execRecursiveOuter(new Ruby.RecursiveFunction() {
public IRubyObject call(IRubyObject obj, boolean recur) {
if(recur) {
if (recur) {
return RubyString.newString(context.runtime, isExclusive ? "(... ... ...)" : "(... .. ...)");
} else {
return inspect(context, obj);
@@ -279,7 +312,7 @@ public IRubyObject call(IRubyObject obj, boolean recur) {
}, value);
}

private static final byte[] DOTDOTDOT = new byte[] { '.','.','.' };
private static final byte[] DOTDOTDOT = new byte[]{'.', '.', '.'};

@JRubyMethod(name = "inspect")
public IRubyObject inspect(final ThreadContext context) {
@@ -309,24 +342,33 @@ public RubyBoolean exclude_end_p() {
}

@JRubyMethod(name = {"==", "eql?"}, required = 1)
@Override
public IRubyObject op_equal(ThreadContext context, IRubyObject other) {
if (this == other) return getRuntime().getTrue();
if (!(other instanceof RubyRange)) return getRuntime().getFalse();
if (this == other) {
return context.runtime.getTrue();
}
if (!(other instanceof RubyRange)) {
return context.runtime.getFalse();
}
RubyRange otherRange = (RubyRange) other;

if (equalInternal(context, begin, otherRange.begin) &&
equalInternal(context, end, otherRange.end) &&
isExclusive == otherRange.isExclusive) return getRuntime().getTrue();

return getRuntime().getFalse();
if (isExclusive != otherRange.isExclusive) {
return context.runtime.getFalse();
}
if (!invokedynamic(context, this.begin, MethodNames.EQL, otherRange.begin).isTrue()) {
return context.runtime.getFalse();
}
if (invokedynamic(context, this.end, MethodNames.EQL, otherRange.end).isTrue()) {
return context.runtime.getTrue();
}
return context.runtime.getFalse();
}

private static abstract class RangeCallBack {

abstract void call(ThreadContext context, IRubyObject arg);
}

private static final class StepBlockCallBack extends RangeCallBack implements BlockCallback {

final Block block;
IRubyObject iter;
final IRubyObject step;
@@ -340,13 +382,13 @@ private static final class StepBlockCallBack extends RangeCallBack implements Bl
@Override
public IRubyObject call(ThreadContext context, IRubyObject[] args, Block originalBlock) {
call(context, args[0]);
return context.runtime.getNil();
return context.nil;
}

@Override
void call(ThreadContext context, IRubyObject arg) {
if (iter instanceof RubyFixnum) {
iter = RubyFixnum.newFixnum(context.runtime, ((RubyFixnum)iter).getLongValue() - 1);
iter = RubyFixnum.newFixnum(context.runtime, ((RubyFixnum) iter).getLongValue() - 1);
} else {
iter = iter.callMethod(context, "-", RubyFixnum.one(context.runtime));
}
@@ -359,17 +401,23 @@ void call(ThreadContext context, IRubyObject arg) {

private IRubyObject rangeLt(ThreadContext context, IRubyObject a, IRubyObject b) {
IRubyObject result = invokedynamic(context, a, MethodNames.OP_CMP, b);
if (result.isNil()) return null;
return RubyComparable.cmpint(context, result, a, b) < 0 ? getRuntime().getTrue() : null;
if (result.isNil()) {
return null;
}
return RubyComparable.cmpint(context, result, a, b) < 0 ? context.runtime.getTrue() : null;
}

private IRubyObject rangeLe(ThreadContext context, IRubyObject a, IRubyObject b) {
IRubyObject result = invokedynamic(context, a, MethodNames.OP_CMP, b);
if (result.isNil()) return null;
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;
}
if (c == 0) {
return RubyFixnum.zero(context.runtime);
}
return c < 0 ? context.runtime.getTrue() : null;
}

private void rangeEach(ThreadContext context, RangeCallBack callback) {
IRubyObject v = begin;
@@ -382,7 +430,9 @@ private void rangeEach(ThreadContext context, RangeCallBack callback) {
IRubyObject c;
while ((c = rangeLe(context, v, end)) != null && c.isTrue()) {
callback.call(context, v);
if (c == RubyFixnum.zero(getRuntime())) break;
if (c == RubyFixnum.zero(context.runtime)) {
break;
}
v = v.callMethod(context, "succ");
}
}
@@ -394,15 +444,19 @@ public IRubyObject to_a(ThreadContext context, final Block block) {

if (begin instanceof RubyFixnum && end instanceof RubyFixnum) {
long lim = ((RubyFixnum) end).getLongValue();
if (!isExclusive) lim++;
if (!isExclusive) {
lim++;
}

long base = ((RubyFixnum) begin).getLongValue();
long size = lim - base;
if (size > Integer.MAX_VALUE) {
throw runtime.newRangeError("Range size too large for to_a");
}
if (size < 0) return RubyArray.newEmptyArray(runtime);
IRubyObject[] array = new IRubyObject[(int)size];
if (size < 0) {
return RubyArray.newEmptyArray(runtime);
}
IRubyObject[] array = new IRubyObject[(int) size];
for (int i = 0; i < size; i++) {
array[i] = RubyFixnum.newFixnum(runtime, base + i);
}
@@ -420,7 +474,9 @@ private void fixnumEach(ThreadContext context, Ruby runtime, Block block) {
// We must avoid integer overflows.
long to = ((RubyFixnum) end).getLongValue();
if (isExclusive) {
if (to == Long.MIN_VALUE) return;
if (to == Long.MIN_VALUE) {
return;
}
to--;
}
long from = ((RubyFixnum) begin).getLongValue();
@@ -447,7 +503,9 @@ private void fixnumEach(ThreadContext context, Ruby runtime, Block block) {
@JRubyMethod(name = "each")
public IRubyObject each19(final ThreadContext context, final Block block) {
Ruby runtime = context.runtime;
if (!block.isGiven()) return enumeratorizeWithSize(context, this, "each", enumSizeFn(context));
if (!block.isGiven()) {
return enumeratorizeWithSize(context, this, "each", enumSizeFn(context));
}

if (begin instanceof RubyTime) {
throw runtime.newTypeError("can't iterate from Time");
@@ -457,11 +515,13 @@ public IRubyObject each19(final ThreadContext context, final Block block) {
begin.asString().uptoCommon19(context, end.asString(), isExclusive, block, true);
} else {
IRubyObject tmp = begin.checkStringType();
if(!tmp.isNil()) {
if (!tmp.isNil()) {
((RubyString) tmp).uptoCommon19(context, end, isExclusive, block);
} else {
if (!begin.respondsTo("succ")) throw getRuntime().newTypeError("can't iterate from " +
begin.getMetaClass().getName());
if (!begin.respondsTo("succ")) {
throw runtime.newTypeError("can't iterate from "
+ begin.getMetaClass().getName());
}
rangeEach(context, new RangeCallBack() {
@Override
void call(ThreadContext context, IRubyObject arg) {
@@ -486,13 +546,17 @@ private void fixnumStep(ThreadContext context, Ruby runtime, long step, Block bl
// Any method calling this method must ensure that "step" is greater than 0.
long to = ((RubyFixnum) end).getLongValue();
if (isExclusive) {
if (to == Long.MIN_VALUE) return;
if (to == Long.MIN_VALUE) {
return;
}
to--;
}
long tov = Long.MAX_VALUE - step;
if (to < tov) tov = to;
if (to < tov) {
tov = to;
}
long i;
for (i = ((RubyFixnum)begin).getLongValue(); i <= tov; i += step) {
for (i = ((RubyFixnum) begin).getLongValue(); i <= tov; i += step) {
block.yield(context, RubyFixnum.newFixnum(runtime, i));
}
if (i <= to) {
@@ -508,33 +572,43 @@ public IRubyObject step19(final ThreadContext context, final Block block) {
@JRubyMethod(name = "step")
public IRubyObject step19(final ThreadContext context, IRubyObject step, final Block block) {
Ruby runtime = context.runtime;
if (!block.isGiven()) return enumeratorizeWithSize(context, this, "step", new IRubyObject[] { step }, stepSizeFn(context));
if (!block.isGiven()) {
return enumeratorizeWithSize(context, this, "step", new IRubyObject[]{step}, stepSizeFn(context));
}

if (!(step instanceof RubyNumeric)) step = step.convertToInteger("to_int");
if (!(step instanceof RubyNumeric)) {
step = step.convertToInteger("to_int");
}
IRubyObject zero = RubyFixnum.zero(runtime);
if (step.callMethod(context, "<", zero).isTrue()) throw runtime.newArgumentError("step can't be negative");
if (!step.callMethod(context, ">", zero).isTrue()) throw runtime.newArgumentError("step can't be 0");
if (step.callMethod(context, "<", zero).isTrue()) {
throw runtime.newArgumentError("step can't be negative");
}
if (!step.callMethod(context, ">", zero).isTrue()) {
throw runtime.newArgumentError("step can't be 0");
}
return stepCommon19(context, step, block);
}

private IRubyObject stepCommon19(ThreadContext context, IRubyObject step, Block block) {
Ruby runtime = context.runtime;
if (begin instanceof RubyFixnum && end instanceof RubyFixnum && step instanceof RubyFixnum) {
fixnumStep(context, runtime, ((RubyFixnum)step).getLongValue(), block);
fixnumStep(context, runtime, ((RubyFixnum) step).getLongValue(), block);
} else if (begin instanceof RubyFloat || end instanceof RubyFloat || step instanceof RubyFloat) {
RubyNumeric.floatStep19(context, runtime, begin, end, step, isExclusive, block);
} else if (begin instanceof RubyNumeric ||
!TypeConverter.checkIntegerType(runtime, begin, "to_int").isNil() ||
!TypeConverter.checkIntegerType(runtime, end, "to_int").isNil()) {
} else if (begin instanceof RubyNumeric
|| !TypeConverter.checkIntegerType(runtime, begin, "to_int").isNil()
|| !TypeConverter.checkIntegerType(runtime, end, "to_int").isNil()) {
numericStep19(context, runtime, step, block);
} else {
IRubyObject tmp = begin.checkStringType();
if (!tmp.isNil()) {
StepBlockCallBack callback = new StepBlockCallBack(block, RubyFixnum.one(runtime), step);
Block blockCallback = CallBlock.newCallClosure(this, runtime.getRange(), Signature.ONE_ARGUMENT, callback, context);
((RubyString)tmp).uptoCommon19(context, end, isExclusive, blockCallback);
((RubyString) tmp).uptoCommon19(context, end, isExclusive, blockCallback);
} else {
if (!begin.respondsTo("succ")) throw runtime.newTypeError("can't iterate from " + begin.getMetaClass().getName());
if (!begin.respondsTo("succ")) {
throw runtime.newTypeError("can't iterate from " + begin.getMetaClass().getName());
}
// range_each_func(range, step_i, b, e, args);
rangeEach(context, new StepBlockCallBack(block, RubyFixnum.one(runtime), step));
}
@@ -605,24 +679,30 @@ public RubyBoolean include_p(ThreadContext context, IRubyObject obj) {
@JRubyMethod(name = {"include?", "member?"}, frame = true)
public IRubyObject include_p19(ThreadContext context, IRubyObject obj) {
Ruby runtime = context.runtime;
if (begin instanceof RubyNumeric || end instanceof RubyNumeric ||
!TypeConverter.convertToTypeWithCheck(begin, runtime.getInteger(), "to_int").isNil() ||
!TypeConverter.convertToTypeWithCheck(end, runtime.getInteger(), "to_int").isNil()) {
if (begin instanceof RubyNumeric || end instanceof RubyNumeric
|| !TypeConverter.convertToTypeWithCheck(begin, runtime.getInteger(), "to_int").isNil()
|| !TypeConverter.convertToTypeWithCheck(end, runtime.getInteger(), "to_int").isNil()) {
return cover_p(context, obj);
} else if (begin instanceof RubyString && end instanceof RubyString &&
((RubyString) begin).getByteList().getRealSize() == 1 &&
((RubyString) end).getByteList().getRealSize() == 1) {
if (obj.isNil()) return runtime.getFalse();
} else if (begin instanceof RubyString && end instanceof RubyString
&& ((RubyString) begin).getByteList().getRealSize() == 1
&& ((RubyString) end).getByteList().getRealSize() == 1) {
if (obj.isNil()) {
return runtime.getFalse();
}
if (obj instanceof RubyString) {
ByteList Vbytes = ((RubyString)obj).getByteList();
if (Vbytes.getRealSize() != 1) return runtime.getFalse();
ByteList Vbytes = ((RubyString) obj).getByteList();
if (Vbytes.getRealSize() != 1) {
return runtime.getFalse();
}
int v = Vbytes.getUnsafeBytes()[Vbytes.getBegin()] & 0xff;
ByteList Bbytes = ((RubyString)begin).getByteList();
ByteList Bbytes = ((RubyString) begin).getByteList();
int b = Bbytes.getUnsafeBytes()[Bbytes.getBegin()] & 0xff;
ByteList Ebytes = ((RubyString)end).getByteList();
ByteList Ebytes = ((RubyString) end).getByteList();
int e = Ebytes.getUnsafeBytes()[Ebytes.getBegin()] & 0xff;
if (Encoding.isAscii(v) && Encoding.isAscii(b) && Encoding.isAscii(e)) {
if ((b <= v && v < e) || (!isExclusive && v == e)) return runtime.getTrue();
if ((b <= v && v < e) || (!isExclusive && v == e)) {
return runtime.getTrue();
}
return runtime.getFalse();
}
}
@@ -634,21 +714,27 @@ public IRubyObject include_p19(ThreadContext context, IRubyObject obj) {
public IRubyObject eqq_p19(ThreadContext context, IRubyObject obj) {
return callMethod(context, "include?", obj);
}

@JRubyMethod(name = "cover?")
public IRubyObject cover_p(ThreadContext context, IRubyObject obj) {
if (rangeLe(context, begin, obj) == null) return context.runtime.getFalse(); // obj < start...end

return context.runtime.newBoolean(isExclusive ? // begin <= obj < end || begin <= obj <= end
if (rangeLe(context, begin, obj) == null) {
return context.runtime.getFalse(); // obj < start...end
}
return context.runtime.newBoolean(isExclusive
? // begin <= obj < end || begin <= obj <= end
rangeLt(context, obj, end) != null : rangeLe(context, obj, end) != null);
}

@JRubyMethod(frame = true)
public IRubyObject min(ThreadContext context, Block block) {
if (block.isGiven()) return Helpers.invokeSuper(context, this, block);
if (block.isGiven()) {
return Helpers.invokeSuper(context, this, block);
}

int cmp = RubyComparable.cmpint(context, invokedynamic(context, begin, MethodNames.OP_CMP, end), begin, end);
if (cmp > 0 || (cmp == 0 && isExclusive)) return context.nil;
if (cmp > 0 || (cmp == 0 && isExclusive)) {
return context.nil;
}

return begin;
}
@@ -662,19 +748,23 @@ public IRubyObject max(ThreadContext context, Block block) {
}

int cmp = RubyComparable.cmpint(context, invokedynamic(context, begin, MethodNames.OP_CMP, end), begin, end);
if (cmp > 0) return context.nil;
if (cmp > 0) {
return context.nil;
}
if (isExclusive) {
if (!(end instanceof RubyInteger)) {
throw context.runtime.newTypeError("cannot exclude non Integer end value");
}

if (cmp == 0) return context.nil;
if (cmp == 0) {
return context.nil;
}

if (!(begin instanceof RubyInteger)) {
throw context.runtime.newTypeError("cannot exclude end value with non Integer begin value");
}
if (end instanceof RubyFixnum) {
return RubyFixnum.newFixnum(context.runtime, ((RubyFixnum)end).getLongValue() - 1);
return RubyFixnum.newFixnum(context.runtime, ((RubyFixnum) end).getLongValue() - 1);
}

return end.callMethod(context, "-", RubyFixnum.one(context.runtime));
@@ -697,11 +787,11 @@ public IRubyObject max(ThreadContext context, IRubyObject arg, Block block) {
public IRubyObject first(ThreadContext context) {
return begin;
}

@JRubyMethod
public IRubyObject begin(ThreadContext context) {
return begin;
}
}

@JRubyMethod
public IRubyObject first(ThreadContext context, IRubyObject arg) {
@@ -714,22 +804,26 @@ public IRubyObject first(ThreadContext context, IRubyObject arg) {
try {
RubyEnumerable.callEach(runtime, context, this, Signature.ONE_ARGUMENT, new BlockCallback() {
int n = num;

@Override
public IRubyObject call(ThreadContext ctx, IRubyObject[] largs, Block blk) {
if (n-- <= 0) throw JumpException.SPECIAL_JUMP;
if (n-- <= 0) {
throw JumpException.SPECIAL_JUMP;
}
result.append(largs[0]);
return runtime.getNil();
}
});
} catch (JumpException.SpecialJump sj) {}
} catch (JumpException.SpecialJump sj) {
}
return result;
}

@JRubyMethod
public IRubyObject last(ThreadContext context) {
return end;
}

@JRubyMethod
public IRubyObject end(ThreadContext context) {
return end;
@@ -748,13 +842,15 @@ public IRubyObject size(ThreadContext context) {
return context.nil;
}

public final boolean isExcludeEnd() { return isExclusive; }
public final boolean isExcludeEnd() {
return isExclusive;
}

private static final ObjectMarshal RANGE_MARSHAL = new ObjectMarshal() {
@Override
public void marshalTo(Ruby runtime, Object obj, RubyClass type,
MarshalStream marshalStream) throws IOException {
RubyRange range = (RubyRange)obj;
MarshalStream marshalStream) throws IOException {
RubyRange range = (RubyRange) obj;

marshalStream.registerLinkTarget(range);
List<Variable<Object>> attrs = range.getVariableList();
@@ -768,17 +864,17 @@ public void marshalTo(Ruby runtime, Object obj, RubyClass type,

@Override
public Object unmarshalFrom(Ruby runtime, RubyClass type,
UnmarshalStream unmarshalStream) throws IOException {
RubyRange range = (RubyRange)type.allocate();
UnmarshalStream unmarshalStream) throws IOException {
RubyRange range = (RubyRange) type.allocate();

unmarshalStream.registerLinkTarget(range);

// FIXME: Maybe we can just gank these off the line directly?
unmarshalStream.defaultVariablesUnmarshal(range);
range.begin = (IRubyObject)range.removeInternalVariable("begin");
range.end = (IRubyObject)range.removeInternalVariable("end");
range.isExclusive = ((IRubyObject)range.removeInternalVariable("excl")).isTrue();

range.begin = (IRubyObject) range.removeInternalVariable("begin");
range.end = (IRubyObject) range.removeInternalVariable("end");
range.isExclusive = ((IRubyObject) range.removeInternalVariable("excl")).isTrue();

return range;
}
75 changes: 71 additions & 4 deletions spec/ruby/core/range/eql_spec.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,73 @@
require File.expand_path('../../../spec_helper', __FILE__)
require File.expand_path('../shared/equal_value', __FILE__)
# Custom Range classes Xs and Ys
class Custom
include Comparable
attr :length
def initialize(n)
@length = n
end
def eql?(other)
inspect.eql? other.inspect
end
def inspect
'custom'
end
def <=>(other)
@length <=> other.length
end
def to_s
sprintf "%2d #{inspect}", @length
end
end

class Xs < Custom # represent a string of 'x's
def succ
Xs.new(@length + 1)
end
def inspect
'x' * @length
end
end

class Ys < Custom # represent a string of 'y's
def succ
Ys.new(@length + 1)
end
def inspect
'y' * @length
end
end

describe :range_eql, shared: true do
it "returns true if other has same begin, end, and exclude_end? values" do
(0..2).send(@method, 0..2).should == true
('G'..'M').send(@method,'G'..'M').should == true
(0.5..2.4).send(@method, 0.5..2.4).should == true
(5..10).send(@method, Range.new(5,10)).should == true
('D'..'V').send(@method, Range.new('D','V')).should == true
(0.5..2.4).send(@method, Range.new(0.5, 2.4)).should == true
(0xffff..0xfffff).send(@method, 0xffff..0xfffff).should == true
(0xffff..0xfffff).send(@method, Range.new(0xffff,0xfffff)).should == true
(Xs.new(3)..Xs.new(5)).send(@method, Range.new(Xs.new(3), Xs.new(5))).should == true

(0..1).send(@method, 0..1.0).should == false
('Q'..'X').send(@method, 'A'..'C').should == false
('Q'...'X').send(@method, 'Q'..'W').should == false
('Q'..'X').send(@method, 'Q'...'X').should == false
(0.5..2.4).send(@method, 0.5...2.4).should == false
(1482..1911).send(@method, 1482...1911).should == false
(0xffff..0xfffff).send(@method, 0xffff...0xfffff).should == false
(Xs.new(3)..Xs.new(5)).send(@method, Range.new(Ys.new(3), Ys.new(5))).should == false
end

it "returns false if other is no Range" do
(1..10).send(@method, 1).should == false
(1..10).send(@method, 'a').should == false
(1..10).send(@method, mock('x')).should == false
end

describe "Range#eql?" do
it_behaves_like(:range_eql, :eql?)
it "returns true for subclasses to Range" do
class MyRange < Range ; end
Range.new(1, 2).send(@method, MyRange.new(1, 2)).should == true
Range.new(Xs.new(3), Xs.new(5)).send(@method, MyRange.new(Xs.new(3), Xs.new(5))).should == true
end
end
2 changes: 1 addition & 1 deletion spec/ruby/core/range/range_spec.rb
Original file line number Diff line number Diff line change
@@ -3,5 +3,5 @@
describe "Range" do
it "includes Enumerable" do
Range.include?(Enumerable).should == true
end
end
end