@Override public Object execute(VirtualFrame frame) { if (optimizedProfile.profile(RubyArguments.isKwOptimized(frame.getArguments()))) { Object kwarg = argumentValueProfile.profile( RubyArguments.getOptimizedKeywordArgument(frame.getArguments(), kwIndex)); if (defaultProfile.profile( kwarg instanceof OptionalKeywordArgMissingNode.OptionalKeywordArgMissing)) { return defaultValue.execute(frame); } else { return kwarg; } } else { final RubyHash hash = RubyArguments.getUserKeywordsHash(frame.getArguments(), minimum); if (defaultProfile.profile(hash == null)) { return defaultValue.execute(frame); } Object value = lookupKeywordInHash(hash); if (defaultProfile.profile(value == null)) { return defaultValue.execute(frame); } return value; } }
public int doInt(VirtualFrame frame, Object object) { // TODO CS 14-Nov-15 this code is crazy - should have separate nodes for ToRubyInteger and // ToJavaInt final Object integerObject = executeIntOrLong(frame, object); if (wasInteger.profile(integerObject instanceof Integer)) { return (int) integerObject; } if (wasLong.profile(integerObject instanceof Long)) { final long longValue = (long) integerObject; if (wasLongInRange.profile(CoreLibrary.fitsIntoInteger(longValue))) { return (int) longValue; } } CompilerDirectives.transferToInterpreter(); if (RubyGuards.isRubyBignum(object)) { throw new RaiseException( getContext().getCoreLibrary().rangeError("bignum too big to convert into `long'", this)); } else { throw new UnsupportedOperationException(object.getClass().toString()); } }
@Specialization(guards = {"isBucketHash(hash)", "!isRubyString(key)"}) public Object setBuckets( VirtualFrame frame, DynamicObject hash, Object key, Object value, boolean byIdentity) { assert HashNodes.verifyStore(hash); if (lookupEntryNode == null) { CompilerDirectives.transferToInterpreterAndInvalidate(); lookupEntryNode = insert(new LookupEntryNode(getContext(), getEncapsulatingSourceSection())); } final HashLookupResult result = lookupEntryNode.lookup(frame, hash, key); final Entry entry = result.getEntry(); if (foundProfile.profile(entry == null)) { final Entry[] entries = (Entry[]) Layouts.HASH.getStore(hash); final Entry newEntry = new Entry(result.getHashed(), key, value); if (bucketCollisionProfile.profile(result.getPreviousEntry() == null)) { entries[result.getIndex()] = newEntry; } else { result.getPreviousEntry().setNextInLookup(newEntry); } final Entry lastInSequence = Layouts.HASH.getLastInSequence(hash); if (appendingProfile.profile(lastInSequence == null)) { Layouts.HASH.setFirstInSequence(hash, newEntry); } else { lastInSequence.setNextInSequence(newEntry); newEntry.setPreviousInSequence(lastInSequence); } Layouts.HASH.setLastInSequence(hash, newEntry); final int newSize = Layouts.HASH.getSize(hash) + 1; Layouts.HASH.setSize(hash, newSize); // TODO CS 11-May-15 could store the next size for resize instead of doing a float operation // each time if (resizeProfile.profile(newSize / (double) entries.length > BucketsStrategy.LOAD_FACTOR)) { BucketsStrategy.resize(hash); } } else { entry.setKeyValue(result.getHashed(), key, value); } assert HashNodes.verifyStore(hash); return value; }
@ExplodeLoop @Specialization(guards = {"isPackedHash(hash)", "!isRubyString(key)"}) public Object setPackedArray( VirtualFrame frame, DynamicObject hash, Object key, Object value, boolean byIdentity) { assert HashNodes.verifyStore(hash); final int hashed = hashNode.hash(frame, key); final Object[] store = (Object[]) Layouts.HASH.getStore(hash); final int size = Layouts.HASH.getSize(hash); for (int n = 0; n < PackedArrayStrategy.MAX_ENTRIES; n++) { if (n < size) { if (hashed == PackedArrayStrategy.getHashed(store, n)) { final boolean equal; if (byIdentityProfile.profile(byIdentity)) { equal = equalNode.executeReferenceEqual(frame, key, PackedArrayStrategy.getKey(store, n)); } else { equal = eqlNode.callBoolean(frame, key, "eql?", null, PackedArrayStrategy.getKey(store, n)); } if (equal) { PackedArrayStrategy.setValue(store, n, value); assert HashNodes.verifyStore(hash); return value; } } } } extendProfile.enter(); if (strategyProfile.profile(size + 1 <= PackedArrayStrategy.MAX_ENTRIES)) { PackedArrayStrategy.setHashedKeyValue(store, size, hashed, key, value); Layouts.HASH.setSize(hash, size + 1); return value; } else { PackedArrayStrategy.promoteToBuckets(hash, store, size); BucketsStrategy.addNewEntry(hash, hashed, key, value); } assert HashNodes.verifyStore(hash); return value; }
@CoreMethod(names = "end", required = 1, lowerFixnumParameters = 1) public abstract static class EndNode extends CoreMethodArrayArgumentsNode { private final ConditionProfile badIndexProfile = ConditionProfile.createBinaryProfile(); public EndNode(RubyContext context, SourceSection sourceSection) { super(context, sourceSection); } @Specialization public Object end(DynamicObject matchData, int index) { CompilerDirectives.transferToInterpreter(); if (badIndexProfile.profile((index < 0) || (index >= getNumberOfRegions(matchData)))) { CompilerDirectives.transferToInterpreter(); throw new RaiseException( getContext() .getCoreLibrary() .indexError(String.format("index %d out of matches", index), this)); } else { return MatchDataNodes.end(matchData, index); } } }
/** * Catch a {@code break} from a call with a block containing a break or inside a while/until loop. */ public class CatchBreakNode extends RubyNode { @Child private RubyNode body; private final BreakID breakID; private final BranchProfile breakProfile = BranchProfile.create(); private final ConditionProfile matchingBreakProfile = ConditionProfile.createCountingProfile(); public CatchBreakNode( RubyContext context, SourceSection sourceSection, RubyNode body, BreakID breakID) { super(context, sourceSection); this.body = body; this.breakID = breakID; } @Override public Object execute(VirtualFrame frame) { try { return body.execute(frame); } catch (BreakException e) { breakProfile.enter(); if (matchingBreakProfile.profile(e.getBreakID() == breakID)) { return e.getResult(); } else { throw e; } } } }
/** * Node which wraps a {@link RubiniusPrimitiveNode}, providing the implicit control flow that you * get with calls to Rubinius primitives. */ public class CallRubiniusPrimitiveNode extends RubyNode { @Child private RubyNode primitive; private final ReturnID returnID; private final ConditionProfile primitiveSucceededCondition = ConditionProfile.createBinaryProfile(); public CallRubiniusPrimitiveNode( RubyContext context, SourceSection sourceSection, RubyNode primitive, ReturnID returnID) { super(context, sourceSection); this.primitive = primitive; this.returnID = returnID; } @Override public void executeVoid(VirtualFrame frame) { final Object value = primitive.execute(frame); if (primitiveSucceededCondition.profile(value != null)) { // If the primitive didn't fail its value is returned in the calling method throw new ReturnException(returnID, value); } // Primitives may return null to indicate that they have failed, in which case we continue with // the fallback } @Override public Object execute(VirtualFrame frame) { executeVoid(frame); return nil(); } }
public static class SmallHashLiteralNode extends HashLiteralNode { private final ConditionProfile stringKeyProfile = ConditionProfile.createBinaryProfile(); @Child private HashNode hashNode; @Child private CallDispatchHeadNode equalNode; @Child private IsFrozenNode isFrozenNode; public SmallHashLiteralNode( RubyContext context, SourceSection sourceSection, RubyNode[] keyValues) { super(context, sourceSection, keyValues); hashNode = new HashNode(context, sourceSection); equalNode = DispatchHeadNodeFactory.createMethodCall(context); } @ExplodeLoop @Override public Object execute(VirtualFrame frame) { final Object[] store = PackedArrayStrategy.createStore(); int size = 0; initializers: for (int n = 0; n < keyValues.length / 2; n++) { Object key = keyValues[n * 2].execute(frame); if (stringKeyProfile.profile(RubyGuards.isRubyString(key))) { if (isFrozenNode == null) { CompilerDirectives.transferToInterpreter(); isFrozenNode = insert(IsFrozenNodeGen.create(getContext(), getSourceSection(), null)); } if (!isFrozenNode.executeIsFrozen(key)) { key = freezeNode.call(frame, dupNode.call(frame, key, "dup", null), "freeze", null); } } final int hashed = hashNode.hash(frame, key); final Object value = keyValues[n * 2 + 1].execute(frame); for (int i = 0; i < n; i++) { if (i < size && hashed == PackedArrayStrategy.getHashed(store, i) && equalNode.callBoolean( frame, key, "eql?", null, PackedArrayStrategy.getKey(store, i))) { PackedArrayStrategy.setKey(store, i, key); PackedArrayStrategy.setValue(store, i, value); continue initializers; } } PackedArrayStrategy.setHashedKeyValue(store, size, hashed, key, value); size++; } return HashNodes.createHash(getContext().getCoreLibrary().getHashClass(), store, size); } }
public Object maybeTaint(DynamicObject source, DynamicObject result) { if (taintProfile.profile(isTaintedNode.executeIsTainted(source))) { if (taintNode == null) { CompilerDirectives.transferToInterpreter(); taintNode = insert(TaintNodeGen.create(getContext(), getSourceSection(), null)); } taintNode.taint(result); } return result; }
@Override public void executeVoid(VirtualFrame frame) { final Object value = primitive.execute(frame); if (primitiveSucceededCondition.profile(value != null)) { // If the primitive didn't fail its value is returned in the calling method throw new ReturnException(returnID, value); } // Primitives may return null to indicate that they have failed, in which case we continue with // the fallback }
@Override public Object execute(VirtualFrame frame) { try { return body.execute(frame); } catch (BreakException e) { breakProfile.enter(); if (matchingBreakProfile.profile(e.getBreakID() == breakID)) { return e.getResult(); } else { throw e; } } }
@Specialization(guards = {"isRubyArray(array)", "isObjectArray(array)"}) public boolean ensureCapacityObject(RubyBasicObject array, int requiredCapacity) { final Object[] store = (Object[]) ArrayNodes.getStore(array); if (allocateProfile.profile(store.length < requiredCapacity)) { ArrayNodes.setStore( array, Arrays.copyOf(store, ArrayUtils.capacity(store.length, requiredCapacity)), ArrayNodes.getSize(array)); return true; } else { return false; } }
@Specialization public Object end(DynamicObject matchData, int index) { CompilerDirectives.transferToInterpreter(); if (badIndexProfile.profile((index < 0) || (index >= getNumberOfRegions(matchData)))) { CompilerDirectives.transferToInterpreter(); throw new RaiseException( getContext() .getCoreLibrary() .indexError(String.format("index %d out of matches", index), this)); } else { return MatchDataNodes.end(matchData, index); } }
@ExplodeLoop @Override public Object execute(VirtualFrame frame) { final Object[] store = PackedArrayStrategy.createStore(); int size = 0; initializers: for (int n = 0; n < keyValues.length / 2; n++) { Object key = keyValues[n * 2].execute(frame); if (stringKeyProfile.profile(RubyGuards.isRubyString(key))) { if (isFrozenNode == null) { CompilerDirectives.transferToInterpreter(); isFrozenNode = insert(IsFrozenNodeGen.create(getContext(), getSourceSection(), null)); } if (!isFrozenNode.executeIsFrozen(key)) { key = freezeNode.call(frame, dupNode.call(frame, key, "dup", null), "freeze", null); } } final int hashed = hashNode.hash(frame, key); final Object value = keyValues[n * 2 + 1].execute(frame); for (int i = 0; i < n; i++) { if (i < size && hashed == PackedArrayStrategy.getHashed(store, i) && equalNode.callBoolean( frame, key, "eql?", null, PackedArrayStrategy.getKey(store, i))) { PackedArrayStrategy.setKey(store, i, key); PackedArrayStrategy.setValue(store, i, value); continue initializers; } } PackedArrayStrategy.setHashedKeyValue(store, size, hashed, key, value); size++; } return HashNodes.createHash(getContext().getCoreLibrary().getHashClass(), store, size); }
@NodeChild(value = "child", type = RubyNode.class) public abstract class ToIntNode extends RubyNode { @Child private CallDispatchHeadNode toIntNode; @Child private FloatNodes.ToINode floatToIntNode; private final ConditionProfile wasInteger = ConditionProfile.createBinaryProfile(); private final ConditionProfile wasLong = ConditionProfile.createBinaryProfile(); private final ConditionProfile wasLongInRange = ConditionProfile.createBinaryProfile(); public static ToIntNode create(RubyContext context, SourceSection sourceSection) { return ToIntNodeGen.create(context, sourceSection, null); } public ToIntNode(RubyContext context, SourceSection sourceSection) { super(context, sourceSection); } public int doInt(VirtualFrame frame, Object object) { // TODO CS 14-Nov-15 this code is crazy - should have separate nodes for ToRubyInteger and // ToJavaInt final Object integerObject = executeIntOrLong(frame, object); if (wasInteger.profile(integerObject instanceof Integer)) { return (int) integerObject; } if (wasLong.profile(integerObject instanceof Long)) { final long longValue = (long) integerObject; if (wasLongInRange.profile(CoreLibrary.fitsIntoInteger(longValue))) { return (int) longValue; } } CompilerDirectives.transferToInterpreter(); if (RubyGuards.isRubyBignum(object)) { throw new RaiseException( getContext().getCoreLibrary().rangeError("bignum too big to convert into `long'", this)); } else { throw new UnsupportedOperationException(object.getClass().toString()); } } public abstract Object executeIntOrLong(VirtualFrame frame, Object object); @Specialization public int coerceInt(int value) { return value; } @Specialization public long coerceLong(long value) { return value; } @Specialization(guards = "isRubyBignum(value)") public DynamicObject coerceRubyBignum(DynamicObject value) { CompilerDirectives.transferToInterpreter(); throw new RaiseException( getContext().getCoreLibrary().rangeError("bignum too big to convert into `long'", this)); } @Specialization public Object coerceDouble(VirtualFrame frame, double value) { if (floatToIntNode == null) { CompilerDirectives.transferToInterpreter(); floatToIntNode = insert( FloatNodesFactory.ToINodeFactory.create( getContext(), getSourceSection(), new RubyNode[] {null})); } return floatToIntNode.executeToI(frame, value); } @Specialization public Object coerceBoolean(VirtualFrame frame, boolean value) { return coerceObject(frame, value); } @Specialization(guards = "!isRubyBignum(object)") public Object coerceBasicObject(VirtualFrame frame, DynamicObject object) { return coerceObject(frame, object); } private Object coerceObject(VirtualFrame frame, Object object) { if (toIntNode == null) { CompilerDirectives.transferToInterpreter(); toIntNode = insert(DispatchHeadNodeFactory.createMethodCall(getContext())); } final Object coerced; try { coerced = toIntNode.call(frame, object, "to_int", null); } catch (RaiseException e) { if (Layouts.BASIC_OBJECT.getLogicalClass(e.getRubyException()) == getContext().getCoreLibrary().getNoMethodErrorClass()) { CompilerDirectives.transferToInterpreter(); throw new RaiseException( getContext().getCoreLibrary().typeErrorNoImplicitConversion(object, "Integer", this)); } else { throw e; } } if (getContext().getCoreLibrary().getLogicalClass(coerced) == getContext().getCoreLibrary().getFixnumClass()) { return coerced; } else { CompilerDirectives.transferToInterpreter(); throw new RaiseException( getContext() .getCoreLibrary() .typeErrorBadCoercion(object, "Integer", "to_int", coerced, this)); } } }
public class TaintResultNode extends RubyNode { private final boolean taintFromSelf; private final int taintFromParameter; private final ConditionProfile taintProfile = ConditionProfile.createBinaryProfile(); @Child private RubyNode method; @Child private IsTaintedNode isTaintedNode; @Child private TaintNode taintNode; public TaintResultNode(boolean taintFromSelf, int taintFromParameter, RubyNode method) { super(method.getContext(), method.getEncapsulatingSourceSection()); this.taintFromSelf = taintFromSelf; this.taintFromParameter = taintFromParameter; this.method = method; this.isTaintedNode = IsTaintedNodeGen.create(getContext(), getSourceSection(), null); } public TaintResultNode(RubyContext context, SourceSection sourceSection) { super(context, sourceSection); this.taintFromSelf = false; this.taintFromParameter = -1; this.isTaintedNode = IsTaintedNodeGen.create(getContext(), getSourceSection(), null); } public Object maybeTaint(DynamicObject source, DynamicObject result) { if (taintProfile.profile(isTaintedNode.executeIsTainted(source))) { if (taintNode == null) { CompilerDirectives.transferToInterpreter(); taintNode = insert(TaintNodeGen.create(getContext(), getSourceSection(), null)); } taintNode.taint(result); } return result; } @Override public Object execute(VirtualFrame frame) { final DynamicObject result; try { result = method.executeDynamicObject(frame); } catch (DoNotTaint e) { return e.getResult(); } catch (UnexpectedResultException e) { throw new UnsupportedOperationException(e); } if (result != nil()) { if (taintFromSelf) { maybeTaint((DynamicObject) RubyArguments.getSelf(frame.getArguments()), result); } // It's possible the taintFromParameter value was misconfigured by the user, but the far more // likely // scenario is that the argument at that position is a NotProvided argument, which doesn't // take up // a space in the frame. if (taintFromParameter < RubyArguments.getArgumentsCount(frame.getArguments())) { final Object argument = RubyArguments.getArgument(frame.getArguments(), taintFromParameter); if (argument instanceof DynamicObject) { final DynamicObject taintSource = (DynamicObject) argument; maybeTaint(taintSource, result); } } } return result; } public static class DoNotTaint extends ControlFlowException { private static final long serialVersionUID = 5321304910918469059L; private final Object result; public DoNotTaint(Object result) { this.result = result; } public Object getResult() { return result; } } }
public class ReadKeywordArgumentNode extends RubyNode { private final int minimum; private final String name; private final int kwIndex; private final ValueProfile argumentValueProfile = ValueProfile.createPrimitiveProfile(); private ConditionProfile optimizedProfile = ConditionProfile.createBinaryProfile(); private ConditionProfile defaultProfile = ConditionProfile.createBinaryProfile(); @Child private RubyNode defaultValue; public ReadKeywordArgumentNode( RubyContext context, SourceSection sourceSection, int minimum, String name, RubyNode defaultValue, int kwIndex) { super(context, sourceSection); this.minimum = minimum; this.name = name; this.defaultValue = defaultValue; this.kwIndex = kwIndex; } @Override public Object execute(VirtualFrame frame) { if (optimizedProfile.profile(RubyArguments.isKwOptimized(frame.getArguments()))) { Object kwarg = argumentValueProfile.profile( RubyArguments.getOptimizedKeywordArgument(frame.getArguments(), kwIndex)); if (defaultProfile.profile( kwarg instanceof OptionalKeywordArgMissingNode.OptionalKeywordArgMissing)) { return defaultValue.execute(frame); } else { return kwarg; } } else { final RubyHash hash = RubyArguments.getUserKeywordsHash(frame.getArguments(), minimum); if (defaultProfile.profile(hash == null)) { return defaultValue.execute(frame); } Object value = lookupKeywordInHash(hash); if (defaultProfile.profile(value == null)) { return defaultValue.execute(frame); } return value; } } @CompilerDirectives.TruffleBoundary private Object lookupKeywordInHash(RubyHash hash) { for (KeyValue keyValue : HashOperations.verySlowToKeyValues(hash)) { if (keyValue.getKey().toString().equals(name)) { return keyValue.getValue(); } } return null; } }
@ImportStatic(HashGuards.class) @NodeChildren({ @NodeChild(value = "hash", type = RubyNode.class), @NodeChild(value = "key", type = RubyNode.class), @NodeChild(value = "value", type = RubyNode.class), @NodeChild(value = "byIdentity", type = RubyNode.class) }) public abstract class SetNode extends RubyNode { @Child private HashNode hashNode; @Child private CallDispatchHeadNode eqlNode; @Child private BasicObjectNodes.ReferenceEqualNode equalNode; @Child private LookupEntryNode lookupEntryNode; private final ConditionProfile byIdentityProfile = ConditionProfile.createBinaryProfile(); private final BranchProfile extendProfile = BranchProfile.create(); private final ConditionProfile strategyProfile = ConditionProfile.createBinaryProfile(); public SetNode(RubyContext context, SourceSection sourceSection) { super(context, sourceSection); hashNode = new HashNode(context, sourceSection); eqlNode = DispatchHeadNodeFactory.createMethodCall(context); equalNode = BasicObjectNodesFactory.ReferenceEqualNodeFactory.create( context, sourceSection, null, null); } public abstract Object executeSet( VirtualFrame frame, DynamicObject hash, Object key, Object value, boolean byIdentity); @Specialization(guards = {"isNullHash(hash)", "!isRubyString(key)"}) public Object setNull( VirtualFrame frame, DynamicObject hash, Object key, Object value, boolean byIdentity) { HashNodes.setStore( hash, PackedArrayStrategy.createStore(hashNode.hash(frame, key), key, value), 1, null, null); assert HashNodes.verifyStore(hash); return value; } @Specialization(guards = {"isNullHash(hash)", "byIdentity", "isRubyString(key)"}) public Object setNullByIdentity( VirtualFrame frame, DynamicObject hash, DynamicObject key, Object value, boolean byIdentity) { return setNull(frame, hash, (Object) key, value, byIdentity); } @Specialization(guards = {"isNullHash(hash)", "!byIdentity", "isRubyString(key)"}) public Object setNullNotByIdentity( VirtualFrame frame, DynamicObject hash, DynamicObject key, Object value, boolean byIdentity) { return setNull( frame, hash, ruby(frame, "key.frozen? ? key : key.dup.freeze", "key", key), value, byIdentity); } @ExplodeLoop @Specialization(guards = {"isPackedHash(hash)", "!isRubyString(key)"}) public Object setPackedArray( VirtualFrame frame, DynamicObject hash, Object key, Object value, boolean byIdentity) { assert HashNodes.verifyStore(hash); final int hashed = hashNode.hash(frame, key); final Object[] store = (Object[]) Layouts.HASH.getStore(hash); final int size = Layouts.HASH.getSize(hash); for (int n = 0; n < PackedArrayStrategy.MAX_ENTRIES; n++) { if (n < size) { if (hashed == PackedArrayStrategy.getHashed(store, n)) { final boolean equal; if (byIdentityProfile.profile(byIdentity)) { equal = equalNode.executeReferenceEqual(frame, key, PackedArrayStrategy.getKey(store, n)); } else { equal = eqlNode.callBoolean(frame, key, "eql?", null, PackedArrayStrategy.getKey(store, n)); } if (equal) { PackedArrayStrategy.setValue(store, n, value); assert HashNodes.verifyStore(hash); return value; } } } } extendProfile.enter(); if (strategyProfile.profile(size + 1 <= PackedArrayStrategy.MAX_ENTRIES)) { PackedArrayStrategy.setHashedKeyValue(store, size, hashed, key, value); Layouts.HASH.setSize(hash, size + 1); return value; } else { PackedArrayStrategy.promoteToBuckets(hash, store, size); BucketsStrategy.addNewEntry(hash, hashed, key, value); } assert HashNodes.verifyStore(hash); return value; } @Specialization(guards = {"isPackedHash(hash)", "byIdentity", "isRubyString(key)"}) public Object setPackedArrayByIdentity( VirtualFrame frame, DynamicObject hash, DynamicObject key, Object value, boolean byIdentity) { return setPackedArray(frame, hash, key, value, byIdentity); } @Specialization(guards = {"isPackedHash(hash)", "!byIdentity", "isRubyString(key)"}) public Object setPackedArrayNotByIdentity( VirtualFrame frame, DynamicObject hash, DynamicObject key, Object value, boolean byIdentity) { return setPackedArray( frame, hash, ruby(frame, "key.frozen? ? key : key.dup.freeze", "key", key), value, byIdentity); } // Can't be @Cached yet as we call from the RubyString specialisation private final ConditionProfile foundProfile = ConditionProfile.createBinaryProfile(); private final ConditionProfile bucketCollisionProfile = ConditionProfile.createBinaryProfile(); private final ConditionProfile appendingProfile = ConditionProfile.createBinaryProfile(); private final ConditionProfile resizeProfile = ConditionProfile.createBinaryProfile(); @Specialization(guards = {"isBucketHash(hash)", "!isRubyString(key)"}) public Object setBuckets( VirtualFrame frame, DynamicObject hash, Object key, Object value, boolean byIdentity) { assert HashNodes.verifyStore(hash); if (lookupEntryNode == null) { CompilerDirectives.transferToInterpreterAndInvalidate(); lookupEntryNode = insert(new LookupEntryNode(getContext(), getEncapsulatingSourceSection())); } final HashLookupResult result = lookupEntryNode.lookup(frame, hash, key); final Entry entry = result.getEntry(); if (foundProfile.profile(entry == null)) { final Entry[] entries = (Entry[]) Layouts.HASH.getStore(hash); final Entry newEntry = new Entry(result.getHashed(), key, value); if (bucketCollisionProfile.profile(result.getPreviousEntry() == null)) { entries[result.getIndex()] = newEntry; } else { result.getPreviousEntry().setNextInLookup(newEntry); } final Entry lastInSequence = Layouts.HASH.getLastInSequence(hash); if (appendingProfile.profile(lastInSequence == null)) { Layouts.HASH.setFirstInSequence(hash, newEntry); } else { lastInSequence.setNextInSequence(newEntry); newEntry.setPreviousInSequence(lastInSequence); } Layouts.HASH.setLastInSequence(hash, newEntry); final int newSize = Layouts.HASH.getSize(hash) + 1; Layouts.HASH.setSize(hash, newSize); // TODO CS 11-May-15 could store the next size for resize instead of doing a float operation // each time if (resizeProfile.profile(newSize / (double) entries.length > BucketsStrategy.LOAD_FACTOR)) { BucketsStrategy.resize(hash); } } else { entry.setKeyValue(result.getHashed(), key, value); } assert HashNodes.verifyStore(hash); return value; } @Specialization(guards = {"isBucketHash(hash)", "byIdentity", "isRubyString(key)"}) public Object setBucketsByIdentity( VirtualFrame frame, DynamicObject hash, DynamicObject key, Object value, boolean byIdentity) { return setBuckets(frame, hash, (Object) key, value, byIdentity); } @Specialization(guards = {"isBucketHash(hash)", "!byIdentity", "isRubyString(key)"}) public Object setBucketsNotByIdentity( VirtualFrame frame, DynamicObject hash, DynamicObject key, Object value, boolean byIdentity) { return setBuckets( frame, hash, ruby(frame, "key.frozen? ? key : key.dup.freeze", "key", key), value, byIdentity); } }
@NodeChildren({ @NodeChild(value = "array", type = RubyNode.class), @NodeChild(value = "requiredCapacity", type = RubyNode.class) }) @ImportStatic(ArrayGuards.class) public abstract class EnsureCapacityArrayNode extends RubyNode { private final ConditionProfile allocateProfile = ConditionProfile.createCountingProfile(); public EnsureCapacityArrayNode(RubyContext context, SourceSection sourceSection) { super(context, sourceSection); } public abstract Object executeEnsureCapacity( VirtualFrame frame, RubyBasicObject array, int requiredCapacity); @Specialization(guards = {"isRubyArray(array)", "isIntArray(array)"}) public boolean ensureCapacityInt(RubyBasicObject array, int requiredCapacity) { final int[] store = (int[]) ArrayNodes.getStore(array); if (allocateProfile.profile(store.length < requiredCapacity)) { ArrayNodes.setStore( array, Arrays.copyOf(store, ArrayUtils.capacity(store.length, requiredCapacity)), ArrayNodes.getSize(array)); return true; } else { return false; } } @Specialization(guards = {"isRubyArray(array)", "isLongArray(array)"}) public boolean ensureCapacityLong(RubyBasicObject array, int requiredCapacity) { final long[] store = (long[]) ArrayNodes.getStore(array); if (allocateProfile.profile(store.length < requiredCapacity)) { ArrayNodes.setStore( array, Arrays.copyOf(store, ArrayUtils.capacity(store.length, requiredCapacity)), ArrayNodes.getSize(array)); return true; } else { return false; } } @Specialization(guards = {"isRubyArray(array)", "isDoubleArray(array)"}) public boolean ensureCapacityDouble(RubyBasicObject array, int requiredCapacity) { final double[] store = (double[]) ArrayNodes.getStore(array); if (allocateProfile.profile(store.length < requiredCapacity)) { ArrayNodes.setStore( array, Arrays.copyOf(store, ArrayUtils.capacity(store.length, requiredCapacity)), ArrayNodes.getSize(array)); return true; } else { return false; } } @Specialization(guards = {"isRubyArray(array)", "isObjectArray(array)"}) public boolean ensureCapacityObject(RubyBasicObject array, int requiredCapacity) { final Object[] store = (Object[]) ArrayNodes.getStore(array); if (allocateProfile.profile(store.length < requiredCapacity)) { ArrayNodes.setStore( array, Arrays.copyOf(store, ArrayUtils.capacity(store.length, requiredCapacity)), ArrayNodes.getSize(array)); return true; } else { return false; } } }