From 5d355cd3f99626c0c525f06bade5d9308a3dede8 Mon Sep 17 00:00:00 2001
From: Tomas Stupka <tomas.stupka@oracle.com>
Date: Mon, 23 Jul 2018 15:50:20 +0200
Subject: [PATCH] implemented "get/get0" builtins with int envir argument

---
 .../truffle/r/nodes/builtin/base/Eval.java    |  20 +-
 .../r/nodes/builtin/base/FrameFunctions.java  |  44 ++-
 .../r/nodes/builtin/base/GetFunctions.java    |  16 +-
 .../truffle/r/test/ExpectedTestOutput.test    | 281 ++++++++++++++++++
 .../r/test/builtins/TestBuiltin_get.java      |  15 +
 .../r/test/builtins/TestBuiltin_sysframe.java |  16 +
 6 files changed, 372 insertions(+), 20 deletions(-)

diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Eval.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Eval.java
index 875b596843..48072851af 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Eval.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Eval.java
@@ -88,6 +88,10 @@ public abstract class Eval extends RBuiltinNode.Arg3 {
 
         @Child private RList2EnvNode rList2EnvNode;
 
+        public static EvalEnvCast create() {
+            return EvalEnvCastNodeGen.create();
+        }
+
         public abstract REnvironment execute(VirtualFrame frame, Object env, Object enclos);
 
         @Specialization
@@ -148,16 +152,16 @@ public abstract class Eval extends RBuiltinNode.Arg3 {
         }
 
         @Specialization
-        protected REnvironment cast(VirtualFrame frame, int envirIn, @SuppressWarnings("unused") Object enclos,
-                        @Cached("create()") SysFrame sysFrameNode) {
-            int envir = envirIn;
-            if (envirIn != 0) {
-                // because we are invoking SysFrame directly and normally SysFrame skips its
-                // .Internal frame
-                envir = envirIn < 0 ? envirIn + 1 : envirIn - 1;
-            }
+        protected REnvironment cast(VirtualFrame frame, int envir, @SuppressWarnings("unused") Object enclos,
+                        @Cached("createSysFrame()") SysFrame sysFrameNode) {
             return sysFrameNode.executeInt(frame, envir);
         }
+
+        protected static SysFrame createSysFrame() {
+            // SysFrame.create(skipDotInternal=true) because we are invoking SysFrame directly and
+            // normally SysFrame skips its .Internal frame
+            return SysFrame.create(true);
+        }
     }
 
     @Child private EvalEnvCast envCast = EvalEnvCastNodeGen.create();
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 f872ce6883..e617025e13 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
@@ -114,13 +114,23 @@ public class FrameFunctions {
          */
         private final FrameAccess access;
 
+        private final boolean ignoreDotInternal;
+
         public FrameHelper(FrameAccess access) {
+            this(access, false);
+        }
+
+        public FrameHelper(FrameAccess access, boolean ignoreDotInternal) {
             this.access = access;
+            this.ignoreDotInternal = ignoreDotInternal;
         }
 
         protected Frame getFrame(VirtualFrame frame, int n) {
-            int actualFrame = decodeFrameNumber(RArguments.getCall(frame), n);
-            return RInternalError.guaranteeNonNull(getNumberedFrame(frame, actualFrame));
+            RCaller c = RArguments.getCall(frame);
+            int actualFrame = decodeFrameNumber(c, n);
+            Frame numberedFrame = getNumberedFrame(frame, actualFrame);
+            Frame ret = RInternalError.guaranteeNonNull(numberedFrame);
+            return ret;
         }
 
         protected RCaller getCall(VirtualFrame frame, int n) {
@@ -135,7 +145,10 @@ public class FrameFunctions {
          */
         private int decodeFrameNumber(RCaller currentCall, int n) {
             RCaller call = currentCall;
-            call = call.getParent(); // skip the .Internal function
+            if (!ignoreDotInternal) {
+                call = call.getParent();
+            }
+
             while (call.isPromise()) {
                 call = call.getParent();
             }
@@ -417,10 +430,19 @@ public class FrameFunctions {
     @RBuiltin(name = "sys.frame", kind = INTERNAL, parameterNames = {"which"}, behavior = COMPLEX)
     public abstract static class SysFrame extends RBuiltinNode.Arg1 {
 
-        @Child private FrameHelper helper = new FrameHelper(FrameAccess.MATERIALIZE);
+        @Child private FrameHelper helper;
         @Child private PromiseDeoptimizeFrameNode deoptFrameNode = new PromiseDeoptimizeFrameNode();
 
         private final ConditionProfile zeroProfile = ConditionProfile.createBinaryProfile();
+        private final boolean skipDotInternal;
+
+        public SysFrame() {
+            this(false);
+        }
+
+        public SysFrame(boolean skipDotInternal) {
+            this.skipDotInternal = skipDotInternal;
+        }
 
         public abstract REnvironment executeInt(VirtualFrame frame, int which);
 
@@ -428,6 +450,10 @@ public class FrameFunctions {
             return SysFrameNodeGen.create();
         }
 
+        public static SysFrame create(boolean skipDotInternal) {
+            return SysFrameNodeGen.create(skipDotInternal);
+        }
+
         static {
             Casts casts = new Casts(SysFrame.class);
             casts.arg("which").asIntegerVector().findFirst();
@@ -439,7 +465,7 @@ public class FrameFunctions {
             if (zeroProfile.profile(which == 0)) {
                 result = REnvironment.globalEnv();
             } else {
-                Frame callerFrame = helper.getFrame(frame, which);
+                Frame callerFrame = getFrameHelper().getFrame(frame, which);
                 result = REnvironment.frameToEnvironment(callerFrame.materialize());
             }
 
@@ -447,6 +473,14 @@ public class FrameFunctions {
             deoptFrameNode.deoptimizeFrame(RArguments.getArguments(result.getFrame()));
             return result;
         }
+
+        private FrameHelper getFrameHelper() {
+            if (helper == null) {
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                helper = insert(new FrameHelper(FrameAccess.MATERIALIZE, skipDotInternal));
+            }
+            return helper;
+        }
     }
 
     @RBuiltin(name = "sys.frames", kind = INTERNAL, parameterNames = {}, behavior = COMPLEX)
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 e5313e931c..633928a8f7 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
@@ -44,6 +44,7 @@ import com.oracle.truffle.r.nodes.access.variables.ReadVariableNode;
 import com.oracle.truffle.r.nodes.attributes.TypeFromModeNode;
 import com.oracle.truffle.r.nodes.attributes.TypeFromModeNodeGen;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
+import com.oracle.truffle.r.nodes.builtin.base.Eval.EvalEnvCast;
 import com.oracle.truffle.r.nodes.function.PromiseHelperNode;
 import com.oracle.truffle.r.nodes.function.call.RExplicitCallNode;
 import com.oracle.truffle.r.nodes.objects.GetS4DataSlot;
@@ -51,7 +52,6 @@ import com.oracle.truffle.r.nodes.unary.CastNode;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
 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;
@@ -189,10 +189,11 @@ public class GetFunctions {
             return get(frame, x, (REnvironment) s4ToEnv.execute(s4Envir), mode, inherits);
         }
 
-        @SuppressWarnings("unused")
         @Specialization
-        protected Object get(VirtualFrame frame, String x, int envir, String mode, boolean inherits) {
-            throw RInternalError.unimplemented();
+        protected Object get(VirtualFrame frame, String x, int envir, String mode, boolean inherits,
+                        @Cached("create()") EvalEnvCast envCast) {
+            Object env = envCast.execute(frame, envir, RMissing.instance);
+            return get(frame, x, (REnvironment) env, mode, inherits);
         }
     }
 
@@ -233,10 +234,11 @@ public class GetFunctions {
             return get0(frame, x, (REnvironment) s4ToEnv.execute(s4Envir), mode, inherits, ifnotfound);
         }
 
-        @SuppressWarnings("unused")
         @Specialization
-        protected Object get(VirtualFrame frame, String x, int envir, String mode, boolean inherits, Object ifnotfound) {
-            throw RInternalError.unimplemented();
+        protected Object get0(VirtualFrame frame, String x, int envir, String mode, boolean inherits, Object ifnotfound,
+                        @Cached("create()") EvalEnvCast envCast) {
+            Object env = envCast.execute(frame, envir, RMissing.instance);
+            return get0(frame, x, (REnvironment) env, mode, inherits, ifnotfound);
         }
     }
 
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 11943a19ec..480198d745 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
@@ -28595,6 +28595,62 @@ In gamma(argv[[1]]) : NaNs produced
 #setClass('foo', representation(x='numeric')); f <- new('foo'); e <- new.env(); e$x <- 1; attr(f, '.xData') <- e; get('x', envir=f)
 [1] 1
 
+##com.oracle.truffle.r.test.builtins.TestBuiltin_get.testGet#
+#{ f <- function() { xx <- 'xv'; f1 <- function() get('xx', envir = -1); f1()}; f() }
+[1] "xv"
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_get.testGet#
+#{ f <- function() { xx <- 'xv'; f1 <- function() get('xx', envir = -2); f1()}; f() }
+[1] "xv"
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_get.testGet#
+#{ f <- function() { xx <- 'xv'; f1 <- function() get('xx', envir = -3); f1()}; f() }
+Error in get("xx", envir = -3) : object 'xx' not found
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_get.testGet#
+#{ f <- function() { xx <- 'xv'; f1 <- function() get('xx', envir = 0); f1()}; f() }
+Error in get("xx", envir = 0) : object 'xx' not found
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_get.testGet#
+#{ f <- function() { xx <- 'xv'; f1 <- function() get('xx', envir = 1); f1()}; f() }
+[1] "xv"
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_get.testGet#
+#{ f <- function() { xx <- 'xv'; f1 <- function() get('xx', envir = 2); f1()}; f() }
+[1] "xv"
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_get.testGet#
+#{ f <- function() { xx <- 'xv'; f1 <- function() get('xx', envir = 3); f1()}; f() }
+Error in get("xx", envir = 3) : object 'xx' not found
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_get.testGet#
+#{ f <- function() { xx <- 'xv'; f1 <- function() get0('xx', envir = -1, ifnotfound = 'DNF'); f1()}; f() }
+[1] "xv"
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_get.testGet#
+#{ f <- function() { xx <- 'xv'; f1 <- function() get0('xx', envir = -2, ifnotfound = 'DNF'); f1()}; f() }
+[1] "xv"
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_get.testGet#
+#{ f <- function() { xx <- 'xv'; f1 <- function() get0('xx', envir = -3, ifnotfound = 'DNF'); f1()}; f() }
+[1] "DNF"
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_get.testGet#
+#{ f <- function() { xx <- 'xv'; f1 <- function() get0('xx', envir = 0, ifnotfound = 'DNF'); f1()}; f() }
+[1] "DNF"
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_get.testGet#
+#{ f <- function() { xx <- 'xv'; f1 <- function() get0('xx', envir = 1, ifnotfound = 'DNF'); f1()}; f() }
+[1] "xv"
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_get.testGet#
+#{ f <- function() { xx <- 'xv'; f1 <- function() get0('xx', envir = 2, ifnotfound = 'DNF'); f1()}; f() }
+[1] "xv"
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_get.testGet#
+#{ f <- function() { xx <- 'xv'; f1 <- function() get0('xx', envir = 3, ifnotfound = 'DNF'); f1()}; f() }
+[1] "DNF"
+
 ##com.oracle.truffle.r.test.builtins.TestBuiltin_get.testGet#
 #{ get(".Platform")$endian }
 [1] "little"
@@ -28607,6 +28663,26 @@ In gamma(argv[[1]]) : NaNs produced
 #{ get("dummy") }
 Error in get("dummy") : object 'dummy' not found
 
+##com.oracle.truffle.r.test.builtins.TestBuiltin_get.testGet#
+#{ x <- 'xv'; get('x', envir = -1) }
+[1] "xv"
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_get.testGet#
+#{ x <- 'xv'; get('x', envir = -2) }
+Error in get("x", envir = -2) : not that many frames on the stack
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_get.testGet#
+#{ x <- 'xv'; get('x', envir = 0) }
+[1] "xv"
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_get.testGet#
+#{ x <- 'xv'; get('x', envir = 1) }
+[1] "x"
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_get.testGet#
+#{ x <- 'xv'; get('x', envir = 2) }
+Error in get("x", envir = 2) : not that many frames on the stack
+
 ##com.oracle.truffle.r.test.builtins.TestBuiltin_get.testGet#
 #{ x <- 33 ; f <- function() { get("x", inherits = FALSE) } ; f() }
 Error in get("x", inherits = FALSE) : object 'x' not found
@@ -28615,6 +28691,106 @@ Error in get("x", inherits = FALSE) : object 'x' not found
 #{ x <- 33 ; f <- function() { if (FALSE) { x <- 22  } ; get("x", inherits = FALSE) } ; f() }
 Error in get("x", inherits = FALSE) : object 'x' not found
 
+##com.oracle.truffle.r.test.builtins.TestBuiltin_get.testGet#
+#{ xx <- 'xv'; f <- function() { get('xx', envir = -1)}; f() }
+[1] "xv"
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_get.testGet#
+#{ xx <- 'xv'; f <- function() { get('xx', envir = -2)}; f() }
+[1] "xv"
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_get.testGet#
+#{ xx <- 'xv'; f <- function() { get('xx', envir = -3)}; f() }
+Error in get("xx", envir = -3) : not that many frames on the stack
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_get.testGet#
+#{ xx <- 'xv'; f <- function() { get('xx', envir = 0)}; f() }
+[1] "xv"
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_get.testGet#
+#{ xx <- 'xv'; f <- function() { get('xx', envir = 1)}; f() }
+[1] "xv"
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_get.testGet#
+#{ xx <- 'xv'; f <- function() { get('xx', envir = 2)}; f() }
+[1] "xv"
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_get.testGet#
+#{ xx <- 'xv'; f <- function() { get('xx', envir = 3)}; f() }
+Error in get("xx", envir = 3) : not that many frames on the stack
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_get.testGet#
+#{ xx <- 'xv'; f <- function() { get0('xx', envir = -1, ifnotfound = 'DNF')}; f() }
+[1] "xv"
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_get.testGet#
+#{ xx <- 'xv'; f <- function() { get0('xx', envir = -2, ifnotfound = 'DNF')}; f() }
+[1] "xv"
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_get.testGet#
+#{ xx <- 'xv'; f <- function() { get0('xx', envir = -3, ifnotfound = 'DNF')}; f() }
+Error in get0("xx", envir = -3, ifnotfound = "DNF") :
+  not that many frames on the stack
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_get.testGet#
+#{ xx <- 'xv'; f <- function() { get0('xx', envir = 0, ifnotfound = 'DNF')}; f() }
+[1] "xv"
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_get.testGet#
+#{ xx <- 'xv'; f <- function() { get0('xx', envir = 1, ifnotfound = 'DNF')}; f() }
+[1] "xv"
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_get.testGet#
+#{ xx <- 'xv'; f <- function() { get0('xx', envir = 2, ifnotfound = 'DNF')}; f() }
+[1] "xv"
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_get.testGet#
+#{ xx <- 'xv'; f <- function() { get0('xx', envir = 3, ifnotfound = 'DNF')}; f() }
+Error in get0("xx", envir = 3, ifnotfound = "DNF") :
+  not that many frames on the stack
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_get.testGet#
+#{ xx <- 'xv'; get('xx', envir =-1) }
+[1] "xv"
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_get.testGet#
+#{ xx <- 'xv'; get('xx', envir =-2) }
+Error in get("xx", envir = -2) : not that many frames on the stack
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_get.testGet#
+#{ xx <- 'xv'; get('xx', envir =0) }
+[1] "xv"
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_get.testGet#
+#{ xx <- 'xv'; get('xx', envir =1) }
+[1] "xv"
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_get.testGet#
+#{ xx <- 'xv'; get('xx', envir =2) }
+Error in get("xx", envir = 2) : not that many frames on the stack
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_get.testGet#
+#{ xx <- 'xv'; get0('xx', envir = -1, ifnotfound = 'DNF') }
+[1] "xv"
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_get.testGet#
+#{ xx <- 'xv'; get0('xx', envir = -2, ifnotfound = 'DNF') }
+Error in get0("xx", envir = -2, ifnotfound = "DNF") :
+  not that many frames on the stack
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_get.testGet#
+#{ xx <- 'xv'; get0('xx', envir = 0, ifnotfound = 'DNF') }
+[1] "xv"
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_get.testGet#
+#{ xx <- 'xv'; get0('xx', envir = 1, ifnotfound = 'DNF') }
+[1] "xv"
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_get.testGet#
+#{ xx <- 'xv'; get0('xx', envir = 2, ifnotfound = 'DNF') }
+Error in get0("xx", envir = 2, ifnotfound = "DNF") :
+  not that many frames on the stack
+
 ##com.oracle.truffle.r.test.builtins.TestBuiltin_get.testGet#
 #{x <- 1L; get('x', mode='double'); }
 [1] 1
@@ -75424,6 +75600,111 @@ v()
 [1] "x"
 
 
+##com.oracle.truffle.r.test.builtins.TestBuiltin_sysframe.sysFrameViaEval#
+#{ f <- function() { xx <- 'xv'; f1 <- function() get('xx', envir = -1);  xy <- new.env(); xy$xx <- 'aa'; eval(parse(text='f1()'), envir=xy)}; f() }
+[1] "xv"
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_sysframe.sysFrameViaEval#
+#{ f <- function() { xx <- 'xv'; f1 <- function() get('xx', envir = -2);  xy <- new.env(); xy$xx <- 'aa'; eval(parse(text='f1()'), envir=xy)}; f() }
+[1] "aa"
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_sysframe.sysFrameViaEval#
+#{ f <- function() { xx <- 'xv'; f1 <- function() get('xx', envir = -3);  xy <- new.env(); xy$xx <- 'aa'; eval(parse(text='f1()'), envir=xy)}; f() }
+Error in get("xx", envir = -3) : object 'xx' not found
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_sysframe.sysFrameViaEval#
+#{ f <- function() { xx <- 'xv'; f1 <- function() get('xx', envir = -4);  xy <- new.env(); xy$xx <- 'aa'; eval(parse(text='f1()'), envir=xy)}; f() }
+[1] "xv"
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_sysframe.sysFrameViaEval#
+#{ f <- function() { xx <- 'xv'; f1 <- function() get('xx', envir = -6);  xy <- new.env(); xy$xx <- 'aa'; eval(parse(text='f1()'), envir=xy)}; f() }
+Error in get("xx", envir = -6) : not that many frames on the stack
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_sysframe.sysFrameViaEval#
+#{ f <- function() { xx <- 'xv'; f1 <- function() get('xx', envir = 0);  xy <- new.env(); xy$xx <- 'aa'; eval(parse(text='f1()'), envir=xy)}; f() }
+Error in get("xx", envir = 0) : object 'xx' not found
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_sysframe.sysFrameViaEval#
+#{ f <- function() { xx <- 'xv'; f1 <- function() get('xx', envir = 1);  xy <- new.env(); xy$xx <- 'aa'; eval(parse(text='f1()'), envir=xy)}; f() }
+[1] "xv"
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_sysframe.sysFrameViaEval#
+#{ f <- function() { xx <- 'xv'; f1 <- function() get('xx', envir = 2);  xy <- new.env(); xy$xx <- 'aa'; eval(parse(text='f1()'), envir=xy)}; f() }
+Error in get("xx", envir = 2) : object 'xx' not found
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_sysframe.sysFrameViaEval#
+#{ f <- function() { xx <- 'xv'; f1 <- function() get('xx', envir = 3);  xy <- new.env(); xy$xx <- 'aa'; eval(parse(text='f1()'), envir=xy)}; f() }
+[1] "aa"
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_sysframe.sysFrameViaEval#
+#{ f <- function() { xx <- 'xv'; f1 <- function() get('xx', envir = 4);  xy <- new.env(); xy$xx <- 'aa'; eval(parse(text='f1()'), envir=xy)}; f() }
+[1] "xv"
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_sysframe.sysFrameViaEval#
+#{ f <- function() { xx <- 'xv'; f1 <- function() get('xx', envir = 5);  xy <- new.env(); xy$xx <- 'aa'; eval(parse(text='f1()'), envir=xy)}; f() }
+Error in get("xx", envir = 5) : object 'xx' not found
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_sysframe.sysFrameViaEval#
+#{ f <- function() { xx <- 'xv'; f1 <- function() get('xx', envir = 6);  xy <- new.env(); xy$xx <- 'aa'; eval(parse(text='f1()'), envir=xy)}; f() }
+Error in get("xx", envir = 6) : not that many frames on the stack
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_sysframe.sysFrameViaEval#
+#{ f <- function() { xx <- 'xv'; f1 <- function() ls(sys.frame(-1));  eval(parse(text='f1()'), envir=environment())}; f() }
+[1] "f1" "xx"
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_sysframe.sysFrameViaEval#Ignored.ImplementationError#
+#{ f <- function() { xx <- 'xv'; f1 <- function() ls(sys.frame(-2));  eval(parse(text='f1()'), envir=environment())}; f() }
+[1] "enclos" "envir"  "expr"
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_sysframe.sysFrameViaEval#Ignored.ImplementationError#
+#{ f <- function() { xx <- 'xv'; f1 <- function() ls(sys.frame(-3));  eval(parse(text='f1()'), envir=environment())}; f() }
+[1] "f1" "xx"
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_sysframe.sysFrameViaEval#Ignored.ImplementationError#
+#{ f <- function() { xx <- 'xv'; f1 <- function() ls(sys.frame(-4));  eval(parse(text='f1()'), envir=environment())}; f() }
+[1] "f"
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_sysframe.sysFrameViaEval#Ignored.ImplementationError#
+#{ f <- function() { xx <- 'xv'; f1 <- function() ls(sys.frame(-6));  eval(parse(text='f1()'), envir=environment())}; f() }
+Error in as.environment(pos) :
+  no item called "sys.frame(-6)" on the search list
+In addition: Warning message:
+In ls(sys.frame(-6)) : ‘sys.frame(-6)’ converted to character string
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_sysframe.sysFrameViaEval#
+#{ f <- function() { xx <- 'xv'; f1 <- function() ls(sys.frame(0));  eval(parse(text='f1()'), envir=environment())}; f() }
+[1] "f"
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_sysframe.sysFrameViaEval#
+#{ f <- function() { xx <- 'xv'; f1 <- function() ls(sys.frame(1));  eval(parse(text='f1()'), envir=environment())}; f() }
+[1] "f1" "xx"
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_sysframe.sysFrameViaEval#Ignored.ImplementationError#
+#{ f <- function() { xx <- 'xv'; f1 <- function() ls(sys.frame(2));  eval(parse(text='f1()'), envir=environment())}; f() }
+[1] "enclos" "envir"  "expr"
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_sysframe.sysFrameViaEval#Ignored.ImplementationError#
+#{ f <- function() { xx <- 'xv'; f1 <- function() ls(sys.frame(3));  eval(parse(text='f1()'), envir=environment())}; f() }
+[1] "f1" "xx"
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_sysframe.sysFrameViaEval#Ignored.ImplementationError#
+#{ f <- function() { xx <- 'xv'; f1 <- function() ls(sys.frame(4));  eval(parse(text='f1()'), envir=environment())}; f() }
+character(0)
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_sysframe.sysFrameViaEval#Ignored.ImplementationError#
+#{ f <- function() { xx <- 'xv'; f1 <- function() ls(sys.frame(5));  eval(parse(text='f1()'), envir=environment())}; f() }
+Error in as.environment(pos) :
+  no item called "sys.frame(5)" on the search list
+In addition: Warning message:
+In ls(sys.frame(5)) : ‘sys.frame(5)’ converted to character string
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_sysframe.sysFrameViaEval#Ignored.ImplementationError#
+#{ f <- function() { xx <- 'xv'; f1 <- function() ls(sys.frame(6));  eval(parse(text='f1()'), envir=environment())}; f() }
+Error in as.environment(pos) :
+  no item called "sys.frame(6)" on the search list
+In addition: Warning message:
+In ls(sys.frame(6)) : ‘sys.frame(6)’ converted to character string
+
 ##com.oracle.truffle.r.test.builtins.TestBuiltin_sysframe.sysFrameWithPromises#
 #{ top <- function(vtop) vtop;foo <- function(vfoo) top(vfoo);boo <- function(vboo) foo(sys.frame(vboo));bar <- function(vbar) do.call(boo, list(vbar), envir = parent.frame(2));baz <- function(vbaz) bar(vbaz);start <- function(vstart) baz(vstart);lapply(lapply(0:8, function(i) start(i)), function(env) sort(tolower(ls(env)))); }
 [[1]]
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_get.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_get.java
index 78e5b30b84..ccbb403f7b 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_get.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_get.java
@@ -27,6 +27,9 @@ import com.oracle.truffle.r.test.TestBase;
 // Checkstyle: stop line length check
 public class TestBuiltin_get extends TestBase {
 
+    private final String[] envirValues2 = {"-2", "-1", "0", "1", "2"};
+    private final String[] envirValues3 = {"-3", "-2", "-1", "0", "1", "2", "3"};
+
     @Test
     public void testGet() {
         assertEval("{y<-function(){y<-2;get(\"y\",mode=\"integer\")};y();}");
@@ -47,5 +50,17 @@ public class TestBuiltin_get extends TestBase {
 
         assertEval("{x <- 1L; get('x', mode='numeric'); }");
         assertEval("{x <- 1L; get('x', mode='double'); }");
+
+        // get('x', envir = 0) => [1] "xv"
+        // get('x', envir = 1) => [1] "x"
+        assertEval(template("{ x <- 'xv'; get('x', envir = %0) }", envirValues2));
+
+        assertEval(template("{ xx <- 'xv'; get('xx', envir =%0) }", envirValues2));
+        assertEval(template("{ xx <- 'xv'; f <- function() { get('xx', envir = %0)}; f() }", envirValues3));
+        assertEval(template("{ f <- function() { xx <- 'xv'; f1 <- function() get('xx', envir = %0); f1()}; f() }", envirValues3));
+
+        assertEval(template("{ xx <- 'xv'; get0('xx', envir = %0, ifnotfound = 'DNF') }", envirValues2));
+        assertEval(template("{ xx <- 'xv'; f <- function() { get0('xx', envir = %0, ifnotfound = 'DNF')}; f() }", envirValues3));
+        assertEval(template("{ f <- function() { xx <- 'xv'; f1 <- function() get0('xx', envir = %0, ifnotfound = 'DNF'); f1()}; f() }", envirValues3));
     }
 }
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_sysframe.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_sysframe.java
index 73cee66097..7ba7a4d9ae 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_sysframe.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_sysframe.java
@@ -46,4 +46,20 @@ public class TestBuiltin_sysframe extends TestBase {
                                         "start <- function(vstart) baz(vstart);" +
                                         "lapply(lapply(0:8, function(i) start(i)), function(env) sort(tolower(ls(env)))); }");
     }
+
+    private final String[] envirValues = {"-6", "-4", "-3", "-2", "-1", "0", "1", "2", "3", "4", "5", "6"};
+
+    @Test
+    public void sysFrameViaEval() {
+        assertEval(template("{ f <- function() { xx <- 'xv'; f1 <- function() get('xx', envir = %0);  xy <- new.env(); xy$xx <- 'aa'; eval(parse(text='f1()'), envir=xy)}; f() }", envirValues));
+
+        for (String envirValue : envirValues) {
+            String input = "{ f <- function() { xx <- 'xv'; f1 <- function() ls(sys.frame(" + envirValue + "));  eval(parse(text='f1()'), envir=environment())}; f() }";
+            if (Math.abs(Integer.parseInt(envirValue)) > 1) {
+                assertEval(Ignored.ImplementationError, input);
+            } else {
+                assertEval(input);
+            }
+        }
+    }
 }
-- 
GitLab