diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/EngineRootNode.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/EngineRootNode.java
new file mode 100644
index 0000000000000000000000000000000000000000..5420becd7a23b9ca41c63fa8f75858f9aa833a65
--- /dev/null
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/EngineRootNode.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (c) 2018, 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.engine;
+
+import java.util.List;
+import java.util.Set;
+
+import com.oracle.truffle.api.CompilerDirectives;
+import com.oracle.truffle.api.Truffle;
+import com.oracle.truffle.api.TruffleLanguage.ContextReference;
+import com.oracle.truffle.api.frame.MaterializedFrame;
+import com.oracle.truffle.api.frame.VirtualFrame;
+import com.oracle.truffle.api.instrumentation.InstrumentableNode;
+import com.oracle.truffle.api.instrumentation.ProbeNode;
+import com.oracle.truffle.api.instrumentation.Tag;
+import com.oracle.truffle.api.nodes.DirectCallNode;
+import com.oracle.truffle.api.nodes.ExplodeLoop;
+import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.api.nodes.RootNode;
+import com.oracle.truffle.api.source.SourceSection;
+import com.oracle.truffle.r.runtime.ExitException;
+import com.oracle.truffle.r.runtime.JumpToTopLevelException;
+import com.oracle.truffle.r.runtime.RError;
+import com.oracle.truffle.r.runtime.RInternalError;
+import com.oracle.truffle.r.runtime.RSource;
+import com.oracle.truffle.r.runtime.ReturnException;
+import com.oracle.truffle.r.runtime.Utils.DebugExitException;
+import com.oracle.truffle.r.runtime.context.RContext;
+import com.oracle.truffle.r.runtime.data.RNull;
+import com.oracle.truffle.r.runtime.interop.R2Foreign;
+import com.oracle.truffle.r.runtime.interop.R2ForeignNodeGen;
+import com.oracle.truffle.r.runtime.nodes.RNode;
+import com.oracle.truffle.r.runtime.nodes.RSyntaxNode;
+
+class EngineRootNode extends RootNode {
+    private final SourceSection sourceSection;
+
+    private final MaterializedFrame executionFrame;
+    private final ContextReference<RContext> contextReference;
+
+    @Child private EngineBodyNode bodyNode;
+    @Child private R2Foreign r2Foreign = R2ForeignNodeGen.create();
+
+    EngineRootNode(EngineBodyNode bodyNode, RContext context, SourceSection sourceSection, MaterializedFrame executionFrame) {
+        super(context.getLanguage());
+        this.sourceSection = sourceSection;
+        this.bodyNode = bodyNode;
+        this.executionFrame = executionFrame;
+        this.contextReference = context.getLanguage().getContextReference();
+    }
+
+    public static EngineRootNode createEngineRoot(REngine engine, RContext context, List<RSyntaxNode> statements, SourceSection sourceSection, MaterializedFrame executionFrame) {
+        return new EngineRootNode(new EngineBodyNode(engine, statements, getPrintResult(sourceSection)), context, sourceSection, executionFrame);
+    }
+
+    /**
+     * The normal {@link REngine#doMakeCallTarget} happens first, then we actually run the call
+     * using the standard FastR machinery, saving and restoring the {@link RContext}, since we have
+     * no control over what that might be when the call is initiated.
+     */
+    @Override
+    public Object execute(VirtualFrame frame) {
+        Object actualFrame = executionFrame != null ? executionFrame : contextReference.get().stateREnvironment.getGlobalFrame();
+        try {
+            return r2Foreign.execute(this.bodyNode.execute(actualFrame));
+        } catch (ReturnException ex) {
+            return ex.getResult();
+        } catch (DebugExitException | JumpToTopLevelException | ExitException | ThreadDeath e) {
+            CompilerDirectives.transferToInterpreter();
+            throw e;
+        } catch (RError e) {
+            CompilerDirectives.transferToInterpreter();
+            throw e;
+        } catch (Throwable t) {
+            CompilerDirectives.transferToInterpreter();
+            // other errors didn't produce an output yet
+            RInternalError.reportError(t);
+            throw t;
+        }
+    }
+
+    @Override
+    public SourceSection getSourceSection() {
+        return sourceSection;
+    }
+
+    public static boolean isEngineBody(Object node) {
+        return node instanceof EngineBodyNode;
+    }
+
+    private static boolean getPrintResult(SourceSection sourceSection) {
+        // can't print if initializing the system in embedded mode (no builtins yet)
+        return !sourceSection.getSource().getName().equals(RSource.Internal.INIT_EMBEDDED.string) && sourceSection.getSource().isInteractive();
+    }
+
+    private static final class EngineBodyNode extends Node implements InstrumentableNode {
+
+        private final REngine engine;
+        private final List<RSyntaxNode> statements;
+        @Children protected final DirectCallNode[] calls;
+        private final boolean printResult;
+
+        EngineBodyNode(REngine engine, List<RSyntaxNode> statements, boolean printResult) {
+            this.engine = engine;
+            this.statements = statements;
+            this.calls = new DirectCallNode[statements.size()];
+            this.printResult = printResult;
+        }
+
+        @ExplodeLoop
+        Object execute(Object actualFrame) {
+            Object lastValue = RNull.instance;
+            int lastStatus = 0;
+            for (int i = 0; i < calls.length; i++) {
+                materializeCall(i);
+                lastValue = calls[i].call(new Object[]{actualFrame});
+            }
+            return lastValue;
+        }
+
+        @Override
+        public boolean isInstrumentable() {
+            return false;
+        }
+
+        @Override
+        public WrapperNode createWrapper(ProbeNode probe) {
+            return null;
+        }
+
+        @Override
+        public boolean hasTag(Class<? extends Tag> tag) {
+            return false;
+        }
+
+        @Override
+        public InstrumentableNode materializeInstrumentableNodes(Set<Class<? extends Tag>> materializedTags) {
+            for (int i = 0; i < calls.length; i++) {
+                materializeCall(i);
+            }
+            return this;
+        }
+
+        protected void materializeCall(int i) {
+            if (calls[i] == null) {
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                RNode node = statements.get(i).asRNode();
+                calls[i] = insert(Truffle.getRuntime().createDirectCallNode(engine.doMakeCallTarget(node, RSource.Internal.REPL_WRAPPER.string, printResult, true)));
+            }
+        }
+    }
+}
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/REngine.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/REngine.java
index 371e16066c0e012dc4f5fc198061ad7b30226b0b..8bfbe34c6cad6cb7fdc336d9096ea8256a675d21 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/REngine.java
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/REngine.java
@@ -22,6 +22,11 @@
  */
 package com.oracle.truffle.r.engine;
 
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.URI;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 import java.util.stream.Collectors;
@@ -33,16 +38,14 @@ import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.RootCallTarget;
 import com.oracle.truffle.api.Truffle;
-import com.oracle.truffle.api.TruffleLanguage.ContextReference;
 import com.oracle.truffle.api.dsl.UnsupportedSpecializationException;
 import com.oracle.truffle.api.frame.Frame;
 import com.oracle.truffle.api.frame.MaterializedFrame;
 import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.interop.TruffleObject;
 import com.oracle.truffle.api.interop.java.JavaInterop;
-import com.oracle.truffle.api.nodes.DirectCallNode;
 import com.oracle.truffle.api.nodes.ExecutableNode;
-import com.oracle.truffle.api.nodes.ExplodeLoop;
+import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.api.nodes.RootNode;
 import com.oracle.truffle.api.profiles.ValueProfile;
 import com.oracle.truffle.api.source.Source;
@@ -78,6 +81,7 @@ import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.RSource;
 import com.oracle.truffle.r.runtime.ReturnException;
 import com.oracle.truffle.r.runtime.RootWithBody;
+import com.oracle.truffle.r.runtime.RootBodyNode;
 import com.oracle.truffle.r.runtime.ThreadTimings;
 import com.oracle.truffle.r.runtime.Utils;
 import com.oracle.truffle.r.runtime.Utils.DebugExitException;
@@ -97,7 +101,6 @@ import com.oracle.truffle.r.runtime.data.RTypedValue;
 import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
 import com.oracle.truffle.r.runtime.env.REnvironment;
 import com.oracle.truffle.r.runtime.interop.R2Foreign;
-import com.oracle.truffle.r.runtime.interop.R2ForeignNodeGen;
 import com.oracle.truffle.r.runtime.nodes.RNode;
 import com.oracle.truffle.r.runtime.nodes.RSyntaxNode;
 
@@ -254,7 +257,7 @@ final class REngine implements Engine, Engine.Timings {
 
     @Override
     public Object parseAndEval(Source source, MaterializedFrame frame, boolean printResult) throws ParseException {
-        List<RSyntaxNode> list = parseImpl(source);
+        List<RSyntaxNode> list = parseSource(source);
         try {
             Object lastValue = RNull.instance;
             for (RSyntaxNode node : list) {
@@ -282,21 +285,23 @@ final class REngine implements Engine, Engine.Timings {
         }
     }
 
-    private List<RSyntaxNode> parseImpl(Source source) throws ParseException {
+    List<RSyntaxNode> parseSource(Source source) throws ParseException {
         RParserFactory.Parser<RSyntaxNode> parser = RParserFactory.getParser();
         return parser.script(source, new RASTBuilder(), context.getLanguage());
     }
 
     @Override
     public RExpression parse(Source source) throws ParseException {
-        List<RSyntaxNode> list = parseImpl(source);
+        List<RSyntaxNode> list = parseSource(source);
         Object[] data = list.stream().map(node -> RASTUtils.createLanguageElement(node)).toArray();
         return RDataFactory.createExpression(data);
     }
 
     @Override
     public CallTarget parseToCallTarget(Source source, MaterializedFrame executionFrame) throws ParseException {
-        if (source == Engine.GET_CONTEXT) {
+        if (source.getPath() != null && !source.isInteractive()) {
+            return Truffle.getRuntime().createCallTarget(createRScriptRoot(source, executionFrame));
+        } else if (source == Engine.GET_CONTEXT) {
             /*
              * The "get context" operations should be executed with as little influence on the
              * actual engine as possible, therefore this special case takes care of it explicitly.
@@ -313,20 +318,21 @@ final class REngine implements Engine, Engine.Timings {
                 }
             });
         } else {
-            List<RSyntaxNode> statements = parseImpl(source);
-            return Truffle.getRuntime().createCallTarget(new PolyglotEngineRootNode(statements, createSourceSection(source, statements), executionFrame));
+            List<RSyntaxNode> statements = parseSource(source);
+            EngineRootNode rootNode = EngineRootNode.createEngineRoot(this, context, statements, createSourceSection(source, statements), executionFrame);
+            return Truffle.getRuntime().createCallTarget(rootNode);
         }
     }
 
     @Override
     public ExecutableNode parseToExecutableNode(Source source) throws ParseException {
-        List<RSyntaxNode> list = parseImpl(source);
+        List<RSyntaxNode> list = parseSource(source);
         RNode[] statements = new RNode[list.size()];
         for (int i = 0; i < statements.length; i++) {
             statements[i] = list.get(i).asRNode();
         }
         return new ExecutableNode(context.getLanguage()) {
-            @Child R2Foreign toForeignNode = R2Foreign.create();
+            @Child private R2Foreign toForeignNode = R2Foreign.create();
 
             @Override
             public Object execute(VirtualFrame frame) {
@@ -353,68 +359,49 @@ final class REngine implements Engine, Engine.Timings {
         }
     }
 
-    private final class PolyglotEngineRootNode extends RootNode {
-
-        private final List<RSyntaxNode> statements;
-        private final MaterializedFrame executionFrame;
-        @Children private final DirectCallNode[] calls;
-        private final boolean printResult;
-        private final SourceSection sourceSection;
-        private final ContextReference<RContext> contextReference;
-
-        @Child private R2Foreign r2Foreign = R2ForeignNodeGen.create();
-
-        PolyglotEngineRootNode(List<RSyntaxNode> statements, SourceSection sourceSection, MaterializedFrame executionFrame) {
-            super(context.getLanguage());
-            this.sourceSection = sourceSection;
-            this.contextReference = context.getLanguage().getContextReference();
-            // can't print if initializing the system in embedded mode (no builtins yet)
-            this.printResult = !sourceSection.getSource().getName().equals(RSource.Internal.INIT_EMBEDDED.string) && sourceSection.getSource().isInteractive();
-            this.statements = statements;
-            this.executionFrame = executionFrame;
-            this.calls = new DirectCallNode[statements.size()];
-        }
-
-        @Override
-        public SourceSection getSourceSection() {
-            return sourceSection;
-        }
-
-        /**
-         * The normal {@link #doMakeCallTarget} happens first, then we actually run the call using
-         * the standard FastR machinery, saving and restoring the {@link RContext}, since we have no
-         * control over what that might be when the call is initiated.
-         */
-        @Override
-        @ExplodeLoop
-        public Object execute(VirtualFrame frame) {
-            Object actualFrame = executionFrame != null ? executionFrame : contextReference.get().stateREnvironment.getGlobalFrame();
-            try {
-                Object lastValue = RNull.instance;
-                for (int i = 0; i < calls.length; i++) {
-                    if (calls[i] == null) {
-                        CompilerDirectives.transferToInterpreterAndInvalidate();
-                        RSyntaxNode node = statements.get(i);
-                        calls[i] = insert(Truffle.getRuntime().createDirectCallNode(doMakeCallTarget(node.asRNode(), RSource.Internal.REPL_WRAPPER.string, printResult, true)));
+    private EngineRootNode createRScriptRoot(Source fullSource, MaterializedFrame frame) {
+        URI uri = fullSource.getURI();
+        String file = fullSource.getPath();
+        ArrayList<RSyntaxNode> statements = new ArrayList<>(128);
+        try {
+            try (BufferedReader br = new BufferedReader(new InputStreamReader(fullSource.getInputStream()))) {
+                int lineIndex = 1;
+                int startLine = lineIndex;
+                StringBuilder sb = new StringBuilder();
+                while (true) {
+                    String input = br.readLine();
+                    if (input == null) {
+                        if (sb.length() != 0) {
+                            // end of file, but not end of statement => error
+                            statements.add(new SyntaxErrorNode(null, fullSource.createSection(startLine, 1, sb.length())));
+                        }
+                        break;
+                    }
+                    sb.append(input);
+                    Source src = Source.newBuilder(sb.toString()).mimeType(RRuntime.R_APP_MIME).name(file + "#" + startLine + "-" + lineIndex).uri(uri).build();
+                    lineIndex++;
+                    List<RSyntaxNode> currentStmts = null;
+                    try {
+                        RParserFactory.Parser<RSyntaxNode> parser = RParserFactory.getParser();
+                        currentStmts = parser.statements(src, fullSource, startLine, new RASTBuilder(), context.getLanguage());
+                    } catch (IncompleteSourceException e) {
+                        sb.append('\n');
+                        continue;
+                    } catch (ParseException e) {
+                        statements.add(new SyntaxErrorNode(e, fullSource.createSection(startLine, 1, sb.length())));
                     }
-                    lastValue = calls[i].call(new Object[]{actualFrame});
+                    if (currentStmts != null) {
+                        statements.addAll(currentStmts);
+                    }
+                    // we did not continue on incomplete source exception
+                    sb.setLength(0);
+                    startLine = lineIndex;
                 }
-                return r2Foreign.execute(lastValue);
-            } catch (ReturnException ex) {
-                return ex.getResult();
-            } catch (DebugExitException | JumpToTopLevelException | ExitException | ThreadDeath e) {
-                CompilerDirectives.transferToInterpreter();
-                throw e;
-            } catch (RError e) {
-                CompilerDirectives.transferToInterpreter();
-                throw e;
-            } catch (Throwable t) {
-                CompilerDirectives.transferToInterpreter();
-                // other errors didn't produce an output yet
-                RInternalError.reportError(t);
-                throw t;
             }
+        } catch (IOException ex) {
+            throw new RuntimeException(ex);
         }
+        return EngineRootNode.createEngineRoot(this, context, statements, createSourceSection(fullSource, statements), frame);
     }
 
     @Override
@@ -508,7 +495,7 @@ final class REngine implements Engine, Engine.Timings {
      * that the body should be executed in.
      */
     @TruffleBoundary
-    private RootCallTarget doMakeCallTarget(RNode body, String description, boolean printResult, boolean topLevel) {
+    RootCallTarget doMakeCallTarget(RNode body, String description, boolean printResult, boolean topLevel) {
         return Truffle.getRuntime().createCallTarget(new AnonymousRootNode(this, body, description, printResult, topLevel));
     }
 
@@ -529,14 +516,14 @@ final class REngine implements Engine, Engine.Timings {
         private final boolean topLevel;
         private final boolean suppressWarnings;
 
-        @Child private RNode body;
+        @Child private RootBodyNode body;
         @Child private GetVisibilityNode visibility = GetVisibilityNode.create();
         @Child private SetVisibilityNode setVisibility = SetVisibilityNode.create();
 
         protected AnonymousRootNode(REngine engine, RNode body, String description, boolean printResult, boolean topLevel) {
             super(engine.context.getLanguage());
             this.suppressWarnings = engine.suppressWarnings;
-            this.body = body;
+            this.body = new AnonymousBodyNode(body);
             this.description = description;
             this.printResult = printResult;
             this.topLevel = topLevel;
@@ -544,12 +531,12 @@ final class REngine implements Engine, Engine.Timings {
 
         @Override
         public SourceSection getSourceSection() {
-            return body.getSourceSection();
+            return getBody().getSourceSection();
         }
 
         @Override
         public boolean isInternal() {
-            return RSyntaxNode.isInternal(body.asRSyntaxNode().getLazySourceSection());
+            return RSyntaxNode.isInternal(getBody().getLazySourceSection());
         }
 
         private VirtualFrame prepareFrame(VirtualFrame frame) {
@@ -620,6 +607,29 @@ final class REngine implements Engine, Engine.Timings {
             return false;
         }
 
+        @Override
+        public RSyntaxNode getBody() {
+            return body.getBody().asRSyntaxNode();
+        }
+    }
+
+    private static final class AnonymousBodyNode extends Node implements RootBodyNode {
+        @Child private RNode body;
+
+        AnonymousBodyNode(RNode body) {
+            this.body = body;
+        }
+
+        @Override
+        public Object visibleExecute(VirtualFrame frame) {
+            return body.visibleExecute(frame);
+        }
+
+        @Override
+        public SourceSection getSourceSection() {
+            return body.getSourceSection();
+        }
+
         @Override
         public RNode getBody() {
             return body;
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/RRuntimeASTAccessImpl.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/RRuntimeASTAccessImpl.java
index 08cbe18cd4f7f2278adb4d28be3c838f9d233733..b4ee6e0c3a8e1ba383f49afd4a08604807b57992 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/RRuntimeASTAccessImpl.java
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/RRuntimeASTAccessImpl.java
@@ -33,6 +33,9 @@ import com.oracle.truffle.api.CompilerAsserts;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.frame.Frame;
 import com.oracle.truffle.api.frame.FrameInstance.FrameAccess;
+import com.oracle.truffle.api.instrumentation.StandardTags.CallTag;
+import com.oracle.truffle.api.instrumentation.StandardTags.RootTag;
+import com.oracle.truffle.api.instrumentation.StandardTags.StatementTag;
 import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.api.nodes.RootNode;
 import com.oracle.truffle.r.launcher.RCommand;
@@ -54,6 +57,8 @@ import com.oracle.truffle.r.nodes.function.ClassHierarchyNode;
 import com.oracle.truffle.r.nodes.function.FunctionDefinitionNode;
 import com.oracle.truffle.r.nodes.function.RCallNode;
 import com.oracle.truffle.r.nodes.instrumentation.RInstrumentation;
+import com.oracle.truffle.r.nodes.instrumentation.RSyntaxTags.FunctionBodyBlockTag;
+import com.oracle.truffle.r.nodes.instrumentation.RSyntaxTags.LoopTag;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
 import com.oracle.truffle.r.runtime.RArguments;
 import com.oracle.truffle.r.runtime.RCaller;
@@ -61,7 +66,7 @@ import com.oracle.truffle.r.runtime.RDeparse;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RError.ShowCallerOf;
 import com.oracle.truffle.r.runtime.RRuntimeASTAccess;
-import com.oracle.truffle.r.runtime.RootWithBody;
+import com.oracle.truffle.r.runtime.RootBodyNode;
 import com.oracle.truffle.r.runtime.Utils;
 import com.oracle.truffle.r.runtime.context.Engine;
 import com.oracle.truffle.r.runtime.context.RContext;
@@ -265,45 +270,49 @@ class RRuntimeASTAccessImpl implements RRuntimeASTAccess {
 
     @Override
     public boolean isTaggedWith(Node node, Class<?> tag) {
+        if (node instanceof RootBodyNode) {
+            return (tag == RootTag.class);
+        }
+        if (node instanceof RootNode) {
+            // roots don't have any tags
+            return false;
+        }
         if (!(node instanceof RSyntaxNode)) {
             return false;
         }
         if (isInternalChild(node)) {
             return false;
         }
-        String className = tag.getSimpleName();
-        switch (className) {
-            case "CallTag":
-                return node instanceof RCallNode;
-
-            case "StatementTag": {
-                Node parent = ((RInstrumentableNode) node).unwrapParent();
-                if (node instanceof BlockNode) {
-                    // TODO we may reconsider this
-                    return false;
-                }
-                // Most likely
-                if (parent instanceof BlockNode) {
-                    return true;
-                } else {
-                    // single statement block, variable parent
-                    // note: RepeatingNode is not a RSyntaxElement but the body of a loop is
-                    // under the repeating node !
-                    return parent instanceof FunctionDefinitionNode || parent instanceof RootWithBody || parent instanceof IfNode || AbstractLoopNode.isLoopBody(node);
-                }
+        if (tag == CallTag.class) {
+            return node instanceof RCallNode;
+        }
+        if (tag == FunctionBodyBlockTag.class) {
+            return node instanceof BlockNode && ((BlockNode) node).unwrapParent() instanceof RootBodyNode;
+        }
+        if (tag == LoopTag.class) {
+            return node instanceof AbstractLoopNode;
+        }
+        if (tag == StatementTag.class) {
+            if (node instanceof BlockNode) {
+                // so that the stepping location is not the block itself, but the first statement in
+                // the block, note that the FastR's own debugging and tracing mechanism uses
+                // FunctionBodyBlockTag to recognize function bodies.
+                return false;
             }
-
-            case "RootTag": {
-                Node parent = ((RInstrumentableNode) node).unwrapParent();
-                return parent instanceof FunctionDefinitionNode || parent instanceof RootWithBody;
+            // How to recognize statement from some node inside a statement (e.g. expression)?
+            Node parent = ((RInstrumentableNode) node).unwrapParent();
+            if (parent instanceof BlockNode) {
+                // It's in a block of statements
+                return true;
+            } else {
+                // single statement block: as function body, if/else body, loop body
+                // note: RepeatingNode is not a RSyntaxElement but the body of a loop is
+                // under the repeating node !
+                return parent instanceof RootBodyNode || parent instanceof IfNode || AbstractLoopNode.isLoopBody(node) || EngineRootNode.isEngineBody(parent);
             }
-
-            case "LoopTag":
-                return node instanceof AbstractLoopNode;
-
-            default:
-                return false;
         }
+        // TODO: ExpressionTag: (!statement && !loop && !if && !call && !root)??
+        return false;
     }
 
     private static boolean isInternalChild(Node node) {
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/SyntaxErrorNode.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/SyntaxErrorNode.java
new file mode 100644
index 0000000000000000000000000000000000000000..10ecf7472e6ff21daa88ceba29822e7140178c0b
--- /dev/null
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/SyntaxErrorNode.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2018, 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.engine;
+
+import com.oracle.truffle.api.frame.VirtualFrame;
+import com.oracle.truffle.api.source.SourceSection;
+import com.oracle.truffle.r.runtime.RInternalError;
+import com.oracle.truffle.r.runtime.context.Engine.ParseException;
+import com.oracle.truffle.r.runtime.nodes.RInstrumentableNode;
+import com.oracle.truffle.r.runtime.nodes.RNode;
+import com.oracle.truffle.r.runtime.nodes.RSyntaxNode;
+
+/**
+ * Node that represents piece of AST that would throw syntax error when parser. Used to delay
+ * parsing error to the point when they should be reported.
+ */
+final class SyntaxErrorNode extends RNode implements RSyntaxNode, RInstrumentableNode {
+    private final ParseException exception;
+    private final SourceSection sourceSection;
+
+    SyntaxErrorNode(ParseException exception, SourceSection sourceSection) {
+        this.exception = exception;
+        this.sourceSection = sourceSection;
+    }
+
+    @Override
+    public Object execute(VirtualFrame frame) {
+        throw exception.throwAsRError();
+    }
+
+    @Override
+    public SourceSection getSourceSection() {
+        return this.sourceSection;
+    }
+
+    @Override
+    public SourceSection getLazySourceSection() {
+        return this.sourceSection;
+    }
+
+    @Override
+    public void setSourceSection(SourceSection source) {
+        throw RInternalError.shouldNotReachHere(String.format("%s should not be serialized/deserialzed.", getClass().getSimpleName()));
+    }
+}
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/TruffleRLanguageImpl.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/TruffleRLanguageImpl.java
index e84096fba53c49263117a1cf1e4ec25ff356c8b1..50633e95daf86e2f7f48ce313b85859912660432 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/TruffleRLanguageImpl.java
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/TruffleRLanguageImpl.java
@@ -34,11 +34,13 @@ import com.oracle.truffle.api.instrumentation.ProvidedTags;
 import com.oracle.truffle.api.instrumentation.StandardTags;
 import com.oracle.truffle.api.nodes.ExecutableNode;
 import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.api.source.Source;
 import com.oracle.truffle.api.source.SourceSection;
 import com.oracle.truffle.r.engine.interop.RForeignAccessFactoryImpl;
 import com.oracle.truffle.r.nodes.RASTBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinPackages;
 import com.oracle.truffle.r.nodes.instrumentation.RSyntaxTags;
+import com.oracle.truffle.r.nodes.instrumentation.RSyntaxTags.FunctionBodyBlockTag;
 import com.oracle.truffle.r.runtime.ExitException;
 import com.oracle.truffle.r.runtime.FastROptions;
 import com.oracle.truffle.r.runtime.RAccuracyInfo;
@@ -57,7 +59,7 @@ import com.oracle.truffle.r.runtime.ffi.RFFIFactory;
 import com.oracle.truffle.r.runtime.nodes.RBaseNode;
 
 @TruffleLanguage.Registration(name = "R", id = "R", version = "3.3.2", mimeType = {RRuntime.R_APP_MIME, RRuntime.R_TEXT_MIME}, interactive = true)
-@ProvidedTags({StandardTags.CallTag.class, StandardTags.StatementTag.class, StandardTags.RootTag.class, RSyntaxTags.LoopTag.class})
+@ProvidedTags({StandardTags.CallTag.class, StandardTags.StatementTag.class, StandardTags.RootTag.class, RSyntaxTags.LoopTag.class, FunctionBodyBlockTag.class})
 public final class TruffleRLanguageImpl extends TruffleRLanguage {
 
     private final HashMap<String, RFunction> builtinFunctionCache = new HashMap<>();
@@ -188,12 +190,13 @@ public final class TruffleRLanguageImpl extends TruffleRLanguage {
     @Override
     protected CallTarget parse(ParsingRequest request) throws Exception {
         CompilerAsserts.neverPartOfCompilation();
+        Source source = request.getSource();
         try {
-            return RContext.getEngine().parseToCallTarget(request.getSource(), null);
+            return RContext.getEngine().parseToCallTarget(source, null);
         } catch (IncompleteSourceException e) {
             throw e;
         } catch (ParseException e) {
-            if (request.getSource().isInteractive()) {
+            if (source.isInteractive()) {
                 throw e.throwAsRError();
             } else {
                 throw e;
diff --git a/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/Launcher.java b/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/Launcher.java
deleted file mode 100644
index 6e73843f7b4bea548187d329a4859a74ef0979fc..0000000000000000000000000000000000000000
--- a/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/Launcher.java
+++ /dev/null
@@ -1,503 +0,0 @@
-/*
- * Copyright (c) 2017, 2018, 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.launcher;
-
-import static java.lang.Integer.max;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import org.graalvm.options.OptionCategory;
-import org.graalvm.options.OptionDescriptor;
-import org.graalvm.options.OptionDescriptors;
-import org.graalvm.polyglot.Engine;
-import org.graalvm.polyglot.Instrument;
-import org.graalvm.polyglot.Language;
-
-public abstract class Launcher {
-    private static final boolean STATIC_VERBOSE = Boolean.getBoolean("org.graalvm.launcher.verbose");
-
-    private Engine tempEngine;
-
-    private final boolean verbose;
-
-    private boolean help;
-    private boolean helpDebug;
-    private boolean helpExpert;
-    private boolean helpTools;
-    private boolean helpLanguages;
-
-    private OptionCategory helpCategory;
-    private VersionAction versionAction = VersionAction.None;
-
-    protected enum VersionAction {
-        None,
-        PrintAndExit,
-        PrintAndContinue
-    }
-
-    public Launcher() {
-        verbose = STATIC_VERBOSE || Boolean.valueOf(System.getenv("VERBOSE_GRAALVM_LAUNCHERS"));
-    }
-
-    private Engine getTempEngine() {
-        if (tempEngine == null) {
-            tempEngine = Engine.create();
-        }
-        return tempEngine;
-    }
-
-    void setHelpCategory(OptionCategory helpCategory) {
-        this.helpCategory = helpCategory;
-    }
-
-    protected void setVersionAction(VersionAction versionAction) {
-        this.versionAction = versionAction;
-    }
-
-    private static class AbortException extends RuntimeException {
-        static final long serialVersionUID = 4681646279864737876L;
-
-        AbortException(String message) {
-            super(message, null);
-        }
-
-        @SuppressWarnings("sync-override")
-        @Override
-        public final Throwable fillInStackTrace() {
-            return null;
-        }
-    }
-
-    protected AbortException exit() {
-        return abort(null, 0);
-    }
-
-    protected AbortException abort(String message) {
-        return abort(message, 1);
-    }
-
-    protected AbortException abort(String message, int exitCode) {
-        if (message != null) {
-            System.err.println("ERROR: " + message);
-        }
-        System.exit(exitCode);
-        throw new AbortException(message);
-    }
-
-    protected abstract void printHelp(OptionCategory maxCategory);
-
-    protected abstract void printVersion();
-
-    protected abstract void collectArguments(Set<String> options);
-
-    protected static void printPolyglotVersions() {
-        Engine engine = Engine.create();
-        System.out.println("GraalVM Polyglot Engine Version " + engine.getVersion());
-        printLanguages(engine, true);
-        printInstruments(engine, true);
-    }
-
-    protected boolean isVerbose() {
-        return verbose;
-    }
-
-    final boolean runPolyglotAction() {
-        OptionCategory maxCategory = helpDebug ? OptionCategory.DEBUG : (helpExpert ? OptionCategory.EXPERT : (helpCategory != null ? helpCategory : OptionCategory.USER));
-
-        switch (versionAction) {
-            case PrintAndContinue:
-                printVersion();
-                return false;
-            case PrintAndExit:
-                printVersion();
-                return true;
-            case None:
-                break;
-        }
-        boolean printDefaultHelp = helpCategory != null || help || ((helpExpert || helpDebug) && !helpTools && !helpLanguages);
-        Engine tmpEngine = null;
-        if (printDefaultHelp) {
-            printHelp(maxCategory);
-            // @formatter:off
-            System.out.println();
-            System.out.println("Runtime Options:");
-            printOption("--polyglot",                   "Run with all other guest languages accessible.");
-            // not applicable at the moment
-            // printOption("--native",                     "Run using the native launcher with limited Java access (default).");
-            // printOption("--native.[option]",            "Pass options to the native image; for example, '--native.Xmx1G'. To see available options, use '--native.help'.");
-            printOption("--jvm",                        "Run on the Java Virtual Machine with Java access.");
-            // not applicable at the moment
-            // printOption("--jvm.[option]",               "Pass options to the JVM; for example, '--jvm.classpath=myapp.jar'. To see available options. use '--jvm.help'.");
-            printOption("--help",                       "Print this help message.");
-            printOption("--help:languages",             "Print options for all installed languages.");
-            printOption("--help:tools",                 "Print options for all installed tools.");
-            printOption("--help:expert",                "Print additional engine options for experts.");
-            if (helpExpert || helpDebug) {
-                printOption("--help:debug",             "Print additional engine options for debugging.");
-            }
-            printOption("--version",                    "Print version information and exit.");
-            printOption("--show-version",               "Print version information and continue execution.");
-            // @formatter:on
-            if (tmpEngine == null) {
-                tmpEngine = Engine.create();
-            }
-            List<PrintableOption> engineOptions = new ArrayList<>();
-            for (OptionDescriptor descriptor : tmpEngine.getOptions()) {
-                if (!descriptor.getName().startsWith("engine.") && !descriptor.getName().startsWith("compiler.")) {
-                    continue;
-                }
-                if (descriptor.getCategory().ordinal() <= maxCategory.ordinal()) {
-                    engineOptions.add(asPrintableOption(descriptor));
-                }
-            }
-            if (!engineOptions.isEmpty()) {
-                printOptions(engineOptions, "Engine options:", 2);
-            }
-        }
-
-        if (helpLanguages) {
-            if (tmpEngine == null) {
-                tmpEngine = Engine.create();
-            }
-            printLanguageOptions(tmpEngine, maxCategory);
-        }
-
-        if (helpTools) {
-            if (tmpEngine == null) {
-                tmpEngine = Engine.create();
-            }
-            printInstrumentOptions(tmpEngine, maxCategory);
-        }
-
-        if (printDefaultHelp || helpLanguages || helpTools) {
-            System.out.println("\nSee http://www.oracle.com/technetwork/oracle-labs/program-languages/overview/index.html for more information.");
-            return true;
-        }
-
-        return false;
-    }
-
-    private static void printInstrumentOptions(Engine engine, OptionCategory maxCategory) {
-        Map<Instrument, List<PrintableOption>> instrumentsOptions = new HashMap<>();
-        List<Instrument> instruments = sortedInstruments(engine);
-        for (Instrument instrument : instruments) {
-            List<PrintableOption> options = new ArrayList<>();
-            for (OptionDescriptor descriptor : instrument.getOptions()) {
-                if (descriptor.getCategory().ordinal() <= maxCategory.ordinal()) {
-                    options.add(asPrintableOption(descriptor));
-                }
-            }
-            if (!options.isEmpty()) {
-                instrumentsOptions.put(instrument, options);
-            }
-        }
-        if (!instrumentsOptions.isEmpty()) {
-            System.out.println();
-            System.out.println("Tool options:");
-            for (Instrument instrument : instruments) {
-                List<PrintableOption> options = instrumentsOptions.get(instrument);
-                if (options != null) {
-                    printOptions(options, "  " + instrument.getName() + ":", 4);
-                }
-            }
-        }
-    }
-
-    private static void printLanguageOptions(Engine engine, OptionCategory maxCategory) {
-        Map<Language, List<PrintableOption>> languagesOptions = new HashMap<>();
-        List<Language> languages = sortedLanguages(engine);
-        for (Language language : languages) {
-            List<PrintableOption> options = new ArrayList<>();
-            for (OptionDescriptor descriptor : language.getOptions()) {
-                if (descriptor.getCategory().ordinal() <= maxCategory.ordinal()) {
-                    options.add(asPrintableOption(descriptor));
-                }
-            }
-            if (!options.isEmpty()) {
-                languagesOptions.put(language, options);
-            }
-        }
-        if (!languagesOptions.isEmpty()) {
-            System.out.println();
-            System.out.println("Language Options:");
-            for (Language language : languages) {
-                List<PrintableOption> options = languagesOptions.get(language);
-                if (options != null) {
-                    printOptions(options, "  " + language.getName() + ":", 4);
-                }
-            }
-        }
-    }
-
-    protected boolean parsePolyglotOption(String defaultOptionPrefix, Map<String, String> options, String arg) {
-        switch (arg) {
-            case "--help":
-                help = true;
-                return true;
-            case "--help:debug":
-                helpDebug = true;
-                return true;
-            case "--help:expert":
-                helpExpert = true;
-                return true;
-            case "--help:tools":
-                helpTools = true;
-                return true;
-            case "--help:languages":
-                helpLanguages = true;
-                return true;
-            case "--version":
-                versionAction = VersionAction.PrintAndExit;
-                return true;
-            case "--show-version":
-                versionAction = VersionAction.PrintAndContinue;
-                return true;
-            // case "--polyglot":
-            // case "--jvm":
-            // case "--native":
-            // return false;
-            default:
-                // getLanguageId() or null?
-                if (arg.length() <= 2 || !arg.startsWith("--")) {
-                    return false;
-                }
-                int eqIdx = arg.indexOf('=');
-                String key;
-                String value;
-                if (eqIdx < 0) {
-                    key = arg.substring(2);
-                    value = null;
-                } else {
-                    key = arg.substring(2, eqIdx);
-                    value = arg.substring(eqIdx + 1);
-                }
-
-                if (value == null) {
-                    value = "true";
-                }
-                int index = key.indexOf('.');
-                String group = key;
-                if (index >= 0) {
-                    group = group.substring(0, index);
-                }
-                OptionDescriptor descriptor = findPolyglotOptionDescriptor(group, key);
-                if (descriptor == null) {
-                    if (defaultOptionPrefix != null) {
-                        descriptor = findPolyglotOptionDescriptor(defaultOptionPrefix, defaultOptionPrefix + "." + key);
-                    }
-                    if (descriptor == null) {
-                        return false;
-                    }
-                }
-                try {
-                    descriptor.getKey().getType().convert(value);
-                } catch (IllegalArgumentException e) {
-                    throw abort(String.format("Invalid argument %s specified. %s'", arg, e.getMessage()));
-                }
-                options.put(key, value);
-                return true;
-        }
-    }
-
-    private OptionDescriptor findPolyglotOptionDescriptor(String group, String key) {
-        OptionDescriptors descriptors = null;
-        switch (group) {
-            case "compiler":
-            case "engine":
-                descriptors = getTempEngine().getOptions();
-                break;
-            default:
-                Engine engine = getTempEngine();
-                if (engine.getLanguages().containsKey(group)) {
-                    descriptors = engine.getLanguages().get(group).getOptions();
-                } else if (engine.getInstruments().containsKey(group)) {
-                    descriptors = engine.getInstruments().get(group).getOptions();
-                }
-                break;
-        }
-        if (descriptors == null) {
-            return null;
-        }
-        return descriptors.get(key);
-    }
-
-    protected static List<Language> sortedLanguages(Engine engine) {
-        List<Language> languages = new ArrayList<>(engine.getLanguages().values());
-        languages.sort(Comparator.comparing(Language::getId));
-        return languages;
-    }
-
-    protected static List<Instrument> sortedInstruments(Engine engine) {
-        List<Instrument> instruments = new ArrayList<>(engine.getInstruments().values());
-        instruments.sort(Comparator.comparing(Instrument::getId));
-        return instruments;
-    }
-
-    protected static void printOption(OptionCategory maxCategory, OptionDescriptor descriptor) {
-        if (descriptor.getCategory().ordinal() <= maxCategory.ordinal()) {
-            printOption(asPrintableOption(descriptor));
-        }
-    }
-
-    protected static PrintableOption asPrintableOption(OptionDescriptor descriptor) {
-        StringBuilder key = new StringBuilder("--");
-        key.append(descriptor.getName());
-        Object defaultValue = descriptor.getKey().getDefaultValue();
-        if (defaultValue instanceof Boolean && defaultValue == Boolean.FALSE) {
-            // nothing to print
-        } else {
-            key.append("=<");
-            key.append(descriptor.getKey().getType().getName());
-            key.append(">");
-        }
-        return new PrintableOption(key.toString(), descriptor.getHelp());
-    }
-
-    protected static void printOption(String option, String description) {
-        printOption(option, description, 2);
-    }
-
-    protected static void printOption(String option, String description, int indentation) {
-        StringBuilder indent = new StringBuilder(indentation);
-        for (int i = 0; i < indentation; i++) {
-            indent.append(' ');
-        }
-        String desc = description != null ? description : "";
-        if (option.length() >= 45 && description != null) {
-            System.out.println(String.format("%s%s%n%s%-45s%s", indent, option, indent, "", desc));
-        } else {
-            System.out.println(String.format("%s%-45s%s", indent, option, desc));
-        }
-    }
-
-    protected static void printOption(PrintableOption option) {
-        printOption(option, 2);
-    }
-
-    protected static void printOption(PrintableOption option, int indentation) {
-        printOption(option.option, option.description, indentation);
-    }
-
-    private static final class PrintableOption implements Comparable<PrintableOption> {
-        final String option;
-        final String description;
-
-        private PrintableOption(String option, String description) {
-            this.option = option;
-            this.description = description;
-        }
-
-        @Override
-        public int compareTo(PrintableOption o) {
-            return this.option.compareTo(o.option);
-        }
-    }
-
-    private static void printOptions(List<PrintableOption> options, String title, int indentation) {
-        Collections.sort(options);
-        System.out.println(title);
-        for (PrintableOption option : options) {
-            printOption(option, indentation);
-        }
-    }
-
-    protected enum OS {
-        Darwin,
-        Linux,
-        Solaris;
-
-        private static OS findCurrent() {
-            final String name = System.getProperty("os.name");
-            if (name.equals("Linux")) {
-                return Linux;
-            }
-            if (name.equals("SunOS")) {
-                return Solaris;
-            }
-            if (name.equals("Mac OS X") || name.equals("Darwin")) {
-                return Darwin;
-            }
-            throw new IllegalArgumentException("unknown OS: " + name);
-        }
-
-        private static final OS current = findCurrent();
-
-        public static OS getCurrent() {
-            return current;
-        }
-    }
-
-    protected static void printLanguages(Engine engine, boolean printWhenEmpty) {
-        if (engine.getLanguages().isEmpty()) {
-            if (printWhenEmpty) {
-                System.out.println("  Installed Languages: none");
-            }
-        } else {
-            System.out.println("  Installed Languages:");
-            List<Language> languages = new ArrayList<>(engine.getLanguages().size());
-            int nameLength = 0;
-            for (Language language : engine.getLanguages().values()) {
-                languages.add(language);
-                nameLength = max(nameLength, language.getName().length());
-            }
-            languages.sort(Comparator.comparing(Language::getId));
-            String langFormat = "    %-" + nameLength + "s%s version %s%n";
-            for (Language language : languages) {
-                String version = language.getVersion();
-                if (version == null || version.length() == 0) {
-                    version = "";
-                }
-                System.out.printf(langFormat, language.getName().isEmpty() ? "Unnamed" : language.getName(), version);
-            }
-        }
-    }
-
-    protected static void printInstruments(Engine engine, boolean printWhenEmpty) {
-        if (engine.getInstruments().isEmpty()) {
-            if (printWhenEmpty) {
-                System.out.println("  Installed Tools: none");
-            }
-        } else {
-            System.out.println("  Installed Tools:");
-            List<Instrument> instruments = sortedInstruments(engine);
-            int nameLength = 0;
-            for (Instrument instrument : instruments) {
-                nameLength = max(nameLength, instrument.getName().length());
-            }
-            String instrumentFormat = "    %-" + nameLength + "s version %s%n";
-            for (Instrument instrument : instruments) {
-                String version = instrument.getVersion();
-                if (version == null || version.length() == 0) {
-                    version = "";
-                }
-                System.out.printf(instrumentFormat, instrument.getName().isEmpty() ? "Unnamed" : instrument.getName(), version);
-            }
-        }
-    }
-}
diff --git a/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/RAbstractLauncher.java b/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/RAbstractLauncher.java
new file mode 100644
index 0000000000000000000000000000000000000000..87fa2f782a004ac798a80ee58f034c7f0cbd388f
--- /dev/null
+++ b/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/RAbstractLauncher.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 2018, 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.launcher;
+
+import java.io.Closeable;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.graalvm.launcher.AbstractLanguageLauncher;
+import org.graalvm.options.OptionCategory;
+import org.graalvm.polyglot.Context;
+import org.graalvm.polyglot.Context.Builder;
+
+import com.oracle.truffle.r.launcher.RCmdOptions.Client;
+import com.oracle.truffle.r.launcher.RCmdOptions.RCmdOption;
+
+public abstract class RAbstractLauncher extends AbstractLanguageLauncher implements Closeable {
+
+    private final Client client;
+    protected final InputStream inStream;
+    protected final OutputStream outStream;
+    protected final OutputStream errStream;
+    protected RCmdOptions options;
+    private boolean useJVM;
+    protected ConsoleHandler consoleHandler;
+    protected Context context;
+
+    RAbstractLauncher(Client client, String[] env, InputStream inStream, OutputStream outStream, OutputStream errStream) {
+        this.client = client;
+        assert env == null : "re-enble environment variables";
+        this.inStream = inStream;
+        this.outStream = outStream;
+        this.errStream = errStream;
+    }
+
+    @Override
+    protected List<String> preprocessArguments(List<String> arguments, Map<String, String> polyglotOptions) {
+        boolean[] recognizedArgsIndices = new boolean[arguments.size()];
+        this.options = RCmdOptions.parseArguments(client, arguments.toArray(new String[arguments.size()]), true, recognizedArgsIndices);
+        List<String> unrecognizedArgs = new ArrayList<>();
+        for (int i = 0; i < arguments.size(); i++) {
+            if ("--jvm".equals(arguments.get(i))) {
+                useJVM = true;
+            } else if (!recognizedArgsIndices[i]) {
+                unrecognizedArgs.add(arguments.get(i));
+            }
+        }
+
+        return unrecognizedArgs;
+    }
+
+    protected abstract String[] getArguments();
+
+    @Override
+    protected void launch(Builder contextBuilder) {
+        this.consoleHandler = RCommand.createConsoleHandler(options, null, inStream, outStream);
+        this.context = contextBuilder.allowAllAccess(true).allowHostAccess(useJVM).arguments("R", getArguments()).in(consoleHandler.createInputStream()).out(
+                        outStream).err(errStream).build();
+        this.consoleHandler.setContext(context);
+    }
+
+    @Override
+    protected String getLanguageId() {
+        return "R";
+    }
+
+    @Override
+    protected void printHelp(OptionCategory maxCategory) {
+        RCmdOptions.printHelp(client);
+    }
+
+    @Override
+    protected void collectArguments(Set<String> opts) {
+        for (RCmdOption option : RCmdOption.values()) {
+            if (option.shortName != null) {
+                opts.add(option.shortName);
+            }
+            if (option.plainName != null) {
+                opts.add(option.plainName);
+            }
+        }
+    }
+
+    @Override
+    public void close() {
+        if (context != null) {
+            context.close();
+        }
+    }
+}
diff --git a/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/RCmdOptions.java b/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/RCmdOptions.java
index f402d46bd91411a36d0dc25b14d08bc825acf36b..d0cb3e769d91cdb1e8247b2779919f8775cb31d8 100644
--- a/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/RCmdOptions.java
+++ b/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/RCmdOptions.java
@@ -268,25 +268,44 @@ public final class RCmdOptions {
      * {@code args[0]} and we do not want to parse that!
      */
     public static RCmdOptions parseArguments(Client client, String[] args, boolean reparse) {
+        return parseArguments(client, args, reparse, null);
+    }
+
+    public static RCmdOptions parseArguments(Client client, String[] args, boolean reparse, boolean[] recognizedArgsIndices) {
+        assert recognizedArgsIndices == null || recognizedArgsIndices.length == args.length;
+
         EnumMap<RCmdOption, Object> options = new EnumMap<>(RCmdOption.class);
+        if (recognizedArgsIndices != null) {
+            recognizedArgsIndices[0] = true;
+        }
         int i = 1; // skip the first argument (the command)
         int firstNonOptionArgIndex = args.length;
         while (i < args.length) {
             final String arg = args[i];
             MatchResult result = matchOption(arg);
             if (result == null) {
-                // for Rscript, this means we are done
-                if (client == Client.RSCRIPT) {
+                boolean isOption = arg.startsWith("--") || arg.startsWith("-");
+                if (!isOption && client == Client.RSCRIPT) {
+                    // for Rscript, this means we are done
+                    if (recognizedArgsIndices != null) {
+                        recognizedArgsIndices[i] = true;
+                    }
                     firstNonOptionArgIndex = i;
                     break;
                 }
                 if (!reparse) {
                     // GnuR does not abort, simply issues a warning
                     System.out.printf("WARNING: unknown option '%s'%n", arg);
+                    if (recognizedArgsIndices != null) {
+                        recognizedArgsIndices[i] = true;
+                    }
                 }
                 i++;
                 continue;
             } else {
+                if (recognizedArgsIndices != null) {
+                    recognizedArgsIndices[i] = true;
+                }
                 RCmdOption option = result.option;
                 if (result.matchedShort && i == args.length - 1) {
                     System.out.println("usage:");
@@ -302,6 +321,9 @@ public final class RCmdOptions {
                 if (result.matchedShort) {
                     i++;
                     setValue(options, option, args[i]);
+                    if (recognizedArgsIndices != null) {
+                        recognizedArgsIndices[i] = true;
+                    }
                 } else {
                     if (option.type == RCmdOptionType.BOOLEAN) {
                         setValue(options, option, true);
@@ -318,6 +340,14 @@ public final class RCmdOptions {
                 }
             }
         }
+
+        if (recognizedArgsIndices != null) {
+            // mark the all non-option arguments (the tail) as recognized
+            for (int j = firstNonOptionArgIndex; j < recognizedArgsIndices.length; j++) {
+                recognizedArgsIndices[j] = true;
+            }
+        }
+
         // adjust for inserted executable name
         return new RCmdOptions(options, args, firstNonOptionArgIndex);
     }
diff --git a/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/RCommand.java b/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/RCommand.java
index 1a1fdc1f067af61e4118964bba654dcfcbcfc698..3b9e23dc93c426900cb0beb1e03f77b80d0e5e98 100644
--- a/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/RCommand.java
+++ b/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/RCommand.java
@@ -30,15 +30,11 @@ import java.io.OutputStream;
 import java.nio.file.Files;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.HashMap;
-import java.util.Iterator;
 import java.util.List;
-import java.util.Map;
-import java.util.Set;
 import java.util.function.Supplier;
 
-import org.graalvm.options.OptionCategory;
 import org.graalvm.polyglot.Context;
+import org.graalvm.polyglot.Context.Builder;
 import org.graalvm.polyglot.PolyglotException;
 import org.graalvm.polyglot.Source;
 
@@ -47,37 +43,6 @@ import com.oracle.truffle.r.launcher.RCmdOptions.RCmdOption;
 
 import jline.console.UserInterruptException;
 
-class RLauncher extends Launcher {
-
-    private final Client client;
-
-    RLauncher(Client client) {
-        this.client = client;
-    }
-
-    @Override
-    protected void printHelp(OptionCategory maxCategory) {
-        RCmdOptions.printHelp(client);
-    }
-
-    @Override
-    protected void printVersion() {
-        RCmdOptions.printVersion();
-    }
-
-    @Override
-    protected void collectArguments(Set<String> options) {
-        for (RCmdOption option : RCmdOption.values()) {
-            if (option.shortName != null) {
-                options.add(option.shortName);
-            }
-            if (option.plainName != null) {
-                options.add(option.plainName);
-            }
-        }
-    }
-}
-
 /*
  * TODO:
  *
@@ -89,40 +54,25 @@ class RLauncher extends Launcher {
 /**
  * Emulates the (Gnu)R command as precisely as possible.
  */
-public class RCommand {
-
-    // CheckStyle: stop system..print check
-    public static RuntimeException fatal(String message, Object... args) {
-        System.out.println("FATAL: " + String.format(message, args));
-        System.exit(-1);
-        return new RuntimeException();
-    }
+public class RCommand extends RAbstractLauncher {
 
-    public static RuntimeException fatal(Throwable t, String message, Object... args) {
-        t.printStackTrace();
-        System.out.println("FATAL: " + String.format(message, args));
-        System.exit(-1);
-        return null;
+    RCommand(String[] env, InputStream inStream, OutputStream outStream, OutputStream errStream) {
+        super(Client.R, env, inStream, outStream, errStream);
     }
 
-    public static void main(String[] args) {
-        try {
-            System.exit(doMain(prependCommand(args), null, System.in, System.out, System.err));
-            // never returns
-            throw fatal("main should never return");
-        } catch (Throwable t) {
-            throw fatal(t, "error during REPL execution");
-        }
+    @Override
+    protected String[] getArguments() {
+        return options.getArguments();
     }
 
-    static String[] prependCommand(String[] args) {
-        String[] result = new String[args.length + 1];
-        result[0] = "R";
-        System.arraycopy(args, 0, result, 1, args.length);
-        return result;
+    @Override
+    protected void launch(Builder contextBuilder) {
+        super.launch(contextBuilder);
+        StartupTiming.timestamp("VM Created");
+        StartupTiming.printSummary();
     }
 
-    public static int doMain(String[] args, String[] env, InputStream inStream, OutputStream outStream, OutputStream errStream) {
+    private int execute(String[] args) {
         StartupTiming.timestamp("Main Entered");
         ArrayList<String> argsList = new ArrayList<>(Arrays.asList(args));
         if (System.console() != null) {
@@ -143,34 +93,8 @@ public class RCommand {
             }
         }
 
-        boolean useJVM = false;
-        RLauncher launcher = new RLauncher(Client.R);
-        Map<String, String> polyglotOptions = new HashMap<>();
-        Iterator<String> iterator = argsList.iterator();
-        if (iterator.hasNext()) {
-            iterator.next(); // skip first argument
-            while (iterator.hasNext()) {
-                String arg = iterator.next();
-                if ("--jvm".equals(arg)) {
-                    useJVM = true;
-                    iterator.remove();
-                } else if (launcher.parsePolyglotOption("R", polyglotOptions, arg)) {
-                    iterator.remove();
-                }
-            }
-        }
-        if (launcher.runPolyglotAction()) {
-            return 0;
-        }
-        RCmdOptions options = RCmdOptions.parseArguments(Client.R, argsList.toArray(new String[argsList.size()]), false);
-        assert env == null || env.length == 0 : "re-enable setting environments";
-        ConsoleHandler consoleHandler = createConsoleHandler(options, null, inStream, outStream);
-        try (Context context = Context.newBuilder().allowAllAccess(true).allowHostAccess(useJVM).options(polyglotOptions).arguments("R", options.getArguments()).in(
-                        consoleHandler.createInputStream()).out(outStream).err(errStream).build()) {
-            consoleHandler.setContext(context);
-            StartupTiming.timestamp("VM Created");
-            StartupTiming.printSummary();
-
+        launch(argsList.toArray(new String[0]));
+        if (context != null) {
             File srcFile = null;
             String fileOption = options.getString(RCmdOption.FILE);
             if (fileOption != null) {
@@ -178,6 +102,45 @@ public class RCommand {
             }
 
             return readEvalPrint(context, consoleHandler, srcFile);
+        } else {
+            return 0;
+        }
+    }
+
+    // CheckStyle: stop system..print check
+    public static RuntimeException fatal(String message, Object... args) {
+        System.out.println("FATAL: " + String.format(message, args));
+        System.exit(-1);
+        return new RuntimeException();
+    }
+
+    public static RuntimeException fatal(Throwable t, String message, Object... args) {
+        t.printStackTrace();
+        System.out.println("FATAL: " + String.format(message, args));
+        System.exit(-1);
+        return null;
+    }
+
+    static String[] prependCommand(String[] args) {
+        String[] result = new String[args.length + 1];
+        result[0] = "R";
+        System.arraycopy(args, 0, result, 1, args.length);
+        return result;
+    }
+
+    public static void main(String[] args) {
+        try {
+            System.exit(doMain(prependCommand(args), null, System.in, System.out, System.err));
+            // never returns
+            throw fatal("main should never return");
+        } catch (Throwable t) {
+            throw fatal(t, "error during REPL execution");
+        }
+    }
+
+    public static int doMain(String[] args, String[] env, InputStream inStream, OutputStream outStream, OutputStream errStream) {
+        try (RCommand rcmd = new RCommand(env, inStream, outStream, errStream)) {
+            return rcmd.execute(args);
         }
     }
 
diff --git a/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/RscriptCommand.java b/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/RscriptCommand.java
index 15c4be2c6404f3332c001d5db43c0e262e069cb5..99ce2d589b25af2bc8de33625b1ec6e7fe876249 100644
--- a/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/RscriptCommand.java
+++ b/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/RscriptCommand.java
@@ -23,16 +23,16 @@
 package com.oracle.truffle.r.launcher;
 
 import java.io.File;
+import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.Iterator;
+import java.util.Collections;
+import java.util.List;
 import java.util.Map;
 
-import org.graalvm.options.OptionCategory;
-import org.graalvm.polyglot.Context;
+import org.graalvm.polyglot.PolyglotException;
+import org.graalvm.polyglot.Source;
 
 import com.oracle.truffle.r.launcher.RCmdOptions.Client;
 import com.oracle.truffle.r.launcher.RCmdOptions.RCmdOption;
@@ -43,11 +43,67 @@ import com.oracle.truffle.r.launcher.RCmdOptions.RCmdOption;
  * way but the effect is similar.
  *
  */
-public class RscriptCommand {
+public final class RscriptCommand extends RAbstractLauncher {
+
+    private String[] rScriptArguments;
+
+    RscriptCommand(String[] env, InputStream inStream, OutputStream outStream, OutputStream errStream) {
+        super(Client.RSCRIPT, env, inStream, outStream, errStream);
+    }
+
+    @Override
+    protected List<String> preprocessArguments(List<String> arguments, Map<String, String> polyglotOptions) {
+        List<String> unrecognizedArgs = super.preprocessArguments(arguments, polyglotOptions);
+        try {
+            this.rScriptArguments = preprocessRScriptOptions(options);
+            return unrecognizedArgs;
+        } catch (PrintHelp e) {
+            return Collections.singletonList("--help");
+        }
+    }
+
+    @Override
+    protected String[] getArguments() {
+        return rScriptArguments;
+    }
+
+    protected int execute(String[] args) {
+        launch(args);
+        if (context != null) {
+            String fileOption = options.getString(RCmdOption.FILE);
+            if (fileOption != null) {
+                return executeFile(fileOption);
+            } else {
+                return RCommand.readEvalPrint(context, consoleHandler, null);
+            }
+        } else {
+            return 0;
+        }
+    }
+
+    private int executeFile(String fileOption) {
+        Source src;
+        try {
+            src = Source.newBuilder("R", new File(fileOption)).interactive(false).build();
+        } catch (IOException ex) {
+            System.err.printf("IO error while reading the source file '%s'.\nDetails: '%s'.", fileOption, ex.getLocalizedMessage());
+            return 1;
+        }
+        try {
+            context.eval(src);
+            return 0;
+        } catch (Throwable ex) {
+            if (ex instanceof PolyglotException && ((PolyglotException) ex).isExit()) {
+                return ((PolyglotException) ex).getExitStatus();
+            }
+            // Internal exceptions are reported by the engine already
+            return 1;
+        }
+    }
 
     // CheckStyle: stop system..print check
 
-    private static String[] preprocessRScriptOptions(RLauncher launcher, RCmdOptions options) {
+    private static String[] preprocessRScriptOptions(RCmdOptions options) throws PrintHelp {
         String[] arguments = options.getArguments();
         int resultArgsLength = arguments.length;
         int firstNonOptionArgIndex = options.getFirstNonOptionArgIndex();
@@ -61,11 +117,7 @@ public class RscriptCommand {
         // Either -e options are set or first non-option arg is a file
         if (options.getStringList(RCmdOption.EXPR) == null) {
             if (firstNonOptionArgIndex == resultArgsLength) {
-                launcher.setHelpCategory(OptionCategory.USER);
-                // does not return
-                if (launcher.runPolyglotAction()) {
-                    System.exit(1);
-                }
+                throw new PrintHelp();
             } else {
                 options.setValue(RCmdOption.FILE, arguments[firstNonOptionArgIndex]);
             }
@@ -99,50 +151,18 @@ public class RscriptCommand {
         return adjArgs.toArray(new String[adjArgs.size()]);
     }
 
+    @SuppressWarnings("serial")
+    static class PrintHelp extends Exception {
+    }
+
     public static void main(String[] args) {
         System.exit(doMain(RCommand.prependCommand(args), null, System.in, System.out, System.err));
         throw RCommand.fatal("should not reach here");
     }
 
     public static int doMain(String[] args, String[] env, InputStream inStream, OutputStream outStream, OutputStream errStream) {
-        assert env == null : "re-enble environment variables";
-
-        ArrayList<String> argsList = new ArrayList<>(Arrays.asList(args));
-        RLauncher launcher = new RLauncher(Client.RSCRIPT) {
-            @Override
-            protected void printVersion() {
-                System.out.print("R scripting front-end version ");
-                System.out.print(RVersionNumber.FULL);
-                System.out.println(RVersionNumber.RELEASE_DATE);
-            }
-        };
-        boolean useJVM = false;
-        Map<String, String> polyglotOptions = new HashMap<>();
-        Iterator<String> iterator = argsList.iterator();
-        if (iterator.hasNext()) {
-            iterator.next(); // skip first argument
-            while (iterator.hasNext()) {
-                String arg = iterator.next();
-                if ("--jvm".equals(arg)) {
-                    useJVM = true;
-                    iterator.remove();
-                } else if (launcher.parsePolyglotOption("R", polyglotOptions, arg)) {
-                    iterator.remove();
-                }
-            }
-        }
-        if (launcher.runPolyglotAction()) {
-            return 0;
-        }
-        RCmdOptions options = RCmdOptions.parseArguments(Client.RSCRIPT, argsList.toArray(new String[argsList.size()]), false);
-        String[] arguments = preprocessRScriptOptions(launcher, options);
-
-        ConsoleHandler consoleHandler = RCommand.createConsoleHandler(options, null, inStream, outStream);
-        try (Context context = Context.newBuilder().allowAllAccess(true).allowHostAccess(useJVM).options(polyglotOptions).arguments("R", arguments).in(consoleHandler.createInputStream()).out(
-                        outStream).err(errStream).build()) {
-            consoleHandler.setContext(context);
-            String fileOption = options.getString(RCmdOption.FILE);
-            return RCommand.readEvalPrint(context, consoleHandler, fileOption != null ? new File(fileOption) : null);
+        try (RscriptCommand rcmd = new RscriptCommand(env, inStream, outStream, errStream)) {
+            return rcmd.execute(args);
         }
     }
 }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/helpers/DebugHandling.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/helpers/DebugHandling.java
index 70bda866deda77c3008f5519f9c85339645824ce..e5d0ee8f424911c7c569c1541388d5e13ff87076 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/helpers/DebugHandling.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/helpers/DebugHandling.java
@@ -46,6 +46,7 @@ import com.oracle.truffle.r.nodes.control.AbstractLoopNode;
 import com.oracle.truffle.r.nodes.function.FunctionDefinitionNode;
 import com.oracle.truffle.r.nodes.instrumentation.RInstrumentation;
 import com.oracle.truffle.r.nodes.instrumentation.RSyntaxTags;
+import com.oracle.truffle.r.nodes.instrumentation.RSyntaxTags.FunctionBodyBlockTag;
 import com.oracle.truffle.r.runtime.JumpToTopLevelException;
 import com.oracle.truffle.r.runtime.RArguments;
 import com.oracle.truffle.r.runtime.RDeparse;
@@ -76,7 +77,7 @@ import com.oracle.truffle.r.runtime.nodes.RSyntaxVisitor;
  * Three different listener classes are defined:
  * <ul>
  * <li>{@link FunctionStatementsEventListener}: attaches to function bodies and handles the special
- * behavior on entry/exit</li>
+ * behavior on entry/exit</li>. Function body is distinguished with tag {@link FunctionBodyBlockTag}
  * <li>{@link StatementEventListener}: attaches to all {@code StandardTags.StatementTag} nodes and
  * handles "n" and "s" browser commands</li>
  * <li>{@link LoopStatementEventListener}: attaches to {@link AbstractLoopNode} instances and
@@ -89,7 +90,7 @@ import com.oracle.truffle.r.runtime.nodes.RSyntaxVisitor;
  * mode and reset it's state on return. This is handled as follows:
  * <ol>
  * <li>On a step-into, attach a {@link StepIntoInstrumentListener} with a filter that matches all
- * functions and the {@code StandardTags.RootTag} tag</li>
+ * functions and their bodies like {@link FunctionStatementsEventListener}.</li>
  * <li>On entry to that listener instrument/enable the function we have entered (if necessary) for
  * one-time (unless already)</li>
  * <li>Dispose the {@link StepIntoInstrumentListener} and continue, which will then stop at the
@@ -219,7 +220,7 @@ public class DebugHandling {
         }
 
         @CompilationFinal private boolean disabled;
-        CyclicAssumption disabledUnchangedAssumption = new CyclicAssumption("debug event disabled state unchanged");
+        private final CyclicAssumption disabledUnchangedAssumption = new CyclicAssumption("debug event disabled state unchanged");
 
         boolean disabled() {
             return disabled || RContext.getInstance().stateInstrumentation.debugGloballyDisabled();
@@ -317,7 +318,7 @@ public class DebugHandling {
         @TruffleBoundary
         private void attachStepInto() {
             FunctionStatementsEventListener parentListener = getFunctionStatementsEventListener(functionDefinitionNode);
-            parentListener.stepIntoInstrument = RInstrumentation.getInstrumenter().attachExecutionEventListener(SourceSectionFilter.newBuilder().tagIs(StandardTags.RootTag.class).build(),
+            parentListener.stepIntoInstrument = RInstrumentation.getInstrumenter().attachExecutionEventListener(SourceSectionFilter.newBuilder().tagIs(FunctionBodyBlockTag.class).build(),
                             new StepIntoInstrumentListener(parentListener));
 
         }
@@ -430,8 +431,10 @@ public class DebugHandling {
 
         public void attach() {
 
+            // Note: BlockStatement is not tagged as a STATEMENT, but there is FastR specific
+            // FunctionBodyBlockTag
             Instrumenter instrumenter = RInstrumentation.getInstrumenter();
-            SourceSectionFilter.Builder functionBuilder = RInstrumentation.createFunctionFilter(functionDefinitionNode, StandardTags.RootTag.class);
+            SourceSectionFilter.Builder functionBuilder = RInstrumentation.createFunctionFilter(functionDefinitionNode, FunctionBodyBlockTag.class);
             setBinding(instrumenter.attachExecutionEventListener(functionBuilder.build(), this));
 
             // Next attach statement handler to all STATEMENTs except LOOPs
@@ -470,7 +473,7 @@ public class DebugHandling {
                     accept(element.getSyntaxBody());
                     return null;
                 }
-            }.accept(functionDefinitionNode);
+            }.accept(functionDefinitionNode.getBody());
         }
 
         @Override
@@ -524,8 +527,9 @@ public class DebugHandling {
             CompilerDirectives.transferToInterpreter();
             print("debugging in: ", false);
             printCall(frame);
-            printNode(context.getInstrumentedNode(), true);
-            browserInteract(context.getInstrumentedNode(), frame);
+            Node node = context.getInstrumentedNode();
+            printNode(node, true);
+            browserInteract(node, frame);
         }
 
         @Override
@@ -582,6 +586,7 @@ public class DebugHandling {
             String callString = RContext.getRRuntimeASTAccess().getCallerSource(RArguments.getCall(frame));
             print(callString, true);
         }
+
     }
 
     @TruffleBoundary
@@ -621,10 +626,7 @@ public class DebugHandling {
                 // in case we did a step into that never called a function
                 clearStepInstrument();
                 RBaseNode node = (RBaseNode) context.getInstrumentedNode();
-                if (node.hasTag(StandardTags.RootTag.class)) {
-                    // already handled
-                    return;
-                }
+                assert !node.hasTag(StandardTags.RootTag.class) : "root is not a statement";
                 printNode(node, false);
                 browserInteract(node, frame);
             }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/helpers/TraceHandling.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/helpers/TraceHandling.java
index 72c99139236f988e32c49eb8cf47458bedabe009..142a26bbddc555a04a1dd018aba329754fc951f5 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/helpers/TraceHandling.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/helpers/TraceHandling.java
@@ -33,9 +33,9 @@ import com.oracle.truffle.api.instrumentation.EventBinding;
 import com.oracle.truffle.api.instrumentation.EventContext;
 import com.oracle.truffle.api.instrumentation.ExecutionEventListener;
 import com.oracle.truffle.api.instrumentation.SourceSectionFilter;
-import com.oracle.truffle.api.instrumentation.StandardTags;
 import com.oracle.truffle.api.utilities.CyclicAssumption;
 import com.oracle.truffle.r.nodes.instrumentation.RInstrumentation;
+import com.oracle.truffle.r.nodes.instrumentation.RSyntaxTags.FunctionBodyBlockTag;
 import com.oracle.truffle.r.runtime.FastROptions;
 import com.oracle.truffle.r.runtime.RArguments;
 import com.oracle.truffle.r.runtime.RCaller;
@@ -88,7 +88,7 @@ public class TraceHandling {
         if (FastROptions.TraceCalls.getBooleanValue()) {
             PrimitiveFunctionEntryEventListener fser = new PrimitiveFunctionEntryEventListener();
             SourceSectionFilter.Builder builder = SourceSectionFilter.newBuilder();
-            builder.tagIs(StandardTags.RootTag.class);
+            builder.tagIs(FunctionBodyBlockTag.class);
             SourceSectionFilter filter = builder.build();
             RInstrumentation.getInstrumenter().attachExecutionEventListener(filter, fser);
             setOutputHandler();
@@ -122,7 +122,7 @@ public class TraceHandling {
     private abstract static class TraceEventListener implements ExecutionEventListener {
 
         @CompilationFinal private boolean disabled;
-        CyclicAssumption disabledUnchangedAssumption = new CyclicAssumption("trace event disabled state unchanged");
+        private final CyclicAssumption disabledUnchangedAssumption = new CyclicAssumption("trace event disabled state unchanged");
 
         protected TraceEventListener() {
         }
diff --git a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/test/SpecialCallTest.java b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/test/SpecialCallTest.java
index 243a5e9e83510806ab7a5f15bc398e09c7b1d9f3..89a959f4b3d4ee8c19bc2edf8dbb05a55b26ba89 100644
--- a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/test/SpecialCallTest.java
+++ b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/test/SpecialCallTest.java
@@ -52,7 +52,7 @@ public class SpecialCallTest extends TestBase {
         public int special;
 
         CountCallsVisitor(RootCallTarget callTarget) {
-            accept(((RootWithBody) callTarget.getRootNode()).getBody().asRSyntaxNode());
+            accept(((RootWithBody) callTarget.getRootNode()).getBody());
         }
 
         @Override
@@ -108,7 +108,7 @@ public class SpecialCallTest extends TestBase {
 
         void print(RootCallTarget callTarget) {
             System.out.println();
-            accept(((RootWithBody) callTarget.getRootNode()).getBody().asRSyntaxNode());
+            accept(((RootWithBody) callTarget.getRootNode()).getBody());
         }
 
         @Override
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/variables/LocalReadVariableNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/variables/LocalReadVariableNode.java
index 533db2210151c99af8b2518e8367a45846453b9a..f67c254ce6760e5525ff1ef194c5455be9f1c515 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/variables/LocalReadVariableNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/variables/LocalReadVariableNode.java
@@ -51,7 +51,6 @@ public final class LocalReadVariableNode extends Node {
 
     private final Object identifier;
     private final boolean forceResult;
-    @CompilationFinal private boolean firstExecution = true;
 
     @CompilationFinal(dimensions = 1) private boolean[] seenValueKinds;
     @CompilationFinal private ValueProfile valueProfile;
@@ -145,13 +144,6 @@ public final class LocalReadVariableNode extends Node {
                 isPromiseProfile = ConditionProfile.createBinaryProfile();
             }
             if (isPromiseProfile.profile(result instanceof RPromise)) {
-                if (firstExecution) {
-                    CompilerDirectives.transferToInterpreterAndInvalidate();
-                    firstExecution = false;
-                    if (identifier instanceof String) {
-                        return ReadVariableNode.evalPromiseSlowPathWithName((String) identifier, frame, (RPromise) result);
-                    }
-                }
                 if (promiseHelper == null) {
                     CompilerDirectives.transferToInterpreterAndInvalidate();
                     promiseHelper = insert(new PromiseHelperNode());
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/BlockNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/BlockNode.java
index 184b48c6d55ccb37ef6ff74f73136536f303f428..bce05cd02c2a12190891b7dbbe6154f94abfe6a0 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/BlockNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/BlockNode.java
@@ -39,7 +39,7 @@ import com.oracle.truffle.r.runtime.nodes.RSyntaxNode;
  */
 public final class BlockNode extends OperatorNode {
 
-    @Children protected final RNode[] sequence;
+    @Children private final RNode[] sequence;
     @Child private SetVisibilityNode visibility;
 
     public BlockNode(SourceSection src, RSyntaxLookup operator, RNode[] sequence) {
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/FunctionBodyNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/FunctionBodyNode.java
new file mode 100644
index 0000000000000000000000000000000000000000..949b918b9417a8f9ff784fd338b9c3782c9e315a
--- /dev/null
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/FunctionBodyNode.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (c) 2018, 2018, 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.nodes.function;
+
+import com.oracle.truffle.api.CompilerDirectives;
+import com.oracle.truffle.api.frame.FrameDescriptor;
+import com.oracle.truffle.api.frame.FrameSlot;
+import com.oracle.truffle.api.frame.FrameSlotKind;
+import com.oracle.truffle.api.frame.VirtualFrame;
+import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.api.profiles.BranchProfile;
+import com.oracle.truffle.api.source.SourceSection;
+import com.oracle.truffle.r.runtime.RArguments;
+import com.oracle.truffle.r.runtime.RArguments.DispatchArgs;
+import com.oracle.truffle.r.runtime.RArguments.S3Args;
+import com.oracle.truffle.r.runtime.RArguments.S4Args;
+import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.runtime.RootBodyNode;
+import com.oracle.truffle.r.runtime.env.frame.FrameSlotChangeMonitor;
+import com.oracle.truffle.r.runtime.nodes.RNode;
+
+public final class FunctionBodyNode extends Node implements RootBodyNode {
+
+    @Child private RNode body;
+    @Child private RNode saveArguments;
+    @Child private SetupS3ArgsNode setupS3Args;
+    @Child private SetupS4ArgsNode setupS4Args;
+
+    public FunctionBodyNode(RNode saveArguments, RNode body) {
+        this.body = body;
+        this.saveArguments = saveArguments;
+    }
+
+    @Override
+    public Object visibleExecute(VirtualFrame frame) {
+        setupDispatchSlots(frame);
+        saveArguments.execute(frame);
+        return body.visibleExecute(frame);
+    }
+
+    @Override
+    public RNode getBody() {
+        return body;
+    }
+
+    @Override
+    public SourceSection getSourceSection() {
+        return body.getSourceSection();
+    }
+
+    private void setupDispatchSlots(VirtualFrame frame) {
+        DispatchArgs dispatchArgs = RArguments.getDispatchArgs(frame);
+        if (dispatchArgs == null) {
+            return;
+        }
+        if (dispatchArgs instanceof S3Args) {
+            S3Args s3Args = (S3Args) dispatchArgs;
+            if (setupS3Args == null) {
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                setupS3Args = insert(new SetupS3ArgsNode(frame.getFrameDescriptor()));
+            }
+            setupS3Args.execute(frame, s3Args);
+        } else {
+            S4Args s4Args = (S4Args) dispatchArgs;
+            if (setupS4Args == null) {
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                setupS4Args = insert(new SetupS4ArgsNode(frame.getFrameDescriptor()));
+            }
+            setupS4Args.execute(frame, s4Args);
+        }
+    }
+
+    private abstract static class SetupDispatchNode extends Node {
+        // S3/S4 slots
+        private final FrameSlot dotGenericSlot;
+        private final FrameSlot dotMethodSlot;
+
+        final BranchProfile invalidateFrameSlotProfile = BranchProfile.create();
+
+        SetupDispatchNode(FrameDescriptor frameDescriptor) {
+            dotGenericSlot = FrameSlotChangeMonitor.findOrAddFrameSlot(frameDescriptor, RRuntime.R_DOT_GENERIC, FrameSlotKind.Object);
+            dotMethodSlot = FrameSlotChangeMonitor.findOrAddFrameSlot(frameDescriptor, RRuntime.R_DOT_METHOD, FrameSlotKind.Object);
+        }
+
+        void executeDispatchArgs(VirtualFrame frame, DispatchArgs args) {
+            FrameSlotChangeMonitor.setObjectAndInvalidate(frame, dotGenericSlot, args.generic, false, invalidateFrameSlotProfile);
+            FrameSlotChangeMonitor.setObjectAndInvalidate(frame, dotMethodSlot, args.method, false, invalidateFrameSlotProfile);
+        }
+    }
+
+    private static final class SetupS3ArgsNode extends SetupDispatchNode {
+        // S3 slots
+        private final FrameSlot dotClassSlot;
+        private final FrameSlot dotGenericCallEnvSlot;
+        private final FrameSlot dotGenericCallDefSlot;
+        private final FrameSlot dotGroupSlot;
+
+        SetupS3ArgsNode(FrameDescriptor frameDescriptor) {
+            super(frameDescriptor);
+            dotClassSlot = FrameSlotChangeMonitor.findOrAddFrameSlot(frameDescriptor, RRuntime.R_DOT_CLASS, FrameSlotKind.Object);
+            dotGenericCallEnvSlot = FrameSlotChangeMonitor.findOrAddFrameSlot(frameDescriptor, RRuntime.R_DOT_GENERIC_CALL_ENV, FrameSlotKind.Object);
+            dotGenericCallDefSlot = FrameSlotChangeMonitor.findOrAddFrameSlot(frameDescriptor, RRuntime.R_DOT_GENERIC_DEF_ENV, FrameSlotKind.Object);
+            dotGroupSlot = FrameSlotChangeMonitor.findOrAddFrameSlot(frameDescriptor, RRuntime.R_DOT_GROUP, FrameSlotKind.Object);
+        }
+
+        void execute(VirtualFrame frame, S3Args args) {
+            super.executeDispatchArgs(frame, args);
+            FrameSlotChangeMonitor.setObjectAndInvalidate(frame, dotClassSlot, args.clazz, false, invalidateFrameSlotProfile);
+            FrameSlotChangeMonitor.setObjectAndInvalidate(frame, dotGenericCallEnvSlot, args.callEnv, false, invalidateFrameSlotProfile);
+            FrameSlotChangeMonitor.setObjectAndInvalidate(frame, dotGenericCallDefSlot, args.defEnv, false, invalidateFrameSlotProfile);
+            FrameSlotChangeMonitor.setObjectAndInvalidate(frame, dotGroupSlot, args.group, false, invalidateFrameSlotProfile);
+        }
+    }
+
+    private static final class SetupS4ArgsNode extends SetupDispatchNode {
+        // S4 slots
+        private final FrameSlot dotDefinedSlot;
+        private final FrameSlot dotTargetSlot;
+        private final FrameSlot dotMethodsSlot;
+
+        SetupS4ArgsNode(FrameDescriptor frameDescriptor) {
+            super(frameDescriptor);
+            dotDefinedSlot = FrameSlotChangeMonitor.findOrAddFrameSlot(frameDescriptor, RRuntime.R_DOT_DEFINED, FrameSlotKind.Object);
+            dotTargetSlot = FrameSlotChangeMonitor.findOrAddFrameSlot(frameDescriptor, RRuntime.R_DOT_TARGET, FrameSlotKind.Object);
+            dotMethodsSlot = FrameSlotChangeMonitor.findOrAddFrameSlot(frameDescriptor, RRuntime.R_DOT_METHODS, FrameSlotKind.Object);
+        }
+
+        void execute(VirtualFrame frame, S4Args args) {
+            super.executeDispatchArgs(frame, args);
+            FrameSlotChangeMonitor.setObjectAndInvalidate(frame, dotDefinedSlot, args.defined, false, invalidateFrameSlotProfile);
+            FrameSlotChangeMonitor.setObjectAndInvalidate(frame, dotTargetSlot, args.target, false, invalidateFrameSlotProfile);
+            FrameSlotChangeMonitor.setObjectAndInvalidate(frame, dotMethodsSlot, args.methods, false, invalidateFrameSlotProfile);
+        }
+    }
+}
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/FunctionDefinitionNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/FunctionDefinitionNode.java
index fbb1754702a62025866fb7a5f843c0f22ddce9ef..34712bf4944693f45161fc1241f8a2387d364341 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/FunctionDefinitionNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/FunctionDefinitionNode.java
@@ -35,7 +35,6 @@ import com.oracle.truffle.api.frame.FrameSlot;
 import com.oracle.truffle.api.frame.FrameSlotKind;
 import com.oracle.truffle.api.frame.FrameSlotTypeException;
 import com.oracle.truffle.api.frame.VirtualFrame;
-import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.api.nodes.NodeUtil;
 import com.oracle.truffle.api.nodes.NodeUtil.NodeCountFilter;
 import com.oracle.truffle.api.profiles.BranchProfile;
@@ -55,16 +54,13 @@ import com.oracle.truffle.r.runtime.ArgumentsSignature;
 import com.oracle.truffle.r.runtime.ExitException;
 import com.oracle.truffle.r.runtime.JumpToTopLevelException;
 import com.oracle.truffle.r.runtime.RArguments;
-import com.oracle.truffle.r.runtime.RArguments.DispatchArgs;
-import com.oracle.truffle.r.runtime.RArguments.S3Args;
-import com.oracle.truffle.r.runtime.RArguments.S4Args;
 import com.oracle.truffle.r.runtime.RDeparse;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RErrorHandling;
 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.ReturnException;
+import com.oracle.truffle.r.runtime.RootBodyNode;
 import com.oracle.truffle.r.runtime.Utils.DebugExitException;
 import com.oracle.truffle.r.runtime.builtins.RBuiltinDescriptor;
 import com.oracle.truffle.r.runtime.context.RContext;
@@ -89,7 +85,7 @@ public final class FunctionDefinitionNode extends RRootNode implements RSyntaxNo
 
     private final FormalArguments formalArguments;
 
-    @Child private RNode body;
+    @Child private RootBodyNode body;
 
     /**
      * This exists for debugging purposes. It is set initially when the function is defined to
@@ -108,7 +104,6 @@ public final class FunctionDefinitionNode extends RRootNode implements RSyntaxNo
 
     private final RNodeClosureCache closureCache = new RNodeClosureCache();
 
-    @Child private RNode saveArguments;
     @Child private FrameSlotNode onExitSlot;
     @Child private InlineCacheNode onExitExpressionCache;
     private final ConditionProfile onExitProfile = ConditionProfile.createBinaryProfile();
@@ -121,9 +116,6 @@ public final class FunctionDefinitionNode extends RRootNode implements RSyntaxNo
 
     @Child private PostProcessArgumentsNode argPostProcess;
 
-    @Child private SetupS3ArgsNode setupS3Args;
-    @Child private SetupS4ArgsNode setupS4Args;
-
     private final boolean needsSplitting;
 
     @CompilationFinal private boolean containsDispatch;
@@ -153,8 +145,7 @@ public final class FunctionDefinitionNode extends RRootNode implements RSyntaxNo
         assert FrameSlotChangeMonitor.isValidFrameDescriptor(frameDesc);
         assert src != null;
         this.sourceSectionR = src;
-        this.saveArguments = saveArguments;
-        this.body = body.asRNode();
+        this.body = new FunctionBodyNode(saveArguments, body.asRNode());
         this.name = name;
         this.onExitSlot = FrameSlotNode.createInitialized(frameDesc, RFrameSlot.OnExit, false);
         this.needsSplitting = needsAnyBuiltinSplitting();
@@ -269,7 +260,7 @@ public final class FunctionDefinitionNode extends RRootNode implements RSyntaxNo
     }
 
     public RSyntaxNode getBody() {
-        return body.asRSyntaxNode();
+        return body.getBody().asRSyntaxNode();
     }
 
     public PostProcessArgumentsNode getArgPostProcess() {
@@ -281,8 +272,6 @@ public final class FunctionDefinitionNode extends RRootNode implements RSyntaxNo
         boolean runOnExitHandlers = true;
         try {
             verifyEnclosingAssumptions(frame);
-            setupDispatchSlots(frame);
-            saveArguments.execute(frame);
             Object result = body.visibleExecute(frame);
             normalExit.enter();
             if (CompilerDirectives.inInterpreter() && result == null) {
@@ -398,91 +387,6 @@ public final class FunctionDefinitionNode extends RRootNode implements RSyntaxNo
         }
     }
 
-    private abstract static class SetupDispatchNode extends Node {
-        // S3/S4 slots
-        private final FrameSlot dotGenericSlot;
-        private final FrameSlot dotMethodSlot;
-
-        final BranchProfile invalidateFrameSlotProfile = BranchProfile.create();
-
-        SetupDispatchNode(FrameDescriptor frameDescriptor) {
-            dotGenericSlot = FrameSlotChangeMonitor.findOrAddFrameSlot(frameDescriptor, RRuntime.R_DOT_GENERIC, FrameSlotKind.Object);
-            dotMethodSlot = FrameSlotChangeMonitor.findOrAddFrameSlot(frameDescriptor, RRuntime.R_DOT_METHOD, FrameSlotKind.Object);
-        }
-
-        void execute(VirtualFrame frame, DispatchArgs args) {
-            FrameSlotChangeMonitor.setObjectAndInvalidate(frame, dotGenericSlot, args.generic, false, invalidateFrameSlotProfile);
-            FrameSlotChangeMonitor.setObjectAndInvalidate(frame, dotMethodSlot, args.method, false, invalidateFrameSlotProfile);
-        }
-    }
-
-    private static final class SetupS3ArgsNode extends SetupDispatchNode {
-        // S3 slots
-        private final FrameSlot dotClassSlot;
-        private final FrameSlot dotGenericCallEnvSlot;
-        private final FrameSlot dotGenericCallDefSlot;
-        private final FrameSlot dotGroupSlot;
-
-        SetupS3ArgsNode(FrameDescriptor frameDescriptor) {
-            super(frameDescriptor);
-            dotClassSlot = FrameSlotChangeMonitor.findOrAddFrameSlot(frameDescriptor, RRuntime.R_DOT_CLASS, FrameSlotKind.Object);
-            dotGenericCallEnvSlot = FrameSlotChangeMonitor.findOrAddFrameSlot(frameDescriptor, RRuntime.R_DOT_GENERIC_CALL_ENV, FrameSlotKind.Object);
-            dotGenericCallDefSlot = FrameSlotChangeMonitor.findOrAddFrameSlot(frameDescriptor, RRuntime.R_DOT_GENERIC_DEF_ENV, FrameSlotKind.Object);
-            dotGroupSlot = FrameSlotChangeMonitor.findOrAddFrameSlot(frameDescriptor, RRuntime.R_DOT_GROUP, FrameSlotKind.Object);
-        }
-
-        void execute(VirtualFrame frame, S3Args args) {
-            super.execute(frame, args);
-            FrameSlotChangeMonitor.setObjectAndInvalidate(frame, dotClassSlot, args.clazz, false, invalidateFrameSlotProfile);
-            FrameSlotChangeMonitor.setObjectAndInvalidate(frame, dotGenericCallEnvSlot, args.callEnv, false, invalidateFrameSlotProfile);
-            FrameSlotChangeMonitor.setObjectAndInvalidate(frame, dotGenericCallDefSlot, args.defEnv, false, invalidateFrameSlotProfile);
-            FrameSlotChangeMonitor.setObjectAndInvalidate(frame, dotGroupSlot, args.group, false, invalidateFrameSlotProfile);
-        }
-    }
-
-    private static final class SetupS4ArgsNode extends SetupDispatchNode {
-        // S4 slots
-        private final FrameSlot dotDefinedSlot;
-        private final FrameSlot dotTargetSlot;
-        private final FrameSlot dotMethodsSlot;
-
-        SetupS4ArgsNode(FrameDescriptor frameDescriptor) {
-            super(frameDescriptor);
-            dotDefinedSlot = FrameSlotChangeMonitor.findOrAddFrameSlot(frameDescriptor, RRuntime.R_DOT_DEFINED, FrameSlotKind.Object);
-            dotTargetSlot = FrameSlotChangeMonitor.findOrAddFrameSlot(frameDescriptor, RRuntime.R_DOT_TARGET, FrameSlotKind.Object);
-            dotMethodsSlot = FrameSlotChangeMonitor.findOrAddFrameSlot(frameDescriptor, RRuntime.R_DOT_METHODS, FrameSlotKind.Object);
-        }
-
-        void execute(VirtualFrame frame, S4Args args) {
-            super.execute(frame, args);
-            FrameSlotChangeMonitor.setObjectAndInvalidate(frame, dotDefinedSlot, args.defined, false, invalidateFrameSlotProfile);
-            FrameSlotChangeMonitor.setObjectAndInvalidate(frame, dotTargetSlot, args.target, false, invalidateFrameSlotProfile);
-            FrameSlotChangeMonitor.setObjectAndInvalidate(frame, dotMethodsSlot, args.methods, false, invalidateFrameSlotProfile);
-        }
-    }
-
-    private void setupDispatchSlots(VirtualFrame frame) {
-        DispatchArgs dispatchArgs = RArguments.getDispatchArgs(frame);
-        if (dispatchArgs == null) {
-            return;
-        }
-        if (dispatchArgs instanceof S3Args) {
-            S3Args s3Args = (S3Args) dispatchArgs;
-            if (setupS3Args == null) {
-                CompilerDirectives.transferToInterpreterAndInvalidate();
-                setupS3Args = insert(new SetupS3ArgsNode(frame.getFrameDescriptor()));
-            }
-            setupS3Args.execute(frame, s3Args);
-        } else {
-            S4Args s4Args = (S4Args) dispatchArgs;
-            if (setupS4Args == null) {
-                CompilerDirectives.transferToInterpreterAndInvalidate();
-                setupS4Args = insert(new SetupS4ArgsNode(frame.getFrameDescriptor()));
-            }
-            setupS4Args.execute(frame, s4Args);
-        }
-    }
-
     private static RPairList getCurrentOnExitList(VirtualFrame frame, FrameSlot slot) {
         try {
             return (RPairList) FrameSlotChangeMonitor.getObject(slot, frame);
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 6f987abcbbe6212c1ee3d90b6fcc47c37eebc22d..1474a417c34bf7d66be37726da02752e677a9e80 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
@@ -787,6 +787,11 @@ public abstract class RCallNode extends RCallBaseNode implements RSyntaxNode, RS
             return arg;
         }
 
+        @Override
+        public SourceSection getSourceSection() {
+            return arg.getSourceSection();
+        }
+
         @Override
         public Object execute(VirtualFrame frame) {
             try {
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/instrumentation/RInstrumentation.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/instrumentation/RInstrumentation.java
index 56581f056d86f41181c127609c525a11ae84f6b4..e83d262ee78ddb1a40352cd94ceddaa022784dbf 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/instrumentation/RInstrumentation.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/instrumentation/RInstrumentation.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2018, 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
@@ -30,6 +30,7 @@ import com.oracle.truffle.api.instrumentation.StandardTags;
 import com.oracle.truffle.api.source.Source;
 import com.oracle.truffle.api.source.SourceSection;
 import com.oracle.truffle.r.nodes.function.FunctionDefinitionNode;
+import com.oracle.truffle.r.nodes.instrumentation.RSyntaxTags.FunctionBodyBlockTag;
 import com.oracle.truffle.r.runtime.FastROptions;
 import com.oracle.truffle.r.runtime.RSource;
 import com.oracle.truffle.r.runtime.context.RContext;
@@ -102,7 +103,7 @@ public class RInstrumentation {
     public static SourceSectionFilter.Builder createFunctionStartFilter(RFunction func) {
         FunctionDefinitionNode fdn = (FunctionDefinitionNode) func.getRootNode();
         SourceSectionFilter.Builder builder = SourceSectionFilter.newBuilder();
-        builder.tagIs(StandardTags.RootTag.class);
+        builder.tagIs(FunctionBodyBlockTag.class);
         builder.sourceSectionEquals(fdn.getBody().getSourceSection());
         return builder;
     }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/instrumentation/RSyntaxTags.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/instrumentation/RSyntaxTags.java
index 75e32fd0802a978710322964a28bb756e54b5277..becb1090f31cd454e5a51e3b59fc0804751bafe3 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/instrumentation/RSyntaxTags.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/instrumentation/RSyntaxTags.java
@@ -24,6 +24,7 @@ package com.oracle.truffle.r.nodes.instrumentation;
 
 import com.oracle.truffle.api.instrumentation.StandardTags;
 import com.oracle.truffle.api.instrumentation.Tag;
+import com.oracle.truffle.r.runtime.RootBodyNode;
 
 public class RSyntaxTags {
 
@@ -33,6 +34,22 @@ public class RSyntaxTags {
         }
     }
 
+    /**
+     * Marks a block of statements that is the body of a function, the difference to
+     * {@link com.oracle.truffle.api.instrumentation.StandardTags.RootTag} is that the
+     * {@code RootTag} is supposed to save arguments and then invoke the actual body tagged with
+     * this tag.
+     *
+     * More technically, this tag tags {@link com.oracle.truffle.r.nodes.control.BlockNode}s that
+     * have parent of type {@link RootBodyNode}.
+     */
+    @Tag.Identifier("FUNCTIONBODYBLOCK")
+    public static final class FunctionBodyBlockTag extends Tag {
+        private FunctionBodyBlockTag() {
+            // no instances
+        }
+    }
+
     @SuppressWarnings("unchecked") public static final Class<? extends Tag>[] ALL_TAGS = (Class<? extends Tag>[]) new Class<?>[]{StandardTags.CallTag.class, StandardTags.StatementTag.class,
-                    StandardTags.RootTag.class, LoopTag.class};
+                    StandardTags.RootTag.class, LoopTag.class, FunctionBodyBlockTag.class};
 }
diff --git a/com.oracle.truffle.r.parser/src/com/oracle/truffle/r/parser/DefaultRParserFactory.java b/com.oracle.truffle.r.parser/src/com/oracle/truffle/r/parser/DefaultRParserFactory.java
index f92d3697ba5833dfbe6f37da88c4e8f578bb0a56..d05bcab85c9f53cf7b617f2441c72612acdf43b5 100644
--- a/com.oracle.truffle.r.parser/src/com/oracle/truffle/r/parser/DefaultRParserFactory.java
+++ b/com.oracle.truffle.r.parser/src/com/oracle/truffle/r/parser/DefaultRParserFactory.java
@@ -62,6 +62,26 @@ public class DefaultRParserFactory extends RParserFactory {
             }
         }
 
+        @Override
+        public List<T> statements(Source source, Source fullSource, int startLine, RCodeBuilder<T> builder, TruffleRLanguage language) throws ParseException {
+            try {
+                try {
+                    RContext context = language.getContextReference().get();
+                    RParser<T> parser = new RParser<>(source, fullSource, startLine, builder, language, context.sourceCache);
+                    return parser.script();
+                } catch (IllegalArgumentException e) {
+                    // the lexer will wrap exceptions in IllegalArgumentExceptions
+                    if (e.getCause() instanceof RecognitionException) {
+                        throw (RecognitionException) e.getCause();
+                    } else {
+                        throw e;
+                    }
+                }
+            } catch (RecognitionException e) {
+                throw handleRecognitionException(source, e);
+            }
+        }
+
         @Override
         public RootCallTarget rootFunction(Source source, String name, RCodeBuilder<T> builder, TruffleRLanguage language) throws ParseException {
             RContext context = language.getContextReference().get();
diff --git a/com.oracle.truffle.r.parser/src/com/oracle/truffle/r/parser/ParserGeneration.java b/com.oracle.truffle.r.parser/src/com/oracle/truffle/r/parser/ParserGeneration.java
index c54448034f70222006046e7952cb8762e17034b6..be131a7a7da9e1fd430fb2d8b5abf352fb2f2f89 100644
--- a/com.oracle.truffle.r.parser/src/com/oracle/truffle/r/parser/ParserGeneration.java
+++ b/com.oracle.truffle.r.parser/src/com/oracle/truffle/r/parser/ParserGeneration.java
@@ -98,6 +98,7 @@ public class ParserGeneration {
         "allow everything but newlines in %<ident>% operators",
         "allow strings in :: and :::",
         "use file for interactive single-line source",
-        ":: and ::: do not set argument names"
+        ":: and ::: do not set argument names",
+        "refactored: use file for interactive single-line source"
     };
 }
diff --git a/com.oracle.truffle.r.parser/src/com/oracle/truffle/r/parser/R.g b/com.oracle.truffle.r.parser/src/com/oracle/truffle/r/parser/R.g
index daab0541ae8da9e5eb327ad196c7950d7165a873..3dbf82f7b36a8bf3370f549c291ca6d8004327b8 100644
--- a/com.oracle.truffle.r.parser/src/com/oracle/truffle/r/parser/R.g
+++ b/com.oracle.truffle.r.parser/src/com/oracle/truffle/r/parser/R.g
@@ -75,90 +75,25 @@ import com.oracle.truffle.r.runtime.RError;
     private TruffleRLanguage language;
     private int fileStartOffset = 0;
     private Map<String, Source> sourceCache;
-    
-    /**
-     * Always use this constructor to initialize the R specific fields.
-     */
+
     public RParser(Source source, RCodeBuilder<T> builder, TruffleRLanguage language, Map<String, Source> sourceCache) {
         super(new CommonTokenStream(new RLexer(new ANTLRStringStream(source.getCharacters().toString()))));
         assert source != null && builder != null;
         this.initialSource = source;
         this.builder = builder;
         this.language = language;
-        this.sourceCache = sourceCache;
-        if (source.getURI() != null && source.getName().contains("#")) {
-        	this.source = createFullSource(source);
-        } else {
-        	this.source = source;
-        }
+        this.source = source;
     }
-    
-    private Source createFullSource(Source original) {
-	    String originalName = original.getName();
 
-	    // check if source name is like 'path/to/source.R#45-54'
-	    int hash_idx = originalName.lastIndexOf("#");
-	    if (hash_idx == -1) {
-	        return original;
-	    }
-
-	    String fileName = originalName.substring(0, hash_idx);
-	    String lineRange = originalName.substring(hash_idx + 1);
-
-	    try {
-	        // check for line range, e.g. '45-54'
-	        int startLine = -1;
-	        int endLine = -1;
-	        int dashIdx = lineRange.indexOf('-');
-	        if (dashIdx != -1) {
-	            startLine = Integer.parseInt(lineRange.substring(0, dashIdx));
-	            endLine = Integer.parseInt(lineRange.substring(dashIdx + 1));
-	        } else {
-	            startLine = Integer.parseInt(lineRange);
-	            endLine = startLine;
-	        }
-	        File f = new File(fileName);
-	        Source fullSource;
-	        String canonicalName;
-	        try {
-	            canonicalName = f.getAbsoluteFile().getCanonicalPath();
-	            fullSource = sourceCache != null ? sourceCache.get(canonicalName) : null;
-	        } catch(IOException e) {
-	            // ignore an freshly load file
-	            fullSource = null;
-	            canonicalName = null;
-	        }
-            if(fullSource == null) {
-    	        Builder<IOException, RuntimeException, RuntimeException> newBuilder = Source.newBuilder(f);
-    	        if (original.isInteractive()) {
-    	            newBuilder.interactive();
-    	        }
-    	        fullSource = newBuilder.build();
-    	        
-    	        if (sourceCache != null && canonicalName != null) {
-    	        	sourceCache.put(canonicalName, fullSource);
-    	        }
-            }
-
-	        // verify to avoid accidentally matching file names
-	        for (int i = 0; i < endLine - startLine + 1; i++) {
-	            if (!original.getCharacters(i + 1).equals(fullSource.getCharacters(startLine + i))) {
-	                return original;
-	            }
-	        }
-
-	        fileStartOffset = -fullSource.getLineStartOffset(startLine);
-	        return fullSource;
-	    } catch (NumberFormatException e) {
-	        // invalid line number
-	    } catch (IllegalArgumentException e) {
-            // file name is accidentally named in the expected scheme
-	    } catch (IOException e) {
-	    } catch (RuntimeException e) {
-	    	assert rethrow(e);
-	    }
-	    return original;
-	}
+    public RParser(Source source, Source fullSource, int startLine, RCodeBuilder<T> builder, TruffleRLanguage language, Map<String, Source> sourceCache) {
+        super(new CommonTokenStream(new RLexer(new ANTLRStringStream(source.getCharacters().toString()))));
+        assert source != null && builder != null;
+        this.initialSource = source;
+        this.builder = builder;
+        this.language = language;
+        this.source = fullSource;
+        fileStartOffset = -fullSource.getLineStartOffset(startLine);
+    }
         
     private <T extends Throwable> boolean rethrow(T e) throws T {
     	throw e;
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RParserFactory.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RParserFactory.java
index f42227d82e276a80c92f214e516232225a6cdf2b..e49e36e7f82ef7ad5af92d6ec050330bad2bbd12 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RParserFactory.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RParserFactory.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2018, 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
@@ -37,6 +37,8 @@ public abstract class RParserFactory {
     public interface Parser<T> {
         List<T> script(Source source, RCodeBuilder<T> builder, TruffleRLanguage language) throws ParseException;
 
+        List<T> statements(Source source, Source fullSource, int startLine, RCodeBuilder<T> builder, TruffleRLanguage language) throws ParseException;
+
         RootCallTarget rootFunction(Source source, String name, RCodeBuilder<T> builder, TruffleRLanguage language) throws ParseException;
 
         boolean isRecognitionException(Throwable t);
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RootBodyNode.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RootBodyNode.java
new file mode 100644
index 0000000000000000000000000000000000000000..68031a62f3c1f75120ec40ea4ebddbde999edaaa
--- /dev/null
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RootBodyNode.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2018, 2018, 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;
+
+import com.oracle.truffle.api.frame.VirtualFrame;
+import com.oracle.truffle.api.instrumentation.ProbeNode;
+import com.oracle.truffle.api.instrumentation.StandardTags.RootTag;
+import com.oracle.truffle.api.instrumentation.Tag;
+import com.oracle.truffle.r.runtime.nodes.RInstrumentableNode;
+import com.oracle.truffle.r.runtime.nodes.RNode;
+import com.oracle.truffle.r.runtime.nodes.instrumentation.RRootBodyNodeWrapper;
+
+/**
+ * Marks a node that represents the body of a {@link com.oracle.truffle.api.nodes.RootNode}, such
+ * nodes are tagged with {@link com.oracle.truffle.api.instrumentation.StandardTags.RootTag} and
+ * should inherit {@link RNode}.
+ *
+ * The {@link RootBodyNode} task is to save the arguments from frame's arguments array to the local
+ * variables and then invoke the actual body statement accessible via {@link #getBody()}.
+ */
+public interface RootBodyNode extends RInstrumentableNode {
+    RNode getBody();
+
+    Object visibleExecute(VirtualFrame frame);
+
+    @Override
+    default boolean isInstrumentable() {
+        return true;
+    }
+
+    @Override
+    default boolean hasTag(Class<? extends Tag> tag) {
+        return tag == RootTag.class;
+    }
+
+    @Override
+    default WrapperNode createWrapper(ProbeNode probe) {
+        return new RRootBodyNodeWrapper(this, probe);
+    }
+}
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RootWithBody.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RootWithBody.java
index b2736e10e5e517f3d2d9d73c1109e44cfdfdd3ba..2af8f9a54a5af6bd615349b9adc13503be91ad58 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RootWithBody.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RootWithBody.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2018, 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
@@ -22,12 +22,16 @@
  */
 package com.oracle.truffle.r.runtime;
 
-import com.oracle.truffle.r.runtime.nodes.RNode;
+import com.oracle.truffle.r.runtime.nodes.RSyntaxNode;
 
 /**
- * Used for testing.
+ * Used to allow access to the body field for testing purposes.
  */
 public interface RootWithBody {
 
-    RNode getBody();
+    /**
+     * Should return the real body, i.e. what is wrapped by {@link RootBodyNode}.
+     */
+    RSyntaxNode getBody();
+
 }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/nodes/instrumentation/RRootBodyNodeWrapper.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/nodes/instrumentation/RRootBodyNodeWrapper.java
new file mode 100644
index 0000000000000000000000000000000000000000..37ca385062c29fb441a49316f962b6ac6b015586
--- /dev/null
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/nodes/instrumentation/RRootBodyNodeWrapper.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2018, 2018, 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.nodes.instrumentation;
+
+import com.oracle.truffle.api.frame.VirtualFrame;
+import com.oracle.truffle.api.instrumentation.InstrumentableNode;
+import com.oracle.truffle.api.instrumentation.ProbeNode;
+import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.api.nodes.NodeCost;
+import com.oracle.truffle.api.nodes.NodeInfo;
+import com.oracle.truffle.api.source.SourceSection;
+import com.oracle.truffle.r.runtime.RootBodyNode;
+import com.oracle.truffle.r.runtime.nodes.RNode;
+
+@NodeInfo(cost = NodeCost.NONE)
+public final class RRootBodyNodeWrapper extends Node implements RootBodyNode, InstrumentableNode.WrapperNode {
+    @Child private RootBodyNode delegate;
+    @Child private ProbeNode probeNode;
+
+    public RRootBodyNodeWrapper(RootBodyNode delegate, ProbeNode probeNode) {
+        assert delegate != null;
+        assert !(delegate instanceof RRootBodyNodeWrapper);
+        this.delegate = delegate;
+        this.probeNode = probeNode;
+    }
+
+    @Override
+    public Node getDelegateNode() {
+        return (Node) delegate;
+    }
+
+    @Override
+    public ProbeNode getProbeNode() {
+        return probeNode;
+    }
+
+    @Override
+    public Object visibleExecute(VirtualFrame frame) {
+        Object returnValue;
+        for (;;) {
+            boolean wasOnReturnExecuted = false;
+            try {
+                probeNode.onEnter(frame);
+                returnValue = delegate.visibleExecute(frame);
+                wasOnReturnExecuted = true;
+                probeNode.onReturnValue(frame, returnValue);
+                break;
+            } catch (Throwable t) {
+                Object result = probeNode.onReturnExceptionalOrUnwind(frame, t, wasOnReturnExecuted);
+                if (result == ProbeNode.UNWIND_ACTION_REENTER) {
+                    continue;
+                } else if (result != null) {
+                    returnValue = result;
+                    break;
+                }
+                throw t;
+            }
+        }
+        return returnValue;
+    }
+
+    @Override
+    public RNode getBody() {
+        return delegate.getBody();
+    }
+
+    @Override
+    public SourceSection getSourceSection() {
+        return ((Node) delegate).getSourceSection();
+    }
+}
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 00e515d31f38a6905516bed94d2d79e31c60714b..a18d440f52151239237a8260a2c58ad206482d6f 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
@@ -39,7 +39,6 @@ import java.util.concurrent.atomic.AtomicInteger;
 import org.junit.After;
 import org.junit.Assert;
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Test;
 
 import com.oracle.truffle.api.debug.Breakpoint;
@@ -498,7 +497,6 @@ public class FastRDebugTest {
     }
 
     @Test
-    @Ignore
     public void testReenterArgumentsAndValues() throws Throwable {
         // Test that after a re-enter, arguments are kept and variables are cleared.
         final Source source = sourceFromText("" +
@@ -719,7 +717,7 @@ public class FastRDebugTest {
             }
             assertNotNull("identifier \"" + expectedIdentifier + "\" not found", value);
             String valueStr = value.as(String.class);
-            assertEquals(expectedValueStr, valueStr);
+            assertEquals(line + ": " + code + "; identifier: '" + expectedIdentifier + "'", expectedValueStr, valueStr);
         }
 
         if (!run.isEmpty()) {
diff --git a/mx.fastr/suite.py b/mx.fastr/suite.py
index 7e17fff9837880a077c7eb7b3c5904bb0a86be44..3d7ca02b4b00d7c08a2ce76cafbfbc1b28b5dd72 100644
--- a/mx.fastr/suite.py
+++ b/mx.fastr/suite.py
@@ -231,6 +231,7 @@ suite = {
       "sourceDirs" : ["src"],
       "dependencies" : [
         "sdk:GRAAL_SDK",
+        "sdk:LAUNCHER_COMMON",
         "truffle:JLINE",
       ],
       "checkstyle" : "com.oracle.truffle.r.runtime",