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 26ccfa8d3e2b0da508933df384fa89ca3e2ca392..b9cc5811661cdbfc597b6b5e5208ba8f4ec7d4c8 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 @@ -168,7 +168,7 @@ public abstract class DoCall extends RBuiltinNode.Arg4 implements InternalRSynta shareObjectNode.execute(argValues[i]); } ArgumentsSignature signature = getArgsNames(argsAsList); - RCaller caller = RCaller.create(virtualFrame, RCallerHelper.createFromArguments(func, new RArgsValuesAndNames(argValues, signature))); + 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)); @@ -246,7 +246,7 @@ public abstract class DoCall extends RBuiltinNode.Arg4 implements InternalRSynta } RLanguage lang = RDataFactory.createLanguage(RASTUtils.createCall(ConstantNode.create(function), true, argsSignature, argsConstants).asRNode()); try { - Object resultValue = RContext.getEngine().eval(lang, env, call); + 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; 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 e7015136cf9bc346213560e8dfd47a03e42c52c3..58e4c4165aee44cee54bf31444e9b3e0ce80dbc0 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 @@ -650,6 +650,8 @@ public class FrameFunctions { @Specialization(guards = "n == 1") protected REnvironment parentFrameDirect(VirtualFrame frame, @SuppressWarnings("unused") int n, @Cached("new()") GetCallerFrameNode getCaller) { + // Note: this works even without checking the call#hasInternalParent() + // The environment in the arguments array is the right one even after 'do.call'. return REnvironment.frameToEnvironment(getCaller.execute(frame)); } @@ -663,7 +665,8 @@ public class FrameFunctions { promiseProfile.enter(); call = call.getParent(); } - for (int i = 0; i < n; i++) { + int i = 0; + while (i < n) { call = call.getParent(); if (call == null) { nullCallerProfile.enter(); @@ -673,6 +676,9 @@ public class FrameFunctions { promiseProfile.enter(); call = call.getParent(); } + if (!call.hasInternalParent()) { + i++; + } } nonNullCallerProfile.enter(); // if (RArguments.getDispatchArgs(f) != null && RArguments.getDispatchArgs(f) instanceof 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 6771af91b7f7403faed55595e3efe24138cdbb9f..646850b0d719b8394561ccbe10b773bab4f374b1 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, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 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 @@ -35,6 +35,13 @@ public final class RCaller { public static final RCaller topLevel = RCaller.createInvalid(null); + /** + * Determines the actual position of the corresponding frame on the execution call stack. When + * one follows the {@link RCaller#parent} chain, then the depth is not always decreasing by only + * one, the reason are promises, which may be evaluated somewhere deep down the call stack, but + * their parent call frame from R prespective could be much higher up the actual execution call + * stack. + */ private final int depth; private boolean visibility; private final RCaller parent; @@ -43,11 +50,20 @@ public final class RCaller { * promise evaluation frames). */ private final Object payload; + /** + * Marks those callers whose parent should not be taken into account when iterating R level + * frames using e.g. {@code parent.frame()}. This is the case for function invoked through + * {@code do.call} -- R pretends that they were called by the caller of {@code do.call} so that + * code like {@code eval(formula, parent.frame(2))} gives the same results regardless of whether + * the function was invoked directly or through {@code do.call}. + */ + private final boolean parentIsInternal; - private RCaller(Frame callingFrame, Object nodeOrSupplier) { + private RCaller(Frame callingFrame, Object nodeOrSupplier, boolean parentIsInternal) { this.depth = depthFromFrame(callingFrame); this.parent = parentFromFrame(callingFrame); this.payload = nodeOrSupplier; + this.parentIsInternal = parentIsInternal; } private static int depthFromFrame(Frame callingFrame) { @@ -58,10 +74,15 @@ public final class RCaller { return callingFrame == null ? null : RArguments.getCall(callingFrame); } - private RCaller(int depth, RCaller parent, Object nodeOrSupplier) { + private RCaller(int depth, RCaller parent, Object nodeOrSupplier, boolean parentIsInternal) { this.depth = depth; this.parent = parent; this.payload = nodeOrSupplier; + this.parentIsInternal = parentIsInternal; + } + + public RCaller withInternalParent() { + return new RCaller(depth, parent, payload, true); } public int getDepth() { @@ -72,6 +93,10 @@ public final class RCaller { return parent; } + public boolean hasInternalParent() { + return parentIsInternal; + } + public RSyntaxElement getSyntaxNode() { assert payload != null && !(payload instanceof RCaller) : payload == null ? "null RCaller" : "promise RCaller"; return payload instanceof RSyntaxElement ? (RSyntaxElement) payload : (RSyntaxElement) ((Supplier<?>) payload).get(); @@ -90,37 +115,42 @@ public final class RCaller { } public static RCaller createInvalid(Frame callingFrame) { - return new RCaller(callingFrame, null); + return new RCaller(callingFrame, null, false); } public static RCaller createInvalid(Frame callingFrame, RCaller parent) { - return new RCaller(depthFromFrame(callingFrame), parent, null); + return new RCaller(depthFromFrame(callingFrame), parent, null, false); } public static RCaller create(Frame callingFrame, RSyntaxElement node) { assert node != null; - return new RCaller(callingFrame, node); + return new RCaller(callingFrame, node, false); } public static RCaller create(Frame callingFrame, RCaller parent, RSyntaxElement node) { assert node != null; - return new RCaller(depthFromFrame(callingFrame), parent, node); + return new RCaller(depthFromFrame(callingFrame), parent, node, false); + } + + public static RCaller createWithInternalParent(Frame callingFrame, Supplier<RSyntaxElement> supplier) { + assert supplier != null; + return new RCaller(callingFrame, supplier, true); } public static RCaller create(Frame callingFrame, Supplier<RSyntaxElement> supplier) { assert supplier != null; - return new RCaller(callingFrame, supplier); + return new RCaller(callingFrame, supplier, false); } public static RCaller create(Frame callingFrame, RCaller parent, Supplier<RSyntaxElement> supplier) { assert supplier != null; - return new RCaller(depthFromFrame(callingFrame), parent, supplier); + return new RCaller(depthFromFrame(callingFrame), parent, supplier, false); } public static RCaller createForPromise(RCaller originalCaller, Frame frame) { int newDepth = frame == null ? 0 : RArguments.getDepth(frame); RCaller originalCall = frame == null ? null : RArguments.getCall(frame); - return new RCaller(newDepth, originalCaller, originalCall); + return new RCaller(newDepth, originalCaller, originalCall, false); } public boolean getVisibility() { 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 2e9161bfb56cc72ddf8ebd4b1cb4dd4dacda6e0f..bb19311e0bb716428d512ff0a3cdb108c94138cc 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 @@ -22328,6 +22328,18 @@ Error in (function (x) : object 'foo' not found #v1 <- as.numeric_version('3.0.0'); v2 <- as.numeric_version('3.1.0'); do.call('<', list(v1, v2)) [1] TRUE +##com.oracle.truffle.r.test.builtins.TestBuiltin_docall.testDoCall# +#{ boo <- function(c) ls(parent.frame(2)); foo <- function(a,b) boo(a); bar <- function(x,z) do.call('foo', list(1,2)); bar() } +[1] "x" "z" + +##com.oracle.truffle.r.test.builtins.TestBuiltin_docall.testDoCall# +#{ boo <- function(c) ls(parent.frame(2)); foo <- function(a,b) boo(a); bar <- function(x,z) do.call('foo', list(parse(text='goo()'),2)); bar() } +[1] "x" "z" + +##com.oracle.truffle.r.test.builtins.TestBuiltin_docall.testDoCall# +#{ boo <- function(c) ls(parent.frame(3)); foo <- function(a,b) boo(a); bar <- function(x,z) do.call('foo', list(parse(text='goo()'),2)); baz <- function(bazX) bar(bazX,1); baz(); } +[1] "bazX" + ##com.oracle.truffle.r.test.builtins.TestBuiltin_docall.testDoCall# #{ do.call("+", list(quote(1), 2))} [1] 3 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 c0003b48744895d87218edb6e67044f4c97b3992..e73f114185c995d4a98c52fafe70426c60912b5d 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 @@ -43,5 +43,9 @@ public class TestBuiltin_docall extends TestBase { 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()); }"); + + assertEval("{ boo <- function(c) ls(parent.frame(2)); foo <- function(a,b) boo(a); bar <- function(x,z) do.call('foo', list(1,2)); bar() }"); + assertEval("{ boo <- function(c) ls(parent.frame(2)); foo <- function(a,b) boo(a); bar <- function(x,z) do.call('foo', list(parse(text='goo()'),2)); bar() }"); + assertEval("{ boo <- function(c) ls(parent.frame(3)); foo <- function(a,b) boo(a); bar <- function(x,z) do.call('foo', list(parse(text='goo()'),2)); baz <- function(bazX) bar(bazX,1); baz(); }"); } }