From 15c97abd390fef9b854b6de61ecb51ec5e872321 Mon Sep 17 00:00:00 2001 From: Mick Jordan <mick.jordan@oracle.com> Date: Tue, 9 Jun 2015 11:55:37 -0700 Subject: [PATCH] FFI improvements; call parseRd through ToolsRFFI --- .../truffle/r/library/tools/C_ParseRd.java | 17 +-- .../fficall/jni/Makefile | 6 +- .../fficall/jni/src/externalptr.c | 6 +- .../fficall/jni/src/listaccess.c | 10 +- .../fficall/jni/src/misc.c | 4 + .../fficall/jni/src/rf_functions.c | 33 +++--- .../fficall/jni/src/rfficall.c | 67 +++++++---- .../fficall/jni/src/rffiutils.c | 105 +++++++++++++++++- .../fficall/jni/src/rffiutils.h | 36 +++++- .../fficall/jni/src/typecoerce.c | 13 ++- .../fficall/jni/src/vectoraccess.c | 24 ++-- .../library/tools/src/gramRd.c | 64 ++++++----- .../library/tools/src/tools.h | 3 +- .../r/runtime/ffi/jnr/CallRFFIHelper.java | 23 +++- .../r/runtime/ffi/jnr/CallRFFIWithJNI.java | 58 +++++++--- .../r/runtime/ffi/jnr/JNR_RFFIFactory.java | 62 ++++++++++- .../oracle/truffle/r/runtime/RContext.java | 4 +- .../oracle/truffle/r/runtime/ffi/RFFI.java | 3 + .../runtime/ffi/RFFIContextStateFactory.java | 52 +++++++++ .../truffle/r/runtime/ffi/RFFIFactory.java | 8 +- .../truffle/r/runtime/ffi/StatsRFFI.java | 2 +- .../truffle/r/runtime/ffi/ToolsRFFI.java | 34 +++--- .../packages/testrffi/testrffi/NAMESPACE | 7 +- .../packages/testrffi/testrffi/R/testrffi.R | 20 ++-- .../packages/testrffi/testrffi/src/testrffi.c | 17 ++- .../truffle/r/test/ExpectedTestOutput.test | 4 +- .../r/test/rpackages/TestRPackages.java | 2 +- 27 files changed, 513 insertions(+), 171 deletions(-) create mode 100644 com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/RFFIContextStateFactory.java rename com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/tools/ToolsNative.java => com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/ToolsRFFI.java (55%) diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/tools/C_ParseRd.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/tools/C_ParseRd.java index dc460817b1..0d6b1b37eb 100644 --- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/tools/C_ParseRd.java +++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/tools/C_ParseRd.java @@ -28,24 +28,27 @@ import com.oracle.truffle.api.dsl.*; import com.oracle.truffle.r.nodes.builtin.*; import com.oracle.truffle.r.runtime.*; import com.oracle.truffle.r.runtime.conn.*; +import com.oracle.truffle.r.runtime.data.*; import com.oracle.truffle.r.runtime.data.model.*; import com.oracle.truffle.r.runtime.env.*; +import com.oracle.truffle.r.runtime.ffi.*; public abstract class C_ParseRd extends RExternalBuiltinNode.Arg7 { - @SuppressWarnings("unused") @Specialization - protected Object parseRd(RConnection con, REnvironment srcfile, String encoding, byte verboseL, RAbstractStringVector basename, byte fragmentL, byte warningCallsL) { - boolean verbose = RRuntime.fromLogical(verboseL); - boolean fragment = RRuntime.fromLogical(fragmentL); + protected Object parseRd(RConnection con, REnvironment srcfile, @SuppressWarnings("unused") String encoding, byte verboseL, RAbstractStringVector basename, byte fragmentL, byte warningCallsL) { if (RRuntime.isNA(warningCallsL)) { throw RError.error(getEncapsulatingSourceSection(), RError.Message.INVALID_ARGUMENT, "warningCalls"); } - boolean warningCalls = RRuntime.fromLogical(warningCallsL); try (RConnection openConn = con.forceOpen("r")) { - Object result = ToolsNative.provider().cParseRd(con, srcfile, verbose, fragment, basename.getDataAt(0), warningCalls); - return result; + // @formatter:off + return RFFIFactory.getRFFI().getToolsRFFI().parseRd(openConn, srcfile, + RDataFactory.createLogicalVectorFromScalar(verboseL), + RDataFactory.createLogicalVectorFromScalar(fragmentL), + RDataFactory.createStringVectorFromScalar(basename.getDataAt(0)), + RDataFactory.createLogicalVectorFromScalar(warningCallsL)); + // @formatter:on } catch (IOException ex) { throw RError.error(getEncapsulatingSourceSection(), RError.Message.GENERIC, ex.getMessage()); } catch (Throwable ex) { diff --git a/com.oracle.truffle.r.native/fficall/jni/Makefile b/com.oracle.truffle.r.native/fficall/jni/Makefile index a01d09c277..4b86d03522 100644 --- a/com.oracle.truffle.r.native/fficall/jni/Makefile +++ b/com.oracle.truffle.r.native/fficall/jni/Makefile @@ -34,6 +34,7 @@ endif OBJ = lib SRC = src C_SOURCES := $(wildcard $(SRC)/*.c) +C_HDRS := $(wildcard $(SRC)/*.h) C_LIBNAME := librfficall$(DYLIB_EXT) C_OBJECTS := $(subst $(SRC),$(OBJ),$(C_SOURCES:.c=.o)) C_LIB := $(TOPDIR)/builtinlibs/$(OBJ)/$(C_LIBNAME) @@ -57,8 +58,11 @@ $(C_LIB): $(OBJ) $(C_OBJECTS) $(OBJ): mkdir -p $(OBJ) -$(OBJ)/%.o: $(SRC)/%.c $(TOPDIR)/include/jni/include/Rinternals.h +$(OBJ)/%.o: $(SRC)/%.c $(TOPDIR)/include/jni/include/Rinternals.h $(C_HDRS) $(CC) $(CFLAGS) $(INCLUDES) -c $< -o $@ + +$(OBJ)/%.E: $(SRC)/%.c $(TOPDIR)/include/jni/include/Rinternals.h + $(CC) -E $(CFLAGS) $(INCLUDES) -c $< > $@ clean: rm -rf $(OBJ) $(C_LIB) diff --git a/com.oracle.truffle.r.native/fficall/jni/src/externalptr.c b/com.oracle.truffle.r.native/fficall/jni/src/externalptr.c index 79943a7a6f..8ab12aa582 100644 --- a/com.oracle.truffle.r.native/fficall/jni/src/externalptr.c +++ b/com.oracle.truffle.r.native/fficall/jni/src/externalptr.c @@ -46,7 +46,7 @@ void init_externalptr(JNIEnv *env) { SEXP R_MakeExternalPtr(void *p, SEXP tag, SEXP prot) { JNIEnv *thisenv = getEnv(); SEXP result = (*thisenv)->CallStaticObjectMethod(thisenv, RDataFactoryClass, createExternalPtrMethodID, (jlong) p, tag, prot); - return mkGlobalRef(thisenv, result); + return checkRef(thisenv, result); } void *R_ExternalPtrAddr(SEXP s) { @@ -57,13 +57,13 @@ void *R_ExternalPtrAddr(SEXP s) { SEXP R_ExternalPtrTag(SEXP s) { JNIEnv *thisenv = getEnv(); SEXP result = (*thisenv)->CallObjectMethod(thisenv, s, externalPtrGetTagMethodID); - return mkGlobalRef(thisenv, result); + return checkRef(thisenv, result); } SEXP R_ExternalPtrProt(SEXP s) { JNIEnv *thisenv = getEnv(); SEXP result = (*thisenv)->CallObjectMethod(thisenv, s, externalPtrGetProtMethodID); - return mkGlobalRef(thisenv, result); + return checkRef(thisenv, result); } void R_SetExternalPtrAddr(SEXP s, void *p) { diff --git a/com.oracle.truffle.r.native/fficall/jni/src/listaccess.c b/com.oracle.truffle.r.native/fficall/jni/src/listaccess.c index e185d60f9e..89d264e651 100644 --- a/com.oracle.truffle.r.native/fficall/jni/src/listaccess.c +++ b/com.oracle.truffle.r.native/fficall/jni/src/listaccess.c @@ -44,13 +44,13 @@ SEXP TAG(SEXP e) { SEXP CAR(SEXP e) { JNIEnv *thisenv = getEnv(); SEXP result = (*thisenv)->CallStaticObjectMethod(thisenv, CallRFFIHelperClass, CAR_MethodID, e); - return mkGlobalRef(thisenv, result); + return checkRef(thisenv, result); } SEXP CDR(SEXP e) { JNIEnv *thisenv = getEnv(); SEXP result = (*thisenv)->CallStaticObjectMethod(thisenv, CallRFFIHelperClass, CDR_MethodID, e); - return mkGlobalRef(thisenv, result); + return checkRef(thisenv, result); } SEXP CAAR(SEXP e) { @@ -64,7 +64,7 @@ SEXP CDAR(SEXP e) { SEXP CADR(SEXP e) { JNIEnv *thisenv = getEnv(); SEXP result = (*thisenv)->CallStaticObjectMethod(thisenv, CallRFFIHelperClass, CADR_MethodID, e); - return mkGlobalRef(thisenv, result); + return checkRef(thisenv, result); } SEXP CDDR(SEXP e) { @@ -98,13 +98,13 @@ void SET_TAG(SEXP x, SEXP y) { SEXP SETCAR(SEXP x, SEXP y) { JNIEnv *thisenv = getEnv(); SEXP result = (*thisenv)->CallStaticObjectMethod(thisenv, CallRFFIHelperClass, SETCAR_MethodID, x, y); - return mkGlobalRef(thisenv, result); + return checkRef(thisenv, result); } SEXP SETCDR(SEXP x, SEXP y) { JNIEnv *thisenv = getEnv(); SEXP result = (*thisenv)->CallStaticObjectMethod(thisenv, CallRFFIHelperClass, SETCDR_MethodID, x, y); - return mkGlobalRef(thisenv, result); + return checkRef(thisenv, result); } SEXP SETCADR(SEXP x, SEXP y) { diff --git a/com.oracle.truffle.r.native/fficall/jni/src/misc.c b/com.oracle.truffle.r.native/fficall/jni/src/misc.c index bc1a103019..5be911f7d4 100644 --- a/com.oracle.truffle.r.native/fficall/jni/src/misc.c +++ b/com.oracle.truffle.r.native/fficall/jni/src/misc.c @@ -29,10 +29,14 @@ void init_misc(JNIEnv *env) { } const char *R_CHAR(SEXP string) { + TRACE("%s(%p)", string); // This is nasty: // 1. the resulting character array has to be copied and zero-terminated. // 2. It causes an (inevitable?) memory leak JNIEnv *thisenv = getEnv(); +#if VALIDATE_REFS + validateRef(thisenv, string, "R_CHAR"); +#endif jsize len = (*thisenv)->GetStringUTFLength(thisenv, string); const char *stringChars = (*thisenv)->GetStringUTFChars(thisenv, string, NULL); char *copyChars = malloc(len + 1); diff --git a/com.oracle.truffle.r.native/fficall/jni/src/rf_functions.c b/com.oracle.truffle.r.native/fficall/jni/src/rf_functions.c index c1dc500ba8..60b33b839e 100644 --- a/com.oracle.truffle.r.native/fficall/jni/src/rf_functions.c +++ b/com.oracle.truffle.r.native/fficall/jni/src/rf_functions.c @@ -41,6 +41,7 @@ static jmethodID Rf_getAttribMethodID; static jmethodID Rf_setAttribMethodID; static jmethodID Rf_isStringMethodID; static jmethodID Rf_isNullMethodID; +static jmethodID Rf_warningMethodID; static jmethodID Rf_NewHashedEnvMethodID; void init_rf_functions(JNIEnv *env) { @@ -54,6 +55,7 @@ void init_rf_functions(JNIEnv *env) { Rf_setAttribMethodID = checkGetMethodID(env, CallRFFIHelperClass, "Rf_setAttrib", "(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)V", 1); Rf_isStringMethodID = checkGetMethodID(env, CallRFFIHelperClass, "Rf_isString", "(Ljava/lang/Object;)I", 1); Rf_isNullMethodID = checkGetMethodID(env, CallRFFIHelperClass, "Rf_isNull", "(Ljava/lang/Object;)I", 1); + Rf_warningMethodID = checkGetMethodID(env, CallRFFIHelperClass, "Rf_warning", "(Ljava/lang/String;)V", 1); createIntArrayMethodID = checkGetMethodID(env, RDataFactoryClass, "createIntVector", "(I)Lcom/oracle/truffle/r/runtime/data/RIntVector;", 1); createDoubleArrayMethodID = checkGetMethodID(env, RDataFactoryClass, "createDoubleVector", "(I)Lcom/oracle/truffle/r/runtime/data/RDoubleVector;", 1); createStringArrayMethodID = checkGetMethodID(env, RDataFactoryClass, "createStringVector", "(I)Lcom/oracle/truffle/r/runtime/data/RStringVector;", 1); @@ -63,24 +65,27 @@ void init_rf_functions(JNIEnv *env) { } SEXP Rf_ScalarInteger(int value) { + TRACE("%s(%d)\n", value); JNIEnv *thisenv = getEnv(); SEXP result = (*thisenv)->CallStaticObjectMethod(thisenv, CallRFFIHelperClass, Rf_ScalarIntegerMethodID, value); - return mkGlobalRef(thisenv, result); + return checkRef(thisenv, result); } SEXP Rf_ScalarReal(double value) { JNIEnv *thisenv = getEnv(); SEXP result = (*thisenv)->CallStaticObjectMethod(thisenv, CallRFFIHelperClass, Rf_ScalarDoubleMethodID, value); - return mkGlobalRef(thisenv, result); + return checkRef(thisenv, result); } SEXP Rf_ScalarString(SEXP value) { + TRACE(TARG1, value); JNIEnv *thisenv = getEnv(); SEXP result = (*thisenv)->CallStaticObjectMethod(thisenv, CallRFFIHelperClass, Rf_ScalarStringMethodID, value); - return mkGlobalRef(thisenv, result); + return checkRef(thisenv, result); } SEXP Rf_allocVector(SEXPTYPE t, R_xlen_t len) { + TRACE(TARG2d, t, len); JNIEnv *thisenv = getEnv(); SEXP result; switch (t) { @@ -105,13 +110,13 @@ SEXP Rf_allocVector(SEXPTYPE t, R_xlen_t len) { unimplemented("vector type not handled"); return NULL; } - return mkGlobalRef(thisenv, result); + return checkRef(thisenv, result); } SEXP Rf_cons(SEXP car, SEXP cdr) { JNIEnv *thisenv = getEnv(); SEXP result = (*thisenv)->CallStaticObjectMethod(thisenv, CallRFFIHelperClass, Rf_consMethodID, car, cdr); - return mkGlobalRef(thisenv, result); + return checkRef(thisenv, result); } void Rf_defineVar(SEXP symbol, SEXP value, SEXP rho) { @@ -122,13 +127,13 @@ void Rf_defineVar(SEXP symbol, SEXP value, SEXP rho) { SEXP Rf_findVar(SEXP symbol, SEXP rho) { JNIEnv *thisenv = getEnv(); SEXP result =(*thisenv)->CallStaticObjectMethod(thisenv, CallRFFIHelperClass, Rf_findVarMethodID, symbol, rho); - return mkGlobalRef(thisenv, result); + return checkRef(thisenv, result); } SEXP Rf_getAttrib(SEXP vec, SEXP name) { JNIEnv *thisenv = getEnv(); SEXP result = (*thisenv)->CallStaticObjectMethod(thisenv, CallRFFIHelperClass, Rf_getAttribMethodID, vec, name); - return mkGlobalRef(thisenv, result); + return checkRef(thisenv, result); } SEXP Rf_setAttrib(SEXP vec, SEXP name, SEXP val) { @@ -140,14 +145,14 @@ SEXP Rf_setAttrib(SEXP vec, SEXP name, SEXP val) { SEXP Rf_duplicate(SEXP x) { JNIEnv *thisenv = getEnv(); SEXP result = (*thisenv)->CallStaticObjectMethod(thisenv, CallRFFIHelperClass, Rf_duplicateMethodID, x); - return mkGlobalRef(thisenv, result); + return checkRef(thisenv, result); } SEXP Rf_install(const char *name) { JNIEnv *thisenv = getEnv(); jstring string = (*thisenv)->NewStringUTF(thisenv, name); SEXP result = (*thisenv)->CallStaticObjectMethod(thisenv, CallRFFIHelperClass, createSymbolMethodID, string); - return mkGlobalRef(thisenv, result); + return checkRef(thisenv, result); } Rboolean Rf_isNull(SEXP s) { @@ -165,7 +170,7 @@ SEXP Rf_mkChar(const char *x) { JNIEnv *thisenv = getEnv(); // TODO encoding, assume UTF for now SEXP result = (*thisenv)->NewStringUTF(thisenv, x); - return mkGlobalRef(thisenv, result); + return checkRef(thisenv, result); } SEXP Rf_mkCharLenCE(const char *x, int len, cetype_t enc) { @@ -175,7 +180,7 @@ SEXP Rf_mkCharLenCE(const char *x, int len, cetype_t enc) { buf[len] = 0; // TODO encoding, assume UTF for now, zero terminated SEXP result = (*thisenv)->NewStringUTF(thisenv, buf); - return mkGlobalRef(thisenv, result); + return checkRef(thisenv, result); } SEXP Rf_mkString(const char *s) { @@ -205,7 +210,9 @@ void Rf_warningcall(SEXP x, const char *msg, ...) { } void Rf_warning(const char *msg, ...) { - unimplemented("Rf_warning"); + JNIEnv *thisenv = getEnv(); + jstring string = (*thisenv)->NewStringUTF(thisenv, msg); + (*thisenv)->CallStaticObjectMethod(thisenv, CallRFFIHelperClass, Rf_warningMethodID, string); } void Rprintf(const char *msg, ...) { @@ -220,5 +227,5 @@ SEXP R_NewHashedEnv(SEXP parent, SEXP size) { JNIEnv *thisenv = getEnv(); int sizeAsInt = Rf_asInteger(size); SEXP result = (*thisenv)->CallStaticObjectMethod(thisenv, RDataFactoryClass, Rf_NewHashedEnvMethodID, parent, NULL, JNI_TRUE, sizeAsInt); - return mkGlobalRef(thisenv, result); + return checkRef(thisenv, result); } diff --git a/com.oracle.truffle.r.native/fficall/jni/src/rfficall.c b/com.oracle.truffle.r.native/fficall/jni/src/rfficall.c index 3d466017f1..93090ab33f 100644 --- a/com.oracle.truffle.r.native/fficall/jni/src/rfficall.c +++ b/com.oracle.truffle.r.native/fficall/jni/src/rfficall.c @@ -56,39 +56,49 @@ typedef SEXP (*call10func)(SEXP arg1, SEXP arg2, SEXP arg3, SEXP arg4, SEXP arg5 JNIEXPORT jobject JNICALL Java_com_oracle_truffle_r_runtime_ffi_jnr_CallRFFIWithJNI_call0(JNIEnv *env, jclass c, jlong address) { - setEnv(env); + callEnter(env); call0func call0 = (call0func) address; - return (*call0)(); + jobject result = (*call0)(); + callExit(env); + return result; } JNIEXPORT jobject JNICALL Java_com_oracle_truffle_r_runtime_ffi_jnr_CallRFFIWithJNI_call1(JNIEnv *env, jclass c, jlong address, jobject arg1) { - setEnv(env); + callEnter(env); call1func call1 = (call1func) address; - return (*call1)(arg1); + jobject result = (*call1)(arg1); + callExit(env); + return result; } JNIEXPORT jobject JNICALL Java_com_oracle_truffle_r_runtime_ffi_jnr_CallRFFIWithJNI_call2(JNIEnv *env, jclass c, jlong address, jobject arg1, jobject arg2) { - setEnv(env); + callEnter(env); call2func call2 = (call2func) address; - return (*call2)(arg1, arg2); + jobject result = (*call2)(arg1, arg2); + callExit(env); + return result; } JNIEXPORT jobject JNICALL Java_com_oracle_truffle_r_runtime_ffi_jnr_CallRFFIWithJNI_call3(JNIEnv *env, jclass c, jlong address, jobject arg1, jobject arg2, jobject arg3) { - setEnv(env); + callEnter(env); call3func call3 = (call3func) address; - return (*call3)(arg1, arg2, arg3); + jobject result = (*call3)(arg1, arg2, arg3); + callExit(env); + return result; } JNIEXPORT jobject JNICALL Java_com_oracle_truffle_r_runtime_ffi_jnr_CallRFFIWithJNI_call4(JNIEnv *env, jclass c, jlong address, jobject arg1, jobject arg2, jobject arg3, jobject arg4) { - setEnv(env); + callEnter(env); call4func call4 = (call4func) address; - return (*call4)(arg1, arg2, arg3, arg4); + jobject result = (*call4)(arg1, arg2, arg3, arg4); + callExit(env); + return result; } JNIEXPORT jobject JNICALL @@ -96,44 +106,54 @@ Java_com_oracle_truffle_r_runtime_ffi_jnr_CallRFFIWithJNI_call5(JNIEnv *env, jcl jobject arg3, jobject arg4, jobject arg5) { setEnv(env); call5func call5 = (call5func) address; - return (*call5)(arg1, arg2, arg3, arg4, arg5); + jobject result = (*call5)(arg1, arg2, arg3, arg4, arg5); + callExit(env); + return result; } JNIEXPORT jobject JNICALL Java_com_oracle_truffle_r_runtime_ffi_jnr_CallRFFIWithJNI_call6(JNIEnv *env, jclass c, jlong address, jobject arg1, jobject arg2, jobject arg3, jobject arg4, jobject arg5, jobject arg6) { - setEnv(env); + callEnter(env); call6func call6 = (call6func) address; - return (*call6)(arg1, arg2, arg3, arg4, arg5, arg6); + jobject result = (*call6)(arg1, arg2, arg3, arg4, arg5, arg6); + callExit(env); + return result; } JNIEXPORT jobject JNICALL Java_com_oracle_truffle_r_runtime_ffi_jnr_CallRFFIWithJNI_call7(JNIEnv *env, jclass c, jlong address, jobject arg1, jobject arg2, jobject arg3, jobject arg4, jobject arg5, jobject arg6, jobject arg7) { - setEnv(env); + callEnter(env); call7func call7 = (call7func) address; - return (*call7)(arg1, arg2, arg3, arg4, arg5, arg6, arg7); + jobject result = (*call7)(arg1, arg2, arg3, arg4, arg5, arg6, arg7); + callExit(env); + return result; } JNIEXPORT jobject JNICALL Java_com_oracle_truffle_r_runtime_ffi_jnr_CallRFFIWithJNI_call8(JNIEnv *env, jclass c, jlong address, jobject arg1, jobject arg2, jobject arg3, jobject arg4, jobject arg5, jobject arg6, jobject arg7, jobject arg8) { - setEnv(env); + callEnter(env); call8func call8 = (call8func) address; - return (*call8)(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8); + jobject result = (*call8)(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8); + callExit(env); + return result; } JNIEXPORT jobject JNICALL Java_com_oracle_truffle_r_runtime_ffi_jnr_CallRFFIWithJNI_call9(JNIEnv *env, jclass c, jlong address, jobject arg1, jobject arg2, jobject arg3, jobject arg4, jobject arg5, jobject arg6, jobject arg7, jobject arg8, jobject arg9) { - setEnv(env); + callEnter(env); call9func call9 = (call9func) address; - return (*call9)(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9); + jobject result = (*call9)(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9); + callExit(env); + return result; } JNIEXPORT jobject JNICALL Java_com_oracle_truffle_r_runtime_ffi_jnr_CallRFFIWithJNI_call(JNIEnv *env, jclass c, jlong address, jobjectArray args) { - setEnv(env); + callEnter(env); jsize len = (*env)->GetArrayLength(env, args); switch (len) { case 10: { @@ -148,7 +168,9 @@ Java_com_oracle_truffle_r_runtime_ffi_jnr_CallRFFIWithJNI_call(JNIEnv *env, jcla jobject arg9 = (*env)->GetObjectArrayElement(env, args, 8); jobject arg10 = (*env)->GetObjectArrayElement(env, args, 9); call10func call10 = (call10func) address; - return (*call10)(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10); + jobject result = (*call10)(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10); + callExit(env); + return result; } default: @@ -161,9 +183,10 @@ typedef void (*callVoid1func)(SEXP arg1); JNIEXPORT void JNICALL Java_com_oracle_truffle_r_runtime_ffi_jnr_CallRFFIWithJNI_callVoid1(JNIEnv *env, jclass c, jlong address, jobject arg1) { - setEnv(env); + callEnter(env); callVoid1func call1 = (callVoid1func) address; (*call1)(arg1); + callExit(env); } diff --git a/com.oracle.truffle.r.native/fficall/jni/src/rffiutils.c b/com.oracle.truffle.r.native/fficall/jni/src/rffiutils.c index 9f8a8d5dc0..e16c70d7ff 100644 --- a/com.oracle.truffle.r.native/fficall/jni/src/rffiutils.c +++ b/com.oracle.truffle.r.native/fficall/jni/src/rffiutils.c @@ -22,6 +22,7 @@ */ #include "rffiutils.h" #include <string.h> +#include <stdlib.h> /* * All calls pass through one of the call(N) methods, which carry the JNIEnv value, @@ -38,11 +39,28 @@ static jmethodID validateMethodID; JNIEnv *curenv = NULL; -//#define DEBUG_CACHE 1 +#define DEBUG_CACHE 0 +#define TRACE_COPIES 0 #define CACHED_GLOBALREFS_TABLE_SIZE 100 static SEXP cachedGlobalRefs[CACHED_GLOBALREFS_TABLE_SIZE]; static SEXP checkCachedGlobalRef(JNIEnv *env, SEXP obj); +typedef struct CopiedVectors_struct { + SEXPTYPE type; + SEXP obj; + void *jArray; + void *data; +} CopiedVector; + +#define COPIED_VECTORS_INITIAL_SIZE 100 +// A table of vectors that have been accessed and whose contents, e.g. the actual data +// as a primitive array have been copied and handed out to the native code. +static CopiedVector *copiedVectors; +// hwm of copiedVectors +static int copiedVectorsIndex; +static int copiedVectorsLength; + + void init_utils(JNIEnv *env) { curenv = env; RDataFactoryClass = checkFindClass(env, "com/oracle/truffle/r/runtime/data/RDataFactory"); @@ -54,9 +72,78 @@ void init_utils(JNIEnv *env) { for (int i = 0; i < CACHED_GLOBALREFS_TABLE_SIZE; i++) { cachedGlobalRefs[i] = NULL; } + copiedVectors = malloc(sizeof(CopiedVector) * COPIED_VECTORS_INITIAL_SIZE); + copiedVectorsLength = COPIED_VECTORS_INITIAL_SIZE; + copiedVectorsIndex = 0; +} + +void callEnter(JNIEnv *env) { + setEnv(env); +// printf("callEnter\n"); +} + +void callExit(JNIEnv *env) { +// printf("callExit\n"); + int i; + for (i = 0; i < copiedVectorsIndex; i++) { + CopiedVector cv = copiedVectors[i]; + switch (cv.type) { + case INTSXP: { + jintArray intArray = (jintArray) cv.jArray; + (*env)->ReleaseIntArrayElements(env, intArray, (jint *)cv.data, 0); + break; + } + default: + fatalError("copiedVector type"); + } + } + copiedVectorsIndex = 0; +} + +void *findCopiedObject(JNIEnv *env, SEXP x) { + int i; + for (i = 0; i < copiedVectorsIndex; i++) { + CopiedVector cv = copiedVectors[i]; + if ((*env)->IsSameObject(env, cv.obj, x)) { + void *data = cv.data; +#if TRACE_COPIES + printf("findCopiedObject(%p): found %p\n", x, data); +#endif + return data; + } + } +#if TRACE_COPIES + printf("findCopiedObject(%p): not found\n", x); +#endif + return NULL; } -SEXP mkGlobalRef(JNIEnv *env, SEXP obj) { +void addCopiedObject(JNIEnv *env, SEXP x, SEXPTYPE type, void *jArray, void *data) { +#if TRACE_COPIES + printf("addCopiedObject(%p, %p)\n", x, data); +#endif + if (copiedVectorsIndex >= copiedVectorsLength) { + int newLength = 2 * copiedVectorsLength; + CopiedVector *newCopiedVectors = malloc(sizeof(CopiedVector) * newLength); + if (newCopiedVectors == NULL) { + fatalError("malloc failure"); + } + memcpy(newCopiedVectors, copiedVectors, copiedVectorsLength * sizeof(CopiedVector)); + free(copiedVectors); + copiedVectors = newCopiedVectors; + copiedVectorsLength = newLength; + } + copiedVectors[copiedVectorsIndex].obj = x; + copiedVectors[copiedVectorsIndex].data = data; + copiedVectors[copiedVectorsIndex].type = type; + copiedVectors[copiedVectorsIndex].jArray = jArray; + copiedVectorsIndex++; +#if TRACE_COPIES + printf("copiedVectorsIndex: %d\n", copiedVectorsIndex); +#endif +} + +SEXP checkRef(JNIEnv *env, SEXP obj) { SEXP result = checkCachedGlobalRef(env, obj); return result; } @@ -86,13 +173,21 @@ static SEXP checkCachedGlobalRef(JNIEnv *env, SEXP obj) { return ref; } } +#if USE_GLOBAL SEXP result = (*env)->NewGlobalRef(env, obj); -#if DEBUG_CACHE - printf("gref: new=%p\n", result); +#else + SEXP result = obj; #endif return result; } +void validateRef(JNIEnv *env, SEXP x, const char *msg) { + jobjectRefType t = (*env)->GetObjectRefType(env, x); + if (t == JNIInvalidRefType) { + fatalError(msg); + } +} + void validate(SEXP x) { (*curenv)->CallStaticObjectMethod(curenv, CallRFFIHelperClass, validateMethodID, x); } @@ -126,7 +221,7 @@ jclass checkFindClass(JNIEnv *env, const char *name) { strcat(buf, name); (*env)->FatalError(env, buf); } - return mkGlobalRef(env, klass); + return (*env)->NewGlobalRef(env, klass); } jmethodID checkGetMethodID(JNIEnv *env, jclass klass, const char *name, const char *sig, int isStatic) { diff --git a/com.oracle.truffle.r.native/fficall/jni/src/rffiutils.h b/com.oracle.truffle.r.native/fficall/jni/src/rffiutils.h index 5044291545..ac4425e2c8 100644 --- a/com.oracle.truffle.r.native/fficall/jni/src/rffiutils.h +++ b/com.oracle.truffle.r.native/fficall/jni/src/rffiutils.h @@ -26,6 +26,8 @@ #include <jni.h> #include <Rinternals.h> +#define VALIDATE_REFS 1 + JNIEnv *getEnv(); void setEnv(JNIEnv *env); @@ -33,11 +35,28 @@ jclass checkFindClass(JNIEnv *env, const char *name); jmethodID checkGetMethodID(JNIEnv *env, jclass klass, const char *name, const char *sig, int isStatic); extern jmethodID createSymbolMethodID; +// use for an unimplemented API function void unimplemented(char *msg); +// use for any fatal error void fatalError(char *msg); +// makes a call to the VM with x as an argument (for debugger validation) void validate(SEXP x); -SEXP mkGlobalRef(JNIEnv *env, SEXP); -SEXP mkNamedGlobalRef(JNIEnv *env, int index, SEXP); +// checks x against the list of canonical (named) refs, returning the canonical version if a match +SEXP checkRef(JNIEnv *env, SEXP x); +// creates a JNI global ref from x for slot index of the named refs table +SEXP mkNamedGlobalRef(JNIEnv *env, int index, SEXP x); +// validate a JNI reference +void validateRef(JNIEnv *env, SEXP x, const char *msg); + +// entering a top-level JNI call +void callEnter(JNIEnv *env); +// exiting a top-level JNI call +void callExit(JNIEnv *env); + +// find an object for which we have cached the internal rep +void *findCopiedObject(JNIEnv *env, SEXP x); +// add a new object to the internal rep cache +void addCopiedObject(JNIEnv *env, SEXP x, SEXPTYPE type, void *jArray, void *data); void init_variables(JNIEnv *env, jobjectArray initialValues); void init_register(JNIEnv *env); @@ -53,4 +72,17 @@ void init_utils(JNIEnv *env); extern jclass RDataFactoryClass; extern jclass CallRFFIHelperClass; +#define TRACE_UPCALLS 0 + +#define TARG1 "%s(%p)\n" +#define TARG2 "%s(%p, %p)\n" +#define TARG2d "%s(%p, %d)\n" + +#if TRACE_UPCALLS +#define TRACE(format, ...) printf(format, __FUNCTION__, __VA_ARGS__) +#else +#define TRACE(format, ...) +#endif + + #endif /* RFFIUTILS_H */ diff --git a/com.oracle.truffle.r.native/fficall/jni/src/typecoerce.c b/com.oracle.truffle.r.native/fficall/jni/src/typecoerce.c index 047414ba87..2782978855 100644 --- a/com.oracle.truffle.r.native/fficall/jni/src/typecoerce.c +++ b/com.oracle.truffle.r.native/fficall/jni/src/typecoerce.c @@ -25,19 +25,22 @@ static jmethodID Rf_asIntegerMethodID; static jmethodID Rf_asRealMethodID; static jmethodID Rf_asCharMethodID; +static jmethodID Rf_asLogicalMethodID; static jmethodID Rf_PairToVectorListMethodID; void init_typecoerce(JNIEnv *env) { Rf_asIntegerMethodID = checkGetMethodID(env, CallRFFIHelperClass, "Rf_asInteger", "(Ljava/lang/Object;)I", 1); Rf_asRealMethodID = checkGetMethodID(env, CallRFFIHelperClass, "Rf_asReal", "(Ljava/lang/Object;)D", 1); Rf_asCharMethodID = checkGetMethodID(env, CallRFFIHelperClass, "Rf_asChar", "(Ljava/lang/Object;)Ljava/lang/String;", 1); + Rf_asLogicalMethodID = checkGetMethodID(env, CallRFFIHelperClass, "Rf_asLogical", "(Ljava/lang/Object;)I", 1); Rf_PairToVectorListMethodID = checkGetMethodID(env, CallRFFIHelperClass, "Rf_PairToVectorList", "(Ljava/lang/Object;)Ljava/lang/Object;", 1); } SEXP Rf_asChar(SEXP x){ + TRACE(TARG1, x); JNIEnv *thisenv = getEnv(); SEXP result = (*thisenv)->CallStaticObjectMethod(thisenv, CallRFFIHelperClass, Rf_asCharMethodID, x); - return mkGlobalRef(thisenv, result); + return checkRef(thisenv, result); } SEXP Rf_coerceVector(SEXP x, SEXPTYPE t){ @@ -47,7 +50,7 @@ SEXP Rf_coerceVector(SEXP x, SEXPTYPE t){ SEXP Rf_PairToVectorList(SEXP x){ JNIEnv *thisenv = getEnv(); SEXP result = (*thisenv)->CallStaticObjectMethod(thisenv, CallRFFIHelperClass, Rf_PairToVectorListMethodID, x); - return mkGlobalRef(thisenv, result); + return checkRef(thisenv, result); } SEXP Rf_VectorToPairList(SEXP x){ @@ -59,15 +62,19 @@ SEXP Rf_asCharacterFactor(SEXP x){ } int Rf_asLogical(SEXP x){ - unimplemented("Rf_asLogical"); + TRACE(TARG1, x); + JNIEnv *thisenv = getEnv(); + return (*thisenv)->CallStaticIntMethod(thisenv, CallRFFIHelperClass, Rf_asLogicalMethodID, x); } int Rf_asInteger(SEXP x) { + TRACE(TARG1, x); JNIEnv *thisenv = getEnv(); return (*thisenv)->CallStaticIntMethod(thisenv, CallRFFIHelperClass, Rf_asIntegerMethodID, x); } double Rf_asReal(SEXP x) { + TRACE(TARG1, x); JNIEnv *thisenv = getEnv(); return (*thisenv)->CallStaticDoubleMethod(thisenv, CallRFFIHelperClass, Rf_asRealMethodID, x); } diff --git a/com.oracle.truffle.r.native/fficall/jni/src/vectoraccess.c b/com.oracle.truffle.r.native/fficall/jni/src/vectoraccess.c index 771eb9d446..b3f0df097f 100644 --- a/com.oracle.truffle.r.native/fficall/jni/src/vectoraccess.c +++ b/com.oracle.truffle.r.native/fficall/jni/src/vectoraccess.c @@ -94,23 +94,22 @@ int SETLEVELS(SEXP x, int v){ unimplemented("SETLEVELS"); } - - int *LOGICAL(SEXP x){ unimplemented("LOGICAL"); } - int *INTEGER(SEXP x){ + TRACE(TARG1, x); // TODO This does not support write access, e.g. INTEGER(x)[i] JNIEnv *thisenv = getEnv(); - jintArray intarray = (*thisenv)->CallStaticObjectMethod(thisenv, CallRFFIHelperClass, INTEGER_MethodID, x); - int len = (*thisenv)->GetArrayLength(thisenv, intarray); - jint *data = (*thisenv)->GetIntArrayElements(thisenv, intarray, NULL); - void *result = malloc(len * 4); - memcpy(result, data, len * 4); - (*thisenv)->ReleaseIntArrayElements(thisenv, intarray, data, JNI_ABORT); - return (int *) result; + jint *data = (jint *) findCopiedObject(thisenv, x); + if (data == NULL) { + jintArray intArray = (*thisenv)->CallStaticObjectMethod(thisenv, CallRFFIHelperClass, INTEGER_MethodID, x); + int len = (*thisenv)->GetArrayLength(thisenv, intArray); + data = (*thisenv)->GetIntArrayElements(thisenv, intArray, NULL); + addCopiedObject(thisenv, x, INTSXP, intArray, data); + } + return data; } @@ -138,16 +137,17 @@ Rcomplex *COMPLEX(SEXP x){ SEXP STRING_ELT(SEXP x, R_xlen_t i){ + TRACE(TARG2d, x, i); JNIEnv *thisenv = getEnv(); SEXP result = (*thisenv)->CallStaticObjectMethod(thisenv, CallRFFIHelperClass, STRING_ELT_MethodID, x, i); - return mkGlobalRef(thisenv, result); + return checkRef(thisenv, result); } SEXP VECTOR_ELT(SEXP x, R_xlen_t i){ JNIEnv *thisenv = getEnv(); SEXP result = (*thisenv)->CallStaticObjectMethod(thisenv, CallRFFIHelperClass, VECTOR_ELT_MethodID, x, i); - return mkGlobalRef(thisenv, result); + return checkRef(thisenv, result); } void SET_INTEGER_ELT(SEXP x, R_xlen_t i, int v) { diff --git a/com.oracle.truffle.r.native/library/tools/src/gramRd.c b/com.oracle.truffle.r.native/library/tools/src/gramRd.c index 02a87523d0..ba2ae3a490 100644 --- a/com.oracle.truffle.r.native/library/tools/src/gramRd.c +++ b/com.oracle.truffle.r.native/library/tools/src/gramRd.c @@ -2858,22 +2858,18 @@ yyreturn: return YYID (yyresult); } - - -extern void SET_INTEGER_ELT(SEXP x, R_xlen_t i, int v); - static SEXP xxpushMode(int newmode, int newitem, int neweqn) { SEXP ans; PROTECT(ans = allocVector(INTSXP, 7)); - SET_INTEGER_ELT(ans, 0, parseState.xxmode); /* Lexer mode */ - SET_INTEGER_ELT(ans, 1, parseState.xxitemType); /* What is \item? */ - SET_INTEGER_ELT(ans, 2, parseState.xxbraceDepth); /* Brace depth used in RCODE and VERBATIM */ - SET_INTEGER_ELT(ans, 3, parseState.xxinRString); /* Quote char that started a string */ - SET_INTEGER_ELT(ans, 4, parseState.xxQuoteLine); /* Where the quote was */ - SET_INTEGER_ELT(ans, 5, parseState.xxQuoteCol); /* " */ - SET_INTEGER_ELT(ans, 6, parseState.xxinEqn); /* In the first arg to \eqn or \deqn: no escapes */ + INTEGER(ans)[0] = parseState.xxmode; /* Lexer mode */ + INTEGER(ans)[1] = parseState.xxitemType; /* What is \item? */ + INTEGER(ans)[2] = parseState.xxbraceDepth; /* Brace depth used in RCODE and VERBATIM */ + INTEGER(ans)[3] = parseState.xxinRString; /* Quote char that started a string */ + INTEGER(ans)[4] = parseState.xxQuoteLine; /* Where the quote was */ + INTEGER(ans)[5] = parseState.xxQuoteCol; /* " */ + INTEGER(ans)[6] = parseState.xxinEqn; /* In the first arg to \eqn or \deqn: no escapes */ #if DEBUGMODE Rprintf("xxpushMode(%d, %s) pushes %d, %s, %d\n", newmode, yytname[YYTRANSLATE(newitem)], @@ -3358,13 +3354,13 @@ static SEXP makeSrcref(YYLTYPE *lloc, SEXP srcfile) SEXP val; PROTECT(val = allocVector(INTSXP, 6)); - SET_INTEGER_ELT(val, 0, lloc->first_line); - SET_INTEGER_ELT(val, 1, lloc->first_byte); - SET_INTEGER_ELT(val, 2, lloc->last_line); - SET_INTEGER_ELT(val, 3, lloc->last_byte); - SET_INTEGER_ELT(val, 4, lloc->first_column); - SET_INTEGER_ELT(val, 5, lloc->last_column); - setAttrib(val, R_SrcfileSymbol, srcfile); + INTEGER(val)[0] = lloc->first_line; + INTEGER(val)[1] = lloc->first_byte; + INTEGER(val)[2] = lloc->last_line; + INTEGER(val)[3] = lloc->last_byte; + INTEGER(val)[4] = lloc->first_column; + INTEGER(val)[5] = lloc->last_column; + setAttrib(val, R_SrcfileSymbol, srcfile); setAttrib(val, R_ClassSymbol, mkString("srcref")); UNPROTECT(1); return val; @@ -3462,8 +3458,9 @@ static int Rconn_fgetc(Rconnection con) { return -1; } +extern JNIEnv *getEnv(); + static Rconnection con_parse; -static JNIEnv *jnienv; static jmethodID getcMethodID; /* need to handle incomplete last line */ @@ -3471,17 +3468,17 @@ static int con_getc(void) { int c; static int last=-1000; - - c = (*jnienv)->CallIntMethod(jnienv, con_parse, getcMethodID, con_parse); + JNIEnv *env = getEnv(); + c = (*env)->CallIntMethod(env, con_parse, getcMethodID, con_parse); if (c == EOF && last != '\n') c = '\n'; return (last = c); } static -SEXP R_ParseRd(JNIEnv *env, Rconnection con, ParseStatus *status, SEXP srcfile, Rboolean fragment) +SEXP R_ParseRd(Rconnection con, ParseStatus *status, SEXP srcfile, Rboolean fragment) { con_parse = con; - jnienv = env; + JNIEnv *env = getEnv(); jclass klass = (*env)->FindClass(env, "com/oracle/truffle/r/runtime/conn/RConnection"); getcMethodID = (*env)->GetMethodID(env, klass, "getc", "()I"); ptr_getc = con_getc; @@ -4271,12 +4268,13 @@ static void PopState() { .External2(C_parseRd,file, srcfile, encoding, verbose, basename, warningCalls) If there is text then that is read and the other arguments are ignored. + + This is derived fron the function of the same name in the GnuR version. + Argument checking has already been performed, however, the types of the + arguments are as per the GnuR version, just passed explicitly (.Call style) + rather then as a list. */ -JNIEXPORT jobject JNICALL -Java_com_oracle_truffle_r_library_tools_ToolsNative_cParseRdNative(JNIEnv *env, jclass c, jobject con, - jobject source, jboolean verbose, jboolean fragment, jstring basename, jboolean warningcalls) -{ - setEnv(env); +SEXP C_parseRd(SEXP con, SEXP source, SEXP verbose, SEXP fragment, SEXP basename, SEXP warningcalls) { SEXP s = R_NilValue; ParseStatus status; @@ -4289,16 +4287,16 @@ Java_com_oracle_truffle_r_library_tools_ToolsNative_cParseRdNative(JNIEnv *env, PushState(); - parseState.xxBasename = basename; - wCalls = warningcalls; +// parseState.xxDebugTokens = asInteger(verbose); + parseState.xxBasename = CHAR(STRING_ELT(basename, 0)); + wCalls = asLogical(warningcalls); - s = R_ParseRd(env, con, &status, source, fragment); + s = R_ParseRd(con, &status, source, asLogical(fragment)); PopState(); if (status != PARSE_OK) { // TODO throw an exception } - - return s; + return s; } /* "do_deparseRd" diff --git a/com.oracle.truffle.r.native/library/tools/src/tools.h b/com.oracle.truffle.r.native/library/tools/src/tools.h index a8e3b7d52a..699bd444fa 100644 --- a/com.oracle.truffle.r.native/library/tools/src/tools.h +++ b/com.oracle.truffle.r.native/library/tools/src/tools.h @@ -43,7 +43,8 @@ SEXP startHTTPD(SEXP sIP, SEXP sPort) { return NULL; } SEXP stopHTTPD(void) { return NULL; } SEXP C_parseLatex(SEXP call, SEXP op, SEXP args, SEXP env) { return NULL; } -SEXP C_parseRd(SEXP call, SEXP op, SEXP args, SEXP env) { return NULL; } +//SEXP C_parseRd(SEXP call, SEXP op, SEXP args, SEXP env); +SEXP C_parseRd(SEXP con, SEXP source, SEXP verbose, SEXP fragment, SEXP basename, SEXP warningcalls); SEXP C_deparseRd(SEXP e, SEXP state) { return NULL; } #endif diff --git a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jnr/CallRFFIHelper.java b/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jnr/CallRFFIHelper.java index 1826713ff9..6415c18dfd 100644 --- a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jnr/CallRFFIHelper.java +++ b/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jnr/CallRFFIHelper.java @@ -73,6 +73,16 @@ public class CallRFFIHelper { } } + static int Rf_asLogical(Object x) { + if (x instanceof Byte) { + return ((Byte) x).intValue(); + } else if (x instanceof RLogicalVector) { + return ((RLogicalVector) x).getDataAt(0); + } else { + throw RInternalError.unimplemented(); + } + } + static String Rf_asChar(Object x) { if (x instanceof String) { return (String) x; @@ -155,6 +165,10 @@ public class CallRFFIHelper { return pl.toRList(); } + static void Rf_warning(String msg) { + RError.warning(RError.Message.GENERIC, msg); + } + static int LENGTH(Object x) { if (x instanceof RAbstractContainer) { return ((RAbstractContainer) x).getLength(); @@ -185,7 +199,7 @@ public class CallRFFIHelper { static byte[] RAW(Object x) { if (x instanceof RRawVector) { - return ((RRawVector) x).getDataCopy(); + return ((RRawVector) x).getDataWithoutCopying(); } else { throw RInternalError.unimplemented(); } @@ -194,14 +208,17 @@ public class CallRFFIHelper { static int[] INTEGER(Object x) { if (x instanceof RIntVector) { - return ((RIntVector) x).getDataCopy(); + return ((RIntVector) x).getDataWithoutCopying(); } else { throw RInternalError.unimplemented(); } } static String STRING_ELT(Object x, int i) { - if (x instanceof RStringVector) { + if (x instanceof String) { + assert i == 0; + return (String) x; + } else if (x instanceof RStringVector) { return ((RStringVector) x).getDataAt(i); } else { throw RInternalError.unimplemented(); diff --git a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jnr/CallRFFIWithJNI.java b/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jnr/CallRFFIWithJNI.java index cf053bb071..c279442a7c 100644 --- a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jnr/CallRFFIWithJNI.java +++ b/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jnr/CallRFFIWithJNI.java @@ -22,6 +22,8 @@ */ package com.oracle.truffle.r.runtime.ffi.jnr; +import java.util.concurrent.*; + import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.source.*; import com.oracle.truffle.r.runtime.*; @@ -36,6 +38,8 @@ import com.oracle.truffle.r.runtime.ffi.DLL.SymbolInfo; * maximum number of args (64). This implementation passes up to 9 arguments explicitly; beyond 9 * they are passed as an array and the JNI code has to call back to get the args (not very * efficient). + * + * The JNI layer is not (currently) MT safe, so all calls are single threaded. */ public class CallRFFIWithJNI implements CallRFFI { @@ -73,10 +77,14 @@ public class CallRFFIWithJNI implements CallRFFI { initialize(INITIALIZE_VALUES); } - // @formatter:off + private static final Semaphore inCritical = new Semaphore(1, false); + public Object invokeCall(SymbolInfo symbolInfo, Object[] args) throws Throwable { long address = symbolInfo.address; - switch (args.length) { + try { + inCritical.acquire(); + switch (args.length) { + // @formatter:off case 0: return call0(address); case 1: return call1(address, args[0]); case 2: return call2(address, args[0], args[1]); @@ -89,32 +97,54 @@ public class CallRFFIWithJNI implements CallRFFI { case 9: return call9(address, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8]); default: return call(address, args); + // @formatter:on + } + } finally { + inCritical.release(); } } public Object invokeExternal(SymbolInfo symbolInfo, Object[] args) throws Throwable { - assert false; - return null; + throw RInternalError.unimplemented(".External"); } private static native void initialize(Object[] initialValues); + private static native Object call(long address, Object[] args); + private static native Object call0(long address); + private static native Object call1(long address, Object arg1); + private static native Object call2(long address, Object arg1, Object arg2); - private static native Object call3(long address, Object arg1, Object arg2, Object arg3); - private static native Object call4(long address, Object arg1, Object arg2, Object arg3, Object arg4); - private static native Object call5(long address, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5); - private static native Object call6(long address, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6); - private static native Object call7(long address, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7); - private static native Object call8(long address, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7, Object arg8); - private static native Object call9(long address, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7, Object arg8, Object arg9); + + private static native Object call3(long address, Object arg1, Object arg2, Object arg3); + + private static native Object call4(long address, Object arg1, Object arg2, Object arg3, Object arg4); + + private static native Object call5(long address, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5); + + private static native Object call6(long address, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6); + + private static native Object call7(long address, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7); + + private static native Object call8(long address, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7, Object arg8); + + private static native Object call9(long address, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7, Object arg8, Object arg9); public void invokeVoidCall(SymbolInfo symbolInfo, Object[] args) throws Throwable { long address = symbolInfo.address; - switch (args.length) { - case 1: callVoid1(address, args[0]); break; - default: assert false; + try { + inCritical.acquire(); + switch (args.length) { + case 1: + callVoid1(address, args[0]); + break; + default: + throw RInternalError.shouldNotReachHere(); + } + } finally { + inCritical.release(); } } diff --git a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jnr/JNR_RFFIFactory.java b/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jnr/JNR_RFFIFactory.java index 09e818401f..5ab5cb3101 100644 --- a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jnr/JNR_RFFIFactory.java +++ b/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jnr/JNR_RFFIFactory.java @@ -25,6 +25,7 @@ package com.oracle.truffle.r.runtime.ffi.jnr; import java.io.*; import java.nio.*; import java.util.*; +import java.util.concurrent.*; import jnr.constants.platform.*; import jnr.ffi.*; @@ -33,13 +34,17 @@ import jnr.posix.*; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.r.runtime.*; +import com.oracle.truffle.r.runtime.RContext.ContextState; +import com.oracle.truffle.r.runtime.conn.*; +import com.oracle.truffle.r.runtime.data.*; +import com.oracle.truffle.r.runtime.env.*; import com.oracle.truffle.r.runtime.ffi.*; import com.oracle.truffle.r.runtime.ffi.DLL.DLLInfo; /** * JNR/JNI-based factory. */ -public class JNR_RFFIFactory extends RFFIFactory implements RFFI, BaseRFFI, StatsRFFI, RApplRFFI, LapackRFFI, UserRngRFFI, PCRERFFI { +public class JNR_RFFIFactory extends RFFIFactory implements RFFI, BaseRFFI, StatsRFFI, ToolsRFFI, RApplRFFI, LapackRFFI, UserRngRFFI, PCRERFFI { public JNR_RFFIFactory() { } @@ -50,6 +55,17 @@ public class JNR_RFFIFactory extends RFFIFactory implements RFFI, BaseRFFI, Stat getCallRFFI(); } + /** + * Placeholder class for context-specific native state. + */ + private static class ContextStateImpl implements RContext.ContextState { + + } + + public ContextState newContext(RContext context, Object... objects) { + return new ContextStateImpl(); + } + private static byte[] wrapChar(char v) { return new byte[]{(byte) v}; } @@ -468,6 +484,50 @@ public class JNR_RFFIFactory extends RFFIFactory implements RFFI, BaseRFFI, Stat return stats().fft_work(a, wrapInt(nseg), wrapInt(n), wrapInt(nspn), wrapInt(isn), work, iwork); } + // Tools + + @Override + public ToolsRFFI getToolsRFFI() { + return this; + } + + private static class ToolsProvider { + private static ToolsProvider tools; + private static DLL.SymbolInfo parseRd; + + @TruffleBoundary + private ToolsProvider() { + System.load(LibPaths.getPackageLibPath("tools")); + parseRd = DLL.findSymbolInfo("C_parseRd", "tools"); + } + + static ToolsProvider toolsProvider() { + if (tools == null) { + tools = new ToolsProvider(); + } + return tools; + } + + DLL.SymbolInfo getParseRd() { + return parseRd; + } + + } + + private static final Semaphore parseRdCritical = new Semaphore(1, false); + + public Object parseRd(RConnection con, REnvironment srcfile, RLogicalVector verbose, RLogicalVector fragment, RStringVector basename, RLogicalVector warningCalls) { + // The C code is not thread safe. + try { + parseRdCritical.acquire(); + return getCallRFFI().invokeCall(ToolsProvider.toolsProvider().getParseRd(), new Object[]{con, srcfile, verbose, fragment, basename, warningCalls}); + } catch (Throwable ex) { + throw RInternalError.shouldNotReachHere(); + } finally { + parseRdCritical.release(); + } + } + /* * UserRng. This is a singleton instance, although the actual library may vary from run to run. */ diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RContext.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RContext.java index 2bf42fbe84..016dcd0ea1 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RContext.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RContext.java @@ -39,6 +39,7 @@ import com.oracle.truffle.r.runtime.data.RPromise.Closure; import com.oracle.truffle.r.runtime.env.*; import com.oracle.truffle.r.runtime.env.frame.*; import com.oracle.truffle.r.runtime.env.REnvironment.*; +import com.oracle.truffle.r.runtime.ffi.*; import com.oracle.truffle.r.runtime.rng.*; /** @@ -368,7 +369,8 @@ public final class RContext extends ExecutionContext { RConnection(ConnectionSupport.class, false), StdConnections(StdConnections.class, true), RNG(RRNG.class, false), - FrameSlotChangeMonitor(FrameSlotChangeMonitor.class, false); + FrameSlotChangeMonitor(FrameSlotChangeMonitor.class, false), + RFFI(RFFIContextStateFactory.class, false); private final Class<? extends StateFactory> klass; private StateFactory factory; diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/RFFI.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/RFFI.java index fe9ea68616..17e532bf6b 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/RFFI.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/RFFI.java @@ -31,6 +31,7 @@ package com.oracle.truffle.r.runtime.ffi; * <li>{@link LapackRFFI}: the specific, typed, foreign functions required by the built-in * {@code Lapack} functions.</li> * <li>{@link StatsRFFI}: native functions in the {@code stats} package.</li> + * <li>{@link ToolsRFFI}: native functions in the {@code tools} package.</li> * <li>{@link RApplRFFI}: the specific, typed, foreign functions required by the built-in * {@code Linpack} functions.</li> * <li>{@link CRFFI}: {@code .C} and {@code .Fortran} call interface.</li> @@ -51,6 +52,8 @@ public interface RFFI { StatsRFFI getStatsRFFI(); + ToolsRFFI getToolsRFFI(); + CRFFI getCRFFI(); CallRFFI getCallRFFI(); diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/RFFIContextStateFactory.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/RFFIContextStateFactory.java new file mode 100644 index 0000000000..b2fc96982c --- /dev/null +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/RFFIContextStateFactory.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2014, 2015, 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.r.runtime.*; +import com.oracle.truffle.r.runtime.RContext.*; + +/** + * This is the factory-independent class referenced by {@link RContext} that manages the + * context-specific state for any given {@link RFFIFactory}. It simply forwards the calls to the + * actual factory. + */ +public class RFFIContextStateFactory implements StateFactory { + private static RFFIFactory theFactory; + + public static void registerFactory(RFFIFactory factory) { + theFactory = factory; + } + + public ContextState newContext(RContext context, Object... objects) { + return theFactory.newContext(context, objects); + } + + public void systemInitialized(RContext context, ContextState state) { + theFactory.systemInitialized(context, state); + } + + public void beforeDestroy(RContext context, ContextState state) { + theFactory.beforeDestroy(context, state); + } + +} diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/RFFIFactory.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/RFFIFactory.java index 026d07024f..9433a030b5 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/RFFIFactory.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/RFFIFactory.java @@ -30,12 +30,18 @@ import com.oracle.truffle.r.runtime.*; /** * Factory class for the different possible implementations of the {@link RFFI} interface. The * choice of factory is made by the R engine and set here by the call to {@link #setRFFIFactory}. + * + * The RFFI may need to do special things in the case of multiple contexts, hence any given factory + * must support the {@link com.oracle.truffle.r.runtime.RContext.StateFactory} interface. However, + * since we don't know exactly which factory will be used, {@link RContext} references the + * {@link RFFIContextStateFactory} class. */ -public abstract class RFFIFactory { +public abstract class RFFIFactory implements RContext.StateFactory { protected static RFFI theRFFI; public static void setRFFIFactory(RFFIFactory factory) { + RFFIContextStateFactory.registerFactory(factory); theRFFI = factory.createRFFI(); } diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/StatsRFFI.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/StatsRFFI.java index bc8d2c3300..a796d4fb61 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/StatsRFFI.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/StatsRFFI.java @@ -23,7 +23,7 @@ package com.oracle.truffle.r.runtime.ffi; /** - * Interface to native (C) methods provides by the {@code stats} package. + * Interface to native (C) methods provided by the {@code stats} package. */ public interface StatsRFFI { // Checkstyle: stop method name diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/tools/ToolsNative.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/ToolsRFFI.java similarity index 55% rename from com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/tools/ToolsNative.java rename to com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/ToolsRFFI.java index 6a542e825a..45bf274c10 100644 --- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/tools/ToolsNative.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/ToolsRFFI.java @@ -20,27 +20,23 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package com.oracle.truffle.r.library.tools; +package com.oracle.truffle.r.runtime.ffi; import com.oracle.truffle.r.runtime.conn.*; +import com.oracle.truffle.r.runtime.data.*; import com.oracle.truffle.r.runtime.env.*; -import com.oracle.truffle.r.runtime.ffi.*; - -public class ToolsNative { - private static ToolsNative singleton; - - static ToolsNative provider() { - if (singleton == null) { - singleton = new ToolsNative(); - System.load(LibPaths.getPackageLibPath("tools")); - } - return singleton; - } - - Object cParseRd(RConnection con, REnvironment srcfile, boolean verbose, boolean fragment, String basename, boolean warningCalls) { - return cParseRdNative(con, srcfile, verbose, fragment, basename, warningCalls); - } - - private static native Object cParseRdNative(RConnection con, REnvironment srcfile, boolean verbose, boolean fragment, String basename, boolean warningCalls); +/** + * Interface to native (C) methods provided by the {@code tools} package. + */ +public interface ToolsRFFI { + /** + * This invokes the Rd parser, written in C, and part of GnuR, that does its work using the R + * FFI interface. The R code initially invokes this via {@code .External2(C_parseRd, ...)}, + * which has a custom specialization in the implementation of the {@code .External2} buitin. + * That does some work in Java, and then calls this method to invoke the actual C code. We can't + * go straight to the GnuR C entry point as that makes GnuR-specific assumptions about, for + * example, how connections are implemented. + */ + Object parseRd(RConnection con, REnvironment srcfile, RLogicalVector verbose, RLogicalVector fragment, RStringVector basename, RLogicalVector warningCalls); } diff --git a/com.oracle.truffle.r.test.native/packages/testrffi/testrffi/NAMESPACE b/com.oracle.truffle.r.test.native/packages/testrffi/testrffi/NAMESPACE index 465189b97d..e8d3772855 100644 --- a/com.oracle.truffle.r.test.native/packages/testrffi/testrffi/NAMESPACE +++ b/com.oracle.truffle.r.test.native/packages/testrffi/testrffi/NAMESPACE @@ -2,9 +2,4 @@ useDynLib(testrffi) ## and exported functions -export(add_int) -export(add_double) -export(createIntVector) -export(createExternalPtr) -export(getExternalPtrAddr) -export(test_TYPEOF) +exportPattern("rffi\\..*") diff --git a/com.oracle.truffle.r.test.native/packages/testrffi/testrffi/R/testrffi.R b/com.oracle.truffle.r.test.native/packages/testrffi/testrffi/R/testrffi.R index efa57a3383..c3e6f9e793 100644 --- a/com.oracle.truffle.r.test.native/packages/testrffi/testrffi/R/testrffi.R +++ b/com.oracle.truffle.r.test.native/packages/testrffi/testrffi/R/testrffi.R @@ -1,23 +1,23 @@ -add_int <- function(a, b) { - .Call("add_int", as.integer(a), as.integer(b), PACKAGE = "testrffi") +rffi.addInt <- function(a, b) { + .Call("addInt", as.integer(a), as.integer(b), PACKAGE = "testrffi") } -add_double <- function(a, b) { - .Call("add_double", as.double(a), as.double(b), PACKAGE = "testrffi") +rffi.addDouble <- function(a, b) { + .Call("addDouble", as.double(a), as.double(b), PACKAGE = "testrffi") } -createIntVector <- function(n) { - .Call("createIntVector", as.integer(n), PACKAGE = "testrffi") +rffi.populateIntVector <- function(n) { + .Call("populateIntVector", as.integer(n), PACKAGE = "testrffi") } -createExternalPtr <- function(addr, tag, prot) { +rffi.createExternalPtr <- function(addr, tag, prot) { .Call("createExternalPtr", as.integer(addr), tag, prot, PACKAGE = "testrffi") } -getExternalPtrAddr <- function(eptr) { +rffi.getExternalPtrAddr <- function(eptr) { .Call("getExternalPtrAddr", eptr) } -test_TYPEOF <- function(x) { - .Call("test_TYPEOF", x, PACKAGE = "testrffi") +rffi.TYPEOF <- function(x) { + .Call("invoke_TYPEOF", x, PACKAGE = "testrffi") } diff --git a/com.oracle.truffle.r.test.native/packages/testrffi/testrffi/src/testrffi.c b/com.oracle.truffle.r.test.native/packages/testrffi/testrffi/src/testrffi.c index 9ef4221195..d87674bc95 100644 --- a/com.oracle.truffle.r.test.native/packages/testrffi/testrffi/src/testrffi.c +++ b/com.oracle.truffle.r.test.native/packages/testrffi/testrffi/src/testrffi.c @@ -27,20 +27,27 @@ #include <Rdefines.h> #include <Rinternals.h> -SEXP add_int(SEXP a, SEXP b) { +SEXP addInt(SEXP a, SEXP b) { int aInt = INTEGER_VALUE(a); int bInt = INTEGER_VALUE(b); return ScalarInteger(aInt + bInt); } -SEXP add_double(SEXP a, SEXP b) { +SEXP addDouble(SEXP a, SEXP b) { double aDouble = NUMERIC_VALUE(a); double bDouble = NUMERIC_VALUE(b); return ScalarReal(aDouble + bDouble); } -SEXP createIntVector(SEXP n) { - SEXP v = allocVector(INTSXP, INTEGER_VALUE(n)); +SEXP populateIntVector(SEXP n) { + SEXP v; + int intN = INTEGER_VALUE(n); + PROTECT(v = allocVector(INTSXP, intN)); + int i; + for (i = 0; i < intN; i++) { + INTEGER(v)[i] = i; + } + UNPROTECT(1); return v; } @@ -52,6 +59,6 @@ SEXP getExternalPtrAddr(SEXP eptr) { return ScalarInteger((int) R_ExternalPtrAddr(eptr)); } -SEXP test_TYPEOF(SEXP x) { +SEXP invoke_TYPEOF(SEXP x) { return ScalarInteger(TYPEOF(x)); } diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test index 29ca141940..671f41f708 100644 --- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test @@ -102417,7 +102417,7 @@ attr(,"foo") [7] 0.35201276 0.16919220 0.93579263 0.26084486 ##com.oracle.truffle.r.test.rpackages.TestRPackages.testLoadTestRFFI -#{ library("testrffi", lib.loc = "com.oracle.truffle.r.test/rpackages/testrlibs_user"); r1 <- add_int(2L, 3L); r2 <- add_double(2, 3); v <- createIntVector(2); v[1] <- 1; v[2] <- 2; detach("package:testrffi"); list(r1, r2, v) } +#{ library("testrffi", lib.loc = "com.oracle.truffle.r.test/rpackages/testrlibs_user"); r1 <- rffi.addInt(2L, 3L); r2 <- rffi.addDouble(2, 3); v <- rffi.populateIntVector(5); detach("package:testrffi"); list(r1, r2, v) } [[1]] [1] 5 @@ -102425,7 +102425,7 @@ attr(,"foo") [1] 5 [[3]] -[1] 1 2 +[1] 0 1 2 3 4 ##com.oracle.truffle.r.test.rpackages.TestRPackages.testLoadVanilla diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/rpackages/TestRPackages.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/rpackages/TestRPackages.java index 8d68f81b8d..9024a7181d 100644 --- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/rpackages/TestRPackages.java +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/rpackages/TestRPackages.java @@ -157,7 +157,7 @@ public class TestRPackages extends TestBase { @Test public void testLoadTestRFFI() { assertEval(TestBase.template( - "{ library(\"testrffi\", lib.loc = \"%0\"); r1 <- add_int(2L, 3L); r2 <- add_double(2, 3); v <- createIntVector(2); v[1] <- 1; v[2] <- 2; detach(\"package:testrffi\"); list(r1, r2, v) }", + "{ library(\"testrffi\", lib.loc = \"%0\"); r1 <- rffi.addInt(2L, 3L); r2 <- rffi.addDouble(2, 3); v <- rffi.populateIntVector(5); detach(\"package:testrffi\"); list(r1, r2, v) }", new String[]{packagePaths.rpackagesLibs.toString()})); } -- GitLab