Esempio n. 1
0
    @Specialization
    public Object step(
        VirtualFrame frame, RubyRange.IntegerFixnumRange range, int step, RubyProc block) {
      int count = 0;

      try {
        outer:
        for (int n = range.getBegin(); n < range.getExclusiveEnd(); n += step) {
          while (true) {
            if (CompilerDirectives.inInterpreter()) {
              count++;
            }

            try {
              yield(frame, block, n);
              continue outer;
            } catch (BreakException e) {
              breakProfile.enter();
              return e.getResult();
            } catch (NextException e) {
              nextProfile.enter();
              continue outer;
            } catch (RedoException e) {
              redoProfile.enter();
            }
          }
        }
      } finally {
        if (CompilerDirectives.inInterpreter()) {
          getRootNode().reportLoopCount(count);
        }
      }

      return range;
    }
Esempio n. 2
0
  @Override
  public Object execute(VirtualFrame frame) {
    while (true) {
      getContext().getSafepointManager().poll();

      Object result;

      try {
        result = tryPart.execute(frame);
      } catch (ControlFlowException exception) {
        controlFlowProfile.enter();
        throw exception;
      } catch (RaiseException exception) {
        raiseExceptionProfile.enter();

        try {
          return handleException(frame, exception);
        } catch (RetryException e) {
          continue;
        }
      } finally {
        clearExceptionVariableNode.execute(frame);
      }

      elseProfile.enter();
      elsePart.executeVoid(frame);
      return result;
    }
  }
Esempio n. 3
0
/** Read the rest of arguments after a certain point into an array. */
public class ReadRestArgumentNode extends RubyNode {

  private final int startIndex;
  private final int negativeEndIndex;
  private final boolean keywordArguments;

  private final BranchProfile noArgumentsLeftProfile = BranchProfile.create();
  private final BranchProfile subsetOfArgumentsProfile = BranchProfile.create();

  public ReadRestArgumentNode(
      RubyContext context,
      SourceSection sourceSection,
      int startIndex,
      int negativeEndIndex,
      boolean keywordArguments) {
    super(context, sourceSection);
    this.startIndex = startIndex;
    this.negativeEndIndex = negativeEndIndex;
    this.keywordArguments = keywordArguments;
  }

  @Override
  public Object execute(VirtualFrame frame) {
    final RubyClass arrayClass = getContext().getCoreLibrary().getArrayClass();

    int count = RubyArguments.getUserArgumentsCount(frame.getArguments());

    int endIndex = count + negativeEndIndex;

    if (keywordArguments) {
      final Object lastArgument =
          RubyArguments.getUserArgument(
              frame.getArguments(), RubyArguments.getUserArgumentsCount(frame.getArguments()) - 1);

      if (lastArgument instanceof RubyHash) {
        endIndex -= 1;
      }
    }

    final int length = endIndex - startIndex;

    if (startIndex == 0) {
      final Object[] arguments = RubyArguments.extractUserArguments(frame.getArguments());
      return new RubyArray(arrayClass, arguments, length);
    } else {
      if (startIndex >= endIndex) {
        noArgumentsLeftProfile.enter();
        return new RubyArray(arrayClass);
      } else {
        subsetOfArgumentsProfile.enter();
        final Object[] arguments = RubyArguments.extractUserArguments(frame.getArguments());
        // TODO(CS): risk here of widening types too much - always going to be Object[] - does seem
        // to be something that does happen
        return new RubyArray(
            arrayClass, ArrayUtils.extractRange(arguments, startIndex, endIndex), length);
      }
    }
  }
}
Esempio n. 4
0
  @CoreMethod(
      names = "each",
      needsBlock = true,
      lowerFixnumSelf = true,
      returnsEnumeratorIfNoBlock = true)
  public abstract static class EachNode extends YieldingCoreMethodNode {

    private final BranchProfile breakProfile = BranchProfile.create();
    private final BranchProfile nextProfile = BranchProfile.create();
    private final BranchProfile redoProfile = BranchProfile.create();

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

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

    @Specialization
    public Object each(VirtualFrame frame, RubyRange.IntegerFixnumRange range, RubyProc block) {
      final int exclusiveEnd = range.getExclusiveEnd();

      int count = 0;

      try {
        outer:
        for (int n = range.getBegin(); n < exclusiveEnd; n++) {
          while (true) {
            if (CompilerDirectives.inInterpreter()) {
              count++;
            }

            try {
              yield(frame, block, n);
              continue outer;
            } catch (BreakException e) {
              breakProfile.enter();
              return e.getResult();
            } catch (NextException e) {
              nextProfile.enter();
              continue outer;
            } catch (RedoException e) {
              redoProfile.enter();
            }
          }
        }
      } finally {
        if (CompilerDirectives.inInterpreter()) {
          getRootNode().reportLoopCount(count);
        }
      }

      return range;
    }
  }
Esempio n. 5
0
    @CompilerDirectives.SlowPath
    private RubyString formatSlow(RubyString format, Object[] args) {
      final RubyContext context = getContext();

      if (args.length == 1 && args[0] instanceof RubyArray) {
        singleArrayProfile.enter();
        return context.makeString(
            StringFormatter.format(format.toString(), ((RubyArray) args[0]).asList()));
      } else {
        multipleArgumentsProfile.enter();
        return context.makeString(StringFormatter.format(format.toString(), Arrays.asList(args)));
      }
    }
Esempio n. 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;
      }
    }
  }
}
Esempio n. 7
0
 @Override
 public Object execute(VirtualFrame frame) {
   if (CompilerDirectives.injectBranchProbability(
       getBranchProbability(), condition.executeBoolean(frame))) {
     if (CompilerDirectives.inInterpreter()) {
       thenCount++;
     }
     thenProfile.enter();
     return thenBody.execute(frame);
   } else {
     if (CompilerDirectives.inInterpreter()) {
       elseCount++;
     }
     elseProfile.enter();
     return elseBody.execute(frame);
   }
 }
Esempio n. 8
0
/** Switches between loading arguments as normal and doing a destructure. */
public class ShouldDestructureNode extends RubyNode {

  private final Arity arity;
  @Child private RespondToNode respondToCheck;

  private final BranchProfile checkRespondProfile = BranchProfile.create();

  public ShouldDestructureNode(
      RubyContext context, SourceSection sourceSection, Arity arity, RespondToNode respondToCheck) {
    super(context, sourceSection);
    this.arity = arity;
    this.respondToCheck = respondToCheck;
  }

  @Override
  public boolean executeBoolean(VirtualFrame frame) {
    // TODO(CS): express this using normal nodes?

    // If we don't accept any arguments, there's never any need to destructure
    // TODO(CS): is this guaranteed by the translator anyway?

    if (!arity.allowsMore() && arity.getRequired() == 0 && arity.getOptional() == 0) {
      return false;
    }

    // If we only accept one argument, there's never any need to destructure

    if (!arity.allowsMore() && arity.getRequired() == 1 && arity.getOptional() == 0) {
      return false;
    }

    // If the caller supplied no arguments, or more than one argument, there's no need to
    // destructure this time

    if (RubyArguments.getUserArgumentsCount(frame.getArguments()) != 1) {
      return false;
    }

    // If the single argument is a RubyArray, destructure
    // TODO(CS): can we not just reply on the respondToCheck? Should experiment.

    if (RubyGuards.isRubyArray(RubyArguments.getUserArgument(frame.getArguments(), 0))) {
      return true;
    }

    // If the single argument responds to #to_ary, then destructure

    checkRespondProfile.enter();

    return respondToCheck.executeBoolean(frame);
  }

  @Override
  public Object execute(VirtualFrame frame) {
    return executeBoolean(frame);
  }
}
 @Override
 public Object execute(VirtualFrame frame) {
   if (RubyArguments.getUserArgumentsCount(frame.getArguments()) < minimum) {
     defaultValueProfile.enter();
     return defaultValue.execute(frame);
   } else {
     return RubyArguments.getUserArgument(frame.getArguments(), index);
   }
 }
Esempio n. 10
0
 @Specialization
 public Object leftShift(DynamicObject a, int b) {
   if (b >= 0) {
     return fixnumOrBignum(Layouts.BIGNUM.getValue(a).shiftRight(b));
   } else {
     bLessThanZero.enter();
     return fixnumOrBignum(Layouts.BIGNUM.getValue(a).shiftLeft(-b));
   }
 }
Esempio n. 11
0
  @Override
  public Object execute(VirtualFrame frame) {
    try {
      assert assertArgumentsShouldBeVisible(frame);

      final Object result = child.execute(frame);

      assert shouldObjectBeVisible(result)
          : "result@" + getEncapsulatingSourceSection().getShortDescription();

      return result;
    } catch (StackOverflowError error) {
      // TODO: we might want to do sth smarter here to avoid consuming frames when we are almost out
      // of it.
      CompilerDirectives.transferToInterpreter();
      throw new RaiseException(translate(error));
    } catch (TruffleFatalException | ThreadExitException exception) {
      throw exception;
    } catch (ControlFlowException exception) {
      controlProfile.enter();
      throw exception;
    } catch (RaiseException exception) {
      rethrowProfile.enter();
      throw exception;
    } catch (MainExitException exception) {
      CompilerDirectives.transferToInterpreter();
      throw exception;
    } catch (ArithmeticException exception) {
      CompilerDirectives.transferToInterpreter();
      throw new RaiseException(translate(exception));
    } catch (UnsupportedSpecializationException exception) {
      CompilerDirectives.transferToInterpreter();
      throw new RaiseException(translate(exception));
    } catch (org.jruby.exceptions.RaiseException e) {
      CompilerDirectives.transferToInterpreter();
      throw new RaiseException(getContext().toTruffle(e.getException(), this));
    } catch (Throwable exception) {
      CompilerDirectives.transferToInterpreter();
      throw new RaiseException(translate(exception));
    }
  }
Esempio n. 12
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;
      }
    }
  }
Esempio n. 13
0
  @Override
  public Object execute(VirtualFrame frame) {
    final RubyClass arrayClass = getContext().getCoreLibrary().getArrayClass();

    int count = RubyArguments.getUserArgumentsCount(frame.getArguments());

    int endIndex = count + negativeEndIndex;

    if (keywordArguments) {
      final Object lastArgument =
          RubyArguments.getUserArgument(
              frame.getArguments(), RubyArguments.getUserArgumentsCount(frame.getArguments()) - 1);

      if (lastArgument instanceof RubyHash) {
        endIndex -= 1;
      }
    }

    final int length = endIndex - startIndex;

    if (startIndex == 0) {
      final Object[] arguments = RubyArguments.extractUserArguments(frame.getArguments());
      return new RubyArray(arrayClass, arguments, length);
    } else {
      if (startIndex >= endIndex) {
        noArgumentsLeftProfile.enter();
        return new RubyArray(arrayClass);
      } else {
        subsetOfArgumentsProfile.enter();
        final Object[] arguments = RubyArguments.extractUserArguments(frame.getArguments());
        // TODO(CS): risk here of widening types too much - always going to be Object[] - does seem
        // to be something that does happen
        return new RubyArray(
            arrayClass, ArrayUtils.extractRange(arguments, startIndex, endIndex), length);
      }
    }
  }
Esempio n. 14
0
  @Override
  public RubyBasicObject executeRubyBasicObject(VirtualFrame frame) {
    final Object object = child.execute(frame);

    RubyBasicObject boxedObject;

    if (object instanceof RubyBasicObject) {
      boxedObject = (RubyBasicObject) object;
    } else {
      boxBranch.enter();
      boxedObject = getContext().getCoreLibrary().box(object);
    }

    return boxedObject;
  }
Esempio n. 15
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;
  }
Esempio n. 16
0
  @Override
  public Object execute(VirtualFrame frame) {
    if (index >= RubyArguments.getUserArgumentsCount(frame.getArguments())) {
      outOfRangeProfile.enter();

      switch (missingArgumentBehaviour) {
        case RUNTIME_ERROR:
          break;

        case UNDEFINED:
          return UndefinedPlaceholder.INSTANCE;

        case NIL:
          return nil();
      }
    }

    return argumentValueProfile.profile(RubyArguments.getUserArgument(frame.getArguments(), index));
  }
Esempio n. 17
0
  @CoreMethod(names = ">>", required = 1, lowerFixnumParameters = 0)
  public abstract static class RightShiftNode extends BignumCoreMethodNode {

    private final BranchProfile bLessThanZero = BranchProfile.create();

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

    @Specialization
    public Object leftShift(DynamicObject a, int b) {
      if (b >= 0) {
        return fixnumOrBignum(Layouts.BIGNUM.getValue(a).shiftRight(b));
      } else {
        bLessThanZero.enter();
        return fixnumOrBignum(Layouts.BIGNUM.getValue(a).shiftLeft(-b));
      }
    }
  }
Esempio n. 18
0
/** Read pre-optional argument. */
public class ReadPreArgumentNode extends RubyNode {

  private final int index;

  private final BranchProfile outOfRangeProfile = BranchProfile.create();
  private final MissingArgumentBehaviour missingArgumentBehaviour;

  private final ValueProfile argumentValueProfile = ValueProfile.createPrimitiveProfile();

  public ReadPreArgumentNode(
      RubyContext context,
      SourceSection sourceSection,
      int index,
      MissingArgumentBehaviour missingArgumentBehaviour) {
    super(context, sourceSection);
    this.index = index;
    this.missingArgumentBehaviour = missingArgumentBehaviour;
  }

  @Override
  public Object execute(VirtualFrame frame) {
    if (index >= RubyArguments.getUserArgumentsCount(frame.getArguments())) {
      outOfRangeProfile.enter();

      switch (missingArgumentBehaviour) {
        case RUNTIME_ERROR:
          break;

        case UNDEFINED:
          return UndefinedPlaceholder.INSTANCE;

        case NIL:
          return nil();
      }
    }

    return argumentValueProfile.profile(RubyArguments.getUserArgument(frame.getArguments(), index));
  }
}
Esempio n. 19
0
  @Override
  public Object execute(VirtualFrame frame) {
    while (true) {
      try {
        final Object result = tryPart.execute(frame);
        elsePart.executeVoid(frame);
        return result;
      } catch (ControlFlowException exception) {
        controlFlowProfile.enter();

        throw exception;
      } catch (RuntimeException exception) {
        CompilerDirectives.transferToInterpreter();

        try {
          return handleException(frame, exception);
        } catch (RetryException e) {
          continue;
        }
      }
    }
  }
Esempio n. 20
0
  @Override
  public boolean executeBoolean(VirtualFrame frame) {
    // TODO(CS): express this using normal nodes?

    // If we don't accept any arguments, there's never any need to destructure
    // TODO(CS): is this guaranteed by the translator anyway?

    if (!arity.allowsMore() && arity.getRequired() == 0 && arity.getOptional() == 0) {
      return false;
    }

    // If we only accept one argument, there's never any need to destructure

    if (!arity.allowsMore() && arity.getRequired() == 1 && arity.getOptional() == 0) {
      return false;
    }

    // If the caller supplied no arguments, or more than one argument, there's no need to
    // destructure this time

    if (RubyArguments.getUserArgumentsCount(frame.getArguments()) != 1) {
      return false;
    }

    // If the single argument is a RubyArray, destructure
    // TODO(CS): can we not just reply on the respondToCheck? Should experiment.

    if (RubyGuards.isRubyArray(RubyArguments.getUserArgument(frame.getArguments(), 0))) {
      return true;
    }

    // If the single argument responds to #to_ary, then destructure

    checkRespondProfile.enter();

    return respondToCheck.executeBoolean(frame);
  }
Esempio n. 21
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);
  }
}
Esempio n. 22
0
public class ExceptionTranslatingNode extends RubyNode {

  private final UnsupportedOperationBehavior unsupportedOperationBehavior;

  @Child private RubyNode child;

  private final BranchProfile controlProfile = BranchProfile.create();
  private final BranchProfile rethrowProfile = BranchProfile.create();

  public ExceptionTranslatingNode(
      RubyContext context, SourceSection sourceSection, RubyNode child) {
    this(context, sourceSection, child, UnsupportedOperationBehavior.TYPE_ERROR);
  }

  public ExceptionTranslatingNode(
      RubyContext context,
      SourceSection sourceSection,
      RubyNode child,
      UnsupportedOperationBehavior unsupportedOperationBehavior) {
    super(context, sourceSection);
    this.child = child;
    this.unsupportedOperationBehavior = unsupportedOperationBehavior;
  }

  @Override
  public Object execute(VirtualFrame frame) {
    try {
      assert assertArgumentsShouldBeVisible(frame);

      final Object result = child.execute(frame);

      assert shouldObjectBeVisible(result)
          : "result@" + getEncapsulatingSourceSection().getShortDescription();

      return result;
    } catch (StackOverflowError error) {
      // TODO: we might want to do sth smarter here to avoid consuming frames when we are almost out
      // of it.
      CompilerDirectives.transferToInterpreter();
      throw new RaiseException(translate(error));
    } catch (TruffleFatalException | ThreadExitException exception) {
      throw exception;
    } catch (ControlFlowException exception) {
      controlProfile.enter();
      throw exception;
    } catch (RaiseException exception) {
      rethrowProfile.enter();
      throw exception;
    } catch (MainExitException exception) {
      CompilerDirectives.transferToInterpreter();
      throw exception;
    } catch (ArithmeticException exception) {
      CompilerDirectives.transferToInterpreter();
      throw new RaiseException(translate(exception));
    } catch (UnsupportedSpecializationException exception) {
      CompilerDirectives.transferToInterpreter();
      throw new RaiseException(translate(exception));
    } catch (org.jruby.exceptions.RaiseException e) {
      CompilerDirectives.transferToInterpreter();
      throw new RaiseException(getContext().toTruffle(e.getException(), this));
    } catch (Throwable exception) {
      CompilerDirectives.transferToInterpreter();
      throw new RaiseException(translate(exception));
    }
  }

  private DynamicObject translate(ArithmeticException exception) {
    if (getContext().getOptions().EXCEPTIONS_PRINT_JAVA) {
      exception.printStackTrace();
    }

    return getContext().getCoreLibrary().zeroDivisionError(this);
  }

  private DynamicObject translate(UnsupportedSpecializationException exception) {
    if (getContext().getOptions().EXCEPTIONS_PRINT_JAVA) {
      exception.printStackTrace();
    }

    final StringBuilder builder = new StringBuilder();
    builder.append("Truffle doesn't have a case for the ");
    builder.append(exception.getNode().getClass().getName());
    builder.append(" node with values of type ");

    for (Object value : exception.getSuppliedValues()) {
      builder.append(" ");

      if (value == null) {
        builder.append("null");
      } else if (value instanceof DynamicObject) {
        builder.append(
            Layouts.MODULE
                .getFields(Layouts.BASIC_OBJECT.getLogicalClass(((DynamicObject) value)))
                .getName());
        builder.append("(");
        builder.append(value.getClass().getName());
        builder.append(")");

        if (RubyGuards.isRubyArray(value)) {
          final DynamicObject array = (DynamicObject) value;
          builder.append("[");

          if (Layouts.ARRAY.getStore(array) == null) {
            builder.append("null");
          } else {
            builder.append(Layouts.ARRAY.getStore(array).getClass().getName());
          }

          builder.append(",");
          builder.append(Layouts.ARRAY.getSize(array));
          builder.append("]");
        } else if (RubyGuards.isRubyHash(value)) {
          final Object store = Layouts.HASH.getStore((DynamicObject) value);

          if (store == null) {
            builder.append("[null]");
          } else {
            builder.append("[");
            builder.append(store.getClass().getName());
            builder.append("]");
          }
        }
      } else {
        builder.append(value.getClass().getName());
      }

      if (value instanceof Number || value instanceof Boolean) {
        builder.append("=");
        builder.append(value.toString());
      }
    }

    switch (unsupportedOperationBehavior) {
      case TYPE_ERROR:
        return getContext().getCoreLibrary().typeError(builder.toString(), this);
      case ARGUMENT_ERROR:
        return getContext().getCoreLibrary().argumentError(builder.toString(), this);
      default:
        throw new UnsupportedOperationException();
    }
  }

  public DynamicObject translate(Throwable throwable) {
    if (getContext().getOptions().EXCEPTIONS_PRINT_JAVA
        || (boolean) getContext().getOptions().EXCEPTIONS_PRINT_UNCAUGHT_JAVA) {
      throwable.printStackTrace();
    }

    if (throwable.getStackTrace().length > 0) {
      return getContext()
          .getCoreLibrary()
          .internalError(
              String.format(
                  "%s %s %s",
                  throwable.getClass().getSimpleName(),
                  throwable.getMessage(),
                  throwable.getStackTrace()[0].toString()),
              this);
    } else {
      return getContext()
          .getCoreLibrary()
          .internalError(
              String.format(
                  "%s %s ???", throwable.getClass().getSimpleName(), throwable.getMessage()),
              this);
    }
  }

  private boolean shouldObjectBeVisible(Object object) {
    return object instanceof TruffleObject
        || object instanceof Boolean
        || object instanceof Integer
        || object instanceof Long
        || object instanceof Double;
  }

  private boolean assertArgumentsShouldBeVisible(VirtualFrame frame) {
    final Object self = RubyArguments.getSelf(frame.getArguments());

    assert shouldObjectBeVisible(self)
        : "self="
            + (self == null ? "null" : self.getClass())
            + "@"
            + getEncapsulatingSourceSection().getShortDescription();

    final Object[] arguments = RubyArguments.extractUserArguments(frame.getArguments());

    for (int n = 0; n < arguments.length; n++) {
      final Object argument = arguments[n];
      assert shouldObjectBeVisible(argument)
          : "arg["
              + n
              + "]="
              + (argument == null ? "null" : argument.getClass() + "=" + toString(argument))
              + "@"
              + getEncapsulatingSourceSection().getShortDescription();
    }

    return true;
  }

  private String toString(Object object) {
    if (object instanceof Object[]) {
      return Arrays.toString((Object[]) object);
    } else {
      return object.toString();
    }
  }
}
Esempio n. 23
0
/**
 * Represents a block of code run with exception handlers. There's no {@code try} keyword in Ruby -
 * it's implicit - but it's similar to a try statement in any other language.
 */
public class TryNode extends RubyNode {

  @Child protected ExceptionTranslatingNode tryPart;
  @Children final RescueNode[] rescueParts;
  @Child protected RubyNode elsePart;
  @Child protected WriteInstanceVariableNode clearExceptionVariableNode;

  private final BranchProfile elseProfile = BranchProfile.create();
  private final BranchProfile controlFlowProfile = BranchProfile.create();
  private final BranchProfile raiseExceptionProfile = BranchProfile.create();

  public TryNode(
      RubyContext context,
      SourceSection sourceSection,
      ExceptionTranslatingNode tryPart,
      RescueNode[] rescueParts,
      RubyNode elsePart) {
    super(context, sourceSection);
    this.tryPart = tryPart;
    this.rescueParts = rescueParts;
    this.elsePart = elsePart;
    clearExceptionVariableNode =
        new WriteInstanceVariableNode(
            context,
            sourceSection,
            "$!",
            new ObjectLiteralNode(
                context, sourceSection, context.getCoreLibrary().getGlobalVariablesObject()),
            new ObjectLiteralNode(context, sourceSection, context.getCoreLibrary().getNilObject()),
            true);
  }

  @Override
  public Object execute(VirtualFrame frame) {
    while (true) {
      getContext().getSafepointManager().poll();

      Object result;

      try {
        result = tryPart.execute(frame);
      } catch (ControlFlowException exception) {
        controlFlowProfile.enter();
        throw exception;
      } catch (RaiseException exception) {
        raiseExceptionProfile.enter();

        try {
          return handleException(frame, exception);
        } catch (RetryException e) {
          continue;
        }
      } finally {
        clearExceptionVariableNode.execute(frame);
      }

      elseProfile.enter();
      elsePart.executeVoid(frame);
      return result;
    }
  }

  @ExplodeLoop
  private Object handleException(VirtualFrame frame, RaiseException exception) {
    CompilerAsserts.neverPartOfCompilation();

    notDesignedForCompilation();
    getContext()
        .getCoreLibrary()
        .getGlobalVariablesObject()
        .getOperations()
        .setInstanceVariable(
            getContext().getCoreLibrary().getGlobalVariablesObject(),
            "$!",
            exception.getRubyException());

    for (RescueNode rescue : rescueParts) {
      if (rescue.canHandle(frame, exception.getRubyException())) {
        return rescue.execute(frame);
      }
    }

    throw exception;
  }
}