From 24c3374020390f4851a7f1e6bc54c66b5430e121 Mon Sep 17 00:00:00 2001 From: stepan <stepan.sindelar@oracle.com> Date: Thu, 1 Feb 2018 14:31:19 +0100 Subject: [PATCH] Rework DoCall builtin do use RExplicitCall and to not create new AST nodes every time --- .../truffle/r/engine/interop/RFunctionMR.java | 6 +- .../truffle/r/library/fastrGrid/ViewPort.java | 2 +- .../truffle/r/nodes/builtin/base/Bind.java | 2 +- .../truffle/r/nodes/builtin/base/DoCall.java | 208 +++++++++--------- .../r/nodes/builtin/base/ForceAndCall.java | 4 +- .../r/nodes/builtin/base/FrameFunctions.java | 38 ++-- .../truffle/r/nodes/builtin/base/Mapply.java | 4 +- .../truffle/r/nodes/builtin/base/Recall.java | 4 +- .../r/nodes/builtin/fastr/FastRInterop.java | 2 +- .../r/nodes/builtin/fastr/FastRTestsTry.java | 2 +- .../r/nodes/access/BaseWriteVariableNode.java | 4 +- .../variables/LocalReadVariableNode.java | 4 +- .../access/variables/ReadVariableNode.java | 4 +- .../truffle/r/nodes/function/RCallNode.java | 20 +- .../call/RExplicitBaseEnvCallDispatcher.java | 4 +- .../function/call/RExplicitCallNode.java | 27 ++- .../com/oracle/truffle/r/runtime/RCaller.java | 27 ++- .../truffle/r/test/ExpectedTestOutput.test | 39 ++++ .../r/test/builtins/TestBuiltin_docall.java | 2 +- .../r/test/builtins/TestBuiltin_sysframe.java | 15 +- .../library/utils/TestInteractiveDebug.java | 5 + 21 files changed, 266 insertions(+), 157 deletions(-) diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RFunctionMR.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RFunctionMR.java index 36d76a99b6..47890c2305 100644 --- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RFunctionMR.java +++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RFunctionMR.java @@ -39,6 +39,7 @@ import com.oracle.truffle.r.runtime.RArguments; import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames; import com.oracle.truffle.r.runtime.data.RFunction; import com.oracle.truffle.r.runtime.data.RObject; +import com.oracle.truffle.r.runtime.data.RNull; import com.oracle.truffle.r.runtime.env.frame.FrameSlotChangeMonitor; import com.oracle.truffle.r.runtime.env.frame.RFrameSlot; import com.oracle.truffle.r.runtime.interop.Foreign2R; @@ -71,13 +72,15 @@ public class RFunctionMR { private static final FrameDescriptor emptyFrameDescriptor = FrameSlotChangeMonitor.initializeFunctionFrameDescriptor("<interop>", new FrameDescriptor("R interop frame")); private static final RFrameSlot argsIdentifier = RFrameSlot.createTemp(false); + private static final RFrameSlot explicitCallerId = RFrameSlot.createTemp(false); private static final FrameSlot slot = FrameSlotChangeMonitor.findOrAddFrameSlot(emptyFrameDescriptor, argsIdentifier, FrameSlotKind.Object); + private static final FrameSlot slotCaller = FrameSlotChangeMonitor.findOrAddFrameSlot(emptyFrameDescriptor, explicitCallerId, FrameSlotKind.Object); static { FrameSlotChangeMonitor.initializeFunctionFrameDescriptor("<function>", emptyFrameDescriptor); } - @Child private RCallBaseNode call = RCallNode.createExplicitCall(argsIdentifier); + @Child private RCallBaseNode call = RCallNode.createExplicitCall(argsIdentifier, explicitCallerId); protected Object access(RFunction receiver, Object[] arguments) { Object[] dummyFrameArgs = RArguments.createUnitialized(); @@ -90,6 +93,7 @@ public class RFunctionMR { RArgsValuesAndNames actualArgs = new RArgsValuesAndNames(convertedArguments, ArgumentsSignature.empty(arguments.length)); try { FrameSlotChangeMonitor.setObject(dummyFrame, slot, actualArgs); + FrameSlotChangeMonitor.setObject(dummyFrame, slotCaller, RNull.instance); Object value = call.execute(dummyFrame, receiver); return r2Foreign.execute(value); } finally { diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/ViewPort.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/ViewPort.java index b1c90a3a02..6d1772a79b 100644 --- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/ViewPort.java +++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/ViewPort.java @@ -151,7 +151,7 @@ public final class ViewPort { public RList execute(VirtualFrame frame) { RFunction gridTopLevel = (RFunction) readGridTopLevel.execute(frame); - RList topVP = (RList) callNode.execute(frame, gridTopLevel, RArgsValuesAndNames.EMPTY); + RList topVP = (RList) callNode.call(frame, gridTopLevel, RArgsValuesAndNames.EMPTY); topVP.makeSharedPermanent(); GridDevice device = GridContext.getContext().getCurrentDevice(); diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Bind.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Bind.java index 165ad928ea..00894b2b3b 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Bind.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Bind.java @@ -585,7 +585,7 @@ public abstract class Bind extends RBaseNode { CompilerDirectives.transferToInterpreterAndInvalidate(); dispatchCallNode = insert(RExplicitCallNode.create()); } - return dispatchCallNode.execute(frame, dispatchFunction, (RArgsValuesAndNames) RArguments.getArgument(frame, 0)); + return dispatchCallNode.call(frame, dispatchFunction, (RArgsValuesAndNames) RArguments.getArgument(frame, 0)); } else { if (bind == null) { CompilerDirectives.transferToInterpreterAndInvalidate(); diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DoCall.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DoCall.java index fed4396ff0..61ef2ee947 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DoCall.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DoCall.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2018, 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 @@ -23,14 +23,19 @@ package com.oracle.truffle.r.nodes.builtin.base; import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.instanceOf; +import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.returnIf; import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.stringValue; import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.toBoolean; import static com.oracle.truffle.r.runtime.RVisibility.CUSTOM; import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX; +import java.util.function.Supplier; + +import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.api.frame.FrameDescriptor; import com.oracle.truffle.api.frame.FrameSlot; import com.oracle.truffle.api.frame.FrameSlotKind; import com.oracle.truffle.api.frame.FrameSlotTypeException; @@ -40,27 +45,26 @@ import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.profiles.BranchProfile; import com.oracle.truffle.api.profiles.ConditionProfile; import com.oracle.truffle.api.profiles.ValueProfile; -import com.oracle.truffle.r.nodes.RASTUtils; -import com.oracle.truffle.r.nodes.access.ConstantNode; import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.GetNamesAttributeNode; import com.oracle.truffle.r.nodes.builtin.RBuiltinNode; import com.oracle.truffle.r.nodes.builtin.base.DoCallNodeGen.DoCallInternalNodeGen; import com.oracle.truffle.r.nodes.builtin.base.GetFunctions.Get; import com.oracle.truffle.r.nodes.builtin.base.GetFunctionsFactory.GetNodeGen; import com.oracle.truffle.r.nodes.function.RCallerHelper; -import com.oracle.truffle.r.nodes.function.opt.ShareObjectNode; +import com.oracle.truffle.r.nodes.function.call.RExplicitCallNode; +import com.oracle.truffle.r.nodes.function.visibility.GetVisibilityNode; import com.oracle.truffle.r.nodes.function.visibility.SetVisibilityNode; +import com.oracle.truffle.r.nodes.profile.TruffleBoundaryNode; import com.oracle.truffle.r.runtime.ArgumentsSignature; import com.oracle.truffle.r.runtime.RArguments; import com.oracle.truffle.r.runtime.RCaller; -import com.oracle.truffle.r.runtime.RDispatch; import com.oracle.truffle.r.runtime.RError; import com.oracle.truffle.r.runtime.RError.Message; 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.VirtualEvalFrame; import com.oracle.truffle.r.runtime.builtins.RBuiltin; -import com.oracle.truffle.r.runtime.builtins.RBuiltinDescriptor; import com.oracle.truffle.r.runtime.builtins.RBuiltinKind; import com.oracle.truffle.r.runtime.context.RContext; import com.oracle.truffle.r.runtime.data.Closure; @@ -79,6 +83,7 @@ import com.oracle.truffle.r.runtime.env.REnvironment; import com.oracle.truffle.r.runtime.env.frame.FrameSlotChangeMonitor; import com.oracle.truffle.r.runtime.env.frame.RFrameSlot; import com.oracle.truffle.r.runtime.nodes.InternalRSyntaxNodeChildren; +import com.oracle.truffle.r.runtime.nodes.RSyntaxElement; import com.oracle.truffle.r.runtime.nodes.RSyntaxNode; @RBuiltin(name = ".fastr.do.call", visibility = CUSTOM, kind = RBuiltinKind.INTERNAL, parameterNames = {"what", "args", "quote", "envir"}, behavior = COMPLEX) @@ -119,8 +124,9 @@ public abstract class DoCall extends RBuiltinNode.Arg4 implements InternalRSynta } protected abstract static class DoCallInternal extends Node { - @Child private GetNamesAttributeNode getNamesNode = GetNamesAttributeNode.create(); - @Child private SetVisibilityNode setVisibilityNode = SetVisibilityNode.create(); + @Child private GetNamesAttributeNode getNamesNode; + @Child private SetVisibilityNode setVisibilityNode; + private final ValueProfile frameAccessProfile = ValueProfile.createClassProfile(); public static DoCallInternal create() { return DoCallInternalNodeGen.create(); @@ -128,23 +134,80 @@ public abstract class DoCall extends RBuiltinNode.Arg4 implements InternalRSynta public abstract Object execute(VirtualFrame virtualFrame, RFunction func, RList argsAsList, boolean quote, REnvironment env); - protected final ArgumentsSignature getArgsNames(RList argsAsList) { - ArgumentsSignature signature = ArgumentsSignature.fromNamesAttribute(getNamesNode.getNames(argsAsList)); - return signature == null ? ArgumentsSignature.empty(argsAsList.getLength()) : signature; + protected FrameDescriptor getFrameDescriptor(REnvironment env) { + return env.getFrame(frameAccessProfile).getFrameDescriptor(); } /** - * Fast version that works only for simple cases. It does not explicitly create the AST and - * evaluate it, but instead it directly implements what the execution of such AST would do. + * Because the underlying AST in {@link RExplicitCallNode} may cache frame slots, i.e. + * expect the {@link FrameDescriptor} to never change, we're caching this AST and also + * {@link GetVisibilityNode} for each {@link FrameDescriptor} we encounter. */ - @Specialization(guards = "isSimple(func, argsAsList)") - public Object doSimple(VirtualFrame virtualFrame, RFunction func, RList argsAsList, boolean quote, REnvironment env, - @Cached("create()") ShareObjectNode shareObjectNode, + @Specialization(guards = {"getFrameDescriptor(env) == fd"}, limit = "20") + public Object doFastPath(VirtualFrame virtualFrame, RFunction func, RList argsAsList, boolean quote, REnvironment env, + @Cached("getFrameDescriptor(env)") @SuppressWarnings("unused") FrameDescriptor fd, + @Cached("create()") RExplicitCallNode explicitCallNode, + @Cached("create()") GetVisibilityNode getVisibilityNode, @Cached("createBinaryProfile()") ConditionProfile quoteProfile, - @Cached("create()") BranchProfile containsRSymbolProfile, - @Cached("createClassProfile()") ValueProfile frameAccessProfile) { + @Cached("create()") BranchProfile containsRSymbolProfile) { + MaterializedFrame promiseFrame = env.getFrame(frameAccessProfile).materialize(); + RArgsValuesAndNames args = getArguments(promiseFrame, quote, quoteProfile, containsRSymbolProfile, argsAsList); + RCaller caller = getExplicitCaller(virtualFrame, promiseFrame, func, args); + MaterializedFrame evalFrame = getEvalFrame(virtualFrame, promiseFrame); + + Object resultValue = explicitCallNode.execute(evalFrame, func, args, caller); + setVisibility(virtualFrame, getVisibilityNode.execute(evalFrame)); + return resultValue; + } + + /** + * Slow-path version avoids the problem by creating {@link RExplicitCallNode} for every call + * again and again and putting it behind truffle boundary to avoid deoptimization. + */ + @Specialization(replaces = "doFastPath") + public Object doSlowPath(VirtualFrame virtualFrame, RFunction func, RList argsAsList, boolean quote, REnvironment env, + @Cached("create()") SlowPathExplicitCall slowPathExplicitCall, + @Cached("createBinaryProfile()") ConditionProfile quoteProfile, + @Cached("create()") BranchProfile containsRSymbolProfile) { + MaterializedFrame promiseFrame = env.getFrame(frameAccessProfile).materialize(); + RArgsValuesAndNames args = getArguments(promiseFrame, quote, quoteProfile, containsRSymbolProfile, argsAsList); + RCaller caller = getExplicitCaller(virtualFrame, promiseFrame, func, args); + MaterializedFrame evalFrame = getEvalFrame(virtualFrame, promiseFrame); + + Object resultValue = slowPathExplicitCall.execute(evalFrame, caller, func, args); + setVisibility(virtualFrame, getVisibilitySlowPath(evalFrame)); + return resultValue; + } + + /** + * The contract is that the function call will be evaluated in the given environment, but at + * the same time some primitives expect to see {@code do.call(foo, ...)} as the caller, so + * we create a frame the fakes caller, but otherwise delegates to the frame backing the + * explicitly given environment. + */ + private MaterializedFrame getEvalFrame(VirtualFrame virtualFrame, MaterializedFrame envFrame) { + return VirtualEvalFrame.create(envFrame, RArguments.getFunction(virtualFrame), RArguments.getCall(virtualFrame)); + } + + /** + * If the call leads to actual call via + * {@link com.oracle.truffle.r.nodes.function.call.CallRFunctionNode}, which creates new + * frame and new set of arguments for it, then for this new arguments we explicitly provide + * a caller that looks like the function was called from the explicitly given environment + * (it will be its parent call), but at the same time its depth is one above the do.call + * function that actually invoked it. + * + * @see RCaller + * @see RArguments + */ + private RCaller getExplicitCaller(VirtualFrame virtualFrame, MaterializedFrame envFrame, RFunction func, RArgsValuesAndNames args) { + Supplier<RSyntaxElement> callerSyntax = RCallerHelper.createFromArguments(func, args); + return RCaller.create(RArguments.getDepth(virtualFrame) + 1, RArguments.getCall(envFrame), callerSyntax); + } + + private RArgsValuesAndNames getArguments(MaterializedFrame promiseFrame, boolean quote, ConditionProfile quoteProfile, + BranchProfile containsRSymbolProfile, RList argsAsList) { Object[] argValues = argsAsList.getDataCopy(); - MaterializedFrame envFrame = env.getFrame(frameAccessProfile).materialize(); if (quoteProfile.profile(!quote)) { for (int i = 0; i < argValues.length; i++) { Object arg = argValues[i]; @@ -154,43 +217,32 @@ public abstract class DoCall extends RBuiltinNode.Arg4 implements InternalRSynta if (symbol.getName().isEmpty()) { argValues[i] = REmpty.instance; } else { - argValues[i] = createLookupPromise(envFrame, symbol); + argValues[i] = createLookupPromise(promiseFrame, symbol); } + } else if (arg instanceof RLanguage) { + argValues[i] = RDataFactory.createPromise(PromiseState.Default, Closure.createPromiseClosure(((RLanguage) arg).getRep()), promiseFrame); } } } - for (int i = 0; i < argValues.length; i++) { - shareObjectNode.execute(argValues[i]); - } ArgumentsSignature signature = getArgsNames(argsAsList); - RCaller caller = RCaller.createWithInternalParent(virtualFrame, RCallerHelper.createFromArguments(func, new RArgsValuesAndNames(argValues, signature))); - try { - Object resultValue = RContext.getEngine().evalFunction(func, envFrame, caller, false, signature, argValues); - setVisibilityNode.execute(virtualFrame, getVisibility(envFrame)); - return resultValue; - } finally { - for (int i = 0; i < argValues.length; i++) { - ShareObjectNode.unshare(argValues[i]); - } - } + return new RArgsValuesAndNames(argValues, signature); } - protected static boolean isSimple(RFunction function, RList args) { - RBuiltinDescriptor builtin = function.getRBuiltin(); - if (builtin != null && builtin.getDispatch() != RDispatch.DEFAULT) { - return false; + private void setVisibility(VirtualFrame frame, boolean value) { + if (setVisibilityNode == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + setVisibilityNode = insert(SetVisibilityNode.create()); } - for (int i = 0; i < args.getLength(); i++) { - if (args.getDataAt(i) instanceof RLanguage) { - // Note: language is tricky because of formulae, which are language that is - // really not meant to be evaluated again in a different frame than the one were - // the were evaluated for the first time. The solution should be to clone the - // language's rep and get its nodes in uninitilized state, but that does not - // work for some reason. - return false; - } + setVisibilityNode.execute(frame, value); + } + + private ArgumentsSignature getArgsNames(RList argsAsList) { + if (getNamesNode == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + getNamesNode = insert(GetNamesAttributeNode.create()); } - return true; + ArgumentsSignature signature = ArgumentsSignature.fromNamesAttribute(getNamesNode.getNames(argsAsList)); + return signature == null ? ArgumentsSignature.empty(argsAsList.getLength()) : signature; } @TruffleBoundary @@ -200,7 +252,7 @@ public abstract class DoCall extends RBuiltinNode.Arg4 implements InternalRSynta } @TruffleBoundary - private static boolean getVisibility(MaterializedFrame envFrame) { + private static boolean getVisibilitySlowPath(MaterializedFrame envFrame) { FrameSlot envVisibilitySlot = FrameSlotChangeMonitor.findOrAddFrameSlot(envFrame.getFrameDescriptor(), RFrameSlot.Visibility, FrameSlotKind.Boolean); if (envVisibilitySlot != null) { try { @@ -211,62 +263,18 @@ public abstract class DoCall extends RBuiltinNode.Arg4 implements InternalRSynta } return false; } + } - @Specialization(guards = "!isSimple(func, argsAsList)") - public Object doGeneric(VirtualFrame virtualFrame, RFunction func, RList argsAsList, boolean quote, REnvironment env) { - CallResult result = doCallGeneric(func, argsAsList.getDataWithoutCopying(), getArgsNames(argsAsList), quote, RArguments.getCall(virtualFrame), env); - setVisibilityNode.execute(virtualFrame, result.visibility); - return result.value; - } + static class SlowPathExplicitCall extends TruffleBoundaryNode { + @Child private RExplicitCallNode slowPathCallNode; - @TruffleBoundary - private static CallResult doCallGeneric(RFunction function, Object[] argValues, ArgumentsSignature argsSignature, boolean quote, RCaller call, REnvironment env) { - RSyntaxNode[] argsConstants = new RSyntaxNode[argValues.length]; - for (int i = 0; i < argValues.length; i++) { - if (!quote && argValues[i] instanceof RLanguage) { - argsConstants[i] = ((RLanguage) argValues[i]).getRep().asRSyntaxNode(); - } else if (!quote && argValues[i] instanceof RSymbol) { - RSymbol symbol = (RSymbol) argValues[i]; - if (symbol.isMissing()) { - argsConstants[i] = ConstantNode.create(REmpty.instance); - } else { - argsConstants[i] = RContext.getASTBuilder().lookup(RSyntaxNode.LAZY_DEPARSE, ((RSymbol) argValues[i]).getName(), false); - } - } else { - argsConstants[i] = ConstantNode.create(argValues[i]); - } - } - for (int i = 0; i < argValues.length; i++) { - ShareObjectNode.share(argValues[i]); - } - Closure closure = Closure.createLanguageClosure(RASTUtils.createCall(ConstantNode.create(function), true, argsSignature, argsConstants).asRNode()); - RLanguage lang = RDataFactory.createLanguage(closure); - try { - Object resultValue = RContext.getEngine().eval(lang, env, call.withInternalParent()); - MaterializedFrame envFrame = env.getFrame(); - FrameSlot envVisibilitySlot = FrameSlotChangeMonitor.findOrAddFrameSlot(envFrame.getFrameDescriptor(), RFrameSlot.Visibility, FrameSlotKind.Boolean); - boolean resultVisibility = false; - if (envVisibilitySlot != null) { - resultVisibility = envFrame.getBoolean(envVisibilitySlot); - } - return new CallResult(resultValue, resultVisibility); - } catch (FrameSlotTypeException e) { - throw RInternalError.shouldNotReachHere(); - } finally { - for (int i = 0; i < argValues.length; i++) { - ShareObjectNode.unshare(argValues[i]); - } - } + public static SlowPathExplicitCall create() { + return new SlowPathExplicitCall(); } - private static final class CallResult { - public final Object value; - public final boolean visibility; - - CallResult(Object value, boolean visibility) { - this.value = value; - this.visibility = visibility; - } + public Object execute(VirtualFrame evalFrame, RCaller caller, RFunction func, RArgsValuesAndNames args) { + slowPathCallNode = insert(RExplicitCallNode.create()); + return slowPathCallNode.execute(evalFrame, func, args, caller); } } } diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ForceAndCall.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ForceAndCall.java index 3cef036d53..0a315155bb 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ForceAndCall.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ForceAndCall.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2018, 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 @@ -60,7 +60,7 @@ public abstract class ForceAndCall extends RBuiltinNode.Arg3 { if (!fun.isBuiltin()) { flattenFirstArgs(frame, cachedN, args); } - return call.execute(frame, fun, args); + return call.call(frame, fun, args); } @ExplodeLoop diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/FrameFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/FrameFunctions.java index e15ff3334c..15afd7016c 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/FrameFunctions.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/FrameFunctions.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2018, 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 @@ -89,6 +89,16 @@ import com.oracle.truffle.r.runtime.nodes.RSyntaxNode; * The frame for the sys functions themselves is not counted in the R spec. Frames are numbered 0, * 1, .. starting from .GlobalEnv. Non-negative arguments are frame numbers, negative arguments are * relative to the current frame. + * + * The main thing to note is distinction between {@code sys.parent} and {@code sys.frame}. + * {@code sys.parent} gives you "logical" parent, but not necessarily the frame preceding the + * current frame on the call stack. In FastR this is captured by {@link RCaller#getParent()}. + * {@code sys.frame(n)} gives you frame with number {@code n} and traverses the stack without taking + * the parent relation into account. In FastR the frame number is captured in + * {@link RCaller#getDepth()}. See also builtins in {@code FrameFunctions} for more details. + * + * @see RArguments + * @see RCaller */ public class FrameFunctions { @@ -112,19 +122,11 @@ public class FrameFunctions { return RInternalError.guaranteeNonNull(getNumberedFrame(frame, actualFrame)); } - protected RCaller getCall(RCaller currentCall, int n) { + protected RCaller getCall(VirtualFrame frame, int n) { + RCaller currentCall = RArguments.getCall(frame); int actualFrame = decodeFrameNumber(currentCall, n); - RCaller call = currentCall; - while (call != null) { - while (call.isPromise()) { - call = call.getPromiseOriginalCall(); - } - if (call.getDepth() == actualFrame) { - return call; - } - call = call.getParent(); - } - throw RInternalError.shouldNotReachHere(); + Frame targetFrame = getNumberedFrame(frame, actualFrame); + return RArguments.getCall(targetFrame); } /** @@ -197,13 +199,12 @@ public class FrameFunctions { /* * sys.call preserves provided names but does not create them, unlike match.call. */ - return createCall(RArguments.getCall(frame), which); + return createCall(helper.getCall(frame, which)); } @TruffleBoundary - private Object createCall(RCaller currentCall, int which) { - RCaller call = helper.getCall(currentCall, which); - assert !call.isPromise(); + private Object createCall(RCaller call) { + assert call == null || !call.isPromise(); if (call == null || !call.isValidCaller()) { return RNull.instance; } @@ -672,9 +673,6 @@ public class FrameFunctions { } int i = 0; while (i < n) { - if (call.hasInternalParent()) { - i--; // in this loop iteration, we deal with the parent, but do not count it - } call = call.getParent(); if (call == null) { nullCallerProfile.enter(); diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Mapply.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Mapply.java index dc3a03b8f5..50a3c846eb 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Mapply.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Mapply.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2018, 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 @@ -227,7 +227,7 @@ public abstract class Mapply extends RBuiltinNode.Arg3 { values[listIndex] = vecElement; } /* Now call the function */ - result[i] = callNode.execute(frame, function, new RArgsValuesAndNames(values, signature)); + result[i] = callNode.call(frame, function, new RArgsValuesAndNames(values, signature)); } return result; } diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Recall.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Recall.java index 5d82e00576..4b9b9eaf9c 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Recall.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Recall.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2018, 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 @@ -68,6 +68,6 @@ public abstract class Recall extends RBuiltinNode.Arg1 { * builtin looks at the arguments passed to the surrounding function. */ RArgsValuesAndNames actualArgs = (RArgsValuesAndNames) readArgs.execute(frame); - return call.execute(frame, function, actualArgs); + return call.call(frame, function, actualArgs); } } diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRInterop.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRInterop.java index 300ec8e7de..8da59509ff 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRInterop.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRInterop.java @@ -973,7 +973,7 @@ public class FastRInterop { boolean isCheck = RRuntime.fromLogical(check); getInteropTryState().stepIn(); try { - return call.execute(frame, function, RArgsValuesAndNames.EMPTY); + return call.call(frame, function, RArgsValuesAndNames.EMPTY); } catch (FastRInteropTryException e) { Throwable cause = e.getCause(); CompilerDirectives.transferToInterpreter(); diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRTestsTry.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRTestsTry.java index 7b7a63ecee..98c3abe8dc 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRTestsTry.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRTestsTry.java @@ -55,7 +55,7 @@ public abstract class FastRTestsTry extends RBuiltinNode.Arg1 { @Specialization public Object tryFunc(VirtualFrame frame, RFunction func) { try { - call.execute(frame, func, RArgsValuesAndNames.EMPTY); + call.call(frame, func, RArgsValuesAndNames.EMPTY); } catch (Throwable ex) { // try to recover from a possibly incosistent state when running tests: // some handlers might still be lying around and interfere with subsequent calls diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/BaseWriteVariableNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/BaseWriteVariableNode.java index 6db88a49aa..53db2b3f5b 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/BaseWriteVariableNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/BaseWriteVariableNode.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2018, 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 @@ -190,7 +190,7 @@ abstract class BaseWriteVariableNode extends WriteVariableNode { } ActiveBinding binding = (ActiveBinding) object; try { - return writeActiveBinding.execute(execFrame, binding.getFunction(), new RArgsValuesAndNames(new Object[]{value}, ArgumentsSignature.empty(1))); + return writeActiveBinding.call(execFrame, binding.getFunction(), new RArgsValuesAndNames(new Object[]{value}, ArgumentsSignature.empty(1))); } finally { binding.setInitialized(true); } diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/variables/LocalReadVariableNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/variables/LocalReadVariableNode.java index f3e42feefc..f67c254ce6 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/variables/LocalReadVariableNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/variables/LocalReadVariableNode.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2018, 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 @@ -131,7 +131,7 @@ public final class LocalReadVariableNode extends Node { if (binding.isHidden() && !binding.isInitialized()) { return null; } - Object readValue = readActiveBinding.execute(frame, binding.getFunction(), RArgsValuesAndNames.EMPTY); + Object readValue = readActiveBinding.call(frame, binding.getFunction(), RArgsValuesAndNames.EMPTY); if (readValue == RMissing.instance) { return null; } diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/variables/ReadVariableNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/variables/ReadVariableNode.java index 74e6cfc821..7104433d3e 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/variables/ReadVariableNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/variables/ReadVariableNode.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2018, 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 @@ -275,7 +275,7 @@ public final class ReadVariableNode extends RBaseNode { if (binding.isHidden() && !binding.isInitialized()) { throw error(mode == RType.Function ? RError.Message.UNKNOWN_FUNCTION : RError.Message.UNKNOWN_OBJECT, identifier); } - Object readValue = readActiveBinding.execute(frame, binding.getFunction(), RArgsValuesAndNames.EMPTY); + Object readValue = readActiveBinding.call(frame, binding.getFunction(), RArgsValuesAndNames.EMPTY); if (readValue == RMissing.instance) { throw error(mode == RType.Function ? RError.Message.UNKNOWN_FUNCTION : RError.Message.UNKNOWN_OBJECT, identifier); } diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/RCallNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/RCallNode.java index 8fd61c7ef5..43e94209b9 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/RCallNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/RCallNode.java @@ -85,7 +85,6 @@ 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.SubstituteVirtualFrame; import com.oracle.truffle.r.runtime.builtins.FastPathFactory; import com.oracle.truffle.r.runtime.builtins.RBuiltinDescriptor; import com.oracle.truffle.r.runtime.conn.RConnection; @@ -98,6 +97,7 @@ 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.RNull; import com.oracle.truffle.r.runtime.data.RPromise; import com.oracle.truffle.r.runtime.data.RStringVector; import com.oracle.truffle.r.runtime.env.REnvironment; @@ -150,6 +150,8 @@ public abstract class RCallNode extends RCallBaseNode implements RSyntaxNode, RS @Child private ReadVariableNode lookupVarArgs; protected final LocalReadVariableNode explicitArgs; + @Child public LocalReadVariableNode explicitCaller; + private final ConditionProfile nullBuiltinProfile = ConditionProfile.createBinaryProfile(); // needed for INTERNAL_GENERIC calls: @@ -160,6 +162,10 @@ public abstract class RCallNode extends RCallBaseNode implements RSyntaxNode, RS if (explicitArgs == null) { return RCaller.create(frame, this); } else { + Object explicitCallerValue = explicitCaller.execute(frame); + if (explicitCallerValue != RNull.instance) { + return (RCaller) explicitCallerValue; + } return RCaller.create(frame, RCallerHelper.createFromArguments(function, (RArgsValuesAndNames) explicitArgs.execute(frame))); } } @@ -174,11 +180,12 @@ public abstract class RCallNode extends RCallBaseNode implements RSyntaxNode, RS this.signature = signature; } - protected RCallNode(SourceSection sourceSection, Object explicitArgsIdentifier) { + protected RCallNode(SourceSection sourceSection, Object explicitArgsIdentifier, Object explicitCallerIdentifier) { assert sourceSection != null; this.sourceSection = sourceSection; this.arguments = null; this.explicitArgs = LocalReadVariableNode.create(explicitArgsIdentifier, false); + this.explicitCaller = LocalReadVariableNode.create(explicitCallerIdentifier, false); this.varArgIndexes = null; this.lookupVarArgs = null; this.signature = null; @@ -751,8 +758,8 @@ public abstract class RCallNode extends RCallBaseNode implements RSyntaxNode, RS * allows to invoke a function with argument(s) supplied by hand. Consider using * {@link com.oracle.truffle.r.nodes.function.call.RExplicitCallNode} instead. */ - public static RCallNode createExplicitCall(Object explicitArgsIdentifier) { - return RCallNodeGen.create(RSyntaxNode.INTERNAL, explicitArgsIdentifier, null); + public static RCallNode createExplicitCall(Object explicitArgsIdentifier, Object explicitCallerIdentifier) { + return RCallNodeGen.create(RSyntaxNode.INTERNAL, explicitArgsIdentifier, explicitCallerIdentifier, null); } static boolean needsSplitting(RootCallTarget target) { @@ -870,9 +877,8 @@ public abstract class RCallNode extends RCallBaseNode implements RSyntaxNode, RS if (e == null || e.cachedTarget != cachedTarget) { entry = e = insert(new GenericCallEntry(cachedTarget, createCacheNode(cachedTarget), createArguments(cachedTarget))); } - VirtualFrame frame = SubstituteVirtualFrame.create(materializedFrame); - RArgsValuesAndNames orderedArguments = e.prepareArguments.execute(frame, (RArgsValuesAndNames) varArgs, (S3DefaultArguments) s3DefaultArguments, originalCall); - return e.leafCall.execute(frame, function, orderedArguments, (S3Args) s3Args); + RArgsValuesAndNames orderedArguments = e.prepareArguments.execute(materializedFrame, (RArgsValuesAndNames) varArgs, (S3DefaultArguments) s3DefaultArguments, originalCall); + return e.leafCall.execute(materializedFrame, function, orderedArguments, (S3Args) s3Args); } } diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/call/RExplicitBaseEnvCallDispatcher.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/call/RExplicitBaseEnvCallDispatcher.java index 0fd6147435..2a26de3d44 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/call/RExplicitBaseEnvCallDispatcher.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/call/RExplicitBaseEnvCallDispatcher.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2018, 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 @@ -63,7 +63,7 @@ public abstract class RExplicitBaseEnvCallDispatcher extends Node { @Specialization public Object doCached(VirtualFrame frame, RArgsValuesAndNames arguments, @Cached("getFunction(frame)") RFunction function) { - return callNode.execute(frame, function, arguments); + return callNode.call(frame, function, arguments); } RFunction getFunction(VirtualFrame frame) { diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/call/RExplicitCallNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/call/RExplicitCallNode.java index 3a4af7be70..d58a059ae2 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/call/RExplicitCallNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/call/RExplicitCallNode.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2018, 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 @@ -30,10 +30,11 @@ import com.oracle.truffle.api.frame.FrameSlot; import com.oracle.truffle.api.frame.FrameSlotKind; import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.nodes.Node; -import com.oracle.truffle.r.nodes.function.RCallBaseNode; import com.oracle.truffle.r.nodes.function.RCallNode; +import com.oracle.truffle.r.runtime.RCaller; import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames; import com.oracle.truffle.r.runtime.data.RFunction; +import com.oracle.truffle.r.runtime.data.RNull; import com.oracle.truffle.r.runtime.env.frame.FrameSlotChangeMonitor; import com.oracle.truffle.r.runtime.env.frame.RFrameSlot; @@ -46,27 +47,39 @@ public abstract class RExplicitCallNode extends Node { return RExplicitCallNodeGen.create(); } - public abstract Object execute(VirtualFrame frame, RFunction function, RArgsValuesAndNames args); + public final Object call(VirtualFrame frame, RFunction function, RArgsValuesAndNames args) { + return execute(frame, function, args, null); + } + + public abstract Object execute(VirtualFrame frame, RFunction function, RArgsValuesAndNames args, RCaller explicitCaller); private final RFrameSlot argsIdentifier = RFrameSlot.createTemp(true); + private final RFrameSlot callerIdentifier = RFrameSlot.createTemp(true); @CompilationFinal private FrameSlot argsFrameSlot; + @CompilationFinal private FrameSlot callerFrameSlot; @Specialization - protected Object doCall(VirtualFrame frame, RFunction function, RArgsValuesAndNames args, - @Cached("createExplicitCall()") RCallBaseNode call) { + protected Object doCall(VirtualFrame frame, RFunction function, RArgsValuesAndNames args, RCaller caller, + @Cached("createExplicitCall()") RCallNode call) { if (argsFrameSlot == null) { CompilerDirectives.transferToInterpreterAndInvalidate(); argsFrameSlot = FrameSlotChangeMonitor.findOrAddFrameSlot(frame.getFrameDescriptor(), argsIdentifier, FrameSlotKind.Object); } + if (callerFrameSlot == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + callerFrameSlot = FrameSlotChangeMonitor.findOrAddFrameSlot(frame.getFrameDescriptor(), callerIdentifier, FrameSlotKind.Object); + } try { FrameSlotChangeMonitor.setObject(frame, argsFrameSlot, args); + FrameSlotChangeMonitor.setObject(frame, callerFrameSlot, caller == null ? RNull.instance : caller); return call.execute(frame, function); } finally { FrameSlotChangeMonitor.setObject(frame, argsFrameSlot, null); + FrameSlotChangeMonitor.setObject(frame, callerFrameSlot, null); } } - protected RCallBaseNode createExplicitCall() { - return RCallNode.createExplicitCall(argsIdentifier); + protected RCallNode createExplicitCall() { + return RCallNode.createExplicitCall(argsIdentifier, callerIdentifier); } } diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RCaller.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RCaller.java index 646850b0d7..cf26ddb47f 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RCaller.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RCaller.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2018, 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 @@ -29,7 +29,14 @@ import com.oracle.truffle.r.runtime.nodes.RSyntaxElement; /** * Represents the caller of a function and stored in {@link RArguments}. A value of this type never - * appears in a Truffle execution. + * appears in a Truffle execution. Caller remembers its parent caller and frame number as described + * in {@code sys.parent} R function documentation: frames are numbered from 0 (global environment), + * parent does not have to have the frame with number one less, e.g. with do.call(fun, args, envir) + * when fun asks for parent, it should get 'envir', moreover, when evaluating promises parent frame + * and frame with number one less are typically also not the same frames. See also builtins in + * {@code FrameFunctions} for more details. + * + * @see RArguments */ public final class RCaller { @@ -66,6 +73,13 @@ public final class RCaller { this.parentIsInternal = parentIsInternal; } + private RCaller(int depth, RCaller parent, Object nodeOrSupplier) { + this.depth = depth; + this.parent = parent; + this.payload = nodeOrSupplier; + this.parentIsInternal = false; + } + private static int depthFromFrame(Frame callingFrame) { return callingFrame == null ? 0 : RArguments.getCall(callingFrame).getDepth() + 1; } @@ -142,6 +156,11 @@ public final class RCaller { return new RCaller(callingFrame, supplier, false); } + public static RCaller create(int depth, RCaller parent, Object payload) { + assert payload != null; + return new RCaller(depth, parent, payload, false); + } + public static RCaller create(Frame callingFrame, RCaller parent, Supplier<RSyntaxElement> supplier) { assert supplier != null; return new RCaller(depthFromFrame(callingFrame), parent, supplier, false); @@ -160,4 +179,8 @@ public final class RCaller { public void setVisibility(boolean visibility) { this.visibility = visibility; } + + public Object getPayload() { + return payload; + } } diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test index 27a1af0f79..7550b1b88e 100644 --- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test @@ -22889,6 +22889,10 @@ NULL #argv <- structure(list(expr = expression(quote(x <- c(1, x)))), .Names = 'expr');do.call('.doTrace', argv) NULL +##com.oracle.truffle.r.test.builtins.TestBuiltin_docall.testDoCall# +#do.call('c', list()) +NULL + ##com.oracle.truffle.r.test.builtins.TestBuiltin_docall.testDoCall#Output.IgnoreErrorContext# #typeof(do.call(function(x) x, list(as.symbol('foo')))) Error in (function (x) : object 'foo' not found @@ -73322,6 +73326,36 @@ v() [1] "x" +##com.oracle.truffle.r.test.builtins.TestBuiltin_sysframe.sysFrameWithPromises# +#{ top <- function(vtop) vtop;foo <- function(vfoo) top(vfoo);boo <- function(vboo) foo(sys.frame(vboo));bar <- function(vbar) do.call(boo, list(vbar), envir = parent.frame(2));baz <- function(vbaz) bar(vbaz);start <- function(vstart) baz(vstart);lapply(lapply(0:8, function(i) start(i)), function(env) sort(tolower(ls(env)))); } +[[1]] +[1] "bar" "baz" "boo" "foo" "start" "top" + +[[2]] +[1] "fun" "i" "x" + +[[3]] +[1] "fun" "i" "x" + +[[4]] +[1] "i" + +[[5]] +[1] "vstart" + +[[6]] +[1] "vbaz" + +[[7]] +[1] "vbar" + +[[8]] +[1] "args" "envir" "quote" "what" + +[[9]] +[1] "vboo" + + ##com.oracle.truffle.r.test.builtins.TestBuiltin_sysframes.frameAccessCommonTest#Ignored.ImplementationError# #{ foo <- function(x) lapply(sys.frames(), function(x) ls(x));bar <- function(ba) do.call(foo, list(ba));boo <- function(bo) bar(bo);callboo <- function(cb) do.call('boo', list(cb));fun <- function(f) callboo(f);fun(42);} [[1]] @@ -164585,6 +164619,11 @@ Error in prod("a") : invalid 'type' (character) of argument Called from: top level +##com.oracle.truffle.r.test.library.utils.TestInteractiveDebug.testBrowser# +#{ bar <- function(vbar) do.call(browser, list(), envir = parent.frame(2));baz <- function(vbaz) bar(vbaz);start <- function(vstart) baz(vstart);start(42); }<<<NEWLINE>>>ls()<<<NEWLINE>>>c +Called from: do.call(browser, list(), envir = parent.frame(2)) +[1] "vstart" + ##com.oracle.truffle.r.test.library.utils.TestInteractiveDebug.testConditionalBreakpoint# #fun <- function(x) { cat('x='); cat(x); cat('\n') }; trace(fun, quote(if (x > 10) browser())); fun(10)<<<NEWLINE>>>; fun(11)<<<NEWLINE>>><<<NEWLINE>>><<<NEWLINE>>><<<NEWLINE>>><<<NEWLINE>>><<<NEWLINE>>> [1] "fun" diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_docall.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_docall.java index 0f49690365..acdd29b2c6 100644 --- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_docall.java +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_docall.java @@ -4,7 +4,7 @@ * http://www.gnu.org/licenses/gpl-2.0.html * * Copyright (c) 2012-2014, Purdue University - * Copyright (c) 2013, 2017, Oracle and/or its affiliates + * Copyright (c) 2013, 2018, Oracle and/or its affiliates * * All rights reserved. */ diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_sysframe.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_sysframe.java index a318459bdd..044f680268 100644 --- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_sysframe.java +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_sysframe.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2018, 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 @@ -33,4 +33,17 @@ public class TestBuiltin_sysframe extends TestBase { public void frameAccessCommonTest() { assertEval("{ foo <- function(x) lapply(1:7, function(x) ls(sys.frame(x)));" + SYS_PARENT_SETUP + "}"); } + + @Test + public void sysFrameWithPromises() { + // Note: the parameter names help to identify the corresponding environments in the output + assertEval( + "{ top <- function(vtop) vtop;" + + "foo <- function(vfoo) top(vfoo);" + + "boo <- function(vboo) foo(sys.frame(vboo));" + + "bar <- function(vbar) do.call(boo, list(vbar), envir = parent.frame(2));" + + "baz <- function(vbaz) bar(vbaz);" + + "start <- function(vstart) baz(vstart);" + + "lapply(lapply(0:8, function(i) start(i)), function(env) sort(tolower(ls(env)))); }"); + } } diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/utils/TestInteractiveDebug.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/utils/TestInteractiveDebug.java index 6f94e4071b..baca92411e 100644 --- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/utils/TestInteractiveDebug.java +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/utils/TestInteractiveDebug.java @@ -130,6 +130,11 @@ public class TestInteractiveDebug extends TestBase { assertEval("do.call('browser', list())\nc\n"); assertEval("browser()\nwhere\nc\n"); assertEval("options(error=browser); prod('a')\nwhere\nc\n"); + assertEval( + "{ bar <- function(vbar) do.call(browser, list(), envir = parent.frame(2));" + + "baz <- function(vbaz) bar(vbaz);" + + "start <- function(vstart) baz(vstart);" + + "start(42); }\nls()\nc"); } @Test -- GitLab