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: b36d85d9e42e
Choose a base ref
...
head repository: jruby/jruby
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 320437a44c68
Choose a head ref
  • 13 commits
  • 5 files changed
  • 1 contributor

Commits on Mar 31, 2016

  1. Copy the full SHA
    3ecdda9 View commit details
  2. Copy the full SHA
    c7a19a8 View commit details
  3. override more of Hash in MapJavaProxy (not just) for improved compati…

    …bility
    
    mostly motivated by the fact that potential external modifications to a Map instance we're proxy-ing for JRuby might change its size thus leading to incorrect behavior (and hard to track bugs)
    kares committed Mar 31, 2016
    Copy the full SHA
    316054d View commit details
  4. Copy the full SHA
    f7a9b58 View commit details
  5. Copy the full SHA
    8a74413 View commit details
  6. Copy the full SHA
    f83a074 View commit details
  7. Copy the full SHA
    df7a913 View commit details
  8. minor RubyHash cleanup

    kares committed Mar 31, 2016
    Copy the full SHA
    de0705c View commit details
  9. minor RecursiveComparator cleanup - avodi initial null value + no nee…

    …d for runtime local
    kares committed Mar 31, 2016
    Copy the full SHA
    7283502 View commit details
  10. Copy the full SHA
    1679753 View commit details
  11. Copy the full SHA
    68494d2 View commit details
  12. Copy the full SHA
    87fd261 View commit details
  13. Copy the full SHA
    320437a View commit details
193 changes: 78 additions & 115 deletions core/src/main/java/org/jruby/RubyHash.java
Original file line number Diff line number Diff line change
@@ -265,6 +265,13 @@ public RubyHash(Ruby runtime, IRubyObject defaultValue, int buckets) {
allocFirst(buckets);
}

protected RubyHash(Ruby runtime, RubyClass metaClass, IRubyObject defaultValue, RubyHashEntry[] initialTable, int threshold) {
super(runtime, metaClass);
this.ifNone = defaultValue;
this.threshold = threshold;
this.table = initialTable;
}

/*
* Constructor for internal usage (mainly for Array#|, Array#&, Array#- and Array#uniq)
* it doesn't initialize ifNone field
@@ -789,26 +796,6 @@ public void modify() {
*
*/
private IRubyObject inspectHash(final ThreadContext context) {
final RubyString str = RubyString.newStringLight(context.runtime, DEFAULT_INSPECT_STR_SIZE);
str.cat((byte)'{');
final boolean[] firstEntry = new boolean[1];

firstEntry[0] = true;
visitAll(new Visitor() {
@Override
public void visit(IRubyObject key, IRubyObject value) {
if (!firstEntry[0]) str.cat((byte)',').cat((byte)' ');

str.cat(inspect(context, key)).cat((byte)'=').cat((byte)'>').cat(inspect(context, value));

firstEntry[0] = false;
}
});
str.cat((byte)'}');
return str;
}

private IRubyObject inspectHash19(final ThreadContext context) {
final RubyString str = RubyString.newStringLight(context.runtime, DEFAULT_INSPECT_STR_SIZE, USASCIIEncoding.INSTANCE);
str.cat((byte)'{');
final boolean[] firstEntry = new boolean[1];
@@ -831,23 +818,23 @@ public void visit(IRubyObject key, IRubyObject value) {
/** rb_hash_inspect
*
*/
public IRubyObject inspect(ThreadContext context) {
return inspect19(context);
}

@JRubyMethod(name = "inspect")
public IRubyObject inspect19(ThreadContext context) {
public IRubyObject inspect(ThreadContext context) {
if (size == 0) return RubyString.newUSASCIIString(context.runtime, "{}");
if (getRuntime().isInspecting(this)) return RubyString.newUSASCIIString(context.runtime, "{...}");

try {
getRuntime().registerInspecting(this);
return inspectHash19(context);
return inspectHash(context);
} finally {
getRuntime().unregisterInspecting(this);
}
}

public IRubyObject inspect19(ThreadContext context) {
return inspect(context);
}

/** rb_hash_size
*
*/
@@ -1005,13 +992,13 @@ public IRubyObject yieldArray(ThreadContext context, IRubyObject value, IRubyObj
/** rb_hash_to_s & to_s_hash
*
*/
@JRubyMethod(name = "to_s")
public IRubyObject to_s(ThreadContext context) {
return to_s19(context);
return inspect(context);
}

@JRubyMethod(name = "to_s")
public IRubyObject to_s19(ThreadContext context) {
return inspect19(context);
public final IRubyObject to_s19(ThreadContext context) {
return to_s(context);
}

/** rb_hash_rehash
@@ -1146,9 +1133,8 @@ public RubyBoolean compare(final ThreadContext context, final MethodNames method
if (!(other instanceof RubyHash)) {
if (!other.respondsTo("to_hash")) {
return runtime.getFalse();
} else {
return Helpers.rbEqual(context, other, this);
}
return Helpers.rbEqual(context, other, this);
}

final RubyHash otherHash = (RubyHash) other;
@@ -1185,8 +1171,8 @@ public void visit(IRubyObject key, IRubyObject value) {
/** rb_hash_equal
*
*/
@JRubyMethod(name = "==")
@Override
@JRubyMethod(name = "==")
public IRubyObject op_equal(final ThreadContext context, IRubyObject other) {
return RecursiveComparator.compare(context, MethodNames.OP_EQUAL, this, other);
}
@@ -1195,10 +1181,15 @@ public IRubyObject op_equal(final ThreadContext context, IRubyObject other) {
*
*/
@JRubyMethod(name = "eql?")
public IRubyObject op_eql19(final ThreadContext context, IRubyObject other) {
public IRubyObject op_eql(final ThreadContext context, IRubyObject other) {
return RecursiveComparator.compare(context, MethodNames.EQL, this, other);
}

@Deprecated
public IRubyObject op_eql19(final ThreadContext context, IRubyObject other) {
return op_eql(context, other);
}

/** rb_hash_aref
*
*/
@@ -1250,54 +1241,33 @@ public IRubyObject op_ge(ThreadContext context, IRubyObject other) {
/** rb_hash_hash
*
*/
// FIXME:
@Override
@JRubyMethod(name = "hash")
public RubyFixnum hash() {
final Ruby runtime = getRuntime();
if (size == 0) return RubyFixnum.zero(runtime);
final ThreadContext context = runtime.getCurrentContext();
if (size == 0 || runtime.isInspecting(this)) return RubyFixnum.zero(runtime);
final long hash[] = new long[]{size};
try {
runtime.registerInspecting(this);
visitAll(new Visitor() {
public void visit(IRubyObject key, IRubyObject value) {
hash[0] ^= invokedynamic(context, key, HASH).convertToInteger().getLongValue();
hash[0] ^= invokedynamic(context, value, HASH).convertToInteger().getLongValue();
return (RubyFixnum) runtime.execRecursiveOuter(new Ruby.RecursiveFunction() {
@Override
public IRubyObject call(IRubyObject obj, boolean recur) {
if (recur) {
return invokedynamic(context, runtime.getHash(), HASH).convertToInteger();
}
});
} finally {
runtime.unregisterInspecting(this);
}
return RubyFixnum.newFixnum(runtime, hash[0]);
final long[] h = new long[]{1};
visitAll(new Visitor() {
@Override
public void visit(IRubyObject key, IRubyObject value) {
h[0] += invokedynamic(context, key, HASH).convertToInteger().getLongValue() ^ invokedynamic(context, value, HASH).convertToInteger().getLongValue();
}
});
return runtime.newFixnum(h[0]);
}
}, this);
}

/** rb_hash_hash
*
*/
@JRubyMethod(name = "hash")
@Deprecated
public RubyFixnum hash19() {
final Ruby runtime = getRuntime();
final ThreadContext context = runtime.getCurrentContext();
return (RubyFixnum)getRuntime().execRecursiveOuter(new Ruby.RecursiveFunction() {
@Override
public IRubyObject call(IRubyObject obj, boolean recur) {
if(size == 0) {
return RubyFixnum.zero(runtime);
}
final long[] h = new long[]{1};
if(recur) {
h[0] ^= RubyNumeric.num2long(invokedynamic(context, runtime.getHash(), HASH));
} else {
visitAll(new Visitor() {
@Override
public void visit(IRubyObject key, IRubyObject value) {
h[0] += invokedynamic(context, key, HASH).convertToInteger().getLongValue() ^ invokedynamic(context, value, HASH).convertToInteger().getLongValue();
}
});
}
return runtime.newFixnum(h[0]);
}
}, this);
return hash();
}

/** rb_hash_fetch
@@ -1477,13 +1447,13 @@ public void visit(IRubyObject key, IRubyObject value) {
return this;
}

@JRubyMethod(name = {"each", "each_pair"})
public IRubyObject each(final ThreadContext context, final Block block) {
return each19(context, block);
return block.isGiven() ? each_pairCommon(context, block, true) : enumeratorizeWithSize(context, this, "each", enumSizeFn());
}

@JRubyMethod(name = {"each", "each_pair"})
public IRubyObject each19(final ThreadContext context, final Block block) {
return block.isGiven() ? each_pairCommon(context, block, true) : enumeratorizeWithSize(context, this, "each", enumSizeFn());
return each(context, block);
}

/** rb_hash_each_pair
@@ -1585,14 +1555,14 @@ public IRubyObject sort(ThreadContext context, Block block) {
/** rb_hash_index
*
*/
@JRubyMethod(name = "index")
public IRubyObject index(ThreadContext context, IRubyObject expected) {
return index19(context, expected);
context.runtime.getWarnings().warn(ID.DEPRECATED_METHOD, "Hash#index is deprecated; use Hash#key");
return key(context, expected);
}

@JRubyMethod(name = "index")
public IRubyObject index19(ThreadContext context, IRubyObject expected) {
context.runtime.getWarnings().warn(ID.DEPRECATED_METHOD, "Hash#index is deprecated; use Hash#key");
return key(context, expected);
return index(context, expected);
}

@JRubyMethod
@@ -1712,12 +1682,8 @@ public IRubyObject delete(ThreadContext context, IRubyObject key, Block block) {
/** rb_hash_select
*
*/
public IRubyObject select(final ThreadContext context, final Block block) {
return select19(context, block);
}

@JRubyMethod(name = "select")
public IRubyObject select19(final ThreadContext context, final Block block) {
public IRubyObject select(final ThreadContext context, final Block block) {
final Ruby runtime = context.runtime;
if (!block.isGiven()) return enumeratorizeWithSize(context, this, "select", enumSizeFn());

@@ -1735,13 +1701,17 @@ public void visit(IRubyObject key, IRubyObject value) {
return result;
}

public IRubyObject select19(final ThreadContext context, final Block block) {
return select(context, block);
}

/** rb_hash_delete_if
*
*/
public RubyHash delete_ifInternal(final ThreadContext context, final Block block) {
modify();

final Ruby runtime = getRuntime();
final Ruby runtime = context.runtime;
final RubyHash self = this;
iteratorVisitAll(new Visitor() {
@Override
@@ -1819,24 +1789,17 @@ public void visit(IRubyObject key, IRubyObject value) {
return result;
}

/** rb_hash_update
*
*/
public RubyHash merge_bang(final ThreadContext context, final IRubyObject other, final Block block) {
return merge_bang19(context, other, block);
}

/** rb_hash_update
*
*/
@JRubyMethod(name = {"merge!", "update"}, required = 1)
public RubyHash merge_bang19(final ThreadContext context, final IRubyObject other, final Block block) {
public RubyHash merge_bang(final ThreadContext context, final IRubyObject other, final Block block) {
modify();
final RubyHash otherHash = other.convertToHash();

if (otherHash.empty_p().isTrue()) return this;

final Ruby runtime = getRuntime();
final Ruby runtime = context.runtime;
final RubyHash self = this;
otherHash.visitAll(new Visitor() {
@Override
@@ -1854,6 +1817,11 @@ public void visit(IRubyObject key, IRubyObject value) {
return this;
}

@Deprecated
public RubyHash merge_bang19(final ThreadContext context, final IRubyObject other, final Block block) {
return merge_bang(context, other, block);
}

/** rb_hash_merge
*
*/
@@ -1862,40 +1830,35 @@ public RubyHash merge(ThreadContext context, IRubyObject other, Block block) {
return ((RubyHash)dup()).merge_bang(context, other, block);
}

/** rb_hash_replace
*
*/
@JRubyMethod(name = "initialize_copy", required = 1, visibility = PRIVATE)
public RubyHash initialize_copy(ThreadContext context, IRubyObject other) {
return initialize_copy19(context, other);
return replace(context, other);
}

/** rb_hash_replace
*
*/
@JRubyMethod(name = "initialize_copy", required = 1, visibility = PRIVATE)
@Deprecated
public RubyHash initialize_copy19(ThreadContext context, IRubyObject other) {
return replace19(context, other);
return initialize_copy(context, other);
}

/** rb_hash_replace
*
*/
public RubyHash replace(final ThreadContext context, IRubyObject other) {
return replace19(context, other);
}

@JRubyMethod(name = "replace", required = 1)
public RubyHash replace19(final ThreadContext context, IRubyObject other) {
public RubyHash replace(final ThreadContext context, IRubyObject other) {
final RubyHash self = this;
return replaceCommon19(context, other, new Visitor() {
return replaceCommon(context, other, new Visitor() {
@Override
public void visit(IRubyObject key, IRubyObject value) {
self.op_aset(context, key, value);
}
});
}

private RubyHash replaceCommon19(final ThreadContext context, IRubyObject other, Visitor visitor) {
public RubyHash replace19(final ThreadContext context, IRubyObject other) {
return replace(context, other);
}

private RubyHash replaceCommon(final ThreadContext context, IRubyObject other, Visitor visitor) {
modify();

final RubyHash otherHash = other.convertToHash();
@@ -1954,7 +1917,7 @@ public void visit(IRubyObject key, IRubyObject value) {
}
}
});
return context.runtime.getNil();
return context.nil;
} catch (FoundPair found) {
return context.runtime.newArray(found.key, found.value);
}
@@ -1971,7 +1934,7 @@ public void visit(IRubyObject key, IRubyObject value) {
}
}
});
return context.runtime.getNil();
return context.nil;
} catch (FoundPair found) {
return context.runtime.newArray(found.key, found.value);
}
@@ -2144,7 +2107,7 @@ public Class getJavaClass() {
return Map.class;
}

// Satisfy java.util.Set interface (for Java integration)
// Satisfy java.util.Map interface (for Java integration)

@Override
public int size() {
139 changes: 104 additions & 35 deletions core/src/main/java/org/jruby/java/proxies/MapJavaProxy.java
Original file line number Diff line number Diff line change
@@ -43,6 +43,7 @@
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.Visibility;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.runtime.invokedynamic.MethodNames;

import java.util.Map;
import java.util.Set;
@@ -98,20 +99,81 @@ private RubyHashMap getOrCreateRubyHashMap() {
return wrappedMap;
}

private static class RubyHashMap extends RubyHash {
private static final class RubyHashMap extends RubyHash {

static final RubyHashEntry[] EMPTY_TABLE = new RubyHashEntry[0];

private final MapJavaProxy receiver;

public RubyHashMap(Ruby runtime, MapJavaProxy receiver) {
super(runtime, 0);
RubyHashMap(Ruby runtime, MapJavaProxy receiver) {
super(runtime, runtime.getHash(), runtime.getNil(), EMPTY_TABLE, 0);
this.receiver = receiver;
}

private void syncSize() { this.size = mapDelegate().size(); }

private void setSize(int size) { this.size = size; }

// the underlying Map object operations should be delegated to
private Map mapDelegate() { return receiver.getMapObject(); }

@Override
public RubyFixnum rb_size() {
return getRuntime().newFixnum( mapDelegate().size() );
}

@Override
public RubyBoolean empty_p() {
return mapDelegate().isEmpty() ? getRuntime().getTrue() : getRuntime().getFalse();
}

@Override
public IRubyObject inspect(ThreadContext context) {
syncSize();
return super.inspect(context);
}

@Override
public RubyArray to_a() {
syncSize();
return super.to_a();
}

@Override
public RubyFixnum hash() {
return getRuntime().newFixnum( mapDelegate().hashCode() );
}

@Override
public RubyArray keys() {
syncSize();
return super.keys();
}

@Override
public RubyArray rb_values() {
syncSize();
return super.rb_values();
}

@Override
public IRubyObject delete(ThreadContext context, IRubyObject key, Block block) {
modify();

Object value = mapDelegate().remove(key.toJava(Object.class));
if ( value != null ) return JavaUtil.convertJavaToUsableRubyObject(getRuntime(), value);

if ( block.isGiven() ) return block.yield(context, key);
return context.nil;
}

@Override
public RubyHash delete_ifInternal(final ThreadContext context, final Block block) {
RubyHash self = super.delete_ifInternal(context, block);
setSize( mapDelegate().size() );
return self;
}

@Override
public void internalPut(final IRubyObject key, final IRubyObject value, final boolean checkForExisting) {
internalPutSmall(key, value, checkForExisting);
@@ -122,15 +184,15 @@ protected final void internalPutSmall(IRubyObject key, IRubyObject value, boolea
@SuppressWarnings("unchecked")
final Map<Object, Object> map = mapDelegate();
map.put(key.toJava(Object.class), value.toJava(Object.class));
this.size = map.size();
setSize( map.size() );
}

@Override
protected final void op_asetForString(Ruby runtime, RubyString key, IRubyObject value) {
@SuppressWarnings("unchecked")
final Map<Object, Object> map = mapDelegate();
map.put(key.decodeString(), value.toJava(Object.class));
this.size = map.size();
setSize( map.size() );
}

@Override
@@ -166,7 +228,7 @@ public RubyHashEntry internalDelete(final IRubyObject key) {

if (value != null) {
map.remove(convertedKey);
this.size = map.size();
setSize( map.size() );
return new RubyHashEntry(key.hashCode(), key, JavaUtil.convertJavaToUsableRubyObject(getRuntime(), value), null, null);
}
return NO_ENTRY;
@@ -179,7 +241,7 @@ public RubyHashEntry internalDeleteEntry(final RubyHashEntry entry) {

if (map.containsKey(convertedKey)) {
map.remove(convertedKey);
this.size = map.size();
setSize( map.size() );
return entry;
}

@@ -199,22 +261,43 @@ public void visitAll(Visitor visitor) {
}
}

@Override
public RubyBoolean compare(final ThreadContext context, final MethodNames method, IRubyObject other) {
setSize( mapDelegate().size() );
if ( other instanceof RubyHashMap ) {
((RubyHashMap) other).syncSize();
}
return super.compare(context, method, other);
}

@Override
public RubyBoolean has_key_p(IRubyObject key) {
final Object convertedKey = key.toJava(Object.class);
return getRuntime().newBoolean( mapDelegate().containsKey(convertedKey) );
}

@Override
public RubyBoolean has_value_p(ThreadContext context, IRubyObject val) {
final Object convertedVal = val.toJava(Object.class);
return getRuntime().newBoolean( mapDelegate().containsValue(convertedVal) );
}

@Override
public RubyHash rehash() {
// java.util.Map does not expose rehash, and many maps don't use hashing, so we do nothing. #3142
return this;
}

@Override
public RubyBoolean getCompareByIdentity_p(ThreadContext context) {
// NOTE: obviously little we can do to detect - but at least report Java built-in one :
return context.runtime.newBoolean( mapDelegate() instanceof java.util.IdentityHashMap );
}

@Override
public RubyHash rb_clear() {
mapDelegate().clear();
this.size = 0;
setSize( 0 );
return this;
}

@@ -277,7 +360,7 @@ public IRubyObject set_default_proc(IRubyObject proc) {
*/
@JRubyMethod(name = "inspect")
public IRubyObject inspect(ThreadContext context) {
return getOrCreateRubyHashMap().inspect19(context);
return getOrCreateRubyHashMap().inspect(context);
}

/** rb_hash_size
@@ -309,7 +392,7 @@ public RubyArray to_a() {
*/
@JRubyMethod(name = "to_s")
public IRubyObject to_s(ThreadContext context) {
return getOrCreateRubyHashMap().to_s19(context);
return getOrCreateRubyHashMap().to_s(context);
}

/** rb_hash_rehash
@@ -344,12 +427,12 @@ public IRubyObject op_equal(final ThreadContext context, IRubyObject other) {
return getOrCreateRubyHashMap().op_equal(context, other);
}

/** rb_hash_eql19
/** rb_hash_eql
*
*/
@JRubyMethod(name = "eql?")
public IRubyObject op_eql19(final ThreadContext context, IRubyObject other) {
return getOrCreateRubyHashMap().op_eql19(context, other);
public IRubyObject op_eql(final ThreadContext context, IRubyObject other) {
return getOrCreateRubyHashMap().op_eql(context, other);
}

/** rb_hash_aref
@@ -365,7 +448,7 @@ public IRubyObject op_aref(ThreadContext context, IRubyObject key) {
*/
@JRubyMethod(name = "hash")
public RubyFixnum hash() {
return getOrCreateRubyHashMap().hash19();
return getOrCreateRubyHashMap().hash();
}

/** rb_hash_fetch
@@ -400,18 +483,9 @@ public RubyBoolean has_value_p(ThreadContext context, IRubyObject expected) {
/** rb_hash_each
*
*/
@JRubyMethod
@JRubyMethod(name = {"each", "each_pair"})
public IRubyObject each(final ThreadContext context, final Block block) {
return getOrCreateRubyHashMap().each19(context, block);
}


/** rb_hash_each_pair
*
*/
@JRubyMethod(name = "each_pair")
public IRubyObject each_pair(final ThreadContext context, final Block block) {
return getOrCreateRubyHashMap().each_pair(context, block);
return getOrCreateRubyHashMap().each(context, block);
}

/** rb_hash_each_value
@@ -459,7 +533,7 @@ public IRubyObject sort(ThreadContext context, Block block) {
*/
@JRubyMethod(name = "index")
public IRubyObject index(ThreadContext context, IRubyObject expected) {
return getOrCreateRubyHashMap().index19(context, expected);
return getOrCreateRubyHashMap().index(context, expected);
}

/** rb_hash_key
@@ -507,7 +581,7 @@ public IRubyObject delete(ThreadContext context, IRubyObject key, Block block) {
*/
@JRubyMethod(name = "select")
public IRubyObject select(final ThreadContext context, final Block block) {
return getOrCreateRubyHashMap().select19(context, block);
return getOrCreateRubyHashMap().select(context, block);
}

/** rb_hash_delete_if
@@ -555,7 +629,7 @@ public RubyHash invert(final ThreadContext context) {
*/
@JRubyMethod(name = {"merge!", "update"}, required = 1)
public RubyHash merge_bang(final ThreadContext context, final IRubyObject other, final Block block) {
return getOrCreateRubyHashMap().merge_bang19(context, other, block);
return getOrCreateRubyHashMap().merge_bang(context, other, block);
}

/** rb_hash_merge
@@ -571,15 +645,15 @@ public RubyHash merge(ThreadContext context, IRubyObject other, Block block) {
*/
@JRubyMethod(name = "initialize_copy", visibility = Visibility.PRIVATE)
public RubyHash initialize_copy(ThreadContext context, IRubyObject other) {
return getOrCreateRubyHashMap().initialize_copy19(context, other);
return getOrCreateRubyHashMap().initialize_copy(context, other);
}

/** rb_hash_replace
*
*/
@JRubyMethod(name = "replace", required = 1)
public RubyHash replace(final ThreadContext context, IRubyObject other) {
return getOrCreateRubyHashMap().replace19(context, other);
return getOrCreateRubyHashMap().replace(context, other);
}

/** rb_hash_values_at
@@ -610,11 +684,6 @@ public IRubyObject flatten(ThreadContext context, IRubyObject level) {
return getOrCreateRubyHashMap().flatten(context, level);
}

@JRubyMethod(name = "compare_by_identity")
public IRubyObject getCompareByIdentity(ThreadContext context) {
return getOrCreateRubyHashMap().getCompareByIdentity(context);
}

@JRubyMethod(name = "compare_by_identity?")
public IRubyObject getCompareByIdentity_p(ThreadContext context) {
return getOrCreateRubyHashMap().getCompareByIdentity_p(context);
9 changes: 4 additions & 5 deletions core/src/main/java/org/jruby/util/RecursiveComparator.java
Original file line number Diff line number Diff line change
@@ -15,17 +15,16 @@
public class RecursiveComparator
{
public static IRubyObject compare(ThreadContext context, final MethodNames method, IRubyObject a, IRubyObject b) {
Ruby runtime = context.runtime;

if (a == b) {
return runtime.getTrue();
return context.runtime.getTrue();
}

boolean clear = false; // whether to clear thread-local set (at top comparison)

try {
Set<Pair> seen = null;
Set<Pair> seen;

if (a instanceof RubyHash && b instanceof RubyHash ||
a instanceof RubyArray && b instanceof RubyArray) {

@@ -36,7 +35,7 @@ public static IRubyObject compare(ThreadContext context, final MethodNames metho
clear = true;
}
else if (seen.contains(pair)) { // are we recursing?
return runtime.getTrue();
return context.runtime.getTrue();
}

seen.add(pair);
27 changes: 27 additions & 0 deletions spec/java_integration/types/map_spec.rb
Original file line number Diff line number Diff line change
@@ -2,6 +2,13 @@

describe "a java.util.Map instance" do

it "return compared_by_identity for IdentityHashMap" do
h = java.util.HashMap.new
expect( h.compare_by_identity? ).to be false
h = java.util.IdentityHashMap.new
expect( h.compare_by_identity? ).to be true
end

it "supports Hash-like operations" do
h = java.util.HashMap.new
test_ok(h.kind_of? java.util.Map)
@@ -21,6 +28,26 @@
test_equal("z not found", h.delete("z") { |el| "#{el} not found" })
test_equal({"c"=>300}, h.delete_if { |key, value| key <= "b" })

h = java.util.concurrent.ConcurrentHashMap.new(10)
h.put(:b, 200); h.put(:c, 300); h.put(:a, 100)
test_equal(h.delete_if { |key, value| key <= :b }, {:"c"=>300})
expect( {:c => 300}.eql? h ).to be true
h.put(:b, 200)
expect( {'c'.to_sym => 300}.eql? h ).to be false
h.remove(:b)
expect( h.eql?({:c => 300}) ).to be true

h.clear; h['a'] = 100; h['c'] = 300; h.put('b', 200)

h2 = java.util.TreeMap.new
h2.put('b', 200); h2.put('c', 300); h2.put('a', 100)
test_equal( h2, h )
expect( h2.eql? h ).to be true
expect( h2.equal? h ).to be false

h.remove('c')
test_equal( h2.delete_if { |key, value| key >= 'c' }, h )

h = java.util.LinkedHashMap.new
h.put("a", 100); h.put("b", 200); h.put("c", 300)
a1=[]; a2=[]
48 changes: 12 additions & 36 deletions test/jruby/test_higher_javasupport.rb
Original file line number Diff line number Diff line change
@@ -973,27 +973,13 @@ def test_that_multiple_threads_including_classes_dont_step_on_each_other
end
end

if javax.xml.namespace.NamespaceContext.instance_of?(Module)

class NSCT
include javax.xml.namespace.NamespaceContext
# JRUBY-66: No super here...make sure we still work.
def initialize(arg); end
def getNamespaceURI(prefix)
'ape:sex'
end
end

else

class NSCT < javax.xml.namespace.NamespaceContext
# JRUBY-66: No super here...make sure we still work.
def initialize(arg); end
def getNamespaceURI(prefix)
'ape:sex'
end
class NSCT
include javax.xml.namespace.NamespaceContext
# JRUBY-66: No super here...make sure we still work.
def initialize(arg); end
def getNamespaceURI(prefix)
'ape:sex'
end

end

def test_no_need_to_call_super_in_initialize_when_implementing_java_interfaces
@@ -1014,23 +1000,13 @@ def test_that_class_methods_are_being_camel_cased
assert(java.lang.System.respond_to?("current_time_millis"))
end

if Java::java.lang.Runnable.instance_of?(Module)
class TestInitBlock
include Java::java.lang.Runnable
def initialize(&block)
raise if !block
@bar = block.call
end
def bar; @bar; end
end
else
class TestInitBlock < Java::java.lang.Runnable
def initialize(&block)
raise if !block
@bar = block.call
end
def bar; @bar; end
class TestInitBlock
include Java::java.lang.Runnable
def initialize(&block)
raise if !block
@bar = block.call
end
def bar; @bar; end
end

def test_that_blocks_are_passed_through_to_the_constructor_for_an_interface_impl