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 8b8e778a0eb4ae4ca2fdf599791cf15688f5d483..ac1e48042118e8b8ebae876e26126aec199f2ff6 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 b6ee57ebda72247b1c49aa79ea353eadfa6abea9..ae1c62d397faac2f1a85ffa2a38dd958fca95e7a 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 57f9b36dad911f2e1f2f9f03980b7bc0dc2118e3..2913e0e1b73b81aba0f4158ae1c9219b2722b0c2 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 9fdfeb7214524061323e7a79b66264f6bdb34de4..6455ab0ee486f962cf03849c96a4620e7fa0e49f 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 070fb25fb0d4ad831bc43084f25de50b58cef01c..5143ea58dfd62c669adbe8b89490e3f372609ccf 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();