示例#1
0
  @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;
    }
  }
示例#2
0
  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());
    }
  }
示例#3
0
  @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;
  }
示例#4
0
  @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;
  }
示例#5
0
  @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);
      }
    }
  }
示例#6
0
/**
 * 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();
  }
}
示例#8
0
  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);
    }
  }
示例#9
0
  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
  }
示例#11
0
  @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;
    }
  }
示例#13
0
    @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);
      }
    }
示例#14
0
    @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);
    }
示例#15
0
@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));
    }
  }
}
示例#16
0
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;
    }
  }
}
示例#17
0
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;
  }
}
示例#18
0
@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;
    }
  }
}