diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/GetFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/GetFunctions.java
index 72d66dead0aad614ce8eb90e21de016588c6dd5b..a7a17f8209246e587408d1f6731774faf9faf28b 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/GetFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/GetFunctions.java
@@ -39,6 +39,7 @@ import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
 import com.oracle.truffle.api.Truffle;
 import com.oracle.truffle.api.dsl.Cached;
 import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.api.frame.MaterializedFrame;
 import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.profiles.BranchProfile;
 import com.oracle.truffle.api.profiles.ConditionProfile;
@@ -62,7 +63,7 @@ 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.SetNeedsCallerFrameClosure;
+import com.oracle.truffle.r.runtime.CallerFrameClosure;
 import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
@@ -260,7 +261,7 @@ public class GetFunctions {
         @Child private CallRFunctionCachedNode callCache = CallRFunctionCachedNodeGen.create(2);
 
         private final Assumption needsNoCallerFrame = Truffle.getRuntime().createAssumption("no caller frame");
-        private final SetNeedsCallerFrameClosure setNeedsCallerFrameClosure = new SetNeedsCallerFrameFlag(needsNoCallerFrame);
+        private final CallerFrameClosure setNeedsCallerFrameClosure = new SetNeedsCallerFrameFlag(needsNoCallerFrame);
 
         static {
             Casts casts = new Casts(MGet.class);
@@ -403,7 +404,7 @@ public class GetFunctions {
             return value;
         }
 
-        private class SetNeedsCallerFrameFlag extends SetNeedsCallerFrameClosure {
+        private class SetNeedsCallerFrameFlag extends CallerFrameClosure {
 
             private final Assumption needsNoCallerFrame;
 
@@ -416,6 +417,11 @@ public class GetFunctions {
                 needsNoCallerFrame.invalidate();
             }
 
+            @Override
+            public MaterializedFrame getMaterializedCallerFrame() {
+                return null;
+            }
+
         }
     }
 }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/HiddenInternalFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/HiddenInternalFunctions.java
index 52907520278ae691705c7cff9046b2747f0bff79..a217ccfdb3038dc7935044633c912fb8466c7c91 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/HiddenInternalFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/HiddenInternalFunctions.java
@@ -49,7 +49,7 @@ 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.RSerialize;
-import com.oracle.truffle.r.runtime.SetNeedsCallerFrameClosure;
+import com.oracle.truffle.r.runtime.CallerFrameClosure;
 import com.oracle.truffle.r.runtime.SubstituteVirtualFrame;
 import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.context.RContext;
@@ -254,7 +254,7 @@ public class HiddenInternalFunctions {
                 RSerialize.CallHook callHook = new RSerialize.CallHook() {
                     @Override
                     public Object eval(Object arg) {
-                        return callCache.execute(SubstituteVirtualFrame.create(frame), envhook, RCaller.create(frame, getOriginalCall()), SetNeedsCallerFrameClosure.DUMMY, new Object[]{arg}, null);
+                        return callCache.execute(SubstituteVirtualFrame.create(frame), envhook, RCaller.create(frame, getOriginalCall()), new CallerFrameClosure.Dummy(0), new Object[]{arg}, null);
                     }
                 };
                 String functionName = ReadVariableNode.getSlowPathEvaluationName();
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 f882af9993d0773fe3658e32d3fb665e738fe44f..c9d12275d3cd53f8f5576e3e264c7a55b4763552 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
@@ -32,7 +32,7 @@ import com.oracle.truffle.r.nodes.function.call.CallRFunctionNode;
 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.SetNeedsCallerFrameClosure;
+import com.oracle.truffle.r.runtime.CallerFrameClosure;
 import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.data.RFunction;
@@ -107,7 +107,7 @@ public abstract class UpdateSlot extends RBuiltinNode.Arg3 {
             if (cached.profile(currentFunction == checkSlotAssignFunction)) {
                 // TODO: technically, someone could override checkAtAssignment function and access
                 // the caller, but it's rather unlikely
-                checkAtAssignmentCall.execute(frame, checkSlotAssignFunction, RCaller.createInvalid(frame), SetNeedsCallerFrameClosure.DUMMY, new Object[]{objClass, name, valClass}, SIGNATURE,
+                checkAtAssignmentCall.execute(frame, checkSlotAssignFunction, RCaller.createInvalid(frame), null, new Object[]{objClass, name, valClass}, SIGNATURE,
                                 checkSlotAssignFunction.getEnclosingFrame(), null);
             } else {
                 // slow path
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/CallMatcherNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/CallMatcherNode.java
index 5ae4e4a86af8ff26dc0eaa4332c59bf62c733875..ef7648287641e87a826d3734a6f660242ddb6985 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/CallMatcherNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/CallMatcherNode.java
@@ -35,7 +35,7 @@ import com.oracle.truffle.r.runtime.RArguments.DispatchArgs;
 import com.oracle.truffle.r.runtime.RCaller;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.RVisibility;
-import com.oracle.truffle.r.runtime.SetNeedsCallerFrameClosure;
+import com.oracle.truffle.r.runtime.CallerFrameClosure;
 import com.oracle.truffle.r.runtime.builtins.FastPathFactory;
 import com.oracle.truffle.r.runtime.builtins.RBuiltinDescriptor;
 import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
@@ -265,7 +265,7 @@ public abstract class CallMatcherNode extends RBaseNode {
                     Supplier<RSyntaxElement> argsSupplier = RCallerHelper.createFromArguments(genFunctionName, preparePermutation, suppliedArguments, suppliedSignature);
                     RCaller caller = genFunctionName == null ? RCaller.createInvalid(frame, parent) : RCaller.create(frame, parent, argsSupplier);
                     try {
-                        return call.execute(frame, cachedFunction, caller, SetNeedsCallerFrameClosure.DUMMY, reorderedArgs, matchedArgs.getSignature(), cachedFunction.getEnclosingFrame(),
+                        return call.execute(frame, cachedFunction, caller, null, reorderedArgs, matchedArgs.getSignature(), cachedFunction.getEnclosingFrame(),
                                         dispatchArgs);
                     } finally {
                         visibility.executeAfterCall(frame, caller);
@@ -346,7 +346,7 @@ public abstract class CallMatcherNode extends RBaseNode {
                             : RCaller.create(frame, RCallerHelper.createFromArguments(genFunctionName,
                                             new RArgsValuesAndNames(reorderedArgs.getArguments(), ArgumentsSignature.empty(reorderedArgs.getLength()))));
             try {
-                return call.execute(frame, function, caller, SetNeedsCallerFrameClosure.DUMMY, reorderedArgs.getArguments(), reorderedArgs.getSignature(), function.getEnclosingFrame(), dispatchArgs);
+                return call.execute(frame, function, caller, new CallerFrameClosure.Dummy(3), reorderedArgs.getArguments(), reorderedArgs.getSignature(), function.getEnclosingFrame(), dispatchArgs);
             } finally {
                 visibility.executeAfterCall(frame, caller);
             }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/GetCallerFrameNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/GetCallerFrameNode.java
index 5a53eddda2212bc88327f49bdd6872f5dfe8118d..6e4ecf5de1af949aeac0a8f51c6ae19209b49937 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/GetCallerFrameNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/GetCallerFrameNode.java
@@ -31,7 +31,7 @@ import com.oracle.truffle.api.nodes.NodeCost;
 import com.oracle.truffle.api.profiles.BranchProfile;
 import com.oracle.truffle.r.runtime.RArguments;
 import com.oracle.truffle.r.runtime.RError;
-import com.oracle.truffle.r.runtime.SetNeedsCallerFrameClosure;
+import com.oracle.truffle.r.runtime.CallerFrameClosure;
 import com.oracle.truffle.r.runtime.Utils;
 import com.oracle.truffle.r.runtime.nodes.RBaseNode;
 
@@ -47,15 +47,18 @@ public final class GetCallerFrameNode extends RBaseNode {
 
     public MaterializedFrame execute(Frame frame) {
         Object callerFrameObject = RArguments.getCallerFrame(frame);
-        if (!(callerFrameObject instanceof MaterializedFrame)) {
+        if (callerFrameObject instanceof CallerFrameClosure) {
+            CallerFrameClosure closure = (CallerFrameClosure) callerFrameObject;
             if (!slowPathInitialized) {
                 CompilerDirectives.transferToInterpreterAndInvalidate();
                 slowPathInitialized = true;
             }
-            notifyCallers(callerFrameObject);
-            if (slowPathInitialized) {
-                RError.performanceWarning("slow caller frame access");
+            notifyCallers(closure);
+            MaterializedFrame materializedCallerFrame = closure.getMaterializedCallerFrame();
+            if (materializedCallerFrame != null) {
+                return materializedCallerFrame;
             }
+            RError.performanceWarning("slow caller frame access");
             // for now, get it on the very slow path
             Frame callerFrame = Utils.getCallerFrame(frame, FrameAccess.MATERIALIZE);
             if (callerFrame != null) {
@@ -69,10 +72,8 @@ public final class GetCallerFrameNode extends RBaseNode {
         return (MaterializedFrame) callerFrameObject;
     }
 
-    private static void notifyCallers(Object callerFrameObject) {
-        if (callerFrameObject instanceof SetNeedsCallerFrameClosure) {
-            // inform the responsible call node to create a caller frame
-            ((SetNeedsCallerFrameClosure) callerFrameObject).setNeedsCallerFrame();
-        }
+    private static void notifyCallers(CallerFrameClosure callerFrameObject) {
+        // inform the responsible call node to create a caller frame
+        callerFrameObject.setNeedsCallerFrame();
     }
 }
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 7b257202f4d403b2e7f517fce403a9c839787d8b..f403228b90cdd8ecd60eed11bd43745ff419bd4f 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
@@ -83,7 +83,7 @@ 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.SetNeedsCallerFrameClosure;
+import com.oracle.truffle.r.runtime.CallerFrameClosure;
 import com.oracle.truffle.r.runtime.SubstituteVirtualFrame;
 import com.oracle.truffle.r.runtime.builtins.FastPathFactory;
 import com.oracle.truffle.r.runtime.builtins.RBuiltinDescriptor;
@@ -155,9 +155,10 @@ public abstract class RCallNode extends RCallBaseNode implements RSyntaxNode, RS
     // needed for INTERNAL_GENERIC calls:
     @Child private FunctionDispatch internalDispatchCall;
 
+    // TODO remove; kept just for compatibility
     private final Assumption needsNoCallerFrame = Truffle.getRuntime().createAssumption("no caller frame");
-    private final SetNeedsCallerFrameClosure setNeedsCallerFrameClosure = new InvalidateNoCallerFrame(needsNoCallerFrame);
 
+    // TODO remove; kept just for compatibility
     public boolean setNeedsCallerFrame() {
         boolean value = !needsNoCallerFrame.isValid();
         needsNoCallerFrame.invalidate();
@@ -993,19 +994,25 @@ public abstract class RCallNode extends RCallBaseNode implements RSyntaxNode, RS
                     call.getCallNode().cloneCallTarget();
                 }
             }
-            Object callerFrameObject = /* CompilerDirectives.inInterpreter() || */originalCall.needsNoCallerFrame.isValid() ? originalCall.setNeedsCallerFrameClosure : frame.materialize();
 
-            return call.execute(frame, function, originalCall.createCaller(frame, function), callerFrameObject, orderedArguments.getArguments(), orderedArguments.getSignature(),
+            return call.execute(frame, function, originalCall.createCaller(frame, function), null, orderedArguments.getArguments(), orderedArguments.getSignature(),
                             function.getEnclosingFrame(), s3Args);
         }
     }
 
-    public static final class InvalidateNoCallerFrame extends SetNeedsCallerFrameClosure {
+    public static final class InvalidateNoCallerFrame extends CallerFrameClosure {
 
         private final Assumption needsNoCallerFrame;
+        private final MaterializedFrame frame;
 
         protected InvalidateNoCallerFrame(Assumption needsNoCallerFrame) {
             this.needsNoCallerFrame = needsNoCallerFrame;
+            this.frame = null;
+        }
+
+        protected InvalidateNoCallerFrame(Assumption needsNoCallerFrame, MaterializedFrame frame) {
+            this.needsNoCallerFrame = needsNoCallerFrame;
+            this.frame = frame;
         }
 
         @Override
@@ -1013,6 +1020,11 @@ public abstract class RCallNode extends RCallBaseNode implements RSyntaxNode, RS
             needsNoCallerFrame.invalidate();
         }
 
+        @Override
+        public MaterializedFrame getMaterializedCallerFrame() {
+            return frame;
+        }
+
     }
 
     @Override
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 8ada74320f87c9d3771663d80faa80066c240897..d8585b338bf6ebe57652bb451e125348009c062a 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
@@ -22,7 +22,9 @@
  */
 package com.oracle.truffle.r.nodes.function.call;
 
+import com.oracle.truffle.api.Assumption;
 import com.oracle.truffle.api.CallTarget;
+import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.Truffle;
 import com.oracle.truffle.api.frame.MaterializedFrame;
 import com.oracle.truffle.api.frame.VirtualFrame;
@@ -32,6 +34,7 @@ import com.oracle.truffle.api.nodes.NodeCost;
 import com.oracle.truffle.api.nodes.NodeInfo;
 import com.oracle.truffle.r.nodes.function.visibility.SetVisibilityNode;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
+import com.oracle.truffle.r.runtime.CallerFrameClosure;
 import com.oracle.truffle.r.runtime.RArguments;
 import com.oracle.truffle.r.runtime.RArguments.DispatchArgs;
 import com.oracle.truffle.r.runtime.RCaller;
@@ -43,6 +46,9 @@ public final class CallRFunctionNode extends Node {
     @Child private DirectCallNode callNode;
     @Child private SetVisibilityNode visibility = SetVisibilityNode.create();
 
+    private final Assumption needsNoCallerFrame = Truffle.getRuntime().createAssumption("no caller frame");
+    private final CallerFrameClosure invalidateNoCallerFrame = new InvalidateNoCallerFrame(needsNoCallerFrame);
+
     private CallRFunctionNode(CallTarget callTarget) {
         this.callNode = Truffle.getRuntime().createDirectCallNode(callTarget);
     }
@@ -51,8 +57,9 @@ public final class CallRFunctionNode extends Node {
         return new CallRFunctionNode(callTarget);
     }
 
-    public Object execute(VirtualFrame frame, RFunction function, RCaller caller, Object callerFrameObject, Object[] evaluatedArgs, ArgumentsSignature suppliedSignature,
+    public Object execute(VirtualFrame frame, RFunction function, RCaller caller, MaterializedFrame callerFrame, Object[] evaluatedArgs, ArgumentsSignature suppliedSignature,
                     MaterializedFrame enclosingFrame, DispatchArgs dispatchArgs) {
+        Object callerFrameObject = needsNoCallerFrame.isValid() ? getCallerFrameObject(frame) : frame.materialize();
         Object[] callArgs = RArguments.create(function, caller, callerFrameObject, evaluatedArgs, suppliedSignature, enclosingFrame, dispatchArgs);
         try {
             return callNode.call(callArgs);
@@ -61,16 +68,25 @@ public final class CallRFunctionNode extends Node {
         }
     }
 
+    private Object getCallerFrameObject(VirtualFrame callerFrame) {
+        if (CompilerDirectives.inInterpreter()) {
+            return new InvalidateNoCallerFrame(needsNoCallerFrame, callerFrame.materialize());
+        }
+        return invalidateNoCallerFrame;
+    }
+
     public DirectCallNode getCallNode() {
         return callNode;
     }
 
     public static Object executeSlowpath(RFunction function, RCaller caller, MaterializedFrame callerFrame, Object[] evaluatedArgs, ArgumentsSignature suppliedSignature, DispatchArgs dispatchArgs) {
+        assert callerFrame != null;
         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) {
+        assert callerFrame != null;
         Object[] callArgs = RArguments.create(function, caller, callerFrame, evaluatedArgs, dispatchArgs);
         return executeSlowpath(function, caller, callerFrame, callArgs);
     }
@@ -82,4 +98,31 @@ public final class CallRFunctionNode extends Node {
             SetVisibilityNode.executeAfterCallSlowPath(callerFrame, caller);
         }
     }
+
+    public static final class InvalidateNoCallerFrame extends CallerFrameClosure {
+
+        private final Assumption needsNoCallerFrame;
+        private final MaterializedFrame frame;
+
+        protected InvalidateNoCallerFrame(Assumption needsNoCallerFrame) {
+            this.needsNoCallerFrame = needsNoCallerFrame;
+            this.frame = null;
+        }
+
+        protected InvalidateNoCallerFrame(Assumption needsNoCallerFrame, MaterializedFrame frame) {
+            this.needsNoCallerFrame = needsNoCallerFrame;
+            this.frame = frame;
+        }
+
+        @Override
+        public void setNeedsCallerFrame() {
+            needsNoCallerFrame.invalidate();
+        }
+
+        @Override
+        public MaterializedFrame getMaterializedCallerFrame() {
+            return frame;
+        }
+
+    }
 }
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 6592433bad3d03be5084988a78fc6bd4dc7cf7c4..3a5cbd249e66e858b870df793b35e5a3f99a3779 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
@@ -32,7 +32,6 @@ 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.RRuntime;
-import com.oracle.truffle.r.runtime.SetNeedsCallerFrameClosure;
 import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.data.RAttributable;
 import com.oracle.truffle.r.runtime.data.RFunction;
@@ -132,7 +131,7 @@ abstract class LoadMethod extends RBaseNode {
             if (cached.profile(currentFunction == loadMethodFunction)) {
                 // TODO: technically, someone could override loadMethod function and access the
                 // caller, but it's rather unlikely
-                ret = (RFunction) loadMethodCall.execute(frame, loadMethodFunction, caller, SetNeedsCallerFrameClosure.DUMMY,
+                ret = (RFunction) loadMethodCall.execute(frame, loadMethodFunction, caller, null,
                                 new Object[]{fdef, fname, REnvironment.frameToEnvironment(frame.materialize())}, SIGNATURE,
                                 loadMethodFunction.getEnclosingFrame(), null);
             } else {
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/SetNeedsCallerFrameClosure.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/CallerFrameClosure.java
similarity index 64%
rename from com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/SetNeedsCallerFrameClosure.java
rename to com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/CallerFrameClosure.java
index 046eef0808745ec45023804609091cdb6864aa61..f642e50181a719ebb39b484a2537349075e57f1b 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/SetNeedsCallerFrameClosure.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/CallerFrameClosure.java
@@ -22,17 +22,38 @@
  */
 package com.oracle.truffle.r.runtime;
 
-public abstract class SetNeedsCallerFrameClosure {
-    public static final SetNeedsCallerFrameClosure DUMMY = new Dummy();
+import com.oracle.truffle.api.frame.MaterializedFrame;
 
+public abstract class CallerFrameClosure {
+// public static final CallerFrameClosure DUMMY = new Dummy();
+
+    /**
+     * Inform the call node to subsequently provide the caller frame.
+     */
     public abstract void setNeedsCallerFrame();
 
-    private static class Dummy extends SetNeedsCallerFrameClosure {
+    /**
+     * Retrieve the materialized caller frame if available (i.e. interpreter only).
+     */
+    public abstract MaterializedFrame getMaterializedCallerFrame();
+
+    public static class Dummy extends CallerFrameClosure {
+
+        private final int id;
+
+        public Dummy(int id) {
+            this.id = id;
+        }
 
         @Override
         public void setNeedsCallerFrame() {
             // do nothing
         }
 
+        @Override
+        public MaterializedFrame getMaterializedCallerFrame() {
+            return null;
+        }
+
     }
 }