diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RArguments.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RArguments.java index 107504369fa3ffdf4f92751bf52f9637bfcf9d10..18afbe3002ddb38c49dbd68b21ec951290e1cb2f 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RArguments.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RArguments.java @@ -47,7 +47,7 @@ import com.oracle.truffle.r.runtime.env.frame.FrameSlotChangeMonitor; * +--------------------+ * INDEX_FUNCTION -> | RFunction | * +--------------------+ - * INDEX_CALL_SRC -> | SourceSection | + * INDEX_CALL -> | RCaller | * +--------------------+ * INDEX_CALLER_FRAME -> | MaterializedFrame | * +--------------------+ @@ -81,11 +81,7 @@ import com.oracle.truffle.r.runtime.env.frame.FrameSlotChangeMonitor; * to how the supplied arguments were permuted. The purpose of this slot is to store the names in the * original signature (especially positional vs. named) for later use in UseMethod. * - * N.B. The depth is always a monotonically increasing value and unique across the active set of stack frames. - * Promise evaluation requires some special support as the stack must reflect the "logical" stack depth, - * else code like {@code sys.frames} does not work correctly, but it must be possible to access the initial frame - * that was associated with the promise else condition handling does not work correctly. Accordingly the - * stack frames associated with a promise evaluation maintain the INDEX_PROMISE_FRAME field for that access. + * @see RCaller */ // @formatter:on public final class RArguments { 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 18d4cc48176558fea78d0267258f79073cb3a33f..99bd051e3fd64774249b9d2078c5acb95c294820 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 @@ -41,6 +41,42 @@ import com.oracle.truffle.r.runtime.nodes.RSyntaxNode; * NOTE: It is important to create new caller instances for each stack frame, so that * {@link ReturnException#getTarget()} can uniquely identify the target frame. * + * Example: + * + * <pre> + * foo <- function(a) a + * bar <- function() foo(promiseFun(1)) + * promiseFun <- function(a) a+1 + * bar() + * </pre> + * + * When {@code promiseFun} is entered (which is at the point where {@code a} is evaluated in + * {@code foo}), the stack frames will look like: + * + * <pre> + * promiseFun (depth = 3, parent = bar, payload = "promiseFun(1)") + * internal frame (depth = 2, parent = global, payload = RCaller:bar) <-- this may not be a real Truffle execution frame! + * foo (depth = 2, parent = bar, payload = "foo(promiseFun(1))") + * bar (depth = 1, parent = global, payload = "bar()") + * global (depth = 0, parent = null, payload = null) + * </pre> + * + * Where the 'internal frame' wraps the frame of bar so that the promise code can access all the + * local variables of bar, but the {@link RCaller} can be different: the depth that would normally + * be 1 is 2, and parent and payload are different (see docs of {@link #isPromise()}). The purpose + * of {@link #payload} in such frames is that we can use it to reach the actual frame where the + * promise is logically evaluated, should the promise call some stack introspection built-in, e.g. + * {@code parent.frame()}. The reason why depths is 2 is that any consecutive function call uses + * current depth + 1 as the new depth and we need the new depth to be 3. + * + * Note that the 'internal frame' may not be on the real execution stack (i.e. not iterable by + * Truffle). The {@code InlineCacheNode} directly injects the AST of the promise into the calling + * function AST (foo in this case), but passes the 'internal frame' to the execute method instead of + * the current {@code VirtualFrame} (so that the injected AST thinks that it is executed inside bar + * and not foo). If the cache is full, then the {@code InlineCacheNode} creates a new + * {@link com.oracle.truffle.api.CallTarget} and calls it with 'internal frame', in which case the + * 'internal frame' appears in Truffle frames iteration. + * * @see RArguments */ public final class RCaller {