gt:     protected ForeignExecute createForeignCall() {
context: /*
   * Copyright (c) 2013, 2021, Oracle and/or its affiliates. All rights reserved.
   * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   *
   * This code is free software; you can redistribute it and/or modify it
   * under the terms of the GNU General Public License version 3 only, as
   * published by the Free Software Foundation.
   *
   * This code is distributed in the hope that it will be useful, but WITHOUT
   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   * version 3 for more details (a copy is included in the LICENSE file that
   * accompanied this code).
   *
   * You should have received a copy of the GNU General Public License version
   * 3 along with this work; if not, write to the Free Software Foundation,
   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   *
   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   * or visit www.oracle.com if you need additional information or have any
   * questions.
   */
  package com.oracle.truffle.r.nodes.function;
  
  import static com.oracle.truffle.r.runtime.context.FastROptions.RestrictForceSplitting;
  
  import java.util.ArrayList;
  import java.util.Arrays;
  import java.util.List;
  
  import com.oracle.truffle.api.CompilerAsserts;
  import com.oracle.truffle.api.CompilerDirectives;
  import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
  import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
  import com.oracle.truffle.api.RootCallTarget;
  import com.oracle.truffle.api.dsl.Cached;
  import com.oracle.truffle.api.dsl.Fallback;
  import com.oracle.truffle.api.dsl.ImportStatic;
  import com.oracle.truffle.api.dsl.NodeChild;
  import com.oracle.truffle.api.dsl.Specialization;
  import com.oracle.truffle.api.frame.FrameSlot;
  import com.oracle.truffle.api.frame.FrameSlotTypeException;
  import com.oracle.truffle.api.frame.MaterializedFrame;
  import com.oracle.truffle.api.frame.VirtualFrame;
  import com.oracle.truffle.api.interop.ArityException;
  import com.oracle.truffle.api.interop.InteropLibrary;
  import com.oracle.truffle.api.interop.TruffleObject;
  import com.oracle.truffle.api.interop.UnknownIdentifierException;
  import com.oracle.truffle.api.interop.UnsupportedMessageException;
  import com.oracle.truffle.api.interop.UnsupportedTypeException;
  import com.oracle.truffle.api.library.CachedLibrary;
  import com.oracle.truffle.api.nodes.ExplodeLoop;
  import com.oracle.truffle.api.nodes.Node;
  import com.oracle.truffle.api.nodes.NodeCost;
  import com.oracle.truffle.api.nodes.NodeInfo;
  import com.oracle.truffle.api.profiles.ConditionProfile;
  import com.oracle.truffle.api.profiles.LoopConditionProfile;
  import com.oracle.truffle.api.profiles.ValueProfile;
  import com.oracle.truffle.api.source.SourceSection;
  import com.oracle.truffle.r.nodes.RASTUtils;
  import com.oracle.truffle.r.nodes.RRootNode;
  import com.oracle.truffle.r.nodes.access.ConstantNode;
  import com.oracle.truffle.r.nodes.access.variables.LocalReadVariableNode;
  import com.oracle.truffle.r.nodes.access.variables.ReadVariableNode;
  import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
  import com.oracle.truffle.r.nodes.builtin.RBuiltinNode.WithSideEffect;
  import com.oracle.truffle.r.nodes.builtin.RBuiltinRootNode;
  import com.oracle.truffle.r.nodes.function.PromiseHelperNode.PromiseCheckHelperNode;
  import com.oracle.truffle.r.nodes.function.RCallNodeGen.FunctionDispatchNodeGen;
  import com.oracle.truffle.r.nodes.function.RCallNodeGen.SendForeignExecuteMessageNodeGen;
  import com.oracle.truffle.r.nodes.function.RCallNodeGen.SendForeignInvokeMessageNodeGen;
  import com.oracle.truffle.r.nodes.function.S3FunctionLookupNode.Result;
  import com.oracle.truffle.r.nodes.function.call.CallRFunctionNode;
  import com.oracle.truffle.r.nodes.function.call.PrepareArguments;
  import com.oracle.truffle.r.nodes.function.visibility.SetVisibilityNode;
  import com.oracle.truffle.r.nodes.profile.TruffleBoundaryNode;
  import com.oracle.truffle.r.nodes.profile.VectorLengthProfile;
  import com.oracle.truffle.r.runtime.Arguments;
  import com.oracle.truffle.r.runtime.ArgumentsSignature;
  import com.oracle.truffle.r.runtime.DSLConfig;
  import com.oracle.truffle.r.runtime.RArguments;
  import com.oracle.truffle.r.runtime.RArguments.S3Args;
  import com.oracle.truffle.r.runtime.RArguments.S3DefaultArguments;
  import com.oracle.truffle.r.runtime.RCaller;
  import com.oracle.truffle.r.runtime.RDeparse;
  import com.oracle.truffle.r.runtime.RDispatch;
  import com.oracle.truffle.r.runtime.RError;
  import com.oracle.truffle.r.runtime.RErrorHandling;
  import com.oracle.truffle.r.runtime.RInternalError;
  import com.oracle.truffle.r.runtime.RRuntime;
  import com.oracle.truffle.r.runtime.RType;
  import com.oracle.truffle.r.runtime.RVisibility;
  import com.oracle.truffle.r.runtime.Utils;
  import com.oracle.truffle.r.runtime.builtins.FastPathFactory;
  import com.oracle.truffle.r.runtime.builtins.RBehavior;
  import com.oracle.truffle.r.runtime.builtins.RBuiltinDescriptor;
  import com.oracle.truffle.r.runtime.conn.RConnection;
  import com.oracle.truffle.r.runtime.context.RContext;
  import com.oracle.truffle.r.runtime.data.Closure;
  import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
  import com.oracle.truffle.r.runtime.data.RAttributable;
  import com.oracle.truffle.r.runtime.data.RDataFactory;
  import com.oracle.truffle.r.runtime.data.REmpty;
  import com.oracle.truffle.r.runtime.data.RFunction;
  import com.oracle.truffle.r.runtime.data.RList;
  import com.oracle.truffle.r.runtime.data.RMissing;
  import com.oracle.truffle.r.runtime.data.RPromise;
  import com.oracle.truffle.r.runtime.data.RStringVector;
  import com.oracle.truffle.r.runtime.env.REnvironment;
  import com.oracle.truffle.r.runtime.env.frame.FrameSlotChangeMonitor;
  import com.oracle.truffle.r.runtime.interop.Foreign2R;
  import com.oracle.truffle.r.runtime.interop.R2Foreign;
  import com.oracle.truffle.r.runtime.nodes.RBaseNode;
  import com.oracle.truffle.r.runtime.nodes.RFastPathNode;
  import com.oracle.truffle.r.runtime.nodes.RNode;
  import com.oracle.truffle.r.runtime.nodes.RSyntaxCall;
  import com.oracle.truffle.r.runtime.nodes.RSyntaxElement;
  import com.oracle.truffle.r.runtime.nodes.RSyntaxLookup;
  import com.oracle.truffle.r.runtime.nodes.RSyntaxNode;
  
  /**
   * Represents and executes the syntax node for a function call.
   *
   * See {@code documentation/dev/functions.md} for the documentation of the {@link RCallNode} AST.
   */
  @NodeInfo(cost = NodeCost.NONE)
  @NodeChild(value = "function", type = RNode.class)
  public abstract class RCallNode extends RCallBaseNode implements RSyntaxNode, RSyntaxCall {
  
      // currently cannot be RSourceSectionNode because of TruffleDSL restrictions
  
      @CompilationFinal private SourceSection sourceSection;
  
      @Override
      public final void setSourceSection(SourceSection sourceSection) {
          assert sourceSection != null;
          this.sourceSection = sourceSection;
      }
  
      @Override
      public final SourceSection getLazySourceSection() {
          return sourceSection;
      }
  
      @Override
      public final SourceSection getSourceSection() {
          RDeparse.ensureSourceSection(this);
          return sourceSection;
      }
  
      @Override
      public boolean isInstrumentable() {
          // Note: we may pull this to RNode to make all nodes with INTERNAL source non-instrumentable
          return sourceSection != RSyntaxNode.INTERNAL;
      }
  
      @Override
      public RBaseNode getErrorContext() {
          return this;
      }
  
      private final RSyntaxNode[] arguments;
      private final int[] varArgIndexes;
      private final ArgumentsSignature signature;
      @Child private ReadVariableNode lookupVarArgs;
      @Child public LocalReadVariableNode explicitArgs;
  
      private final ConditionProfile nullBuiltinProfile = ConditionProfile.createBinaryProfile();
  
      // needed for INTERNAL_GENERIC calls:
      @Child private FunctionDispatch internalDispatchCall;
      @Child private GetBasicFunction getBasicFunction;
  
      private ExplicitArgs readExplicitArgs(VirtualFrame frame) {
          Object result = explicitArgs.execute(frame);
          if (result instanceof ExplicitArgs) {
              return (ExplicitArgs) result;
          } else {
              CompilerDirectives.transferToInterpreter();
              throw RInternalError.shouldNotReachHere("explicit args should always be of type ExplicitArgs, not " + result);
          }
      }
  
      protected RCaller createCaller(VirtualFrame frame, RFunction function) {
          if (explicitArgs == null) {
              return RCaller.create(frame, this);
          } else {
              RCaller explicitCallerValue = readExplicitArgs(frame).caller;
              if (explicitCallerValue != null) {
                  return explicitCallerValue;
              }
              return RCaller.create(frame, RCallerHelper.createFromArguments(function, readExplicitArgs(frame).args));
          }
      }
  
      protected Object getCallerFrame(VirtualFrame frame) {
          return explicitArgs == null ? null : readExplicitArgs(frame).callerFrame;
      }
  
      protected RCallNode(SourceSection sourceSection, RSyntaxNode[] arguments, ArgumentsSignature signature) {
          assert sourceSection != null;
          this.sourceSection = sourceSection;
          this.arguments = arguments;
          this.explicitArgs = null;
          this.varArgIndexes = getVarArgIndexes(arguments);
          this.lookupVarArgs = varArgIndexes.length == 0 ? null : ReadVariableNode.createSilent(ArgumentsSignature.VARARG_NAME, RType.Any);
          this.signature = signature;
      }
  
      protected RCallNode(SourceSection sourceSection, Object explicitArgsIdentifier) {
          assert sourceSection != null;
          this.sourceSection = sourceSection;
          this.arguments = null;
          this.explicitArgs = LocalReadVariableNode.create(explicitArgsIdentifier, false);
          this.varArgIndexes = null;
          this.lookupVarArgs = null;
          this.signature = null;
      }
  
      public static int[] getVarArgIndexes(RSyntaxNode[] arguments) {
          List<Integer> varArgsSymbolIndices = new ArrayList<>();
          for (int i = 0; i < arguments.length; i++) {
              RSyntaxNode arg = arguments[i];
              if (arg instanceof RSyntaxLookup) {
                  RSyntaxLookup lookup = (RSyntaxLookup) arg;
                  // Check for presence of "..." in the arguments
                  if (ArgumentsSignature.VARARG_NAME.equals(lookup.getIdentifier())) {
                      varArgsSymbolIndices.add(i);
                  }
              }
          }
          // Setup and return
          int[] varArgsSymbolIndicesArr = new int[varArgsSymbolIndices.size()];
          for (int i = 0; i < varArgsSymbolIndicesArr.length; i++) {
              varArgsSymbolIndicesArr[i] = varArgsSymbolIndices.get(i);
          }
          return varArgsSymbolIndicesArr;
      }
  
      @Override
      public Arguments<RSyntaxNode> getArguments() {
          return Arguments.create(arguments, signature);
      }
  
      private RArgsValuesAndNames lookupVarArgs(VirtualFrame frame, boolean ignore, RBuiltinDescriptor builtin) {
          return ignore ? null : lookupVarArgs(frame, builtin);
      }
  
      private RArgsValuesAndNames lookupVarArgs(VirtualFrame frame, RBuiltinDescriptor builtin) {
          if (explicitArgs != null) {
              return readExplicitArgs(frame).args;
          }
          if (lookupVarArgs == null) {
              return null;
          }
          if (builtin != null && !builtin.lookupVarArgs()) {
              return null;
          }
          Object varArgs = lookupVarArgs.execute(frame);
          if (!(varArgs instanceof RArgsValuesAndNames)) {
              CompilerDirectives.transferToInterpreter();
              throw RError.error(RError.SHOW_CALLER, RError.Message.NO_DOT_DOT_DOT);
          }
          return (RArgsValuesAndNames) varArgs;
      }
  
      protected FunctionDispatch createUninitializedCall() {
          return FunctionDispatchNodeGen.create(this, explicitArgs != null, null);
      }
  
      protected FunctionDispatch createUninitializedExplicitCall() {
          return FunctionDispatchNodeGen.create(this, true, null);
      }
  
      /**
       * If there are no parameters, or the target function does not refer to a builtin, or the
       * builtin has no special dispatching, then we know that we will just call the function with no
       * special dispatch logic.
       */
      protected boolean isDefaultDispatch(RFunction function) {
          return (signature != null && signature.isEmpty()) || nullBuiltinProfile.profile(function.getRBuiltin() == null) || function.getRBuiltin().getDispatch() == RDispatch.DEFAULT;
      }
  
      @Specialization
      public Object callForeign(VirtualFrame frame, DeferredFunctionValue function,
                      @Cached("createForeignInvoke()") ForeignInvoke call) {
          return call.execute(frame, function);
      }
  
      @Specialization(guards = "isDefaultDispatch(function)")
      public Object call(VirtualFrame frame, RFunction function,
                      @Cached("createUninitializedCall()") FunctionDispatch call,
                      @Cached("createIdentityProfile()") ValueProfile builtinValueProfile) {
          RBuiltinDescriptor builtin = builtinValueProfile.profile(function.getRBuiltin());
          return call.execute(frame, function, lookupVarArgs(frame, builtin), null, null);
      }
  
      protected RNode createDispatchArgument(int index) {
          return RContext.getASTBuilder().process(arguments[index]).asRNode();
      }
  
      /**
       * If the target function refers to a builtin that requires internal generic dispatch and there
       * are actual parameters to dispatch on, then we will do an internal generic dispatch on the
       * first parameter.
       */
      protected boolean isInternalGenericDispatch(RFunction function) {
          if (signature != null && signature.isEmpty()) {
              return false;
          }
          RBuiltinDescriptor builtin = function.getRBuiltin();
          return builtin != null && builtin.getDispatch() == RDispatch.INTERNAL_GENERIC;
      }
  
      @Specialization(guards = {"explicitArgs == null", "isInternalGenericDispatch(function)"})
      public Object callInternalGeneric(VirtualFrame frame, RFunction function,
                      @Cached("createDispatchArgument(0)") RNode dispatchArgument,
                      @Cached("new()") TemporarySlotNode dispatchTempSlot,
                      @Cached("create()") ClassHierarchyNode classHierarchyNode,
                      @Cached("createWithError()") S3FunctionLookupNode dispatchLookup,
                      @Cached("createIdentityProfile()") ValueProfile builtinProfile,
                      @Cached("createBinaryProfile()") ConditionProfile implicitTypeProfile,
                      @Cached("createBinaryProfile()") ConditionProfile isAttributableProfile,
                      @Cached("createBinaryProfile()") ConditionProfile resultIsBuiltinProfile,
                      @Cached("create()") GetBaseEnvFrameNode getBaseEnvFrameNode,
                      @Cached("createBinaryProfile()") ConditionProfile isS4Profile) {
  
          RBuiltinDescriptor builtin = builtinProfile.profile(function.getRBuiltin());
          Object dispatchObject = dispatchArgument.execute(frame);
          // Cannot dispatch on REmpty
          if (dispatchObject == REmpty.instance) {
              throw error(RError.Message.ARGUMENT_EMPTY, 1);
          }
  
          FrameSlot slot = dispatchTempSlot.initialize(frame, dispatchObject);
          try {
              boolean isFieldAccess = builtin.isFieldAccess();
              if (internalDispatchCall == null) {
                  createInternDispatchCall(isFieldAccess, slot);
              }
  
              if (isAttributableProfile.profile(dispatchObject instanceof RAttributable) && isS4Profile.profile(((RAttributable) dispatchObject).isS4())) {
                  if (getBasicFunction == null) {
                      CompilerDirectives.transferToInterpreterAndInvalidate();
                      getBasicFunction = insert(new GetBasicFunction());
                  }
                  Object basicFun = getBasicFunction.execute(frame, builtin.getName());
                  if (basicFun != null) {
                      Object result = internalDispatchCall.execute(frame, (RFunction) basicFun, lookupVarArgs(frame, isFieldAccess, builtin), null, null);
                      if (result != RRuntime.DEFERRED_DEFAULT_MARKER) {
                          return result;
                      }
                  }
              }
              RStringVector type = classHierarchyNode.execute(dispatchObject);
              S3Args s3Args;
              RFunction resultFunction;
              if (implicitTypeProfile.profile(type != null)) {
                  Result result = dispatchLookup.execute(frame, builtin.getGenericName(), type, null, frame.materialize(), getBaseEnvFrameNode.execute());
                  if (resultIsBuiltinProfile.profile(result.function.isBuiltin())) {
                      s3Args = null;
                  } else {
                      s3Args = result.createS3Args(frame);
                  }
                  resultFunction = result.function;
              } else {
                  // We always call the builtin even if there is "xyz.default" function.
                  // This means that the builtin can implement a fast-path of the "default" function
                  // for class-less dispatch argument values
                  // Note: e.g., "range" relies on this
                  s3Args = null;
                  resultFunction = function;
              }
              if (internalDispatchCall == null || internalDispatchCall.tempFrameSlot != slot) {
                  createInternDispatchCall(isFieldAccess, slot);
              }
              return internalDispatchCall.execute(frame, resultFunction, lookupVarArgs(frame, isFieldAccess, builtin), s3Args, null);
          } finally {
              TemporarySlotNode.cleanup(frame, dispatchObject, slot);
          }
      }
  
      private void createInternDispatchCall(boolean isFieldAccess, FrameSlot slot) {
          CompilerDirectives.transferToInterpreterAndInvalidate();
          AlteredArguments alteredArguments = null;
          if (isFieldAccess) {
              RSyntaxNode[] newArgs = Arrays.copyOf(arguments, arguments.length);
              newArgs[1] = RContext.getASTBuilder().constant(newArgs[1].getSourceSection(), CallUtils.unevaluatedArgAsFieldName(this, newArgs[1]));
              // we know that there are no varargs in the signature, but this RCallNode
              // instance could have been confused by lookup of "..." as the field, in which
              // case it would think it should lookup varargs.
              alteredArguments = new AlteredArguments(newArgs, new int[0]);
          }
          internalDispatchCall = insert(FunctionDispatchNodeGen.create(this, alteredArguments, false, slot));
      }
  
      @Specialization(guards = {"explicitArgs != null", "isInternalGenericDispatch(function)"})
      public Object callInternalGenericExplicit(VirtualFrame frame, RFunction function,
                      @Cached("create()") ClassHierarchyNode classHierarchyNode,
                      @Cached("createWithError()") S3FunctionLookupNode dispatchLookup,
                      @Cached("createIdentityProfile()") ValueProfile builtinProfile,
                      @Cached("createBinaryProfile()") ConditionProfile implicitTypeProfile,
                      @Cached("createBinaryProfile()") ConditionProfile isAttributableProfile,
                      @Cached("createBinaryProfile()") ConditionProfile resultIsBuiltinProfile,
                      @Cached("createPromiseHelper()") PromiseCheckHelperNode promiseHelperNode,
                      @Cached("createUninitializedExplicitCall()") FunctionDispatch call,
                      @Cached("createBinaryProfile()") ConditionProfile isS4Profile,
                      @Cached("create()") GetBaseEnvFrameNode getBaseEnvFrameNode) {
          RBuiltinDescriptor builtin = builtinProfile.profile(function.getRBuiltin());
          RArgsValuesAndNames argAndNames = readExplicitArgs(frame).args;
  
          RStringVector type = null;
          if (!argAndNames.isEmpty()) {
              Object dispatchObject = argAndNames.getArgument(0);
              if (isAttributableProfile.profile(dispatchObject instanceof RAttributable) && isS4Profile.profile(((RAttributable) dispatchObject).isS4())) {
                  if (getBasicFunction == null) {
                      CompilerDirectives.transferToInterpreterAndInvalidate();
                      getBasicFunction = insert(new GetBasicFunction());
                  }
                  Object basicFun = getBasicFunction.execute(frame, builtin.getName());
                  if (basicFun != null) {
                      Object result = call.execute(frame, (RFunction) basicFun, argAndNames, null, null);
                      if (result != RRuntime.DEFERRED_DEFAULT_MARKER) {
                          return result;
                      }
                  }
              }
              type = classHierarchyNode.execute(promiseHelperNode.checkVisibleEvaluate(frame, dispatchObject));
          }
  
          S3Args s3Args;
          RFunction resultFunction;
          if (implicitTypeProfile.profile(type != null)) {
              Result result = dispatchLookup.execute(frame, builtin.getName(), type, null, frame.materialize(), getBaseEnvFrameNode.execute());
              if (resultIsBuiltinProfile.profile(result.function.isBuiltin())) {
                  s3Args = null;
              } else {
                  s3Args = result.createS3Args(frame);
              }
              resultFunction = result.function;
          } else {
              s3Args = null;
              resultFunction = function;
          }
          return call.execute(frame, resultFunction, argAndNames, s3Args, null);
      }
  
      protected CallArgumentsNode createGenericDispatchArguments() {
          return signature == null ? null : createArguments(null, false, true);
      }
  
      protected boolean isGroupGenericDispatch(RFunction function) {
          if (signature != null && signature.isEmpty()) {
              return false;
          }
          RBuiltinDescriptor builtin = function.getRBuiltin();
          return builtin != null && builtin.getDispatch().isGroupGeneric();
      }
  
      protected PromiseCheckHelperNode createPromiseHelper() {
          return new PromiseCheckHelperNode();
      }
  
      @CompilationFinal private ArgumentsSignature summaryGroupSignatureCached = null;
      @CompilationFinal private boolean summaryGroupHasNaRmCached;
  
      @Specialization(guards = "isGroupGenericDispatch(function)")
      public Object callGroupGeneric(VirtualFrame frame, RFunction function,
                      @Cached("createGenericDispatchArguments()") CallArgumentsNode callArguments,
                      @Cached("create()") ClassHierarchyNode classHierarchyNodeX,
                      @Cached("createWithException()") S3FunctionLookupNode dispatchLookupX,
                      @Cached("create()") ClassHierarchyNode classHierarchyNodeY,
                      @Cached("createWithException()") S3FunctionLookupNode dispatchLookupY,
                      @Cached("createIdentityProfile()") ValueProfile builtinProfile,
                      @Cached("createBinaryProfile()") ConditionProfile emptyArgumentsProfile,
                      @Cached("createBinaryProfile()") ConditionProfile implicitTypeProfileX,
                      @Cached("createBinaryProfile()") ConditionProfile implicitTypeProfileY,
                      @Cached("createBinaryProfile()") ConditionProfile mismatchProfile,
                      @Cached("createBinaryProfile()") ConditionProfile resultIsBuiltinProfile,
                      @Cached("createBinaryProfile()") ConditionProfile summaryGroupNaRmProfile,
                      @Cached("createBinaryProfile()") ConditionProfile summaryGroupProfile,
                      @Cached("createBinaryProfile()") ConditionProfile isAttributableProfile,
                      @Cached("createBinaryProfile()") ConditionProfile isS4Profile,
                      @Cached("createCountingProfile()") LoopConditionProfile s4ArgsProfile,
                      @Cached("createPromiseHelper()") PromiseCheckHelperNode promiseHelperNode,
                      @Cached("createUninitializedExplicitCall()") FunctionDispatch call,
                      @Cached("create()") GetBaseEnvFrameNode getBaseEnvFrameNode) {
  
          RBuiltinDescriptor builtin = builtinProfile.profile(function.getRBuiltin());
          Object[] args;
          ArgumentsSignature argsSignature;
          if (explicitArgs != null) {
              ExplicitArgs explicitArgsVal = readExplicitArgs(frame);
              args = explicitArgsVal.args.getArguments();
              argsSignature = explicitArgsVal.args.getSignature();
          } else {
              RArgsValuesAndNames varArgsVal = lookupVarArgs(frame, builtin);
              args = callArguments.evaluateFlattenObjects(frame, varArgsVal);
              argsSignature = callArguments.flattenNames(varArgsVal);
          }
  
          if (emptyArgumentsProfile.profile(args.length == 0)) {
              // nothing to dispatch on, this is a valid situation, e.g. prod() == 1
              return call.execute(frame, function, new RArgsValuesAndNames(args, argsSignature), null, null);
          }
  
          RDispatch dispatch = builtin.getDispatch();
          // max(na.rm=TRUE,arg1) dispatches to whatever is class of arg1 not taking the
          // named argument 'na.rm' into account. Note: signatures should be interned, identity
          // comparison is enough. Signature length > 0, because we dispatched on at least one arg
          int typeXIdx = 0;
          if (summaryGroupNaRmProfile.profile(dispatch == RDispatch.SUMMARY_GROUP_GENERIC &&
                          Utils.identityEquals(argsSignature.getName(typeXIdx), RArguments.SUMMARY_GROUP_NA_RM_ARG_NAME))) {
              typeXIdx = 1;
          }
  
          Object dispatchObject = promiseHelperNode.checkVisibleEvaluate(frame, args[typeXIdx]);
  
          boolean isS4Dispatch = false;
  
          // CHECK FOR S4 DISPATCH
          // First, check S4 dispatch for 'dispatchObject' (= first suitable argument)
          if (isAttributableProfile.profile(dispatchObject instanceof RAttributable) && isS4Profile.profile(((RAttributable) dispatchObject).isS4())) {
              isS4Dispatch = true;
          } else if (args.length > typeXIdx + 1 && dispatch == RDispatch.OPS_GROUP_GENERIC) {
              s4ArgsProfile.profileCounted(args.length - typeXIdx);
              for (int i = typeXIdx + 1; s4ArgsProfile.inject(i < args.length); i++) {
                  Object argi = promiseHelperNode.checkEvaluate(frame, args[i]);
                  if (isAttributableProfile.profile(argi instanceof RAttributable) && isS4Profile.profile(((RAttributable) argi).isS4())) {
                      isS4Dispatch = true;
                      break;
                  }
              }
          }
  
          if (isS4Dispatch) {
              RList list = (RList) promiseHelperNode.checkVisibleEvaluate(frame, REnvironment.getRegisteredNamespace("methods").get(".BasicFunsList"));
              int index = list.getElementIndexByName(builtin.getName());
              if (index != -1) {
                  RFunction basicFun = (RFunction) list.getDataAt(index);
                  Object res = call.execute(frame, basicFun, new RArgsValuesAndNames(args, argsSignature), null, null);
                  if (res != RRuntime.DEFERRED_DEFAULT_MARKER) {
                      return res;
                  }
              }
          }
  
          RStringVector typeX = classHierarchyNodeX.execute(dispatchObject);
          Result resultX = null;
          if (implicitTypeProfileX.profile(typeX != null)) {
              resultX = dispatchLookupX.execute(frame, builtin.getName(), typeX, dispatch.getGroupGenericName(), frame.materialize(), getBaseEnvFrameNode.execute());
          }
          Result resultY = null;
          if (args.length > 1 && dispatch == RDispatch.OPS_GROUP_GENERIC) {
              RStringVector typeY = classHierarchyNodeY.execute(promiseHelperNode.checkEvaluate(frame, args[1]));
              if (implicitTypeProfileY.profile(typeY != null)) {
                  resultY = dispatchLookupY.execute(frame, builtin.getName(), typeY, dispatch.getGroupGenericName(), frame.materialize(), getBaseEnvFrameNode.execute());
              }
          }
  
          Result result;
          RStringVector dotMethod;
          if (resultX == null) {
              if (resultY == null) {
                  result = null;
                  dotMethod = null;
              } else {
                  result = resultY;
                  dotMethod = RDataFactory.createStringVector(new String[]{"", result.targetFunctionName}, true);
              }
          } else {
              if (resultY == null) {
                  result = resultX;
                  dotMethod = RDataFactory.createStringVector(new String[]{result.targetFunctionName, ""}, true);
              } else {
                  if (mismatchProfile.profile(resultX.function != resultY.function)) {
                      RError.warning(this, RError.Message.INCOMPATIBLE_METHODS, resultX.targetFunctionName, resultY.targetFunctionName, dispatch.getGroupGenericName());
                      result = null;
                      dotMethod = null;
                  } else {
                      result = resultX;
                      dotMethod = RDataFactory.createStringVector(new String[]{result.targetFunctionName, result.targetFunctionName}, true);
                  }
              }
          }
          final S3Args s3Args;
          final RFunction resultFunction;
          if (result == null) {
              s3Args = null;
              resultFunction = function;
          } else {
              if (resultIsBuiltinProfile.profile(result.function.isBuiltin())) {
                  s3Args = null;
              } else {
                  s3Args = new S3Args(builtin.getName(), result.clazz, dotMethod, frame.materialize(), null, result.groupMatch ? dispatch.getGroupGenericName() : null);
              }
              resultFunction = result.function;
          }
  
          // Note: since we are actually not executing the function prologue which would write default
          // values to local variables representing arguments with default values, we have to check
          // whether we need to provide default values ourselves. The next call invoked by the
          // call.convert statement thinks that the formal signature is formal signature of the
          // concrete method, e.g. max.data.frame, which may not have default value for the same
          // arguments as the entry method, e.g. max(...,na.rm=TRUE). Unfortunately, in GnuR this is
          // inconsistent and only applies for 'Summary' group and not, for example, for Math group
          // with its default value for 'digits'
          S3DefaultArguments s3DefaulArguments = null;
          if (summaryGroupProfile.profile(dispatch == RDispatch.SUMMARY_GROUP_GENERIC)) {
              if (argsSignature != summaryGroupSignatureCached) {
                  CompilerDirectives.transferToInterpreterAndInvalidate();
                  summaryGroupSignatureCached = argsSignature;
                  summaryGroupHasNaRmCached = false;
                  for (int i = 0; i < argsSignature.getLength(); i++) {
                      if (Utils.identityEquals(argsSignature.getName(i), RArguments.SUMMARY_GROUP_NA_RM_ARG_NAME)) {
                          summaryGroupHasNaRmCached = true;
                          break;
                      }
                  }
              }
              if (!summaryGroupHasNaRmCached) {
                  s3DefaulArguments = RArguments.SUMMARY_GROUP_DEFAULT_VALUE_NA_RM;
              }
          }
  
          return call.execute(frame, resultFunction, new RArgsValuesAndNames(args, argsSignature), s3Args, s3DefaulArguments);
      }
  
      protected abstract static class ForeignCall extends LeafCallNode {
          @Child private CallArgumentsNode arguments;
          @Child private Foreign2R foreign2RNode;
  
          protected ForeignCall(RCallNode originalCall, CallArgumentsNode arguments) {
              super(originalCall);
              this.arguments = arguments;
          }
  
          protected Object[] evaluateArgs(VirtualFrame frame) {
              return originalCall.explicitArgs != null ? originalCall.readExplicitArgs(frame).args.getArguments()
                              : arguments.evaluateFlattenObjects(frame, originalCall.lookupVarArgs(frame, null));
          }
  
          protected Foreign2R getForeign2RNode() {
              if (foreign2RNode == null) {
                  CompilerDirectives.transferToInterpreterAndInvalidate();
                  foreign2RNode = insert(Foreign2R.create());
              }
              return foreign2RNode;
          }
      }
  
      protected static final class ForeignExecute extends ForeignCall {
          @Child private SendForeignExecuteMessage sendExecuteMessage = SendForeignExecuteMessageNodeGen.create();
  
          protected ForeignExecute(RCallNode originalCall, CallArgumentsNode arguments) {
              super(originalCall, arguments);
          }
  
          public Object execute(VirtualFrame frame, TruffleObject function) {
              return getForeign2RNode().convert(sendExecuteMessage.execute(function, evaluateArgs(frame)));
          }
      }
  
      protected static final class ForeignInvoke extends ForeignCall {
          @Child private SendForeignInvokeMessage sendInvokeMessage = SendForeignInvokeMessageNodeGen.create();
  
          protected ForeignInvoke(RCallNode originalCall, CallArgumentsNode arguments) {
              super(originalCall, arguments);
          }
  
          public Object execute(VirtualFrame frame, DeferredFunctionValue function) {
              return getForeign2RNode().convert(sendInvokeMessage.execute(function, evaluateArgs(frame)));
          }
      }
  
      @ImportStatic(DSLConfig.class)
      protected abstract static class SendForeignMessageBase extends Node {
          @Child private R2Foreign r2ForeignNode;
  
          protected Object[] args2Foreign(Object[] args) {
              if (r2ForeignNode == null) {
                  CompilerDirectives.transferToInterpreterAndInvalidate();
                  r2ForeignNode = insert(R2Foreign.create());
              }
              for (int i = 0; i < args.length; i++) {
                  args[i] = r2ForeignNode.convert(args[i]);
              }
              return args;
          }
      }
  
      /**
       * Calls a foreign function using message EXECUTE.
       */
      protected abstract static class SendForeignExecuteMessage extends SendForeignMessageBase {
  
          public abstract Object execute(TruffleObject function, Object[] args);
  
          @Specialization(guards = "argumentsArray.length == foreignCallArgCount", limit = "getCacheSize(8)")
          protected Object doCached(TruffleObject function, Object[] argumentsArray,
                          @CachedLibrary("function") InteropLibrary interop,
                          @Cached("argumentsArray.length") @SuppressWarnings("unused") int foreignCallArgCount) {
              try {
                  return interop.execute(function, args2Foreign(argumentsArray));
              } catch (ArityException | UnsupportedMessageException | UnsupportedTypeException e) {
                  CompilerDirectives.transferToInterpreter();
                  RInternalError.reportError(e);
                  throw RError.interopError(RError.findParentRBase(this), e, function);
              } catch (RuntimeException e) {
                  throw RErrorHandling.handleInteropException(this, e);
              }
          }
  
          @Specialization(replaces = "doCached")
          @TruffleBoundary
          protected Object doGeneric(TruffleObject function, Object[] argumentsArray) {
              return doCached(function, argumentsArray, InteropLibrary.getFactory().getUncached(), argumentsArray.length);
          }
      }
  
      /**
       * Calls a foreign function using message INVOKE.
       */
      protected abstract static class SendForeignInvokeMessage extends SendForeignMessageBase {
  
          public abstract Object execute(DeferredFunctionValue function, Object[] args);
  
          @Specialization(guards = "argumentsArray.length == foreignCallArgCount", limit = "getCacheSize(8)")
          protected Object doCached(DeferredFunctionValue lhs, Object[] argumentsArray,
                          @CachedLibrary("lhs.getLHSReceiver()") InteropLibrary interop,
                          @Cached("argumentsArray.length") @SuppressWarnings("unused") int foreignCallArgCount) {
              TruffleObject receiver = lhs.getLHSReceiver();
              String member = lhs.getLHSMember();
              try {
                  return interop.invokeMember(receiver, member, args2Foreign(argumentsArray));
              } catch (ArityException | UnsupportedMessageException | UnsupportedTypeException | UnknownIdentifierException e) {
                  CompilerDirectives.transferToInterpreter();
                  RInternalError.reportError(e);
                  throw RError.interopError(RError.findParentRBase(this), e, receiver);
              }
          }
  
          @Specialization(replaces = "doCached")
          @TruffleBoundary
          protected Object doGeneric(DeferredFunctionValue lhs, Object[] argumentsArray) {
              return doCached(lhs, argumentsArray, InteropLibrary.getFactory().getUncached(), argumentsArray.length);
          }
      }
  
  