Skip to content

Commit

Permalink
[Truffle] Dup and freeze string keys in hash literals.
Browse files Browse the repository at this point in the history
  • Loading branch information
chrisseaton committed Oct 4, 2014
1 parent 6d43d9f commit fb49596
Show file tree
Hide file tree
Showing 3 changed files with 28 additions and 4 deletions.
Expand Up @@ -14,19 +14,25 @@
import com.oracle.truffle.api.frame.*;
import com.oracle.truffle.api.nodes.*;
import org.jruby.truffle.nodes.*;
import org.jruby.truffle.nodes.dispatch.DispatchHeadNode;
import org.jruby.truffle.runtime.*;
import org.jruby.truffle.runtime.core.RubyHash;
import org.jruby.truffle.runtime.core.RubyString;

import java.util.LinkedHashMap;

public abstract class HashLiteralNode extends RubyNode {

@Children protected final RubyNode[] keyValues;
@Child protected DispatchHeadNode dupNode;
@Child protected DispatchHeadNode freezeNode;

protected HashLiteralNode(RubyContext context, SourceSection sourceSection, RubyNode[] keyValues) {
super(context, sourceSection);
assert keyValues.length % 2 == 0;
this.keyValues = keyValues;
dupNode = new DispatchHeadNode(context);
freezeNode = new DispatchHeadNode(context);
}

public static HashLiteralNode create(RubyContext context, SourceSection sourceSection, RubyNode[] keyValues) {
Expand Down Expand Up @@ -84,8 +90,16 @@ public SmallHashLiteralNode(RubyContext context, SourceSection sourceSection, Ru
public RubyHash executeRubyHash(VirtualFrame frame) {
final Object[] storage = new Object[RubyContext.HASHES_SMALL * 2];

for (int n = 0; n < keyValues.length; n++) {
storage[n] = keyValues[n].execute(frame);
for (int n = 0; n < keyValues.length; n += 2) {
Object key = keyValues[n].execute(frame);
final Object value = keyValues[n + 1].execute(frame);

if (key instanceof RubyString) {
key = freezeNode.call(frame, dupNode.call(frame, key, "dup", null), "freeze", null);
}

storage[n] = key;
storage[n + 1] = value;
}

return new RubyHash(getContext().getCoreLibrary().getHashClass(), null, storage, keyValues.length / 2);
Expand All @@ -107,7 +121,14 @@ public RubyHash executeRubyHash(VirtualFrame frame) {
final LinkedHashMap<Object, Object> storage = new LinkedHashMap<>();

for (int n = 0; n < keyValues.length; n += 2) {
storage.put(keyValues[n].execute(frame), keyValues[n + 1].execute(frame));
Object key = keyValues[n].execute(frame);
final Object value = keyValues[n + 1].execute(frame);

if (key instanceof RubyString) {
key = freezeNode.call(frame, dupNode.call(frame, key, "dup", null), "freeze", null);
}

storage.put(key, value);
}

return new RubyHash(getContext().getCoreLibrary().getHashClass(), null, storage, 0);
Expand Down
Expand Up @@ -27,6 +27,10 @@ public RubyObject(RubyClass rubyClass) {
super(rubyClass);
}

public boolean isFrozen() {
return frozen;
}

public void checkFrozen(Node currentNode) {
if (frozen) {
CompilerDirectives.transferToInterpreter();
Expand Down
1 change: 0 additions & 1 deletion spec/truffle/tags/language/hash_tags.txt
@@ -1,2 +1 @@
fails:Hash literal freezes string keys on initialization
fails:Hash literal checks duplicated keys on initialization

3 comments on commit fb49596

@eregon
Copy link
Member

@eregon eregon commented on fb49596 Oct 5, 2014

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if the key is already frozen?
MRI does not duplicate it in that case.

@chrisseaton
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh yeah, should be key instanceof RubyString && !((RubyString) key).isFrozen() - thanks.

@eregon
Copy link
Member

@eregon eregon commented on fb49596 Oct 5, 2014

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 8d1f6d2.

Please sign in to comment.