diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_C.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_C.java
index 5b0c8f546e17f2d739ebcdce3fad98451d759eae..4fe1bc302f4ab5e310006f81af0af294e7c4c3a0 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_C.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_C.java
@@ -41,7 +41,7 @@ class TruffleLLVM_C implements CRFFI {
         private int numArgs;
 
         @Override
-        public synchronized void execute(NativeCallInfo nativeCallInfo, Object[] args, boolean hasStrings) {
+        public synchronized void execute(NativeCallInfo nativeCallInfo, Object[] args) {
             Object[] wargs = wrap(args);
             try {
                 if (messageNode == null) {
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_C.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_C.java
index 54e3f8b3a3f59a4ded7b4dcea1ab7dac7e913361..d74f6aec88b242439f430ab7687e79d592d3775e 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_C.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_C.java
@@ -22,122 +22,39 @@
  */
 package com.oracle.truffle.r.ffi.impl.nfi;
 
-import java.nio.charset.StandardCharsets;
-
+import com.oracle.truffle.api.CompilerAsserts;
 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.interop.CanResolve;
 import com.oracle.truffle.api.interop.ForeignAccess;
 import com.oracle.truffle.api.interop.InteropException;
 import com.oracle.truffle.api.interop.Message;
-import com.oracle.truffle.api.interop.MessageResolution;
-import com.oracle.truffle.api.interop.Resolve;
 import com.oracle.truffle.api.interop.TruffleObject;
-import com.oracle.truffle.api.interop.java.JavaInterop;
 import com.oracle.truffle.api.nodes.Node;
-import com.oracle.truffle.r.ffi.impl.interop.UnsafeAdapter;
 import com.oracle.truffle.r.ffi.impl.nfi.TruffleNFI_CFactory.TruffleNFI_InvokeCNodeGen;
 import com.oracle.truffle.r.runtime.RInternalError;
-import com.oracle.truffle.r.runtime.data.RDataFactory;
-import com.oracle.truffle.r.runtime.data.RStringVector;
-import com.oracle.truffle.r.runtime.data.model.RAbstractAtomicVector;
-import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
 import com.oracle.truffle.r.runtime.ffi.CRFFI;
 import com.oracle.truffle.r.runtime.ffi.NativeCallInfo;
 
-import sun.misc.Unsafe;
-
 public class TruffleNFI_C implements CRFFI {
 
-    @MessageResolution(receiverType = StringWrapper.class)
-    public static class StringWrapperMR {
-
-        @Resolve(message = "IS_POINTER")
-        public abstract static class StringWrapperNativeIsPointerNode extends Node {
-            protected Object access(@SuppressWarnings("unused") StringWrapper receiver) {
-                return true;
-            }
-        }
-
-        @Resolve(message = "AS_POINTER")
-        public abstract static class StringWrapperNativeAsPointerNode extends Node {
-            protected Object access(StringWrapper receiver) {
-                return receiver.asPointer();
-            }
-        }
-
-        @CanResolve
-        public abstract static class StringWrapperCheck extends Node {
-
-            protected static boolean test(TruffleObject receiver) {
-                return receiver instanceof StringWrapper;
-            }
-        }
-    }
-
-    public static final class StringWrapper implements TruffleObject {
-
-        private final RAbstractStringVector vector;
-        private long address;
-
-        public StringWrapper(RAbstractStringVector vector) {
-            this.vector = vector;
-        }
-
-        @Override
-        public ForeignAccess getForeignAccess() {
-            return StringWrapperMRForeign.ACCESS;
-        }
+    private static final String[] SIGNATURES = new String[32];
 
-        public long asPointer() {
-            if (address == 0) {
-                address = allocate();
-            }
-            return address;
-        }
-
-        @TruffleBoundary
-        private long allocate() {
-            int length = vector.getLength();
-            int size = length * 8;
-            byte[][] bytes = new byte[length][];
-            for (int i = 0; i < length; i++) {
-                String element = vector.getDataAt(i);
-                bytes[i] = element.getBytes(StandardCharsets.US_ASCII);
-                size += bytes[i].length + 1;
+    private static String getSignatureForArity(int arity) {
+        CompilerAsserts.neverPartOfCompilation();
+        if (arity >= SIGNATURES.length || SIGNATURES[arity] == null) {
+            StringBuilder str = new StringBuilder().append('(');
+            for (int i = 0; i < arity; i++) {
+                str.append(i > 0 ? ", " : "");
+                str.append("pointer");
             }
-            long memory = UnsafeAdapter.UNSAFE.allocateMemory(size);
-            long ptr = memory + length * 8; // start of the actual character data
-            for (int i = 0; i < length; i++) {
-                UnsafeAdapter.UNSAFE.putLong(memory + i * 8, ptr);
-                UnsafeAdapter.UNSAFE.copyMemory(bytes[i], Unsafe.ARRAY_BYTE_BASE_OFFSET, null, ptr, bytes[i].length);
-                ptr += bytes[i].length;
-                UnsafeAdapter.UNSAFE.putByte(ptr++, (byte) 0);
-            }
-            assert ptr == memory + size : "should have filled everything";
-            return memory;
-        }
-
-        public RAbstractStringVector copyBack(RAbstractStringVector original) {
-            if (address == 0) {
-                return original;
-            } else {
-                RStringVector result = original.materialize();
-                String[] data = result.isTemporary() ? result.getDataWithoutCopying() : result.getDataCopy();
-                for (int i = 0; i < data.length; i++) {
-                    long ptr = UnsafeAdapter.UNSAFE.getLong(address + i * 8);
-                    int length = 0;
-                    while (UnsafeAdapter.UNSAFE.getByte(ptr + length) != 0) {
-                        length++;
-                    }
-                    byte[] bytes = new byte[length];
-                    UnsafeAdapter.UNSAFE.copyMemory(null, ptr, bytes, Unsafe.ARRAY_BYTE_BASE_OFFSET, length);
-                    data[i] = new String(bytes, StandardCharsets.US_ASCII);
-                }
-                UnsafeAdapter.UNSAFE.freeMemory(address);
-                return RDataFactory.createStringVector(data, true);
+            String signature = str.append("): void").toString();
+            if (arity < SIGNATURES.length) {
+                SIGNATURES[arity] = signature;
             }
+            return signature;
+        } else {
+            return SIGNATURES[arity];
         }
     }
 
@@ -145,31 +62,25 @@ public class TruffleNFI_C implements CRFFI {
 
         @Child private Node bindNode = Message.createInvoke(1).createNode();
 
-        @Override
-        protected Object getNativeArgument(int index, ArgumentType type, RAbstractAtomicVector vector) {
-            if (type == ArgumentType.VECTOR_STRING) {
-                return new StringWrapper((RAbstractStringVector) vector);
-            } else {
-                return super.getNativeArgument(index, type, vector);
-            }
-        }
-
-        @Override
-        protected Object postProcessArgument(ArgumentType type, RAbstractAtomicVector vector, Object nativeArgument) {
-            if (type == ArgumentType.VECTOR_STRING) {
-                return ((StringWrapper) nativeArgument).copyBack((RAbstractStringVector) vector);
-            } else {
-                return super.postProcessArgument(type, vector, nativeArgument);
+        @TruffleBoundary
+        protected TruffleObject getFunction(TruffleObject address, int arity) {
+            // cache signatures
+            try {
+                return (TruffleObject) ForeignAccess.sendInvoke(bindNode, address, "bind", getSignatureForArity(arity));
+            } catch (InteropException ex) {
+                throw RInternalError.shouldNotReachHere(ex);
             }
         }
 
-        @Specialization(guards = "args.length == 0")
-        protected void invokeCall0(NativeCallInfo nativeCallInfo, @SuppressWarnings("unused") Object[] args, @SuppressWarnings("unused") boolean hasStrings,
-                        @Cached("createExecute(args.length)") Node executeNode) {
+        @Specialization(guards = {"args.length == cachedArgsLength", "nativeCallInfo.address.asTruffleObject() == cachedAddress"})
+        protected void invokeCallCached(@SuppressWarnings("unused") NativeCallInfo nativeCallInfo, Object[] args,
+                        @SuppressWarnings("unused") @Cached("args.length") int cachedArgsLength,
+                        @Cached("createExecute(cachedArgsLength)") Node executeNode,
+                        @SuppressWarnings("unused") @Cached("nativeCallInfo.address.asTruffleObject()") TruffleObject cachedAddress,
+                        @Cached("getFunction(cachedAddress, cachedArgsLength)") TruffleObject cachedFunction) {
             synchronized (TruffleNFI_Call.class) {
                 try {
-                    TruffleObject callFunction = (TruffleObject) ForeignAccess.sendInvoke(bindNode, nativeCallInfo.address.asTruffleObject(), "bind", "(): void");
-                    ForeignAccess.sendExecute(executeNode, callFunction);
+                    ForeignAccess.sendExecute(executeNode, cachedFunction, args);
                 } catch (InteropException ex) {
                     throw RInternalError.shouldNotReachHere(ex);
                 }
@@ -177,14 +88,12 @@ public class TruffleNFI_C implements CRFFI {
         }
 
         @Specialization(limit = "99", guards = "args.length == cachedArgsLength")
-        protected void invokeCall1(NativeCallInfo nativeCallInfo, Object[] args, @SuppressWarnings("unused") boolean hasStrings,
+        protected void invokeCallCachedLength(NativeCallInfo nativeCallInfo, Object[] args,
                         @Cached("args.length") int cachedArgsLength,
                         @Cached("createExecute(cachedArgsLength)") Node executeNode) {
             synchronized (TruffleNFI_Call.class) {
                 try {
-                    Object[] nargs = new Object[cachedArgsLength];
-                    TruffleObject callFunction = (TruffleObject) ForeignAccess.sendInvoke(bindNode, nativeCallInfo.address.asTruffleObject(), "bind", getSignature(args, nargs));
-                    ForeignAccess.sendExecute(executeNode, callFunction, nargs);
+                    ForeignAccess.sendExecute(executeNode, getFunction(nativeCallInfo.address.asTruffleObject(), cachedArgsLength), args);
                 } catch (InteropException ex) {
                     throw RInternalError.shouldNotReachHere(ex);
                 }
@@ -196,30 +105,6 @@ public class TruffleNFI_C implements CRFFI {
         }
     }
 
-    @TruffleBoundary
-    private static String getSignature(Object[] args, Object[] nargs) {
-        StringBuilder sb = new StringBuilder();
-        sb.append('(');
-        for (int i = 0; i < args.length; i++) {
-            Object arg = args[i];
-            if (arg instanceof int[]) {
-                sb.append("[sint32]");
-            } else if (arg instanceof double[]) {
-                sb.append("[double]");
-            } else if (arg instanceof StringWrapper) {
-                sb.append("pointer");
-            } else {
-                throw RInternalError.unimplemented(".C type: " + arg.getClass().getSimpleName());
-            }
-            nargs[i] = JavaInterop.asTruffleObject(arg);
-            if (i < args.length - 1) {
-                sb.append(", ");
-            }
-        }
-        sb.append("): void");
-        return sb.toString();
-    }
-
     @Override
     public InvokeCNode createInvokeCNode() {
         return TruffleNFI_InvokeCNodeGen.create();
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_Call.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_Call.java
index 74bf04cb475ed478f986f1cc298faedf974df61a..b32d2ebd75900d2c15806c897424d872d08c8b33 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_Call.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_Call.java
@@ -235,32 +235,36 @@ public class TruffleNFI_Call implements CallRFFI {
         }
     }
 
+    private static final String[] SIGNATURES = new String[32];
+
+    private static String getSignatureForArity(int arity) {
+        CompilerAsserts.neverPartOfCompilation();
+        if (arity >= SIGNATURES.length || SIGNATURES[arity] == null) {
+            StringBuilder str = new StringBuilder().append('(');
+            for (int i = 0; i < arity; i++) {
+                str.append(i > 0 ? ", " : "");
+                str.append("pointer");
+            }
+            String signature = str.append("): pointer").toString();
+            if (arity < SIGNATURES.length) {
+                SIGNATURES[arity] = signature;
+            }
+            return signature;
+        } else {
+            return SIGNATURES[arity];
+        }
+    }
+
     @ImportStatic(FFIWrapNode.class)
     public abstract static class TruffleNFI_InvokeCallNode extends Node implements InvokeCallNode {
-        private static final String[] SIGNATURES = new String[32];
 
         @Child private Node bindNode = Message.createInvoke(1).createNode();
 
         @TruffleBoundary
         protected TruffleObject getFunction(TruffleObject address, int arity) {
             // cache signatures
-            String signature;
-            if (arity >= SIGNATURES.length || SIGNATURES[arity] == null) {
-                StringBuilder str = new StringBuilder().append('(');
-                for (int i = 0; i < arity; i++) {
-                    str.append(i > 0 ? ", " : "");
-                    str.append("pointer");
-                }
-                str.append("): pointer");
-                signature = str.toString();
-                if (arity < SIGNATURES.length) {
-                    SIGNATURES[arity] = signature;
-                }
-            } else {
-                signature = SIGNATURES[arity];
-            }
             try {
-                return (TruffleObject) ForeignAccess.sendInvoke(bindNode, address, "bind", signature);
+                return (TruffleObject) ForeignAccess.sendInvoke(bindNode, address, "bind", getSignatureForArity(arity));
             } catch (InteropException ex) {
                 throw RInternalError.shouldNotReachHere(ex);
             }
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_UpCallsRFFIImpl.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_UpCallsRFFIImpl.java
index 627f2be72dd4a0721820fff6d945ce9c28d99d83..80a67a01665d7323df703d4a08221b147eac5c69 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_UpCallsRFFIImpl.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_UpCallsRFFIImpl.java
@@ -24,8 +24,6 @@ package com.oracle.truffle.r.ffi.impl.nfi;
 
 import static com.oracle.truffle.r.ffi.impl.common.RFFIUtils.guaranteeInstanceOf;
 
-import java.nio.charset.StandardCharsets;
-
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.interop.CanResolve;
 import com.oracle.truffle.api.interop.ForeignAccess;
@@ -37,18 +35,12 @@ import com.oracle.truffle.api.interop.TruffleObject;
 import com.oracle.truffle.api.interop.UnsupportedMessageException;
 import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.r.ffi.impl.common.JavaUpCallsRFFIImpl;
-import com.oracle.truffle.r.ffi.impl.common.RFFIUtils;
-import com.oracle.truffle.r.ffi.impl.interop.UnsafeAdapter;
-import com.oracle.truffle.r.ffi.impl.nfi.TruffleNFI_C.StringWrapper;
 import com.oracle.truffle.r.ffi.impl.upcalls.FFIUnwrapNode;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RInternalError;
-import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RIntVector;
 import com.oracle.truffle.r.runtime.data.RLogicalVector;
-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;
 import com.oracle.truffle.r.runtime.ffi.CharSXPWrapper;
 import com.oracle.truffle.r.runtime.ffi.DLL;
 import com.oracle.truffle.r.runtime.ffi.DLL.CEntry;
@@ -57,8 +49,6 @@ import com.oracle.truffle.r.runtime.ffi.DLL.DotSymbol;
 import com.oracle.truffle.r.runtime.ffi.DLL.SymbolHandle;
 import com.oracle.truffle.r.runtime.gnur.SEXPTYPE;
 
-import sun.misc.Unsafe;
-
 public class TruffleNFI_UpCallsRFFIImpl extends JavaUpCallsRFFIImpl {
 
     private static final String SETSYMBOL_SIGNATURE = "(pointer, sint32, pointer, sint32): pointer";
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/CallAndExternalFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/CallAndExternalFunctions.java
index 5960ab3be7d4a1c7c74fe33c98b62aaaadeebfec..b126fa2ddfd67c2e0e033f99e2f2977066edb89d 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/CallAndExternalFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/CallAndExternalFunctions.java
@@ -70,6 +70,7 @@ import com.oracle.truffle.r.library.utils.TypeConvertNodeGen;
 import com.oracle.truffle.r.library.utils.UnzipNodeGen;
 import com.oracle.truffle.r.nodes.builtin.RExternalBuiltinNode;
 import com.oracle.truffle.r.nodes.builtin.RInternalCodeBuiltinNode;
+import com.oracle.truffle.r.nodes.builtin.base.foreign.LookupAdapter.ExtractNativeCallInfoNode;
 import com.oracle.truffle.r.nodes.objects.GetPrimNameNodeGen;
 import com.oracle.truffle.r.nodes.objects.NewObjectNodeGen;
 import com.oracle.truffle.r.runtime.FastROptions;
@@ -653,10 +654,11 @@ public class CallAndExternalFunctions {
          */
         @SuppressWarnings("unused")
         @Specialization(limit = "2", guards = {"cached == symbol", "builtin == null"})
-        protected Object callNamedFunction(VirtualFrame frame, RList symbol, RArgsValuesAndNames args, Object packageName,
+        protected Object callNamedFunction(RList symbol, RArgsValuesAndNames args, Object packageName,
                         @Cached("symbol") RList cached,
                         @Cached("lookupBuiltin(symbol)") RExternalBuiltinNode builtin,
-                        @Cached("extractSymbolInfo.execute(frame, symbol)") NativeCallInfo nativeCallInfo) {
+                        @Cached("new()") ExtractNativeCallInfoNode extractSymbolInfo,
+                        @Cached("extractSymbolInfo.execute(symbol)") NativeCallInfo nativeCallInfo) {
             return callRFFINode.execute(nativeCallInfo, args.getArguments());
         }
 
@@ -666,12 +668,13 @@ public class CallAndExternalFunctions {
          */
         @SuppressWarnings("unused")
         @Specialization(replaces = {"callNamedFunction", "doExternal"})
-        protected Object callNamedFunctionGeneric(VirtualFrame frame, RList symbol, RArgsValuesAndNames args, Object packageName) {
+        protected Object callNamedFunctionGeneric(RList symbol, RArgsValuesAndNames args, Object packageName,
+                        @Cached("new()") ExtractNativeCallInfoNode extractSymbolInfo) {
             RExternalBuiltinNode builtin = lookupBuiltin(symbol);
             if (builtin != null) {
                 throw RInternalError.shouldNotReachHere("Cache for .Calls with FastR reimplementation (lookupBuiltin(...) != null) exceeded the limit");
             }
-            NativeCallInfo nativeCallInfo = extractSymbolInfo.execute(frame, symbol);
+            NativeCallInfo nativeCallInfo = extractSymbolInfo.execute(symbol);
             return callRFFINode.execute(nativeCallInfo, args.getArguments());
         }
 
@@ -786,9 +789,10 @@ public class CallAndExternalFunctions {
 
         @SuppressWarnings("unused")
         @Specialization(limit = "1", guards = {"cached == symbol"})
-        protected Object callNamedFunction(VirtualFrame frame, RList symbol, RArgsValuesAndNames args, Object packageName,
+        protected Object callNamedFunction(RList symbol, RArgsValuesAndNames args, Object packageName,
                         @Cached("symbol") RList cached,
-                        @Cached("extractSymbolInfo.execute(frame, symbol)") NativeCallInfo nativeCallInfo) {
+                        @Cached("new()") ExtractNativeCallInfoNode extractSymbolInfo,
+                        @Cached("extractSymbolInfo.execute(symbol)") NativeCallInfo nativeCallInfo) {
             Object list = encodeArgumentPairList(args, nativeCallInfo.name);
             return callRFFINode.execute(nativeCallInfo, new Object[]{list});
         }
@@ -882,9 +886,10 @@ public class CallAndExternalFunctions {
 
         @SuppressWarnings("unused")
         @Specialization(limit = "1", guards = {"cached == symbol"})
-        protected Object callNamedFunction(VirtualFrame frame, RList symbol, RArgsValuesAndNames args, Object packageName,
+        protected Object callNamedFunction(RList symbol, RArgsValuesAndNames args, Object packageName,
                         @Cached("symbol") RList cached,
-                        @Cached("extractSymbolInfo.execute(frame, symbol)") NativeCallInfo nativeCallInfo) {
+                        @Cached("new()") ExtractNativeCallInfoNode extractSymbolInfo,
+                        @Cached("extractSymbolInfo.execute(symbol)") NativeCallInfo nativeCallInfo) {
             Object list = encodeArgumentPairList(args, nativeCallInfo.name);
             return callRFFINode.execute(nativeCallInfo, new Object[]{CALL, getOp(), list, RHO});
         }
@@ -938,8 +943,9 @@ public class CallAndExternalFunctions {
         }
 
         @Specialization
-        protected Object callNamedFunction(VirtualFrame frame, RList symbol, RArgsValuesAndNames args, @SuppressWarnings("unused") Object packageName) {
-            NativeCallInfo nativeCallInfo = extractSymbolInfo.execute(frame, symbol);
+        protected Object callNamedFunction(RList symbol, RArgsValuesAndNames args, @SuppressWarnings("unused") Object packageName,
+                        @Cached("new()") ExtractNativeCallInfoNode extractSymbolInfo) {
+            NativeCallInfo nativeCallInfo = extractSymbolInfo.execute(symbol);
             Object list = encodeArgumentPairList(args, nativeCallInfo.name);
             return callRFFINode.execute(nativeCallInfo, new Object[]{list});
         }
@@ -1000,8 +1006,9 @@ public class CallAndExternalFunctions {
         }
 
         @Specialization
-        protected Object callNamedFunction(VirtualFrame frame, RList symbol, RArgsValuesAndNames args, @SuppressWarnings("unused") Object packageName) {
-            NativeCallInfo nativeCallInfo = extractSymbolInfo.execute(frame, symbol);
+        protected Object callNamedFunction(RList symbol, RArgsValuesAndNames args, @SuppressWarnings("unused") Object packageName,
+                        @Cached("new()") ExtractNativeCallInfoNode extractSymbolInfo) {
+            NativeCallInfo nativeCallInfo = extractSymbolInfo.execute(symbol);
             return callRFFINode.execute(nativeCallInfo, args.getArguments());
         }
 
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/FortranAndCFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/FortranAndCFunctions.java
index 6455ab0ee486f962cf03849c96a4620e7fa0e49f..a0f416daf666ceb721b1951bccafda1284f39c27 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/FortranAndCFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/FortranAndCFunctions.java
@@ -27,7 +27,6 @@ import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.builtin.RExternalBuiltinNode;
 import com.oracle.truffle.r.nodes.builtin.base.foreign.FortranAndCFunctionsFactory.FortranResultNamesSetterNodeGen;
 import com.oracle.truffle.r.nodes.builtin.base.foreign.LookupAdapter.ExtractNativeCallInfoNode;
-import com.oracle.truffle.r.nodes.builtin.base.foreign.LookupAdapterFactory.ExtractNativeCallInfoNodeGen;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RRuntime;
@@ -56,7 +55,6 @@ public class FortranAndCFunctions {
 
     protected abstract static class CRFFIAdapter extends RBuiltinNode.Arg6 {
 
-        @Child protected ExtractNativeCallInfoNode extractSymbolInfo = ExtractNativeCallInfoNodeGen.create();
         @Child protected CRFFI.InvokeCNode invokeCNode = RFFIFactory.getCRFFI().createInvokeCNode();
 
         @Override
@@ -101,16 +99,15 @@ public class FortranAndCFunctions {
 
         @SuppressWarnings("unused")
         @Specialization(limit = "1", guards = {"cached == symbol", "builtin != null"})
-        protected Object doExternal(VirtualFrame frame, RList symbol, RArgsValuesAndNames args, byte naok, byte dup, Object rPackage, RMissing encoding,
-                        @Cached("symbol") RList cached,
+        protected Object doExternal(VirtualFrame frame, RList symbol, RArgsValuesAndNames args, byte naok, byte dup, Object rPackage, RMissing encoding, @Cached("symbol") RList cached,
                         @Cached("lookupBuiltin(symbol)") RExternalBuiltinNode builtin) {
             return resNamesSetter.execute(builtin.call(frame, args), args);
         }
 
         @Specialization(guards = "lookupBuiltin(symbol) == null")
-        protected RList c(VirtualFrame frame, RList symbol, RArgsValuesAndNames args, byte naok, byte dup, @SuppressWarnings("unused") Object rPackage,
-                        @SuppressWarnings("unused") RMissing encoding) {
-            NativeCallInfo nativeCallInfo = extractSymbolInfo.execute(frame, symbol);
+        protected RList c(RList symbol, RArgsValuesAndNames args, byte naok, byte dup, @SuppressWarnings("unused") Object rPackage, @SuppressWarnings("unused") RMissing encoding,
+                        @Cached("new()") ExtractNativeCallInfoNode extractSymbolInfo) {
+            NativeCallInfo nativeCallInfo = extractSymbolInfo.execute(symbol);
             return invokeCNode.dispatch(nativeCallInfo, naok, dup, args);
         }
 
@@ -179,8 +176,9 @@ public class FortranAndCFunctions {
         }
 
         @Specialization
-        protected RList c(VirtualFrame frame, RList symbol, RArgsValuesAndNames args, byte naok, byte dup, @SuppressWarnings("unused") Object rPackage, @SuppressWarnings("unused") RMissing encoding) {
-            NativeCallInfo nativeCallInfo = extractSymbolInfo.execute(frame, symbol);
+        protected RList c(RList symbol, RArgsValuesAndNames args, byte naok, byte dup, @SuppressWarnings("unused") Object rPackage, @SuppressWarnings("unused") RMissing encoding,
+                        @Cached("new()") ExtractNativeCallInfoNode extractSymbolInfo) {
+            NativeCallInfo nativeCallInfo = extractSymbolInfo.execute(symbol);
             return invokeCNode.dispatch(nativeCallInfo, naok, dup, args);
         }
 
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/LookupAdapter.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/LookupAdapter.java
index 881a395fcde021c36f781208e3960553207e47c7..7ab299db0127e37ac02c2a93d6afda9a1f0ddcb9 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/LookupAdapter.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/LookupAdapter.java
@@ -23,10 +23,7 @@
 package com.oracle.truffle.r.nodes.builtin.base.foreign;
 
 import com.oracle.truffle.api.CompilerAsserts;
-import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
-import com.oracle.truffle.api.dsl.Specialization;
-import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.r.library.stats.RandFunctionsNodes;
 import com.oracle.truffle.r.nodes.access.vector.ElementAccessMode;
@@ -34,7 +31,6 @@ import com.oracle.truffle.r.nodes.access.vector.ExtractVectorNode;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.builtin.RExternalBuiltinNode;
 import com.oracle.truffle.r.nodes.builtin.RInternalCodeBuiltinNode;
-import com.oracle.truffle.r.nodes.builtin.base.foreign.LookupAdapterFactory.ExtractNativeCallInfoNodeGen;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RInternalCode;
 import com.oracle.truffle.r.runtime.RInternalError;
@@ -72,7 +68,6 @@ interface Lookup {
  * still implemented by native code.
  */
 abstract class LookupAdapter extends RBuiltinNode.Arg3 implements Lookup {
-    @Child protected ExtractNativeCallInfoNode extractSymbolInfo = ExtractNativeCallInfoNodeGen.create();
 
     protected static class UnimplementedExternal extends RExternalBuiltinNode {
         private final String name;
@@ -139,35 +134,19 @@ abstract class LookupAdapter extends RBuiltinNode.Arg3 implements Lookup {
      * Extracts the salient information needed for a native call from the {@link RList} value
      * provided from R.
      */
-    public abstract static class ExtractNativeCallInfoNode extends Node {
+    public static final class ExtractNativeCallInfoNode extends Node {
         @Child private ExtractVectorNode nameExtract = ExtractVectorNode.create(ElementAccessMode.SUBSCRIPT, true);
         @Child private ExtractVectorNode addressExtract = ExtractVectorNode.create(ElementAccessMode.SUBSCRIPT, true);
         @Child private ExtractVectorNode packageExtract = ExtractVectorNode.create(ElementAccessMode.SUBSCRIPT, true);
         @Child private ExtractVectorNode infoExtract = ExtractVectorNode.create(ElementAccessMode.SUBSCRIPT, true);
 
-        protected abstract NativeCallInfo execute(VirtualFrame frame, RList symbol);
-
-        @Specialization
-        protected NativeCallInfo extractNativeCallInfo(RList symbol) {
-            if (nameExtract == null) {
-                CompilerDirectives.transferToInterpreterAndInvalidate();
-                nameExtract = insert(ExtractVectorNode.create(ElementAccessMode.SUBSCRIPT, true));
-            }
-            if (addressExtract == null) {
-                CompilerDirectives.transferToInterpreterAndInvalidate();
-                addressExtract = insert(ExtractVectorNode.create(ElementAccessMode.SUBSCRIPT, true));
-            }
-            if (packageExtract == null) {
-                CompilerDirectives.transferToInterpreterAndInvalidate();
-                packageExtract = insert(ExtractVectorNode.create(ElementAccessMode.SUBSCRIPT, true));
-            }
+        protected NativeCallInfo execute(RList symbol) {
             String name = RRuntime.asString(nameExtract.applyAccessField(symbol, "name"));
             SymbolHandle address = ((RExternalPtr) addressExtract.applyAccessField(symbol, "address")).getAddr();
             // field name may be "package" or "dll", but always at (R) index 3
             RList packageList = (RList) packageExtract.apply(symbol, new Object[]{3}, RLogical.valueOf(false), RMissing.instance);
-            DLLInfo dllInfo = (DLLInfo) ((RExternalPtr) addressExtract.applyAccessField(packageList, "info")).getExternalObject();
+            DLLInfo dllInfo = (DLLInfo) ((RExternalPtr) infoExtract.applyAccessField(packageList, "info")).getExternalObject();
             return new NativeCallInfo(name, address, dllInfo);
-
         }
     }
 
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/CRFFI.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/CRFFI.java
index 80d1e70c8706ca9f0459912d7f7a0aec2b1fe1f1..bcb0a801559bbe7faf54e60d9d85b835a8fa5881 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/CRFFI.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/CRFFI.java
@@ -22,140 +22,370 @@
  */
 package com.oracle.truffle.r.runtime.ffi;
 
-import java.nio.charset.Charset;
 import java.nio.charset.StandardCharsets;
 
-import com.oracle.truffle.api.CompilerAsserts;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.interop.CanResolve;
+import com.oracle.truffle.api.interop.ForeignAccess;
+import com.oracle.truffle.api.interop.MessageResolution;
+import com.oracle.truffle.api.interop.Resolve;
+import com.oracle.truffle.api.interop.TruffleObject;
+import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.api.profiles.ConditionProfile;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
 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.data.RArgsValuesAndNames;
+import com.oracle.truffle.r.runtime.data.RComplex;
+import com.oracle.truffle.r.runtime.data.RComplexVector;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
+import com.oracle.truffle.r.runtime.data.RDoubleVector;
+import com.oracle.truffle.r.runtime.data.RIntVector;
 import com.oracle.truffle.r.runtime.data.RList;
+import com.oracle.truffle.r.runtime.data.RLogicalVector;
+import com.oracle.truffle.r.runtime.data.RRawVector;
 import com.oracle.truffle.r.runtime.data.RStringVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractAtomicVector;
+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.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.nodes.RBaseNode;
 
-/**
- * Support for the {.C} and {.Fortran} calls.
- */
-public interface CRFFI {
+import sun.misc.Unsafe;
 
-    public static abstract class InvokeCNode extends RBaseNode {
+@MessageResolution(receiverType = TemporaryWrapper.class)
+class TemporaryWrapperMR {
 
-        public enum ArgumentType {
-            VECTOR_DOUBLE,
-            VECTOR_INT,
-            VECTOR_LOGICAL,
-            VECTOR_STRING;
+    @Resolve(message = "IS_POINTER")
+    public abstract static class TemporaryWrapperIsPointerNode extends Node {
+        protected Object access(@SuppressWarnings("unused") TemporaryWrapper receiver) {
+            return true;
         }
+    }
 
-        private static final Charset charset = StandardCharsets.US_ASCII;
+    @Resolve(message = "AS_POINTER")
+    public abstract static class TemporaryWrapperAsPointerNode extends Node {
+        private final ConditionProfile profile = ConditionProfile.createBinaryProfile();
 
-        /**
-         * Invoke the native method identified by {@code symbolInfo} passing it the arguments in
-         * {@code args}. The values in {@code args} should be native types,e.g., {@code double[]}
-         * not {@code RDoubleVector}. Strings are already converted to 2-dimensional byte arrays.
-         *
-         * @param hasStrings if {@code true}, then the {@code args} array may contain one or more
-         *            values of type {@code byte[][]}, which represent arrays of strings in ASCII
-         *            encoding.
-         */
-        protected abstract void execute(NativeCallInfo nativeCallInfo, Object[] args, boolean hasStrings);
-
-        @TruffleBoundary
-        protected Object getNativeArgument(int index, ArgumentType type, RAbstractAtomicVector vector) {
-            CompilerAsserts.neverPartOfCompilation();
-            switch (type) {
-                case VECTOR_DOUBLE: {
-                    double[] data = ((RAbstractDoubleVector) vector).materialize().getDataCopy();
-                    for (int i = 0; i < data.length; i++) {
-                        if (!RRuntime.isFinite(data[i])) {
-                            throw error(RError.Message.NA_NAN_INF_IN_FOREIGN_FUNCTION_CALL, index + 1);
-                        }
-                    }
-                    return data;
-                }
-                case VECTOR_INT: {
-                    int[] data = ((RAbstractIntVector) vector).materialize().getDataCopy();
-                    for (int i = 0; i < data.length; i++) {
-                        if (RRuntime.isNA(data[i])) {
-                            throw error(RError.Message.NA_IN_FOREIGN_FUNCTION_CALL, index + 1);
-                        }
-                    }
-                    return data;
-                }
-                case VECTOR_LOGICAL: {
-                    // passed as int[]
-                    byte[] data = ((RAbstractLogicalVector) vector).materialize().getDataWithoutCopying();
-                    int[] dataAsInt = new int[data.length];
-                    for (int j = 0; j < data.length; j++) {
-                        // An NA is an error but the error handling happens in checkNAs
-                        dataAsInt[j] = RRuntime.isNA(data[j]) ? RRuntime.INT_NA : data[j];
-                    }
-                    for (int i = 0; i < dataAsInt.length; i++) {
-                        if (RRuntime.isNA(dataAsInt[i])) {
-                            throw error(RError.Message.NA_IN_FOREIGN_FUNCTION_CALL, index + 1);
-                        }
-                    }
-                    return dataAsInt;
-                }
-                case VECTOR_STRING: {
-                    RAbstractStringVector data = (RAbstractStringVector) vector;
-                    for (int i = 0; i < data.getLength(); i++) {
-                        if (RRuntime.isNA(data.getDataAt(i))) {
-                            throw error(RError.Message.NA_IN_FOREIGN_FUNCTION_CALL, index + 1);
-                        }
-                    }
-                    return encodeStrings((RAbstractStringVector) vector);
-                }
-                default:
-                    throw RInternalError.shouldNotReachHere();
+        protected Object access(TemporaryWrapper receiver) {
+            long address = receiver.address;
+            if (profile.profile(address == 0)) {
+                receiver.address = address = receiver.allocate();
             }
+            return address;
         }
+    }
 
-        private Object[] getNativeArguments(Object[] array, ArgumentType[] argTypes) {
-            Object[] nativeArgs = new Object[array.length];
-            for (int i = 0; i < array.length; i++) {
-                nativeArgs[i] = getNativeArgument(i, argTypes[i], (RAbstractAtomicVector) array[i]);
-            }
-            return nativeArgs;
+    @Resolve(message = "READ")
+    public abstract static class TemporaryWrapperReadNode extends Node {
+        protected Object access(TemporaryWrapper receiver, long index) {
+            return receiver.read(index);
         }
 
-        @TruffleBoundary
-        protected Object postProcessArgument(ArgumentType type, RAbstractAtomicVector vector, Object nativeArgument) {
-            switch (type) {
-                case VECTOR_STRING:
-                    return ((RAbstractStringVector) vector).materialize().copyResetData(decodeStrings((byte[][]) nativeArgument));
-                case VECTOR_DOUBLE:
-                    return ((RAbstractDoubleVector) vector).materialize().copyResetData((double[]) nativeArgument);
-                case VECTOR_INT:
-                    return ((RAbstractIntVector) vector).materialize().copyResetData((int[]) nativeArgument);
-                case VECTOR_LOGICAL: {
-                    int[] intData = (int[]) nativeArgument;
-                    byte[] byteData = new byte[intData.length];
-                    for (int j = 0; j < intData.length; j++) {
-                        byteData[j] = RRuntime.isNA(intData[j]) ? RRuntime.LOGICAL_NA : RRuntime.asLogical(intData[j] != 0);
-                    }
-                    return ((RAbstractLogicalVector) vector).materialize().copyResetData(byteData);
-                }
-                default:
-                    throw RInternalError.shouldNotReachHere();
+        protected Object access(TemporaryWrapper receiver, int index) {
+            return receiver.read(index);
+        }
+    }
+
+    @Resolve(message = "WRITE")
+    public abstract static class TemporaryWrapperWriteNode extends Node {
+        protected Object access(TemporaryWrapper receiver, long index, Object value) {
+            receiver.write(index, value);
+            return value;
+        }
+
+        protected Object access(TemporaryWrapper receiver, int index, Object value) {
+            receiver.write(index, value);
+            return value;
+        }
+    }
+
+    @CanResolve
+    public abstract static class TemporaryWrapperCheck extends Node {
+        protected static boolean test(TruffleObject receiver) {
+            return receiver instanceof TemporaryWrapper;
+        }
+    }
+}
+
+abstract class TemporaryWrapper implements TruffleObject {
+
+    protected long address;
+    protected RAbstractAtomicVector vector;
+
+    public TemporaryWrapper(RAbstractAtomicVector vector) {
+        this.vector = vector;
+    }
+
+    public abstract long allocate();
+
+    public Object read(long index) {
+        throw RInternalError.unimplemented("read at " + index);
+    }
+
+    public void write(long index, Object value) {
+        throw RInternalError.unimplemented("write of value " + value + " at index " + index);
+    }
+
+    @Override
+    public final ForeignAccess getForeignAccess() {
+        return TemporaryWrapperMRForeign.ACCESS;
+    }
+
+    public final RAbstractAtomicVector cleanup() {
+        if (address == 0) {
+            return vector;
+        } else {
+            return copyBack();
+        }
+    }
+
+    protected abstract RAbstractAtomicVector copyBack();
+}
+
+final class StringWrapper extends TemporaryWrapper {
+
+    public StringWrapper(RAbstractStringVector vector) {
+        super(vector);
+    }
+
+    @Override
+    @TruffleBoundary
+    public long allocate() {
+        RAbstractStringVector v = (RAbstractStringVector) vector;
+        int length = v.getLength();
+        int size = length * 8;
+        byte[][] bytes = new byte[length][];
+        for (int i = 0; i < length; i++) {
+            String element = v.getDataAt(i);
+            bytes[i] = element.getBytes(StandardCharsets.US_ASCII);
+            size += bytes[i].length + 1;
+        }
+        long memory = UnsafeAdapter.UNSAFE.allocateMemory(size);
+        long ptr = memory + length * 8; // start of the actual character data
+        for (int i = 0; i < length; i++) {
+            UnsafeAdapter.UNSAFE.putLong(memory + i * 8, ptr);
+            UnsafeAdapter.UNSAFE.copyMemory(bytes[i], Unsafe.ARRAY_BYTE_BASE_OFFSET, null, ptr, bytes[i].length);
+            ptr += bytes[i].length;
+            UnsafeAdapter.UNSAFE.putByte(ptr++, (byte) 0);
+        }
+        assert ptr == memory + size : "should have filled everything";
+        return memory;
+    }
+
+    @Override
+    @TruffleBoundary
+    protected RStringVector copyBack() {
+        RStringVector result = ((RAbstractStringVector) vector).materialize();
+        String[] data = result.isTemporary() ? result.getDataWithoutCopying() : result.getDataCopy();
+        for (int i = 0; i < data.length; i++) {
+            long ptr = UnsafeAdapter.UNSAFE.getLong(address + i * 8);
+            int length = 0;
+            while (UnsafeAdapter.UNSAFE.getByte(ptr + length) != 0) {
+                length++;
             }
+            byte[] bytes = new byte[length];
+            UnsafeAdapter.UNSAFE.copyMemory(null, ptr, bytes, Unsafe.ARRAY_BYTE_BASE_OFFSET, length);
+            data[i] = new String(bytes, StandardCharsets.US_ASCII);
         }
+        UnsafeAdapter.UNSAFE.freeMemory(address);
+        return RDataFactory.createStringVector(data, true);
+    }
+}
 
-        private Object[] postProcessArguments(Object[] array, ArgumentType[] argTypes, Object[] nativeArgs) {
-            Object[] results = new Object[array.length];
-            for (int i = 0; i < array.length; i++) {
-                results[i] = postProcessArgument(argTypes[i], (RAbstractAtomicVector) array[i], nativeArgs[i]);
+final class IntWrapper extends TemporaryWrapper {
+
+    public IntWrapper(RAbstractIntVector vector) {
+        super(vector);
+    }
+
+    @Override
+    @TruffleBoundary
+    public long allocate() {
+        RAbstractIntVector v = (RAbstractIntVector) vector;
+        int length = v.getLength();
+        long memory = UnsafeAdapter.UNSAFE.allocateMemory(length * Unsafe.ARRAY_INT_INDEX_SCALE);
+        for (int i = 0; i < length; i++) {
+            UnsafeAdapter.UNSAFE.putInt(memory + (i * Unsafe.ARRAY_INT_INDEX_SCALE), v.getDataAt(i));
+        }
+        return memory;
+    }
+
+    @Override
+    @TruffleBoundary
+    protected RIntVector copyBack() {
+        RIntVector result = ((RAbstractIntVector) vector).materialize();
+        int[] data = result.isTemporary() ? result.getDataWithoutCopying() : result.getDataCopy();
+        UnsafeAdapter.UNSAFE.copyMemory(null, address, data, Unsafe.ARRAY_INT_BASE_OFFSET, vector.getLength() * Unsafe.ARRAY_INT_INDEX_SCALE);
+        UnsafeAdapter.UNSAFE.freeMemory(address);
+        return RDataFactory.createIntVector(data, false);
+    }
+}
+
+final class LogicalWrapper extends TemporaryWrapper {
+
+    public LogicalWrapper(RAbstractLogicalVector vector) {
+        super(vector);
+    }
+
+    @Override
+    @TruffleBoundary
+    public long allocate() {
+        RAbstractLogicalVector v = (RAbstractLogicalVector) vector;
+        int length = v.getLength();
+        long memory = UnsafeAdapter.UNSAFE.allocateMemory(length * Unsafe.ARRAY_INT_INDEX_SCALE);
+        for (int i = 0; i < length; i++) {
+            UnsafeAdapter.UNSAFE.putInt(memory + (i * Unsafe.ARRAY_INT_INDEX_SCALE), RRuntime.logical2int(v.getDataAt(i)));
+        }
+        return memory;
+    }
+
+    @Override
+    @TruffleBoundary
+    protected RLogicalVector copyBack() {
+        RLogicalVector result = ((RAbstractLogicalVector) vector).materialize();
+        byte[] data = result.isTemporary() ? result.getDataWithoutCopying() : result.getDataCopy();
+        int length = vector.getLength();
+        for (int i = 0; i < length; i++) {
+            data[i] = RRuntime.int2logical(UnsafeAdapter.UNSAFE.getInt(address + (i * Unsafe.ARRAY_INT_INDEX_SCALE)));
+        }
+        UnsafeAdapter.UNSAFE.freeMemory(address);
+        return RDataFactory.createLogicalVector(data, false);
+    }
+}
+
+final class DoubleWrapper extends TemporaryWrapper {
+
+    public DoubleWrapper(RAbstractDoubleVector vector) {
+        super(vector);
+    }
+
+    @Override
+    @TruffleBoundary
+    public long allocate() {
+        RAbstractDoubleVector v = (RAbstractDoubleVector) vector;
+        int length = v.getLength();
+        long memory = UnsafeAdapter.UNSAFE.allocateMemory(length * Unsafe.ARRAY_DOUBLE_INDEX_SCALE);
+        for (int i = 0; i < length; i++) {
+            UnsafeAdapter.UNSAFE.putDouble(memory + (i * Unsafe.ARRAY_DOUBLE_INDEX_SCALE), v.getDataAt(i));
+        }
+        return memory;
+    }
+
+    @Override
+    @TruffleBoundary
+    protected RDoubleVector copyBack() {
+        RDoubleVector result = ((RAbstractDoubleVector) vector).materialize();
+        double[] data = result.isTemporary() ? result.getDataWithoutCopying() : result.getDataCopy();
+        UnsafeAdapter.UNSAFE.copyMemory(null, address, data, Unsafe.ARRAY_DOUBLE_BASE_OFFSET, vector.getLength() * Unsafe.ARRAY_DOUBLE_INDEX_SCALE);
+        UnsafeAdapter.UNSAFE.freeMemory(address);
+        return RDataFactory.createDoubleVector(data, false);
+    }
+}
+
+final class ComplexWrapper extends TemporaryWrapper {
+
+    public ComplexWrapper(RAbstractComplexVector vector) {
+        super(vector);
+    }
+
+    @Override
+    @TruffleBoundary
+    public long allocate() {
+        RAbstractComplexVector v = (RAbstractComplexVector) vector;
+        int length = v.getLength();
+        long memory = UnsafeAdapter.UNSAFE.allocateMemory(length * Unsafe.ARRAY_DOUBLE_INDEX_SCALE * 2);
+        for (int i = 0; i < length; i++) {
+            RComplex element = v.getDataAt(i);
+            UnsafeAdapter.UNSAFE.putDouble(memory + (i * Unsafe.ARRAY_DOUBLE_INDEX_SCALE * 2), element.getRealPart());
+            UnsafeAdapter.UNSAFE.putDouble(memory + (i * Unsafe.ARRAY_DOUBLE_INDEX_SCALE * 2) + 8, element.getImaginaryPart());
+        }
+        return memory;
+    }
+
+    @Override
+    @TruffleBoundary
+    protected RComplexVector copyBack() {
+        RComplexVector result = ((RAbstractComplexVector) vector).materialize();
+        double[] data = result.isTemporary() ? result.getDataWithoutCopying() : result.getDataCopy();
+        UnsafeAdapter.UNSAFE.copyMemory(null, address, data, Unsafe.ARRAY_DOUBLE_BASE_OFFSET, vector.getLength() * 2 * Unsafe.ARRAY_DOUBLE_INDEX_SCALE);
+        UnsafeAdapter.UNSAFE.freeMemory(address);
+        return RDataFactory.createComplexVector(data, false);
+    }
+}
+
+final class RawWrapper extends TemporaryWrapper {
+
+    public RawWrapper(RAbstractRawVector vector) {
+        super(vector);
+    }
+
+    @Override
+    @TruffleBoundary
+    public long allocate() {
+        RAbstractRawVector v = (RAbstractRawVector) vector;
+        int length = v.getLength();
+        long memory = UnsafeAdapter.UNSAFE.allocateMemory(length * Unsafe.ARRAY_BYTE_INDEX_SCALE);
+        for (int i = 0; i < length; i++) {
+            UnsafeAdapter.UNSAFE.putByte(memory + (i * Unsafe.ARRAY_BYTE_INDEX_SCALE), v.getRawDataAt(i));
+        }
+        return memory;
+    }
+
+    @Override
+    @TruffleBoundary
+    protected RRawVector copyBack() {
+        RRawVector result = ((RAbstractRawVector) vector).materialize();
+        byte[] data = result.isTemporary() ? result.getDataWithoutCopying() : result.getDataCopy();
+        UnsafeAdapter.UNSAFE.copyMemory(null, address, data, Unsafe.ARRAY_BYTE_BASE_OFFSET, vector.getLength() * Unsafe.ARRAY_BYTE_INDEX_SCALE);
+        UnsafeAdapter.UNSAFE.freeMemory(address);
+        return RDataFactory.createRawVector(data);
+    }
+}
+
+/**
+ * Support for the {.C} and {.Fortran} calls.
+ */
+public interface CRFFI {
+
+    public static abstract class InvokeCNode extends RBaseNode {
+
+        /**
+         * Invoke the native method identified by {@code symbolInfo} passing it the arguments in
+         * {@code args}. The values in {@code args} should be support the IS_POINTER/AS_POINTER
+         * messages.
+         */
+        protected abstract void execute(NativeCallInfo nativeCallInfo, Object[] args);
+
+        @TruffleBoundary
+        protected TemporaryWrapper getNativeArgument(int index, Object vector) {
+            if (vector instanceof RAbstractDoubleVector) {
+                return new DoubleWrapper((RAbstractDoubleVector) vector);
+            } else if (vector instanceof RAbstractIntVector) {
+                return new IntWrapper((RAbstractIntVector) vector);
+            } else if (vector instanceof RAbstractLogicalVector) {
+                return new LogicalWrapper((RAbstractLogicalVector) vector);
+            } else if (vector instanceof RAbstractComplexVector) {
+                return new ComplexWrapper((RAbstractComplexVector) vector);
+            } else if (vector instanceof RAbstractStringVector) {
+                return new StringWrapper((RAbstractStringVector) vector);
+            } else if (vector instanceof RAbstractRawVector) {
+                return new RawWrapper((RAbstractRawVector) vector);
+            } else if (vector instanceof String) {
+                return new StringWrapper(RDataFactory.createStringVectorFromScalar((String) vector));
+            } else if (vector instanceof Double) {
+                return new DoubleWrapper(RDataFactory.createDoubleVectorFromScalar((double) vector));
+            } else if (vector instanceof Integer) {
+                return new IntWrapper(RDataFactory.createIntVectorFromScalar((int) vector));
+            } else if (vector instanceof Byte) {
+                return new LogicalWrapper(RDataFactory.createLogicalVectorFromScalar((byte) vector));
+            } else {
+                throw error(RError.Message.UNIMPLEMENTED_ARG_TYPE, index + 1);
             }
-            return results;
         }
 
         @TruffleBoundary
@@ -164,52 +394,22 @@ public interface CRFFI {
             boolean dupArgs = RRuntime.fromLogical(dup);
             @SuppressWarnings("unused")
             boolean checkNA = RRuntime.fromLogical(naok);
+
             // Analyze the args, making copies (ignoring dup for now)
             Object[] array = new Object[args.getLength()];
-            ArgumentType[] argTypes = new ArgumentType[array.length];
-            boolean hasStrings = false;
             for (int i = 0; i < array.length; i++) {
-                Object arg = args.getArgument(i);
-                ArgumentType type;
-                RAbstractAtomicVector vector;
-                if (arg instanceof RAbstractDoubleVector) {
-                    vector = (RAbstractDoubleVector) arg;
-                    type = ArgumentType.VECTOR_DOUBLE;
-                } else if (arg instanceof RAbstractIntVector) {
-                    vector = (RAbstractIntVector) arg;
-                    type = ArgumentType.VECTOR_INT;
-                } else if (arg instanceof RAbstractLogicalVector) {
-                    vector = (RAbstractLogicalVector) arg;
-                    type = ArgumentType.VECTOR_LOGICAL;
-                } else if (arg instanceof RAbstractStringVector) {
-                    hasStrings = true;
-                    vector = (RAbstractStringVector) arg;
-                    type = ArgumentType.VECTOR_STRING;
-                } else if (arg instanceof String) {
-                    hasStrings = true;
-                    vector = RDataFactory.createStringVectorFromScalar((String) arg);
-                    type = ArgumentType.VECTOR_STRING;
-                } else if (arg instanceof Double) {
-                    vector = RDataFactory.createDoubleVectorFromScalar((double) arg);
-                    type = ArgumentType.VECTOR_DOUBLE;
-                } else if (arg instanceof Integer) {
-                    vector = RDataFactory.createIntVectorFromScalar((int) arg);
-                    type = ArgumentType.VECTOR_INT;
-                } else if (arg instanceof Byte) {
-                    vector = RDataFactory.createLogicalVectorFromScalar((byte) arg);
-                    type = ArgumentType.VECTOR_LOGICAL;
-                } else {
-                    throw error(RError.Message.UNIMPLEMENTED_ARG_TYPE, i + 1);
-                }
-                argTypes[i] = type;
-                array[i] = vector;
+                array[i] = getNativeArgument(i, args.getArgument(i));
             }
-            Object[] nativeArgs = getNativeArguments(array, argTypes);
-            execute(nativeCallInfo, nativeArgs, hasStrings);
+
+            execute(nativeCallInfo, array);
+
             // we have to assume that the native method updated everything
-            RStringVector listNames = validateArgNames(array.length, args.getSignature());
-            Object[] results = postProcessArguments(array, argTypes, nativeArgs);
-            return RDataFactory.createList(results, listNames);
+            Object[] results = new Object[array.length];
+            for (int i = 0; i < array.length; i++) {
+                results[i] = ((TemporaryWrapper) array[i]).cleanup();
+            }
+
+            return RDataFactory.createList(results, validateArgNames(array.length, args.getSignature()));
         }
 
         private static RStringVector validateArgNames(int argsLength, ArgumentsSignature signature) {
@@ -223,34 +423,6 @@ public interface CRFFI {
             }
             return RDataFactory.createStringVector(listArgNames, RDataFactory.COMPLETE_VECTOR);
         }
-
-        private static Object encodeStrings(RAbstractStringVector vector) {
-            byte[][] result = new byte[vector.getLength()][];
-            for (int i = 0; i < vector.getLength(); i++) {
-                result[i] = encodeString(vector.getDataAt(i));
-            }
-            return result;
-        }
-
-        private static byte[] encodeString(String str) {
-            byte[] bytes = str.getBytes(charset);
-            byte[] result = new byte[bytes.length + 1];
-            System.arraycopy(bytes, 0, result, 0, bytes.length);
-            return result;
-        }
-
-        private static String[] decodeStrings(byte[][] bytes) {
-            String[] result = new String[bytes.length];
-            for (int i = 0; i < bytes.length; i++) {
-                int length = 0;
-                while (length < bytes[i].length && bytes[i][length] != 0) {
-                    length++;
-                }
-                result[i] = new String(bytes[i], 0, length, charset);
-            }
-            return result;
-        }
-
     }
 
     InvokeCNode createInvokeCNode();
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/DLL.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/DLL.java
index 35b3f1ec87c75f89563972b3406c20e2fe0dc068..81d536b6a788c1ec995091ebb4c1084fed52853e 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/DLL.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/DLL.java
@@ -13,6 +13,7 @@ package com.oracle.truffle.r.runtime.ffi;
 
 import java.io.File;
 import java.util.ArrayList;
+import java.util.HashSet;
 import java.util.concurrent.atomic.AtomicInteger;
 
 import com.oracle.truffle.api.CompilerAsserts;
@@ -190,6 +191,7 @@ public class DLL {
         private boolean forceSymbols;
         private final DotSymbol[][] nativeSymbols = new DotSymbol[NativeSymbolType.values().length][];
         private ArrayList<CEntry> cEntryTable = null;
+        private final HashSet<String> unsuccessfulLookups = new HashSet<>();
 
         private DLLInfo(String name, String path, boolean dynamicLookup, Object handle) {
             this.id = ID.getAndIncrement();
@@ -581,7 +583,7 @@ public class DLL {
     }
 
     public static final class RFindSymbolNode extends Node {
-        @Child RdlsymNode rdlsymNode = new RdlsymNode();
+        @Child private RdlsymNode rdlsymNode = new RdlsymNode();
 
         /**
          * Directly analogous to the GnuR function {@code R_FindSymbol}.
@@ -681,9 +683,13 @@ public class DLL {
                 mName = name + "_";
             }
             try {
+                if (dllInfo.unsuccessfulLookups.contains(mName)) {
+                    return SYMBOL_NOT_FOUND;
+                }
                 SymbolHandle symValue = dlSymNode.execute(dllInfo.handle, mName);
                 return symValue;
             } catch (UnsatisfiedLinkError ex) {
+                dllInfo.unsuccessfulLookups.add(mName);
                 return SYMBOL_NOT_FOUND;
             }
         }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/UnsafeAdapter.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/UnsafeAdapter.java
new file mode 100644
index 0000000000000000000000000000000000000000..1dd44d213fb6d24f221fffb8c326ec15b33877d2
--- /dev/null
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/UnsafeAdapter.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.r.runtime.ffi;
+
+import java.lang.reflect.Field;
+
+import sun.misc.Unsafe;
+
+public class UnsafeAdapter {
+    public static final Unsafe UNSAFE = initUnsafe();
+
+    private static Unsafe initUnsafe() {
+        try {
+            return Unsafe.getUnsafe();
+        } catch (SecurityException se) {
+            try {
+                Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
+                theUnsafe.setAccessible(true);
+                return (Unsafe) theUnsafe.get(Unsafe.class);
+            } catch (Exception e) {
+                throw new RuntimeException("exception while trying to get Unsafe", e);
+            }
+        }
+    }
+}