diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/ReadVariableNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/ReadVariableNode.java
index 07d76cc910f8df78dd98fd3c551e732f55388c4e..098f100eace5c3036306ac9b25e967a401cda10b 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/ReadVariableNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/ReadVariableNode.java
@@ -139,7 +139,7 @@ public abstract class ReadVariableNode extends RNode implements VisibilityContro
         }
 
         private ReadVariableNode resolveNonFrame() {
-            RFunction lookupResult = RContext.getLookup().lookup(RRuntime.toString(symbol));
+            RFunction lookupResult = RContext.getEngine().lookupBuiltin(RRuntime.toString(symbol));
             if (lookupResult != null) {
                 return BuiltinFunctionVariableNodeFactory.create(lookupResult);
             } else {
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/RBuiltinPackage.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/RBuiltinPackage.java
index 322b2dfc052b801b6f0093265f31a2734b07e464..94bd2aa01937d29d9c0c7ba6c90eb7566a576308 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/RBuiltinPackage.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/RBuiltinPackage.java
@@ -126,7 +126,7 @@ public abstract class RBuiltinPackage {
         List<Component> sources = rSources.get(getName());
         if (sources != null) {
             for (Component src : sources) {
-                REngine.parseAndEval(src.libContents, frame, envForFrame, false);
+                RContext.getEngine().parseAndEval(src.libContents, frame, envForFrame, false);
             }
         }
     }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/DoCall.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/DoCall.java
index 87159b8f4b96a4032a6b995baf2f0b746357a51f..666355af5e2f1843574e56f06533acdd7ac3512a 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/DoCall.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/DoCall.java
@@ -41,7 +41,7 @@ public abstract class DoCall extends RBuiltinNode {
 
     @Specialization(guards = "lengthOne")
     public Object doDoCall(VirtualFrame frame, RAbstractStringVector fname, RList argsAsList, @SuppressWarnings("unused") REnvironment env) {
-        RFunction func = RContext.getLookup().lookup(fname.getDataAt(0));
+        RFunction func = RContext.getEngine().lookupBuiltin(fname.getDataAt(0));
         if (func == null) {
             throw RError.getGenericError(getEncapsulatingSourceSection(), "could not find function " + fname);
         }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/EvalFunctions.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/EvalFunctions.java
index 4f14edb54596c8e163161b5bda42feda246d491d..a7ac46878e1aa2814ec6fe48f82ea88b9e329292 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/EvalFunctions.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/EvalFunctions.java
@@ -71,9 +71,9 @@ public class EvalFunctions {
                 try {
                     Object result;
                     if (expr instanceof RExpression) {
-                        result = REngine.eval(getFunction(), (RExpression) expr, envir, REnvironment.emptyEnv());
+                        result = RContext.getEngine().eval(getFunction(), (RExpression) expr, envir, REnvironment.emptyEnv());
                     } else {
-                        result = REngine.eval(getFunction(), (RLanguage) expr, envir, REnvironment.emptyEnv());
+                        result = RContext.getEngine().eval(getFunction(), (RLanguage) expr, envir, REnvironment.emptyEnv());
                     }
                     return result;
                 } catch (PutException x) {
@@ -88,7 +88,7 @@ public class EvalFunctions {
 
         protected RFunction getFunction() {
             if (function == null) {
-                function = RContext.getLookup().lookup(getRBuiltin().name());
+                function = RContext.getEngine().lookupBuiltin(getRBuiltin().name());
             }
             return function;
         }
@@ -114,7 +114,7 @@ public class EvalFunctions {
              * caller, so we can evaluate the promise using frame.
              */
             controlVisibility();
-            Object exprVal = REngine.evalPromise(expr, frame);
+            Object exprVal = RContext.getEngine().evalPromise(expr, frame);
             return doEvalBody(exprVal, envir, enclos);
         }
 
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/Exists.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/Exists.java
index 54925f09777e914551c9dbf39e97d4f9402b936a..7ee760128db8f141edc3cdf7671dc38cbc93db2e 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/Exists.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/Exists.java
@@ -102,7 +102,7 @@ public abstract class Exists extends RBuiltinNode {
         if (!name.equals(lastName)) {
             CompilerDirectives.transferToInterpreterAndInvalidate();
             lastName = name;
-            lastLookup = RContext.getLookup().lookup(name) != null;
+            lastLookup = RContext.getEngine().lookupBuiltin(name) != null;
         }
         // FIXME deal with changes in packages due to deleting symbols
         return lastLookup;
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/Parse.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/Parse.java
index f712b2540629f3e85e34cd6c3ff7b670dac6a39f..ae79b928c600d6ce608871d56b14f5349db367ab 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/Parse.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/Parse.java
@@ -23,6 +23,7 @@
 package com.oracle.truffle.r.nodes.builtin.base;
 
 import static com.oracle.truffle.r.nodes.builtin.RBuiltinKind.*;
+import static com.oracle.truffle.r.runtime.RContext.Engine.ParseException;
 
 import java.io.*;
 
@@ -30,7 +31,6 @@ import com.oracle.truffle.api.*;
 import com.oracle.truffle.api.CompilerDirectives.SlowPath;
 import com.oracle.truffle.api.dsl.*;
 import com.oracle.truffle.r.nodes.builtin.*;
-import com.oracle.truffle.r.nodes.builtin.REngine.ParseException;
 import com.oracle.truffle.r.runtime.*;
 import com.oracle.truffle.r.runtime.data.*;
 
@@ -91,7 +91,7 @@ public abstract class Parse extends RInvisibleBuiltinNode {
 
     @SlowPath
     private static RExpression doParse(String script) throws ParseException {
-        return REngine.parse(script);
+        return RContext.getEngine().parse(script);
     }
 
     @SlowPath
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/PrettyPrinterNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/PrettyPrinterNode.java
index f854294286c9080e878200b85a0c053dfd8d851d..fbabf6f952e72d95bd9e032142fcdfab109d9eb4 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/PrettyPrinterNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/PrettyPrinterNode.java
@@ -629,7 +629,6 @@ public abstract class PrettyPrinterNode extends RNode {
         if (re == null) {
             // the two are allocated side by side; checking for re is sufficient
             CompilerDirectives.transferToInterpreterAndInvalidate();
-            RBuiltinPackages packages = (RBuiltinPackages) RContext.getLookup();
             re = insert(ReFactory.create(new RNode[1], RBuiltinPackages.lookupBuiltin("Re")));
             im = insert(ImFactory.create(new RNode[1], RBuiltinPackages.lookupBuiltin("Im")));
         }
@@ -665,7 +664,7 @@ public abstract class PrettyPrinterNode extends RNode {
         if (operand.getRowNames() == RNull.instance || ((RAbstractVector) operand.getRowNames()).getLength() == 0) {
             return "NULL\n<0 rows> (or 0-length row.names)";
         }
-        RFunction getFunction = RContext.getLookup().lookup("get");
+        RFunction getFunction = RContext.getEngine().lookupBuiltin("get");
         RFunction formatFunction = (RFunction) indirectCall.call(frame, getFunction.getTarget(), RArguments.create(getFunction, REnvironment.globalEnv().getFrame(), new Object[]{"format.data.frame"}));
         return RRuntime.toString(indirectCall.call(frame, formatFunction.getTarget(), RArguments.create(formatFunction, new Object[]{operand})));
     }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/ProcTime.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/ProcTime.java
index cb7bf926ab0d865ca79b03001898f2635e197daf..eba93720cc5653a0064c05cdcdef55f1fd93bc29 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/ProcTime.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/ProcTime.java
@@ -43,7 +43,7 @@ public abstract class ProcTime extends RBuiltinNode {
     public RDoubleVector procTime() {
         controlVisibility();
         double[] data = new double[5];
-        long nowInNanos = REngine.elapsedTimeInNanos();
+        long nowInNanos = RContext.getEngine().elapsedTimeInNanos();
         if (bean == null) {
             bean = ManagementFactory.getThreadMXBean();
         }
@@ -52,7 +52,7 @@ public abstract class ProcTime extends RBuiltinNode {
         data[0] = asDoubleSecs(userTimeInNanos);
         data[1] = asDoubleSecs(sysTimeInNanos);
         data[2] = asDoubleSecs(nowInNanos);
-        long[] childTimes = REngine.childTimesInNanos();
+        long[] childTimes = RContext.getEngine().childTimesInNanos();
         boolean na = childTimes[0] < 0 || childTimes[1] < 0;
         boolean complete = na ? RDataFactory.INCOMPLETE_VECTOR : RDataFactory.COMPLETE_VECTOR;
         data[3] = na ? RRuntime.DOUBLE_NA : asDoubleSecs(childTimes[0]);
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/debug/DebugInfoBuiltin.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/debug/DebugInfoBuiltin.java
index 821a2a2f3f1baacc9ec857518635dcf45c6bc47d..1c27d44a9e18cbbb086817b2e824a7235734194b 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/debug/DebugInfoBuiltin.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/debug/DebugInfoBuiltin.java
@@ -40,7 +40,7 @@ public abstract class DebugInfoBuiltin extends RBuiltinNode {
     public Object printTree() {
         controlVisibility();
         RContext.getInstance();
-        RBuiltinPackages packages = (RBuiltinPackages) RContext.getLookup();
+        RBuiltinPackages packages = RDefaultBuiltinPackages.getInstance();
         StringBuilder b = new StringBuilder();
         for (RBuiltinPackage pack : packages.getPackages()) {
             b.append(createPackageString(pack));
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 d19ff45cf1ccf805df0db1a616028dc776de236f..b4a52f879a5a23a9c29f7459fa3c24914a96ff3a 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
@@ -26,7 +26,6 @@ import com.oracle.truffle.api.frame.*;
 import com.oracle.truffle.api.nodes.*;
 import com.oracle.truffle.api.source.*;
 import com.oracle.truffle.r.nodes.*;
-import com.oracle.truffle.r.nodes.builtin.*;
 import com.oracle.truffle.r.nodes.control.*;
 import com.oracle.truffle.r.runtime.*;
 
@@ -42,12 +41,12 @@ public final class FunctionDefinitionNode extends RRootNode {
     private final String description;
 
     /**
-     * An instance of this node may be called from {@link REngine#runCall} with the intention to
-     * have its execution leave a footprint behind in a specific frame/environment, e.g., during
-     * library loading, commands from the shell, or R's {@code eval} and its friends. In that case,
-     * {@code substituteFrame} is {@code true}, and the {@link #execute(VirtualFrame)} method must
-     * be invoked with one argument, namely the {@link VirtualFrame} to be side-effected. Execution
-     * will then proceed in the context of that frame.
+     * An instance of this node may be called from with the intention to have its execution leave a
+     * footprint behind in a specific frame/environment, e.g., during library loading, commands from
+     * the shell, or R's {@code eval} and its friends. In that case, {@code substituteFrame} is
+     * {@code true}, and the {@link #execute(VirtualFrame)} method must be invoked with one
+     * argument, namely the {@link VirtualFrame} to be side-effected. Execution will then proceed in
+     * the context of that frame.
      */
     private final boolean substituteFrame;
 
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/DCF.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/DCF.java
index 9ce4ec2d8a544c0a7edca15da5375b46540daba2..d3ada37db2bcc27ac873bf8fa2ee37da8e49424a 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/DCF.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/DCF.java
@@ -22,7 +22,6 @@
  */
 package com.oracle.truffle.r.runtime;
 
-import java.io.*;
 import java.util.*;
 
 /**
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RBuiltinLookupProvider.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RBuiltinLookupProvider.java
deleted file mode 100644
index 75d5fb43878fbe8f0d031d17dfd126dc0207e117..0000000000000000000000000000000000000000
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RBuiltinLookupProvider.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (c) 2014, 2014, 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;
-
-public interface RBuiltinLookupProvider {
-    RBuiltinLookup getRBuiltinLookup();
-}
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RContext.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RContext.java
index 60c3c410047815c1da2c5bd7b8b850190337a770..cd6aaf90d79cb3584dc515841b5a5ed9e688751d 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RContext.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RContext.java
@@ -25,12 +25,18 @@ package com.oracle.truffle.r.runtime;
 import java.util.*;
 
 import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
+import com.oracle.truffle.api.frame.*;
+import com.oracle.truffle.r.runtime.REnvironment.*;
 import com.oracle.truffle.r.runtime.data.*;
 
 /**
- * Helper class carrying state for an {@code REngine}. As per {@code REngine}, there is exactly one
- * instance of this class, which is tied to the {@code REngine}. It has a separate existence to
- * solve circularities in the current project structure.
+ * Provides access to the runtime state ("context") without being tied statically to a particular
+ * implementation of the runtime.
+ *
+ * The context provides two sub-interfaces {@link ConsoleHandler} and {@link Engine}that are
+ * (typically) implemented elsewhere, but accessed through {@link #getConsoleHandler()} and {@loink
+ * #getEngine()}, respectively.
+ *
  */
 public final class RContext {
 
@@ -86,6 +92,92 @@ public final class RContext {
         int getWidth();
     }
 
+    public interface Engine {
+        /**
+         * Elapsed time of runtime.
+         *
+         * @return elapsed time in nanosecs.
+         */
+        public long elapsedTimeInNanos();
+
+        /**
+         * Return user and system times for any spawned child processes in nanosecs, < 0 means not
+         * available (Windows).
+         */
+        public long[] childTimesInNanos();
+
+        public static class ParseException extends Exception {
+            private static final long serialVersionUID = 1L;
+
+            public ParseException(String msg) {
+                super(msg);
+            }
+        }
+
+        /**
+         * Load a package from the default set of packages identified at startup.
+         *
+         * @param name name of the package, e.g. {@code base}, {@code stats}.
+         * @param frame for evaluating any associated R code
+         * @param envForFrame the namespace environment associated with the package.
+         */
+        public void loadDefaultPackage(String name, VirtualFrame frame, REnvironment envForFrame);
+
+        /**
+         * Return the {@link RFunction} for the builtin {@code name}.
+         *
+         */
+        public RFunction lookupBuiltin(String name);
+
+        /**
+         * Parse an R expression and return an {@link RExpression} object representing the Truffle
+         * ASTs for the components.
+         */
+        public RExpression parse(String rscript) throws ParseException;
+
+        /**
+         * Parse and evaluate {@code rscript} in {@code frame}. {@code printResult == true}, the
+         * result of the evaluation is printed to the console.
+         *
+         * @param envForFrame the environment that {@code frame} is bound to.
+         * @return the object returned by the evaluation or {@code null} if an error occurred.
+         */
+        public Object parseAndEval(String rscript, VirtualFrame frame, REnvironment envForFrame, boolean printResult);
+
+        /**
+         *
+         * This is intended for use by the unit test environment, where a "fresh" global environment
+         * is desired for each evaluation.
+         */
+        public Object parseAndEvalTest(String rscript, boolean printResult);
+
+        /**
+         * Support for the {@code eval} builtin using an {@link RExpression}.
+         *
+         * @param function identifies the eval variant, e.g. {@code local}, {@code eval},
+         *            {@code evalq} being invoked.
+         */
+        public Object eval(RFunction function, RExpression expr, REnvironment envir, REnvironment enclos) throws PutException;
+
+        /**
+         * Support for the {@code eval} builtin. This is tricky because the {@link Frame} "f"
+         * associated with {@code envir} has been materialized so we can't evaluate in it directly.
+         * Instead we create a new {@link VirtualFrame}, that is a logical clone of "f", evaluate in
+         * that, and then update "f" on return.
+         *
+         * @param function the actual function that invoked the "eval", e.g. {@code eval},
+         *            {@code evalq} , {@code local}.
+         */
+        public Object eval(RFunction function, RLanguage expr, REnvironment envir, REnvironment enclos) throws PutException;
+
+        /**
+         * Evaluate a promise in the given frame (for a builtin, where we can use the
+         * {@link VirtualFrame}) of the caller directly).
+         */
+        public Object evalPromise(RPromise expr, VirtualFrame frame) throws RError;
+
+    }
+
     private final HashMap<Object, RFunction> cachedFunctions = new HashMap<>();
     private final GlobalAssumptions globalAssumptions = new GlobalAssumptions();
     private LinkedList<String> evalWarnings;
@@ -101,10 +193,9 @@ public final class RContext {
      */
     @CompilationFinal private boolean headless;
 
-    private static RBuiltinLookup lookup;
-
-    private ConsoleHandler consoleHandler;
-    private String[] commandArgs;
+    @CompilationFinal private ConsoleHandler consoleHandler;
+    @CompilationFinal private String[] commandArgs;
+    @CompilationFinal private Engine engine;
 
     private static final RContext singleton = new RContext();
 
@@ -115,13 +206,12 @@ public final class RContext {
         return globalAssumptions;
     }
 
-    public static RContext linkRBuiltinLookupProvider(RBuiltinLookupProvider rbp) {
-        lookup = rbp.getRBuiltinLookup();
+    public static RContext getInstance() {
         return singleton;
     }
 
-    public static RContext getInstance() {
-        return singleton;
+    public static Engine getEngine() {
+        return singleton.engine;
     }
 
     /**
@@ -131,10 +221,12 @@ public final class RContext {
      * @param commandArgs
      * @param consoleHandler
      */
-    public static void setRuntimeState(String[] commandArgs, ConsoleHandler consoleHandler, boolean headless) {
+    public static RContext setRuntimeState(Engine engine, String[] commandArgs, ConsoleHandler consoleHandler, boolean headless) {
+        singleton.engine = engine;
         singleton.commandArgs = commandArgs;
         singleton.consoleHandler = consoleHandler;
         singleton.headless = headless;
+        return singleton;
     }
 
     public static boolean isVisible() {
@@ -149,13 +241,6 @@ public final class RContext {
         return singleton.headless;
     }
 
-    public static RBuiltinLookup getLookup() {
-        if (lookup == null) {
-            Utils.fail("no builtin lookup set");
-        }
-        return lookup;
-    }
-
     public ConsoleHandler getConsoleHandler() {
         if (consoleHandler == null) {
             Utils.fail("no console handler set");
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/REnvironment.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/REnvironment.java
index 7fd85d0b1a8888b8e8353abf4948f65202f142c3..c400db6580c67a13afd69b32aee1d7fe5c250b01 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/REnvironment.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/REnvironment.java
@@ -228,7 +228,7 @@ public abstract class REnvironment implements RAttributable {
         initSearchList();
 
         // load base package first
-        RPackages.loadBuiltin("base", baseFrame, baseEnv);
+        RContext.getEngine().loadDefaultPackage("base", baseFrame, baseEnv);
     }
 
     public static void packagesInitialize(ArrayList<RPackage> rPackages) {
@@ -237,7 +237,7 @@ public abstract class REnvironment implements RAttributable {
         for (RPackage rPackage : rPackages) {
             VirtualFrame pkgFrame = RRuntime.createVirtualFrame();
             Package pkgEnv = new Package(pkgParent, rPackage.name, pkgFrame, rPackage.path);
-            RPackages.loadBuiltin(rPackage.name, pkgFrame, pkgEnv);
+            RContext.getEngine().loadDefaultPackage(rPackage.name, pkgFrame, pkgEnv);
             attach(2, pkgEnv);
             pkgParent = pkgEnv;
         }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RPackages.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RPackages.java
index eb07fe02f736f4ae124f22cb2dc5a2b35c2133d0..89884fe173bb8ebbbaf75c4951dd458bfcb5c885 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RPackages.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RPackages.java
@@ -66,19 +66,4 @@ public class RPackages {
         return packages;
     }
 
-    /**
-     * Purely a workaround for project circularity between nodes and runtime.
-     * 
-     * @param envForFrame TODO
-     */
-    public static void loadBuiltin(String name, VirtualFrame frame, REnvironment envForFrame) {
-        try {
-            Method loadMethod = Class.forName("com.oracle.truffle.r.nodes.builtin.RDefaultBuiltinPackages").getDeclaredMethod("load", String.class, VirtualFrame.class, REnvironment.class);
-            loadMethod.invoke(null, new Object[]{name, frame, envForFrame});
-        } catch (Exception ex) {
-            ex.printStackTrace();
-            Utils.fail("failed to load builtin package: " + name + ": " + ex);
-        }
-    }
-
 }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/REngine.java b/com.oracle.truffle.r.shell/src/com/oracle/truffle/r/engine/REngine.java
similarity index 79%
rename from com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/REngine.java
rename to com.oracle.truffle.r.shell/src/com/oracle/truffle/r/engine/REngine.java
index 42f8929f84c95ad0a5759f4d37719c2e53aeb63f..060819bdc02f2c413a808ca861c9d06f656ead94 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/REngine.java
+++ b/com.oracle.truffle.r.shell/src/com/oracle/truffle/r/engine/REngine.java
@@ -20,7 +20,7 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.nodes.builtin;
+package com.oracle.truffle.r.engine;
 
 import java.io.*;
 import java.util.*;
@@ -32,6 +32,7 @@ import com.oracle.truffle.api.frame.*;
 import com.oracle.truffle.api.nodes.*;
 import com.oracle.truffle.api.source.*;
 import com.oracle.truffle.r.nodes.*;
+import com.oracle.truffle.r.nodes.builtin.*;
 import com.oracle.truffle.r.nodes.function.*;
 import com.oracle.truffle.r.parser.*;
 import com.oracle.truffle.r.parser.ast.*;
@@ -45,21 +46,18 @@ import com.oracle.truffle.r.runtime.rng.*;
  * The engine for the FastR implementation. Handles parsing and evaluation. There is exactly one
  * instance of this class, stored in {link #singleton}.
  */
-public final class REngine implements RBuiltinLookupProvider {
+public final class REngine implements RContext.Engine {
 
     private static REngine singleton = new REngine();
-    private static final RContext context = RContext.linkRBuiltinLookupProvider(singleton);
     private static boolean crashOnFatalError;
     private static long startTime;
     private static long[] childTimes;
+    private static RContext context;
+    private static RBuiltinLookup builtinLookup;
 
     private REngine() {
     }
 
-    public RBuiltinLookup getRBuiltinLookup() {
-        return RDefaultBuiltinPackages.getInstance();
-    }
-
     /**
      * Initialize the engine.
      *
@@ -76,7 +74,8 @@ public final class REngine implements RBuiltinLookupProvider {
         Locale.setDefault(Locale.ROOT);
         RPerfAnalysis.initialize();
         crashOnFatalError = crashOnFatalErrorArg;
-        RContext.setRuntimeState(commandArgs, consoleHandler, headless);
+        builtinLookup = RDefaultBuiltinPackages.getInstance();
+        context = RContext.setRuntimeState(singleton, commandArgs, consoleHandler, headless);
         VirtualFrame globalFrame = RRuntime.createVirtualFrame();
         VirtualFrame baseFrame = RRuntime.createVirtualFrame();
         REnvironment.baseInitialize(globalFrame, baseFrame);
@@ -88,64 +87,51 @@ public final class REngine implements RBuiltinLookupProvider {
         ROptions.initialize();
         RProfile.initialize();
         // eval the system profile
-        REngine.parseAndEval(RProfile.systemProfile(), baseFrame, REnvironment.baseEnv(), false);
+        singleton.parseAndEval(RProfile.systemProfile(), baseFrame, REnvironment.baseEnv(), false);
         REnvironment.packagesInitialize(RPackages.initialize());
         RPackageVariables.initialize(); // TODO replace with R code
         String siteProfile = RProfile.siteProfile();
         if (siteProfile != null) {
-            REngine.parseAndEval(siteProfile, baseFrame, REnvironment.baseEnv(), false);
+            singleton.parseAndEval(siteProfile, baseFrame, REnvironment.baseEnv(), false);
         }
         String userProfile = RProfile.userProfile();
         if (userProfile != null) {
-            REngine.parseAndEval(userProfile, globalFrame, REnvironment.globalEnv(), false);
+            singleton.parseAndEval(userProfile, globalFrame, REnvironment.globalEnv(), false);
         }
         return globalFrame;
     }
 
-    /**
-     * Elapsed time of process.
-     *
-     * @return elapsed time in nanosecs.
-     */
-    public static long elapsedTimeInNanos() {
-        return System.nanoTime() - startTime;
+    public static REngine getInstance() {
+        return singleton;
     }
 
-    /**
-     * Return user and system times for any spawned child processes in nanosecs, < 0 means not
-     * available (Windows).
-     */
-    public static long[] childTimesInNanos() {
-        return childTimes;
+    public void loadDefaultPackage(String name, VirtualFrame frame, REnvironment envForFrame) {
+        RDefaultBuiltinPackages.load(name, frame, envForFrame);
     }
 
-    public static RContext getContext() {
-        return context;
+    public RFunction lookupBuiltin(String name) {
+        return builtinLookup.lookup(name);
     }
 
-    /**
-     * Parse and evaluate {@code rscript} in {@code frame}. {@code printResult == true}, the result
-     * of the evaluation is printed to the console.
-     *
-     * @param envForFrame the environment that {@code frame} is bound to.
-     * @return the object returned by the evaluation or {@code null} if an error occurred.
-     */
-    public static Object parseAndEval(String rscript, VirtualFrame frame, REnvironment envForFrame, boolean printResult) {
+    public long elapsedTimeInNanos() {
+        return System.nanoTime() - startTime;
+    }
+
+    public long[] childTimesInNanos() {
+        return childTimes;
+    }
+
+    public Object parseAndEval(String rscript, VirtualFrame frame, REnvironment envForFrame, boolean printResult) {
         return parseAndEvalImpl(new ANTLRStringStream(rscript), Source.asPseudoFile(rscript, "<shell_input>"), frame, envForFrame, printResult);
     }
 
-    /**
-     *
-     * This is intended for use by the unit test environment, where a "fresh" global environment is
-     * desired for each evaluation.
-     */
-    public static Object parseAndEvalTest(String rscript, boolean printResult) {
+    public Object parseAndEvalTest(String rscript, boolean printResult) {
         VirtualFrame frame = RRuntime.createVirtualFrame();
         REnvironment.resetForTest(frame);
         return parseAndEvalImpl(new ANTLRStringStream(rscript), Source.asPseudoFile(rscript, "<test_input>"), frame, REnvironment.globalEnv(), printResult);
     }
 
-    public static class ParseException extends Exception {
+    public class ParseException extends Exception {
         private static final long serialVersionUID = 1L;
 
         public ParseException(String msg) {
@@ -153,11 +139,7 @@ public final class REngine implements RBuiltinLookupProvider {
         }
     }
 
-    /**
-     * Parse an R expression and return an {@link RExpression} object representing the Truffle ASTs
-     * for the components.
-     */
-    public static RExpression parse(String rscript) throws ParseException {
+    public RExpression parse(String rscript) throws RContext.Engine.ParseException {
         try {
             Sequence seq = (Sequence) ParseUtil.parseAST(new ANTLRStringStream(rscript), Source.asPseudoFile(rscript, "<parse_input>"));
             ASTNode[] exprs = seq.getExprs();
@@ -167,16 +149,11 @@ public final class REngine implements RBuiltinLookupProvider {
             }
             return RDataFactory.createExpression(RDataFactory.createList(data));
         } catch (RecognitionException ex) {
-            throw new ParseException(ex.getMessage());
+            throw new RContext.Engine.ParseException(ex.getMessage());
         }
     }
 
-    /**
-     * Support for the {@code eval} builtin using an {@link RExpression}.
-     *
-     * @param function TODO
-     */
-    public static Object eval(RFunction function, RExpression expr, REnvironment envir, REnvironment enclos) throws PutException {
+    public Object eval(RFunction function, RExpression expr, REnvironment envir, REnvironment enclos) throws PutException {
         Object result = null;
         for (int i = 0; i < expr.getLength(); i++) {
             result = eval(function, (RLanguage) expr.getDataAt(i), envir, enclos);
@@ -184,16 +161,7 @@ public final class REngine implements RBuiltinLookupProvider {
         return result;
     }
 
-    /**
-     * Support for the {@code eval} builtin. This is tricky because the {@link Frame} "f" associated
-     * with {@code envir} has been materialized so we can't evaluate in it directly. Instead we
-     * create a new {@link VirtualFrame}, that is a logical clone of "f", evaluate in that, and then
-     * update "f" on return.
-     *
-     * @param function the actual function that invoked the "eval", e.g. {@code eval}, {@code evalq}
-     *            , {@code local}.
-     */
-    public static Object eval(RFunction function, RLanguage expr, REnvironment envir, @SuppressWarnings("unused") REnvironment enclos) throws PutException {
+    public Object eval(RFunction function, RLanguage expr, REnvironment envir, REnvironment enclos) throws PutException {
         RootCallTarget callTarget = makeCallTarget((RNode) expr.getRep(), REnvironment.globalEnv());
         MaterializedFrame envFrame = envir.getFrame();
         VirtualFrame vFrame = RRuntime.createVirtualFrame();
@@ -240,11 +208,7 @@ public final class REngine implements RBuiltinLookupProvider {
         return result;
     }
 
-    /**
-     * Evaluate a promise in the given frame (for a builtin, where we can use the
-     * {@link VirtualFrame}) of the caller directly).
-     */
-    public static Object evalPromise(RPromise expr, VirtualFrame frame) throws RError {
+    public Object evalPromise(RPromise expr, VirtualFrame frame) throws RError {
         RootCallTarget callTarget = makeCallTarget((RNode) expr.getRep(), REnvironment.emptyEnv());
         return expr.setValue(runCall(callTarget, frame, false, false));
     }
@@ -333,7 +297,8 @@ public final class REngine implements RBuiltinLookupProvider {
 
     private static void printResult(Object result) {
         if (RContext.isVisible()) {
-            RFunction function = RContext.getLookup().lookup("print");
+            // TODO cache this
+            RFunction function = builtinLookup.lookup("print");
             function.getTarget().call(RArguments.create(function, new Object[]{result}));
         }
     }
diff --git a/com.oracle.truffle.r.shell/src/com/oracle/truffle/r/shell/RCommand.java b/com.oracle.truffle.r.shell/src/com/oracle/truffle/r/shell/RCommand.java
index 38766750f87d889c967dca38732d4becf7ebb690..eb484ab1050bbea14e5c7963f673e50835004129 100644
--- a/com.oracle.truffle.r.shell/src/com/oracle/truffle/r/shell/RCommand.java
+++ b/com.oracle.truffle.r.shell/src/com/oracle/truffle/r/shell/RCommand.java
@@ -28,7 +28,7 @@ import java.io.*;
 import java.util.*;
 
 import com.oracle.truffle.api.frame.*;
-import com.oracle.truffle.r.nodes.builtin.*;
+import com.oracle.truffle.r.engine.*;
 import com.oracle.truffle.r.runtime.*;
 
 import jline.console.*;
@@ -145,7 +145,7 @@ public class RCommand {
             String content = new String(bytes);
             JLineConsoleHandler consoleHandler = new JLineConsoleHandler(false, new ConsoleReader(null, System.out));
             VirtualFrame frame = REngine.initialize(commandArgs, consoleHandler, true, true);
-            REngine.parseAndEval(content, frame, REnvironment.globalEnv(), true);
+            REngine.getInstance().parseAndEval(content, frame, REnvironment.globalEnv(), true);
         } catch (IOException ex) {
             Utils.fail("unexpected error reading file input");
         }
@@ -168,7 +168,7 @@ public class RCommand {
                     continue;
                 }
 
-                REngine.parseAndEval(line, globalFrame, REnvironment.globalEnv(), true);
+                REngine.getInstance().parseAndEval(line, globalFrame, REnvironment.globalEnv(), true);
             }
         } catch (UserInterruptException e) {
             // interrupted
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/generate/FastRSession.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/generate/FastRSession.java
index bce98f4cb13e78c61e56e72c323ba39de2359bdf..d38518f5772f218742ed4c0fca774587c21800f4 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/generate/FastRSession.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/generate/FastRSession.java
@@ -22,7 +22,7 @@
  */
 package com.oracle.truffle.r.test.generate;
 
-import com.oracle.truffle.r.nodes.builtin.*;
+import com.oracle.truffle.r.engine.*;
 import com.oracle.truffle.r.runtime.*;
 
 public class FastRSession implements RSession {
@@ -87,7 +87,7 @@ public class FastRSession implements RSession {
 
     public String eval(String expression) {
         consoleHandler.reset();
-        REngine.parseAndEvalTest(expression, true);
+        REngine.getInstance().parseAndEvalTest(expression, true);
         return consoleHandler.buffer.toString();
     }
 
diff --git a/mx.fastr/projects b/mx.fastr/projects
index 56cabdd66dc5b3b7f769abb966f85e676a130954..b52e410686d9d2c11e20083e8c89a30e8bd13f62 100644
--- a/mx.fastr/projects
+++ b/mx.fastr/projects
@@ -92,7 +92,7 @@ project@com.oracle.truffle.r.test.ignore.processor@workingSets=Truffle,FastR
 
 # com.oracle.truffle.r.test
 project@com.oracle.truffle.r.test@sourceDirs=src
-project@com.oracle.truffle.r.test@dependencies=com.oracle.truffle.r.nodes,JUNIT
+project@com.oracle.truffle.r.test@dependencies=com.oracle.truffle.r.nodes,com.oracle.truffle.r.shell,JUNIT
 project@com.oracle.truffle.r.test@checkstyle=com.oracle.truffle.r.runtime
 project@com.oracle.truffle.r.test@javaCompliance=1.8
 project@com.oracle.truffle.r.test@annotationProcessors=com.oracle.truffle.r.test.ignore.processor