diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/common/JavaUpCallsRFFIImpl.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/common/JavaUpCallsRFFIImpl.java
index 4fd88f4a7b73ec3fcd66ba8cb69adb51108db65c..5c4f604aa5853122bbf3d7bc854efbebbe02b2de 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/common/JavaUpCallsRFFIImpl.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/common/JavaUpCallsRFFIImpl.java
@@ -1360,7 +1360,7 @@ public abstract class JavaUpCallsRFFIImpl implements UpCallsRFFI {
     private static VectorRFFIWrapper wrapString(String s) {
         CharSXPWrapper v = CharSXPWrapper.create(s);
         NativeDataAccess.asPointer(v);
-        return new VectorRFFIWrapper(v);
+        return VectorRFFIWrapper.get(v);
     }
 
     @Override
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/FFI_RForeignAccessFactoryImpl.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/FFI_RForeignAccessFactoryImpl.java
index 8c7367b7b4f4fac169b9dd94592383629c19bf18..9914b38c2c60ca6b3b92bd59cc177e42bb91b2ef 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/FFI_RForeignAccessFactoryImpl.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/FFI_RForeignAccessFactoryImpl.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
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 e5babb3b23ee522afebde0e4c7c249fd76c06ad6..d16b67adc421004bedc17cb2be3734b19c90060a 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
@@ -22,66 +22,23 @@
  */
 package com.oracle.truffle.r.ffi.impl.llvm;
 
-import com.oracle.truffle.api.CompilerDirectives;
-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.TruffleObject;
-import com.oracle.truffle.api.nodes.Node;
-import com.oracle.truffle.r.ffi.impl.interop.NativeDoubleArray;
-import com.oracle.truffle.r.ffi.impl.interop.NativeIntegerArray;
-import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.ffi.CRFFI;
-import com.oracle.truffle.r.runtime.ffi.NativeCallInfo;
-import com.oracle.truffle.r.runtime.ffi.interop.NativeRawArray;
+import com.oracle.truffle.r.runtime.ffi.InvokeCNode;
+import com.oracle.truffle.r.runtime.ffi.InvokeCNode.FunctionObjectGetter;
+import com.oracle.truffle.r.runtime.ffi.InvokeCNodeGen;
 
-class TruffleLLVM_C implements CRFFI {
-    private static final class TruffleLLVM_InvokeCNode extends InvokeCNode {
-
-        @Child private Node messageNode;
-        private int numArgs;
+public class TruffleLLVM_C implements CRFFI {
 
+    static final class LLVMFunctionObjectGetter extends FunctionObjectGetter {
         @Override
-        public void execute(NativeCallInfo nativeCallInfo, Object[] args) {
-            Object[] wargs = wrap(args);
-            try {
-                if (messageNode == null) {
-                    CompilerDirectives.transferToInterpreterAndInvalidate();
-                    // TODO: we assume that the number of args doesn't change, is that correct?
-                    messageNode = Message.createExecute(args.length).createNode();
-                    numArgs = args.length;
-                }
-                assert numArgs == args.length;
-                ForeignAccess.sendExecute(messageNode, nativeCallInfo.address.asTruffleObject(), wargs);
-            } catch (InteropException ex) {
-                throw RInternalError.shouldNotReachHere(ex);
-            }
-        }
-
-        static Object[] wrap(Object[] args) {
-            Object[] nargs = new Object[args.length];
-            for (int i = 0; i < args.length; i++) {
-                Object arg = args[i];
-                Object narg;
-                if (arg instanceof int[]) {
-                    narg = new NativeIntegerArray((int[]) arg);
-                } else if (arg instanceof double[]) {
-                    narg = new NativeDoubleArray((double[]) arg);
-                } else if (arg instanceof byte[]) {
-                    narg = new NativeRawArray((byte[]) arg);
-                } else if (arg instanceof TruffleObject) {
-                    narg = arg;
-                } else {
-                    throw RInternalError.unimplemented(".C type: " + arg.getClass().getSimpleName());
-                }
-                nargs[i] = narg;
-            }
-            return nargs;
+        public TruffleObject execute(TruffleObject address, int arity) {
+            return address;
         }
     }
 
     @Override
     public InvokeCNode createInvokeCNode() {
-        return new TruffleLLVM_InvokeCNode();
+        return InvokeCNodeGen.create(new LLVMFunctionObjectGetter());
     }
 }
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/managed/Managed_RFFIFactory.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/managed/Managed_RFFIFactory.java
index 3d32fdf2838e08d6b5e1c1c9bf0587a980b3d2c0..62cc7e662e7f66d008d1b35e60eef4836f32b078 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/managed/Managed_RFFIFactory.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/managed/Managed_RFFIFactory.java
@@ -33,6 +33,7 @@ import com.oracle.truffle.r.runtime.ffi.CRFFI;
 import com.oracle.truffle.r.runtime.ffi.CallRFFI;
 import com.oracle.truffle.r.runtime.ffi.CallRFFI.HandleUpCallExceptionNode;
 import com.oracle.truffle.r.runtime.ffi.DLLRFFI;
+import com.oracle.truffle.r.runtime.ffi.InvokeCNode;
 import com.oracle.truffle.r.runtime.ffi.LapackRFFI;
 import com.oracle.truffle.r.runtime.ffi.MiscRFFI;
 import com.oracle.truffle.r.runtime.ffi.NativeFunction;
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 7e34eee2870a947c70c3e7cc25559817341c6ad6..39b788f6c1c006a8b68071c47504b3f720a9cfb3 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
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -24,17 +24,16 @@ package com.oracle.truffle.r.ffi.impl.nfi;
 
 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.ForeignAccess;
 import com.oracle.truffle.api.interop.InteropException;
 import com.oracle.truffle.api.interop.Message;
 import com.oracle.truffle.api.interop.TruffleObject;
 import com.oracle.truffle.api.nodes.Node;
-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.ffi.CRFFI;
-import com.oracle.truffle.r.runtime.ffi.NativeCallInfo;
+import com.oracle.truffle.r.runtime.ffi.InvokeCNode;
+import com.oracle.truffle.r.runtime.ffi.InvokeCNode.FunctionObjectGetter;
+import com.oracle.truffle.r.runtime.ffi.InvokeCNodeGen;
 
 public class TruffleNFI_C implements CRFFI {
 
@@ -58,51 +57,25 @@ public class TruffleNFI_C implements CRFFI {
         }
     }
 
-    abstract static class TruffleNFI_InvokeCNode extends InvokeCNode {
+    static final class NFIFunctionObjectGetter extends FunctionObjectGetter {
 
         @Child private Node bindNode = Message.createInvoke(1).createNode();
 
+        @Override
         @TruffleBoundary
-        protected TruffleObject getFunction(TruffleObject address, int arity) {
+        public TruffleObject execute(TruffleObject address, int arity) {
             // cache signatures
             try {
-                return (TruffleObject) ForeignAccess.sendInvoke(bindNode, address, "bind", getSignatureForArity(arity));
+                return (TruffleObject) ForeignAccess.sendInvoke(bindNode, address, "bind",
+                                getSignatureForArity(arity));
             } catch (InteropException ex) {
                 throw RInternalError.shouldNotReachHere(ex);
             }
         }
-
-        @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) {
-            try {
-                ForeignAccess.sendExecute(executeNode, cachedFunction, args);
-            } catch (InteropException ex) {
-                throw RInternalError.shouldNotReachHere(ex);
-            }
-        }
-
-        @Specialization(limit = "99", guards = "args.length == cachedArgsLength")
-        protected void invokeCallCachedLength(NativeCallInfo nativeCallInfo, Object[] args,
-                        @Cached("args.length") int cachedArgsLength,
-                        @Cached("createExecute(cachedArgsLength)") Node executeNode) {
-            try {
-                ForeignAccess.sendExecute(executeNode, getFunction(nativeCallInfo.address.asTruffleObject(), cachedArgsLength), args);
-            } catch (InteropException ex) {
-                throw RInternalError.shouldNotReachHere(ex);
-            }
-        }
-
-        public static Node createExecute(int n) {
-            return Message.createExecute(n).createNode();
-        }
     }
 
     @Override
     public InvokeCNode createInvokeCNode() {
-        return TruffleNFI_InvokeCNodeGen.create();
+        return InvokeCNodeGen.create(new NFIFunctionObjectGetter());
     }
 }
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 cc83d8dbe5ce473224ba182e64a02bc501a4d793..5dfa769de92240a73b209a8f19fcbf87b315a4da 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
@@ -5,7 +5,7 @@
  *
  * Copyright (c) 1995-2012, The R Core Team
  * Copyright (c) 2003, The R Foundation
- * Copyright (c) 2015, 2017, Oracle and/or its affiliates
+ * Copyright (c) 2015, 2018, Oracle and/or its affiliates
  *
  * All rights reserved.
  */
@@ -39,6 +39,7 @@ import com.oracle.truffle.r.runtime.data.RMissing;
 import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
 import com.oracle.truffle.r.runtime.ffi.CRFFI;
 import com.oracle.truffle.r.runtime.ffi.DLL;
+import com.oracle.truffle.r.runtime.ffi.InvokeCNode;
 import com.oracle.truffle.r.runtime.ffi.NativeCallInfo;
 import com.oracle.truffle.r.runtime.ffi.RFFIFactory;
 import com.oracle.truffle.r.runtime.nodes.RBaseNode;
@@ -55,7 +56,7 @@ public class FortranAndCFunctions {
 
     protected abstract static class CRFFIAdapter extends RBuiltinNode.Arg6 {
 
-        @Child protected CRFFI.InvokeCNode invokeCNode = RFFIFactory.getCRFFI().createInvokeCNode();
+        @Child protected InvokeCNode invokeCNode = RFFIFactory.getCRFFI().createInvokeCNode();
 
         @Override
         public Object[] getDefaultParameterValues() {
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/NativeDataAccess.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/NativeDataAccess.java
index 5a74d7b08bfc35032e2fdee88ce6251002b515a6..eb50ba3007e580d356578053bac324f78668b6aa 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/NativeDataAccess.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/NativeDataAccess.java
@@ -166,37 +166,6 @@ public final class NativeDataAccess {
             assert this.length == 0 || dataAddress != EMPTY_DATA_ADDRESS;
         }
 
-        @TruffleBoundary
-        void allocateNativeStringArray(String[] data) {
-            assert dataAddress == 0;
-            // We allocate contiguous memory that we'll use to store both the array of pointers
-            // (char**)
-            // and the arrays of characters (char*). Given vector of size N, we allocate memory for
-            // N
-            // addresses (long) and after those we put individual strings character by character,
-            // the
-            // pointers from the first segment of this memory will be pointing to the starts of
-            // those
-            // strings.
-            length = data.length;
-            int size = data.length * Long.BYTES;
-            byte[][] bytes = new byte[data.length][];
-            for (int i = 0; i < length; i++) {
-                String element = data[i];
-                bytes[i] = element.getBytes(StandardCharsets.US_ASCII);
-                size += bytes[i].length + 1;
-            }
-            dataAddress = UnsafeAdapter.UNSAFE.allocateMemory(size);
-            long ptr = dataAddress + length * Long.BYTES; // start of the actual character data
-            for (int i = 0; i < length; i++) {
-                UnsafeAdapter.UNSAFE.putLong(dataAddress + 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 == dataAddress + size : "should have filled everything";
-        }
-
         // TODO: turn this into reference queues
         @Override
         protected void finalize() throws Throwable {
@@ -443,29 +412,6 @@ public final class NativeDataAccess {
         return data;
     }
 
-    @TruffleBoundary
-    public static String[] copyStringNativeData(Object mirrorObj) {
-        NativeMirror mirror = (NativeMirror) mirrorObj;
-        long address = mirror.dataAddress;
-        assert address != 0;
-        String[] data = new String[(int) mirror.length];
-        for (int i = 0; i < data.length; i++) {
-            long ptr = UnsafeAdapter.UNSAFE.getLong(address + i * 8);
-            data[i] = readNativeString(ptr);
-        }
-        return data;
-    }
-
-    @TruffleBoundary
-    public static String readNativeString(long addr) {
-        int len;
-        for (len = 0; UnsafeAdapter.UNSAFE.getByte(addr + len) != 0; len++) {
-        }
-        byte[] bytes = new byte[len];
-        UnsafeAdapter.UNSAFE.copyMemory(null, addr, bytes, Unsafe.ARRAY_BYTE_BASE_OFFSET, len);
-        return new String(bytes, StandardCharsets.US_ASCII);
-    }
-
     // methods operating on vectors that may have a native mirror assigned:
 
     private static final Assumption noIntNative = Truffle.getRuntime().createAssumption();
@@ -474,7 +420,6 @@ public final class NativeDataAccess {
     private static final Assumption noComplexNative = Truffle.getRuntime().createAssumption();
     private static final Assumption noRawNative = Truffle.getRuntime().createAssumption();
     private static final Assumption noCharSXPNative = Truffle.getRuntime().createAssumption();
-    private static final Assumption noStringArrayNative = Truffle.getRuntime().createAssumption();
 
     static int getData(RIntVector vector, int[] data, int index) {
         if (noIntNative.isValid() || data != null) {
@@ -672,25 +617,6 @@ public final class NativeDataAccess {
         }
     }
 
-    static String[] copyBackFromNative(RStringVector vector, String[] data) {
-        if (noStringArrayNative.isValid()) {
-            return data;
-        } else {
-            NativeMirror mirror = (NativeMirror) vector.getNativeMirror();
-            assert mirror != null;
-            if (mirror.dataAddress == 0) {
-                return data;
-            } else {
-                try {
-                    return copyStringNativeData(mirror);
-                } finally {
-                    // Forget the mirror
-                    vector.setNativeMirror(null);
-                }
-            }
-        }
-    }
-
     static long allocateNativeContents(RLogicalVector vector, byte[] data, int length) {
         NativeMirror mirror = (NativeMirror) vector.getNativeMirror();
         assert mirror != null;
@@ -761,15 +687,56 @@ public final class NativeDataAccess {
         return mirror.dataAddress;
     }
 
-    static long allocateNativeContents(RStringVector vector, String[] data) {
-        NativeMirror mirror = (NativeMirror) vector.getNativeMirror();
-        assert mirror != null;
-        assert mirror.dataAddress == 0 ^ data == null;
-        if (mirror.dataAddress == 0) {
-            noStringArrayNative.invalidate();
-            mirror.allocateNativeStringArray(data);
+    @TruffleBoundary
+    public static long allocateNativeStringArray(String[] data) {
+        // We allocate contiguous memory that we'll use to store both the array of pointers (char**)
+        // and the arrays of characters (char*). Given vector of size N, we allocate memory for N
+        // addresses (long) and after those we put individual strings character by character, the
+        // pointers from the first segment of this memory will be pointing to the starts of those
+        // strings.
+        int length = data.length;
+        int size = data.length * Long.BYTES;
+        byte[][] bytes = new byte[data.length][];
+        for (int i = 0; i < length; i++) {
+            String element = data[i];
+            bytes[i] = element.getBytes(StandardCharsets.US_ASCII);
+            size += bytes[i].length + 1;
+        }
+        long dataAddress = UnsafeAdapter.UNSAFE.allocateMemory(size);
+        long ptr = dataAddress + length * Long.BYTES; // start of the actual character data
+        for (int i = 0; i < length; i++) {
+            UnsafeAdapter.UNSAFE.putLong(dataAddress + 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 == dataAddress + size : "should have filled everything";
+        return dataAddress;
+    }
+
+    @TruffleBoundary
+    public static String[] releaseNativeStringArray(long address, int length) {
+        assert address != 0;
+        try {
+            String[] data = new String[length];
+            for (int i = 0; i < length; i++) {
+                long ptr = UnsafeAdapter.UNSAFE.getLong(address + i * 8);
+                data[i] = readNativeString(ptr);
+            }
+            return data;
+        } finally {
+            UnsafeAdapter.UNSAFE.freeMemory(address);
         }
-        return mirror.dataAddress;
+    }
+
+    @TruffleBoundary
+    public static String readNativeString(long addr) {
+        int len;
+        for (len = 0; UnsafeAdapter.UNSAFE.getByte(addr + len) != 0; len++) {
+        }
+        byte[] bytes = new byte[len];
+        UnsafeAdapter.UNSAFE.copyMemory(null, addr, bytes, Unsafe.ARRAY_BYTE_BASE_OFFSET, len);
+        return new String(bytes, StandardCharsets.US_ASCII);
     }
 
     public static void setNativeContents(RObject obj, long address, int length) {
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RStringVector.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RStringVector.java
index 9b8bf51ee84e483363792e93862c6bc3737b54a5..33b3cc8e805447893d58e00a99c5777910ae6936 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RStringVector.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RStringVector.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -40,11 +40,10 @@ import com.oracle.truffle.r.runtime.ops.na.NACheck;
 
 public final class RStringVector extends RVector<String[]> implements RAbstractStringVector {
 
-    private String[] data;
+    private final String[] data;
 
     RStringVector(String[] data, boolean complete) {
         super(complete);
-        assert data != null;
         this.data = data;
         assert RAbstractVector.verify(this);
     }
@@ -99,18 +98,6 @@ public final class RStringVector extends RVector<String[]> implements RAbstractS
         return data.length;
     }
 
-    public long allocateNativeContents() {
-        return NativeDataAccess.allocateNativeContents(this, data);
-    }
-
-    public RStringVector copyBackFromNative() {
-        String[] contents = NativeDataAccess.copyBackFromNative(this, data);
-        if (contents != data) {
-            data = contents;
-        }
-        return this;
-    }
-
     @Override
     public String[] getDataCopy() {
         String[] copy = new String[data.length];
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/StringArrayWrapper.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/StringArrayWrapper.java
new file mode 100644
index 0000000000000000000000000000000000000000..a03082bf0b8f7d5bd392a2376d313ce2f3db62a0
--- /dev/null
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/StringArrayWrapper.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.r.runtime.data;
+
+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;
+
+@MessageResolution(receiverType = StringArrayWrapper.class)
+final class StringArrayWrapperMR {
+
+    @Resolve(message = "IS_POINTER")
+    public abstract static class StringArrayWrapperIsPointerNode extends Node {
+        protected Object access(@SuppressWarnings("unused") StringArrayWrapper receiver) {
+            return true;
+        }
+    }
+
+    @Resolve(message = "AS_POINTER")
+    public abstract static class StringArrayWrapperAsPointerNode extends Node {
+        private final ConditionProfile profile = ConditionProfile.createBinaryProfile();
+
+        protected Object access(StringArrayWrapper receiver) {
+            long address = receiver.address;
+            if (profile.profile(address == 0)) {
+                return receiver.asPointer();
+            }
+            return address;
+        }
+    }
+
+    @CanResolve
+    public abstract static class StringArrayWrapperCheck extends Node {
+        protected static boolean test(TruffleObject receiver) {
+            return receiver instanceof StringArrayWrapper;
+        }
+    }
+}
+
+public final class StringArrayWrapper implements TruffleObject {
+
+    long address;
+    private final RStringVector vector;
+
+    public StringArrayWrapper(RStringVector vector) {
+        this.vector = vector;
+    }
+
+    @Override
+    public ForeignAccess getForeignAccess() {
+        return StringArrayWrapperMRForeign.ACCESS;
+    }
+
+    public long asPointer() {
+        address = NativeDataAccess.allocateNativeStringArray(vector.getInternalManagedData());
+        return address;
+    }
+
+    public RStringVector copyBackFromNative() {
+        if (address == 0) {
+            return vector;
+        } else {
+            String[] contents = NativeDataAccess.releaseNativeStringArray(address, vector.getLength());
+            address = 0;
+            RStringVector copy = new RStringVector(contents, false);
+            copy.copyAttributesFrom(vector);
+            return copy;
+        }
+    }
+
+}
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 52770c4a7e9a94558b08e79aa84a092f9f519d6d..1bff2fb19460c0050f65e8ba61cbf4c0b90328ae 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,19 +22,6 @@
  */
 package com.oracle.truffle.r.runtime.ffi;
 
-import com.oracle.truffle.r.runtime.ArgumentsSignature;
-import com.oracle.truffle.r.runtime.RRuntime;
-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.RList;
-import com.oracle.truffle.r.runtime.data.RStringVector;
-import com.oracle.truffle.r.runtime.ffi.UnwrapVectorNode.UnwrapVectorsNode;
-import com.oracle.truffle.r.runtime.ffi.UnwrapVectorNodeGen.UnwrapVectorsNodeGen;
-import com.oracle.truffle.r.runtime.ffi.WrapVectorNode.WrapVectorsNode;
-import com.oracle.truffle.r.runtime.ffi.WrapVectorNodeGen.WrapVectorsNodeGen;
-import com.oracle.truffle.r.runtime.nodes.RBaseNode;
-
 /**
  * Support for the {.C} and {.Fortran} calls. Arguments of these calls are only arrays of primitive
  * types, in the case character vectors, only the first string. The vectors coming from the R side
@@ -49,48 +36,5 @@ import com.oracle.truffle.r.runtime.nodes.RBaseNode;
  */
 public interface CRFFI {
 
-    abstract class InvokeCNode extends RBaseNode {
-
-        @Child WrapVectorsNode argsWrapperNode = WrapVectorsNodeGen.create();
-        @Child UnwrapVectorsNode argsUnwrapperNode = UnwrapVectorsNodeGen.create();
-
-        /**
-         * 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);
-
-        public final RList dispatch(NativeCallInfo nativeCallInfo, byte naok, byte dup, RArgsValuesAndNames args) {
-            @SuppressWarnings("unused")
-            boolean dupArgs = RRuntime.fromLogical(dup);
-            @SuppressWarnings("unused")
-            boolean checkNA = RRuntime.fromLogical(naok);
-
-            VectorRFFIWrapper[] preparedArgs = argsWrapperNode.execute(args.getArguments());
-
-            RFFIContext stateRFFI = RContext.getInstance().getStateRFFI();
-            long before = stateRFFI.beforeDowncall();
-            try {
-                execute(nativeCallInfo, preparedArgs);
-                return RDataFactory.createList(argsUnwrapperNode.execute(preparedArgs), validateArgNames(preparedArgs.length, args.getSignature()));
-            } finally {
-                stateRFFI.afterDowncall(before);
-            }
-        }
-
-        private static RStringVector validateArgNames(int argsLength, ArgumentsSignature signature) {
-            String[] listArgNames = new String[argsLength];
-            for (int i = 0; i < argsLength; i++) {
-                String name = signature.getName(i);
-                if (name == null) {
-                    name = RRuntime.NAMES_ATTR_EMPTY_VALUE;
-                }
-                listArgNames[i] = name;
-            }
-            return RDataFactory.createStringVector(listArgNames, RDataFactory.COMPLETE_VECTOR);
-        }
-    }
-
     InvokeCNode createInvokeCNode();
 }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/UnwrapVectorNode.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/CRFFIUnwrapVectorNode.java
similarity index 61%
rename from com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/UnwrapVectorNode.java
rename to com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/CRFFIUnwrapVectorNode.java
index 5f04b7287c5e6278e7dde9096d446add6123866b..362505f05f4c0790895631257317458df88a0b76 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/UnwrapVectorNode.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/CRFFIUnwrapVectorNode.java
@@ -23,51 +23,42 @@
 package com.oracle.truffle.r.runtime.ffi;
 
 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.interop.TruffleObject;
 import com.oracle.truffle.api.nodes.ExplodeLoop;
 import com.oracle.truffle.api.nodes.Node;
-import com.oracle.truffle.r.runtime.RInternalError;
-import com.oracle.truffle.r.runtime.data.RStringVector;
-import com.oracle.truffle.r.runtime.data.RVector;
-import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
+import com.oracle.truffle.r.runtime.data.StringArrayWrapper;
 
-public abstract class UnwrapVectorNode extends Node {
+public abstract class CRFFIUnwrapVectorNode extends Node {
 
-    public abstract Object execute(VectorRFFIWrapper arg);
+    public abstract Object execute(Object arg);
 
-    protected static boolean isRStringVector(VectorRFFIWrapper wrapper) {
-        return wrapper.getVector() instanceof RStringVector;
+    @Specialization
+    protected Object unwrapRStringVector(StringArrayWrapper wrapper) {
+        return wrapper.copyBackFromNative();
     }
 
-    @Specialization(guards = "isRStringVector(wrapper)")
-    protected Object unwrapRStringVector(VectorRFFIWrapper wrapper) {
-        return ((RStringVector) wrapper.getVector()).copyBackFromNative();
-    }
-
-    @Specialization(guards = "!isRStringVector(wrapper)")
+    @Specialization
     protected Object unwrapOthers(VectorRFFIWrapper wrapper) {
         return wrapper.getVector();
     }
 
-    public abstract static class UnwrapVectorsNode extends Node {
+    public abstract static class CRFFIUnwrapVectorsNode extends Node {
 
-        public abstract Object[] execute(VectorRFFIWrapper[] wrappers);
+        public abstract Object[] execute(Object[] wrappers);
 
-        protected UnwrapVectorNode[] createUnwrapNodes(int length) {
-            UnwrapVectorNode[] nodes = new UnwrapVectorNode[length];
+        protected CRFFIUnwrapVectorNode[] createUnwrapNodes(int length) {
+            CRFFIUnwrapVectorNode[] nodes = new CRFFIUnwrapVectorNode[length];
             for (int i = 0; i < nodes.length; i++) {
-                nodes[i] = UnwrapVectorNodeGen.create();
+                nodes[i] = CRFFIUnwrapVectorNodeGen.create();
             }
             return nodes;
         }
 
         @Specialization(limit = "99", guards = "wrappers.length == cachedLength")
         @ExplodeLoop
-        protected Object[] wrapArray(VectorRFFIWrapper[] wrappers,
+        protected Object[] wrapArray(Object[] wrappers,
                         @SuppressWarnings("unused") @Cached("wrappers.length") int cachedLength,
-                        @Cached("createUnwrapNodes(wrappers.length)") UnwrapVectorNode[] unwrapNodes) {
+                        @Cached("createUnwrapNodes(wrappers.length)") CRFFIUnwrapVectorNode[] unwrapNodes) {
             Object[] results = new Object[unwrapNodes.length];
             for (int i = 0; i < unwrapNodes.length; i++) {
                 results[i] = unwrapNodes[i].execute(wrappers[i]);
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/WrapVectorNode.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/CRFFIWrapVectorNode.java
similarity index 66%
rename from com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/WrapVectorNode.java
rename to com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/CRFFIWrapVectorNode.java
index 54a0320195330f8c748ffab4fe093ffeb32b7352..ac1c1e530dde4d27c676b6d3a3312a27239e3c06 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/WrapVectorNode.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/CRFFIWrapVectorNode.java
@@ -29,32 +29,43 @@ import com.oracle.truffle.api.interop.TruffleObject;
 import com.oracle.truffle.api.nodes.ExplodeLoop;
 import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.r.runtime.RInternalError;
+import com.oracle.truffle.r.runtime.data.RStringVector;
 import com.oracle.truffle.r.runtime.data.RVector;
+import com.oracle.truffle.r.runtime.data.StringArrayWrapper;
 import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
 
-public abstract class WrapVectorNode extends Node {
+public abstract class CRFFIWrapVectorNode extends Node {
 
     @Child FFIWrapNode wrapNode = FFIWrapNode.create();
 
-    public abstract VectorRFFIWrapper execute(Object vector);
+    public abstract Object execute(Object vector);
 
-    public final VectorRFFIWrapper dispatch(Object vector) {
+    public final Object dispatch(Object vector) {
         return execute(wrapNode.execute(vector));
     }
 
     protected boolean isTemporary(Object vector) {
         // if the vector is temporary, we can re-use it. We turn it into native memory backed
         // vector, keep it so and reuse it as the result.
-        return (vector instanceof RVector<?> && ((RVector<?>) vector).isTemporary());
+        return vector instanceof RVector<?> && ((RVector<?>) vector).isTemporary();
     }
 
-    @Specialization(guards = "isTemporary(vector)")
-    protected VectorRFFIWrapper temporaryToNative(TruffleObject vector) {
+    protected static boolean isStringVector(Object vector) {
+        return vector instanceof RStringVector;
+    }
+
+    @Specialization
+    protected Object temporaryToNative(RStringVector vector) {
+        return new StringArrayWrapper(vector);
+    }
+
+    @Specialization(guards = {"isTemporary(vector)", "!isStringVector(vector)"})
+    protected Object temporaryToNative(TruffleObject vector) {
         return VectorRFFIWrapper.get(vector);
     }
 
-    @Specialization(guards = "!isTemporary(vector)")
-    protected VectorRFFIWrapper nonTemporaryToNative(RAbstractVector vector) {
+    @Specialization(guards = {"!isTemporary(vector)", "!isStringVector(vector)"})
+    protected Object nonTemporaryToNative(RAbstractVector vector) {
         return VectorRFFIWrapper.get(vector.copy());
     }
 
@@ -63,24 +74,24 @@ public abstract class WrapVectorNode extends Node {
         throw RInternalError.shouldNotReachHere("Unimplemented native conversion of argument");
     }
 
-    public abstract static class WrapVectorsNode extends Node {
+    public abstract static class CRFFIWrapVectorsNode extends Node {
 
-        public abstract VectorRFFIWrapper[] execute(Object[] vectors);
+        public abstract Object[] execute(Object[] vectors);
 
-        protected WrapVectorNode[] createWrapNodes(int length) {
-            WrapVectorNode[] nodes = new WrapVectorNode[length];
+        protected CRFFIWrapVectorNode[] createWrapNodes(int length) {
+            CRFFIWrapVectorNode[] nodes = new CRFFIWrapVectorNode[length];
             for (int i = 0; i < nodes.length; i++) {
-                nodes[i] = WrapVectorNodeGen.create();
+                nodes[i] = CRFFIWrapVectorNodeGen.create();
             }
             return nodes;
         }
 
         @Specialization(limit = "99", guards = "vectors.length == cachedLength")
         @ExplodeLoop
-        protected VectorRFFIWrapper[] wrapArray(Object[] vectors,
+        protected Object[] wrapArray(Object[] vectors,
                         @SuppressWarnings("unused") @Cached("vectors.length") int cachedLength,
-                        @Cached("createWrapNodes(vectors.length)") WrapVectorNode[] wrapNodes) {
-            VectorRFFIWrapper[] results = new VectorRFFIWrapper[wrapNodes.length];
+                        @Cached("createWrapNodes(vectors.length)") CRFFIWrapVectorNode[] wrapNodes) {
+            Object[] results = new Object[wrapNodes.length];
             for (int i = 0; i < wrapNodes.length; i++) {
                 results[i] = wrapNodes[i].dispatch(vectors[i]);
             }
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 6a9731b2cb88cb82b0dab1d1a01916c8167fabd2..95d5d02e8e52d7108a7ccc57ec03344c69df2c94 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
@@ -49,6 +49,7 @@ import com.oracle.truffle.r.runtime.data.RObject;
 import com.oracle.truffle.r.runtime.data.RStringVector;
 import com.oracle.truffle.r.runtime.data.RSymbol;
 import com.oracle.truffle.r.runtime.data.RTruffleObject;
+import com.oracle.truffle.r.runtime.data.StringArrayWrapper;
 import com.oracle.truffle.r.runtime.ffi.CallRFFI.InvokeVoidCallNode;
 import com.oracle.truffle.r.runtime.ffi.DLLRFFI.DLCloseRootNode;
 import com.oracle.truffle.r.runtime.nodes.RSyntaxNode;
@@ -315,8 +316,7 @@ public class DLL {
         @Override
         public long getCustomMirrorAddress() {
             RStringVector table = RDataFactory.createStringVector(new String[]{path, name}, true);
-            NativeDataAccess.asPointer(table);
-            return table.allocateNativeContents();
+            return new StringArrayWrapper(table).asPointer();
         }
     }
 
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/InvokeCNode.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/InvokeCNode.java
new file mode 100644
index 0000000000000000000000000000000000000000..ec9ab0c59a2e290383f40fad79013d17a67c5042
--- /dev/null
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/InvokeCNode.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.r.runtime.ffi;
+
+import com.oracle.truffle.api.dsl.Cached;
+import com.oracle.truffle.api.dsl.Specialization;
+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.TruffleObject;
+import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.r.runtime.ArgumentsSignature;
+import com.oracle.truffle.r.runtime.RInternalError;
+import com.oracle.truffle.r.runtime.RRuntime;
+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.RList;
+import com.oracle.truffle.r.runtime.data.RStringVector;
+import com.oracle.truffle.r.runtime.ffi.CRFFIUnwrapVectorNode.CRFFIUnwrapVectorsNode;
+import com.oracle.truffle.r.runtime.ffi.CRFFIUnwrapVectorNodeGen.CRFFIUnwrapVectorsNodeGen;
+import com.oracle.truffle.r.runtime.ffi.CRFFIWrapVectorNode.CRFFIWrapVectorsNode;
+import com.oracle.truffle.r.runtime.ffi.CRFFIWrapVectorNodeGen.CRFFIWrapVectorsNodeGen;
+import com.oracle.truffle.r.runtime.nodes.RBaseNode;
+
+public abstract class InvokeCNode extends RBaseNode {
+
+    @Child private CRFFIWrapVectorsNode argsWrapperNode = CRFFIWrapVectorsNodeGen.create();
+    @Child private CRFFIUnwrapVectorsNode argsUnwrapperNode = CRFFIUnwrapVectorsNodeGen.create();
+    @Child private FunctionObjectGetter functionGetterNode;
+
+    public InvokeCNode(FunctionObjectGetter functionGetterNode) {
+        this.functionGetterNode = functionGetterNode;
+    }
+
+    /**
+     * Invoke the native method identified by {@code symbolInfo} passing it the arguments in
+     * {@code args}. The values in {@code args} should support the IS_POINTER/AS_POINTER messages.
+     */
+    protected abstract void execute(NativeCallInfo nativeCallInfo, Object[] args);
+
+    public final RList dispatch(NativeCallInfo nativeCallInfo, byte naok, byte dup, RArgsValuesAndNames args) {
+        @SuppressWarnings("unused")
+        boolean dupArgs = RRuntime.fromLogical(dup);
+        @SuppressWarnings("unused")
+        boolean checkNA = RRuntime.fromLogical(naok);
+
+        Object[] preparedArgs = argsWrapperNode.execute(args.getArguments());
+
+        RFFIContext stateRFFI = RContext.getInstance().getStateRFFI();
+        long before = stateRFFI.beforeDowncall();
+        try {
+            execute(nativeCallInfo, preparedArgs);
+            return RDataFactory.createList(argsUnwrapperNode.execute(preparedArgs), validateArgNames(preparedArgs.length, args.getSignature()));
+        } finally {
+            stateRFFI.afterDowncall(before);
+        }
+    }
+
+    protected final TruffleObject getFunction(TruffleObject address, int arity) {
+        return functionGetterNode.execute(address, arity);
+    }
+
+    public static Node createExecute(int n) {
+        return Message.createExecute(n).createNode();
+    }
+
+    @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) {
+        try {
+            ForeignAccess.sendExecute(executeNode, cachedFunction, args);
+        } catch (InteropException ex) {
+            throw RInternalError.shouldNotReachHere(ex);
+        }
+    }
+
+    @Specialization(replaces = "invokeCallCached", limit = "99", guards = "args.length == cachedArgsLength")
+    protected void invokeCallCachedLength(NativeCallInfo nativeCallInfo, Object[] args,
+                    @Cached("args.length") int cachedArgsLength,
+                    @Cached("createExecute(cachedArgsLength)") Node executeNode) {
+        try {
+            ForeignAccess.sendExecute(executeNode, getFunction(nativeCallInfo.address.asTruffleObject(), cachedArgsLength), args);
+        } catch (InteropException ex) {
+            throw RInternalError.shouldNotReachHere(ex);
+        }
+    }
+
+    private static RStringVector validateArgNames(int argsLength, ArgumentsSignature signature) {
+        String[] listArgNames = new String[argsLength];
+        for (int i = 0; i < argsLength; i++) {
+            String name = signature.getName(i);
+            if (name == null) {
+                name = RRuntime.NAMES_ATTR_EMPTY_VALUE;
+            }
+            listArgNames[i] = name;
+        }
+        return RDataFactory.createStringVector(listArgNames, RDataFactory.COMPLETE_VECTOR);
+    }
+
+    public abstract static class FunctionObjectGetter extends Node {
+
+        public abstract TruffleObject execute(TruffleObject address, int arity);
+
+    }
+
+}
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/VectorRFFIWrapper.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/VectorRFFIWrapper.java
index 518474a9e68197834b0921f8a260071f04ac0cca..48efe8fbd709fe92a2f28e9d4b113604d44d64ae 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/VectorRFFIWrapper.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/VectorRFFIWrapper.java
@@ -52,7 +52,6 @@ import com.oracle.truffle.r.runtime.data.RLogicalVector;
 import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.RObject;
 import com.oracle.truffle.r.runtime.data.RRawVector;
-import com.oracle.truffle.r.runtime.data.RStringVector;
 import com.oracle.truffle.r.runtime.ffi.VectorRFFIWrapperFactory.VectorRFFIWrapperNativePointerFactory.DispatchAllocateNodeGen;
 import com.oracle.truffle.r.runtime.nodes.RSyntaxNode;
 
@@ -152,12 +151,6 @@ public final class VectorRFFIWrapper implements TruffleObject {
                 return vector.allocateNativeContents();
             }
 
-            @Specialization
-            @TruffleBoundary
-            protected static long get(RStringVector vector) {
-                return vector.allocateNativeContents();
-            }
-
             @Specialization
             @TruffleBoundary
             protected static long get(CharSXPWrapper vector) {