diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/RContextFactory.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/RContextFactory.java
index e32f9ec9a61cf342a453d5c515c61c96103266ef..a3c3e05c2d9c8866a73a94ba092256e509a8ba52 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/RContextFactory.java
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/RContextFactory.java
@@ -22,12 +22,17 @@
  */
 package com.oracle.truffle.r.engine;
 
+import java.io.*;
 import java.util.*;
+import java.util.concurrent.*;
+import java.util.function.*;
 
+import com.oracle.truffle.api.vm.*;
+import com.oracle.truffle.api.vm.TruffleVM.Builder;
 import com.oracle.truffle.r.nodes.builtin.*;
 import com.oracle.truffle.r.nodes.instrument.*;
 import com.oracle.truffle.r.runtime.*;
-import com.oracle.truffle.r.runtime.RContext.*;
+import com.oracle.truffle.r.runtime.context.*;
 import com.oracle.truffle.r.runtime.ffi.*;
 
 /**
@@ -42,52 +47,47 @@ public class RContextFactory {
      */
     static {
         Load_RFFIFactory.initialize();
+        RInstrument.initialize();
+        RPerfStats.initialize();
+        Locale.setDefault(Locale.ROOT);
+        RAccuracyInfo.initialize();
+        RVersionInfo.initialize();
+        TempPathName.initialize();
+        RContext.initialize(new RRuntimeASTAccessImpl(), RBuiltinPackages.getInstance(), FastROptions.IgnoreVisibility);
     }
 
-    private static boolean initialized;
+    private static final Semaphore createSemaphore = new Semaphore(1, true);
 
     /**
-     * Initialize all the context-independent aspects of the system.
+     * Create a context of given kind.
      */
-    public static void initialize() {
-        if (!initialized) {
-            FastROptions.initialize();
-            REnvVars.initialize();
-            RInstrument.initialize();
-            RPerfStats.initialize();
-            Locale.setDefault(Locale.ROOT);
-            RAccuracyInfo.initialize();
-            RVersionInfo.initialize();
-            TempPathName.initialize();
-            RProfile.initialize();
-            RContext.initialize(new RRuntimeASTAccessImpl(), RBuiltinPackages.getInstance(), FastROptions.IgnoreVisibility.getValue());
-            initialized = true;
+    public static TruffleVM create(ContextInfo info, Consumer<TruffleVM.Builder> setup) {
+        try {
+            createSemaphore.acquire();
+            RContext.tempInitializingContextInfo = info;
+            Builder builder = TruffleVM.newVM();
+            if (setup != null) {
+                setup.accept(builder);
+            }
+            TruffleVM vm = builder.build();
+            try {
+                vm.eval(TruffleRLanguage.MIME, "invisible(1)");
+            } catch (IOException e) {
+                createSemaphore.release();
+                throw RInternalError.shouldNotReachHere(e);
+            }
+            RContext.associate(vm);
+            createSemaphore.release();
+            return vm;
+        } catch (InterruptedException x) {
+            throw RError.error(RError.NO_NODE, RError.Message.GENERIC, "Error creating parallel R runtime instance");
         }
     }
 
-    public static RContext createShareParentReadWrite(RContext parent, String[] commandArgs, ConsoleHandler consoleHandler) {
-        RContext context = RContext.createShareParentReadWrite(parent, commandArgs, consoleHandler);
-        return context;
-    }
-
-    public static RContext createShareParentReadOnly(RContext parent, String[] commandArgs, ConsoleHandler consoleHandler) {
-        RContext context = RContext.createShareParentReadOnly(parent, commandArgs, consoleHandler);
-        return context;
-    }
-
-    /**
-     * Create the initial context with no parent.
-     */
-    public static RContext createInitial(String[] commandArgs, ConsoleHandler consoleHandler) {
-        RContext context = RContext.createShareNothing(null, commandArgs, consoleHandler);
-        return context;
-    }
-
     /**
      * Create a context of given kind.
      */
-    public static RContext create(RContext parent, Kind kind, String[] commandArgs, ConsoleHandler consoleHandler) {
-        RContext context = RContext.create(parent, kind, commandArgs, consoleHandler);
-        return context;
+    public static TruffleVM create(ContextInfo info) {
+        return create(info, null);
     }
 }
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 b6473efd77d8fe5b9f11d02fe87f42b49b74da25..737f8b3c1547a86c4344402613d31458db012c13 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,23 +22,24 @@
  */
 package com.oracle.truffle.r.engine;
 
-import static com.oracle.truffle.r.runtime.RCmdOptions.NO_RESTORE;
-
 import java.io.*;
 import java.util.*;
 import java.util.stream.*;
 
 import org.antlr.runtime.*;
 
+import com.oracle.truffle.api.*;
 import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
-import com.oracle.truffle.api.*;
 import com.oracle.truffle.api.dsl.*;
 import com.oracle.truffle.api.frame.*;
+import com.oracle.truffle.api.impl.*;
 import com.oracle.truffle.api.instrument.*;
+import com.oracle.truffle.api.interop.*;
 import com.oracle.truffle.api.nodes.*;
+import com.oracle.truffle.api.nodes.Node.Child;
 import com.oracle.truffle.api.source.*;
-import com.oracle.truffle.api.vm.*;
+import com.oracle.truffle.r.engine.interop.*;
 import com.oracle.truffle.r.library.graphics.*;
 import com.oracle.truffle.r.nodes.*;
 import com.oracle.truffle.r.nodes.access.*;
@@ -50,14 +51,13 @@ import com.oracle.truffle.r.nodes.runtime.*;
 import com.oracle.truffle.r.parser.*;
 import com.oracle.truffle.r.parser.ast.*;
 import com.oracle.truffle.r.runtime.*;
-import com.oracle.truffle.r.runtime.RContext.ConsoleHandler;
+import com.oracle.truffle.r.runtime.RCmdOptions.RCmdOption;
 import com.oracle.truffle.r.runtime.Utils.DebugExitException;
-import com.oracle.truffle.r.runtime.conn.*;
+import com.oracle.truffle.r.runtime.context.*;
 import com.oracle.truffle.r.runtime.data.*;
 import com.oracle.truffle.r.runtime.data.RPromise.Closure;
 import com.oracle.truffle.r.runtime.data.model.*;
 import com.oracle.truffle.r.runtime.env.*;
-import com.oracle.truffle.r.runtime.env.REnvironment.PutException;
 import com.oracle.truffle.r.runtime.env.frame.*;
 import com.oracle.truffle.r.runtime.nodes.*;
 
@@ -65,7 +65,7 @@ import com.oracle.truffle.r.runtime.nodes.*;
  * The engine for the FastR implementation. Handles parsing and evaluation. There is one instance of
  * this class per {@link RContext}.
  */
-final class REngine implements RContext.Engine {
+final class REngine implements Engine {
 
     /**
      * Controls the behavior when an implementation errors occurs. In normal use this is fatal as
@@ -87,14 +87,6 @@ final class REngine implements RContext.Engine {
      */
     private final RContext context;
 
-    /**
-     * Every engine has an associated {@link TruffleVM}. The actual {@link TruffleVM} instance is
-     * not "built" until the {@link #activate} method is invoked.
-     */
-    private final TruffleVM.Builder truffleVMBuilder;
-
-    @CompilationFinal private TruffleVM truffleVM;
-
     /**
      * The unique frame for the global environment for this engine.
      */
@@ -116,7 +108,6 @@ final class REngine implements RContext.Engine {
     }
 
     private REngine(RContext context) {
-        this.truffleVMBuilder = TruffleVM.newVM();
         this.context = context;
         this.childTimes = new long[]{0, 0};
     }
@@ -126,12 +117,10 @@ final class REngine implements RContext.Engine {
         return engine;
     }
 
-    public void activate() {
-        truffleVM = truffleVMBuilder.build();
-        this.globalFrame = RRuntime.createNonFunctionFrame().materialize();
+    public void activate(REnvironment.ContextStateImpl stateREnvironment) {
+        this.globalFrame = stateREnvironment.getGlobalFrame();
         this.startTime = System.nanoTime();
-        context.installCustomClassState(RContext.ClassStateKind.REnvironment, new REnvironment.ClassStateFactory().newContext(context, globalFrame));
-        if (context.getKind() == RContext.Kind.SHARE_NOTHING) {
+        if (context.getKind() == RContext.ContextKind.SHARE_NOTHING) {
             initializeShared();
         }
     }
@@ -140,7 +129,7 @@ final class REngine implements RContext.Engine {
         suppressWarnings = true;
         MaterializedFrame baseFrame = RRuntime.createNonFunctionFrame().materialize();
         REnvironment.baseInitialize(baseFrame, globalFrame);
-        loadBase = FastROptions.LoadBase.getValue();
+        loadBase = FastROptions.LoadBase;
         RBuiltinPackages.loadBase(baseFrame, loadBase);
         RGraphics.initialize();
         if (loadBase) {
@@ -148,19 +137,31 @@ final class REngine implements RContext.Engine {
              * eval the system/site/user profiles. Experimentally GnuR does not report warnings
              * during system profile evaluation, but does for the site/user profiles.
              */
-            parseAndEval(RProfile.systemProfile(), baseFrame, false, false);
+            try {
+                parseAndEval(RProfile.systemProfile(), baseFrame, false);
+            } catch (ParseException e) {
+                throw new RInternalError(e, "error while parsing system profile from %s", RProfile.systemProfile().getName());
+            }
             checkAndRunStartupFunction(".OptRequireMethods");
 
             suppressWarnings = false;
-            Source siteProfile = RProfile.siteProfile();
+            Source siteProfile = context.stateRProfile.siteProfile();
             if (siteProfile != null) {
-                parseAndEval(siteProfile, baseFrame, false, false);
+                try {
+                    parseAndEval(siteProfile, baseFrame, false);
+                } catch (ParseException e) {
+                    throw new RInternalError(e, "error while parsing site profile from %s", siteProfile.getName());
+                }
             }
-            Source userProfile = RProfile.userProfile();
+            Source userProfile = context.stateRProfile.userProfile();
             if (userProfile != null) {
-                parseAndEval(userProfile, globalFrame, false, false);
+                try {
+                    parseAndEval(userProfile, globalFrame, false);
+                } catch (ParseException e) {
+                    throw new RInternalError(e, "error while parsing user profile from %s", userProfile.getName());
+                }
             }
-            if (!NO_RESTORE.getValue()) {
+            if (!context.getOptions().getBoolean(RCmdOption.NO_RESTORE)) {
                 /*
                  * TODO This is where we would load any saved user data
                  */
@@ -169,7 +170,6 @@ final class REngine implements RContext.Engine {
             checkAndRunStartupFunction(".First.sys");
             RBuiltinPackages.loadDefaultPackageOverrides();
         }
-        context.systemInitialized();
     }
 
     private void checkAndRunStartupFunction(String name) {
@@ -182,7 +182,11 @@ final class REngine implements RContext.Engine {
             RInstrument.checkDebugRequested(name, (RFunction) func);
             String call = name + "()";
             // Should this print the result?
-            parseAndEval(Source.fromText(call, "<startup>"), globalFrame, false, false);
+            try {
+                parseAndEval(Source.fromText(call, "<startup>"), globalFrame, false);
+            } catch (ParseException e) {
+                throw new RInternalError(e, "error while parsing startup function");
+            }
         }
     }
 
@@ -198,19 +202,12 @@ final class REngine implements RContext.Engine {
         return childTimes;
     }
 
-    public TruffleVM getTruffleVM() {
-        assert truffleVM != null;
-        return truffleVM;
-    }
-
-    public TruffleVM.Builder getTruffleVMBuilder() {
-        return truffleVMBuilder;
-    }
-
     @Override
-    public Object parseAndEval(Source source, MaterializedFrame frame, boolean printResult, boolean allowIncompleteSource) {
+    public Object parseAndEval(Source source, MaterializedFrame frame, boolean printResult) throws ParseException {
+        RSyntaxNode node = transform(parseImpl(source));
+        RootCallTarget callTarget = doMakeCallTarget(node.asRNode(), "<repl wrapper>");
         try {
-            return parseAndEvalImpl(source, frame, printResult, allowIncompleteSource);
+            return runCall(callTarget, frame, printResult, true);
         } catch (ReturnException ex) {
             return ex.getResult();
         } catch (DebugExitException | QuitException | BrowserQuitException e) {
@@ -233,99 +230,79 @@ final class REngine implements RContext.Engine {
     }
 
     @Override
-    public Object parseAndEval(Source source, boolean printResult, boolean allowIncompleteSource) {
-        return parseAndEval(source, globalFrame, printResult, allowIncompleteSource);
+    public Object parseAndEval(Source source, boolean printResult) throws ParseException {
+        return parseAndEval(source, globalFrame, printResult);
     }
 
-    @Override
-    public Object parseAndEvalTest(Source source, boolean printResult, boolean allowIncompleteSource) {
+    private static ASTNode parseImpl(Source source) throws ParseException {
         try {
-            return parseAndEvalImpl(source, globalFrame, printResult, allowIncompleteSource);
-        } catch (RInternalError e) {
-            context.getConsoleHandler().printErrorln("FastR internal error: " + e.getMessage());
-            RInternalError.reportError(e);
-            throw e;
+            return ParseUtil.parseAST(new ANTLRStringStream(source.getCode()), source);
         } catch (RecognitionException e) {
-            throw new RInternalError(e, "recognition exception");
-        }
-    }
-
-    private Object parseAndEvalImpl(Source source, MaterializedFrame frame, boolean printResult, boolean allowIncompleteSource) throws RecognitionException {
-        RSyntaxNode node;
-        try {
-            node = parseToRNode(source);
-        } catch (NoViableAltException | MismatchedTokenException e) {
-            if (e.token.getType() == Token.EOF && allowIncompleteSource) {
+            String line = e.line <= source.getLineCount() ? source.getCode(e.line) : "";
+            String substring = line.substring(0, Math.min(line.length(), e.charPositionInLine + 1));
+            String token = e.token.getText();
+            if (e.token.getType() == Token.EOF && (e instanceof NoViableAltException || e instanceof MismatchedTokenException)) {
                 // the parser got stuck at the eof, request another line
-                return INCOMPLETE_SOURCE;
+                throw new IncompleteSourceException(e, source, token, substring, e.line);
+            } else {
+                throw new ParseException(e, source, token, substring, e.line);
             }
-            String line = source.getCode(e.line);
-            String message = "Error: unexpected '" + e.token.getText() + "' in \"" + line.substring(0, Math.min(line.length(), e.charPositionInLine + 1)) + "\"";
-            writeStderr(source.getLineCount() == 1 ? message : (message + " (line " + e.line + ")"), true);
-            return null;
-        }
-        RootCallTarget callTarget = doMakeCallTarget(node.asRNode(), "<repl wrapper>");
-        try {
-            return runCall(callTarget, frame, printResult, true);
-        } catch (BreakException | NextException cfe) {
-            throw RError.error(RError.NO_NODE, RError.Message.NO_LOOP_FOR_BREAK_NEXT);
         }
     }
 
-    public RExpression parse(Source source) throws RContext.Engine.ParseException {
-        try {
-            Sequence seq = (Sequence) ParseUtil.parseAST(new ANTLRStringStream(source.getCode()), source);
-            ASTNode[] exprs = seq.getExpressions();
-            Object[] data = new Object[exprs.length];
-            for (int i = 0; i < exprs.length; i++) {
-                data[i] = RDataFactory.createLanguage(transform(exprs[i]).asRNode());
-            }
-            return RDataFactory.createExpression(RDataFactory.createList(data));
-        } catch (RecognitionException ex) {
-            throw new RContext.Engine.ParseException(ex, ex.getMessage());
-        }
+    public RExpression parse(Source source) throws ParseException {
+        Sequence seq = (Sequence) parseImpl(source);
+        Object[] data = Arrays.stream(seq.getExpressions()).map(expr -> RDataFactory.createLanguage(transform(expr).asRNode())).toArray();
+        return RDataFactory.createExpression(RDataFactory.createList(data));
     }
 
-    public RFunction parseFunction(String name, Source source, MaterializedFrame enclosingFrame) throws RContext.Engine.ParseException {
-        try {
-            Sequence seq = (Sequence) ParseUtil.parseAST(new ANTLRStringStream(source.getCode()), source);
-            ASTNode[] exprs = seq.getExpressions();
-            assert exprs.length == 1;
+    public RFunction parseFunction(String name, Source source, MaterializedFrame enclosingFrame) throws ParseException {
+        Sequence seq = (Sequence) parseImpl(source);
+        ASTNode[] exprs = seq.getExpressions();
+        assert exprs.length == 1;
 
-            return new RTruffleVisitor().transformFunction(name, (Function) exprs[0], enclosingFrame);
-        } catch (RecognitionException ex) {
-            throw new RContext.Engine.ParseException(ex, ex.getMessage());
-        }
+        return new RTruffleVisitor().transformFunction(name, (Function) exprs[0], enclosingFrame);
     }
 
-    public CallTarget parseToCallTarget(Source source) {
-        RSyntaxNode node;
-        try {
-            node = parseToRNode(source);
-            return new TVMCallTarget(doMakeCallTarget(node.asRNode(), "<tl parse>"));
-        } catch (RecognitionException ex) {
-            return null;
-        }
+    @Override
+    public CallTarget parseToCallTarget(Source source, boolean printResult) throws ParseException {
+        ASTNode ast = parseImpl(source);
+        return new TVMCallTarget(ast, printResult);
     }
 
-    private class TVMCallTarget implements RootCallTarget {
-        private RootCallTarget delegate;
+    private static class TVMCallTarget implements RootCallTarget {
+
+        private final ASTNode ast;
+        private final boolean printResult;
+
+        @SuppressWarnings("unchecked") @Child private FindContextNode<RContext> findContext = (FindContextNode<RContext>) TruffleRLanguage.INSTANCE.actuallyCreateFindContextNode();
 
-        TVMCallTarget(RootCallTarget delegate) {
-            this.delegate = delegate;
+        TVMCallTarget(ASTNode ast, boolean printResult) {
+            this.ast = ast;
+            this.printResult = printResult;
         }
 
         @Override
         public Object call(Object... arguments) {
-            return runCall(delegate, globalFrame, true, true);
+            RSyntaxNode node = transform(ast);
+            RootCallTarget callTarget = doMakeCallTarget(node.asRNode(), "<repl wrapper>");
+
+            RContext oldContext = RContext.threadLocalContext.get();
+            RContext context = findContext.executeFindContext();
+            RContext.threadLocalContext.set(context);
+            try {
+                return ((REngine) context.getThisEngine()).runCall(callTarget, context.stateREnvironment.getGlobalFrame(), printResult, true);
+            } finally {
+                RContext.threadLocalContext.set(oldContext);
+            }
         }
 
         public RootNode getRootNode() {
-            return delegate.getRootNode();
+            return null;
         }
     }
 
-    public Object eval(RExpression exprs, REnvironment envir, REnvironment enclos, int depth) throws PutException {
+    public Object eval(RExpression exprs, REnvironment envir, REnvironment enclos, int depth) {
         Object result = RNull.instance;
         for (int i = 0; i < exprs.getLength(); i++) {
             Object obj = RASTUtils.checkForRSymbol(exprs.getDataAt(i));
@@ -338,7 +315,7 @@ final class REngine implements RContext.Engine {
         return result;
     }
 
-    public Object eval(RLanguage expr, REnvironment envir, REnvironment enclos, int depth) throws PutException {
+    public Object eval(RLanguage expr, REnvironment envir, REnvironment enclos, int depth) {
         return evalNode((RNode) expr.getRep(), envir, enclos, depth);
     }
 
@@ -402,18 +379,6 @@ final class REngine implements RContext.Engine {
         return runCall(closure.getCallTarget(), frame, false, false);
     }
 
-    /**
-     * Parses a text stream into a Truffle AST.
-     *
-     * @return the root node of the Truffle AST
-     * @throws RecognitionException on parse error
-     */
-    private static RSyntaxNode parseToRNode(Source source) throws RecognitionException {
-        String code = source.getCode();
-        RSyntaxNode result = transform(ParseUtil.parseAST(new ANTLRStringStream(code), source));
-        return result;
-    }
-
     /**
      * Transforms an AST produced by the parser into a Truffle AST.
      *
@@ -468,18 +433,10 @@ final class REngine implements RContext.Engine {
     private Object runCall(RootCallTarget callTarget, MaterializedFrame frame, boolean printResult, boolean topLevel) {
         Object result = null;
         try {
-            try {
-                // FIXME: callTargets should only be called via Direct/IndirectCallNode
-                result = callTarget.call(frame);
-            } catch (ReturnException ex) {
-                // condition handling can cause a "return" that needs to skip over this call
-                throw ex;
-            } catch (BreakException | NextException cfe) {
-                // there can be an outer loop
-                throw cfe;
-            }
+            // FIXME: callTargets should only be called via Direct/IndirectCallNode
+            result = callTarget.call(frame);
             assert checkResult(result);
-            if (printResult) {
+            if (printResult && result != null) {
                 assert topLevel;
                 if (context.isVisible()) {
                     printResult(result);
@@ -491,9 +448,15 @@ final class REngine implements RContext.Engine {
         } catch (RError e) {
             throw e;
         } catch (ReturnException ex) {
+            // condition handling can cause a "return" that needs to skip over this call
             throw ex;
         } catch (BreakException | NextException cfe) {
-            throw cfe;
+            if (topLevel) {
+                throw RError.error(RError.NO_NODE, RError.Message.NO_LOOP_FOR_BREAK_NEXT);
+            } else {
+                // there can be an outer loop
+                throw cfe;
+            }
         } catch (UnsupportedSpecializationException use) {
             throw use;
         } catch (DebugExitException | QuitException | BrowserQuitException e) {
@@ -513,7 +476,7 @@ final class REngine implements RContext.Engine {
 
     @TruffleBoundary
     private static boolean checkResult(Object result) {
-        if (FastROptions.CheckResultCompleteness.getValue() && result instanceof RAbstractVector && ((RAbstractVector) result).isComplete()) {
+        if (FastROptions.CheckResultCompleteness && result instanceof RAbstractVector && ((RAbstractVector) result).isComplete()) {
             assert ((RAbstractVector) result).checkCompleteness() : "vector: " + result + " is not complete, but isComplete flag is true";
         }
         return true;
@@ -524,20 +487,27 @@ final class REngine implements RContext.Engine {
 
     @TruffleBoundary
     public void printResult(Object result) {
-        Object resultValue = result instanceof RPromise ? PromiseHelperNode.evaluateSlowPath(null, (RPromise) result) : result;
-        if (loadBase) {
-            Object printMethod = REnvironment.globalEnv().findFunction("print");
-            RFunction function = (RFunction) (printMethod instanceof RPromise ? PromiseHelperNode.evaluateSlowPath(null, (RPromise) printMethod) : printMethod);
-            if (FastROptions.NewStateTransition && resultValue instanceof RShareable) {
-                ((RShareable) resultValue).incRefCount();
-            }
-            function.getTarget().call(RArguments.create(function, null, REnvironment.globalEnv().getFrame(), 1, new Object[]{resultValue, RMissing.instance}, PRINT_SIGNATURE, null));
-            if (FastROptions.NewStateTransition && resultValue instanceof RShareable) {
-                ((RShareable) resultValue).decRefCount();
-            }
+        // this supports printing of non-R values (via toString for now)
+        if (result instanceof TruffleObject && !(result instanceof RTypedValue)) {
+            RContext.getInstance().getConsoleHandler().println(String.valueOf(result));
+        } else if (result instanceof CharSequence && !(result instanceof String)) {
+            RContext.getInstance().getConsoleHandler().println("\"" + String.valueOf(result) + "\"");
         } else {
-            // we only have the .Internal print.default method available
-            getPrintInternal().getTarget().call(RArguments.create(printInternal, null, REnvironment.globalEnv().getFrame(), 1, new Object[]{resultValue}, PRINT_INTERNAL_SIGNATURE, null));
+            Object resultValue = result instanceof RPromise ? PromiseHelperNode.evaluateSlowPath(null, (RPromise) result) : result;
+            if (loadBase) {
+                Object printMethod = REnvironment.globalEnv().findFunction("print");
+                RFunction function = (RFunction) (printMethod instanceof RPromise ? PromiseHelperNode.evaluateSlowPath(null, (RPromise) printMethod) : printMethod);
+                if (FastROptions.NewStateTransition && resultValue instanceof RShareable) {
+                    ((RShareable) resultValue).incRefCount();
+                }
+                function.getTarget().call(RArguments.create(function, null, REnvironment.globalEnv().getFrame(), 1, new Object[]{resultValue, RMissing.instance}, PRINT_SIGNATURE, null));
+                if (FastROptions.NewStateTransition && resultValue instanceof RShareable) {
+                    ((RShareable) resultValue).decRefCount();
+                }
+            } else {
+                // we only have the .Internal print.default method available
+                getPrintInternal().getTarget().call(RArguments.create(printInternal, null, REnvironment.globalEnv().getFrame(), 1, new Object[]{resultValue}, PRINT_INTERNAL_SIGNATURE, null));
+            }
         }
     }
 
@@ -550,7 +520,7 @@ final class REngine implements RContext.Engine {
             try {
                 RExpression funDef = parse(INTERNAL_PRINT);
                 printInternal = (RFunction) eval(funDef, REnvironment.baseEnv().getFrame());
-            } catch (RContext.Engine.ParseException ex) {
+            } catch (Engine.ParseException ex) {
                 Utils.fail("failed to parse print.internal");
             }
         }
@@ -558,6 +528,10 @@ final class REngine implements RContext.Engine {
 
     }
 
+    public Class<? extends TruffleLanguage<RContext>> getTruffleLanguage() {
+        return TruffleRLanguage.class;
+    }
+
     @TruffleBoundary
     private void reportImplementationError(Throwable e) {
         // R suicide, unless, e.g., we are running units tests.
@@ -571,16 +545,13 @@ final class REngine implements RContext.Engine {
         }
     }
 
-    private void writeStderr(String s, boolean nl) {
-        try {
-            StdConnections.getStderr().writeString(s, nl);
-        } catch (IOException ex) {
-            // Very unlikely
-            ConsoleHandler consoleHandler = context.getConsoleHandler();
-            consoleHandler.printErrorln("Error writing to stderr: " + ex.getMessage());
-            consoleHandler.printErrorln(s);
-
+    public ForeignAccess getForeignAccess(RTypedValue value) {
+        if (value instanceof RAbstractVector) {
+            return ForeignAccess.create(RAbstractVector.class, new RAbstractVectorAccessFactory());
+        } else if (value instanceof RFunction) {
+            return ForeignAccess.create(RFunction.class, new RFunctionAccessFactory());
+        } else {
+            throw RInternalError.shouldNotReachHere("cannot create ForeignAccess for " + value);
         }
     }
-
 }
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 e27baf8395c759197377356a136fded6bf45ced8..f31b8311faf005bb68917c88a8c05d403844baec 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
@@ -25,6 +25,7 @@ package com.oracle.truffle.r.engine;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.frame.*;
 import com.oracle.truffle.api.nodes.*;
+import com.oracle.truffle.api.vm.*;
 import com.oracle.truffle.r.nodes.*;
 import com.oracle.truffle.r.nodes.access.*;
 import com.oracle.truffle.r.nodes.access.array.read.*;
@@ -35,9 +36,8 @@ import com.oracle.truffle.r.nodes.function.*;
 import com.oracle.truffle.r.nodes.instrument.debug.*;
 import com.oracle.truffle.r.nodes.runtime.*;
 import com.oracle.truffle.r.runtime.*;
-import com.oracle.truffle.r.runtime.RContext.ConsoleHandler;
-import com.oracle.truffle.r.runtime.RContext.Engine;
 import com.oracle.truffle.r.runtime.RDeparse.State;
+import com.oracle.truffle.r.runtime.context.*;
 import com.oracle.truffle.r.runtime.data.*;
 import com.oracle.truffle.r.runtime.data.model.*;
 import com.oracle.truffle.r.runtime.env.*;
@@ -125,6 +125,8 @@ public class RRuntimeASTAccessImpl implements RRuntimeASTAccess {
             return baseResult;
         } else if (node instanceof AccessFieldNode) {
             return 3;
+        } else if (node instanceof ReplacementNode) {
+            return 3;
         } else {
             // TODO fill out
             assert false : node;
@@ -219,6 +221,8 @@ public class RRuntimeASTAccessImpl implements RRuntimeASTAccess {
                 default:
                     assert false;
             }
+        } else if (node instanceof ReplacementNode) {
+            return RNull.instance;
         } else {
             // TODO fill out
             assert false;
@@ -226,6 +230,31 @@ public class RRuntimeASTAccessImpl implements RRuntimeASTAccess {
         return null;
     }
 
+    public Object fromList(RAbstractVector list) {
+        int length = list.getLength();
+        if (length == 0) {
+            return RNull.instance;
+        } else if (length == 1) {
+            return list.getDataAtAsObject(0);
+        } else {
+            RNode fn = unwrapToRNode(list.getDataAtAsObject(0));
+            RSyntaxNode[] arguments = new RSyntaxNode[length - 1];
+            for (int i = 1; i < length; i++) {
+                arguments[i - 1] = (RSyntaxNode) unwrapToRNode(list.getDataAtAsObject(i));
+            }
+            return RDataFactory.createLanguage(RASTUtils.createCall(fn, ArgumentsSignature.empty(arguments.length), arguments).asRNode());
+        }
+    }
+
+    private static RNode unwrapToRNode(Object o) {
+        if (o instanceof RLanguage) {
+            return (RNode) RASTUtils.unwrap(((RLanguage) o).getRep());
+        } else {
+            // o is RSymbol or a primitive value
+            return ConstantNode.create(o);
+        }
+    }
+
     public RList asList(RLanguage rl) {
         Object[] data = new Object[getLength(rl)];
         for (int i = 0; i < data.length; i++) {
@@ -395,11 +424,10 @@ public class RRuntimeASTAccessImpl implements RRuntimeASTAccess {
 
     public void setFunctionName(RootNode node, String name) {
         ((FunctionDefinitionNode) node).setDescription(name);
-
     }
 
-    public RContext create(RContext parent, RContext.Kind kind, String[] commandArgs, ConsoleHandler consoleHandler) {
-        return RContextFactory.create(parent, kind, commandArgs, consoleHandler);
+    public TruffleVM create(ContextInfo info) {
+        return RContextFactory.create(info);
     }
 
     public Engine createEngine(RContext context) {
@@ -436,7 +464,7 @@ public class RRuntimeASTAccessImpl implements RRuntimeASTAccess {
      * {@code call == null}, it's pretty simple as we just back off to the frame, where the call
      * will have been stored. Otherwise, we have to deal with the different ways in which the
      * internal implementation can generate an error/warning and locate the correct node, and try to
-     * maych the behavior of GnuR regarding {@code .Internal} (TODO).
+     * match the behavior of GnuR regarding {@code .Internal} (TODO).
      */
     public Object findCaller(Node call) {
         Frame frame = Utils.getActualCurrentFrame();
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/TruffleRLanguage.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/TruffleRLanguage.java
index 433577d9810597aa94ab1b2a672288d2e4a3a1a7..822ad7d45b002ecc1d5577de40c5e82a4004b49b 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/TruffleRLanguage.java
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/TruffleRLanguage.java
@@ -30,19 +30,21 @@ import com.oracle.truffle.api.instrument.*;
 import com.oracle.truffle.api.nodes.*;
 import com.oracle.truffle.api.source.*;
 import com.oracle.truffle.r.engine.repl.debug.*;
-import com.oracle.truffle.r.runtime.*;
+import com.oracle.truffle.r.runtime.context.*;
 
 /**
  * Only does the minimum for running under the debugger. It is not completely clear how to correctly
  * integrate the R startup in {@code RCommand} with this API.
  */
-@TruffleLanguage.Registration(name = "R", version = "3.1.3", mimeType = "application/x-r")
+@TruffleLanguage.Registration(name = "R", version = "0.1", mimeType = {"application/x-r", "text/x-r"})
 public final class TruffleRLanguage extends TruffleLanguage<RContext> {
 
     private DebugSupportProvider debugSupport;
 
     public static final TruffleRLanguage INSTANCE = new TruffleRLanguage();
 
+    public static final String MIME = "application/x-r";
+
     private TruffleRLanguage() {
     }
 
@@ -66,12 +68,7 @@ public final class TruffleRLanguage extends TruffleLanguage<RContext> {
 
     @Override
     protected RContext createContext(Env env) {
-        /*
-         * TODO we assume here that the initial context has already been created, which is certainly
-         * true by fiat when running under the debugger, but may not be in general.
-         */
-        RContext result = RContext.getInstance();
-        return result;
+        return RContext.create(env);
     }
 
     @Override
@@ -82,17 +79,22 @@ public final class TruffleRLanguage extends TruffleLanguage<RContext> {
          * TruffleVM does not know about, we have to use a delegation mechanism via a wrapper
          * CallTarget class, using a special REngine entry point.
          */
-        return RContext.getEngine().parseToCallTarget(source);
+        return RContext.getEngine().parseToCallTarget(source, true);
     }
 
     @Override
     protected Object findExportedSymbol(RContext context, String globalName, boolean onlyExplicit) {
-        throw RInternalError.unimplemented("findExportedSymbol");
+        return context.getExportedSymbols().get(globalName);
     }
 
     @Override
     protected Object getLanguageGlobal(RContext context) {
-        throw RInternalError.unimplemented("getLanguageGlobal");
+        // TODO: what's the meaning of "language global" for R?
+        return null;
     }
 
+    // TODO: why isn't the original method public?
+    public Node actuallyCreateFindContextNode() {
+        return createFindContextNode();
+    }
 }
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ConstantRootNode.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ConstantRootNode.java
new file mode 100644
index 0000000000000000000000000000000000000000..05a4ead0e543af2a660e8ce2da44816c6d3d4d2f
--- /dev/null
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ConstantRootNode.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2015, 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.interop;
+
+import com.oracle.truffle.api.frame.*;
+import com.oracle.truffle.api.nodes.*;
+import com.oracle.truffle.r.engine.*;
+import com.oracle.truffle.r.nodes.access.*;
+
+public class ConstantRootNode extends RootNode {
+
+    @Child private ConstantNode constant;
+
+    public ConstantRootNode(ConstantNode constant) {
+        super(TruffleRLanguage.class, null, null);
+        this.constant = constant;
+    }
+
+    @Override
+    public Object execute(VirtualFrame frame) {
+        return constant.execute(frame);
+    }
+}
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RAbstractVectorAccessFactory.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RAbstractVectorAccessFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..31538e041e0ae7a421eaa9ad801cab12ee18ce99
--- /dev/null
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RAbstractVectorAccessFactory.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2015, 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.interop;
+
+import com.oracle.truffle.api.*;
+import com.oracle.truffle.api.frame.*;
+import com.oracle.truffle.api.interop.*;
+import com.oracle.truffle.api.interop.ForeignAccess.Factory10;
+import com.oracle.truffle.api.nodes.*;
+import com.oracle.truffle.r.engine.*;
+import com.oracle.truffle.r.runtime.*;
+import com.oracle.truffle.r.runtime.data.model.*;
+
+public final class RAbstractVectorAccessFactory implements Factory10 {
+
+    public abstract class InteropRootNode extends RootNode {
+        public InteropRootNode() {
+            super(TruffleRLanguage.class, null, null);
+        }
+    }
+
+    public CallTarget accessIsNull() {
+        throw RInternalError.shouldNotReachHere("message: accessIsNull");
+    }
+
+    public CallTarget accessIsExecutable() {
+        throw RInternalError.shouldNotReachHere("message: accessIsExecutable");
+    }
+
+    public CallTarget accessIsBoxed() {
+        return Truffle.getRuntime().createCallTarget(new InteropRootNode() {
+            @Override
+            public Object execute(VirtualFrame frame) {
+                RAbstractVector arg = (RAbstractVector) ForeignAccess.getReceiver(frame);
+                return arg.getLength() == 1;
+            }
+        });
+    }
+
+    public CallTarget accessHasSize() {
+        return Truffle.getRuntime().createCallTarget(new InteropRootNode() {
+            @Override
+            public Object execute(VirtualFrame frame) {
+                return true;
+            }
+        });
+    }
+
+    public CallTarget accessGetSize() {
+        return Truffle.getRuntime().createCallTarget(new VectorSizeNode());
+    }
+
+    public CallTarget accessUnbox() {
+        return Truffle.getRuntime().createCallTarget(new InteropRootNode() {
+            @Override
+            public Object execute(VirtualFrame frame) {
+                RAbstractVector arg = (RAbstractVector) ForeignAccess.getReceiver(frame);
+                return arg.getDataAtAsObject(0);
+            }
+        });
+    }
+
+    public CallTarget accessRead() {
+        return Truffle.getRuntime().createCallTarget(new VectorReadNode());
+    }
+
+    public CallTarget accessWrite() {
+        throw RInternalError.shouldNotReachHere("message: accessWrite");
+    }
+
+    public CallTarget accessExecute(int argumentsLength) {
+        throw RInternalError.shouldNotReachHere("message: accessExecute");
+    }
+
+    public CallTarget accessInvoke(int argumentsLength) {
+        throw RInternalError.shouldNotReachHere("message: accessInvoke");
+    }
+
+    public CallTarget accessMessage(Message unknown) {
+        throw RInternalError.shouldNotReachHere("message: " + unknown);
+    }
+}
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RFunctionAccessFactory.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RFunctionAccessFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..4e40bd5c34184a0b6ea6a4cc8ca132b12557b100
--- /dev/null
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RFunctionAccessFactory.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2015, 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.interop;
+
+import com.oracle.truffle.api.*;
+import com.oracle.truffle.api.interop.ForeignAccess.Factory10;
+import com.oracle.truffle.api.interop.*;
+import com.oracle.truffle.r.nodes.access.*;
+import com.oracle.truffle.r.runtime.*;
+
+public final class RFunctionAccessFactory implements Factory10 {
+
+    public CallTarget accessIsNull() {
+        throw RInternalError.shouldNotReachHere("message: accessIsNull");
+    }
+
+    public CallTarget accessIsExecutable() {
+        return Truffle.getRuntime().createCallTarget(new ConstantRootNode(ConstantNode.create(true)));
+    }
+
+    public CallTarget accessIsBoxed() {
+        throw RInternalError.shouldNotReachHere("message: accessIsBoxed");
+    }
+
+    public CallTarget accessHasSize() {
+        return Truffle.getRuntime().createCallTarget(new ConstantRootNode(ConstantNode.create(false)));
+    }
+
+    public CallTarget accessGetSize() {
+        throw RInternalError.shouldNotReachHere("message: accessIsBoxed");
+    }
+
+    public CallTarget accessUnbox() {
+        throw RInternalError.shouldNotReachHere("message: accessUnbox");
+    }
+
+    public CallTarget accessRead() {
+        throw RInternalError.shouldNotReachHere("message: accessIsBoxed");
+    }
+
+    public CallTarget accessWrite() {
+        throw RInternalError.shouldNotReachHere("message: accessWrite");
+    }
+
+    public CallTarget accessExecute(int argumentsLength) {
+        return Truffle.getRuntime().createCallTarget(new RInteropExecuteNode(argumentsLength));
+    }
+
+    public CallTarget accessInvoke(int argumentsLength) {
+        throw RInternalError.shouldNotReachHere("message: accessInvoke");
+    }
+
+    public CallTarget accessMessage(Message unknown) {
+        throw RInternalError.shouldNotReachHere("message: " + unknown);
+    }
+}
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RInteropExecuteNode.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RInteropExecuteNode.java
new file mode 100644
index 0000000000000000000000000000000000000000..7e5b652bca5a2f88469e24f01d777fbd196e80ea
--- /dev/null
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RInteropExecuteNode.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2015, 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.interop;
+
+import java.util.*;
+
+import com.oracle.truffle.api.*;
+import com.oracle.truffle.api.frame.*;
+import com.oracle.truffle.api.interop.*;
+import com.oracle.truffle.api.nodes.*;
+import com.oracle.truffle.r.engine.*;
+import com.oracle.truffle.r.nodes.function.*;
+import com.oracle.truffle.r.runtime.*;
+import com.oracle.truffle.r.runtime.data.*;
+
+public class RInteropExecuteNode extends RootNode {
+
+    private static final FrameDescriptor emptyFrameDescriptor = new FrameDescriptor();
+
+    @Child CallMatcherNode callMatcher = CallMatcherNode.create(false, true);
+
+    private final ArgumentsSignature suppliedSignature;
+
+    public RInteropExecuteNode(int argumentsLength) {
+        super(TruffleRLanguage.class, null, null);
+        suppliedSignature = ArgumentsSignature.empty(argumentsLength);
+    }
+
+    @Override
+    public Object execute(VirtualFrame frame) {
+        RFunction function = (RFunction) ForeignAccess.getReceiver(frame);
+        List<Object> arguments = ForeignAccess.getArguments(frame);
+
+        Object[] dummyFrameArgs = RArguments.createUnitialized();
+        VirtualFrame dummyFrame = Truffle.getRuntime().createVirtualFrame(dummyFrameArgs, emptyFrameDescriptor);
+
+        return callMatcher.execute(dummyFrame, suppliedSignature, arguments.toArray(), function, null);
+    }
+}
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/VectorReadNode.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/VectorReadNode.java
new file mode 100644
index 0000000000000000000000000000000000000000..e64d7932d6017199840691066ecf43ee0c018fae
--- /dev/null
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/VectorReadNode.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2015, 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.interop;
+
+import com.oracle.truffle.api.*;
+import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
+import com.oracle.truffle.api.frame.*;
+import com.oracle.truffle.api.interop.*;
+import com.oracle.truffle.api.nodes.*;
+import com.oracle.truffle.api.utilities.*;
+import com.oracle.truffle.r.engine.*;
+import com.oracle.truffle.r.nodes.builtin.base.InfixEmulationFunctions.AccessArraySubscriptBuiltin;
+import com.oracle.truffle.r.nodes.builtin.base.InfixEmulationFunctionsFactory.AccessArraySubscriptBuiltinNodeGen;
+import com.oracle.truffle.r.runtime.*;
+import com.oracle.truffle.r.runtime.data.*;
+import com.oracle.truffle.r.runtime.data.model.*;
+
+public class VectorReadNode extends RootNode {
+
+    @CompilationFinal private boolean lengthAccess;
+    @Child private AccessArraySubscriptBuiltin builtin;
+
+    private final BranchProfile intIndex = BranchProfile.create();
+    private final BranchProfile doubleIndex = BranchProfile.create();
+    private final BranchProfile longIndex = BranchProfile.create();
+
+    public VectorReadNode() {
+        super(TruffleRLanguage.class, null, null);
+        this.lengthAccess = false;
+    }
+
+    @Override
+    public Object execute(VirtualFrame frame) {
+        Object label = ForeignAccess.getArguments(frame).get(0);
+        if (lengthAccess && !(label instanceof String)) {
+            CompilerDirectives.transferToInterpreter();
+            lengthAccess = false;
+        }
+        if (!lengthAccess && (label instanceof String)) {
+            CompilerDirectives.transferToInterpreter();
+            lengthAccess = true;
+        }
+
+        RAbstractVector arg = (RAbstractVector) ForeignAccess.getReceiver(frame);
+        if (lengthAccess) {
+            if (label.equals("length")) {
+                return arg.getLength();
+            } else {
+                CompilerDirectives.transferToInterpreter();
+                throw RInternalError.shouldNotReachHere("unknown message: " + label);
+            }
+        } else {
+            if (builtin == null) {
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                builtin = insert(AccessArraySubscriptBuiltinNodeGen.create(null, null, null));
+            }
+            int index;
+            if (label instanceof Integer) {
+                intIndex.enter();
+                index = (int) label;
+            } else if (label instanceof Long) {
+                longIndex.enter();
+                index = (int) (long) label;
+            } else if (label instanceof Double) {
+                doubleIndex.enter();
+                index = (int) (double) label;
+            } else {
+                CompilerDirectives.transferToInterpreter();
+                throw RInternalError.shouldNotReachHere("invalid index type: " + label);
+            }
+            return builtin.execute(frame, arg, new RArgsValuesAndNames(new Object[]{index + 1}, ArgumentsSignature.empty(1)), RRuntime.LOGICAL_TRUE, RRuntime.LOGICAL_TRUE);
+        }
+    }
+
+}
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/VectorSizeNode.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/VectorSizeNode.java
new file mode 100644
index 0000000000000000000000000000000000000000..15dcb8866128d893b8c307a05a9a654236aa2f91
--- /dev/null
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/VectorSizeNode.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2015, 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.interop;
+
+import com.oracle.truffle.api.frame.*;
+import com.oracle.truffle.api.interop.*;
+import com.oracle.truffle.api.nodes.*;
+import com.oracle.truffle.r.engine.*;
+import com.oracle.truffle.r.runtime.data.model.*;
+
+public class VectorSizeNode extends RootNode {
+
+    public VectorSizeNode() {
+        super(TruffleRLanguage.class, null, null);
+    }
+
+    @Override
+    public Object execute(VirtualFrame frame) {
+        RAbstractVector arg = (RAbstractVector) ForeignAccess.getReceiver(frame);
+        return arg.getLength();
+    }
+}
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/repl/RREPLServer.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/repl/RREPLServer.java
index d98abbacc807ed6bce82350dbce07e3443121862..e8bf544eb3eaf7b325fed32178fbe2ed1912d1ba 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/repl/RREPLServer.java
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/repl/RREPLServer.java
@@ -29,8 +29,10 @@ import com.oracle.truffle.api.instrument.*;
 import com.oracle.truffle.api.source.*;
 import com.oracle.truffle.api.vm.*;
 import com.oracle.truffle.api.vm.TruffleVM.Language;
+import com.oracle.truffle.r.engine.*;
 import com.oracle.truffle.r.engine.shell.*;
 import com.oracle.truffle.r.runtime.*;
+import com.oracle.truffle.r.runtime.context.*;
 import com.oracle.truffle.tools.debug.shell.*;
 import com.oracle.truffle.tools.debug.shell.client.*;
 import com.oracle.truffle.tools.debug.shell.server.*;
@@ -118,13 +120,11 @@ public final class RREPLServer extends REPLServer {
         String[] debugArgs = new String[args.length + 1];
         debugArgs[0] = "--debugger=rrepl";
         System.arraycopy(args, 0, debugArgs, 1, args.length);
-        RContext initialContext = RCommand.debuggerMain(debugArgs);
-        initialContext.getThisEngine().getTruffleVMBuilder().onEvent(onHalted).onEvent(onExec);
-        // This actually "builds" the TruffleVM
-        initialContext.activate();
-        this.vm = initialContext.getThisEngine().getTruffleVM();
-        assert vm != null;
-        this.language = vm.getLanguages().get("application/x-r");
+        RCmdOptions options = RCmdOptions.parseArguments(RCmdOptions.Client.R, args);
+        ContextInfo info = RCommand.createContextInfoFromCommandLine(options);
+        RContext.tempInitializingContextInfo = info;
+        this.vm = RContextFactory.create(info, builder -> builder.onEvent(onHalted).onEvent(onExec));
+        this.language = vm.getLanguages().get(TruffleRLanguage.MIME);
         assert language != null;
 
         this.statusPrefix = language.getShortName() + " REPL:";
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/repl/debug/RDebugSupportProvider.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/repl/debug/RDebugSupportProvider.java
index f7792862adb2a845041740a8deb53deae7b8c0e0..511df46026f5d4e5c81eeb5d02e0ec56c705890e 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/repl/debug/RDebugSupportProvider.java
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/repl/debug/RDebugSupportProvider.java
@@ -31,8 +31,9 @@ import com.oracle.truffle.api.instrument.impl.*;
 import com.oracle.truffle.api.nodes.*;
 import com.oracle.truffle.api.source.*;
 import com.oracle.truffle.r.runtime.*;
-import com.oracle.truffle.r.runtime.RContext.Engine.ParseException;
 import com.oracle.truffle.r.runtime.conn.*;
+import com.oracle.truffle.r.runtime.context.Engine.ParseException;
+import com.oracle.truffle.r.runtime.context.*;
 import com.oracle.truffle.r.runtime.data.*;
 import com.oracle.truffle.r.runtime.nodes.*;
 
@@ -43,7 +44,11 @@ public final class RDebugSupportProvider implements DebugSupportProvider {
 
     @Override
     public Object evalInContext(Source source, Node node, MaterializedFrame frame) {
-        return RContext.getEngine().parseAndEval(source, frame, false, false);
+        try {
+            return RContext.getEngine().parseAndEval(source, frame, false);
+        } catch (ParseException e) {
+            throw new RInternalError(e, "error while parsing debug statement from %s", source.getName());
+        }
     }
 
     @Override
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/JLineConsoleHandler.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/JLineConsoleHandler.java
index 2373cf2e38c123a1a5f1358a46f208ab9ddc59a9..2909ff8411d51d2c90f74b7186b77efc2e4a3d03 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/JLineConsoleHandler.java
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/JLineConsoleHandler.java
@@ -26,10 +26,11 @@ import java.io.*;
 
 import jline.console.*;
 
-import com.oracle.truffle.api.CompilerDirectives.*;
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.r.runtime.*;
+import com.oracle.truffle.r.runtime.context.*;
 
-public class JLineConsoleHandler implements RContext.ConsoleHandler {
+public class JLineConsoleHandler implements ConsoleHandler {
     private final ConsoleReader console;
     private final boolean isInteractive;
     private final PrintWriter printWriter;
@@ -98,4 +99,8 @@ public class JLineConsoleHandler implements RContext.ConsoleHandler {
     public int getWidth() {
         return RContext.CONSOLE_WIDTH;
     }
+
+    public String getInputDescription() {
+        return "<shell_input>";
+    }
 }
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/RCmdOptionsParser.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/RCmdOptionsParser.java
deleted file mode 100644
index 9ae976eb8cc446f8d49ae35aa39256efa25ca8f9..0000000000000000000000000000000000000000
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/RCmdOptionsParser.java
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * Copyright (c) 2013, 2015, 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.shell;
-
-import com.oracle.truffle.r.runtime.*;
-import com.oracle.truffle.r.runtime.RCmdOptions.Client;
-import com.oracle.truffle.r.runtime.RCmdOptions.Option;
-import com.oracle.truffle.r.runtime.RCmdOptions.OptionType;
-
-/**
- * Implements the standard R/Rscript command line syntax.
- *
- * R supports {@code --arg=value} or {@code -arg value} for string-valued options.
- *
- * The spec for {@code commandArgs()} states that it returns the executable by which R was invoked
- * in element 0, which is consistent with the C {@code main} function, but defines the exact form to
- * be platform independent. Java does not provide the executable (for obvious reasons) so we use
- * "FastR".
- */
-public class RCmdOptionsParser {
-    // CheckStyle: stop system..print check
-    public static class Result {
-        /**
-         * The original {@code args} array, with element zero set to "FastR".
-         */
-        public final String[] args;
-        /**
-         * Index in {@code args} of the first non-option argument or {@code args.length} if none.
-         */
-        public final int firstNonOptionArgIndex;
-
-        Result(String[] args, int firstNonOptionArgIndex) {
-            this.args = args;
-            this.firstNonOptionArgIndex = firstNonOptionArgIndex;
-        }
-    }
-
-    /**
-     * Parse the arguments, setting the corresponding {@code Option values}.
-     */
-    public static Result parseArguments(Client client, String[] args) {
-        RCmdOptions.reset();
-        int i = 0;
-        int firstNonOptionArgIndex = -1;
-        while (i < args.length) {
-            final String arg = args[i];
-            Option<?> option = RCmdOptions.matchOption(arg);
-            if (option == null) {
-                // for Rscript, this means we are done
-                if (client == Client.RSCRIPT) {
-                    firstNonOptionArgIndex = i;
-                    break;
-                }
-                // GnuR does not abort, simply issues a warning
-                System.out.printf("WARNING: unknown option '%s'%n", arg);
-                i++;
-                continue;
-            } else if (option.matchedShort() && i == args.length - 1) {
-                System.out.println("usage:");
-                printHelp(client, 1);
-            }
-            // check implemented
-            if (!option.implemented) {
-                System.out.println("WARNING: option: " + arg + " is not implemented");
-            }
-            if (option.matchedShort()) {
-                i++;
-                option.setValue(args[i]);
-            } else {
-                if (option.type == OptionType.BOOLEAN) {
-                    option.setValue(true);
-                } else if (option.type == OptionType.STRING) {
-                    int eqx = arg.indexOf('=');
-                    option.setValue(arg.substring(eqx + 1));
-                }
-            }
-            i++;
-            // check for --args, in which case stop parsing
-            if (option == RCmdOptions.ARGS) {
-                firstNonOptionArgIndex = i;
-                break;
-            }
-        }
-        String[] xargs = new String[args.length + 1];
-        xargs[0] = "FastR";
-        System.arraycopy(args, 0, xargs, 1, args.length);
-        return new Result(xargs, firstNonOptionArgIndex < 0 ? xargs.length : firstNonOptionArgIndex + 1); // adj
-        // for zeroth arg insert
-    }
-
-    public static void printHelp(Client client, int exitCode) {
-        System.out.println(client.usage());
-        System.out.println("Options:");
-        for (Option<?> option : RCmdOptions.optionList()) {
-            System.out.printf("  %-22s  %s%n", option.getHelpName(), option.help);
-        }
-        System.out.println("\nFILE may contain spaces but not shell metacharacters.\n");
-        if (exitCode >= 0) {
-            System.exit(exitCode);
-        }
-    }
-
-}
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/RCommand.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/RCommand.java
index 99444a4c394ca9dbff36dc83922b4a616a110b25..f3fed0c1701c131acfda1ff1dc08b361f22fadc5 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/RCommand.java
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/RCommand.java
@@ -22,7 +22,7 @@
  */
 package com.oracle.truffle.r.engine.shell;
 
-import static com.oracle.truffle.r.runtime.RCmdOptions.*;
+import static com.oracle.truffle.r.runtime.RCmdOptions.RCmdOption.*;
 
 import java.io.*;
 import java.nio.file.*;
@@ -30,14 +30,17 @@ import java.util.*;
 
 import jline.console.*;
 
-import com.oracle.truffle.api.instrument.*;
 import com.oracle.truffle.api.source.*;
+import com.oracle.truffle.api.vm.*;
 import com.oracle.truffle.r.engine.*;
 import com.oracle.truffle.r.nodes.builtin.base.*;
 import com.oracle.truffle.r.runtime.*;
-import com.oracle.truffle.r.runtime.RContext.ConsoleHandler;
-import com.oracle.truffle.r.runtime.RContext.Engine;
+import com.oracle.truffle.r.runtime.context.*;
+import com.oracle.truffle.r.runtime.context.Engine.IncompleteSourceException;
+import com.oracle.truffle.r.runtime.context.Engine.ParseException;
+import com.oracle.truffle.r.runtime.context.RContext.ContextKind;
 import com.oracle.truffle.r.runtime.data.*;
+import com.oracle.truffle.r.runtime.data.model.*;
 
 /**
  * Emulates the (Gnu)R command as precisely as possible.
@@ -47,65 +50,34 @@ public class RCommand {
     // CheckStyle: stop system..print check
 
     public static void main(String[] args) {
-        internalMain(args, true);
+        RCmdOptions options = RCmdOptions.parseArguments(RCmdOptions.Client.R, args);
+        options.printHelpAndVersion();
+        ContextInfo info = createContextInfoFromCommandLine(options);
+        // never returns
+        readEvalPrint(info);
+        throw RInternalError.shouldNotReachHere();
     }
 
-    public static RContext internalMain(String[] args, boolean eval) {
-        try {
-            RCmdOptionsParser.Result result = RCmdOptionsParser.parseArguments(RCmdOptions.Client.R, args);
-            if (HELP.getValue()) {
-                RCmdOptionsParser.printHelp(RCmdOptions.Client.R, 0);
-            } else if (VERSION.getValue()) {
-                printVersionAndExit();
-            } else if (RHOME.getValue()) {
-                printRHomeAndExit();
-            }
-            return subMainInit(result.args, eval);
-        } catch (Utils.DebugExitException ex) {
-            /*
-             * This is thrown instead of doing System.exit, when we are running under the in-process
-             * Truffle debugger. We just return to the debugger command loop, possibly to be
-             * re-entered with a new evaluation.
-             */
-            return null;
-        } catch (QuitException ex) {
-            /* This is thrown by the Truffle debugger when the user executes the 'q' command. */
-            return null;
+    public static ContextInfo createContextInfoFromCommandLine(RCmdOptions options) {
+        if (options.getBoolean(SLAVE)) {
+            options.setValue(QUIET, true);
+            options.setValue(NO_SAVE, true);
         }
-    }
-
-    public static RContext debuggerMain(String[] args) {
-        return internalMain(args, false);
-    }
 
-    /**
-     * Entry point for {@link RscriptCommand} avoiding re-parsing.
-     */
-    public static void rscriptMain(String[] args) {
-        subMainInit(args, true);
-    }
-
-    public static RContext subMainInit(String[] args, boolean eval) {
-
-        if (SLAVE.getValue()) {
-            QUIET.setValue(true);
-            NO_SAVE.setValue(true);
+        if (options.getBoolean(VANILLA)) {
+            options.setValue(NO_SAVE, true);
+            options.setValue(NO_ENVIRON, true);
+            options.setValue(NO_INIT_FILE, true);
+            options.setValue(NO_RESTORE, true);
         }
 
-        if (VANILLA.getValue()) {
-            NO_SAVE.setValue(true);
-            NO_ENVIRON.setValue(true);
-            NO_INIT_FILE.setValue(true);
-            NO_RESTORE.setValue(true);
-        }
-
-        String fileArg = FILE.getValue();
+        String fileArg = options.getString(FILE);
         if (fileArg != null) {
-            if (EXPR.getValue() != null) {
+            if (options.getStringList(EXPR) != null) {
                 Utils.fatalError("cannot use -e with -f or --file");
             }
-            if (!SAVE.getValue()) {
-                NO_SAVE.setValue(true);
+            if (!options.getBoolean(SLAVE)) {
+                options.setValue(NO_SAVE, true);
             }
             if (fileArg.equals("-")) {
                 // means stdin, but still implies NO_SAVE
@@ -113,9 +85,7 @@ public class RCommand {
             }
         }
 
-        RContextFactory.initialize();
-
-        if (!(QUIET.getValue() || SILENT.getValue())) {
+        if (!(options.getBoolean(QUIET) || options.getBoolean(SILENT))) {
             System.out.println(RRuntime.WELCOME_MESSAGE);
         }
         /*
@@ -126,9 +96,9 @@ public class RCommand {
         ConsoleHandler consoleHandler;
         InputStream consoleInput = System.in;
         OutputStream consoleOutput = System.out;
-        String filePath = null;
         if (fileArg != null) {
             List<String> lines;
+            String filePath;
             try {
                 File file = new File(fileArg);
                 lines = Files.readAllLines(file.toPath());
@@ -136,13 +106,13 @@ public class RCommand {
             } catch (IOException e) {
                 throw Utils.fatalError("cannot open file '" + fileArg + "': " + e.getMessage());
             }
-            consoleHandler = new StringConsoleHandler(lines, System.out);
-        } else if (EXPR.getValue() != null) {
-            List<String> exprs = EXPR.getValue();
-            if (!SAVE.getValue()) {
-                NO_SAVE.setValue(true);
+            consoleHandler = new StringConsoleHandler(lines, System.out, filePath);
+        } else if (options.getStringList(EXPR) != null) {
+            List<String> exprs = options.getStringList(EXPR);
+            if (!options.getBoolean(SLAVE)) {
+                options.setValue(NO_SAVE, true);
             }
-            consoleHandler = new StringConsoleHandler(exprs, System.out);
+            consoleHandler = new StringConsoleHandler(exprs, System.out, "<expression input>");
         } else {
             /*
              * GnuR behavior differs from the manual entry for {@code interactive} in that {@code
@@ -158,37 +128,18 @@ public class RCommand {
             } catch (IOException ex) {
                 throw Utils.fail("unexpected error opening console reader");
             }
-            boolean isInteractive = INTERACTIVE.getValue() || sysConsole != null;
-            if (!isInteractive && !SAVE.getValue() && !NO_SAVE.getValue() && !VANILLA.getValue()) {
+            boolean isInteractive = options.getBoolean(INTERACTIVE) || sysConsole != null;
+            if (!isInteractive && !options.getBoolean(SAVE) && !options.getBoolean(NO_SAVE) && !options.getBoolean(VANILLA)) {
                 throw Utils.fatalError("you must specify '--save', '--no-save' or '--vanilla'");
             }
             // long start = System.currentTimeMillis();
             consoleHandler = new JLineConsoleHandler(isInteractive, consoleReader);
         }
-        RContext context = RContextFactory.createInitial(args, consoleHandler);
-        if (eval) {
-            // never returns
-            context.activate();
-            readEvalPrint(consoleHandler, context, filePath);
-            throw RInternalError.shouldNotReachHere();
-        } else {
-            return context;
-        }
-    }
-
-    private static void printVersionAndExit() {
-        System.out.print("FastR version ");
-        System.out.println(RVersionNumber.FULL);
-        System.out.println(RRuntime.LICENSE);
-        Utils.exit(0);
-    }
-
-    private static void printRHomeAndExit() {
-        System.out.println(REnvVars.rHome());
-        throw Utils.exit(0);
+        return ContextInfo.create(options, ContextKind.SHARE_NOTHING, null, consoleHandler);
     }
 
-    private static final Source QUIT_EOF = Source.fromText("quit(\"default\", 0L, TRUE)", "<quit_file>");
+    private static final String GET_ECHO = "invisible(getOption('echo'))";
+    private static final String QUIT_EOF = "quit(\"default\", 0L, TRUE)";
 
     /**
      * The read-eval-print loop, which can take input from a console, command line expression or a
@@ -201,13 +152,14 @@ public class RCommand {
      * In case 2, we must implicitly execute a {@code quit("default, 0L, TRUE} command before
      * exiting. So,in either case, we never return.
      */
-    private static void readEvalPrint(ConsoleHandler consoleHandler, RContext context, String filePath) {
-        String inputDescription = filePath == null ? "<shell_input>" : filePath;
-        Source source = Source.fromNamedAppendableText(inputDescription);
+    public static void readEvalPrint(ContextInfo info) {
+        TruffleVM vm = RContextFactory.create(info);
+        ConsoleHandler consoleHandler = info.getConsoleHandler();
+        Source source = Source.fromNamedAppendableText(consoleHandler.getInputDescription());
         try {
             // console.println("initialize time: " + (System.currentTimeMillis() - start));
-            for (;;) {
-                boolean doEcho = doEcho();
+            REPL: for (;;) {
+                boolean doEcho = doEcho(vm);
                 consoleHandler.setPrompt(doEcho ? "> " : null);
                 try {
                     String input = consoleHandler.readLine();
@@ -227,7 +179,28 @@ public class RCommand {
                     try {
                         String continuePrompt = getContinuePrompt();
                         Source subSource = Source.subSource(source, startLength);
-                        while (context.getThisEngine().parseAndEval(subSource, true, true) == Engine.INCOMPLETE_SOURCE) {
+                        while (true) {
+                            try {
+                                // TODO: how to pass subSource to TruffleVM?
+                                vm.eval(TruffleRLanguage.MIME, subSource.getCode());
+                                continue REPL;
+                            } catch (IncompleteSourceException e) {
+                                // read another line of input
+                            } catch (ParseException e) {
+                                try {
+                                    throw e.throwAsRError();
+                                } catch (RError e2) {
+                                    // this error is expected
+                                }
+                                continue REPL;
+                            } catch (IOException e) {
+                                if (e.getCause() instanceof RError) {
+                                    // nothing to do
+                                } else {
+                                    RInternalError.reportError(e);
+                                }
+                                continue REPL;
+                            }
                             consoleHandler.setPrompt(doEcho ? continuePrompt : null);
                             String additionalInput = consoleHandler.readLine();
                             if (additionalInput == null) {
@@ -246,22 +219,34 @@ public class RCommand {
         } catch (BrowserQuitException e) {
             // can happen if user profile invokes browser
         } catch (EOFException ex) {
-            context.getThisEngine().parseAndEval(QUIT_EOF, false, false);
+            try {
+                vm.eval(TruffleRLanguage.MIME, QUIT_EOF);
+            } catch (IOException e) {
+                throw RInternalError.shouldNotReachHere(e);
+            }
         } finally {
-            context.destroy();
+            RContext.destroyContext(vm);
         }
     }
 
-    private static boolean doEcho() {
-        if (SLAVE.getValue()) {
-            return false;
+    private static boolean doEcho(TruffleVM vm) {
+        Object echo;
+        try {
+            echo = vm.eval(TruffleRLanguage.MIME, GET_ECHO);
+        } catch (IOException e) {
+            throw RInternalError.shouldNotReachHere(e);
+        }
+        if (echo instanceof Byte) {
+            return RRuntime.fromLogical((Byte) echo);
+        } else if (echo instanceof RAbstractLogicalVector) {
+            return RRuntime.fromLogical(((RAbstractLogicalVector) echo).getDataAt(0));
+        } else {
+            throw RInternalError.shouldNotReachHere();
         }
-        RLogicalVector echo = (RLogicalVector) RRuntime.asAbstractVector(RContext.getROptionsState().getValue("echo"));
-        return RRuntime.fromLogical(echo.getDataAt(0));
     }
 
     private static String getContinuePrompt() {
-        RStringVector continuePrompt = (RStringVector) RRuntime.asAbstractVector(RContext.getROptionsState().getValue("continue"));
+        RStringVector continuePrompt = (RStringVector) RRuntime.asAbstractVector(RContext.getInstance().stateROptions.getValue("continue"));
         return continuePrompt.getDataAt(0);
     }
 
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/RscriptCommand.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/RscriptCommand.java
index 64ce406ebeb3979e5fd2d7e8290eee2790a868d9..25c2493fb47a242e01d59000863c8affd1a28b3c 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/RscriptCommand.java
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/RscriptCommand.java
@@ -22,11 +22,13 @@
  */
 package com.oracle.truffle.r.engine.shell;
 
-import static com.oracle.truffle.r.runtime.RCmdOptions.*;
+import static com.oracle.truffle.r.runtime.RCmdOptions.RCmdOption.*;
 
 import java.util.*;
 
+import com.oracle.truffle.api.instrument.*;
 import com.oracle.truffle.r.runtime.*;
+import com.oracle.truffle.r.runtime.context.*;
 
 /**
  * Emulates the (Gnu)Rscript command as precisely as possible. in GnuR, Rscript is a genuine wrapper
@@ -37,43 +39,41 @@ import com.oracle.truffle.r.runtime.*;
  */
 public class RscriptCommand {
     // CheckStyle: stop system..print check
-    public static void main(String[] args) {
-        // Since many of the options are shared parse them from an RSCRIPT perspective.
-        // Handle --help and --version specially, as they exit.
-        RCmdOptionsParser.Result result = RCmdOptionsParser.parseArguments(RCmdOptions.Client.RSCRIPT, args);
-        int resultArgsLength = result.args.length;
-        int firstNonOptionArgIndex = result.firstNonOptionArgIndex;
-        if (HELP.getValue()) {
-            RCmdOptionsParser.printHelp(RCmdOptions.Client.RSCRIPT, 0);
+
+    private static void preprocessRScriptOptions(RCmdOptions options) {
+        String[] arguments = options.getArguments();
+        int resultArgsLength = arguments.length;
+        int firstNonOptionArgIndex = options.getFirstNonOptionArgIndex();
+        if (options.getBoolean(HELP)) {
+            RCmdOptions.printHelp(RCmdOptions.Client.RSCRIPT, 0);
             Utils.exit(0);
-        } else if (VERSION.getValue()) {
+        } else if (options.getBoolean(VERSION)) {
             printVersionAndExit();
         }
         // Now reformat the args, setting --slave and --no-restore as per the spec
-        // and invoke RCommand.subMain
         ArrayList<String> adjArgs = new ArrayList<>(resultArgsLength + 1);
-        adjArgs.add(result.args[0]);
+        adjArgs.add(arguments[0]);
         adjArgs.add("--slave");
-        SLAVE.setValue(true);
+        options.setValue(SLAVE, true);
         adjArgs.add("--no-restore");
-        NO_RESTORE.setValue(true);
+        options.setValue(NO_RESTORE, true);
         // Either -e options are set or first non-option arg is a file
-        if (EXPR.getValue() == null) {
+        if (options.getStringList(EXPR) == null) {
             if (firstNonOptionArgIndex == resultArgsLength) {
                 System.err.println("filename is missing");
                 Utils.exit(2);
             } else {
-                FILE.setValue(result.args[firstNonOptionArgIndex]);
+                options.setValue(FILE, arguments[firstNonOptionArgIndex]);
             }
         }
         // copy up to non-option args
         int rx = 1;
         while (rx < firstNonOptionArgIndex) {
-            adjArgs.add(result.args[rx]);
+            adjArgs.add(arguments[rx]);
             rx++;
         }
-        if (FILE.getValue() != null) {
-            adjArgs.add("--file=" + FILE.getValue());
+        if (options.getString(FILE) != null) {
+            adjArgs.add("--file=" + options.getString(FILE));
             rx++; // skip over file arg
             firstNonOptionArgIndex++;
         }
@@ -81,12 +81,29 @@ public class RscriptCommand {
         if (firstNonOptionArgIndex < resultArgsLength) {
             adjArgs.add("--args");
             while (rx < resultArgsLength) {
-                adjArgs.add(result.args[rx++]);
+                adjArgs.add(arguments[rx++]);
             }
         }
-        String[] adjArgsArray = new String[adjArgs.size()];
-        adjArgs.toArray(adjArgsArray);
-        RCommand.rscriptMain(adjArgsArray);
+        options.setArguments(adjArgs.toArray(new String[adjArgs.size()]));
+    }
+
+    public static void main(String[] args) {
+        // Since many of the options are shared parse them from an RSCRIPT perspective.
+        // Handle --help and --version specially, as they exit.
+        RCmdOptions options = RCmdOptions.parseArguments(RCmdOptions.Client.RSCRIPT, args);
+        preprocessRScriptOptions(options);
+        ContextInfo info = RCommand.createContextInfoFromCommandLine(options);
+        try {
+            RCommand.readEvalPrint(info);
+        } catch (Utils.DebugExitException ex) {
+            /*
+             * This is thrown instead of doing System.exit, when we are running under the in-process
+             * Truffle debugger. We just return to the debugger command loop, possibly to be
+             * re-entered with a new evaluation.
+             */
+        } catch (QuitException ex) {
+            /* This is thrown by the Truffle debugger when the user executes the 'q' command. */
+        }
     }
 
     private static void printVersionAndExit() {
@@ -94,5 +111,4 @@ public class RscriptCommand {
         System.out.println(RVersionNumber.FULL);
         Utils.exit(0);
     }
-
 }
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/StringConsoleHandler.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/StringConsoleHandler.java
index 0f04aa247f81ecf78b311862dbc0272c3fa8ae7f..2a026cd1cddc445833b1ff5f5ce3960be7d14016 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/StringConsoleHandler.java
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/StringConsoleHandler.java
@@ -26,17 +26,19 @@ import java.io.*;
 import java.util.*;
 
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
-import com.oracle.truffle.r.runtime.*;
+import com.oracle.truffle.r.runtime.context.*;
 
-public class StringConsoleHandler implements RContext.ConsoleHandler {
+public class StringConsoleHandler implements ConsoleHandler {
     private final PrintStream output;
     private final List<String> lines;
+    private final String inputDescription;
     private String prompt;
     private int currentLine;
 
-    public StringConsoleHandler(List<String> lines, PrintStream output) {
+    public StringConsoleHandler(List<String> lines, PrintStream output, String inputDescription) {
         this.lines = lines;
         this.output = output;
+        this.inputDescription = inputDescription;
     }
 
     @TruffleBoundary
@@ -94,4 +96,8 @@ public class StringConsoleHandler implements RContext.ConsoleHandler {
     public int getWidth() {
         return RContext.CONSOLE_WIDTH;
     }
+
+    public String getInputDescription() {
+        return inputDescription;
+    }
 }
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastr/FastRContext.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastr/FastRContext.java
index 83a6bc602be0e0d9f598fb7280b999b2a706ca63..c7de1cb42b1b4b06032462b5812803bc46457299 100644
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastr/FastRContext.java
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastr/FastRContext.java
@@ -24,11 +24,15 @@ package com.oracle.truffle.r.library.fastr;
 
 import java.io.*;
 
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.*;
 import com.oracle.truffle.api.source.*;
+import com.oracle.truffle.api.vm.*;
 import com.oracle.truffle.r.nodes.builtin.*;
 import com.oracle.truffle.r.runtime.*;
+import com.oracle.truffle.r.runtime.RCmdOptions.Client;
 import com.oracle.truffle.r.runtime.conn.*;
+import com.oracle.truffle.r.runtime.context.*;
 import com.oracle.truffle.r.runtime.data.*;
 import com.oracle.truffle.r.runtime.data.model.*;
 import com.oracle.truffle.r.runtime.nodes.*;
@@ -37,27 +41,29 @@ public class FastRContext {
 
     public abstract static class Create extends RExternalBuiltinNode.Arg2 {
         @Specialization
+        @TruffleBoundary
         protected int create(RAbstractStringVector args, RIntVector kindVec) {
-            String[] argsArray = args.getLength() == 1 ? new String[]{args.getDataAt(0)} : ((RStringVector) args).getDataCopy();
-            RContext current = RContext.getInstance();
-            RContext.ConsoleHandler consoleHandler = current.getConsoleHandler();
-            RContext.Kind kind = RContext.Kind.values()[kindVec.getDataAt(0) - 1];
-            RContext newContext = RContext.getRRuntimeASTAccess().create(current, kind, argsArray, consoleHandler);
-            return (int) newContext.getId();
+            RContext.ContextKind kind = RContext.ContextKind.VALUES[kindVec.getDataAt(0) - 1];
+            RCmdOptions options = RCmdOptions.parseArguments(Client.RSCRIPT, args.materialize().getDataCopy());
+            return ContextInfo.createDeferred(options, kind, RContext.getInstance(), RContext.getInstance().getConsoleHandler());
         }
     }
 
     public abstract static class Print extends RExternalBuiltinNode.Arg1 {
         @Specialization
+        @TruffleBoundary
         protected RNull print(RAbstractIntVector ctxt) {
             if (ctxt.getLength() != 1) {
                 throw RError.error(this, RError.Message.INVALID_ARGUMENT, "context");
             }
             int contextId = ctxt.getDataAt(0);
-            @SuppressWarnings("unused")
-            RContext context = checkContext(contextId, this);
+            ContextInfo info = ContextInfo.get(contextId);
             try {
-                StdConnections.getStdout().writeString("context: " + contextId, true);
+                if (info == null) {
+                    StdConnections.getStdout().writeString("obsolete context: " + contextId, true);
+                } else {
+                    StdConnections.getStdout().writeString("context: " + contextId, true);
+                }
                 return RNull.instance;
             } catch (IOException ex) {
                 throw RError.error(this, RError.Message.GENERIC, ex.getMessage());
@@ -67,11 +73,12 @@ public class FastRContext {
 
     public abstract static class Spawn extends RExternalBuiltinNode.Arg2 {
         @Specialization
+        @TruffleBoundary
         protected RNull eval(RIntVector contexts, RAbstractStringVector exprs) {
             RContext.EvalThread[] threads = new RContext.EvalThread[contexts.getLength()];
             for (int i = 0; i < threads.length; i++) {
-                RContext context = checkContext(contexts.getDataAt(i), this);
-                threads[i] = new RContext.EvalThread(context, Source.fromText(exprs.getDataAt(i % threads.length), "<context_eval>"));
+                ContextInfo info = checkContext(contexts.getDataAt(i), this);
+                threads[i] = new RContext.EvalThread(info, Source.fromText(exprs.getDataAt(i % threads.length), "<context_eval>"));
             }
             for (int i = 0; i < threads.length; i++) {
                 threads[i].start();
@@ -85,12 +92,12 @@ public class FastRContext {
         protected RNull eval(RIntVector contexts) {
             try {
                 for (int i = 0; i < contexts.getLength(); i++) {
-                    RContext context = RContext.find(contexts.getDataAt(i));
-                    if (context == null) {
+                    Thread thread = RContext.EvalThread.threads.get(contexts.getDataAt(i));
+                    if (thread == null) {
                         // already done
                         continue;
                     } else {
-                        context.joinThread();
+                        thread.join();
                     }
                 }
             } catch (InterruptedException ex) {
@@ -103,12 +110,13 @@ public class FastRContext {
 
     public abstract static class Eval extends RExternalBuiltinNode.Arg3 {
         @Specialization
+        @TruffleBoundary
         protected RNull eval(RIntVector contexts, RAbstractStringVector exprs, byte par) {
             if (RRuntime.fromLogical(par)) {
                 RContext.EvalThread[] threads = new RContext.EvalThread[contexts.getLength()];
                 for (int i = 0; i < threads.length; i++) {
-                    RContext context = checkContext(contexts.getDataAt(i), this);
-                    threads[i] = new RContext.EvalThread(context, Source.fromText(exprs.getDataAt(i % threads.length), "<context_eval>"));
+                    ContextInfo info = checkContext(contexts.getDataAt(i), this);
+                    threads[i] = new RContext.EvalThread(info, Source.fromText(exprs.getDataAt(i % threads.length), "<context_eval>"));
                 }
                 for (int i = 0; i < threads.length; i++) {
                     threads[i].start();
@@ -122,12 +130,14 @@ public class FastRContext {
                 }
             } else {
                 for (int i = 0; i < contexts.getLength(); i++) {
-                    RContext context = checkContext(contexts.getDataAt(i), this);
+                    ContextInfo info = checkContext(contexts.getDataAt(i), this);
+                    TruffleVM vm = info.newContext();
                     try {
-                        context.activate();
-                        context.getThisEngine().parseAndEval(Source.fromText(exprs.getDataAt(i), "<context_eval>"), true, false);
+                        vm.eval("application/x-r", exprs.getDataAt(i));
+                    } catch (IOException e) {
+                        throw RInternalError.shouldNotReachHere(e);
                     } finally {
-                        context.destroy();
+                        RContext.destroyContext(vm);
                     }
                 }
             }
@@ -135,12 +145,12 @@ public class FastRContext {
         }
     }
 
-    private static RContext checkContext(int contextId, RBaseNode invokingNode) throws RError {
-        RContext context = RContext.find(contextId);
-        if (context == null) {
+    private static ContextInfo checkContext(int contextId, RBaseNode invokingNode) throws RError {
+        ContextInfo info = ContextInfo.get(contextId);
+        if (info == null) {
             throw RError.error(invokingNode, RError.Message.GENERIC, "no context: " + contextId);
         } else {
-            return context;
+            return info;
         }
     }
 
@@ -155,6 +165,7 @@ public class FastRContext {
 
     public abstract static class CreateChannel extends RExternalBuiltinNode.Arg1 {
         @Specialization
+        @TruffleBoundary
         protected int createChannel(RAbstractVector key) {
             validateChannelArg(this, key);
             return RChannel.createChannel(((RAbstractIntVector) key).getDataAt(0));
@@ -163,6 +174,7 @@ public class FastRContext {
 
     public abstract static class GetChannel extends RExternalBuiltinNode.Arg1 {
         @Specialization
+        @TruffleBoundary
         protected int getChannel(RAbstractVector key) {
             validateChannelArg(this, key);
             return RChannel.getChannel(((RAbstractIntVector) key).getDataAt(0));
@@ -171,6 +183,7 @@ public class FastRContext {
 
     public abstract static class CloseChannel extends RExternalBuiltinNode.Arg1 {
         @Specialization
+        @TruffleBoundary
         protected RNull getChannel(RAbstractVector id) {
             validateChannelArg(this, id);
             RChannel.closeChannel(((RAbstractIntVector) id).getDataAt(0));
@@ -180,6 +193,7 @@ public class FastRContext {
 
     public abstract static class ChannelSend extends RExternalBuiltinNode.Arg2 {
         @Specialization
+        @TruffleBoundary
         protected RNull send(RAbstractVector id, Object data) {
             validateChannelArg(this, id);
             RChannel.send(((RAbstractIntVector) id).getDataAt(0), data);
@@ -189,6 +203,7 @@ public class FastRContext {
 
     public abstract static class ChannelReceive extends RExternalBuiltinNode.Arg1 {
         @Specialization
+        @TruffleBoundary
         protected Object receive(RAbstractVector id) {
             validateChannelArg(this, id);
             return RChannel.receive(((RAbstractIntVector) id).getDataAt(0));
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastr/FastRStackTrace.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastr/FastRStackTrace.java
index c545129a94f5fbfe4c0a80ca395e18fdf09ba63c..d477d07a42850890f62fee67563e99852ddf7264 100644
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastr/FastRStackTrace.java
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastr/FastRStackTrace.java
@@ -25,6 +25,7 @@ package com.oracle.truffle.r.library.fastr;
 import com.oracle.truffle.api.dsl.*;
 import com.oracle.truffle.r.nodes.builtin.*;
 import com.oracle.truffle.r.runtime.*;
+import com.oracle.truffle.r.runtime.context.*;
 import com.oracle.truffle.r.runtime.data.*;
 
 public abstract class FastRStackTrace extends RExternalBuiltinNode.Arg1 {
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastr/InteropExport.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastr/InteropExport.java
new file mode 100644
index 0000000000000000000000000000000000000000..b540f61ccfb335df594d93d3892050ca8ad0b5d9
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastr/InteropExport.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2015, 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.library.fastr;
+
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.dsl.*;
+import com.oracle.truffle.api.interop.*;
+import com.oracle.truffle.r.nodes.builtin.*;
+import com.oracle.truffle.r.runtime.*;
+import com.oracle.truffle.r.runtime.context.*;
+import com.oracle.truffle.r.runtime.data.*;
+
+public abstract class InteropExport extends RExternalBuiltinNode.Arg2 {
+
+    @Specialization
+    @TruffleBoundary
+    protected Object debugSource(Object name, RTypedValue value) {
+        String stringName = RRuntime.asString(name);
+        if (stringName == null) {
+            throw RError.error(this, RError.Message.INVALID_ARG_TYPE, "name");
+        }
+        if (value instanceof TruffleObject) {
+            RContext.getInstance().getExportedSymbols().put(stringName, (TruffleObject) value);
+        } else {
+            throw RError.error(this, RError.Message.NO_INTEROP, "value", value.getClass().getSimpleName());
+        }
+        return RNull.instance;
+    }
+}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastr/InteropImport.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastr/InteropImport.java
new file mode 100644
index 0000000000000000000000000000000000000000..59781981ac924495c2fa586c76c7114f53279698
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastr/InteropImport.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2015, 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.library.fastr;
+
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.dsl.*;
+import com.oracle.truffle.r.nodes.builtin.*;
+import com.oracle.truffle.r.runtime.*;
+import com.oracle.truffle.r.runtime.context.*;
+
+public abstract class InteropImport extends RExternalBuiltinNode.Arg1 {
+
+    @Specialization
+    @TruffleBoundary
+    protected Object debugSource(Object name) {
+        String stringName = RRuntime.asString(name);
+        if (stringName == null) {
+            throw RError.error(this, RError.Message.INVALID_ARG_TYPE, "name");
+        }
+        Object object = RContext.getInstance().getEnv().importSymbol(stringName);
+        if (object == null) {
+            throw RError.error(this, RError.Message.NO_IMPORT_OBJECT, stringName);
+        }
+        return object;
+    }
+}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/RGraphics.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/RGraphics.java
index 72ec09640886fd7330ecc5ee0a1e56b9e48c0627..9bf7e1daa608cdcb6fcf3c8985c66d0aa1a791c5 100644
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/RGraphics.java
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/RGraphics.java
@@ -15,8 +15,7 @@
 package com.oracle.truffle.r.library.graphics;
 
 import com.oracle.truffle.r.library.graphics.core.*;
-import com.oracle.truffle.r.runtime.*;
-import com.oracle.truffle.r.runtime.RContext.*;
+import com.oracle.truffle.r.runtime.context.*;
 import com.oracle.truffle.r.runtime.data.*;
 import com.oracle.truffle.r.runtime.env.*;
 
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/methods/MethodsListDispatch.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/methods/MethodsListDispatch.java
index 2cd94ebed16774ba854bd346aa4259c773c98303..ec801c5738807ea575b3516daf8418ec46b04bb5 100644
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/methods/MethodsListDispatch.java
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/methods/MethodsListDispatch.java
@@ -11,10 +11,11 @@
  */
 package com.oracle.truffle.r.library.methods;
 
-import com.oracle.truffle.api.CompilerDirectives.*;
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.*;
 import com.oracle.truffle.r.nodes.builtin.*;
 import com.oracle.truffle.r.runtime.*;
+import com.oracle.truffle.r.runtime.context.*;
 import com.oracle.truffle.r.runtime.data.*;
 import com.oracle.truffle.r.runtime.data.model.*;
 import com.oracle.truffle.r.runtime.env.*;
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/utils/Menu.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/utils/Menu.java
index 3128645a2b09504dbdc3dda59c488ad89b22cdc5..3ab8aedea9a9d78243d48122d9bf2d59f03faab0 100644
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/utils/Menu.java
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/utils/Menu.java
@@ -15,7 +15,7 @@ import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.*;
 import com.oracle.truffle.r.nodes.builtin.*;
 import com.oracle.truffle.r.runtime.*;
-import com.oracle.truffle.r.runtime.RContext.ConsoleHandler;
+import com.oracle.truffle.r.runtime.context.*;
 import com.oracle.truffle.r.runtime.data.model.*;
 
 // Translated from GnuR: library/utils/io.c
diff --git a/com.oracle.truffle.r.native/library/fastr/src/NAMESPACE b/com.oracle.truffle.r.native/library/fastr/src/NAMESPACE
index ab0090b5af4eab4bbc16cd707eb132c49d9c865c..18ff461a6906f32a5728bb96e1c6c193c9761ddc 100644
--- a/com.oracle.truffle.r.native/library/fastr/src/NAMESPACE
+++ b/com.oracle.truffle.r.native/library/fastr/src/NAMESPACE
@@ -1,4 +1,6 @@
 ## exported functions
+export(Interop.import)
+export(Interop.export)
 export(fastr.createcc)
 export(fastr.getcc)
 export(fastr.compile)
diff --git a/com.oracle.truffle.r.native/library/fastr/src/R/fastr.R b/com.oracle.truffle.r.native/library/fastr/src/R/fastr.R
index d782c83b104bdb686dec7a1dffce65a3f161c9c7..93883096c09f91c26293da88490853b09d58038b 100644
--- a/com.oracle.truffle.r.native/library/fastr/src/R/fastr.R
+++ b/com.oracle.truffle.r.native/library/fastr/src/R/fastr.R
@@ -21,6 +21,10 @@
 # questions.
 #
 
+Interop.import <- function(name) invisible(.FastR(.NAME="Interop.import", name))
+
+Interop.export <- function(name, value) invisible(.FastR(.NAME="Interop.export", name, value))
+
 fastr.createcc <- function(func) invisible(.FastR(.NAME="createcc", func))
 
 fastr.getcc <- function(func) .FastR(.NAME="getcc", func)
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/RBuiltinPackage.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/RBuiltinPackage.java
index 70c5d407d1a25036ade83ece7b88aa68eebcbf33..27b3079b8e1c23aae48502853e1b2d6bd805cd12 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/RBuiltinPackage.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/RBuiltinPackage.java
@@ -29,6 +29,8 @@ import com.oracle.truffle.api.frame.*;
 import com.oracle.truffle.api.source.*;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinFactory.NodeGenFactory;
 import com.oracle.truffle.r.runtime.*;
+import com.oracle.truffle.r.runtime.context.*;
+import com.oracle.truffle.r.runtime.context.Engine.ParseException;
 import com.oracle.truffle.r.runtime.env.*;
 
 /**
@@ -139,7 +141,11 @@ public abstract class RBuiltinPackage {
         ArrayList<Source> sources = rSources.get(getName());
         if (sources != null) {
             for (Source source : sources) {
-                RContext.getEngine().parseAndEval(source, frame, false, false);
+                try {
+                    RContext.getEngine().parseAndEval(source, frame, false);
+                } catch (ParseException e) {
+                    throw new RInternalError(e, "error while parsing overrides from %s", source.getName());
+                }
             }
         }
     }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/RBuiltinPackages.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/RBuiltinPackages.java
index 34ba07e67d34cd4a1665e7a168b66b3efb8dc92e..1d10d77bf0acacb9660a2ec677274e4f59107011 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/RBuiltinPackages.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/RBuiltinPackages.java
@@ -31,6 +31,8 @@ import com.oracle.truffle.api.frame.*;
 import com.oracle.truffle.api.source.*;
 import com.oracle.truffle.r.nodes.builtin.base.*;
 import com.oracle.truffle.r.runtime.*;
+import com.oracle.truffle.r.runtime.context.*;
+import com.oracle.truffle.r.runtime.context.Engine.ParseException;
 import com.oracle.truffle.r.runtime.data.*;
 import com.oracle.truffle.r.runtime.data.model.*;
 import com.oracle.truffle.r.runtime.env.*;
@@ -95,7 +97,11 @@ public final class RBuiltinPackages implements RBuiltinLookup {
         // Any RBuiltinKind.SUBSTITUTE functions installed above should not be overridden
         try {
             RContext.getInstance().setLoadingBase(true);
-            RContext.getEngine().parseAndEval(baseSource, frame, false, false);
+            try {
+                RContext.getEngine().parseAndEval(baseSource, frame, false);
+            } catch (ParseException e) {
+                throw new RInternalError(e, "error while parsing base source from %s", baseSource.getName());
+            }
         } finally {
             RContext.getInstance().setLoadingBase(false);
         }
@@ -103,7 +109,7 @@ public final class RBuiltinPackages implements RBuiltinLookup {
     }
 
     public static void loadDefaultPackageOverrides() {
-        Object defaultPackages = RContext.getROptionsState().getValue("defaultPackages");
+        Object defaultPackages = RContext.getInstance().stateROptions.getValue("defaultPackages");
         if (defaultPackages instanceof RAbstractStringVector) {
             RAbstractStringVector defPkgs = (RAbstractStringVector) defaultPackages;
             for (int i = 0; i < defPkgs.getLength(); i++) {
@@ -118,16 +124,25 @@ public final class RBuiltinPackages implements RBuiltinLookup {
                  */
                 REnvironment env = REnvironment.baseEnv();
                 for (Source source : componentList) {
-                    RContext.getEngine().parseAndEval(source, env.getFrame(), false, false);
+                    try {
+                        RContext.getEngine().parseAndEval(source, env.getFrame(), false);
+                    } catch (ParseException e) {
+                        throw new RInternalError(e, "error while parsing default package override from %s", source.getName());
+                    }
                 }
             }
         }
     }
 
+    /**
+     * Global builtin cache.
+     */
+    private static final HashMap<Object, RFunction> cachedBuiltinFunctions = new HashMap<>();
+
     @Override
     public RFunction lookupBuiltin(String methodName) {
         CompilerAsserts.neverPartOfCompilation();
-        RFunction function = RContext.getCachedBuiltin(methodName);
+        RFunction function = cachedBuiltinFunctions.get(methodName);
         if (function != null) {
             return function;
         }
@@ -142,7 +157,9 @@ public final class RBuiltinPackages implements RBuiltinLookup {
     private static RFunction createFunction(RBuiltinFactory builtinFactory, String methodName) {
         try {
             RootCallTarget callTarget = RBuiltinNode.createArgumentsCallTarget(builtinFactory);
-            return RContext.cacheBuiltin(methodName, RDataFactory.createFunction(builtinFactory.getName(), callTarget, builtinFactory, REnvironment.baseEnv().getFrame(), false));
+            RFunction function = RDataFactory.createFunction(builtinFactory.getName(), callTarget, builtinFactory, REnvironment.baseEnv().getFrame(), false);
+            cachedBuiltinFunctions.put(methodName, function);
+            return function;
         } catch (Throwable t) {
             throw new RuntimeException("error while creating builtin " + methodName + " / " + builtinFactory, t);
         }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BrowserFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BrowserFunctions.java
index 50c30abeee51ac9bf069b35c6406f61204a15a05..211b97031e21e51d3b255b8439c547780526ff59 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BrowserFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BrowserFunctions.java
@@ -31,6 +31,7 @@ import com.oracle.truffle.r.nodes.builtin.*;
 import com.oracle.truffle.r.nodes.instrument.debug.*;
 import com.oracle.truffle.r.runtime.*;
 import com.oracle.truffle.r.runtime.RError.Message;
+import com.oracle.truffle.r.runtime.context.*;
 import com.oracle.truffle.r.runtime.data.*;
 import com.oracle.truffle.r.runtime.env.*;
 
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Cat.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Cat.java
index 5b1d9b3d0b31d9b2f78e858d887f3fed7cd1f4c8..f15aa93ed9df348990acfb6482acd06bf0e05c3b 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Cat.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Cat.java
@@ -34,6 +34,7 @@ import com.oracle.truffle.r.nodes.builtin.*;
 import com.oracle.truffle.r.nodes.unary.*;
 import com.oracle.truffle.r.runtime.*;
 import com.oracle.truffle.r.runtime.conn.*;
+import com.oracle.truffle.r.runtime.context.*;
 import com.oracle.truffle.r.runtime.data.*;
 import com.oracle.truffle.r.runtime.data.model.*;
 
@@ -87,7 +88,7 @@ public abstract class Cat extends RInvisibleBuiltinNode {
         checkFillLength(fill);
         int fillWidth = -1;
         if (RRuntime.fromLogical(fill.getDataAt(0))) {
-            fillWidth = ((RIntVector) RContext.getROptionsState().getValue("width")).getDataAt(0);
+            fillWidth = ((RIntVector) RContext.getInstance().stateROptions.getValue("width")).getDataAt(0);
         }
         return output(args, conn, sepVec, fillWidth, checkLabels(labels), append);
     }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/CommandArgs.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/CommandArgs.java
index d09b54b706805e0e708c7650568ca2161360cd22..f51aec4d57980e80f26828a19bfb981af9bfd456 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/CommandArgs.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/CommandArgs.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2015, 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
@@ -28,6 +28,7 @@ import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.*;
 import com.oracle.truffle.r.nodes.builtin.*;
 import com.oracle.truffle.r.runtime.*;
+import com.oracle.truffle.r.runtime.context.*;
 import com.oracle.truffle.r.runtime.data.*;
 
 @RBuiltin(name = "commandArgs", kind = INTERNAL, parameterNames = {})
@@ -41,7 +42,7 @@ public abstract class CommandArgs extends RBuiltinNode {
 
     @TruffleBoundary
     private static RStringVector getCommandArgs() {
-        String[] s = RContext.getInstance().getCommandArgs();
+        String[] s = RContext.getInstance().getOptions().getArguments();
         return RDataFactory.createStringVector(s, RDataFactory.COMPLETE_VECTOR);
     }
 
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ConnectionFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ConnectionFunctions.java
index d2c75c5d3e1e9a9d338477f485203e4b750070d1..d8df9077ee2d91490f397bccfd3655049b256c24 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ConnectionFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ConnectionFunctions.java
@@ -44,6 +44,7 @@ import com.oracle.truffle.r.runtime.conn.*;
 import com.oracle.truffle.r.runtime.conn.SocketConnections.RSocketConnection;
 import com.oracle.truffle.r.runtime.conn.TextConnections.TextRConnection;
 import com.oracle.truffle.r.runtime.conn.URLConnections.URLRConnection;
+import com.oracle.truffle.r.runtime.context.*;
 import com.oracle.truffle.r.runtime.data.*;
 import com.oracle.truffle.r.runtime.data.model.*;
 import com.oracle.truffle.r.runtime.env.*;
@@ -266,9 +267,9 @@ public abstract class ConnectionFunctions {
         protected RList summary(Object object) {
             BaseRConnection baseCon;
             if (object instanceof Integer) {
-                baseCon = RContext.getRConnectionState().getConnection((int) object);
+                baseCon = RContext.getInstance().stateRConnection.getConnection((int) object);
             } else if (object instanceof Double) {
-                baseCon = RContext.getRConnectionState().getConnection((int) Math.floor((Double) object));
+                baseCon = RContext.getInstance().stateRConnection.getConnection((int) Math.floor((Double) object));
             } else {
                 RConnection con = checkIsConnection(object);
                 baseCon = getBaseConnection(con);
@@ -943,7 +944,7 @@ public abstract class ConnectionFunctions {
         @TruffleBoundary
         protected RConnection getConnection(int what) {
             controlVisibility();
-            BaseRConnection con = RContext.getRConnectionState().getConnection(what);
+            BaseRConnection con = RContext.getInstance().stateRConnection.getConnection(what);
             if (con == null) {
                 throw RError.error(this, RError.Message.NO_SUCH_CONNECTION, what);
             } else {
@@ -958,7 +959,7 @@ public abstract class ConnectionFunctions {
         @TruffleBoundary
         protected RIntVector getAllConnections() {
             controlVisibility();
-            return RContext.getRConnectionState().getAllConnections();
+            return RContext.getInstance().stateRConnection.getAllConnections();
         }
     }
 
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DatePOSIXFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DatePOSIXFunctions.java
index a2ac1cedaf928417c28c02647380871588a32e44..cb86d84657c0b46579888d9616e8d09bbf891d8d 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DatePOSIXFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DatePOSIXFunctions.java
@@ -21,6 +21,7 @@ import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.*;
 import com.oracle.truffle.r.nodes.builtin.*;
 import com.oracle.truffle.r.runtime.*;
+import com.oracle.truffle.r.runtime.context.*;
 import com.oracle.truffle.r.runtime.data.*;
 import com.oracle.truffle.r.runtime.data.model.*;
 
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DuplicatedFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DuplicatedFunctions.java
index c33c9cb1d4000e17aeb62159f14b7cda1ac47f9d..0b6c8bd9a8762b2b291574dd9538e42277f9e466 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DuplicatedFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DuplicatedFunctions.java
@@ -191,5 +191,11 @@ public class DuplicatedFunctions {
         protected int anyDuplicatedEmpty(RAbstractContainer x, RAbstractContainer incomparables, byte fromLast) {
             return 0;
         }
+
+        @SuppressWarnings("unused")
+        @Specialization
+        protected int anyDuplicatedNull(RNull x, RAbstractContainer incomparables, byte fromLast) {
+            return 0;
+        }
     }
 }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/EvalFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/EvalFunctions.java
index 8d701b7251b224c6acfd15f5a3f32d847674053b..790ff3db820762373764493107fab58f06337d16 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/EvalFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/EvalFunctions.java
@@ -24,16 +24,16 @@ package com.oracle.truffle.r.nodes.builtin.base;
 
 import static com.oracle.truffle.r.runtime.RBuiltinKind.*;
 
-import com.oracle.truffle.api.CompilerDirectives.*;
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.*;
 import com.oracle.truffle.api.frame.*;
 import com.oracle.truffle.r.nodes.*;
 import com.oracle.truffle.r.nodes.builtin.*;
 import com.oracle.truffle.r.runtime.*;
+import com.oracle.truffle.r.runtime.context.*;
 import com.oracle.truffle.r.runtime.data.*;
-import com.oracle.truffle.r.runtime.nodes.*;
 import com.oracle.truffle.r.runtime.env.*;
-import com.oracle.truffle.r.runtime.env.REnvironment.*;
+import com.oracle.truffle.r.runtime.nodes.*;
 
 /**
  * The {@code eval} {@code .Internal} and the {@code withVisible} {@code .Primitive}.
@@ -46,18 +46,10 @@ public class EvalFunctions {
         protected Object doEvalBody(int depth, Object exprArg, REnvironment envir, REnvironment enclos) {
             Object expr = RASTUtils.checkForRSymbol(exprArg);
 
-            if (RASTUtils.isLanguageOrExpression(expr)) {
-                try {
-                    Object result;
-                    if (expr instanceof RExpression) {
-                        result = RContext.getEngine().eval((RExpression) expr, envir, enclos, depth);
-                    } else {
-                        result = RContext.getEngine().eval((RLanguage) expr, envir, enclos, depth);
-                    }
-                    return result;
-                } catch (PutException ex) {
-                    throw RError.error(this, ex);
-                }
+            if (expr instanceof RExpression) {
+                return RContext.getEngine().eval((RExpression) expr, envir, enclos, depth);
+            } else if (expr instanceof RLanguage) {
+                return RContext.getEngine().eval((RLanguage) expr, envir, enclos, depth);
             } else {
                 // just return value
                 return expr;
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Format.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Format.java
index 9e8604f557dd4a04d46ec2310d1d1b6617cb33fa..2282d892211616466be0632e643360ca2f34c0f3 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Format.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Format.java
@@ -20,6 +20,7 @@ import com.oracle.truffle.api.utilities.*;
 import com.oracle.truffle.r.nodes.builtin.*;
 import com.oracle.truffle.r.nodes.unary.*;
 import com.oracle.truffle.r.runtime.*;
+import com.oracle.truffle.r.runtime.context.*;
 import com.oracle.truffle.r.runtime.data.*;
 import com.oracle.truffle.r.runtime.data.model.*;
 
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/FrameFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/FrameFunctions.java
index d546d2e65d6dda34810b8faa7f1276c98423da3c..d3461965d2a0cb1ea5adceceb28af77a60f0ccbe 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/FrameFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/FrameFunctions.java
@@ -40,8 +40,9 @@ import com.oracle.truffle.r.nodes.function.PromiseHelperNode.PromiseDeoptimizeFr
 import com.oracle.truffle.r.nodes.function.PromiseNode.VarArgNode;
 import com.oracle.truffle.r.nodes.function.PromiseNode.VarArgsPromiseNode;
 import com.oracle.truffle.r.runtime.*;
+import com.oracle.truffle.r.runtime.context.*;
 import com.oracle.truffle.r.runtime.data.*;
-import com.oracle.truffle.r.runtime.data.RPromise.*;
+import com.oracle.truffle.r.runtime.data.RPromise.Closure;
 import com.oracle.truffle.r.runtime.env.*;
 import com.oracle.truffle.r.runtime.gnur.*;
 import com.oracle.truffle.r.runtime.nodes.*;
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/HiddenInternalFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/HiddenInternalFunctions.java
index 6ad991cd42e2fa12fd9cafab5efa0951d5d25186..d34e75930e6fa893264168cddc0b7f1ad2a02a73 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/HiddenInternalFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/HiddenInternalFunctions.java
@@ -30,10 +30,11 @@ import com.oracle.truffle.r.nodes.runtime.*;
 import com.oracle.truffle.r.nodes.unary.*;
 import com.oracle.truffle.r.runtime.*;
 import com.oracle.truffle.r.runtime.RError.Message;
+import com.oracle.truffle.r.runtime.context.*;
 import com.oracle.truffle.r.runtime.data.*;
 import com.oracle.truffle.r.runtime.data.model.*;
 import com.oracle.truffle.r.runtime.env.*;
-import com.oracle.truffle.r.runtime.env.REnvironment.*;
+import com.oracle.truffle.r.runtime.env.REnvironment.PutException;
 import com.oracle.truffle.r.runtime.ffi.*;
 import com.oracle.truffle.r.runtime.nodes.*;
 
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/InfixEmulationFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/InfixEmulationFunctions.java
index 739ba519a5fb4ec15221491340903ad42451f44d..4e19b00adffb2b361dc4f17b7e35f2468fff359c 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/InfixEmulationFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/InfixEmulationFunctions.java
@@ -35,6 +35,7 @@ import com.oracle.truffle.api.utilities.*;
 import com.oracle.truffle.r.nodes.access.*;
 import com.oracle.truffle.r.nodes.access.array.read.*;
 import com.oracle.truffle.r.nodes.access.array.write.*;
+import com.oracle.truffle.r.nodes.access.vector.*;
 import com.oracle.truffle.r.nodes.builtin.*;
 import com.oracle.truffle.r.nodes.builtin.base.InfixEmulationFunctionsFactory.PromiseEvaluatorNodeGen;
 import com.oracle.truffle.r.nodes.function.*;
@@ -178,8 +179,11 @@ public class InfixEmulationFunctions {
 
     public abstract static class AccessArrayBuiltin extends RBuiltinNode {
 
+        // old
         @Child private AccessArrayNode accessNode;
         @Child private AccessPositions positions;
+        // new
+        @Child private ExtractVectorNode extractNode;
 
         @Override
         protected void createCasts(CastBuilder casts) {
@@ -189,16 +193,27 @@ public class InfixEmulationFunctions {
         protected abstract boolean isSubset();
 
         protected Object access(Object vector, byte exact, RArgsValuesAndNames inds, Object dropDim) {
-            if (accessNode == null || positions.getLength() != inds.getLength()) {
-                CompilerDirectives.transferToInterpreterAndInvalidate();
-                if (accessNode == null) {
-                    accessNode = insert(AccessArrayNodeGen.create(isSubset(), false, false, null, null, null, null, null));
+            Object result;
+            if (ExtractVectorNode.USE_NODE) {
+                if (extractNode == null) {
+                    CompilerDirectives.transferToInterpreterAndInvalidate();
+                    extractNode = insert(ExtractVectorNode.create(isSubset() ? ElementAccessMode.SUBSET : ElementAccessMode.SUBSCRIPT));
                 }
-                int len = inds.getLength();
-                positions = insert(AccessPositions.create(isSubset(), len));
+                result = extractNode.apply(vector, inds.getArguments(), RLogical.valueOf(exact), dropDim);
+            } else {
+                if (accessNode == null || positions.getLength() != inds.getLength()) {
+                    CompilerDirectives.transferToInterpreterAndInvalidate();
+                    if (accessNode == null) {
+                        accessNode = insert(AccessArrayNodeGen.create(isSubset(), false, false, null, null, null, null, null));
+                    }
+                    int len = inds.getLength();
+                    positions = insert(AccessPositions.create(isSubset(), len));
+                }
+                Object[] pos = inds.getArguments();
+                result = accessNode.executeAccess(vector, exact, 0, positions.execute(vector, pos, exact, pos), dropDim);
             }
-            Object[] pos = inds.getArguments();
-            return accessNode.executeAccess(vector, exact, 0, positions.execute(vector, pos, exact, pos), dropDim);
+            return result;
+
         }
 
         protected boolean noInd(RArgsValuesAndNames inds) {
@@ -273,6 +288,10 @@ public class InfixEmulationFunctions {
         }
 
         protected Object getInternal(Object x, RArgsValuesAndNames inds, RAbstractLogicalVector exactVec) {
+            /*
+             * TODO this should not be handled here. The new vector access nodes handle this, remove
+             * this check as soon as its the default and the old implementation is gone.
+             */
             byte exact;
             if (emptyExactProfile.profile(exactVec.getLength() == 0)) {
                 exact = RRuntime.LOGICAL_FALSE;
@@ -323,6 +342,7 @@ public class InfixEmulationFunctions {
     public abstract static class UpdateArrayBuiltin extends RBuiltinNode {
 
         @Child private UpdateArrayHelperNode updateNode;
+        @Child private ReplaceVectorNode replaceNode;
         @Child private UpdatePositions positions;
         @Child private CoerceVector coerceVector;
 
@@ -330,24 +350,40 @@ public class InfixEmulationFunctions {
         private final ConditionProfile argsLengthLargerThanOneProfile = ConditionProfile.createBinaryProfile();
 
         protected Object update(Object vector, RArgsValuesAndNames args, Object value, boolean isSubset) {
-            int len = argsLengthOneProfile.profile(args.getLength() == 1) ? 1 : args.getLength() - 1;
-
-            if (updateNode == null || positions.getLength() != len) {
-                CompilerDirectives.transferToInterpreterAndInvalidate();
-                if (updateNode == null) {
-                    updateNode = insert(UpdateArrayHelperNodeGen.create(isSubset, 0, null, null, null, null));
+            Object result;
+            if (ReplaceVectorNode.USE_NODE) {
+                if (replaceNode == null) {
+                    CompilerDirectives.transferToInterpreterAndInvalidate();
+                    replaceNode = insert(ReplaceVectorNode.create(isSubset ? ElementAccessMode.SUBSET : ElementAccessMode.SUBSCRIPT));
                 }
-                positions = insert(UpdatePositions.create(isSubset, len));
-                coerceVector = insert(CoerceVectorNodeGen.create(null, null, null));
-            }
-            Object[] pos;
-            if (argsLengthLargerThanOneProfile.profile(args.getLength() > 1)) {
-                pos = Arrays.copyOf(args.getArguments(), args.getLength() - 1);
+                Object[] pos;
+                if (argsLengthLargerThanOneProfile.profile(args.getLength() > 1)) {
+                    pos = Arrays.copyOf(args.getArguments(), args.getLength() - 1);
+                } else {
+                    pos = new Object[]{RMissing.instance};
+                }
+                result = replaceNode.apply(vector, pos, value);
             } else {
-                pos = new Object[]{RMissing.instance};
+                int len = argsLengthOneProfile.profile(args.getLength() == 1) ? 1 : args.getLength() - 1;
+
+                if (updateNode == null || positions.getLength() != len) {
+                    CompilerDirectives.transferToInterpreterAndInvalidate();
+                    if (updateNode == null) {
+                        updateNode = insert(UpdateArrayHelperNodeGen.create(isSubset, 0, null, null, null, null));
+                    }
+                    positions = insert(UpdatePositions.create(isSubset, len));
+                    coerceVector = insert(CoerceVectorNodeGen.create(null, null, null));
+                }
+                Object[] pos;
+                if (argsLengthLargerThanOneProfile.profile(args.getLength() > 1)) {
+                    pos = Arrays.copyOf(args.getArguments(), args.getLength() - 1);
+                } else {
+                    pos = new Object[]{RMissing.instance};
+                }
+                Object newPositions = positions.execute(vector, pos, pos, value);
+                result = updateNode.executeUpdate(vector, value, newPositions, coerceVector.executeEvaluated(value, vector, newPositions));
             }
-            Object newPositions = positions.execute(vector, pos, pos, value);
-            return updateNode.executeUpdate(vector, value, newPositions, coerceVector.executeEvaluated(value, vector, newPositions));
+            return result;
         }
 
         @SuppressWarnings("unused")
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Interactive.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Interactive.java
index e5bf51358f6286c9687d6c78f8dee45456f6ce3f..9143345171db8b1edf46550b1ff96fadcc257f4a 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Interactive.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Interactive.java
@@ -25,6 +25,7 @@ package com.oracle.truffle.r.nodes.builtin.base;
 import com.oracle.truffle.api.dsl.*;
 import com.oracle.truffle.r.nodes.builtin.*;
 import com.oracle.truffle.r.runtime.*;
+import com.oracle.truffle.r.runtime.context.*;
 
 @RBuiltin(name = "interactive", kind = RBuiltinKind.PRIMITIVE, parameterNames = {})
 public abstract class Interactive extends RBuiltinNode {
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Internal.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Internal.java
index b3693a4b7ebd0e3da038a1bea9df16c1363c1c76..067a477758c8e611350be46a73de15485e51c804 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Internal.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Internal.java
@@ -36,6 +36,7 @@ import com.oracle.truffle.r.nodes.builtin.*;
 import com.oracle.truffle.r.nodes.function.*;
 import com.oracle.truffle.r.nodes.function.RCallNode.LeafCallNode;
 import com.oracle.truffle.r.runtime.*;
+import com.oracle.truffle.r.runtime.context.*;
 import com.oracle.truffle.r.runtime.data.*;
 import com.oracle.truffle.r.runtime.nodes.*;
 
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Lapply.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Lapply.java
index f161f499c631ebdf56a91f4440119fc78727983b..19e003c76728be88674da6ce224893a0272908de 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Lapply.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Lapply.java
@@ -26,7 +26,8 @@ import com.oracle.truffle.r.nodes.builtin.*;
 import com.oracle.truffle.r.nodes.builtin.base.LapplyNodeGen.LapplyInternalNodeGen;
 import com.oracle.truffle.r.nodes.function.*;
 import com.oracle.truffle.r.runtime.*;
-import com.oracle.truffle.r.runtime.RContext.Engine.ParseException;
+import com.oracle.truffle.r.runtime.context.Engine.ParseException;
+import com.oracle.truffle.r.runtime.context.*;
 import com.oracle.truffle.r.runtime.data.*;
 import com.oracle.truffle.r.runtime.data.model.*;
 import com.oracle.truffle.r.runtime.env.*;
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Match.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Match.java
index 1415094a7a2e28ad3676868b097a7338c9d4c5c2..979f842286b719c8a2b58fc557d50fcdac577c9e 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Match.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Match.java
@@ -347,6 +347,18 @@ public abstract class Match extends RBuiltinNode {
         return match(RClosures.createLogicalToStringVector(x), table, nomatchObj, incomparables);
     }
 
+    @Specialization
+    protected RIntVector match(RAbstractIntVector x, RAbstractStringVector table, Object nomatchObj, Object incomparables) {
+        naCheck.enable(x);
+        return match(RClosures.createIntToStringVector(x), table, nomatchObj, incomparables);
+    }
+
+    @Specialization
+    protected RIntVector match(RAbstractDoubleVector x, RAbstractStringVector table, Object nomatchObj, Object incomparables) {
+        naCheck.enable(x);
+        return match(RClosures.createDoubleToStringVector(x), table, nomatchObj, incomparables);
+    }
+
     @Specialization
     @SuppressWarnings("unused")
     protected RIntVector match(RAbstractLogicalVector x, RAbstractLogicalVector table, Object nomatchObj, Object incomparables) {
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Options.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Options.java
index 680856255c188993e4cb439465dd5eeb6bad95b7..3624eb87eb15013a170e06bd9e3e124d4214cfe7 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Options.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Options.java
@@ -33,6 +33,7 @@ import com.oracle.truffle.r.nodes.builtin.*;
 import com.oracle.truffle.r.runtime.*;
 import com.oracle.truffle.r.runtime.RError.Message;
 import com.oracle.truffle.r.runtime.ROptions.OptionsException;
+import com.oracle.truffle.r.runtime.context.*;
 import com.oracle.truffle.r.runtime.data.*;
 
 @RBuiltin(name = "options", kind = INTERNAL, parameterNames = {"..."})
@@ -48,7 +49,7 @@ public abstract class Options extends RBuiltinNode {
     @Specialization
     protected RList options(@SuppressWarnings("unused") RMissing x) {
         controlVisibility();
-        Set<Map.Entry<String, Object>> optionSettings = RContext.getROptionsState().getValues();
+        Set<Map.Entry<String, Object>> optionSettings = RContext.getInstance().stateROptions.getValues();
         Object[] data = new Object[optionSettings.size()];
         String[] names = new String[data.length];
         int i = 0;
@@ -69,7 +70,7 @@ public abstract class Options extends RBuiltinNode {
     @TruffleBoundary
     protected Object options(RArgsValuesAndNames args) {
         try {
-            ROptions.ContextState options = RContext.getROptionsState();
+            ROptions.ContextStateImpl options = RContext.getInstance().stateROptions;
             Object[] values = args.getArguments();
             ArgumentsSignature signature = args.getSignature();
             Object[] data = new Object[values.length];
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Parse.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Parse.java
index adb5042c175fba3af175ff74896e36820ebf0f3c..f465111a0e5e9fa53ab902e4b914b5fd2aacb86d 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Parse.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Parse.java
@@ -33,8 +33,9 @@ import com.oracle.truffle.api.source.*;
 import com.oracle.truffle.r.nodes.builtin.*;
 import com.oracle.truffle.r.nodes.unary.*;
 import com.oracle.truffle.r.runtime.*;
-import com.oracle.truffle.r.runtime.RContext.Engine.ParseException;
 import com.oracle.truffle.r.runtime.conn.*;
+import com.oracle.truffle.r.runtime.context.Engine.ParseException;
+import com.oracle.truffle.r.runtime.context.*;
 import com.oracle.truffle.r.runtime.data.*;
 import com.oracle.truffle.r.runtime.data.model.*;
 import com.oracle.truffle.r.runtime.env.*;
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/PrettyPrinterNode.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/PrettyPrinterNode.java
index 0185ebe7fce584f0b070558e84fd215eaacc8b22..b746c83368714956b52197f2166640f39a9997e3 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/PrettyPrinterNode.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/PrettyPrinterNode.java
@@ -44,6 +44,7 @@ import com.oracle.truffle.r.nodes.function.*;
 import com.oracle.truffle.r.nodes.unary.*;
 import com.oracle.truffle.r.runtime.*;
 import com.oracle.truffle.r.runtime.conn.*;
+import com.oracle.truffle.r.runtime.context.*;
 import com.oracle.truffle.r.runtime.data.*;
 import com.oracle.truffle.r.runtime.data.RAttributes.RAttribute;
 import com.oracle.truffle.r.runtime.data.closures.*;
@@ -425,7 +426,7 @@ public abstract class PrettyPrinterNode extends RNode {
 
     private static int getMaxPrintLength() {
         int maxPrint = -1; // infinity
-        Object maxPrintObj = RContext.getROptionsState().getValue("max.print");
+        Object maxPrintObj = RContext.getInstance().stateROptions.getValue("max.print");
         if (maxPrintObj != null) {
 
             if (maxPrintObj instanceof Integer) {
@@ -611,7 +612,7 @@ public abstract class PrettyPrinterNode extends RNode {
         if (rowHeaderUsesIndices(dimNames)) {
             return concat("[", intString(c), ",]");
         } else {
-            RStringVector dimNamesVector = (RStringVector) dimNames.getDataAt(0);
+            RAbstractStringVector dimNamesVector = (RAbstractStringVector) getDimNamesAt(dimNames, 1);
             String dimId = dimNamesVector.getDataAt(c - 1);
             if (RRuntime.isNA(dimId)) {
                 dimId = RRuntime.NA_HEADER;
@@ -630,15 +631,23 @@ public abstract class PrettyPrinterNode extends RNode {
     private static String getDimId(RAbstractVector vector, int dimLevel, int dimInd, RAttributeProfiles attrProfiles) {
         String dimId;
         RList dimNames = vector.getDimNames(attrProfiles);
-        if (dimNames == null || dimNames.getDataAt(dimLevel - 1) == RNull.instance) {
+        if (dimNames == null || getDimNamesAt(dimNames, dimLevel) == RNull.instance) {
             dimId = intString(dimInd + 1);
         } else {
-            RStringVector dimNamesVector = (RStringVector) dimNames.getDataAt(dimLevel - 1);
+            RAbstractStringVector dimNamesVector = (RAbstractStringVector) getDimNamesAt(dimNames, dimLevel);
             dimId = dimNamesVector.getDataAt(dimInd);
         }
         return dimId;
     }
 
+    private static Object getDimNamesAt(RList dimNames, int dimLevel) {
+        Object result = dimNames.getDataAt(dimLevel - 1);
+        if (result instanceof String) {
+            return RString.valueOf((String) result);
+        }
+        return result;
+    }
+
     private static double calcRoundFactor(double input, long maxFactor) {
         if (Double.isNaN(input) || Double.isInfinite(input) || input == 0.0) {
             return maxFactor * 10;
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Primitive.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Primitive.java
index d7f3c2736ac618bac2a2817535d822f1623557a3..2fc55db2512244cc29850d1b03baf58b99372d45 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Primitive.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Primitive.java
@@ -28,6 +28,7 @@ import com.oracle.truffle.api.dsl.*;
 import com.oracle.truffle.api.utilities.*;
 import com.oracle.truffle.r.nodes.builtin.*;
 import com.oracle.truffle.r.runtime.*;
+import com.oracle.truffle.r.runtime.context.*;
 import com.oracle.truffle.r.runtime.data.*;
 
 @RBuiltin(name = ".Primitive", kind = PRIMITIVE, parameterNames = "name")
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ProcTime.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ProcTime.java
index b0f6c5fa3948fc1c1a394bd2aaedef14db90b37f..d24720bda8ebbdd78ed043a599ea552ffad53afc 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ProcTime.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ProcTime.java
@@ -30,6 +30,7 @@ import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.*;
 import com.oracle.truffle.r.nodes.builtin.*;
 import com.oracle.truffle.r.runtime.*;
+import com.oracle.truffle.r.runtime.context.*;
 import com.oracle.truffle.r.runtime.data.*;
 
 @RBuiltin(name = "proc.time", kind = PRIMITIVE, parameterNames = {})
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Quit.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Quit.java
index df8889cdda6dcc2c43d3f54b8a70fa4f207eeded..7abe3ad2dd4747a75d5ef950db951f0fa49753e4 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Quit.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Quit.java
@@ -28,6 +28,8 @@ import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.*;
 import com.oracle.truffle.r.nodes.builtin.*;
 import com.oracle.truffle.r.runtime.*;
+import com.oracle.truffle.r.runtime.RCmdOptions.RCmdOption;
+import com.oracle.truffle.r.runtime.context.*;
 import com.oracle.truffle.r.runtime.data.*;
 
 @RBuiltin(name = "quit", kind = INTERNAL, parameterNames = {"save", "status", "runLast"})
@@ -40,13 +42,13 @@ public abstract class Quit extends RInvisibleBuiltinNode {
 
     @Specialization
     @TruffleBoundary
-    protected Object doQuit(final String saveArg, int status, byte runLast) {
+    protected Object doQuit(String saveArg, int status, byte runLast) {
         controlVisibility();
         String save = saveArg;
         // Quit does not divert its output to sink
-        RContext.ConsoleHandler consoleHandler = RContext.getInstance().getConsoleHandler();
+        ConsoleHandler consoleHandler = RContext.getInstance().getConsoleHandler();
         if (save.equals("default")) {
-            if (RCmdOptions.NO_SAVE.getValue()) {
+            if (RContext.getInstance().getOptions().getBoolean(RCmdOption.NO_SAVE)) {
                 save = "no";
             } else {
                 if (consoleHandler.isInteractive()) {
@@ -92,10 +94,8 @@ public abstract class Quit extends RInvisibleBuiltinNode {
             // TODO errors should return to prompt if interactive
             RContext.getEngine().checkAndRunLast(".Last.sys");
         }
-        // destroy the context
-        RContext.getInstance().destroy();
+        // destroy the context inside exit() method as it still needs to access it
         Utils.exit(status);
         return null;
     }
-
 }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ReadREnviron.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ReadREnviron.java
index 09ff05dc08d2501a6b69c1eddfbe320ad9698868..da577263fd61331d03439d52ab327038d98bb8ef 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ReadREnviron.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ReadREnviron.java
@@ -30,6 +30,7 @@ import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.*;
 import com.oracle.truffle.r.nodes.builtin.*;
 import com.oracle.truffle.r.runtime.*;
+import com.oracle.truffle.r.runtime.context.*;
 import com.oracle.truffle.r.runtime.data.model.*;
 
 @RBuiltin(name = "readRenviron", kind = INTERNAL, parameterNames = "path")
@@ -42,7 +43,7 @@ public abstract class ReadREnviron extends RInvisibleBuiltinNode {
         String path = Utils.tildeExpand(vec.getDataAt(0));
         byte result = RRuntime.LOGICAL_TRUE;
         try {
-            REnvVars.readEnvironFile(path);
+            RContext.getInstance().stateREnvVars.readEnvironFile(path);
         } catch (FileNotFoundException ex) {
             RError.warning(this, RError.Message.GENERIC, ex.getMessage());
             result = RRuntime.LOGICAL_FALSE;
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Readline.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Readline.java
index 434b265a8ae08dbc39b23537a9b69bb0cd057152..90e161c0f66dc1025a50778676acea45e023b52e 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Readline.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Readline.java
@@ -25,6 +25,7 @@ package com.oracle.truffle.r.nodes.builtin.base;
 import com.oracle.truffle.api.dsl.*;
 import com.oracle.truffle.r.nodes.builtin.*;
 import com.oracle.truffle.r.runtime.*;
+import com.oracle.truffle.r.runtime.context.*;
 import com.oracle.truffle.r.runtime.data.model.*;
 
 @RBuiltin(name = "readline", kind = RBuiltinKind.INTERNAL, parameterNames = "prompt")
@@ -34,7 +35,7 @@ public abstract class Readline extends RBuiltinNode {
         if (!RContext.getInstance().isInteractive()) {
             return "";
         }
-        RContext.ConsoleHandler consoleHandler = RContext.getInstance().getConsoleHandler();
+        ConsoleHandler consoleHandler = RContext.getInstance().getConsoleHandler();
         String savedPrompt = consoleHandler.getPrompt();
         consoleHandler.setPrompt(prompt.getDataAt(0));
         String input = consoleHandler.readLine();
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/SysFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/SysFunctions.java
index d2ce8f5a27da612593793c34d02e5922c8eb96e2..33de4d8aafc3482e7dd4c929f4f4a2df1f2bf0b2 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/SysFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/SysFunctions.java
@@ -22,21 +22,22 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import static com.oracle.truffle.r.runtime.RBuiltinKind.*;
 
 import java.io.*;
 import java.util.*;
 
 import com.oracle.truffle.api.*;
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.*;
-import com.oracle.truffle.api.utilities.ConditionProfile;
+import com.oracle.truffle.api.utilities.*;
 import com.oracle.truffle.r.nodes.builtin.*;
 import com.oracle.truffle.r.runtime.*;
+import com.oracle.truffle.r.runtime.context.*;
 import com.oracle.truffle.r.runtime.data.*;
 import com.oracle.truffle.r.runtime.data.model.*;
-import com.oracle.truffle.r.runtime.ffi.*;
 import com.oracle.truffle.r.runtime.ffi.BaseRFFI.UtsName;
+import com.oracle.truffle.r.runtime.ffi.*;
 
 public class SysFunctions {
 
@@ -60,7 +61,7 @@ public class SysFunctions {
         @TruffleBoundary
         protected Object sysGetEnv(RAbstractStringVector x, RAbstractStringVector unset) {
             controlVisibility();
-            Map<String, String> envMap = REnvVars.getMap();
+            Map<String, String> envMap = RContext.getInstance().stateREnvVars.getMap();
             int len = x.getLength();
             if (zeroLengthProfile.profile(len == 0)) {
                 String[] data = new String[envMap.size()];
@@ -102,12 +103,13 @@ public class SysFunctions {
     @RBuiltin(name = "Sys.setenv", kind = INTERNAL, parameterNames = {"nm", "values"})
     public abstract static class SysSetEnv extends RInvisibleBuiltinNode {
 
-        @Specialization()
+        @Specialization
         @TruffleBoundary
         protected RLogicalVector doSysSetEnv(RStringVector names, RStringVector values) {
             byte[] data = new byte[names.getLength()];
+            REnvVars stateREnvVars = RContext.getInstance().stateREnvVars;
             for (int i = 0; i < data.length; i++) {
-                REnvVars.put(names.getDataAt(i), values.getDataAt(i));
+                stateREnvVars.put(names.getDataAt(i), values.getDataAt(i));
                 data[i] = RRuntime.LOGICAL_TRUE;
             }
             return RDataFactory.createLogicalVector(data, RDataFactory.COMPLETE_VECTOR);
@@ -122,8 +124,9 @@ public class SysFunctions {
         @TruffleBoundary
         protected RLogicalVector doSysSetEnv(RAbstractStringVector argVec) {
             byte[] data = new byte[argVec.getLength()];
+            REnvVars stateREnvVars = RContext.getInstance().stateREnvVars;
             for (int i = 0; i < data.length; i++) {
-                data[i] = RRuntime.asLogical(REnvVars.unset(argVec.getDataAt(i)));
+                data[i] = RRuntime.asLogical(stateREnvVars.unset(argVec.getDataAt(i)));
             }
             return RDataFactory.createLogicalVector(data, RDataFactory.COMPLETE_VECTOR);
         }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/SystemFunction.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/SystemFunction.java
index c91767c25b35411edf59b3f03c29c7c099b7e140..e050f36c17bb9bb953f4d5e0026b7f5a8ae63e87 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/SystemFunction.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/SystemFunction.java
@@ -23,13 +23,14 @@
 package com.oracle.truffle.r.nodes.builtin.base;
 
 import java.io.*;
-import java.lang.ProcessBuilder.*;
+import java.lang.ProcessBuilder.Redirect;
 import java.util.*;
 
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.*;
 import com.oracle.truffle.r.nodes.builtin.*;
 import com.oracle.truffle.r.runtime.*;
+import com.oracle.truffle.r.runtime.context.*;
 import com.oracle.truffle.r.runtime.data.*;
 import com.oracle.truffle.r.runtime.data.model.*;
 
@@ -40,7 +41,7 @@ public abstract class SystemFunction extends RBuiltinNode {
     public Object system(RAbstractStringVector command, byte internLogical) {
         Object result;
         boolean intern = RRuntime.fromLogical(internLogical);
-        String shell = REnvVars.get("SHELL");
+        String shell = RContext.getInstance().stateREnvVars.get("SHELL");
         if (shell == null) {
             shell = "/bin/sh";
         }
@@ -93,7 +94,7 @@ public abstract class SystemFunction extends RBuiltinNode {
 
     private static void updateEnvironment(ProcessBuilder pb) {
         Map<String, String> pEnv = pb.environment();
-        Map<String, String> rEnv = REnvVars.getMap();
+        Map<String, String> rEnv = RContext.getInstance().stateREnvVars.getMap();
         for (Map.Entry<String, String> entry : rEnv.entrySet()) {
             String name = entry.getKey();
             String value = entry.getValue();
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Xtfrm.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Xtfrm.java
index 4c67af3936af368f14534f1adfed31c748b0e2ce..632d9c5ed2405a84b3daf2b2f5389656efe01d09 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Xtfrm.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Xtfrm.java
@@ -29,8 +29,9 @@ import com.oracle.truffle.api.*;
 import com.oracle.truffle.api.dsl.*;
 import com.oracle.truffle.api.frame.*;
 import com.oracle.truffle.r.nodes.builtin.*;
-import com.oracle.truffle.r.nodes.builtin.base.GetFunctionsFactory.*;
+import com.oracle.truffle.r.nodes.builtin.base.GetFunctionsFactory.GetNodeGen;
 import com.oracle.truffle.r.runtime.*;
+import com.oracle.truffle.r.runtime.context.*;
 import com.oracle.truffle.r.runtime.data.*;
 import com.oracle.truffle.r.runtime.nodes.*;
 
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/FastR.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/FastR.java
index c8f9b77579392aa2d219c2afc57a6cad0b905116..271ecc6d1afb84cc8d4b3a4ecc75ba34cbcc7096 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/FastR.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/FastR.java
@@ -56,6 +56,10 @@ public abstract class FastR extends RBuiltinNode {
 
     protected RExternalBuiltinNode lookupName(RAbstractStringVector name) {
         switch (name.getDataAt(0)) {
+            case "Interop.import":
+                return InteropImportNodeGen.create();
+            case "Interop.export":
+                return InteropExportNodeGen.create();
             case "createcc":
                 return FastRCallCountingFactory.CreateCallCounterNodeGen.create();
             case "getcc":
@@ -102,7 +106,6 @@ public abstract class FastR extends RBuiltinNode {
                 return FastRContextFactory.ChannelSendNodeGen.create();
             case "fastr.channel.receive":
                 return FastRContextFactory.ChannelReceiveNodeGen.create();
-
             default:
                 return null;
         }
diff --git a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/access/vector/ExtractVectorNodeTest.java b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/access/vector/ExtractVectorNodeTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..8f35da8a79f442cebb840fca4b81a81afd40c092
--- /dev/null
+++ b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/access/vector/ExtractVectorNodeTest.java
@@ -0,0 +1,290 @@
+/*
+ * Copyright (c) 2015, 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.access.vector;
+
+import static com.oracle.truffle.r.nodes.test.TestUtilities.*;
+import static org.hamcrest.CoreMatchers.*;
+import static org.hamcrest.MatcherAssert.*;
+import static org.junit.Assume.*;
+
+import org.junit.*;
+import org.junit.experimental.theories.*;
+import org.junit.runner.*;
+
+import com.oracle.truffle.r.nodes.binary.*;
+import com.oracle.truffle.r.nodes.test.*;
+import com.oracle.truffle.r.nodes.test.TestUtilities.NodeHandle;
+import com.oracle.truffle.r.runtime.*;
+import com.oracle.truffle.r.runtime.data.*;
+import com.oracle.truffle.r.runtime.data.model.*;
+
+@RunWith(Theories.class)
+public class ExtractVectorNodeTest extends TestBase {
+
+    @DataPoints public static RType[] vectorTypes = RType.getVectorTypes();
+
+    @DataPoint public static RAbstractVector accessEmpty = RDataFactory.createEmptyLogicalVector();
+    @DataPoint public static RAbstractVector accessFirst = RDataFactory.createIntVector(new int[]{1}, true);
+    @DataPoint public static RAbstractVector accessSecond = RDataFactory.createIntVector(new int[]{2}, true);
+    @DataPoint public static RAbstractVector accessEverythingButFirst = RDataFactory.createIntVector(new int[]{-1}, true);
+    @DataPoint public static RAbstractVector accessNA = RDataFactory.createIntVector(new int[]{RRuntime.INT_NA}, false);
+    @DataPoint public static RAbstractVector accessZero = RDataFactory.createIntVector(new int[]{0}, false);
+    @DataPoint public static RAbstractVector accessFirstTwo = RDataFactory.createIntVector(new int[]{1, 2}, true);
+
+    @DataPoint public static RAbstractVector accessPositiveSequence = RDataFactory.createIntSequence(1, 1, 2);
+    @DataPoint public static RAbstractVector accessPositiveSequenceStride2 = RDataFactory.createIntSequence(1, 2, 2);
+    @DataPoint public static RAbstractVector accessNegativeSequence = RDataFactory.createIntSequence(-1, -1, 2);
+
+    @DataPoints public static ElementAccessMode[] allModes = ElementAccessMode.values();
+
+    @Test
+    public void testSubsetMultiDimension() {
+        RAbstractIntVector vector;
+
+        // replace rectangle with rectangle indices
+        vector = generateInteger(20, true);
+        vector.setDimensions(new int[]{5, 4});
+        vector = executeExtract(ElementAccessMode.SUBSET, vector, //
+                        RDataFactory.createIntVector(new int[]{2, 3, 4}, true), RDataFactory.createIntVector(new int[]{2, 3}, true));
+        assertIndicies(vector, 6, 7, 8, 11, 12, 13);
+
+        // replace box with box indices
+        vector = generateInteger(9, true);
+        vector.setDimensions(new int[]{3, 3});
+        vector = executeExtract(ElementAccessMode.SUBSET, vector, //
+                        RDataFactory.createIntVector(new int[]{2, 3}, true), RDataFactory.createIntVector(new int[]{2, 3}, true));
+        assertIndicies(vector, 4, 5, 7, 8);
+
+        // replace three dimensions
+        vector = generateInteger(24, true);
+        vector.setDimensions(new int[]{2, 3, 4});
+        vector = executeExtract(ElementAccessMode.SUBSET, vector, //
+                        RDataFactory.createIntVector(new int[]{2}, true), RDataFactory.createIntVector(new int[]{2}, true), RDataFactory.createIntVector(new int[]{2}, true));
+        assertIndicies(vector, 9);
+
+        // replace three dimensions
+        vector = generateInteger(24, true);
+        vector.setDimensions(new int[]{2, 3, 4});
+        vector = executeExtract(ElementAccessMode.SUBSET, vector, //
+                        RDataFactory.createIntVector(new int[]{2}, true), RDataFactory.createIntVector(new int[]{2, 3}, true), RDataFactory.createIntVector(new int[]{2, 3, 4}, true));
+        assertIndicies(vector, 9, 11, 15, 17, 21, 23);
+
+    }
+
+    private static void assertIndicies(RAbstractIntVector vector, int... expectedValues) {
+        assertThat(vector.getLength(), is(expectedValues.length));
+
+        int[] actual = new int[vector.getLength()];
+        for (int i = 0; i < expectedValues.length; i++) {
+            actual[i] = vector.getDataAt(i);
+        }
+        assertThat(actual, is(expectedValues));
+    }
+
+    @Test
+    public void testSubsetSingleDimension() {
+        RAbstractIntVector vector;
+
+        // extract scalar with logical vector with NA
+        vector = generateInteger(4, true);
+        vector = executeExtract(ElementAccessMode.SUBSET, vector, //
+                        new Object[]{RDataFactory.createLogicalVector(new byte[]{RRuntime.LOGICAL_TRUE, RRuntime.LOGICAL_NA}, false)});
+        assertIndicies(vector, 0, RRuntime.INT_NA, 2, RRuntime.INT_NA);
+
+        // extract scalar with sequence stride=1
+        vector = generateInteger(9, true);
+        vector = executeExtract(ElementAccessMode.SUBSET, vector, new Object[]{RDataFactory.createIntSequence(5, 1, 3)});
+        assertIndicies(vector, 4, 5, 6);
+
+        // extract scalar with sequence stride>1
+        vector = generateInteger(9, true);
+        vector = executeExtract(ElementAccessMode.SUBSET, vector, new Object[]{RDataFactory.createIntSequence(5, 2, 2)});
+        assertIndicies(vector, 4, 6);
+
+        // extract scalar with negative integer vector
+        vector = generateInteger(4, true);
+        vector = executeExtract(ElementAccessMode.SUBSET, vector, new Object[]{RDataFactory.createIntVector(new int[]{-2}, true)});
+        assertIndicies(vector, 0, 2, 3);
+
+        // extract scalar with logical scalar
+        vector = generateInteger(3, true);
+        vector = executeExtract(ElementAccessMode.SUBSET, vector, //
+                        new Object[]{RDataFactory.createLogicalVector(new byte[]{RRuntime.LOGICAL_TRUE}, true)});
+        assertIndicies(vector, 0, 1, 2);
+
+        // extract scalar with integer vector with NA
+        vector = generateInteger(4, true);
+        vector = executeExtract(ElementAccessMode.SUBSET, vector, //
+                        new Object[]{RDataFactory.createIntVector(new int[]{1, RRuntime.INT_NA}, false)});
+        assertIndicies(vector, 0, RRuntime.INT_NA);
+
+        // extract scalar with logical vector
+        vector = generateInteger(4, true);
+        vector = executeExtract(ElementAccessMode.SUBSET, vector, //
+                        new Object[]{RDataFactory.createLogicalVector(new byte[]{RRuntime.LOGICAL_TRUE, RRuntime.LOGICAL_FALSE}, true)});
+        assertIndicies(vector, 0, 2);
+
+        // extract vector indexed by logical vector
+        vector = generateInteger(4, true);
+        vector = executeExtract(ElementAccessMode.SUBSET, vector, //
+                        new Object[]{RDataFactory.createLogicalVector(new byte[]{RRuntime.LOGICAL_TRUE, RRuntime.LOGICAL_FALSE}, true)});
+        assertIndicies(vector, 0, 2);
+
+        // extract scalar with integer vector
+        vector = generateInteger(9, true);
+        vector = executeExtract(ElementAccessMode.SUBSET, vector, new Object[]{RDataFactory.createIntVector(new int[]{9, 8}, true)});
+        assertIndicies(vector, 8, 7);
+
+        // extract scalar with integer scalar
+        vector = generateInteger(9, true);
+        vector = executeExtract(ElementAccessMode.SUBSET, vector, new Object[]{RDataFactory.createIntVector(new int[]{9}, true)});
+        assertIndicies(vector, 8);
+
+    }
+
+    @Theory
+    public void testNames(RType targetType) {
+        RAbstractVector vector = generateVector(targetType, 4, true);
+
+        RStringVector names = (RStringVector) generateVector(RType.Character, 4, true);
+        vector.setNames(names);
+        RAbstractVector result = executeExtract(ElementAccessMode.SUBSET, vector, RInteger.valueOf(2));
+
+        RStringVector newNames = result.getNames(RAttributeProfiles.create());
+        assertThat(newNames.getLength(), is(1));
+        assertThat(newNames.getDataAt(0), is(names.getDataAt(1)));
+    }
+
+    @Theory
+    public void testOutOfBoundsAccess(RType targetType) {
+        RAbstractVector vector = generateVector(targetType, 4, true);
+
+        RAbstractVector result = executeExtract(ElementAccessMode.SUBSET, vector, RInteger.valueOf(5));
+
+        assertThat(vector.getRType(), is(result.getRType()));
+        assertThat(result.getLength(), is(1));
+        Object expectedValue = targetType.create(1, true).getDataAtAsObject(0);
+        assertThat(result.getDataAtAsObject(0), is(expectedValue));
+    }
+
+    @Theory
+    public void testCompletenessOutOfBounds(RType targetType) {
+        RAbstractVector vector = generateVector(targetType, 4, true);
+
+        assumeTrue(targetType != RType.Raw);
+
+        RAbstractVector result = executeExtract(ElementAccessMode.SUBSET, vector, RInteger.valueOf(10));
+
+        assertThat(result.isComplete(), is(false));
+    }
+
+    @Theory
+    public void testCompletenessAfterExtraction(RType targetType) {
+        RAbstractVector vector = generateVector(targetType, 4, false);
+
+        assumeThat(vector.isComplete(), is(false));
+        RAbstractVector result = executeExtract(ElementAccessMode.SUBSET, vector, RInteger.valueOf(1));
+
+        assertThat(result.isComplete(), is(true));
+    }
+
+    @Theory
+    public void testCompletenessAfterSelectAll(RType targetType) {
+        RAbstractVector vector = generateVector(targetType, 4, false);
+
+        assumeThat(vector.isComplete(), is(false));
+        RAbstractVector result = executeExtract(ElementAccessMode.SUBSET, vector, RLogical.valueOf(true));
+
+        assertThat(result.isComplete(), is(false));
+    }
+
+    @Theory
+    public void testCompletenessPositionNA(RType targetType) {
+        RAbstractVector vector = generateVector(targetType, 4, true);
+
+        RAbstractVector result = executeExtract(ElementAccessMode.SUBSET, vector, RLogical.NA);
+
+        assertThat(result.isComplete(), is(false));
+    }
+
+    @Theory
+    public void testSubsetSingleDimensionTheory(RType targetType, RAbstractVector position) {
+        assumeTrue(position.getLength() <= 4);
+        assumeTrue(position.getLength() >= 1);
+
+        RAbstractVector vector = generateVector(targetType, 4, true);
+
+        executeExtract(ElementAccessMode.SUBSET, vector, position);
+    }
+
+    @Theory
+    public void testSubscriptSingleDimensionTheory(RType targetType, RAbstractVector position) {
+        assumeTrue(position.getLength() == 1);
+        if (position instanceof RAbstractIntVector) {
+            assumeTrue(((RAbstractIntVector) position).getDataAt(0) > 0);
+        }
+
+        RAbstractVector vector = generateVector(targetType, 4, true);
+
+        executeExtract(ElementAccessMode.SUBSCRIPT, vector, position);
+    }
+
+    private NodeHandle<ExtractVectorNode> handle;
+    private NodeHandle<BoxPrimitiveNode> box;
+    private ElementAccessMode currentMode;
+    private boolean currentExact;
+    private boolean currentDropDimension;
+
+    @Before
+    public void setUp() {
+        handle = null;
+    }
+
+    @After
+    public void tearDown() {
+        handle = null;
+    }
+
+    @SuppressWarnings("unchecked")
+    private <T extends RAbstractVector> T executeExtract(ElementAccessMode mode, T vector, Object... positions) {
+        return (T) executeExtract(mode, false, false, vector, positions);
+    }
+
+    private RAbstractVector executeExtract(ElementAccessMode mode, boolean exact, boolean dropDimension, Object vector, Object... positions) {
+        if (handle == null || this.currentMode != mode || currentExact != exact || currentDropDimension != dropDimension) {
+            handle = create(mode, exact, dropDimension);
+            this.currentMode = mode;
+        }
+        if (box == null) {
+            box = createHandle(BoxPrimitiveNodeGen.create(), (node, args) -> node.execute(args[0]));
+        }
+
+        return (RAbstractVector) box.call(handle.call(vector, positions));
+    }
+
+    private static NodeHandle<ExtractVectorNode> create(ElementAccessMode mode, boolean exact, boolean dropDimension) {
+        return createHandle(ExtractVectorNode.create(mode), //
+                        (node, args) -> node.apply(args[0], (Object[]) args[1], RLogical.valueOf(exact), RLogical.valueOf(dropDimension)));
+    }
+
+}
diff --git a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/access/vector/ReplaceVectorNodeTest.java b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/access/vector/ReplaceVectorNodeTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..65760088de1454f166846aa78b75c0b94ed65e0b
--- /dev/null
+++ b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/access/vector/ReplaceVectorNodeTest.java
@@ -0,0 +1,285 @@
+/*
+ * Copyright (c) 2015, 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.access.vector;
+
+import static com.oracle.truffle.r.nodes.test.TestUtilities.*;
+import static org.hamcrest.CoreMatchers.*;
+import static org.hamcrest.MatcherAssert.*;
+import static org.junit.Assume.*;
+
+import org.junit.*;
+import org.junit.experimental.theories.*;
+import org.junit.runner.*;
+
+import com.oracle.truffle.r.nodes.binary.*;
+import com.oracle.truffle.r.nodes.test.*;
+import com.oracle.truffle.r.nodes.test.TestUtilities.NodeHandle;
+import com.oracle.truffle.r.runtime.*;
+import com.oracle.truffle.r.runtime.data.*;
+import com.oracle.truffle.r.runtime.data.model.*;
+
+@RunWith(Theories.class)
+public class ReplaceVectorNodeTest extends TestBase {
+
+    @DataPoints public static RType[] vectorTypes = RType.getVectorTypes();
+
+    @DataPoint public static RAbstractVector accessEmpty = RDataFactory.createEmptyLogicalVector();
+    @DataPoint public static RAbstractVector accessFirst = RDataFactory.createIntVector(new int[]{1}, true);
+    @DataPoint public static RAbstractVector accessSecond = RDataFactory.createIntVector(new int[]{2}, true);
+    @DataPoint public static RAbstractVector accessEverythingButFirst = RDataFactory.createIntVector(new int[]{-1}, true);
+    @DataPoint public static RAbstractVector accessNA = RDataFactory.createIntVector(new int[]{RRuntime.INT_NA}, false);
+    @DataPoint public static RAbstractVector accessZero = RDataFactory.createIntVector(new int[]{0}, false);
+    @DataPoint public static RAbstractVector accessFirstTwo = RDataFactory.createIntVector(new int[]{1, 2}, true);
+
+    @DataPoint public static RAbstractVector accessPositiveSequence = RDataFactory.createIntSequence(1, 1, 2);
+    @DataPoint public static RAbstractVector accessPositiveSequenceStride2 = RDataFactory.createIntSequence(1, 2, 2);
+    @DataPoint public static RAbstractVector accessNegativeSequence = RDataFactory.createIntSequence(-1, -1, 2);
+
+    @DataPoints public static ElementAccessMode[] allModes = ElementAccessMode.values();
+
+    @Test
+    public void testSubsetMultiDimension() {
+        RAbstractIntVector vector;
+
+        // replace rectangle with rectangle indices
+        vector = generateInteger(20, true);
+        vector.setDimensions(new int[]{5, 4});
+        executeReplace(ElementAccessMode.SUBSET, vector, RInteger.valueOf(-1), //
+                        RDataFactory.createIntVector(new int[]{2, 3, 4}, true), RDataFactory.createIntVector(new int[]{2, 3}, true));
+        assertIndicies(vector, 0, 1, 2, 3, 4, 5, -1, -1, -1, 9, 10, -1, -1, -1, 14, 15, 16, 17, 18, 19);
+
+        // replace box with box indices
+        vector = generateInteger(9, true);
+        vector.setDimensions(new int[]{3, 3});
+        executeReplace(ElementAccessMode.SUBSET, vector, RInteger.valueOf(-1), //
+                        RDataFactory.createIntVector(new int[]{2, 3}, true), RDataFactory.createIntVector(new int[]{2, 3}, true));
+        assertIndicies(vector, 0, 1, 2, 3, -1, -1, 6, -1, -1);
+
+        // replace three dimensions
+        vector = generateInteger(24, true);
+        vector.setDimensions(new int[]{2, 3, 4});
+        executeReplace(ElementAccessMode.SUBSET, vector, RInteger.valueOf(-1), //
+                        RDataFactory.createIntVector(new int[]{2}, true), RDataFactory.createIntVector(new int[]{2}, true), RDataFactory.createIntVector(new int[]{2}, true));
+        assertIndicies(vector, 0, 1, 2, 3, 4, 5, 6, 7, 8, -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23);
+
+        // replace three dimensions
+        vector = generateInteger(24, true);
+        vector.setDimensions(new int[]{2, 3, 4});
+        executeReplace(ElementAccessMode.SUBSET, vector, RInteger.valueOf(-1), //
+                        RDataFactory.createIntVector(new int[]{2}, true), RDataFactory.createIntVector(new int[]{2, 3}, true), RDataFactory.createIntVector(new int[]{2, 3, 4}, true));
+        assertIndicies(vector, 0, 1, 2, 3, 4, 5, 6, 7, 8, -1, 10, -1, 12, 13, 14, -1, 16, -1, 18, 19, 20, -1, 22, -1);
+    }
+
+    private static void assertIndicies(RAbstractIntVector vector, int... expectedValues) {
+        int[] actual = new int[vector.getLength()];
+        for (int i = 0; i < expectedValues.length; i++) {
+            actual[i] = vector.getDataAt(i);
+
+        }
+        assertThat(actual, is(expectedValues));
+    }
+
+    @Test
+    public void testSubsetSingleDimension() {
+        RAbstractIntVector vector;
+
+        // replace scalar with sequence stride=1
+        vector = generateInteger(9, true);
+        executeReplace(ElementAccessMode.SUBSET, vector, RInteger.valueOf(-1), new Object[]{RDataFactory.createIntSequence(5, 1, 3)});
+        assertIndicies(vector, 0, 1, 2, 3, -1, -1, -1, 7, 8);
+
+        // replace scalar with sequence stride>1
+        vector = generateInteger(9, true);
+        executeReplace(ElementAccessMode.SUBSET, vector, RInteger.valueOf(-1), new Object[]{RDataFactory.createIntSequence(5, 2, 2)});
+        assertIndicies(vector, 0, 1, 2, 3, -1, 5, -1, 7, 8);
+
+        // replace scalar with negative integer vector
+        vector = generateInteger(4, true);
+        executeReplace(ElementAccessMode.SUBSET, vector, RInteger.valueOf(-1), new Object[]{RDataFactory.createIntVector(new int[]{-2}, true)});
+        assertIndicies(vector, -1, 1, -1, -1);
+
+        // replace scalar with logical scalar
+        vector = generateInteger(3, true);
+        executeReplace(ElementAccessMode.SUBSET, vector, RInteger.valueOf(-1), //
+                        new Object[]{RDataFactory.createLogicalVector(new byte[]{RRuntime.LOGICAL_TRUE}, true)});
+        assertIndicies(vector, -1, -1, -1);
+
+        // replace scalar with logical vector
+        vector = generateInteger(4, true);
+        executeReplace(ElementAccessMode.SUBSET, vector, RInteger.valueOf(-1), //
+                        new Object[]{RDataFactory.createLogicalVector(new byte[]{RRuntime.LOGICAL_TRUE, RRuntime.LOGICAL_FALSE}, true)});
+        assertIndicies(vector, -1, 1, -1, 3);
+
+        // replace vector indexed by logical vector
+        vector = generateInteger(4, true);
+        executeReplace(ElementAccessMode.SUBSET, vector, RDataFactory.createIntVector(new int[]{-1, -2}, true), //
+                        new Object[]{RDataFactory.createLogicalVector(new byte[]{RRuntime.LOGICAL_TRUE, RRuntime.LOGICAL_FALSE}, true)});
+        assertIndicies(vector, -1, 1, -2, 3);
+
+        // replace scalar with integer vector
+        vector = generateInteger(9, true);
+        executeReplace(ElementAccessMode.SUBSET, vector, RInteger.valueOf(-1), new Object[]{RDataFactory.createIntVector(new int[]{9, 8}, true)});
+        assertIndicies(vector, 0, 1, 2, 3, 4, 5, 6, -1, -1);
+
+        // replace scalar with integer scalar
+        vector = generateInteger(9, true);
+        executeReplace(ElementAccessMode.SUBSET, vector, RInteger.valueOf(-1), new Object[]{RDataFactory.createIntVector(new int[]{9}, true)});
+        assertIndicies(vector, 0, 1, 2, 3, 4, 5, 6, 7, -1);
+    }
+
+    @Theory
+    public void testNames(RType targetType) {
+        RAbstractVector vector = generateVector(targetType, 4, true);
+        RStringVector names = (RStringVector) generateVector(RType.Character, 4, true);
+        vector.setNames(names);
+
+        RAbstractVector value = generateVector(targetType, 4, true);
+        RStringVector valueNames = (RStringVector) generateVector(RType.Character, 4, true);
+        value.setNames(valueNames);
+
+        RAbstractVector result = executeReplace(ElementAccessMode.SUBSET, vector, value, RLogical.TRUE);
+
+        RStringVector newNames = result.getNames(RAttributeProfiles.create());
+        assertThat(newNames.getLength(), is(names.getLength()));
+        assertThat(newNames.getDataAt(0), is(names.getDataAt(0)));
+        assertThat(newNames.getDataAt(1), is(names.getDataAt(1)));
+        assertThat(newNames.getDataAt(2), is(names.getDataAt(2)));
+        assertThat(newNames.getDataAt(3), is(names.getDataAt(3)));
+    }
+
+    @Theory
+    public void testCompletenessAfterReplace(RType targetType) {
+        RAbstractVector vector = generateVector(targetType, 4, false);
+        RAbstractVector replaceWith = generateVector(targetType, 1, true);
+
+        assumeThat(vector.isComplete(), is(false));
+        RAbstractVector result = executeReplace(ElementAccessMode.SUBSET, vector, replaceWith, RInteger.valueOf(1));
+        assertThat(result.isComplete(), is(false));
+    }
+
+    @Theory
+    public void testCompletenessAfterReplaceAll(RType targetType) {
+        RAbstractVector vector = generateVector(targetType, 4, false);
+        RAbstractVector replaceWith = generateVector(targetType, 1, true);
+
+        assumeThat(vector.isComplete(), is(false));
+        executeReplace(ElementAccessMode.SUBSET, vector, replaceWith, RLogical.valueOf(true));
+
+        // TODO we would need to find out if we replace all elements. we should support this.
+        // assertThat(result.isComplete(), is(true));
+    }
+
+    @Theory
+    public void testCompletenessPositionNA(RType targetType) {
+        RAbstractVector vector = generateVector(targetType, 4, true);
+        RAbstractVector replaceWith = generateVector(targetType, 1, true);
+
+        RAbstractVector result = executeReplace(ElementAccessMode.SUBSET, vector, replaceWith, RLogical.NA);
+
+        assertThat(result.isComplete(), is(true));
+    }
+
+    @Theory
+    public void testCompletenessOutOfBounds(RType targetType) {
+        assumeTrue(targetType != RType.Raw);
+        RAbstractVector vector = generateVector(targetType, 4, true);
+        RAbstractVector replaceWith = generateVector(targetType, 1, true);
+
+        RAbstractVector result = executeReplace(ElementAccessMode.SUBSET, vector, replaceWith, RInteger.valueOf(10));
+
+        assertThat(result.isComplete(), is(false));
+    }
+
+    @Theory
+    public void testCasts(RType targetType, RType valueType) {
+        if (targetType != valueType) {
+            assumeTrue(targetType != RType.Raw && valueType != RType.Raw);
+        }
+        RType resultType = RType.maxPrecedence(targetType, valueType);
+
+        RAbstractVector vector = generateVector(targetType, 4, true);
+        RAbstractVector value = generateVector(valueType, 4, true);
+
+        RAbstractVector result = executeReplace(ElementAccessMode.SUBSET, vector, value, accessFirst);
+        assertThat(result.getRType(), is(resultType));
+    }
+
+    @Theory
+    public void testSubsetSingleDimensionTheory(RType targetType, RAbstractVector position) {
+        assumeTrue(position.getLength() <= 4);
+        assumeTrue(position.getLength() >= 1);
+        assumeTrue(position.isComplete());
+
+        RAbstractVector vector = generateVector(targetType, 4, true);
+        RAbstractVector value = generateVector(targetType, 4, true);
+
+        RAbstractVector result = executeReplace(ElementAccessMode.SUBSET, vector, value, position);
+        assertThat(result, is(sameInstance(vector)));
+    }
+
+    @Theory
+    public void testSubscriptSingleDimensionTheory(RType targetType, RAbstractVector position) {
+        assumeTrue(position.getLength() == 1);
+        if (position instanceof RAbstractIntVector) {
+            assumeTrue(((RAbstractIntVector) position).getDataAt(0) > 0);
+        }
+
+        RAbstractVector vector = generateVector(targetType, 4, true);
+        RAbstractVector value = generateVector(targetType, 1, true);
+
+        executeReplace(ElementAccessMode.SUBSCRIPT, vector, value, position);
+
+    }
+
+    private NodeHandle<ReplaceVectorNode> handle;
+    private NodeHandle<BoxPrimitiveNode> box;
+    private ElementAccessMode currentMode;
+
+    @Before
+    public void setUp() {
+        handle = null;
+    }
+
+    @After
+    public void tearDown() {
+        handle = null;
+    }
+
+    private RAbstractVector executeReplace(ElementAccessMode mode, Object vector, Object value, Object... positions) {
+        if (handle == null || this.currentMode != mode) {
+            handle = create(mode);
+            this.currentMode = mode;
+        }
+        if (box == null) {
+            box = createHandle(BoxPrimitiveNodeGen.create(), (node, args) -> node.execute(args[0]));
+        }
+
+        return (RAbstractVector) box.call(handle.call(vector, positions, value));
+    }
+
+    private static NodeHandle<ReplaceVectorNode> create(ElementAccessMode mode) {
+        return createHandle(ReplaceVectorNode.create(mode), //
+                        (node, args) -> node.apply(args[0], (Object[]) args[1], args[2]));
+    }
+
+}
diff --git a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/access/vector/StringCompareNodeTest.java b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/access/vector/StringCompareNodeTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..c07140429a02cb6a188c03b0ea57a06e07bf51e0
--- /dev/null
+++ b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/access/vector/StringCompareNodeTest.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2015, 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.access.vector;
+
+import static com.oracle.truffle.r.nodes.test.TestUtilities.*;
+import static org.hamcrest.CoreMatchers.*;
+import static org.hamcrest.MatcherAssert.*;
+import static org.junit.Assume.*;
+
+import org.junit.*;
+import org.junit.experimental.theories.*;
+import org.junit.runner.*;
+
+import com.oracle.truffle.r.nodes.access.vector.SearchFirstStringNode.CompareStringNode;
+import com.oracle.truffle.r.nodes.test.*;
+import com.oracle.truffle.r.nodes.test.TestUtilities.NodeHandle;
+import com.oracle.truffle.r.runtime.*;
+
+@RunWith(Theories.class)
+public class StringCompareNodeTest extends TestBase {
+
+    // Please note that "FB" and "Ea" produce a hash collision. Thats why its tested here.
+    @DataPoints public static String[] TEST = {RRuntime.STRING_NA, "a", "abc", "bc".intern(), "bc".intern(), "FB", "Ea"};
+
+    @Theory
+    public void testExactNA(String a, String b) {
+        assumeTrue(a == RRuntime.STRING_NA || b == RRuntime.STRING_NA);
+        try {
+            executeCompare(true, a, b);
+            Assert.fail();
+        } catch (AssertionError e) {
+        }
+    }
+
+    @Theory
+    public void testNonExactNA(String a, String b) {
+        assumeTrue(a == RRuntime.STRING_NA || b == RRuntime.STRING_NA);
+        try {
+            executeCompare(false, a, b);
+            Assert.fail();
+        } catch (AssertionError e) {
+        }
+    }
+
+    @Theory
+    public void testExact(String a, String b) {
+        assumeFalse(a == RRuntime.STRING_NA);
+        assumeFalse(b == RRuntime.STRING_NA);
+        assertThat(executeCompare(true, a, b), is(a.equals(b)));
+    }
+
+    @Theory
+    public void testNonExact(String a, String b) {
+        assumeFalse(a == RRuntime.STRING_NA);
+        assumeFalse(b == RRuntime.STRING_NA);
+        assertThat(executeCompare(false, a, b), is(a.startsWith(b)));
+    }
+
+    private static boolean executeCompare(boolean exact, String a, String b) {
+        NodeHandle<CompareStringNode> handle = createHandle(exact ? CompareStringNode.createEquals() : CompareStringNode.createStartsWith(), //
+                        (node, args) -> node.executeCompare((String) args[0], (String) args[1]));
+        return (Boolean) handle.call(a, b);
+    }
+
+}
diff --git a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/access/vector/StringSearchNodeTest.java b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/access/vector/StringSearchNodeTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..bdec2bec68a4f90f65276f5b9d65f7cf3a91c7c5
--- /dev/null
+++ b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/access/vector/StringSearchNodeTest.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2015, 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.access.vector;
+
+import static com.oracle.truffle.r.nodes.test.TestUtilities.*;
+import static org.hamcrest.CoreMatchers.*;
+import static org.hamcrest.MatcherAssert.*;
+
+import org.junit.experimental.theories.*;
+import org.junit.runner.*;
+
+import com.oracle.truffle.r.nodes.test.*;
+import com.oracle.truffle.r.nodes.test.TestUtilities.NodeHandle;
+import com.oracle.truffle.r.runtime.*;
+import com.oracle.truffle.r.runtime.data.*;
+import com.oracle.truffle.r.runtime.data.model.*;
+
+@RunWith(Theories.class)
+public class StringSearchNodeTest extends TestBase {
+
+    // Please note that "FB" and "Ea" produce a hash collision. Thats why its tested here.
+    @DataPoints public static String[] TEST = {RRuntime.STRING_NA, "a", "abc", "bc".intern(), "bc".intern(), "FB", "Ea"};
+
+    @Theory
+    public void testTheory(String aString, String bString, String cString) {
+        create();
+
+        RAbstractStringVector a;
+        RAbstractStringVector b;
+
+        a = createVector(aString);
+        b = createVector(bString);
+        assertResult(a, b, executeSearch(a, b));
+
+        a = createVector(aString, bString);
+        b = createVector(cString);
+        assertResult(a, b, executeSearch(a, b));
+
+        a = createVector(aString);
+        b = createVector(bString, cString);
+        assertResult(a, b, executeSearch(a, b));
+
+        a = createVector(aString, bString);
+        b = createVector(bString, cString);
+        assertResult(a, b, executeSearch(a, b));
+    }
+
+    private static RAbstractStringVector createVector(String... elements) {
+        boolean complete = true;
+        for (int i = 0; i < elements.length; i++) {
+            if (elements[i] == RRuntime.STRING_NA) {
+                complete = false;
+                break;
+            }
+        }
+        return RDataFactory.createStringVector(elements, complete);
+    }
+
+    private static void assertResult(RAbstractStringVector a, RAbstractStringVector b, RAbstractIntVector result) {
+        assertThat(result.getLength(), is(b.getLength()));
+        for (int i = 0; i < b.getLength(); i++) {
+            int resultIndex = result.getDataAt(i);
+            String element = b.getDataAt(i);
+            if (element == RRuntime.STRING_NA) {
+                assertThat(resultIndex > a.getLength(), is(true));
+                continue;
+            }
+            if (resultIndex > a.getLength()) {
+                for (int j = 0; j < a.getLength(); j++) {
+                    assertCompare(false, a.getDataAt(j), element);
+                }
+            } else {
+                for (int j = 0; j < resultIndex - 1; j++) {
+                    assertCompare(false, a.getDataAt(j), element);
+                }
+                assertCompare(true, a.getDataAt(resultIndex - 1), element);
+            }
+        }
+    }
+
+    private static void assertCompare(boolean expectedResult, String target, String element) {
+        boolean actualResult;
+        actualResult = target.equals(element);
+        assertThat(actualResult, is(expectedResult));
+    }
+
+    private NodeHandle<SearchFirstStringNode> handle;
+
+    private void create() {
+        handle = createHandle(SearchFirstStringNode.createNode(true, false), //
+                        (node, args) -> {
+                            RAbstractStringVector target = (RAbstractStringVector) args[0];
+                            return node.apply(target, (RAbstractStringVector) args[1], target.getLength());
+                        });
+    }
+
+    private RAbstractIntVector executeSearch(RAbstractStringVector a, RAbstractStringVector b) {
+        return (RAbstractIntVector) handle.call(a, b);
+    }
+}
diff --git a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/test/TestBase.java b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/test/TestBase.java
index a5fd1823e58a6c3a09881568ed13dc0dfb2739d7..cd70ea2ef56c46787d512371ab993f5f9ff7558d 100644
--- a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/test/TestBase.java
+++ b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/test/TestBase.java
@@ -24,21 +24,21 @@ package com.oracle.truffle.r.nodes.test;
 
 import org.junit.*;
 
-import com.oracle.truffle.r.runtime.*;
+import com.oracle.truffle.api.vm.*;
+import com.oracle.truffle.r.runtime.context.*;
 import com.oracle.truffle.r.test.generate.*;
 
 public class TestBase {
 
-    private static RContext testContext;
+    private static TruffleVM testVM;
 
     @BeforeClass
     public static void setupClass() {
-        testContext = FastRSession.create().createTestContext();
+        testVM = FastRSession.create().createTestContext();
     }
 
     @AfterClass
     public static void finishClass() {
-        testContext.destroy();
+        RContext.destroyContext(testVM);
     }
-
 }
diff --git a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/test/TestUtilities.java b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/test/TestUtilities.java
index 6708b1ff9bd109b7df9248e041d9dfcc328e6b99..d13c857a8154c7f0b18550c2726f749650fa32b7 100644
--- a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/test/TestUtilities.java
+++ b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/test/TestUtilities.java
@@ -26,9 +26,116 @@ import com.oracle.truffle.api.*;
 import com.oracle.truffle.api.frame.*;
 import com.oracle.truffle.api.nodes.*;
 import com.oracle.truffle.r.runtime.*;
+import com.oracle.truffle.r.runtime.data.*;
+import com.oracle.truffle.r.runtime.data.model.*;
 
 public class TestUtilities {
 
+    private static final int NA_INDEX = 3;
+
+    public static RAbstractVector generateVector(RType type, int size, boolean complete) {
+        switch (type) {
+            case Raw:
+                return generateRaw(size);
+            case Logical:
+                return generateLogical(size, complete);
+            case Integer:
+                return generateInteger(size, complete);
+            case Double:
+                return generateDouble(size, complete);
+            case Complex:
+                return generateComplex(size, complete);
+            case Character:
+                return generateCharacter(size, complete);
+            case List:
+                return generateList(size, complete);
+            default:
+                throw new AssertionError();
+
+        }
+    }
+
+    private static RAbstractVector generateRaw(int size) {
+        byte[] array = new byte[size];
+        for (int i = 0; i < size; i++) {
+            array[i] = (byte) i;
+        }
+        return RDataFactory.createRawVector(array);
+    }
+
+    public static RAbstractVector generateLogical(int size, boolean complete) {
+        byte[] array = new byte[size];
+        for (int i = 0; i < size; i++) {
+            int mode;
+            if (complete) {
+                mode = i % 2;
+            } else {
+                mode = i % 3;
+            }
+            byte value;
+            switch (mode) {
+                case 0:
+                    value = RRuntime.LOGICAL_FALSE;
+                    break;
+                case 1:
+                    value = RRuntime.LOGICAL_TRUE;
+                    break;
+                case 2:
+                    value = RRuntime.LOGICAL_NA;
+                    break;
+                default:
+                    throw new AssertionError();
+
+            }
+            array[i] = value;
+        }
+        return RDataFactory.createLogicalVector(array, complete || !complete && size < 3);
+    }
+
+    public static RAbstractIntVector generateInteger(int size, boolean complete) {
+        int[] array = new int[size];
+        for (int i = 0; i < size; i++) {
+            array[i] = !complete && i % (NA_INDEX + 1) == NA_INDEX ? RRuntime.INT_NA : i;
+        }
+        return RDataFactory.createIntVector(array, complete || !complete && size < 3);
+    }
+
+    public static RAbstractDoubleVector generateDouble(int size, boolean complete) {
+        double[] array = new double[size];
+        for (int i = 0; i < size; i++) {
+            array[i] = !complete && i % (NA_INDEX + 1) == NA_INDEX ? RRuntime.DOUBLE_NA : i;
+        }
+        return RDataFactory.createDoubleVector(array, complete || !complete && size < 3);
+    }
+
+    public static RAbstractVector generateComplex(int size, boolean complete) {
+        double[] array = new double[size << 1];
+        for (int i = 0; i < size; i++) {
+            boolean useNA = !complete && i % (NA_INDEX + 1) == NA_INDEX;
+            array[i << 1 - 1] = useNA ? RComplex.NA.getRealPart() : i;
+            array[i << 1] = useNA ? RComplex.NA.getRealPart() : i;
+        }
+        return RDataFactory.createComplexVector(array, complete || !complete && size < NA_INDEX);
+    }
+
+    public static RAbstractVector generateCharacter(int size, boolean complete) {
+        String[] array = new String[size];
+        for (int i = 0; i < size; i++) {
+            array[i] = !complete && i % (NA_INDEX + 1) == NA_INDEX ? RRuntime.STRING_NA : String.valueOf(i);
+        }
+        return RDataFactory.createStringVector(array, complete || !complete && size < 3);
+    }
+
+    public static RAbstractVector generateList(int size, boolean complete) {
+        Object[] array = new Object[size];
+        for (int i = 0; i < size; i++) {
+            array[i] = !complete && i % (NA_INDEX + 1) == NA_INDEX ? RNull.instance : i;
+        }
+        RAbstractVector list = RDataFactory.createList(array);
+        list.setComplete(complete || !complete && size < 3);
+        return list;
+    }
+
     /**
      * Creates a handle that emulates the behavior as if this node would be executed inside of an R
      * call.
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/InlineCacheNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/InlineCacheNode.java
index 93878456188ae531108e22fa26eaeb68081294de..55951561911d766208f536f1046d25fc44ff0c9e 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/InlineCacheNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/InlineCacheNode.java
@@ -30,8 +30,7 @@ import com.oracle.truffle.api.frame.*;
 import com.oracle.truffle.api.nodes.*;
 import com.oracle.truffle.api.utilities.*;
 import com.oracle.truffle.r.nodes.function.*;
-import com.oracle.truffle.r.runtime.*;
-import com.oracle.truffle.r.runtime.RContext.Engine;
+import com.oracle.truffle.r.runtime.context.*;
 import com.oracle.truffle.r.runtime.data.*;
 import com.oracle.truffle.r.runtime.data.RPromise.Closure;
 import com.oracle.truffle.r.runtime.nodes.*;
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RRootNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RRootNode.java
index c9a489d5b2cbb3a6fd6a643bd518738d93bb5021..b9ca0a89a7fb8082e0ac3e6db1b4ddae20a177b3 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RRootNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RRootNode.java
@@ -31,6 +31,7 @@ import com.oracle.truffle.api.utilities.*;
 import com.oracle.truffle.r.nodes.builtin.*;
 import com.oracle.truffle.r.nodes.function.*;
 import com.oracle.truffle.r.runtime.*;
+import com.oracle.truffle.r.runtime.context.*;
 import com.oracle.truffle.r.runtime.env.frame.*;
 
 /**
@@ -48,7 +49,7 @@ public abstract class RRootNode extends RootNode implements HasSignature {
     private final FormalArguments formalArguments;
 
     protected RRootNode(SourceSection src, FormalArguments formalArguments, FrameDescriptor frameDescriptor) {
-        super(src, frameDescriptor);
+        super(RContext.getEngine().getTruffleLanguage(), src, frameDescriptor);
         this.formalArguments = formalArguments;
     }
 
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/AccessFieldNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/AccessFieldNode.java
index 902871070eb226f019ba09c9315d61338b4bfdad..e048c6b2421965591c0d45c1ad71568b7d205fdf 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/AccessFieldNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/AccessFieldNode.java
@@ -26,6 +26,7 @@ import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.*;
 import com.oracle.truffle.api.utilities.*;
 import com.oracle.truffle.r.runtime.*;
+import com.oracle.truffle.r.runtime.context.*;
 import com.oracle.truffle.r.runtime.data.*;
 import com.oracle.truffle.r.runtime.data.model.*;
 import com.oracle.truffle.r.runtime.env.*;
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/UpdateFieldNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/UpdateFieldNode.java
index caf62eb740e684319aaa0b5e363e019b96a97e6c..651c5389ca4b04bc2c16088d61ae9c952cea7a6f 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/UpdateFieldNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/UpdateFieldNode.java
@@ -30,6 +30,7 @@ import com.oracle.truffle.api.dsl.*;
 import com.oracle.truffle.api.utilities.*;
 import com.oracle.truffle.r.nodes.unary.*;
 import com.oracle.truffle.r.runtime.*;
+import com.oracle.truffle.r.runtime.context.*;
 import com.oracle.truffle.r.runtime.data.*;
 import com.oracle.truffle.r.runtime.data.model.*;
 import com.oracle.truffle.r.runtime.env.*;
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/WriteLocalFrameVariableNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/WriteLocalFrameVariableNode.java
index 39ddd42562463a36f6ed0825c55169e532070588..19e7829ec14a2dc1f264e51f3726d74ba6e3b22d 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/WriteLocalFrameVariableNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/WriteLocalFrameVariableNode.java
@@ -91,7 +91,6 @@ public abstract class WriteLocalFrameVariableNode extends BaseWriteVariableNode
             FrameSlot frameSlot = findOrAddFrameSlot(frame.getFrameDescriptor(), getName(), initialKind);
             replace(ResolvedWriteLocalFrameVariableNode.create(getRhs(), getName(), frameSlot, getMode())).execute(frame, value);
         }
-
     }
 
     @NodeFields({@NodeField(name = "frameSlot", type = FrameSlot.class), @NodeField(name = "mode", type = Mode.class)})
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/WriteVariableNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/WriteVariableNode.java
index f617962d14a72e7cb5fd8897dbe3e4c3d69a32d0..2dfb0525963bb6fe2b230a35cd649d1c1f837a45 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/WriteVariableNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/WriteVariableNode.java
@@ -62,7 +62,7 @@ public abstract class WriteVariableNode extends RNode {
      * Variant for saving function arguments, i.e. from {@link RArguments} into the frame.
      */
     public static WriteVariableNode createArgSave(String name, RNode rhs) {
-        if (FastROptions.InvisibleArgs.getValue()) {
+        if (FastROptions.InvisibleArgs) {
             return WriteLocalFrameVariableNode.create(name, rhs, Mode.INVISIBLE);
         } else {
             return WriteLocalFrameVariableNode.create(name, rhs, Mode.REGULAR);
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/variables/ReadVariableNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/variables/ReadVariableNode.java
index 8e613c8b1f94caa0d17221a8aefd544eb21f6976..f07ebf185e07763313af6d7a393a882eaa9c1739 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/variables/ReadVariableNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/variables/ReadVariableNode.java
@@ -561,7 +561,7 @@ public final class ReadVariableNode extends RNode implements RSyntaxNode, Visibi
             lastLevel = new MultiAssumptionLevel(lastLevel, assumptions.toArray(new Assumption[assumptions.size()]));
         }
 
-        if (FastROptions.PrintComplexLookups.getValue() && levels.size() > 1 && complex) {
+        if (FastROptions.PrintComplexLookups && levels.size() > 1 && complex) {
             System.out.println(identifier + " " + lastLevel);
         }
 
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/CachedExtractVectorNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/CachedExtractVectorNode.java
new file mode 100644
index 0000000000000000000000000000000000000000..24d2e0dabadfc4c0c6564b49055e81c246817dc1
--- /dev/null
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/CachedExtractVectorNode.java
@@ -0,0 +1,401 @@
+/*
+ * Copyright (c) 2015, 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.access.vector;
+
+import com.oracle.truffle.api.*;
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.dsl.*;
+import com.oracle.truffle.api.nodes.*;
+import com.oracle.truffle.api.utilities.*;
+import com.oracle.truffle.r.nodes.access.vector.CachedExtractVectorNodeFactory.SetNamesNodeGen;
+import com.oracle.truffle.r.nodes.access.vector.PositionsCheckNode.PositionProfile;
+import com.oracle.truffle.r.nodes.profile.*;
+import com.oracle.truffle.r.runtime.*;
+import com.oracle.truffle.r.runtime.context.*;
+import com.oracle.truffle.r.runtime.data.*;
+import com.oracle.truffle.r.runtime.data.model.*;
+import com.oracle.truffle.r.runtime.env.*;
+import com.oracle.truffle.r.runtime.nodes.*;
+
+final class CachedExtractVectorNode extends CachedVectorNode {
+
+    protected static final boolean DEFAULT_EXACT = true;
+    protected static final boolean DEFAULT_DROP_DIMENSION = true;
+
+    private final Class<? extends RTypedValue> targetClass;
+    private final Class<? extends RTypedValue> exactClass;
+    private final Class<? extends RTypedValue> dropDimensionsClass;
+    private final boolean exact;
+    private final boolean dropDimensions;
+
+    private final VectorLengthProfile vectorLengthProfile = VectorLengthProfile.create();
+    private final RAttributeProfiles vectorNamesProfile = RAttributeProfiles.create();
+
+    @Child private WriteIndexedVectorNode writeVectorNode;
+    @Child private PositionsCheckNode positionsCheckNode;
+    @Child private SetNamesNode setNamesNode;
+    @Children private final CachedExtractVectorNode[] extractNames;
+
+    @Child private ExtractDimNamesNode extractDimNames;
+
+    private final ConditionProfile resultHasDimensions = ConditionProfile.createBinaryProfile();
+
+    /**
+     * Profile if any metadata was applied at any point in time. This is useful extract primitive
+     * values from the result in case no metadata was ever applied.
+     */
+    private final BranchProfile metadataApplied = BranchProfile.create();
+
+    public CachedExtractVectorNode(ElementAccessMode mode, RTypedValue vector, Object[] positions, RTypedValue exact, RTypedValue dropDimensions, boolean recursive) {
+        super(mode, vector, positions, recursive);
+        this.targetClass = vector.getClass();
+        this.exactClass = exact.getClass();
+        this.dropDimensionsClass = dropDimensions.getClass();
+        Object[] convertedPositions = filterPositions(positions);
+        this.extractNames = new CachedExtractVectorNode[convertedPositions.length];
+        this.exact = logicalAsBoolean(exact, DEFAULT_EXACT);
+        this.dropDimensions = logicalAsBoolean(dropDimensions, DEFAULT_DROP_DIMENSION);
+        this.positionsCheckNode = new PositionsCheckNode(mode, vectorType, convertedPositions, this.exact, false, recursive);
+        if (vectorType != RType.Null && vectorType != RType.Environment) {
+            this.writeVectorNode = WriteIndexedVectorNode.create(vectorType, convertedPositions.length, true, false, false);
+        }
+    }
+
+    public boolean isSupported(Object target, Object[] positions, Object exactValue, Object dropDimensionsValue) {
+        if (targetClass == target.getClass() && exactValue.getClass() == this.exactClass //
+                        && logicalAsBoolean(dropDimensionsClass.cast(dropDimensionsValue), DEFAULT_DROP_DIMENSION) == this.dropDimensions //
+                        && dropDimensionsValue.getClass() == this.dropDimensionsClass //
+                        && logicalAsBoolean(exactClass.cast(exactValue), DEFAULT_EXACT) == this.exact) {
+            return positionsCheckNode.isSupported(positions);
+        }
+        return false;
+    }
+
+    public Object apply(Object originalVector, Object[] originalPositions, PositionProfile[] originalProfiles, Object originalExact, Object originalDropDimensions) {
+        final Object[] positions = filterPositions(originalPositions);
+
+        assert isSupported(originalVector, positions, originalExact, originalDropDimensions);
+
+        final RTypedValue castVector = targetClass.cast(originalVector);
+        final RAbstractContainer vector;
+        switch (vectorType) {
+            case Null:
+                return RNull.instance;
+            case Environment:
+                /*
+                 * TODO (chumer) the environment case cannot be applied to the default extract
+                 * method as it does not implement RAbstractContainer. This should be harmonized
+                 * later.
+                 */
+                return doEnvironment((REnvironment) castVector, positions);
+            case Integer:
+                if (castVector instanceof RFactor) {
+                    vector = ((RFactor) castVector).getVector();
+                    break;
+                }
+            default:
+                vector = (RAbstractContainer) castVector;
+        }
+
+        int vectorLength = vectorLengthProfile.profile(vector.getLength());
+
+        int[] dimensions = getDimensions(vector);
+
+        PositionProfile[] positionProfiles;
+        if (originalProfiles == null) {
+            positionProfiles = positionsCheckNode.executeCheck(vector, dimensions, vectorLength, positions);
+        } else {
+            positionProfiles = originalProfiles;
+        }
+
+        if (isMissingSingleDimension()) {
+            // special case for x<-matrix(1:4, ncol=2); x[]
+            return originalVector;
+        }
+
+        int extractedVectorLength = positionsCheckNode.getSelectedPositionsCount(positionProfiles);
+        final RAbstractVector extractedVector;
+        switch (vectorType) {
+            case Language:
+            case DataFrame:
+            case Expression:
+            case PairList:
+                extractedVector = RType.List.create(extractedVectorLength, false);
+                break;
+            default:
+                extractedVector = vectorType.create(extractedVectorLength, false);
+                break;
+        }
+
+        if (mode.isSubset()) {
+            if (extractedVectorLength > 0) {
+                writeVectorNode.enableValueNACheck(vector);
+                writeVectorNode.apply(extractedVector, extractedVectorLength, positions, vector, vectorLength, dimensions);
+                extractedVector.setComplete(writeVectorNode.neverSeenNAInValue());
+                RNode.reportWork(this, extractedVectorLength);
+            }
+            if (numberOfDimensions == 1) {
+                // names only need to be considered for single dimensional accesses
+                RStringVector originalNames = vector.getNames(vectorNamesProfile);
+                if (originalNames != null) {
+                    metadataApplied.enter();
+                    setNames(extractedVector, extractNames(originalNames, positions, positionProfiles, 0, originalExact, originalDropDimensions));
+                }
+            } else {
+                assert numberOfDimensions > 1;
+                applyDimensions(vector, extractedVector, extractedVectorLength, positionProfiles, positions);
+            }
+
+            switch (vectorType) {
+                case Expression:
+                    return new RExpression((RList) extractedVector);
+                case Language:
+                    return materializeLanguage(extractedVector);
+                default:
+                    return trySubsetPrimitive(extractedVector);
+            }
+
+        } else {
+            writeVectorNode.apply(extractedVector, extractedVectorLength, positions, vector, vectorLength, dimensions);
+            RNode.reportWork(this, 1);
+            assert extractedVectorLength == 1;
+            return extractedVector.getDataAtAsObject(0);
+        }
+    }
+
+    private int[] getDimensions(final RAbstractContainer vector) {
+        int[] dimensions;
+        if (numberOfDimensions == 1) {
+            dimensions = null;
+        } else {
+            dimensions = loadVectorDimensions(vector);
+        }
+        return dimensions;
+    }
+
+    private Object trySubsetPrimitive(RAbstractVector extractedVector) {
+        if (!metadataApplied.isVisited() && positionsCheckNode.getCachedSelectedPositionsCount() == 1 && !isList()) {
+            /*
+             * If the selected count was always 1 and no metadata was ever set we can just extract
+             * the primitive value from the vector. This branch has to fold to a constant because we
+             * want to avoid the toggling of the return types depending on input values.
+             */
+            assert extractedVector.getNames(RAttributeProfiles.create()) == null;
+            assert extractedVector.getDimensions() == null;
+            assert extractedVector.getDimNames(null) == null;
+            return extractedVector.getDataAtAsObject(0);
+        }
+        return extractedVector;
+    }
+
+    private Object doEnvironment(REnvironment env, Object[] positions) {
+        if (mode.isSubset()) {
+            errorBranch.enter();
+            throw RError.error(this, RError.Message.OBJECT_NOT_SUBSETTABLE, RType.Environment.getName());
+        }
+
+        String positionString = tryCastSingleString(positionsCheckNode, positions);
+        if (positionString != null) {
+            Object obj = env.get(positionString);
+            return obj == null ? RNull.instance : obj;
+        }
+        errorBranch.enter();
+        throw RError.error(this, RError.Message.WRONG_ARGS_SUBSET_ENV);
+    }
+
+    private boolean isMissingSingleDimension() {
+        return numberOfDimensions == 1 && positionsCheckNode.isMissing();
+    }
+
+    @TruffleBoundary
+    private static Object materializeLanguage(RAbstractVector extractedVector) {
+        return RContext.getRRuntimeASTAccess().fromList(extractedVector);
+    }
+
+    private Object extract(int dimensionIndex, RTypedValue vector, Object pos, PositionProfile profile) {
+        if (extractDimNames == null) {
+            CompilerDirectives.transferToInterpreterAndInvalidate();
+            extractDimNames = new ExtractDimNamesNode(numberOfDimensions);
+        }
+        return extractDimNames.extract(dimensionIndex, vector, pos, profile);
+    }
+
+    private boolean isList() {
+        return vectorType == RType.List;
+    }
+
+    private final NullProfile dimNamesNull = NullProfile.create();
+
+    @ExplodeLoop
+    private void applyDimensions(RAbstractContainer originalTarget, RAbstractVector extractedTarget, int extractedTargetLength, PositionProfile[] positionProfile, Object[] positions) {
+        // TODO speculate on the number of counted dimensions
+        int dimCount = countDimensions(positionProfile);
+
+        int[] newDimensions = new int[dimCount];
+        RList originalDimNames = dimNamesNull.profile(originalTarget.getDimNames(null));
+        Object[] newDimNames = null;
+        if (originalDimNames != null) {
+            newDimNames = new Object[dimCount];
+        }
+
+        int dimIndex = -1;
+        for (int i = 0; i < numberOfDimensions; i++) {
+            int selectedPositionsCount = positionProfile[i].selectedPositionsCount;
+            if (selectedPositionsCount != 1 || !dropDimensions) {
+                dimIndex++;
+                newDimensions[dimIndex] = selectedPositionsCount;
+                if (originalDimNames != null) {
+                    Object dataAt = originalDimNames.getDataAt(i);
+                    newDimNames[dimIndex] = extract(i, (RTypedValue) dataAt, positions[i], positionProfile[i]);
+                }
+            }
+        }
+
+        if (resultHasDimensions.profile(dimCount > 1)) {
+            metadataApplied.enter();
+            extractedTarget.setDimensions(newDimensions);
+            if (newDimNames != null) {
+                extractedTarget.setDimNames(RDataFactory.createList(newDimNames));
+            }
+        } else if (originalDimNames != null && originalDimNames.getLength() > 0) {
+            RAbstractStringVector foundNames = translateDimNamesToNames(positionProfile, originalDimNames, extractedTargetLength, positions);
+            if (foundNames != null && foundNames.getLength() > 0) {
+                metadataApplied.enter();
+                setNames(extractedTarget, foundNames);
+            }
+        }
+    }
+
+    @ExplodeLoop
+    private int countDimensions(PositionProfile[] boundsProfile) {
+        int dimCount = 0;
+        for (int i = 0; i < numberOfDimensions; i++) {
+            int selectedPositionsCount = boundsProfile[i].selectedPositionsCount;
+            if (selectedPositionsCount != 1 || !dropDimensions) {
+                dimCount++;
+            }
+        }
+        return dimCount;
+    }
+
+    @ExplodeLoop
+    private RAbstractStringVector translateDimNamesToNames(PositionProfile[] positionProfile, RList originalDimNames, int newVectorLength, Object[] positions) {
+        RAbstractStringVector foundNames = null;
+        for (int currentDimIndex = numberOfDimensions - 1; currentDimIndex >= 0; currentDimIndex--) {
+            PositionProfile profile = positionProfile[currentDimIndex];
+            if (profile.selectedPositionsCount != newVectorLength) {
+                continue;
+            }
+
+            Object srcNames = originalDimNames.getDataAt(currentDimIndex);
+            if (srcNames != RNull.instance) {
+                Object position = positions[currentDimIndex];
+
+                Object newNames = extractNames((RAbstractStringVector) srcNames, new Object[]{position}, new PositionProfile[]{profile}, currentDimIndex, RLogical.valueOf(true),
+                                RLogical.valueOf(dropDimensions));
+                if (newNames != RNull.instance) {
+                    if (newNames instanceof String) {
+                        newNames = RDataFactory.createStringVector((String) newNames);
+                    }
+                    RAbstractStringVector castFoundNames = (RAbstractStringVector) newNames;
+                    if (castFoundNames.getLength() == newVectorLength) {
+                        if (foundNames != null) {
+                            /*
+                             * the idea here is that you can get names from dimnames only if the
+                             * name of of an item can be unambiguously identified (there can be only
+                             * one matching name in all dimensions - if "name" has already been set,
+                             * we might as well return null already)
+                             */
+                            foundNames = null;
+                            break;
+                        }
+                        foundNames = (RAbstractStringVector) newNames;
+                    }
+                }
+
+            }
+        }
+        return foundNames;
+    }
+
+    private Object extractNames(RAbstractStringVector originalNames, Object[] positions, PositionProfile[] profiles, int dimension, Object originalExact, Object originalDropDimensions) {
+        if (extractNames[dimension] == null) {
+            CompilerDirectives.transferToInterpreterAndInvalidate();
+            extractNames[dimension] = insert(new CachedExtractVectorNode(mode, originalNames, positions, (RTypedValue) originalExact, (RTypedValue) originalDropDimensions, recursive));
+        }
+        assert extractNames[dimension].isSupported(originalNames, positions, originalExact, originalDropDimensions);
+        Object newNames = extractNames[dimension].apply(originalNames, positions, profiles, originalExact, originalDropDimensions);
+        return newNames;
+    }
+
+    private void setNames(RAbstractContainer vector, Object newNames) {
+        if (setNamesNode == null) {
+            CompilerDirectives.transferToInterpreterAndInvalidate();
+            setNamesNode = insert(SetNamesNodeGen.create());
+        }
+        setNamesNode.execute(vector, newNames);
+    }
+
+    protected abstract static class SetNamesNode extends Node {
+
+        public abstract void execute(RAbstractContainer container, Object newNames);
+
+        @Specialization
+        protected void setNames(RAbstractContainer container, RAbstractStringVector newNames) {
+            container.setNames(newNames.materialize());
+        }
+
+        @Specialization
+        protected void setNames(RAbstractContainer container, String newNames) {
+            container.setNames(RString.valueOf(newNames).materialize());
+        }
+
+        @Specialization
+        protected void setNames(RAbstractContainer container, @SuppressWarnings("unused") RNull newNames) {
+            container.setNames(null);
+        }
+
+    }
+
+    private static class ExtractDimNamesNode extends Node {
+
+        @Children private final CachedExtractVectorNode[] extractNodes;
+
+        public ExtractDimNamesNode(int dimensions) {
+            this.extractNodes = new CachedExtractVectorNode[dimensions];
+        }
+
+        public Object extract(int dimensionIndex, RTypedValue vector, Object position, PositionProfile profile) {
+            Object[] positions = new Object[]{position};
+            PositionProfile[] profiles = new PositionProfile[]{profile};
+            if (extractNodes[dimensionIndex] == null) {
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                extractNodes[dimensionIndex] = insert(new CachedExtractVectorNode(ElementAccessMode.SUBSET, vector, positions, RLogical.TRUE, RLogical.TRUE, false));
+            }
+            CompilerAsserts.partialEvaluationConstant(dimensionIndex);
+            assert extractNodes[dimensionIndex].isSupported(vector, positions, RLogical.TRUE, RLogical.TRUE);
+            return extractNodes[dimensionIndex].apply(vector, positions, profiles, RLogical.TRUE, RLogical.TRUE);
+        }
+    }
+
+}
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/CachedReplaceVectorNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/CachedReplaceVectorNode.java
new file mode 100644
index 0000000000000000000000000000000000000000..1cf1800a2a8a1e89e7720d45ff14cb642489a115
--- /dev/null
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/CachedReplaceVectorNode.java
@@ -0,0 +1,548 @@
+/*
+ * Copyright (c) 2015, 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.access.vector;
+
+import java.util.*;
+
+import com.oracle.truffle.api.*;
+import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.nodes.*;
+import com.oracle.truffle.api.utilities.*;
+import com.oracle.truffle.r.nodes.access.vector.PositionsCheckNode.PositionProfile;
+import com.oracle.truffle.r.nodes.binary.*;
+import com.oracle.truffle.r.nodes.profile.*;
+import com.oracle.truffle.r.nodes.unary.*;
+import com.oracle.truffle.r.runtime.*;
+import com.oracle.truffle.r.runtime.RError.Message;
+import com.oracle.truffle.r.runtime.context.*;
+import com.oracle.truffle.r.runtime.data.*;
+import com.oracle.truffle.r.runtime.data.model.*;
+import com.oracle.truffle.r.runtime.env.*;
+import com.oracle.truffle.r.runtime.nodes.*;
+
+final class CachedReplaceVectorNode extends CachedVectorNode {
+
+    private static final Object DELETE_MARKER = new Object();
+
+    private final Class<?> vectorClass;
+    private final Class<?> valueClass;
+
+    private final VectorLengthProfile targetLengthProfile = VectorLengthProfile.create();
+    private final VectorLengthProfile valueLengthProfile = VectorLengthProfile.create();
+    private final BranchProfile warningBranch = BranchProfile.create();
+    private final RAttributeProfiles vectorNamesProfile = RAttributeProfiles.create();
+    private final RAttributeProfiles positionNamesProfile = RAttributeProfiles.create();
+    private final ConditionProfile rightIsShared = ConditionProfile.createBinaryProfile();
+    private final BranchProfile rightIsNonTemp = BranchProfile.create();
+    private final BranchProfile rightIsTemp = BranchProfile.create();
+    private final BranchProfile resizeProfile = BranchProfile.create();
+    private final BranchProfile sharedProfile = BranchProfile.create();
+
+    private final RType valueType;
+    private final RType castType;
+    private final boolean updatePositionNames;
+
+    @Child private WriteIndexedVectorNode writeVectorNode;
+    @Child private PositionsCheckNode positionsCheckNode;
+    @Child private CastNode castVectorNode;
+    @Child private CachedReplaceVectorNode copyPositionNames;
+    @Child private DeleteElementsNode deleteElementsNode;
+
+    public CachedReplaceVectorNode(ElementAccessMode mode, RTypedValue vector, Object[] positions, RTypedValue value, boolean updatePositionNames, boolean recursive) {
+        super(mode, vector, positions, recursive);
+
+        this.updatePositionNames = updatePositionNames;
+        this.vectorClass = vector.getClass();
+        this.valueClass = value.getClass();
+        this.valueType = value.getRType();
+        this.castType = verifyCastType(resolveCastVectorType());
+        this.castVectorNode = createCastVectorNode();
+        this.deleteElementsNode = isDeleteElements() ? new DeleteElementsNode() : null;
+
+        Object[] convertedPositions = filterPositions(positions);
+        this.positionsCheckNode = new PositionsCheckNode(mode, vectorType, convertedPositions, true, true, recursive);
+        if (castType != null && !castType.isNull()) {
+            this.writeVectorNode = WriteIndexedVectorNode.create(castType, convertedPositions.length, false, true, mode.isSubscript() && !isDeleteElements());
+        }
+    }
+
+    public boolean isSupported(Object target, Object[] positions, Object values) {
+        if (vectorClass == target.getClass() && values.getClass() == valueClass) {
+            return positionsCheckNode.isSupported(positions);
+        }
+        return false;
+    }
+
+    public Object apply(Object originalVector, Object[] originalPositions, Object originalValues) {
+        final Object[] positions = filterPositions(originalPositions);
+        assert isSupported(originalVector, positions, originalValues);
+
+        Object castVector = vectorClass.cast(originalVector);
+        Object castValue = valueClass.cast(originalValues);
+
+        RAbstractContainer value;
+        if (valueType == RType.Null) {
+            if (vectorType == RType.Null) {
+                // we cast Null to Logical, but in the end it will fold and return Null
+                value = RType.Logical.getEmpty();
+            } else if (castType == RType.List) {
+                value = RDataFactory.createList(new Object[]{DELETE_MARKER});
+            } else {
+                value = castType.getEmpty();
+            }
+        } else {
+            if (!isList() && castValue instanceof RFactor) {
+                value = ((RFactor) castValue).getVector();
+            } else {
+                value = (RAbstractContainer) castValue;
+            }
+        }
+
+        int appliedValueLength = valueLengthProfile.profile(value.getLength());
+
+        int valueLength;
+        if (this.numberOfDimensions > 1 && isDeleteElements()) {
+            valueLength = 0;
+        } else {
+            valueLength = appliedValueLength;
+        }
+
+        if (vectorType == RType.Null) {
+            if (valueLength == 0) {
+                return RNull.instance;
+            }
+        }
+
+        /*
+         * Unfortunately special behavior for some RTypes are necessary. We should aim for getting
+         * rid of them as much as possible in the future.
+         */
+        RAbstractVector vector;
+        switch (vectorType) {
+            case Null:
+                vector = castType.getEmpty();
+                break;
+            case Factor:
+                vector = ((RFactor) castVector).getVector();
+                break;
+            case PairList:
+                vector = ((RPairList) castVector).toRList();
+                break;
+            case Environment:
+                return doEnvironment((REnvironment) castVector, positions, castValue);
+            case Language:
+                vector = RContext.getRRuntimeASTAccess().asList((RLanguage) castVector);
+                break;
+            case Expression:
+                vector = ((RExpression) castVector).getList();
+                break;
+            default:
+                vector = (RAbstractVector) castVector;
+                break;
+        }
+
+        int vectorLength = targetLengthProfile.profile(vector.getLength());
+        int[] vectorDimensions;
+        if (numberOfDimensions == 1) {
+            /* For single dimension case we never need to load the dimension. */
+            vectorDimensions = null;
+        } else {
+            assert numberOfDimensions > 1;
+            vectorDimensions = loadVectorDimensions(vector);
+        }
+
+        PositionProfile[] positionProfiles = positionsCheckNode.executeCheck(vector, vectorDimensions, vectorLength, positions);
+
+        if (castVectorNode != null) {
+            vector = (RAbstractVector) castVectorNode.execute(vector);
+        }
+
+        int replacementLength = positionsCheckNode.getSelectedPositionsCount(positionProfiles);
+        if (replacementLength == 0) {
+            /* Nothing to modify */
+            return vector;
+        }
+
+        if (valueLength != 1) {
+            verifyValueLength(positionProfiles, valueLength);
+        }
+
+        if (isList()) {
+            if (mode.isSubscript()) {
+                value = copyValueOnAssignment(value);
+            }
+        } else if (value instanceof RAbstractVector) {
+            value = ((RAbstractVector) value).castSafe(castType);
+        }
+
+        vector = share(vector);
+
+        int maxOutOfBounds = positionsCheckNode.getMaxOutOfBounds(positionProfiles);
+        if (maxOutOfBounds > vectorLength) {
+            resizeProfile.enter();
+            if (isDeleteElements() && mode.isSubscript()) {
+                return vector;
+            }
+            vector = resizeVector(vector, maxOutOfBounds);
+            vectorLength = maxOutOfBounds;
+        }
+        vector = vector.materialize();
+
+        vectorLength = targetLengthProfile.profile(vector.getLength());
+
+        if (mode.isSubset()) {
+            /*
+             * Interestingly we always need to provide not NOT_MULTIPLE_REPLACEMENT error messages
+             * for multi-dimensional deletes.
+             */
+            if ((this.numberOfDimensions > 1 && isDeleteElements()) || replacementLength % valueLength != 0) {
+                if (this.numberOfDimensions > 1) {
+                    errorBranch.enter();
+                    throw RError.error(this, RError.Message.NOT_MULTIPLE_REPLACEMENT);
+                } else {
+                    warningBranch.enter();
+                    RError.warning(this, RError.Message.NOT_MULTIPLE_REPLACEMENT);
+                }
+            }
+        }
+
+        writeVectorNode.enableValueNACheck(value);
+        writeVectorNode.apply(vector, vectorLength, positions, value, appliedValueLength, vectorDimensions);
+        vector.setComplete(vector.isComplete() && writeVectorNode.neverSeenNAInValue());
+        RNode.reportWork(this, replacementLength);
+
+        if (isDeleteElements()) {
+            assert deleteElementsNode != null;
+            vector = deleteElementsNode.deleteElements(vector, vectorLength);
+        } else if (this.numberOfDimensions == 1 && updatePositionNames) {
+            // depth must be == 0 to avoid recursive position name updates
+            updateVectorWithPositionNames(vector, positions);
+        }
+
+        return vector;
+    }
+
+    private RType verifyCastType(RType compatibleType) {
+        if (compatibleType == null && (vectorType.isNull() || vectorType.isVector())) {
+            Message message;
+            if (mode.isSubset()) {
+                message = RError.Message.SUBASSIGN_TYPE_FIX;
+            } else {
+                message = RError.Message.SUBSCRIPT_TYPES;
+            }
+            throw RError.error(this, message, valueType.getName(), vectorType.getName(), false);
+        }
+        return compatibleType;
+    }
+
+    private CastNode createCastVectorNode() {
+        if (castType == vectorType || castType == null || castType == RType.Null) {
+            return null;
+        }
+        /*
+         * All casts except list casts preserve dimension names.
+         */
+        if (castType == RType.List) {
+            return CastListNodeGen.create(true, false, true);
+        } else {
+            return CastTypeNode.createCast(castType, true, true, true);
+        }
+    }
+
+    private boolean isDeleteElements() {
+        return castType == RType.List && valueClass == RNull.class;
+    }
+
+    private boolean isList() {
+        return castType == RType.List;
+    }
+
+    private RType resolveCastVectorType() {
+        final RType vector;
+        // convert type for list like values
+        switch (this.vectorType) {
+            case Language:
+            case DataFrame:
+            case Expression:
+            case PairList:
+                vector = RType.List;
+                break;
+            default:
+                vector = this.vectorType;
+                break;
+        }
+
+        RType value = this.valueType;
+
+        if (vector.isVector() && value.isVector()) {
+            if (vector != value) {
+                if (vector == RType.List || value == RType.List) {
+                    return RType.List;
+                }
+                if (vector == RType.Raw || value == RType.Raw) {
+                    return null;
+                }
+            }
+            return RType.maxPrecedence(value, vector);
+        } else if (vector.isNull() || value.isNull()) {
+            if (!value.isNull()) {
+                return value;
+            }
+            if (mode.isSubscript() && numberOfDimensions > 1) {
+                return null;
+            }
+            return vector;
+        } else {
+            return null;
+        }
+    }
+
+    private void verifyValueLength(PositionProfile[] positionProfiles, int valueLength) {
+        if (mode.isSubscript()) {
+            if (!isList()) {
+                errorBranch.enter();
+                if (valueLength == 0) {
+                    throw RError.error(this, RError.Message.REPLACEMENT_0);
+                } else {
+                    throw RError.error(this, RError.Message.MORE_SUPPLIED_REPLACE);
+                }
+            }
+        } else {
+            assert mode.isSubset();
+            if (valueLength == 0) {
+                errorBranch.enter();
+                throw RError.error(this, RError.Message.REPLACEMENT_0);
+            } else if (positionsCheckNode.getContainsNA(positionProfiles)) {
+                errorBranch.enter();
+                throw RError.error(this, RError.Message.NA_SUBSCRIPTED);
+            }
+        }
+    }
+
+    private void updateVectorWithPositionNames(RAbstractVector vector, Object[] positions) {
+        Object position = positionsCheckNode.getPositionCheckAt(0).profilePosition(positions[0]);
+        RStringVector positionNames;
+        if (position instanceof RMissing) {
+            positionNames = null;
+        } else {
+            positionNames = ((RAbstractVector) position).getNames(positionNamesProfile);
+        }
+        if (positionNames != null && positionNames.getLength() > 0) {
+            updatePositionNames(vector, positionNames, positions);
+        }
+    }
+
+    private Object doEnvironment(REnvironment env, Object[] positions, Object originalValues) {
+        if (mode.isSubset()) {
+            errorBranch.enter();
+            throw RError.error(this, RError.Message.OBJECT_NOT_SUBSETTABLE, RType.Environment.getName());
+        }
+
+        String positionString = tryCastSingleString(positionsCheckNode, positions);
+        if (positionString != null) {
+            env.setAttr(positionString.intern(), originalValues);
+            return originalValues;
+        }
+        errorBranch.enter();
+        throw RError.error(this, RError.Message.WRONG_ARGS_SUBSET_ENV);
+    }
+
+    private final ConditionProfile sharedConditionProfile = ConditionProfile.createBinaryProfile();
+
+    private final ValueProfile sharedClassProfile = ValueProfile.createClassProfile();
+
+    /*
+     * TODO (chumer) share code between {@link #share(RAbstractVector)} and {@link
+     * #copyValueOnAssignment(RAbstractContainer)}
+     */
+    private RAbstractVector share(RAbstractVector vector) {
+        RAbstractVector returnVector = vector;
+        if (returnVector instanceof RShareable) {
+            RShareable shareable = (RShareable) returnVector;
+            // TODO find out if we need to copy always in the recursive case
+            if (sharedConditionProfile.profile(shareable.isShared()) || recursive) {
+                sharedProfile.enter();
+                shareable = (RShareable) returnVector.copy();
+                returnVector = (RAbstractVector) shareable;
+                if (FastROptions.NewStateTransition) {
+                    shareable.incRefCount();
+                } else if (shareable.isTemporary()) {
+                    shareable.markNonTemporary();
+                }
+            }
+        }
+        returnVector = sharedClassProfile.profile(returnVector);
+
+        CompilerAsserts.partialEvaluationConstant(returnVector.getClass());
+
+        return returnVector;
+    }
+
+    private RAbstractContainer copyValueOnAssignment(RAbstractContainer value) {
+        RShareable val = value.materializeToShareable();
+        if (FastROptions.NewStateTransition) {
+            if (rightIsShared.profile(val.isShared())) {
+                val = val.copy();
+            } else {
+                val.incRefCount();
+            }
+        } else {
+            if (rightIsShared.profile(val.isShared())) {
+                val = val.copy();
+            } else if (!val.isTemporary()) {
+                rightIsNonTemp.enter();
+                val.makeShared();
+            } else {
+                assert val.isTemporary();
+                rightIsTemp.enter();
+                val.markNonTemporary();
+            }
+        }
+        return (RAbstractContainer) val;
+    }
+
+    // TODO (chumer) this is way to compilicated at the moment
+    // not yet worth compiling. we should introduce some nodes for this
+    @TruffleBoundary
+    private static void copyAttributes(RAbstractVector vector, RList result) {
+        result.copyRegAttributesFrom(vector);
+    }
+
+    // TODO (chumer) this is way to complicated at the moment
+    // its not yet worth compiling it we need a better attribute system
+    @TruffleBoundary
+    private RVector resizeVector(RAbstractVector vector, int size) {
+        RStringVector oldNames = vector.getNames(vectorNamesProfile);
+        RVector res = vector.copyResized(size, true).materialize();
+        if (vector instanceof RVector) {
+            res.copyAttributesFrom(positionNamesProfile, vector);
+        }
+        res.setDimensionsNoCheck(null);
+        res.setDimNamesNoCheck(null);
+        if (oldNames != null) {
+            oldNames = oldNames.resizeWithEmpty(size);
+            res.setNames(oldNames);
+        }
+        return res;
+    }
+
+    private void updatePositionNames(RAbstractVector resultVector, RAbstractStringVector positionNames, Object[] positions) {
+        RTypedValue names = resultVector.getNames(positionNamesProfile);
+        if (names == null) {
+            String[] emptyVector = new String[resultVector.getLength()];
+            Arrays.fill(emptyVector, "");
+            names = RDataFactory.createStringVector(emptyVector, true);
+        }
+        if (copyPositionNames == null) {
+            CompilerDirectives.transferToInterpreterAndInvalidate();
+            copyPositionNames = insert(new CachedReplaceVectorNode(mode, names, positions, positionNames, false, recursive));
+        }
+        assert copyPositionNames.isSupported(names, positions, positionNames);
+        RAbstractStringVector newNames = (RAbstractStringVector) copyPositionNames.apply(names, positions, positionNames);
+        resultVector.setNames(newNames.materialize());
+    }
+
+    private static final class DeleteElementsNode extends Node {
+
+        private static final int PREVIOUS_RESULT_GENERIC = -2;
+        private static final int PREVIOUS_RESULT_UNINITIALIZED = -1;
+
+        /*
+         * We speculate here for the result length to be always the same. This way we can omit the
+         * first round of counting deleted elements.
+         */
+        @CompilationFinal private int previousResultLength = PREVIOUS_RESULT_UNINITIALIZED;
+
+        private final RAttributeProfiles vectorNamesProfile = RAttributeProfiles.create();
+
+        public RAbstractVector deleteElements(RAbstractVector vector, int vectorLength) {
+            // we can speculate here that we delete always the same number of elements
+            // without counting them.
+            int resultLength;
+            if (isPreviousResultSpecialized()) {
+                resultLength = previousResultLength;
+            } else if (previousResultLength == PREVIOUS_RESULT_UNINITIALIZED) {
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                resultLength = previousResultLength = calculateResultSize(vector, vectorLength);
+            } else {
+                resultLength = calculateResultSize(vector, vectorLength);
+            }
+
+            Object[] data = new Object[resultLength];
+            RStringVector names = vector.getNames(vectorNamesProfile);
+            boolean hasNames = names != null;
+            String[] newNames = null;
+            if (hasNames) {
+                newNames = new String[resultLength];
+            }
+
+            int resultIndex = 0;
+            for (int i = 0; i < vectorLength; i++) {
+                Object element = vector.getDataAtAsObject(i);
+                if (element != DELETE_MARKER) {
+                    data[resultIndex] = element;
+                    if (hasNames) {
+                        newNames[resultIndex] = names.getDataAt(i);
+                    }
+                    resultIndex++;
+                    if (isPreviousResultSpecialized() && resultIndex > resultLength) {
+                        // got too many elements
+                        CompilerDirectives.transferToInterpreterAndInvalidate();
+                        previousResultLength = PREVIOUS_RESULT_GENERIC;
+                        return deleteElements(vector, vectorLength);
+                    }
+                }
+            }
+            if (isPreviousResultSpecialized() && resultIndex != resultLength) {
+                // got less elements to delete
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                previousResultLength = PREVIOUS_RESULT_GENERIC;
+                return deleteElements(vector, vectorLength);
+            }
+
+            RStringVector newNamesVector = null;
+            if (hasNames) {
+                newNamesVector = RDataFactory.createStringVector(newNames, names.isComplete());
+            }
+            RList result = RDataFactory.createList(data, newNamesVector);
+            copyAttributes(vector, result);
+            return result;
+        }
+
+        private boolean isPreviousResultSpecialized() {
+            return previousResultLength >= 0;
+        }
+
+        private static int calculateResultSize(RAbstractVector vector, int vectorLength) {
+            int resultSize = 0;
+            for (int i = 0; i < vectorLength; i++) {
+                if (vector.getDataAtAsObject(i) != DELETE_MARKER) {
+                    resultSize++;
+                }
+            }
+            return resultSize;
+        }
+    }
+
+}
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/CachedVectorNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/CachedVectorNode.java
new file mode 100644
index 0000000000000000000000000000000000000000..339fa59bc46ddef4cf423e04b03c9f35ea0af308
--- /dev/null
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/CachedVectorNode.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright (c) 2015, 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.access.vector;
+
+import com.oracle.truffle.api.*;
+import com.oracle.truffle.api.nodes.*;
+import com.oracle.truffle.api.utilities.*;
+import com.oracle.truffle.r.runtime.*;
+import com.oracle.truffle.r.runtime.data.*;
+import com.oracle.truffle.r.runtime.data.model.*;
+import com.oracle.truffle.r.runtime.nodes.*;
+
+abstract class CachedVectorNode extends RBaseNode {
+
+    protected final ElementAccessMode mode;
+    protected final RType vectorType;
+
+    /**
+     * Recursive indicates that the vector node is used inside {@link RecursiveReplaceSubscriptNode}
+     * or {@link RecursiveExtractSubscriptNode}.
+     */
+    protected final boolean recursive;
+
+    protected final BranchProfile errorBranch = BranchProfile.create();
+    protected final int numberOfDimensions;
+    protected final int filteredPositionsLength;
+
+    @Child private GetDataFrameDimensionNode getDataFrameDimension;
+
+    public CachedVectorNode(ElementAccessMode mode, RTypedValue vector, Object[] positions, boolean recursive) {
+        this.mode = mode;
+        this.vectorType = vector.getRType();
+        this.recursive = recursive;
+        this.filteredPositionsLength = initializeFilteredPositionsCount(positions);
+        if (filteredPositionsLength == -1) {
+            this.numberOfDimensions = positions.length;
+        } else {
+            this.numberOfDimensions = filteredPositionsLength;
+        }
+        if (!isSubsetable(vectorType)) {
+            throw RError.error(this, RError.Message.OBJECT_NOT_SUBSETTABLE, vectorType.getName());
+        }
+    }
+
+    protected int initializeFilteredPositionsCount(Object[] positions) {
+        int dimensions = 0;
+        for (int i = 0; i < positions.length; i++) {
+            // for cases like RMissing the position does not contribute to the number of dimensions
+            if (!isRemovePosition(positions[i])) {
+                dimensions++;
+            }
+        }
+        if (positions.length == dimensions || dimensions <= 0) {
+            return -1;
+        } else {
+            return dimensions;
+        }
+    }
+
+    @ExplodeLoop
+    protected Object[] filterPositions(Object[] positions) {
+        // we assume that the positions count cannot change as the isRemovePosition check
+        // is just based on types and therefore does not change per position instance.
+        assert initializeFilteredPositionsCount(positions) == filteredPositionsLength;
+        if (filteredPositionsLength != -1) {
+            Object[] newPositions = new Object[filteredPositionsLength];
+            int newPositionIndex = 0;
+            for (int i = 0; i < filteredPositionsLength; i++) {
+                Object position = positions[i];
+                if (!isRemovePosition(position)) {
+                    newPositions[newPositionIndex++] = position;
+                }
+            }
+            return newPositions;
+        }
+        return positions;
+    }
+
+    private static boolean isRemovePosition(Object positions) {
+        return positions instanceof RMissing;
+    }
+
+    protected static boolean logicalAsBoolean(RTypedValue cast, boolean defaultValue) {
+        if (cast instanceof RMissing) {
+            return defaultValue;
+        } else {
+            RAbstractLogicalVector logical = (RAbstractLogicalVector) cast;
+            if (logical.getLength() == 0) {
+                return defaultValue;
+            } else {
+                return logical.getDataAt(0) == RRuntime.LOGICAL_TRUE;
+            }
+        }
+    }
+
+    private static boolean isSubsetable(RType type) {
+        if (type.isVector()) {
+            return true;
+        }
+        switch (type) {
+            case Null:
+            case Language:
+            case Factor:
+            case PairList:
+            case Environment:
+            case Expression:
+                return true;
+            default:
+                return false;
+        }
+    }
+
+    protected final int[] loadVectorDimensions(RAbstractContainer vector) {
+        if (vector instanceof RDataFrame) {
+            // TODO (chumer) its unfortunate that we need this hack for the data frame dimensions
+            // maybe we can rid of this as soon as a data frame is of the same type as a list.
+            if (getDataFrameDimension == null) {
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                getDataFrameDimension = insert(new GetDataFrameDimensionNode());
+            }
+            return getDataFrameDimension.calculateFrameDimensions((RDataFrame) vector);
+        } else {
+            return vector.getDimensions();
+        }
+    }
+
+    public ElementAccessMode getMode() {
+        return mode;
+    }
+
+    protected String tryCastSingleString(PositionsCheckNode check, Object[] positions) {
+        if (numberOfDimensions > 1) {
+            return null;
+        }
+
+        String positionString = null;
+        Object position = check.getPositionCheckAt(0).getPositionClass().cast(positions[0]);
+        if (position instanceof String) {
+            positionString = (String) position;
+        } else if (position instanceof RAbstractStringVector) {
+            RAbstractStringVector vector = (RAbstractStringVector) position;
+            if (vector.getLength() == 1) {
+                positionString = vector.getDataAt(0);
+            }
+        }
+        return positionString;
+    }
+
+    private final class GetDataFrameDimensionNode extends Node {
+
+        private final ConditionProfile compressedProfile = ConditionProfile.createBinaryProfile();
+        private final RAttributeProfiles attrProfiles = RAttributeProfiles.create();
+        private final ValueProfile rowNamesProfile = ValueProfile.createClassProfile();
+        private final ConditionProfile intVecProfile = ConditionProfile.createBinaryProfile();
+        private final ConditionProfile nameConditionProfile = ConditionProfile.createBinaryProfile();
+
+        public int[] calculateFrameDimensions(RDataFrame container) {
+            // this largely reproduces code from ShortRowNames
+            Object rowNames = container.getRowNames(attrProfiles);
+            if (nameConditionProfile.profile(rowNames == RNull.instance)) {
+                return new int[]{0, container.getLength()};
+            } else {
+                return new int[]{Math.abs(calculateN((RAbstractVector) rowNames)), container.getLength()};
+            }
+        }
+
+        private int calculateN(RAbstractVector rowNames) {
+            RAbstractVector profiledRowNames = rowNamesProfile.profile(rowNames);
+            if (intVecProfile.profile(profiledRowNames.getRType() == RType.Integer && profiledRowNames.getLength() == 2)) {
+                RAbstractIntVector rowNamesIntVector = (RAbstractIntVector) profiledRowNames;
+                if (compressedProfile.profile(RRuntime.isNA(rowNamesIntVector.getDataAt(0)))) {
+                    return rowNamesIntVector.getDataAt(1);
+                } else {
+                    return profiledRowNames.getLength();
+                }
+            } else {
+                return profiledRowNames.getLength();
+            }
+        }
+
+    }
+
+}
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/model/RAccessibleStore.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/ElementAccessMode.java
similarity index 66%
rename from com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/model/RAccessibleStore.java
rename to com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/ElementAccessMode.java
index a931d56148bdf2a3dd9eb89e8141989230ba5508..e0fe53bd1131389fb151c95f530e226d54f38bca 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/model/RAccessibleStore.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/ElementAccessMode.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 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
@@ -20,15 +20,22 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.runtime.data.model;
+package com.oracle.truffle.r.nodes.access.vector;
 
-/**
- * Marks a vectors internal store to be accessible. As the internal store depends on the
- * implementation of the vector, users of this method are discouraged to use this interface unless
- * it is really necessary. One reason might be to avoid store field lookups when traversing vectors.
- */
-public interface RAccessibleStore<T> {
+public enum ElementAccessMode {
+
+    /* x[[a]] */
+    SUBSCRIPT,
+
+    /* x[a] */
+    SUBSET;
+
+    public boolean isSubset() {
+        return this == SUBSET;
+    }
 
-    T getInternalStore();
+    public boolean isSubscript() {
+        return this == SUBSCRIPT;
+    }
 
 }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/ExtractVectorNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/ExtractVectorNode.java
new file mode 100644
index 0000000000000000000000000000000000000000..3e02b18887e815a8a30f35a96ec5c5493cc804d0
--- /dev/null
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/ExtractVectorNode.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 2015, 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.access.vector;
+
+import com.oracle.truffle.api.*;
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.dsl.*;
+import com.oracle.truffle.api.nodes.*;
+import com.oracle.truffle.r.nodes.binary.*;
+import com.oracle.truffle.r.nodes.profile.*;
+import com.oracle.truffle.r.runtime.data.*;
+import com.oracle.truffle.r.runtime.data.model.*;
+
+public abstract class ExtractVectorNode extends Node {
+
+    /*
+     * TODO remove this as soon as the new vector access nodes are the default.
+     */
+    public static final boolean USE_NODE = ReplaceVectorNode.USE_NODE;
+
+    protected static final int CACHE_LIMIT = 5;
+
+    protected final ElementAccessMode mode;
+    private final boolean recursive;
+
+    @Child private BoxPrimitiveNode boxVector = BoxPrimitiveNode.create();
+    @Child private BoxPrimitiveNode boxExact = BoxPrimitiveNode.create();
+    @Child private BoxPrimitiveNode boxDropdimensions = BoxPrimitiveNode.create();
+
+    ExtractVectorNode(ElementAccessMode mode, boolean recursive) {
+        this.mode = mode;
+        this.recursive = recursive;
+    }
+
+    public ElementAccessMode getMode() {
+        return mode;
+    }
+
+    public final Object apply(Object vector, Object[] positions, Object exact, Object dropDimensions) {
+        return execute(boxVector.execute(vector), positions, boxExact.execute(exact), boxDropdimensions.execute(dropDimensions));
+    }
+
+    public static ExtractVectorNode create(ElementAccessMode accessMode) {
+        return ExtractVectorNodeGen.create(accessMode, false);
+    }
+
+    static ExtractVectorNode createRecursive(ElementAccessMode accessMode) {
+        return ExtractVectorNodeGen.create(accessMode, true);
+    }
+
+    protected abstract Object execute(Object vector, Object[] positions, Object exact, Object dropDimensions);
+
+    @Specialization(guards = {"cached != null", "cached.isSupported(vector, positions)"})
+    protected Object doReplaceRecursive(RAbstractListVector vector, Object[] positions, Object exact, Object dropDimensions,  //
+                    @Cached("createRecursiveCache(vector, positions)") RecursiveExtractSubscriptNode cached) {
+        return cached.apply(vector, positions, exact, dropDimensions);
+    }
+
+    protected RecursiveExtractSubscriptNode createRecursiveCache(Object vector, Object[] positions) {
+        if (isRecursiveSubscript(vector, positions)) {
+            return RecursiveExtractSubscriptNode.create((RAbstractListVector) vector, positions[0]);
+        }
+        return null;
+    }
+
+    protected boolean isRecursiveSubscript(Object vector, Object[] positions) {
+        return !recursive && mode.isSubscript() && vector instanceof RAbstractListVector && positions.length == 1;
+    }
+
+    @Specialization(limit = "CACHE_LIMIT", guards = {"cached != null", "cached.isSupported(vector, positions, exact, dropDimensions)"})
+    protected Object doReplaceDefaultCached(Object vector, Object[] positions, Object exact, Object dropDimensions,  //
+                    @Cached("createDefaultCache(getThis(), vector, positions, exact, dropDimensions)") CachedExtractVectorNode cached) {
+        assert !isRecursiveSubscript(vector, positions);
+        return cached.apply(vector, positions, null, exact, dropDimensions);
+    }
+
+    protected static CachedExtractVectorNode createDefaultCache(ExtractVectorNode node, Object vector, Object[] positions, Object exact, Object dropDimensions) {
+        return new CachedExtractVectorNode(node.getMode(), (RTypedValue) vector, positions, (RTypedValue) exact, (RTypedValue) dropDimensions, node.recursive);
+    }
+
+    @Specialization(contains = "doReplaceDefaultCached")
+    @TruffleBoundary
+    protected Object doReplaceDefaultGeneric(Object vector, Object[] positions, Object exact, Object dropDimensions,  //
+                    @Cached("new(createDefaultCache(getThis(), vector, positions, exact, dropDimensions))") GenericVectorExtractNode generic) {
+        return generic.get(this, vector, positions, exact, dropDimensions).apply(vector, positions, null, exact, dropDimensions);
+    }
+
+    // TODO hack until Truffle-DSL supports this.
+    protected ExtractVectorNode getThis() {
+        return this;
+    }
+
+    protected static final class GenericVectorExtractNode extends TruffleBoundaryNode {
+
+        @Child private CachedExtractVectorNode cached;
+
+        public GenericVectorExtractNode(CachedExtractVectorNode cachedOperation) {
+            this.cached = insert(cachedOperation);
+        }
+
+        public CachedExtractVectorNode get(ExtractVectorNode node, Object vector, Object[] positions, Object exact, Object dropDimensions) {
+            CompilerAsserts.neverPartOfCompilation();
+            if (!cached.isSupported(vector, positions, exact, dropDimensions)) {
+                cached = cached.replace(createDefaultCache(node, vector, positions, exact, dropDimensions));
+            }
+            return cached;
+        }
+
+    }
+
+}
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/PositionCastNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/PositionCastNode.java
new file mode 100644
index 0000000000000000000000000000000000000000..fc1ab281272b49e6e8c3bd8d0577479ca6d85889
--- /dev/null
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/PositionCastNode.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (c) 2015, 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.access.vector;
+
+import com.oracle.truffle.api.dsl.*;
+import com.oracle.truffle.api.nodes.*;
+import com.oracle.truffle.r.nodes.binary.*;
+import com.oracle.truffle.r.nodes.unary.*;
+import com.oracle.truffle.r.runtime.*;
+import com.oracle.truffle.r.runtime.data.*;
+import com.oracle.truffle.r.runtime.data.model.*;
+import com.oracle.truffle.r.runtime.env.*;
+import com.oracle.truffle.r.runtime.ops.na.*;
+
+@SuppressWarnings("unused")
+abstract class PositionCastNode extends Node {
+
+    private final ElementAccessMode mode;
+    private final boolean replace;
+
+    PositionCastNode(ElementAccessMode mode, boolean replace) {
+        this.mode = mode;
+        this.replace = replace;
+    }
+
+    public static PositionCastNode create(ElementAccessMode mode, boolean replace) {
+        return PositionCastNodeGen.create(mode, replace);
+    }
+
+    public abstract RTypedValue execute(Object position);
+
+    // TODO these boxing specializations can be removed as we enabled boxing of every value.
+    @Specialization
+    protected RAbstractVector doInteger(int position) {
+        return RInteger.valueOf(position);
+    }
+
+    @Specialization
+    protected RAbstractVector doString(String position) {
+        return RString.valueOf(position);
+    }
+
+    @Specialization
+    protected RAbstractVector doString(RAbstractStringVector position) {
+        // directly supported
+        return position;
+    }
+
+    @Specialization
+    protected RAbstractVector doInteger(RAbstractIntVector position) {
+        // directly supported
+        return position;
+    }
+
+    @Specialization
+    protected RAbstractVector doFactor(RFactor position) {
+        return position.getVector();
+    }
+
+    @Specialization
+    protected RAbstractVector doDouble(double position, @Cached("create()") NACheck check) {
+        if (mode.isSubscript()) {
+            check.enable(position);
+            return RInteger.valueOf(check.convertDoubleToInt(position));
+        } else {
+            return RDouble.valueOf(position);
+        }
+    }
+
+    @Specialization
+    protected RAbstractVector doDouble(RAbstractDoubleVector position, //
+                    @Cached("createIntegerCast()") CastIntegerNode cast, //
+                    @Cached("create()") BoxPrimitiveNode box) {
+        if (mode.isSubscript()) {
+            // double gets casted to integer for subscript
+            return (RAbstractVector) box.execute(cast.execute(position));
+        } else {
+            // because we need to perform a special bounds check with doubles
+            // we cannot yet convert the double array to int for subsets
+            return (RAbstractVector) box.execute(position);
+        }
+    }
+
+    protected CastIntegerNode createIntegerCast() {
+        return CastIntegerNodeGen.create(true, false, false);
+    }
+
+    @Specialization
+    protected RAbstractVector doLogical(byte position) {
+        // directly supported
+        return RLogical.valueOf(position);
+    }
+
+    @Specialization
+    protected RAbstractVector doLogical(RAbstractLogicalVector position) {
+        // directly supported
+        return position;
+    }
+
+    @Specialization
+    protected RMissing doSymbol(RSymbol position) {
+        if (position.getName().length() == 0) {
+            return doMissing(RMissing.instance);
+        } else {
+            throw RError.error(this, RError.Message.INVALID_SUBSCRIPT_TYPE, "symbol");
+        }
+    }
+
+    @Specialization
+    protected RMissing doMissing(RMissing position) {
+        if (mode.isSubscript()) {
+            if (replace) {
+                throw RError.error(this, RError.Message.MISSING_SUBSCRIPT);
+            } else {
+                throw RError.error(this, RError.Message.INVALID_SUBSCRIPT_TYPE, "symbol");
+            }
+        } else {
+            return RMissing.instance;
+        }
+    }
+
+    @Specialization
+    protected RMissing doEmpty(REmpty position) {
+        return doMissing(null);
+    }
+
+    @Specialization
+    protected RAbstractVector doNull(RNull position) {
+        // NULL expands to integer(0).
+        return RDataFactory.createEmptyIntVector();
+    }
+
+    @Specialization(guards = "getInvalidType(position) != null")
+    protected RAbstractVector doInvalidType(Object position) {
+        throw RError.error(this, RError.Message.INVALID_SUBSCRIPT_TYPE, getInvalidType(position).getName());
+    }
+
+    protected static RType getInvalidType(Object positionValue) {
+        if (positionValue instanceof RAbstractRawVector) {
+            return RType.Raw;
+        } else if (positionValue instanceof RAbstractListVector) {
+            return RType.List;
+        } else if (positionValue instanceof RFunction) {
+            return RType.Closure;
+        } else if (positionValue instanceof REnvironment) {
+            return RType.Environment;
+        } else if (positionValue instanceof RAbstractComplexVector) {
+            return RType.Complex;
+        }
+        return null;
+    }
+
+}
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/PositionCharacterLookupNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/PositionCharacterLookupNode.java
new file mode 100644
index 0000000000000000000000000000000000000000..b16a4a29802ffce824d89451e62aa914a982ce97
--- /dev/null
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/PositionCharacterLookupNode.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2015, 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.access.vector;
+
+import com.oracle.truffle.api.nodes.*;
+import com.oracle.truffle.api.utilities.*;
+import com.oracle.truffle.r.runtime.*;
+import com.oracle.truffle.r.runtime.RError.Message;
+import com.oracle.truffle.r.runtime.data.*;
+import com.oracle.truffle.r.runtime.data.model.*;
+
+final class PositionCharacterLookupNode extends Node {
+
+    private final ElementAccessMode mode;
+    private final RAttributeProfiles attributeProfiles = RAttributeProfiles.create();
+    private final int numDimensions;
+    private final int dimensionIndex;
+    private final BranchProfile emptyProfile = BranchProfile.create();
+
+    @Child private SearchFirstStringNode searchNode;
+
+    public PositionCharacterLookupNode(ElementAccessMode mode, int numDimensions, int dimensionIndex, boolean useNAForNotFound, boolean exact) {
+        this.numDimensions = numDimensions;
+        this.dimensionIndex = dimensionIndex;
+        this.searchNode = SearchFirstStringNode.createNode(exact, useNAForNotFound);
+        this.mode = mode;
+    }
+
+    public RAbstractIntVector execute(RAbstractContainer target, RAbstractStringVector position, int notFoundStartIndex) {
+        // lookup names for single dimension case
+        RAbstractIntVector result;
+        if (numDimensions <= 1) {
+            RStringVector names = target.getNames(attributeProfiles);
+            if (names == null) {
+                emptyProfile.enter();
+                names = RDataFactory.createEmptyStringVector();
+            }
+            result = searchNode.apply(names, position, notFoundStartIndex);
+            result.setNames(position.materialize());
+        } else {
+            RList dimNames = target.getDimNames(attributeProfiles);
+            if (dimNames != null) {
+                Object dataAt = dimNames.getDataAt(dimensionIndex);
+                if (dataAt != RNull.instance) {
+                    RStringVector dimName = (RStringVector) dataAt;
+                    result = searchNode.apply(dimName, position, notFoundStartIndex);
+                } else {
+                    emptyProfile.enter();
+                    throw noDimNames();
+                }
+            } else {
+                emptyProfile.enter();
+                throw noDimNames();
+            }
+
+        }
+        return result;
+    }
+
+    private RError noDimNames() {
+        if (mode.isSubset()) {
+            return RError.error(this, Message.NO_ARRAY_DIMNAMES);
+        } else {
+            return RError.error(this, Message.SUBSCRIPT_BOUNDS);
+        }
+    }
+
+}
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/PositionCheckNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/PositionCheckNode.java
new file mode 100644
index 0000000000000000000000000000000000000000..4e4a3872305a7938723b527a11b67b8d69b371f8
--- /dev/null
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/PositionCheckNode.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 2015, 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.access.vector;
+
+import com.oracle.truffle.api.nodes.*;
+import com.oracle.truffle.api.utilities.*;
+import com.oracle.truffle.r.nodes.access.vector.PositionsCheckNode.PositionProfile;
+import com.oracle.truffle.r.nodes.control.*;
+import com.oracle.truffle.r.nodes.profile.*;
+import com.oracle.truffle.r.runtime.*;
+import com.oracle.truffle.r.runtime.data.*;
+import com.oracle.truffle.r.runtime.data.model.*;
+
+abstract class PositionCheckNode extends Node {
+
+    protected final Class<?> positionClass;
+    protected final int dimensionIndex;
+    protected final int numDimensions;
+    protected final VectorLengthProfile positionLengthProfile = VectorLengthProfile.create();
+    protected final BranchProfile error = BranchProfile.create();
+    protected final boolean replace;
+    protected final RType containerType;
+    @Child private PositionCastNode castNode;
+    @Child private RLengthNode positionLengthNode = RLengthNode.create();
+    @Child private PositionCharacterLookupNode characterLookup;
+
+    PositionCheckNode(ElementAccessMode mode, RType containerType, Object positionValue, int dimensionIndex, int numDimensions, boolean exact, boolean replace) {
+        this.positionClass = positionValue.getClass();
+        this.dimensionIndex = dimensionIndex;
+        this.numDimensions = numDimensions;
+        this.replace = replace;
+        this.containerType = containerType;
+        this.castNode = PositionCastNode.create(mode, replace);
+        if (positionValue instanceof String || positionValue instanceof RAbstractStringVector) {
+            boolean useNAForNotFound = !replace && containerType == RType.List && mode.isSubscript();
+            characterLookup = new PositionCharacterLookupNode(mode, numDimensions, dimensionIndex, useNAForNotFound, exact);
+        }
+    }
+
+    public boolean isIgnoreDimension() {
+        return positionClass == RMissing.class;
+    }
+
+    public Class<?> getPositionClass() {
+        return positionClass;
+    }
+
+    public final boolean isSupported(Object object) {
+        return object.getClass() == positionClass;
+    }
+
+    public static PositionCheckNode createNode(ElementAccessMode mode, RType containerType, Object position, int positionIndex, int numDimensions, boolean exact, boolean replace, boolean recursive) {
+        if (mode.isSubset()) {
+            return PositionCheckSubsetNodeGen.create(mode, containerType, position, positionIndex, numDimensions, exact, replace);
+        } else {
+            return PositionCheckSubscriptNodeGen.create(mode, containerType, position, positionIndex, numDimensions, exact, replace, recursive);
+        }
+    }
+
+    protected boolean isMultiDimension() {
+        return numDimensions > 1;
+    }
+
+    public final Object execute(PositionProfile profile, RAbstractContainer vector, int[] vectorDimensions, int vectorLength, Object position) {
+        Object castPosition = castNode.execute(positionClass.cast(position));
+
+        int dimensionLength;
+        if (numDimensions == 1) {
+            dimensionLength = vectorLength;
+        } else {
+            assert vectorDimensions != null;
+            assert vectorDimensions.length == numDimensions;
+            dimensionLength = vectorDimensions[dimensionIndex];
+        }
+
+        if (characterLookup != null) {
+            castPosition = characterLookup.execute(vector, (RAbstractStringVector) castPosition, dimensionLength);
+        }
+
+        RTypedValue positionVector = (RTypedValue) profilePosition(castPosition);
+
+        int positionLength;
+        if (positionVector instanceof RMissing) {
+            positionLength = -1;
+        } else {
+            positionLength = positionLengthProfile.profile(((RAbstractVector) positionVector).getLength());
+        }
+
+        assert isValidCastedType(positionVector) : "result type of a position cast node must be integer or logical";
+
+        return execute(profile, dimensionLength, positionVector, positionLength);
+    }
+
+    private final ValueProfile castedValue = ValueProfile.createClassProfile();
+
+    Object profilePosition(Object positionVector) {
+        return castedValue.profile(positionVector);
+    }
+
+    private static boolean isValidCastedType(RTypedValue positionVector) {
+        RType type = positionVector.getRType();
+        return type == RType.Integer || type == RType.Logical || type == RType.Character || type == RType.Double || type == RType.Null;
+    }
+
+    public abstract Object execute(PositionProfile statistics, int dimensionLength, Object position, int positionLength);
+
+}
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/PositionCheckSubscriptNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/PositionCheckSubscriptNode.java
new file mode 100644
index 0000000000000000000000000000000000000000..43643d26027d9205dd0b55bb235d82aab26ce710
--- /dev/null
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/PositionCheckSubscriptNode.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (c) 2015, 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.access.vector;
+
+import com.oracle.truffle.api.dsl.*;
+import com.oracle.truffle.api.utilities.*;
+import com.oracle.truffle.r.nodes.access.vector.PositionsCheckNode.PositionProfile;
+import com.oracle.truffle.r.runtime.*;
+import com.oracle.truffle.r.runtime.RError.Message;
+import com.oracle.truffle.r.runtime.data.*;
+import com.oracle.truffle.r.runtime.data.model.*;
+import com.oracle.truffle.r.runtime.ops.na.*;
+
+abstract class PositionCheckSubscriptNode extends PositionCheckNode {
+
+    private final NACheck positionNACheck = NACheck.create();
+    private final ConditionProfile greaterZero = ConditionProfile.createBinaryProfile();
+
+    private final boolean recursive;
+
+    private final RAttributeProfiles attributeProfile = RAttributeProfiles.create();
+
+    PositionCheckSubscriptNode(ElementAccessMode mode, RType containerType, Object positionValue, int dimensionIndex, int numDimensions, boolean exact, boolean assignment, boolean recursive) {
+        super(mode, containerType, positionValue, dimensionIndex, numDimensions, exact, assignment);
+        this.recursive = recursive;
+    }
+
+    @SuppressWarnings("unused")
+    @Specialization
+    protected Object doMissing(PositionProfile statistics, int dimSize, RMissing position, int positionLength) {
+        statistics.selectedPositionsCount = dimSize;
+        return position;
+    }
+
+    @Specialization
+    protected RAbstractVector doLogical(PositionProfile statistics, int dimSize, RAbstractLogicalVector position, int positionLength) {
+        positionNACheck.enable(position);
+        byte value = position.getDataAt(0);
+        if (positionLength != 1) {
+            error.enter();
+            if (positionLength >= 3) {
+                throw RError.error(this, RError.Message.SELECT_MORE_1);
+            } else {
+                if (value == RRuntime.LOGICAL_TRUE) {
+                    throw RError.error(this, RError.Message.SELECT_MORE_1);
+                } else {
+                    throw RError.error(this, RError.Message.SELECT_LESS_1);
+                }
+            }
+        }
+
+        return doIntegerImpl(statistics, dimSize, positionNACheck.convertLogicalToInt(value), position);
+    }
+
+    @Specialization
+    protected RAbstractVector doInteger(PositionProfile profile, int dimSize, RAbstractIntVector position, int positionLength) {
+        if (positionLength != 1) {
+            error.enter();
+            if (positionLength > 1) {
+                throw RError.error(this, RError.Message.SELECT_MORE_1);
+            } else {
+                throw RError.error(this, RError.Message.SELECT_LESS_1);
+            }
+        }
+        assert positionLength == 1;
+        positionNACheck.enable(position);
+        return doIntegerImpl(profile, dimSize, position.getDataAt(0), position);
+    }
+
+    private RAbstractVector doIntegerImpl(PositionProfile profile, int dimSize, int value, RAbstractVector originalVector) {
+        int result;
+        if (greaterZero.profile(value > 0)) {
+            // fast path
+            assert RRuntime.INT_NA <= 0;
+            result = value;
+            if (!replace && result > dimSize) {
+                error.enter();
+                throwBoundsError();
+            }
+            profile.maxOutOfBoundsIndex = result;
+        } else {
+            // slow path
+            result = doIntegerSlowPath(profile, dimSize, value);
+        }
+        profile.selectedPositionsCount = 1;
+
+        RStringVector names = originalVector.getNames(attributeProfile);
+        if (names != null) {
+            return RDataFactory.createIntVector(new int[]{result}, !profile.containsNA, names);
+        } else {
+            return RInteger.valueOf(result);
+        }
+    }
+
+    private int doIntegerSlowPath(PositionProfile profile, int dimSize, int value) {
+        positionNACheck.enable(value);
+        if (positionNACheck.check(value)) {
+            handleNA(dimSize);
+            profile.containsNA = true;
+            return value;
+        } else {
+            if (dimSize == 2) {
+                if (value == -2) {
+                    return 1;
+                } else if (value == -1) {
+                    return 2;
+                }
+            }
+            error.enter();
+            if (value == 0 || dimSize <= 2) {
+                throw RError.error(this, RError.Message.SELECT_LESS_1);
+            } else {
+                throw RError.error(this, RError.Message.SELECT_MORE_1);
+            }
+        }
+    }
+
+    private void handleNA(int dimSize) {
+        if (replace) {
+            error.enter();
+            Message message;
+            if (isMultiDimension()) {
+                message = RError.Message.SUBSCRIPT_BOUNDS_SUB;
+            } else {
+                if (dimSize < 2) {
+                    message = RError.Message.SELECT_LESS_1;
+                } else {
+                    message = RError.Message.SELECT_MORE_1;
+                }
+            }
+            throw RError.error(this, message);
+        } else {
+            if (containerType == RType.List && numDimensions == 1) {
+                // lists pass on the NA value
+            } else {
+                error.enter();
+                throwBoundsError();
+            }
+        }
+    }
+
+    private void throwBoundsError() {
+        if (recursive) {
+            throw new RecursiveIndexNotFoundError();
+        } else {
+            throw RError.error(this, RError.Message.SUBSCRIPT_BOUNDS);
+        }
+    }
+
+}
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/PositionCheckSubsetNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/PositionCheckSubsetNode.java
new file mode 100644
index 0000000000000000000000000000000000000000..39be205e5cd8d3303bd89f696410c6b42289882d
--- /dev/null
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/PositionCheckSubsetNode.java
@@ -0,0 +1,291 @@
+/*
+ * Copyright (c) 2015, 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.access.vector;
+
+import java.util.*;
+
+import com.oracle.truffle.api.dsl.*;
+import com.oracle.truffle.api.utilities.*;
+import com.oracle.truffle.r.nodes.access.vector.PositionsCheckNode.PositionProfile;
+import com.oracle.truffle.r.runtime.*;
+import com.oracle.truffle.r.runtime.data.*;
+import com.oracle.truffle.r.runtime.data.model.*;
+import com.oracle.truffle.r.runtime.ops.na.*;
+
+abstract class PositionCheckSubsetNode extends PositionCheckNode {
+
+    private final NACheck positionNACheck = NACheck.create();
+
+    PositionCheckSubsetNode(ElementAccessMode mode, RType containerType, Object positionValue, int dimensionIndex, int numDimensions, boolean exact, boolean assignment) {
+        super(mode, containerType, positionValue, dimensionIndex, numDimensions, exact, assignment);
+    }
+
+    @SuppressWarnings("unused")
+    @Specialization
+    protected Object doMissing(PositionProfile statistics, int dimSize, RMissing position, int positionLength) {
+        statistics.selectedPositionsCount = dimSize;
+        return position;
+    }
+
+    @Specialization(guards = {"isMultiplesOf(dimensionLength, positionLength)", "positionLength <= dimensionLength"})
+    protected RAbstractVector doLogicalMultiplesInBounds(PositionProfile statistics, int dimensionLength, RAbstractLogicalVector position, int positionLength) {
+        assert positionLength > 0;
+        positionNACheck.enable(position);
+        int elementCount = 0;
+        boolean hasSeenNA = false;
+        for (int i = 0; i < positionLength; i++) {
+            byte positionValue = position.getDataAt(i);
+            if (positionNACheck.check(positionValue)) {
+                hasSeenNA = true;
+                elementCount++;
+            }
+            if (positionValue == RRuntime.LOGICAL_TRUE) {
+                elementCount++;
+            }
+        }
+        statistics.containsNA = hasSeenNA;
+        statistics.maxOutOfBoundsIndex = positionLength;
+        statistics.selectedPositionsCount = elementCount * (dimensionLength / positionLength);
+        return position;
+    }
+
+    protected static boolean isMultiplesOf(int a, int b) {
+        return b != 0 && a % b == 0;
+    }
+
+    @Specialization(contains = "doLogicalMultiplesInBounds")
+    protected RAbstractVector doLogicalGenericInBounds(PositionProfile statistics,  //
+                    int dimensionLength, RAbstractLogicalVector position, int positionLength, @Cached("create()") BranchProfile outOfBoundsProfile) {
+        positionNACheck.enable(position);
+        int positionIndex = 0;
+        int elementCount = 0;
+        boolean hasSeenNA = false;
+        if (positionLength > 0) {
+            int length = dimensionLength;
+            if (length < positionLength) {
+                outOfBoundsProfile.enter();
+                if (isMultiDimension()) {
+                    error.enter();
+                    throw RError.error(this, RError.Message.LOGICAL_SUBSCRIPT_LONG);
+                }
+                length = positionLength;
+            }
+            for (int i = 0; i < length; i++) {
+                byte positionValue = position.getDataAt(positionIndex);
+                // boolean outOfBounds = outOfBoundsProfile.isVisited() && i >= dimensionLength;
+                if (positionNACheck.check(positionValue)) {
+                    hasSeenNA = true;
+                    elementCount++;
+                }
+                if (positionValue == RRuntime.LOGICAL_TRUE) {
+                    elementCount++;
+                }
+                positionIndex = Utils.incMod(positionIndex, positionLength);
+            }
+        }
+        statistics.containsNA = hasSeenNA;
+        statistics.maxOutOfBoundsIndex = positionLength;
+        statistics.selectedPositionsCount = elementCount;
+        return position;
+    }
+
+    private final RAttributeProfiles attrProfiles = RAttributeProfiles.create();
+
+    @Specialization(/* contains = "doSequence" */)
+    protected RAbstractVector doDouble(PositionProfile profile, int dimensionLength, RAbstractDoubleVector position, int positionLength, //
+                    @Cached("create()") BranchProfile seenZeroProfile, //
+                    @Cached("create()") BranchProfile seenPositiveProfile, //
+                    @Cached("create()") BranchProfile seenNegativeProfile, //
+                    @Cached("create()") BranchProfile seenOutOfBounds, //
+                    @Cached("create()") NullProfile hasNamesProfile) {
+        RAbstractIntVector intPosition = RDataFactory.createIntVector(positionLength);
+        // requires names preservation
+        RStringVector names = hasNamesProfile.profile(position.getNames(attrProfiles));
+        if (names != null) {
+            intPosition.setNames(names);
+        }
+        Object convertedStore = intPosition.getInternalStore();
+
+        positionNACheck.enable(position);
+        boolean hasSeenPositive = false;
+        boolean hasSeenNegative = false;
+        boolean hasSeenNA = false;
+        int outOfBoundsCount = 0;
+        int zeroCount = 0;
+        int maxOutOfBoundsIndex = 0;
+        for (int i = 0; i < positionLength; i++) {
+            double positionValue = position.getDataAt(i);
+            if (positionValue >= 1) {
+                seenPositiveProfile.enter();
+                hasSeenPositive = true;
+                int intPositionValue = RRuntime.double2intNoCheck(positionValue);
+                if (positionValue > dimensionLength) {
+                    seenOutOfBounds.enter();
+                    outOfBoundsCount++;
+                    maxOutOfBoundsIndex = Math.max(maxOutOfBoundsIndex, intPositionValue);
+                }
+                intPosition.setDataAt(convertedStore, i, intPositionValue);
+            } else if (positionValue >= 0 && positionValue < 1) {
+                seenZeroProfile.enter();
+                zeroCount++;
+            } else if (positionNACheck.checkNAorNaN(positionValue)) {
+                hasSeenNA = true;
+                intPosition.setNA(convertedStore, i);
+            } else {
+                seenNegativeProfile.enter();
+                assert positionValue < 0;
+                hasSeenNegative = true;
+                int intPositionValue = RRuntime.double2intNoCheck(positionValue);
+                if (intPositionValue == 0) {
+                    /*
+                     * It seems that the range ]-2:0[ is all translated to -1. So much for
+                     * continuous math properties.
+                     */
+                    intPositionValue = -1;
+                }
+                if (-positionValue > dimensionLength) {
+                    seenOutOfBounds.enter();
+                    outOfBoundsCount++;
+                    /*
+                     * We need to decrement the value to ensure that the later nodes see that the
+                     * value is actually out of bounds.
+                     */
+                    intPositionValue--;
+                }
+                intPosition.setDataAt(convertedStore, i, intPositionValue);
+            }
+        }
+
+        return doIntegerProfiled(profile, dimensionLength, intPosition, positionLength, hasSeenPositive, hasSeenNegative, hasSeenNA, outOfBoundsCount, zeroCount, maxOutOfBoundsIndex);
+
+    }
+
+    @Specialization(/* contains = "doSequence" */)
+    protected RAbstractVector doInteger(PositionProfile profile, int dimensionLength, RAbstractIntVector position, int positionLength, //
+                    @Cached("create()") BranchProfile seenZeroProfile, //
+                    @Cached("create()") BranchProfile seenPositiveProfile, //
+                    @Cached("create()") BranchProfile seenNegativeProfile, //
+                    @Cached("create()") BranchProfile seenOutOfBounds) {
+
+        positionNACheck.enable(position);
+        boolean hasSeenPositive = false;
+        boolean hasSeenNegative = false;
+        boolean hasSeenNA = false;
+        int outOfBoundsCount = 0;
+        int zeroCount = 0;
+        int maxOutOfBoundsIndex = 0;
+        for (int i = 0; i < positionLength; i++) {
+            int positionValue = position.getDataAt(i);
+            if (positionValue > 0) {
+                seenPositiveProfile.enter();
+                hasSeenPositive = true;
+                if (positionValue > dimensionLength) {
+                    seenOutOfBounds.enter();
+                    outOfBoundsCount++;
+                    maxOutOfBoundsIndex = Math.max(maxOutOfBoundsIndex, positionValue);
+                }
+            } else if (positionValue == 0) {
+                seenZeroProfile.enter();
+                zeroCount++;
+            } else if (positionNACheck.check(positionValue)) {
+                hasSeenNA = true;
+            } else {
+                assert positionValue != RRuntime.INT_NA;
+                seenNegativeProfile.enter();
+                assert positionValue < 0;
+                hasSeenNegative = true;
+                if (-positionValue > dimensionLength) {
+                    seenOutOfBounds.enter();
+                    outOfBoundsCount++;
+                }
+            }
+        }
+
+        return doIntegerProfiled(profile, dimensionLength, position, positionLength, hasSeenPositive, hasSeenNegative, hasSeenNA, outOfBoundsCount, zeroCount, maxOutOfBoundsIndex);
+    }
+
+    private RAbstractVector doIntegerProfiled(PositionProfile profile, int dimensionLength, RAbstractIntVector intPosition, int positionLength, boolean hasSeenPositive, boolean hasSeenNegative,
+                    boolean hasSeenNA, int outOfBoundsCount, int zeroCount, int maxOutOfBoundsIndex) {
+        if (hasSeenPositive || hasSeenNA) {
+            if (numDimensions > 1 && outOfBoundsCount > 0) {
+                error.enter();
+                throw RError.error(this, RError.Message.SUBSCRIPT_BOUNDS);
+            }
+            if (hasSeenNegative) {
+                error.enter();
+                throw RError.error(this, RError.Message.ONLY_0_MIXED);
+            }
+            profile.maxOutOfBoundsIndex = maxOutOfBoundsIndex;
+            profile.selectedPositionsCount = positionLength - zeroCount;
+            boolean containsNAForOutOfBounds = !replace && outOfBoundsCount > 0;
+            profile.containsNA = hasSeenNA || containsNAForOutOfBounds;
+
+            if (zeroCount == 0 && !containsNAForOutOfBounds) {
+                // fast path (most common expected behavior)
+                return intPosition;
+            } else {
+                return eliminateZerosAndOutOfBounds(intPosition, positionLength, dimensionLength, outOfBoundsCount, zeroCount, hasSeenNA);
+            }
+        } else if (hasSeenNegative) {
+            assert !hasSeenNA;
+            return transformNegative(profile, dimensionLength, intPosition, positionLength, zeroCount > 0);
+        } else {
+            return RDataFactory.createEmptyIntVector();
+        }
+    }
+
+    private RAbstractVector eliminateZerosAndOutOfBounds(RAbstractIntVector position, int positionLength, int dimensionLength, int outOfBoundsCount, int zeroCount, boolean hasSeenNA) {
+        int[] newIndices = new int[positionLength - zeroCount];
+        int newPositionIndex = 0;
+        for (int i = 0; i < positionLength; i++) {
+            int positionValue = position.getDataAt(i);
+            if (zeroCount > 0 && positionValue == 0) {
+                continue;
+            } else if (!replace && outOfBoundsCount > 0 && positionValue > dimensionLength) {
+                newIndices[newPositionIndex++] = RRuntime.INT_NA;
+            } else {
+                newIndices[newPositionIndex++] = positionValue;
+            }
+        }
+        return RDataFactory.createIntVector(newIndices, !hasSeenNA && outOfBoundsCount == 0);
+    }
+
+    private static RAbstractVector transformNegative(PositionProfile statistics, int dimLength, RAbstractIntVector position, int positionLength, boolean hasZeros) {
+        byte[] mask = new byte[dimLength];
+        Arrays.fill(mask, RRuntime.LOGICAL_TRUE);
+        int allPositionsNum = dimLength;
+        for (int i = 0; i < positionLength; i++) {
+            int pos = -position.getDataAt(i);
+            if (hasZeros && pos == 0) {
+                continue;
+            }
+            assert pos > 0;
+            if (pos <= dimLength && mask[pos - 1] != RRuntime.LOGICAL_FALSE) {
+                allPositionsNum--;
+                mask[pos - 1] = RRuntime.LOGICAL_FALSE;
+            }
+        }
+        statistics.selectedPositionsCount = allPositionsNum;
+        return RDataFactory.createLogicalVector(mask, RDataFactory.COMPLETE_VECTOR);
+    }
+}
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/PositionsCheckNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/PositionsCheckNode.java
new file mode 100644
index 0000000000000000000000000000000000000000..ab3c7c1e2302dd30bbdac22cf18446c57922cd77
--- /dev/null
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/PositionsCheckNode.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright (c) 2015, 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.access.vector;
+
+import com.oracle.truffle.api.CompilerDirectives.*;
+import com.oracle.truffle.api.nodes.*;
+import com.oracle.truffle.api.utilities.*;
+import com.oracle.truffle.r.nodes.profile.*;
+import com.oracle.truffle.r.runtime.*;
+import com.oracle.truffle.r.runtime.data.*;
+import com.oracle.truffle.r.runtime.data.model.*;
+
+final class PositionsCheckNode extends Node {
+
+    @Children private final PositionCheckNode[] positionsCheck;
+
+    private final ElementAccessMode mode;
+    private final BranchProfile errorBranch = BranchProfile.create();
+    private final VectorLengthProfile selectedPositionsCountProfile = VectorLengthProfile.create();
+    private final VectorLengthProfile maxOutOfBoundsProfile = VectorLengthProfile.create();
+    private final ConditionProfile containsNAProfile = ConditionProfile.createBinaryProfile();
+    private final boolean replace;
+    private final int positionsLength;
+
+    public PositionsCheckNode(ElementAccessMode mode, RType containerType, Object[] positions, boolean exact, boolean replace, boolean recursive) {
+        this.mode = mode;
+        this.replace = replace;
+        this.positionsCheck = new PositionCheckNode[positions.length];
+        for (int i = 0; i < positions.length; i++) {
+            positionsCheck[i] = PositionCheckNode.createNode(mode, containerType, positions[i], i, positions.length, exact, replace, recursive);
+        }
+        this.positionsLength = positions.length;
+    }
+
+    public PositionCheckNode getPositionCheckAt(int index) {
+        return positionsCheck[index];
+    }
+
+    @ExplodeLoop
+    public boolean isSupported(Object[] positions) {
+        if (positionsCheck.length != positions.length) {
+            return false;
+        }
+        for (int i = 0; i < positionsCheck.length; i++) {
+            if (!positionsCheck[i].isSupported(positions[i])) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    public int getDimensions() {
+        return positionsCheck.length;
+    }
+
+    public boolean isSingleDimension() {
+        return positionsCheck.length == 1;
+    }
+
+    public boolean isMultiDimension() {
+        return positionsCheck.length > 1;
+    }
+
+    @ExplodeLoop
+    public PositionProfile[] executeCheck(RAbstractContainer vector, int[] vectorDimensions, int vectorLength, Object[] positions) {
+        assert isSupported(positions);
+        verifyDimensions(vectorDimensions);
+
+        PositionProfile[] statistics = new PositionProfile[positionsLength];
+        for (int i = 0; i < positionsLength; i++) {
+            Object position = positions[i];
+            PositionProfile profile = new PositionProfile();
+            positions[i] = positionsCheck[i].execute(profile, vector, vectorDimensions, vectorLength, position);
+            statistics[i] = profile;
+        }
+        return statistics;
+    }
+
+    @TruffleBoundary
+    private void print() {
+        System.out.println(positionsCheck.length);
+    }
+
+    private void verifyDimensions(int[] vectorDimensions) {
+        if (vectorDimensions == null) {
+            if (isMultiDimension()) {
+                errorBranch.enter();
+                throw dimensionsError();
+            }
+        } else {
+            if (getDimensions() > vectorDimensions.length || getDimensions() < vectorDimensions.length) {
+                errorBranch.enter();
+                throw dimensionsError();
+            }
+        }
+    }
+
+    private RError dimensionsError() {
+        if (replace) {
+            if (mode.isSubset()) {
+                if (getDimensions() == 2) {
+                    return RError.error(this, RError.Message.INCORRECT_SUBSCRIPTS_MATRIX);
+                } else {
+                    return RError.error(this, RError.Message.INCORRECT_SUBSCRIPTS);
+                }
+            } else {
+                return RError.error(this, RError.Message.IMPROPER_SUBSCRIPT);
+            }
+        } else {
+            return RError.error(this, RError.Message.INCORRECT_DIMENSIONS);
+        }
+    }
+
+    @ExplodeLoop
+    public int getSelectedPositionsCount(PositionProfile[] profiles) {
+        if (positionsCheck.length == 1) {
+            return selectedPositionsCountProfile.profile(profiles[0].selectedPositionsCount);
+        } else {
+            int newSize = 1;
+            for (int i = 0; i < positionsCheck.length; i++) {
+                newSize *= profiles[i].selectedPositionsCount;
+            }
+            return selectedPositionsCountProfile.profile(newSize);
+        }
+    }
+
+    public int getCachedSelectedPositionsCount() {
+        return selectedPositionsCountProfile.getCachedLength();
+    }
+
+    @ExplodeLoop
+    public boolean getContainsNA(PositionProfile[] profiles) {
+        if (positionsCheck.length == 1) {
+            return containsNAProfile.profile(profiles[0].containsNA);
+        } else {
+            boolean containsNA = false;
+            for (int i = 0; i < positionsCheck.length; i++) {
+                containsNA |= profiles[i].containsNA;
+            }
+            return containsNAProfile.profile(containsNA);
+        }
+    }
+
+    @ExplodeLoop
+    public int getMaxOutOfBounds(PositionProfile[] replacementStatistics) {
+        if (positionsCheck.length == 1) {
+            return maxOutOfBoundsProfile.profile(replacementStatistics[0].maxOutOfBoundsIndex);
+        } else {
+            // impossible to be relevant as position check will throw an error in this case.
+            return 0;
+        }
+    }
+
+    public boolean isMissing() {
+        return positionsCheck.length == 1 && //
+                        (positionsCheck[0].getPositionClass() == RMissing.class ||  //
+                        positionsCheck[0].getPositionClass() == RSymbol.class);
+    }
+
+    final class PositionProfile {
+
+        int selectedPositionsCount;
+
+        int maxOutOfBoundsIndex;
+
+        boolean containsNA;
+
+    }
+
+}
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/RecursiveExtractSubscriptNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/RecursiveExtractSubscriptNode.java
new file mode 100644
index 0000000000000000000000000000000000000000..1300b80b543be6d8156d3abca1c8ae52c47dca07
--- /dev/null
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/RecursiveExtractSubscriptNode.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2015, 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.access.vector;
+
+import com.oracle.truffle.api.dsl.*;
+import com.oracle.truffle.r.runtime.*;
+import com.oracle.truffle.r.runtime.data.*;
+import com.oracle.truffle.r.runtime.data.model.*;
+
+abstract class RecursiveExtractSubscriptNode extends RecursiveSubscriptNode {
+
+    @Child private ExtractVectorNode recursiveSubscriptExtract = ExtractVectorNode.createRecursive(ElementAccessMode.SUBSCRIPT);
+    @Child private ExtractVectorNode getPositionExtract = ExtractVectorNode.create(ElementAccessMode.SUBSCRIPT);
+
+    public RecursiveExtractSubscriptNode(RAbstractListVector vector, Object position) {
+        super(vector, position);
+    }
+
+    public static RecursiveExtractSubscriptNode create(RAbstractListVector vector, Object position) {
+        return RecursiveExtractSubscriptNodeGen.create(vector, position);
+    }
+
+    public final Object apply(Object vector, Object[] positions, Object exact, Object dropDimensions) {
+        Object firstPosition = positions[0];
+        int length = positionLengthNode.executeInteger(firstPosition);
+        return execute(vector, positions, firstPosition, length, exact, dropDimensions);
+    }
+
+    protected abstract Object execute(Object vector, Object[] positions, Object firstPosition, int positionLength, Object exact, Object dropDimensions);
+
+    @Specialization(guards = "positionLength <= 1")
+    @SuppressWarnings("unused")
+    protected Object doDefault(Object vector, Object[] positions, Object firstPosition, int positionLength, Object exact, Object dropDimensions) {
+        try {
+            return recursiveSubscriptExtract.apply(vector, positions, exact, dropDimensions);
+        } catch (RecursiveIndexNotFoundError e) {
+            errorBranch.enter();
+            throw RError.error(this, RError.Message.SUBSCRIPT_BOUNDS);
+        }
+    }
+
+    @Specialization(contains = "doDefault")
+    @SuppressWarnings("unused")
+    protected Object doRecursive(Object vector, Object[] positions, Object originalFirstPosition, int positionLength, Object exact, Object dropDimensions, //
+                    @Cached("createPositionCast()") PositionCastNode positionCast) {
+        Object firstPosition = positionCast.execute(originalFirstPosition);
+        Object currentVector = vector;
+        for (int i = 1; i <= positionLength; i++) {
+            Object selection = getPositionExtract.apply(firstPosition, new Object[]{RInteger.valueOf(i)}, RLogical.TRUE, RLogical.TRUE);
+            try {
+                if (i < positionLength && !(currentVector instanceof RAbstractListVector)) {
+                    if (currentVector == RNull.instance) {
+                        throw noSuchIndex(i - 1);
+                    } else {
+                        throw indexingFailed(i - 1);
+                    }
+                } else if (i <= positionLength && currentVector == RNull.instance) {
+                    throw noSuchIndex(i - 1);
+                }
+                currentVector = recursiveSubscriptExtract.apply(currentVector, new Object[]{selection}, exact, dropDimensions);
+
+            } catch (RecursiveIndexNotFoundError e) {
+                errorBranch.enter();
+                if (i > 1) {
+                    throw noSuchIndex(i - 1);
+                } else {
+                    throw RError.error(this, RError.Message.SUBSCRIPT_BOUNDS);
+                }
+            }
+        }
+        return currentVector;
+    }
+
+    protected PositionCastNode createPositionCast() {
+        return PositionCastNode.create(ElementAccessMode.SUBSCRIPT, true);
+    }
+}
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/GlobalAssumptions.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/RecursiveIndexNotFoundError.java
similarity index 65%
rename from com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/GlobalAssumptions.java
rename to com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/RecursiveIndexNotFoundError.java
index dc0c079a0b5c2257ec062969bd406c4894f846b8..6cf1a25c4e42106aaed77cafcef9fab656557139 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/GlobalAssumptions.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/RecursiveIndexNotFoundError.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 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
@@ -20,17 +20,12 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.runtime;
+package com.oracle.truffle.r.nodes.access.vector;
 
-import com.oracle.truffle.api.*;
+import com.oracle.truffle.api.nodes.*;
 
-public final class GlobalAssumptions {
+final class RecursiveIndexNotFoundError extends ControlFlowException {
 
-    GlobalAssumptions() {
-    }
-
-    public final Assumption naIntroduced = Truffle.getRuntime().createAssumption("naIntroduced");
-    public final Assumption outOfRange = Truffle.getRuntime().createAssumption("outOfRange");
-    public final Assumption imagDiscarded = Truffle.getRuntime().createAssumption("imagDiscarded");
+    private static final long serialVersionUID = 1L;
 
 }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/RecursiveReplaceSubscriptNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/RecursiveReplaceSubscriptNode.java
new file mode 100644
index 0000000000000000000000000000000000000000..7a48b04431d51579d0ac896db77da6e59a4e14ee
--- /dev/null
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/RecursiveReplaceSubscriptNode.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2015, 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.access.vector;
+
+import com.oracle.truffle.api.dsl.*;
+import com.oracle.truffle.r.runtime.data.*;
+import com.oracle.truffle.r.runtime.data.model.*;
+
+abstract class RecursiveReplaceSubscriptNode extends RecursiveSubscriptNode {
+
+    @Child private ReplaceVectorNode recursiveSubscriptReplace = ReplaceVectorNode.createRecursive(ElementAccessMode.SUBSCRIPT);
+    @Child private ExtractVectorNode getPositionExtract = ExtractVectorNode.createRecursive(ElementAccessMode.SUBSCRIPT);
+    @Child private ExtractVectorNode recursiveSubscriptExtract = ExtractVectorNode.createRecursive(ElementAccessMode.SUBSCRIPT);
+
+    public RecursiveReplaceSubscriptNode(RAbstractListVector vector, Object position) {
+        super(vector, position);
+    }
+
+    public static RecursiveReplaceSubscriptNode create(RAbstractListVector vector, Object position) {
+        return RecursiveReplaceSubscriptNodeGen.create(vector, position);
+    }
+
+    public final Object apply(Object vector, Object[] positions, Object value) {
+        assert isSupported(vector, positions);
+        Object firstPosition = positionClass.cast(positions[0]);
+        int length = positionLengthNode.executeInteger(firstPosition);
+        return execute(vectorClass.cast(vector), positions, firstPosition, length, value);
+    }
+
+    protected abstract Object execute(Object vector, Object[] positions, Object firstPosition, int positionLength, Object value);
+
+    @Specialization(guards = "positionLength <= 1")
+    @SuppressWarnings("unused")
+    protected Object doDefault(Object vector, Object[] positions, Object firstPosition, int positionLength, Object value) {
+        return recursiveSubscriptReplace.apply(vector, positions, value);
+    }
+
+    /**
+     * Exemplary expansion. <code>
+     * a <- list(1,list(1,list(1))); a[[c(2,2,2)]] <- 2
+     * Gets desugared into:
+     * tmp1 <- a[[2]]
+     * tmp2 <- tmp1[[2]]
+     * tmp2[[2]] <- 2
+     * tmp1[[2]] <- tmp2
+     * a[[2]] <- tmp1
+     * </code>
+     */
+    @Specialization(contains = "doDefault")
+    @SuppressWarnings("unused")
+    protected Object doRecursive(Object vector, Object[] positions, Object originalFirstPosition, int positionLength, Object value, //
+                    @Cached("createPositionCast()") PositionCastNode positionCast) {
+        Object firstPosition = positionCast.execute(originalFirstPosition);
+        Object[] positionStack = new Object[positionLength];
+        Object[] valueStack = new Object[positionLength];
+        valueStack[0] = vector;
+        Object currentVector = vector;
+        for (int i = 1; i < positionLength; i++) {
+            Object parentPosition = getPositionValue(firstPosition, i - 1);
+            positionStack[i - 1] = parentPosition;
+            try {
+                if (!(currentVector instanceof RAbstractListVector)) {
+                    throw indexingFailed(i);
+                }
+                currentVector = recursiveSubscriptExtract.apply(currentVector, new Object[]{parentPosition}, RLogical.TRUE, RLogical.TRUE);
+
+                if (currentVector == RNull.instance) {
+                    throw noSuchIndex(i);
+                }
+                valueStack[i] = currentVector;
+            } catch (RecursiveIndexNotFoundError e) {
+                throw noSuchIndex(i);
+            }
+        }
+        Object recursiveValue = value;
+        positionStack[positionLength - 1] = getPositionValue(firstPosition, positionLength - 1);
+        for (int i = positionLength - 1; i >= 0; i--) {
+            recursiveValue = recursiveSubscriptReplace.apply(valueStack[i], new Object[]{positionStack[i]}, recursiveValue);
+        }
+        return recursiveValue;
+    }
+
+    protected PositionCastNode createPositionCast() {
+        return PositionCastNode.create(ElementAccessMode.SUBSCRIPT, false);
+    }
+
+    private Object getPositionValue(Object firstPosition, int i) {
+        return getPositionExtract.apply(firstPosition, new Object[]{RInteger.valueOf(i + 1)}, RLogical.TRUE, RLogical.TRUE);
+    }
+}
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/RecursiveSubscriptNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/RecursiveSubscriptNode.java
new file mode 100644
index 0000000000000000000000000000000000000000..abdf54b5dd789feea8694c0c696e7a3ef928dd09
--- /dev/null
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/RecursiveSubscriptNode.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2015, 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.access.vector;
+
+import com.oracle.truffle.api.nodes.*;
+import com.oracle.truffle.api.utilities.*;
+import com.oracle.truffle.r.nodes.control.*;
+import com.oracle.truffle.r.runtime.*;
+import com.oracle.truffle.r.runtime.data.model.*;
+
+abstract class RecursiveSubscriptNode extends Node {
+
+    protected final Class<?> vectorClass;
+    protected final Class<?> positionClass;
+
+    protected final BranchProfile errorBranch = BranchProfile.create();
+
+    @Child protected RLengthNode positionLengthNode = RLengthNode.create();
+
+    public RecursiveSubscriptNode(RAbstractListVector vector, Object position) {
+        this.vectorClass = vector.getClass();
+        this.positionClass = position.getClass();
+    }
+
+    public final boolean isSupported(Object vector, Object[] positions) {
+        if (vector.getClass() == vectorClass && positions.length == 1 && positions[0].getClass() == positionClass) {
+            return true;
+        }
+        return false;
+    }
+
+    protected final RError indexingFailed(int i) {
+        errorBranch.enter();
+        return RError.error(this, RError.Message.RECURSIVE_INDEXING_FAILED, i);
+    }
+
+    protected final RError noSuchIndex(int i) {
+        errorBranch.enter();
+        return RError.error(this, RError.Message.NO_SUCH_INDEX, i);
+    }
+
+}
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/ReplaceVectorNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/ReplaceVectorNode.java
new file mode 100644
index 0000000000000000000000000000000000000000..de68da1418eaa0913f786321bc641ce428d9eff6
--- /dev/null
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/ReplaceVectorNode.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 2015, 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.access.vector;
+
+import com.oracle.truffle.api.*;
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.dsl.*;
+import com.oracle.truffle.api.nodes.*;
+import com.oracle.truffle.r.nodes.binary.*;
+import com.oracle.truffle.r.nodes.profile.*;
+import com.oracle.truffle.r.runtime.data.*;
+import com.oracle.truffle.r.runtime.data.model.*;
+import com.oracle.truffle.r.runtime.env.*;
+
+/**
+ * Syntax node for element writes.
+ */
+public abstract class ReplaceVectorNode extends Node {
+
+    /*
+     * TODO remove this as soon as the new vector access nodes are the default.
+     */
+    public static final boolean USE_NODE = System.getProperty("newVectorAccess") != null ? System.getProperty("newVectorAccess").equals("true") : false;
+
+    protected static final int CACHE_LIMIT = 5;
+
+    private final ElementAccessMode mode;
+    private final boolean recursive;
+
+    @Child private BoxPrimitiveNode boxVector = BoxPrimitiveNode.create();
+    @Child private BoxPrimitiveNode boxValue = BoxPrimitiveNode.create();
+
+    public ReplaceVectorNode(ElementAccessMode mode, boolean recursive) {
+        this.mode = mode;
+        this.recursive = recursive;
+    }
+
+    public final Object apply(Object vector, Object[] positions, Object value) {
+        return execute(boxVector.execute(vector), positions, boxValue.execute(value));
+    }
+
+    protected abstract Object execute(Object vector, Object[] positions, Object value);
+
+    public static ReplaceVectorNode create(ElementAccessMode mode) {
+        return ReplaceVectorNodeGen.create(mode, false);
+    }
+
+    static ReplaceVectorNode createRecursive(ElementAccessMode mode) {
+        return ReplaceVectorNodeGen.create(mode, true);
+    }
+
+    @SuppressWarnings("unchecked")
+    protected Class<? extends RTypedValue> getInvalidVectorType(Object vector) {
+        if (vector instanceof REnvironment || vector instanceof RFunction) {
+            return (Class<? extends RTypedValue>) vector.getClass();
+        }
+        return null;
+    }
+
+    @Specialization(limit = "CACHE_LIMIT", guards = {"cached != null", "cached.isSupported(vector, positions)"})
+    protected Object doRecursive(RAbstractListVector vector, Object[] positions, Object value,  //
+                    @Cached("createRecursiveCache(vector, positions)") RecursiveReplaceSubscriptNode cached) {
+        return cached.apply(vector, positions, value);
+    }
+
+    protected RecursiveReplaceSubscriptNode createRecursiveCache(Object vector, Object[] positions) {
+        if (isRecursiveSubscript(vector, positions)) {
+            return RecursiveReplaceSubscriptNode.create((RAbstractListVector) vector, positions[0]);
+        }
+        return null;
+    }
+
+    private boolean isRecursiveSubscript(Object vector, Object[] positions) {
+        return !recursive && mode.isSubscript() && vector instanceof RAbstractListVector && positions.length == 1;
+    }
+
+    @Specialization(limit = "CACHE_LIMIT", guards = {"cached != null", "cached.isSupported(vector, positions, value)"})
+    protected Object doReplaceCached(Object vector, Object[] positions, Object value,  //
+                    @Cached("createDefaultCached(getThis(), vector, positions, value)") CachedReplaceVectorNode cached) {
+        assert !isRecursiveSubscript(vector, positions);
+        return cached.apply(vector, positions, value);
+    }
+
+    protected static CachedReplaceVectorNode createDefaultCached(ReplaceVectorNode node, Object vector, Object[] positions, Object value) {
+        return new CachedReplaceVectorNode(node.mode, (RTypedValue) vector, positions, (RTypedValue) value, true, node.recursive);
+    }
+
+    public ElementAccessMode getMode() {
+        return mode;
+    }
+
+    @Specialization(contains = "doReplaceCached")
+    @TruffleBoundary
+    protected Object doReplaceDefaultGeneric(Object vector, Object[] positions, Object value,  //
+                    @Cached("new(createDefaultCached(getThis(), vector, positions, value))") GenericVectorReplaceNode generic) {
+        return generic.get(this, vector, positions, value).apply(vector, positions, value);
+    }
+
+    // TODO hack until Truffle-DSL supports this.
+    protected ReplaceVectorNode getThis() {
+        return this;
+    }
+
+    protected static final class GenericVectorReplaceNode extends TruffleBoundaryNode {
+
+        @Child private CachedReplaceVectorNode cached;
+
+        public GenericVectorReplaceNode(CachedReplaceVectorNode cachedOperation) {
+            this.cached = insert(cachedOperation);
+        }
+
+        public CachedReplaceVectorNode get(ReplaceVectorNode node, Object vector, Object[] positions, Object value) {
+            CompilerAsserts.neverPartOfCompilation();
+            if (!cached.isSupported(vector, positions, value)) {
+                cached = cached.replace(createDefaultCached(node, vector, positions, value));
+            }
+            return cached;
+        }
+    }
+}
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/SearchFirstStringNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/SearchFirstStringNode.java
new file mode 100644
index 0000000000000000000000000000000000000000..fb3c0f3138e6f823edaee2cb74f540184eae0590
--- /dev/null
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/SearchFirstStringNode.java
@@ -0,0 +1,289 @@
+/*
+ * Copyright (c) 2015, 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.access.vector;
+
+import com.oracle.truffle.api.*;
+import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
+import com.oracle.truffle.api.nodes.*;
+import com.oracle.truffle.api.utilities.*;
+import com.oracle.truffle.r.nodes.profile.*;
+import com.oracle.truffle.r.runtime.*;
+import com.oracle.truffle.r.runtime.data.*;
+import com.oracle.truffle.r.runtime.data.model.*;
+import com.oracle.truffle.r.runtime.ops.na.*;
+
+/**
+ * This node encapsulates a speculative search of the first elements in an character vector and
+ * returns an integer vector with their indices.
+ */
+final class SearchFirstStringNode extends Node {
+
+    private static final int[] UNINTIALIZED_CACHED_INDICES = new int[0];
+
+    private final VectorLengthProfile targetLengthProfile = VectorLengthProfile.create();
+    private final VectorLengthProfile elementsLengthProfile = VectorLengthProfile.create();
+    private final ValueProfile targetClassProfile = ValueProfile.createClassProfile();
+    private final ValueProfile elementsClassProfile = ValueProfile.createClassProfile();
+
+    @Child private CompareStringNode stringEquals = CompareStringNode.createEquals();
+    @Child private CompareStringNode stringStartsWith;
+    @Child private CompareStringNode equalsDuplicate;
+
+    private final NACheck elementsNACheck = NACheck.create();
+    private final NACheck targetNACheck = NACheck.create();
+    private final BranchProfile everFoundDuplicate = BranchProfile.create();
+    private final BranchProfile seenInvalid = BranchProfile.create();
+
+    /** Instead of using the notFoundStartIndex we use NA. */
+    private final boolean useNAForNotFound;
+    protected final boolean exactMatch;
+
+    @CompilationFinal private int[] cachedIndices;
+
+    public SearchFirstStringNode(boolean exactMatch, boolean useNAForNotFound) {
+        this.exactMatch = exactMatch;
+        this.useNAForNotFound = useNAForNotFound;
+        if (!exactMatch) {
+            stringStartsWith = CompareStringNode.createStartsWith();
+        }
+    }
+
+    public RAbstractIntVector apply(RAbstractStringVector target, RAbstractStringVector elements, int notFoundStartIndex) {
+        RAbstractStringVector targetProfiled = targetClassProfile.profile(target);
+        RAbstractStringVector elementsProfiled = elementsClassProfile.profile(elements);
+
+        int targetLength = targetLengthProfile.profile(targetProfiled.getLength());
+        int elementsLength = elementsLengthProfile.profile(elementsProfiled.getLength());
+
+        targetNACheck.enable(target);
+        elementsNACheck.enable(elements);
+
+        if (cachedIndices == UNINTIALIZED_CACHED_INDICES) {
+            CompilerDirectives.transferToInterpreterAndInvalidate();
+            cachedIndices = searchCached(targetProfiled, targetLength, elementsProfiled, elementsLength);
+        }
+        if (cachedIndices != null) {
+            if (!isCacheValid(targetProfiled, targetLength, elementsProfiled, elementsLength, cachedIndices)) {
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                cachedIndices = null; // set to generic
+            }
+            assert sameVector(searchCached(target, targetLength, elements, elementsLength), cachedIndices);
+            return RDataFactory.createIntVector(cachedIndices, true);
+        }
+
+        return searchGeneric(targetProfiled, targetLength, elementsProfiled, elementsLength, notFoundStartIndex, false);
+    }
+
+    public static SearchFirstStringNode createNode(boolean exactMatch, boolean useNAForNotFound) {
+        return new SearchFirstStringNode(exactMatch, useNAForNotFound);
+    }
+
+    protected int[] searchCached(RAbstractStringVector target, int targetLength, RAbstractStringVector elements, int elementsLength) {
+        if (exactMatch) {
+            return (int[]) searchGeneric(target, targetLength, elements, elementsLength, -1, true).getInternalStore();
+        }
+        return null;
+    }
+
+    protected boolean isCacheValid(RAbstractStringVector target, int targetLength, //
+                    RAbstractStringVector elements, int elementsLength, int[] cached) {
+        int cachedLength = cached.length;
+        if (elementsLength != cachedLength) {
+            seenInvalid.enter();
+            return false;
+        }
+
+        for (int i = 0; i < cachedLength; i++) {
+            int cachedIndex = cached[i];
+            String cachedElement = elements.getDataAt(i);
+
+            if (elementsNACheck.check(cachedElement) || cachedElement.length() == 0) {
+                seenInvalid.enter();
+                return false;
+            }
+
+            int cachedTranslatedIndex = cachedIndex - 1;
+            for (int j = 0; j < cachedTranslatedIndex; j++) {
+                String targetString = target.getDataAt(j);
+                if (!targetNACheck.check(targetString) && stringEquals.executeCompare(targetString, cachedElement)) {
+                    seenInvalid.enter();
+                    return false;
+                }
+            }
+            if (cachedTranslatedIndex < targetLength) {
+                String targetString = target.getDataAt(cachedTranslatedIndex);
+                if (!targetNACheck.check(targetString) && !stringEquals.executeCompare(targetString, cachedElement)) {
+                    seenInvalid.enter();
+                    return false;
+                }
+            } else {
+                seenInvalid.enter();
+                return false;
+            }
+        }
+        return true;
+
+    }
+
+    private static boolean sameVector(int[] a, int[] b) {
+        if (a == null) {
+            return false;
+        }
+        if (a.length != b.length) {
+            return false;
+        }
+
+        for (int i = 0; i < a.length; i++) {
+            if (a[i] != b[i]) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    private final BranchProfile notFoundProfile = BranchProfile.create();
+
+    protected RAbstractIntVector searchGeneric(RAbstractStringVector target, int targetLength, RAbstractStringVector elements, int elementsLength, int notFoundStartIndex, boolean nullOnNotFound) {
+        int notFoundIndex = notFoundStartIndex;
+        int[] indices = new int[elementsLength];
+        boolean resultComplete = true;
+        for (int i = 0; i < elementsLength; i++) {
+            String element = elements.getDataAt(i);
+            boolean isElementNA = elementsNACheck.check(element) || element.length() == 0;
+            if (!isElementNA) {
+                int index = findIndex(target, targetLength, element);
+                if (index >= 0) {
+                    indices[i] = index + 1;
+                    continue;
+                }
+            }
+            notFoundProfile.enter();
+            if (nullOnNotFound) {
+                return null;
+            } else {
+                int prevDuplicateIndex = -1;
+                if (!isElementNA) {
+                    prevDuplicateIndex = findFirstDuplicate(elements, element, i);
+                }
+                int nextIndex;
+                if (prevDuplicateIndex == -1) {
+                    if (useNAForNotFound) {
+                        resultComplete = false;
+                        nextIndex = RRuntime.INT_NA;
+                    } else {
+                        nextIndex = ++notFoundIndex;
+                    }
+                } else {
+                    nextIndex = indices[prevDuplicateIndex];
+                }
+                indices[i] = nextIndex;
+            }
+        }
+
+        return RDataFactory.createIntVector(indices, resultComplete && elements.isComplete());
+    }
+
+    private int findIndex(RAbstractStringVector target, int targetLength, String element) {
+        int nonExactIndex = -1;
+        for (int j = 0; j < targetLength; j++) {
+            String targetValue = target.getDataAt(j);
+            if (!targetNACheck.check(targetValue)) {
+                if (stringEquals.executeCompare(targetValue, element)) {
+                    return j;
+                }
+                if (!exactMatch) {
+                    if (stringStartsWith.executeCompare(targetValue, element)) {
+                        if (nonExactIndex == -1) {
+                            nonExactIndex = j;
+                        } else {
+                            nonExactIndex = -2;
+                        }
+                    }
+                }
+            }
+        }
+        return nonExactIndex;
+    }
+
+    private int findFirstDuplicate(RAbstractStringVector elements, String element, int currentIndex) {
+        if (equalsDuplicate == null) {
+            CompilerDirectives.transferToInterpreterAndInvalidate();
+            equalsDuplicate = insert(CompareStringNode.createEquals());
+        }
+
+        for (int j = 0; j < currentIndex; j++) {
+            String otherElement = elements.getDataAt(j);
+            if (!targetNACheck.check(otherElement) && equalsDuplicate.executeCompare(element, otherElement)) {
+                everFoundDuplicate.enter();
+                return j;
+            }
+        }
+        return -1;
+    }
+
+    abstract static class CompareStringNode extends Node {
+
+        public abstract boolean executeCompare(String target, String element);
+
+        public static CompareStringNode createEquals() {
+            return new StringEqualsNode();
+        }
+
+        public static CompareStringNode createStartsWith() {
+            return new StringStartsWithNode();
+        }
+
+        private static class StringEqualsNode extends CompareStringNode {
+
+            private final ConditionProfile identityEquals = ConditionProfile.createBinaryProfile();
+
+            @Override
+            public final boolean executeCompare(String a, String b) {
+                assert a != RRuntime.STRING_NA;
+                assert b != RRuntime.STRING_NA;
+                if (identityEquals.profile(a == b)) {
+                    return true;
+                } else {
+                    return a.equals(b);
+                }
+            }
+        }
+
+        private static class StringStartsWithNode extends CompareStringNode {
+
+            private final ConditionProfile identityEquals = ConditionProfile.createBinaryProfile();
+
+            @Override
+            public final boolean executeCompare(String a, String b) {
+                assert a != RRuntime.STRING_NA;
+                assert b != RRuntime.STRING_NA;
+                if (identityEquals.profile(a == b)) {
+                    return true;
+                } else {
+                    return a.startsWith(b);
+                }
+            }
+        }
+    }
+
+}
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/WriteIndexedVectorNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/WriteIndexedVectorNode.java
new file mode 100644
index 0000000000000000000000000000000000000000..4bb40e51c186ac1324752ef334b2bbdb8e0a1999
--- /dev/null
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/WriteIndexedVectorNode.java
@@ -0,0 +1,430 @@
+/*
+ * Copyright (c) 2015, 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.access.vector;
+
+import com.oracle.truffle.api.dsl.*;
+import com.oracle.truffle.api.nodes.*;
+import com.oracle.truffle.api.utilities.*;
+import com.oracle.truffle.r.nodes.profile.*;
+import com.oracle.truffle.r.runtime.*;
+import com.oracle.truffle.r.runtime.data.*;
+import com.oracle.truffle.r.runtime.data.model.*;
+import com.oracle.truffle.r.runtime.ops.na.*;
+
+/**
+ * Primitive indexed N-dimensional vector write node. It can be used for vector replaces and
+ * extracts. The only difference is that replace indexes the left vector and extract indexes the
+ * right vector. The index direction is indicated with the boolean flag
+ * {@link #positionsApplyToRight}.
+ */
+abstract class WriteIndexedVectorNode extends Node {
+
+    private final int dimensionIndex;
+    private final int totalDimensions;
+
+    /**
+     * Indicates if the position vectors index into the left or the right vector. This enables us to
+     * share the same node for vector replaces and vector extracts.
+     */
+    private final boolean positionsApplyToRight;
+    /**
+     * If skipNA is true then no action should be invoked for NA values and its indexed
+     * subdimensions.
+     */
+    private final boolean skipNA;
+
+    private final VectorLengthProfile positionLengthProfile = VectorLengthProfile.create();
+    private final VectorLengthProfile positionOffsetProfile = VectorLengthProfile.create();
+    private final VectorLengthProfile dimensionValueProfile = VectorLengthProfile.create();
+    private final ValueProfile positionClassProfile = ValueProfile.createClassProfile();
+    private final NACheck positionNACheck = NACheck.create();
+
+    @Child private WriteIndexedScalarNode<RAbstractVector, RAbstractContainer> scalarNode;
+    @Child private WriteIndexedVectorNode innerVectorNode;
+
+    @SuppressWarnings("unchecked")
+    protected WriteIndexedVectorNode(RType vectorType, int totalDimensions, int dimensionIndex, boolean positionAppliesToRight, boolean skipNA, boolean setListElementAsObject) {
+        this.scalarNode = (WriteIndexedScalarNode<RAbstractVector, RAbstractContainer>) createIndexedAction(vectorType, setListElementAsObject);
+        this.dimensionIndex = dimensionIndex;
+        this.totalDimensions = totalDimensions;
+        this.positionsApplyToRight = positionAppliesToRight;
+        this.skipNA = skipNA;
+        if (dimensionIndex > 0) {
+            innerVectorNode = WriteIndexedVectorNodeGen.create(vectorType, totalDimensions, dimensionIndex - 1, positionAppliesToRight, skipNA, setListElementAsObject);
+        }
+    }
+
+    public static WriteIndexedVectorNode create(RType vectorType, int totalDimensions, boolean positionAppliesToValue, boolean skipNA, boolean setListElementAsObject) {
+        return WriteIndexedVectorNodeGen.create(vectorType, totalDimensions, totalDimensions - 1, positionAppliesToValue, skipNA, setListElementAsObject);
+    }
+
+    public NACheck getValueNACheck() {
+        return scalarNode.valueNACheck;
+    }
+
+    public void enableValueNACheck(RAbstractContainer vector) {
+        getValueNACheck().enable(vector);
+        if (innerVectorNode != null) {
+            innerVectorNode.enableValueNACheck(vector);
+        }
+    }
+
+    public boolean neverSeenNAInValue() {
+        if (getValueNACheck().neverSeenNA()) {
+            if (innerVectorNode == null || innerVectorNode.neverSeenNAInValue()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public final void apply(RAbstractVector left, int leftLength, //
+                    Object[] positions, RAbstractContainer right, int rightLength, int[] positionTargetDimensions) {
+        assert left.getLength() == leftLength;
+        assert right.getLength() == rightLength;
+        assert totalDimensions == positions.length : "totalDimensions must be constant per vector write node";
+
+        Object leftStore = left.getInternalStore();
+        Object rightStore = right.getInternalStore();
+
+        int initialPositionOffset;
+        if (positionsApplyToRight) {
+            initialPositionOffset = rightLength;
+        } else {
+            initialPositionOffset = leftLength;
+        }
+
+        int firstTargetDimension;
+        if (totalDimensions == 0 || positionTargetDimensions == null) {
+            // no dimensions
+            firstTargetDimension = initialPositionOffset;
+        } else {
+            firstTargetDimension = dimensionValueProfile.profile(positionTargetDimensions[dimensionIndex]);
+        }
+
+        applyImpl(left, leftStore, 0, leftLength, positionTargetDimensions, firstTargetDimension, //
+                        positions, initialPositionOffset, //
+                        right, rightStore, 0, rightLength, false);
+    }
+
+    private int applyImpl(//
+                    RAbstractVector left, Object leftStore, int leftBase, int leftLength, Object targetDimensions, int targetDimension, //
+                    Object[] positions, int positionOffset, //
+                    RAbstractContainer right, Object rightStore, int rightBase, int rightLength, boolean parentNA) {
+
+        Object position = positionClassProfile.profile(positions[dimensionIndex]);
+
+        int positionLength = getPositionLength(position);
+        int newPositionOffset;
+        if (positionOffset == targetDimension) {
+            newPositionOffset = 1;
+        } else {
+            newPositionOffset = positionOffsetProfile.profile(positionOffset / targetDimension);
+        }
+        return execute(left, leftStore, leftBase, leftLength, targetDimensions, targetDimension, //
+                        positions, position, newPositionOffset, positionLength, //
+                        right, rightStore, rightBase, rightLength, parentNA);
+    }
+
+    public int getPositionLength(Object position) {
+        if (position instanceof RAbstractVector) {
+            return positionLengthProfile.profile(((RAbstractVector) position).getLength());
+        } else {
+            return -1;
+        }
+    }
+
+    protected abstract int execute(RAbstractVector left, Object leftStore, int storeBase, int storeLength, Object targetDimensions, int targetDimension, //
+                    Object[] positions, Object position, int positionOffset, int positionLength, //
+                    RAbstractContainer right, Object rightStore, int valueBase, int valueLength, boolean parentNA);
+
+    @SuppressWarnings("unused")
+    @Specialization
+    protected int doMissing(RAbstractVector left, Object leftStore, int leftBase, int leftLength, Object targetDimensions, int targetDimension, //
+                    Object[] positions, RMissing position, int positionOffset, int positionLength, //
+                    RAbstractContainer right, Object rightStore, int rightBase, int rightLength, boolean parentNA//
+    ) {
+        int rightIndex = rightBase;
+        for (int positionValue = 0; positionValue < targetDimension; positionValue += 1) {
+            rightIndex = applyInner(//
+                            left, leftStore, leftBase, leftLength, targetDimensions, //
+                            positions, positionOffset, positionValue, //
+                            right, rightStore, rightLength, rightIndex, parentNA);
+        }
+        return rightIndex;
+    }
+
+    @Specialization
+    protected int doLogicalPosition(RAbstractVector left, Object leftStore, int leftBase, int leftLength, Object targetDimensions, int targetDimension, //
+                    Object[] positions, RAbstractLogicalVector position, int positionOffset, int positionLength, //
+                    RAbstractContainer right, Object rightStore, int rightBase, int rightLength, boolean parentNA, //
+                    @Cached("create()") BranchProfile wasTrue, @Cached("create()") BranchProfile outOfBounds) {
+        positionNACheck.enable(!skipNA && !position.isComplete());
+
+        int length = targetDimension;
+        if (positionLength > targetDimension) {
+            outOfBounds.enter();
+            length = positionLength;
+        }
+
+        int rightIndex = rightBase;
+        if (positionLength > 0) {
+            int positionIndex = 0;
+            for (int i = 0; i < length; i++) {
+                byte positionValue = position.getDataAt(positionIndex);
+                boolean isNA = positionNACheck.check(positionValue);
+                if (isNA || positionValue == RRuntime.LOGICAL_TRUE) {
+                    wasTrue.enter();
+                    if (outOfBounds.isVisited() && i >= targetDimension) {
+                        isNA = true;
+                    }
+                    rightIndex = applyInner(//
+                                    left, leftStore, leftBase, leftLength, targetDimensions, //
+                                    positions, positionOffset, i, //
+                                    right, rightStore, rightLength, rightIndex, isNA || parentNA);
+                }
+                positionIndex = Utils.incMod(positionIndex, positionLength);
+            }
+        }
+        return rightIndex;
+    }
+
+    /**
+     * For integer sequences we need to make sure that start and stride is profiled.
+     *
+     * @throws SlowPathException
+     */
+    @Specialization(rewriteOn = SlowPathException.class)
+    protected int doIntegerSequencePosition(RAbstractVector left, Object leftStore, int leftBase, int leftLength, Object targetDimensions, @SuppressWarnings("unused") int targetDimension, //
+                    Object[] positions, RIntSequence position, int positionOffset, int positionLength, //
+                    RAbstractContainer right, Object rightStore, int rightBase, int rightLength, boolean parentNA, //
+                    @Cached("create()") IntValueProfile startProfile, //
+                    @Cached("create()") IntValueProfile strideProfile, //
+                    @Cached("createBinaryProfile()") ConditionProfile conditionProfile) throws SlowPathException {
+        // skip NA check. sequences never contain NA values.
+        int rightIndex = rightBase;
+        int start = startProfile.profile(position.getStart() - 1);
+        int stride = strideProfile.profile(position.getStride());
+        int end = start + positionLength * stride;
+
+        if (start < 0 || end <= 0) {
+            throw new SlowPathException("rewrite to doIntegerPosition");
+        }
+
+        boolean ascending = conditionProfile.profile(start < end);
+        for (int positionValue = start; ascending ? positionValue < end : positionValue > end; positionValue += stride) {
+            rightIndex = applyInner(//
+                            left, leftStore, leftBase, leftLength, targetDimensions, //
+                            positions, positionOffset, positionValue, //
+                            right, rightStore, rightLength, rightIndex, parentNA);
+        }
+        return rightIndex;
+    }
+
+    /**
+     * Integer vectors iterate over the number of positions because we assume that the number of
+     * positions in an integer vector is significantly lower than the number of elements in the
+     * store. This might not be always true and could benefit from more investigation.
+     */
+    @Specialization(contains = "doIntegerSequencePosition")
+    protected int doIntegerPosition(RAbstractVector left, Object leftStore, int leftBase, int leftLength, Object targetDimensions, @SuppressWarnings("unused") int targetDimension, //
+                    Object[] positions, RAbstractIntVector position, int positionOffset, int positionLength, //
+                    RAbstractContainer right, Object rightStore, int rightBase, int rightLength, boolean parentNA //
+    ) {
+        positionNACheck.enable(position);
+        int rightIndex = rightBase;
+
+        for (int i = 0; i < positionLength; i++) {
+            int positionValue = position.getDataAt(i);
+            boolean isNA = positionNACheck.check(positionValue);
+            if (isNA) {
+                if (skipNA) {
+                    continue;
+                }
+            }
+            rightIndex = applyInner(//
+                            left, leftStore, leftBase, leftLength, targetDimensions, //
+                            positions, positionOffset, positionValue - 1, //
+                            right, rightStore, rightLength, rightIndex, isNA || parentNA);
+        }
+        return rightIndex;
+    }
+
+    private int applyInner(//
+                    RAbstractVector left, Object leftStore, int leftBase, int leftLength, Object targetDimensions, //
+                    Object[] positions, int positionOffset, int positionValue, //
+                    RAbstractContainer right, Object rightStore, int rightLength, int rightIndex, boolean isNA) {
+        int newTargetIndex = leftBase + positionValue * positionOffset;
+        if (dimensionIndex == 0) {
+            // for-loops leaf for innermost dimension
+
+            // if position indexes value we just need to switch indices
+            int actionLeftIndex;
+            int actionRightIndex;
+            int actionRightMod;
+            if (positionsApplyToRight) {
+                actionLeftIndex = rightIndex;
+                actionRightIndex = newTargetIndex;
+                actionRightMod = leftLength;
+            } else {
+                actionLeftIndex = newTargetIndex;
+                actionRightIndex = rightIndex;
+                actionRightMod = rightLength;
+            }
+            if (isNA) {
+                left.setNA(leftStore, actionLeftIndex);
+                getValueNACheck().seenNA();
+            } else {
+                scalarNode.apply(left, leftStore, actionLeftIndex, right, rightStore, actionRightIndex);
+            }
+
+            return Utils.incMod(rightIndex, actionRightMod);
+        } else {
+            // generate another for-loop for other dimensions
+            int nextTargetDimension = innerVectorNode.dimensionValueProfile.profile(((int[]) targetDimensions)[innerVectorNode.dimensionIndex]);
+            return innerVectorNode.applyImpl(//
+                            left, leftStore, newTargetIndex, leftLength, targetDimensions, nextTargetDimension, //
+                            positions, positionOffset, //
+                            right, rightStore, rightIndex, rightLength, isNA);
+        }
+    }
+
+    private static WriteIndexedScalarNode<? extends RAbstractVector, ? extends RAbstractContainer> createIndexedAction(RType type, boolean setListElementAsObject) {
+        switch (type) {
+            case Logical:
+                return new WriteLogicalAction();
+            case Integer:
+                return new WriteIntegerAction();
+            case Double:
+                return new WriteDoubleAction();
+            case Complex:
+                return new WriteComplexAction();
+            case Character:
+                return new WriteCharacterAction();
+            case Raw:
+                return new WriteRawAction();
+            case Language:
+            case DataFrame:
+            case Expression:
+            case PairList:
+            case List:
+                return new WriteListAction(setListElementAsObject);
+            default:
+                throw RInternalError.shouldNotReachHere();
+        }
+    }
+
+    private abstract static class WriteIndexedScalarNode<A extends RAbstractVector, V extends RAbstractContainer> extends Node {
+
+        final NACheck valueNACheck = NACheck.create();
+
+        abstract void apply(A leftAccess, Object leftStore, int leftIndex, V rightAccess, Object rightStore, int rightIndex);
+
+    }
+
+    private static final class WriteLogicalAction extends WriteIndexedScalarNode<RAbstractLogicalVector, RAbstractLogicalVector> {
+
+        @Override
+        void apply(RAbstractLogicalVector leftAccess, Object leftStore, int leftIndex, RAbstractLogicalVector rightAccess, Object rightStore, int rightIndex) {
+            byte value = rightAccess.getDataAt(rightStore, rightIndex);
+            leftAccess.setDataAt(leftStore, leftIndex, value);
+            valueNACheck.check(value);
+        }
+    }
+
+    private static final class WriteIntegerAction extends WriteIndexedScalarNode<RAbstractIntVector, RAbstractIntVector> {
+
+        @Override
+        void apply(RAbstractIntVector leftAccess, Object leftStore, int leftIndex, RAbstractIntVector rightAccess, Object rightStore, int rightIndex) {
+            int value = rightAccess.getDataAt(rightStore, rightIndex);
+            leftAccess.setDataAt(leftStore, leftIndex, value);
+            valueNACheck.check(value);
+        }
+    }
+
+    private static final class WriteDoubleAction extends WriteIndexedScalarNode<RAbstractDoubleVector, RAbstractDoubleVector> {
+
+        @Override
+        void apply(RAbstractDoubleVector leftAccess, Object leftStore, int leftIndex, RAbstractDoubleVector rightAccess, Object rightStore, int rightIndex) {
+            double value = rightAccess.getDataAt(rightStore, rightIndex);
+            leftAccess.setDataAt(leftStore, leftIndex, value);
+            valueNACheck.check(value);
+        }
+    }
+
+    private static final class WriteComplexAction extends WriteIndexedScalarNode<RAbstractComplexVector, RAbstractComplexVector> {
+
+        @Override
+        void apply(RAbstractComplexVector leftAccess, Object leftStore, int leftIndex, RAbstractComplexVector rightAccess, Object rightStore, int rightIndex) {
+            RComplex value = rightAccess.getDataAt(rightStore, rightIndex);
+            leftAccess.setDataAt(leftStore, leftIndex, value);
+            valueNACheck.check(value);
+        }
+    }
+
+    private static final class WriteCharacterAction extends WriteIndexedScalarNode<RAbstractStringVector, RAbstractStringVector> {
+
+        @Override
+        void apply(RAbstractStringVector leftAccess, Object leftStore, int leftIndex, RAbstractStringVector rightAccess, Object rightStore, int rightIndex) {
+            String value = rightAccess.getDataAt(rightStore, rightIndex);
+            leftAccess.setDataAt(leftStore, leftIndex, value);
+            valueNACheck.check(value);
+        }
+    }
+
+    private static final class WriteRawAction extends WriteIndexedScalarNode<RAbstractRawVector, RAbstractRawVector> {
+
+        @Override
+        void apply(RAbstractRawVector leftAccess, Object leftStore, int leftIndex, RAbstractRawVector rightAccess, Object rightStore, int rightIndex) {
+            byte value = rightAccess.getRawDataAt(rightStore, rightIndex);
+            leftAccess.setRawDataAt(leftStore, leftIndex, value);
+            valueNACheck.check(value);
+        }
+    }
+
+    private static final class WriteListAction extends WriteIndexedScalarNode<RAbstractListVector, RAbstractContainer> {
+
+        private final boolean setListElementAsObject;
+
+        public WriteListAction(boolean setListElementAsObject) {
+            this.setListElementAsObject = setListElementAsObject;
+        }
+
+        @Override
+        void apply(RAbstractListVector leftAccess, Object leftStore, int leftIndex, RAbstractContainer rightAccess, Object rightStore, int rightIndex) {
+            Object rightValue;
+            if (setListElementAsObject) {
+                rightValue = rightAccess;
+                if (rightValue instanceof RAbstractVector) {
+                    // TODO we should unbox instead of materialize here
+                    rightValue = ((RAbstractVector) rightValue).materialize();
+                }
+            } else {
+                rightValue = rightAccess.getDataAtAsObject(rightStore, rightIndex);
+            }
+            leftAccess.setDataAt(leftStore, leftIndex, rightValue);
+            valueNACheck.checkListElement(rightValue);
+        }
+    }
+
+}
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/binary/CastTypeNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/binary/CastTypeNode.java
index 1dbadbcd7ec3f3b4c06bfe4eaab12171590d99fd..33ac9867a2bc25bb612f799627b035fed5a46a73 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/binary/CastTypeNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/binary/CastTypeNode.java
@@ -56,22 +56,27 @@ public abstract class CastTypeNode extends BinaryNode {
 
     @TruffleBoundary
     public static CastNode createCast(RType type) {
+        return createCast(type, false, false, false);
+    }
+
+    @TruffleBoundary
+    public static CastNode createCast(RType type, boolean preserveNames, boolean preserveDimensions, boolean preserveAttributes) {
         switch (type) {
             case Character:
-                return CastStringNodeGen.create(false, false, false, false);
+                return CastStringNodeGen.create(preserveNames, preserveDimensions, preserveAttributes, false);
             case Complex:
-                return CastComplexNodeGen.create(false, false, false);
+                return CastComplexNodeGen.create(preserveNames, preserveDimensions, preserveAttributes);
             case Double:
             case Numeric:
-                return CastDoubleNodeGen.create(false, false, false);
+                return CastDoubleNodeGen.create(preserveNames, preserveDimensions, preserveAttributes);
             case Integer:
-                return CastIntegerNodeGen.create(false, false, false);
+                return CastIntegerNodeGen.create(preserveNames, preserveDimensions, preserveAttributes);
             case Logical:
-                return CastLogicalNodeGen.create(false, false, false);
+                return CastLogicalNodeGen.create(preserveNames, preserveDimensions, preserveAttributes);
             case Raw:
-                return CastRawNodeGen.create(false, false, false);
+                return CastRawNodeGen.create(preserveNames, preserveDimensions, preserveAttributes);
             case List:
-                return CastListNodeGen.create(false, false, false);
+                return CastListNodeGen.create(preserveNames, preserveDimensions, preserveAttributes);
             default:
                 return null;
 
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 2e169414177614011bbf4f9822368e59c7c6f5d9..91b24aef9cba508c181947fbce15c5104827a7a5 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
@@ -23,6 +23,7 @@
 package com.oracle.truffle.r.nodes.control;
 
 import com.oracle.truffle.api.CompilerDirectives.*;
+import com.oracle.truffle.api.frame.*;
 import com.oracle.truffle.api.source.*;
 import com.oracle.truffle.r.runtime.*;
 import com.oracle.truffle.r.runtime.env.*;
@@ -32,7 +33,7 @@ import com.oracle.truffle.r.runtime.nodes.*;
  * A {@link BlockNode} represents a sequence of statements such as the body of a {@code while} loop.
  *
  */
-public class BlockNode extends SequenceNode implements RSyntaxNode {
+public class BlockNode extends SequenceNode implements RSyntaxNode, VisibilityController {
     public static final RNode[] EMPTY_BLOCK = new RNode[0];
 
     public BlockNode(SourceSection src, RNode[] sequence) {
@@ -47,6 +48,12 @@ public class BlockNode extends SequenceNode implements RSyntaxNode {
         this(src, convert(node));
     }
 
+    @Override
+    public Object execute(VirtualFrame frame) {
+        controlVisibility();
+        return super.execute(frame);
+    }
+
     /**
      * Ensures that {@code node} is a {@link BlockNode}.
      */
@@ -89,7 +96,7 @@ public class BlockNode extends SequenceNode implements RSyntaxNode {
          * it is represented as a LANGSXP with symbol "{" and a NULL cdr, representing the empty
          * sequence. This is an unpleasant special case in FastR that we can only detect by
          * re-examining the original source.
-         * 
+         *
          * A sequence of length 1, i.e. a single statement, is represented as itself, e.g. a SYMSXP
          * for "x" or a LANGSXP for a function call. Otherwise, the representation is a LISTSXP
          * pairlist, where the car is the statement and the cdr is either NILSXP or a LISTSXP for
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/ForNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/ForNode.java
index 718773b5c373be24c61b470616700538a00907ef..5d794f9ba23508b18fc9ee5941336f0398e50eee 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/ForNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/ForNode.java
@@ -33,7 +33,8 @@ import com.oracle.truffle.r.nodes.access.WriteVariableNode.Mode;
 import com.oracle.truffle.r.nodes.access.variables.*;
 import com.oracle.truffle.r.nodes.function.*;
 import com.oracle.truffle.r.runtime.*;
-import com.oracle.truffle.r.runtime.RContext.Engine.ParseException;
+import com.oracle.truffle.r.runtime.context.Engine.ParseException;
+import com.oracle.truffle.r.runtime.context.*;
 import com.oracle.truffle.r.runtime.data.*;
 import com.oracle.truffle.r.runtime.env.*;
 import com.oracle.truffle.r.runtime.gnur.*;
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/ReplacementNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/ReplacementNode.java
index 91ab933db03b9618e59e4895c3a5df864db58362..54ebe6c5e62599843a346857ee7faa473ac56c97 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/ReplacementNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/ReplacementNode.java
@@ -85,14 +85,8 @@ public final class ReplacementNode extends RNode implements RSyntaxNode {
         state.endNodeDeparse(this);
     }
 
-    private static ReplacementNode current;
-
     @Override
     public void serializeImpl(RSerialize.State state) {
-        if (this == current) {
-            throw RInternalError.shouldNotReachHere("replacement recursion");
-        }
-        current = this;
         syntaxAST.serializeImpl(state);
     }
 
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/CallArgumentsNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/CallArgumentsNode.java
index e3f98636925e9c3f474f2991e1addd42c1c2f889..1d59cb4c12c7be8d7a54735304548f8a92226a0f 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/CallArgumentsNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/CallArgumentsNode.java
@@ -207,6 +207,29 @@ public class CallArgumentsNode extends ArgumentsNode {
         return new RArgsValuesAndNames(values, resultSignature);
     }
 
+    @ExplodeLoop
+    public Object[] evaluateFlattenObjects(VirtualFrame frame) {
+        int size = arguments.length;
+        RArgsValuesAndNames varArgInfo = null;
+        if (containsVarArgsSymbol()) {
+            varArgInfo = getVarargsAndNames(frame);
+            size += (varArgInfo.getLength() - 1) * varArgsSymbolIndices.length;
+        }
+        Object[] values = new Object[size];
+        int vargsSymbolsIndex = 0;
+        int index = 0;
+        for (int i = 0; i < arguments.length; i++) {
+            if (vargsSymbolsIndex < varArgsSymbolIndices.length && varArgsSymbolIndices[vargsSymbolsIndex] == i) {
+                index = flattenVarArgsObject(frame, varArgInfo, values, index);
+                vargsSymbolsIndex++;
+            } else {
+                values[index] = arguments[i] == null ? RMissing.instance : arguments[i].execute(frame);
+                index++;
+            }
+        }
+        return values;
+    }
+
     private int flattenVarArgs(VirtualFrame frame, RArgsValuesAndNames varArgInfo, String[] names, Object[] values, int startIndex) {
         int index = startIndex;
         for (int j = 0; j < varArgInfo.getLength(); j++) {
@@ -251,4 +274,17 @@ public class CallArgumentsNode extends ArgumentsNode {
         }
         return result;
     }
+
+    private int flattenVarArgsObject(VirtualFrame frame, RArgsValuesAndNames varArgInfo, Object[] values, int startIndex) {
+        int index = startIndex;
+        for (int j = 0; j < varArgInfo.getLength(); j++) {
+            if (promiseHelper == null) {
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                promiseHelper = insert(new PromiseCheckHelperNode());
+            }
+            values[index] = promiseHelper.checkEvaluate(frame, varArgInfo.getArgument(j));
+            index++;
+        }
+        return index;
+    }
 }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/CallMatcherNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/CallMatcherNode.java
index ccab18e3cbeb3a2a421d9db5024e74fa66d97e92..09d68b7b14f11dfa534a1e37b247b1108437be86 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/CallMatcherNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/CallMatcherNode.java
@@ -91,13 +91,7 @@ public abstract class CallMatcherNode extends RBaseNode {
     }
 
     protected Object[] prepareArguments(VirtualFrame frame, Object[] reorderedArgs, ArgumentsSignature reorderedSignature, RFunction function, S3Args s3Args) {
-        RCaller caller = RArguments.getCall(frame);
-        MaterializedFrame callerFrame = RArguments.getCallerFrame(frame);
-        if (caller == null && callerFrame == null) {
-            callerFrame = RArguments.getEnvironment(frame).getFrame();
-            assert callerFrame != null;
-        }
-        return argsNode.execute(function, caller, callerFrame, RArguments.getDepth(frame) + 1, reorderedArgs, reorderedSignature, s3Args);
+        return argsNode.execute(function, RDataFactory.createCaller(this), null, RArguments.getDepth(frame) + 1, reorderedArgs, reorderedSignature, s3Args);
     }
 
     protected final void evaluatePromises(VirtualFrame frame, RFunction function, Object[] args, int varArgIndex) {
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 0aa2795c813d636d9f997854be36e0e6b5d1f7b8..ffad550f94cd6b039310244e1daf041dc3e39e9b 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
@@ -41,6 +41,7 @@ import com.oracle.truffle.r.nodes.instrument.*;
 import com.oracle.truffle.r.runtime.*;
 import com.oracle.truffle.r.runtime.RArguments.S3Args;
 import com.oracle.truffle.r.runtime.Utils.DebugExitException;
+import com.oracle.truffle.r.runtime.context.*;
 import com.oracle.truffle.r.runtime.data.*;
 import com.oracle.truffle.r.runtime.env.*;
 import com.oracle.truffle.r.runtime.env.frame.*;
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/GroupDispatchNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/GroupDispatchNode.java
index 87c16c2c611d24c5461d786dc8f1ece20fd9386c..22e8e9885369d4a54ad5b0e08c48cd51a0e859f6 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/GroupDispatchNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/GroupDispatchNode.java
@@ -24,6 +24,7 @@ import com.oracle.truffle.r.nodes.function.S3FunctionLookupNode.Result;
 import com.oracle.truffle.r.nodes.runtime.*;
 import com.oracle.truffle.r.runtime.*;
 import com.oracle.truffle.r.runtime.RArguments.S3Args;
+import com.oracle.truffle.r.runtime.context.*;
 import com.oracle.truffle.r.runtime.data.*;
 import com.oracle.truffle.r.runtime.env.*;
 import com.oracle.truffle.r.runtime.nodes.*;
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/PromiseHelperNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/PromiseHelperNode.java
index 7d74aa810f71b56ed87e6602761be15edc419dd7..d85c38664f18a927909b556f75a9293a43abd558 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/PromiseHelperNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/PromiseHelperNode.java
@@ -30,8 +30,12 @@ import com.oracle.truffle.api.nodes.*;
 import com.oracle.truffle.api.utilities.*;
 import com.oracle.truffle.r.nodes.*;
 import com.oracle.truffle.r.runtime.*;
+import com.oracle.truffle.r.runtime.context.*;
 import com.oracle.truffle.r.runtime.data.*;
-import com.oracle.truffle.r.runtime.data.RPromise.*;
+import com.oracle.truffle.r.runtime.data.RPromise.EagerPromise;
+import com.oracle.truffle.r.runtime.data.RPromise.OptType;
+import com.oracle.truffle.r.runtime.data.RPromise.PromiseType;
+import com.oracle.truffle.r.runtime.data.RPromise.VarargPromise;
 import com.oracle.truffle.r.runtime.nodes.*;
 
 /**
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 0755a6322e444a7d21e61719ed8a83ec74c3fc56..b8cbf5928b27cc0ab1f3bd138c22a55ba303a65b 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
@@ -28,6 +28,7 @@ import com.oracle.truffle.api.*;
 import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.frame.*;
+import com.oracle.truffle.api.interop.*;
 import com.oracle.truffle.api.nodes.*;
 import com.oracle.truffle.api.source.*;
 import com.oracle.truffle.api.utilities.*;
@@ -209,6 +210,11 @@ public final class RCallNode extends RNode implements RSyntaxNode {
     private final ConditionProfile isPromiseProfile = ConditionProfile.createBinaryProfile();
     private final BranchProfile normalDispatchProfile = BranchProfile.create();
     private final BranchProfile errorProfile = BranchProfile.create();
+    private final ConditionProfile isRFunctionProfile = ConditionProfile.createBinaryProfile();
+
+    @Child private Node foreignCall;
+    @CompilationFinal private int foreignCallArgCount;
+    @Child private CallArgumentsNode foreignCallArguments;
 
     public RCallNode(RNode function, RSyntaxNode[] arguments, ArgumentsSignature signature) {
         this.functionNode = function;
@@ -225,7 +231,28 @@ public final class RCallNode extends RNode implements RSyntaxNode {
         return execute(frame, executeFunctionNode(frame));
     }
 
-    public Object execute(VirtualFrame frame, RFunction function) {
+    public Object execute(VirtualFrame frame, Object functionObject) {
+        RFunction function;
+        if (isRFunctionProfile.profile(functionObject instanceof RFunction)) {
+            function = (RFunction) functionObject;
+            // fall through
+        } else if (functionObject instanceof TruffleObject && !(functionObject instanceof RTypedValue)) {
+            if (foreignCallArguments == null) {
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                foreignCallArguments = insert(createArguments(null, true, true));
+            }
+            Object[] argumentsArray = foreignCallArguments.evaluateFlattenObjects(frame);
+            if (foreignCall == null || foreignCallArgCount != argumentsArray.length) {
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                foreignCall = insert(Message.createExecute(argumentsArray.length).createNode());
+                foreignCallArgCount = argumentsArray.length;
+            }
+            return ForeignAccess.execute(foreignCall, frame, (TruffleObject) functionObject, argumentsArray);
+        } else {
+            errorProfile.enter();
+            throw RError.error(this, RError.Message.APPLY_NON_FUNCTION);
+        }
+
         if (!signature.isEmpty() && function.getRBuiltin() != null) {
             RBuiltinDescriptor builtin = builtinProfile.profile(function.getRBuiltin());
             if (builtin.getDispatch() == RDispatch.INTERNAL_GENERIC) {
@@ -270,7 +297,7 @@ public final class RCallNode extends RNode implements RSyntaxNode {
         return call.execute(frame, function, null);
     }
 
-    private RFunction executeFunctionNode(VirtualFrame frame) {
+    private Object executeFunctionNode(VirtualFrame frame) {
         /**
          * Expressions such as "pkg:::f" can result in (delayedAssign) promises that must be
          * explicitly resolved.
@@ -283,12 +310,7 @@ public final class RCallNode extends RNode implements RSyntaxNode {
             }
             value = promiseHelper.evaluate(frame, (RPromise) value);
         }
-        if (value instanceof RFunction) {
-            return (RFunction) value;
-        } else {
-            errorProfile.enter();
-            throw RError.error(this, RError.Message.APPLY_NON_FUNCTION);
-        }
+        return value;
     }
 
     public RNode getFunctionNode() {
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/S3FunctionLookupNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/S3FunctionLookupNode.java
index bfd848701103cd94a9ed534be28212906235080a..a83085214806b9bbda39c878f2d46ebac19c2d07 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/S3FunctionLookupNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/S3FunctionLookupNode.java
@@ -22,6 +22,7 @@ import com.oracle.truffle.api.utilities.*;
 import com.oracle.truffle.r.nodes.access.variables.*;
 import com.oracle.truffle.r.nodes.access.variables.ReadVariableNode.ReadKind;
 import com.oracle.truffle.r.runtime.*;
+import com.oracle.truffle.r.runtime.context.*;
 import com.oracle.truffle.r.runtime.data.*;
 import com.oracle.truffle.r.runtime.env.*;
 import com.oracle.truffle.r.runtime.nodes.*;
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/UseMethodInternalNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/UseMethodInternalNode.java
index c75307c7fa3f8074ec3a83eb34493240ba45a8ae..d7212fe3176e5de82bee2457b885de827561847e 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/UseMethodInternalNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/UseMethodInternalNode.java
@@ -33,7 +33,7 @@ public final class UseMethodInternalNode extends RNode implements VisibilityCont
     public UseMethodInternalNode(String generic, ArgumentsSignature signature, boolean wrap) {
         this.generic = generic;
         this.signature = signature;
-        this.wrap = wrap && FastROptions.InvisibleArgs.getValue();
+        this.wrap = wrap && FastROptions.InvisibleArgs;
     }
 
     public Object execute(VirtualFrame frame, RAbstractContainer dispatchOn, Object[] arguments) {
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/opt/EagerEvalHelper.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/opt/EagerEvalHelper.java
index 564042082bd50a2af18ae0a44295c4f47142c177..9c692fb85ca1da073ca1bed3ca8d1ac2afea17cd 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/opt/EagerEvalHelper.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/opt/EagerEvalHelper.java
@@ -39,28 +39,28 @@ public class EagerEvalHelper {
      * @return Whether to use optimizations for constants
      */
     public static boolean optConsts() {
-        return FastROptions.EagerEval.getValue() || FastROptions.EagerEvalConstants.getValue();
+        return FastROptions.EagerEval || FastROptions.EagerEvalConstants;
     }
 
     /**
      * @return Whether to use optimizations for single variables
      */
     public static boolean optVars() {
-        return FastROptions.EagerEval.getValue() || FastROptions.EagerEvalVariables.getValue();
+        return FastROptions.EagerEval || FastROptions.EagerEvalVariables;
     }
 
     /**
      * @return Whether to use optimizations for single variables
      */
     public static boolean optDefault() {
-        return FastROptions.EagerEval.getValue() || FastROptions.EagerEvalDefault.getValue();
+        return FastROptions.EagerEval || FastROptions.EagerEvalDefault;
     }
 
     /**
      * @return Whether to use optimizations for arbitrary expressions
      */
     public static boolean optExprs() {
-        return FastROptions.EagerEval.getValue() || FastROptions.EagerEvalExpressions.getValue();
+        return FastROptions.EagerEval || FastROptions.EagerEvalExpressions;
     }
 
     public static boolean isOptimizableConstant(RNode expr) {
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/instrument/RInstrument.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/instrument/RInstrument.java
index 47d17b49b3dab0e7ac80a84d00ce82c3774c5144..46891d253b0c037e42037d983232af25eef3bf58 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/instrument/RInstrument.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/instrument/RInstrument.java
@@ -177,7 +177,7 @@ public class RInstrument {
                 }
             } else if (tag == StandardSyntaxTag.START_METHOD) {
                 putProbe((FunctionUID) tagValue, probe);
-                if (FastROptions.TraceCalls.getValue()) {
+                if (FastROptions.TraceCalls) {
                     TraceHandling.attachTraceHandler((FunctionUID) tagValue);
                 }
             } else if (tag == StandardSyntaxTag.STATEMENT) {
@@ -236,18 +236,15 @@ public class RInstrument {
      * features that need it are also enabled.
      */
     public static void initialize() {
-        // @formatter:off
-        instrumentingEnabled = FastROptions.Instrument.getValue() || FastROptions.TraceCalls.getValue() || FastROptions.Rdebug.getValue() != null ||
-                        REntryCounters.Function.enabled() || RNodeTimer.Statement.enabled();
-        // @formatter:on
+        instrumentingEnabled = FastROptions.Instrument || FastROptions.TraceCalls || FastROptions.Rdebug != null || REntryCounters.Function.enabled() || RNodeTimer.Statement.enabled();
         if (instrumentingEnabled) {
             Probe.registerASTProber(RASTProber.getRASTProber());
             Probe.addProbeListener(new RProbeListener());
         }
-        if (instrumentingEnabled || FastROptions.LoadPkgSourcesIndex.getValue()) {
+        if (instrumentingEnabled || FastROptions.LoadPkgSourcesIndex) {
             RPackageSource.initialize();
         }
-        String rdebugValue = FastROptions.Rdebug.getValue();
+        String rdebugValue = FastROptions.Rdebug;
         if (rdebugValue != null) {
             debugFunctionNames = rdebugValue.split(",");
         }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/instrument/debug/Browser.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/instrument/debug/Browser.java
index 8cf0c6fa82f1bff5fbb3a020e5e29d51a9775104..629f012a9db494d8203e906b1a3e42db9a72d3bc 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/instrument/debug/Browser.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/instrument/debug/Browser.java
@@ -26,6 +26,8 @@ import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.frame.*;
 import com.oracle.truffle.api.source.*;
 import com.oracle.truffle.r.runtime.*;
+import com.oracle.truffle.r.runtime.context.*;
+import com.oracle.truffle.r.runtime.context.Engine.ParseException;
 import com.oracle.truffle.r.runtime.data.*;
 
 /**
@@ -45,7 +47,7 @@ public class Browser {
 
     @TruffleBoundary
     public static ExitMode interact(MaterializedFrame frame) {
-        RContext.ConsoleHandler ch = RContext.getInstance().getConsoleHandler();
+        ConsoleHandler ch = RContext.getInstance().getConsoleHandler();
         String savedPrompt = ch.getPrompt();
         ch.setPrompt(browserPrompt(RArguments.getDepth(frame)));
         ExitMode exitMode = ExitMode.NEXT;
@@ -53,7 +55,7 @@ public class Browser {
             LW: while (true) {
                 String input = ch.readLine().trim();
                 if (input.length() == 0) {
-                    RLogicalVector browserNLdisabledVec = (RLogicalVector) RContext.getROptionsState().getValue("browserNLdisabled");
+                    RLogicalVector browserNLdisabledVec = (RLogicalVector) RContext.getInstance().stateROptions.getValue("browserNLdisabled");
                     if (!RRuntime.fromLogical(browserNLdisabledVec.getDataAt(0))) {
                         input = lastEmptyLineCommand;
                     }
@@ -89,7 +91,14 @@ public class Browser {
                     }
 
                     default:
-                        RContext.getEngine().parseAndEval(Source.fromText(input, BROWSER_SOURCE), frame, true, false);
+                        try {
+                            RContext.getEngine().parseAndEval(Source.fromText(input, BROWSER_SOURCE), frame, true);
+                        } catch (ReturnException e) {
+                            exitMode = ExitMode.NEXT;
+                            break LW;
+                        } catch (ParseException e) {
+                            throw e.throwAsRError();
+                        }
                         break;
                 }
             }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/instrument/debug/DebugHandling.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/instrument/debug/DebugHandling.java
index c69ca9c8da90ef41efd0d0223f717c26c779bc3a..7c9a806706f1a0ee9641e7511820d27be5186376 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/instrument/debug/DebugHandling.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/instrument/debug/DebugHandling.java
@@ -35,6 +35,7 @@ import com.oracle.truffle.r.nodes.control.*;
 import com.oracle.truffle.r.nodes.function.*;
 import com.oracle.truffle.r.nodes.instrument.*;
 import com.oracle.truffle.r.runtime.*;
+import com.oracle.truffle.r.runtime.context.*;
 import com.oracle.truffle.r.runtime.data.*;
 import com.oracle.truffle.r.runtime.nodes.*;
 
@@ -400,7 +401,7 @@ public class DebugHandling {
     }
 
     private static void printNode(Node node, boolean curly) {
-        RContext.ConsoleHandler consoleHandler = RContext.getInstance().getConsoleHandler();
+        ConsoleHandler consoleHandler = RContext.getInstance().getConsoleHandler();
         RDeparse.State state = RDeparse.State.createPrintableState();
         ((RBaseNode) node).deparse(state);
         consoleHandler.print("debug: ");
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/instrument/trace/TraceHandling.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/instrument/trace/TraceHandling.java
index 32fe70c27f241fcfe8f114fbeba6bf0c50213409..632d498b98c7a592e4f287ba7c42cac37e123f27 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/instrument/trace/TraceHandling.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/instrument/trace/TraceHandling.java
@@ -32,6 +32,7 @@ import com.oracle.truffle.api.utilities.*;
 import com.oracle.truffle.r.nodes.function.*;
 import com.oracle.truffle.r.nodes.instrument.*;
 import com.oracle.truffle.r.runtime.*;
+import com.oracle.truffle.r.runtime.context.*;
 import com.oracle.truffle.r.runtime.data.*;
 
 public class TraceHandling {
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/primitive/BinaryMapNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/primitive/BinaryMapNode.java
index 9b0105d24f460d9e9d6ce763b486a010c7dfed25..7c544396a25aac15ba183e0810ed90eba067312a 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/primitive/BinaryMapNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/primitive/BinaryMapNode.java
@@ -226,12 +226,7 @@ public final class BinaryMapNode extends RBaseNode {
         if (target == null) {
             int maxLength = Math.max(leftLength, rightLength);
             target = createOrShareVector(leftLength, left, rightLength, right, maxLength);
-            Object store;
-            if (target instanceof RAccessibleStore) {
-                store = ((RAccessibleStore<?>) target).getInternalStore();
-            } else {
-                throw RInternalError.shouldNotReachHere();
-            }
+            Object store = target.getInternalStore();
 
             assert left.getLength() == leftLength;
             assert right.getLength() == rightLength;
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/primitive/UnaryMapNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/primitive/UnaryMapNode.java
index 02919e7095040ca8c1f61980424eb4d7a665fe20..a2b6a1ffcd8fd45e18fed553a6e45261d187c6e0 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/primitive/UnaryMapNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/primitive/UnaryMapNode.java
@@ -124,12 +124,7 @@ public final class UnaryMapNode extends RBaseNode {
         }
         if (target == null) {
             target = createOrShareVector(operandLength, operand);
-            Object store;
-            if (target instanceof RAccessibleStore) {
-                store = ((RAccessibleStore<?>) target).getInternalStore();
-            } else {
-                throw RInternalError.shouldNotReachHere();
-            }
+            Object store = target.getInternalStore();
             vectorNode.apply(scalarNode, store, operandCast, operandLength);
             RNode.reportWork(this, operandLength);
             target.setComplete(scalarNode.isComplete());
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/profile/CountedLoopConditionProfile.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/profile/CountedLoopConditionProfile.java
new file mode 100644
index 0000000000000000000000000000000000000000..c997c2e4c7b2e6ffb91a6462bc47b26fb457997e
--- /dev/null
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/profile/CountedLoopConditionProfile.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2015, 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.profile;
+
+import com.oracle.truffle.api.*;
+import com.oracle.truffle.api.CompilerDirectives.*;
+
+public final class CountedLoopConditionProfile {
+
+    @CompilationFinal private int count;
+    @CompilationFinal private int sum;
+
+    public void profileLength(int length) {
+        if (CompilerDirectives.inInterpreter()) {
+            sum += length;
+            count++;
+        }
+    }
+
+    public boolean inject(boolean condition) {
+        if (CompilerDirectives.inCompiledCode()) {
+            double averageLength = (double) sum / (double) count;
+            return CompilerDirectives.injectBranchProbability(averageLength / (averageLength + 1.0), condition);
+        } else {
+            return condition;
+        }
+    }
+
+    public static CountedLoopConditionProfile create() {
+        return new CountedLoopConditionProfile();
+    }
+}
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/profile/IntValueProfile.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/profile/IntValueProfile.java
index 569d7d027f5df351ea1a772719a1c4296353ca22..26e95f1f5594ffd7a1a546501586383aadaac1bc 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/profile/IntValueProfile.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/profile/IntValueProfile.java
@@ -32,8 +32,7 @@ public final class IntValueProfile {
     private static final byte GENERIC = 2;
 
     @CompilationFinal private int cachedValue;
-
-    private byte state = 0;
+    @CompilationFinal private byte state = 0;
 
     private IntValueProfile() {
     }
@@ -47,6 +46,7 @@ public final class IntValueProfile {
             CompilerDirectives.transferToInterpreterAndInvalidate();
             if (state == UNINITIALIZED) {
                 this.cachedValue = value;
+                this.state = SPECIALIZED;
             } else {
                 this.state = GENERIC;
             }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/profile/VectorLengthProfile.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/profile/VectorLengthProfile.java
index 4c14c6688e780a833560b8a104ed97620bf770c4..a4a439febae2e7d7826d996115b5bc279f72157c 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/profile/VectorLengthProfile.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/profile/VectorLengthProfile.java
@@ -55,6 +55,10 @@ public final class VectorLengthProfile {
         }
     }
 
+    public int getCachedLength() {
+        return cachedLength;
+    }
+
     public static VectorLengthProfile create() {
         return new VectorLengthProfile();
     }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastIntegerNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastIntegerNode.java
index bd515df322a359bac748ab52ee55fd69dfe9de06..2f11fed3829f37d318e8c449d455d803ece7018d 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastIntegerNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastIntegerNode.java
@@ -77,12 +77,12 @@ public abstract class CastIntegerNode extends CastBaseNode {
     }
 
     @Specialization
-    protected RIntVector doIntVector(RIntVector operand) {
+    protected RIntSequence doIntSequence(RIntSequence operand) {
         return operand;
     }
 
     @Specialization
-    protected RIntSequence doIntSequence(RIntSequence operand) {
+    protected RAbstractIntVector doIntVector(RAbstractIntVector operand) {
         return operand;
     }
 
@@ -161,7 +161,7 @@ public abstract class CastIntegerNode extends CastBaseNode {
     }
 
     @Specialization
-    protected RIntVector doComplexVector(RComplexVector operand) {
+    protected RIntVector doComplexVector(RAbstractComplexVector operand) {
         naCheck.enable(operand);
         int length = operand.getLength();
         int[] idata = new int[length];
@@ -181,7 +181,7 @@ public abstract class CastIntegerNode extends CastBaseNode {
     }
 
     @Specialization
-    protected RIntVector doStringVector(RStringVector operand, //
+    protected RIntVector doStringVector(RAbstractStringVector operand, //
                     @Cached("createBinaryProfile()") ConditionProfile emptyStringProfile) {
         naCheck.enable(operand);
         int[] idata = new int[operand.getLength()];
@@ -217,28 +217,28 @@ public abstract class CastIntegerNode extends CastBaseNode {
     }
 
     @Specialization
-    public RIntVector doLogicalVector(RLogicalVector operand) {
+    public RIntVector doLogicalVector(RAbstractLogicalVector operand) {
         return createResultVector(operand, index -> naCheck.convertLogicalToInt(operand.getDataAt(index)));
     }
 
     @Specialization
-    protected RIntVector doDoubleVector(RDoubleVector operand) {
+    protected RIntVector doDoubleVector(RAbstractDoubleVector operand) {
         naCheck.enable(operand);
         return createResultVector(operand, naCheck.convertDoubleVectorToIntData(operand));
     }
 
     @Specialization
-    protected RIntVector doRawVector(RRawVector operand) {
+    protected RIntVector doRawVector(RAbstractRawVector operand) {
         return createResultVector(operand, index -> RRuntime.raw2int(operand.getDataAt(index)));
     }
 
     @Specialization
-    protected RIntVector doList(RList list) {
+    protected RIntVector doList(RAbstractListVector list) {
         int length = list.getLength();
         int[] result = new int[length];
         boolean seenNA = false;
         for (int i = 0; i < length; i++) {
-            Object entry = list.getDataAt(i);
+            Object entry = list.getDataAtAsObject(i);
             if (entry instanceof RList) {
                 result[i] = RRuntime.INT_NA;
                 seenNA = true;
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastListNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastListNode.java
index 1f3612ebaba870dde9b0799235bbfeff36091149..5ca10dddd14a13783bde7d2b043372dbef4f77b7 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastListNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastListNode.java
@@ -28,6 +28,7 @@ import com.oracle.truffle.api.*;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.*;
 import com.oracle.truffle.r.runtime.*;
+import com.oracle.truffle.r.runtime.context.*;
 import com.oracle.truffle.r.runtime.data.*;
 import com.oracle.truffle.r.runtime.data.RAttributes.RAttribute;
 import com.oracle.truffle.r.runtime.data.model.*;
diff --git a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/gnfi/GNFI_RFFIFactory.java b/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/gnfi/GNFI_RFFIFactory.java
index abd210cac8e9889eeab7706ef1536e5ef8cf6fa1..61472f263347daafc2b1e3ccbc49e389e5308b6d 100644
--- a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/gnfi/GNFI_RFFIFactory.java
+++ b/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/gnfi/GNFI_RFFIFactory.java
@@ -30,7 +30,8 @@ import java.util.*;
 import com.oracle.nfi.*;
 import com.oracle.nfi.api.*;
 import com.oracle.truffle.r.runtime.*;
-import com.oracle.truffle.r.runtime.RContext.ContextState;
+import com.oracle.truffle.r.runtime.context.*;
+import com.oracle.truffle.r.runtime.context.RContext.ContextState;
 import com.oracle.truffle.r.runtime.ffi.*;
 
 /**
@@ -207,8 +208,8 @@ public class GNFI_RFFIFactory extends RFFIFactory implements RFFI, BaseRFFI {
         throw Utils.fail("glob not implemented");
     }
 
-    public ContextState newContext(RContext context, Object... objects) {
+    @Override
+    public ContextState newContext(RContext context) {
         throw RInternalError.unimplemented();
     }
-
 }
diff --git a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jnr/CallRFFIHelper.java b/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jnr/CallRFFIHelper.java
index 76648106be8befd9d474e6b942a76b9e14c64a35..c91106200b723abc56eddc37207a5b2a8a49a72e 100644
--- a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jnr/CallRFFIHelper.java
+++ b/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jnr/CallRFFIHelper.java
@@ -23,6 +23,7 @@
 package com.oracle.truffle.r.runtime.ffi.jnr;
 
 import com.oracle.truffle.r.runtime.*;
+import com.oracle.truffle.r.runtime.context.*;
 import com.oracle.truffle.r.runtime.data.*;
 import com.oracle.truffle.r.runtime.data.model.*;
 import com.oracle.truffle.r.runtime.env.*;
@@ -308,19 +309,19 @@ public class CallRFFIHelper {
     }
 
     static Object getGlobalEnv() {
-        return RContext.getREnvironmentState().getGlobalEnv();
+        return RContext.getInstance().stateREnvironment.getGlobalEnv();
     }
 
     static Object getBaseEnv() {
-        return RContext.getREnvironmentState().getBaseEnv();
+        return RContext.getInstance().stateREnvironment.getBaseEnv();
     }
 
     static Object getBaseNamespace() {
-        return RContext.getREnvironmentState().getBaseNamespace();
+        return RContext.getInstance().stateREnvironment.getBaseNamespace();
     }
 
     static Object getNamespaceRegistry() {
-        return RContext.getREnvironmentState().getNamespaceRegistry();
+        return RContext.getInstance().stateREnvironment.getNamespaceRegistry();
     }
 
     static int isInteractive() {
diff --git a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jnr/JNR_RFFIFactory.java b/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jnr/JNR_RFFIFactory.java
index e6f420c416215c781878fd9b187eacb367246552..8168b6dfc50db9c6e86135649bd8114a1257af55 100644
--- a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jnr/JNR_RFFIFactory.java
+++ b/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jnr/JNR_RFFIFactory.java
@@ -34,8 +34,9 @@ import jnr.posix.*;
 
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.r.runtime.*;
-import com.oracle.truffle.r.runtime.RContext.ContextState;
 import com.oracle.truffle.r.runtime.conn.*;
+import com.oracle.truffle.r.runtime.context.*;
+import com.oracle.truffle.r.runtime.context.RContext.ContextState;
 import com.oracle.truffle.r.runtime.data.*;
 import com.oracle.truffle.r.runtime.env.*;
 import com.oracle.truffle.r.runtime.ffi.*;
@@ -68,7 +69,8 @@ public class JNR_RFFIFactory extends RFFIFactory implements RFFI, BaseRFFI, Stat
 
     }
 
-    public ContextState newContext(RContext context, Object... objects) {
+    @Override
+    public ContextState newContext(RContext context) {
         return new ContextStateImpl();
     }
 
@@ -413,8 +415,7 @@ public class JNR_RFFIFactory extends RFFIFactory implements RFFI, BaseRFFI, Stat
         @TruffleBoundary
         private static Linpack createAndLoadLib() {
             // need to load blas lib as Fortran functions in appl lib need it
-            LibraryLoader.create(Linpack.class).load("Rblas");
-            return LibraryLoader.create(Linpack.class).load("appl");
+            return LibraryLoader.create(Linpack.class).library("Rblas").library("appl").load();
         }
 
         static Linpack linpack() {
@@ -449,7 +450,7 @@ public class JNR_RFFIFactory extends RFFIFactory implements RFFI, BaseRFFI, Stat
     public interface Stats {
         /*
          * TODO add @In/@Out to any arrays that are known to be either @In or @Out (default is
-         * 
+         *
          * @Inout)
          */
 
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/FastROptions.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/FastROptions.java
index 0fd9498d675978f3a20a70c5f905813c28c39791..2beeb56b301a351aea20cc04bb574df5b793de98 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/FastROptions.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/FastROptions.java
@@ -25,126 +25,103 @@ package com.oracle.truffle.r.runtime;
 import java.util.*;
 import java.util.Map.Entry;
 
-import com.oracle.truffle.api.*;
-import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
-import com.oracle.truffle.r.runtime.RCmdOptions.*;
-
 /**
  * Options to control the behavior of the FastR system, that relate to the implementation, i.e., are
- * <b>not</b> part of the standard set of R options or command line options.
- *
- * Currently it is not possible to include the FastR options in Graal as FastR is not part of Graal.
- * Someday it may be possible to register such options with the Graal VM, but in the interim, we
- * override the implementation to use system properties. The syntax follows that for Graal options
- * except that a {@code D} (for setting system property) must be the first character,e.g.
- * {@code -DR:+OptionName} to set a boolean valued option to {@code true}.
+ * <b>not</b> part of the standard set of R options or command line options. The syntax follows that
+ * for Graal options except that a {@code D} (for setting system property) must be the first
+ * character,e.g. {@code -DR:+OptionName} to set a boolean valued option to {@code true}.
  */
 public class FastROptions {
-    private static final List<Option<?>> optionsList = new ArrayList<>();
-
-    //@formatter:off
-    public static final Option<Boolean> PrintErrorStacktraces = newBooleanOption("PrintErrorStacktraces", false, "Prints Java and R stack traces for all errors");
-    public static final Option<Boolean> PrintErrorStacktracesToFile = newBooleanOption("PrintErrorStacktracesToFile", true, "Dumps Java and R stack traces to file for all errors");
-    public static final Option<Boolean> CheckResultCompleteness = newBooleanOption("CheckResultCompleteness", true, "Assert completeness of results vectors after evaluating unit tests and R shell commands");
-    public static final Option<String>  Debug = newStringOption("Debug", null, "Debug=name1,name2,...; Turn on debugging output for 'name1', 'name2', etc.");
-    public static final Option<Boolean> Instrument = newBooleanOption("Instrument", false, "Enable Instrumentation");
-    public static final Option<Boolean> TraceCalls = newBooleanOption("TraceCalls", false, "Trace all R function calls (implies +Instrument)");
-    public static final Option<String>  PerfStats = newStringOption("PerfStats", null, "PerfStats=p1,p2,...; Collect performance stats identified by p1, etc.");
-    public static final Option<String>  PerfStatsFile = newStringOption("PerfStatsFile", null, "PerfStatsFile=file; Send performance stats to 'file', default stdout");
-    public static final Option<String>  Rdebug = newStringOption("Rdebug", null, "Rdebug=f1,f2.,,,; list of R function to call debug on (implies +Instrument)");
-    public static final Option<Boolean> PerformanceWarnings = newBooleanOption("PerformanceWarnings", false, "Print FastR performance warning");
-    public static final Option<Boolean> LoadBase = newBooleanOption("LoadBase", true, "Load base package");
-    public static final Option<Boolean> PrintComplexLookups = newBooleanOption("PrintComplexLookups", false, "Print a message for each non-trivial variable lookup");
-    public static final Option<Boolean> IgnoreVisibility = newBooleanOption("IgnoreVisibility", false, "Ignore setting of the visibility flag");
-    public static final Option<Boolean> LoadPkgSourcesIndex = newBooleanOption("LoadPkgSourcesIndex", true, "Load R package sources index");
-    public static final Option<Boolean> InvisibleArgs = newBooleanOption("InvisibleArgs", true, "Argument writes do not trigger state transitions");
-    public static final Option<Boolean> ExperimentalStateTrans = newBooleanOption("ExperimentalStateTrans", true, "Eperimental state transition implementation");
-    public static final Option<Boolean> RefCountIncOnly = newBooleanOption("RefCountIncOnly", false, "Disable reference count decrements for eperimental state transition implementation");
-
-    // Promises optimizations
-    public static final Option<Boolean> EagerEval = newBooleanOption("EagerEval", false, "If enabled, overrides all other EagerEval switches (see EagerEvalHelper)");
-    public static final Option<Boolean> EagerEvalConstants = newBooleanOption("EagerEvalConstants", true, "Unconditionally evaluates constants before creating Promises");
-    public static final Option<Boolean> EagerEvalVariables = newBooleanOption("EagerEvalVariables", true, "Enables optimistic eager evaluation of single variables reads");
-    public static final Option<Boolean> EagerEvalDefault = newBooleanOption("EagerEvalDefault", false, "Enables optimistic eager evaluation of single variables reads (for default parameters)");
-    public static final Option<Boolean> EagerEvalExpressions = newBooleanOption("EagerEvalExpressions", false, "Enables optimistic eager evaluation of trivial expressions");
-    //@formatter:on
-
-    @CompilationFinal public static boolean NewStateTransition;
-    @CompilationFinal public static boolean RefCountIncrementOnly;
-
-    private static boolean initialized;
-
-    public static Option<Boolean> newBooleanOption(String name, boolean defaultValue, String help) {
-        Option<Boolean> option = new Option<>(true, OptionType.BOOLEAN, null, name, help, defaultValue);
-        optionsList.add(option);
-        return option;
-    }
-
-    public static Option<String> newStringOption(String name, String defaultValue, String help) {
-        Option<String> option = new Option<>(null, true, OptionType.STRING, null, name, help, defaultValue);
-        optionsList.add(option);
-        return option;
-    }
 
-    public static void initialize() {
-        if (initialized) {
-            return;
-        }
-        CompilerAsserts.neverPartOfCompilation();
-        initialized = true;
+    private static Map<String, String> options = new HashMap<>();
+    private static boolean printHelp;
+    static {
         for (Entry<Object, Object> entry : System.getProperties().entrySet()) {
             String prop = (String) entry.getKey();
             if (prop.equals("R")) {
-                printHelp();
-                Utils.exit(0);
+                printHelp = true;
             }
             if (prop.startsWith("R:")) {
-                String optName = optionName(prop);
-                Option<?> option = findOption(optName);
-                if (option == null) {
-                    System.err.println(prop + " is not a valid FastR option");
-                    System.exit(2);
-                } else {
-                    switch (option.type) {
-                        case BOOLEAN: {
-                            boolean value = booleanOptionValue(prop);
-                            option.setValue(value);
-                            break;
-                        }
+                options.put(optionName(prop), prop);
+            }
+        }
+    }
 
-                        case STRING: {
-                            String value = (String) entry.getValue();
-                            option.setValue(value);
-                            break;
-                        }
+    public static final boolean PrintErrorStacktraces = parseBooleanOption("PrintErrorStacktraces", false, "Prints Java and R stack traces for all errors");
+    public static final boolean PrintErrorStacktracesToFile = parseBooleanOption("PrintErrorStacktracesToFile", true, "Dumps Java and R stack traces to file for all errors");
+    public static final boolean CheckResultCompleteness = parseBooleanOption("CheckResultCompleteness", true, "Assert completeness of results vectors after evaluating unit tests and R shell commands");
+    public static String Debug = parseStringOption("Debug", null, "Debug=name1,name2,...; Turn on debugging output for 'name1', 'name2', etc.");
+    public static final boolean Instrument = parseBooleanOption("Instrument", false, "Enable Instrumentation");
+    public static final boolean TraceCalls = parseBooleanOption("TraceCalls", false, "Trace all R function calls (implies +Instrument)");
+    public static final String PerfStats = parseStringOption("PerfStats", null, "PerfStats=p1,p2,...; Collect performance stats identified by p1, etc.");
+    public static final String PerfStatsFile = parseStringOption("PerfStatsFile", null, "PerfStatsFile=file; Send performance stats to 'file', default stdout");
+    public static final String Rdebug = parseStringOption("Rdebug", null, "Rdebug=f1,f2.,,,; list of R function to call debug on (implies +Instrument)");
+    public static final boolean PerformanceWarnings = parseBooleanOption("PerformanceWarnings", false, "Print FastR performance warning");
+    public static final boolean LoadBase = parseBooleanOption("LoadBase", true, "Load base package");
+    public static final boolean PrintComplexLookups = parseBooleanOption("PrintComplexLookups", false, "Print a message for each non-trivial variable lookup");
+    public static final boolean IgnoreVisibility = parseBooleanOption("IgnoreVisibility", false, "Ignore setting of the visibility flag");
+    public static final boolean LoadPkgSourcesIndex = parseBooleanOption("LoadPkgSourcesIndex", true, "Load R package sources index");
+    public static final boolean InvisibleArgs = parseBooleanOption("InvisibleArgs", true, "Argument writes do not trigger state transitions");
+    public static final boolean NewStateTransition = parseBooleanOption("NewStateTransition", true, "Eperimental state transition implementation");
+    public static final boolean RefCountIncrementOnly = parseBooleanOption("RefCountIncrementOnly", false, "Disable reference count decrements for eperimental state transition implementation");
+
+    // Promises optimizations
+    public static final boolean EagerEval = parseBooleanOption("EagerEval", false, "If enabled, overrides all other EagerEval switches (see EagerEvalHelper)");
+    public static final boolean EagerEvalConstants = parseBooleanOption("EagerEvalConstants", true, "Unconditionally evaluates constants before creating Promises");
+    public static final boolean EagerEvalVariables = parseBooleanOption("EagerEvalVariables", true, "Enables optimistic eager evaluation of single variables reads");
+    public static final boolean EagerEvalDefault = parseBooleanOption("EagerEvalDefault", false, "Enables optimistic eager evaluation of single variables reads (for default parameters)");
+    public static final boolean EagerEvalExpressions = parseBooleanOption("EagerEvalExpressions", false, "Enables optimistic eager evaluation of trivial expressions");
+
+    static {
+        if (options != null && !options.isEmpty()) {
+            System.err.println(options.keySet() + " are not valid FastR options");
+            System.exit(2);
+        }
+        if (printHelp) {
+            System.exit(0);
+        }
+    }
 
-                        default:
-                            throw RInternalError.shouldNotReachHere();
-                    }
+    public static boolean parseBooleanOption(String name, boolean defaultValue, String help) {
+        if (printHelp) {
+            System.out.printf("%35s %s (default: %b)\n", "-DR:" + name, help, defaultValue);
+            return false;
+        } else {
+            String optionValue = options.remove(name);
+            if (optionValue == null) {
+                return defaultValue;
+            } else {
+                switch (optionValue.charAt(2)) {
+                    case '+':
+                        return true;
+                    case '-':
+                        return false;
+                    default:
+                        System.out.println("-DR:[+-]" + name + " expected");
+                        System.exit(2);
+                        return false;
                 }
             }
         }
-        NewStateTransition = FastROptions.ExperimentalStateTrans.getValue();
-        RefCountIncrementOnly = FastROptions.RefCountIncOnly.getValue();
-        // debug();
     }
 
-    private static void printHelp() {
-        for (Option<?> option : optionsList) {
-            String helpName = option.plainName;
-            System.out.printf("    -DR:%s", helpName);
-            int spaces;
-            int optLength = helpName.length() + 8;
-            if (optLength >= 22) {
-                System.out.println();
-                spaces = 22;
+    public static String parseStringOption(String name, String defaultValue, String help) {
+        if (printHelp) {
+            System.out.printf("%35s %s (default: %b)\n", "-DR:" + name, help, defaultValue);
+            return "";
+        } else {
+            String optionValue = options.remove(name);
+            if (optionValue == null) {
+                return defaultValue;
             } else {
-                spaces = 22 - optLength;
-            }
-            for (int i = 0; i < spaces; i++) {
-                System.out.print(' ');
+                int equals = optionValue.indexOf('=');
+                if (equals == -1) {
+                    System.out.println("-DR:" + name + "=value expected");
+                    System.exit(2);
+                    return null;
+                }
+                return optionValue.substring(equals + 1);
             }
-            System.out.println(option.help);
         }
     }
 
@@ -174,18 +151,16 @@ public class FastROptions {
      * @return {@code ""} if the option is set with no {@code =value} component, the element if
      *         {@code element} matches an element, {@code null} otherwise.
      */
-    public static String matchesElement(String element, Option<String> stringOption) {
-        initialize();
-        String s = stringOption.getValue();
-        if (s == null) {
+    public static String matchesElement(String element, String option) {
+        if (option == null) {
             return null;
-        } else if (s.length() == 0) {
-            return s;
+        } else if (option.length() == 0) {
+            return option;
         } else {
-            String[] parts = s.split(",");
+            String[] parts = option.split(",");
             for (String part : parts) {
                 if (part.startsWith(element)) {
-                    return s;
+                    return option;
                 }
             }
         }
@@ -197,7 +172,7 @@ public class FastROptions {
      * use-case.
      */
     public static void debugUpdate(String element) {
-        String s = Debug.getValue();
+        String s = Debug;
         if (s == null) {
             // nothing was set
             s = element;
@@ -213,16 +188,7 @@ public class FastROptions {
             }
             s = s + "," + element;
         }
-        Debug.setValue(s);
-    }
-
-    private static Option<?> findOption(String key) {
-        for (Option<?> option : optionsList) {
-            if (option.plainName.equals(key)) {
-                return option;
-            }
-        }
-        return null;
+        Debug = s;
     }
 
     private static String optionName(String key) {
@@ -235,17 +201,4 @@ public class FastROptions {
             return s;
         }
     }
-
-    private static boolean booleanOptionValue(String option) {
-        return option.charAt(2) == '+';
-    }
-
-    @SuppressWarnings("unused")
-    private static void debug() {
-        for (Option<?> option : optionsList) {
-            System.out.printf("%s: ", option.plainName);
-            System.out.println(option.getValue());
-        }
-    }
-
 }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RArguments.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RArguments.java
index 8890bf4e7a23152702f31505baa7c48b857c98ea..82156504f7329ceb6055bbf9a374c84cb1765ba4 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RArguments.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RArguments.java
@@ -305,6 +305,11 @@ public final class RArguments {
      */
     public static Frame unwrap(Frame frame) {
         Object[] arguments = frame.getArguments();
-        return arguments.length == 1 ? (Frame) arguments[0] : frame;
+        return arguments.length == 1 && arguments[0] instanceof Frame ? (Frame) arguments[0] : frame;
+    }
+
+    public static boolean isRFrame(Frame frame) {
+        Object[] arguments = frame.getArguments();
+        return arguments.length >= MINIMAL_ARRAY_LENGTH && (arguments[INDEX_ENVIRONMENT] instanceof REnvironment || arguments[INDEX_FUNCTION] instanceof RFunction);
     }
 }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RChannel.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RChannel.java
index dad360bda10a469e49011b1084c2a3db844b0556..ffc0d94ddb73f7267e04a4b1476cbcb3b8a6d58f 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RChannel.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RChannel.java
@@ -25,8 +25,10 @@ package com.oracle.truffle.r.runtime;
 import java.io.*;
 import java.util.concurrent.*;
 
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.r.runtime.conn.*;
 import com.oracle.truffle.r.runtime.data.*;
-import com.oracle.truffle.r.runtime.data.model.*;
+import com.oracle.truffle.r.runtime.env.*;
 
 /**
  * Implementation of a channel abstraction used for communication between parallel contexts in
@@ -138,19 +140,74 @@ public class RChannel {
         }
     }
 
-    public static void send(int id, Object data) {
-        Object msg = data;
-        RChannel channel = getChannelFromId(id);
-        if ((msg instanceof RAbstractVector && !(msg instanceof RList)) || msg instanceof RDataFrame || msg instanceof RFactor) {
-            // make sure that what's passed through the channel will be copied on the first
-            // update
-            RShareable shareable = (RShareable) msg;
+    private static class SerializedList {
+
+        private RList list;
+
+        public SerializedList(RList list) {
+            this.list = list;
+        }
+
+        public RList getList() {
+            return list;
+        }
+    }
+
+    public static void makeShared(Object o) {
+        if (o instanceof RShareable) {
+            RShareable shareable = (RShareable) o;
             if (FastROptions.NewStateTransition) {
                 shareable.incRefCount();
                 shareable.incRefCount();
             } else {
                 shareable.makeShared();
             }
+        }
+    }
+
+    private static Object convertPrivate(Object o) throws IOException {
+        if (o instanceof RList) {
+            RList list = (RList) o;
+            return createShareable(list);
+        } else if (!(o instanceof RFunction || o instanceof REnvironment || o instanceof RConnection || o instanceof RLanguage)) {
+            // TODO: should we make internal values shareable?
+            return o;
+        } else {
+            return RSerialize.serialize(o, false, true, RSerialize.DEFAULT_VERSION, null);
+        }
+    }
+
+    @TruffleBoundary
+    private static Object createShareable(RList list) throws IOException {
+        RList newList = list;
+        for (int i = 0; i < list.getLength(); i++) {
+            Object el = list.getDataAt(i);
+            Object newEl = convertPrivate(el);
+            if (el != newEl) {
+                // conversion happened update element
+                if (list == newList) {
+                    // create a shallow copy
+                    newList = (RList) list.copy();
+                }
+                newList.updateDataAt(i, newEl, null);
+            }
+        }
+        return list == newList ? list : new SerializedList(newList);
+    }
+
+    public static void send(int id, Object data) {
+        Object msg = data;
+        RChannel channel = getChannelFromId(id);
+        if (msg instanceof RList) {
+            try {
+                msg = createShareable((RList) msg);
+            } catch (IOException x) {
+                throw RError.error(RError.NO_NODE, RError.Message.GENERIC, "error creating shareable list");
+            }
+        } else if (!(msg instanceof RFunction || msg instanceof REnvironment || msg instanceof RConnection || msg instanceof RLanguage)) {
+            // make sure that what's passed through the channel will be copied on the first
+            // update
+            makeShared(msg);
         } else {
             msg = RSerialize.serialize(msg, false, true, RSerialize.DEFAULT_VERSION, null);
         }
@@ -161,11 +218,30 @@ public class RChannel {
         }
     }
 
+    @TruffleBoundary
+    private static void unserializeList(RList list) throws IOException {
+        for (int i = 0; i < list.getLength(); i++) {
+            Object el = list.getDataAt(i);
+            if (el instanceof SerializedList) {
+                RList elList = ((SerializedList) el).getList();
+                unserializeList(elList);
+                list.updateDataAtAsObject(i, elList, null);
+            } else if (el instanceof byte[]) {
+                list.updateDataAt(i, RSerialize.unserialize((byte[]) el, null, null), null);
+            }
+        }
+    }
+
     public static Object receive(int id) {
         RChannel channel = getChannelFromId(id);
         try {
             Object msg = (id < 0 ? channel.masterToClient : channel.clientToMaster).take();
-            if (msg instanceof byte[]) {
+            if (msg instanceof SerializedList) {
+                RList list = ((SerializedList) msg).getList();
+                // list is already private (a shallow copy - do the appropriate changes in place)
+                unserializeList(list);
+                return list;
+            } else if (msg instanceof byte[]) {
                 return RSerialize.unserialize((byte[]) msg, null, null);
             } else {
                 return msg;
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RCmdOptions.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RCmdOptions.java
index 4adade483410a4bb69e79bcb20956d4d916afc4e..2f851da27a6ad5b4328c42dbc487924b2a5da2f8 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RCmdOptions.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RCmdOptions.java
@@ -22,14 +22,16 @@
  */
 package com.oracle.truffle.r.runtime;
 
+import static com.oracle.truffle.r.runtime.RCmdOptions.RCmdOption.*;
+
 import java.util.*;
 
 /**
  * (Abstract) definition of the standard R command line options. The setting of the values from the
  * environment is handled in some other class.
  */
-public class RCmdOptions {
-    public enum Client {
+public final class RCmdOptions {
+    public static enum Client {
         R {
             @Override
             public String usage() {
@@ -56,177 +58,98 @@ public class RCmdOptions {
         public abstract String usage();
     }
 
-    private static List<Option<?>> optionList = new ArrayList<>();
-    public static final Option<Boolean> HELP = newBooleanOption(true, "h", "help", false, "Print short help message and exit");
-    public static final Option<Boolean> VERSION = newBooleanOption(true, "version", false, "Print version info and exit");
-    public static final Option<String> ENCODING = newStringOption(false, null, "encoding=ENC", null, "Specify encoding to be used for stdin");
-    public static final Option<Boolean> RHOME = newBooleanOption(true, null, "RHOME", false, "Print path to R home directory and exit");
-    public static final Option<Boolean> SAVE = newBooleanOption(false, null, "save", false, "Do save workspace at the end of the session");
-    public static final Option<Boolean> NO_SAVE = newBooleanOption(true, null, "no-save", false, "Don't save it");
-    public static final Option<Boolean> NO_ENVIRON = newBooleanOption(false, null, "no-environ", false, "Don't read the site and user environment files");
-    public static final Option<Boolean> NO_SITE_FILE = newBooleanOption(false, null, "no-site-file", false, "Don't read the site-wide Rprofile");
-    public static final Option<Boolean> NO_INIT_FILE = newBooleanOption(false, null, "no-init-file", false, "Don't read the user R profile");
-    public static final Option<Boolean> RESTORE = newBooleanOption(false, null, "restore", true, "Do restore previously saved objects at startup");
-    public static final Option<Boolean> NO_RESTORE_DATA = newBooleanOption(false, null, "no-restore-data", false, "Don't restore previously saved objects");
-    public static final Option<Boolean> NO_RESTORE_HISTORY = newBooleanOption(false, null, "no-restore-history", false, "Don't restore the R history file");
-    public static final Option<Boolean> NO_RESTORE = newBooleanOption(true, null, "no-restore", false, "Don't restore anything");
-    public static final Option<Boolean> VANILLA = newBooleanOption(true, null, "vanilla", false, "Combine --no-save, --no-restore, --no-site-file,\n"
-                    + "                          --no-init-file and --no-environ");
-    public static final Option<Boolean> NO_READLINE = newBooleanOption(false, null, "no-readline", false, "Don't use readline for command-line editing");
-    public static final Option<String> MAX_PPSIZE = newStringOption(false, null, "max-ppsize", null, "Set max size of protect stack to N");
-    public static final Option<Boolean> QUIET = newBooleanOption(true, "q", "quiet", false, "Don't print startup message");
-    public static final Option<Boolean> SILENT = newBooleanOption(true, "silent", false, "Same as --quiet");
-    public static final Option<Boolean> SLAVE = newBooleanOption(true, "slave", false, "Make R run as quietly as possible");
-    public static final Option<Boolean> INTERACTIVE = newBooleanOption(false, "interactive", false, "Force an interactive session");
-    public static final Option<Boolean> VERBOSE = newBooleanOption(false, "verbose", false, "Print more information about progress");
-    public static final Option<String> DEBUGGER = newStringOption(true, "d", "debugger=NAME", null, "Run R through debugger NAME");
-    public static final Option<String> DEBUGGER_ARGS = newStringOption(false, null, "debugger-args=ARGS", null, "Pass ARGS as arguments to the debugger");
-    public static final Option<String> GUI = newStringOption(false, "g TYPE", "gui=TYPE", null, "Use TYPE as GUI; possible values are 'X11' (default)\n" + "                          and 'Tk'.");
-    public static final Option<String> ARCH = newStringOption(false, null, "arch=NAME", null, "Specify a sub-architecture");
-    public static final Option<Boolean> ARGS = newBooleanOption(true, "args", false, "Skip the rest of the command line");
-    public static final Option<String> FILE = newStringOption(true, "f FILE", "file=FILE", null, "Take input from 'FILE'");
-    public static final Option<List<String>> EXPR = newStringListOption(true, "e EXPR", null, null, "Execute 'EXPR' and exit");
-    public static final Option<String> DEFAULT_PACKAGES = newStringOption(Client.RSCRIPT, false, null, "default-packages=list", null, "Where 'list' is a comma-separated set\n"
-                    + "                          of package names, or 'NULL'");
-
-    public static Option<Boolean> newBooleanOption(boolean implemented, String name, boolean defaultValue, String help) {
-        return newBooleanOption(implemented, null, name, defaultValue, help);
-    }
-
-    public static Option<Boolean> newBooleanOption(boolean implemented, String shortName, String name, boolean defaultValue, String help) {
-        Option<Boolean> option = new Option<>(implemented, OptionType.BOOLEAN, shortName, name, help, defaultValue);
-        optionList.add(option);
-        return option;
-    }
-
-    public static Option<String> newStringOption(boolean implemented, String shortName, String name, String defaultValue, String help) {
-        return newStringOption(Client.EITHER, implemented, shortName, name, defaultValue, help);
-    }
-
-    public static Option<String> newStringOption(Client client, boolean implemented, String shortName, String name, String defaultValue, String help) {
-        Option<String> option = new Option<>(client, implemented, OptionType.STRING, shortName, name, help, defaultValue);
-        optionList.add(option);
-        return option;
-    }
-
-    public static Option<List<String>> newStringListOption(boolean implemented, String shortName, String name, List<String> defaultValue, String help) {
-        Option<List<String>> option = new Option<>(Client.EITHER, implemented, OptionType.REPEATED_STRING, shortName, name, help, defaultValue);
-        optionList.add(option);
-        return option;
-    }
-
-    public static List<Option<?>> optionList() {
-        return optionList;
-    }
-
-    public static void reset() {
-        for (Option<?> option : optionList) {
-            option.reset();
-        }
-    }
-
-    public static enum OptionType {
+    public static enum RCmdOptionType {
         BOOLEAN,
         STRING,
         REPEATED_STRING
     }
 
-    public static class Option<T> {
-        public final OptionType type;
-        /**
-         * The plain option name as passed to the constructor.
-         */
-        public final String plainName;
-        /**
-         * The option name prefixed by {@code --} or {@code null} if no {@code --} form.
-         */
-        private final String name;
-        /**
-         * The short option name prefixed by {@code -} or {@code null} if no {@code -} form.
-         */
+    public static enum RCmdOption {
+        HELP(RCmdOptionType.BOOLEAN, true, "h", "help", false, "Print short help message and exit"),
+        VERSION(RCmdOptionType.BOOLEAN, true, "version", false, "Print version info and exit"),
+        ENCODING(RCmdOptionType.STRING, false, "encoding=ENC", null, "Specify encoding to be used for stdin"),
+        RHOME(RCmdOptionType.BOOLEAN, true, "RHOME", false, "Print path to R home directory and exit"),
+        SAVE(RCmdOptionType.BOOLEAN, false, "save", false, "Do save workspace at the end of the session"),
+        NO_SAVE(RCmdOptionType.BOOLEAN, true, "no-save", false, "Don't save it"),
+        NO_ENVIRON(RCmdOptionType.BOOLEAN, false, "no-environ", false, "Don't read the site and user environment files"),
+        NO_SITE_FILE(RCmdOptionType.BOOLEAN, false, "no-site-file", false, "Don't read the site-wide Rprofile"),
+        NO_INIT_FILE(RCmdOptionType.BOOLEAN, false, "no-init-file", false, "Don't read the user R profile"),
+        RESTORE(RCmdOptionType.BOOLEAN, false, "restore", true, "Do restore previously saved objects at startup"),
+        NO_RESTORE_DATA(RCmdOptionType.BOOLEAN, false, "no-restore-data", false, "Don't restore previously saved objects"),
+        NO_RESTORE_HISTORY(RCmdOptionType.BOOLEAN, false, "no-restore-history", false, "Don't restore the R history file"),
+        NO_RESTORE(RCmdOptionType.BOOLEAN, true, "no-restore", false, "Don't restore anything"),
+        VANILLA(RCmdOptionType.BOOLEAN, true, "vanilla", false, "Combine --no-save, --no-restore, --no-site-file,\n--no-init-file and --no-environ"),
+        NO_READLINE(RCmdOptionType.BOOLEAN, false, "no-readline", false, "Don't use readline for command-line editing"),
+        MAX_PPSIZE(RCmdOptionType.STRING, false, "max-ppsize", null, "Set max size of protect stack to N"),
+        QUIET(RCmdOptionType.BOOLEAN, true, "q", "quiet", false, "Don't print startup message"),
+        SILENT(RCmdOptionType.BOOLEAN, true, "silent", false, "Same as --quiet"),
+        SLAVE(RCmdOptionType.BOOLEAN, true, "slave", false, "Make R run as quietly as possible"),
+        INTERACTIVE(RCmdOptionType.BOOLEAN, false, "interactive", false, "Force an interactive session"),
+        VERBOSE(RCmdOptionType.BOOLEAN, false, "verbose", false, "Print more information about progress"),
+        DEBUGGER(RCmdOptionType.STRING, true, "d", "debugger=NAME", null, "Run R through debugger NAME"),
+        DEBUGGER_ARGS(RCmdOptionType.STRING, false, "debugger-args=ARGS", null, "Pass ARGS as arguments to the debugger"),
+        GUI(RCmdOptionType.STRING, false, "g TYPE", "gui=TYPE", null, "Use TYPE as GUI; possible values are 'X11' (default)\nand 'Tk'."),
+        ARCH(RCmdOptionType.STRING, false, "arch=NAME", null, "Specify a sub-architecture"),
+        ARGS(RCmdOptionType.BOOLEAN, true, "args", false, "Skip the rest of the command line"),
+        FILE(RCmdOptionType.STRING, true, "f FILE", "file=FILE", null, "Take input from 'FILE'"),
+        EXPR(RCmdOptionType.REPEATED_STRING, true, "e EXPR", null, null, "Execute 'EXPR' and exit"),
+        DEFAULT_PACKAGES(RCmdOptionType.STRING, Client.RSCRIPT, false, null, "default-packages=list", null, "Where 'list' is a comma-separated set\nof package names, or 'NULL'");
+
+        private final RCmdOptionType type;
+        @SuppressWarnings("unused") private final Client client;
+        // Whether this option is actually implemented in FastR
+        private final boolean implemented;
+        // The short option name prefixed by {@code -} or {@code null} if no {@code -} form.
         private final String shortName;
-        /**
-         * The '=' separated suffix, e.g. {@code --file=FILE}.
-         */
-        private String suffix;
-        /**
-         * The space separated suffix, e.g. {@code -g TYPE}.
-         */
-        private String shortSuffix;
-        /**
-         * The help text.
-         */
-        public final String help;
-
-        /** The default value. */
-        private final T defaultValue;
-        /**
-         * The value, either from the default or set on the command line.
-         */
-
-        private T value;
-        /**
-         * Set {@code true} iff the short form of a {@code OptionType.STRING} option matched.
-         */
-        private boolean matchedShort;
-        /**
-         * Temporary field indicating not implemented.
-         */
-        public final boolean implemented;
-
-        /**
-         * Option specificity.
-         */
-        public final Client client;
-
-        Option(Client client, boolean implemented, OptionType type, String shortName, String name, String help, T defaultValue) {
+        // The option name prefixed by {@code --} or {@code null} if no {@code --} form.
+        private final String name;
+        // The plain option name as passed to the constructor.
+        public final String plainName;
+        // The '=' separated suffix, e.g. {@code --file=FILE}.
+        private final String suffix;
+        // The space separated suffix, e.g. {@code -g TYPE}.
+        private final String shortSuffix;
+        private final Object defaultValue;
+        private final String help;
+
+        private RCmdOption(RCmdOptionType type, Client client, boolean implemented, String shortName, String name, Object defaultValue, String help) {
+            this.type = type;
             this.client = client;
             this.implemented = implemented;
-            this.type = type;
-            this.shortName = shortName == null ? null : shortKey(shortName);
+            if (shortName == null) {
+                this.shortName = null;
+                this.shortSuffix = null;
+            } else {
+                int spx = shortName.indexOf(' ');
+                this.shortName = "-" + (spx > 0 ? shortName.substring(0, spx) : shortName);
+                this.shortSuffix = spx > 0 ? shortName.substring(spx) : null;
+            }
             this.plainName = name;
-            this.name = name == null ? null : key(name);
-            this.help = help;
+            if (name == null) {
+                this.name = null;
+                this.suffix = null;
+            } else {
+                int eqx = noPrefix(name) ? -1 : name.indexOf('=');
+                this.name = "--" + (eqx > 0 ? name.substring(0, eqx) : name);
+                this.suffix = eqx > 0 ? name.substring(eqx) : null;
+            }
             this.defaultValue = defaultValue;
-            this.value = defaultValue;
-        }
-
-        Option(boolean implemented, OptionType type, String shortName, String name, String help, T defaultValue) {
-            this(Client.EITHER, implemented, type, shortName, name, help, defaultValue);
-        }
-
-        private static boolean noPrefix(String arg) {
-            return arg.equals("RHOME") || arg.equals("CMD");
+            this.help = help.replace("\n", "\n                          ");
         }
 
-        private String key(String keyName) {
-            if (noPrefix(keyName)) {
-                return keyName;
-            }
-            String xName = keyName;
-            int eqx = keyName.indexOf('=');
-            if (eqx > 0) {
-                xName = keyName.substring(0, eqx);
-                suffix = keyName.substring(eqx);
-            }
-            return "--" + xName;
+        private RCmdOption(RCmdOptionType type, boolean implemented, String shortName, String name, Object defaultValue, String help) {
+            this(type, Client.EITHER, implemented, shortName, name, defaultValue, help);
         }
 
-        private String shortKey(String keyName) {
-            String xName = keyName;
-            int spx = keyName.indexOf(' ');
-            if (spx > 0) {
-                xName = keyName.substring(0, spx);
-                shortSuffix = keyName.substring(spx);
-            }
-            return "-" + xName;
+        private RCmdOption(RCmdOptionType type, boolean implemented, String name, Object defaultValue, String help) {
+            this(type, Client.EITHER, implemented, null, name, defaultValue, help);
         }
 
-        public T getValue() {
-            return value;
+        private static boolean noPrefix(String arg) {
+            return arg.equals("RHOME") || arg.equals("CMD");
         }
 
-        boolean matches(String arg) {
+        private boolean matches(String arg) {
             if (shortName != null && arg.equals(shortName)) {
                 return true;
             }
@@ -236,28 +159,7 @@ public class RCmdOptions {
             return false;
         }
 
-        @SuppressWarnings("unchecked")
-        public void setValue(boolean value) {
-            this.value = (T) new Boolean(value);
-        }
-
-        public void reset() {
-            this.value = defaultValue;
-        }
-
-        @SuppressWarnings("unchecked")
-        public void setValue(String value) {
-            if (type == OptionType.REPEATED_STRING) {
-                if (this.value == null) {
-                    this.value = (T) new ArrayList<String>();
-                }
-                ((List<String>) this.value).add(value);
-            } else {
-                this.value = (T) value;
-            }
-        }
-
-        public String getHelpName() {
+        private String getHelpName() {
             String result = "";
             if (shortName != null) {
                 result = shortName;
@@ -276,30 +178,207 @@ public class RCmdOptions {
             }
             return result;
         }
+    }
 
-        public boolean matchedShort() {
-            return matchedShort;
+    private final EnumMap<RCmdOption, Object> optionValues;
+    /**
+     * The original {@code args} array, with element zero set to "FastR".
+     */
+    private String[] arguments;
+    /**
+     * Index in {@code args} of the first non-option argument or {@code args.length} if none.
+     */
+    private final int firstNonOptionArgIndex;
+
+    private RCmdOptions(EnumMap<RCmdOption, Object> optionValues, String[] args, int firstNonOptionArgIndex) {
+        this.optionValues = optionValues;
+        this.arguments = args;
+        this.firstNonOptionArgIndex = firstNonOptionArgIndex;
+    }
+
+    private static void setValue(EnumMap<RCmdOption, Object> optionValues, RCmdOption option, boolean value) {
+        assert option.type == RCmdOptionType.BOOLEAN;
+        optionValues.put(option, value);
+    }
+
+    private static void setValue(EnumMap<RCmdOption, Object> optionValues, RCmdOption option, String value) {
+        if (option.type == RCmdOptionType.REPEATED_STRING) {
+            @SuppressWarnings("unchecked")
+            ArrayList<String> list = (ArrayList<String>) optionValues.get(option);
+            if (list == null) {
+                optionValues.put(option, list = new ArrayList<>());
+            }
+            list.add(value);
+        } else {
+            assert option.type == RCmdOptionType.STRING;
+            optionValues.put(option, value);
         }
     }
 
-    public static Option<?> matchOption(String arg) {
-        for (Option<?> option : optionList) {
-            if (option.type == OptionType.BOOLEAN) {
+    public void setValue(RCmdOption option, boolean value) {
+        setValue(optionValues, option, value);
+    }
+
+    public void setValue(RCmdOption option, String value) {
+        setValue(optionValues, option, value);
+    }
+
+    public boolean getBoolean(RCmdOption option) {
+        assert option.type == RCmdOptionType.BOOLEAN;
+        Object value = optionValues.get(option);
+        return value == null ? (Boolean) option.defaultValue : (Boolean) value;
+    }
+
+    public String getString(RCmdOption option) {
+        assert option.type == RCmdOptionType.STRING;
+        Object value = optionValues.get(option);
+        return value == null ? (String) option.defaultValue : (String) value;
+    }
+
+    @SuppressWarnings("unchecked")
+    public List<String> getStringList(RCmdOption option) {
+        assert option.type == RCmdOptionType.REPEATED_STRING;
+        Object value = optionValues.get(option);
+        return value == null ? (List<String>) option.defaultValue : (List<String>) value;
+    }
+
+    private static final class MatchResult {
+        private final RCmdOption option;
+        private final boolean matchedShort;
+
+        public MatchResult(RCmdOption option, boolean matchedShort) {
+            this.option = option;
+            this.matchedShort = matchedShort;
+        }
+    }
+
+    private static MatchResult matchOption(String arg) {
+        for (RCmdOption option : RCmdOption.values()) {
+            if (option.type == RCmdOptionType.BOOLEAN) {
                 // these must match exactly
                 if (option.matches(arg)) {
-                    return option;
+                    return new MatchResult(option, false);
                 }
-            } else if (option.type == OptionType.STRING || option.type == OptionType.REPEATED_STRING) {
+            } else if (option.type == RCmdOptionType.STRING || option.type == RCmdOptionType.REPEATED_STRING) {
                 // short forms must match exactly (and consume next argument)
                 if (option.shortName != null && option.shortName.equals(arg)) {
-                    option.matchedShort = true;
-                    return option;
+                    return new MatchResult(option, true);
                 } else if (arg.indexOf('=') > 0 && option.name != null && arg.startsWith(option.name)) {
-                    return option;
+                    return new MatchResult(option, false);
                 }
             }
         }
         return null;
     }
 
+    /**
+     * Parse the arguments from the standard R/Rscript command line syntax, setting the
+     * corresponding values.
+     *
+     * R supports {@code --arg=value} or {@code -arg value} for string-valued options.
+     *
+     * The spec for {@code commandArgs()} states that it returns the executable by which R was
+     * invoked in element 0, which is consistent with the C {@code main} function, but defines the
+     * exact form to be platform independent. Java does not provide the executable (for obvious
+     * reasons) so we use "FastR".
+     */
+    public static RCmdOptions parseArguments(Client client, String[] args) {
+        EnumMap<RCmdOption, Object> options = new EnumMap<>(RCmdOption.class);
+        int i = 0;
+        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) {
+                    firstNonOptionArgIndex = i;
+                    break;
+                }
+                // GnuR does not abort, simply issues a warning
+                System.out.printf("WARNING: unknown option '%s'%n", arg);
+                i++;
+                continue;
+            } else {
+                RCmdOption option = result.option;
+                if (result.matchedShort && i == args.length - 1) {
+                    System.out.println("usage:");
+                    printHelp(client, 1);
+                }
+                // check implemented
+                if (!option.implemented) {
+                    System.out.println("WARNING: option: " + arg + " is not implemented");
+                }
+                if (result.matchedShort) {
+                    i++;
+                    setValue(options, option, args[i]);
+                } else {
+                    if (option.type == RCmdOptionType.BOOLEAN) {
+                        setValue(options, option, true);
+                    } else if (option.type == RCmdOptionType.STRING) {
+                        int eqx = arg.indexOf('=');
+                        setValue(options, option, arg.substring(eqx + 1));
+                    }
+                }
+                i++;
+                // check for --args, in which case stop parsing
+                if (option == RCmdOption.ARGS) {
+                    firstNonOptionArgIndex = i;
+                    break;
+                }
+            }
+        }
+        String[] xargs = new String[args.length + 1];
+        xargs[0] = "FastR";
+        System.arraycopy(args, 0, xargs, 1, args.length);
+
+        // adjust for inserted executable name
+        return new RCmdOptions(options, xargs, firstNonOptionArgIndex + 1);
+    }
+
+    public String[] getArguments() {
+        return arguments;
+    }
+
+    public int getFirstNonOptionArgIndex() {
+        return firstNonOptionArgIndex;
+    }
+
+    public void setArguments(String[] arguments) {
+        this.arguments = arguments;
+    }
+
+    public void printHelpAndVersion() {
+        if (getBoolean(HELP)) {
+            RCmdOptions.printHelp(RCmdOptions.Client.R, 0);
+        } else if (getBoolean(VERSION)) {
+            printVersionAndExit();
+        } else if (getBoolean(RHOME)) {
+            printRHomeAndExit();
+        }
+    }
+
+    public static void printHelp(Client client, int exitCode) {
+        System.out.println(client.usage());
+        System.out.println("Options:");
+        for (RCmdOption option : RCmdOption.values()) {
+            System.out.printf("  %-22s  %s%n", option.getHelpName(), option.help);
+        }
+        System.out.println("\nFILE may contain spaces but not shell metacharacters.\n");
+        if (exitCode >= 0) {
+            System.exit(exitCode);
+        }
+    }
+
+    private static void printVersionAndExit() {
+        System.out.print("FastR version ");
+        System.out.println(RVersionNumber.FULL);
+        System.out.println(RRuntime.LICENSE);
+        Utils.exit(0);
+    }
+
+    private static void printRHomeAndExit() {
+        System.out.println(REnvVars.rHome());
+        throw Utils.exit(0);
+    }
 }
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
deleted file mode 100644
index 0f5618d8337bbeef1b63bb0c684ee4c5f88a9ef3..0000000000000000000000000000000000000000
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RContext.java
+++ /dev/null
@@ -1,944 +0,0 @@
-/*
- * Copyright (c) 2013, 2015, 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 java.util.*;
-import java.util.concurrent.*;
-import java.util.concurrent.atomic.*;
-
-import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
-import com.oracle.truffle.api.*;
-import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
-import com.oracle.truffle.api.frame.*;
-import com.oracle.truffle.api.nodes.*;
-import com.oracle.truffle.api.source.*;
-import com.oracle.truffle.api.vm.*;
-import com.oracle.truffle.r.runtime.conn.*;
-import com.oracle.truffle.r.runtime.data.*;
-import com.oracle.truffle.r.runtime.env.*;
-import com.oracle.truffle.r.runtime.env.REnvironment.*;
-import com.oracle.truffle.r.runtime.ffi.*;
-import com.oracle.truffle.r.runtime.rng.*;
-
-/**
- * Encapsulates the runtime state ("context") of an R session. All access to that state from the
- * implementation <b>must</b> go through this class. There can be multiple instances
- * (multiple-tenancy) active within a single process/Java-VM.
- *
- * The context provides two sub-interfaces {@link ConsoleHandler} and {@link Engine}that are
- * (typically) implemented elsewhere, and accessed through {@link #getConsoleHandler()} and
- * {@link #getEngine()}, respectively.
- *
- * Context-specific state for implementation classes is managed by this class (or the associated
- * engine) and accessed through the {@code getXXXState} methods.
- *
- * The life-cycle of a {@link RContext} is:
- * <ol>
- * <li>created: {@link #createShareNothing(RContext, String[], ConsoleHandler)} or
- * {@link #createShareParentReadOnly(RContext, String[], ConsoleHandler)}</li>
- * <li>activated: {@link #activate()}</li>
- * <li>destroyed: {@link #destroy()}</li>
- * </ol>
- *
- * Evaluations are only possible on an active context.
- *
- */
-public final class RContext extends ExecutionContext {
-
-    public static final int CONSOLE_WIDTH = 80;
-
-    /**
-     * The interface to a source of input/output for the context, which may have different
-     * implementations for different contexts. Since I/O is involved, all methods are tagged with
-     * {@link TruffleBoundary} as a hint that so should the associated implementation methods.
-     */
-    public interface ConsoleHandler {
-        /**
-         * Normal output with a new line.
-         */
-        @TruffleBoundary
-        void println(String s);
-
-        /**
-         * Normal output without a newline.
-         */
-        @TruffleBoundary
-        void print(String s);
-
-        /**
-         * Formatted output.
-         */
-        @TruffleBoundary
-        default void printf(String format, Object... args) {
-            print(String.format(format, args));
-        }
-
-        /**
-         * Error output with a newline.
-         *
-         * @param s
-         */
-        @TruffleBoundary
-        void printErrorln(String s);
-
-        /**
-         * Error output without a newline.
-         */
-        @TruffleBoundary
-        void printError(String s);
-
-        /**
-         * Read a line of input, newline included in result. Returns null if
-         * {@link #isInteractive() == false}. The rationale for including the readline is to ensure
-         * that the accumulated input, whether it be from a file or the console accurately reflects
-         * the the source. TODO worry about "\r\n"?
-         */
-        @TruffleBoundary
-        String readLine();
-
-        /**
-         * Return {@code true} if and only if this console is interactive.
-         */
-        @TruffleBoundary
-        boolean isInteractive();
-
-        /**
-         * Redirect error output to the normal output.
-         */
-        @TruffleBoundary
-        void redirectError();
-
-        /**
-         * Get the current prompt.
-         */
-        @TruffleBoundary
-        String getPrompt();
-
-        /**
-         * Set the R prompt.
-         */
-        @TruffleBoundary
-        void setPrompt(String prompt);
-
-        /**
-         * Get the console width.
-         */
-        @TruffleBoundary
-        int getWidth();
-    }
-
-    public enum Kind {
-        /**
-         * Essentially a clean restart, modulo the basic VM-wide initialization. which does include,
-         * for example, reading the external environment variables. I.e., it is not a goal to create
-         * a multi-user environment with different external inputs. This kind of context can be used
-         * in a parallel computation. The initial context is always of this kind. The intent is that
-         * all mutable state is localized to the context, which may not be completely achievable.
-         * For example, shared native libraries are assumed to contain no mutable state and be
-         * re-entrant.
-         */
-        SHARE_NOTHING,
-
-        /**
-         * Shares the set of loaded packages of the parent context at the time the context is
-         * created. Only useful when there is a priori knowledge on the evaluation that the context
-         * will be used for. Cannot safely be used for parallel context evaluation. Must be created
-         * as a child of an existing parent context of type {@link #SHARE_NOTHING} or
-         * {@link #SHARE_PARENT_RO} and only one such child is allowed. (Strictly speaking the
-         * invariant should be only one active child, but the implementation enforces it at creation
-         * time). Evidently any changes made to the shared environment, e.g., loading a package,
-         * affect the parent.
-         */
-        SHARE_PARENT_RW,
-
-        /**
-         * Intermediate between {@link #SHARE_NOTHING} and {@link #SHARE_PARENT_RW}, this is similar
-         * to the standard shared code/copied data model provided by operating systems, although the
-         * code/data distinction isn't completely applicable to a language like R. Unlike
-         * {@link #SHARE_NOTHING}, where the ASTs for the functions in the default packages are
-         * distinct copies in each context, in this kind of context, they are shared. Strictly
-         * speaking, the bindings of R functions are shared, and this is achieved by creating a
-         * shallow copy of the environments associated with the default packages of the parent
-         * context at the time the context is created.
-         */
-        SHARE_PARENT_RO,
-    }
-
-    /**
-     * Denotes a class that has context-specific state, e.g. {@link REnvironment}. Such a class must
-     * implement the {@link #newContext} method.
-     */
-    public interface StateFactory {
-        /**
-         * Create the class-specific state for a new context.
-         *
-         * @param context the context
-         * @param objects additional arguments for custom initialization, typically empty
-         */
-        ContextState newContext(RContext context, Object... objects);
-
-        /**
-         * A state factory may want to snapshot the state of the context just after the basic system
-         * is initialized, in which case they can override this method. N.B. This is only invoked
-         * for {@link Kind#SHARE_NOTHING} contexts. The definition of "system initialized" is that
-         * the default packages have been loaded, profiles evaluated and {@code .First, First.Sys}
-         * executed.
-         */
-        @SuppressWarnings("unused")
-        default void systemInitialized(RContext context, ContextState state) {
-
-        }
-
-        /**
-         * Called in response to the {@link RContext#destroy} method. Provides a hook for finalizing
-         * any state before the context is destroyed.
-         */
-        @SuppressWarnings("unused")
-        default void beforeDestroy(RContext context, ContextState state) {
-
-        }
-    }
-
-    /**
-     * Tagging interface denoting a class that carries the context-specific state for a class that
-     * implements {@link StateFactory}. The class specific state must implement this interface.
-     */
-    public interface ContextState {
-
-    }
-
-    public interface Engine {
-        /**
-         * Make the engine ready for evaluations.
-         */
-        void activate();
-
-        /**
-         * Return the {@link TruffleVM} instance associated with this engine.
-         */
-        TruffleVM getTruffleVM();
-
-        /**
-         * Return the {@link com.oracle.truffle.api.vm.TruffleVM.Builder} instance associated with
-         * this engine. This is only used by the command line debugger.
-         */
-        TruffleVM.Builder getTruffleVMBuilder();
-
-        /**
-         * Elapsed time of runtime.
-         *
-         * @return elapsed time in nanosecs.
-         */
-        long elapsedTimeInNanos();
-
-        /**
-         * Return user and system times for any spawned child processes in nanosecs, < 0 means not
-         * available (Windows).
-         */
-        long[] childTimesInNanos();
-
-        public static class ParseException extends Exception {
-            private static final long serialVersionUID = 1L;
-
-            public ParseException(Throwable cause, String msg) {
-                super(msg, cause);
-            }
-        }
-
-        /**
-         * Parse an R expression and return an {@link RExpression} object representing the Truffle
-         * ASTs for the components.
-         */
-        RExpression parse(Source source) throws ParseException;
-
-        /**
-         * A (perhaps temporary) interface to support {@link TruffleLanguage}.
-         */
-        CallTarget parseToCallTarget(Source source);
-
-        /**
-         * Parse and evaluate {@code rscript} in {@code frame}. {@code printResult == true}, the
-         * result of the evaluation is printed to the console.
-         *
-         * @param sourceDesc a {@link Source} object that describes the input to be parsed
-         * @param frame the frame in which to evaluate the input
-         * @param printResult {@code true} iff the result of the evaluation should be printed to the
-         *            console
-         * @param allowIncompleteSource {@code true} if partial input is acceptable
-         * @return the object returned by the evaluation or {@code null} if an error occurred.
-         */
-        Object parseAndEval(Source sourceDesc, MaterializedFrame frame, boolean printResult, boolean allowIncompleteSource);
-
-        /**
-         * Variant of {@link #parseAndEval(Source, MaterializedFrame, boolean, boolean)} for
-         * evaluation in the global frame.
-         */
-        Object parseAndEval(Source sourceDesc, boolean printResult, boolean allowIncompleteSource);
-
-        /**
-         * Variant of {@link #parseAndEval(Source, MaterializedFrame, boolean, boolean)} for test
-         * evaluation in the global frame.
-         */
-        Object parseAndEvalTest(Source sourceDesc, boolean printResult, boolean allowIncompleteSource);
-
-        Object INCOMPLETE_SOURCE = new Object();
-
-        /**
-         * Support for the {@code eval} {@code .Internal}.
-         */
-        Object eval(RExpression expr, REnvironment envir, REnvironment enclos, int depth) throws PutException;
-
-        /**
-         * Convenience method for common case.
-         */
-        default Object eval(RExpression expr, REnvironment envir, int depth) throws PutException {
-            return eval(expr, envir, null, depth);
-        }
-
-        /**
-         * Variant of {@link #eval(RExpression, REnvironment, REnvironment, int)} for a single
-         * language element.
-         */
-        Object eval(RLanguage expr, REnvironment envir, REnvironment enclos, int depth) throws PutException;
-
-        /**
-         * Convenience method for common case.
-         */
-        default Object eval(RLanguage expr, REnvironment envir, int depth) throws PutException {
-            return eval(expr, envir, null, depth);
-        }
-
-        /**
-         * Evaluate {@code expr} in {@code frame}.
-         */
-        Object eval(RExpression expr, MaterializedFrame frame);
-
-        /**
-         * Variant of {@link #eval(RExpression, MaterializedFrame)} for a single language element.
-         */
-        Object eval(RLanguage expr, MaterializedFrame frame);
-
-        /**
-         * Variant of {@link #eval(RLanguage, MaterializedFrame)} where we already have the
-         * {@link RFunction} and the evaluated arguments, but do not have a frame available, and we
-         * are behind a {@link TruffleBoundary}, so call inlining is not an issue. This is primarily
-         * used for R callbacks from {@link RErrorHandling} and {@link RSerialize}.
-         */
-        Object evalFunction(RFunction func, Object... args);
-
-        /**
-         * Evaluates an {@link com.oracle.truffle.r.runtime.data.RPromise.Closure} in {@code frame}.
-         */
-        Object evalPromise(RPromise.Closure closure, MaterializedFrame frame);
-
-        /**
-         * Checks for the existence of {@code .Last/.Last.sys} and if present and bound to a
-         * function, invokes the (parameterless) function.
-         */
-        void checkAndRunLast(String name);
-
-        /**
-         * Wraps the Truffle AST in {@code body} in an anonymous function and returns a
-         * {@link RootCallTarget} for it.
-         *
-         * N.B. For certain expressions, there might be some value in enclosing the wrapper function
-         * in a specific lexical scope. E.g., as a way to access names in the expression known to be
-         * defined in that scope.
-         *
-         * @param body The AST for the body of the wrapper, i.e., the expression being evaluated.
-         */
-        RootCallTarget makePromiseCallTarget(Object body, String funName);
-
-        /**
-         * Used by Truffle debugger; invokes the internal "print" support in R for {@code value}.
-         * Essentially this is equivalent to {@link #evalFunction} using the {@code "print"}
-         * function.
-         */
-        void printResult(Object value);
-
-        RFunction parseFunction(String name, Source source, MaterializedFrame enclosingFrame) throws ParseException;
-
-    }
-
-    /**
-     * The set of classes for which the context manages context-specific state, and their state. We
-     * could do this more dynamically with a registration process, perhaps driven by an annotation
-     * processor, but the set is relatively small, so we just enumerate them here.
-     */
-    public static enum ClassStateKind {
-        ROptions(ROptions.class, false),
-        REnvironment(REnvironment.ClassStateFactory.class, true),
-        RErrorHandling(RErrorHandling.class, false),
-        RConnection(ConnectionSupport.class, false),
-        StdConnections(StdConnections.class, true),
-        RNG(RRNG.class, false),
-        RFFI(RFFIContextStateFactory.class, false),
-        RSerialize(RSerialize.class, false),
-        REnvVars(REnvVars.class, false);
-
-        private final Class<? extends StateFactory> klass;
-        private StateFactory factory;
-        private final boolean customCreate;
-
-        private ClassStateKind(Class<? extends StateFactory> klass, boolean customCreate) {
-            this.klass = klass;
-            this.customCreate = customCreate;
-        }
-
-        private static final ClassStateKind[] VALUES = values();
-        // Avoid reflection at runtime (AOT VM).
-        static {
-            for (ClassStateKind css : VALUES) {
-                try {
-                    css.factory = css.klass.newInstance();
-                } catch (IllegalAccessException | InstantiationException ex) {
-                    throw Utils.fail("failed to instantiate ClassStateFactory: " + css.klass.getSimpleName());
-                }
-            }
-        }
-
-    }
-
-    /**
-     * A thread that is explicitly associated with a context for efficient lookup.
-     */
-    public static class ContextThread extends Thread {
-        protected RContext context;
-
-        public ContextThread(RContext context) {
-            this.context = context;
-        }
-
-        protected ContextThread() {
-
-        }
-
-        public void setContext(RContext context) {
-            this.context = context;
-        }
-
-    }
-
-    /**
-     * A thread for performing an evaluation (used by {@code fastr} package.
-     */
-    public static class EvalThread extends ContextThread {
-        private final Source source;
-
-        public EvalThread(RContext context, Source source) {
-            super(context);
-            this.source = source;
-            context.evalThread = this;
-        }
-
-        @Override
-        public void run() {
-            try {
-                context.activate();
-                context.engine.parseAndEval(source, true, false);
-            } finally {
-                context.destroy();
-            }
-        }
-
-    }
-
-    /**
-     * Builtin cache. Valid across all contexts.
-     */
-    private static final HashMap<Object, RFunction> cachedBuiltinFunctions = new HashMap<>();
-
-    private final Kind kind;
-
-    private final GlobalAssumptions globalAssumptions = new GlobalAssumptions();
-
-    /**
-     * Denote whether the result of an expression should be printed in the shell or not.
-     */
-    private boolean resultVisible = true;
-
-    /**
-     * A context-specific value that is checked in {@code HiddenInternalFunctions} to avoid an error
-     * report on a {@code SUBSTITUTE} builtin. Not worth promoting to a {@link ContextState}.
-     */
-    private boolean loadingBase;
-
-    /**
-     * Denote whether the FastR instance is running in 'interactive' mode. This can be set in a
-     * number of ways and is <b>not</> simply equivalent to taking input from a file. However, it is
-     * final once set.
-     */
-    @CompilationFinal private boolean interactive;
-
-    @CompilationFinal private ConsoleHandler consoleHandler;
-    @CompilationFinal private String[] commandArgs;
-    @CompilationFinal private Engine engine;
-
-    /**
-     * The array is indexed by {@link ClassStateKind#ordinal()}.
-     */
-    @CompilationFinal private ContextState[] contextState;
-
-    /**
-     * Any context created by another has a parent. When such a context is destroyed we must reset
-     * the {@link #threadLocalContext} to the parent.
-     */
-    private final RContext parent;
-
-    /**
-     * At most one shared child.
-     */
-    private RContext sharedChild;
-
-    /**
-     * Back pointer to the evalThread.
-     */
-    private EvalThread evalThread;
-
-    /**
-     * Typically there is a 1-1 relationship between an {@link RContext} and the thread that is
-     * performing the evaluation, so we can store the {@link RContext} in a {@link ThreadLocal}.
-     *
-     * When a context is first created no threads are attached, to allow contexts to be used as
-     * values in the experimental {@code fastr.createcontext} function. The {@link #engine} must
-     * call the {@link #activate} method once the context becomes active. Additional threads can be
-     * added by the {@link #attachThread} method.
-     */
-    @CompilationFinal private static final ThreadLocal<RContext> threadLocalContext = new ThreadLocal<>();
-
-    /**
-     * Used by the MethodListDispatch class.
-     */
-
-    private boolean methodTableDispatchOn = true;
-
-    /*
-     * Primarily for debugging.
-     */
-    private static final AtomicLong ID = new AtomicLong();
-    @CompilationFinal private long id;
-    private boolean active;
-
-    private static final Deque<RContext> allContexts = new ConcurrentLinkedDeque<>();
-
-    private static final Semaphore allContextsSemaphore = new Semaphore(1, true);
-
-    /**
-     * A (hopefully) temporary workaround to ignore the setting of {@link #resultVisible} for
-     * benchmarks. Set across all contexts.
-     */
-    @CompilationFinal private static boolean ignoreVisibility;
-
-    /*
-     * Workarounds to finesse project circularities between runtime/nodes.
-     */
-    @CompilationFinal private static RRuntimeASTAccess runtimeASTAccess;
-    @CompilationFinal private static RBuiltinLookup builtinLookup;
-
-    /**
-     * Initialize VM-wide static values.
-     */
-    public static void initialize(RRuntimeASTAccess rASTHelperArg, RBuiltinLookup rBuiltinLookupArg, boolean ignoreVisibilityArg) {
-        runtimeASTAccess = rASTHelperArg;
-        builtinLookup = rBuiltinLookupArg;
-        ignoreVisibility = ignoreVisibilityArg;
-    }
-
-    /**
-     * Associates this {@link RContext} with the current thread.
-     */
-    public void attachThread() {
-        Thread current = Thread.currentThread();
-        if (current instanceof ContextThread) {
-            ((ContextThread) current).setContext(this);
-        } else {
-            threadLocalContext.set(this);
-        }
-    }
-
-    /**
-     * Waits for the associated EvalThread to finish.
-     *
-     * @throws InterruptedException
-     */
-    public void joinThread() throws InterruptedException {
-        EvalThread t = this.evalThread;
-        if (t == null) {
-            throw RError.error(RError.NO_NODE, RError.Message.GENERIC, "no eval thread in a given context");
-        }
-        this.evalThread = null;
-        t.join();
-    }
-
-    private static final Assumption singleContextAssumption = Truffle.getRuntime().createAssumption("single RContext");
-    @CompilationFinal private static RContext singleContext;
-
-    private RContext(Kind kind, RContext parent, String[] commandArgs, ConsoleHandler consoleHandler) {
-        if (kind == Kind.SHARE_PARENT_RW) {
-            if (parent.sharedChild != null) {
-                throw RError.error(RError.NO_NODE, RError.Message.GENERIC, "can't have multiple active SHARED_PARENT_RW contexts");
-            }
-            parent.sharedChild = this;
-        }
-        this.kind = kind;
-        this.parent = parent;
-        this.id = ID.getAndIncrement();
-        this.commandArgs = commandArgs;
-        if (consoleHandler == null) {
-            throw Utils.fail("no console handler set");
-        }
-        this.consoleHandler = consoleHandler;
-        this.interactive = consoleHandler.isInteractive();
-        try {
-            allContextsSemaphore.acquire();
-            allContexts.add(this);
-            allContextsSemaphore.release();
-        } catch (InterruptedException x) {
-            throw RError.error(RError.NO_NODE, RError.Message.GENERIC, "error destroying context");
-        }
-
-        if (singleContextAssumption.isValid()) {
-            if (singleContext == null) {
-                singleContext = this;
-            } else {
-                singleContext = null;
-                singleContextAssumption.invalidate();
-            }
-        }
-    }
-
-    public void installCustomClassState(ClassStateKind classStateKind, ContextState state) {
-        assert classStateKind.customCreate;
-        assert contextState[classStateKind.ordinal()] == null;
-        contextState[classStateKind.ordinal()] = state;
-    }
-
-    /**
-     * Inform state factories that the system is initialized.
-     */
-    public void systemInitialized() {
-        for (ClassStateKind classStateKind : ClassStateKind.VALUES) {
-            classStateKind.factory.systemInitialized(this, contextState[classStateKind.ordinal()]);
-        }
-    }
-
-    /**
-     * Create a {@link Kind#SHARE_NOTHING} {@link RContext}.
-     *
-     * @param parent if non-null {@code null} the parent creating the context
-     * @param commandArgs the command line arguments passed this R session
-     * @param consoleHandler a {@link ConsoleHandler} for output
-     */
-    public static RContext createShareNothing(RContext parent, String[] commandArgs, ConsoleHandler consoleHandler) {
-        RContext result = create(parent, Kind.SHARE_NOTHING, commandArgs, consoleHandler);
-        return result;
-    }
-
-    /**
-     * Create a {@link Kind#SHARE_PARENT_RO} {@link RContext}.
-     *
-     * @param parent parent context with which to shgre
-     * @param commandArgs the command line arguments passed this R session
-     * @param consoleHandler a {@link ConsoleHandler} for output
-     */
-    public static RContext createShareParentReadOnly(RContext parent, String[] commandArgs, ConsoleHandler consoleHandler) {
-        RContext result = create(parent, Kind.SHARE_PARENT_RO, commandArgs, consoleHandler);
-        return result;
-
-    }
-
-    /**
-     * Create a {@link Kind#SHARE_PARENT_RW} {@link RContext}.
-     *
-     * @param parent parent context with which to shgre
-     * @param commandArgs the command line arguments passed this R session
-     * @param consoleHandler a {@link ConsoleHandler} for output
-     */
-    public static RContext createShareParentReadWrite(RContext parent, String[] commandArgs, ConsoleHandler consoleHandler) {
-        RContext result = create(parent, Kind.SHARE_PARENT_RW, commandArgs, consoleHandler);
-        return result;
-
-    }
-
-    /**
-     * Create a context of a given kind.
-     */
-    public static RContext create(RContext parent, Kind kind, String[] commandArgs, ConsoleHandler consoleHandler) {
-        RContext result = new RContext(kind, parent, commandArgs, consoleHandler);
-        result.engine = RContext.getRRuntimeASTAccess().createEngine(result);
-        return result;
-    }
-
-    /**
-     * Active the context by attaching the current thread and initializing the {@link StateFactory}
-     * objects. Note that we attach the thread before creating the new context state. This means
-     * that code that accesses the state through this interface will receive a {@code null} value.
-     * Access to the parent state is available through the {@link RContext} argument passed to the
-     * {@link StateFactory#newContext(RContext, Object...)} method. It might be better to attach the
-     * thread after state creation but it is a finely balanced decision and risks incorrectly
-     * accessing the parent state.
-     */
-    public RContext activate() {
-        assert !active;
-        active = true;
-        attachThread();
-        contextState = new ContextState[ClassStateKind.VALUES.length];
-        for (ClassStateKind classStateKind : ClassStateKind.VALUES) {
-            if (!classStateKind.customCreate) {
-                contextState[classStateKind.ordinal()] = classStateKind.factory.newContext(this);
-            }
-        }
-        installCustomClassState(ClassStateKind.StdConnections, new StdConnections().newContext(this, consoleHandler));
-        if (kind == Kind.SHARE_PARENT_RW) {
-            parent.sharedChild = this;
-        }
-        // The environment state installation is handled by the engine
-        engine.activate();
-        return this;
-    }
-
-    /**
-     * Destroy this context.
-     */
-    public void destroy() {
-        for (ClassStateKind classStateKind : ClassStateKind.VALUES) {
-            classStateKind.factory.beforeDestroy(this, contextState[classStateKind.ordinal()]);
-        }
-        if (kind == Kind.SHARE_PARENT_RW) {
-            parent.sharedChild = null;
-        }
-        engine = null;
-        try {
-            allContextsSemaphore.acquire();
-            allContexts.remove(this);
-            allContextsSemaphore.release();
-        } catch (InterruptedException x) {
-            throw RError.error(RError.NO_NODE, RError.Message.GENERIC, "error destroying context");
-        }
-        if (parent == null) {
-            threadLocalContext.set(null);
-        } else {
-            threadLocalContext.set(parent);
-        }
-    }
-
-    public RContext getParent() {
-        return parent;
-    }
-
-    public Kind getKind() {
-        return kind;
-    }
-
-    public long getId() {
-        return id;
-    }
-
-    public static RContext find(int id) {
-        try {
-            allContextsSemaphore.acquire();
-            for (RContext context : allContexts) {
-                if (context.id == id) {
-                    allContextsSemaphore.release();
-                    return context;
-                }
-            }
-            allContextsSemaphore.release();
-        } catch (InterruptedException x) {
-            throw RError.error(RError.NO_NODE, RError.Message.GENERIC, "error destroying context");
-        }
-        return null;
-    }
-
-    public GlobalAssumptions getAssumptions() {
-        return globalAssumptions;
-    }
-
-    @TruffleBoundary
-    private static RContext getInstanceInternal() {
-        RContext result = threadLocalContext.get();
-        assert result != null;
-        assert result.active;
-        return result;
-    }
-
-    public static RContext getInstance() {
-        RContext context = singleContext;
-        if (context != null) {
-            try {
-                singleContextAssumption.check();
-                return context;
-            } catch (InvalidAssumptionException e) {
-                // fallback to slow case
-            }
-        }
-
-        Thread current = Thread.currentThread();
-        if (current instanceof ContextThread) {
-            context = ((ContextThread) current).context;
-            assert context != null;
-            return context;
-        } else {
-            return getInstanceInternal();
-        }
-
-    }
-
-    /**
-     * Access to the engine, when an {@link RContext} object is available, and/or when {@code this}
-     * context is not active.
-     */
-    public Engine getThisEngine() {
-        return engine;
-    }
-
-    /**
-     * Access to the {@link ContextState}, when an {@link RContext} object is available, and/or when
-     * {@code this} context is not active.
-     */
-    public ContextState getThisContextState(ClassStateKind classStateKind) {
-        return contextState[classStateKind.ordinal()];
-    }
-
-    public boolean isVisible() {
-        return resultVisible;
-    }
-
-    public void setVisible(boolean v) {
-        resultVisible = v;
-    }
-
-    public boolean isMethodTableDispatchOn() {
-        return methodTableDispatchOn;
-    }
-
-    public void setMethodTableDispatchOn(boolean on) {
-        methodTableDispatchOn = on;
-    }
-
-    public boolean isInteractive() {
-        return interactive;
-    }
-
-    public static boolean isIgnoringVisibility() {
-        return ignoreVisibility;
-    }
-
-    public ConsoleHandler getConsoleHandler() {
-        return consoleHandler;
-    }
-
-    private TimeZone timeZone = TimeZone.getDefault();
-
-    public TimeZone getSystemTimeZone() {
-        return timeZone;
-    }
-
-    public void setSystemTimeZone(TimeZone timeZone) {
-        this.timeZone = timeZone;
-    }
-
-    /**
-     * This is a static property of the implementation and not context-specific.
-     */
-    public static RRuntimeASTAccess getRRuntimeASTAccess() {
-        return runtimeASTAccess;
-    }
-
-    /**
-     * Is {@code name} a builtin function (but not a {@link RBuiltinKind#INTERNAL}?
-     */
-    public static boolean isPrimitiveBuiltin(String name) {
-        return builtinLookup.isPrimitiveBuiltin(name);
-    }
-
-    /**
-     * Return the {@link RFunction} for the builtin {@code name}.
-     */
-    public static RFunction lookupBuiltin(String name) {
-        return builtinLookup.lookupBuiltin(name);
-    }
-
-    /**
-     * Returns the descriptor for the builtin with the given name. This does not cause an RFunction
-     * to be created.
-     */
-    public static RBuiltinDescriptor lookupBuiltinDescriptor(String name) {
-        return builtinLookup.lookupBuiltinDescriptor(name);
-    }
-
-    public static RFunction cacheBuiltin(Object key, RFunction function) {
-        cachedBuiltinFunctions.put(key, function);
-        return function;
-    }
-
-    public static RFunction getCachedBuiltin(Object key) {
-        return cachedBuiltinFunctions.get(key);
-    }
-
-    public String[] getCommandArgs() {
-        if (commandArgs == null) {
-            throw Utils.fail("no command args set");
-        }
-        return commandArgs;
-    }
-
-    @Override
-    public String toString() {
-        return "context: " + id;
-    }
-
-    /*
-     * static functions necessary in code where the context is only implicit in the thread(s)
-     * running an evaluation
-     */
-
-    public static Engine getEngine() {
-        return RContext.getInstance().engine;
-    }
-
-    public static ContextState getContextState(ClassStateKind kind) {
-        return getInstance().contextState[kind.ordinal()];
-    }
-
-    public static REnvironment.ContextState getREnvironmentState() {
-        return (REnvironment.ContextState) getInstance().contextState[ClassStateKind.REnvironment.ordinal()];
-    }
-
-    public static ROptions.ContextState getROptionsState() {
-        return (ROptions.ContextState) getInstance().contextState[ClassStateKind.ROptions.ordinal()];
-    }
-
-    public static ConnectionSupport.ContextState getRConnectionState() {
-        return (ConnectionSupport.ContextState) getInstance().contextState[ClassStateKind.RConnection.ordinal()];
-    }
-
-    public void setLoadingBase(boolean b) {
-        loadingBase = b;
-    }
-
-    public boolean getLoadingBase() {
-        return loadingBase;
-    }
-
-}
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RDeparse.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RDeparse.java
index e733356efb021a8f0b9f05229fe241e45c88a9de..3b0c10587df95c69e373b618bf9aea80c9bd95c5 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RDeparse.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RDeparse.java
@@ -17,6 +17,7 @@ import java.util.*;
 import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.source.*;
+import com.oracle.truffle.r.runtime.context.*;
 import com.oracle.truffle.r.runtime.data.*;
 import com.oracle.truffle.r.runtime.data.RAttributes.RAttribute;
 import com.oracle.truffle.r.runtime.gnur.*;
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/REnvVars.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/REnvVars.java
index 50360274dae4ffae497f5f130c4d88537dc087fa..639567f9d1f71994c2169496a674864023cec750 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/REnvVars.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/REnvVars.java
@@ -27,79 +27,38 @@ import java.nio.file.*;
 import java.nio.file.FileSystem;
 import java.util.*;
 
-import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.r.runtime.RCmdOptions.RCmdOption;
+import com.oracle.truffle.r.runtime.context.*;
 import com.oracle.truffle.r.runtime.ffi.*;
 
 /**
  * Repository for environment variables, including those set by FastR itself, e.g.
  * {@code R_LIBS_USER}.
- *
- * Environment variables are context specific and, certainly there is one case in package loading
- * where R uses an environment variable as a global variable to detect recursion.
- *
- * On startup, before we have any contexts created, we have to support access to the environment
- * variables inherited from the OS environment, e.g., for {@code R_PROFILE}. Additional variable are
- * set during the package loading and these are captured in {@link #systemInitEnvVars}. All
- * subsequent contexts inherit that set and may modify it further.
  */
-public class REnvVars implements RContext.StateFactory {
-
-    private static Map<String, String> initialEnvVars;
-    private static HashMap<String, String> systemInitEnvVars;
-    @CompilationFinal private static boolean initialized;
-
-    private static class ContextStateImpl implements RContext.ContextState {
-        private Map<String, String> envVars = new HashMap<>();
-
-    }
-
-    public RContext.ContextState newContext(RContext context, Object... objects) {
-        ContextStateImpl result = new ContextStateImpl();
-        result.envVars.putAll(systemInitEnvVars == null ? initialEnvVars : systemInitEnvVars);
-        if (!initialized) {
-            initialized = true;
-        }
-        return result;
-    }
-
-    @Override
-    public void systemInitialized(RContext context, RContext.ContextState state) {
-        ContextStateImpl optionsState = (ContextStateImpl) state;
-        systemInitEnvVars = new HashMap<>(optionsState.envVars.size());
-        systemInitEnvVars.putAll(optionsState.envVars);
-    }
-
-    private static ContextStateImpl getState() {
-        return (ContextStateImpl) RContext.getContextState(RContext.ClassStateKind.REnvVars);
-    }
+public final class REnvVars implements RContext.ContextState {
 
-    private static Map<String, String> getInitialEnvVars() {
-        if (initialEnvVars == null) {
-            initialEnvVars = new HashMap<>(System.getenv());
-        }
-        return initialEnvVars;
-    }
+    private final Map<String, String> envVars = new HashMap<>(System.getenv());
 
-    private static Map<String, String> getEnvVars() {
-        if (initialized) {
-            return getState().envVars;
-        } else {
-            return initialEnvVars;
-        }
-    }
-
-    public static void initialize() {
-        getInitialEnvVars();
+    private REnvVars(RContext context) {
         // set the standard vars defined by R
         String rHome = rHome();
-        initialEnvVars.put("R_HOME", rHome);
+
+        // Check any external setting is consistent
+        String envRHomePath = envVars.get("R_HOME");
+        if (envRHomePath != null) {
+            new File(envRHomePath).getAbsolutePath();
+            if (!envRHomePath.equals(rHomePath)) {
+                Utils.fail("R_HOME set to unexpected value in the environment");
+            }
+        }
+        envVars.put("R_HOME", rHome);
         // Always read the system file
         FileSystem fileSystem = FileSystems.getDefault();
         safeReadEnvironFile(fileSystem.getPath(rHome, "etc", "Renviron").toString());
-        getEnvVars().put("R_DOC_DIR", fileSystem.getPath(rHome, "doc").toString());
-        getEnvVars().put("R_INCLUDE_DIR", fileSystem.getPath(rHome, "include").toString());
-        getEnvVars().put("R_SHARE_DIR", fileSystem.getPath(rHome, "share").toString());
+        envVars.put("R_DOC_DIR", fileSystem.getPath(rHome, "doc").toString());
+        envVars.put("R_INCLUDE_DIR", fileSystem.getPath(rHome, "include").toString());
+        envVars.put("R_SHARE_DIR", fileSystem.getPath(rHome, "share").toString());
         String rLibsUserProperty = System.getenv("R_LIBS_USER");
         if (rLibsUserProperty == null) {
             String os = System.getProperty("os.name");
@@ -108,19 +67,19 @@ public class REnvVars implements RContext.StateFactory {
             } else {
                 rLibsUserProperty = "~/R/%p-library/%v";
             }
-            getEnvVars().put("R_LIBS_USER", rLibsUserProperty);
+            envVars.put("R_LIBS_USER", rLibsUserProperty);
             // This gets expanded by R code in the system profile
         }
 
-        if (!RCmdOptions.NO_ENVIRON.getValue()) {
-            String siteFile = getEnvVars().get("R_ENVIRON");
+        if (!context.getOptions().getBoolean(RCmdOption.NO_ENVIRON)) {
+            String siteFile = envVars.get("R_ENVIRON");
             if (siteFile == null) {
                 siteFile = fileSystem.getPath(rHome, "etc", "Renviron.site").toString();
             }
             if (new File(siteFile).exists()) {
                 safeReadEnvironFile(siteFile);
             }
-            String userFile = getEnvVars().get("R_ENVIRON_USER");
+            String userFile = envVars.get("R_ENVIRON_USER");
             if (userFile == null) {
                 String dotRenviron = ".Renviron";
                 userFile = fileSystem.getPath(RFFIFactory.getRFFI().getBaseRFFI().getwd(), dotRenviron).toString();
@@ -149,13 +108,13 @@ public class REnvVars implements RContext.StateFactory {
         }
     }
 
-    private static String getEitherCase(String var) {
-        String val = getEnvVars().get(var);
-        if (val != null) {
-            return val;
-        } else {
-            return getEnvVars().get(var.toUpperCase());
-        }
+    public static REnvVars newContext(RContext context) {
+        return new REnvVars(context);
+    }
+
+    private String getEitherCase(String var) {
+        String val = envVars.get(var);
+        return val != null ? val : envVars.get(var.toUpperCase());
     }
 
     private static String rHomePath;
@@ -182,38 +141,30 @@ public class REnvVars implements RContext.StateFactory {
                     Utils.fail("cannot find a valid R_HOME");
                 }
             }
-            // Check any external setting is consistent
-            String envRHomePath = getInitialEnvVars().get("R_HOME");
-            if (envRHomePath != null) {
-                new File(envRHomePath).getAbsolutePath();
-                if (!envRHomePath.equals(rHomePath)) {
-                    Utils.fail("R_HOME set to unexpected value in the environment");
-                }
-            }
         }
         return rHomePath;
     }
 
-    public static String put(String key, String value) {
+    public String put(String key, String value) {
         // TODO need to set value for sub-processes
-        return getEnvVars().put(key, value);
+        return envVars.put(key, value);
     }
 
-    public static String get(String key) {
-        return getEnvVars().get(key);
+    public String get(String key) {
+        return envVars.get(key);
     }
 
-    public static boolean unset(String key) {
+    public boolean unset(String key) {
         // TODO remove at the system level
-        getEnvVars().remove(key);
+        envVars.remove(key);
         return true;
     }
 
-    public static Map<String, String> getMap() {
-        return getEnvVars();
+    public Map<String, String> getMap() {
+        return envVars;
     }
 
-    public static void readEnvironFile(String path) throws IOException {
+    public void readEnvironFile(String path) throws IOException {
         try (BufferedReader r = new BufferedReader(new FileReader(path))) {
             String line = null;
             while ((line = r.readLine()) != null) {
@@ -228,12 +179,12 @@ public class REnvVars implements RContext.StateFactory {
                 String var = line.substring(0, ix);
                 String value = expandParameters(line.substring(ix + 1)).trim();
                 // GnuR does not seem to remove quotes, although the spec says it should
-                getEnvVars().put(var, value);
+                envVars.put(var, value);
             }
         }
     }
 
-    protected static String expandParameters(String value) {
+    protected String expandParameters(String value) {
         StringBuffer result = new StringBuffer();
         int x = 0;
         int paramStart = value.indexOf("${", x);
@@ -248,7 +199,7 @@ public class REnvVars implements RContext.StateFactory {
                 paramName = param.substring(0, dx);
                 paramDefault = expandParameters(param.substring(dx + 1));
             }
-            String paramValue = getEnvVars().get(paramName);
+            String paramValue = envVars.get(paramName);
             if (paramValue == null || paramValue.length() == 0) {
                 paramValue = paramDefault;
             }
@@ -265,7 +216,7 @@ public class REnvVars implements RContext.StateFactory {
         throw new IOException("   File " + path + " contains invalid line(s)\n      " + line + "\n   They were ignored\n");
     }
 
-    public static void safeReadEnvironFile(String path) {
+    public void safeReadEnvironFile(String path) {
         try {
             readEnvironFile(path);
         } catch (IOException ex) {
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RError.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RError.java
index fbb0eaf27202e81a1611288722015f46ed3083a1..cdd1a9f02a81e748ad831e0636cf87e6e36fcb0b 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RError.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RError.java
@@ -114,11 +114,32 @@ public final class RError extends RuntimeException {
         throw error0(node, msg, args);
     }
 
+    @TruffleBoundary
+    public static RError error(Node node, Message msg, Object... args) {
+        throw error0(findParentRBase(node), msg, args);
+    }
+
     @TruffleBoundary
     public static RError error(RBaseNode node, Message msg) {
         throw error0(node, msg, (Object[]) null);
     }
 
+    @TruffleBoundary
+    public static RError error(Node node, Message msg) {
+        throw error0(findParentRBase(node), msg, (Object[]) null);
+    }
+
+    private static RBaseNode findParentRBase(Node node) {
+        Node current = node;
+        while (current != null) {
+            if (current instanceof RBaseNode) {
+                return (RBaseNode) current;
+            }
+            current = current.getParent();
+        }
+        throw new AssertionError("Could not find RBaseNode for given Node. Is it not adopted in the AST?");
+    }
+
     /**
      * Handles an R error with the most general argument signature. All other facade variants
      * delegate to this method.
@@ -177,6 +198,12 @@ public final class RError extends RuntimeException {
         RErrorHandling.warningcall(true, node, msg, args);
     }
 
+    @TruffleBoundary
+    public static void warning(Node node, Message msg, Object... args) {
+        assert node != null;
+        warning(findParentRBase(node), msg, args);
+    }
+
     @TruffleBoundary
     public static RError stop(boolean showCall, RBaseNode node, Message msg, Object arg) {
         assert node != null;
@@ -186,7 +213,7 @@ public final class RError extends RuntimeException {
 
     @TruffleBoundary
     public static void performanceWarning(String string) {
-        if (FastROptions.PerformanceWarnings.getValue()) {
+        if (FastROptions.PerformanceWarnings) {
             warning(RError.NO_NODE, Message.PERFORMANCE, string);
         }
     }
@@ -545,6 +572,7 @@ public final class RError extends RuntimeException {
         CHAR_VEC_ARGUMENT("a character vector argument expected"),
         QUOTE_G_ONE("only the first character of 'quote' will be used"),
         UNEXPECTED("unexpected '%s' in \"%s\""),
+        UNEXPECTED_LINE("unexpected '%s' in \"%s\" (line %d)"),
         FIRST_ELEMENT_USED("first element used of '%s' argument"),
         MUST_BE_COERCIBLE_INTEGER("argument must be coercible to non-negative integer"),
         DEFAULT_METHOD_NOT_IMPLEMENTED_FOR_TYPE("default method not implemented for type '%s'"),
@@ -591,7 +619,10 @@ public final class RError extends RuntimeException {
         BAD_RESTART("bad restart"),
         RESTART_NOT_ON_STACK("restart not on stack"),
         PERFORMANCE("performance problem: %s"),
-        MUST_BE_SMALL_INT("argument '%s' must be a small integer");
+        MUST_BE_SMALL_INT("argument '%s' must be a small integer"),
+        NO_INTEROP("'%s' is not an object that supports interoperability (class %s)"),
+        NO_IMPORT_OBJECT("'%s' is not an exported object"),
+        NO_FUNCTION_RETURN("no function to return from, jumping to top level");
 
         public final String message;
         final boolean hasArgs;
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RErrorHandling.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RErrorHandling.java
index 8e19885192e0c3f52aaeadb3f7c5e0fbf9952c43..156025ae232fb3b8143914486df5556befe4f1f2 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RErrorHandling.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RErrorHandling.java
@@ -17,7 +17,9 @@ import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.frame.*;
 import com.oracle.truffle.api.source.*;
 import com.oracle.truffle.r.runtime.RError.Message;
+import com.oracle.truffle.r.runtime.context.*;
 import com.oracle.truffle.r.runtime.data.*;
+import com.oracle.truffle.r.runtime.data.model.*;
 import com.oracle.truffle.r.runtime.env.*;
 import com.oracle.truffle.r.runtime.nodes.*;
 
@@ -38,7 +40,7 @@ import com.oracle.truffle.r.runtime.nodes.*;
  * <p>
  * TODO Consider using an {@link RLanguage} object to denote the call (somehow).
  */
-public class RErrorHandling implements RContext.StateFactory {
+public class RErrorHandling {
 
     private static final int IN_HANDLER = 3;
     private static final RStringVector RESTART_CLASS = RDataFactory.createStringVectorFromScalar("restart");
@@ -67,7 +69,7 @@ public class RErrorHandling implements RContext.StateFactory {
      * Holds all the context-specific state that is relevant for error/warnings. Simple value class
      * for which geterrs/setters are unnecessary.
      */
-    private static class ContextStateImpl implements RContext.ContextState {
+    public static class ContextStateImpl implements RContext.ContextState {
         /**
          * Values is either NULL or an RPairList, for {@code restarts}.
          */
@@ -127,6 +129,9 @@ public class RErrorHandling implements RContext.StateFactory {
             return dotSignalSimpleWarning;
         }
 
+        public static ContextStateImpl newContext(@SuppressWarnings("unused") RContext context) {
+            return new ContextStateImpl();
+        }
     }
 
     /**
@@ -145,12 +150,8 @@ public class RErrorHandling implements RContext.StateFactory {
 
     private static final Object RESTART_TOKEN = new Object();
 
-    public RContext.ContextState newContext(RContext context, Object... objects) {
-        return new ContextStateImpl();
-    }
-
     private static ContextStateImpl getRErrorHandlingState() {
-        return (ContextStateImpl) RContext.getContextState(RContext.ClassStateKind.RErrorHandling);
+        return RContext.getInstance().stateRErrorHandling;
     }
 
     public static Object getHandlerStack() {
@@ -435,7 +436,7 @@ public class RErrorHandling implements RContext.StateFactory {
         }
 
         // we are not quite done - need to check for options(error=expr)
-        Object errorExpr = RContext.getROptionsState().getValue("error");
+        Object errorExpr = RContext.getInstance().stateROptions.getValue("error");
         if (errorExpr != RNull.instance) {
             int oldInError = errorHandlingState.inError;
             try {
@@ -525,7 +526,7 @@ public class RErrorHandling implements RContext.StateFactory {
         if (errorHandlingState.inWarning) {
             return;
         }
-        Object s = RContext.getROptionsState().getValue("warning.expression");
+        Object s = RContext.getInstance().stateROptions.getValue("warning.expression");
         if (s != RNull.instance) {
             if (!(s instanceof RLanguage || s instanceof RExpression)) {
                 // TODO
@@ -534,7 +535,12 @@ public class RErrorHandling implements RContext.StateFactory {
         }
 
         // ensured in ROptions
-        int w = ((RIntVector) RContext.getROptionsState().getValue("warn")).getDataAt(0);
+
+        Object value = RContext.getInstance().stateROptions.getValue("warn");
+        int w = 0;
+        if (value != RNull.instance) {
+            w = ((RAbstractIntVector) value).getDataAt(0);
+        }
         if (w == RRuntime.INT_NA) {
             w = 0;
         }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RInternalError.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RInternalError.java
index 69f0432a6ddea556c17f7e7fc4d74d44269ae071..58d173349e4044f524b9db3931e91ab7dbe7ae7a 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RInternalError.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RInternalError.java
@@ -78,7 +78,7 @@ public final class RInternalError extends Error {
     }
 
     static String createVerboseStackTrace() {
-        if (FastROptions.PrintErrorStacktracesToFile.getValue() || FastROptions.PrintErrorStacktraces.getValue()) {
+        if (FastROptions.PrintErrorStacktracesToFile || FastROptions.PrintErrorStacktraces) {
             return Utils.createStackTrace(true);
         } else {
             return "";
@@ -86,7 +86,7 @@ public final class RInternalError extends Error {
     }
 
     public static void reportError(Throwable t) {
-        if (FastROptions.PrintErrorStacktracesToFile.getValue() || FastROptions.PrintErrorStacktraces.getValue()) {
+        if (FastROptions.PrintErrorStacktracesToFile || FastROptions.PrintErrorStacktraces) {
             ByteArrayOutputStream out = new ByteArrayOutputStream();
             t.printStackTrace(new PrintStream(out));
             String verboseStackTrace;
@@ -97,11 +97,11 @@ public final class RInternalError extends Error {
             } else {
                 verboseStackTrace = "";
             }
-            if (FastROptions.PrintErrorStacktraces.getValue()) {
+            if (FastROptions.PrintErrorStacktraces) {
                 System.err.println(out.toString());
                 System.err.println(verboseStackTrace);
             }
-            if (FastROptions.PrintErrorStacktracesToFile.getValue()) {
+            if (FastROptions.PrintErrorStacktracesToFile) {
                 try (BufferedWriter writer = Files.newBufferedWriter(FileSystems.getDefault().getPath(REnvVars.rHome(), "fastr_errors.log"), StandardCharsets.UTF_8, StandardOpenOption.APPEND,
                                 StandardOpenOption.CREATE)) {
                     writer.append(new Date().toString()).append('\n');
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ROptions.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ROptions.java
index ab98285672dfa9937afdd2c2c6f09f3d2226a5f8..9eb886fcc0d5297ca8fe64c71f3b3bf236db8199 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ROptions.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ROptions.java
@@ -11,11 +11,12 @@
  */
 package com.oracle.truffle.r.runtime;
 
-import static com.oracle.truffle.r.runtime.RCmdOptions.*;
-
 import java.util.*;
 import java.util.Map.Entry;
 
+import com.oracle.truffle.r.runtime.RCmdOptions.RCmdOption;
+import com.oracle.truffle.r.runtime.context.*;
+import com.oracle.truffle.r.runtime.context.RContext.*;
 import com.oracle.truffle.r.runtime.data.*;
 
 /**
@@ -24,23 +25,14 @@ import com.oracle.truffle.r.runtime.data.*;
  *
  * An unset option does not appear in the map but is represented as the value {@link RNull#instance}
  * . Setting with {@link RNull#instance} removes the option from the map and, therefore, from being
- * visible in a call to {@code options()}. N.B. An option in the {@link #CHECKED_OPTIONS} set can
- * never be removed and this is handled by checking the value passed on update, where
+ * visible in a call to {@code options()}. N.B. An option in the {@link #CHECKED_OPTIONS_SET} set
+ * can never be removed and this is handled by checking the value passed on update, where
  * {@link RNull#instance} is illegal.
  *
  */
-public class ROptions implements RContext.StateFactory {
-    public interface ContextState extends RContext.ContextState {
-        Set<Map.Entry<String, Object>> getValues();
-
-        Object getValue(String name);
-
-        Object setValueNoCheck(String name, Object value);
-
-        Object setValue(String name, Object value) throws OptionsException;
-    }
+public class ROptions {
 
-    private static class ContextStateImpl implements ContextState {
+    public static final class ContextStateImpl implements RContext.ContextState {
         /**
          * The current values for a given context.
          */
@@ -87,6 +79,15 @@ public class ROptions implements RContext.StateFactory {
             return setValueNoCheck(name, coercedValue);
         }
 
+        public static ContextStateImpl newContext(RContext context, REnvVars envVars) {
+            HashMap<String, Object> map = new HashMap<>();
+            if (context.getKind() == ContextKind.SHARE_NOTHING) {
+                applyDefaults(map, context.getOptions(), envVars);
+            } else {
+                map.putAll(context.getParent().stateROptions.map);
+            }
+            return new ContextStateImpl(map);
+        }
     }
 
     @SuppressWarnings("serial")
@@ -100,64 +101,36 @@ public class ROptions implements RContext.StateFactory {
         }
     }
 
-    private static final String[] CHECKED_OPTIONS = new String[]{"width", "deparse.cutoff", "digits", "expressions", "keep.source", "editor", "continue", "prompt", "contrasts", "check.bounds",
-                    "warn", "warning.length", "warning.expression", "max.print", "nwarnings", "error", "show.error.messages", "echo", "OutDec", "max.contour.segments", "rl_word_breaks",
-                    "warnPartialMatchDollar", "warnPartialMatchArgs", "warnPartialMatchAttr", "showWarnCalls", "showErrorCalls", "showNCalls", "par.ask.default", "browserNLdisabled", "CBoundsCheck"};
-
-    private static final Set<String> CHECKED_OPTIONS_SET = new HashSet<>();
-
-    /**
-     * Holds the default values on startup of an {@link RContext}.
-     */
-    private static final HashMap<String, Object> defaultsMap = new HashMap<>();
-    private static HashMap<String, Object> systemInitMap;
-
-    private static void initDefaults() {
-        for (String s : CHECKED_OPTIONS) {
-            CHECKED_OPTIONS_SET.add(s);
-        }
-        defaultsMap.put("add.smooth", RDataFactory.createLogicalVectorFromScalar(true));
-        defaultsMap.put("check.bounds", RDataFactory.createLogicalVectorFromScalar(false));
-        defaultsMap.put("continue", RDataFactory.createStringVector("+ "));
-        defaultsMap.put("deparse.cutoff", RDataFactory.createIntVectorFromScalar(60));
-        defaultsMap.put("digits", RDataFactory.createIntVectorFromScalar(7));
-        defaultsMap.put("echo", RDataFactory.createLogicalVectorFromScalar(SLAVE.getValue() ? false : true));
-        defaultsMap.put("encoding", RDataFactory.createStringVector("native.enc"));
-        defaultsMap.put("expressions", RDataFactory.createIntVectorFromScalar(5000));
-        boolean keepPkgSource = optionFromEnvVar("R_KEEP_PKG_SOURCE");
-        defaultsMap.put("keep.source", RDataFactory.createLogicalVectorFromScalar(keepPkgSource));
-        defaultsMap.put("keep.source.pkgs", RDataFactory.createLogicalVectorFromScalar(keepPkgSource));
-        defaultsMap.put("OutDec", RDataFactory.createStringVector("."));
-        defaultsMap.put("prompt", RDataFactory.createStringVector("> "));
-        defaultsMap.put("verbose", RDataFactory.createLogicalVectorFromScalar(false));
-        defaultsMap.put("nwarnings", RDataFactory.createIntVectorFromScalar(50));
-        defaultsMap.put("warning.length", RDataFactory.createIntVectorFromScalar(1000));
-        defaultsMap.put("width", RDataFactory.createIntVectorFromScalar(80));
-        defaultsMap.put("browserNLdisabled", RDataFactory.createLogicalVectorFromScalar(false));
-        boolean cBoundsCheck = optionFromEnvVar("R_C_BOUNDS_CHECK");
-        defaultsMap.put("CBoundsCheck", RDataFactory.createLogicalVectorFromScalar(cBoundsCheck));
-    }
-
-    public ContextState newContext(RContext context, Object... objects) {
-        if (defaultsMap.isEmpty()) {
-            initDefaults();
-        }
-        HashMap<String, Object> map = new HashMap<>();
-        map.putAll(systemInitMap == null ? defaultsMap : systemInitMap);
-        return new ContextStateImpl(map);
-    }
-
-    @Override
-    public void systemInitialized(RContext context, RContext.ContextState state) {
-        ContextStateImpl optionsState = (ContextStateImpl) state;
-        systemInitMap = new HashMap<>(optionsState.map.size());
-        systemInitMap.putAll(optionsState.map);
+    private static final Set<String> CHECKED_OPTIONS_SET = new HashSet<>(Arrays.asList("width", "deparse.cutoff", "digits", "expressions", "keep.source", "editor", "continue", "prompt", "contrasts",
+                    "check.bounds", "warn", "warning.length", "warning.expression", "max.print", "nwarnings", "error", "show.error.messages", "echo", "OutDec", "max.contour.segments",
+                    "rl_word_breaks", "warnPartialMatchDollar", "warnPartialMatchArgs", "warnPartialMatchAttr", "showWarnCalls", "showErrorCalls", "showNCalls", "par.ask.default",
+                    "browserNLdisabled", "CBoundsCheck"));
+
+    private static void applyDefaults(HashMap<String, Object> map, RCmdOptions options, REnvVars envVars) {
+        map.put("add.smooth", RDataFactory.createLogicalVectorFromScalar(true));
+        map.put("check.bounds", RDataFactory.createLogicalVectorFromScalar(false));
+        map.put("continue", RDataFactory.createStringVector("+ "));
+        map.put("deparse.cutoff", RDataFactory.createIntVectorFromScalar(60));
+        map.put("digits", RDataFactory.createIntVectorFromScalar(7));
+        map.put("echo", RDataFactory.createLogicalVectorFromScalar(options.getBoolean(RCmdOption.SLAVE) ? false : true));
+        map.put("encoding", RDataFactory.createStringVector("native.enc"));
+        map.put("expressions", RDataFactory.createIntVectorFromScalar(5000));
+        boolean keepPkgSource = optionFromEnvVar("R_KEEP_PKG_SOURCE", envVars);
+        map.put("keep.source", RDataFactory.createLogicalVectorFromScalar(keepPkgSource));
+        map.put("keep.source.pkgs", RDataFactory.createLogicalVectorFromScalar(keepPkgSource));
+        map.put("OutDec", RDataFactory.createStringVector("."));
+        map.put("prompt", RDataFactory.createStringVector("> "));
+        map.put("verbose", RDataFactory.createLogicalVectorFromScalar(false));
+        map.put("nwarnings", RDataFactory.createIntVectorFromScalar(50));
+        map.put("warning.length", RDataFactory.createIntVectorFromScalar(1000));
+        map.put("width", RDataFactory.createIntVectorFromScalar(80));
+        map.put("browserNLdisabled", RDataFactory.createLogicalVectorFromScalar(false));
+        boolean cBoundsCheck = optionFromEnvVar("R_C_BOUNDS_CHECK", envVars);
+        map.put("CBoundsCheck", RDataFactory.createLogicalVectorFromScalar(cBoundsCheck));
     }
 
-    private static boolean optionFromEnvVar(String envVar) {
-        String envValue = REnvVars.get(envVar);
-        return envValue != null && envValue.equals("yes");
-
+    private static boolean optionFromEnvVar(String envVar, REnvVars envVars) {
+        return "yes".equals(envVars.get(envVar));
     }
 
     private static Object check(String name, Object value) throws OptionsException {
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RPerfStats.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RPerfStats.java
index 727795594e70bed87e4648077f512dd35a32d8cc..614a701256aa93a9a75ae6c593f65ac7ec14d220 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RPerfStats.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RPerfStats.java
@@ -188,7 +188,7 @@ public class RPerfStats {
             return;
         }
         reporting = true;
-        String file = FastROptions.PerfStatsFile.getValue();
+        String file = FastROptions.PerfStatsFile;
         if (file != null) {
             try {
                 out = new PrintStream(new FileOutputStream(file));
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RProfile.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RProfile.java
index f090fd858245f2712d657443ebd61d9f924f09c2..efed9307f1ae92aba748d13e20483308c995f75a 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RProfile.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RProfile.java
@@ -22,24 +22,30 @@
  */
 package com.oracle.truffle.r.runtime;
 
+import static com.oracle.truffle.r.runtime.RCmdOptions.RCmdOption.*;
+
 import java.io.*;
 import java.nio.file.*;
+import java.nio.file.FileSystem;
 
 import com.oracle.truffle.api.source.*;
+import com.oracle.truffle.r.runtime.context.*;
 import com.oracle.truffle.r.runtime.ffi.*;
 
 /**
- * Handles the setup of system, site and user profile code. N.B. {@link #initialize()} only reads
- * the files and leaves the evaluation to the caller, using {@link #siteProfile()} and
- * {@link #userProfile()}.
+ * Handles the setup of system, site and user profile code. N.B. this class only reads the files and
+ * leaves the evaluation to the caller, using {@link #siteProfile()} and {@link #userProfile()}.
  */
-public class RProfile {
-    public static void initialize() {
+public final class RProfile implements RContext.ContextState {
+
+    private RProfile(RContext context, REnvVars envVars) {
         String rHome = REnvVars.rHome();
         FileSystem fileSystem = FileSystems.getDefault();
+        Source newSiteProfile = null;
+        Source newUserProfile = null;
 
-        if (!RCmdOptions.NO_SITE_FILE.getValue()) {
-            String siteProfilePath = REnvVars.get("R_PROFILE");
+        if (!context.getOptions().getBoolean(NO_SITE_FILE)) {
+            String siteProfilePath = envVars.get("R_PROFILE");
             if (siteProfilePath == null) {
                 siteProfilePath = fileSystem.getPath(rHome, "etc", "Rprofile.site").toString();
             } else {
@@ -47,12 +53,12 @@ public class RProfile {
             }
             File siteProfileFile = new File(siteProfilePath);
             if (siteProfileFile.exists()) {
-                siteProfile = getProfile(siteProfilePath);
+                newSiteProfile = getProfile(siteProfilePath);
             }
         }
 
-        if (!RCmdOptions.NO_INIT_FILE.getValue()) {
-            String userProfilePath = REnvVars.get("R_PROFILE_USER");
+        if (!context.getOptions().getBoolean(NO_INIT_FILE)) {
+            String userProfilePath = envVars.get("R_PROFILE_USER");
             if (userProfilePath == null) {
                 String dotRenviron = ".Rprofile";
                 userProfilePath = fileSystem.getPath(RFFIFactory.getRFFI().getBaseRFFI().getwd(), dotRenviron).toString();
@@ -65,15 +71,16 @@ public class RProfile {
             if (userProfilePath != null) {
                 File userProfileFile = new File(userProfilePath);
                 if (userProfileFile.exists()) {
-                    userProfile = getProfile(userProfilePath);
+                    newUserProfile = getProfile(userProfilePath);
                 }
             }
-
         }
+        siteProfile = newSiteProfile;
+        userProfile = newUserProfile;
     }
 
-    private static Source siteProfile;
-    private static Source userProfile;
+    private final Source siteProfile;
+    private final Source userProfile;
 
     public static Source systemProfile() {
         Path path = FileSystems.getDefault().getPath(REnvVars.rHome(), "library", "base", "R", "Rprofile");
@@ -84,11 +91,11 @@ public class RProfile {
         return source;
     }
 
-    public static Source siteProfile() {
+    public Source siteProfile() {
         return siteProfile;
     }
 
-    public static Source userProfile() {
+    public Source userProfile() {
         return userProfile;
     }
 
@@ -101,4 +108,7 @@ public class RProfile {
         }
     }
 
+    public static RProfile newContext(RContext context, REnvVars envVars) {
+        return new RProfile(context, envVars);
+    }
 }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RRuntime.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RRuntime.java
index 0455314c2ee8f76228daaea9e98d44c4419bfc7c..5cdeca0edb151a16781b7bce6d67913e8edef1c7 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RRuntime.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RRuntime.java
@@ -18,6 +18,7 @@ import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.*;
 import com.oracle.truffle.api.frame.*;
+import com.oracle.truffle.api.interop.*;
 import com.oracle.truffle.r.runtime.data.*;
 import com.oracle.truffle.r.runtime.data.model.*;
 import com.oracle.truffle.r.runtime.env.frame.*;
@@ -291,8 +292,6 @@ public class RRuntime {
         } catch (NumberFormatException e) {
             if (exceptionOnFail) {
                 throw e;
-            } else {
-                RContext.getInstance().getAssumptions().naIntroduced.invalidate();
             }
         }
         return INT_NA;
@@ -329,8 +328,6 @@ public class RRuntime {
             }
             if (exceptionOnFail) {
                 throw e;
-            } else {
-                RContext.getInstance().getAssumptions().naIntroduced.invalidate();
             }
         }
         return DOUBLE_NA;
@@ -365,8 +362,6 @@ public class RRuntime {
             default:
                 if (exceptionOnFail) {
                     throw new NumberFormatException();
-                } else {
-                    RContext.getInstance().getAssumptions().naIntroduced.invalidate();
                 }
                 return LOGICAL_NA;
         }
@@ -805,7 +800,7 @@ public class RRuntime {
             return true;
         }
         if (type == RType.Function || type == RType.Closure || type == RType.Builtin || type == RType.Special) {
-            return obj instanceof RFunction;
+            return (obj instanceof RFunction) || (obj instanceof TruffleObject && !(obj instanceof RTypedValue));
         }
         if (type == RType.Character) {
             return obj instanceof String || obj instanceof RStringVector;
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RRuntimeASTAccess.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RRuntimeASTAccess.java
index 4388992444150d913c845629fdb9b0764bc20510..ed0c86ebc68153d35067f7ffe016f2fdcf775299 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RRuntimeASTAccess.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RRuntimeASTAccess.java
@@ -23,8 +23,10 @@
 package com.oracle.truffle.r.runtime;
 
 import com.oracle.truffle.api.nodes.*;
-import com.oracle.truffle.r.runtime.RContext.*;
+import com.oracle.truffle.api.vm.*;
+import com.oracle.truffle.r.runtime.context.*;
 import com.oracle.truffle.r.runtime.data.*;
+import com.oracle.truffle.r.runtime.data.model.*;
 
 /**
  * A collection of methods that need access to the AST types, needed by code that resides in the
@@ -48,6 +50,8 @@ public interface RRuntimeASTAccess {
      */
     RList asList(RLanguage rl);
 
+    Object fromList(RAbstractVector list);
+
     /**
      * Get the "names" attribute for an {@link RLanguage} object, or {@code null} if none.
      */
@@ -148,9 +152,9 @@ public interface RRuntimeASTAccess {
      */
     void setFunctionName(RootNode node, String name);
 
-    RContext create(RContext parent, Kind kind, String[] commandArgs, ConsoleHandler consoleHandler);
+    TruffleVM create(ContextInfo info);
 
-    RContext.Engine createEngine(RContext context);
+    Engine createEngine(RContext context);
 
     /**
      * Returns {@code true} iff {@code node} is an instance of {@code ReplacementNode}, which is not
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RSerialize.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RSerialize.java
index 5ce467cbbc3cbbd5489372d96598d25bb3b0e55f..c919e1990ec49259c6efac6489ff1c3fe87f6a0a 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RSerialize.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RSerialize.java
@@ -17,9 +17,9 @@ import java.util.*;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.frame.*;
 import com.oracle.truffle.api.source.*;
-import com.oracle.truffle.r.runtime.RContext.ContextState;
-import com.oracle.truffle.r.runtime.RContext.Engine.ParseException;
 import com.oracle.truffle.r.runtime.conn.*;
+import com.oracle.truffle.r.runtime.context.Engine.ParseException;
+import com.oracle.truffle.r.runtime.context.*;
 import com.oracle.truffle.r.runtime.data.*;
 import com.oracle.truffle.r.runtime.data.RAttributes.RAttribute;
 import com.oracle.truffle.r.runtime.data.model.*;
@@ -49,7 +49,7 @@ import com.oracle.truffle.r.runtime.instrument.*;
  *
  */
 // Checkstyle: stop final class check
-public class RSerialize implements RContext.StateFactory {
+public class RSerialize {
 
     private static class Flags {
         static final int IS_OBJECT_BIT_MASK = 1 << 8;
@@ -122,7 +122,7 @@ public class RSerialize implements RContext.StateFactory {
         Object eval(Object arg);
     }
 
-    private static class ContextStateImpl implements RContext.ContextState {
+    public static final class ContextStateImpl implements RContext.ContextState {
         /**
          * {@code true} iff we are saving the source from the deparse of an unserialized function
          * (for debugging later).
@@ -147,10 +147,10 @@ public class RSerialize implements RContext.StateFactory {
             }
             return dotDotFindNamespace;
         }
-    }
 
-    public ContextState newContext(RContext context, Object... objects) {
-        return new ContextStateImpl();
+        public static ContextStateImpl newContext(@SuppressWarnings("unused") RContext context) {
+            return new ContextStateImpl();
+        }
     }
 
     private static final int MAX_PACKED_INDEX = Integer.MAX_VALUE >> 8;
@@ -232,7 +232,7 @@ public class RSerialize implements RContext.StateFactory {
     }
 
     private static ContextStateImpl getContextState() {
-        return (ContextStateImpl) RContext.getContextState(RContext.ClassStateKind.RSerialize);
+        return RContext.getInstance().stateRSerialize;
     }
 
     /**
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/Utils.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/Utils.java
index df0ce5e9123731372fd3fea6b7f8da6e68d9cc4e..7b181fb60473c40c21c08061f100b7beca09b8ff 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/Utils.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/Utils.java
@@ -25,6 +25,7 @@ package com.oracle.truffle.r.runtime;
 import java.io.*;
 import java.net.*;
 import java.nio.file.*;
+import java.nio.file.FileSystem;
 import java.nio.file.attribute.*;
 import java.util.*;
 
@@ -34,8 +35,9 @@ import com.oracle.truffle.api.frame.*;
 import com.oracle.truffle.api.frame.FrameInstance.FrameAccess;
 import com.oracle.truffle.api.nodes.*;
 import com.oracle.truffle.api.source.*;
-import com.oracle.truffle.r.runtime.RContext.ConsoleHandler;
+import com.oracle.truffle.r.runtime.RCmdOptions.RCmdOption;
 import com.oracle.truffle.r.runtime.conn.*;
+import com.oracle.truffle.r.runtime.context.*;
 import com.oracle.truffle.r.runtime.data.*;
 import com.oracle.truffle.r.runtime.data.model.*;
 
@@ -124,9 +126,10 @@ public final class Utils {
      */
     public static RuntimeException exit(int status) {
         RPerfStats.report();
-        if (RCmdOptions.DEBUGGER.getValue() != null) {
+        if (RContext.getInstance().getOptions().getString(RCmdOption.DEBUGGER) != null) {
             throw new DebugExitException();
         } else {
+            RContext.getInstance().destroy();
             System.exit(status);
             return null;
         }
@@ -345,29 +348,33 @@ public final class Utils {
             str.append("\n");
         }
         Frame unwrapped = RArguments.unwrap(frame);
-        RCaller call = RArguments.getCall(unwrapped);
-        if (call != null) {
-            RRuntimeASTAccess rASTAccess = RContext.getRRuntimeASTAccess();
-            String callSrc = rASTAccess.getCallerSource(rASTAccess.getSyntaxCaller(call));
-            str.append("Frame: ").append(callTarget).append(isVirtual ? " (virtual)" : "");
-            str.append(" (called as: ").append(callSrc).append(')');
-        }
-        if (printFrameSlots) {
-            FrameDescriptor frameDescriptor = unwrapped.getFrameDescriptor();
-            for (FrameSlot s : frameDescriptor.getSlots()) {
-                str.append("\n      ").append(s.getIdentifier()).append(" = ");
-                Object value = unwrapped.getValue(s);
-                try {
-                    if (value instanceof RAbstractContainer && ((RAbstractContainer) value).getLength() > 32) {
-                        str.append('<').append(value.getClass().getSimpleName()).append(" with ").append(((RAbstractContainer) value).getLength()).append(" elements>");
-                    } else {
-                        String text = String.valueOf(value);
-                        str.append(text.length() < 256 ? text : text.substring(0, 256) + "...");
+        if (!RArguments.isRFrame(frame)) {
+            str.append("<unknown frame>");
+        } else {
+            RCaller call = RArguments.getCall(unwrapped);
+            if (call != null) {
+                RRuntimeASTAccess rASTAccess = RContext.getRRuntimeASTAccess();
+                String callSrc = rASTAccess.getCallerSource(rASTAccess.getSyntaxCaller(call));
+                str.append("Frame: ").append(callTarget).append(isVirtual ? " (virtual)" : "");
+                str.append(" (called as: ").append(callSrc).append(')');
+            }
+            if (printFrameSlots) {
+                FrameDescriptor frameDescriptor = unwrapped.getFrameDescriptor();
+                for (FrameSlot s : frameDescriptor.getSlots()) {
+                    str.append("\n      ").append(s.getIdentifier()).append(" = ");
+                    Object value = unwrapped.getValue(s);
+                    try {
+                        if (value instanceof RAbstractContainer && ((RAbstractContainer) value).getLength() > 32) {
+                            str.append('<').append(value.getClass().getSimpleName()).append(" with ").append(((RAbstractContainer) value).getLength()).append(" elements>");
+                        } else {
+                            String text = String.valueOf(value);
+                            str.append(text.length() < 256 ? text : text.substring(0, 256) + "...");
+                        }
+                    } catch (Throwable t) {
+                        // RLanguage values may not react kindly to getLength() calls
+                        str.append("<exception ").append(t.getClass().getSimpleName()).append(" while printing value of type ").append(value == null ? "null" : value.getClass().getSimpleName()).append(
+                                        '>');
                     }
-                } catch (Throwable t) {
-                    // RLanguage values may not react kindly to getLength() calls
-                    str.append("<exception ").append(t.getClass().getSimpleName()).append(" while printing value of type ").append(value == null ? "null" : value.getClass().getSimpleName()).append(
-                                    '>');
                 }
             }
         }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/VisibilityController.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/VisibilityController.java
index 8cb7120ec3cad0db6b111602b58758e681a5bbb2..c75845186eadc1f72af2065545c810ecafe80d8e 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/VisibilityController.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/VisibilityController.java
@@ -22,6 +22,8 @@
  */
 package com.oracle.truffle.r.runtime;
 
+import com.oracle.truffle.r.runtime.context.*;
+
 /**
  * This interface must be implemented by all nodes in the FastR implementation that control the
  * visibility of results in the shell. All specializations or, if no specializations exist,
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/ConnectionSupport.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/ConnectionSupport.java
index 0404e5901d79290900e295817ab083694c5a0183..0bcc330d21c6ffd5d3146da85caa05f7bb86763f 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/ConnectionSupport.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/ConnectionSupport.java
@@ -29,26 +29,21 @@ import java.util.*;
 
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.r.runtime.*;
+import com.oracle.truffle.r.runtime.context.*;
 import com.oracle.truffle.r.runtime.data.*;
 import com.oracle.truffle.r.runtime.data.model.*;
 
 /**
  * Basic support classes and methods for the connection implementations.
  */
-public class ConnectionSupport implements RContext.StateFactory {
-
-    public interface ContextState extends RContext.ContextState {
-        BaseRConnection getConnection(int index);
-
-        RIntVector getAllConnections();
-    }
+public class ConnectionSupport {
 
     /**
      * The context specific state, which is the set of active connections. We stored these as weak
      * references to detect when they become unused whuch, when detected, results in them being
      * closed if still open.
      */
-    private static final class ContextStateImpl implements ContextState {
+    public static final class ContextStateImpl implements RContext.ContextState {
         private static final int MAX_CONNECTIONS = 128;
         /**
          * Records all connections. The index in the array is the "descriptor" used in
@@ -129,7 +124,8 @@ public class ConnectionSupport implements RContext.StateFactory {
             throw RError.error(RError.NO_NODE, RError.Message.ALL_CONNECTIONS_IN_USE);
         }
 
-        private void beforeDestroy() {
+        @Override
+        public void beforeDestroy(RContext context) {
             // close all open connections
             for (int i = 3; i <= hwm; i++) {
                 WeakReference<BaseRConnection> ref = allConnections.get(i);
@@ -170,20 +166,14 @@ public class ConnectionSupport implements RContext.StateFactory {
                 }
             }
         }
-    }
 
-    private static ContextStateImpl getContextStateImpl() {
-        return (ContextStateImpl) RContext.getRConnectionState();
-    }
-
-    @Override
-    public ContextState newContext(RContext context, Object... objects) {
-        return new ContextStateImpl();
+        public static ContextStateImpl newContext(@SuppressWarnings("unused") RContext context) {
+            return new ContextStateImpl();
+        }
     }
 
-    @Override
-    public void beforeDestroy(RContext context, RContext.ContextState state) {
-        ((ContextStateImpl) state).beforeDestroy();
+    private static ContextStateImpl getContextStateImpl() {
+        return RContext.getInstance().stateRConnection;
     }
 
     private static final class ModeException extends IOException {
@@ -334,7 +324,7 @@ public class ConnectionSupport implements RContext.StateFactory {
      */
     public static RConnection fromVector(RVector vector, @SuppressWarnings("unused") RStringVector classAttr) {
         int index = RRuntime.asInteger(vector);
-        RConnection result = RContext.getRConnectionState().getConnection(index);
+        RConnection result = RContext.getInstance().stateRConnection.getConnection(index);
         if (result == null) {
             // non-existent connection, still legal according to GnuR
             // we cannot throw an error here as this can be used by parallel packages - do it lazily
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/StdConnections.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/StdConnections.java
index 9cc9bcb13af7c8a6519b9e84af571eda30e3ac1a..075d7006fddfbbf6d45fd4cbbcff4eeb800a98a3 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/StdConnections.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/StdConnections.java
@@ -26,14 +26,14 @@ import java.io.*;
 import java.nio.*;
 import java.util.*;
 
-import com.oracle.truffle.api.CompilerDirectives.*;
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.r.runtime.*;
-import com.oracle.truffle.r.runtime.RContext.ContextState;
-import com.oracle.truffle.r.runtime.RContext.ConsoleHandler;
-import com.oracle.truffle.r.runtime.conn.ConnectionSupport.*;
+import com.oracle.truffle.r.runtime.conn.ConnectionSupport.AbstractOpenMode;
+import com.oracle.truffle.r.runtime.conn.ConnectionSupport.BaseRConnection;
+import com.oracle.truffle.r.runtime.context.*;
 import com.oracle.truffle.r.runtime.data.model.*;
 
-public class StdConnections implements RContext.StateFactory {
+public class StdConnections {
 
     private static class Diversion {
         final RConnection conn;
@@ -45,32 +45,31 @@ public class StdConnections implements RContext.StateFactory {
         }
     }
 
-    private static class ContextStateImpl implements RContext.ContextState {
+    public static final class ContextStateImpl implements RContext.ContextState {
         private final StdinConnection stdin;
         private final StdoutConnection stdout;
         private final StderrConnection stderr;
         private final Diversion[] diversions = new Diversion[20];
         private int top = -1;
 
-        ContextStateImpl(StdinConnection stdin, StdoutConnection stdout, StderrConnection stderr) {
+        private ContextStateImpl(StdinConnection stdin, StdoutConnection stdout, StderrConnection stderr) {
             this.stdin = stdin;
             this.stdout = stdout;
             this.stderr = stderr;
         }
 
-    }
-
-    public ContextState newContext(RContext context, Object... objects) {
-        ConsoleHandler consoleHandler = (ConsoleHandler) objects[0];
-        try {
-            return new ContextStateImpl(new StdinConnection(), new StdoutConnection(consoleHandler), new StderrConnection(consoleHandler));
-        } catch (IOException ex) {
-            throw Utils.fail("failed to open stdconnections:");
+        public static ContextStateImpl newContext(RContext context) {
+            ConsoleHandler consoleHandler = context.getConsoleHandler();
+            try {
+                return new ContextStateImpl(new StdinConnection(), new StdoutConnection(consoleHandler), new StderrConnection(consoleHandler));
+            } catch (IOException ex) {
+                throw Utils.fail("failed to open stdconnections:");
+            }
         }
     }
 
     private static ContextStateImpl getContextState() {
-        return (ContextStateImpl) RContext.getContextState(RContext.ClassStateKind.StdConnections);
+        return RContext.getInstance().stateStdConnections;
     }
 
     public static RConnection getStdin() {
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/ConsoleHandler.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/ConsoleHandler.java
new file mode 100644
index 0000000000000000000000000000000000000000..ee957d9a6108158dec625e7a78ba7ea4c4257b9d
--- /dev/null
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/ConsoleHandler.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2013, 2015, 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.context;
+
+import com.oracle.truffle.api.CompilerDirectives.*;
+
+/**
+ * The interface to a source of input/output for the context, which may have different
+ * implementations for different contexts. Since I/O is involved, all methods are tagged with
+ * {@link TruffleBoundary} as a hint that so should the associated implementation methods.
+ */
+public interface ConsoleHandler {
+    /**
+     * Normal output with a new line.
+     */
+    @TruffleBoundary
+    void println(String s);
+
+    /**
+     * Normal output without a newline.
+     */
+    @TruffleBoundary
+    void print(String s);
+
+    /**
+     * Formatted output.
+     */
+    @TruffleBoundary
+    default void printf(String format, Object... args) {
+        print(String.format(format, args));
+    }
+
+    /**
+     * Error output with a newline.
+     *
+     * @param s
+     */
+    @TruffleBoundary
+    void printErrorln(String s);
+
+    /**
+     * Error output without a newline.
+     */
+    @TruffleBoundary
+    void printError(String s);
+
+    /**
+     * Read a line of input, newline included in result. Returns null if {@link #isInteractive() ==
+     * false}. The rationale for including the readline is to ensure that the accumulated input,
+     * whether it be from a file or the console accurately reflects the the source. TODO worry about
+     * "\r\n"?
+     */
+    @TruffleBoundary
+    String readLine();
+
+    /**
+     * Denote whether the FastR instance is running in 'interactive' mode. This can be set in a
+     * number of ways and is <b>not</> simply equivalent to taking input from a file. However, it is
+     * final once set.
+     */
+    @TruffleBoundary
+    boolean isInteractive();
+
+    /**
+     * Redirect error output to the normal output.
+     */
+    @TruffleBoundary
+    void redirectError();
+
+    /**
+     * Get the current prompt.
+     */
+    @TruffleBoundary
+    String getPrompt();
+
+    /**
+     * Set the R prompt.
+     */
+    @TruffleBoundary
+    void setPrompt(String prompt);
+
+    /**
+     * Get the console width.
+     */
+    @TruffleBoundary
+    int getWidth();
+
+    String getInputDescription();
+}
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/ContextInfo.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/ContextInfo.java
new file mode 100644
index 0000000000000000000000000000000000000000..4fcdc0b1d6a7c187a172c6694d8e4b676f80b70c
--- /dev/null
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/ContextInfo.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2013, 2015, 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.context;
+
+import java.util.*;
+import java.util.concurrent.*;
+import java.util.concurrent.atomic.*;
+
+import com.oracle.truffle.api.vm.*;
+import com.oracle.truffle.r.runtime.*;
+import com.oracle.truffle.r.runtime.context.RContext.ContextKind;
+
+public final class ContextInfo {
+    private static final ConcurrentHashMap<Integer, ContextInfo> contextInfos = new ConcurrentHashMap<>();
+    private static final AtomicInteger contextInfoIds = new AtomicInteger();
+
+    private final RCmdOptions options;
+    private final RContext.ContextKind kind;
+    private final TimeZone systemTimeZone;
+
+    /**
+     * Any context created by another has a parent. When such a context is destroyed we must reset
+     * the RContext.threadLocalContext to the parent.
+     */
+    private final RContext parent;
+    private final ConsoleHandler consoleHandler;
+    private final int id;
+
+    private ContextInfo(RCmdOptions options, ContextKind kind, RContext parent, ConsoleHandler consoleHandler, TimeZone systemTimeZone, int id) {
+        this.options = options;
+        this.kind = kind;
+        this.parent = parent;
+        this.consoleHandler = consoleHandler;
+        this.systemTimeZone = systemTimeZone;
+        this.id = id;
+    }
+
+    public TruffleVM newContext() {
+        contextInfos.remove(id);
+        return RContext.getRRuntimeASTAccess().create(this);
+    }
+
+    /**
+     * Create a context configuration object.
+     *
+     * @param parent if non-null {@code null}, the parent creating the context
+     * @param kind defines the degree to which this context shares base and package environments
+     *            with its parent
+     * @param options the command line arguments passed this R session
+     * @param consoleHandler a {@link ConsoleHandler} for output
+     * @param systemTimeZone the system's time zone
+     */
+    public static ContextInfo create(RCmdOptions options, ContextKind kind, RContext parent, ConsoleHandler consoleHandler, TimeZone systemTimeZone) {
+        int id = contextInfoIds.incrementAndGet();
+        return new ContextInfo(options, kind, parent, consoleHandler, systemTimeZone, id);
+    }
+
+    public static ContextInfo create(RCmdOptions options, ContextKind kind, RContext parent, ConsoleHandler consoleHandler) {
+        return create(options, kind, parent, consoleHandler, TimeZone.getDefault());
+    }
+
+    public static int createDeferred(RCmdOptions options, ContextKind kind, RContext parent, ConsoleHandler consoleHandler) {
+        ContextInfo info = create(options, kind, parent, consoleHandler, TimeZone.getDefault());
+        contextInfos.put(info.id, info);
+        return info.id;
+    }
+
+    public static ContextInfo get(int id) {
+        return contextInfos.get(id);
+    }
+
+    public RCmdOptions getOptions() {
+        return options;
+    }
+
+    public ContextKind getKind() {
+        return kind;
+    }
+
+    public RContext getParent() {
+        return parent;
+    }
+
+    public ConsoleHandler getConsoleHandler() {
+        return consoleHandler;
+    }
+
+    public TimeZone getSystemTimeZone() {
+        return systemTimeZone;
+    }
+
+    public int getId() {
+        return id;
+    }
+}
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/DefaultConsoleHandler.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/DefaultConsoleHandler.java
new file mode 100644
index 0000000000000000000000000000000000000000..9a3bcad867752a987576e42934921ba5a26dcc54
--- /dev/null
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/DefaultConsoleHandler.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2015, 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.context;
+
+import java.io.*;
+
+import com.oracle.truffle.api.TruffleLanguage.Env;
+
+public class DefaultConsoleHandler implements ConsoleHandler {
+
+    private final Env env;
+    private final BufferedReader in;
+
+    public DefaultConsoleHandler(Env env) {
+        this.env = env;
+        in = new BufferedReader(env.stdIn());
+    }
+
+    public void println(String s) {
+        try {
+            env.stdOut().append(s).append('\n');
+        } catch (IOException e) {
+            // TODO: handle this error
+        }
+    }
+
+    public void print(String s) {
+        try {
+            env.stdOut().append(s).append('\n');
+        } catch (IOException e) {
+            // TODO: handle this error
+        }
+    }
+
+    public void printErrorln(String s) {
+        try {
+            env.stdOut().append(s).append('\n');
+        } catch (IOException e) {
+            // TODO: handle this error
+        }
+    }
+
+    public void printError(String s) {
+        try {
+            env.stdOut().append(s).append('\n');
+        } catch (IOException e) {
+            // TODO: handle this error
+        }
+    }
+
+    public String readLine() {
+        try {
+            return in.readLine();
+        } catch (IOException e) {
+            // TODO: handle this error
+            return null;
+        }
+    }
+
+    public boolean isInteractive() {
+        return true;
+    }
+
+    public void redirectError() {
+        // ?
+    }
+
+    public String getPrompt() {
+        return "> ";
+    }
+
+    public void setPrompt(String prompt) {
+        // ?
+    }
+
+    public int getWidth() {
+        return 80;
+    }
+
+    public String getInputDescription() {
+        return "<TruffleVM env input>";
+    }
+}
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/Engine.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/Engine.java
new file mode 100644
index 0000000000000000000000000000000000000000..29b959bdebd85b255166ce77fe660aed6074b77b
--- /dev/null
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/Engine.java
@@ -0,0 +1,206 @@
+/*
+ * Copyright (c) 2013, 2015, 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.context;
+
+import java.io.*;
+
+import com.oracle.truffle.api.*;
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.frame.*;
+import com.oracle.truffle.api.interop.*;
+import com.oracle.truffle.api.source.*;
+import com.oracle.truffle.r.runtime.*;
+import com.oracle.truffle.r.runtime.data.*;
+import com.oracle.truffle.r.runtime.env.*;
+
+public interface Engine {
+
+    public static class ParseException extends IOException {
+        private static final long serialVersionUID = 1L;
+
+        private final Source source;
+        private final String token;
+        private final String substring;
+        private final int line;
+
+        public ParseException(Throwable cause, Source source, String token, String substring, int line) {
+            super("parse exception", cause);
+            this.source = source;
+            this.token = token;
+            this.substring = substring;
+            this.line = line;
+        }
+
+        public RError throwAsRError() {
+            if (source.getLineCount() == 1) {
+                throw RError.error(RError.NO_CALLER, RError.Message.UNEXPECTED, token, substring);
+            } else {
+                throw RError.error(RError.NO_CALLER, RError.Message.UNEXPECTED_LINE, token, substring, line);
+            }
+        }
+
+        public void report(ConsoleHandler consoleHandler) {
+            String msg;
+            if (source.getLineCount() == 1) {
+                msg = String.format(RError.Message.UNEXPECTED.message, token, substring);
+            } else {
+                msg = String.format(RError.Message.UNEXPECTED_LINE.message, token, substring, line);
+            }
+            consoleHandler.println("Error: " + msg);
+        }
+    }
+
+    public static final class IncompleteSourceException extends ParseException {
+        private static final long serialVersionUID = -6688699706193438722L;
+
+        public IncompleteSourceException(Throwable cause, Source source, String token, String substring, int line) {
+            super(cause, source, token, substring, line);
+        }
+    }
+
+    /**
+     * Make the engine ready for evaluations.
+     */
+    void activate(REnvironment.ContextStateImpl stateREnvironment);
+
+    /**
+     * Elapsed time of runtime.
+     *
+     * @return elapsed time in nanosecs.
+     */
+    long elapsedTimeInNanos();
+
+    /**
+     * Return user and system times for any spawned child processes in nanosecs, < 0 means not
+     * available (Windows).
+     */
+    long[] childTimesInNanos();
+
+    /**
+     * Parse an R expression and return an {@link RExpression} object representing the Truffle ASTs
+     * for the components.
+     */
+    RExpression parse(Source source) throws ParseException;
+
+    /**
+     * A (perhaps temporary) interface to support {@link TruffleLanguage}.
+     */
+    CallTarget parseToCallTarget(Source source, boolean printResult) throws ParseException;
+
+    /**
+     * Parse and evaluate {@code rscript} in {@code frame}. {@code printResult == true}, the result
+     * of the evaluation is printed to the console.
+     *
+     * @param sourceDesc a {@link Source} object that describes the input to be parsed
+     * @param frame the frame in which to evaluate the input
+     * @param printResult {@code true} iff the result of the evaluation should be printed to the
+     *            console
+     * @return the object returned by the evaluation or {@code null} if an error occurred.
+     */
+    Object parseAndEval(Source sourceDesc, MaterializedFrame frame, boolean printResult) throws ParseException;
+
+    /**
+     * Variant of {@link #parseAndEval(Source, MaterializedFrame, boolean)} for evaluation in the
+     * global frame.
+     */
+    Object parseAndEval(Source sourceDesc, boolean printResult) throws ParseException;
+
+    /**
+     * Support for the {@code eval} {@code .Internal}.
+     */
+    Object eval(RExpression expr, REnvironment envir, REnvironment enclos, int depth);
+
+    /**
+     * Convenience method for common case.
+     */
+    default Object eval(RExpression expr, REnvironment envir, int depth) {
+        return eval(expr, envir, null, depth);
+    }
+
+    /**
+     * Variant of {@link #eval(RExpression, REnvironment, REnvironment, int)} for a single language
+     * element.
+     */
+    Object eval(RLanguage expr, REnvironment envir, REnvironment enclos, int depth);
+
+    /**
+     * Convenience method for common case.
+     */
+    default Object eval(RLanguage expr, REnvironment envir, int depth) {
+        return eval(expr, envir, null, depth);
+    }
+
+    /**
+     * Evaluate {@code expr} in {@code frame}.
+     */
+    Object eval(RExpression expr, MaterializedFrame frame);
+
+    /**
+     * Variant of {@link #eval(RExpression, MaterializedFrame)} for a single language element.
+     */
+    Object eval(RLanguage expr, MaterializedFrame frame);
+
+    /**
+     * Variant of {@link #eval(RLanguage, MaterializedFrame)} where we already have the
+     * {@link RFunction} and the evaluated arguments, but do not have a frame available, and we are
+     * behind a {@link TruffleBoundary}, so call inlining is not an issue. This is primarily used
+     * for R callbacks from {@link RErrorHandling} and {@link RSerialize}.
+     */
+    Object evalFunction(RFunction func, Object... args);
+
+    /**
+     * Evaluates an {@link com.oracle.truffle.r.runtime.data.RPromise.Closure} in {@code frame}.
+     */
+    Object evalPromise(RPromise.Closure closure, MaterializedFrame frame);
+
+    /**
+     * Checks for the existence of {@code .Last/.Last.sys} and if present and bound to a function,
+     * invokes the (parameterless) function.
+     */
+    void checkAndRunLast(String name);
+
+    /**
+     * Wraps the Truffle AST in {@code body} in an anonymous function and returns a
+     * {@link RootCallTarget} for it.
+     *
+     * N.B. For certain expressions, there might be some value in enclosing the wrapper function in
+     * a specific lexical scope. E.g., as a way to access names in the expression known to be
+     * defined in that scope.
+     *
+     * @param body The AST for the body of the wrapper, i.e., the expression being evaluated.
+     */
+    RootCallTarget makePromiseCallTarget(Object body, String funName);
+
+    /**
+     * Used by Truffle debugger; invokes the internal "print" support in R for {@code value}.
+     * Essentially this is equivalent to {@link #evalFunction} using the {@code "print"} function.
+     */
+    void printResult(Object value);
+
+    RFunction parseFunction(String name, Source source, MaterializedFrame enclosingFrame) throws ParseException;
+
+    ForeignAccess getForeignAccess(RTypedValue value);
+
+    Class<? extends TruffleLanguage<RContext>> getTruffleLanguage();
+
+}
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/RContext.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/RContext.java
new file mode 100644
index 0000000000000000000000000000000000000000..0774e6e3fa49de90c17bed0bc567d66e353671d4
--- /dev/null
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/RContext.java
@@ -0,0 +1,537 @@
+/*
+ * Copyright (c) 2013, 2015, 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.context;
+
+import java.util.*;
+import java.util.concurrent.*;
+
+import com.oracle.truffle.api.*;
+import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.TruffleLanguage.Env;
+import com.oracle.truffle.api.interop.*;
+import com.oracle.truffle.api.nodes.*;
+import com.oracle.truffle.api.source.*;
+import com.oracle.truffle.api.vm.*;
+import com.oracle.truffle.r.runtime.*;
+import com.oracle.truffle.r.runtime.RCmdOptions.Client;
+import com.oracle.truffle.r.runtime.conn.*;
+import com.oracle.truffle.r.runtime.context.Engine.ParseException;
+import com.oracle.truffle.r.runtime.data.*;
+import com.oracle.truffle.r.runtime.env.*;
+import com.oracle.truffle.r.runtime.ffi.*;
+import com.oracle.truffle.r.runtime.rng.*;
+
+/**
+ * Encapsulates the runtime state ("context") of an R session. All access to that state from the
+ * implementation <b>must</b> go through this class. There can be multiple instances
+ * (multiple-tenancy) active within a single process/Java-VM.
+ *
+ * The context provides two sub-interfaces {@link ConsoleHandler} and {@link Engine}that are
+ * (typically) implemented elsewhere, and accessed through {@link #getConsoleHandler()} and
+ * {@link #getEngine()}, respectively.
+ *
+ * Context-specific state for implementation classes is managed by this class (or the associated
+ * engine) and accessed through the {@code getXXXState} methods.
+ *
+ * The life-cycle of a {@link RContext} is:
+ * <ol>
+ * <li>created: {@link #create(Env)}</li>
+ * <li>destroyed: {@link #destroy()}</li>
+ * </ol>
+ *
+ * Evaluations are only possible on an active context.
+ *
+ */
+public final class RContext extends ExecutionContext {
+
+    public static final int CONSOLE_WIDTH = 80;
+
+    public enum ContextKind {
+        /**
+         * Essentially a clean restart, modulo the basic VM-wide initialization. which does include,
+         * for example, reading the external environment variables. I.e., it is not a goal to create
+         * a multi-user environment with different external inputs. This kind of context can be used
+         * in a parallel computation. The initial context is always of this kind. The intent is that
+         * all mutable state is localized to the context, which may not be completely achievable.
+         * For example, shared native libraries are assumed to contain no mutable state and be
+         * re-entrant.
+         */
+        SHARE_NOTHING,
+
+        /**
+         * Shares the set of loaded packages of the parent context at the time the context is
+         * created. Only useful when there is a priori knowledge on the evaluation that the context
+         * will be used for. Cannot safely be used for parallel context evaluation. Must be created
+         * as a child of an existing parent context of type {@link #SHARE_NOTHING} or
+         * {@link #SHARE_PARENT_RO} and only one such child is allowed. (Strictly speaking the
+         * invariant should be only one active child, but the implementation enforces it at creation
+         * time). Evidently any changes made to the shared environment, e.g., loading a package,
+         * affect the parent.
+         */
+        SHARE_PARENT_RW,
+
+        /**
+         * Intermediate between {@link #SHARE_NOTHING} and {@link #SHARE_PARENT_RW}, this is similar
+         * to the standard shared code/copied data model provided by operating systems, although the
+         * code/data distinction isn't completely applicable to a language like R. Unlike
+         * {@link #SHARE_NOTHING}, where the ASTs for the functions in the default packages are
+         * distinct copies in each context, in this kind of context, they are shared. Strictly
+         * speaking, the bindings of R functions are shared, and this is achieved by creating a
+         * shallow copy of the environments associated with the default packages of the parent
+         * context at the time the context is created.
+         */
+        SHARE_PARENT_RO;
+
+        public static final ContextKind[] VALUES = values();
+    }
+
+    /**
+     * Tagging interface denoting a class that carries the context-specific state for a class that
+     * has context-specific state. The class specific state must implement this interface.
+     */
+    public interface ContextState {
+        /**
+         * Called in response to the {@link RContext#destroy} method. Provides a hook for finalizing
+         * any state before the context is destroyed.
+         */
+        @SuppressWarnings("unused")
+        default void beforeDestroy(RContext context) {
+            // default empty implementation
+        }
+    }
+
+    /**
+     * A thread that is explicitly associated with a context for efficient lookup.
+     */
+    public static class ContextThread extends Thread {
+        protected RContext context;
+
+        public ContextThread(RContext context) {
+            this.context = context;
+        }
+
+        protected ContextThread() {
+
+        }
+
+        public void setContext(RContext context) {
+            this.context = context;
+        }
+    }
+
+    /**
+     * A thread for performing an evaluation (used by {@code fastr} package.
+     */
+    public static class EvalThread extends ContextThread {
+        private final Source source;
+        private final ContextInfo info;
+
+        public static final Map<Integer, Thread> threads = new ConcurrentHashMap<>();
+
+        public EvalThread(ContextInfo info, Source source) {
+            super(null);
+            this.info = info;
+            this.source = source;
+            threads.put(info.getId(), this);
+        }
+
+        @Override
+        public void run() {
+            TruffleVM vm = info.newContext();
+            setContext(truffleVMContexts.get(vm));
+            try {
+                try {
+                    context.engine.parseAndEval(source, true);
+                } catch (ParseException e) {
+                    throw e.throwAsRError();
+                }
+            } finally {
+                context.destroy();
+                threads.remove(info.getId());
+            }
+        }
+    }
+
+    private final ContextInfo info;
+    private final Engine engine;
+
+    /**
+     * Denote whether the result of an expression should be printed in the shell or not.
+     */
+    private boolean resultVisible = true;
+
+    /**
+     * A context-specific value that is checked in {@code HiddenInternalFunctions} to avoid an error
+     * report on a {@code SUBSTITUTE} builtin. Not worth promoting to a {@link ContextState}.
+     */
+    private boolean loadingBase;
+
+    /**
+     * At most one shared child.
+     */
+    private RContext sharedChild;
+
+    /**
+     * Back pointer to the evalThread.
+     */
+    private EvalThread evalThread;
+
+    /**
+     * Typically there is a 1-1 relationship between an {@link RContext} and the thread that is
+     * performing the evaluation, so we can store the {@link RContext} in a {@link ThreadLocal}.
+     *
+     * When a context is first created no threads are attached, to allow contexts to be used as
+     * values in the experimental {@code fastr.createcontext} function. Additional threads can be
+     * added by the {@link #attachThread} method.
+     */
+    public static final ThreadLocal<RContext> threadLocalContext = new ThreadLocal<>();
+
+    /**
+     * Used by the MethodListDispatch class.
+     */
+
+    private boolean methodTableDispatchOn = true;
+
+    private boolean active;
+
+    /**
+     * A (hopefully) temporary workaround to ignore the setting of {@link #resultVisible} for
+     * benchmarks. Set across all contexts.
+     */
+    @CompilationFinal private static boolean ignoreVisibility;
+
+    /*
+     * Workarounds to finesse project circularities between runtime/nodes.
+     */
+    @CompilationFinal private static RRuntimeASTAccess runtimeASTAccess;
+    @CompilationFinal private static RBuiltinLookup builtinLookup;
+
+    /**
+     * Initialize VM-wide static values.
+     */
+    public static void initialize(RRuntimeASTAccess rASTHelperArg, RBuiltinLookup rBuiltinLookupArg, boolean ignoreVisibilityArg) {
+        runtimeASTAccess = rASTHelperArg;
+        builtinLookup = rBuiltinLookupArg;
+        ignoreVisibility = ignoreVisibilityArg;
+    }
+
+    /**
+     * Associates this {@link RContext} with the current thread.
+     */
+    public void attachThread() {
+        Thread current = Thread.currentThread();
+        if (current instanceof ContextThread) {
+            ((ContextThread) current).setContext(this);
+        } else {
+            threadLocalContext.set(this);
+        }
+    }
+
+    /**
+     * Waits for the associated EvalThread to finish.
+     *
+     * @throws InterruptedException
+     */
+    public void joinThread() throws InterruptedException {
+        EvalThread t = this.evalThread;
+        if (t == null) {
+            throw RError.error(RError.NO_NODE, RError.Message.GENERIC, "no eval thread in a given context");
+        }
+        this.evalThread = null;
+        t.join();
+    }
+
+    private static final Assumption singleContextAssumption = Truffle.getRuntime().createAssumption("single RContext");
+    @CompilationFinal private static RContext singleContext;
+
+    private final Env env;
+    private final HashMap<String, TruffleObject> exportedSymbols = new HashMap<>();
+
+    /**
+     * The set of classes for which the context manages context-specific state, and their state. We
+     * could do this more dynamically with a registration process, perhaps driven by an annotation
+     * processor, but the set is relatively small, so we just enumerate them here.
+     */
+    public final REnvVars stateREnvVars;
+    public final RProfile stateRProfile;
+    public final ROptions.ContextStateImpl stateROptions;
+    public final REnvironment.ContextStateImpl stateREnvironment;
+    public final RErrorHandling.ContextStateImpl stateRErrorHandling;
+    public final ConnectionSupport.ContextStateImpl stateRConnection;
+    public final StdConnections.ContextStateImpl stateStdConnections;
+    public final RRNG.ContextStateImpl stateRNG;
+    public final ContextState stateRFFI;
+    public final RSerialize.ContextStateImpl stateRSerialize;
+
+    private ContextState[] contextStates() {
+        return new ContextState[]{stateREnvVars, stateRProfile, stateROptions, stateREnvironment, stateRErrorHandling, stateRConnection, stateStdConnections, stateRNG, stateRFFI, stateRSerialize};
+    }
+
+    private RContext(Env env) {
+        if (tempInitializingContextInfo == null) {
+            this.info = ContextInfo.create(RCmdOptions.parseArguments(Client.R, new String[0]), ContextKind.SHARE_NOTHING, null, new DefaultConsoleHandler(env));
+        } else {
+            this.info = tempInitializingContextInfo;
+            lastContext = this;
+        }
+
+        this.env = env;
+        if (info.getConsoleHandler() == null) {
+            throw Utils.fail("no console handler set");
+        }
+
+        if (singleContextAssumption.isValid()) {
+            if (singleContext == null) {
+                singleContext = this;
+            } else {
+                singleContext = null;
+                singleContextAssumption.invalidate();
+            }
+        }
+        engine = RContext.getRRuntimeASTAccess().createEngine(this);
+
+        /*
+         * Activate the context by attaching the current thread and initializing the {@link
+         * ContextState} objects. Note that we attach the thread before creating the new context
+         * state. This means that code that accesses the state through this interface will receive a
+         * {@code null} value. Access to the parent state is available through the {@link RContext}
+         * argument passed to the newContext methods. It might be better to attach the thread after
+         * state creation but it is a finely balanced decision and risks incorrectly accessing the
+         * parent state.
+         */
+        assert !active;
+        active = true;
+        attachThread();
+        stateREnvVars = REnvVars.newContext(this);
+        stateRProfile = RProfile.newContext(this, stateREnvVars);
+        stateROptions = ROptions.ContextStateImpl.newContext(this, stateREnvVars);
+        stateREnvironment = REnvironment.ContextStateImpl.newContext(this);
+        stateRErrorHandling = RErrorHandling.ContextStateImpl.newContext(this);
+        stateRConnection = ConnectionSupport.ContextStateImpl.newContext(this);
+        stateStdConnections = StdConnections.ContextStateImpl.newContext(this);
+        stateRNG = RRNG.ContextStateImpl.newContext(this);
+        stateRFFI = RFFIContextStateFactory.newContext(this);
+        stateRSerialize = RSerialize.ContextStateImpl.newContext(this);
+        engine.activate(stateREnvironment);
+
+        if (info.getKind() == ContextKind.SHARE_PARENT_RW) {
+            if (info.getParent().sharedChild != null) {
+                throw RError.error(RError.NO_NODE, RError.Message.GENERIC, "can't have multiple active SHARED_PARENT_RW contexts");
+            }
+            info.getParent().sharedChild = this;
+        }
+        for (ContextState state : contextStates()) {
+            assert state != null;
+        }
+    }
+
+    /**
+     * Create a context with the given configuration.
+     */
+    public static RContext create(Env env) {
+        return new RContext(env);
+    }
+
+    /**
+     * Destroy this context.
+     */
+    public void destroy() {
+        for (ContextState state : contextStates()) {
+            state.beforeDestroy(this);
+        }
+        if (info.getKind() == ContextKind.SHARE_PARENT_RW) {
+            info.getParent().sharedChild = null;
+        }
+        if (info.getParent() == null) {
+            threadLocalContext.set(null);
+        } else {
+            threadLocalContext.set(info.getParent());
+        }
+    }
+
+    public RContext getParent() {
+        return info.getParent();
+    }
+
+    public Env getEnv() {
+        return env;
+    }
+
+    public ContextKind getKind() {
+        return info.getKind();
+    }
+
+    @TruffleBoundary
+    private static RContext getInstanceInternal() {
+        RContext result = threadLocalContext.get();
+        assert result != null;
+        assert result.active;
+        return result;
+    }
+
+    public static RContext getInstance() {
+        RContext context = singleContext;
+        if (context != null) {
+            try {
+                singleContextAssumption.check();
+                return context;
+            } catch (InvalidAssumptionException e) {
+                // fallback to slow case
+            }
+        }
+
+        Thread current = Thread.currentThread();
+        if (current instanceof ContextThread) {
+            context = ((ContextThread) current).context;
+            assert context != null;
+            return context;
+        } else {
+            return getInstanceInternal();
+        }
+    }
+
+    /**
+     * Access to the engine, when an {@link RContext} object is available, and/or when {@code this}
+     * context is not active.
+     */
+    public Engine getThisEngine() {
+        return engine;
+    }
+
+    public boolean isVisible() {
+        return resultVisible;
+    }
+
+    public void setVisible(boolean v) {
+        resultVisible = v;
+    }
+
+    public boolean isMethodTableDispatchOn() {
+        return methodTableDispatchOn;
+    }
+
+    public void setMethodTableDispatchOn(boolean on) {
+        methodTableDispatchOn = on;
+    }
+
+    public boolean isInteractive() {
+        return info.getConsoleHandler().isInteractive();
+    }
+
+    public static boolean isIgnoringVisibility() {
+        return ignoreVisibility;
+    }
+
+    public ConsoleHandler getConsoleHandler() {
+        return info.getConsoleHandler();
+    }
+
+    /**
+     * This is a static property of the implementation and not context-specific.
+     */
+    public static RRuntimeASTAccess getRRuntimeASTAccess() {
+        return runtimeASTAccess;
+    }
+
+    /**
+     * Is {@code name} a builtin function (but not a {@link RBuiltinKind#INTERNAL}?
+     */
+    public static boolean isPrimitiveBuiltin(String name) {
+        return builtinLookup.isPrimitiveBuiltin(name);
+    }
+
+    /**
+     * Return the {@link RFunction} for the builtin {@code name}.
+     */
+    @TruffleBoundary
+    public static RFunction lookupBuiltin(String name) {
+        return builtinLookup.lookupBuiltin(name);
+    }
+
+    /**
+     * Returns the descriptor for the builtin with the given name. This does not cause an RFunction
+     * to be created.
+     */
+    public static RBuiltinDescriptor lookupBuiltinDescriptor(String name) {
+        return builtinLookup.lookupBuiltinDescriptor(name);
+    }
+
+    public RCmdOptions getOptions() {
+        return info.getOptions();
+    }
+
+    @Override
+    public String toString() {
+        return "context: " + info.getId();
+    }
+
+    /*
+     * static functions necessary in code where the context is only implicit in the thread(s)
+     * running an evaluation
+     */
+
+    public static Engine getEngine() {
+        return RContext.getInstance().engine;
+    }
+
+    public void setLoadingBase(boolean b) {
+        loadingBase = b;
+    }
+
+    public boolean getLoadingBase() {
+        return loadingBase;
+    }
+
+    public Map<String, TruffleObject> getExportedSymbols() {
+        return exportedSymbols;
+    }
+
+    public TimeZone getSystemTimeZone() {
+        return info.getSystemTimeZone();
+    }
+
+    /*
+     * TODO: this fields are used to convey initialization information from outside TruffleVM to
+     * RContext. need to be replaced with a mechanism provided by TruffleVM once this is available.
+     */
+    public static ContextInfo tempInitializingContextInfo;
+    private static RContext lastContext;
+    private static final Map<TruffleVM, RContext> truffleVMContexts = new HashMap<>();
+
+    // TODO: destroying a TruffleVM should be handled in TruffleVM itself
+    public static void destroyContext(TruffleVM vm) {
+        truffleVMContexts.get(vm).destroy();
+    }
+
+    public static void associate(TruffleVM vm) {
+        assert lastContext != null;
+        truffleVMContexts.put(vm, lastContext);
+        lastContext = null;
+        tempInitializingContextInfo = null;
+    }
+
+    public static RContext fromTruffleVM(TruffleVM vm) {
+        return truffleVMContexts.get(vm);
+    }
+}
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RComplexVector.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RComplexVector.java
index 24317054af712640643d2a0d93500373aa282e9a..af6b20276f821086b52e3c438bf205534ddf307d 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RComplexVector.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RComplexVector.java
@@ -29,7 +29,7 @@ import com.oracle.truffle.r.runtime.data.closures.*;
 import com.oracle.truffle.r.runtime.data.model.*;
 import com.oracle.truffle.r.runtime.ops.na.*;
 
-public final class RComplexVector extends RVector implements RAbstractComplexVector, RAccessibleStore<double[]> {
+public final class RComplexVector extends RVector implements RAbstractComplexVector {
 
     public static final RStringVector implicitClassHeader = RDataFactory.createStringVectorFromScalar(RType.Complex.getName());
 
@@ -74,6 +74,24 @@ public final class RComplexVector extends RVector implements RAbstractComplexVec
         }
     }
 
+    public void setDataAt(Object store, int index, RComplex value) {
+        assert data == store;
+        double[] array = (double[]) store;
+        array[index << 1] = value.getRealPart();
+        array[(index << 1) + 1] = value.getImaginaryPart();
+    }
+
+    public RComplex getDataAt(Object store, int i) {
+        assert data == store;
+        double[] doubleStore = (double[]) store;
+        int index = i << 1;
+        return RDataFactory.createComplex(doubleStore[index], doubleStore[index + 1]);
+    }
+
+    public RComplex getDataAt(int i) {
+        return getDataAt(data, i);
+    }
+
     @Override
     public String toString() {
         return toString(i -> RRuntime.complexToString(getDataAt(i)));
@@ -91,17 +109,6 @@ public final class RComplexVector extends RVector implements RAbstractComplexVec
         return true;
     }
 
-    public RAbstractVector setDataAt(int index, RComplex value) {
-        data[index << 1] = value.getRealPart();
-        data[(index << 1) + 1] = value.getImaginaryPart();
-        return this;
-    }
-
-    public RComplex getDataAt(int i) {
-        int index = i << 1;
-        return RDataFactory.createComplex(data[index], data[index + 1]);
-    }
-
     public double[] getDataCopy() {
         double[] copy = new double[data.length];
         System.arraycopy(data, 0, copy, 0, data.length);
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDataFrame.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDataFrame.java
index 51f5b921768ebb4375349479ae889304166b1bb8..dab55fab2e1249ca4e023351df48c6191b1316c9 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDataFrame.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDataFrame.java
@@ -154,8 +154,7 @@ public final class RDataFrame implements RShareable, RAbstractContainer {
 
     @Override
     public RList getDimNames(RAttributeProfiles attrProfiles) {
-        RInternalError.unimplemented("data frame's dimnames needs to be obtained using builtins");
-        return null;
+        return vector.getDimNames(attrProfiles);
     }
 
     @Override
@@ -226,4 +225,9 @@ public final class RDataFrame implements RShareable, RAbstractContainer {
         return vector.setClassAttr(classAttr, convertToInt);
     }
 
+    @Override
+    public String toString() {
+        return "RDataFrame [vector=" + vector + ", length=" + length + "]";
+    }
+
 }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDoubleVector.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDoubleVector.java
index 53dc236f9121f5f1f8385cc66370dd7769d1a5a6..0a9ed7a4f52ebcf044b46b3c59f2e2fe58987ef3 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDoubleVector.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDoubleVector.java
@@ -29,7 +29,7 @@ import com.oracle.truffle.r.runtime.data.closures.*;
 import com.oracle.truffle.r.runtime.data.model.*;
 import com.oracle.truffle.r.runtime.ops.na.*;
 
-public final class RDoubleVector extends RVector implements RAbstractDoubleVector, RAccessibleStore<double[]> {
+public final class RDoubleVector extends RVector implements RAbstractDoubleVector {
 
     public static final RStringVector implicitClassHeader = RDataFactory.createStringVectorFromScalar(RType.Numeric.getName());
 
@@ -73,6 +73,16 @@ public final class RDoubleVector extends RVector implements RAbstractDoubleVecto
         return data;
     }
 
+    public void setDataAt(Object store, int index, double value) {
+        assert data == store;
+        ((double[]) store)[index] = value;
+    }
+
+    public double getDataAt(Object store, int index) {
+        assert data == store;
+        return ((double[]) store)[index];
+    }
+
     public RDoubleVector copyResetData(double[] newData) {
         boolean isComplete = true;
         for (int i = 0; i < newData.length; i++) {
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RExpression.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RExpression.java
index 917cac281c727428c65cd3c9222061c6e108f005..83738de2a4f1facbb40b84997c7ee3b6e24a1420 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RExpression.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RExpression.java
@@ -204,4 +204,9 @@ public class RExpression implements RShareable, RAbstractContainer {
         return data.setClassAttr(classAttr, convertToInt);
     }
 
+    @Override
+    public String toString() {
+        return String.format("RExpression(data=%s)", data);
+    }
+
 }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RFactor.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RFactor.java
index 419f3178795b73f77f8ff44bcc770df04859ed88..36f05605a20550ccd9655de6a6359943d3a79e28 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RFactor.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RFactor.java
@@ -36,6 +36,10 @@ public final class RFactor implements RShareable, RAbstractContainer {
         this.ordered = ordered;
     }
 
+    public int[] getInternalStore() {
+        return vector.getInternalStore();
+    }
+
     public RType getRType() {
         return RType.Integer;
     }
@@ -221,4 +225,5 @@ public final class RFactor implements RShareable, RAbstractContainer {
         RVector levels = getLevels(attrProfiles);
         return levels == null ? 0 : levels.getLength();
     }
+
 }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RFunction.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RFunction.java
index 2e71e9e77a0de8e4c193d35c8b11da3d8a9dbcb6..79271368c0edb9ce33fa50b75e069ec645286312 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RFunction.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RFunction.java
@@ -24,8 +24,10 @@ package com.oracle.truffle.r.runtime.data;
 
 import com.oracle.truffle.api.*;
 import com.oracle.truffle.api.frame.*;
+import com.oracle.truffle.api.interop.*;
 import com.oracle.truffle.api.nodes.*;
 import com.oracle.truffle.r.runtime.*;
+import com.oracle.truffle.r.runtime.context.*;
 
 /**
  * An instance of {@link RFunction} represents a function defined in R. The properties of a function
@@ -40,7 +42,7 @@ import com.oracle.truffle.r.runtime.*;
  * {@link #enclosingFrame}.
  * </ul>
  */
-public final class RFunction extends RAttributeStorage implements RTypedValue {
+public final class RFunction extends RAttributeStorage implements RTypedValue, TruffleObject {
 
     private String name;
     private final RootCallTarget target;
@@ -111,4 +113,8 @@ public final class RFunction extends RAttributeStorage implements RTypedValue {
         this.name = name;
         RContext.getRRuntimeASTAccess().setFunctionName(getRootNode(), name);
     }
+
+    public ForeignAccess getForeignAccess() {
+        return RContext.getEngine().getForeignAccess(this);
+    }
 }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RIntVector.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RIntVector.java
index 8b4c4c9eb8cf1ae5e20a12c62504b5deb403e050..f5a1de57906b3da71b428059ea6f72e948cdee37 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RIntVector.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RIntVector.java
@@ -29,7 +29,7 @@ import com.oracle.truffle.r.runtime.data.closures.*;
 import com.oracle.truffle.r.runtime.data.model.*;
 import com.oracle.truffle.r.runtime.ops.na.*;
 
-public final class RIntVector extends RVector implements RAbstractIntVector, RAccessibleStore<int[]> {
+public final class RIntVector extends RVector implements RAbstractIntVector {
 
     public static final RStringVector implicitClassHeader = RDataFactory.createStringVectorFromScalar(RType.Integer.getName());
 
@@ -72,6 +72,17 @@ public final class RIntVector extends RVector implements RAbstractIntVector, RAc
         return data[index];
     }
 
+    public int getDataAt(Object store, int index) {
+        assert data == store;
+        return ((int[]) store)[index];
+    }
+
+    @Override
+    public void setDataAt(Object store, int index, int value) {
+        assert data == store;
+        ((int[]) store)[index] = value;
+    }
+
     @Override
     protected RIntVector internalCopy() {
         return new RIntVector(Arrays.copyOf(data, data.length), isComplete(), null);
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RLanguage.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RLanguage.java
index 897811278fea534b6942adb7d4d0550c2def9b72..d1add494cc24afb06a6b3a89de9c865faf6524be 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RLanguage.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RLanguage.java
@@ -24,6 +24,7 @@ package com.oracle.truffle.r.runtime.data;
 
 import com.oracle.truffle.api.CompilerDirectives.ValueType;
 import com.oracle.truffle.r.runtime.*;
+import com.oracle.truffle.r.runtime.context.*;
 import com.oracle.truffle.r.runtime.data.model.*;
 import com.oracle.truffle.r.runtime.nodes.*;
 
@@ -241,4 +242,9 @@ public class RLanguage extends RLanguageRep implements RAbstractContainer, RAttr
         refCount--;
     }
 
+    @Override
+    public String toString() {
+        return String.format("RLanguage(rep=%s)", getRep());
+    }
+
 }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RList.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RList.java
index d8af43553f6e618ce05dedc3098ab9efa9a0e70b..b1b8231dcae98c5f931b6f6bed6ae58c22193981 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RList.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RList.java
@@ -25,9 +25,8 @@ package com.oracle.truffle.r.runtime.data;
 import java.util.*;
 
 import com.oracle.truffle.r.runtime.*;
-import com.oracle.truffle.r.runtime.data.model.*;
 
-public final class RList extends RListBase implements RAbstractVector, RGPBits {
+public final class RList extends RListBase implements RGPBits {
 
     private static final RStringVector implicitClassHeader = RDataFactory.createStringVectorFromScalar(RType.List.getName());
 
@@ -38,8 +37,9 @@ public final class RList extends RListBase implements RAbstractVector, RGPBits {
         super(data, dims, names);
     }
 
-    public RType getRType() {
-        return RType.List;
+    @Override
+    public RList materialize() {
+        return this;
     }
 
     @Override
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RListBase.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RListBase.java
index 9d9c948089e2845e8a4d656c078aeee7d153be1f..852c5ae1508227bfa7838f0f762ccc4054362ea6 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RListBase.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RListBase.java
@@ -29,7 +29,7 @@ import com.oracle.truffle.r.runtime.data.model.*;
 import com.oracle.truffle.r.runtime.ops.na.*;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 
-public abstract class RListBase extends RVector implements RAbstractVector {
+public abstract class RListBase extends RVector implements RAbstractListVector {
 
     protected final Object[] data;
 
@@ -44,6 +44,22 @@ public abstract class RListBase extends RVector implements RAbstractVector {
         return data.length;
     }
 
+    public Object[] getInternalStore() {
+        return data;
+    }
+
+    @Override
+    public Object getDataAtAsObject(Object store, int index) {
+        assert store == data;
+        return ((Object[]) store)[index];
+    }
+
+    @Override
+    public void setDataAt(Object store, int index, Object value) {
+        assert store == data;
+        ((Object[]) store)[index] = value;
+    }
+
     @Override
     public String toString() {
         return toString(i -> RRuntime.toString(getDataAt(i)));
@@ -138,11 +154,6 @@ public abstract class RListBase extends RVector implements RAbstractVector {
         }
     }
 
-    @Override
-    public final RVector materialize() {
-        return this;
-    }
-
     @Override
     public final Object getDataAtAsObject(int index) {
         return this.getDataAt(index);
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RLogicalVector.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RLogicalVector.java
index 47c423143384a6aa3150559b8b977c0f3c8648a7..596334df9a89ed860132837fc9474211db58c595 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RLogicalVector.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RLogicalVector.java
@@ -29,7 +29,7 @@ import com.oracle.truffle.r.runtime.data.closures.*;
 import com.oracle.truffle.r.runtime.data.model.*;
 import com.oracle.truffle.r.runtime.ops.na.*;
 
-public final class RLogicalVector extends RVector implements RAbstractLogicalVector, RAccessibleStore<byte[]> {
+public final class RLogicalVector extends RVector implements RAbstractLogicalVector {
 
     public static final RStringVector implicitClassHeader = RDataFactory.createStringVectorFromScalar(RType.Logical.getName());
 
@@ -70,6 +70,18 @@ public final class RLogicalVector extends RVector implements RAbstractLogicalVec
         return data;
     }
 
+    @Override
+    public void setDataAt(Object store, int index, byte value) {
+        assert data == store;
+        ((byte[]) store)[index] = value;
+    }
+
+    @Override
+    public byte getDataAt(Object store, int index) {
+        assert data == store;
+        return ((byte[]) store)[index];
+    }
+
     @Override
     protected RLogicalVector internalCopy() {
         return new RLogicalVector(Arrays.copyOf(data, data.length), isComplete(), null);
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RPromise.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RPromise.java
index 61f9c8c92689fb8fd12bb0fbd77eaa1872d41939..0944e9a4f6665608b7415634e70d0dba55406f9c 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RPromise.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RPromise.java
@@ -28,6 +28,7 @@ import com.oracle.truffle.api.CompilerDirectives.ValueType;
 import com.oracle.truffle.api.frame.FrameInstance.FrameAccess;
 import com.oracle.truffle.api.frame.*;
 import com.oracle.truffle.r.runtime.*;
+import com.oracle.truffle.r.runtime.context.*;
 import com.oracle.truffle.r.runtime.nodes.*;
 
 /**
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RRaw.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RRaw.java
index 63336ae9fa41ba4bfb861ab4a7ccd6b1bfa5d43f..b65ea90dabfc91a4aea2f020bec828abf9faade5 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RRaw.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RRaw.java
@@ -77,6 +77,19 @@ public final class RRaw extends RScalarVector implements RAbstractRawVector {
         return value;
     }
 
+    @Override
+    public boolean equals(Object obj) {
+        if (obj instanceof RRaw) {
+            return value == ((RRaw) obj).value;
+        }
+        return super.equals(obj);
+    }
+
+    @Override
+    public int hashCode() {
+        return value;
+    }
+
     @Override
     public String toString() {
         CompilerAsserts.neverPartOfCompilation();
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RRawVector.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RRawVector.java
index 13c1c48c2cd71e19b56db4ca6f1a8b8586ead7c4..e478f89a76fdc1ee3a8c3ff5aafa208513dea5e5 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RRawVector.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RRawVector.java
@@ -29,7 +29,7 @@ import com.oracle.truffle.r.runtime.data.closures.*;
 import com.oracle.truffle.r.runtime.data.model.*;
 import com.oracle.truffle.r.runtime.ops.na.*;
 
-public final class RRawVector extends RVector implements RAbstractRawVector, RAccessibleStore<byte[]> {
+public final class RRawVector extends RVector implements RAbstractRawVector {
 
     public static final RStringVector implicitClassHeader = RDataFactory.createStringVectorFromScalar(RType.Raw.getName());
 
@@ -66,10 +66,22 @@ public final class RRawVector extends RVector implements RAbstractRawVector, RAc
         return data[index];
     }
 
+    @Override
+    public byte getRawDataAt(Object store, int index) {
+        assert data == store;
+        return ((byte[]) store)[index];
+    }
+
     public byte[] getInternalStore() {
         return data;
     }
 
+    @Override
+    public void setRawDataAt(Object store, int index, byte value) {
+        assert data == store;
+        ((byte[]) store)[index] = value;
+    }
+
     @Override
     protected RRawVector internalCopy() {
         return new RRawVector(Arrays.copyOf(data, data.length), null);
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RString.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RString.java
index 38307480a0ac7ffe3f0ab19f56e589650c71cebd..fd07f8187eaf52a935909185d16b5567c59d663b 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RString.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RString.java
@@ -66,11 +66,11 @@ public final class RString extends RScalarVector implements RAbstractStringVecto
     }
 
     public RStringVector materialize() {
-        return RDataFactory.createStringVector(getValue());
+        return RDataFactory.createStringVector(new String[]{getValue()}, isComplete());
     }
 
     @Override
     public boolean isNA() {
-        return false;
+        return !RRuntime.isComplete(value);
     }
 }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RStringVector.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RStringVector.java
index 012e629d4b3648f7d84672ae763fdb811edc3474..171a295943d5785896936ad45c4bbe8149bc5da4 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RStringVector.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RStringVector.java
@@ -25,6 +25,7 @@ package com.oracle.truffle.r.runtime.data;
 import java.util.*;
 
 import com.oracle.truffle.r.runtime.*;
+import com.oracle.truffle.r.runtime.data.closures.*;
 import com.oracle.truffle.r.runtime.data.model.*;
 import com.oracle.truffle.r.runtime.ops.na.*;
 
@@ -44,6 +45,33 @@ public final class RStringVector extends RVector implements RAbstractStringVecto
         this(data, complete, dims, null);
     }
 
+    public RAbstractVector castSafe(RType type) {
+        switch (type) {
+            case Character:
+                return this;
+            case List:
+                return RClosures.createAbstractVectorToListVector(this);
+            default:
+                return null;
+        }
+    }
+
+    public String[] getInternalStore() {
+        return data;
+    }
+
+    @Override
+    public void setDataAt(Object store, int index, String value) {
+        assert data == store;
+        ((String[]) store)[index] = value;
+    }
+
+    @Override
+    public String getDataAt(Object store, int index) {
+        assert data == store;
+        return ((String[]) store)[index];
+    }
+
     @Override
     protected RStringVector internalCopy() {
         return new RStringVector(Arrays.copyOf(data, data.length), isComplete(), null);
@@ -96,7 +124,13 @@ public final class RStringVector extends RVector implements RAbstractStringVecto
 
     @Override
     protected boolean internalVerify() {
-        // TODO: Implement String + NA
+        if (isComplete()) {
+            for (String b : data) {
+                if (b == RRuntime.STRING_NA) {
+                    return false;
+                }
+            }
+        }
         return true;
     }
 
@@ -153,7 +187,7 @@ public final class RStringVector extends RVector implements RAbstractStringVecto
         return RDataFactory.createStringVector(copyResizedData(size, fillNA ? RRuntime.STRING_NA : null), isComplete);
     }
 
-    RStringVector resizeWithEmpty(int size) {
+    public RStringVector resizeWithEmpty(int size) {
         return RDataFactory.createStringVector(createResizedData(size, RRuntime.NAMES_ATTR_EMPTY_VALUE), isComplete());
     }
 
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/closures/RToStringVectorClosure.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/closures/RToStringVectorClosure.java
index 0de20b825ecee116df0de10f875a1fc524ad8926..8d78bf828e00571268c3506cca5ac541328a5b4a 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/closures/RToStringVectorClosure.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/closures/RToStringVectorClosure.java
@@ -42,7 +42,7 @@ public abstract class RToStringVectorClosure extends RToVectorClosure implements
             String data = getDataAt(i);
             result[i] = data;
         }
-        return RDataFactory.createStringVector(result, vector.isComplete());
+        return RDataFactory.createStringVector(result, vector.isComplete(), getDimensions(), getNames(null));
     }
 
     @Override
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/closures/RToVectorClosure.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/closures/RToVectorClosure.java
index ff2a29d8b762fcf963126a96944a4ffa4c74ab7b..0a3c9ade1e94df5ec8b39729985344688146a79f 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/closures/RToVectorClosure.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/closures/RToVectorClosure.java
@@ -38,6 +38,10 @@ public abstract class RToVectorClosure implements RAbstractVector {
         return vector.getLength();
     }
 
+    public Void getInternalStore() {
+        return null;
+    }
+
     @Override
     public final RAbstractContainer resize(int size) {
         return vector.resize(size);
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/model/RAbstractComplexVector.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/model/RAbstractComplexVector.java
index f064fbe5413a18a353c70dc2c583d94203b520eb..1abd29300cf169ffd4acbdeb4c69d62178689b49 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/model/RAbstractComplexVector.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/model/RAbstractComplexVector.java
@@ -34,8 +34,21 @@ public interface RAbstractComplexVector extends RAbstractVector {
 
     RComplex getDataAt(int index);
 
+    default RComplex getDataAt(@SuppressWarnings("unused") Object store, int index) {
+        return getDataAt(index);
+    }
+
     RComplexVector materialize();
 
+    @SuppressWarnings("unused")
+    default void setDataAt(Object store, int index, RComplex value) {
+        throw new UnsupportedOperationException();
+    }
+
+    default void setNA(Object store, int index) {
+        setDataAt(store, index, RComplex.NA);
+    }
+
     default boolean checkCompleteness() {
         for (int i = 0; i < getLength(); i++) {
             if (RRuntime.isNA(getDataAt(i))) {
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/model/RAbstractContainer.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/model/RAbstractContainer.java
index 148785b2db098d7d7be8a6b862de75af19b425ec..f5a12ad39d430efa5a226f25e8860fabd8a7f42b 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/model/RAbstractContainer.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/model/RAbstractContainer.java
@@ -46,6 +46,14 @@ public interface RAbstractContainer extends RAttributable, RTypedValue {
 
     Object getDataAtAsObject(int index);
 
+    default Object getDataAtAsObject(@SuppressWarnings("unused") Object store, int index) {
+        return getDataAtAsObject(index);
+    }
+
+    default Object getInternalStore() {
+        return null;
+    }
+
     RStringVector getNames(RAttributeProfiles attrProfiles);
 
     void setNames(RStringVector newNames);
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/model/RAbstractDoubleVector.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/model/RAbstractDoubleVector.java
index 0a1ef6604b5c4634995e1ccb1b91b9f42794986f..83b9821a6068186fcaf9c082edc4198affd0493d 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/model/RAbstractDoubleVector.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/model/RAbstractDoubleVector.java
@@ -32,8 +32,21 @@ public interface RAbstractDoubleVector extends RAbstractVector {
         return getDataAt(index);
     }
 
+    default double getDataAt(@SuppressWarnings("unused") Object store, int index) {
+        return getDataAt(index);
+    }
+
     double getDataAt(int index);
 
+    @SuppressWarnings("unused")
+    default void setDataAt(Object store, int index, double value) {
+        throw new UnsupportedOperationException();
+    }
+
+    default void setNA(Object store, int index) {
+        setDataAt(store, index, RRuntime.DOUBLE_NA);
+    }
+
     RDoubleVector materialize();
 
     default boolean checkCompleteness() {
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/model/RAbstractIntVector.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/model/RAbstractIntVector.java
index ce8195278734c36440cc59ca2b44d2eed1e20766..8bc658e4072af3e538425a6f0dfb70f2c472f20b 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/model/RAbstractIntVector.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/model/RAbstractIntVector.java
@@ -32,8 +32,21 @@ public interface RAbstractIntVector extends RAbstractVector {
         return getDataAt(index);
     }
 
+    default int getDataAt(@SuppressWarnings("unused") Object store, int index) {
+        return getDataAt(index);
+    }
+
     int getDataAt(int index);
 
+    @SuppressWarnings("unused")
+    default void setDataAt(Object store, int index, int value) {
+        throw new UnsupportedOperationException();
+    }
+
+    default void setNA(Object store, int index) {
+        setDataAt(store, index, RRuntime.INT_NA);
+    }
+
     RIntVector materialize();
 
     default boolean checkCompleteness() {
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/model/RAbstractListVector.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/model/RAbstractListVector.java
index 1b657ab5838f3baad84d47d4700fe351e3c01f0c..e577a15db7ca17fa9b7e45d95c995f12af5272fe 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/model/RAbstractListVector.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/model/RAbstractListVector.java
@@ -30,6 +30,10 @@ public interface RAbstractListVector extends RAbstractVector {
     @Override
     Object getDataAtAsObject(int index);
 
+    default Object getDataAtAsObject(Object store, int i) {
+        return getDataAtAsObject(i);
+    }
+
     RList materialize();
 
     default boolean checkCompleteness() {
@@ -44,4 +48,13 @@ public interface RAbstractListVector extends RAbstractVector {
         return Object.class;
     }
 
+    @SuppressWarnings("unused")
+    default void setDataAt(Object store, int index, Object value) {
+        throw new UnsupportedOperationException();
+    }
+
+    default void setNA(Object store, int index) {
+        setDataAt(store, index, RNull.instance);
+    }
+
 }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/model/RAbstractLogicalVector.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/model/RAbstractLogicalVector.java
index 6510c71fec605664f5ef200a274417b8be0a3eaa..0327514ee99898f5ff4fefa5f4ac79c2e6aeb6bf 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/model/RAbstractLogicalVector.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/model/RAbstractLogicalVector.java
@@ -32,8 +32,21 @@ public interface RAbstractLogicalVector extends RAbstractVector {
         return getDataAt(index);
     }
 
+    default byte getDataAt(@SuppressWarnings("unused") Object store, int index) {
+        return getDataAt(index);
+    }
+
     byte getDataAt(int index);
 
+    @SuppressWarnings("unused")
+    default void setDataAt(Object store, int index, byte value) {
+        throw new UnsupportedOperationException();
+    }
+
+    default void setNA(Object store, int index) {
+        setDataAt(store, index, RRuntime.LOGICAL_NA);
+    }
+
     RLogicalVector materialize();
 
     default boolean checkCompleteness() {
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/model/RAbstractRawVector.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/model/RAbstractRawVector.java
index 398ed92d27adb163a55bda7e52d10576ea782c36..126ec1dfadec0d78b85e9f9809851e6d4b040e75 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/model/RAbstractRawVector.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/model/RAbstractRawVector.java
@@ -32,6 +32,18 @@ public interface RAbstractRawVector extends RAbstractVector {
         return getDataAt(index);
     }
 
+    default byte getRawDataAt(@SuppressWarnings("unused") Object store, int index) {
+        return getRawDataAt(index);
+    }
+
+    @SuppressWarnings("unused")
+    default void setRawDataAt(Object store, int index, byte value) {
+        throw new UnsupportedOperationException();
+    }
+
+    default void setNA(Object store, int index) {
+    }
+
     RRaw getDataAt(int index);
 
     byte getRawDataAt(int index);
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/model/RAbstractStringVector.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/model/RAbstractStringVector.java
index dbe553212e7cad87cf12a565892fa5bf779244da..a30351c95161d9918c6af0f577eecdbf65888f0e 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/model/RAbstractStringVector.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/model/RAbstractStringVector.java
@@ -32,6 +32,19 @@ public interface RAbstractStringVector extends RAbstractVector {
         return getDataAt(index);
     }
 
+    default String getDataAt(@SuppressWarnings("unused") Object store, int index) {
+        return getDataAt(index);
+    }
+
+    @SuppressWarnings("unused")
+    default void setDataAt(Object store, int index, String value) {
+        throw new UnsupportedOperationException();
+    }
+
+    default void setNA(Object store, int index) {
+        setDataAt(store, index, RRuntime.STRING_NA);
+    }
+
     String getDataAt(int index);
 
     RStringVector materialize();
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/model/RAbstractVector.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/model/RAbstractVector.java
index 13e565df325d197d2324bd9ff197d14387b92a57..17a9145be54778440c2ba8fc118dd5c28d442a1d 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/model/RAbstractVector.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/model/RAbstractVector.java
@@ -22,10 +22,12 @@
  */
 package com.oracle.truffle.r.runtime.data.model;
 
+import com.oracle.truffle.api.interop.*;
 import com.oracle.truffle.r.runtime.*;
+import com.oracle.truffle.r.runtime.context.*;
 import com.oracle.truffle.r.runtime.data.*;
 
-public interface RAbstractVector extends RAbstractContainer {
+public interface RAbstractVector extends RAbstractContainer, TruffleObject {
 
     /**
      * Creates a copy of the vector. This copies all of the contained data as well. If the data in
@@ -72,4 +74,9 @@ public interface RAbstractVector extends RAbstractContainer {
 
     void setComplete(boolean complete);
 
+    void setNA(Object store, int index);
+
+    default ForeignAccess getForeignAccess() {
+        return RContext.getEngine().getForeignAccess(this);
+    }
 }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/env/REnvironment.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/env/REnvironment.java
index 581553fd19a76c1e990ffd6eff49bf597979d1b6..2be05154e78ba3300486103691e224ed10557488 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/env/REnvironment.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/env/REnvironment.java
@@ -30,6 +30,7 @@ import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.frame.*;
 import com.oracle.truffle.r.runtime.*;
 import com.oracle.truffle.r.runtime.RError.RErrorException;
+import com.oracle.truffle.r.runtime.context.*;
 import com.oracle.truffle.r.runtime.data.*;
 import com.oracle.truffle.r.runtime.env.frame.*;
 
@@ -80,8 +81,8 @@ import com.oracle.truffle.r.runtime.env.frame.*;
  * Multi-tenancy (multiple {@link RContext}s).
  * <p>
  * The logic for implementing the three different forms of
- * {@link com.oracle.truffle.r.runtime.RContext.Kind} is encapsulated in the {@link #createContext}
- * method.
+ * {@link com.oracle.truffle.r.runtime.context.RContext.ContextKind} is encapsulated in the
+ * {@link #createContext} method.
  */
 public abstract class REnvironment extends RAttributeStorage implements RTypedValue {
 
@@ -92,22 +93,7 @@ public abstract class REnvironment extends RAttributeStorage implements RTypedVa
         return implicitClass;
     }
 
-    public interface ContextState extends RContext.ContextState {
-        MaterializedFrame getGlobalFrame();
-
-        REnvironment getGlobalEnv();
-
-        Base getBaseEnv();
-
-        REnvironment getBaseNamespace();
-
-        REnvironment getNamespaceRegistry();
-
-        REnvironment.SearchPath getSearchPath();
-
-    }
-
-    private static class ContextStateImpl implements ContextState {
+    public static class ContextStateImpl implements RContext.ContextState {
         private SearchPath searchPath;
         private final MaterializedFrame globalFrame;
         private Base baseEnv;
@@ -161,21 +147,13 @@ public abstract class REnvironment extends RAttributeStorage implements RTypedVa
             this.namespaceRegistry = namespaceRegistry;
         }
 
-    }
-
-    /**
-     * Since {@REnvironment} is a Truffle value and already has non-zero-arg
-     * constructors, we define this class as the mechanism for creating the context-specific state.
-     */
-    public static class ClassStateFactory implements RContext.StateFactory {
         @Override
-        public ContextState newContext(RContext context, Object... objects) {
-            return createContext(context, (MaterializedFrame) objects[0]);
+        public void beforeDestroy(RContext context) {
+            beforeDestroyContext(context, this);
         }
 
-        @Override
-        public void beforeDestroy(RContext context, RContext.ContextState state) {
-            beforeDestroyContext(context, state);
+        public static ContextStateImpl newContext(RContext context) {
+            return createContext(context, RRuntime.createNonFunctionFrame().materialize());
         }
     }
 
@@ -247,7 +225,7 @@ public abstract class REnvironment extends RAttributeStorage implements RTypedVa
      * Value returned by {@code globalenv()}.
      */
     public static REnvironment globalEnv() {
-        return RContext.getREnvironmentState().getGlobalEnv();
+        return RContext.getInstance().stateREnvironment.getGlobalEnv();
     }
 
     /**
@@ -265,14 +243,14 @@ public abstract class REnvironment extends RAttributeStorage implements RTypedVa
      * Check whether the given frame is indeed the frame stored in the global environment.
      */
     public static boolean isGlobalEnvFrame(Frame frame) {
-        return isFrameForEnv(frame, RContext.getREnvironmentState().getGlobalEnv());
+        return isFrameForEnv(frame, RContext.getInstance().stateREnvironment.getGlobalEnv());
     }
 
     /**
      * Value returned by {@code baseenv()}. This is the "package:base" environment.
      */
     public static REnvironment baseEnv() {
-        Base baseEnv = RContext.getREnvironmentState().getBaseEnv();
+        Base baseEnv = RContext.getInstance().stateREnvironment.getBaseEnv();
         assert baseEnv != null;
         return baseEnv;
     }
@@ -281,7 +259,7 @@ public abstract class REnvironment extends RAttributeStorage implements RTypedVa
      * Value set in {@code .baseNameSpaceEnv} variable. This is the "namespace:base" environment.
      */
     public static REnvironment baseNamespaceEnv() {
-        Base baseEnv = RContext.getREnvironmentState().getBaseEnv();
+        Base baseEnv = RContext.getInstance().stateREnvironment.getBaseEnv();
         assert baseEnv != null;
         return baseEnv.getNamespace();
     }
@@ -301,7 +279,7 @@ public abstract class REnvironment extends RAttributeStorage implements RTypedVa
     public static void baseInitialize(MaterializedFrame baseFrame, MaterializedFrame initialGlobalFrame) {
         // TODO if namespaceRegistry is ever used in an eval an internal env won't suffice.
         REnvironment namespaceRegistry = RDataFactory.createInternalEnv();
-        ContextStateImpl state = (ContextStateImpl) RContext.getREnvironmentState();
+        ContextStateImpl state = RContext.getInstance().stateREnvironment;
         state.setNamespaceRegistry(namespaceRegistry);
         Base baseEnv = new Base(baseFrame, initialGlobalFrame);
         namespaceRegistry.safePut("base", baseEnv.namespaceEnv);
@@ -319,10 +297,10 @@ public abstract class REnvironment extends RAttributeStorage implements RTypedVa
      * path, just replacing the {@code globalenv} component. For a {@code SHARE_PARENT_RO} context
      * we make shallow copies of the package environments.
      *
-     * N.B.Calling {@link RContext#getREnvironmentState()} accesses the new, as yet uninitialized
+     * N.B. {@link RContext#stateREnvironment} accesses the new, as yet uninitialized
      * {@link ContextStateImpl} object
      */
-    private static ContextState createContext(RContext context, MaterializedFrame globalFrame) {
+    private static ContextStateImpl createContext(RContext context, MaterializedFrame globalFrame) {
         switch (context.getKind()) {
             case SHARE_PARENT_RW: {
                 /*
@@ -330,7 +308,7 @@ public abstract class REnvironment extends RAttributeStorage implements RTypedVa
                  * parent of the previous global env. Then we create a copy of the SearchPath and
                  * patch the global entry.
                  */
-                ContextStateImpl parentState = (ContextStateImpl) context.getParent().getThisContextState(RContext.ClassStateKind.REnvironment);
+                ContextStateImpl parentState = context.getParent().stateREnvironment;
                 Base parentBaseEnv = parentState.getBaseEnv();
                 NSBaseMaterializedFrame nsBaseFrame = (NSBaseMaterializedFrame) parentBaseEnv.namespaceEnv.frameAccess.getFrame();
                 MaterializedFrame prevGlobalFrame = RArguments.getEnclosingFrame(nsBaseFrame);
@@ -348,7 +326,7 @@ public abstract class REnvironment extends RAttributeStorage implements RTypedVa
 
             case SHARE_PARENT_RO: {
                 /* We make shallow copies of all the default package environments in the parent */
-                ContextStateImpl parentState = (ContextStateImpl) context.getParent().getThisContextState(RContext.ClassStateKind.REnvironment);
+                ContextStateImpl parentState = context.getParent().stateREnvironment;
                 SearchPath parentSearchPath = parentState.getSearchPath();
                 // clone all the environments below global from the parent
                 REnvironment e = parentSearchPath.get(1).cloneEnv(globalFrame);
@@ -390,7 +368,7 @@ public abstract class REnvironment extends RAttributeStorage implements RTypedVa
                  */
                 MaterializedFrame parentGlobalFrame = ((ContextStateImpl) state).parentGlobalFrame;
                 Global parentGlobalEnv = (Global) RArguments.getEnvironment(parentGlobalFrame);
-                ContextStateImpl parentState = (ContextStateImpl) context.getParent().getThisContextState(RContext.ClassStateKind.REnvironment);
+                ContextStateImpl parentState = context.getParent().stateREnvironment;
                 NSBaseMaterializedFrame nsBaseFrame = (NSBaseMaterializedFrame) parentState.baseEnv.namespaceEnv.frameAccess.getFrame();
                 nsBaseFrame.updateGlobalFrame(parentGlobalFrame);
                 parentState.baseEnv.safePut(".GlobalEnv", parentGlobalEnv);
@@ -464,7 +442,7 @@ public abstract class REnvironment extends RAttributeStorage implements RTypedVa
      * Data for the {@code search} function.
      */
     public static String[] searchPath() {
-        SearchPath searchPath = RContext.getREnvironmentState().getSearchPath();
+        SearchPath searchPath = RContext.getInstance().stateREnvironment.getSearchPath();
         String[] result = new String[searchPath.size()];
         for (int i = 0; i < searchPath.size(); i++) {
             REnvironment env = searchPath.get(i);
@@ -480,7 +458,7 @@ public abstract class REnvironment extends RAttributeStorage implements RTypedVa
      * @return the environment or {@code null} if not found.
      */
     public static REnvironment lookupOnSearchPath(String name) {
-        SearchPath searchPath = RContext.getREnvironmentState().getSearchPath();
+        SearchPath searchPath = RContext.getInstance().stateREnvironment.getSearchPath();
         int i = lookupIndexOnSearchPath(name);
         return i <= 0 ? null : searchPath.get(i - 1);
     }
@@ -492,7 +470,7 @@ public abstract class REnvironment extends RAttributeStorage implements RTypedVa
      * @return the index (1-based) or {@code 0} if not found.
      */
     public static int lookupIndexOnSearchPath(String name) {
-        SearchPath searchPath = RContext.getREnvironmentState().getSearchPath();
+        SearchPath searchPath = RContext.getInstance().stateREnvironment.getSearchPath();
         for (int i = 0; i < searchPath.size(); i++) {
             REnvironment env = searchPath.get(i);
             String searchName = env.getSearchName();
@@ -504,11 +482,11 @@ public abstract class REnvironment extends RAttributeStorage implements RTypedVa
     }
 
     public static REnvironment getNamespaceRegistry() {
-        return RContext.getREnvironmentState().getNamespaceRegistry();
+        return RContext.getInstance().stateREnvironment.getNamespaceRegistry();
     }
 
     public static void registerNamespace(String name, REnvironment env) {
-        RContext.getREnvironmentState().getNamespaceRegistry().safePut(name, env);
+        RContext.getInstance().stateREnvironment.getNamespaceRegistry().safePut(name, env);
     }
 
     /**
@@ -517,7 +495,7 @@ public abstract class REnvironment extends RAttributeStorage implements RTypedVa
      * namespace.
      */
     public static REnvironment getRegisteredNamespace(String name) {
-        return (REnvironment) RContext.getREnvironmentState().getNamespaceRegistry().get(name);
+        return (REnvironment) RContext.getInstance().stateREnvironment.getNamespaceRegistry().get(name);
     }
 
     /**
@@ -531,7 +509,7 @@ public abstract class REnvironment extends RAttributeStorage implements RTypedVa
         assert pos >= 2;
         // N.B. pos is 1-based
         int bpos = pos - 1;
-        SearchPath searchPath = RContext.getREnvironmentState().getSearchPath();
+        SearchPath searchPath = RContext.getInstance().stateREnvironment.getSearchPath();
         if (bpos > searchPath.size() - 1) {
             bpos = searchPath.size() - 1;
         }
@@ -562,7 +540,7 @@ public abstract class REnvironment extends RAttributeStorage implements RTypedVa
      * @return the {@link REnvironment} that was detached.
      */
     public static REnvironment detach(int pos) throws DetachException {
-        SearchPath searchPath = RContext.getREnvironmentState().getSearchPath();
+        SearchPath searchPath = RContext.getInstance().stateREnvironment.getSearchPath();
         if (pos == searchPath.size()) {
             detachException(RError.Message.ENV_DETACH_BASE);
         }
@@ -676,7 +654,7 @@ public abstract class REnvironment extends RAttributeStorage implements RTypedVa
      * "namespace" env.
      */
     public REnvironment getPackageNamespaceEnv() {
-        if (this == RContext.getREnvironmentState().getBaseEnv()) {
+        if (this == RContext.getInstance().stateREnvironment.getBaseEnv()) {
             return ((Base) this).namespaceEnv;
         }
         String envName;
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/DLL.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/DLL.java
index f687cd54027dc92e7ee35be0299171eac675cca3..0fd93d267537a7914ef4a9144b1113ac042f5859 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/DLL.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/DLL.java
@@ -18,6 +18,7 @@ import java.util.concurrent.atomic.*;
 
 import com.oracle.truffle.r.runtime.*;
 import com.oracle.truffle.r.runtime.RError.RErrorException;
+import com.oracle.truffle.r.runtime.context.*;
 import com.oracle.truffle.r.runtime.data.*;
 
 /**
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/RFFIContextStateFactory.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/RFFIContextStateFactory.java
index b2fc96982c974d578d2fbc3ddf503a7a5c9f3c61..9c1cd75dff5485aa4db38131984c24904303aa69 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/RFFIContextStateFactory.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/RFFIContextStateFactory.java
@@ -22,31 +22,22 @@
  */
 package com.oracle.truffle.r.runtime.ffi;
 
-import com.oracle.truffle.r.runtime.*;
-import com.oracle.truffle.r.runtime.RContext.*;
+import com.oracle.truffle.r.runtime.context.*;
+import com.oracle.truffle.r.runtime.context.RContext.ContextState;
 
 /**
  * This is the factory-independent class referenced by {@link RContext} that manages the
  * context-specific state for any given {@link RFFIFactory}. It simply forwards the calls to the
  * actual factory.
  */
-public class RFFIContextStateFactory implements StateFactory {
+public class RFFIContextStateFactory {
     private static RFFIFactory theFactory;
 
     public static void registerFactory(RFFIFactory factory) {
         theFactory = factory;
     }
 
-    public ContextState newContext(RContext context, Object... objects) {
-        return theFactory.newContext(context, objects);
+    public static ContextState newContext(RContext context) {
+        return theFactory.newContext(context);
     }
-
-    public void systemInitialized(RContext context, ContextState state) {
-        theFactory.systemInitialized(context, state);
-    }
-
-    public void beforeDestroy(RContext context, ContextState state) {
-        theFactory.beforeDestroy(context, state);
-    }
-
 }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/RFFIFactory.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/RFFIFactory.java
index a5bd96357a8f49e74a8afbf5c6139e6c5221934d..0eb95d6bc7155e95a03b5e402788b4f26bd35ecc 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/RFFIFactory.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/RFFIFactory.java
@@ -26,17 +26,18 @@ import java.io.*;
 
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.r.runtime.*;
+import com.oracle.truffle.r.runtime.context.*;
+import com.oracle.truffle.r.runtime.context.RContext.*;
 
 /**
  * Factory class for the different possible implementations of the {@link RFFI} interface. The
  * choice of factory is made by the R engine and set here by the call to {@link #setRFFIFactory}.
  *
  * The RFFI may need to do special things in the case of multiple contexts, hence any given factory
- * must support the {@link com.oracle.truffle.r.runtime.RContext.StateFactory} interface. However,
- * since we don't know exactly which factory will be used, {@link RContext} references the
- * {@link RFFIContextStateFactory} class.
+ * must support the {@link #newContext(RContext)} method. However, since we don't know exactly which
+ * factory will be used, {@link RContext} references the {@link RFFIContextStateFactory} class.
  */
-public abstract class RFFIFactory implements RContext.StateFactory {
+public abstract class RFFIFactory {
 
     protected static RFFI theRFFI;
 
@@ -103,4 +104,5 @@ public abstract class RFFIFactory implements RContext.StateFactory {
         throw new IOException(errMsg);
     }
 
+    public abstract ContextState newContext(RContext context);
 }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/nodes/RBaseNode.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/nodes/RBaseNode.java
index 2bd8b5b3391b72b336d03a9556b26c1caa139c3d..8f447a37bf711415ef56d90568b22996120a1275 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/nodes/RBaseNode.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/nodes/RBaseNode.java
@@ -27,6 +27,7 @@ import com.oracle.truffle.api.nodes.*;
 import com.oracle.truffle.api.source.*;
 import com.oracle.truffle.r.runtime.*;
 import com.oracle.truffle.r.runtime.RDeparse.*;
+import com.oracle.truffle.r.runtime.context.*;
 import com.oracle.truffle.r.runtime.env.*;
 
 /**
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/nodes/RSyntaxNode.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/nodes/RSyntaxNode.java
index fe05efa00548d9b36e67df3b8b4bf3f10bb4899f..5c1152172b7a10324e2e5728e4e869a379ce4406 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/nodes/RSyntaxNode.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/nodes/RSyntaxNode.java
@@ -24,7 +24,7 @@ package com.oracle.truffle.r.runtime.nodes;
 
 import com.oracle.truffle.api.nodes.*;
 import com.oracle.truffle.api.source.*;
-import com.oracle.truffle.r.runtime.*;
+import com.oracle.truffle.r.runtime.context.*;
 
 /**
  * An interface that identifies an AST node as being part of the syntactic structure of the
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ops/na/NACheck.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ops/na/NACheck.java
index abacaffd589fc0ff3118f29e3ac27c79b6d792c6..b8e6ef30c03c92061a18f032ebad9022c6f22080 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ops/na/NACheck.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ops/na/NACheck.java
@@ -77,7 +77,7 @@ public final class NACheck {
         }
     }
 
-    public void enable(RAbstractVector value) {
+    public void enable(RAbstractContainer value) {
         if (state == NO_CHECK) {
             enable(!value.isComplete());
         }
@@ -111,6 +111,13 @@ public final class NACheck {
         return false;
     }
 
+    public void seenNA() {
+        if (state != CHECK) {
+            CompilerDirectives.transferToInterpreterAndInvalidate();
+            state = CHECK;
+        }
+    }
+
     public boolean check(int value) {
         if (state != NO_CHECK && isNA(value)) {
             if (state == CHECK_DEOPT) {
@@ -144,6 +151,17 @@ public final class NACheck {
         return false;
     }
 
+    public boolean checkListElement(Object value) {
+        if (state != NO_CHECK && value == RNull.instance) {
+            if (state == CHECK_DEOPT) {
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                state = CHECK;
+            }
+            return true;
+        }
+        return false;
+    }
+
     public int convertLogicalToInt(byte value) {
         if (check(value)) {
             return RRuntime.INT_NA;
@@ -181,8 +199,13 @@ public final class NACheck {
         return RDataFactory.createComplex(value, 0);
     }
 
+    public boolean isEnabled() {
+        return state != NO_CHECK;
+    }
+
     public boolean neverSeenNA() {
-        // need to check for both NA and NaN (the latter used for double to int conversions)
+        // need to check for both NA and NaN (the latter used for double to int
+        // conversions)
         return state != CHECK && !seenNaN;
     }
 
@@ -302,7 +325,7 @@ public final class NACheck {
         return result;
     }
 
-    public int[] convertDoubleVectorToIntData(RDoubleVector vector) {
+    public int[] convertDoubleVectorToIntData(RAbstractDoubleVector vector) {
         int length = vector.getLength();
         int[] result = new int[length];
         boolean warning = false;
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/rng/RRNG.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/rng/RRNG.java
index 6a2d72df06ce9917e87043ca664bfaf9eb2332b9..53e9d2ca1fffe4e35710a6451e6f032239738e50 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/rng/RRNG.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/rng/RRNG.java
@@ -11,11 +11,12 @@
  */
 package com.oracle.truffle.r.runtime.rng;
 
-import com.oracle.truffle.api.CompilerDirectives.*;
+import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.frame.*;
 import com.oracle.truffle.r.runtime.*;
-import com.oracle.truffle.r.runtime.RContext.ContextState;
 import com.oracle.truffle.r.runtime.RError.RErrorException;
+import com.oracle.truffle.r.runtime.context.*;
 import com.oracle.truffle.r.runtime.data.*;
 import com.oracle.truffle.r.runtime.env.*;
 import com.oracle.truffle.r.runtime.ffi.*;
@@ -35,7 +36,7 @@ import com.oracle.truffle.r.runtime.rng.user.*;
  * we do create/update it when the seed/kind is changed, primarily as a debugging aid. N.B. GnuR
  * updates it on <i>every</i> random number generation!
  */
-public class RRNG implements RContext.StateFactory {
+public class RRNG {
     /**
      * The standard kinds provided by GnuR, where the ordinal value corresponds to the argument to
      * {@link RRNG#doSetSeed}.
@@ -146,7 +147,7 @@ public class RRNG implements RContext.StateFactory {
         }
     }
 
-    private static final class ContextStateImpl implements RContext.ContextState {
+    public static final class ContextStateImpl implements RContext.ContextState {
         private Kind currentKind;
         private NormKind currentNormKind;
 
@@ -159,24 +160,23 @@ public class RRNG implements RContext.StateFactory {
             currentKind = kind;
         }
 
-        @SuppressWarnings("unused")
         void updateCurrentNormKind(NormKind normKind) {
             currentNormKind = normKind;
         }
-    }
 
-    public ContextState newContext(RContext context, Object... objects) {
-        int seed = timeToSeed();
-        try {
-            initGenerator(DEFAULT_KIND.setGenerator(), seed);
-        } catch (RNGException ex) {
-            Utils.fail("failed to initialize default random number generator");
+        public static ContextStateImpl newContext(@SuppressWarnings("unused") RContext context) {
+            int seed = timeToSeed();
+            try {
+                initGenerator(DEFAULT_KIND.setGenerator(), seed);
+            } catch (RNGException ex) {
+                Utils.fail("failed to initialize default random number generator");
+            }
+            return new ContextStateImpl(DEFAULT_KIND, DEFAULT_NORM_KIND);
         }
-        return new ContextStateImpl(DEFAULT_KIND, DEFAULT_NORM_KIND);
     }
 
     private static ContextStateImpl getContextState() {
-        return (ContextStateImpl) RContext.getContextState(RContext.ClassStateKind.RNG);
+        return RContext.getInstance().stateRNG;
     }
 
     public static int currentKindAsInt() {
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test
index f4b8ca54b61a6c3c27342c4bedd8c2d269b74813..15c294106f6470a210604ae2d924030a51f386a2 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test
@@ -49446,10 +49446,6 @@ $x
 #{ x<-7; f<-function() x<<-42; f_copy<-as.list(environment())[["f"]]; f_copy(); x }
 [1] 42
 
-##com.oracle.truffle.r.test.builtins.TestMiscBuiltins.testChannels
-#{ if (length(grep("FastR", R.Version()$version.string)) == 1) { ch <- fastr.channel.create(1L); cx <- fastr.context.create("SHARED_NOTHING"); fastr.context.spawn(cx, "ch <- fastr.channel.get(1L); x<-fastr.channel.receive(ch); x[1]<-7; fastr.channel.send(ch, x)"); y<-c(42); fastr.channel.send(ch, y); x<-fastr.channel.receive(ch); fastr.context.join(cx); fastr.channel.close(ch); print(c(x,y)) } else { print(c(7L, 42L)) } }
-[1]  7 42
-
 ##com.oracle.truffle.r.test.builtins.TestMiscBuiltins.testDataFrameTypeCheck
 #is.data.frame("1")
 [1] FALSE
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/TestBase.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/TestBase.java
index 55b15d8a1ecc036259556cbabb7660bc7eb71b98..9b356f5ad18a09a0d0135d9bc4f1795e6b2e7236 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/TestBase.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/TestBase.java
@@ -312,6 +312,8 @@ public class TestBase {
     protected static final String ERROR = "Error";
     protected static final String WARNING = "Warning message";
 
+    private static final boolean IGNORE_ERROR_COMPARISON = false;
+
     /**
      * If this is set to {@code true}, {@link Output#ContainsError} will compare the full output
      * instead of truncating leading "Error" strings and such. This means it will behave like
@@ -352,6 +354,11 @@ public class TestBase {
         // empty
     }
 
+    // support testing of FastR-only functionality (equivalent GNU R output provided separately)
+    protected void assertEvalFastR(String input, String gnuROutput) {
+        evalAndCompare(new String[]{"if (length(grep(\"FastR\", R.Version()$version.string)) != 1) { " + gnuROutput + " } else " + input});
+    }
+
     /*
      * implementation support methods
      */
@@ -425,27 +432,12 @@ public class TestBase {
             } else {
                 String result = fastREval(input);
 
-                boolean ok;
-                if (expected.equals(result) || searchWhiteLists(whiteLists, input, expected, result)) {
-                    ok = true;
-                } else {
-                    if (containsWarning || (mayContainWarning && expected.contains(WARNING))) {
-                        String resultWarning = getWarningMessage(result);
-                        String expectedWarning = getWarningMessage(expected);
-                        ok = resultWarning.equals(expectedWarning);
-                        result = getOutputWithoutWarning(result);
-                        expected = getOutputWithoutWarning(expected);
-                    } else {
-                        ok = true;
-                    }
-                    if (ok) {
-                        if (containsError || (mayContainError && expected.startsWith(ERROR))) {
-                            ok = result.startsWith(ERROR) && checkMessageStripped(expected, result);
-                        } else {
-                            ok = expected.equals(result);
-                        }
-                    }
-                }
+                CheckResult checkResult = checkResult(whiteLists, input, expected, result, containsWarning, mayContainWarning, containsError, mayContainError);
+
+                result = checkResult.result;
+                expected = checkResult.expected;
+                boolean ok = checkResult.ok;
+
                 if (ProcessFailedTests) {
                     if (ok) {
                         unexpectedSuccessfulMicroTests.add(getTestContext() + ": " + input);
@@ -488,15 +480,64 @@ public class TestBase {
         }
     }
 
-    private static boolean searchWhiteLists(WhiteList[] whiteLists, String input, String expected, String result) {
+    private static class CheckResult {
+
+        public final boolean ok;
+        public final String result;
+        public final String expected;
+
+        public CheckResult(boolean ok, String result, String expected) {
+            this.ok = ok;
+            this.result = result;
+            this.expected = expected;
+        }
+
+    }
+
+    @SuppressWarnings("unused")
+    private CheckResult checkResult(WhiteList[] whiteLists, String input, String originalExpected, String originalResult, boolean containsWarning, boolean mayContainWarning, boolean containsError,
+                    boolean mayContainError) {
+        boolean ok;
+        String result = originalResult;
+        String expected = originalExpected;
+        if (expected.equals(result) || searchWhiteLists(whiteLists, input, expected, result, containsWarning, mayContainWarning, containsError, mayContainError)) {
+            ok = true;
+        } else {
+            if (containsWarning || (mayContainWarning && expected.contains(WARNING))) {
+                String resultWarning = getWarningMessage(result);
+                String expectedWarning = getWarningMessage(expected);
+                ok = resultWarning.equals(expectedWarning);
+                result = getOutputWithoutWarning(result);
+                expected = getOutputWithoutWarning(expected);
+            } else {
+                ok = true;
+            }
+            if (ok) {
+                if (containsError || (mayContainError && expected.startsWith(ERROR))) {
+                    ok = result.startsWith(ERROR) && (IGNORE_ERROR_COMPARISON || checkMessageStripped(expected, result));
+                } else {
+                    ok = expected.equals(result);
+                }
+            }
+        }
+        return new CheckResult(ok, result, expected);
+    }
+
+    private boolean searchWhiteLists(WhiteList[] whiteLists, String input, String expected, String result, boolean containsWarning, boolean mayContainWarning, boolean containsError,
+                    boolean mayContainError) {
+        if (whiteLists == null) {
+            return false;
+        }
         for (WhiteList list : whiteLists) {
             WhiteList.Results wlr = list.get(input);
             if (wlr != null) {
-                if (!wlr.expected.equals(expected)) {
+                CheckResult checkedResult = checkResult(null, input, wlr.expected, expected, containsWarning, mayContainWarning, containsError, mayContainError);
+                if (!checkedResult.ok) {
                     System.out.println("expected output does not match: " + wlr.expected + " vs. " + expected);
                     return false;
                 }
-                if (wlr.fastR.equals(result)) {
+                CheckResult fastRResult = checkResult(null, input, wlr.fastR, result, containsWarning, mayContainWarning, containsError, mayContainError);
+                if (fastRResult.ok) {
                     list.markUsed(input);
                     return true;
                 }
@@ -559,24 +600,11 @@ public class TestBase {
         if (cxr < 0 || cxe < 0) {
             return false;
         }
-        String resultStripped = stripWhitespace(result, cxr + 1);
-        String expectedStripped = stripWhitespace(expected, cxe + 1);
+        String resultStripped = result.substring(cxr + 1).trim();
+        String expectedStripped = expected.substring(cxe + 1).trim();
         return resultStripped.equals(expectedStripped);
     }
 
-    private static String stripWhitespace(String r, int ix) {
-        int x = ix;
-        int rl = r.length();
-        char ch = r.charAt(x);
-        if (Character.isWhitespace(ch)) {
-            while (Character.isWhitespace(ch) && x < rl) {
-                ch = r.charAt(x++);
-            }
-            x--;
-        }
-        return r.substring(x);
-    }
-
     /**
      * Evaluate {@code input} in FastR, returning all (virtual) console output that was produced.
      */
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_Sysgetenv.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_Sysgetenv.java
index cb24b6620433a519ae4833efc6e637630655760d..6d0e81bd0856c09df543b4ff5170e2d07c83848e 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_Sysgetenv.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_Sysgetenv.java
@@ -12,7 +12,6 @@ package com.oracle.truffle.r.test.builtins;
 
 import org.junit.*;
 
-import com.oracle.truffle.r.runtime.*;
 import com.oracle.truffle.r.test.*;
 
 // Checkstyle: stop line length check
@@ -32,10 +31,7 @@ public class TestBuiltin_Sysgetenv extends TestBase {
     public void testEnvVars() {
         assertEval(Output.ContainsError, "{ Sys.setenv(\"a\") } ");
         assertEval("{ Sys.setenv(FASTR_A=\"a\"); Sys.getenv(\"FASTR_A\"); } ");
-        REnvVars.unset("FASTR_A");
         assertEval("{ Sys.setenv(FASTR_A=\"a\", FASTR_B=\"b\"); Sys.getenv(c(\"FASTR_A\", \"FASTR_B\"));  } ");
-        REnvVars.unset("FASTR_A");
-        REnvVars.unset("FASTR_B");
         assertEval("{ Sys.getenv(\"FASTR_A\") } ");
         assertEval("{ Sys.getenv(\"FASTR_A\", unset=\"UNSET\") } ");
     }
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestMiscBuiltins.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestMiscBuiltins.java
index de5c5c41adb057874d1c8bed7d057b6734321ca7..ab4cc83be5d435134eaa6dd16576ca30192348cb 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestMiscBuiltins.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestMiscBuiltins.java
@@ -346,8 +346,4 @@ public class TestMiscBuiltins extends TestBase {
         assertEval("{ x<-as.character(list(a=\"0\", b=\"0\", c=\"0.3\")); type.convert(x, as.is=FALSE) }");
     }
 
-    @Test
-    public void testChannels() {
-        assertEval("{ if (length(grep(\"FastR\", R.Version()$version.string)) == 1) { ch <- fastr.channel.create(1L); cx <- fastr.context.create(\"SHARED_NOTHING\"); fastr.context.spawn(cx, \"ch <- fastr.channel.get(1L); x<-fastr.channel.receive(ch); x[1]<-7; fastr.channel.send(ch, x)\"); y<-c(42); fastr.channel.send(ch, y); x<-fastr.channel.receive(ch); fastr.context.join(cx); fastr.channel.close(ch); print(c(x,y)) } else { print(c(7L, 42L)) } }");
-    }
 }
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 c2f552450c629d4d84966843ad96197e5187acdf..a0f032e2697c9aa0d58366e3b5148a6a8b9cb1c2 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
@@ -26,9 +26,13 @@ import java.util.*;
 import java.util.concurrent.*;
 
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
-import com.oracle.truffle.api.source.*;
+import com.oracle.truffle.api.vm.*;
 import com.oracle.truffle.r.engine.*;
 import com.oracle.truffle.r.runtime.*;
+import com.oracle.truffle.r.runtime.RCmdOptions.Client;
+import com.oracle.truffle.r.runtime.context.*;
+import com.oracle.truffle.r.runtime.context.Engine.ParseException;
+import com.oracle.truffle.r.runtime.context.RContext.ContextKind;
 
 public final class FastRSession implements RSession {
 
@@ -38,7 +42,7 @@ public final class FastRSession implements RSession {
      * A (virtual) console handler that collects the output in a {@link StringBuilder} for
      * comparison. It does not separate error output as the test analysis doesn't need it.
      */
-    private static class ConsoleHandler implements RContext.ConsoleHandler {
+    public static class TestConsoleHandler implements ConsoleHandler {
         private final StringBuilder buffer = new StringBuilder();
 
         @TruffleBoundary
@@ -91,13 +95,17 @@ public final class FastRSession implements RSession {
             return RContext.CONSOLE_WIDTH;
         }
 
+        public String getInputDescription() {
+            return "<test input>";
+        }
     }
 
-    private static ConsoleHandler consoleHandler;
     private static FastRSession singleton;
 
+    private final TestConsoleHandler consoleHandler;
+    private final TruffleVM main;
+
     private EvalThread evalThread;
-    private final RContext main;
 
     public static FastRSession create() {
         if (singleton == null) {
@@ -106,18 +114,19 @@ public final class FastRSession implements RSession {
         return singleton;
     }
 
-    public RContext createTestContext() {
+    public TruffleVM createTestContext() {
         create();
-        RContext context = RContextFactory.createShareParentReadWrite(main, new String[0], consoleHandler).activate();
-        context.setSystemTimeZone(TimeZone.getTimeZone("CET"));
-        return context;
+        RCmdOptions options = RCmdOptions.parseArguments(Client.RSCRIPT, new String[0]);
+        ContextInfo info = ContextInfo.create(options, ContextKind.SHARE_PARENT_RW, RContext.fromTruffleVM(main), consoleHandler, TimeZone.getTimeZone("CET"));
+        return RContextFactory.create(info);
     }
 
     private FastRSession() {
-        consoleHandler = new ConsoleHandler();
-        RContextFactory.initialize();
+        consoleHandler = new TestConsoleHandler();
         try {
-            main = RContextFactory.createInitial(new String[0], consoleHandler).activate();
+            RCmdOptions options = RCmdOptions.parseArguments(Client.RSCRIPT, new String[0]);
+            ContextInfo info = ContextInfo.create(options, ContextKind.SHARE_NOTHING, null, consoleHandler);
+            main = RContextFactory.create(info);
         } finally {
             System.out.print(consoleHandler.buffer.toString());
         }
@@ -179,16 +188,22 @@ public final class FastRSession implements RSession {
                     break;
                 }
                 try {
-                    RContext testContext = createTestContext();
+                    TruffleVM vm = createTestContext();
                     try {
-                        testContext.getThisEngine().parseAndEvalTest(Source.fromText(expression, "<test_input>"), true, false);
+                        vm.eval(TruffleRLanguage.MIME, expression);
                     } finally {
-                        testContext.destroy();
+                        RContext.destroyContext(vm);
                     }
+                } catch (ParseException e) {
+                    e.report(consoleHandler);
                 } catch (RError e) {
                     // nothing to do
                 } catch (Throwable t) {
-                    killedByException = t;
+                    if (t.getCause() instanceof RError) {
+                        // nothing to do
+                    } else {
+                        killedByException = t;
+                    }
                 } finally {
                     exit.release();
                 }
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/fastr/TestChannels.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/fastr/TestChannels.java
new file mode 100644
index 0000000000000000000000000000000000000000..887abbc4497a1c66b062715bb4b4164978feb814
--- /dev/null
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/fastr/TestChannels.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2015, 2015, 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.test.library.fastr;
+
+import org.junit.*;
+
+import com.oracle.truffle.r.test.*;
+
+public class TestChannels extends TestBase {
+
+    @Test
+    public void testChannels() {
+        assertEvalFastR("{ ch <- fastr.channel.create(1L); cx <- fastr.context.create(\"SHARED_NOTHING\"); fastr.context.spawn(cx, \"ch <- fastr.channel.get(1L); x<-fastr.channel.receive(ch); x[1]<-7; fastr.channel.send(ch, x)\"); y<-c(42); fastr.channel.send(ch, y); x<-fastr.channel.receive(ch); fastr.context.join(cx); fastr.channel.close(ch); print(c(x,y)) }",
+                        "print(c(7L, 42L))");
+        assertEvalFastR("{ ch <- fastr.channel.create(1L); cx <- fastr.context.create(\"SHARED_NOTHING\"); fastr.context.spawn(cx, \"ch <- fastr.channel.get(1L); x<-fastr.channel.receive(ch); x[1][1]<-7; fastr.channel.send(ch, x)\"); y<-list(c(42)); fastr.channel.send(ch, y); x<-fastr.channel.receive(ch); fastr.context.join(cx); fastr.channel.close(ch); print(c(x,y)) }",
+                        "print(list(7L, 42L))");
+    }
+}
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/rpackages/TestRPackages.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/rpackages/TestRPackages.java
index 888ee70a6a966fad78aebf38b40a5b2aff6c4edf..455976bfe1c71f411e2c07c711550a033339a26c 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/rpackages/TestRPackages.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/rpackages/TestRPackages.java
@@ -31,6 +31,7 @@ import java.util.*;
 import org.junit.*;
 
 import com.oracle.truffle.r.runtime.*;
+import com.oracle.truffle.r.runtime.context.*;
 import com.oracle.truffle.r.test.*;
 
 /**
@@ -107,7 +108,7 @@ public class TestRPackages extends TestBase {
             Map<String, String> env = pb.environment();
             env.put("R_LIBS_USER", rpackagesLibs.toString());
             if (!generatingExpected()) {
-                env.put("R_INSTALL_TAR", REnvVars.get("TAR"));
+                env.put("R_INSTALL_TAR", RContext.getInstance().stateREnvVars.get("TAR"));
             }
             try {
                 if (FastROptions.debugMatches("TestRPackages")) {
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/tck/FastRTckTest.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/tck/FastRTckTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..1a1aa5f9b72e19a8e3d505cff6aadf8f134d33cb
--- /dev/null
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/tck/FastRTckTest.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2015, 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.test.tck;
+
+import com.oracle.truffle.tck.TruffleTCK;
+import com.oracle.truffle.api.vm.TruffleVM;
+
+import static org.junit.Assert.*;
+
+import org.junit.Test;
+
+public class FastRTckTest extends TruffleTCK {
+    @Test
+    public void testVerifyPresence() {
+        TruffleVM vm = TruffleVM.newVM().build();
+        assertTrue("Our language is present", vm.getLanguages().containsKey("text/x-r"));
+    }
+
+    @Override
+    protected TruffleVM prepareVM() throws Exception {
+        TruffleVM vm = TruffleVM.newVM().build();
+        // @formatter:off
+        vm.eval("text/x-r",
+            "fourtyTwo <- function() {\n" +
+            "  42L\n" +
+            "}\n" +
+            "Interop.export('fourtyTwo', fourtyTwo)\n" +
+            "plus <- function(a, b) {\n" +
+            "  a + b\n" +
+            "}\n" +
+            "Interop.export('plus', plus)\n" +
+            "apply <- function(f) {\n" +
+            "  f(18L, 32L) + 10L\n" +
+            "}\n" +
+            "Interop.export('apply', apply)\n" +
+            "null <- function() {\n" +
+            "  NULL\n" +
+            "}\n" +
+            "Interop.export('null', null)\n" +
+            "counter <- 0L\n" +
+            "count <- function() {\n" +
+            "  counter <<- counter + 1L\n" +
+            "}\n" +
+            "Interop.export('count', count)\n"
+        );
+        // @formatter:on
+        return vm;
+    }
+
+    @Override
+    protected String mimeType() {
+        return "text/x-r";
+    }
+
+    @Override
+    protected String fourtyTwo() {
+        return "fourtyTwo";
+    }
+
+    @Override
+    protected String plusInt() {
+        return "plus";
+    }
+
+    @Override
+    protected String returnsNull() {
+        return "null";
+    }
+
+    @Override
+    protected String applyNumbers() {
+        return "apply";
+    }
+
+    @Override
+    protected String countInvocations() {
+        return "count";
+    }
+
+    @Override
+    protected String invalidCode() {
+        // @formatter:off
+        return
+            "main <- f unction() {\n" +
+            "  re turn(42)\n" +
+            "}\n";
+        // @formatter:on
+    }
+
+    @Override
+    @Test
+    public void testNull() {
+        // disabled because we don't provide a Java "null" value in R
+    }
+}
diff --git a/mx.fastr/copyrights/overrides b/mx.fastr/copyrights/overrides
index 6a5eedd0b1df9fa688295021a2252c9c7ff8a1ca..c04e8cf7e6dbd8949306a52c05cc23a69c6b98d2 100644
--- a/mx.fastr/copyrights/overrides
+++ b/mx.fastr/copyrights/overrides
@@ -19,8 +19,6 @@ com.oracle.truffle.r.native/builtinlibs/src/rdummy.c,no.copyright
 com.oracle.truffle.r.native/fficall/jni/src/register.c,gnu_r.copyright
 com.oracle.truffle.r.native/fficall/jni/src/alloc.c,gnu_r.copyright
 com.oracle.truffle.r.native/include/jni/src/libintl.h,no.copyright
-com.oracle.truffle.r.native/include/jni/src/R_ext/GraphicsDevice.h,no.copyright
-com.oracle.truffle.r.native/include/jni/src/R_ext/GraphicsEngine.h,no.copyright
 com.oracle.truffle.r.native/library/base/src/registration.c,no.copyright
 com.oracle.truffle.r.native/library/graphics/src/graphics.h,no.copyright
 com.oracle.truffle.r.native/library/graphics/src/init.c,no.copyright
@@ -41,6 +39,22 @@ com.oracle.truffle.r.native/library/tools/src/init.c,no.copyright
 com.oracle.truffle.r.native/library/tools/src/tools.h,no.copyright
 com.oracle.truffle.r.native/library/utils/src/init.c,no.copyright
 com.oracle.truffle.r.native/library/utils/src/utils.h,no.copyright
+com.oracle.truffle.r.native/library/grid/src/gpar.c no.copyright
+com.oracle.truffle.r.native/library/grid/src/grid.c no.copyright
+com.oracle.truffle.r.native/library/grid/src/grid.h no.copyright
+com.oracle.truffle.r.native/library/grid/src/just.c no.copyright
+com.oracle.truffle.r.native/library/grid/src/layout.c no.copyright
+com.oracle.truffle.r.native/library/grid/src/matrix.c no.copyright
+com.oracle.truffle.r.native/library/grid/src/register.c no.copyright
+com.oracle.truffle.r.native/library/grid/src/state.c no.copyright
+com.oracle.truffle.r.native/library/grid/src/unit.c no.copyright
+com.oracle.truffle.r.native/library/grid/src/util.c no.copyright
+com.oracle.truffle.r.native/library/grid/src/viewport.c no.copyright
+com.oracle.truffle.r.native/library/parallel/src/glpi.h has missing copyright
+com.oracle.truffle.r.native/library/parallel/src/init.c no.copyright
+com.oracle.truffle.r.native/library/parallel/src/parallel.h no.copyright
+com.oracle.truffle.r.native/library/parallel/src/rngstream.c no.copyright
+com.oracle.truffle.r.native/library/splines/src/splines.c no.copyright
 com.oracle.truffle.r.native/run/R.sh,oracle_bash.copyright
 com.oracle.truffle.r.native/run/Rscript_exec.sh,oracle_bash.copyright
 com.oracle.truffle.r.native/run/Rscript.sh,oracle_bash.copyright