From f27c342cb3a1f140ff684668afb23046488ac9d2 Mon Sep 17 00:00:00 2001
From: Lukas Stadler <lukas.stadler@oracle.com>
Date: Fri, 28 Jul 2017 09:30:32 +0200
Subject: [PATCH] allow NFI/LLVM more freedom in .C call handling

---
 .../oracle/truffle/r/ffi/impl/jni/JNI_C.java  |   3 +-
 .../r/ffi/impl/llvm/TruffleLLVM_C.java        |   2 +-
 .../truffle/r/ffi/impl/nfi/TruffleNFI_C.java  |   2 +-
 .../base/foreign/FortranAndCFunctions.java    | 196 +---------------
 .../oracle/truffle/r/runtime/ffi/CRFFI.java   | 219 +++++++++++++++++-
 5 files changed, 224 insertions(+), 198 deletions(-)

diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_C.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_C.java
index 8b8e778a0e..ac1e480421 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_C.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_C.java
@@ -26,12 +26,11 @@ import static com.oracle.truffle.r.ffi.impl.common.RFFIUtils.traceDownCall;
 import static com.oracle.truffle.r.ffi.impl.common.RFFIUtils.traceEnabled;
 
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
-import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.r.runtime.ffi.CRFFI;
 import com.oracle.truffle.r.runtime.ffi.NativeCallInfo;
 
 public class JNI_C implements CRFFI {
-    private static class JNI_InvokeCNode extends Node implements InvokeCNode {
+    private static class JNI_InvokeCNode extends InvokeCNode {
         /**
          * This is rather similar to {@link JNI_Call}, except the objects are guaranteed to be
          * native array types, no upcalls are possible, and no result is returned. However, the
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 b6ee57ebda..ae1c62d397 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
@@ -34,7 +34,7 @@ import com.oracle.truffle.r.runtime.ffi.CRFFI;
 import com.oracle.truffle.r.runtime.ffi.NativeCallInfo;
 
 class TruffleLLVM_C implements CRFFI {
-    private static class TruffleLLVM_InvokeCNode extends Node implements InvokeCNode {
+    private static class TruffleLLVM_InvokeCNode extends InvokeCNode {
 
         @Child private Node messageNode;
         private int numArgs;
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 57f9b36dad..2913e0e1b7 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
@@ -37,7 +37,7 @@ import com.oracle.truffle.r.runtime.ffi.CRFFI;
 import com.oracle.truffle.r.runtime.ffi.NativeCallInfo;
 
 public class TruffleNFI_C implements CRFFI {
-    abstract static class TruffleNFI_InvokeCNode extends Node implements InvokeCNode {
+    abstract static class TruffleNFI_InvokeCNode extends InvokeCNode {
 
         @Child private Node bindNode = Message.createInvoke(1).createNode();
 
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 9fdfeb7214..6455ab0ee4 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
@@ -14,11 +14,8 @@ package com.oracle.truffle.r.nodes.builtin.base.foreign;
 import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX;
 import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
 
-import java.nio.charset.Charset;
-import java.nio.charset.StandardCharsets;
 import java.util.Arrays;
 
-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.Fallback;
@@ -40,11 +37,6 @@ import com.oracle.truffle.r.runtime.data.RAttributable;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RList;
 import com.oracle.truffle.r.runtime.data.RMissing;
-import com.oracle.truffle.r.runtime.data.RString;
-import com.oracle.truffle.r.runtime.data.RStringVector;
-import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector;
-import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
-import com.oracle.truffle.r.runtime.data.model.RAbstractLogicalVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
 import com.oracle.truffle.r.runtime.ffi.CRFFI;
 import com.oracle.truffle.r.runtime.ffi.DLL;
@@ -63,192 +55,14 @@ import com.oracle.truffle.r.runtime.nodes.RBaseNode;
 public class FortranAndCFunctions {
 
     protected abstract static class CRFFIAdapter extends RBuiltinNode.Arg6 {
-        private static final int SCALAR_DOUBLE = 0;
-        private static final int SCALAR_INT = 1;
-        private static final int SCALAR_LOGICAL = 2;
-        private static final int SCALAR_STRING = 3;
-        private static final int VECTOR_DOUBLE = 10;
-        private static final int VECTOR_INT = 11;
-        private static final int VECTOR_LOGICAL = 12;
-        private static final int VECTOR_STRING = 13;
-
-        private static final Charset charset = StandardCharsets.US_ASCII;
 
         @Child protected ExtractNativeCallInfoNode extractSymbolInfo = ExtractNativeCallInfoNodeGen.create();
-        @Child private CRFFI.InvokeCNode invokeCNode = RFFIFactory.getCRFFI().createInvokeCNode();
+        @Child protected CRFFI.InvokeCNode invokeCNode = RFFIFactory.getCRFFI().createInvokeCNode();
 
         @Override
         public Object[] getDefaultParameterValues() {
             return new Object[]{RMissing.instance, RArgsValuesAndNames.EMPTY, RRuntime.LOGICAL_FALSE, RRuntime.LOGICAL_FALSE, RMissing.instance, RMissing.instance};
         }
-
-        @TruffleBoundary
-        protected RList dispatch(RBuiltinNode node, NativeCallInfo nativeCallInfo, byte naok, byte dup, RArgsValuesAndNames args) {
-            @SuppressWarnings("unused")
-            boolean dupArgs = RRuntime.fromLogical(dup);
-            @SuppressWarnings("unused")
-            boolean checkNA = RRuntime.fromLogical(naok);
-            // Analyze the args, making copies (ignoring dup for now)
-            Object[] array = args.getArguments();
-            int[] argTypes = new int[array.length];
-            Object[] nativeArgs = new Object[array.length];
-            boolean hasStrings = false;
-            for (int i = 0; i < array.length; i++) {
-                Object arg = array[i];
-                if (arg instanceof RAbstractDoubleVector) {
-                    argTypes[i] = VECTOR_DOUBLE;
-                    nativeArgs[i] = checkNAs(node, i + 1, ((RAbstractDoubleVector) arg).materialize().getDataCopy());
-                } else if (arg instanceof RAbstractIntVector) {
-                    argTypes[i] = VECTOR_INT;
-                    nativeArgs[i] = checkNAs(node, i + 1, ((RAbstractIntVector) arg).materialize().getDataCopy());
-                } else if (arg instanceof RAbstractLogicalVector) {
-                    argTypes[i] = VECTOR_LOGICAL;
-                    // passed as int[]
-                    byte[] data = ((RAbstractLogicalVector) arg).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];
-                    }
-                    nativeArgs[i] = checkNAs(node, i + 1, dataAsInt);
-                } else if (arg instanceof RAbstractStringVector) {
-                    hasStrings = true;
-                    argTypes[i] = VECTOR_STRING;
-                    checkNAs(node, i + 1, (RAbstractStringVector) arg);
-                    nativeArgs[i] = encodeStrings((RAbstractStringVector) arg);
-                } else if (arg instanceof String) {
-                    hasStrings = true;
-                    argTypes[i] = SCALAR_STRING;
-                    checkNAs(node, i + 1, RString.valueOf((String) arg));
-                    nativeArgs[i] = new byte[][]{encodeString((String) arg)};
-                } else if (arg instanceof Double) {
-                    argTypes[i] = SCALAR_DOUBLE;
-                    nativeArgs[i] = checkNAs(node, i + 1, new double[]{(double) arg});
-                } else if (arg instanceof Integer) {
-                    argTypes[i] = SCALAR_INT;
-                    nativeArgs[i] = checkNAs(node, i + 1, new int[]{(int) arg});
-                } else if (arg instanceof Byte) {
-                    argTypes[i] = SCALAR_LOGICAL;
-                    nativeArgs[i] = checkNAs(node, i + 1, new int[]{RRuntime.isNA((byte) arg) ? RRuntime.INT_NA : (byte) arg});
-                } else {
-                    throw node.error(RError.Message.UNIMPLEMENTED_ARG_TYPE, i + 1);
-                }
-            }
-            invokeCNode.execute(nativeCallInfo, nativeArgs, hasStrings);
-            // we have to assume that the native method updated everything
-            RStringVector listNames = validateArgNames(array.length, args.getSignature());
-            Object[] results = new Object[array.length];
-            for (int i = 0; i < array.length; i++) {
-                switch (argTypes[i]) {
-                    case SCALAR_DOUBLE:
-                        results[i] = RDataFactory.createDoubleVector((double[]) nativeArgs[i], RDataFactory.COMPLETE_VECTOR);
-                        break;
-                    case SCALAR_INT:
-                        results[i] = RDataFactory.createIntVector((int[]) nativeArgs[i], RDataFactory.COMPLETE_VECTOR);
-                        break;
-                    case SCALAR_LOGICAL:
-                        // have to convert back from int[]
-                        int[] nativeIntArgs = (int[]) nativeArgs[i];
-                        byte[] nativeByteArgs = new byte[nativeIntArgs.length];
-                        for (int j = 0; j < nativeByteArgs.length; j++) {
-                            int nativeInt = nativeIntArgs[j];
-                            nativeByteArgs[j] = (byte) (nativeInt == RRuntime.INT_NA ? RRuntime.LOGICAL_NA : nativeInt & 0xFF);
-                        }
-                        results[i] = RDataFactory.createLogicalVector(nativeByteArgs, RDataFactory.COMPLETE_VECTOR);
-                        break;
-                    case SCALAR_STRING:
-                        results[i] = RDataFactory.createStringVector(decodeStrings((byte[][]) nativeArgs[i]), RDataFactory.COMPLETE_VECTOR);
-                        break;
-                    case VECTOR_STRING:
-                        results[i] = ((RAbstractStringVector) array[i]).materialize().copyResetData(decodeStrings((byte[][]) nativeArgs[i]));
-                        break;
-                    case VECTOR_DOUBLE:
-                        results[i] = ((RAbstractDoubleVector) array[i]).materialize().copyResetData((double[]) nativeArgs[i]);
-                        break;
-                    case VECTOR_INT:
-                        results[i] = ((RAbstractIntVector) array[i]).materialize().copyResetData((int[]) nativeArgs[i]);
-                        break;
-                    case VECTOR_LOGICAL: {
-                        int[] intData = (int[]) nativeArgs[i];
-                        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);
-                        }
-                        results[i] = ((RAbstractLogicalVector) array[i]).materialize().copyResetData(byteData);
-                        break;
-                    }
-                }
-            }
-            return RDataFactory.createList(results, listNames);
-        }
-
-        private static int[] checkNAs(RBuiltinNode node, int argIndex, int[] data) {
-            CompilerAsserts.neverPartOfCompilation();
-            for (int i = 0; i < data.length; i++) {
-                if (RRuntime.isNA(data[i])) {
-                    throw node.error(RError.Message.NA_IN_FOREIGN_FUNCTION_CALL, argIndex);
-                }
-            }
-            return data;
-        }
-
-        private static void checkNAs(RBuiltinNode node, int argIndex, RAbstractStringVector data) {
-            CompilerAsserts.neverPartOfCompilation();
-            for (int i = 0; i < data.getLength(); i++) {
-                if (RRuntime.isNA(data.getDataAt(i))) {
-                    throw node.error(RError.Message.NA_IN_FOREIGN_FUNCTION_CALL, argIndex);
-                }
-            }
-        }
-
-        private static double[] checkNAs(RBuiltinNode node, int argIndex, double[] data) {
-            CompilerAsserts.neverPartOfCompilation();
-            for (int i = 0; i < data.length; i++) {
-                if (!RRuntime.isFinite(data[i])) {
-                    throw node.error(RError.Message.NA_NAN_INF_IN_FOREIGN_FUNCTION_CALL, argIndex);
-                }
-            }
-            return data;
-        }
-
-        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);
-        }
-
-        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;
-        }
     }
 
     /**
@@ -297,7 +111,7 @@ public class FortranAndCFunctions {
         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);
-            return dispatch(this, nativeCallInfo, naok, dup, args);
+            return invokeCNode.dispatch(nativeCallInfo, naok, dup, args);
         }
 
         @Specialization
@@ -309,7 +123,7 @@ public class FortranAndCFunctions {
             if (func == DLL.SYMBOL_NOT_FOUND) {
                 throw error(RError.Message.C_SYMBOL_NOT_IN_TABLE, symbol);
             }
-            return dispatch(this, new NativeCallInfo(symbol.getDataAt(0), func, rns.getDllInfo()), naok, dup, args);
+            return invokeCNode.dispatch(new NativeCallInfo(symbol.getDataAt(0), func, rns.getDllInfo()), naok, dup, args);
         }
 
         @SuppressWarnings("unused")
@@ -367,7 +181,7 @@ 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);
-            return dispatch(this, nativeCallInfo, naok, dup, args);
+            return invokeCNode.dispatch(nativeCallInfo, naok, dup, args);
         }
 
         @Specialization
@@ -385,7 +199,7 @@ public class FortranAndCFunctions {
             if (func == DLL.SYMBOL_NOT_FOUND) {
                 throw error(RError.Message.C_SYMBOL_NOT_IN_TABLE, symbol);
             }
-            return dispatch(this, new NativeCallInfo(symbol.getDataAt(0), func, rns.getDllInfo()), naok, dup, args);
+            return invokeCNode.dispatch(new NativeCallInfo(symbol.getDataAt(0), func, rns.getDllInfo()), naok, dup, args);
         }
     }
 }
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 070fb25fb0..5143ea58df 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,13 +22,43 @@
  */
 package com.oracle.truffle.r.runtime.ffi;
 
-import com.oracle.truffle.api.nodes.NodeInterface;
+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.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.RDataFactory;
+import com.oracle.truffle.r.runtime.data.RList;
+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.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.RAbstractStringVector;
+import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
+import com.oracle.truffle.r.runtime.nodes.RBaseNode;
 
 /**
  * Support for the {.C} and {.Fortran} calls.
  */
 public interface CRFFI {
-    interface InvokeCNode extends NodeInterface {
+
+    public static abstract class InvokeCNode extends RBaseNode {
+
+        enum ArgumentType {
+            VECTOR_DOUBLE,
+            VECTOR_INT,
+            VECTOR_LOGICAL,
+            VECTOR_STRING;
+        }
+
+        private static final Charset charset = StandardCharsets.US_ASCII;
+
         /**
          * 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[]}
@@ -38,7 +68,190 @@ public interface CRFFI {
          *            values of type {@code byte[][]}, which represent arrays of strings in ASCII
          *            encoding.
          */
-        void execute(NativeCallInfo nativeCallInfo, Object[] args, boolean hasStrings);
+        protected abstract void execute(NativeCallInfo nativeCallInfo, Object[] args, boolean hasStrings);
+
+        @TruffleBoundary
+        private 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[] 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;
+        }
+
+        @TruffleBoundary
+        private static 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[] 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]);
+            }
+            return results;
+        }
+
+        @TruffleBoundary
+        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);
+            // 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;
+            }
+            Object[] nativeArgs = getNativeArguments(array, argTypes);
+            execute(nativeCallInfo, nativeArgs, hasStrings);
+            // 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);
+        }
+
+        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);
+        }
+
+        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();
-- 
GitLab