diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/TruffleRLanguage.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/TruffleRLanguage.java
index 6c98e3dd2b977f0b9916370373d985a7c723cf7b..7b91b6f22401363b844fb8316586e10d63202c20 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/TruffleRLanguage.java
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/TruffleRLanguage.java
@@ -28,11 +28,13 @@ import com.oracle.truffle.api.CallTarget;
 import com.oracle.truffle.api.CompilerAsserts;
 import com.oracle.truffle.api.Truffle;
 import com.oracle.truffle.api.TruffleLanguage;
+import com.oracle.truffle.api.frame.Frame;
 import com.oracle.truffle.api.frame.FrameDescriptor;
 import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.instrumentation.Instrumenter;
 import com.oracle.truffle.api.instrumentation.ProvidedTags;
 import com.oracle.truffle.api.instrumentation.StandardTags;
+import com.oracle.truffle.api.metadata.ScopeProvider;
 import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.api.nodes.RootNode;
 import com.oracle.truffle.api.source.SourceSection;
@@ -54,6 +56,7 @@ import com.oracle.truffle.r.runtime.context.RContext.RCloseable;
 import com.oracle.truffle.r.runtime.data.RFunction;
 import com.oracle.truffle.r.runtime.data.RPromise;
 import com.oracle.truffle.r.runtime.data.RTypedValue;
+import com.oracle.truffle.r.runtime.env.RScope;
 import com.oracle.truffle.r.runtime.ffi.RFFIFactory;
 import com.oracle.truffle.r.runtime.nodes.RBaseNode;
 
@@ -63,7 +66,7 @@ import com.oracle.truffle.r.runtime.nodes.RBaseNode;
  */
 @TruffleLanguage.Registration(name = "R", version = "0.1", mimeType = {RRuntime.R_APP_MIME, RRuntime.R_TEXT_MIME}, interactive = true)
 @ProvidedTags({StandardTags.CallTag.class, StandardTags.StatementTag.class, StandardTags.RootTag.class, RSyntaxTags.LoopTag.class})
-public final class TruffleRLanguage extends TruffleLanguage<RContext> {
+public final class TruffleRLanguage extends TruffleLanguage<RContext> implements ScopeProvider<RContext> {
 
     /**
      * The choice of {@link RFFIFactory} is made statically so that it is bound into an AOT-compiled
@@ -242,4 +245,9 @@ public final class TruffleRLanguage extends TruffleLanguage<RContext> {
     public RContext actuallyFindContext0(Node contextNode) {
         return findContext(contextNode);
     }
+
+    @Override
+    public AbstractScope findScope(RContext langContext, Node node, Frame frame) {
+        return RScope.createScope(node, frame);
+    }
 }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/env/RScope.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/env/RScope.java
new file mode 100644
index 0000000000000000000000000000000000000000..ced4f106a5be00264f3977fb9608df5ea6e5b75e
--- /dev/null
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/env/RScope.java
@@ -0,0 +1,320 @@
+/*
+ * 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.r.runtime.env;
+
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.frame.Frame;
+import com.oracle.truffle.api.interop.ForeignAccess;
+import com.oracle.truffle.api.interop.Message;
+import com.oracle.truffle.api.interop.MessageResolution;
+import com.oracle.truffle.api.interop.Resolve;
+import com.oracle.truffle.api.interop.TruffleObject;
+import com.oracle.truffle.api.interop.UnknownIdentifierException;
+import com.oracle.truffle.api.interop.UnsupportedMessageException;
+import com.oracle.truffle.api.metadata.ScopeProvider.AbstractScope;
+import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.r.runtime.ArgumentsSignature;
+import com.oracle.truffle.r.runtime.RArguments;
+import com.oracle.truffle.r.runtime.RInternalError;
+import com.oracle.truffle.r.runtime.data.RFunction;
+import com.oracle.truffle.r.runtime.data.RStringVector;
+import com.oracle.truffle.r.runtime.env.REnvironment.PutException;
+
+/**
+ * Represents a variable scope for external tools like a debugger.<br>
+ * This is basically a view on R environments.
+ */
+public final class RScope extends AbstractScope {
+
+    private final Node current;
+    private REnvironment env;
+
+    /**
+     * Intended to be used when creating a parent scope where we do not know any associated node.
+     */
+    private RScope(REnvironment env) {
+        this.env = env;
+        this.current = null;
+    }
+
+    private RScope(Node current, REnvironment env) {
+        this.current = current;
+        this.env = env;
+    }
+
+    @Override
+    protected String getName() {
+        // TODO promises (= closure)
+        return "function";
+    }
+
+    @Override
+    protected Node getNode() {
+        return current;
+    }
+
+    @Override
+    protected Object getVariables(Frame frame) {
+        return new VariablesMapObject(env, false);
+    }
+
+    private static REnvironment getEnv(Frame frame) {
+        assert RArguments.isRFrame(frame);
+        return REnvironment.frameToEnvironment(frame.materialize());
+    }
+
+    @Override
+    protected Object getArguments(Frame frame) {
+        return new VariablesMapObject(env, true);
+    }
+
+    @Override
+    protected AbstractScope findParent() {
+        if (this.env == REnvironment.emptyEnv()) {
+            return null;
+        }
+
+        return new RScope(env.getParent());
+    }
+
+    private static String[] ls(REnvironment env) {
+        RStringVector ls = env.ls(true, null, false);
+        return ls.getDataWithoutCopying();
+    }
+
+    private static String[] collectArgs(REnvironment env) {
+        ArgumentsSignature signature = RArguments.getSignature(env.getFrame());
+        return signature.getNames();
+    }
+
+    public static RScope createScope(Node node, Frame frame) {
+        return new RScope(node.getRootNode(), getEnv(frame));
+    }
+
+    private static Object getInteropValue(Object value) {
+        return value;
+    }
+
+    static final class VariablesMapObject implements TruffleObject {
+
+        private final REnvironment env;
+        private final boolean argumentsOnly;
+
+        private VariablesMapObject(REnvironment env, boolean argumentsOnly) {
+            this.env = env;
+            this.argumentsOnly = argumentsOnly;
+        }
+
+        @Override
+        public ForeignAccess getForeignAccess() {
+            return VariablesMapMessageResolutionForeign.ACCESS;
+        }
+
+        public static boolean isInstance(TruffleObject obj) {
+            return obj instanceof VariablesMapObject;
+        }
+
+        @MessageResolution(receiverType = VariablesMapObject.class)
+        static final class VariablesMapMessageResolution {
+
+            @Resolve(message = "KEYS")
+            abstract static class VarsMapKeysNode extends Node {
+
+                @TruffleBoundary
+                public Object access(VariablesMapObject varMap) {
+                    if (varMap.argumentsOnly) {
+                        return new ArgumentNamesObject(collectArgs(varMap.env));
+                    } else {
+                        return new VariableNamesObject(varMap.env);
+                    }
+                }
+            }
+
+            @Resolve(message = "KEY_INFO")
+            public abstract static class VarMapsKeyInfoNode extends Node {
+
+                private static final int READABLE = 1 << 1;
+                private static final int WRITABLE = 1 << 2;
+                private static final int INVOCABLE = 1 << 3;
+
+                @SuppressWarnings("try")
+                protected Object access(VariablesMapObject receiver, String identifier) {
+                    int info = READABLE;
+
+                    if (!receiver.env.bindingIsLocked(identifier)) {
+                        info += WRITABLE;
+                    }
+                    if (receiver.env.get(identifier) instanceof RFunction) {
+                        info += INVOCABLE;
+                    }
+                    return info;
+                }
+            }
+
+            @Resolve(message = "READ")
+            abstract static class VarsMapReadNode extends Node {
+
+                @TruffleBoundary
+                public Object access(VariablesMapObject varMap, String name) {
+                    if (varMap.env == null) {
+                        throw UnsupportedMessageException.raise(Message.READ);
+                    }
+                    Object value = varMap.env.get(name);
+
+                    // If Java-null is returned, the identifier does not exist !
+                    if (value == null) {
+                        throw UnknownIdentifierException.raise(name);
+                    } else {
+                        return getInteropValue(value);
+                    }
+                }
+            }
+
+            @Resolve(message = "WRITE")
+            abstract static class VarsMapWriteNode extends Node {
+
+                @TruffleBoundary
+                public Object access(VariablesMapObject varMap, String name, Object value) {
+                    if (varMap.env == null) {
+                        throw UnsupportedMessageException.raise(Message.WRITE);
+                    }
+                    try {
+                        varMap.env.put(name, value);
+                        return value;
+                    } catch (PutException e) {
+                        throw RInternalError.shouldNotReachHere(e);
+                    }
+                }
+            }
+
+        }
+    }
+
+    static final class VariableNamesObject implements TruffleObject {
+
+        private final REnvironment env;
+
+        private VariableNamesObject(REnvironment env) {
+            this.env = env;
+        }
+
+        @Override
+        public ForeignAccess getForeignAccess() {
+            return VariableNamesMessageResolutionForeign.ACCESS;
+        }
+
+        public static boolean isInstance(TruffleObject obj) {
+            return obj instanceof VariableNamesObject;
+        }
+
+        @MessageResolution(receiverType = VariableNamesObject.class)
+        static final class VariableNamesMessageResolution {
+
+            @Resolve(message = "HAS_SIZE")
+            abstract static class VarNamesHasSizeNode extends Node {
+
+                @SuppressWarnings("unused")
+                public Object access(VariableNamesObject varNames) {
+                    return true;
+                }
+            }
+
+            @Resolve(message = "GET_SIZE")
+            abstract static class VarNamesGetSizeNode extends Node {
+
+                public Object access(VariableNamesObject varNames) {
+                    return ls(varNames.env).length;
+                }
+            }
+
+            @Resolve(message = "READ")
+            abstract static class VarNamesReadNode extends Node {
+
+                @TruffleBoundary
+                public Object access(VariableNamesObject varNames, int index) {
+                    String[] names = ls(varNames.env);
+                    if (index >= 0 && index < names.length) {
+                        return names[index];
+                    } else {
+                        throw UnknownIdentifierException.raise(Integer.toString(index));
+                    }
+                }
+            }
+
+        }
+    }
+
+    static final class ArgumentNamesObject implements TruffleObject {
+
+        private final String[] names;
+
+        private ArgumentNamesObject(String[] names) {
+            this.names = names;
+        }
+
+        @Override
+        public ForeignAccess getForeignAccess() {
+            return VariableNamesMessageResolutionForeign.ACCESS;
+        }
+
+        public static boolean isInstance(TruffleObject obj) {
+            return obj instanceof ArgumentNamesObject;
+        }
+
+        @MessageResolution(receiverType = ArgumentNamesObject.class)
+        static final class ArgumentNamesMessageResolution {
+
+            @Resolve(message = "HAS_SIZE")
+            abstract static class ArgNamesHasSizeNode extends Node {
+
+                @SuppressWarnings("unused")
+                public Object access(ArgumentNamesObject varNames) {
+                    return true;
+                }
+            }
+
+            @Resolve(message = "GET_SIZE")
+            abstract static class ArgNamesGetSizeNode extends Node {
+
+                public Object access(ArgumentNamesObject varNames) {
+                    return varNames.names.length;
+                }
+            }
+
+            @Resolve(message = "READ")
+            abstract static class ArgNamesReadNode extends Node {
+
+                @TruffleBoundary
+                public Object access(ArgumentNamesObject varNames, int index) {
+                    if (index >= 0 && index < varNames.names.length) {
+                        return varNames.names[index];
+                    } else {
+                        throw UnknownIdentifierException.raise(Integer.toString(index));
+                    }
+                }
+            }
+
+        }
+    }
+
+}
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/tck/FastRDebugTest.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/tck/FastRDebugTest.java
index 5edb60b06faa3bfaa9edbc2bdcaaa386137adcee..cf4d6207f54c143c1a15b360595f851e546d86c9 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/tck/FastRDebugTest.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/tck/FastRDebugTest.java
@@ -29,7 +29,9 @@ import static org.junit.Assert.assertTrue;
 
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
+import java.util.HashSet;
 import java.util.LinkedList;
+import java.util.Set;
 import java.util.concurrent.atomic.AtomicInteger;
 
 import org.junit.After;
@@ -38,6 +40,7 @@ import org.junit.Before;
 import org.junit.Test;
 
 import com.oracle.truffle.api.debug.Breakpoint;
+import com.oracle.truffle.api.debug.DebugScope;
 import com.oracle.truffle.api.debug.DebugStackFrame;
 import com.oracle.truffle.api.debug.DebugValue;
 import com.oracle.truffle.api.debug.Debugger;
@@ -54,6 +57,7 @@ import com.oracle.truffle.r.runtime.context.ContextInfo;
 import com.oracle.truffle.r.runtime.context.DefaultConsoleHandler;
 import com.oracle.truffle.r.runtime.context.RContext.ContextKind;
 import com.oracle.truffle.r.runtime.data.RPromise.EagerPromise;
+import com.oracle.truffle.r.runtime.env.REnvironment;
 import com.oracle.truffle.r.runtime.env.frame.RFrameSlot;
 
 public class FastRDebugTest {
@@ -230,6 +234,136 @@ public class FastRDebugTest {
         assertExecutedOK();
     }
 
+    @Test
+    public void testScopeFunction() throws Throwable {
+        final Source srcFunMain = RSource.fromTextInternal("function () {\n" +
+                        "    i = 3L\n" +
+                        "    n = 15L\n" +
+                        "    str = \"hello\"\n" +
+                        "    i <- i + 1L\n" +
+                        "    ab <<- i\n" +
+                        "    i\n" +
+                        "}", RSource.Internal.DEBUGTEST_DEBUG);
+        final Source source = RSource.fromTextInternal("x <- 10L\n" +
+                        "makeActiveBinding('ab', function(v) { if(missing(v)) x else x <<- v }, .GlobalEnv)\n" +
+                        "main <- " + srcFunMain.getCode() + "\n",
+                        RSource.Internal.DEBUGTEST_DEBUG);
+        engine.eval(source);
+
+        // @formatter:on
+        run.addLast(() -> {
+            assertNull(suspendedEvent);
+            assertNotNull(debuggerSession);
+            debuggerSession.suspendNextExecution();
+        });
+
+        assertLocation(1, "main()", "x", 10, "ab", 10, "main", srcFunMain.getCode());
+        stepInto(1);
+        assertLocation(4, "i = 3L");
+        stepOver(1);
+        assertLocation(5, "n = 15L", "i", 3);
+        stepOver(1);
+        assertLocation(6, "str = \"hello\"", "i", 3, "n", 15);
+        stepOver(1);
+        assertLocation(7, "i <- i + 1L", "i", 3, "n", 15, "str", "hello");
+        stepOver(1);
+        assertLocation(8, "ab <<- i", "i", 4, "n", 15, "str", "hello");
+        stepOver(1);
+        assertScope(9, "i", true, false, "ab", 4, "x", 4);
+        stepOut();
+        assertLocation(1, "main()", "x", 4, "ab", 4, "main", srcFunMain.getCode());
+        performWork();
+
+        final Source evalSource = RSource.fromTextInternal("main()\n", RSource.Internal.DEBUGTEST_EVAL);
+        engine.eval(evalSource);
+
+        assertExecutedOK();
+    }
+
+    @Test
+    public void testScopePromise() throws Throwable {
+        final Source source = RSource.fromTextInternal("main <- function(e) {\n" +
+                        "   x <- 10L\n" +
+                        "   e()\n" +
+                        "   x\n" +
+                        "}\n" +
+                        "closure <- function() {\n" +
+                        "   x <<- 123L\n" +
+                        "   x\n" +
+                        "}\n",
+
+                        RSource.Internal.DEBUGTEST_DEBUG);
+        engine.eval(source);
+
+        // @formatter:on
+        run.addLast(() -> {
+            assertNull(suspendedEvent);
+            assertNotNull(debuggerSession);
+            debuggerSession.suspendNextExecution();
+        });
+
+        stepOver(1);
+        stepInto(1);
+        stepOver(1);
+        assertScope(3, "e()", false, false, "x", 10);
+        stepInto(1);
+        assertLocation(7, "x <<- 123L");
+        assertScope(7, "x <<- 123L", true, false, "x", 0);
+        stepOver(1);
+        assertScope(8, "x", true, false, "x", 123);
+        continueExecution();
+        performWork();
+
+        final Source evalSource = RSource.fromTextInternal("x <- 0L\nmain(closure)\n", RSource.Internal.DEBUGTEST_EVAL);
+        engine.eval(evalSource);
+
+        assertExecutedOK();
+    }
+
+    @Test
+    public void testChangedScopeChain() throws Throwable {
+        final Source source = RSource.fromTextInternal("main <- function(e) {\n" +
+                        "   x <- 10L\n" +
+                        "   environment(e) <- environment()\n" +
+                        "   e()\n" +
+                        "   x\n" +
+                        "}\n" +
+                        "closure <- function() {\n" +
+                        "   x <<- 123L\n" +
+                        "   x\n" +
+                        "}\n",
+                        RSource.Internal.DEBUGTEST_DEBUG);
+        engine.eval(source);
+
+        // @formatter:on
+        run.addLast(() -> {
+            assertNull(suspendedEvent);
+            assertNotNull(debuggerSession);
+            debuggerSession.suspendNextExecution();
+        });
+
+        stepOver(1);
+        stepInto(1);
+        stepOver(2);
+        assertScope(4, "e()", false, false, "x", 10);
+        stepInto(1);
+        assertLocation(8, "x <<- 123L");
+        assertScope(8, "x <<- 123L", true, false, "x", 10);
+        stepOver(1);
+        stepOut();
+        assertScope(9, "x", false, false, "x", 123);
+        assertIdentifiers(false, "x", "e");
+        stepOut();
+        assertScope(9, "x", false, false, "x", 0);
+        continueExecution();
+        performWork();
+
+        final Source evalSource = RSource.fromTextInternal("x <- 0L\nmain(closure)\n", RSource.Internal.DEBUGTEST_EVAL);
+        engine.eval(evalSource);
+
+        assertExecutedOK();
+    }
+
     private void performWork() {
         try {
             if (ex == null && !run.isEmpty()) {
@@ -246,7 +380,7 @@ public class FastRDebugTest {
     }
 
     private void stepOut() {
-        run.addLast(() -> suspendedEvent.prepareStepOut());
+        run.addLast(() -> suspendedEvent.prepareStepOut(1));
     }
 
     private void continueExecution() {
@@ -257,6 +391,30 @@ public class FastRDebugTest {
         run.addLast(() -> suspendedEvent.prepareStepInto(size));
     }
 
+    private void assertIdentifiers(boolean includeAncestors, String... identifiers) {
+        run.addLast(() -> {
+
+            final DebugStackFrame frame = suspendedEvent.getTopStackFrame();
+            DebugScope scope = frame.getScope();
+
+            Set<String> actualIdentifiers = new HashSet<>();
+            do {
+                scope.getDeclaredValues().forEach((x) -> actualIdentifiers.add(x.getName()));
+            } while (includeAncestors && scope != null && !REnvironment.baseEnv().getName().equals(scope.getName()));
+
+            Set<String> expected = new HashSet<>();
+            for (String s : identifiers) {
+                expected.add(s);
+            }
+
+            assertEquals(expected, actualIdentifiers);
+
+            if (!run.isEmpty()) {
+                run.removeFirst().run();
+            }
+        });
+    }
+
     private void assertLocation(final int line, final String code, final Object... expectedFrame) {
         run.addLast(() -> {
             try {
@@ -265,31 +423,33 @@ public class FastRDebugTest {
                 assertEquals(line, currentLine);
                 final String currentCode = suspendedEvent.getSourceSection().getCode().trim();
                 assertEquals(code, currentCode);
-                final DebugStackFrame frame = suspendedEvent.getTopStackFrame();
+                compareScope(line, code, false, true, expectedFrame);
+            } catch (RuntimeException | Error e) {
 
-                final AtomicInteger numFrameVars = new AtomicInteger(0);
+                final DebugStackFrame frame = suspendedEvent.getTopStackFrame();
                 frame.forEach(var -> {
-                    // skip synthetic slots
-                    for (RFrameSlot slot : RFrameSlot.values()) {
-                        if (slot.toString().equals(var.getName())) {
-                            return;
-                        }
-                    }
-                    numFrameVars.incrementAndGet();
+                    System.out.println(var);
                 });
-                assertEquals(line + ": " + code, expectedFrame.length / 2, numFrameVars.get());
-
-                for (int i = 0; i < expectedFrame.length; i = i + 2) {
-                    String expectedIdentifier = (String) expectedFrame[i];
-                    Object expectedValue = expectedFrame[i + 1];
-                    String expectedValueStr = (expectedValue != null) ? expectedValue.toString() : null;
-                    DebugValue value = frame.getValue(expectedIdentifier);
-                    assertNotNull(value);
-                    String valueStr = value.as(String.class);
-                    assertEquals(expectedValueStr, valueStr);
-                }
+                throw e;
+            }
+        });
+    }
 
-                run.removeFirst().run();
+    /**
+     * Ensure that the scope at a certain program position contains an expected set of key-value
+     * pairs.
+     *
+     * @param line line number
+     * @param code the code snippet of the program location
+     * @param includeAncestors Include current scope's ancestors for the identifier lookup.
+     * @param completeMatch {@code true} if the defined key-value pairs should be the only pairs in
+     *            the scope.
+     * @param expectedFrame the key-value pairs (e.g. {@code "id0", 1, "id1", "strValue"})
+     */
+    private void assertScope(final int line, final String code, boolean includeAncestors, boolean completeMatch, final Object... expectedFrame) {
+        run.addLast(() -> {
+            try {
+                compareScope(line, code, includeAncestors, completeMatch, expectedFrame);
             } catch (RuntimeException | Error e) {
 
                 final DebugStackFrame frame = suspendedEvent.getTopStackFrame();
@@ -301,6 +461,43 @@ public class FastRDebugTest {
         });
     }
 
+    private void compareScope(final int line, final String code, boolean includeAncestors, boolean completeMatch, final Object[] expectedFrame) {
+        final DebugStackFrame frame = suspendedEvent.getTopStackFrame();
+
+        final AtomicInteger numFrameVars = new AtomicInteger(0);
+        frame.forEach(var -> {
+            // skip synthetic slots
+            for (RFrameSlot slot : RFrameSlot.values()) {
+                if (slot.toString().equals(var.getName())) {
+                    return;
+                }
+            }
+            numFrameVars.incrementAndGet();
+        });
+        if (completeMatch) {
+            assertEquals(line + ": " + code, expectedFrame.length / 2, numFrameVars.get());
+        }
+
+        for (int i = 0; i < expectedFrame.length; i = i + 2) {
+            String expectedIdentifier = (String) expectedFrame[i];
+            Object expectedValue = expectedFrame[i + 1];
+            String expectedValueStr = (expectedValue != null) ? expectedValue.toString() : null;
+            DebugScope scope = frame.getScope();
+            DebugValue value;
+            do {
+                value = scope.getDeclaredValue(expectedIdentifier);
+                scope = scope.getParent();
+            } while (includeAncestors && value == null && scope != null && !REnvironment.baseEnv().getName().equals(scope.getName()));
+            assertNotNull("identifier \"" + expectedIdentifier + "\" not found", value);
+            String valueStr = value.as(String.class);
+            assertEquals(expectedValueStr, valueStr);
+        }
+
+        if (!run.isEmpty()) {
+            run.removeFirst().run();
+        }
+    }
+
     Object getRValue(Object value) {
         // This will only work in simple cases
         if (value instanceof EagerPromise) {
diff --git a/mx.fastr/suite.py b/mx.fastr/suite.py
index b915323c9f59225b929f763f466f1f47938b95e8..31410e15f94313f2528981b781c72c0cb33f5c0e 100644
--- a/mx.fastr/suite.py
+++ b/mx.fastr/suite.py
@@ -29,7 +29,7 @@ suite = {
             {
                "name" : "truffle",
                "subdir" : True,
-               "version" : "7d960d6682ef3636cc7455e28132c1e537ea81f0",
+               "version" : "acbe9ec935090e0824372e508563c122b0e46682",
                "urls" : [
                     {"url" : "https://github.com/graalvm/graal", "kind" : "git"},
                     {"url" : "https://curio.ssw.jku.at/nexus/content/repositories/snapshots", "kind" : "binary"},