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 9744d89ebda248d647be26c292d7a621fa0cf48e..8c6219e025259da327baa3ef5e0c2db103981188 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
@@ -47,7 +47,6 @@ import com.oracle.truffle.r.nodes.control.IfNode;
 import com.oracle.truffle.r.nodes.control.ReplacementNode;
 import com.oracle.truffle.r.nodes.function.FunctionDefinitionNode;
 import com.oracle.truffle.r.nodes.function.FunctionExpressionNode;
-import com.oracle.truffle.r.nodes.function.PromiseHelperNode;
 import com.oracle.truffle.r.nodes.function.RCallNode;
 import com.oracle.truffle.r.runtime.Arguments;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
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 30a134642b932dbd8197084dfad5f5562ecdf335..04c66c9f1e98f0a38d1966e699a4142dfe2392ad 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
@@ -242,7 +242,7 @@ public class RCommand {
                             } else if (cause instanceof ExitException) {
                                 // usually from quit
                                 vm.dispose();
-                                System.exit(((ExitException) cause).getStatus());
+                                Utils.systemExit(((ExitException) cause).getStatus());
                             } else {
                                 RInternalError.reportErrorAndConsoleLog(cause, consoleHandler, 0);
                                 // We continue the repl even though the system may be broken
@@ -262,7 +262,7 @@ public class RCommand {
             } catch (Throwable e) {
                 if (e.getCause() instanceof ExitException) {
                     // normal quit, but with exit code based on lastStatus
-                    System.exit(lastStatus);
+                    Utils.systemExit(lastStatus);
                 }
                 throw RInternalError.shouldNotReachHere(e);
             }
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/utils/ObjectSize.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/utils/ObjectSize.java
index 5b4d80c96f3f3d8b085cf7f2a706ebb056a7eba9..b952a0da7a3b74f43725f163a7221b7d57d48379 100644
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/utils/ObjectSize.java
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/utils/ObjectSize.java
@@ -22,199 +22,56 @@
  */
 package com.oracle.truffle.r.library.utils;
 
-import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.dsl.Fallback;
 import com.oracle.truffle.api.dsl.Specialization;
-import com.oracle.truffle.r.library.utils.ObjectSizeNodeGen.RecursiveObjectSizeNodeGen;
+import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.r.nodes.builtin.RExternalBuiltinNode;
-import com.oracle.truffle.r.nodes.profile.TruffleBoundaryNode;
-import com.oracle.truffle.r.runtime.data.RAttributable;
-import com.oracle.truffle.r.runtime.data.RAttributes;
-import com.oracle.truffle.r.runtime.data.RAttributes.RAttribute;
-import com.oracle.truffle.r.runtime.data.RComplex;
-import com.oracle.truffle.r.runtime.data.RComplexVector;
-import com.oracle.truffle.r.runtime.data.RDoubleSequence;
-import com.oracle.truffle.r.runtime.data.RDoubleVector;
-import com.oracle.truffle.r.runtime.data.RExpression;
+import com.oracle.truffle.r.runtime.data.RObjectSize;
 import com.oracle.truffle.r.runtime.data.RFunction;
-import com.oracle.truffle.r.runtime.data.RIntSequence;
-import com.oracle.truffle.r.runtime.data.RIntVector;
-import com.oracle.truffle.r.runtime.data.RLanguage;
-import com.oracle.truffle.r.runtime.data.RList;
-import com.oracle.truffle.r.runtime.data.RLogicalVector;
 import com.oracle.truffle.r.runtime.data.RNull;
-import com.oracle.truffle.r.runtime.data.RPairList;
-import com.oracle.truffle.r.runtime.data.RRaw;
-import com.oracle.truffle.r.runtime.data.RRawVector;
-import com.oracle.truffle.r.runtime.data.RStringVector;
+import com.oracle.truffle.r.runtime.data.RTypedValue;
 
-/*
- * Similarly to GNU R's version, this is very approximate
- * (e.g. overhead related to Java object headers is not included)
- * and is only (semi) accurate for atomic vectors.
+/**
+ * Similarly to GNU R's version, this is approximate and based, for {@link RTypedValue} instances on
+ * {@link RObjectSize#getObjectSize}. As per GNU R the AST size for a closure is included. TODO AST
+ * size not included owing to problems sizing it automatically.
  */
+@SuppressWarnings("unused")
 public abstract class ObjectSize extends RExternalBuiltinNode.Arg1 {
 
-    protected abstract int executeInt(Object o);
-
-    @Child RecursiveObjectSize recursiveObjectSize;
-
-    protected int recursiveObjectSize(Object o) {
-        if (recursiveObjectSize == null) {
-            CompilerDirectives.transferToInterpreterAndInvalidate();
-            recursiveObjectSize = insert(RecursiveObjectSizeNodeGen.create());
-        }
-        return recursiveObjectSize.executeInt(o);
-    }
-
-    @Specialization
-    protected int objectSize(@SuppressWarnings("unused") RNull o) {
-        return 64; // pointer?
-    }
-
-    @Specialization
-    protected int objectSize(@SuppressWarnings("unused") int o) {
-        return 32;
-    }
-
-    @Specialization
-    protected int objectSize(@SuppressWarnings("unused") double o) {
-        return 64;
-    }
-
-    @Specialization
-    protected int objectSize(@SuppressWarnings("unused") byte o) {
-        return 8;
-    }
-
-    @Specialization
-    protected int objectSize(String o) {
-        return o.length() * 16;
-    }
-
-    @Specialization
-    protected int objectSize(@SuppressWarnings("unused") RRaw o) {
-        return 8;
-    }
-
-    @Specialization
-    protected int objectSize(@SuppressWarnings("unused") RComplex o) {
-        return 128;
-    }
-
-    @Specialization
-    protected int objectSize(RIntSequence o) {
-        int res = 96; // int length + int start + int stride
-        return res + attrSize(o);
-    }
-
-    @Specialization
-    protected int objectSize(RDoubleSequence o) {
-        int res = 160; // int length + double start + double stride
-        return res + attrSize(o);
-    }
-
-    @Specialization
-    protected int objectSize(RIntVector o) {
-        return o.getLength() * 32 + attrSize(o);
-    }
-
-    @Specialization
-    protected int objectSize(RDoubleVector o) {
-        return o.getLength() * 64 + attrSize(o);
-    }
-
-    @Specialization
-    protected int objectSize(RStringVector o) {
-        int res = 0;
-        for (int i = 0; i < o.getLength(); i++) {
-            res += o.getLength() * 16;
-        }
-        return res + attrSize(o);
-    }
-
-    @Specialization
-    protected int objectSize(RLogicalVector o) {
-        return o.getLength() * 8 + attrSize(o);
-    }
-
-    @Specialization
-    protected int objectSize(RComplexVector o) {
-        return o.getLength() * 128 + attrSize(o);
-    }
-
-    @Specialization
-    protected int objectSize(RRawVector o) {
-        return o.getLength() * 8 + attrSize(o);
-    }
-
-    @Specialization
-    @TruffleBoundary
-    protected int objectSize(RList o) {
-        int res = 0;
-        for (int i = 0; i < o.getLength(); i++) {
-            res += recursiveObjectSize(o.getDataAt(i));
-        }
-        return res + attrSize(o);
-    }
-
-    @Specialization
-    @TruffleBoundary
-    protected int objectSize(RPairList o) {
-        RPairList list = o;
-        Object car = list.car();
-        int res = 0;
-        while (true) {
-            res += recursiveObjectSize(car);
-            Object cdr = list.cdr();
-            if (cdr == RNull.instance) {
-                break;
+    private static class MyIgnoreObjectHandler implements RObjectSize.IgnoreObjectHandler {
+        @Override
+        public boolean ignore(Object rootObject, Object obj) {
+            if (obj == RNull.instance) {
+                return true;
             } else {
-                list = (RPairList) cdr;
-                car = list.car();
+                return false;
             }
         }
-        return res + attrSize(o);
-    }
 
-    @Specialization
-    protected int objectSize(@SuppressWarnings("unused") RFunction o) {
-        return 256; // arbitrary, but does it really matter?
     }
 
+    private static final MyIgnoreObjectHandler ignoreObjectHandler = new MyIgnoreObjectHandler();
+
     @Specialization
-    protected int objectSize(@SuppressWarnings("unused") RLanguage o) {
-        return 256; // arbitrary, but does it really matter?
+    protected int objectSize(int o) {
+        return RObjectSize.INT_SIZE;
     }
 
     @Specialization
-    protected int objectSize(@SuppressWarnings("unused") RExpression o) {
-        return 256; // arbitrary, but does it really matter?
+    protected int objectSize(double o) {
+        return RObjectSize.DOUBLE_SIZE;
     }
 
-    protected int attrSize(RAttributable o) {
-        return o.getAttributes() == null ? 0 : attrSizeInternal(o.getAttributes());
+    @Specialization
+    protected int objectSize(byte o) {
+        return RObjectSize.BYTE_SIZE;
     }
 
+    @Fallback
     @TruffleBoundary
-    protected int attrSizeInternal(RAttributes attributes) {
-        int size = 0;
-        for (RAttribute attr : attributes) {
-            size += attr.getName().length() * 16;
-            size += recursiveObjectSize(attr.getValue());
-        }
-        return size;
-    }
-
-    protected abstract static class RecursiveObjectSize extends TruffleBoundaryNode {
-
-        protected abstract int executeInt(Object o);
-
-        @Child ObjectSize objectSize = ObjectSizeNodeGen.create();
-
-        @Specialization
-        protected int objectSize(Object o) {
-            return objectSize.executeInt(o);
-        }
+    protected int objectSize(Object o) {
+        return (int) RObjectSize.getObjectSize(o, ignoreObjectHandler);
     }
 }
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/utils/Rprof.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/utils/Rprof.java
index d03f663b1151eaae9a39bdecede4f62e9996a80e..db38227316cc29e6eec6064ae342363e887c6018 100644
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/utils/Rprof.java
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/utils/Rprof.java
@@ -50,12 +50,36 @@ import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.RSource;
 import com.oracle.truffle.r.runtime.Utils;
 import com.oracle.truffle.r.runtime.context.RContext;
+import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RNull;
+import com.oracle.truffle.r.runtime.data.RObjectSize;
+import com.oracle.truffle.r.runtime.data.RTypedValue;
+import com.oracle.truffle.r.runtime.data.MemoryCopyTracer;
 import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
+import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
 import com.oracle.truffle.r.runtime.instrument.InstrumentationState.RprofState;
+import com.oracle.truffle.r.runtime.instrument.InstrumentationState.RprofState.MemoryQuad;
 import com.oracle.truffle.r.runtime.nodes.RSyntaxNode;
 
-public abstract class Rprof extends RExternalBuiltinNode.Arg8 {
+/**
+ * Implements the {@code Rprof} external.
+ *
+ * The output is basically a sequence of call stacks, output at each sample interval, with entries
+ * in the stack identified by quoted function names. If memory profiling, the stack is preceded by a
+ * auad of numbers {@code :smallv:bigv:nodes:duplicate_counter:} allocated in the interval. If line
+ * profiling is enabled source files are listed as
+ *
+ * <pre>
+ * #File N: path
+ * </pre>
+ *
+ * and then the {@code N} is used in line number references of the form {@code N#L},which precede
+ * the function name.
+ *
+ */
+public abstract class Rprof extends RExternalBuiltinNode.Arg8 implements RDataFactory.Listener, MemoryCopyTracer.Listener {
+
+    private RprofState profState;
 
     @SuppressWarnings("unused")
     @Specialization
@@ -64,13 +88,13 @@ public abstract class Rprof extends RExternalBuiltinNode.Arg8 {
         if (!RContext.getInstance().isInitial()) {
             throw RError.error(this, RError.Message.GENERIC, "profiling not supported in created contexts");
         }
-        RprofState profState = RContext.getInstance().stateInstrumentation.getRprof();
+        profState = RContext.getInstance().stateInstrumentation.getRprof();
         String filename = filenameVec.getDataAt(0);
         if (filename.length() == 0) {
             // disable
             endProfiling();
         } else {
-            // enable
+            // enable after ending any previous session
             if (profState.out() != null) {
                 endProfiling();
             }
@@ -79,18 +103,21 @@ public abstract class Rprof extends RExternalBuiltinNode.Arg8 {
             boolean gcProfiling = RRuntime.fromLogical(gcProfilingL);
             try {
                 PrintWriter out = new PrintWriter(new FileWriter(filename, append));
-                if (memProfiling) {
-                    RError.warning(this, RError.Message.GENERIC, "Rprof: memory profiling not supported");
-                }
                 if (gcProfiling) {
                     RError.warning(this, RError.Message.GENERIC, "Rprof: gc profiling not supported");
                 }
+                if (memProfiling) {
+                    RDataFactory.addListener(this);
+                    RDataFactory.setAllocationTracing(true);
+                    MemoryCopyTracer.addListener(this);
+                    MemoryCopyTracer.setTracingState(true);
+                }
                 // interval is in seconds, we convert to millis
                 long intervalInMillis = (long) (1E3 * intervalD);
                 StatementListener statementListener = new StatementListener();
                 ProfileThread profileThread = new ProfileThread(intervalInMillis, statementListener);
                 profileThread.setDaemon(true);
-                profState.initialize(out, profileThread, statementListener, intervalInMillis, RRuntime.fromLogical(lineProfilingL));
+                profState.initialize(out, profileThread, statementListener, intervalInMillis, RRuntime.fromLogical(lineProfilingL), memProfiling);
                 profileThread.start();
             } catch (IOException ex) {
                 throw RError.error(this, RError.Message.GENERIC, String.format("Rprof: cannot open profile file '%s'", filename));
@@ -99,13 +126,37 @@ public abstract class Rprof extends RExternalBuiltinNode.Arg8 {
         return RNull.instance;
     }
 
-    private static void endProfiling() {
-        RprofState profState = RContext.getInstance().stateInstrumentation.getRprof();
+    @Override
+    @TruffleBoundary
+    public void reportAllocation(RTypedValue data) {
+        long size = RObjectSize.getObjectSize(data, Rprofmem.myIgnoreObjectHandler);
+        if (data instanceof RAbstractVector) {
+            if (size >= Rprofmem.LARGE_VECTOR) {
+                profState.memoryQuad().largeV += size;
+            } else {
+                profState.memoryQuad().smallV += size;
+            }
+        } else {
+            profState.memoryQuad().nodes += size;
+        }
+
+    }
+
+    @Override
+    @TruffleBoundary
+    public void reportCopying(RAbstractVector source, RAbstractVector dest) {
+        profState.memoryQuad().copied += RObjectSize.getObjectSize(source, Rprofmem.myIgnoreObjectHandler);
+    }
+
+    private void endProfiling() {
         ProfileThread profileThread = (ProfileThread) profState.profileThread();
         profileThread.running = false;
         HashMap<String, Integer> fileMap = null;
         PrintWriter out = profState.out();
         StatementListener statementListener = (StatementListener) profState.statementListener();
+        if (profState.memoryProfiling()) {
+            out.print("memory profiling: ");
+        }
         if (profState.lineProfiling()) {
             out.print("line profiling: ");
         }
@@ -124,7 +175,12 @@ public abstract class Rprof extends RExternalBuiltinNode.Arg8 {
                 }
             }
         }
+        int index = 0;
         for (ArrayList<RSyntaxNode> intervalStack : statementListener.intervalStacks) {
+            if (profState.memoryProfiling()) {
+                MemoryQuad mq = statementListener.intervalMemory.get(index);
+                out.printf(":%d:%d:%d:%d:", mq.largeV, mq.smallV, mq.nodes, mq.copied);
+            }
             for (RSyntaxNode node : intervalStack) {
                 RootNode rootNode = node.asRNode().getRootNode();
                 if (rootNode instanceof FunctionDefinitionNode) {
@@ -139,8 +195,15 @@ public abstract class Rprof extends RExternalBuiltinNode.Arg8 {
                 }
             }
             out.println();
+            index++;
         }
         out.close();
+        profState.setOut(null);
+        if (profState.memoryProfiling()) {
+            RDataFactory.setAllocationTracing(false);
+            MemoryCopyTracer.setTracingState(false);
+        }
+
     }
 
     private static String getPath(RSyntaxNode node) {
@@ -177,8 +240,9 @@ public abstract class Rprof extends RExternalBuiltinNode.Arg8 {
      * Emulates a sampling timer by checking when the sample interval rolls over and at that point
      * collects the stack of functions.
      */
-    private static final class StatementListener implements ExecutionEventListener {
+    private final class StatementListener implements ExecutionEventListener {
         private ArrayList<ArrayList<RSyntaxNode>> intervalStacks = new ArrayList<>();
+        private ArrayList<MemoryQuad> intervalMemory = new ArrayList<>();
         private volatile boolean newInterval;
 
         private StatementListener() {
@@ -200,12 +264,16 @@ public abstract class Rprof extends RExternalBuiltinNode.Arg8 {
                 stack.add((RSyntaxNode) context.getInstrumentedNode());
                 collectStack(stack);
                 intervalStacks.add(stack);
+                if (profState.memoryProfiling()) {
+                    intervalMemory.add(profState.memoryQuad().copyAndClear());
+                }
+
                 newInterval = false;
             }
         }
 
         @TruffleBoundary
-        private static void collectStack(final ArrayList<RSyntaxNode> stack) {
+        private void collectStack(final ArrayList<RSyntaxNode> stack) {
             Utils.iterateRFrames(FrameAccess.READ_ONLY, new Function<Frame, Object>() {
 
                 @Override
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/utils/Rprofmem.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/utils/Rprofmem.java
new file mode 100644
index 0000000000000000000000000000000000000000..5fa5c672e50bcf65325c39085cb018110c0dc822
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/utils/Rprofmem.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (c) 2016, 2016, 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.utils;
+
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.api.frame.Frame;
+import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.r.nodes.builtin.RExternalBuiltinNode;
+import com.oracle.truffle.r.runtime.RArguments;
+import com.oracle.truffle.r.runtime.RError;
+import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.runtime.Utils;
+import com.oracle.truffle.r.runtime.context.RContext;
+import com.oracle.truffle.r.runtime.data.RNull;
+import com.oracle.truffle.r.runtime.data.RTypedValue;
+import com.oracle.truffle.r.runtime.data.RDataFactory;
+import com.oracle.truffle.r.runtime.data.RObjectSize;
+import com.oracle.truffle.r.runtime.data.RFunction;
+import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector;
+import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
+import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
+import com.oracle.truffle.r.runtime.instrument.InstrumentationState.RprofmemState;
+
+public abstract class Rprofmem extends RExternalBuiltinNode.Arg3 implements RDataFactory.Listener {
+
+    private RprofmemState profmemState;
+
+    @Specialization
+    @TruffleBoundary
+    public Object doRprofmem(RAbstractStringVector filenameVec, byte appendL, RAbstractDoubleVector thresholdVec) {
+        if (!RContext.getInstance().isInitial()) {
+            throw RError.error(this, RError.Message.GENERIC, "profiling not supported in created contexts");
+        }
+        String filename = filenameVec.getDataAt(0);
+        if (filename.length() == 0) {
+            // disable
+            endProfiling();
+        } else {
+            // enable after ending any previous session
+            profmemState = RContext.getInstance().stateInstrumentation.getRprofmem();
+            if (profmemState.out() != null) {
+                endProfiling();
+            }
+            boolean append = RRuntime.fromLogical(appendL);
+            try {
+                PrintWriter out = new PrintWriter(new FileWriter(filename, append));
+                profmemState.initialize(out, thresholdVec.getDataAt(0));
+                RDataFactory.addListener(this);
+                RDataFactory.setAllocationTracing(true);
+            } catch (IOException ex) {
+                throw RError.error(this, RError.Message.GENERIC, String.format("Rprofmem: cannot open profile file '%s'", filename));
+            }
+        }
+        return RNull.instance;
+    }
+
+    private void endProfiling() {
+        if (profmemState != null) {
+            RDataFactory.setAllocationTracing(false);
+            profmemState.out().flush();
+            profmemState.out().close();
+            profmemState.setOut(null);
+        }
+    }
+
+    private static final int PAGE_SIZE = 2000;
+    static final int LARGE_VECTOR = 128;
+
+    /**
+     * We ignore nested {@link RTypedValue} instances as these will have been counted already. We
+     * also ignore {@link Node} instances, except in {@link RFunction} objects.
+     */
+    private static class MyIgnoreObjectHandler implements RObjectSize.IgnoreObjectHandler {
+        @Override
+        public boolean ignore(Object rootObject, Object obj) {
+            if (obj == RNull.instance) {
+                return true;
+            } else {
+                Class<?> klass = obj.getClass();
+                if (RTypedValue.class.isAssignableFrom(klass)) {
+                    return true;
+                } else {
+                    return false;
+                }
+            }
+        }
+
+    }
+
+    static final RObjectSize.IgnoreObjectHandler myIgnoreObjectHandler = new MyIgnoreObjectHandler();
+
+    @Override
+    @TruffleBoundary
+    public void reportAllocation(RTypedValue data) {
+        // We could do some in memory buffering
+        // TODO write out full stack
+        Frame frame = Utils.getActualCurrentFrame();
+        if (frame == null) {
+            // not an R evaluation, some internal use
+            return;
+        }
+        RFunction func = RArguments.getFunction(frame);
+        if (func == null) {
+            return;
+        }
+        String name = func.getRootNode().getName();
+
+        long size = RObjectSize.getObjectSize(data, myIgnoreObjectHandler);
+        if (data instanceof RAbstractVector && size >= LARGE_VECTOR) {
+            profmemState.out().printf("%d: %s\n", size, name);
+        } else {
+            int pageCount = profmemState.pageCount();
+            long pcs = pageCount + size;
+            if (pcs > PAGE_SIZE) {
+                profmemState.out().printf("new page: %s\n", name);
+                profmemState.setPageCount((int) (pcs - PAGE_SIZE));
+            } else {
+                profmemState.setPageCount((int) pcs);
+            }
+        }
+
+    }
+
+}
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AllNames.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AllNames.java
index 70e3f59befe6762be709e18a834ab931dee2261f..905fb5e0d0e90b19380f9fc56fe0ee2f68a8d9fc 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AllNames.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AllNames.java
@@ -22,7 +22,6 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.*;
 import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE;
 import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
 
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BasePackage.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BasePackage.java
index 1b9d8c311222962e25d0bd5c7565c90cf4ea1b92..8170e1d1b19f680b8335398e890fe242f5ef98cb 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BasePackage.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BasePackage.java
@@ -574,8 +574,8 @@ public class BasePackage extends RBuiltinPackage {
         add(Tabulate.class, TabulateNodeGen::create);
         add(TempDir.class, TempDirNodeGen::create);
         add(TempFile.class, TempFileNodeGen::create);
-        add(ToLowerOrUpper.ToLower.class, ToLowerOrUpper::createToLower);
-        add(ToLowerOrUpper.ToUpper.class, ToLowerOrUpper::createToUpper);
+        add(ToLowerOrUpper.ToLower.class, ToLowerOrUpperFactory.ToLowerNodeGen::create);
+        add(ToLowerOrUpper.ToUpper.class, ToLowerOrUpperFactory.ToUpperNodeGen::create);
         add(Traceback.class, TracebackNodeGen::create);
         add(TraceFunctions.PrimTrace.class, TraceFunctionsFactory.PrimTraceNodeGen::create);
         add(TraceFunctions.PrimUnTrace.class, TraceFunctionsFactory.PrimUnTraceNodeGen::create);
@@ -624,8 +624,8 @@ public class BasePackage extends RBuiltinPackage {
         add(Vector.class, VectorNodeGen::create);
         add(Warning.class, WarningNodeGen::create);
         add(WhichFunctions.Which.class, WhichFunctionsFactory.WhichNodeGen::create);
-        add(WhichFunctions.WhichMax.class, WhichFunctionsFactory.WhichMaxNodeGen::create);
-        add(WhichFunctions.WhichMin.class, WhichFunctionsFactory.WhichMinNodeGen::create);
+        add(WhichFunctions.WhichMax.class, WhichFunctions.WhichMax::create);
+        add(WhichFunctions.WhichMin.class, WhichFunctions.WhichMin::create);
         add(Xtfrm.class, XtfrmNodeGen::create);
     }
 
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Combine.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Combine.java
index fcad0d222e6f7ac43fafc9bdd51bfecb6ee0119e..aa454c0002d8a29000c90055b468fb1ee87c8f77 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Combine.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Combine.java
@@ -362,7 +362,7 @@ public abstract class Combine extends RBuiltinNode {
             case LOGICAL_PRECEDENCE:
                 return CastLogicalNodeGen.create(true, false, false);
             case STRING_PRECEDENCE:
-                return CastStringNodeGen.create(true, false, false, false);
+                return CastStringNodeGen.create(true, false, false);
             case RAW_PRECEDENCE:
                 return CastRawNodeGen.create(true, false, false);
             case EXPRESSION_PRECEDENCE:
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Crossprod.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Crossprod.java
index 177768859206b1c9efe5fd9e697239ff55fffd5c..bd48e8980243abb1d75214c3cc5b599653657019 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Crossprod.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Crossprod.java
@@ -52,7 +52,7 @@ public abstract class Crossprod extends RBuiltinNode {
         return matMult.executeObject(op1, op2);
     }
 
-    private Object transpose(Object value) {
+    private Object transpose(RAbstractVector value) {
         if (transpose == null) {
             CompilerDirectives.transferToInterpreterAndInvalidate();
             transpose = insert(TransposeNodeGen.create(null));
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/FileFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/FileFunctions.java
index 9d902b5d481e52d677b754eee866fc0df6b3bf84..33546b1db6d04b2303b66e91a9fca9b7f9e497f7 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/FileFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/FileFunctions.java
@@ -741,7 +741,7 @@ public class FileFunctions {
         private CastStringNode initCastStringNode() {
             if (castStringNode == null) {
                 CompilerDirectives.transferToInterpreterAndInvalidate();
-                castStringNode = insert(CastStringNodeGen.create(false, false, false, false));
+                castStringNode = insert(CastStringNodeGen.create(false, false, false));
             }
             return castStringNode;
         }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/FormatC.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/FormatC.java
index c929d73972986ea4b0ac31e5336b72524325dac2..fa762a0a2f9e0fa7a67d30cc5a43595aaac4c592 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/FormatC.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/FormatC.java
@@ -35,7 +35,7 @@ public abstract class FormatC extends RBuiltinNode {
     private RStringVector castStringVector(Object o) {
         if (castStringNode == null) {
             CompilerDirectives.transferToInterpreterAndInvalidate();
-            castStringNode = insert(CastStringNodeGen.create(true, true, true, false));
+            castStringNode = insert(CastStringNodeGen.create(true, true, true));
         }
         return (RStringVector) ((RStringVector) castStringNode.executeString(o)).copyDropAttributes();
     }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/GetFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/GetFunctions.java
index 81d4ba97e495193b6a14d90228fa3747042825ba..19d0acc4b7f43f8d331e226fccf24349f207d157 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/GetFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/GetFunctions.java
@@ -56,7 +56,6 @@ import com.oracle.truffle.r.runtime.RError.Message;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.RType;
 import com.oracle.truffle.r.runtime.builtins.RBuiltin;
-import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RFunction;
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 cf527e00fb408b7a65f378b1031ff47245ac7122..bbb1f2bc9bffa1cc9b8065a6ac566e44bb48c950 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
@@ -80,7 +80,7 @@ public abstract class Match extends RBuiltinNode {
     private RAbstractStringVector castString(RAbstractVector operand) {
         if (castString == null) {
             CompilerDirectives.transferToInterpreterAndInvalidate();
-            castString = insert(CastStringNodeGen.create(false, false, false, false));
+            castString = insert(CastStringNodeGen.create(false, false, false));
         }
         return (RAbstractStringVector) castString.execute(operand);
     }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/NZChar.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/NZChar.java
index 7d4a5f2d108673a2310e1f2f826caf4e48faf24f..2400bc8fe76c7070f0f6fd940073e6772e22f7af 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/NZChar.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/NZChar.java
@@ -45,7 +45,7 @@ public abstract class NZChar extends RBuiltinNode {
     private String coerceContent(Object content) {
         if (convertString == null) {
             CompilerDirectives.transferToInterpreterAndInvalidate();
-            convertString = insert(CastStringNodeGen.create(false, false, false, false));
+            convertString = insert(CastStringNodeGen.create(false, false, false));
         }
         return (String) convertString.execute(content);
     }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/PMinMax.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/PMinMax.java
index a64a2900dbbed90e93852da8d52b9ade8aaee138..78b39957ba995d6bbf24687ff2a1f96a8f0cb11f 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/PMinMax.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/PMinMax.java
@@ -118,7 +118,7 @@ public abstract class PMinMax extends RBuiltinNode {
     private CastNode getStringCastNode() {
         if (castString == null) {
             CompilerDirectives.transferToInterpreterAndInvalidate();
-            castString = insert(CastStringNodeGen.create(true, true, true, false));
+            castString = insert(CastStringNodeGen.create(true, true, true));
         }
         return castString;
     }
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 4f61514f592b76207e3b56bf3f37b2172a344b72..10550c3ed5be05f2679fb47d7740b92f74fa5c80 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
@@ -113,7 +113,7 @@ public abstract class Parse extends RBuiltinNode {
         if (castStringNode == null) {
             CompilerDirectives.transferToInterpreterAndInvalidate();
             castVectorNode = insert(CastToVectorNodeGen.create(false));
-            castStringNode = insert(CastStringNodeGen.create(false, false, false, false));
+            castStringNode = insert(CastStringNodeGen.create(false, false, false));
         }
         return (RStringVector) castStringNode.executeString(castVectorNode.execute(s));
     }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Paste.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Paste.java
index 1935e7272540a4e7347a3ee11bd22f38799e49db..699b9fe4219a49ab2dd8e3b8977e5dacf4125e7f 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Paste.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Paste.java
@@ -40,9 +40,7 @@ import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RList;
 import com.oracle.truffle.r.runtime.data.RNull;
-import com.oracle.truffle.r.runtime.data.RSequence;
 import com.oracle.truffle.r.runtime.data.RStringVector;
-import com.oracle.truffle.r.runtime.data.RVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
 
 @RBuiltin(name = "paste", kind = INTERNAL, parameterNames = {"", "sep", "collapse"}, behavior = PURE)
@@ -71,7 +69,7 @@ public abstract class Paste extends RBuiltinNode {
     private RStringVector castCharacterVector(Object o) {
         if (castCharacterNode == null) {
             CompilerDirectives.transferToInterpreterAndInvalidate();
-            castCharacterNode = insert(CastStringNodeGen.create(false, false, false, false));
+            castCharacterNode = insert(CastStringNodeGen.create(false, false, false));
         }
         Object ret = castCharacterNode.executeString(o);
         if (ret instanceof String) {
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 db9729cdf6736b4720305df90f902393d8cb17e4..fdcad6132cc24b5119b071873ca097ca83765435 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
@@ -22,14 +22,19 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.singleElement;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.stringValue;
 import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE;
 import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
 
-import com.oracle.truffle.api.dsl.Fallback;
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.dsl.Cached;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.profiles.BranchProfile;
+import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.runtime.RError;
+import com.oracle.truffle.r.runtime.RError.Message;
 import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.builtins.RBuiltinKind;
 import com.oracle.truffle.r.runtime.context.RContext;
@@ -37,24 +42,34 @@ import com.oracle.truffle.r.runtime.data.RFunction;
 
 @RBuiltin(name = ".Primitive", kind = PRIMITIVE, parameterNames = "name", behavior = PURE)
 public abstract class Primitive extends RBuiltinNode {
+
     private final BranchProfile errorProfile = BranchProfile.create();
 
-    // TODO: implement inline caching
+    @Override
+    protected void createCasts(CastBuilder casts) {
+        casts.arg("name").defaultError(Message.STRING_ARGUMENT_REQUIRED).mustBe(stringValue()).asStringVector().mustBe(singleElement()).findFirst();
+    }
+
+    @Specialization(guards = "name == cachedName")
+    protected RFunction primitiveCached(@SuppressWarnings("unused") String name,
+                    @Cached("name") @SuppressWarnings("unused") String cachedName,
+                    @Cached("lookup(name)") RFunction function) {
+        return function;
+    }
 
-    @Specialization
+    @Specialization(contains = "primitiveCached")
     protected RFunction primitive(String name) {
+        RFunction function = lookup(name);
+        return function;
+    }
+
+    @TruffleBoundary
+    protected RFunction lookup(String name) {
         RFunction function = RContext.lookupBuiltin(name);
         if (function == null || function.getRBuiltin() != null && function.getRBuiltin().getKind() != RBuiltinKind.PRIMITIVE) {
             errorProfile.enter();
             throw RError.error(this, RError.Message.NO_SUCH_PRIMITIVE, name);
         }
-
-        // .Primitive function is validated
         return function;
     }
-
-    @Fallback
-    protected RFunction primitive(@SuppressWarnings("unused") Object name) {
-        throw RError.error(this, RError.Message.STRING_ARGUMENT_REQUIRED);
-    }
 }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Scan.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Scan.java
index 304af5f57e8333b6ea11b5a962f98d9607d0ca77..049375ae56f6834addf7f3827751016107526c6f 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Scan.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Scan.java
@@ -12,7 +12,16 @@
 
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.*;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.charAt0;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.constant;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.length;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.lengthLte;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.logicalValue;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.lt;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.nullValue;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.singleElement;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.stringValue;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.toBoolean;
 import static com.oracle.truffle.r.runtime.builtins.RBehavior.IO;
 import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
 
@@ -30,7 +39,6 @@ import com.oracle.truffle.r.nodes.unary.CastToVectorNodeGen;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.RRuntime;
-import com.oracle.truffle.r.runtime.builtins.RBehavior;
 import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.conn.RConnection;
 import com.oracle.truffle.r.runtime.conn.StdConnections;
@@ -45,14 +53,11 @@ import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.RRaw;
 import com.oracle.truffle.r.runtime.data.RString;
 import com.oracle.truffle.r.runtime.data.RVector;
-import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractLogicalVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
 import com.oracle.truffle.r.runtime.ops.na.NACheck;
-import com.sun.org.apache.bcel.internal.generic.INSTANCEOF;
 
-@SuppressWarnings("unused")
 @RBuiltin(name = "scan", kind = INTERNAL, parameterNames = {"file", "what", "nmax", "sep", "dec", "quote", "skip", "nlines", "na.strings", "flush", "fill", "strip.white", "quiet", "blank.lines.skip",
                 "multi.line", "comment.char", "allowEscapes", "encoding", "skipNull"}, behavior = IO)
 public abstract class Scan extends RBuiltinNode {
@@ -74,6 +79,7 @@ public abstract class Scan extends RBuiltinNode {
         return ((RAbstractVector) castVector.execute(value)).materialize();
     }
 
+    @SuppressWarnings("unused")
     private static class LocalData {
         RAbstractStringVector naStrings = null;
         boolean quiet = false;
@@ -181,7 +187,6 @@ public abstract class Scan extends RBuiltinNode {
         // TODO: quite a few more things happen in GNU R around connections
         data.con = file;
 
-        Object result = RNull.instance;
         data.save = 0;
 
         try (RConnection openConn = data.con.forceOpen("r")) {
@@ -199,14 +204,6 @@ public abstract class Scan extends RBuiltinNode {
         }
     }
 
-    private static int firstElementOrNA(RAbstractIntVector nmaxVec) {
-        return nmaxVec.getLength() == 0 ? RRuntime.INT_NA : nmaxVec.getDataAt(0);
-    }
-
-    private static byte firstElementOrNA(RAbstractLogicalVector flushVec) {
-        return flushVec.getLength() == 0 ? RRuntime.LOGICAL_NA : flushVec.getDataAt(0);
-    }
-
     private static int getFirstQuoteInd(String str, char sepChar) {
         int quoteInd = str.indexOf(sepChar);
         if (quoteInd >= 0) {
@@ -320,7 +317,8 @@ public abstract class Scan extends RBuiltinNode {
         }
     }
 
-    private RVector scanFrame(RList what, int maxRecords, int maxLines, boolean flush, boolean fill, boolean stripWhite, boolean blSkip, boolean multiLine, LocalData data) throws IOException {
+    private RVector scanFrame(RList what, int maxRecords, int maxLines, boolean flush, boolean fill, @SuppressWarnings("unused") boolean stripWhite, boolean blSkip, boolean multiLine, LocalData data)
+                    throws IOException {
 
         int nc = what.getLength();
         if (nc == 0) {
@@ -442,7 +440,8 @@ public abstract class Scan extends RBuiltinNode {
     }
 
     @TruffleBoundary
-    private RVector scanVector(RAbstractVector what, int maxItems, int maxLines, boolean flush, boolean stripWhite, boolean blSkip, LocalData data) throws IOException {
+    private RVector scanVector(RAbstractVector what, int maxItems, int maxLines, @SuppressWarnings("unused") boolean flush, @SuppressWarnings("unused") boolean stripWhite, boolean blSkip,
+                    LocalData data) throws IOException {
         int blockSize = maxItems > 0 ? maxItems : SCAN_BLOCKSIZE;
         RVector vec = what.createEmptySameType(blockSize, RDataFactory.COMPLETE_VECTOR);
         naCheck.enable(true);
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Strtrim.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Strtrim.java
index f7fe84aa1e73285af79cd08a77842cf3f4f1eb9d..6124f541a90c1eeefcf644560379ea0a185ed8ba 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Strtrim.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Strtrim.java
@@ -15,16 +15,21 @@ package com.oracle.truffle.r.nodes.builtin.base;
 import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE;
 import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
 
+import java.util.function.BiFunction;
+
+import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
-import com.oracle.truffle.api.dsl.Fallback;
+import com.oracle.truffle.api.dsl.Cached;
 import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.api.profiles.ConditionProfile;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
+import com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
+import com.oracle.truffle.r.nodes.builtin.base.ToLowerOrUpper.StringMapNode;
 import com.oracle.truffle.r.runtime.RError;
+import com.oracle.truffle.r.runtime.RError.Message;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.builtins.RBuiltin;
-import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
-import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RStringVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
@@ -32,52 +37,43 @@ import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
 @RBuiltin(name = "strtrim", kind = INTERNAL, parameterNames = {"x", "width"}, behavior = PURE)
 public abstract class Strtrim extends RBuiltinNode {
 
-    private RAttributeProfiles attrProfiles = RAttributeProfiles.create();
-
     @Override
     protected void createCasts(CastBuilder casts) {
-        casts.toInteger(1);
+        casts.arg("x").mustBe(Predef.stringValue(), Message.REQUIRES_CHAR_VECTOR, "strtrim()").asStringVector(true, true, true);
+        casts.arg("width").asIntegerVector();
     }
 
     @Specialization
-    protected RStringVector srtrim(RAbstractStringVector x, RAbstractIntVector width) {
+    protected RStringVector srtrim(RAbstractStringVector x, RAbstractIntVector width,
+                    @Cached("create()") StringMapNode mapNode,
+                    @Cached("createBinaryProfile()") ConditionProfile fitsProfile) {
         int len = x.getLength();
         int nw = width.getLength();
         if (nw == 0 || nw < len && (len % nw != 0)) {
+            CompilerDirectives.transferToInterpreter();
             throw RError.error(this, RError.Message.INVALID_ARGUMENT, "width");
         }
         for (int i = 0; i < nw; i++) {
-            int widthi = width.getDataAt(i);
-            if (widthi == RRuntime.INT_NA || widthi < 0) {
+            assert RRuntime.INT_NA < 0; // check for NA folded into < 0
+            if (width.getDataAt(i) < 0) {
+                CompilerDirectives.transferToInterpreter();
                 throw RError.error(this, RError.Message.INVALID_ARGUMENT, "width");
             }
         }
-        String[] data = new String[len];
-        boolean complete = RDataFactory.COMPLETE_VECTOR;
-        for (int i = 0; i < len; i++) {
-            String element = x.getDataAt(i);
-            if (RRuntime.isNA(element)) {
-                data[i] = element;
-                complete = RDataFactory.INCOMPLETE_VECTOR;
-                continue;
-            }
+        BiFunction<String, Integer, String> function = (element, i) -> {
             // TODO multibyte character handling
             int w = width.getDataAt(i % nw);
-            if (w > element.length()) {
-                data[i] = element;
+            if (fitsProfile.profile(w >= element.length())) {
+                return element;
             } else {
-                data[i] = element.substring(0, w);
+                return substring(element, w);
             }
-        }
-        RStringVector result = RDataFactory.createStringVector(data, complete);
-        result.copyAttributesFrom(attrProfiles, x);
-        return result;
+        };
+        return mapNode.apply(x, function);
     }
 
-    @SuppressWarnings("unused")
-    @Fallback
     @TruffleBoundary
-    RStringVector strtrim(Object x, Object width) {
-        throw RError.error(this, RError.Message.REQUIRES_CHAR_VECTOR, "strtrim()");
+    private static String substring(String element, int w) {
+        return element.substring(0, w);
     }
 }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ToLowerOrUpper.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ToLowerOrUpper.java
index d12bcadb18d22e87bd27a4b3046fe3b117c3aeb4..4010f36ea20ed08601c93d5b0f72456d7b58d100 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ToLowerOrUpper.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ToLowerOrUpper.java
@@ -22,73 +22,122 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.stringValue;
 import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE;
 import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
 
+import java.util.function.BiFunction;
+import java.util.function.Function;
+
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
-import com.oracle.truffle.api.dsl.Cached;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.profiles.LoopConditionProfile;
 import com.oracle.truffle.r.nodes.attributes.CopyOfRegAttributesNode;
+import com.oracle.truffle.r.nodes.attributes.CopyOfRegAttributesNodeGen;
+import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
+import com.oracle.truffle.r.nodes.profile.VectorLengthProfile;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.builtins.RBuiltin;
+import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RStringVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
-import com.oracle.truffle.r.runtime.nodes.RNode;
+import com.oracle.truffle.r.runtime.nodes.RBaseNode;
 import com.oracle.truffle.r.runtime.ops.na.NACheck;
-import com.oracle.truffle.r.runtime.ops.na.NAProfile;
 
-public abstract class ToLowerOrUpper extends RBuiltinNode {
+public abstract class ToLowerOrUpper {
 
-    @RBuiltin(name = "tolower", kind = INTERNAL, parameterNames = {"x"}, behavior = PURE)
-    public static final class ToLower {
-    }
+    public static final class StringMapNode extends RBaseNode {
 
-    @RBuiltin(name = "toupper", kind = INTERNAL, parameterNames = {"x"}, behavior = PURE)
-    public static final class ToUpper {
-    }
+        private final VectorLengthProfile lengthProfile = VectorLengthProfile.create();
+        private final LoopConditionProfile loopProfile = LoopConditionProfile.createCountingProfile();
+        private final NACheck na = NACheck.create();
+        private final RAttributeProfiles attrProfiles = RAttributeProfiles.create();
 
-    public static ToLowerOrUpper createToLower(RNode[] arguments) {
-        return ToLowerOrUpperNodeGen.create(true, arguments);
-    }
+        @Child private CopyOfRegAttributesNode copyAttributes = CopyOfRegAttributesNodeGen.create();
 
-    public static ToLowerOrUpper createToUpper(RNode[] arguments) {
-        return ToLowerOrUpperNodeGen.create(false, arguments);
-    }
+        private StringMapNode() {
+            // nothing to do
+        }
 
-    private final boolean lower;
+        public static StringMapNode create() {
+            return new StringMapNode();
+        }
 
-    public ToLowerOrUpper(boolean lower) {
-        this.lower = lower;
-    }
+        private String elementFunction(String value, int i, BiFunction<String, Integer, String> function) {
+            return na.check(value) ? RRuntime.STRING_NA : function.apply(value, i);
+        }
+
+        public String apply(String value, BiFunction<String, Integer, String> function) {
+            na.enable(value);
+            return elementFunction(value, 0, function);
+        }
 
-    @TruffleBoundary
-    private String processElement(String value) {
-        return lower ? value.toLowerCase() : value.toUpperCase();
+        public RStringVector apply(RAbstractStringVector vector, BiFunction<String, Integer, String> function) {
+            na.enable(vector);
+            int length = lengthProfile.profile(vector.getLength());
+            String[] stringVector = new String[length];
+            loopProfile.profileCounted(length);
+            for (int i = 0; loopProfile.inject(i < length); i++) {
+                String value = vector.getDataAt(i);
+                stringVector[i] = elementFunction(value, i, function);
+            }
+            RStringVector result = RDataFactory.createStringVector(stringVector, vector.isComplete(), vector.getDimensions(), vector.getNames(attrProfiles));
+            copyAttributes.execute(vector, result);
+            return result;
+        }
     }
 
-    @Specialization
-    protected String toLower(String value, //
-                    @Cached("create()") NAProfile na) {
-        return na.isNA(value) ? RRuntime.STRING_NA : processElement(value);
+    @RBuiltin(name = "tolower", kind = INTERNAL, parameterNames = {"x"}, behavior = PURE)
+    public abstract static class ToLower extends RBuiltinNode {
+
+        @Child private StringMapNode mapNode = StringMapNode.create();
+
+        @Override
+        protected void createCasts(CastBuilder casts) {
+            casts.arg(0, "x").mustBe(stringValue()).asStringVector(true, true, true);
+        }
+
+        @TruffleBoundary
+        private static String processElement(String value, int i) {
+            return value.toLowerCase();
+        }
+
+        @Specialization
+        protected String toLower(String value) {
+            return mapNode.apply(value, ToLower::processElement);
+        }
+
+        @Specialization
+        protected RStringVector toLower(RAbstractStringVector vector) {
+            return mapNode.apply(vector, ToLower::processElement);
+        }
     }
 
-    @Specialization
-    protected RStringVector toLower(RAbstractStringVector vector, //
-                    @Cached("createCountingProfile()") LoopConditionProfile loopProfile, //
-                    @Cached("create()") NACheck na, //
-                    @Cached("create()") CopyOfRegAttributesNode copyAttributes) {
-        na.enable(vector);
-        String[] stringVector = new String[vector.getLength()];
-        loopProfile.profileCounted(vector.getLength());
-        for (int i = 0; loopProfile.inject(i < vector.getLength()); i++) {
-            String value = vector.getDataAt(i);
-            stringVector[i] = na.check(value) ? RRuntime.STRING_NA : processElement(value);
+    @RBuiltin(name = "toupper", kind = INTERNAL, parameterNames = {"x"}, behavior = PURE)
+    public abstract static class ToUpper extends RBuiltinNode {
+
+        @Child private StringMapNode mapNode = StringMapNode.create();
+
+        @Override
+        protected void createCasts(CastBuilder casts) {
+            casts.arg(0, "x").mustBe(stringValue()).asStringVector(true, true, true);
+        }
+
+        @TruffleBoundary
+        private static String processElement(String value, int i) {
+            return value.toUpperCase();
+        }
+
+        @Specialization
+        protected String toLower(String value) {
+            return mapNode.apply(value, ToUpper::processElement);
+        }
+
+        @Specialization
+        protected RStringVector toLower(RAbstractStringVector vector) {
+            return mapNode.apply(vector, ToUpper::processElement);
         }
-        RStringVector result = RDataFactory.createStringVector(stringVector, vector.isComplete(), vector.getDimensions());
-        copyAttributes.execute(vector, result);
-        return result;
     }
 }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/TraceFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/TraceFunctions.java
index d294b653a4db4d9fc01226325f0dca790b9db417..13c44d60cfdd6e5a75207c2b9d794f8507b106a1 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/TraceFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/TraceFunctions.java
@@ -54,7 +54,7 @@ import com.oracle.truffle.r.runtime.RType;
 import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.conn.StdConnections;
 import com.oracle.truffle.r.runtime.context.RContext;
-import com.oracle.truffle.r.runtime.data.MemoryTracer;
+import com.oracle.truffle.r.runtime.data.MemoryCopyTracer;
 import com.oracle.truffle.r.runtime.data.RFunction;
 import com.oracle.truffle.r.runtime.data.RMissing;
 import com.oracle.truffle.r.runtime.data.RNull;
@@ -79,7 +79,7 @@ public class TraceFunctions {
     public abstract static class PrimTrace extends Helper {
 
         @Specialization
-        protected RNull primUnTrace(VirtualFrame frame, RAbstractStringVector funcName) {
+        protected RNull primTrace(VirtualFrame frame, RAbstractStringVector funcName) {
             return primTrace((RFunction) getFunction(frame, funcName.getDataAt(0)));
         }
 
@@ -118,10 +118,12 @@ public class TraceFunctions {
         @Specialization
         @TruffleBoundary
         protected byte traceOnOff(byte state) {
+            /* TODO GnuR appears to accept ANY value as an argument */
             boolean prevState = RContext.getInstance().stateInstrumentation.getTracingState();
             boolean newState = RRuntime.fromLogical(state);
             if (newState != prevState) {
                 RContext.getInstance().stateInstrumentation.setTracingState(newState);
+                MemoryCopyTracer.setTracingState(newState);
             }
             return RRuntime.asLogical(prevState);
         }
@@ -135,7 +137,7 @@ public class TraceFunctions {
 
     public abstract static class TracememBase extends RBuiltinNode {
         static {
-            MemoryTracer.setListener(new TracememBase.TracememListener());
+            MemoryCopyTracer.addListener(new TracememBase.TracememListener());
         }
 
         protected static HashSet<Object> getTracedObjects() {
@@ -147,8 +149,16 @@ public class TraceFunctions {
         }
 
         protected static void startTracing(Object x) {
+            /*
+             * There is no explicit command to enable tracing, it is implicit in the call to
+             * tracemem. However, it can be disabled by tracingState(F), so we can't unilaterally
+             * turn on tracing here.
+             */
             getTracedObjects().add(x);
-            MemoryTracer.reportEvents();
+            boolean tracingState = RContext.getInstance().stateInstrumentation.getTracingState();
+            if (tracingState) {
+                MemoryCopyTracer.setTracingState(true);
+            }
         }
 
         protected static void printToStdout(String msg) {
@@ -176,7 +186,7 @@ public class TraceFunctions {
             return result.toString();
         }
 
-        private static final class TracememListener implements MemoryTracer.Listener {
+        private static final class TracememListener implements MemoryCopyTracer.Listener {
             @Override
             public void reportCopying(RAbstractVector src, RAbstractVector dest) {
                 if (getTracedObjects().contains(src)) {
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Transpose.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Transpose.java
index c1a394c20d9e60501af8df2d9a4d7e1e24272140..e9088eeb636daa0ba882e7913ceef4134f86132b 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Transpose.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Transpose.java
@@ -13,88 +13,86 @@
 package com.oracle.truffle.r.nodes.builtin.base;
 
 import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE;
-import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.SUBSTITUTE;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
 
+import java.util.function.BiFunction;
+import java.util.function.Function;
+
+import com.oracle.truffle.api.dsl.Fallback;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.profiles.BranchProfile;
 import com.oracle.truffle.api.profiles.ConditionProfile;
+import com.oracle.truffle.api.profiles.LoopConditionProfile;
 import com.oracle.truffle.r.nodes.attributes.CopyOfRegAttributesNode;
 import com.oracle.truffle.r.nodes.attributes.CopyOfRegAttributesNodeGen;
 import com.oracle.truffle.r.nodes.attributes.InitAttributesNode;
 import com.oracle.truffle.r.nodes.attributes.PutAttributeNode;
 import com.oracle.truffle.r.nodes.attributes.PutAttributeNodeGen;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
+import com.oracle.truffle.r.nodes.profile.VectorLengthProfile;
+import com.oracle.truffle.r.runtime.RError;
+import com.oracle.truffle.r.runtime.RError.Message;
 import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
+import com.oracle.truffle.r.runtime.data.RComplex;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RList;
-import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.RVector;
+import com.oracle.truffle.r.runtime.data.model.RAbstractComplexVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
+import com.oracle.truffle.r.runtime.data.model.RAbstractListVector;
+import com.oracle.truffle.r.runtime.data.model.RAbstractLogicalVector;
+import com.oracle.truffle.r.runtime.data.model.RAbstractRawVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
 import com.oracle.truffle.r.runtime.nodes.RNode;
 
-@RBuiltin(name = "t.default", kind = SUBSTITUTE, parameterNames = {"x"}, behavior = PURE)
-// TODO INTERNAL
+@RBuiltin(name = "t.default", kind = INTERNAL, parameterNames = {"x"}, behavior = PURE)
 public abstract class Transpose extends RBuiltinNode {
 
     private final RAttributeProfiles attrProfiles = RAttributeProfiles.create();
     private final BranchProfile hasDimNamesProfile = BranchProfile.create();
     private final ConditionProfile isMatrixProfile = ConditionProfile.createBinaryProfile();
 
+    private final VectorLengthProfile lengthProfile = VectorLengthProfile.create();
+    private final LoopConditionProfile loopProfile = LoopConditionProfile.createCountingProfile();
+
     @Child private CopyOfRegAttributesNode copyRegAttributes = CopyOfRegAttributesNodeGen.create();
     @Child private InitAttributesNode initAttributes = InitAttributesNode.create();
     @Child private PutAttributeNode putDimensions = PutAttributeNodeGen.createDim();
     @Child private PutAttributeNode putDimNames = PutAttributeNodeGen.createDimNames();
 
-    public abstract Object execute(Object o);
-
-    @Specialization
-    protected RNull transpose(RNull value) {
-        return value;
-    }
-
-    @Specialization
-    protected int transpose(int value) {
-        return value;
-    }
-
-    @Specialization
-    protected double transpose(double value) {
-        return value;
-    }
-
-    @Specialization
-    protected byte transpose(byte value) {
-        return value;
-    }
-
-    @Specialization(guards = "isEmpty2D(vector)")
-    protected RAbstractVector transpose(RAbstractVector vector) {
-        int[] dim = vector.getDimensions();
-        return vector.copyWithNewDimensions(new int[]{dim[1], dim[0]});
-    }
+    public abstract Object execute(RAbstractVector o);
 
     @FunctionalInterface
-    private interface InnerLoop<T extends RAbstractVector> {
-        RVector apply(T vector, int firstDim);
+    private interface WriteArray<T extends RAbstractVector, A> {
+        void apply(A array, T vector, int i, int j);
     }
 
-    protected <T extends RAbstractVector> RVector transposeInternal(T vector, InnerLoop<T> innerLoop) {
+    protected <T extends RAbstractVector, A> RVector transposeInternal(T vector, Function<Integer, A> createArray, WriteArray<T, A> writeArray, BiFunction<A, Boolean, RVector> createResult) {
+        int length = lengthProfile.profile(vector.getLength());
         int firstDim;
         int secondDim;
         if (isMatrixProfile.profile(vector.isMatrix())) {
             firstDim = vector.getDimensions()[0];
             secondDim = vector.getDimensions()[1];
         } else {
-            firstDim = vector.getLength();
+            firstDim = length;
             secondDim = 1;
         }
-        RNode.reportWork(this, vector.getLength());
+        RNode.reportWork(this, length);
 
-        RVector r = innerLoop.apply(vector, firstDim);
+        A array = createArray.apply(length);
+        int j = 0;
+        loopProfile.profileCounted(length);
+        for (int i = 0; loopProfile.inject(i < length); i++, j += firstDim) {
+            if (j > (length - 1)) {
+                j -= (length - 1);
+            }
+            writeArray.apply(array, vector, i, j);
+        }
+        RVector r = createResult.apply(array, vector.isComplete());
         // copy attributes
         copyRegAttributes.execute(vector, r);
         // set new dimensions
@@ -113,61 +111,47 @@ public abstract class Transpose extends RBuiltinNode {
         return r;
     }
 
-    private static RVector innerLoopInt(RAbstractIntVector vector, int firstDim) {
-        int[] result = new int[vector.getLength()];
-        int j = 0;
-        for (int i = 0; i < result.length; i++, j += firstDim) {
-            if (j > (result.length - 1)) {
-                j -= (result.length - 1);
-            }
-            result[i] = vector.getDataAt(j);
-        }
-        return RDataFactory.createIntVector(result, vector.isComplete());
+    @Specialization
+    protected RVector transpose(RAbstractIntVector x) {
+        return transposeInternal(x, l -> new int[l], (a, v, i, j) -> a[i] = v.getDataAt(j), RDataFactory::createIntVector);
     }
 
-    private static RVector innerLoopDouble(RAbstractDoubleVector vector, int firstDim) {
-        double[] result = new double[vector.getLength()];
-        int j = 0;
-        for (int i = 0; i < result.length; i++, j += firstDim) {
-            if (j > (result.length - 1)) {
-                j -= (result.length - 1);
-            }
-            result[i] = vector.getDataAt(j);
-        }
-        return RDataFactory.createDoubleVector(result, vector.isComplete());
+    @Specialization
+    protected RVector transpose(RAbstractLogicalVector x) {
+        return transposeInternal(x, l -> new byte[l], (a, v, i, j) -> a[i] = v.getDataAt(j), RDataFactory::createLogicalVector);
     }
 
-    private static RVector innerLoopString(RAbstractStringVector vector, int firstDim) {
-        String[] result = new String[vector.getLength()];
-        int j = 0;
-        for (int i = 0; i < result.length; i++, j += firstDim) {
-            if (j > (result.length - 1)) {
-                j -= (result.length - 1);
-            }
-            result[i] = vector.getDataAt(j);
-        }
-        return RDataFactory.createStringVector(result, vector.isComplete());
+    @Specialization
+    protected RVector transpose(RAbstractDoubleVector x) {
+        return transposeInternal(x, l -> new double[l], (a, v, i, j) -> a[i] = v.getDataAt(j), RDataFactory::createDoubleVector);
     }
 
-    @Specialization(guards = "!isEmpty2D(vector)")
-    protected RVector transpose(RAbstractIntVector vector) {
-        return transposeInternal(vector, Transpose::innerLoopInt);
+    @Specialization
+    protected RVector transpose(RAbstractComplexVector x) {
+        return transposeInternal(x, l -> new double[l * 2], (a, v, i, j) -> {
+            RComplex d = v.getDataAt(j);
+            a[i * 2] = d.getRealPart();
+            a[i * 2 + 1] = d.getImaginaryPart();
+        }, RDataFactory::createComplexVector);
     }
 
-    @Specialization(guards = "!isEmpty2D(vector)")
-    protected RVector transpose(RAbstractDoubleVector vector) {
-        return transposeInternal(vector, Transpose::innerLoopDouble);
+    @Specialization
+    protected RVector transpose(RAbstractStringVector x) {
+        return transposeInternal(x, l -> new String[l], (a, v, i, j) -> a[i] = v.getDataAt(j), RDataFactory::createStringVector);
     }
 
-    @Specialization(guards = "!isEmpty2D(vector)")
-    protected RVector transpose(RAbstractStringVector vector) {
-        return transposeInternal(vector, Transpose::innerLoopString);
+    @Specialization
+    protected RVector transpose(RAbstractListVector x) {
+        return transposeInternal(x, l -> new Object[l], (a, v, i, j) -> a[i] = v.getDataAt(j), (a, c) -> RDataFactory.createList(a));
     }
 
-    protected static boolean isEmpty2D(RAbstractVector vector) {
-        if (!vector.hasDimensions()) {
-            return false;
-        }
-        return vector.getDimensions().length == 2 && vector.getLength() == 0;
+    @Specialization
+    protected RVector transpose(RAbstractRawVector x) {
+        return transposeInternal(x, l -> new byte[l], (a, v, i, j) -> a[i] = v.getRawDataAt(j), (a, c) -> RDataFactory.createRawVector(a));
+    }
+
+    @Fallback
+    protected RVector transpose(@SuppressWarnings("unused") Object x) {
+        throw RError.error(RError.SHOW_CALLER, Message.ARGUMENT_NOT_MATRIX);
     }
 }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateClass.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateClass.java
index 25207ed80eabb2649f34aff15ec6b84215f3dc85..bba597a2585760808ef85fadaae692cb3156482f 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateClass.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateClass.java
@@ -67,7 +67,7 @@ public abstract class UpdateClass extends RBuiltinNode {
     private void initCastStringNode() {
         if (castStringNode == null) {
             CompilerDirectives.transferToInterpreterAndInvalidate();
-            castStringNode = insert(CastStringNodeGen.create(false, false, false, false));
+            castStringNode = insert(CastStringNodeGen.create(false, false, false));
         }
     }
 
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateDimNames.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateDimNames.java
index c1949bd51d41c8ce87e9beb8f4718d69b63c677a..9b49702aa2fcc30d926f3fde4b21f6213c0cc8ba 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateDimNames.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateDimNames.java
@@ -61,7 +61,7 @@ public abstract class UpdateDimNames extends RBuiltinNode {
     private Object castString(Object o) {
         if (castStringNode == null) {
             CompilerDirectives.transferToInterpreterAndInvalidate();
-            castStringNode = insert(CastStringNodeGen.create(true, true, true, false));
+            castStringNode = insert(CastStringNodeGen.create(true, true, true));
         }
         return castStringNode.execute(o);
     }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateNames.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateNames.java
index 4d92e592fb6e56ef7f5c4a7150d58add79cbb3ea..fd9b979a7964a6a0b0532c753bfff0a6e8d5c3f4 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateNames.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateNames.java
@@ -48,7 +48,7 @@ public abstract class UpdateNames extends RBuiltinNode {
     private Object castString(Object o) {
         if (castStringNode == null) {
             CompilerDirectives.transferToInterpreterAndInvalidate();
-            castStringNode = insert(CastStringNodeGen.create(false, false, false, false));
+            castStringNode = insert(CastStringNodeGen.create(false, false, false));
         }
         return castStringNode.executeString(o);
     }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateOldClass.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateOldClass.java
index 963fb48e49d9bc006ffb8007ca700d4a03560932..f05261a585f42113af69f41576f5a2897eddc6df 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateOldClass.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateOldClass.java
@@ -59,7 +59,7 @@ public abstract class UpdateOldClass extends RBuiltinNode {
     private void initCastStringNode() {
         if (castStringNode == null) {
             CompilerDirectives.transferToInterpreterAndInvalidate();
-            castStringNode = insert(CastStringNodeGen.create(false, false, false, false));
+            castStringNode = insert(CastStringNodeGen.create(false, false, false));
         }
     }
 
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/VApply.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/VApply.java
index 3d608da7397fb8e2c95b3bb9214288837354361c..b878565580cb566b94d9b2728004d11527840110 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/VApply.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/VApply.java
@@ -126,7 +126,7 @@ public abstract class VApply extends RBuiltinNode {
     private Object castString(Object operand, boolean preserveAllAttr) {
         if (castString == null) {
             CompilerDirectives.transferToInterpreterAndInvalidate();
-            castString = insert(CastStringNodeGen.create(true, preserveAllAttr, preserveAllAttr, false));
+            castString = insert(CastStringNodeGen.create(true, preserveAllAttr, preserveAllAttr));
         }
         return castString.execute(operand);
     }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Warning.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Warning.java
index 249cee47c1bb624b7831a854d95cf177a698e389..6ee52996d3b615a84c3bc978430ab84f9962f23d 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Warning.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Warning.java
@@ -47,7 +47,7 @@ public abstract class Warning extends RBuiltinNode {
     private Object castString(Object operand) {
         if (castString == null) {
             CompilerDirectives.transferToInterpreterAndInvalidate();
-            castString = insert(CastStringNodeGen.create(false, false, false, false));
+            castString = insert(CastStringNodeGen.create(false, false, false));
         }
         return castString.execute(operand);
     }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/WhichFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/WhichFunctions.java
index ffa0cc5c29b71e409b06dadc9080542cfd02c7cb..17662ce966d830026fbd35be2cfbb5ba69a8f6b2 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/WhichFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/WhichFunctions.java
@@ -22,23 +22,28 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.logicalValue;
 import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE;
 import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
 
-import java.util.ArrayList;
-
-import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.dsl.Cached;
 import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.api.profiles.ConditionProfile;
+import com.oracle.truffle.api.profiles.LoopConditionProfile;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
+import com.oracle.truffle.r.nodes.builtin.base.WhichFunctionsFactory.WhichMinMaxNodeGen;
+import com.oracle.truffle.r.nodes.profile.VectorLengthProfile;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RIntVector;
+import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.RStringVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractLogicalVector;
+import com.oracle.truffle.r.runtime.nodes.RNode;
 import com.oracle.truffle.r.runtime.ops.na.NACheck;
 
 /**
@@ -49,94 +54,125 @@ public class WhichFunctions {
     @RBuiltin(name = "which", kind = INTERNAL, parameterNames = {"x"}, behavior = PURE)
     public abstract static class Which extends RBuiltinNode {
 
-        private final NACheck naCheck = NACheck.create();
-        private final RAttributeProfiles attrProfiles = RAttributeProfiles.create();
+        @Override
+        protected void createCasts(CastBuilder casts) {
+            casts.arg("x").mustBe(logicalValue()).asLogicalVector();
+        }
 
-        @Specialization(guards = "!hasNames(x)")
-        @TruffleBoundary
-        protected RIntVector which(RAbstractLogicalVector x) {
-            ArrayList<Integer> w = new ArrayList<>();
-            for (int i = 0; i < x.getLength(); i++) {
+        @Specialization
+        protected RIntVector which(RAbstractLogicalVector x,
+                        @Cached("create()") VectorLengthProfile lengthProfile,
+                        @Cached("createCountingProfile()") LoopConditionProfile loopProfile,
+                        @Cached("createBinaryProfile()") ConditionProfile hasNamesProfile,
+                        @Cached("create()") RAttributeProfiles attrProfiles,
+                        @Cached("create()") NACheck naCheck) {
+            int length = lengthProfile.profile(x.getLength());
+            loopProfile.profileCounted(length);
+            // determine the length of the result
+            int resultLength = 0;
+            for (int i = 0; loopProfile.inject(i < length); i++) {
                 if (x.getDataAt(i) == RRuntime.LOGICAL_TRUE) {
-                    w.add(i);
+                    resultLength++;
                 }
             }
-            int[] result = new int[w.size()];
-            for (int i = 0; i < result.length; i++) {
-                result[i] = w.get(i) + 1;
-            }
-            return RDataFactory.createIntVector(result, RDataFactory.COMPLETE_VECTOR);
-        }
-
-        @Specialization(guards = "hasNames(x)")
-        @TruffleBoundary
-        protected RIntVector whichNames(RAbstractLogicalVector x) {
-            ArrayList<Integer> w = new ArrayList<>();
-            ArrayList<String> n = new ArrayList<>();
-            RStringVector oldNames = x.getNames(attrProfiles);
-            naCheck.enable(oldNames);
-            for (int i = 0; i < x.getLength(); i++) {
+            // collect result indexes
+            int[] result = new int[resultLength];
+            int pos = 0;
+            for (int i = 0; loopProfile.inject(i < length); i++) {
                 if (x.getDataAt(i) == RRuntime.LOGICAL_TRUE) {
-                    w.add(i);
-                    String s = oldNames.getDataAt(i);
-                    naCheck.check(s);
-                    n.add(s);
+                    result[pos++] = i + 1;
                 }
             }
-            int[] result = new int[w.size()];
-            for (int i = 0; i < result.length; i++) {
-                result[i] = w.get(i) + 1;
+            RStringVector names = x.getNames(attrProfiles);
+            if (hasNamesProfile.profile(names != null)) {
+                // collect result names
+                String[] resultNames = new String[resultLength];
+                naCheck.enable(names);
+                pos = 0;
+                for (int i = 0; i < x.getLength(); i++) {
+                    if (x.getDataAt(i) == RRuntime.LOGICAL_TRUE) {
+                        String name = names.getDataAt(i);
+                        naCheck.check(name);
+                        resultNames[pos++] = name;
+                    }
+                }
+                return RDataFactory.createIntVector(result, RDataFactory.COMPLETE_VECTOR, RDataFactory.createStringVector(resultNames, naCheck.neverSeenNA()));
+            } else {
+                return RDataFactory.createIntVector(result, RDataFactory.COMPLETE_VECTOR);
             }
-            String[] names = new String[n.size()];
-            return RDataFactory.createIntVector(result, RDataFactory.COMPLETE_VECTOR, RDataFactory.createStringVector(n.toArray(names), naCheck.neverSeenNA()));
-        }
-
-        protected boolean hasNames(RAbstractLogicalVector x) {
-            return x.getNames(attrProfiles) != null;
         }
     }
 
     @RBuiltin(name = "which.max", kind = INTERNAL, parameterNames = {"x"}, behavior = PURE)
-    public abstract static class WhichMax extends RBuiltinNode {
-
-        @Override
-        protected void createCasts(CastBuilder casts) {
-            casts.toDouble(0);
+    public abstract static class WhichMax {
+        private WhichMax() {
+            // private
         }
 
-        @Specialization
-        protected int which(RAbstractDoubleVector x) {
-            double max = x.getDataAt(0);
-            int maxIndex = 0;
-            for (int i = 0; i < x.getLength(); i++) {
-                if (x.getDataAt(i) > max) {
-                    max = x.getDataAt(i);
-                    maxIndex = i;
-                }
-            }
-            return maxIndex + 1;
+        public static WhichMinMax create(RNode[] arguments) {
+            return WhichMinMaxNodeGen.create(true, arguments);
         }
     }
 
     @RBuiltin(name = "which.min", kind = INTERNAL, parameterNames = {"x"}, behavior = PURE)
-    public abstract static class WhichMin extends RBuiltinNode {
+    public abstract static class WhichMin {
+        private WhichMin() {
+            // private
+        }
+
+        public static WhichMinMax create(RNode[] arguments) {
+            return WhichMinMaxNodeGen.create(false, arguments);
+        }
+    }
+
+    public abstract static class WhichMinMax extends RBuiltinNode {
+
+        private final boolean isMax;
+
+        protected WhichMinMax(boolean isMax) {
+            this.isMax = isMax;
+        }
 
         @Override
         protected void createCasts(CastBuilder casts) {
-            casts.toDouble(0);
+            casts.arg(0, "x").asDoubleVector(true, false, false);
         }
 
         @Specialization
-        protected int which(RAbstractDoubleVector x) {
-            double minimum = x.getDataAt(0);
-            int minIndex = 0;
-            for (int i = 0; i < x.getLength(); i++) {
-                if (x.getDataAt(i) < minimum) {
-                    minimum = x.getDataAt(i);
-                    minIndex = i;
+        protected RIntVector which(RAbstractDoubleVector x,
+                        @Cached("create()") VectorLengthProfile lengthProfile,
+                        @Cached("createCountingProfile()") LoopConditionProfile loopProfile,
+                        @Cached("createBinaryProfile()") ConditionProfile isNaNProfile,
+                        @Cached("createBinaryProfile()") ConditionProfile hasNamesProfile,
+                        @Cached("create()") RAttributeProfiles attrProfiles) {
+            int length = lengthProfile.profile(x.getLength());
+            loopProfile.profileCounted(length);
+            double extreme = Double.NaN;
+            int extremeIndex = -1;
+            for (int i = 0; loopProfile.inject(i < length); i++) {
+                double d = x.getDataAt(i);
+                // inverted comparison to pass when extreme is NaN
+                if (!Double.isNaN(d) && (isMax ? !(d <= extreme) : !(d >= extreme))) {
+                    extreme = x.getDataAt(i);
+                    extremeIndex = i;
                 }
             }
-            return minIndex + 1;
+            if (isNaNProfile.profile(extremeIndex == -1)) {
+                return RDataFactory.createEmptyIntVector();
+            }
+            RStringVector names = x.getNames(attrProfiles);
+            if (hasNamesProfile.profile(names != null)) {
+                // collect result names
+                RStringVector resultNames = RDataFactory.createStringVectorFromScalar(names.getDataAt(extremeIndex));
+                return RDataFactory.createIntVector(new int[]{extremeIndex + 1}, true, resultNames);
+            } else {
+                return RDataFactory.createIntVectorFromScalar(extremeIndex + 1);
+            }
+        }
+
+        @Specialization
+        protected RIntVector which(@SuppressWarnings("unused") RNull x) {
+            return RDataFactory.createEmptyIntVector();
         }
     }
 }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/ForeignFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/ForeignFunctions.java
index 582337c61d1c2c1eff10889bcbff2597c3761af0..624b0baa010ced6e4ec76b6d3e99758a2bff94c1 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/ForeignFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/ForeignFunctions.java
@@ -66,6 +66,7 @@ import com.oracle.truffle.r.library.utils.Download;
 import com.oracle.truffle.r.library.utils.MenuNodeGen;
 import com.oracle.truffle.r.library.utils.ObjectSizeNodeGen;
 import com.oracle.truffle.r.library.utils.RprofNodeGen;
+import com.oracle.truffle.r.library.utils.RprofmemNodeGen;
 import com.oracle.truffle.r.library.utils.TypeConvertNodeGen;
 import com.oracle.truffle.r.library.utils.WriteTable;
 import com.oracle.truffle.r.nodes.access.vector.ElementAccessMode;
@@ -576,8 +577,9 @@ public class ForeignFunctions {
                     return getExternalModelBuiltinNode("termsform");
                 case "Rprof":
                     return RprofNodeGen.create();
-                case "unzip":
                 case "Rprofmem":
+                    return RprofmemNodeGen.create();
+                case "unzip":
                 case "addhistory":
                 case "loadhistory":
                 case "savehistory":
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/DoubleVectorPrinter.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/DoubleVectorPrinter.java
index 457b1c404ba589315ba895b2bb3173381b463041..b75899478f34a0c3723eae0c6498afa13336a72e 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/DoubleVectorPrinter.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/DoubleVectorPrinter.java
@@ -157,9 +157,9 @@ final class DoubleVectorPrinter extends VectorPrinter<RAbstractDoubleVector> {
         /*
          * F Format: use "F" format WHENEVER we use not more space than 'E' and still satisfy
          * 'R_print.digits' {but as if nsmall==0 !}
-         * 
+         *
          * E Format has the form [S]X[.XXX]E+XX[X]
-         * 
+         *
          * This is indicated by setting *e to non-zero (usually 1) If the additional exponent digit
          * is required *e is set to 2
          */
@@ -247,7 +247,7 @@ final class DoubleVectorPrinter extends VectorPrinter<RAbstractDoubleVector> {
          * for a number x , determine sgn = 1_{x < 0} {0/1} kpower = Exponent of 10; nsig =
          * min(R_print.digits, #{significant digits of alpha}) roundingwidens = 1 if rounding causes
          * x to increase in width, 0 otherwise
-         * 
+         *
          * where |x| = alpha * 10^kpower and 1 <= alpha < 10
          */
         double alpha;
@@ -382,8 +382,7 @@ final class DoubleVectorPrinter extends VectorPrinter<RAbstractDoubleVector> {
                 buff = snprintf(NB, fmt, x);
             }
         } else { /* e = 0 */
-            StringBuilder sb = new StringBuilder("#.#");
-            DecimalFormat df = new DecimalFormat(sb.toString());
+            DecimalFormat df = new DecimalFormat("#.#");
             df.setRoundingMode(RoundingMode.HALF_EVEN);
             df.setDecimalSeparatorAlwaysShown(false);
             df.setMinimumFractionDigits(d);
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRContext.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRContext.java
index 81a02f918888202c7f9cbc30d4b5758a129e9664..ae7d75ffc1eff9aaa109ff114b61611e4fff620b 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRContext.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRContext.java
@@ -181,23 +181,30 @@ public class FastRContext {
         @TruffleBoundary
         protected Object eval(RAbstractStringVector exprs, int pc, String kind, RAbstractStringVector args) {
             RContext.ContextKind contextKind = RContext.ContextKind.valueOf(kind);
+
             Object[] results = new Object[pc];
-            // separate threads that run in parallel; invoking thread waits for completion
-            RContext.EvalThread[] threads = new RContext.EvalThread[pc];
-            for (int i = 0; i < pc; i++) {
+            if (pc == 1) {
                 ContextInfo info = createContextInfo(contextKind, args);
-                threads[i] = new RContext.EvalThread(info, RSource.fromTextInternal(exprs.getDataAt(i % exprs.getLength()), RSource.Internal.CONTEXT_EVAL));
-            }
-            for (int i = 0; i < pc; i++) {
-                threads[i].start();
-            }
-            try {
+                PolyglotEngine vm = info.createVM();
+                results[0] = RContext.EvalThread.run(vm, info, RSource.fromTextInternal(exprs.getDataAt(0), RSource.Internal.CONTEXT_EVAL));
+            } else {
+                // separate threads that run in parallel; invoking thread waits for completion
+                RContext.EvalThread[] threads = new RContext.EvalThread[pc];
                 for (int i = 0; i < pc; i++) {
-                    threads[i].join();
-                    results[i] = threads[i].getEvalResult();
+                    ContextInfo info = createContextInfo(contextKind, args);
+                    threads[i] = new RContext.EvalThread(info, RSource.fromTextInternal(exprs.getDataAt(i % exprs.getLength()), RSource.Internal.CONTEXT_EVAL));
+                }
+                for (int i = 0; i < pc; i++) {
+                    threads[i].start();
+                }
+                try {
+                    for (int i = 0; i < pc; i++) {
+                        threads[i].join();
+                        results[i] = threads[i].getEvalResult();
+                    }
+                } catch (InterruptedException ex) {
+                    throw RError.error(this, RError.Message.GENERIC, "error finishing eval thread");
                 }
-            } catch (InterruptedException ex) {
-                throw RError.error(this, RError.Message.GENERIC, "error finishing eval thread");
             }
             return RDataFactory.createList(results);
         }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRInterop.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRInterop.java
index 1dda3508749ac98bfd666025679dccd78199226e..550f3efb3191bf745599764bd3fc6b9f598b0d80 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRInterop.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRInterop.java
@@ -22,6 +22,8 @@
  */
 package com.oracle.truffle.r.nodes.builtin.fastr;
 
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.singleElement;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.stringValue;
 import static com.oracle.truffle.r.runtime.RVisibility.OFF;
 import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX;
 import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
@@ -33,6 +35,7 @@ import com.oracle.truffle.api.CompilerAsserts;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.Truffle;
 import com.oracle.truffle.api.dsl.Cached;
+import com.oracle.truffle.api.dsl.Fallback;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.nodes.DirectCallNode;
@@ -41,10 +44,10 @@ import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RError.Message;
-import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.RSource;
 import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.context.RContext;
+import com.oracle.truffle.r.runtime.data.RMissing;
 import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.RTypedValue;
 
@@ -55,8 +58,8 @@ public class FastRInterop {
 
         @Override
         protected void createCasts(CastBuilder casts) {
-            casts.firstStringWithError(0, Message.INVALID_ARGUMENT, "mimeType");
-            casts.firstStringWithError(1, Message.INVALID_ARGUMENT, "source");
+            casts.arg("mimeType").mustBe(stringValue()).asStringVector().mustBe(singleElement()).findFirst();
+            casts.arg("source").mustBe(stringValue()).asStringVector().mustBe(singleElement()).findFirst();
         }
 
         protected CallTarget parse(String mimeType, String source) {
@@ -77,9 +80,9 @@ public class FastRInterop {
 
         @SuppressWarnings("unused")
         @Specialization(guards = {"cachedMimeType != null", "cachedMimeType.equals(mimeType)", "cachedSource != null", "cachedSource.equals(source)"})
-        protected Object evalCached(VirtualFrame frame, String mimeType, String source, //
-                        @Cached("mimeType") String cachedMimeType, //
-                        @Cached("source") String cachedSource, //
+        protected Object evalCached(VirtualFrame frame, String mimeType, String source,
+                        @Cached("mimeType") String cachedMimeType,
+                        @Cached("source") String cachedSource,
                         @Cached("createCall(mimeType, source)") DirectCallNode call) {
             return call.call(frame, EMPTY_OBJECT_ARRAY);
         }
@@ -98,31 +101,49 @@ public class FastRInterop {
     @RBuiltin(name = ".fastr.interop.export", visibility = OFF, kind = PRIMITIVE, parameterNames = {"name", "value"}, behavior = COMPLEX)
     public abstract static class Export extends RBuiltinNode {
 
-        @Specialization
+        @Override
+        protected void createCasts(CastBuilder casts) {
+            casts.arg("name").mustBe(stringValue()).asStringVector().mustBe(singleElement()).findFirst();
+            casts.boxPrimitive(1);
+        }
+
+        @Specialization(guards = "!isRMissing(value)")
         @TruffleBoundary
-        protected Object exportSymbol(Object name, RTypedValue value) {
-            String stringName = RRuntime.asString(name);
-            if (stringName == null) {
+        protected Object exportSymbol(String name, RTypedValue value) {
+            if (name == null) {
                 throw RError.error(this, RError.Message.INVALID_ARG_TYPE, "name");
             }
-            RContext.getInstance().getExportedSymbols().put(stringName, value);
+            RContext.getInstance().getExportedSymbols().put(name, value);
             return RNull.instance;
         }
+
+        @Specialization
+        @TruffleBoundary
+        protected Object exportSymbol(@SuppressWarnings("unused") String name, @SuppressWarnings("unused") RMissing value) {
+            throw RError.error(this, Message.ARGUMENT_MISSING, "value");
+        }
+
+        @Fallback
+        @TruffleBoundary
+        protected Object exportSymbol(@SuppressWarnings("unused") Object name, @SuppressWarnings("unused") Object value) {
+            throw RError.error(this, Message.GENERIC, "only R language objects can be exported");
+        }
     }
 
     @RBuiltin(name = ".fastr.interop.import", visibility = OFF, kind = PRIMITIVE, parameterNames = {"name"}, behavior = COMPLEX)
     public abstract static class Import extends RBuiltinNode {
 
+        @Override
+        protected void createCasts(CastBuilder casts) {
+            casts.arg("name").mustBe(stringValue()).asStringVector().mustBe(singleElement()).findFirst();
+        }
+
         @Specialization
         @TruffleBoundary
-        protected Object importSymbol(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);
+        protected Object importSymbol(String name) {
+            Object object = RContext.getInstance().getEnv().importSymbol(name);
             if (object == null) {
-                throw RError.error(this, RError.Message.NO_IMPORT_OBJECT, stringName);
+                throw RError.error(this, RError.Message.NO_IMPORT_OBJECT, name);
             }
             return object;
         }
diff --git a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/builtin/CastBuilderTest.java b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/builtin/CastBuilderTest.java
index a7835b6c048720307b08440201cdf3df53c1bcc9..681a49131af2005f5d4f01561a6c8388dd4fad56 100644
--- a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/builtin/CastBuilderTest.java
+++ b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/builtin/CastBuilderTest.java
@@ -22,14 +22,14 @@
  */
 package com.oracle.truffle.r.nodes.builtin;
 
-import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.elementAt;
-import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.asStringVector;
 import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.asLogicalVector;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.asStringVector;
 import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.chain;
 import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.complexValue;
 import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.constant;
 import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.defaultValue;
 import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.doubleValue;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.elementAt;
 import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.equalTo;
 import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.findFirst;
 import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.gte;
@@ -69,8 +69,6 @@ import com.oracle.truffle.r.nodes.binary.BoxPrimitiveNodeGen;
 import com.oracle.truffle.r.nodes.builtin.ArgumentFilter.ArgumentTypeFilter;
 import com.oracle.truffle.r.nodes.builtin.ArgumentFilter.ArgumentValueFilter;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder.InitialPhaseBuilder;
-import com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef;
-import com.oracle.truffle.r.nodes.builtin.base.IsNA;
 import com.oracle.truffle.r.nodes.casts.ArgumentFilterSampler;
 import com.oracle.truffle.r.nodes.casts.CastNodeSampler;
 import com.oracle.truffle.r.nodes.casts.PredefFiltersSamplers;
@@ -85,7 +83,6 @@ import com.oracle.truffle.r.runtime.RError.Message;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.data.RComplex;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
-import com.oracle.truffle.r.runtime.data.RIntVector;
 import com.oracle.truffle.r.runtime.data.RInteger;
 import com.oracle.truffle.r.runtime.data.RList;
 import com.oracle.truffle.r.runtime.data.RLogical;
@@ -634,7 +631,7 @@ public class CastBuilderTest {
         }
     }
 
-    private String argType(Object arg) {
+    private static String argType(Object arg) {
         return arg.getClass().getSimpleName();
     }
 
diff --git a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/casts/CastNodeSampler.java b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/casts/CastNodeSampler.java
index f90d1ec73c701252e434c56b4e9a4e3e4b6703dd..0e64e2c2cd775661cf03bad5498dce8dcaecad4a 100644
--- a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/casts/CastNodeSampler.java
+++ b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/casts/CastNodeSampler.java
@@ -23,11 +23,6 @@
 package com.oracle.truffle.r.nodes.casts;
 
 import java.lang.reflect.Constructor;
-import java.lang.reflect.Type;
-import java.util.Collections;
-import java.util.Set;
-import java.util.function.Predicate;
-import java.util.stream.Collectors;
 
 import com.oracle.truffle.r.nodes.unary.CastNode;
 
diff --git a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/casts/CastUtils.java b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/casts/CastUtils.java
index 9981b36e9248af9aa982861c06f101a588f6aef0..a59861970ff7893d9ee0e84082e813a27e9d793f 100644
--- a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/casts/CastUtils.java
+++ b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/casts/CastUtils.java
@@ -52,8 +52,6 @@ import com.oracle.truffle.r.runtime.data.model.RAbstractLogicalVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
 
-import sun.java2d.xr.XRCompositeManager;
-
 public class CastUtils {
 
     public static final class Cast {
diff --git a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/casts/TestCasts.java b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/casts/TestCasts.java
index 05d7c3b1e78cd10130300c569de72aa7be04bf51..494f5e2116c721d4c45615faa7f51a370d209f15 100644
--- a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/casts/TestCasts.java
+++ b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/casts/TestCasts.java
@@ -45,9 +45,6 @@ import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.scalarLogica
 import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.singleElement;
 import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.stringValue;
 
-import java.io.IOException;
-import java.io.OutputStream;
-
 import org.junit.Test;
 
 import com.oracle.truffle.api.CompilerAsserts;
diff --git a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/casts/ValuePredicateArgumentFilterSampler.java b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/casts/ValuePredicateArgumentFilterSampler.java
index cf17d773155e154ea3d74f9b6bee05caf07661b0..1b29123007f6ee8a4fc2d0ddef798fc84de3efdc 100644
--- a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/casts/ValuePredicateArgumentFilterSampler.java
+++ b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/casts/ValuePredicateArgumentFilterSampler.java
@@ -30,7 +30,6 @@ import java.util.stream.Collectors;
 
 import com.oracle.truffle.r.nodes.builtin.ValuePredicateArgumentFilter;
 import com.oracle.truffle.r.nodes.casts.ArgumentFilterSampler.ArgumentValueFilterSampler;
-import com.sun.source.doctree.AttributeTree.ValueKind;
 
 public class ValuePredicateArgumentFilterSampler<T> extends ValuePredicateArgumentFilter<T> implements ArgumentValueFilterSampler<T> {
 
diff --git a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/casts/VectorPredicateArgumentFilterSampler.java b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/casts/VectorPredicateArgumentFilterSampler.java
index 33f3ebe0ea4419c7efe1973399f1bead927710af..92f4853c4918c6be5ecaa85499177ff6292459b1 100644
--- a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/casts/VectorPredicateArgumentFilterSampler.java
+++ b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/casts/VectorPredicateArgumentFilterSampler.java
@@ -28,7 +28,6 @@ import java.util.List;
 import java.util.Set;
 import java.util.function.Predicate;
 import java.util.stream.Collectors;
-import java.util.stream.Stream;
 
 import com.oracle.truffle.r.nodes.builtin.VectorPredicateArgumentFilter;
 import com.oracle.truffle.r.nodes.casts.ArgumentFilterSampler.ArgumentValueFilterSampler;
diff --git a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/unary/FilterNodeGenSampler.java b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/unary/FilterNodeGenSampler.java
index 047ced6cff65c7bf52f15672ccd52189a42737da..bfdbdd71f798d6f2b8c4530c5911014aee10cfc5 100644
--- a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/unary/FilterNodeGenSampler.java
+++ b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/unary/FilterNodeGenSampler.java
@@ -24,7 +24,6 @@ package com.oracle.truffle.r.nodes.unary;
 
 import com.oracle.truffle.r.nodes.casts.ArgumentFilterSampler;
 import com.oracle.truffle.r.nodes.casts.CastNodeSampler;
-import com.oracle.truffle.r.nodes.casts.CastUtils;
 import com.oracle.truffle.r.nodes.casts.Samples;
 import com.oracle.truffle.r.nodes.casts.TypeExpr;
 
diff --git a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/unary/FindFirstNodeGenSampler.java b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/unary/FindFirstNodeGenSampler.java
index 3fa48e1f4a8e503607ae54d9135563aa65e32f56..0b92ad8b7dcc6f93b0e971727b1b71ea6bad8ebb 100644
--- a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/unary/FindFirstNodeGenSampler.java
+++ b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/unary/FindFirstNodeGenSampler.java
@@ -24,9 +24,7 @@ package com.oracle.truffle.r.nodes.unary;
 
 import java.lang.reflect.Type;
 import java.util.HashSet;
-import java.util.Optional;
 import java.util.Set;
-import java.util.function.Function;
 import java.util.function.Predicate;
 import java.util.stream.Collectors;
 
@@ -36,7 +34,6 @@ import com.oracle.truffle.r.nodes.casts.Samples;
 import com.oracle.truffle.r.nodes.casts.TypeExpr;
 import com.oracle.truffle.r.runtime.data.RMissing;
 import com.oracle.truffle.r.runtime.data.RNull;
-import com.oracle.truffle.r.runtime.data.model.RAbstractContainer;
 import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
 
 public class FindFirstNodeGenSampler extends CastNodeSampler<FindFirstNodeGen> {
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/ReadVariadicComponentNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/ReadVariadicComponentNode.java
index 46ab6dc79f9f12af5c770c05d4dd1f2f2a07cfe9..1441a1741586f7b8c1495521281adfb2dabccba5 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/ReadVariadicComponentNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/ReadVariadicComponentNode.java
@@ -102,7 +102,7 @@ public class ReadVariadicComponentNode extends RSourceSectionNode implements RSy
 
     @Override
     public String getIdentifier() {
-        return getPrintForm();
+        return getPrintForm().intern();
     }
 
     @Override
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 6d813c4a45aa5e8d837aa4b520e374ca167f0980..245067ec8469059f34357adc436ef56abbfef8a0 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
@@ -70,7 +70,7 @@ public abstract class CastTypeNode extends BinaryNode {
     public static CastNode createCast(RType type, boolean preserveNames, boolean preserveDimensions, boolean preserveAttributes) {
         switch (type) {
             case Character:
-                return CastStringNodeGen.create(preserveNames, preserveDimensions, preserveAttributes, false);
+                return CastStringNodeGen.create(preserveNames, preserveDimensions, preserveAttributes);
             case Complex:
                 return CastComplexNodeGen.create(preserveNames, preserveDimensions, preserveAttributes);
             case Double:
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/CastBuilder.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/CastBuilder.java
index 5816dfbcff6f1139789451fa644d7f4a5ac59797..564660b681f2661cfb9902918ce9f6fd945b3422 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/CastBuilder.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/CastBuilder.java
@@ -131,7 +131,11 @@ public final class CastBuilder {
     }
 
     public CastBuilder toCharacter(int index) {
-        return insert(index, CastStringNodeGen.create(false, false, false, false));
+        return insert(index, CastStringNodeGen.create(false, false, false));
+    }
+
+    public CastBuilder toCharacter(int index, boolean preserveNames, boolean dimensionsPreservation, boolean attrPreservation) {
+        return insert(index, CastStringNodeGen.create(preserveNames, dimensionsPreservation, attrPreservation));
     }
 
     public CastBuilder toComplex(int index) {
@@ -710,7 +714,7 @@ public final class CastBuilder {
         }
 
         public static <T> Function<ArgCastBuilder<T, ?>, CastNode> asStringVector() {
-            return phaseBuilder -> CastStringNodeGen.create(false, false, false, false);
+            return phaseBuilder -> CastStringNodeGen.create(false, false, false);
         }
 
         public static <T> Function<ArgCastBuilder<T, ?>, CastNode> asComplexVector() {
@@ -722,7 +726,7 @@ public final class CastBuilder {
         }
 
         public static <T> Function<ArgCastBuilder<T, ?>, CastNode> asStringVector(boolean preserveNames, boolean preserveDimensions, boolean preserveAttributes) {
-            return phaseBuilder -> CastStringNodeGen.create(preserveNames, preserveDimensions, preserveAttributes, false);
+            return phaseBuilder -> CastStringNodeGen.create(preserveNames, preserveDimensions, preserveAttributes);
         }
 
         public static <T> Function<ArgCastBuilder<T, ?>, CastNode> asLogical() {
@@ -1388,6 +1392,11 @@ public final class CastBuilder {
             return state().factory.newCoercedPhaseBuilder(this, Byte.class);
         }
 
+        default CoercedPhaseBuilder<RAbstractStringVector, String> asStringVector(boolean preserveNames, boolean dimensionsPreservation, boolean attrPreservation) {
+            state().castBuilder().toCharacter(state().index(), preserveNames, dimensionsPreservation, attrPreservation);
+            return state().factory.newCoercedPhaseBuilder(this, String.class);
+        }
+
         default CoercedPhaseBuilder<RAbstractStringVector, String> asStringVector() {
             state().castBuilder().toCharacter(state().index());
             return state().factory.newCoercedPhaseBuilder(this, String.class);
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/helpers/RFactorNodes.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/helpers/RFactorNodes.java
index 2c468c42b1bbd4b6c25dfb88ff3952db68b03e5c..cf65a15c602e85f5eccf5e005c31402e5c68ff30 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/helpers/RFactorNodes.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/helpers/RFactorNodes.java
@@ -101,7 +101,7 @@ public final class RFactorNodes {
             } else {
                 if (castString == null) {
                     CompilerDirectives.transferToInterpreterAndInvalidate();
-                    castString = insert(CastStringNodeGen.create(false, false, false, false));
+                    castString = insert(CastStringNodeGen.create(false, false, false));
                 }
                 RStringVector slevels = (RStringVector) castString.executeString(vec);
                 return RDataFactory.createStringVector(slevels.getDataWithoutCopying(), RDataFactory.COMPLETE_VECTOR);
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastStringNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastStringNode.java
index ec36ae7b6365f9ec9d374abef3e71a26db3c0132..d416956e4a2689e6f5f1098a9b7a4de604697d56 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastStringNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastStringNode.java
@@ -30,19 +30,11 @@ import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.RStringVector;
 import com.oracle.truffle.r.runtime.data.RSymbol;
 import com.oracle.truffle.r.runtime.data.model.RAbstractContainer;
-import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
 
 public abstract class CastStringNode extends CastStringBaseNode {
 
-    private final boolean convertEmptyVectorToNull;
-
-    protected CastStringNode(boolean preserveNames, boolean preserveDimensions, boolean preserveAttributes, boolean convertEmptyVectorToNull) {
+    protected CastStringNode(boolean preserveNames, boolean preserveDimensions, boolean preserveAttributes) {
         super(preserveNames, preserveDimensions, preserveAttributes);
-        this.convertEmptyVectorToNull = convertEmptyVectorToNull;
-    }
-
-    public boolean convertEmptyVectorToNull() {
-        return convertEmptyVectorToNull;
     }
 
     public abstract Object executeString(int o);
@@ -58,11 +50,6 @@ public abstract class CastStringNode extends CastStringBaseNode {
         return RNull.instance;
     }
 
-    @Specialization(guards = "vector.getLength() == 0")
-    protected Object doEmptyVector(@SuppressWarnings("unused") RAbstractVector vector) {
-        return convertEmptyVectorToNull ? RNull.instance : RDataFactory.createStringVector(0);
-    }
-
     private RStringVector vectorCopy(RAbstractContainer operand, String[] data) {
         RStringVector ret = RDataFactory.createStringVector(data, operand.isComplete(), getPreservedDimensions(operand), getPreservedNames(operand));
         preserveDimensionNames(operand, ret);
@@ -72,7 +59,7 @@ public abstract class CastStringNode extends CastStringBaseNode {
         return ret;
     }
 
-    @Specialization(guards = "vector.getLength() != 0")
+    @Specialization
     protected RStringVector doStringVector(RStringVector vector) {
         if (preserveAttributes() && preserveDimensions() && preserveNames()) {
             return vector;
@@ -81,7 +68,7 @@ public abstract class CastStringNode extends CastStringBaseNode {
         }
     }
 
-    @Specialization(guards = "operand.getLength() != 0")
+    @Specialization
     protected RStringVector doIntVector(RAbstractContainer operand) {
         String[] sdata = new String[operand.getLength()];
         // conversions to character will not introduce new NAs
@@ -102,10 +89,10 @@ public abstract class CastStringNode extends CastStringBaseNode {
     }
 
     public static CastStringNode create() {
-        return CastStringNodeGen.create(true, true, true, false);
+        return CastStringNodeGen.create(true, true, true);
     }
 
     public static CastStringNode createNonPreserving() {
-        return CastStringNodeGen.create(false, false, false, false);
+        return CastStringNodeGen.create(false, false, false);
     }
 }
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 d644738e48811feaa3e60840a01274d4506c88bf..a44b8ff62a92abd152f40e7db3f18345c91508fa 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,8 +23,6 @@
 package com.oracle.truffle.r.runtime.ffi.jnr;
 
 import java.nio.charset.StandardCharsets;
-import java.util.HashMap;
-import java.util.Map;
 import java.util.function.Function;
 
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
@@ -35,17 +33,16 @@ import com.oracle.truffle.api.source.SourceSection;
 import com.oracle.truffle.r.runtime.RArguments;
 import com.oracle.truffle.r.runtime.RCaller;
 import com.oracle.truffle.r.runtime.RCleanUp;
-import com.oracle.truffle.r.runtime.RDeparse;
 import com.oracle.truffle.r.runtime.REnvVars;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RErrorHandling;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.RRuntime;
-import com.oracle.truffle.r.runtime.RSrcref;
 import com.oracle.truffle.r.runtime.RSource;
+import com.oracle.truffle.r.runtime.RSrcref;
+import com.oracle.truffle.r.runtime.RStartParams.SA_TYPE;
 import com.oracle.truffle.r.runtime.RType;
 import com.oracle.truffle.r.runtime.Utils;
-import com.oracle.truffle.r.runtime.RStartParams.SA_TYPE;
 import com.oracle.truffle.r.runtime.context.Engine.ParseException;
 import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.data.RAttributable;
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 8733ab397016e835599532c18d617f86ab877e7b..486c4b98687d8b2f54e026a3d432d2ecdb9cd7c0 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
@@ -169,6 +169,18 @@ public final class Utils {
         throw new ExitException(2);
     }
 
+    /**
+     * This the real, final, non-overrideable, exit of the entire R system. TODO well, modulo how
+     * quit() is interpreted when R is started implicitly from a Polyglot shell that is running
+     * other languages.
+     *
+     * @param status
+     */
+    public static void systemExit(int status) {
+        RPerfStats.report();
+        System.exit(status);
+    }
+
     private static String userHome;
 
     private static String userHome() {
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
index c21d8b5555efa5960fd7a0b924d696a7a711cad9..bbc30f71a915fe474e2bb4cb8d801c723a2aeb89 100644
--- 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
@@ -199,35 +199,44 @@ public final class RContext extends ExecutionContext implements TruffleObject {
                 throw new RInternalError(e1, "error while initializing eval thread");
             }
             try {
-                try {
-                    PolyglotEngine.Value resultValue = vm.eval(source);
-                    evalResult = createEvalResult(resultValue);
-                } catch (ParseException e) {
-                    e.report(info.getConsoleHandler());
-                    evalResult = createErrorResult(e.getMessage());
-                } catch (IOException e) {
-                    Throwable cause = e.getCause();
-                    if (cause instanceof ExitException) {
-                        // termination, treat this as "success"
-                        ExitException exitException = (ExitException) cause;
-                        evalResult = RDataFactory.createList(new Object[]{exitException.getStatus()});
-                    } else {
-                        // some internal error
-                        RInternalError.reportErrorAndConsoleLog(cause, info.getConsoleHandler(), info.getId());
-                        evalResult = createErrorResult(cause.getClass().getSimpleName());
-                    }
-                }
+                evalResult = run(vm, info, source);
             } finally {
                 vm.dispose();
                 threads.remove(info.getId());
             }
         }
 
+        /**
+         * Convenience method for {@code .fastr.context.eval} in same thread.
+         */
+        public static RList run(PolyglotEngine vm, ContextInfo info, Source source) {
+            RList evalResult;
+            try {
+                PolyglotEngine.Value resultValue = vm.eval(source);
+                evalResult = createEvalResult(resultValue);
+            } catch (ParseException e) {
+                e.report(info.getConsoleHandler());
+                evalResult = createErrorResult(e.getMessage());
+            } catch (IOException e) {
+                Throwable cause = e.getCause();
+                if (cause instanceof ExitException) {
+                    // termination, treat this as "success"
+                    ExitException exitException = (ExitException) cause;
+                    evalResult = RDataFactory.createList(new Object[]{exitException.getStatus()});
+                } else {
+                    // some internal error
+                    RInternalError.reportErrorAndConsoleLog(cause, info.getConsoleHandler(), info.getId());
+                    evalResult = createErrorResult(cause.getClass().getSimpleName());
+                }
+            }
+            return evalResult;
+        }
+
         /**
          * The result is an {@link RList} contain the value, plus an "error" attribute if the
          * evaluation resulted in an error.
          */
-        public static RList createEvalResult(PolyglotEngine.Value resultValue) throws IOException {
+        private static RList createEvalResult(PolyglotEngine.Value resultValue) throws IOException {
             Object result = resultValue.get();
             Object listResult = result;
             String error = null;
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/AgentObjectSizeFactory.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/AgentObjectSizeFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..7e303054edb4f818d2153f011bac4e37a0baebff
--- /dev/null
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/AgentObjectSizeFactory.java
@@ -0,0 +1,228 @@
+/*
+ * Copyright (c) 2016, 2016, 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.data;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.management.ManagementFactory;
+import java.lang.reflect.Array;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.jar.Attributes;
+import java.util.jar.JarEntry;
+import java.util.jar.JarOutputStream;
+import java.util.jar.Manifest;
+
+import javax.tools.ToolProvider;
+
+import com.oracle.truffle.r.runtime.RInternalError;
+import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.runtime.Utils;
+import com.oracle.truffle.r.runtime.data.RObjectSize.IgnoreObjectHandler;
+import com.oracle.truffle.r.runtime.data.RObjectSize.TypeCustomizer;
+
+/**
+ * Uses an instrumentation agent to get an accurate estimate of an objects size, plus reflection to
+ * aggregate the size of object-valued fields. Sharing is not handled in the general case, although
+ * some special cases are handed, such as the fact that {@link RNull} is a singleton.
+ *
+ * In order to satisfy the requirements of the Java instrumentation API, we have to load the agent
+ * from a jar file. The creation and loading is all orchestrated by this class.
+ */
+public class AgentObjectSizeFactory extends ObjectSizeFactory {
+
+    private Map<Class<?>, ArrayList<Field>> objectFieldsMap = new HashMap<>();
+    private static Map<Class<?>, TypeCustomizer> customizerMap = new HashMap<>(); // system wide
+
+    public AgentObjectSizeFactory() {
+        if (!ObjSizeAgent.isInitialized()) {
+            try {
+                createAgentJar();
+            } catch (Exception ex) {
+                // not available
+                Utils.rSuicide("failed to load ObjSizeAgent: " + ex.getMessage());
+            }
+        }
+    }
+
+    /**
+     * Adds the class file bytes for a given class to a JAR stream.
+     */
+    static void add(JarOutputStream jar, Class<?> c) throws IOException {
+        String name = c.getName();
+        String classAsPath = name.replace('.', '/') + ".class";
+        jar.putNextEntry(new JarEntry(classAsPath));
+
+        InputStream stream = c.getClassLoader().getResourceAsStream(classAsPath);
+
+        int nRead;
+        byte[] buf = new byte[1024];
+        while ((nRead = stream.read(buf, 0, buf.length)) != -1) {
+            jar.write(buf, 0, nRead);
+        }
+
+        jar.closeEntry();
+    }
+
+    protected void createAgentJar() throws Exception {
+        Manifest manifest = new Manifest();
+        manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0");
+        Attributes mainAttrs = manifest.getMainAttributes();
+        mainAttrs.putValue("Agent-Class", ObjSizeAgent.class.getName());
+        mainAttrs.putValue("Premain-Class", ObjSizeAgent.class.getName());
+
+        Path jar = Files.createTempFile("myagent", ".jar");
+        try {
+            JarOutputStream jarStream = new JarOutputStream(new FileOutputStream(jar.toFile()), manifest);
+            add(jarStream, ObjSizeAgent.class);
+            jarStream.close();
+
+            loadAgent(jar);
+        } finally {
+            Files.deleteIfExists(jar);
+        }
+    }
+
+    public static void loadAgent(Path agent) throws Exception {
+        String vmName = ManagementFactory.getRuntimeMXBean().getName();
+        int p = vmName.indexOf('@');
+        String pid = vmName.substring(0, p);
+        ClassLoader cl = ToolProvider.getSystemToolClassLoader();
+        Class<?> c = Class.forName("com.sun.tools.attach.VirtualMachine", true, cl);
+        Method attach = c.getDeclaredMethod("attach", String.class);
+        Method loadAgent = c.getDeclaredMethod("loadAgent", String.class, String.class);
+        Object vm = attach.invoke(null, pid);
+        loadAgent.invoke(vm, agent.toString(), "");
+    }
+
+    @Override
+    public long getObjectSize(Object obj, IgnoreObjectHandler ignoreObjectHandler) {
+        return getObjectSize(obj, obj, ignoreObjectHandler);
+    }
+
+    private long getObjectSize(Object rootObj, Object obj, IgnoreObjectHandler ignoreObjectHandler) {
+        try {
+            long basicSize = ObjSizeAgent.objectSize(obj);
+            long size = basicSize;
+            Class<?> klass = obj.getClass();
+            if (klass.isArray() && !klass.getComponentType().isPrimitive()) {
+                for (int i = 0; i < Array.getLength(obj); i++) {
+                    Object elem = Array.get(obj, i);
+                    if (elem == null || isNa(elem)) {
+                        continue;
+                    } else {
+                        size += getObjectSize(rootObj, elem, ignoreObjectHandler);
+                    }
+                }
+            } else {
+                ArrayList<Field> objectFields = objectFieldsMap.get(klass);
+                if (objectFields == null) {
+                    objectFields = new ArrayList<>();
+                    findObjectFields(obj.getClass(), objectFields);
+                    objectFieldsMap.put(klass, objectFields);
+                }
+                for (Field objectField : objectFields) {
+                    Object fieldObj = objectField.get(obj);
+                    if (fieldObj == null || ignoreObjectHandler.ignore(rootObj, fieldObj)) {
+                        continue;
+                    } else {
+                        TypeCustomizer typeCustomizer = getCustomizer(fieldObj.getClass());
+                        if (typeCustomizer == null) {
+                            size += getObjectSize(rootObj, fieldObj, ignoreObjectHandler);
+                        } else {
+                            size += typeCustomizer.getObjectSize(fieldObj);
+                        }
+                    }
+                }
+            }
+            return size;
+        } catch (Throwable t) {
+            throw RInternalError.shouldNotReachHere(t);
+        }
+
+    }
+
+    private static boolean isNa(Object elem) {
+        String typeName = elem.getClass().getSimpleName();
+        switch (typeName) {
+            case "Integer":
+                return RRuntime.isNA((int) elem);
+            case "Double":
+                return RRuntime.isNA((double) elem);
+            case "String":
+                return RRuntime.isNA((String) elem);
+            default:
+                return false;
+        }
+    }
+
+    /**
+     * Walks the superclass hierarchy of {@code klass} and accumulates all object-valued fields in
+     * {@code objectFields}.
+     */
+    private static void findObjectFields(Class<?> klass, ArrayList<Field> objectFields) {
+        if (klass != Object.class) {
+            findObjectFields(klass.getSuperclass(), objectFields);
+            Field[] fields = klass.getDeclaredFields();
+            for (Field field : fields) {
+                Class<?> fieldClass = field.getType();
+                if (fieldClass.isPrimitive()) {
+                    continue;
+                }
+                int modifiers = field.getModifiers();
+                if (Modifier.isStatic(modifiers)) {
+                    continue;
+                }
+                // check for special case of an completely ignored type
+                if (getCustomizer(fieldClass) == RObjectSize.IGNORE) {
+                    continue;
+                }
+                field.setAccessible(true);
+                objectFields.add(field);
+            }
+        }
+    }
+
+    private static TypeCustomizer getCustomizer(Class<?> objClass) {
+        for (Map.Entry<Class<?>, TypeCustomizer> entry : customizerMap.entrySet()) {
+            if (entry.getKey().isAssignableFrom(objClass)) {
+                return entry.getValue();
+            }
+        }
+        return null;
+
+    }
+
+    @Override
+    public void registerTypeCustomizer(Class<?> klass, TypeCustomizer typeCustomizer) {
+        customizerMap.put(klass, typeCustomizer);
+    }
+
+}
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/MemoryTracer.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/MemoryCopyTracer.java
similarity index 56%
rename from com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/MemoryTracer.java
rename to com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/MemoryCopyTracer.java
index 05b5db6c2ab0659d63d734052b21dd3216ae1553..f6e7cabae776ea3598ae3034f6ee4da2ee799ca1 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/MemoryTracer.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/MemoryCopyTracer.java
@@ -23,48 +23,56 @@
 
 package com.oracle.truffle.r.runtime.data;
 
-import com.oracle.truffle.api.Assumption;
-import com.oracle.truffle.api.Truffle;
-import com.oracle.truffle.r.runtime.context.RContext;
+import java.util.Deque;
+import java.util.concurrent.ConcurrentLinkedDeque;
+
+import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
+import com.oracle.truffle.api.utilities.CyclicAssumption;
 import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
 
 /**
- * Helper for tracing memory related events. All implementors of {@link RAbstractVector} are
- * expected to report to {@link MemoryTracer} and others can listen to them through {@link Listener}
- * interface. Use method {@link #reportEvents()} to start the tracing.
+ * Helper for tracing memory copying events, as used by the {@code tracemem} bultin. All
+ * implementors of {@link RAbstractVector} are expected to report to {@link MemoryCopyTracer} and
+ * others can listen to them through {@link Listener} interface. Use method
+ * {@link #setTracingState(boolean)} to enable/disable the tracing.
  */
-public final class MemoryTracer {
-    private static Listener listener;
-    private static final Assumption noMemoryTracingAssumption = Truffle.getRuntime().createAssumption();
+public final class MemoryCopyTracer {
+    private static Deque<Listener> listeners = new ConcurrentLinkedDeque<>();
+    @CompilationFinal private static boolean enabled;
+
+    private static final CyclicAssumption noMemoryCopyTracingAssumption = new CyclicAssumption("data copying");
 
-    private MemoryTracer() {
+    private MemoryCopyTracer() {
         // only static methods
     }
 
     /**
-     * Sets the listener of memory tracing events. For the time being there can only be one
-     * listener. This can be extended to an array should we need more listeners.
+     * Adds a listener of memory copying events.
      */
-    public static void setListener(Listener newListener) {
-        listener = newListener;
+    public static void addListener(Listener listener) {
+        listeners.addLast(listener);
     }
 
     /**
      * After calling this method memory related events will be reported to the listener. This
      * invalidates global assumption and should be used with caution.
      */
-    public static void reportEvents() {
-        noMemoryTracingAssumption.invalidate();
+    public static void setTracingState(boolean newState) {
+        if (enabled != newState) {
+            noMemoryCopyTracingAssumption.invalidate();
+            enabled = newState;
+        }
     }
 
     /**
      * Reports copy event to the listener. If there are no traced objects, this should turn into
-     * no-op. TODO might be worth interposing on a change in {@code tracingState} to turn off the
-     * collection.
+     * no-op.
      */
     public static void reportCopying(RAbstractVector source, RAbstractVector dest) {
-        if (!noMemoryTracingAssumption.isValid() && listener != null && RContext.getInstance().stateInstrumentation.getTracingState()) {
-            listener.reportCopying(source, dest);
+        if (enabled) {
+            for (Listener listener : listeners) {
+                listener.reportCopying(source, dest);
+            }
         }
     }
 
diff --git a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/unary/CastStringNodeGenSampler.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/ObjSizeAgent.java
similarity index 50%
rename from com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/unary/CastStringNodeGenSampler.java
rename to com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/ObjSizeAgent.java
index 777b8e0e05be525d5a074cf03e726f00ac77707a..b6756248470bea75bf369bfd2859bd071c11de48 100644
--- a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/unary/CastStringNodeGenSampler.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/ObjSizeAgent.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2016, 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,25 +20,33 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.nodes.unary;
+package com.oracle.truffle.r.runtime.data;
 
-import com.oracle.truffle.r.nodes.casts.CastNodeSampler;
-import com.oracle.truffle.r.nodes.casts.TypeExpr;
-import com.oracle.truffle.r.runtime.data.RNull;
+import java.lang.instrument.Instrumentation;
 
-public class CastStringNodeGenSampler extends CastNodeSampler<CastStringNodeGen> {
+/**
+ * This is agent class for object sizing. It has to be separate as it is loaded from a jar file.
+ * This implements the basic call to the JVM for the "struct" part of the object.
+ * {@link AgentObjectSizeFactory} handles the recursive sizing based on the field/array types.
+ *
+ */
+public class ObjSizeAgent {
+    private static Instrumentation instrumentation;
+
+    public static void premain(@SuppressWarnings("unused") String agentArgs, Instrumentation inst) {
+        instrumentation = inst;
+    }
 
-    public CastStringNodeGenSampler(CastStringNodeGen castNode) {
-        super(castNode);
+    public static void agentmain(@SuppressWarnings("unused") String agentArgs, Instrumentation inst) {
+        instrumentation = inst;
     }
 
-    @Override
-    public TypeExpr resultTypes(TypeExpr inputType) {
-        TypeExpr rt = super.resultTypes(inputType);
-        if (castNode.convertEmptyVectorToNull()) {
-            return rt.or(TypeExpr.union(RNull.class));
-        } else {
-            return rt;
-        }
+    public static long objectSize(Object obj) {
+        return instrumentation.getObjectSize(obj);
     }
+
+    static boolean isInitialized() {
+        return instrumentation != null;
+    }
+
 }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/ObjectSizeFactory.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/ObjectSizeFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..18a0cb472db916172c7386ab5e62fb9334a0552f
--- /dev/null
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/ObjectSizeFactory.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2016, 2016, 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.data;
+
+import com.oracle.truffle.r.runtime.data.RObjectSize.IgnoreObjectHandler;
+import com.oracle.truffle.r.runtime.data.RObjectSize.TypeCustomizer;
+
+public abstract class ObjectSizeFactory {
+
+    static {
+        final String prop = System.getProperty("fastr.objectsize.factory.class", "com.oracle.truffle.r.runtime.data.AgentObjectSizeFactory");
+        try {
+            theInstance = (ObjectSizeFactory) Class.forName(prop).newInstance();
+        } catch (Exception ex) {
+            // CheckStyle: stop system..print check
+            System.err.println("Failed to instantiate class: " + prop);
+        }
+    }
+
+    private static ObjectSizeFactory theInstance;
+
+    public static ObjectSizeFactory getInstance() {
+        return theInstance;
+    }
+
+    /**
+     * See {@link RObjectSize#getObjectSize}.
+     */
+    public abstract long getObjectSize(Object obj, IgnoreObjectHandler ignoreObjectHandler);
+
+    public abstract void registerTypeCustomizer(Class<?> klass, TypeCustomizer typeCustomizer);
+
+}
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/OutputAgentObjectSizeFactory.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/OutputAgentObjectSizeFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..4d7279743c01e4518d20124db73a4676a5c65a0a
--- /dev/null
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/OutputAgentObjectSizeFactory.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2016, 2016, 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.data;
+
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+
+import com.oracle.truffle.r.runtime.Utils;
+import com.oracle.truffle.r.runtime.data.RObjectSize.IgnoreObjectHandler;
+
+/**
+ * A debugging tool, logs all calls to {@link #getObjectSize(Object, IgnoreObjectHandler)} to a
+ * file.
+ *
+ */
+public class OutputAgentObjectSizeFactory extends AgentObjectSizeFactory {
+
+    private PrintWriter printWriter;
+
+    public OutputAgentObjectSizeFactory() {
+        try {
+            printWriter = new PrintWriter(new FileWriter(Utils.getLogPath("fastr_objectsize.log").toString()));
+        } catch (IOException ex) {
+            Utils.rSuicide(ex.getMessage());
+        }
+
+    }
+
+    @Override
+    public long getObjectSize(Object obj, IgnoreObjectHandler ignoreObjectHandler) {
+        long size = super.getObjectSize(obj, ignoreObjectHandler);
+        printWriter.printf("%s: %d\n", obj.getClass().getSimpleName(), (int) size);
+        printWriter.flush();
+        return size;
+    }
+}
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RComplex.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RComplex.java
index 1bfe70252de40c3162543da9ef30c416b27154b2..430be1e43025c645c81ffbb1c59aa6f756c8bee6 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RComplex.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RComplex.java
@@ -68,7 +68,7 @@ public final class RComplex extends RScalarVector implements RAbstractComplexVec
     @Override
     public RComplexVector materialize() {
         RComplexVector result = RDataFactory.createComplexVectorFromScalar(this);
-        MemoryTracer.reportCopying(this, result);
+        MemoryCopyTracer.reportCopying(this, result);
         return result;
     }
 
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDataFactory.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDataFactory.java
index 6131402db4e6ecb0b06453b5f497cb289ce5314e..dad4afd45a6e0e52823f4e946f459dd9a5514e1e 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDataFactory.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDataFactory.java
@@ -23,8 +23,10 @@
 package com.oracle.truffle.r.runtime.data;
 
 import java.util.Arrays;
+import java.util.Deque;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.concurrent.ConcurrentLinkedDeque;
 import java.util.concurrent.atomic.AtomicInteger;
 
 import com.oracle.truffle.api.Assumption;
@@ -32,7 +34,7 @@ import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.RootCallTarget;
 import com.oracle.truffle.api.frame.MaterializedFrame;
-import com.oracle.truffle.api.profiles.ConditionProfile;
+import com.oracle.truffle.api.utilities.CyclicAssumption;
 import com.oracle.truffle.r.runtime.FastROptions;
 import com.oracle.truffle.r.runtime.RCaller;
 import com.oracle.truffle.r.runtime.RInternalError;
@@ -49,11 +51,6 @@ import com.oracle.truffle.r.runtime.nodes.RNode;
 
 public final class RDataFactory {
 
-    /**
-     * Profile for creation tracing; must precede following declarations.
-     */
-    private static final ConditionProfile statsProfile = ConditionProfile.createBinaryProfile();
-
     public static final boolean INCOMPLETE_VECTOR = false;
     public static final boolean COMPLETE_VECTOR = true;
 
@@ -498,24 +495,79 @@ public final class RDataFactory {
         return traceDataCreated(new RExternalPtr(value, tag, RNull.instance));
     }
 
-    @CompilationFinal private static PerfHandler stats;
+    /*
+     * Support for collecting information on allocations in this class. Rprofmem/Rprof register a
+     * listener when active which, when memory profiling is enabled, is called with the object being
+     * allocated. Owing to the use of the Assumption, there should be no overhead when disabled.
+     */
+
+    private static Deque<Listener> listeners = new ConcurrentLinkedDeque<>();
+    @CompilationFinal private static boolean enabled;
+    private static final CyclicAssumption noAllocationTracingAssumption = new CyclicAssumption("data allocation");
+
+    public static void setAllocationTracing(boolean newState) {
+        if (enabled != newState) {
+            noAllocationTracingAssumption.invalidate();
+            enabled = newState;
+        }
+    }
 
     private static <T> T traceDataCreated(T data) {
-        if (statsProfile.profile(stats != null)) {
-            stats.record(data);
+        if (enabled) {
+            for (Listener listener : listeners) {
+                listener.reportAllocation((RTypedValue) data);
+            }
         }
         return data;
     }
 
+    public interface Listener {
+        /**
+         * Invoked when an instance of an {@link RTypedValue} is created. Note that the initial
+         * state of the complex objects, i.e., those with additional {@code Object} subclass fields,
+         * which may also be {@link RTypedValue} instances is undefined other than by inspection. A
+         * listener that computes the "size" of an object must take into account that
+         * {@link RTypedValue} instances passed to a {@code createXXX} method will already have been
+         * reported, but other data such as {@code int[]} instances for array dimensions will not.
+         */
+        void reportAllocation(RTypedValue data);
+    }
+
+    /**
+     * Sets the listener of memory tracing events. For the time being there can only be one
+     * listener. This can be extended to an array should we need more listeners.
+     */
+    public static void addListener(Listener listener) {
+        listeners.addLast(listener);
+    }
+
+    /*
+     * (Legacy) support for R:PerfStats option. This does produce more information than Rprofmem
+     * regarding the types and length of the objects being allocated, but it does not record where
+     * in R the allocation took place.
+     */
     static {
         RPerfStats.register(new PerfHandler());
     }
 
-    private static class PerfHandler implements RPerfStats.Handler {
+    private static class PerfHandler implements RPerfStats.Handler, Listener {
         private static Map<Class<?>, RPerfStats.Histogram> histMap;
 
+        @Override
+        public void initialize(String optionData) {
+            histMap = new HashMap<>();
+            addListener(this);
+            setAllocationTracing(true);
+        }
+
+        @Override
+        public String getName() {
+            return "datafactory";
+        }
+
+        @Override
         @TruffleBoundary
-        void record(Object data) {
+        public void reportAllocation(RTypedValue data) {
             Class<?> klass = data.getClass();
             boolean isBounded = data instanceof RAbstractVector;
             RPerfStats.Histogram hist = histMap.get(klass);
@@ -527,17 +579,6 @@ public final class RDataFactory {
             hist.inc(length);
         }
 
-        @Override
-        public void initialize(String optionData) {
-            stats = this;
-            histMap = new HashMap<>();
-        }
-
-        @Override
-        public String getName() {
-            return "datafactory";
-        }
-
         @Override
         public void report() {
             RPerfStats.out().println("Scalar types");
@@ -558,5 +599,6 @@ public final class RDataFactory {
             }
             RPerfStats.out().println();
         }
+
     }
 }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDouble.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDouble.java
index 096356c36aaf5031ea8b85166e820cfbd7d960b5..2caf812292e1c4270b619fe81165d7e1066aa7b2 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDouble.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDouble.java
@@ -90,7 +90,7 @@ public final class RDouble extends RScalarVector implements RAbstractDoubleVecto
     @Override
     public RDoubleVector materialize() {
         RDoubleVector result = RDataFactory.createDoubleVectorFromScalar(getValue());
-        MemoryTracer.reportCopying(this, result);
+        MemoryCopyTracer.reportCopying(this, result);
         return result;
     }
 
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RInteger.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RInteger.java
index 075d9ee51126ff8bd297ebffb5b11bed48055b13..33ba1ebd83bac82664770babb137ec2908cf5148 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RInteger.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RInteger.java
@@ -88,7 +88,7 @@ public final class RInteger extends RScalarVector implements RAbstractIntVector
     @Override
     public RIntVector materialize() {
         RIntVector result = RDataFactory.createIntVectorFromScalar(value);
-        MemoryTracer.reportCopying(this, result);
+        MemoryCopyTracer.reportCopying(this, result);
         return result;
     }
 
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RLogical.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RLogical.java
index 05d0e6d1b1d642a39fc13b620d15a3727a3c130d..44e432804a569f815eeb52fe6b9533d3f79c97a4 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RLogical.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RLogical.java
@@ -94,7 +94,7 @@ public final class RLogical extends RScalarVector implements RAbstractLogicalVec
     @Override
     public RLogicalVector materialize() {
         RLogicalVector result = RDataFactory.createLogicalVectorFromScalar(value);
-        MemoryTracer.reportCopying(this, result);
+        MemoryCopyTracer.reportCopying(this, result);
         return result;
     }
 
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RObjectSize.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RObjectSize.java
new file mode 100644
index 0000000000000000000000000000000000000000..14b70ab39912d6a656926780f59ffe37d48e3daa
--- /dev/null
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RObjectSize.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 2016, 2016, 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.data;
+
+import com.oracle.truffle.api.Assumption;
+import com.oracle.truffle.api.CallTarget;
+import com.oracle.truffle.api.frame.Frame;
+import com.oracle.truffle.api.frame.FrameDescriptor;
+import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.r.runtime.RCaller;
+import com.oracle.truffle.r.runtime.builtins.RBuiltinDescriptor;
+import com.oracle.truffle.r.runtime.gnur.SEXPTYPE;
+
+/**
+ * Support for the sizing of the objects that flow through the interpreter, i.e., mostly
+ * {@link RTypedValue}, but also including scalar types like {@code String} and dimension data for
+ * arrays, i.e., {@code int[]}.
+ *
+ * The actually implementation is controlled by {@link ObjectSizeFactory} to finesse problems with
+ * Java VMs that do not support reflection.
+ *
+ * Owing to the (implementation) complexity of some of the types, two levels of customization are
+ * provided:
+ * <ol>
+ * <li>A completely custom sizing implementation can be provided for a specific type. This effects
+ * all sizing computations.</li>
+ * <li>In any given call to {@link #getObjectSize} an instance of {@IgnoreObjectHandler} can passed.
+ * This allows some additional dynamic control over certain fields depending of the context of the
+ * call. For example, when tracking the incremental memory allocation via {@link RDataFactory}, we
+ * do not want to (double) count fields of type {@link RTypedValue}. However, when computing the
+ * total size of the object, e.g. for the {@code utils::object.size} builtin, we do want to count
+ * them.</li>
+ * </ol>
+ *
+ */
+public class RObjectSize {
+    public static final int INT_SIZE = 32;
+    public static final int DOUBLE_SIZE = 64;
+    public static final int BYTE_SIZE = 8;
+
+    public interface TypeCustomizer {
+        /**
+         * Allows complete control over sizing of a type registered with
+         * {@link #registerTypeCustomizer}.
+         */
+        long getObjectSize(Object obj);
+    }
+
+    public interface IgnoreObjectHandler {
+        /**
+         * Controls which fields of an object passed to {@link #getObjectSize} will be ignored.
+         * {@code rootObject} is the initiating object and {@code obj} is some field of that object
+         * or one of its components. The return value should be {@code true} if {@code obj} should
+         * be ignored.
+         */
+        boolean ignore(Object rootObject, Object obj);
+    }
+
+    /**
+     * Returns an estimate of the size of the this object, including the size of any object-valued
+     * fields, recursively. Evidently this is a snapshot and the size can change as, e.g.,
+     * attributes are added/removed.
+     *
+     * If called immediately after creation by {@link RDataFactory}, with an
+     * {@link IgnoreObjectHandler} that ignores objects created separately and, it provides an
+     * approximation of the incremental memory usage of the system.
+     *
+     * @param ignoreObjectHandler An object that is called to decide whether to include the
+     *            contribution a field of this object (and its sub-objects) in the result. Passing
+     *            {@code null} includes everything. N.B. {@code obj} is typed as {@code Object} only
+     *            to allow scalar typed such as {@code String} to be passed.
+     *
+     */
+    public static long getObjectSize(Object obj, IgnoreObjectHandler ignoreObjectHandler) {
+        return (int) ObjectSizeFactory.getInstance().getObjectSize(obj, ignoreObjectHandler);
+    }
+
+    /**
+     * Register a {@link TypeCustomizer} for {@code klass} and its subclasses. I.e. and object
+     * {@code obj} is customized iff {@code klass.isAssignableFrom(obj.getClass())}.
+     */
+    public static void registerTypeCustomizer(Class<?> klass, TypeCustomizer typeCustomizer) {
+        ObjectSizeFactory.getInstance().registerTypeCustomizer(klass, typeCustomizer);
+    }
+
+    /**
+     * This denotes a special customizer that completely ignores instances of the type and its
+     * subclasses. It allows a more efficient implementation as the type can be suppressed
+     * completely from the computation at the time fields of a containing type are analyzed.
+     */
+    public static final TypeCustomizer IGNORE = new TypeCustomizer() {
+
+        @Override
+        public long getObjectSize(Object obj) {
+            return 0;
+        }
+
+    };
+
+    // TODO construct proper customizers for some of these.
+    static {
+        registerTypeCustomizer(Frame.class, IGNORE);
+        registerTypeCustomizer(FrameDescriptor.class, IGNORE);
+        registerTypeCustomizer(Node.class, IGNORE);
+        registerTypeCustomizer(CallTarget.class, IGNORE);
+        registerTypeCustomizer(RBuiltinDescriptor.class, IGNORE);
+        registerTypeCustomizer(RPromise.Closure.class, IGNORE);
+        registerTypeCustomizer(Assumption.class, IGNORE);
+        registerTypeCustomizer(RCaller.class, IGNORE);
+        registerTypeCustomizer(SEXPTYPE.class, IGNORE);
+    }
+
+}
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 d843cacfb937a8518c91151e91e7e33b3c553076..a18f274d43736e0c7e9936de1eaa8d6adf0aa544 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
@@ -65,7 +65,7 @@ public final class RRaw extends RScalarVector implements RAbstractRawVector {
     @Override
     public RRawVector materialize() {
         RRawVector result = RDataFactory.createRawVector(new byte[]{value});
-        MemoryTracer.reportCopying(this, result);
+        MemoryCopyTracer.reportCopying(this, result);
         return result;
     }
 
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RScalarVector.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RScalarVector.java
index 6e4a7ca5d9590b16c937b8bb62608c6f25ef8cd7..e12aed17f8fbb58a7789401701ae49a288788850 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RScalarVector.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RScalarVector.java
@@ -143,28 +143,28 @@ public abstract class RScalarVector extends RScalar implements RAbstractVector {
     @Override
     public RVector copyResized(int size, boolean fillNA) {
         RVector result = materialize().copyResized(size, fillNA);
-        MemoryTracer.reportCopying(this, result);
+        MemoryCopyTracer.reportCopying(this, result);
         return result;
     }
 
     @Override
     public RAbstractVector copyWithNewDimensions(int[] newDimensions) {
         RAbstractVector result = materialize().copyWithNewDimensions(newDimensions);
-        MemoryTracer.reportCopying(this, result);
+        MemoryCopyTracer.reportCopying(this, result);
         return result;
     }
 
     @Override
     public RVector copyResizedWithDimensions(int[] newDimensions, boolean fillNA) {
         RVector result = materialize().copyResizedWithDimensions(newDimensions, fillNA);
-        MemoryTracer.reportCopying(this, result);
+        MemoryCopyTracer.reportCopying(this, result);
         return result;
     }
 
     @Override
     public RAbstractVector copyDropAttributes() {
         RVector result = materialize().copyDropAttributes();
-        MemoryTracer.reportCopying(this, result);
+        MemoryCopyTracer.reportCopying(this, result);
         return result;
     }
 
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RSequence.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RSequence.java
index 3dcc48a783d72896755c68f58238721596a0d86c..75a022269b5c8b5057b02859a0d6f750d412e76b 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RSequence.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RSequence.java
@@ -81,7 +81,7 @@ public abstract class RSequence implements RAbstractVector {
 
     public final RVector createVector() {
         RVector result = internalCreateVector();
-        MemoryTracer.reportCopying(this, result);
+        MemoryCopyTracer.reportCopying(this, result);
         return result;
     }
 
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 764f4c4b4ba2c88556989eb83461fdf4b8409bc5..af3281e0c19f749edcdbd5f3ab44ed4562315ce6 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
@@ -74,7 +74,7 @@ public final class RString extends RScalarVector implements RAbstractStringVecto
     @Override
     public RStringVector materialize() {
         RStringVector result = RDataFactory.createStringVector(new String[]{getValue()}, isComplete());
-        MemoryTracer.reportCopying(this, result);
+        MemoryCopyTracer.reportCopying(this, result);
         return result;
     }
 
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RVector.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RVector.java
index 159addc3e228b3bda6e2e3bbcc9e9504544c5987..a75b54fdb4bd80a721e5dfb173f611368db7cb62 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RVector.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RVector.java
@@ -581,19 +581,19 @@ public abstract class RVector extends RSharingAttributeStorage implements RShare
 
     protected final RVector internalCopyAndReport() {
         RVector result = internalCopy();
-        MemoryTracer.reportCopying(this, result);
+        MemoryCopyTracer.reportCopying(this, result);
         return result;
     }
 
     protected final RVector internalDeepCopyAndReport() {
         RVector result = internalDeepCopy();
-        MemoryTracer.reportCopying(this, result);
+        MemoryCopyTracer.reportCopying(this, result);
         return result;
     }
 
     protected final RVector internalCopyResizedAndReport(int size, boolean fillNA) {
         RVector result = internalCopyResized(size, fillNA);
-        MemoryTracer.reportCopying(this, result);
+        MemoryCopyTracer.reportCopying(this, result);
         return result;
     }
 
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 0fe0baccfc90e7e0bb0b1a374322b1d431b37cc2..99383ff1a2bf7e9f9c5afd5ed0158a00ffb02835 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
@@ -22,7 +22,7 @@
  */
 package com.oracle.truffle.r.runtime.data.closures;
 
-import com.oracle.truffle.r.runtime.data.MemoryTracer;
+import com.oracle.truffle.r.runtime.data.MemoryCopyTracer;
 import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RAttributes;
 import com.oracle.truffle.r.runtime.data.RList;
@@ -129,14 +129,14 @@ abstract class RToVectorClosure implements RAbstractVector {
     @Override
     public final RAbstractVector copy() {
         RAbstractVector result = vector.copy();
-        MemoryTracer.reportCopying(this, result);
+        MemoryCopyTracer.reportCopying(this, result);
         return result;
     }
 
     @Override
     public final RVector copyResized(int size, boolean fillNA) {
         RVector result = vector.copyResized(size, fillNA);
-        MemoryTracer.reportCopying(this, result);
+        MemoryCopyTracer.reportCopying(this, result);
         return result;
     }
 
@@ -152,7 +152,7 @@ abstract class RToVectorClosure implements RAbstractVector {
     @Override
     public final RAbstractVector copyDropAttributes() {
         RAbstractVector result = vector.copyDropAttributes();
-        MemoryTracer.reportCopying(this, result);
+        MemoryCopyTracer.reportCopying(this, result);
         return result;
     }
 
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 a2ae57288b474717f66033b2bacd43a882e5e899..a9b3eebf0606c05d5a434500e994a5ab907cc38c 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
@@ -24,11 +24,11 @@ package com.oracle.truffle.r.runtime.data.model;
 
 import com.oracle.truffle.api.profiles.ConditionProfile;
 import com.oracle.truffle.r.runtime.RType;
-import com.oracle.truffle.r.runtime.data.MemoryTracer;
+import com.oracle.truffle.r.runtime.data.MemoryCopyTracer;
 import com.oracle.truffle.r.runtime.data.RVector;
 
 /**
- * When implementing, make sure to invoke related {@link MemoryTracer} methods.
+ * When implementing, make sure to invoke related {@link MemoryCopyTracer} methods.
  */
 public interface RAbstractVector extends RAbstractContainer {
 
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/instrument/InstrumentationState.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/instrument/InstrumentationState.java
index 09f5a10046d079cd9b7697e1d51e4fb172fb4556..0b601ce31970ad98c9347a687fc8de366dbcef6c 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/instrument/InstrumentationState.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/instrument/InstrumentationState.java
@@ -68,6 +68,11 @@ public final class InstrumentationState implements RContext.ContextState {
      */
     private final RprofState rprofState;
 
+    /**
+     * The {@link RprofmemState} state, if any, associated with this {@link RContext}.
+     */
+    private final RprofmemState rprofmemState;
+
     private final TracememContext tracememContext;
 
     /**
@@ -96,32 +101,72 @@ public final class InstrumentationState implements RContext.ContextState {
      */
     private boolean debugGloballyDisabled;
 
+    private abstract static class RprofAdapter {
+        protected PrintWriter out;
+
+        /**
+         * Return current output or {@code null} if not profiling.
+         */
+        public PrintWriter out() {
+            return out;
+        }
+
+        public void setOut(PrintWriter out) {
+            this.out = out;
+        }
+    }
+
     /**
      * State used by {@code Rprof}.
      *
      */
-    public static final class RprofState {
-        private PrintWriter out;
+    public static final class RprofState extends RprofAdapter {
         private Thread profileThread;
         private ExecutionEventListener statementListener;
         private long intervalInMillis;
         private boolean lineProfiling;
+        private MemoryQuad memoryQuad;
+
+        public static final class MemoryQuad {
+            public long smallV;
+            public long largeV;
+            public long nodes;
+            public long copied;
+
+            public MemoryQuad copyAndClear() {
+                MemoryQuad result = new MemoryQuad();
+                result.copied = copied;
+                result.largeV = largeV;
+                result.smallV = smallV;
+                result.nodes = nodes;
+                copied = 0;
+                largeV = 0;
+                smallV = 0;
+                nodes = 0;
+                return result;
+            }
+        }
 
         public void initialize(PrintWriter outA, Thread profileThreadA, ExecutionEventListener statementListenerA, long intervalInMillisA,
-                        boolean lineProfilingA) {
+                        boolean lineProfilingA, boolean memoryProfilingA) {
             this.out = outA;
             this.profileThread = profileThreadA;
             this.statementListener = statementListenerA;
             this.intervalInMillis = intervalInMillisA;
             this.lineProfiling = lineProfilingA;
+            this.memoryQuad = memoryProfilingA ? new MemoryQuad() : null;
         }
 
         public boolean lineProfiling() {
             return lineProfiling;
         }
 
-        public PrintWriter out() {
-            return out;
+        public boolean memoryProfiling() {
+            return memoryQuad != null;
+        }
+
+        public MemoryQuad memoryQuad() {
+            return memoryQuad;
         }
 
         public long intervalInMillis() {
@@ -138,6 +183,28 @@ public final class InstrumentationState implements RContext.ContextState {
 
     }
 
+    public static final class RprofmemState extends RprofAdapter {
+        private double threshold;
+        private int pageCount;
+
+        public void initialize(PrintWriter outA, double thresholdA) {
+            this.out = outA;
+            this.threshold = thresholdA;
+        }
+
+        public double threshold() {
+            return threshold;
+        }
+
+        public int pageCount() {
+            return pageCount;
+        }
+
+        public void setPageCount(int pageCount) {
+            this.pageCount = pageCount;
+        }
+    }
+
     public static class BrowserState {
         private boolean inBrowser;
         private String lastEmptyLineCommand = "n";
@@ -162,6 +229,7 @@ public final class InstrumentationState implements RContext.ContextState {
     private InstrumentationState(Instrumenter instrumenter) {
         this.instrumenter = instrumenter;
         this.rprofState = new RprofState();
+        this.rprofmemState = new RprofmemState();
         this.tracememContext = new TracememContext();
         this.browserState = new BrowserState();
     }
@@ -215,6 +283,10 @@ public final class InstrumentationState implements RContext.ContextState {
         return rprofState;
     }
 
+    public RprofmemState getRprofmem() {
+        return rprofmemState;
+    }
+
     public TracememContext getTracemem() {
         return tracememContext;
     }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/nodes/RSyntaxLookup.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/nodes/RSyntaxLookup.java
index 705968c91b77d672bdbac6ee8212c7130ac7a259..ac2cfe029130c6db14079f91223bc1a623d59354 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/nodes/RSyntaxLookup.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/nodes/RSyntaxLookup.java
@@ -29,6 +29,9 @@ import com.oracle.truffle.api.source.SourceSection;
  */
 public interface RSyntaxLookup extends RSyntaxElement {
 
+    /**
+     * @return The identifier that this lookup represents - this needs to be an interned string.
+     */
     String getIdentifier();
 
     boolean isFunctionLookup();
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 373fefd48255861cdcdae7e0480e801ac6c8a825..01e3fcd48ae14532a5137cb0329f0b4184b4e46e 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
@@ -1101,6 +1101,26 @@ character(0)
 #argv <- list(structure(list(sec = c(0, 0, 0, 0, 0), min = c(0L, 0L, 0L, 0L, 0L), hour = c(0L, 0L, 0L, 0L, 0L), mday = 22:26, mon = c(3L, 3L, 3L, 3L, 3L), year = c(108L, 108L, 108L, 108L, 108L), wday = 2:6, yday = 112:116, isdst = c(-1L, -1L, -1L, -1L, -1L)), .Names = c('sec', 'min', 'hour', 'mday', 'mon', 'year', 'wday', 'yday', 'isdst'), class = c('POSIXlt', 'POSIXt'), tzone = 'GMT')); .Internal(POSIXlt2Date(argv[[1]]))
 [1] "2008-04-22" "2008-04-23" "2008-04-24" "2008-04-25" "2008-04-26"
 
+##com.oracle.truffle.r.test.builtins.TestBuiltin_Primitive.testPrimitive1
+#.Primitive('any')
+function (..., na.rm = FALSE)  .Primitive("any")
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_Primitive.testPrimitive1
+#.Primitive('complex')
+Error in .Primitive("complex") : no such primitive function
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_Primitive.testPrimitive1
+#.Primitive('foo')
+Error in .Primitive("foo") : no such primitive function
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_Primitive.testPrimitive1
+#.Primitive(1)
+Error in .Primitive(1) : string argument required
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_Primitive.testPrimitive1
+#.Primitive(c('c', 'b'))
+Error in .Primitive(c("c", "b")) : string argument required
+
 ##com.oracle.truffle.r.test.builtins.TestBuiltin_Primitive.testPrimitive1
 #argv <- list('c');.Primitive(argv[[1]]);
 function (..., recursive = FALSE)  .Primitive("c")
@@ -45976,6 +45996,16 @@ character(0)
 #argv <- list(character(0), 8L); .Internal(strtoi(argv[[1]], argv[[2]]))
 integer(0)
 
+##com.oracle.truffle.r.test.builtins.TestBuiltin_strtrim.teststrtrim
+#v <- c('a', 'fooooo', 'bbbbbb', 'cccccccccc', 'dd', NA); names(v) <- as.character(1:6); strtrim(v, c(2, 5))
+      1       2       3       4       5       6
+    "a" "foooo"    "bb" "ccccc"    "dd"      NA
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_strtrim.teststrtrim
+#v <- c('a', 'fooooo', 'bbbbbb', 'cccccccccc', 'dd', NA); names(v) <- as.character(1:6); strtrim(v, c(2L, 5L))
+      1       2       3       4       5       6
+    "a" "foooo"    "bb" "ccccc"    "dd"      NA
+
 ##com.oracle.truffle.r.test.builtins.TestBuiltin_strtrim.teststrtrim1
 #argv <- list(c('\'time\'', '\'status\''), 128); .Internal(strtrim(argv[[1]], argv[[2]]))
 [1] "'time'"   "'status'"
@@ -48022,6 +48052,39 @@ integer(0)
 #{ u <- function() sys.parents() ; f <- function(x) x ; g <- function(y) f(y) ; h <- function(z=u()) g(z) ; h() }
 [1] 0 1 2 1
 
+##com.oracle.truffle.r.test.builtins.TestBuiltin_t.testTranspose
+#t(1)
+     [,1]
+[1,]    1
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_t.testTranspose
+#t(TRUE)
+     [,1]
+[1,] TRUE
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_t.testTranspose
+#t(as.raw(c(1,2,3,4)))
+     [,1] [,2] [,3] [,4]
+[1,]   01   02   03   04
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_t.testTranspose
+#t(new.env())
+Error in t.default(new.env()) : argument is not a matrix
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_t.testTranspose
+#v <- as.complex(1:50); dim(v) <- c(5,10); dimnames(v) <- list(as.character(40:44), as.character(10:19)); t(v)
+      40    41    42    43    44
+10  1+0i  2+0i  3+0i  4+0i  5+0i
+11  6+0i  7+0i  8+0i  9+0i 10+0i
+12 11+0i 12+0i 13+0i 14+0i 15+0i
+13 16+0i 17+0i 18+0i 19+0i 20+0i
+14 21+0i 22+0i 23+0i 24+0i 25+0i
+15 26+0i 27+0i 28+0i 29+0i 30+0i
+16 31+0i 32+0i 33+0i 34+0i 35+0i
+17 36+0i 37+0i 38+0i 39+0i 40+0i
+18 41+0i 42+0i 43+0i 44+0i 45+0i
+19 46+0i 47+0i 48+0i 49+0i 50+0i
+
 ##com.oracle.truffle.r.test.builtins.TestBuiltin_t.testTranspose
 #{ m <- double() ; dim(m) <- c(0,4) ; t(m) }
 
@@ -107105,26 +107168,38 @@ a b c d
 a b c d e
 1 2 3 4 5
 
-##com.oracle.truffle.r.test.library.fastr.TestInteropEval.testInteropEval
+##com.oracle.truffle.r.test.library.fastr.TestInterop.testInteropEval
 #if (length(grep("FastR", R.Version()$version.string)) != 1) { 1 } else { .fastr.interop.eval('application/x-r', '1') }
 [1] 1
 
-##com.oracle.truffle.r.test.library.fastr.TestInteropEval.testInteropEval
+##com.oracle.truffle.r.test.library.fastr.TestInterop.testInteropEval
 #if (length(grep("FastR", R.Version()$version.string)) != 1) { 16 } else { .fastr.interop.eval('application/x-r', '14 + 2') }
 [1] 16
 
-##com.oracle.truffle.r.test.library.fastr.TestInteropEval.testInteropEval
+##com.oracle.truffle.r.test.library.fastr.TestInterop.testInteropEval
 #if (length(grep("FastR", R.Version()$version.string)) != 1) { 1L } else { .fastr.interop.eval('application/x-r', '1L') }
 [1] 1
 
-##com.oracle.truffle.r.test.library.fastr.TestInteropEval.testInteropEval
+##com.oracle.truffle.r.test.library.fastr.TestInterop.testInteropEval
 #if (length(grep("FastR", R.Version()$version.string)) != 1) { TRUE } else { .fastr.interop.eval('application/x-r', 'TRUE') }
 [1] TRUE
 
-##com.oracle.truffle.r.test.library.fastr.TestInteropEval.testInteropEval
+##com.oracle.truffle.r.test.library.fastr.TestInterop.testInteropEval
 #if (length(grep("FastR", R.Version()$version.string)) != 1) { as.character(123) } else { .fastr.interop.eval('application/x-r', 'as.character(123)') }
 [1] "123"
 
+##com.oracle.truffle.r.test.library.fastr.TestInterop.testInteropExport
+#if (length(grep("FastR", R.Version()$version.string)) != 1) { invisible() } else { .fastr.interop.export('foo', 'foo') }
+
+##com.oracle.truffle.r.test.library.fastr.TestInterop.testInteropExport
+#if (length(grep("FastR", R.Version()$version.string)) != 1) { invisible() } else { .fastr.interop.export('foo', 14 + 2) }
+
+##com.oracle.truffle.r.test.library.fastr.TestInterop.testInteropExport
+#if (length(grep("FastR", R.Version()$version.string)) != 1) { invisible() } else { .fastr.interop.export('foo', 1:100) }
+
+##com.oracle.truffle.r.test.library.fastr.TestInterop.testInteropExport
+#if (length(grep("FastR", R.Version()$version.string)) != 1) { invisible() } else { .fastr.interop.export('foo', new.env()) }
+
 ##com.oracle.truffle.r.test.library.fastr.TestStateTrans.testTransitions
 #if (length(grep("FastR", R.Version()$version.string)) != 1) { 1 } else { { f<-function(x) .fastr.refcountinfo(x); f(c(1,2)) } }
 [1] 1
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_Primitive.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_Primitive.java
index dd1cb83bac27a21d515b2dbc9d66231d545b9f4b..0a5be54776f6d0366f142b7175f1fc4fb3284c57 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_Primitive.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_Primitive.java
@@ -19,6 +19,13 @@ public class TestBuiltin_Primitive extends TestBase {
 
     @Test
     public void testPrimitive1() {
-        assertEval(Ignored.Unknown, "argv <- list('c');.Primitive(argv[[1]]);");
+        // our c() primitive is missing an argument
+        assertEval(Ignored.ImplementationError, "argv <- list('c');.Primitive(argv[[1]]);");
+
+        assertEval(".Primitive(c('c', 'b'))");
+        assertEval(".Primitive('any')");
+        assertEval(".Primitive('foo')");
+        assertEval(".Primitive('complex')");
+        assertEval(".Primitive(1)");
     }
 }
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_strtrim.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_strtrim.java
index 6c566f53a4cd418c70b1f9cf22aeb9019ebc992c..00fc2dc52149e953dff0057469a515912a4decb2 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_strtrim.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_strtrim.java
@@ -51,4 +51,10 @@ public class TestBuiltin_strtrim extends TestBase {
     public void teststrtrim8() {
         assertEval("argv <- list(character(0), 40L); .Internal(strtrim(argv[[1]], argv[[2]]))");
     }
+
+    @Test
+    public void teststrtrim() {
+        assertEval("v <- c('a', 'fooooo', 'bbbbbb', 'cccccccccc', 'dd', NA); names(v) <- as.character(1:6); strtrim(v, c(2L, 5L))");
+        assertEval("v <- c('a', 'fooooo', 'bbbbbb', 'cccccccccc', 'dd', NA); names(v) <- as.character(1:6); strtrim(v, c(2, 5))");
+    }
 }
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_t.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_t.java
index 3d851f24c7e6da980f8521ef2ff438e066cf4420..693cde32eb6e252583908b569377a1a8b142a8c2 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_t.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_t.java
@@ -40,5 +40,11 @@ public class TestBuiltin_t extends TestBase {
         assertEval("{ t(t(matrix(1:4, nrow=2))) }");
 
         assertEval("{ x<-matrix(1:2, ncol=2, dimnames=list(\"a\", c(\"b\", \"c\"))); t(x) }");
+
+        assertEval("t(new.env())");
+        assertEval("v <- as.complex(1:50); dim(v) <- c(5,10); dimnames(v) <- list(as.character(40:44), as.character(10:19)); t(v)");
+        assertEval("t(1)");
+        assertEval("t(TRUE)");
+        assertEval("t(as.raw(c(1,2,3,4)))");
     }
 }
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_tolower.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_tolower.java
index 8b0588b2bdd7b3dbe88f9dcc5db85d1dbc65c20f..f216fb17383d13f45e41e65f7a756002c2cc96a6 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_tolower.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_tolower.java
@@ -39,7 +39,7 @@ public class TestBuiltin_tolower extends TestBase {
 
     @Test
     public void testtolower5() {
-        assertEval(Ignored.Unimplemented, "argv <- list(structure('base', .Names = 'Priority')); .Internal(tolower(argv[[1]]))");
+        assertEval("argv <- list(structure('base', .Names = 'Priority')); .Internal(tolower(argv[[1]]))");
     }
 
     @Test
@@ -57,8 +57,9 @@ public class TestBuiltin_tolower extends TestBase {
         assertEval("{ tolower(c(\"Hello\",\"ByE\")) }");
         assertEval("{ tolower(c()) }");
 
+        // double-to-string conversion problem
         assertEval(Ignored.OutputFormatting, "{ tolower(1E100) }");
-        assertEval(Ignored.Unimplemented, "{ tolower(c(a=\"HI\", \"HELlo\")) }");
+        assertEval("{ tolower(c(a=\"HI\", \"HELlo\")) }");
         assertEval("{ tolower(NA) }");
 
         assertEval("tolower(c('NA', 'na'))");
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_toupper.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_toupper.java
index d00b82523eba4d16051e8f3682407f4e391229be..a6a2008deac27591833cea3559e7b408bbcffd1f 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_toupper.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_toupper.java
@@ -34,8 +34,7 @@ public class TestBuiltin_toupper extends TestBase {
 
     @Test
     public void testtoupper4() {
-        assertEval(Ignored.Unimplemented,
-                        "argv <- list(structure(c('BasicClasses', 'Classes', 'Documentation', 'environment-class', 'GenericFunctions', 'language-class', 'LinearMethodsList-class', 'MethodDefinition-class', 'MethodWithNext-class', 'Methods', 'MethodsList-class', 'callNextMethod', 'ObjectsWithPackage-class', 'S3Part', 'S4groupGeneric', 'SClassExtension-class', 'StructureClasses', 'TraceClasses', 'as', 'callGeneric', 'canCoerce', 'cbind2', 'className', 'classRepresentation-class', 'classesToAM', 'dotsMethods', 'evalSource', 'findClass', 'findMethods', 'fixPre1.8', 'genericFunction-class', 'getClass', 'getMethod', 'getPackageName', 'hasArg', 'implicitGeneric', 'inheritedSlotNames', 'initialize-methods', 'is', 'isSealedMethod', 'LocalReferenceClasses', 'method.skeleton', 'new', 'nonStructure-class', 'promptClass', 'promptMethods', 'ReferenceClasses', 'representation', 'selectSuperClasses', 'setClass', 'setClassUnion', 'setGeneric', 'setLoadActions', 'setMethod', 'setOldClass', 'makeClassRepresentation', 'show', 'showMethods', 'signature-class', 'slot', 'envRefClass-class', 'testInheritedMethods', 'validObject', '.BasicFunsList'), .Names = c('/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/BasicClasses.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/Classes.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/Documentation.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/EnvironmentClass.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/GenericFunctions.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/LanguageClasses.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/LinearMethodsList-class.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/MethodDefinition-class.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/MethodWithNext-class.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/Methods.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/MethodsList-class.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/NextMethod.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/ObjectsWithPackage-class.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/S3Part.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/S4groupGeneric.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/SClassExtension-class.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/StructureClasses.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/TraceClasses.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/as.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/callGeneric.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/canCoerce.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/cbind2.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/className.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/classRepresentation-class.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/classesToAM.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/dotsMethods.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/evalSource.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/findClass.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/findMethods.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/fixPrevious.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/genericFunction-class.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/getClass.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/getMethod.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/getPackageName.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/hasArg.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/implicitGeneric.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/inheritedSlotNames.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/initialize-methods.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/is.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/isSealedMethod.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/localRefClass.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/method.skeleton.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/new.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/nonStructure-class.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/promptClass.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/promptMethods.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/refClass.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/representation.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/selectSuperClasses.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/setClass.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/setClassUnion.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/setGeneric.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/setLoadActions.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/setMethod.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/setOldClass.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/setSClass.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/show.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/showMethods.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/signature-class.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/slot.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/stdRefClass.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/testInheritedMethods.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/validObject.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/zBasicFunsList.tex'))); .Internal(toupper(argv[[1]]))");
+        assertEval("argv <- list(structure(c('BasicClasses', 'Classes', 'Documentation', 'environment-class', 'GenericFunctions', 'language-class', 'LinearMethodsList-class', 'MethodDefinition-class', 'MethodWithNext-class', 'Methods', 'MethodsList-class', 'callNextMethod', 'ObjectsWithPackage-class', 'S3Part', 'S4groupGeneric', 'SClassExtension-class', 'StructureClasses', 'TraceClasses', 'as', 'callGeneric', 'canCoerce', 'cbind2', 'className', 'classRepresentation-class', 'classesToAM', 'dotsMethods', 'evalSource', 'findClass', 'findMethods', 'fixPre1.8', 'genericFunction-class', 'getClass', 'getMethod', 'getPackageName', 'hasArg', 'implicitGeneric', 'inheritedSlotNames', 'initialize-methods', 'is', 'isSealedMethod', 'LocalReferenceClasses', 'method.skeleton', 'new', 'nonStructure-class', 'promptClass', 'promptMethods', 'ReferenceClasses', 'representation', 'selectSuperClasses', 'setClass', 'setClassUnion', 'setGeneric', 'setLoadActions', 'setMethod', 'setOldClass', 'makeClassRepresentation', 'show', 'showMethods', 'signature-class', 'slot', 'envRefClass-class', 'testInheritedMethods', 'validObject', '.BasicFunsList'), .Names = c('/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/BasicClasses.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/Classes.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/Documentation.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/EnvironmentClass.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/GenericFunctions.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/LanguageClasses.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/LinearMethodsList-class.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/MethodDefinition-class.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/MethodWithNext-class.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/Methods.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/MethodsList-class.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/NextMethod.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/ObjectsWithPackage-class.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/S3Part.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/S4groupGeneric.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/SClassExtension-class.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/StructureClasses.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/TraceClasses.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/as.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/callGeneric.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/canCoerce.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/cbind2.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/className.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/classRepresentation-class.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/classesToAM.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/dotsMethods.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/evalSource.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/findClass.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/findMethods.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/fixPrevious.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/genericFunction-class.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/getClass.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/getMethod.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/getPackageName.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/hasArg.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/implicitGeneric.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/inheritedSlotNames.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/initialize-methods.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/is.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/isSealedMethod.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/localRefClass.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/method.skeleton.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/new.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/nonStructure-class.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/promptClass.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/promptMethods.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/refClass.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/representation.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/selectSuperClasses.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/setClass.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/setClassUnion.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/setGeneric.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/setLoadActions.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/setMethod.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/setOldClass.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/setSClass.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/show.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/showMethods.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/signature-class.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/slot.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/stdRefClass.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/testInheritedMethods.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/validObject.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/zBasicFunsList.tex'))); .Internal(toupper(argv[[1]]))");
     }
 
     @Test
@@ -51,6 +50,6 @@ public class TestBuiltin_toupper extends TestBase {
 
         assertEval(Ignored.OutputFormatting, "{ toupper(1E100) }");
         assertEval("{ m <- matrix(\"hi\") ; toupper(m) }");
-        assertEval(Ignored.Unimplemented, "{ toupper(c(a=\"hi\", \"hello\")) }");
+        assertEval("{ toupper(c(a=\"hi\", \"hello\")) }");
     }
 }
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_whichmax.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_whichmax.java
index 2fe9d80df244312a224515e494fa1fc7ced6783c..ddf4c669f82f8a705cacddb8ba2f8b227097aad2 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_whichmax.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_whichmax.java
@@ -24,7 +24,7 @@ public class TestBuiltin_whichmax extends TestBase {
 
     @Test
     public void testwhichmax2() {
-        assertEval(Ignored.Unknown, "argv <- list(structure(c(TRUE, FALSE), .Names = c('d', 'I(as.numeric(d)^2)'))); .Internal(which.max(argv[[1]]))");
+        assertEval("argv <- list(structure(c(TRUE, FALSE), .Names = c('d', 'I(as.numeric(d)^2)'))); .Internal(which.max(argv[[1]]))");
     }
 
     @Test
@@ -34,23 +34,22 @@ public class TestBuiltin_whichmax extends TestBase {
 
     @Test
     public void testwhichmax4() {
-        assertEval(Ignored.Unknown,
-                        "argv <- list(structure(c(NA, 87, 82, 75, 63, 50, 43, 32, 35, 60, 54, 55, 36, 39, NA, NA, 69, 57, 57, 51, 45, 37, 46, 39, 36, 24, 32, 23, 25, 32, NA, 32, 59, 74, 75, 60, 71, 61, 71, 57, 71, 68, 79, 73, 76, 71, 67, 75, 79, 62, 63, 57, 60, 49, 48, 52, 57, 62, 61, 66, 71, 62, 61, 57, 72, 83, 71, 78, 79, 71, 62, 74, 76, 64, 62, 57, 80, 73, 69, 69, 71, 64, 69, 62, 63, 46, 56, 44, 44, 52, 38, 46, 36, 49, 35, 44, 59, 65, 65, 56, 66, 53, 61, 52, 51, 48, 54, 49, 49, 61, NA, NA, 68, 44, 40, 27, 28, 25, 24, 24), .Tsp = c(1945, 1974.75, 4), class = 'ts')); .Internal(which.max(argv[[1]]))");
+        assertEval("argv <- list(structure(c(NA, 87, 82, 75, 63, 50, 43, 32, 35, 60, 54, 55, 36, 39, NA, NA, 69, 57, 57, 51, 45, 37, 46, 39, 36, 24, 32, 23, 25, 32, NA, 32, 59, 74, 75, 60, 71, 61, 71, 57, 71, 68, 79, 73, 76, 71, 67, 75, 79, 62, 63, 57, 60, 49, 48, 52, 57, 62, 61, 66, 71, 62, 61, 57, 72, 83, 71, 78, 79, 71, 62, 74, 76, 64, 62, 57, 80, 73, 69, 69, 71, 64, 69, 62, 63, 46, 56, 44, 44, 52, 38, 46, 36, 49, 35, 44, 59, 65, 65, 56, 66, 53, 61, 52, 51, 48, 54, 49, 49, 61, NA, NA, 68, 44, 40, 27, 28, 25, 24, 24), .Tsp = c(1945, 1974.75, 4), class = 'ts')); .Internal(which.max(argv[[1]]))");
     }
 
     @Test
     public void testwhichmax5() {
-        assertEval(Ignored.Unknown, "argv <- list(NULL); .Internal(which.max(argv[[1]]))");
+        assertEval("argv <- list(NULL); .Internal(which.max(argv[[1]]))");
     }
 
     @Test
     public void testwhichmax6() {
-        assertEval(Ignored.Unknown, "argv <- list(list()); .Internal(which.max(argv[[1]]))");
+        assertEval("argv <- list(list()); .Internal(which.max(argv[[1]]))");
     }
 
     @Test
     public void testwhichmax8() {
-        assertEval(Ignored.Unknown, "argv <- structure(list(x = c(NA, NA)), .Names = 'x');do.call('which.max', argv)");
+        assertEval("argv <- structure(list(x = c(NA, NA)), .Names = 'x');do.call('which.max', argv)");
     }
 
     @Test
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_whichmin.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_whichmin.java
index c68a12f92aeb6613580324898e4074e1212331ef..8dbadd6a6face7e01fb9f1830fe277a4b5186edd 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_whichmin.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_whichmin.java
@@ -19,41 +19,37 @@ public class TestBuiltin_whichmin extends TestBase {
 
     @Test
     public void testwhichmin1() {
-        assertEval(Ignored.Unknown,
-                        "argv <- list(structure(c(345595, 172795, 69115, 34555, 23035, 11515, 5755, 2875, 1147, 571, 379, 187, 91, 27, 11, 3, 1, 3, 4.42857142857143, 4.73716632443532, 4.86858316221766, 4.95619438740589, 4.97809719370294, 4.98904859685147, 4.99452429842574, 4.99780971937029, 4.99890485968515, 4.99945242984257, 4.99978097193703, 4.99989048596851, 4.99994524298426, 4.9999780971937, 4.99998904859685), .Names = c('1 sec', '2 secs', '5 secs', '10 secs', '15 secs', '30 secs', '1 min', '2 mins', '5 mins', '10 mins', '15 mins', '30 mins', '1 hour', '3 hours', '6 hours', '12 hours', '1 DSTday', '2 DSTdays', '1 week', 'halfmonth', '1 month', '3 months', '6 months', '1 year', '2 years', '5 years', '10 years', '20 years', '50 years', '100 years', '200 years', '500 years', '1000 years'))); .Internal(which.min(argv[[1]]))");
+        assertEval("argv <- list(structure(c(345595, 172795, 69115, 34555, 23035, 11515, 5755, 2875, 1147, 571, 379, 187, 91, 27, 11, 3, 1, 3, 4.42857142857143, 4.73716632443532, 4.86858316221766, 4.95619438740589, 4.97809719370294, 4.98904859685147, 4.99452429842574, 4.99780971937029, 4.99890485968515, 4.99945242984257, 4.99978097193703, 4.99989048596851, 4.99994524298426, 4.9999780971937, 4.99998904859685), .Names = c('1 sec', '2 secs', '5 secs', '10 secs', '15 secs', '30 secs', '1 min', '2 mins', '5 mins', '10 mins', '15 mins', '30 mins', '1 hour', '3 hours', '6 hours', '12 hours', '1 DSTday', '2 DSTdays', '1 week', 'halfmonth', '1 month', '3 months', '6 months', '1 year', '2 years', '5 years', '10 years', '20 years', '50 years', '100 years', '200 years', '500 years', '1000 years'))); .Internal(which.min(argv[[1]]))");
     }
 
     @Test
     public void testwhichmin2() {
-        assertEval(Ignored.Unknown,
-                        "argv <- list(structure(c(295, 145, 55, 25, 15, 5, 0, 2.5, 4, 4.5, 4.66666666666667, 4.83333333333333, 4.91666666666667, 4.97222222222222, 4.98611111111111, 4.99305555555556, 4.99652777777778, 4.99826388888889, 4.99950396825397, 4.99977184576774, 4.99988592288387, 4.99996197429462, 4.99998098714731, 4.99999049357366, 4.99999524678683, 4.99999809871473, 4.99999904935737, 4.99999952467868, 4.99999980987147, 4.99999990493574, 4.99999995246787, 4.99999998098715, 4.99999999049357), .Names = c('1 sec', '2 secs', '5 secs', '10 secs', '15 secs', '30 secs', '1 min', '2 mins', '5 mins', '10 mins', '15 mins', '30 mins', '1 hour', '3 hours', '6 hours', '12 hours', '1 DSTday', '2 DSTdays', '1 week', 'halfmonth', '1 month', '3 months', '6 months', '1 year', '2 years', '5 years', '10 years', '20 years', '50 years', '100 years', '200 years', '500 years', '1000 years'))); .Internal(which.min(argv[[1]]))");
+        assertEval("argv <- list(structure(c(295, 145, 55, 25, 15, 5, 0, 2.5, 4, 4.5, 4.66666666666667, 4.83333333333333, 4.91666666666667, 4.97222222222222, 4.98611111111111, 4.99305555555556, 4.99652777777778, 4.99826388888889, 4.99950396825397, 4.99977184576774, 4.99988592288387, 4.99996197429462, 4.99998098714731, 4.99999049357366, 4.99999524678683, 4.99999809871473, 4.99999904935737, 4.99999952467868, 4.99999980987147, 4.99999990493574, 4.99999995246787, 4.99999998098715, 4.99999999049357), .Names = c('1 sec', '2 secs', '5 secs', '10 secs', '15 secs', '30 secs', '1 min', '2 mins', '5 mins', '10 mins', '15 mins', '30 mins', '1 hour', '3 hours', '6 hours', '12 hours', '1 DSTday', '2 DSTdays', '1 week', 'halfmonth', '1 month', '3 months', '6 months', '1 year', '2 years', '5 years', '10 years', '20 years', '50 years', '100 years', '200 years', '500 years', '1000 years'))); .Internal(which.min(argv[[1]]))");
     }
 
     @Test
     public void testwhichmin3() {
-        assertEval(Ignored.Unknown, "argv <- list(NULL); .Internal(which.min(argv[[1]]))");
+        assertEval("argv <- list(NULL); .Internal(which.min(argv[[1]]))");
     }
 
     @Test
     public void testwhichmin4() {
-        assertEval(Ignored.Unknown, "argv <- list(list()); .Internal(which.min(argv[[1]]))");
+        assertEval("argv <- list(list()); .Internal(which.min(argv[[1]]))");
     }
 
     @Test
     public void testwhichmin5() {
-        assertEval(Ignored.Unknown,
-                        "argv <- list(c(NA, 0.951840581382975, 0.805577027554469, 0.663985017923499, 0.53717416750558, 0.496765449963868, 0.472038350505409, 0.463306413812878, 0.485896454097402, 0.520777596351646, 0.524391122960607, 0.492063804965834, 0.513821989320989, 0.521702559081969, 0.533525525673351)); .Internal(which.min(argv[[1]]))");
+        assertEval("argv <- list(c(NA, 0.951840581382975, 0.805577027554469, 0.663985017923499, 0.53717416750558, 0.496765449963868, 0.472038350505409, 0.463306413812878, 0.485896454097402, 0.520777596351646, 0.524391122960607, 0.492063804965834, 0.513821989320989, 0.521702559081969, 0.533525525673351)); .Internal(which.min(argv[[1]]))");
     }
 
     @Test
     public void testwhichmin6() {
-        assertEval(Ignored.Unknown,
-                        "argv <- list(structure(c(NA, 87, 82, 75, 63, 50, 43, 32, 35, 60, 54, 55, 36, 39, NA, NA, 69, 57, 57, 51, 45, 37, 46, 39, 36, 24, 32, 23, 25, 32, NA, 32, 59, 74, 75, 60, 71, 61, 71, 57, 71, 68, 79, 73, 76, 71, 67, 75, 79, 62, 63, 57, 60, 49, 48, 52, 57, 62, 61, 66, 71, 62, 61, 57, 72, 83, 71, 78, 79, 71, 62, 74, 76, 64, 62, 57, 80, 73, 69, 69, 71, 64, 69, 62, 63, 46, 56, 44, 44, 52, 38, 46, 36, 49, 35, 44, 59, 65, 65, 56, 66, 53, 61, 52, 51, 48, 54, 49, 49, 61, NA, NA, 68, 44, 40, 27, 28, 25, 24, 24), .Tsp = c(1945, 1974.75, 4), class = 'ts')); .Internal(which.min(argv[[1]]))");
+        assertEval("argv <- list(structure(c(NA, 87, 82, 75, 63, 50, 43, 32, 35, 60, 54, 55, 36, 39, NA, NA, 69, 57, 57, 51, 45, 37, 46, 39, 36, 24, 32, 23, 25, 32, NA, 32, 59, 74, 75, 60, 71, 61, 71, 57, 71, 68, 79, 73, 76, 71, 67, 75, 79, 62, 63, 57, 60, 49, 48, 52, 57, 62, 61, 66, 71, 62, 61, 57, 72, 83, 71, 78, 79, 71, 62, 74, 76, 64, 62, 57, 80, 73, 69, 69, 71, 64, 69, 62, 63, 46, 56, 44, 44, 52, 38, 46, 36, 49, 35, 44, 59, 65, 65, 56, 66, 53, 61, 52, 51, 48, 54, 49, 49, 61, NA, NA, 68, 44, 40, 27, 28, 25, 24, 24), .Tsp = c(1945, 1974.75, 4), class = 'ts')); .Internal(which.min(argv[[1]]))");
     }
 
     @Test
     public void testwhichmin8() {
-        assertEval(Ignored.Unknown, "argv <- structure(list(x = c(NA, NA, Inf)), .Names = 'x');do.call('which.min', argv)");
+        assertEval("argv <- structure(list(x = c(NA, NA, Inf)), .Names = 'x');do.call('which.min', argv)");
     }
 
     @Test
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/fastr/TestInteropEval.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/fastr/TestInterop.java
similarity index 79%
rename from com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/fastr/TestInteropEval.java
rename to com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/fastr/TestInterop.java
index f866e6f2e6f40e52d0b39934bb32c6aeef5dc77e..0f8ad2f8dd8531ed611b23c3a89d9173f3f1d0fe 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/fastr/TestInteropEval.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/fastr/TestInterop.java
@@ -26,7 +26,7 @@ import org.junit.Test;
 
 import com.oracle.truffle.r.test.TestBase;
 
-public class TestInteropEval extends TestBase {
+public class TestInterop extends TestBase {
 
     @Test
     public void testInteropEval() {
@@ -36,4 +36,12 @@ public class TestInteropEval extends TestBase {
         assertEvalFastR(".fastr.interop.eval('application/x-r', 'TRUE')", "TRUE");
         assertEvalFastR(".fastr.interop.eval('application/x-r', 'as.character(123)')", "as.character(123)");
     }
+
+    @Test
+    public void testInteropExport() {
+        assertEvalFastR(".fastr.interop.export('foo', 14 + 2)", "invisible()");
+        assertEvalFastR(".fastr.interop.export('foo', 'foo')", "invisible()");
+        assertEvalFastR(".fastr.interop.export('foo', 1:100)", "invisible()");
+        assertEvalFastR(".fastr.interop.export('foo', new.env())", "invisible()");
+    }
 }