/** * 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; } }
/** * {@link WriteSuperFrameVariableNode} captures a write to a variable in some parent frame. * * <p>The state starts out a "unresolved" and transforms to "resolved". */ @SuppressWarnings("unused") @NodeChildren({ @NodeChild(value = "enclosingFrame", type = AccessEnclosingFrameNode.class), @NodeChild(value = "frameSlotNode", type = FrameSlotNode.class) }) @NodeField(name = "mode", type = Mode.class) public abstract class WriteSuperFrameVariableNode extends WriteSuperFrameVariableNodeHelper { private final ValueProfile storedObjectProfile = ValueProfile.createClassProfile(); private final BranchProfile invalidateProfile = BranchProfile.create(); private final ValueProfile enclosingFrameProfile = ValueProfile.createClassProfile(); protected abstract FrameSlotNode getFrameSlotNode(); public abstract Mode getMode(); public static WriteVariableNode create(String name, RNode rhs, Mode mode) { return new UnresolvedWriteSuperFrameVariableNode(name, rhs, mode); } @Specialization(guards = "isLogicalKind(frame, frameSlot)") protected void doLogical( VirtualFrame frame, byte value, MaterializedFrame enclosingFrame, FrameSlot frameSlot) { FrameSlotChangeMonitor.setByteAndInvalidate( enclosingFrameProfile.profile(enclosingFrame), frameSlot, value, true, invalidateProfile); } @Specialization(guards = "isIntegerKind(frame, frameSlot)") protected void doInteger( VirtualFrame frame, int value, MaterializedFrame enclosingFrame, FrameSlot frameSlot) { FrameSlotChangeMonitor.setIntAndInvalidate( enclosingFrameProfile.profile(enclosingFrame), frameSlot, value, true, invalidateProfile); } @Specialization(guards = "isDoubleKind(frame, frameSlot)") protected void doDouble( VirtualFrame frame, double value, MaterializedFrame enclosingFrame, FrameSlot frameSlot) { FrameSlotChangeMonitor.setDoubleAndInvalidate( enclosingFrameProfile.profile(enclosingFrame), frameSlot, value, true, invalidateProfile); } @Specialization protected void doObject( VirtualFrame frame, Object value, MaterializedFrame enclosingFrame, FrameSlot frameSlot) { MaterializedFrame profiledFrame = enclosingFrameProfile.profile(enclosingFrame); Object newValue = shareObjectValue( profiledFrame, frameSlot, storedObjectProfile.profile(value), getMode(), true); FrameSlotChangeMonitor.setObjectAndInvalidate( profiledFrame, frameSlot, newValue, true, invalidateProfile); } public static class UnresolvedWriteSuperFrameVariableNode extends WriteSuperFrameVariableNodeHelper { @Child private RNode rhs; private final String symbol; private final BaseWriteVariableNode.Mode mode; public UnresolvedWriteSuperFrameVariableNode( String symbol, RNode rhs, BaseWriteVariableNode.Mode mode) { this.rhs = rhs; this.symbol = symbol; this.mode = mode; } @Override public String getName() { return symbol; } @Override public RNode getRhs() { return rhs; } @Override public void execute(VirtualFrame frame, Object value, MaterializedFrame enclosingFrame) { CompilerDirectives.transferToInterpreterAndInvalidate(); if (getName().isEmpty()) { throw RError.error(this, RError.Message.ZERO_LENGTH_VARIABLE); } final WriteSuperFrameVariableNodeHelper writeNode; if (REnvironment.isGlobalEnvFrame(enclosingFrame)) { /* * we've reached the global scope, do unconditional write. if this is the first node * in the chain, needs the rhs and enclosingFrame nodes */ AccessEnclosingFrameNode enclosingFrameNode = RArguments.getEnclosingFrame(frame) == enclosingFrame ? new AccessEnclosingFrameNode() : null; writeNode = WriteSuperFrameVariableNodeGen.create( getRhs(), enclosingFrameNode, FrameSlotNode.create( findOrAddFrameSlot( enclosingFrame.getFrameDescriptor(), symbol, FrameSlotKind.Illegal)), getName(), mode); } else { WriteSuperFrameVariableNode actualWriteNode = WriteSuperFrameVariableNodeGen.create( null, null, FrameSlotNode.create(symbol), this.getName(), mode); writeNode = new WriteSuperFrameVariableConditionalNode( actualWriteNode, new UnresolvedWriteSuperFrameVariableNode(symbol, null, mode), getRhs()); } replace(writeNode).execute(frame, value, enclosingFrame); } @Override public void execute(VirtualFrame frame, Object value) { CompilerDirectives.transferToInterpreterAndInvalidate(); MaterializedFrame enclosingFrame = RArguments.getEnclosingFrame(frame); if (enclosingFrame != null) { execute(frame, value, enclosingFrame); } else { // we're in global scope, do a local write instead replace(UnresolvedWriteLocalFrameVariableNodeGen.create(getRhs(), symbol, mode)) .execute(frame, value); } } } public static class WriteSuperFrameVariableConditionalNode extends WriteSuperFrameVariableNodeHelper { @Child private WriteSuperFrameVariableNode writeNode; @Child private WriteSuperFrameVariableNodeHelper nextNode; @Child private RNode rhs; private final ValueProfile enclosingFrameProfile = ValueProfile.createClassProfile(); private final ConditionProfile hasValueProfile = ConditionProfile.createBinaryProfile(); private final ConditionProfile nullSuperFrameProfile = ConditionProfile.createBinaryProfile(); WriteSuperFrameVariableConditionalNode( WriteSuperFrameVariableNode writeNode, WriteSuperFrameVariableNodeHelper nextNode, RNode rhs) { this.writeNode = writeNode; this.nextNode = nextNode; this.rhs = rhs; } @Override public Object getName() { return writeNode.getName(); } @Override public RNode getRhs() { return rhs; } @Override public void execute(VirtualFrame frame, Object value, MaterializedFrame enclosingFrame) { MaterializedFrame profiledEnclosingFrame = enclosingFrameProfile.profile(enclosingFrame); if (hasValueProfile.profile(writeNode.getFrameSlotNode().hasValue(profiledEnclosingFrame))) { writeNode.execute(frame, value, profiledEnclosingFrame); } else { MaterializedFrame superFrame = RArguments.getEnclosingFrame(profiledEnclosingFrame); if (nullSuperFrameProfile.profile(superFrame == null)) { // Might be the case if "{ x <<- 42 }": This is in globalEnv! superFrame = REnvironment.globalEnv().getFrame(); } nextNode.execute(frame, value, superFrame); } } @Override public void execute(VirtualFrame frame, Object value) { assert RArguments.getEnclosingFrame(frame) != null; execute(frame, value, RArguments.getEnclosingFrame(frame)); } } }
abstract class PositionCheckNode extends Node { protected final Class<?> positionClass; protected final int dimensionIndex; protected final int numDimensions; protected final VectorLengthProfile positionLengthProfile = VectorLengthProfile.create(); protected final BranchProfile error = BranchProfile.create(); protected final boolean replace; protected final RType containerType; @Child private PositionCastNode castNode; @Child private RLengthNode positionLengthNode = RLengthNode.create(); @Child private PositionCharacterLookupNode characterLookup; PositionCheckNode( ElementAccessMode mode, RType containerType, Object positionValue, int dimensionIndex, int numDimensions, boolean exact, boolean replace) { this.positionClass = positionValue.getClass(); this.dimensionIndex = dimensionIndex; this.numDimensions = numDimensions; this.replace = replace; this.containerType = containerType; this.castNode = PositionCastNode.create(mode, replace); if (positionValue instanceof String || positionValue instanceof RAbstractStringVector) { boolean useNAForNotFound = !replace && isListLike(containerType) && mode.isSubscript(); characterLookup = new PositionCharacterLookupNode( mode, numDimensions, dimensionIndex, useNAForNotFound, exact); } } protected static boolean isListLike(RType type) { switch (type) { case Language: case DataFrame: case Expression: case PairList: case List: return true; } return false; } public boolean isIgnoreDimension() { return positionClass == RMissing.class; } public Class<?> getPositionClass() { return positionClass; } public final boolean isSupported(Object object) { return object.getClass() == positionClass; } public static PositionCheckNode createNode( ElementAccessMode mode, RType containerType, Object position, int positionIndex, int numDimensions, boolean exact, boolean replace, boolean recursive) { if (mode.isSubset()) { return PositionCheckSubsetNodeGen.create( mode, containerType, position, positionIndex, numDimensions, exact, replace); } else { return PositionCheckSubscriptNodeGen.create( mode, containerType, position, positionIndex, numDimensions, exact, replace, recursive); } } protected boolean isMultiDimension() { return numDimensions > 1; } public final Object execute( PositionProfile profile, RAbstractContainer vector, int[] vectorDimensions, int vectorLength, Object position) { Object castPosition = castNode.execute(positionClass.cast(position)); int dimensionLength; if (numDimensions == 1) { dimensionLength = vectorLength; } else { assert vectorDimensions != null; assert vectorDimensions.length == numDimensions; dimensionLength = vectorDimensions[dimensionIndex]; } if (characterLookup != null) { castPosition = characterLookup.execute(vector, (RAbstractStringVector) castPosition, dimensionLength); } RTypedValue positionVector = (RTypedValue) profilePosition(castPosition); int positionLength; if (positionVector instanceof RMissing) { positionLength = -1; } else { positionLength = positionLengthProfile.profile(((RAbstractVector) positionVector).getLength()); } assert isValidCastedType(positionVector) : "result type of a position cast node must be integer or logical"; return execute(profile, dimensionLength, positionVector, positionLength); } private final ValueProfile castedValue = ValueProfile.createClassProfile(); Object profilePosition(Object positionVector) { return castedValue.profile(positionVector); } private static boolean isValidCastedType(RTypedValue positionVector) { RType type = positionVector.getRType(); return type == RType.Integer || type == RType.Logical || type == RType.Character || type == RType.Double || type == RType.Null; } public abstract Object execute( PositionProfile statistics, int dimensionLength, Object position, int positionLength); }