@NodeChildren({
    @NodeChild(value = "enclosingFrame", type = AccessEnclosingFrameNode.class),
    @NodeChild(value = "frameSlotNode", type = FrameSlotNode.class)
  })
  protected abstract static class ResolvedWriteSuperFrameVariableNode
      extends WriteSuperFrameVariableNode {

    private final ValueProfile storedObjectProfile = ValueProfile.createClassProfile();
    private final BranchProfile invalidateProfile = BranchProfile.create();
    private final ValueProfile enclosingFrameProfile = ValueProfile.createClassProfile();

    private final Mode mode;

    public ResolvedWriteSuperFrameVariableNode(Mode mode) {
      this.mode = mode;
    }

    protected abstract FrameSlotNode getFrameSlotNode();

    @Specialization(guards = "isLogicalKind(enclosingFrame, frameSlot)")
    protected void doLogical(byte value, MaterializedFrame enclosingFrame, FrameSlot frameSlot) {
      FrameSlotChangeMonitor.setByteAndInvalidate(
          enclosingFrameProfile.profile(enclosingFrame), frameSlot, value, true, invalidateProfile);
    }

    @Specialization(guards = "isIntegerKind(enclosingFrame, frameSlot)")
    protected void doInteger(int value, MaterializedFrame enclosingFrame, FrameSlot frameSlot) {
      FrameSlotChangeMonitor.setIntAndInvalidate(
          enclosingFrameProfile.profile(enclosingFrame), frameSlot, value, true, invalidateProfile);
    }

    @Specialization(guards = "isDoubleKind(enclosingFrame, frameSlot)")
    protected void doDouble(double value, MaterializedFrame enclosingFrame, FrameSlot frameSlot) {
      FrameSlotChangeMonitor.setDoubleAndInvalidate(
          enclosingFrameProfile.profile(enclosingFrame), frameSlot, value, true, invalidateProfile);
    }

    @Specialization
    protected void doObject(Object value, MaterializedFrame enclosingFrame, FrameSlot frameSlot) {
      MaterializedFrame profiledFrame = enclosingFrameProfile.profile(enclosingFrame);
      Object newValue =
          shareObjectValue(
              profiledFrame, frameSlot, storedObjectProfile.profile(value), mode, true);
      FrameSlotChangeMonitor.setObjectAndInvalidate(
          profiledFrame, frameSlot, newValue, true, invalidateProfile);
    }
  }
  private static final class WriteSuperFrameVariableConditionalNode
      extends WriteSuperFrameVariableNode {

    @Child private ResolvedWriteSuperFrameVariableNode writeNode;
    @Child private WriteSuperFrameVariableNode nextNode;
    @Child private RNode rhs;

    private final ValueProfile enclosingFrameProfile = ValueProfile.createClassProfile();
    private final ConditionProfile hasValueProfile = ConditionProfile.createBinaryProfile();
    private final ConditionProfile nullSuperFrameProfile = ConditionProfile.createBinaryProfile();

    WriteSuperFrameVariableConditionalNode(
        ResolvedWriteSuperFrameVariableNode writeNode,
        WriteSuperFrameVariableNode 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));
    }
  }
/** Call target that is optimized by Graal upon surpassing a specific invocation threshold. */
public class OptimizedCallTarget extends InstalledCode
    implements RootCallTarget, LoopCountReceiver, ReplaceObserver {
  private static final RootNode UNINITIALIZED = RootNode.createConstantNode(null);

  protected final GraalTruffleRuntime runtime;
  private SpeculationLog speculationLog;
  protected final CompilationProfile compilationProfile;
  protected final CompilationPolicy compilationPolicy;
  private final OptimizedCallTarget sourceCallTarget;
  private final AtomicInteger callSitesKnown = new AtomicInteger(0);
  private final ValueProfile exceptionProfile = ValueProfile.createClassProfile();

  @CompilationFinal private Class<?>[] profiledArgumentTypes;
  @CompilationFinal private Assumption profiledArgumentTypesAssumption;
  @CompilationFinal private Class<?> profiledReturnType;
  @CompilationFinal private Assumption profiledReturnTypeAssumption;

  private final RootNode rootNode;
  private volatile RootNode uninitializedRootNode = UNINITIALIZED;

  /* Experimental fields for new splitting. */
  private final Map<TruffleStamp, OptimizedCallTarget> splitVersions = new HashMap<>();
  private TruffleStamp argumentStamp = DefaultTruffleStamp.getInstance();

  private TruffleInlining inlining;
  private int cachedNonTrivialNodeCount = -1;
  private int cloneIndex;

  /**
   * When this call target is inlined, the inlining {@link InstalledCode} registers this assumption.
   * It gets invalidated when a node rewriting is performed. This ensures that all compiled methods
   * that have this call target inlined are properly invalidated.
   */
  private final CyclicAssumption nodeRewritingAssumption;

  private volatile Future<?> compilationTask;

  public final RootNode getRootNode() {
    return rootNode;
  }

  public OptimizedCallTarget(
      OptimizedCallTarget sourceCallTarget,
      RootNode rootNode,
      GraalTruffleRuntime runtime,
      CompilationPolicy compilationPolicy,
      SpeculationLog speculationLog) {
    super(rootNode.toString());
    this.sourceCallTarget = sourceCallTarget;
    this.runtime = runtime;
    this.speculationLog = speculationLog;
    this.rootNode = rootNode;
    this.compilationPolicy = compilationPolicy;
    this.rootNode.adoptChildren();
    this.rootNode.applyInstrumentation();
    if (TruffleCallTargetProfiling.getValue()) {
      this.compilationProfile = new TraceCompilationProfile();
    } else {
      this.compilationProfile = new CompilationProfile();
    }
    this.nodeRewritingAssumption =
        new CyclicAssumption("nodeRewritingAssumption of " + rootNode.toString());
  }

  public final void log(String message) {
    runtime.log(message);
  }

  public final boolean isCompiling() {
    return getCompilationTask() != null;
  }

  private static RootNode cloneRootNode(RootNode root) {
    if (root == null || !root.isCloningAllowed()) {
      return null;
    }
    return NodeUtil.cloneNode(root);
  }

  public Assumption getNodeRewritingAssumption() {
    return nodeRewritingAssumption.getAssumption();
  }

  public final void mergeArgumentStamp(TruffleStamp p) {
    this.argumentStamp = this.argumentStamp.join(p);
  }

  public final TruffleStamp getArgumentStamp() {
    return argumentStamp;
  }

  public int getCloneIndex() {
    return cloneIndex;
  }

  public OptimizedCallTarget cloneUninitialized() {
    ensureCloned();
    RootNode copiedRoot = cloneRootNode(uninitializedRootNode);
    if (copiedRoot == null) {
      return null;
    }
    OptimizedCallTarget splitTarget =
        (OptimizedCallTarget) runtime.createClonedCallTarget(this, copiedRoot);
    splitTarget.cloneIndex = cloneIndex++;
    return splitTarget;
  }

  private void ensureCloned() {
    if (uninitializedRootNode == UNINITIALIZED) {
      synchronized (this) {
        if (uninitializedRootNode == UNINITIALIZED) {
          this.uninitializedRootNode =
              sourceCallTarget == null
                  ? cloneRootNode(rootNode)
                  : sourceCallTarget.uninitializedRootNode;
        }
      }
    }
  }

  public Map<TruffleStamp, OptimizedCallTarget> getSplitVersions() {
    return splitVersions;
  }

  public SpeculationLog getSpeculationLog() {
    return speculationLog;
  }

  @Override
  public Object call(Object... args) {
    compilationProfile.reportIndirectCall();
    if (profiledArgumentTypesAssumption != null && profiledArgumentTypesAssumption.isValid()) {
      // Argument profiling is not possible for targets of indirect calls.
      CompilerDirectives.transferToInterpreterAndInvalidate();
      profiledArgumentTypesAssumption.invalidate();
      profiledArgumentTypes = null;
    }
    return doInvoke(args);
  }

  public final Object callDirect(Object... args) {
    compilationProfile.reportDirectCall();
    profileArguments(args);
    try {
      Object result = doInvoke(args);
      Class<?> klass = profiledReturnType;
      if (klass != null
          && CompilerDirectives.inCompiledCode()
          && profiledReturnTypeAssumption.isValid()) {
        result = unsafeCast(result, klass, true, true);
      }
      return result;
    } catch (Throwable t) {
      t = exceptionProfile.profile(t);
      if (t instanceof RuntimeException) {
        throw (RuntimeException) t;
      } else if (t instanceof Error) {
        throw (Error) t;
      } else {
        CompilerDirectives.transferToInterpreter();
        throw new RuntimeException(t);
      }
    }
  }

  public final Object callInlined(Object... arguments) {
    compilationProfile.reportInlinedCall();
    VirtualFrame frame = createFrame(getRootNode().getFrameDescriptor(), arguments);
    return callProxy(frame);
  }

  @ExplodeLoop
  public void profileArguments(Object[] args) {
    Assumption typesAssumption = profiledArgumentTypesAssumption;
    if (typesAssumption == null) {
      CompilerDirectives.transferToInterpreterAndInvalidate();
      initializeProfiledArgumentTypes(args);
    } else {
      Class<?>[] types = profiledArgumentTypes;
      if (types != null) {
        if (types.length != args.length) {
          CompilerDirectives.transferToInterpreterAndInvalidate();
          typesAssumption.invalidate();
          profiledArgumentTypes = null;
        } else if (typesAssumption.isValid()) {
          for (int i = 0; i < types.length; i++) {
            Class<?> type = types[i];
            Object value = args[i];
            if (type != null && (value == null || value.getClass() != type)) {
              CompilerDirectives.transferToInterpreterAndInvalidate();
              updateProfiledArgumentTypes(args, types);
              break;
            }
          }
        }
      }
    }
  }

  private void initializeProfiledArgumentTypes(Object[] args) {
    CompilerAsserts.neverPartOfCompilation();
    profiledArgumentTypesAssumption =
        Truffle.getRuntime().createAssumption("Profiled Argument Types");
    if (TruffleArgumentTypeSpeculation.getValue()) {
      Class<?>[] result = new Class<?>[args.length];
      for (int i = 0; i < args.length; i++) {
        result[i] = classOf(args[i]);
      }

      profiledArgumentTypes = result;
    }
  }

  private void updateProfiledArgumentTypes(Object[] args, Class<?>[] types) {
    CompilerAsserts.neverPartOfCompilation();
    profiledArgumentTypesAssumption.invalidate();
    for (int j = 0; j < types.length; j++) {
      types[j] = joinTypes(types[j], classOf(args[j]));
    }
    profiledArgumentTypesAssumption =
        Truffle.getRuntime().createAssumption("Profiled Argument Types");
  }

  private static Class<?> classOf(Object arg) {
    return arg != null ? arg.getClass() : null;
  }

  private static Class<?> joinTypes(Class<?> class1, Class<?> class2) {
    if (class1 == class2) {
      return class1;
    } else {
      return null;
    }
  }

  protected Object doInvoke(Object[] args) {
    return callBoundary(args);
  }

  @TruffleCallBoundary
  protected final Object callBoundary(Object[] args) {
    if (CompilerDirectives.inInterpreter()) {
      // We are called and we are still in Truffle interpreter mode.
      interpreterCall();
    } else {
      // We come here from compiled code
    }

    return callRoot(args);
  }

  public final Object callRoot(Object[] originalArguments) {
    Object[] args = originalArguments;
    if (CompilerDirectives.inCompiledCode()) {
      Assumption argumentTypesAssumption = this.profiledArgumentTypesAssumption;
      if (argumentTypesAssumption != null && argumentTypesAssumption.isValid()) {
        args =
            unsafeCast(
                castArrayFixedLength(args, profiledArgumentTypes.length),
                Object[].class,
                true,
                true);
        args = castArguments(args);
      }
    }

    VirtualFrame frame = createFrame(getRootNode().getFrameDescriptor(), args);
    Object result = callProxy(frame);

    profileReturnType(result);

    return result;
  }

  public void profileReturnType(Object result) {
    Assumption returnTypeAssumption = profiledReturnTypeAssumption;
    if (returnTypeAssumption == null) {
      if (TruffleReturnTypeSpeculation.getValue()) {
        CompilerDirectives.transferToInterpreterAndInvalidate();
        profiledReturnType = (result == null ? null : result.getClass());
        profiledReturnTypeAssumption =
            Truffle.getRuntime().createAssumption("Profiled Return Type");
      }
    } else if (profiledReturnType != null) {
      if (result == null || profiledReturnType != result.getClass()) {
        CompilerDirectives.transferToInterpreterAndInvalidate();
        profiledReturnType = null;
        returnTypeAssumption.invalidate();
      }
    }
  }

  @Override
  public void invalidate() {
    invalidate(null, null);
  }

  protected void invalidate(Object source, CharSequence reason) {
    if (isValid()) {
      this.runtime.invalidateInstalledCode(this, source, reason);
    }
    cachedNonTrivialNodeCount = -1;
  }

  public TruffleInlining getInlining() {
    return inlining;
  }

  public void setInlining(TruffleInlining inliningDecision) {
    this.inlining = inliningDecision;
  }

  private boolean cancelInstalledTask(Node source, CharSequence reason) {
    return this.runtime.cancelInstalledTask(this, source, reason);
  }

  private void interpreterCall() {
    if (isValid()) {
      // Stubs were deoptimized => reinstall.
      this.runtime.reinstallStubs();
    } else {
      if (uninitializedRootNode == UNINITIALIZED) {
        ensureCloned();
      }
      compilationProfile.reportInterpreterCall();
      if (!isCompiling()
          && compilationPolicy.shouldCompile(compilationProfile, getCompilerOptions())) {
        compile();
      }
    }
  }

  public final void compile() {
    if (!isCompiling()) {
      ensureCloned();
      runtime.compile(
          this,
          TruffleBackgroundCompilation.getValue()
              && !TruffleCompilationExceptionsAreThrown.getValue());
    }
  }

  public void notifyCompilationFailed(Throwable t) {
    if (t instanceof BailoutException && !((BailoutException) t).isPermanent()) {
      /*
       * Non permanent bailouts are expected cases. A non permanent bailout would be for
       * example class redefinition during code installation. As opposed to permanent
       * bailouts, non permanent bailouts will trigger recompilation and are not considered a
       * failure state.
       */
    } else {
      compilationPolicy.recordCompilationFailure(t);
      if (TruffleCompilationExceptionsAreThrown.getValue()) {
        throw new OptimizationFailedException(t, this);
      }
      if (TruffleCompilationExceptionsArePrinted.getValue()
          || TruffleCompilationExceptionsAreFatal.getValue()) {
        printException(t);
        if (TruffleCompilationExceptionsAreFatal.getValue()) {
          System.exit(-1);
        }
      }
    }
  }

  private void printException(Throwable e) {
    StringWriter string = new StringWriter();
    e.printStackTrace(new PrintWriter(string));
    log(string.toString());
  }

  public void notifyCompilationFinished(boolean successful) {
    if (successful && inlining != null) {
      dequeueInlinedCallSites(inlining);
    }
    setCompilationTask(null);
  }

  private void dequeueInlinedCallSites(TruffleInlining parentDecision) {
    for (TruffleInliningDecision decision : parentDecision) {
      if (decision.isInline()) {
        OptimizedCallTarget target = decision.getTarget();
        target.cancelInstalledTask(
            decision.getProfile().getCallNode(), "Inlining caller compiled.");
        dequeueInlinedCallSites(decision);
      }
    }
  }

  protected final Object callProxy(VirtualFrame frame) {
    try {
      return getRootNode().execute(frame);
    } finally {
      // this assertion is needed to keep the values from being cleared as non-live locals
      assert frame != null && this != null;
    }
  }

  public final int getKnownCallSiteCount() {
    return callSitesKnown.get();
  }

  public final void incrementKnownCallSites() {
    callSitesKnown.incrementAndGet();
  }

  public final void decrementKnownCallSites() {
    callSitesKnown.decrementAndGet();
  }

  public final OptimizedCallTarget getSourceCallTarget() {
    return sourceCallTarget;
  }

  @Override
  public String toString() {
    CompilerAsserts.neverPartOfCompilation();
    String superString = rootNode.toString();
    if (isValid()) {
      superString += " <opt>";
    }
    if (sourceCallTarget != null) {
      superString += " <split-" + cloneIndex + "-" + argumentStamp.toStringShort() + ">";
    }
    return superString;
  }

  public CompilationProfile getCompilationProfile() {
    return compilationProfile;
  }

  @ExplodeLoop
  private Object[] castArguments(Object[] originalArguments) {
    Class<?>[] types = profiledArgumentTypes;
    Object[] castArguments = new Object[types.length];
    for (int i = 0; i < types.length; i++) {
      castArguments[i] =
          types[i] != null
              ? unsafeCast(originalArguments[i], types[i], true, true)
              : originalArguments[i];
    }
    return castArguments;
  }

  private static Object castArrayFixedLength(
      Object[] args, @SuppressWarnings("unused") int length) {
    return args;
  }

  public static VirtualFrame createFrame(FrameDescriptor descriptor, Object[] args) {
    if (TruffleCompilerOptions.TruffleUseFrameWithoutBoxing.getValue()) {
      return new FrameWithoutBoxing(descriptor, args);
    } else {
      return new FrameWithBoxing(descriptor, args);
    }
  }

  public List<OptimizedDirectCallNode> getCallNodes() {
    final List<OptimizedDirectCallNode> callNodes = new ArrayList<>();
    getRootNode()
        .accept(
            new NodeVisitor() {
              public boolean visit(Node node) {
                if (node instanceof OptimizedDirectCallNode) {
                  callNodes.add((OptimizedDirectCallNode) node);
                }
                return true;
              }
            });
    return callNodes;
  }

  @Override
  public void reportLoopCount(int count) {
    compilationProfile.reportLoopCount(count);
  }

  @Override
  public boolean nodeReplaced(Node oldNode, Node newNode, CharSequence reason) {
    CompilerAsserts.neverPartOfCompilation();
    if (isValid()) {
      invalidate(newNode, reason);
    }
    /* Notify compiled method that have inlined this call target that the tree changed. */
    nodeRewritingAssumption.invalidate();

    compilationProfile.reportNodeReplaced();
    if (cancelInstalledTask(newNode, reason)) {
      compilationProfile.reportInvalidated();
    }
    return false;
  }

  public void accept(NodeVisitor visitor, boolean includeInlinedNodes) {
    TruffleInlining inliner = getInlining();
    if (includeInlinedNodes && inliner != null) {
      inlining.accept(this, visitor);
    } else {
      getRootNode().accept(visitor);
    }
  }

  public Stream<Node> nodeStream(boolean includeInlinedNodes) {
    Iterator<Node> iterator;
    TruffleInlining inliner = getInlining();
    if (includeInlinedNodes && inliner != null) {
      iterator = inliner.makeNodeIterator(this);
    } else {
      iterator = NodeUtil.makeRecursiveIterator(this.getRootNode());
    }
    return StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, 0), false);
  }

  public final int getNonTrivialNodeCount() {
    if (cachedNonTrivialNodeCount == -1) {
      cachedNonTrivialNodeCount = calculateNonTrivialNodes(getRootNode());
    }
    return cachedNonTrivialNodeCount;
  }

  public static int calculateNonTrivialNodes(Node node) {
    NonTrivialNodeCountVisitor visitor = new NonTrivialNodeCountVisitor();
    node.accept(visitor);
    return visitor.nodeCount;
  }

  public Map<String, Object> getDebugProperties() {
    Map<String, Object> properties = new LinkedHashMap<>();
    AbstractDebugCompilationListener.addASTSizeProperty(this, properties);
    properties.putAll(getCompilationProfile().getDebugProperties());
    return properties;
  }

  public static Method getCallDirectMethod() {
    try {
      return OptimizedCallTarget.class.getDeclaredMethod("callDirect", Object[].class);
    } catch (NoSuchMethodException | SecurityException e) {
      throw new JVMCIError(e);
    }
  }

  public static Method getCallInlinedMethod() {
    try {
      return OptimizedCallTarget.class.getDeclaredMethod("callInlined", Object[].class);
    } catch (NoSuchMethodException | SecurityException e) {
      throw new JVMCIError(e);
    }
  }

  private CompilerOptions getCompilerOptions() {
    final CompilerOptions options = rootNode.getCompilerOptions();
    if (options != null) {
      return options;
    }
    return DefaultCompilerOptions.INSTANCE;
  }

  @SuppressWarnings({"unchecked", "unused"})
  private static <T> T unsafeCast(Object value, Class<T> type, boolean condition, boolean nonNull) {
    return (T) value;
  }

  private static final class NonTrivialNodeCountVisitor implements NodeVisitor {
    public int nodeCount;

    public boolean visit(Node node) {
      if (!node.getCost().isTrivial()) {
        nodeCount++;
      }
      return true;
    }
  }

  @Override
  public final boolean equals(Object obj) {
    return obj == this;
  }

  @Override
  public final int hashCode() {
    return System.identityHashCode(this);
  }

  Future<?> getCompilationTask() {
    return compilationTask;
  }

  void setCompilationTask(Future<?> compilationTask) {
    this.compilationTask = compilationTask;
  }
}
/**
 * {@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));
    }
  }
}
Example #5
0
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);
}