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

Commits on Dec 16, 2016

  1. Fix typo

    eregon committed Dec 16, 2016
    Copy the full SHA
    abaffa3 View commit details
  2. Copy the full SHA
    f2b0915 View commit details
  3. Copy the full SHA
    908f5af View commit details
  4. Copy the full SHA
    95c9ae6 View commit details
  5. Copy the full SHA
    7074942 View commit details
  6. Copy the full SHA
    7dc7106 View commit details
21 changes: 19 additions & 2 deletions spec/ruby/core/hash/compare_by_identity_spec.rb
Original file line number Diff line number Diff line change
@@ -14,10 +14,27 @@
@h["a".dup].should be_nil
end

it "rehashes internally so that old keys can be looked up" do
h = {}
(1..10).each { |k| h[k] = k }
o = Object.new
def o.hash; 123; end
h[o] = 1
h.compare_by_identity
h[o].should == 1
end

it "returns self" do
h = {}
h[:foo] = :bar
h.compare_by_identity.should == h
h.compare_by_identity.should equal h
end

it "has no effect on an already compare_by_identity hash" do
@idh[:foo] = :bar
@idh.compare_by_identity.should equal @idh
@idh.compare_by_identity?.should == true
@idh[:foo].should == :bar
end

it "uses the semantics of BasicObject#equal? to determine key identity" do
@@ -72,7 +89,7 @@
end

# Behaviour confirmed in bug #1871
it "perists over #dups" do
it "persists over #dups" do
@idh['foo'] = :bar
@idh['foo'] = :glark
@idh.dup.should == @idh
7 changes: 7 additions & 0 deletions spec/ruby/core/hash/shared/store.rb
Original file line number Diff line number Diff line change
@@ -34,6 +34,13 @@ def key.reverse() "bar" end
h[key].should == "foo"
end

it " accepts keys with a Bignum hash" do
o = mock(hash: 1 << 100)
h = {}
h[o] = 1
h[o].should == 1
end

it "duplicates and freezes string keys" do
key = "foo"
h = {}
Original file line number Diff line number Diff line change
@@ -11,7 +11,10 @@

import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.object.DynamicObject;
import com.oracle.truffle.api.profiles.ConditionProfile;

import org.jruby.truffle.Layouts;
import org.jruby.truffle.core.ObjectNodes.ObjectIDPrimitiveNode;
import org.jruby.truffle.core.ObjectNodesFactory.ObjectIDPrimitiveNodeFactory;
import org.jruby.truffle.language.RubyBaseNode;
@@ -25,6 +28,7 @@ public class HashNode extends RubyBaseNode {

private final ConditionProfile isIntegerProfile = ConditionProfile.createBinaryProfile();
private final ConditionProfile isLongProfile = ConditionProfile.createBinaryProfile();
private final ConditionProfile isBignumProfile = ConditionProfile.createBinaryProfile();

public int hash(VirtualFrame frame, Object key, boolean compareByIdentity) {
final Object hashedObject;
@@ -38,6 +42,8 @@ public int hash(VirtualFrame frame, Object key, boolean compareByIdentity) {
return (int) hashedObject;
} else if (isLongProfile.profile(hashedObject instanceof Long)) {
return (int) (long) hashedObject;
} else if (isBignumProfile.profile(Layouts.BIGNUM.isBignum(hashedObject))) {
return Layouts.BIGNUM.getValue((DynamicObject) hashedObject).hashCode();
} else {
throw new UnsupportedOperationException();
}
63 changes: 47 additions & 16 deletions truffle/src/main/java/org/jruby/truffle/core/hash/HashNodes.java
Original file line number Diff line number Diff line change
@@ -35,6 +35,7 @@
import org.jruby.truffle.core.array.ArrayBuilderNode;
import org.jruby.truffle.core.hash.HashNodesFactory.DefaultValueNodeFactory;
import org.jruby.truffle.core.hash.HashNodesFactory.GetIndexNodeFactory;
import org.jruby.truffle.core.hash.HashNodesFactory.InternalRehashNodeGen;
import org.jruby.truffle.language.NotProvided;
import org.jruby.truffle.language.PerformanceWarnings;
import org.jruby.truffle.language.RubyGuards;
@@ -351,11 +352,18 @@ public DynamicObject empty(DynamicObject hash) {
}

@CoreMethod(names = "compare_by_identity", raiseIfFrozenSelf = true)
@ImportStatic(HashGuards.class)
public abstract static class CompareByIdentityNode extends CoreMethodArrayArgumentsNode {

@Specialization
public DynamicObject compareByIdentity(DynamicObject hash) {
@Specialization(guards = "!isCompareByIdentity(hash)")
DynamicObject compareByIdentity(VirtualFrame frame, DynamicObject hash,
@Cached("create()") InternalRehashNode internalRehashNode) {
Layouts.HASH.setCompareByIdentity(hash, true);
return internalRehashNode.executeRehash(frame, hash);
}

@Specialization(guards = "isCompareByIdentity(hash)")
DynamicObject alreadyCompareByIdentity(DynamicObject hash) {
return hash;
}

@@ -1198,33 +1206,35 @@ public int sizePackedArray(DynamicObject hash) {

}

@CoreMethod(names = "rehash", raiseIfFrozenSelf = true)
@ImportStatic(HashGuards.class)
public abstract static class RehashNode extends CoreMethodArrayArgumentsNode {
@NodeChild("hash")
public abstract static class InternalRehashNode extends RubyNode {

@Child private HashNode hashNode = new HashNode();

@Specialization(guards = "isNullHash(hash)")
public DynamicObject rehashNull(DynamicObject hash) {
return hash;
public static InternalRehashNode create() {
return InternalRehashNodeGen.create(null);
}

@Specialization(guards = "isCompareByIdentity(hash)")
public DynamicObject rehashIdentity(DynamicObject hash) {
// the identity hash of objects never change.
public abstract DynamicObject executeRehash(VirtualFrame frame, DynamicObject hash);

@Specialization(guards = "isNullHash(hash)")
DynamicObject rehashNull(DynamicObject hash) {
return hash;
}

@Specialization(guards = {"!isCompareByIdentity(hash)", "isPackedHash(hash)"})
public DynamicObject rehashPackedArray(VirtualFrame frame, DynamicObject hash) {
@Specialization(guards = "isPackedHash(hash)")
DynamicObject rehashPackedArray(VirtualFrame frame, DynamicObject hash,
@Cached("createBinaryProfile()") ConditionProfile byIdentityProfile) {
assert HashOperations.verifyStore(getContext(), hash);

final boolean compareByIdentity = byIdentityProfile.profile(Layouts.HASH.getCompareByIdentity(hash));
final Object[] store = (Object[]) Layouts.HASH.getStore(hash);
final int size = Layouts.HASH.getSize(hash);

for (int n = 0; n < getContext().getOptions().HASH_PACKED_ARRAY_MAX; n++) {
if (n < size) {
PackedArrayStrategy.setHashed(store, n, hashNode.hash(frame, PackedArrayStrategy.getKey(store, n), false));
PackedArrayStrategy.setHashed(store, n, hashNode.hash(frame, PackedArrayStrategy.getKey(store, n), compareByIdentity));
}
}

@@ -1233,17 +1243,21 @@ public DynamicObject rehashPackedArray(VirtualFrame frame, DynamicObject hash) {
return hash;
}

@TruffleBoundary
@Specialization(guards = {"!isCompareByIdentity(hash)", "isBucketHash(hash)"})
public DynamicObject rehashBuckets(DynamicObject hash) {
@Specialization(guards = "isBucketHash(hash)")
DynamicObject rehashBuckets(VirtualFrame frame, DynamicObject hash,
@Cached("createBinaryProfile()") ConditionProfile byIdentityProfile) {
assert HashOperations.verifyStore(getContext(), hash);

final boolean compareByIdentity = byIdentityProfile.profile(Layouts.HASH.getCompareByIdentity(hash));
final Entry[] entries = (Entry[]) Layouts.HASH.getStore(hash);
Arrays.fill(entries, null);

Entry entry = Layouts.HASH.getFirstInSequence(hash);

while (entry != null) {
final int newHash = hashNode.hash(frame, entry.getKey(), compareByIdentity);
entry.setHashed(newHash);
entry.setNextInLookup(null);
final int index = BucketsStrategy.getBucketIndex(entry.getHashed(), entries.length);
Entry bucketEntry = entries[index];

@@ -1261,10 +1275,27 @@ public DynamicObject rehashBuckets(DynamicObject hash) {
}

assert HashOperations.verifyStore(getContext(), hash);
return hash;
}

}

@CoreMethod(names = "rehash", raiseIfFrozenSelf = true)
@ImportStatic(HashGuards.class)
public abstract static class RehashNode extends CoreMethodArrayArgumentsNode {

@Specialization(guards = "isCompareByIdentity(hash)")
public DynamicObject rehashIdentity(DynamicObject hash) {
// the identity hash of objects never change.
return hash;
}

@Specialization(guards = "!isCompareByIdentity(hash)")
public DynamicObject rehashNotIdentity(VirtualFrame frame, DynamicObject hash,
@Cached("create()") InternalRehashNode internalRehashNode) {
return internalRehashNode.executeRehash(frame, hash);
}

}

@NonStandard
Original file line number Diff line number Diff line change
@@ -73,7 +73,7 @@ public int chmod(DynamicObject path, int mode) {

}

@CoreMethod(names = "chown", isModuleFunction = true, required = 3, lowerFixnum = { 1, 2 }, unsafe = UnsafeGroup.IO)
@CoreMethod(names = "chown", isModuleFunction = true, required = 3, lowerFixnum = { 2, 3 }, unsafe = UnsafeGroup.IO)
public abstract static class ChownNode extends CoreMethodArrayArgumentsNode {

@CompilerDirectives.TruffleBoundary
@@ -356,7 +356,7 @@ public int utimes(DynamicObject path, DynamicObject pointer) {

}

@CoreMethod(names = "mkdir", isModuleFunction = true, required = 2, lowerFixnum = 1, unsafe = UnsafeGroup.IO)
@CoreMethod(names = "mkdir", isModuleFunction = true, required = 2, lowerFixnum = 2, unsafe = UnsafeGroup.IO)
public abstract static class MkdirNode extends CoreMethodArrayArgumentsNode {

@CompilerDirectives.TruffleBoundary
@@ -923,7 +923,7 @@ public int close(int file) {

}

@CoreMethod(names = "kill", isModuleFunction = true, unsafe = { UnsafeGroup.PROCESSES, UnsafeGroup.SIGNALS }, required = 3)
@CoreMethod(names = "kill", isModuleFunction = true, required = 3, lowerFixnum = { 1, 2 }, unsafe = { UnsafeGroup.PROCESSES, UnsafeGroup.SIGNALS })
public abstract static class KillNode extends CoreMethodArrayArgumentsNode {

@TruffleBoundary