From 76d46a584a31f1f89adc1976f4ef376c6377eef7 Mon Sep 17 00:00:00 2001 From: stepan <stepan.sindelar@oracle.com> Date: Fri, 2 Jun 2017 21:14:25 +0200 Subject: [PATCH] Fix parent.frame() incorrect behavior after do.call. DoCall has two paths: one generic for builtins with other than default dispatch and arguments not including RLanguage, and one optimized, which uses REngine.evalFunction and does directly what the generic one would do through constructing AST nodes and executing them. --- .../com/oracle/truffle/r/engine/REngine.java | 17 +- .../r/engine/RRuntimeASTAccessImpl.java | 2 +- .../r/engine/shell/JLineConsoleCompleter.java | 2 +- .../r/library/fastrGrid/GridContext.java | 2 +- .../truffle/r/nodes/builtin/base/DoCall.java | 224 ++++++++++++++---- .../r/nodes/builtin/base/PrintFunctions.java | 2 +- .../r/nodes/builtin/base/StandardGeneric.java | 2 +- .../r/nodes/builtin/base/UpdateSlot.java | 2 +- .../truffle/r/nodes/builtin/base/Xtfrm.java | 2 +- .../builtin/base/printer/S4ObjectPrinter.java | 2 +- .../r/nodes/access/AccessSlotNode.java | 4 +- .../r/nodes/access/UpdateSlotNode.java | 2 +- .../r/nodes/ffi/JavaUpCallsRFFIImpl.java | 10 +- .../r/nodes/function/ArgumentMatcher.java | 3 +- .../r/nodes/function/ClassHierarchyNode.java | 2 +- .../function/call/CallRFunctionNode.java | 9 + .../r/nodes/objects/DispatchGeneric.java | 2 +- .../truffle/r/nodes/objects/LoadMethod.java | 2 +- .../truffle/r/runtime/ArgumentsSignature.java | 41 +++- .../truffle/r/runtime/RErrorHandling.java | 4 +- .../oracle/truffle/r/runtime/RSerialize.java | 2 +- .../truffle/r/runtime/context/Engine.java | 10 +- .../r/runtime/env/frame/ActiveBinding.java | 5 +- .../truffle/r/test/ExpectedTestOutput.test | 57 +++++ .../r/test/builtins/TestBuiltin_docall.java | 14 +- 25 files changed, 325 insertions(+), 99 deletions(-) diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/REngine.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/REngine.java index 6837ec9a35..6c2f34cee1 100644 --- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/REngine.java +++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/REngine.java @@ -97,7 +97,6 @@ import com.oracle.truffle.r.runtime.data.RLanguage; 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.data.RSymbol; import com.oracle.truffle.r.runtime.data.RTypedValue; import com.oracle.truffle.r.runtime.data.model.RAbstractVector; @@ -436,7 +435,7 @@ final class REngine implements Engine, Engine.Timings { @Override @TruffleBoundary - public Object evalFunction(RFunction func, MaterializedFrame frame, RCaller caller, RStringVector names, Object... args) { + public Object evalFunction(RFunction func, MaterializedFrame frame, RCaller caller, boolean evalPromises, ArgumentsSignature names, Object... args) { assert frame == null || caller != null; MaterializedFrame actualFrame = frame; if (actualFrame == null) { @@ -449,15 +448,17 @@ final class REngine implements Engine, Engine.Timings { } } RArgsValuesAndNames reorderedArgs = CallMatcherGenericNode.reorderArguments(args, func, - names == null ? ArgumentsSignature.empty(args.length) : ArgumentsSignature.get(names.getDataWithoutCopying()), RError.NO_CALLER); + names == null ? ArgumentsSignature.empty(args.length) : names, RError.NO_CALLER); Object[] newArgs = reorderedArgs.getArguments(); - for (int i = 0; i < newArgs.length; i++) { - Object arg = newArgs[i]; - if (arg instanceof RPromise) { - newArgs[i] = PromiseHelperNode.evaluateSlowPath(null, (RPromise) arg); + if (evalPromises) { + for (int i = 0; i < newArgs.length; i++) { + Object arg = newArgs[i]; + if (arg instanceof RPromise) { + newArgs[i] = PromiseHelperNode.evaluateSlowPath(null, (RPromise) arg); + } } } - return CallRFunctionNode.executeSlowpath(func, caller == null ? RArguments.getCall(actualFrame) : caller, actualFrame, newArgs, null); + return CallRFunctionNode.executeSlowpath(func, caller == null ? RArguments.getCall(actualFrame) : caller, actualFrame, newArgs, reorderedArgs.getSignature(), null); } private Object evalNode(RSyntaxElement exprRep, REnvironment envir, RCaller caller) { diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/RRuntimeASTAccessImpl.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/RRuntimeASTAccessImpl.java index 7c24294435..454eafb417 100644 --- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/RRuntimeASTAccessImpl.java +++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/RRuntimeASTAccessImpl.java @@ -367,7 +367,7 @@ class RRuntimeASTAccessImpl implements RRuntimeASTAccess { public Object callback(RFunction f, Object[] args) { boolean gd = RContext.getInstance().stateInstrumentation.setDebugGloballyDisabled(true); try { - return RContext.getEngine().evalFunction(f, null, null, null, args); + return RContext.getEngine().evalFunction(f, null, null, true, null, args); } catch (ReturnException ex) { // cannot throw return exceptions further up. return ex.getResult(); diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/JLineConsoleCompleter.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/JLineConsoleCompleter.java index eaeeb329ea..3d2a62fd7f 100644 --- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/JLineConsoleCompleter.java +++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/JLineConsoleCompleter.java @@ -99,7 +99,7 @@ public class JLineConsoleCompleter implements Completer { env.safePut("token", buffer.substring(start, cursor)); MaterializedFrame callingFrame = REnvironment.globalEnv().getFrame(); - RContext.getEngine().evalFunction(completeToken, callingFrame, RCaller.createInvalid(callingFrame), null, new Object[]{}); + RContext.getEngine().evalFunction(completeToken, callingFrame, RCaller.createInvalid(callingFrame), true, null, new Object[]{}); o = env.get("comps"); if (!(o instanceof RAbstractStringVector)) { diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GridContext.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GridContext.java index a2c92c4c53..efd87be667 100644 --- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GridContext.java +++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GridContext.java @@ -129,7 +129,7 @@ public final class GridContext { internalCode = RInternalCode.lookup(RContext.getInstance(), "grid", RInternalCode.loadSourceRelativeTo(LInitGrid.class, "fastrGrid.R")); } RFunction redrawAll = internalCode.lookupFunction(functionName); - return RContext.getEngine().evalFunction(redrawAll, REnvironment.baseEnv().getFrame(), RCaller.topLevel, null, args); + return RContext.getEngine().evalFunction(redrawAll, REnvironment.baseEnv().getFrame(), RCaller.topLevel, true, null, args); } private static final class DeviceAndState { 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 ec10b5b52c..ce6d340ec5 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 @@ -28,25 +28,43 @@ 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.Arrays; + +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.FrameSlot; +import com.oracle.truffle.api.frame.FrameSlotKind; +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.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.access.variables.ReadVariableNode; 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.call.RExplicitCallNode; +import com.oracle.truffle.r.nodes.function.RCallerHelper; import com.oracle.truffle.r.nodes.function.opt.ShareObjectNode; +import com.oracle.truffle.r.nodes.function.visibility.SetVisibilityNode; 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.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.RArgsValuesAndNames; @@ -58,24 +76,18 @@ import com.oracle.truffle.r.runtime.data.RList; import com.oracle.truffle.r.runtime.data.RPromise; import com.oracle.truffle.r.runtime.data.RPromise.Closure; import com.oracle.truffle.r.runtime.data.RPromise.PromiseState; -import com.oracle.truffle.r.runtime.data.RStringVector; import com.oracle.truffle.r.runtime.data.RSymbol; import com.oracle.truffle.r.runtime.data.model.RAbstractListVector; import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector; 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.RSyntaxNode; @RBuiltin(name = ".fastr.do.call", visibility = CUSTOM, kind = RBuiltinKind.INTERNAL, parameterNames = {"what", "args", "quote", "envir"}, behavior = COMPLEX) public abstract class DoCall extends RBuiltinNode.Arg4 implements InternalRSyntaxNodeChildren { - @Child private GetNamesAttributeNode getNamesNode = GetNamesAttributeNode.create(); - - private final BranchProfile containsRLanguageProfile = BranchProfile.create(); - private final BranchProfile containsRSymbolProfile = BranchProfile.create(); - - @Child private RExplicitCallNode call = RExplicitCallNode.create(); - static { Casts casts = new Casts(DoCall.class); casts.arg("what").defaultError(Message.MUST_BE_STRING_OR_FUNCTION, "what").mustBe(instanceOf(RFunction.class).or(stringValue())); @@ -88,72 +100,180 @@ public abstract class DoCall extends RBuiltinNode.Arg4 implements InternalRSynta return GetNodeGen.create(); } + // Note: quote tells us if symbols in args list should be interpreted as symbols (quote=TRUE) or + // they should be interpreted as read of given symbol from the given environment. + @Specialization protected Object doCall(VirtualFrame frame, RAbstractStringVector what, RList argsAsList, boolean quote, REnvironment env, + @Cached("create()") DoCallInternal internal, @Cached("createGet()") Get getNode) { if (what.getLength() != 1) { throw error(RError.Message.MUST_BE_STRING_OR_FUNCTION, "what"); } + // Note: if the function is in fact a promise, we are evaluating it here slightly earlier + // than GNU R. It should not a be a problem. RFunction func = (RFunction) getNode.execute(frame, what.getDataAt(0), env, RType.Function.getName(), true); - return doCall(frame, func, argsAsList, quote, env); + return doCall(frame, func, argsAsList, quote, env, internal); } @Specialization - protected Object doCall(VirtualFrame frame, RFunction func, RList argsAsList, boolean quote, REnvironment env) { - /* - * To re-create the illusion of a normal call, turn the values in argsAsList into promises. + protected Object doCall(VirtualFrame virtualFrame, RFunction func, RList argsAsList, boolean quote, REnvironment env, + @Cached("create()") DoCallInternal internal) { + return internal.execute(virtualFrame, func, argsAsList, quote, env); + } + + protected abstract static class DoCallInternal extends Node { + @Child private GetNamesAttributeNode getNamesNode = GetNamesAttributeNode.create(); + @Child private SetVisibilityNode setVisibilityNode = SetVisibilityNode.create(); + + public static DoCallInternal create() { + return DoCallInternalNodeGen.create(); + } + + 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; + } + + /** + * 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. */ - Object[] argValues = argsAsList.getDataCopy(); - RStringVector n = getNamesNode.getNames(argsAsList); - ArgumentsSignature signature; - if (n == null) { - signature = ArgumentsSignature.empty(argValues.length); - } else { - String[] argNames = new String[argValues.length]; + @Specialization(guards = "isSimple(func, argsAsList)") + public Object doSimple(VirtualFrame virtualFrame, RFunction func, RList argsAsList, boolean quote, REnvironment env, + @Cached("create()") ShareObjectNode shareObjectNode, + @Cached("createBinaryProfile()") ConditionProfile quoteProfile, + @Cached("create()") BranchProfile containsRSymbolProfile, + @Cached("createClassProfile()") ValueProfile frameAccessProfile) { + Object[] argValuesData = argsAsList.getDataWithoutCopying(); + Object[] argValues = argValuesData; + MaterializedFrame envFrame = env.getFrame(frameAccessProfile).materialize(); + if (quoteProfile.profile(!quote)) { + argValues = Arrays.copyOf(argValuesData, argValuesData.length); + for (int i = 0; i < argValues.length; i++) { + Object arg = argValues[i]; + if (arg instanceof RSymbol) { + containsRSymbolProfile.enter(); + RSymbol symbol = (RSymbol) arg; + if (symbol.getName().isEmpty()) { + argValues[i] = REmpty.instance; + } else { + argValues[i] = createLookupPromise(envFrame, symbol); + } + } + } + } for (int i = 0; i < argValues.length; i++) { - String name = n.getDataAt(i); - argNames[i] = name == null ? null : name.isEmpty() ? null : name; + shareObjectNode.execute(argValues[i]); + } + ArgumentsSignature signature = getArgsNames(argsAsList); + RCaller caller = RCaller.create(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]); + } } - signature = ArgumentsSignature.get(argNames); } - if (!quote) { + + protected static boolean isSimple(RFunction function, RList args) { + RBuiltinDescriptor builtin = function.getRBuiltin(); + if (builtin != null && builtin.getDispatch() != RDispatch.DEFAULT) { + CompilerDirectives.transferToInterpreter(); + return false; + } + for (int i = 0; i < args.getLength(); i++) { + if (args.getDataAt(i) instanceof RLanguage) { + CompilerDirectives.transferToInterpreter(); + // 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; + } + } + return true; + } + + @TruffleBoundary + private static RPromise createLookupPromise(MaterializedFrame callerFrame, RSymbol symbol) { + Closure closure = RPromise.Closure.create(RContext.getASTBuilder().lookup(RSyntaxNode.SOURCE_UNAVAILABLE, symbol.getName(), false).asRNode()); + return RDataFactory.createPromise(PromiseState.Supplied, closure, callerFrame); + } + + @TruffleBoundary + private boolean getVisibility(MaterializedFrame envFrame) { + FrameSlot envVisibilitySlot = FrameSlotChangeMonitor.findOrAddFrameSlot(envFrame.getFrameDescriptor(), RFrameSlot.Visibility, FrameSlotKind.Boolean); + if (envVisibilitySlot != null) { + try { + return envFrame.getBoolean(envVisibilitySlot); + } catch (FrameSlotTypeException e) { + throw RInternalError.shouldNotReachHere(); + } + } + 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; + } + + @TruffleBoundary + private 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++) { - Object arg = argValues[i]; - if (arg instanceof RLanguage) { - containsRLanguageProfile.enter(); - RLanguage lang = (RLanguage) arg; - argValues[i] = createRLanguagePromise(env.getFrame().materialize(), lang); - } else if (arg instanceof RSymbol) { - containsRSymbolProfile.enter(); - RSymbol symbol = (RSymbol) arg; - if (symbol.getName().isEmpty()) { - argValues[i] = REmpty.instance; + 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 { - argValues[i] = createLookupPromise(env.getFrame().materialize(), symbol); + argsConstants[i] = ReadVariableNode.create(((RSymbol) argValues[i]).getName()); } + } else { + argsConstants[i] = ConstantNode.create(argValues[i]); } } - } - for (int i = 0; i < argValues.length; i++) { - ShareObjectNode.share(argValues[i]); - } - try { - return call.execute(frame, func, new RArgsValuesAndNames(argValues, signature)); - } finally { for (int i = 0; i < argValues.length; i++) { - ShareObjectNode.unshare(argValues[i]); + ShareObjectNode.share(argValues[i]); + } + RLanguage lang = RDataFactory.createLanguage(RASTUtils.createCall(ConstantNode.create(function), true, argsSignature, argsConstants).asRNode()); + try { + Object resultValue = RContext.getEngine().eval(lang, env, call); + 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]); + } } } - } - @TruffleBoundary - private static RPromise createLookupPromise(MaterializedFrame callerFrame, RSymbol symbol) { - Closure closure = RPromise.Closure.create(RContext.getASTBuilder().lookup(RSyntaxNode.SOURCE_UNAVAILABLE, symbol.getName(), false).asRNode()); - return RDataFactory.createPromise(PromiseState.Supplied, closure, callerFrame); - } + private static final class CallResult { + public final Object value; + public final boolean visibility; - @TruffleBoundary - private static RPromise createRLanguagePromise(MaterializedFrame callerFrame, RLanguage lang) { - return RDataFactory.createPromise(PromiseState.Supplied, RPromise.Closure.create(RASTUtils.cloneNode(lang.getRep())), callerFrame); + CallResult(Object value, boolean visibility) { + this.value = value; + this.visibility = visibility; + } + } } } diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/PrintFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/PrintFunctions.java index 873b10c3c1..3c217de32a 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/PrintFunctions.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/PrintFunctions.java @@ -93,7 +93,7 @@ public class PrintFunctions { @Cached("createShowFunction(frame)") RFunction showFunction) { if (noOpt) { // S4 should only be called in case noOpt is true - RContext.getEngine().evalFunction(showFunction, null, null, null, o); + RContext.getEngine().evalFunction(showFunction, null, null, true, null, o); } else { printDefault(showFunction, digits, quote, naPrint, printGap, right, max, useSource, noOpt); } diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/StandardGeneric.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/StandardGeneric.java index 0334382ddc..cdd90a0908 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/StandardGeneric.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/StandardGeneric.java @@ -97,7 +97,7 @@ public abstract class StandardGeneric extends RBuiltinNode.Arg2 { // and this slow path should not be executed again REnvironment methodsEnv = REnvironment.getRegisteredNamespace("methods"); RFunction currentFunction = ReadVariableNode.lookupFunction(".getMethodsTable", methodsEnv.getFrame(), true, true); - mtable = (REnvironment) RContext.getEngine().evalFunction(currentFunction, frame.materialize(), RCaller.create(frame, getOriginalCall()), null, def); + mtable = (REnvironment) RContext.getEngine().evalFunction(currentFunction, frame.materialize(), RCaller.create(frame, getOriginalCall()), true, null, def); } RList sigArgs = (RList) readSigARgs.execute(null, fnFrame); int sigLength = (int) castIntScalar.doCast(readSigLength.execute(null, fnFrame)); diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateSlot.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateSlot.java index 560b5fc442..083a7bcbe2 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateSlot.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateSlot.java @@ -111,7 +111,7 @@ public abstract class UpdateSlot extends RBuiltinNode.Arg3 { checkSlotAssignFunction.getEnclosingFrame(), null); } else { // slow path - RContext.getEngine().evalFunction(currentFunction, frame.materialize(), RCaller.createInvalid(frame), null, objClass, name, valClass); + RContext.getEngine().evalFunction(currentFunction, frame.materialize(), RCaller.createInvalid(frame), true, null, objClass, name, valClass); } } } diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Xtfrm.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Xtfrm.java index abac7f57b8..a6f492fda3 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Xtfrm.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Xtfrm.java @@ -67,6 +67,6 @@ public abstract class Xtfrm extends RBuiltinNode.Arg1 { env = REnvironment.createEnclosingEnvironments(frame.materialize()); } RFunction func = (RFunction) getNode.execute(frame, "xtfrm.default", env, RType.Function.getName(), true); - return RContext.getEngine().evalFunction(func, null, null, null, x); + return RContext.getEngine().evalFunction(func, null, null, true, null, x); } } diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/S4ObjectPrinter.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/S4ObjectPrinter.java index 2ff2c2bf36..173799ff0b 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/S4ObjectPrinter.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/S4ObjectPrinter.java @@ -63,7 +63,7 @@ final class S4ObjectPrinter implements ValuePrinter<RS4Object> { static void printS4(PrintContext printCtx, Object o) { Frame frame = com.oracle.truffle.r.runtime.Utils.getActualCurrentFrame(); - RContext.getEngine().evalFunction(createShowFunction(frame), null, null, null, o); + RContext.getEngine().evalFunction(createShowFunction(frame), null, null, true, null, o); // The show function prints an additional new line character. The following attribute // instructs the ValuePrinter.println method not to print the new line since it was // already printed. diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/AccessSlotNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/AccessSlotNode.java index 1d8fccff96..61f7c1074d 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/AccessSlotNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/AccessSlotNode.java @@ -76,7 +76,7 @@ public abstract class AccessSlotNode extends RBaseNode { // TODO: any way to cache it or use a mechanism similar to overrides? REnvironment methodsNamespace = REnvironment.getRegisteredNamespace("methods"); RFunction dataPart = getDataPartFunction(methodsNamespace); - return RContext.getEngine().evalFunction(dataPart, methodsNamespace.getFrame(), RCaller.create(null, RASTUtils.getOriginalCall(this)), null, object); + return RContext.getEngine().evalFunction(dataPart, methodsNamespace.getFrame(), RCaller.create(null, RASTUtils.getOriginalCall(this)), true, null, object); } else if (name == RRuntime.NAMES_ATTR_KEY && object instanceof RAbstractVector) { assert false; // RS4Object can never be a vector? return RNull.instance; @@ -126,7 +126,7 @@ public abstract class AccessSlotNode extends RBaseNode { // TODO: any way to cache it or use a mechanism similar to overrides? REnvironment methodsNamespace = REnvironment.getRegisteredNamespace("methods"); RFunction dataPart = getDataPartFunction(methodsNamespace); - return RContext.getEngine().evalFunction(dataPart, methodsNamespace.getFrame(), RCaller.create(null, RASTUtils.getOriginalCall(this)), null, object); + return RContext.getEngine().evalFunction(dataPart, methodsNamespace.getFrame(), RCaller.create(null, RASTUtils.getOriginalCall(this)), true, null, object); } // this is really a fallback specialization but @Fallback does not work here (because of the diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/UpdateSlotNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/UpdateSlotNode.java index 59d8ab2c49..f895c1d2bb 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/UpdateSlotNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/UpdateSlotNode.java @@ -59,7 +59,7 @@ public abstract class UpdateSlotNode extends RBaseNode { REnvironment methodsNamespace = REnvironment.getRegisteredNamespace("methods"); Object f = methodsNamespace.findFunction(SET_DATA_PART); RFunction dataPart = (RFunction) RContext.getRRuntimeASTAccess().forcePromise(SET_DATA_PART, f); - return RContext.getEngine().evalFunction(dataPart, methodsNamespace.getFrame(), RCaller.createInvalid(null), null, object, prepareValue(value), RRuntime.LOGICAL_TRUE); + return RContext.getEngine().evalFunction(dataPart, methodsNamespace.getFrame(), RCaller.createInvalid(null), true, null, object, prepareValue(value), RRuntime.LOGICAL_TRUE); } protected boolean isData(String name) { diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/ffi/JavaUpCallsRFFIImpl.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/ffi/JavaUpCallsRFFIImpl.java index a6e270047d..5e88b23d2c 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/ffi/JavaUpCallsRFFIImpl.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/ffi/JavaUpCallsRFFIImpl.java @@ -44,6 +44,7 @@ import com.oracle.truffle.api.source.SourceSection; import com.oracle.truffle.r.nodes.RASTUtils; import com.oracle.truffle.r.nodes.ffi.ParseResult.ParseStatus; import com.oracle.truffle.r.nodes.function.ClassHierarchyNode; +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.RCleanUp; @@ -204,7 +205,7 @@ public abstract class JavaUpCallsRFFIImpl implements UpCallsRFFI { public Object R_do_MAKE_CLASS(Object clazz) { String name = "getClass"; RFunction getClass = (RFunction) RContext.getRRuntimeASTAccess().forcePromise(name, REnvironment.getRegisteredNamespace("methods").get(name)); - return RContext.getEngine().evalFunction(getClass, null, RCaller.createInvalid(null), null, clazz); + return RContext.getEngine().evalFunction(getClass, null, RCaller.createInvalid(null), true, null, clazz); } @Override @@ -733,10 +734,11 @@ public abstract class JavaUpCallsRFFIImpl implements UpCallsRFFI { RFunction f = (RFunction) l.car(); Object args = l.cdr(); if (args == RNull.instance) { - result = RContext.getEngine().evalFunction(f, env == REnvironment.globalEnv() ? null : ((REnvironment) env).getFrame(), RCaller.topLevel, null, new Object[0]); + result = RContext.getEngine().evalFunction(f, env == REnvironment.globalEnv() ? null : ((REnvironment) env).getFrame(), RCaller.topLevel, true, null, new Object[0]); } else { RList argsList = ((RPairList) args).toRList(); - result = RContext.getEngine().evalFunction(f, env == REnvironment.globalEnv() ? null : ((REnvironment) env).getFrame(), RCaller.topLevel, argsList.getNames(), + result = RContext.getEngine().evalFunction(f, env == REnvironment.globalEnv() ? null : ((REnvironment) env).getFrame(), RCaller.topLevel, true, + ArgumentsSignature.fromNamesAttribute(argsList.getNames()), argsList.getDataNonShared()); } } else { @@ -794,7 +796,7 @@ public abstract class JavaUpCallsRFFIImpl implements UpCallsRFFI { @Override public int R_compute_identical(Object x, Object y, int flags) { RFunction indenticalBuiltin = RContext.lookupBuiltin("identical"); - Object res = RContext.getEngine().evalFunction(indenticalBuiltin, null, null, null, x, y, RRuntime.asLogical((!((flags & 1) == 0))), + Object res = RContext.getEngine().evalFunction(indenticalBuiltin, null, null, true, null, x, y, RRuntime.asLogical((!((flags & 1) == 0))), RRuntime.asLogical((!((flags & 2) == 0))), RRuntime.asLogical((!((flags & 4) == 0))), RRuntime.asLogical((!((flags & 8) == 0))), RRuntime.asLogical((!((flags & 16) == 0)))); return (int) res; } diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/ArgumentMatcher.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/ArgumentMatcher.java index 1a6f910110..db25097a00 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/ArgumentMatcher.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/ArgumentMatcher.java @@ -232,7 +232,6 @@ public class ArgumentMatcher { }, index -> evaluatedArgs.getSignature().getName(index), null); Object[] evaledArgs = new Object[match.resultPermutation.length]; - for (int formalIndex = 0; formalIndex < match.resultPermutation.length; formalIndex++) { int suppliedIndex = match.resultPermutation[formalIndex]; @@ -260,7 +259,7 @@ public class ArgumentMatcher { evaledArgs[formalIndex] = evaluatedArgs.getArgument(suppliedIndex); } } - return new RArgsValuesAndNames(evaledArgs, formals.getSignature()); + return new RArgsValuesAndNames(evaledArgs, match.resultSignature); } /** diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/ClassHierarchyNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/ClassHierarchyNode.java index dcaab1397e..00a81a5535 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/ClassHierarchyNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/ClassHierarchyNode.java @@ -257,7 +257,7 @@ abstract class S4Class extends RBaseNode { // the assumption here is that the R function can only return either a String or // RStringVector s4Extends = (RStringVector) castToVector.doCast( - RContext.getEngine().evalFunction(sExtendsForS3Function, methodsEnv.getFrame(), RCaller.create(null, RASTUtils.getOriginalCall(this)), null, classAttr)); + RContext.getEngine().evalFunction(sExtendsForS3Function, methodsEnv.getFrame(), RCaller.create(null, RASTUtils.getOriginalCall(this)), true, null, classAttr)); RContext.getInstance().putS4Extends(classAttr, s4Extends); } return s4Extends; diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/call/CallRFunctionNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/call/CallRFunctionNode.java index 02a315c52e..ee1b587d0e 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/call/CallRFunctionNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/call/CallRFunctionNode.java @@ -65,8 +65,17 @@ public final class CallRFunctionNode extends Node { return callNode; } + public static Object executeSlowpath(RFunction function, RCaller caller, MaterializedFrame callerFrame, Object[] evaluatedArgs, ArgumentsSignature suppliedSignature, DispatchArgs dispatchArgs) { + Object[] callArgs = RArguments.create(function, caller, callerFrame, evaluatedArgs, suppliedSignature, function.getEnclosingFrame(), dispatchArgs); + return executeSlowpath(function, caller, callerFrame, callArgs); + } + public static Object executeSlowpath(RFunction function, RCaller caller, MaterializedFrame callerFrame, Object[] evaluatedArgs, DispatchArgs dispatchArgs) { Object[] callArgs = RArguments.create(function, caller, callerFrame, evaluatedArgs, dispatchArgs); + return executeSlowpath(function, caller, callerFrame, callArgs); + } + + private static Object executeSlowpath(RFunction function, RCaller caller, MaterializedFrame callerFrame, Object[] callArgs) { try { return function.getTarget().call(callArgs); } finally { diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/objects/DispatchGeneric.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/objects/DispatchGeneric.java index 9d4fdeb7f0..4098d1e5d5 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/objects/DispatchGeneric.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/objects/DispatchGeneric.java @@ -73,7 +73,7 @@ public abstract class DispatchGeneric extends RBaseNode { CompilerDirectives.transferToInterpreterAndInvalidate(); REnvironment methodsEnv = REnvironment.getRegisteredNamespace("methods"); RFunction currentFunction = ReadVariableNode.lookupFunction(".InheritForDispatch", methodsEnv.getFrame(), true, true); - method = (RFunction) RContext.getEngine().evalFunction(currentFunction, frame.materialize(), RCaller.create(frame, RASTUtils.getOriginalCall(this)), null, classes, fdef, mtable); + method = (RFunction) RContext.getEngine().evalFunction(currentFunction, frame.materialize(), RCaller.create(frame, RASTUtils.getOriginalCall(this)), true, null, classes, fdef, mtable); } method = loadMethod.executeRFunction(frame, method, fname); Object ret = executeMethod.executeObject(frame, method, fname); diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/objects/LoadMethod.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/objects/LoadMethod.java index 236289defb..3780418d16 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/objects/LoadMethod.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/objects/LoadMethod.java @@ -136,7 +136,7 @@ abstract class LoadMethod extends RBaseNode { loadMethodFunction.getEnclosingFrame(), null); } else { // slow path - ret = (RFunction) RContext.getEngine().evalFunction(currentFunction, frame.materialize(), caller, null, fdef, fname, REnvironment.frameToEnvironment(frame.materialize())); + ret = (RFunction) RContext.getEngine().evalFunction(currentFunction, frame.materialize(), caller, true, null, fdef, fname, REnvironment.frameToEnvironment(frame.materialize())); } } else { ret = fdef; diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ArgumentsSignature.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ArgumentsSignature.java index ca2ec69bbd..ac69a5a14f 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ArgumentsSignature.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ArgumentsSignature.java @@ -30,6 +30,7 @@ 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.r.runtime.data.RStringVector; public final class ArgumentsSignature implements Iterable<String> { @@ -47,7 +48,7 @@ public final class ArgumentsSignature implements Iterable<String> { public static final int NO_VARARG = -1; @CompilationFinal(dimensions = 1) private static final ArgumentsSignature[] EMPTY_SIGNATURES = new ArgumentsSignature[32]; - public static final ArgumentsSignature INVALID_SIGNATURE = new ArgumentsSignature(new String[]{"<<invalid>>"}); + public static final ArgumentsSignature INVALID_SIGNATURE = new ArgumentsSignature(new String[]{"<<invalid>>"}, false); static { for (int i = 0; i < EMPTY_SIGNATURES.length; i++) { @@ -55,12 +56,8 @@ public final class ArgumentsSignature implements Iterable<String> { } } - @TruffleBoundary public static ArgumentsSignature get(String... names) { - assert names != null; - ArgumentsSignature newSignature = new ArgumentsSignature(names); - ArgumentsSignature oldSignature = signatures.putIfAbsent(newSignature, newSignature); - return oldSignature != null ? oldSignature : newSignature; + return get(names, false); } public static ArgumentsSignature empty(int length) { @@ -73,15 +70,41 @@ public final class ArgumentsSignature implements Iterable<String> { return get(new String[length]); } + /** + * Returns {@code null} if the the vector is {@code null}. Any empty string in the vector is + * converted to {@code null} value. + */ + public static ArgumentsSignature fromNamesAttribute(RStringVector names) { + return names == null ? null : get(names.getDataWithoutCopying(), true); + } + + @TruffleBoundary + private static ArgumentsSignature get(String[] names, boolean convertEmpty) { + assert names != null; + ArgumentsSignature newSignature = new ArgumentsSignature(names, convertEmpty); + ArgumentsSignature oldSignature = signatures.putIfAbsent(newSignature, newSignature); + return oldSignature != null ? oldSignature : newSignature; + } + @CompilationFinal(dimensions = 1) private final String[] names; @CompilationFinal(dimensions = 1) private final int[] varArgIndexes; @CompilationFinal(dimensions = 1) private final boolean[] isVarArg; private final int varArgIndex; private final int nonNullCount; - private ArgumentsSignature(String[] names) { - this.names = Arrays.stream(names).map(s -> s == null || s == UNMATCHED ? s : s.intern()).toArray(String[]::new); - this.nonNullCount = (int) Arrays.stream(names).filter(s -> s != null).count(); + private ArgumentsSignature(String[] names, boolean convertEmpty) { + this.names = new String[names.length]; + int nonNullCount = 0; + for (int i = 0; i < names.length; i++) { + String s = names[i]; + if (s == null || (s.isEmpty() && convertEmpty)) { + this.names[i] = null; + continue; + } + nonNullCount++; + this.names[i] = s == UNMATCHED ? s : s.intern(); + } + this.nonNullCount = nonNullCount; int index = NO_VARARG; int count = 0; diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RErrorHandling.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RErrorHandling.java index ac0af78b88..6913a4f102 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RErrorHandling.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RErrorHandling.java @@ -356,7 +356,7 @@ public class RErrorHandling { errorcallDfltWithCall(fromCall(call), Message.GENERIC, msg); } else { RFunction hf = (RFunction) h; - RContext.getEngine().evalFunction(hf, null, null, null, cond); + RContext.getEngine().evalFunction(hf, null, null, true, null, cond); } } else { throw gotoExitingHandler(cond, call, entry); @@ -524,7 +524,7 @@ public class RErrorHandling { evaluatedArgs[i] = RMissing.instance; } } - RContext.getEngine().evalFunction(errorFunction, null, null, null, evaluatedArgs); + RContext.getEngine().evalFunction(errorFunction, null, null, true, null, evaluatedArgs); } else if (errorExpr instanceof RLanguage || errorExpr instanceof RExpression) { if (errorExpr instanceof RLanguage) { RContext.getEngine().eval((RLanguage) errorExpr, materializedFrame); diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RSerialize.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RSerialize.java index 8ee00ebb4a..85b7cd674b 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RSerialize.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RSerialize.java @@ -466,7 +466,7 @@ public class RSerialize { // fast path through getRegisteredNamespace Object namespace = REnvironment.getRegisteredNamespace(s.getDataAt(0)); if (namespace == null) { - namespace = RContext.getEngine().evalFunction(contextState.getDotDotFindNamespace(), null, null, null, s, ""); + namespace = RContext.getEngine().evalFunction(contextState.getDotDotFindNamespace(), null, null, true, null, s, ""); } return checkResult(addReadRef(namespace)); } diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/Engine.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/Engine.java index 6c7f7f692c..fe6fc77fa9 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/Engine.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/Engine.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2017, 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 @@ -28,13 +28,13 @@ import com.oracle.truffle.api.RootCallTarget; import com.oracle.truffle.api.frame.MaterializedFrame; import com.oracle.truffle.api.source.Source; import com.oracle.truffle.api.vm.PolyglotEngine; +import com.oracle.truffle.r.runtime.ArgumentsSignature; import com.oracle.truffle.r.runtime.RCaller; import com.oracle.truffle.r.runtime.RError; import com.oracle.truffle.r.runtime.RSource; import com.oracle.truffle.r.runtime.data.RExpression; import com.oracle.truffle.r.runtime.data.RFunction; import com.oracle.truffle.r.runtime.data.RLanguage; -import com.oracle.truffle.r.runtime.data.RStringVector; import com.oracle.truffle.r.runtime.env.REnvironment; import com.oracle.truffle.r.runtime.nodes.RNode; @@ -171,8 +171,12 @@ public interface Engine { * namespace, but the current stack is not empty. So when {@code frame} is not {@code null} a * {@code caller} should be passed to maintain the call stack correctly. {@code names} string * vector describing (optional) argument names + * + * @param names signature of the given parameters, may be {@code null} in which case the empty + * signature of correct cardinality shall be used. + * @param evalPromises whether to evaluate promises in args array before calling the function. */ - Object evalFunction(RFunction func, MaterializedFrame frame, RCaller caller, RStringVector names, Object... args); + Object evalFunction(RFunction func, MaterializedFrame frame, RCaller caller, boolean evalPromises, ArgumentsSignature names, Object... args); /** * Checks for the existence of (startup/shutdown) function {@code name} and, if present, invokes diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/env/frame/ActiveBinding.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/env/frame/ActiveBinding.java index f44d5c5222..892566e5bf 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/env/frame/ActiveBinding.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/env/frame/ActiveBinding.java @@ -27,7 +27,6 @@ import java.util.Objects; import com.oracle.truffle.r.runtime.RCaller; import com.oracle.truffle.r.runtime.RType; import com.oracle.truffle.r.runtime.context.RContext; -import com.oracle.truffle.r.runtime.data.RDataFactory; import com.oracle.truffle.r.runtime.data.RFunction; import com.oracle.truffle.r.runtime.data.RTruffleObject; import com.oracle.truffle.r.runtime.env.REnvironment; @@ -64,11 +63,11 @@ public class ActiveBinding implements RTruffleObject { } public Object writeValue(Object value) { - return RContext.getEngine().evalFunction(function, REnvironment.baseEnv().getFrame(), RCaller.createInvalid(null), null, value); + return RContext.getEngine().evalFunction(function, REnvironment.baseEnv().getFrame(), RCaller.createInvalid(null), true, null, value); } public Object readValue() { - return RContext.getEngine().evalFunction(function, REnvironment.baseEnv().getFrame(), RCaller.createInvalid(null), RDataFactory.createEmptyStringVector()); + return RContext.getEngine().evalFunction(function, REnvironment.baseEnv().getFrame(), RCaller.createInvalid(null), true, null); } } 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 a4a69155ca..5c89154642 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 @@ -22257,6 +22257,9 @@ Error in (function (x) : object 'foo' not found #{ do.call("+", list(quote(1), 2))} [1] 3 +##com.oracle.truffle.r.test.builtins.TestBuiltin_docall.testDoCall# +#{ do.call('+', list(data.frame(1), data.frame(2)), envir = new.env()); do.call('assign', list('a',2,new.env()), envir = new.env()); } + ##com.oracle.truffle.r.test.builtins.TestBuiltin_docall.testDoCall# #{ do.call(quote, list(quote(1)))} [1] 1 @@ -22269,6 +22272,22 @@ x #{ do.call(quote, list(quote(x+1)))} x + 1 +##com.oracle.truffle.r.test.builtins.TestBuiltin_docall.testDoCall# +#{ e <- new.env(); assign('a', 42, e); a <- 1; foo <- function(x) force(x); do.call('foo', list(as.name('a')), envir=e); } +[1] 42 + +##com.oracle.truffle.r.test.builtins.TestBuiltin_docall.testDoCall# +#{ e <- new.env(); assign('a', 42, e); a <- 1; foo <- function(x) force(x); do.call('foo', list(as.name('a')), envir=e, quote=T); } +a + +##com.oracle.truffle.r.test.builtins.TestBuiltin_docall.testDoCall# +#{ e <- new.env(); assign('foo', 42, e); foo <- function(x) 1; do.call('foo', list(), envir=e); } +[1] 1 + +##com.oracle.truffle.r.test.builtins.TestBuiltin_docall.testDoCall# +#{ e <- new.env(); assign('foo', function() 42, e); foo <- function(x) 1; do.call('foo', list(), envir=e); } +[1] 42 + ##com.oracle.truffle.r.test.builtins.TestBuiltin_docall.testDoCall#Output.IgnoreErrorContext# #{ f <- function(x) x; do.call(f, list(quote(y + 1)))} Error in (function (x) : object 'y' not found @@ -22277,6 +22296,44 @@ Error in (function (x) : object 'y' not found #{ f <- function(x) x; do.call(f, list(quote(y)))} Error in (function (x) : object 'y' not found +##com.oracle.truffle.r.test.builtins.TestBuiltin_docall.testDoCall# +#{ foo <- function() ls(parent.frame()); bar <- function(a,b,c) do.call('foo', list()); bar(a=1,b=2,c=3); } +[1] "a" "b" "c" + +##com.oracle.truffle.r.test.builtins.TestBuiltin_docall.testDoCall# +#{ foo <- function() ls(parent.frame()); bar <- function(a,b,c) do.call('foo', list(), envir=globalenv()); bar(a=1,b=2,c=3) } +[1] "bar" "foo" + +##com.oracle.truffle.r.test.builtins.TestBuiltin_docall.testDoCall# +#{ foo <- function(a,b) list(a=a,b=b); e <- new.env(); assign('a', 2); assign('b', 3); a<-0; b<-1; do.call('foo', list(a,b), env=e); } +$a +[1] 0 + +$b +[1] 1 + + +##com.oracle.truffle.r.test.builtins.TestBuiltin_docall.testDoCall# +#{ foo <- function(a,b) list(a=a,b=b); e <- new.env(); assign('a', 2); assign('b', 3); a<-0; b<-1; do.call('foo', list(a=as.name('a'),as.name('b')), env=e) } +$a +[1] 0 + +$b +[1] 1 + + +##com.oracle.truffle.r.test.builtins.TestBuiltin_docall.testDoCall# +#{ foo <- function(a,b,c) { cat('foo called.'); list(a=a,b=b,c=c); }; side <- function() { cat('side effect!'); 42 }; do.call('foo', list(parse(text='side()')[[1]], 0, 0)); } +foo called.side effect!$a +[1] 42 + +$b +[1] 0 + +$c +[1] 0 + + ##com.oracle.truffle.r.test.builtins.TestBuiltin_docall.testDoCall# #{ x<-list(c(1,2)); do.call("as.matrix", x) } [,1] 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 73fbac9e68..c0003b4874 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, 2016, Oracle and/or its affiliates + * Copyright (c) 2013, 2017, Oracle and/or its affiliates * * All rights reserved. */ @@ -31,5 +31,17 @@ public class TestBuiltin_docall extends TestBase { assertEval("v1 <- as.numeric_version('3.0.0'); v2 <- as.numeric_version('3.1.0'); do.call('<', list(quote(v1), quote(v2)))"); assertEval(Output.IgnoreErrorContext, "typeof(do.call(function(x) x, list(as.symbol('foo'))))"); assertEval("typeof(do.call(function(x) x, list(as.symbol('foo')), quote=TRUE))"); + + assertEval("{ foo <- function() ls(parent.frame()); bar <- function(a,b,c) do.call('foo', list()); bar(a=1,b=2,c=3); }"); + assertEval("{ foo <- function() ls(parent.frame()); bar <- function(a,b,c) do.call('foo', list(), envir=globalenv()); bar(a=1,b=2,c=3) }"); + assertEval("{ foo <- function(a,b) list(a=a,b=b); e <- new.env(); assign('a', 2); assign('b', 3); a<-0; b<-1; do.call('foo', list(a,b), env=e); }"); + assertEval("{ foo <- function(a,b) list(a=a,b=b); e <- new.env(); assign('a', 2); assign('b', 3); a<-0; b<-1; do.call('foo', list(a=as.name('a'),as.name('b')), env=e) }"); + assertEval("{ foo <- function(a,b,c) { cat('foo called.'); list(a=a,b=b,c=c); }; side <- function() { cat('side effect!'); 42 }; do.call('foo', list(parse(text='side()')[[1]], 0, 0)); }"); + + assertEval("{ e <- new.env(); assign('a', 42, e); a <- 1; foo <- function(x) force(x); do.call('foo', list(as.name('a')), envir=e); }"); + assertEval("{ e <- new.env(); assign('a', 42, e); a <- 1; foo <- function(x) force(x); do.call('foo', list(as.name('a')), envir=e, quote=T); }"); + assertEval("{ e <- new.env(); assign('foo', function() 42, e); foo <- function(x) 1; do.call('foo', list(), envir=e); }"); + assertEval("{ e <- new.env(); assign('foo', 42, e); foo <- function(x) 1; do.call('foo', list(), envir=e); }"); + assertEval("{ do.call('+', list(data.frame(1), data.frame(2)), envir = new.env()); do.call('assign', list('a',2,new.env()), envir = new.env()); }"); } } -- GitLab