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: 3525a1336ae0
Choose a base ref
...
head repository: jruby/jruby
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 41ce8ce4b75b
Choose a head ref
  • 3 commits
  • 16 files changed
  • 1 contributor

Commits on Mar 7, 2015

  1. [Truffle] Hash#shift

    chrisseaton committed Mar 7, 2015
    Copy the full SHA
    0085ad5 View commit details
  2. [Truffle] Hash#select

    chrisseaton committed Mar 7, 2015
    Copy the full SHA
    ffc272d View commit details
  3. Copy the full SHA
    41ce8ce View commit details
1 change: 0 additions & 1 deletion spec/truffle/tags/core/hash/delete_if_tags.txt

This file was deleted.

1 change: 0 additions & 1 deletion spec/truffle/tags/core/hash/delete_tags.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
fails:Hash#delete calls supplied block if the key is not found
fails:Hash#delete accepts keys with private #hash method
1 change: 0 additions & 1 deletion spec/truffle/tags/core/hash/element_reference_tags.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
fails:Hash#[] calls subclass implementations of default
fails:Hash#[] finds a value via an identical key even when its #eql? isn't reflexive
fails:Hash#[] supports keys with private #hash method
1 change: 0 additions & 1 deletion spec/truffle/tags/core/hash/element_set_tags.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
fails:Hash#[]= accepts keys with private #hash method
fails:Hash#[]= doesn't duplicate and freeze already frozen string keys
1 change: 0 additions & 1 deletion spec/truffle/tags/core/hash/has_key_tags.txt

This file was deleted.

1 change: 0 additions & 1 deletion spec/truffle/tags/core/hash/include_tags.txt

This file was deleted.

4 changes: 0 additions & 4 deletions spec/truffle/tags/core/hash/index_tags.txt

This file was deleted.

5 changes: 0 additions & 5 deletions spec/truffle/tags/core/hash/key_tags.txt

This file was deleted.

1 change: 0 additions & 1 deletion spec/truffle/tags/core/hash/member_tags.txt

This file was deleted.

1 change: 0 additions & 1 deletion spec/truffle/tags/core/hash/rehash_tags.txt

This file was deleted.

9 changes: 0 additions & 9 deletions spec/truffle/tags/core/hash/select_tags.txt

This file was deleted.

4 changes: 0 additions & 4 deletions spec/truffle/tags/core/hash/shift_tags.txt

This file was deleted.

Original file line number Diff line number Diff line change
@@ -24,7 +24,18 @@ public static boolean isBuckets(RubyHash hash) {

public static boolean isCompareByIdentity(RubyHash hash) {
return hash.isCompareByIdentity();

}

public static boolean isEmpty(RubyHash hash) {
return hash.getSize() == 0;
}

public static boolean hasDefaultValue(RubyHash hash) {
return hash.getDefaultValue() != null;
}

public static boolean hasDefaultBlock(RubyHash hash) {
return hash.getDefaultBlock() != null;
}

}
148 changes: 146 additions & 2 deletions truffle/src/main/java/org/jruby/truffle/nodes/core/HashNodes.java
Original file line number Diff line number Diff line change
@@ -18,6 +18,7 @@
import com.oracle.truffle.api.utilities.BranchProfile;
import com.oracle.truffle.api.utilities.ConditionProfile;
import org.jruby.runtime.Visibility;
import org.jruby.truffle.nodes.RubyNode;
import org.jruby.truffle.nodes.dispatch.*;
import org.jruby.truffle.nodes.hash.FindEntryNode;
import org.jruby.truffle.nodes.yield.YieldDispatchHeadNode;
@@ -244,7 +245,7 @@ public RubyHash construct(RubyClass hashClass, Object[] args) {

@CoreMethod(names = "[]", required = 1)
public abstract static class GetIndexNode extends HashCoreMethodNode {

@Child private CallDispatchHeadNode eqlNode;
@Child private CallDispatchHeadNode equalNode;
@Child private YieldDispatchHeadNode yield;
@@ -253,6 +254,8 @@ public abstract static class GetIndexNode extends HashCoreMethodNode {
private final ConditionProfile byIdentityProfile = ConditionProfile.createBinaryProfile();
private final BranchProfile notInHashProfile = BranchProfile.create();
private final BranchProfile useDefaultProfile = BranchProfile.create();

@CompilerDirectives.CompilationFinal private Object undefinedValue;

public GetIndexNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
@@ -268,13 +271,18 @@ public GetIndexNode(GetIndexNode prev) {
equalNode = prev.equalNode;
yield = prev.yield;
findEntryNode = prev.findEntryNode;
undefinedValue = prev.undefinedValue;
}

public abstract Object executeGet(VirtualFrame frame, RubyHash hash, Object key);

@Specialization(guards = "isNull")
public Object getNull(VirtualFrame frame, RubyHash hash, Object key) {
notDesignedForCompilation();

if (hash.getDefaultBlock() != null) {
if (undefinedValue != null) {
return undefinedValue;
} else if (hash.getDefaultBlock() != null) {
return yield.dispatch(frame, hash.getDefaultBlock(), hash, key);
} else if (hash.getDefaultValue() != null) {
return hash.getDefaultValue();
@@ -306,6 +314,10 @@ public Object getPackedArray(VirtualFrame frame, RubyHash hash, Object key) {
}

notInHashProfile.enter();

if (undefinedValue != null) {
return undefinedValue;
}

if (hash.getDefaultBlock() != null) {
useDefaultProfile.enter();
@@ -330,6 +342,10 @@ public Object getBuckets(VirtualFrame frame, RubyHash hash, Object key) {

notInHashProfile.enter();

if (undefinedValue != null) {
return undefinedValue;
}

if (hash.getDefaultBlock() != null) {
useDefaultProfile.enter();
return yield.dispatch(frame, hash.getDefaultBlock(), hash, key);
@@ -341,6 +357,34 @@ public Object getBuckets(VirtualFrame frame, RubyHash hash, Object key) {

return getContext().getCoreLibrary().getNilObject();
}

public void setUndefinedValue(Object undefinedValue) {
this.undefinedValue = undefinedValue;

}

}

@CoreMethod(names = "_get_or_undefined", required = 1)
public abstract static class GetOrUndefinedNode extends HashCoreMethodNode {

@Child private GetIndexNode getIndexNode;

public GetOrUndefinedNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
getIndexNode = HashNodesFactory.GetIndexNodeFactory.create(context, sourceSection, new RubyNode[]{null, null});
getIndexNode.setUndefinedValue(context.getCoreLibrary().getRubiniusUndefined());
}

public GetOrUndefinedNode(GetOrUndefinedNode prev) {
super(prev);
getIndexNode = prev.getIndexNode;
}

@Specialization
public Object getOrUndefined(VirtualFrame frame, RubyHash hash, Object key) {
return getIndexNode.executeGet(frame, hash, key);
}

}

@@ -1137,6 +1181,106 @@ public Object setDefault(VirtualFrame frame, RubyHash hash, Object defaultValue)
}
}

@CoreMethod(names = "shift", raiseIfFrozenSelf = true)
public abstract static class ShiftNode extends HashCoreMethodNode {

public ShiftNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}

public ShiftNode(ShiftNode prev) {
super(prev);
}

@Specialization(guards = {"isEmpty", "!hasDefaultValue", "!hasDefaultBlock"})
public RubyNilClass shiftEmpty(RubyHash hash) {
return getContext().getCoreLibrary().getNilObject();
}

@Specialization(guards = {"isEmpty", "hasDefaultValue", "!hasDefaultBlock"})
public Object shiftEmpyDefaultValue(RubyHash hash) {
return hash.getDefaultValue();
}

@Specialization(guards = {"isEmpty", "!hasDefaultValue", "hasDefaultBlock"})
public Object shiftEmptyDefaultProc(RubyHash hash) {
notDesignedForCompilation();

return hash.getDefaultBlock().rootCall(hash, getContext().getCoreLibrary().getNilObject());
}

@Specialization(guards = {"!isEmpty", "!isNull", "!isBuckets"})
public RubyArray shiftPackedArray(RubyHash hash) {
notDesignedForCompilation();

final Object[] store = (Object[]) hash.getStore();

final Object key = store[0];
final Object value = store[1];

System.arraycopy(store, 2, store, 0, HashOperations.SMALL_HASH_SIZE * 2 - 2);

hash.setSize(hash.getSize() - 1);

return RubyArray.fromObjects(getContext().getCoreLibrary().getArrayClass(), key, value);
}

@Specialization(guards = {"!isEmpty", "isBuckets"})
public RubyArray shiftBuckets(RubyHash hash) {
notDesignedForCompilation();

final Entry first = hash.getFirstInSequence();

final Object key = first.getKey();
final Object value = first.getValue();

hash.setFirstInSequence(first.getNextInSequence());

if (first.getPreviousInSequence() != null) {
first.getNextInSequence().setPreviousInSequence(null);
}

if (hash.getLastInSequence() == first) {
hash.setLastInSequence(null);
}

/*
* TODO CS 7-Mar-15 this isn't great - we need to remove from the
* lookup sequence for which we need the previous entry in the
* bucket. However we normally get that from the search result, and
* we haven't done a search here - we've just taken the first
* result. For the moment we'll just do a manual search.
*/

final Entry[] store = (Entry[]) hash.getStore();

bucketLoop: for (int n = 0; n < store.length; n++) {
Entry previous = null;
Entry entry = store[n];

while (entry != null) {
if (entry == first) {
if (previous == null) {
store[n] = first.getNextInLookup();
} else {
previous.setNextInLookup(first.getNextInLookup());
}

break bucketLoop;
}

previous = entry;
entry = entry.getNextInLookup();
}
}

hash.setSize(hash.getSize() - 1);

return RubyArray.fromObjects(getContext().getCoreLibrary().getArrayClass(), key, value);
}

}

@CoreMethod(names = {"size", "length"})
public abstract static class SizeNode extends HashCoreMethodNode {

4 changes: 2 additions & 2 deletions truffle/src/main/ruby/core/hash.rb
Original file line number Diff line number Diff line change
@@ -39,8 +39,8 @@ def merge_fallback(other, &block)
end

def find_item(key)
value = self[key]
if value.nil?
value = _get_or_undefined(key)
if undefined == value
nil
else
# TODO CS 7-Mar-15 maybe we should return the stored key?
44 changes: 44 additions & 0 deletions truffle/src/main/ruby/core/rubinius/common/hash.rb
Original file line number Diff line number Diff line change
@@ -236,4 +236,48 @@ def self.try_convert(obj)

alias_method :store, :[]=

def select
return to_enum(:select) unless block_given?

selected = Hash.allocate

each_item do |item|
if yield(item.key, item.value)
selected[item.key] = item.value
end
end

selected
end

def select!
return to_enum(:select!) unless block_given?

Rubinius.check_frozen

return nil if empty?

size = @size
each_item { |e| delete e.key unless yield(e.key, e.value) }
return nil if size == @size

self
end

def key?(key)
find_item(key) != nil
end

alias_method :has_key?, :key?
alias_method :include?, :key?
alias_method :member?, :key?

def index(value)
each_item do |item|
return item.key if item.value == value
end
end

alias_method :key, :index

end