<commit_before>package som.interpreter.nodes;

import som.compiler.ClassBuilder.ClassDefinitionId;
import som.interpreter.LexicalScope.ClassScope.ClassIdAndContextLevel;
import som.interpreter.LexicalScope.MethodScope;
import som.interpreter.nodes.MessageSendNode.AbstractMessageSendNode;
import som.vmobjects.SSymbol;

import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.source.SourceSection;


public class ResolvingImplicitReceiverSend extends AbstractMessageSendNode {

  private final SSymbol     selector;
  private final MethodScope currentScope;
  private final ClassDefinitionId classDefId;

  // this is only a helper field, used to handle the specialization race
  private PreevaluatedExpression replacedBy;

  public ResolvingImplicitReceiverSend(final SSymbol selector,
      final ExpressionNode[] arguments, final MethodScope currentScope,
      final ClassDefinitionId classDefId, final SourceSection source) {
    super(arguments, source);
    this.selector     = selector;
    this.currentScope = currentScope;
    this.classDefId   = classDefId;
  }

  @Override
  public Object doPreEvaluated(final VirtualFrame frame, final Object[] args) {
    // the specialize() is racy and needs to be handled with care.
    // it rewrites existing nodes and adapts the args array
    // in some cases
    //
    // specifically, it takes the argumentNode[0] node and move it one level
    // down, under another Outer node
    PreevaluatedExpression newNode = atomic(() -> specialize(args));
    return newNode.
        doPreEvaluated(frame, args);
  }

  protected PreevaluatedExpression specialize(final Object[] args) {
    PreevaluatedExpression newNode;
    // first check whether it is an outer send
    // it it is, we get the context level of the outer send and rewrite to one
    ClassIdAndContextLevel result = currentScope.lookupSlotOrClass(selector);
    if (result != null) {
      if (replacedBy == null) {
        assert result.contextLevel >= 0;
        OuterObjectRead outer = OuterObjectReadNodeGen.create(result.contextLevel,
            classDefId, result.classId, getSourceSection(), argumentNodes[0]);
        argumentNodes[0] = insert(outer);
        newNode = MessageSendNode.createMessageSend(selector, argumentNodes,
            getSourceSection());
        replace((ExpressionNode) newNode);
        args[0] = outer.executeEvaluated(args[0]);
        replacedBy = newNode;
      } else {
        // this can happen as part of a race condition
        // and it means argumentNodes[0] has already been replaced
        // and we can use it to determine the outer receiver, but don't
        // need to do much else
        args[0] = ((OuterObjectRead) argumentNodes[0]).executeEvaluated(args[0]);
        newNode = replacedBy;
      }
    } else {
      if (replacedBy == null) {
        newNode = MessageSendNode.createMessageSend(selector, argumentNodes,
            getSourceSection());
        replace((ExpressionNode) newNode);
      } else {
        newNode = replacedBy;
      }
    }
    return newNode;
  }

  @Override
  public String toString() {
    return "ImplicitSend(" + selector.toString() + ")";
  }
}<commit_msg>Don't modify the argumentNodes array in ResolvingImplicitReceiverSend<commit_after>package som.interpreter.nodes;

import som.compiler.ClassBuilder.ClassDefinitionId;
import som.interpreter.LexicalScope.ClassScope.ClassIdAndContextLevel;
import som.interpreter.LexicalScope.MethodScope;
import som.interpreter.nodes.MessageSendNode.AbstractMessageSendNode;
import som.vmobjects.SSymbol;

import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.source.SourceSection;


public class ResolvingImplicitReceiverSend extends AbstractMessageSendNode {

  private final SSymbol     selector;
  private final MethodScope currentScope;
  private final ClassDefinitionId classDefId;

  // this is only a helper field, used to handle the specialization race
  private PreevaluatedExpression replacedBy;
  private OuterObjectRead        newReceiverNode;

  public ResolvingImplicitReceiverSend(final SSymbol selector,
      final ExpressionNode[] arguments, final MethodScope currentScope,
      final ClassDefinitionId classDefId, final SourceSection source) {
    super(arguments, source);
    this.selector     = selector;
    this.currentScope = currentScope;
    this.classDefId   = classDefId;
  }

  @Override
  public Object doPreEvaluated(final VirtualFrame frame, final Object[] args) {
    // this specialize method is designed to be execute only once and
    // tracks its replacement nodes to avoid re-specialization in case of
    // re-execution
    PreevaluatedExpression newNode = atomic(() -> specialize(args));
    return newNode.
        doPreEvaluated(frame, args);
  }

  protected PreevaluatedExpression specialize(final Object[] args) {
    // first check whether it is an outer send
    // it it is, we get the context level of the outer send and rewrite to one
    ClassIdAndContextLevel result = currentScope.lookupSlotOrClass(selector);
    if (result != null) {
      if (replacedBy == null) {
        assert result.contextLevel >= 0;

        newReceiverNode = OuterObjectReadNodeGen.create(result.contextLevel,
            classDefId, result.classId, getSourceSection(), argumentNodes[0]);
        ExpressionNode[] msgArgNodes = argumentNodes.clone();
        msgArgNodes[0] = newReceiverNode;

        replacedBy = MessageSendNode.createMessageSend(selector, msgArgNodes,
            getSourceSection());

        replace((ExpressionNode) replacedBy);
      }
      args[0] = newReceiverNode.executeEvaluated(args[0]);
    } else {
      if (replacedBy == null) {
        replacedBy = MessageSendNode.createMessageSend(selector, argumentNodes,
            getSourceSection());
        replace((ExpressionNode) replacedBy);
      }
    }
    return replacedBy;
  }

  @Override
  public String toString() {
    return "ImplicitSend(" + selector.toString() + ")";
  }
}
{'fraction_non_alphanumeric': 0.08194536975349767, 'fraction_numerical': 0.002498334443704197, 'mean_word_length': 3.5804729214340196, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '676411', 'n_tokens_mistral': 1609, 'n_tokens_neox': 1585, 'n_words': 451}