From 7cea8caec09c65343d853fac78da6ce6ebed03aa Mon Sep 17 00:00:00 2001
From: Mick Jordan <mick.jordan@oracle.com>
Date: Fri, 2 Dec 2016 16:25:14 -0800
Subject: [PATCH] rffi: add interface for RFFI upcalls; refactor native layer
 to use instance not static class; add factory for alternate upcall
 implementations

---
 .../fficall/src/jni/Parse.c                   |   6 +-
 .../fficall/src/jni/Random.c                  |  12 +-
 .../fficall/src/jni/Rembedded.c               |  44 +-
 .../fficall/src/jni/Rinternals.c              | 384 ++++----
 .../fficall/src/jni/call_rffi.c               |   5 +-
 .../fficall/src/jni/rffiutils.c               |  25 +-
 .../fficall/src/jni/rffiutils.h               |   5 +-
 .../fficall/src/jni/variables.c               |  32 +-
 ...llRFFIHelper.java => JavaUpCallsRFFI.java} | 899 ++++++++++--------
 .../r/runtime/ffi/JavaUpCallsRFFIFactory.java |  32 +
 .../truffle/r/runtime/ffi/RFFIVariables.java  |   3 +-
 .../truffle/r/runtime/ffi/jni/JNI_Call.java   |   6 +-
 .../runtime/ffi/jni/TraceUpCallsAdapter.java  | 893 +++++++++++++++++
 .../truffle/r/runtime/ffi/CharSXPWrapper.java |  55 ++
 .../truffle/r/runtime/ffi/ParseResult.java    |  44 +
 .../oracle/truffle/r/runtime/ffi/RFFI.java    |   3 +
 .../truffle/r/runtime/ffi/UpCallsRFFI.java    | 279 ++++++
 .../r/runtime/ffi/UpCallsRFFIFactory.java     |  44 +
 18 files changed, 2100 insertions(+), 671 deletions(-)
 rename com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/{jni/CallRFFIHelper.java => JavaUpCallsRFFI.java} (66%)
 create mode 100644 com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/JavaUpCallsRFFIFactory.java
 create mode 100644 com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jni/TraceUpCallsAdapter.java
 create mode 100644 com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/CharSXPWrapper.java
 create mode 100644 com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/ParseResult.java
 create mode 100644 com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/UpCallsRFFI.java
 create mode 100644 com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/UpCallsRFFIFactory.java

diff --git a/com.oracle.truffle.r.native/fficall/src/jni/Parse.c b/com.oracle.truffle.r.native/fficall/src/jni/Parse.c
index cc55518f19..233a58dbc1 100644
--- a/com.oracle.truffle.r.native/fficall/src/jni/Parse.c
+++ b/com.oracle.truffle.r.native/fficall/src/jni/Parse.c
@@ -30,15 +30,15 @@ static jfieldID parseExprFieldID;
 
 
 void init_parse(JNIEnv *env) {
-	parseMethodID = checkGetMethodID(env, CallRFFIHelperClass, "R_ParseVector", "(Ljava/lang/Object;ILjava/lang/Object;)Ljava/lang/Object;", 1);
-	parseResultClass = checkFindClass(env, "com/oracle/truffle/r/runtime/ffi/jni/CallRFFIHelper$ParseResult");
+	parseMethodID = checkGetMethodID(env, UpCallsRFFIClass, "R_ParseVector", "(Ljava/lang/Object;ILjava/lang/Object;)Ljava/lang/Object;", 0);
+	parseResultClass = checkFindClass(env, "com/oracle/truffle/r/runtime/ffi/ParseResult");
 	parseStatusFieldID = checkGetFieldID(env, parseResultClass, "parseStatus", "I", 0);
 	parseExprFieldID = checkGetFieldID(env, parseResultClass, "expr", "Ljava/lang/Object;", 0);
 }
 
 SEXP R_ParseVector(SEXP text, int n, ParseStatus *z, SEXP srcfile) {
 	JNIEnv *env = getEnv();
-	jobject result = (*env)->CallStaticObjectMethod(env, CallRFFIHelperClass, parseMethodID, text, n, srcfile);
+	jobject result = (*env)->CallObjectMethod(env, UpCallsRFFIObject, parseMethodID, text, n, srcfile);
 	*z = (*env)->GetIntField(env, result, parseStatusFieldID);
     return (*env)->GetObjectField(env, result, parseExprFieldID);
 }
diff --git a/com.oracle.truffle.r.native/fficall/src/jni/Random.c b/com.oracle.truffle.r.native/fficall/src/jni/Random.c
index 70341f309a..4c958521f1 100644
--- a/com.oracle.truffle.r.native/fficall/src/jni/Random.c
+++ b/com.oracle.truffle.r.native/fficall/src/jni/Random.c
@@ -27,24 +27,24 @@ static jmethodID PutRNGstate_MethodID;
 static jmethodID UnifRand_MethodID;
 
 void init_random(JNIEnv *env) {
-	GetRNGstate_MethodID = checkGetMethodID(env, CallRFFIHelperClass, "getRNGstate", "()V", 1);
-	PutRNGstate_MethodID = checkGetMethodID(env, CallRFFIHelperClass, "putRNGstate", "()V", 1);
-	UnifRand_MethodID = checkGetMethodID(env, CallRFFIHelperClass, "unifRand", "()D", 1);
+	GetRNGstate_MethodID = checkGetMethodID(env, UpCallsRFFIClass, "GetRNGstate", "()V", 0);
+	PutRNGstate_MethodID = checkGetMethodID(env, UpCallsRFFIClass, "PutRNGstate", "()V", 0);
+	UnifRand_MethodID = checkGetMethodID(env, UpCallsRFFIClass, "unif_rand", "()D", 0);
 }
 
 void GetRNGstate() {
 	JNIEnv *thisenv = getEnv();
-	(*thisenv)->CallStaticVoidMethod(thisenv, CallRFFIHelperClass, GetRNGstate_MethodID);
+	(*thisenv)->CallVoidMethod(thisenv, UpCallsRFFIObject, GetRNGstate_MethodID);
 }
 
 void PutRNGstate() {
 	JNIEnv *thisenv = getEnv();
-	(*thisenv)->CallStaticVoidMethod(thisenv, CallRFFIHelperClass, PutRNGstate_MethodID);
+	(*thisenv)->CallVoidMethod(thisenv, UpCallsRFFIObject, PutRNGstate_MethodID);
 }
 
 double unif_rand() {
 	JNIEnv *thisenv = getEnv();
-	return (*thisenv)->CallStaticDoubleMethod(thisenv, CallRFFIHelperClass, UnifRand_MethodID);
+	return (*thisenv)->CallDoubleMethod(thisenv, UpCallsRFFIObject, UnifRand_MethodID);
 }
 
 double norm_rand() {
diff --git a/com.oracle.truffle.r.native/fficall/src/jni/Rembedded.c b/com.oracle.truffle.r.native/fficall/src/jni/Rembedded.c
index f7cb4401f5..11bfb92d75 100644
--- a/com.oracle.truffle.r.native/fficall/src/jni/Rembedded.c
+++ b/com.oracle.truffle.r.native/fficall/src/jni/Rembedded.c
@@ -178,8 +178,8 @@ int Rf_initialize_R(int argc, char *argv[]) {
 
 char *R_HomeDir(void) {
 	JNIEnv *jniEnv = getEnv();
-	jmethodID R_HomeDirMethodID = checkGetMethodID(jniEnv, CallRFFIHelperClass, "R_HomeDir", "()Ljava/lang/String;", 1);
-	jstring homeDir = (*jniEnv)->CallStaticObjectMethod(jniEnv, CallRFFIHelperClass, R_HomeDirMethodID);
+	jmethodID R_HomeDirMethodID = checkGetMethodID(jniEnv, UpCallsRFFIClass, "R_HomeDir", "()Ljava/lang/String;", 0);
+	jstring homeDir = (*jniEnv)->CallObjectMethod(jniEnv, UpCallsRFFIObject, R_HomeDirMethodID);
 	const char *homeDirChars = stringToChars(jniEnv, homeDir);
 	return (char *)homeDirChars;
 }
@@ -308,8 +308,8 @@ void uR_Busy(int x) {
 
 void uR_CleanUp(SA_TYPE x, int y, int z) {
 	JNIEnv *jniEnv = getEnv();
-	jmethodID methodID = checkGetMethodID(jniEnv, CallRFFIHelperClass, "R_CleanUp", "(III)V", 1);
-	(*jniEnv)->CallStaticVoidMethod(jniEnv, CallRFFIHelperClass, methodID, x, y, z);
+	jmethodID methodID = checkGetMethodID(jniEnv, UpCallsRFFIClass, "R_CleanUp", "(III)V", 1);
+	(*jniEnv)->CallStaticVoidMethod(jniEnv, UpCallsRFFIClass, methodID, x, y, z);
 }
 
 int uR_ShowFiles(int a, const char **b, const char **c,
@@ -599,63 +599,63 @@ static char **update_environ_with_java_home(void) {
 
 CTXT R_getGlobalFunctionContext() {
 	JNIEnv *jniEnv = getEnv();
-	jmethodID methodID = checkGetMethodID(jniEnv, CallRFFIHelperClass, "R_getGlobalFunctionContext", "()Ljava/lang/Object;", 1);
-    CTXT result = (*jniEnv)->CallStaticObjectMethod(jniEnv, CallRFFIHelperClass, methodID);
+	jmethodID methodID = checkGetMethodID(jniEnv, UpCallsRFFIClass, "R_getGlobalFunctionContext", "()Ljava/lang/Object;", 0);
+    CTXT result = (*jniEnv)->CallObjectMethod(jniEnv, UpCallsRFFIObject, methodID);
     SEXP new_result = checkRef(jniEnv, result);
     return new_result == R_NilValue ? NULL : addGlobalRef(jniEnv, result, 0);
 }
 
 CTXT R_getParentFunctionContext(CTXT c) {
 	JNIEnv *jniEnv = getEnv();
-	jmethodID methodID = checkGetMethodID(jniEnv, CallRFFIHelperClass, "R_getParentFunctionContext", "(Ljava/lang/Object;)Ljava/lang/Object;", 1);
-    CTXT result = (*jniEnv)->CallStaticObjectMethod(jniEnv, CallRFFIHelperClass, methodID, c);
+	jmethodID methodID = checkGetMethodID(jniEnv, UpCallsRFFIClass, "R_getParentFunctionContext", "(Ljava/lang/Object;)Ljava/lang/Object;", 0);
+    CTXT result = (*jniEnv)->CallObjectMethod(jniEnv, UpCallsRFFIObject, methodID, c);
     SEXP new_result = checkRef(jniEnv, result);
     return new_result == R_NilValue ? NULL : addGlobalRef(jniEnv, result, 0);
 }
 
 SEXP R_getContextEnv(CTXT context) {
 	JNIEnv *jniEnv = getEnv();
-	jmethodID methodID = checkGetMethodID(jniEnv, CallRFFIHelperClass, "R_getContextEnv", "(Ljava/lang/Object;)Ljava/lang/Object;", 1);
-    SEXP result = (*jniEnv)->CallStaticObjectMethod(jniEnv, CallRFFIHelperClass, methodID, context);
+	jmethodID methodID = checkGetMethodID(jniEnv, UpCallsRFFIClass, "R_getContextEnv", "(Ljava/lang/Object;)Ljava/lang/Object;", 0);
+    SEXP result = (*jniEnv)->CallObjectMethod(jniEnv, UpCallsRFFIObject, methodID, context);
     return checkRef(jniEnv, result);
 }
 
 SEXP R_getContextFun(CTXT context) {
 	JNIEnv *jniEnv = getEnv();
-	jmethodID methodID = checkGetMethodID(jniEnv, CallRFFIHelperClass, "R_getContextFun", "(Ljava/lang/Object;)Ljava/lang/Object;", 1);
-    SEXP result = (*jniEnv)->CallStaticObjectMethod(jniEnv, CallRFFIHelperClass, methodID, context);
+	jmethodID methodID = checkGetMethodID(jniEnv, UpCallsRFFIClass, "R_getContextFun", "(Ljava/lang/Object;)Ljava/lang/Object;", 0);
+    SEXP result = (*jniEnv)->CallObjectMethod(jniEnv, UpCallsRFFIObject, methodID, context);
     return checkRef(jniEnv, result);
 }
 
 SEXP R_getContextCall(CTXT context) {
 	JNIEnv *jniEnv = getEnv();
-	jmethodID methodID = checkGetMethodID(jniEnv, CallRFFIHelperClass, "R_getContextCall", "(Ljava/lang/Object;)Ljava/lang/Object;", 1);
-    SEXP result = (*jniEnv)->CallStaticObjectMethod(jniEnv, CallRFFIHelperClass, methodID, context);
+	jmethodID methodID = checkGetMethodID(jniEnv, UpCallsRFFIClass, "R_getContextCall", "(Ljava/lang/Object;)Ljava/lang/Object;", 0);
+    SEXP result = (*jniEnv)->CallObjectMethod(jniEnv, UpCallsRFFIObject, methodID, context);
     return checkRef(jniEnv, result);
 }
 
 SEXP R_getContextSrcRef(CTXT context) {
     JNIEnv *jniEnv = getEnv();
-    jmethodID methodID = checkGetMethodID(jniEnv, CallRFFIHelperClass, "R_getContextSrcRef", "(Ljava/lang/Object;)Ljava/lang/Object;", 1);
-    SEXP result = (*jniEnv)->CallStaticObjectMethod(jniEnv, CallRFFIHelperClass, methodID, context);
+    jmethodID methodID = checkGetMethodID(jniEnv, UpCallsRFFIClass, "R_getContextSrcRef", "(Ljava/lang/Object;)Ljava/lang/Object;", 0);
+    SEXP result = (*jniEnv)->CallObjectMethod(jniEnv, UpCallsRFFIObject, methodID, context);
     result = checkRef(jniEnv, result);
     return result == R_NilValue ? NULL : result;
 }
 
 int R_insideBrowser() {
 	JNIEnv *jniEnv = getEnv();
-	jmethodID methodID = checkGetMethodID(jniEnv, CallRFFIHelperClass, "R_insideBrowser", "()I", 1);
-    return (*jniEnv)->CallStaticIntMethod(jniEnv, CallRFFIHelperClass, methodID);
+	jmethodID methodID = checkGetMethodID(jniEnv, UpCallsRFFIClass, "R_insideBrowser", "()I", 0);
+    return (*jniEnv)->CallStaticIntMethod(jniEnv, UpCallsRFFIClass, methodID);
 }
 
 int R_isGlobal(CTXT context) {
 	JNIEnv *jniEnv = getEnv();
-	jmethodID methodID = checkGetMethodID(jniEnv, CallRFFIHelperClass, "R_isGlobal", "(Ljava/lang/Object;)I", 1);
-    return (*jniEnv)->CallStaticIntMethod(jniEnv, CallRFFIHelperClass, methodID, context);
+	jmethodID methodID = checkGetMethodID(jniEnv, UpCallsRFFIClass, "R_isGlobal", "(Ljava/lang/Object;)I", 0);
+    return (*jniEnv)->CallStaticIntMethod(jniEnv, UpCallsRFFIClass, methodID, context);
 }
 
 int R_isEqual(void* x, void* y) {
 	JNIEnv *jniEnv = getEnv();
-	jmethodID methodID = checkGetMethodID(jniEnv, CallRFFIHelperClass, "R_isEqual", "(Ljava/lang/Object;Ljava/lang/Object;)I", 1);
-    return (*jniEnv)->CallStaticIntMethod(jniEnv, CallRFFIHelperClass, methodID, x, y);
+	jmethodID methodID = checkGetMethodID(jniEnv, UpCallsRFFIClass, "R_isEqual", "(Ljava/lang/Object;Ljava/lang/Object;)I", 0);
+    return (*jniEnv)->CallStaticIntMethod(jniEnv, UpCallsRFFIClass, methodID, x, y);
 }
diff --git a/com.oracle.truffle.r.native/fficall/src/jni/Rinternals.c b/com.oracle.truffle.r.native/fficall/src/jni/Rinternals.c
index 3972ae97f4..aa4b3352b6 100644
--- a/com.oracle.truffle.r.native/fficall/src/jni/Rinternals.c
+++ b/com.oracle.truffle.r.native/fficall/src/jni/Rinternals.c
@@ -30,7 +30,7 @@
 // to ensure that a global JNI handle is created (if necessary) and returned,
 // otherwise a GC might reclaim the result.
 
-// N.B. ALL functions go via CallRFFIHelper to provide a single point of re-entry
+// N.B. ALL functions go via UpCallsRFFI to provide a single point of re-entry
 
 static jmethodID Rf_ScalarIntegerMethodID;
 static jmethodID Rf_ScalarDoubleMethodID;
@@ -102,7 +102,7 @@ static jmethodID TYPEOF_MethodID;
 static jmethodID OBJECT_MethodID;
 static jmethodID DUPLICATE_ATTRIB_MethodID;
 static jmethodID isS4ObjectMethodID;
-static jmethodID logObject_MethodID;
+static jmethodID logNotCharSXPWrapperMethodID;
 static jmethodID R_tryEvalMethodID;
 static jmethodID RDEBUGMethodID;
 static jmethodID SET_RDEBUGMethodID;
@@ -113,7 +113,7 @@ static jmethodID PRVALUEMethodID;
 static jmethodID R_lsInternal3MethodID;
 static jmethodID R_do_MAKE_CLASS_MethodID;
 
-static jmethodID resetAndGetHandlerStacksMethodID;
+static jmethodID R_ToplevelExecMethodID;
 static jmethodID restoreHandlerStacksMethodID;
 
 static jmethodID R_MakeExternalPtrMethodID;
@@ -136,107 +136,107 @@ static jfieldID CharSXPWrapperContentsFieldID;
 jmethodID setCompleteMethodID;
 
 void init_internals(JNIEnv *env) {
-	Rf_ScalarIntegerMethodID = checkGetMethodID(env, CallRFFIHelperClass, "Rf_ScalarInteger", "(I)Lcom/oracle/truffle/r/runtime/data/RIntVector;", 1);
-	Rf_ScalarDoubleMethodID = checkGetMethodID(env, CallRFFIHelperClass, "Rf_ScalarDouble", "(D)Lcom/oracle/truffle/r/runtime/data/RDoubleVector;", 1);
-	Rf_ScalarStringMethodID = checkGetMethodID(env, CallRFFIHelperClass, "Rf_ScalarString", "(Ljava/lang/Object;)Lcom/oracle/truffle/r/runtime/data/RStringVector;", 1);
-	Rf_ScalarLogicalMethodID = checkGetMethodID(env, CallRFFIHelperClass, "Rf_ScalarLogical", "(I)Lcom/oracle/truffle/r/runtime/data/RLogicalVector;", 1);
-	Rf_consMethodID = checkGetMethodID(env, CallRFFIHelperClass, "Rf_cons", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", 1);
-	Rf_evalMethodID = checkGetMethodID(env, CallRFFIHelperClass, "Rf_eval", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", 1);
-	Rf_findfunMethodID = checkGetMethodID(env, CallRFFIHelperClass, "Rf_findfun", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", 1);
-	Rf_defineVarMethodID = checkGetMethodID(env, CallRFFIHelperClass, "Rf_defineVar", "(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)V", 1);
-	Rf_findVarMethodID = checkGetMethodID(env, CallRFFIHelperClass, "Rf_findVar", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", 1);
-	Rf_findVarInFrameMethodID = checkGetMethodID(env, CallRFFIHelperClass, "Rf_findVarInFrame", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", 1);
-	Rf_findVarInFrame3MethodID = checkGetMethodID(env, CallRFFIHelperClass, "Rf_findVarInFrame3", "(Ljava/lang/Object;Ljava/lang/Object;I)Ljava/lang/Object;", 1);
-	Rf_getAttribMethodID = checkGetMethodID(env, CallRFFIHelperClass, "Rf_getAttrib", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", 1);
-	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_installMethodID = checkGetMethodID(env, CallRFFIHelperClass, "Rf_install", "(Ljava/lang/String;)Ljava/lang/Object;", 1);
-	Rf_warningMethodID = checkGetMethodID(env, CallRFFIHelperClass, "Rf_warning", "(Ljava/lang/String;)V", 1);
-	Rf_warningcallMethodID = checkGetMethodID(env, CallRFFIHelperClass, "Rf_warningcall", "(Ljava/lang/Object;Ljava/lang/String;)V", 1);
-	Rf_errorMethodID = checkGetMethodID(env, CallRFFIHelperClass, "Rf_error", "(Ljava/lang/String;)V", 1);
-	Rf_allocateVectorMethodID = checkGetMethodID(env, CallRFFIHelperClass, "Rf_allocateVector", "(II)Ljava/lang/Object;", 1);
-	Rf_allocateMatrixMethodID = checkGetMethodID(env, CallRFFIHelperClass, "Rf_allocateMatrix", "(III)Ljava/lang/Object;", 1);
-	Rf_allocateArrayMethodID = checkGetMethodID(env, CallRFFIHelperClass, "Rf_allocateArray", "(ILjava/lang/Object;)Ljava/lang/Object;", 1);
-	Rf_duplicateMethodID = checkGetMethodID(env, CallRFFIHelperClass, "Rf_duplicate", "(Ljava/lang/Object;I)Ljava/lang/Object;", 1);
-	Rf_anyDuplicatedMethodID = checkGetMethodID(env, CallRFFIHelperClass, "Rf_anyDuplicated", "(Ljava/lang/Object;I)I", 1);
-	R_NewHashedEnvMethodID = checkGetMethodID(env, CallRFFIHelperClass, "R_NewHashedEnv", "(Lcom/oracle/truffle/r/runtime/env/REnvironment;Ljava/lang/String;ZI)Lcom/oracle/truffle/r/runtime/env/REnvironment;", 1);
-	Rf_classgetsMethodID = checkGetMethodID(env, CallRFFIHelperClass, "Rf_classgets", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", 1);
-	RprintfMethodID = checkGetMethodID(env, CallRFFIHelperClass, "printf", "(Ljava/lang/String;)V", 1);
-	R_do_MAKE_CLASS_MethodID = checkGetMethodID(env, CallRFFIHelperClass, "R_do_MAKE_CLASS", "(Ljava/lang/String;)Ljava/lang/Object;", 1);
-	R_FindNamespaceMethodID = checkGetMethodID(env, CallRFFIHelperClass, "R_FindNamespace", "(Ljava/lang/Object;)Ljava/lang/Object;", 1);
-	R_BindingIsLockedID = checkGetMethodID(env, CallRFFIHelperClass, "R_BindingIsLocked", "(Ljava/lang/Object;Ljava/lang/Object;)I", 1);
-	Rf_GetOption1MethodID = checkGetMethodID(env, CallRFFIHelperClass, "Rf_GetOption1", "(Ljava/lang/Object;)Ljava/lang/Object;", 1);
-	Rf_gsetVarMethodID = checkGetMethodID(env, CallRFFIHelperClass, "Rf_gsetVar", "(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)V", 1);
-	Rf_inheritsMethodID = checkGetMethodID(env, CallRFFIHelperClass, "Rf_inherits", "(Ljava/lang/Object;Ljava/lang/String;)I", 1);
-	Rf_lengthgetsMethodID = checkGetMethodID(env, CallRFFIHelperClass, "Rf_lengthgets", "(Ljava/lang/Object;I)Ljava/lang/Object;", 1);
-//	Rf_rPsortMethodID = checkGetMethodID(env, CallRFFIHelperClass, "Rf_rPsort", "(Lcom/oracle/truffle/r/runtime/data/RDoubleVector;II)", 1);
-//	Rf_iPsortMethodID = checkGetMethodID(env, CallRFFIHelperClass, "Rf_iPsort", "(Lcom/oracle/truffle/r/runtime/data/RIntVector;II)", 1);
-	CADR_MethodID = checkGetMethodID(env, CallRFFIHelperClass, "CADR", "(Ljava/lang/Object;)Ljava/lang/Object;", 1);
-	CADDR_MethodID = checkGetMethodID(env, CallRFFIHelperClass, "CADDR", "(Ljava/lang/Object;)Ljava/lang/Object;", 1);
-	TAG_MethodID = checkGetMethodID(env, CallRFFIHelperClass, "TAG", "(Ljava/lang/Object;)Ljava/lang/Object;", 1);
-	PRINTNAME_MethodID = checkGetMethodID(env, CallRFFIHelperClass, "PRINTNAME", "(Ljava/lang/Object;)Ljava/lang/Object;", 1);
-	CAR_MethodID = checkGetMethodID(env, CallRFFIHelperClass, "CAR", "(Ljava/lang/Object;)Ljava/lang/Object;", 1);
-	CDR_MethodID = checkGetMethodID(env, CallRFFIHelperClass, "CDR", "(Ljava/lang/Object;)Ljava/lang/Object;", 1);
-	CDDR_MethodID = checkGetMethodID(env, CallRFFIHelperClass, "CDDR", "(Ljava/lang/Object;)Ljava/lang/Object;", 1);
-	SET_TAG_MethodID = checkGetMethodID(env, CallRFFIHelperClass, "SET_TAG", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", 1);
-	SETCAR_MethodID = checkGetMethodID(env, CallRFFIHelperClass, "SETCAR", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", 1);
-	SETCDR_MethodID = checkGetMethodID(env, CallRFFIHelperClass, "SETCDR", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", 1);
-	SETCADR_MethodID = checkGetMethodID(env, CallRFFIHelperClass, "SETCADR", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", 1);
-	SYMVALUE_MethodID = checkGetMethodID(env, CallRFFIHelperClass, "SYMVALUE", "(Ljava/lang/Object;)Ljava/lang/Object;", 1);
-	SET_SYMVALUE_MethodID = checkGetMethodID(env, CallRFFIHelperClass, "SET_SYMVALUE", "(Ljava/lang/Object;Ljava/lang/Object;)V", 1);
-	SET_STRING_ELT_MethodID = checkGetMethodID(env, CallRFFIHelperClass, "SET_STRING_ELT", "(Ljava/lang/Object;ILjava/lang/Object;)V", 1);
-	SET_VECTOR_ELT_MethodID = checkGetMethodID(env, CallRFFIHelperClass, "SET_VECTOR_ELT", "(Ljava/lang/Object;ILjava/lang/Object;)V", 1);
-	RAW_MethodID = checkGetMethodID(env, CallRFFIHelperClass, "RAW", "(Ljava/lang/Object;)[B", 1);
-	REAL_MethodID = checkGetMethodID(env, CallRFFIHelperClass, "REAL", "(Ljava/lang/Object;)[D", 1);
-	LOGICAL_MethodID = checkGetMethodID(env, CallRFFIHelperClass, "LOGICAL", "(Ljava/lang/Object;)[B", 1);
-	INTEGER_MethodID = checkGetMethodID(env, CallRFFIHelperClass, "INTEGER", "(Ljava/lang/Object;)[I", 1);
-	STRING_ELT_MethodID = checkGetMethodID(env, CallRFFIHelperClass, "STRING_ELT", "(Ljava/lang/Object;I)Ljava/lang/Object;", 1);
-	VECTOR_ELT_MethodID = checkGetMethodID(env, CallRFFIHelperClass, "VECTOR_ELT", "(Ljava/lang/Object;I)Ljava/lang/Object;", 1);
-	LENGTH_MethodID = checkGetMethodID(env, CallRFFIHelperClass, "LENGTH", "(Ljava/lang/Object;)I", 1);
-	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/Object;", 1);
-	Rf_mkCharLenCEMethodID = checkGetMethodID(env, CallRFFIHelperClass, "Rf_mkCharLenCE", "([BI)Ljava/lang/Object;", 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);
-	NAMED_MethodID = checkGetMethodID(env, CallRFFIHelperClass, "NAMED", "(Ljava/lang/Object;)I", 1);
-	SET_TYPEOF_FASTR_MethodID = checkGetMethodID(env, CallRFFIHelperClass, "SET_TYPEOF_FASTR", "(Ljava/lang/Object;I)Ljava/lang/Object;", 1);
-	TYPEOF_MethodID = checkGetMethodID(env, CallRFFIHelperClass, "TYPEOF", "(Ljava/lang/Object;)I", 1);
-	OBJECT_MethodID = checkGetMethodID(env, CallRFFIHelperClass, "OBJECT", "(Ljava/lang/Object;)I", 1);
-	DUPLICATE_ATTRIB_MethodID = checkGetMethodID(env, CallRFFIHelperClass, "DUPLICATE_ATTRIB", "(Ljava/lang/Object;Ljava/lang/Object;)V", 1);
-	isS4ObjectMethodID = checkGetMethodID(env, CallRFFIHelperClass, "isS4Object", "(Ljava/lang/Object;)I", 1);
-	logObject_MethodID = checkGetMethodID(env, CallRFFIHelperClass, "logObject", "(Ljava/lang/Object;)V", 1);
-	R_tryEvalMethodID = checkGetMethodID(env, CallRFFIHelperClass, "R_tryEval", "(Ljava/lang/Object;Ljava/lang/Object;Z)Ljava/lang/Object;", 1);
-	RDEBUGMethodID = checkGetMethodID(env, CallRFFIHelperClass, "RDEBUG", "(Ljava/lang/Object;)I", 1);
-	SET_RDEBUGMethodID = checkGetMethodID(env, CallRFFIHelperClass, "SET_RDEBUG", "(Ljava/lang/Object;I)V", 1);
-	RSTEPMethodID = checkGetMethodID(env, CallRFFIHelperClass, "RSTEP", "(Ljava/lang/Object;)I", 1);
-	SET_RSTEPMethodID = checkGetMethodID(env, CallRFFIHelperClass, "SET_RSTEP", "(Ljava/lang/Object;I)V", 1);
-	ENCLOSMethodID = checkGetMethodID(env, CallRFFIHelperClass, "ENCLOS", "(Ljava/lang/Object;)Ljava/lang/Object;", 1);
-	PRVALUEMethodID = checkGetMethodID(env, CallRFFIHelperClass, "PRVALUE", "(Ljava/lang/Object;)Ljava/lang/Object;", 1);
-	R_lsInternal3MethodID = checkGetMethodID(env, CallRFFIHelperClass, "R_lsInternal3", "(Ljava/lang/Object;II)Ljava/lang/Object;", 1);
-
-	resetAndGetHandlerStacksMethodID = checkGetMethodID(env, CallRFFIHelperClass, "resetAndGetErrorHandlerStacks", "()Ljava/lang/Object;", 1);
-	restoreHandlerStacksMethodID = checkGetMethodID(env, CallRFFIHelperClass, "restoreErrorHandlerStacks", "(Ljava/lang/Object;)V", 1);
-
-	R_MakeExternalPtrMethodID = checkGetMethodID(env, CallRFFIHelperClass, "R_MakeExternalPtr", "(JLjava/lang/Object;Ljava/lang/Object;)Lcom/oracle/truffle/r/runtime/data/RExternalPtr;", 1);
-	R_ExternalPtrAddrMethodID = checkGetMethodID(env, CallRFFIHelperClass, "R_ExternalPtrAddr", "(Ljava/lang/Object;)J", 1);
-	R_ExternalPtrTagMethodID = checkGetMethodID(env, CallRFFIHelperClass, "R_ExternalPtrTag", "(Ljava/lang/Object;)Ljava/lang/Object;", 1);
-	R_ExternalPtrProtMethodID = checkGetMethodID(env, CallRFFIHelperClass, "R_ExternalPtrProt", "(Ljava/lang/Object;)Ljava/lang/Object;", 1);
-	R_SetExternalPtrAddrMethodID = checkGetMethodID(env, CallRFFIHelperClass, "R_SetExternalPtrAddr", "(Ljava/lang/Object;J)V", 1);
-	R_SetExternalPtrTagMethodID = checkGetMethodID(env, CallRFFIHelperClass, "R_SetExternalPtrTag", "(Ljava/lang/Object;Ljava/lang/Object;)V", 1);
-	R_SetExternalPtrProtMethodID = checkGetMethodID(env, CallRFFIHelperClass, "R_SetExternalPtrProt", "(Ljava/lang/Object;Ljava/lang/Object;)V", 1);
-
-	CharSXPWrapperClass = checkFindClass(env, "com/oracle/truffle/r/runtime/ffi/jni/CallRFFIHelper$CharSXPWrapper");
+	Rf_ScalarIntegerMethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rf_ScalarInteger", "(I)Lcom/oracle/truffle/r/runtime/data/RIntVector;", 0);
+	Rf_ScalarDoubleMethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rf_ScalarDouble", "(D)Lcom/oracle/truffle/r/runtime/data/RDoubleVector;", 0);
+	Rf_ScalarStringMethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rf_ScalarString", "(Ljava/lang/Object;)Lcom/oracle/truffle/r/runtime/data/RStringVector;", 0);
+	Rf_ScalarLogicalMethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rf_ScalarLogical", "(I)Lcom/oracle/truffle/r/runtime/data/RLogicalVector;", 0);
+	Rf_consMethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rf_cons", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", 0);
+	Rf_evalMethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rf_eval", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", 0);
+	Rf_findfunMethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rf_findfun", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", 0);
+	Rf_defineVarMethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rf_defineVar", "(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)V", 0);
+	Rf_findVarMethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rf_findVar", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", 0);
+	Rf_findVarInFrameMethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rf_findVarInFrame", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", 0);
+	Rf_findVarInFrame3MethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rf_findVarInFrame3", "(Ljava/lang/Object;Ljava/lang/Object;I)Ljava/lang/Object;", 0);
+	Rf_getAttribMethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rf_getAttrib", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", 0);
+	Rf_setAttribMethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rf_setAttrib", "(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)V", 0);
+	Rf_isStringMethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rf_isString", "(Ljava/lang/Object;)I", 0);
+	Rf_isNullMethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rf_isNull", "(Ljava/lang/Object;)I", 0);
+	Rf_installMethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rf_install", "(Ljava/lang/String;)Ljava/lang/Object;", 0);
+	Rf_warningMethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rf_warning", "(Ljava/lang/String;)V", 0);
+	Rf_warningcallMethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rf_warningcall", "(Ljava/lang/Object;Ljava/lang/String;)V", 0);
+	Rf_errorMethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rf_error", "(Ljava/lang/String;)V", 0);
+	Rf_allocateVectorMethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rf_allocateVector", "(II)Ljava/lang/Object;", 0);
+	Rf_allocateMatrixMethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rf_allocateMatrix", "(III)Ljava/lang/Object;", 0);
+	Rf_allocateArrayMethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rf_allocateArray", "(ILjava/lang/Object;)Ljava/lang/Object;", 0);
+	Rf_duplicateMethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rf_duplicate", "(Ljava/lang/Object;I)Ljava/lang/Object;", 0);
+	Rf_anyDuplicatedMethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rf_anyDuplicated", "(Ljava/lang/Object;I)I", 0);
+	R_NewHashedEnvMethodID = checkGetMethodID(env, UpCallsRFFIClass, "R_NewHashedEnv", "(Lcom/oracle/truffle/r/runtime/env/REnvironment;Ljava/lang/String;ZI)Lcom/oracle/truffle/r/runtime/env/REnvironment;", 0);
+	Rf_classgetsMethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rf_classgets", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", 0);
+	RprintfMethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rprintf", "(Ljava/lang/String;)V", 0);
+	R_do_MAKE_CLASS_MethodID = checkGetMethodID(env, UpCallsRFFIClass, "R_do_MAKE_CLASS", "(Ljava/lang/String;)Ljava/lang/Object;", 0);
+	R_FindNamespaceMethodID = checkGetMethodID(env, UpCallsRFFIClass, "R_FindNamespace", "(Ljava/lang/Object;)Ljava/lang/Object;", 0);
+	R_BindingIsLockedID = checkGetMethodID(env, UpCallsRFFIClass, "R_BindingIsLocked", "(Ljava/lang/Object;Ljava/lang/Object;)I", 0);
+	Rf_GetOption1MethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rf_GetOption1", "(Ljava/lang/Object;)Ljava/lang/Object;", 0);
+	Rf_gsetVarMethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rf_gsetVar", "(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)V", 0);
+	Rf_inheritsMethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rf_inherits", "(Ljava/lang/Object;Ljava/lang/String;)I", 0);
+	Rf_lengthgetsMethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rf_lengthgets", "(Ljava/lang/Object;I)Ljava/lang/Object;", 0);
+//	Rf_rPsortMethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rf_rPsort", "(Lcom/oracle/truffle/r/runtime/data/RDoubleVector;II)", 0);
+//	Rf_iPsortMethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rf_iPsort", "(Lcom/oracle/truffle/r/runtime/data/RIntVector;II)", 0);
+	CADR_MethodID = checkGetMethodID(env, UpCallsRFFIClass, "CADR", "(Ljava/lang/Object;)Ljava/lang/Object;", 0);
+	CADDR_MethodID = checkGetMethodID(env, UpCallsRFFIClass, "CADDR", "(Ljava/lang/Object;)Ljava/lang/Object;", 0);
+	TAG_MethodID = checkGetMethodID(env, UpCallsRFFIClass, "TAG", "(Ljava/lang/Object;)Ljava/lang/Object;", 0);
+	PRINTNAME_MethodID = checkGetMethodID(env, UpCallsRFFIClass, "PRINTNAME", "(Ljava/lang/Object;)Ljava/lang/Object;", 0);
+	CAR_MethodID = checkGetMethodID(env, UpCallsRFFIClass, "CAR", "(Ljava/lang/Object;)Ljava/lang/Object;", 0);
+	CDR_MethodID = checkGetMethodID(env, UpCallsRFFIClass, "CDR", "(Ljava/lang/Object;)Ljava/lang/Object;", 0);
+	CDDR_MethodID = checkGetMethodID(env, UpCallsRFFIClass, "CDDR", "(Ljava/lang/Object;)Ljava/lang/Object;", 0);
+	SET_TAG_MethodID = checkGetMethodID(env, UpCallsRFFIClass, "SET_TAG", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", 0);
+	SETCAR_MethodID = checkGetMethodID(env, UpCallsRFFIClass, "SETCAR", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", 0);
+	SETCDR_MethodID = checkGetMethodID(env, UpCallsRFFIClass, "SETCDR", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", 0);
+	SETCADR_MethodID = checkGetMethodID(env, UpCallsRFFIClass, "SETCADR", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", 0);
+	SYMVALUE_MethodID = checkGetMethodID(env, UpCallsRFFIClass, "SYMVALUE", "(Ljava/lang/Object;)Ljava/lang/Object;", 0);
+	SET_SYMVALUE_MethodID = checkGetMethodID(env, UpCallsRFFIClass, "SET_SYMVALUE", "(Ljava/lang/Object;Ljava/lang/Object;)V", 0);
+	SET_STRING_ELT_MethodID = checkGetMethodID(env, UpCallsRFFIClass, "SET_STRING_ELT", "(Ljava/lang/Object;ILjava/lang/Object;)V", 0);
+	SET_VECTOR_ELT_MethodID = checkGetMethodID(env, UpCallsRFFIClass, "SET_VECTOR_ELT", "(Ljava/lang/Object;ILjava/lang/Object;)V", 0);
+	RAW_MethodID = checkGetMethodID(env, UpCallsRFFIClass, "RAW", "(Ljava/lang/Object;)[B", 0);
+	REAL_MethodID = checkGetMethodID(env, UpCallsRFFIClass, "REAL", "(Ljava/lang/Object;)[D", 0);
+	LOGICAL_MethodID = checkGetMethodID(env, UpCallsRFFIClass, "LOGICAL", "(Ljava/lang/Object;)[B", 0);
+	INTEGER_MethodID = checkGetMethodID(env, UpCallsRFFIClass, "INTEGER", "(Ljava/lang/Object;)[I", 0);
+	STRING_ELT_MethodID = checkGetMethodID(env, UpCallsRFFIClass, "STRING_ELT", "(Ljava/lang/Object;I)Ljava/lang/Object;", 0);
+	VECTOR_ELT_MethodID = checkGetMethodID(env, UpCallsRFFIClass, "VECTOR_ELT", "(Ljava/lang/Object;I)Ljava/lang/Object;", 0);
+	LENGTH_MethodID = checkGetMethodID(env, UpCallsRFFIClass, "LENGTH", "(Ljava/lang/Object;)I", 0);
+	Rf_asIntegerMethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rf_asInteger", "(Ljava/lang/Object;)I", 0);
+//	Rf_asRealMethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rf_asReal", "(Ljava/lang/Object;)D", 0);
+	Rf_asCharMethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rf_asChar", "(Ljava/lang/Object;)Ljava/lang/Object;", 0);
+	Rf_mkCharLenCEMethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rf_mkCharLenCE", "([BI)Ljava/lang/Object;", 0);
+	Rf_asLogicalMethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rf_asLogical", "(Ljava/lang/Object;)I", 0);
+	Rf_PairToVectorListMethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rf_PairToVectorList", "(Ljava/lang/Object;)Ljava/lang/Object;", 0);
+	NAMED_MethodID = checkGetMethodID(env, UpCallsRFFIClass, "NAMED", "(Ljava/lang/Object;)I", 0);
+	SET_TYPEOF_FASTR_MethodID = checkGetMethodID(env, UpCallsRFFIClass, "SET_TYPEOF_FASTR", "(Ljava/lang/Object;I)Ljava/lang/Object;", 0);
+	TYPEOF_MethodID = checkGetMethodID(env, UpCallsRFFIClass, "TYPEOF", "(Ljava/lang/Object;)I", 0);
+	OBJECT_MethodID = checkGetMethodID(env, UpCallsRFFIClass, "OBJECT", "(Ljava/lang/Object;)I", 0);
+	DUPLICATE_ATTRIB_MethodID = checkGetMethodID(env, UpCallsRFFIClass, "DUPLICATE_ATTRIB", "(Ljava/lang/Object;Ljava/lang/Object;)V", 0);
+	isS4ObjectMethodID = checkGetMethodID(env, UpCallsRFFIClass, "isS4Object", "(Ljava/lang/Object;)I", 0);
+	logNotCharSXPWrapperMethodID = checkGetMethodID(env, UpCallsRFFIClass, "logNotCharSXPWrapper", "(Ljava/lang/Object;)V", 0);
+	R_tryEvalMethodID = checkGetMethodID(env, UpCallsRFFIClass, "R_tryEval", "(Ljava/lang/Object;Ljava/lang/Object;Z)Ljava/lang/Object;", 0);
+	RDEBUGMethodID = checkGetMethodID(env, UpCallsRFFIClass, "RDEBUG", "(Ljava/lang/Object;)I", 0);
+	SET_RDEBUGMethodID = checkGetMethodID(env, UpCallsRFFIClass, "SET_RDEBUG", "(Ljava/lang/Object;I)V", 0);
+	RSTEPMethodID = checkGetMethodID(env, UpCallsRFFIClass, "RSTEP", "(Ljava/lang/Object;)I", 0);
+	SET_RSTEPMethodID = checkGetMethodID(env, UpCallsRFFIClass, "SET_RSTEP", "(Ljava/lang/Object;I)V", 0);
+	ENCLOSMethodID = checkGetMethodID(env, UpCallsRFFIClass, "ENCLOS", "(Ljava/lang/Object;)Ljava/lang/Object;", 0);
+	PRVALUEMethodID = checkGetMethodID(env, UpCallsRFFIClass, "PRVALUE", "(Ljava/lang/Object;)Ljava/lang/Object;", 0);
+	R_lsInternal3MethodID = checkGetMethodID(env, UpCallsRFFIClass, "R_lsInternal3", "(Ljava/lang/Object;II)Ljava/lang/Object;", 0);
+
+	R_ToplevelExecMethodID = checkGetMethodID(env, UpCallsRFFIClass, "R_ToplevelExec", "()Ljava/lang/Object;", 0);
+	restoreHandlerStacksMethodID = checkGetMethodID(env, UpCallsRFFIClass, "R_ToplevelExecRestoreErrorHandlerStacks", "(Ljava/lang/Object;)V", 0);
+
+	R_MakeExternalPtrMethodID = checkGetMethodID(env, UpCallsRFFIClass, "R_MakeExternalPtr", "(JLjava/lang/Object;Ljava/lang/Object;)Lcom/oracle/truffle/r/runtime/data/RExternalPtr;", 0);
+	R_ExternalPtrAddrMethodID = checkGetMethodID(env, UpCallsRFFIClass, "R_ExternalPtrAddr", "(Ljava/lang/Object;)J", 0);
+	R_ExternalPtrTagMethodID = checkGetMethodID(env, UpCallsRFFIClass, "R_ExternalPtrTag", "(Ljava/lang/Object;)Ljava/lang/Object;", 0);
+	R_ExternalPtrProtMethodID = checkGetMethodID(env, UpCallsRFFIClass, "R_ExternalPtrProt", "(Ljava/lang/Object;)Ljava/lang/Object;", 0);
+	R_SetExternalPtrAddrMethodID = checkGetMethodID(env, UpCallsRFFIClass, "R_SetExternalPtrAddr", "(Ljava/lang/Object;J)V", 0);
+	R_SetExternalPtrTagMethodID = checkGetMethodID(env, UpCallsRFFIClass, "R_SetExternalPtrTag", "(Ljava/lang/Object;Ljava/lang/Object;)V", 0);
+	R_SetExternalPtrProtMethodID = checkGetMethodID(env, UpCallsRFFIClass, "R_SetExternalPtrProt", "(Ljava/lang/Object;Ljava/lang/Object;)V", 0);
+
+	CharSXPWrapperClass = checkFindClass(env, "com/oracle/truffle/r/runtime/ffi/CharSXPWrapper");
 	CharSXPWrapperContentsFieldID = checkGetFieldID(env, CharSXPWrapperClass, "contents", "Ljava/lang/String;", 0);
 
-    R_computeIdenticalMethodID = checkGetMethodID(env, CallRFFIHelperClass, "R_computeIdentical", "(Ljava/lang/Object;Ljava/lang/Object;I)I", 1);
-    Rf_copyListMatrixMethodID = checkGetMethodID(env, CallRFFIHelperClass, "Rf_copyListMatrix", "(Ljava/lang/Object;Ljava/lang/Object;I)V", 1);
-    Rf_copyMatrixMethodID = checkGetMethodID(env, CallRFFIHelperClass, "Rf_copyMatrix", "(Ljava/lang/Object;Ljava/lang/Object;I)V", 1);
-    Rf_nrowsMethodID = checkGetMethodID(env, CallRFFIHelperClass, "Rf_nrows", "(Ljava/lang/Object;)I", 1);
-    Rf_ncolsMethodID = checkGetMethodID(env, CallRFFIHelperClass, "Rf_ncols", "(Ljava/lang/Object;)I", 1);
+    R_computeIdenticalMethodID = checkGetMethodID(env, UpCallsRFFIClass, "R_computeIdentical", "(Ljava/lang/Object;Ljava/lang/Object;I)I", 0);
+    Rf_copyListMatrixMethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rf_copyListMatrix", "(Ljava/lang/Object;Ljava/lang/Object;I)V", 0);
+    Rf_copyMatrixMethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rf_copyMatrix", "(Ljava/lang/Object;Ljava/lang/Object;I)V", 0);
+    Rf_nrowsMethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rf_nrows", "(Ljava/lang/Object;)I", 0);
+    Rf_ncolsMethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rf_ncols", "(Ljava/lang/Object;)I", 0);
 
-    setCompleteMethodID = checkGetMethodID(env, CallRFFIHelperClass, "setComplete", "(Ljava/lang/Object;Z)V", 1);
+    setCompleteMethodID = checkGetMethodID(env, UpCallsRFFIClass, "setComplete", "(Ljava/lang/Object;Z)V", 0);
 }
 
 static jstring stringFromCharSXP(JNIEnv *thisenv, SEXP charsxp) {
@@ -244,7 +244,7 @@ static jstring stringFromCharSXP(JNIEnv *thisenv, SEXP charsxp) {
 	validateRef(thisenv, charsxp, "stringFromCharSXP");
 	if (!(*thisenv)->IsInstanceOf(thisenv, charsxp, CharSXPWrapperClass)) {
 
-	    (*thisenv)->CallStaticVoidMethod(thisenv, CallRFFIHelperClass, logObject_MethodID, charsxp);
+	    (*thisenv)->CallVoidMethod(thisenv, UpCallsRFFIObject, logNotCharSXPWrapperMethodID, charsxp);
 	    fatalError("only CharSXPWrapper expected in stringFromCharSXP");
 	}
 #endif
@@ -254,27 +254,27 @@ static jstring stringFromCharSXP(JNIEnv *thisenv, SEXP charsxp) {
 SEXP Rf_ScalarInteger(int value) {
 	TRACE(TARGp, value);
 	JNIEnv *thisenv = getEnv();
-	SEXP result = (*thisenv)->CallStaticObjectMethod(thisenv, CallRFFIHelperClass, Rf_ScalarIntegerMethodID, value);
+	SEXP result = (*thisenv)->CallObjectMethod(thisenv, UpCallsRFFIObject, Rf_ScalarIntegerMethodID, value);
     return checkRef(thisenv, result);
 }
 
 SEXP Rf_ScalarReal(double value) {
 	JNIEnv *thisenv = getEnv();
-	SEXP result = (*thisenv)->CallStaticObjectMethod(thisenv, CallRFFIHelperClass, Rf_ScalarDoubleMethodID, value);
+	SEXP result = (*thisenv)->CallObjectMethod(thisenv, UpCallsRFFIObject, Rf_ScalarDoubleMethodID, value);
     return checkRef(thisenv, result);
 }
 
 SEXP Rf_ScalarString(SEXP value) {
 	TRACE(TARGp, value);
 	JNIEnv *thisenv = getEnv();
-	SEXP result = (*thisenv)->CallStaticObjectMethod(thisenv, CallRFFIHelperClass, Rf_ScalarStringMethodID, value);
+	SEXP result = (*thisenv)->CallObjectMethod(thisenv, UpCallsRFFIObject, Rf_ScalarStringMethodID, value);
     return checkRef(thisenv, result);
 }
 
 SEXP Rf_ScalarLogical(int value) {
 	TRACE(TARGp, value);
 	JNIEnv *thisenv = getEnv();
-	SEXP result = (*thisenv)->CallStaticObjectMethod(thisenv, CallRFFIHelperClass, Rf_ScalarLogicalMethodID, value);
+	SEXP result = (*thisenv)->CallObjectMethod(thisenv, UpCallsRFFIObject, Rf_ScalarLogicalMethodID, value);
     return checkRef(thisenv, result);
 }
 
@@ -285,14 +285,14 @@ SEXP Rf_allocVector3(SEXPTYPE t, R_xlen_t len, R_allocator_t* allocator) {
     }
     TRACE(TARGpd, t, len);
     JNIEnv *thisenv = getEnv();
-    SEXP result = (*thisenv)->CallStaticObjectMethod(thisenv, CallRFFIHelperClass, Rf_allocateVectorMethodID, t, len);
+    SEXP result = (*thisenv)->CallObjectMethod(thisenv, UpCallsRFFIObject, Rf_allocateVectorMethodID, t, len);
     return checkRef(thisenv, result);
 }
 
 SEXP Rf_allocArray(SEXPTYPE t, SEXP dims) {
 	TRACE(TARGppd, t, dims);
 	JNIEnv *thisenv = getEnv();
-	SEXP result = (*thisenv)->CallStaticObjectMethod(thisenv, CallRFFIHelperClass, Rf_allocateArrayMethodID, t, dims);
+	SEXP result = (*thisenv)->CallObjectMethod(thisenv, UpCallsRFFIObject, Rf_allocateArrayMethodID, t, dims);
 	return checkRef(thisenv, result);
 }
 
@@ -303,7 +303,7 @@ SEXP Rf_alloc3DArray(SEXPTYPE t, int x, int y, int z) {
 SEXP Rf_allocMatrix(SEXPTYPE mode, int nrow, int ncol) {
 	TRACE(TARGppd, mode, nrow, ncol);
 	JNIEnv *thisenv = getEnv();
-	SEXP result = (*thisenv)->CallStaticObjectMethod(thisenv, CallRFFIHelperClass, Rf_allocateMatrixMethodID, mode, nrow, ncol);
+	SEXP result = (*thisenv)->CallObjectMethod(thisenv, UpCallsRFFIObject, Rf_allocateMatrixMethodID, mode, nrow, ncol);
 	return checkRef(thisenv, result);
 }
 
@@ -319,14 +319,14 @@ SEXP Rf_allocSExp(SEXPTYPE t) {
 SEXP Rf_cons(SEXP car, SEXP cdr) {
 	TRACE(TARGpp, car, cdr);
 	JNIEnv *thisenv = getEnv();
-	SEXP result = (*thisenv)->CallStaticObjectMethod(thisenv, CallRFFIHelperClass, Rf_consMethodID, car, cdr);
+	SEXP result = (*thisenv)->CallObjectMethod(thisenv, UpCallsRFFIObject, Rf_consMethodID, car, cdr);
     return checkRef(thisenv, result);
 }
 
 void Rf_defineVar(SEXP symbol, SEXP value, SEXP rho) {
 	TRACE(TARGppp, symbol, value, rho);
 	JNIEnv *thisenv = getEnv();
-	(*thisenv)->CallStaticObjectMethod(thisenv, CallRFFIHelperClass, Rf_defineVarMethodID, symbol, value, rho);
+	(*thisenv)->CallObjectMethod(thisenv, UpCallsRFFIObject, Rf_defineVarMethodID, symbol, value, rho);
 }
 
 void Rf_setVar(SEXP x, SEXP y, SEXP z) {
@@ -345,63 +345,63 @@ SEXP Rf_eval(SEXP expr, SEXP env) {
 	TRACE(TARGpp, expr, env);
     JNIEnv *thisenv = getEnv();
     updateNativeArrays(thisenv);
-    SEXP result = (*thisenv)->CallStaticObjectMethod(thisenv, CallRFFIHelperClass, Rf_evalMethodID, expr, env);
+    SEXP result = (*thisenv)->CallObjectMethod(thisenv, UpCallsRFFIObject, Rf_evalMethodID, expr, env);
     return checkRef(thisenv, result);
 }
 
 SEXP Rf_findFun(SEXP symbol, SEXP rho) {
 	TRACE(TARGpp, symbol, rho);
 	JNIEnv *thisenv = getEnv();
-	SEXP result = (*thisenv)->CallStaticObjectMethod(thisenv, CallRFFIHelperClass, Rf_findfunMethodID, symbol, rho);
+	SEXP result = (*thisenv)->CallObjectMethod(thisenv, UpCallsRFFIObject, Rf_findfunMethodID, symbol, rho);
 	return checkRef(thisenv, result);
 }
 
 SEXP Rf_findVar(SEXP sym, SEXP rho) {
 	TRACE(TARGpp, sym, rho);
 	JNIEnv *thisenv = getEnv();
-	SEXP result =(*thisenv)->CallStaticObjectMethod(thisenv, CallRFFIHelperClass, Rf_findVarMethodID, sym, rho);
+	SEXP result =(*thisenv)->CallObjectMethod(thisenv, UpCallsRFFIObject, Rf_findVarMethodID, sym, rho);
     return checkRef(thisenv, result);
 }
 
 SEXP Rf_findVarInFrame(SEXP rho, SEXP sym) {
 	TRACE(TARGpp, rho, sym);
 	JNIEnv *thisenv = getEnv();
-	SEXP result =(*thisenv)->CallStaticObjectMethod(thisenv, CallRFFIHelperClass, Rf_findVarInFrameMethodID, rho, sym);
+	SEXP result =(*thisenv)->CallObjectMethod(thisenv, UpCallsRFFIObject, Rf_findVarInFrameMethodID, rho, sym);
     return checkRef(thisenv, result);
 }
 
 SEXP Rf_findVarInFrame3(SEXP rho, SEXP sym, Rboolean b) {
 	TRACE(TARGppd, rho, sym, b);
 	JNIEnv *thisenv = getEnv();
-	SEXP result = (*thisenv)->CallStaticObjectMethod(thisenv, CallRFFIHelperClass, Rf_findVarInFrame3MethodID, rho, sym, b);
+	SEXP result = (*thisenv)->CallObjectMethod(thisenv, UpCallsRFFIObject, Rf_findVarInFrame3MethodID, rho, sym, b);
     return checkRef(thisenv, result);
 }
 
 SEXP Rf_getAttrib(SEXP vec, SEXP name) {
 	TRACE(TARGpp, vec, name);
 	JNIEnv *thisenv = getEnv();
-	SEXP result = (*thisenv)->CallStaticObjectMethod(thisenv, CallRFFIHelperClass, Rf_getAttribMethodID, vec, name);
+	SEXP result = (*thisenv)->CallObjectMethod(thisenv, UpCallsRFFIObject, Rf_getAttribMethodID, vec, name);
 	return checkRef(thisenv, result);
 }
 
 SEXP Rf_setAttrib(SEXP vec, SEXP name, SEXP val) {
 	TRACE(TARGppp, vec,name, val);
 	JNIEnv *thisenv = getEnv();
-	(*thisenv)->CallStaticVoidMethod(thisenv, CallRFFIHelperClass, Rf_setAttribMethodID, vec, name, val);
+	(*thisenv)->CallVoidMethod(thisenv, UpCallsRFFIObject, Rf_setAttribMethodID, vec, name, val);
 	return val;
 }
 
 SEXP Rf_duplicate(SEXP x) {
 	TRACE(TARGp, x);
 	JNIEnv *thisenv = getEnv();
-	SEXP result = (*thisenv)->CallStaticObjectMethod(thisenv, CallRFFIHelperClass, Rf_duplicateMethodID, x, 1);
+	SEXP result = (*thisenv)->CallObjectMethod(thisenv, UpCallsRFFIObject, Rf_duplicateMethodID, x, 1);
 	return checkRef(thisenv, result);
 }
 
 SEXP Rf_shallow_duplicate(SEXP x) {
 	TRACE(TARGp, x);
 	JNIEnv *thisenv = getEnv();
-	SEXP result = (*thisenv)->CallStaticObjectMethod(thisenv, CallRFFIHelperClass, Rf_duplicateMethodID, x, 0);
+	SEXP result = (*thisenv)->CallObjectMethod(thisenv, UpCallsRFFIObject, Rf_duplicateMethodID, x, 0);
 	return checkRef(thisenv, result);
 }
 
@@ -409,7 +409,7 @@ R_xlen_t Rf_any_duplicated(SEXP x, Rboolean from_last) {
 	TRACE(TARGpd, x, from_last);
     if (!isVector(x)) error(_("'duplicated' applies only to vectors"));
 	JNIEnv *thisenv = getEnv();
-    return (*thisenv)->CallStaticIntMethod(thisenv, CallRFFIHelperClass, Rf_anyDuplicatedMethodID, x, from_last);
+    return (*thisenv)->CallIntMethod(thisenv, UpCallsRFFIObject, Rf_anyDuplicatedMethodID, x, from_last);
 }
 
 SEXP Rf_duplicated(SEXP x, Rboolean y) {
@@ -437,7 +437,7 @@ Rboolean Rf_inherits(SEXP x, const char * klass) {
 	TRACE(TARGps, x, klass);
     JNIEnv *thisenv = getEnv();
     jstring klazz = (*thisenv)->NewStringUTF(thisenv, klass);
-    return (*thisenv)->CallStaticIntMethod(thisenv, CallRFFIHelperClass, Rf_inheritsMethodID, x, klazz);
+    return (*thisenv)->CallIntMethod(thisenv, UpCallsRFFIObject, Rf_inheritsMethodID, x, klazz);
 }
 
 Rboolean Rf_isReal(SEXP x) {
@@ -477,7 +477,7 @@ SEXP Rf_install(const char *name) {
 	TRACE(TARGs, name);
 	JNIEnv *thisenv = getEnv();
 	jstring string = (*thisenv)->NewStringUTF(thisenv, name);
-	SEXP result = (*thisenv)->CallStaticObjectMethod(thisenv, CallRFFIHelperClass, Rf_installMethodID, string);
+	SEXP result = (*thisenv)->CallObjectMethod(thisenv, UpCallsRFFIObject, Rf_installMethodID, string);
 	return checkRef(thisenv, result);
 }
 
@@ -485,18 +485,18 @@ SEXP Rf_installChar(SEXP charsxp) {
 	TRACE(TARGp, charsxp);
 	JNIEnv *thisenv = getEnv();
 	jstring string = stringFromCharSXP(thisenv, charsxp);
-	SEXP result = (*thisenv)->CallStaticObjectMethod(thisenv, CallRFFIHelperClass, Rf_installMethodID, string);
+	SEXP result = (*thisenv)->CallObjectMethod(thisenv, UpCallsRFFIObject, Rf_installMethodID, string);
 	return checkRef(thisenv, result);
 }
 
 Rboolean Rf_isNull(SEXP s) {
 	JNIEnv *thisenv = getEnv();
-	return (*thisenv)->CallStaticIntMethod(thisenv, CallRFFIHelperClass, Rf_isNullMethodID, s);
+	return (*thisenv)->CallIntMethod(thisenv, UpCallsRFFIObject, Rf_isNullMethodID, s);
 }
 
 Rboolean Rf_isString(SEXP s) {
 	JNIEnv *thisenv = getEnv();
-	return (*thisenv)->CallStaticIntMethod(thisenv, CallRFFIHelperClass, Rf_isStringMethodID, s);
+	return (*thisenv)->CallIntMethod(thisenv, UpCallsRFFIObject, Rf_isStringMethodID, s);
 }
 
 Rboolean R_cycle_detected(SEXP s, SEXP child) {
@@ -527,7 +527,7 @@ SEXP Rf_mkCharLenCE(const char *x, int len, cetype_t enc) {
 	JNIEnv *thisenv = getEnv();
 	jbyteArray bytes = (*thisenv)->NewByteArray(thisenv, len);
 	(*thisenv)->SetByteArrayRegion(thisenv, bytes, 0, len, (const jbyte *) x);
-	SEXP result = (*thisenv)->CallStaticObjectMethod(thisenv, CallRFFIHelperClass, Rf_mkCharLenCEMethodID, bytes, (int) enc);
+	SEXP result = (*thisenv)->CallObjectMethod(thisenv, UpCallsRFFIObject, Rf_mkCharLenCEMethodID, bytes, (int) enc);
 	return checkRef(thisenv, result);
 }
 
@@ -543,13 +543,13 @@ SEXP Rf_mkString(const char *s) {
 int Rf_ncols(SEXP x) {
 	TRACE(TARGs, x);
 	JNIEnv *thisenv = getEnv();
-	return (*thisenv)->CallStaticIntMethod(thisenv, CallRFFIHelperClass, Rf_ncolsMethodID, x);
+	return (*thisenv)->CallIntMethod(thisenv, UpCallsRFFIObject, Rf_ncolsMethodID, x);
 }
 
 int Rf_nrows(SEXP x) {
 	TRACE(TARGs, x);
 	JNIEnv *thisenv = getEnv();
-	return (*thisenv)->CallStaticIntMethod(thisenv, CallRFFIHelperClass, Rf_nrowsMethodID, x);
+	return (*thisenv)->CallIntMethod(thisenv, UpCallsRFFIObject, Rf_nrowsMethodID, x);
 }
 
 
@@ -591,7 +591,7 @@ void Rf_error(const char *format, ...) {
 	// RError.error does quite a lot of stuff including potentially searching for R condition handlers
 	// and, if it finds any, does not return, but throws a different exception than RError.
 	// We definitely need to exit the FFI call and we certainly cannot return to our caller.
-	// So we call CallRFFIHelper.Rf_error to throw the RError exception. When the pending
+	// So we call RFFIUpCallsObject.Rf_error to throw the RError exception. When the pending
 	// exception (whatever it is) is observed by JNI, the call to Rf_error will return where we do a
 	// non-local transfer of control back to the entry point (which will cleanup).
 	char buf[8192];
@@ -602,7 +602,7 @@ void Rf_error(const char *format, ...) {
 	JNIEnv *thisenv = getEnv();
 	jstring string = (*thisenv)->NewStringUTF(thisenv, buf);
 	// This will set a pending exception
-	(*thisenv)->CallStaticObjectMethod(thisenv, CallRFFIHelperClass, Rf_errorMethodID, string);
+	(*thisenv)->CallObjectMethod(thisenv, UpCallsRFFIObject, Rf_errorMethodID, string);
 	// just transfer back which will cleanup and exit the entire JNI call
 	longjmp(*getErrorJmpBuf(), 1);
 
@@ -620,7 +620,7 @@ void Rf_warningcall(SEXP x, const char *format, ...) {
 	va_end(ap);
 	JNIEnv *thisenv = getEnv();
 	jstring string = (*thisenv)->NewStringUTF(thisenv, buf);
-	(*thisenv)->CallStaticObjectMethod(thisenv, CallRFFIHelperClass, Rf_warningcallMethodID, x, string);
+	(*thisenv)->CallObjectMethod(thisenv, UpCallsRFFIObject, Rf_warningcallMethodID, x, string);
 }
 
 void Rf_warning(const char *format, ...) {
@@ -631,7 +631,7 @@ void Rf_warning(const char *format, ...) {
 	va_end(ap);
 	JNIEnv *thisenv = getEnv();
 	jstring string = (*thisenv)->NewStringUTF(thisenv, buf);
-	(*thisenv)->CallStaticObjectMethod(thisenv, CallRFFIHelperClass, Rf_warningMethodID, string);
+	(*thisenv)->CallObjectMethod(thisenv, UpCallsRFFIObject, Rf_warningMethodID, string);
 }
 
 void Rprintf(const char *format, ...) {
@@ -642,7 +642,7 @@ void Rprintf(const char *format, ...) {
 	va_end(ap);
 	JNIEnv *thisenv = getEnv();
 	jstring string = (*thisenv)->NewStringUTF(thisenv, buf);
-	(*thisenv)->CallStaticObjectMethod(thisenv, CallRFFIHelperClass, RprintfMethodID, string);
+	(*thisenv)->CallObjectMethod(thisenv, UpCallsRFFIObject, RprintfMethodID, string);
 }
 
 /*
@@ -660,7 +660,7 @@ void REprintf(const char *format, ...)
 	va_end(ap);
 	JNIEnv *thisenv = getEnv();
 	jstring string = (*thisenv)->NewStringUTF(thisenv, buf);
-	(*thisenv)->CallStaticObjectMethod(thisenv, CallRFFIHelperClass, RprintfMethodID, string);
+	(*thisenv)->CallObjectMethod(thisenv, UpCallsRFFIObject, RprintfMethodID, string);
 }
 
 void Rvprintf(const char *format, va_list args) {
@@ -681,13 +681,13 @@ void R_ProcessEvents(void) {
 // Tools package support, not in public API
 SEXP R_NewHashedEnv(SEXP parent, SEXP size) {
 	JNIEnv *thisenv = getEnv();
-	SEXP result = (*thisenv)->CallStaticObjectMethod(thisenv, CallRFFIHelperClass, R_NewHashedEnvMethodID, parent, size);
+	SEXP result = (*thisenv)->CallObjectMethod(thisenv, UpCallsRFFIObject, R_NewHashedEnvMethodID, parent, size);
 	return checkRef(thisenv, result);
 }
 
 SEXP Rf_classgets(SEXP vec, SEXP klass) {
 	JNIEnv *thisenv = getEnv();
-	SEXP result = (*thisenv)->CallStaticObjectMethod(thisenv, CallRFFIHelperClass, Rf_classgetsMethodID, vec, klass);
+	SEXP result = (*thisenv)->CallObjectMethod(thisenv, UpCallsRFFIObject, Rf_classgetsMethodID, vec, klass);
 	return checkRef(thisenv, result);
 }
 
@@ -716,7 +716,7 @@ SEXP Rf_lengthgets(SEXP x, R_len_t y) {
 	TRACE(TARGp, x);
 	JNIEnv *thisenv = getEnv();
 	invalidateNativeArray(thisenv, x);
-	SEXP result = (*thisenv)->CallStaticObjectMethod(thisenv, CallRFFIHelperClass, Rf_lengthgetsMethodID, x, y);
+	SEXP result = (*thisenv)->CallObjectMethod(thisenv, UpCallsRFFIObject, Rf_lengthgetsMethodID, x, y);
 	return checkRef(thisenv, result);
 }
 
@@ -731,7 +731,7 @@ SEXP R_lsInternal(SEXP env, Rboolean all) {
 
 SEXP R_lsInternal3(SEXP env, Rboolean all, Rboolean sorted) {
 	JNIEnv *thisenv = getEnv();
-	SEXP result = (*thisenv)->CallStaticObjectMethod(thisenv, CallRFFIHelperClass, R_lsInternal3MethodID, env, all, sorted);
+	SEXP result = (*thisenv)->CallObjectMethod(thisenv, UpCallsRFFIObject, R_lsInternal3MethodID, env, all, sorted);
 	return checkRef(thisenv, result);
 }
 
@@ -742,7 +742,7 @@ SEXP Rf_namesgets(SEXP x, SEXP y) {
 SEXP GetOption1(SEXP tag)
 {
 	JNIEnv *thisenv = getEnv();
-	SEXP result = (*thisenv)->CallStaticObjectMethod(thisenv, CallRFFIHelperClass, Rf_GetOption1MethodID, tag);
+	SEXP result = (*thisenv)->CallObjectMethod(thisenv, UpCallsRFFIObject, Rf_GetOption1MethodID, tag);
 	return checkRef(thisenv, result);
 }
 
@@ -803,34 +803,34 @@ Rboolean Rf_GetOptionDeviceAsk(void)
 void Rf_gsetVar(SEXP symbol, SEXP value, SEXP rho)
 {
 	JNIEnv *thisenv = getEnv();
-	(*thisenv)->CallStaticObjectMethod(thisenv, CallRFFIHelperClass, Rf_gsetVarMethodID, symbol, value, rho);
+	(*thisenv)->CallObjectMethod(thisenv, UpCallsRFFIObject, Rf_gsetVarMethodID, symbol, value, rho);
 }
 
 SEXP TAG(SEXP e) {
     TRACE(TARGp, e);
     JNIEnv *thisenv = getEnv();
-    SEXP result = (*thisenv)->CallStaticObjectMethod(thisenv, CallRFFIHelperClass, TAG_MethodID, e);
+    SEXP result = (*thisenv)->CallObjectMethod(thisenv, UpCallsRFFIObject, TAG_MethodID, e);
     return checkRef(thisenv, result);
 }
 
 SEXP PRINTNAME(SEXP e) {
     TRACE(TARGp, e);
     JNIEnv *thisenv = getEnv();
-    SEXP result = (*thisenv)->CallStaticObjectMethod(thisenv, CallRFFIHelperClass, PRINTNAME_MethodID, e);
+    SEXP result = (*thisenv)->CallObjectMethod(thisenv, UpCallsRFFIObject, PRINTNAME_MethodID, e);
     return checkRef(thisenv, result);
 }
 
 SEXP CAR(SEXP e) {
     TRACE(TARGp, e);
     JNIEnv *thisenv = getEnv();
-    SEXP result = (*thisenv)->CallStaticObjectMethod(thisenv, CallRFFIHelperClass, CAR_MethodID, e);
+    SEXP result = (*thisenv)->CallObjectMethod(thisenv, UpCallsRFFIObject, CAR_MethodID, e);
     return checkRef(thisenv, result);
 }
 
 SEXP CDR(SEXP e) {
     TRACE(TARGp, e);
     JNIEnv *thisenv = getEnv();
-    SEXP result = (*thisenv)->CallStaticObjectMethod(thisenv, CallRFFIHelperClass, CDR_MethodID, e);
+    SEXP result = (*thisenv)->CallObjectMethod(thisenv, UpCallsRFFIObject, CDR_MethodID, e);
     return checkRef(thisenv, result);
 }
 
@@ -847,14 +847,14 @@ SEXP CDAR(SEXP e) {
 SEXP CADR(SEXP e) {
     TRACE(TARGp, e);
     JNIEnv *thisenv = getEnv();
-    SEXP result = (*thisenv)->CallStaticObjectMethod(thisenv, CallRFFIHelperClass, CADR_MethodID, e);
+    SEXP result = (*thisenv)->CallObjectMethod(thisenv, UpCallsRFFIObject, CADR_MethodID, e);
     return checkRef(thisenv, result);
 }
 
 SEXP CDDR(SEXP e) {
     TRACE(TARGp, e);
     JNIEnv *thisenv = getEnv();
-    SEXP result = (*thisenv)->CallStaticObjectMethod(thisenv, CallRFFIHelperClass, CDDR_MethodID, e);
+    SEXP result = (*thisenv)->CallObjectMethod(thisenv, UpCallsRFFIObject, CDDR_MethodID, e);
     return checkRef(thisenv, result);
 }
 
@@ -866,7 +866,7 @@ SEXP CDDDR(SEXP e) {
 SEXP CADDR(SEXP e) {
     TRACE(TARGp, e);
     JNIEnv *thisenv = getEnv();
-    SEXP result = (*thisenv)->CallStaticObjectMethod(thisenv, CallRFFIHelperClass, CADDR_MethodID, e);
+    SEXP result = (*thisenv)->CallObjectMethod(thisenv, UpCallsRFFIObject, CADDR_MethodID, e);
     return checkRef(thisenv, result);
 }
 
@@ -892,27 +892,27 @@ void SET_MISSING(SEXP x, int v) {
 void SET_TAG(SEXP x, SEXP y) {
     TRACE(TARGpp, x, y);
     JNIEnv *thisenv = getEnv();
-    (*thisenv)->CallStaticObjectMethod(thisenv, CallRFFIHelperClass, SET_TAG_MethodID, x, y);
+    (*thisenv)->CallObjectMethod(thisenv, UpCallsRFFIObject, SET_TAG_MethodID, x, y);
 }
 
 SEXP SETCAR(SEXP x, SEXP y) {
     TRACE(TARGpp, x, y);
     JNIEnv *thisenv = getEnv();
-    SEXP result = (*thisenv)->CallStaticObjectMethod(thisenv, CallRFFIHelperClass, SETCAR_MethodID, x, y);
+    SEXP result = (*thisenv)->CallObjectMethod(thisenv, UpCallsRFFIObject, SETCAR_MethodID, x, y);
     return checkRef(thisenv, result);
 }
 
 SEXP SETCDR(SEXP x, SEXP y) {
     TRACE(TARGpp, x, y);
     JNIEnv *thisenv = getEnv();
-    SEXP result = (*thisenv)->CallStaticObjectMethod(thisenv, CallRFFIHelperClass, SETCDR_MethodID, x, y);
+    SEXP result = (*thisenv)->CallObjectMethod(thisenv, UpCallsRFFIObject, SETCDR_MethodID, x, y);
     return checkRef(thisenv, result);
 }
 
 SEXP SETCADR(SEXP x, SEXP y) {
     TRACE(TARGpp, x, y);
     JNIEnv *thisenv = getEnv();
-    SEXP result = (*thisenv)->CallStaticObjectMethod(thisenv, CallRFFIHelperClass, SETCADR_MethodID, x, y);
+    SEXP result = (*thisenv)->CallObjectMethod(thisenv, UpCallsRFFIObject, SETCADR_MethodID, x, y);
     return checkRef(thisenv, result);
 }
 
@@ -945,12 +945,12 @@ SEXP CLOENV(SEXP x) {
 
 int RDEBUG(SEXP x) {
     JNIEnv *thisenv = getEnv();
-    return (*thisenv)->CallStaticIntMethod(thisenv, CallRFFIHelperClass, RDEBUGMethodID, x);
+    return (*thisenv)->CallIntMethod(thisenv, UpCallsRFFIObject, RDEBUGMethodID, x);
 }
 
 int RSTEP(SEXP x) {
     JNIEnv *thisenv = getEnv();
-    return (*thisenv)->CallStaticIntMethod(thisenv, CallRFFIHelperClass, RSTEPMethodID, x);
+    return (*thisenv)->CallIntMethod(thisenv, UpCallsRFFIObject, RSTEPMethodID, x);
 }
 
 int RTRACE(SEXP x) {
@@ -960,12 +960,12 @@ int RTRACE(SEXP x) {
 
 void SET_RDEBUG(SEXP x, int v) {
     JNIEnv *thisenv = getEnv();
-    (*thisenv)->CallStaticVoidMethod(thisenv, CallRFFIHelperClass, SET_RDEBUGMethodID, x, v);
+    (*thisenv)->CallVoidMethod(thisenv, UpCallsRFFIObject, SET_RDEBUGMethodID, x, v);
 }
 
 void SET_RSTEP(SEXP x, int v) {
     JNIEnv *thisenv = getEnv();
-    (*thisenv)->CallStaticVoidMethod(thisenv, CallRFFIHelperClass, SET_RSTEPMethodID, x, v);
+    (*thisenv)->CallVoidMethod(thisenv, UpCallsRFFIObject, SET_RSTEPMethodID, x, v);
 }
 
 void SET_RTRACE(SEXP x, int v) {
@@ -986,7 +986,7 @@ void SET_CLOENV(SEXP x, SEXP v) {
 
 SEXP SYMVALUE(SEXP x) {
     JNIEnv *thisenv = getEnv();
-    SEXP result = (*thisenv)->CallStaticObjectMethod(thisenv, CallRFFIHelperClass, SYMVALUE_MethodID, x);
+    SEXP result = (*thisenv)->CallObjectMethod(thisenv, UpCallsRFFIObject, SYMVALUE_MethodID, x);
     return checkRef(thisenv, result);
 }
 
@@ -1005,7 +1005,7 @@ void SET_DDVAL(SEXP x, int v) {
 
 void SET_SYMVALUE(SEXP x, SEXP v) {
 	JNIEnv *thisenv = getEnv();
-	(*thisenv)->CallStaticVoidMethod(thisenv, CallRFFIHelperClass, SET_SYMVALUE_MethodID, x, v);
+	(*thisenv)->CallVoidMethod(thisenv, UpCallsRFFIObject, SET_SYMVALUE_MethodID, x, v);
 }
 
 void SET_INTERNAL(SEXP x, SEXP v) {
@@ -1018,7 +1018,7 @@ SEXP FRAME(SEXP x) {
 
 SEXP ENCLOS(SEXP x) {
 	JNIEnv *thisenv = getEnv();
-	SEXP result = (*thisenv)->CallStaticObjectMethod(thisenv, CallRFFIHelperClass, ENCLOSMethodID, x);
+	SEXP result = (*thisenv)->CallObjectMethod(thisenv, UpCallsRFFIObject, ENCLOSMethodID, x);
     return checkRef(thisenv, result);
 }
 
@@ -1059,7 +1059,7 @@ SEXP PRENV(SEXP x) {
 
 SEXP PRVALUE(SEXP x) {
     JNIEnv *thisenv = getEnv();
-    SEXP result = (*thisenv)->CallStaticObjectMethod(thisenv, CallRFFIHelperClass, PRVALUEMethodID, x);
+    SEXP result = (*thisenv)->CallObjectMethod(thisenv, UpCallsRFFIObject, PRVALUEMethodID, x);
     return checkRef(thisenv, result);
 }
 
@@ -1086,7 +1086,7 @@ void SET_PRCODE(SEXP x, SEXP v) {
 int LENGTH(SEXP x) {
     TRACE(TARGp, x);
     JNIEnv *thisenv = getEnv();
-    return (*thisenv)->CallStaticIntMethod(thisenv, CallRFFIHelperClass, LENGTH_MethodID, x);
+    return (*thisenv)->CallIntMethod(thisenv, UpCallsRFFIObject, LENGTH_MethodID, x);
 }
 
 int TRUELENGTH(SEXP x){
@@ -1172,7 +1172,7 @@ Rcomplex *COMPLEX(SEXP x){
 SEXP STRING_ELT(SEXP x, R_xlen_t i){
 	TRACE(TARGpd, x, i);
 	JNIEnv *thisenv = getEnv();
-    SEXP result = (*thisenv)->CallStaticObjectMethod(thisenv, CallRFFIHelperClass, STRING_ELT_MethodID, x, i);
+    SEXP result = (*thisenv)->CallObjectMethod(thisenv, UpCallsRFFIObject, STRING_ELT_MethodID, x, i);
     return checkRef(thisenv, result);
 }
 
@@ -1180,21 +1180,21 @@ SEXP STRING_ELT(SEXP x, R_xlen_t i){
 SEXP VECTOR_ELT(SEXP x, R_xlen_t i){
 	TRACE(TARGpd, x, i);
 	JNIEnv *thisenv = getEnv();
-    SEXP result = (*thisenv)->CallStaticObjectMethod(thisenv, CallRFFIHelperClass, VECTOR_ELT_MethodID, x, i);
+    SEXP result = (*thisenv)->CallObjectMethod(thisenv, UpCallsRFFIObject, VECTOR_ELT_MethodID, x, i);
     return checkRef(thisenv, result);
 }
 
 void SET_STRING_ELT(SEXP x, R_xlen_t i, SEXP v){
 	TRACE("%s(%p, %d, %p)\n", x, i, v);
 	JNIEnv *thisenv = getEnv();
-	(*thisenv)->CallStaticVoidMethod(thisenv, CallRFFIHelperClass, SET_STRING_ELT_MethodID, x, i, v);
+	(*thisenv)->CallVoidMethod(thisenv, UpCallsRFFIObject, SET_STRING_ELT_MethodID, x, i, v);
 }
 
 
 SEXP SET_VECTOR_ELT(SEXP x, R_xlen_t i, SEXP v){
 	TRACE("%s(%p, %d, %p)\n", x, i, v);
 	JNIEnv *thisenv = getEnv();
-	(*thisenv)->CallStaticVoidMethod(thisenv, CallRFFIHelperClass, SET_VECTOR_ELT_MethodID, x, i, v);
+	(*thisenv)->CallVoidMethod(thisenv, UpCallsRFFIObject, SET_VECTOR_ELT_MethodID, x, i, v);
 	return v;
 }
 
@@ -1213,14 +1213,14 @@ SEXP *VECTOR_PTR(SEXP x){
 SEXP Rf_asChar(SEXP x){
 	TRACE(TARGp, x);
 	JNIEnv *thisenv = getEnv();
-	SEXP result = (*thisenv)->CallStaticObjectMethod(thisenv, CallRFFIHelperClass, Rf_asCharMethodID, x);
+	SEXP result = (*thisenv)->CallObjectMethod(thisenv, UpCallsRFFIObject, Rf_asCharMethodID, x);
 	return checkRef(thisenv, result);
 }
 
 SEXP Rf_PairToVectorList(SEXP x){
 	TRACE(TARGp, x);
 	JNIEnv *thisenv = getEnv();
-	SEXP result = (*thisenv)->CallStaticObjectMethod(thisenv, CallRFFIHelperClass, Rf_PairToVectorListMethodID, x);
+	SEXP result = (*thisenv)->CallObjectMethod(thisenv, UpCallsRFFIObject, Rf_PairToVectorListMethodID, x);
 	return checkRef(thisenv, result);
 }
 
@@ -1237,19 +1237,19 @@ SEXP Rf_asCharacterFactor(SEXP x){
 int Rf_asLogical(SEXP x){
 	TRACE(TARGp, x);
 	JNIEnv *thisenv = getEnv();
-	return (*thisenv)->CallStaticIntMethod(thisenv, CallRFFIHelperClass, Rf_asLogicalMethodID, x);
+	return (*thisenv)->CallIntMethod(thisenv, UpCallsRFFIObject, Rf_asLogicalMethodID, x);
 }
 
 int Rf_asInteger(SEXP x) {
 	TRACE(TARGp, x);
 	JNIEnv *thisenv = getEnv();
-	return (*thisenv)->CallStaticIntMethod(thisenv, CallRFFIHelperClass, Rf_asIntegerMethodID, x);
+	return (*thisenv)->CallIntMethod(thisenv, UpCallsRFFIObject, Rf_asIntegerMethodID, x);
 }
 
 //double Rf_asReal(SEXP x) {
 //	TRACE(TARGp, x);
 //	JNIEnv *thisenv = getEnv();
-//	return (*thisenv)->CallStaticDoubleMethod(thisenv, CallRFFIHelperClass, Rf_asRealMethodID, x);
+//	return (*thisenv)->CallDoubleMethod(thisenv, UpCallsRFFIObject, Rf_asRealMethodID, x);
 //}
 
 Rcomplex Rf_asComplex(SEXP x){
@@ -1260,7 +1260,7 @@ Rcomplex Rf_asComplex(SEXP x){
 int TYPEOF(SEXP x) {
 	TRACE(TARGp, x);
 	JNIEnv *thisenv = getEnv();
-	return (*thisenv)->CallStaticIntMethod(thisenv, CallRFFIHelperClass, TYPEOF_MethodID, x);
+	return (*thisenv)->CallIntMethod(thisenv, UpCallsRFFIObject, TYPEOF_MethodID, x);
 }
 
 SEXP ATTRIB(SEXP x){
@@ -1270,7 +1270,7 @@ SEXP ATTRIB(SEXP x){
 
 int OBJECT(SEXP x){
 	JNIEnv *env = getEnv();
-	return 	(*env)->CallStaticIntMethod(env, CallRFFIHelperClass, OBJECT_MethodID, x);
+	return 	(*env)->CallIntMethod(env, UpCallsRFFIObject, OBJECT_MethodID, x);
 }
 
 int MARK(SEXP x){
@@ -1280,7 +1280,7 @@ int MARK(SEXP x){
 
 int NAMED(SEXP x){
     JNIEnv *thisenv = getEnv();
-    return (*thisenv)->CallStaticIntMethod(thisenv, CallRFFIHelperClass, NAMED_MethodID, x);
+    return (*thisenv)->CallIntMethod(thisenv, UpCallsRFFIObject, NAMED_MethodID, x);
 }
 
 int REFCNT(SEXP x){
@@ -1298,7 +1298,7 @@ void SET_TYPEOF(SEXP x, int v){
 
 SEXP SET_TYPEOF_FASTR(SEXP x, int v){
     JNIEnv *thisenv = getEnv();
-    SEXP result = (*thisenv)->CallStaticObjectMethod(thisenv, CallRFFIHelperClass, SET_TYPEOF_FASTR_MethodID, x, v);
+    SEXP result = (*thisenv)->CallObjectMethod(thisenv, UpCallsRFFIObject, SET_TYPEOF_FASTR_MethodID, x, v);
     return checkRef(thisenv, result);
 }
 
@@ -1312,7 +1312,7 @@ void SET_ATTRIB(SEXP x, SEXP v){
 
 void DUPLICATE_ATTRIB(SEXP to, SEXP from){
     JNIEnv *thisenv = getEnv();
-    (*thisenv)->CallStaticVoidMethod(thisenv, CallRFFIHelperClass, DUPLICATE_ATTRIB_MethodID, to, from);
+    (*thisenv)->CallVoidMethod(thisenv, UpCallsRFFIObject, DUPLICATE_ATTRIB_MethodID, to, from);
 }
 
 char *dgettext(const char *domainname, const char *msgid) {
@@ -1367,7 +1367,7 @@ R_len_t R_BadLongVector(SEXP x, const char *y, int z) {
 
 int IS_S4_OBJECT(SEXP x) {
 	JNIEnv *env = getEnv();
-	return 	(*env)->CallStaticIntMethod(env, CallRFFIHelperClass, isS4ObjectMethodID, x);
+	return 	(*env)->CallIntMethod(env, UpCallsRFFIObject, isS4ObjectMethodID, x);
 }
 
 void SET_S4_OBJECT(SEXP x) {
@@ -1379,9 +1379,9 @@ void UNSET_S4_OBJECT(SEXP x) {
 
 Rboolean R_ToplevelExec(void (*fun)(void *), void *data) {
 	JNIEnv *env = getEnv();
-	jobject handlerStacks = (*env)->CallStaticObjectMethod(env, CallRFFIHelperClass, resetAndGetHandlerStacksMethodID);
+	jobject handlerStacks = (*env)->CallObjectMethod(env, UpCallsRFFIObject, R_ToplevelExecMethodID);
 	fun(data);
-	(*env)->CallStaticVoidMethod(env, CallRFFIHelperClass, restoreHandlerStacksMethodID, handlerStacks);
+	(*env)->CallVoidMethod(env, UpCallsRFFIObject, restoreHandlerStacksMethodID, handlerStacks);
 	// TODO how do we detect error
 	return TRUE;
 }
@@ -1418,7 +1418,7 @@ SEXP R_NamespaceEnvSpec(SEXP rho) {
 
 SEXP R_FindNamespace(SEXP info) {
 	JNIEnv *thisenv = getEnv();
-	SEXP result = (*thisenv)->CallStaticObjectMethod(thisenv, CallRFFIHelperClass, R_FindNamespaceMethodID, info);
+	SEXP result = (*thisenv)->CallObjectMethod(thisenv, UpCallsRFFIObject, R_FindNamespaceMethodID, info);
 	return checkRef(thisenv, result);
 }
 
@@ -1444,7 +1444,7 @@ void R_MakeActiveBinding(SEXP sym, SEXP fun, SEXP env) {
 
 Rboolean R_BindingIsLocked(SEXP sym, SEXP env) {
 	JNIEnv *thisenv = getEnv();
-	return (*thisenv)->CallStaticIntMethod(thisenv, CallRFFIHelperClass, R_BindingIsLockedID, sym, env);
+	return (*thisenv)->CallIntMethod(thisenv, UpCallsRFFIObject, R_BindingIsLockedID, sym, env);
 }
 
 Rboolean R_BindingIsActive(SEXP sym, SEXP env) {
@@ -1467,7 +1467,7 @@ SEXP Rf_asS4(SEXP x, Rboolean b, int i) {
 static SEXP R_tryEvalInternal(SEXP x, SEXP y, int *ErrorOccurred, jboolean silent) {
 	JNIEnv *thisenv = getEnv();
     updateNativeArrays(thisenv);
-	jobject tryResult =  (*thisenv)->CallStaticObjectMethod(thisenv, CallRFFIHelperClass, R_tryEvalMethodID, x, y, silent);
+	jobject tryResult =  (*thisenv)->CallObjectMethod(thisenv, UpCallsRFFIObject, R_tryEvalMethodID, x, y, silent);
 	// If tryResult is NULL, an error occurred
 	if (ErrorOccurred) {
 		*ErrorOccurred = tryResult == NULL;
@@ -1507,40 +1507,40 @@ SEXP R_forceAndCall(SEXP e, int n, SEXP rho) {
 
 SEXP R_MakeExternalPtr(void *p, SEXP tag, SEXP prot) {
 	JNIEnv *thisenv = getEnv();
-	SEXP result =  (*thisenv)->CallStaticObjectMethod(thisenv, CallRFFIHelperClass, R_MakeExternalPtrMethodID, (jlong) p, tag, prot);
+	SEXP result =  (*thisenv)->CallObjectMethod(thisenv, UpCallsRFFIObject, R_MakeExternalPtrMethodID, (jlong) p, tag, prot);
     return checkRef(thisenv, result);
 }
 
 void *R_ExternalPtrAddr(SEXP s) {
 	JNIEnv *thisenv = getEnv();
-	return (void *) (*thisenv)->CallStaticLongMethod(thisenv, CallRFFIHelperClass, R_ExternalPtrAddrMethodID, s);
+	return (void *) (*thisenv)->CallLongMethod(thisenv, UpCallsRFFIObject, R_ExternalPtrAddrMethodID, s);
 }
 
 SEXP R_ExternalPtrTag(SEXP s) {
 	JNIEnv *thisenv = getEnv();
-	SEXP result =  (*thisenv)->CallStaticObjectMethod(thisenv, CallRFFIHelperClass, R_ExternalPtrTagMethodID, s);
+	SEXP result =  (*thisenv)->CallObjectMethod(thisenv, UpCallsRFFIObject, R_ExternalPtrTagMethodID, s);
     return checkRef(thisenv, result);
 }
 
 SEXP R_ExternalPtrProt(SEXP s) {
 	JNIEnv *thisenv = getEnv();
-	SEXP result =  (*thisenv)->CallStaticObjectMethod(thisenv, CallRFFIHelperClass, R_ExternalPtrProtMethodID, s);
+	SEXP result =  (*thisenv)->CallObjectMethod(thisenv, UpCallsRFFIObject, R_ExternalPtrProtMethodID, s);
     return checkRef(thisenv, result);
 }
 
 void R_SetExternalPtrAddr(SEXP s, void *p) {
 	JNIEnv *thisenv = getEnv();
-	(*thisenv)->CallStaticVoidMethod(thisenv, CallRFFIHelperClass, R_SetExternalPtrAddrMethodID, s, (jlong) p);
+	(*thisenv)->CallVoidMethod(thisenv, UpCallsRFFIObject, R_SetExternalPtrAddrMethodID, s, (jlong) p);
 }
 
 void R_SetExternalPtrTag(SEXP s, SEXP tag) {
 	JNIEnv *thisenv = getEnv();
-	(*thisenv)->CallStaticVoidMethod(thisenv, CallRFFIHelperClass, R_SetExternalPtrTagMethodID, s, tag);
+	(*thisenv)->CallVoidMethod(thisenv, UpCallsRFFIObject, R_SetExternalPtrTagMethodID, s, tag);
 }
 
 void R_SetExternalPtrProtected(SEXP s, SEXP p) {
 	JNIEnv *thisenv = getEnv();
-	(*thisenv)->CallStaticVoidMethod(thisenv, CallRFFIHelperClass, R_SetExternalPtrProtMethodID, s, p);
+	(*thisenv)->CallVoidMethod(thisenv, UpCallsRFFIObject, R_SetExternalPtrProtMethodID, s, p);
 }
 
 void R_ClearExternalPtr(SEXP s) {
@@ -1602,7 +1602,7 @@ int R_has_slot(SEXP obj, SEXP name) {
 SEXP R_do_MAKE_CLASS(const char *what) {
 	JNIEnv *thisenv = getEnv();
 	jstring string = (*thisenv)->NewStringUTF(thisenv, what);
-	return (*thisenv)->CallStaticObjectMethod(thisenv, CallRFFIHelperClass, R_do_MAKE_CLASS_MethodID, string);
+	return (*thisenv)->CallObjectMethod(thisenv, UpCallsRFFIObject, R_do_MAKE_CLASS_MethodID, string);
 }
 
 SEXP R_getClassDef (const char *what) {
@@ -1637,15 +1637,15 @@ void R_dot_Last(void) {
 
 Rboolean R_compute_identical(SEXP x, SEXP y, int flags) {
 	JNIEnv *thisenv = getEnv();
-	return (*thisenv)->CallStaticIntMethod(thisenv, CallRFFIHelperClass, R_computeIdenticalMethodID, x, y, flags);
+	return (*thisenv)->CallIntMethod(thisenv, UpCallsRFFIObject, R_computeIdenticalMethodID, x, y, flags);
 }
 
 void Rf_copyListMatrix(SEXP s, SEXP t, Rboolean byrow) {
 	JNIEnv *thisenv = getEnv();
-    (*thisenv)->CallStaticIntMethod(thisenv, CallRFFIHelperClass, Rf_copyListMatrixMethodID, s, t, byrow);
+    (*thisenv)->CallIntMethod(thisenv, UpCallsRFFIObject, Rf_copyListMatrixMethodID, s, t, byrow);
 }
 
 void Rf_copyMatrix(SEXP s, SEXP t, Rboolean byrow) {
 	JNIEnv *thisenv = getEnv();
-    (*thisenv)->CallStaticIntMethod(thisenv, CallRFFIHelperClass, Rf_copyMatrixMethodID, s, t, byrow);
+    (*thisenv)->CallIntMethod(thisenv, UpCallsRFFIObject, Rf_copyMatrixMethodID, s, t, byrow);
 }
diff --git a/com.oracle.truffle.r.native/fficall/src/jni/call_rffi.c b/com.oracle.truffle.r.native/fficall/src/jni/call_rffi.c
index 7b9a14296c..eb81606672 100644
--- a/com.oracle.truffle.r.native/fficall/src/jni/call_rffi.c
+++ b/com.oracle.truffle.r.native/fficall/src/jni/call_rffi.c
@@ -25,10 +25,11 @@
 #include <string.h>
 #include <setjmp.h>
 
+// The entry point from JNI_Call that initializes the system
 JNIEXPORT void JNICALL
 Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1Call_initialize(JNIEnv *env, jclass c,
-		jobjectArray initialValues) {
-	init_utils(env); // must be first
+		jobject upCallInstance, jobjectArray initialValues) {
+	init_utils(env, upCallInstance); // must be first
 	init_variables(env, initialValues);
 	init_dynload(env);
 	init_internals(env);
diff --git a/com.oracle.truffle.r.native/fficall/src/jni/rffiutils.c b/com.oracle.truffle.r.native/fficall/src/jni/rffiutils.c
index 0010587bcc..69d19f2aec 100644
--- a/com.oracle.truffle.r.native/fficall/src/jni/rffiutils.c
+++ b/com.oracle.truffle.r.native/fficall/src/jni/rffiutils.c
@@ -32,11 +32,10 @@
  * so we are safe to use static variables. TODO Figure out where to store such state
  * (portably) for MT use. JNI provides no help.
  */
-jclass CallRFFIHelperClass;
+jclass UpCallsRFFIClass;
+jobject UpCallsRFFIObject;
 jclass CharSXPWrapperClass;
 
-static jmethodID validateMethodID;
-
 static JNIEnv *curenv = NULL;
 
 // default for trace output when enabled
@@ -87,8 +86,10 @@ static int nativeArrayTableHwmStack[CALLDEPTH_STACK_SIZE];
 static jmp_buf* callErrorJmpBufTable[CALLDEPTH_STACK_SIZE];
 
 
-void init_utils(JNIEnv *env) {
+void init_utils(JNIEnv *env, jobject upCallsInstance) {
 	curenv = env;
+	UpCallsRFFIClass = (*env)->GetObjectClass(env, upCallsInstance);
+	UpCallsRFFIObject = (*env)->NewGlobalRef(env, upCallsInstance);
 	if (TRACE_ENABLED && traceFile == NULL) {
 		if (!isEmbedded) {
 			traceFile = stdout;
@@ -109,8 +110,6 @@ void init_utils(JNIEnv *env) {
 		    setvbuf(traceFile, (char*) NULL, _IONBF, 0);
 		}
 	}
-	CallRFFIHelperClass = checkFindClass(env, "com/oracle/truffle/r/runtime/ffi/jni/CallRFFIHelper");
-    validateMethodID = checkGetMethodID(env, CallRFFIHelperClass, "validate", "(Ljava/lang/Object;)Ljava/lang/Object;", 1);
     cachedGlobalRefs = calloc(CACHED_GLOBALREFS_INITIAL_SIZE, sizeof(GlobalRefElem));
     cachedGlobalRefsLength = CACHED_GLOBALREFS_INITIAL_SIZE;
     cachedGlobalRefsHwm = 0;
@@ -230,7 +229,7 @@ void *getNativeArray(JNIEnv *thisenv, SEXP x, SEXPTYPE type) {
 		jarray jArray;
 		switch (type) {
 		case INTSXP: {
-			jintArray intArray = (*thisenv)->CallStaticObjectMethod(thisenv, CallRFFIHelperClass, INTEGER_MethodID, x);
+			jintArray intArray = (*thisenv)->CallObjectMethod(thisenv, UpCallsRFFIObject, INTEGER_MethodID, x);
 			int len = (*thisenv)->GetArrayLength(thisenv, intArray);
 			data = (*thisenv)->GetIntArrayElements(thisenv, intArray, &isCopy);
 			jArray = intArray;
@@ -238,7 +237,7 @@ void *getNativeArray(JNIEnv *thisenv, SEXP x, SEXPTYPE type) {
 		}
 
 		case REALSXP: {
-			jdoubleArray doubleArray = (*thisenv)->CallStaticObjectMethod(thisenv, CallRFFIHelperClass, REAL_MethodID, x);
+			jdoubleArray doubleArray = (*thisenv)->CallObjectMethod(thisenv, UpCallsRFFIObject, REAL_MethodID, x);
 			int len = (*thisenv)->GetArrayLength(thisenv, doubleArray);
 			data = (*thisenv)->GetDoubleArrayElements(thisenv, doubleArray, &isCopy);
 			jArray = doubleArray;
@@ -246,7 +245,7 @@ void *getNativeArray(JNIEnv *thisenv, SEXP x, SEXPTYPE type) {
 		}
 
 		case RAWSXP: {
-		    jbyteArray byteArray = (*thisenv)->CallStaticObjectMethod(thisenv, CallRFFIHelperClass, RAW_MethodID, x);
+		    jbyteArray byteArray = (*thisenv)->CallObjectMethod(thisenv, UpCallsRFFIObject, RAW_MethodID, x);
 		    int len = (*thisenv)->GetArrayLength(thisenv, byteArray);
 		    data = (*thisenv)->GetByteArrayElements(thisenv, byteArray, &isCopy);
 	        jArray = byteArray;
@@ -255,7 +254,7 @@ void *getNativeArray(JNIEnv *thisenv, SEXP x, SEXPTYPE type) {
 
 		case LGLSXP: {
 			// Special treatment becuase R FFI wants int* and FastR represents using byte[]
-		    jbyteArray byteArray = (*thisenv)->CallStaticObjectMethod(thisenv, CallRFFIHelperClass, LOGICAL_MethodID, x);
+		    jbyteArray byteArray = (*thisenv)->CallObjectMethod(thisenv, UpCallsRFFIObject, LOGICAL_MethodID, x);
 		    int len = (*thisenv)->GetArrayLength(thisenv, byteArray);
 		    jbyte* internalData = (*thisenv)->GetByteArrayElements(thisenv, byteArray, &isCopy);
 		    int* idata = malloc(len * sizeof(int));
@@ -333,7 +332,7 @@ static void releaseNativeArray(JNIEnv *env, int i, int freedata) {
 			fatalError("releaseNativeArray type");
 		}
 		// update complete status
-		(*env)->CallVoidMethod(env, cv.obj, setCompleteMethodID, cv.obj, complete);
+		(*env)->CallVoidMethod(env, UpCallsRFFIObject, setCompleteMethodID, cv.obj, complete);
 
         if (freedata) {
             // free up the slot
@@ -427,10 +426,6 @@ void validateRef(JNIEnv *env, SEXP x, const char *msg) {
 	}
 }
 
-void validate(SEXP x) {
-	(*curenv)->CallStaticObjectMethod(curenv, CallRFFIHelperClass, validateMethodID, x);
-}
-
 JNIEnv *getEnv() {
 //	fprintf(traceFile, "getEnv()=%p\n", curenv);
 	return curenv;
diff --git a/com.oracle.truffle.r.native/fficall/src/jni/rffiutils.h b/com.oracle.truffle.r.native/fficall/src/jni/rffiutils.h
index 3a3539579e..99732f9376 100644
--- a/com.oracle.truffle.r.native/fficall/src/jni/rffiutils.h
+++ b/com.oracle.truffle.r.native/fficall/src/jni/rffiutils.h
@@ -76,12 +76,12 @@ void updateNativeArrays(JNIEnv *env);
 
 SEXP addGlobalRef(JNIEnv *env, SEXP obj, int permanent);
 
+void init_utils(JNIEnv *env, jobject upCallsInstance);
 void init_rmath(JNIEnv *env);
 void init_variables(JNIEnv *env, jobjectArray initialValues);
 void init_dynload(JNIEnv *env);
 void init_internals(JNIEnv *env);
 void init_random(JNIEnv *env);
-void init_utils(JNIEnv *env);
 void init_parse(JNIEnv *env);
 void init_pcre(JNIEnv *env);
 void init_c(JNIEnv *env);
@@ -90,7 +90,8 @@ void setEmbedded(void);
 
 void setTempDir(JNIEnv *, jstring tempDir);
 
-extern jclass CallRFFIHelperClass;
+extern jclass UpCallsRFFIClass;
+extern jobject UpCallsRFFIObject;
 extern FILE *traceFile;
 
 // tracing/debugging support, set to 1 and recompile to enable
diff --git a/com.oracle.truffle.r.native/fficall/src/jni/variables.c b/com.oracle.truffle.r.native/fficall/src/jni/variables.c
index ca381df5e2..aaf00d68fe 100644
--- a/com.oracle.truffle.r.native/fficall/src/jni/variables.c
+++ b/com.oracle.truffle.r.native/fficall/src/jni/variables.c
@@ -31,37 +31,37 @@
 #include <rffiutils.h>
 #include <variable_defs.h>
 
-jmethodID getGlobalEnvMethodID;
-jmethodID getBaseEnvMethodID;
-jmethodID getBaseNamespaceMethodID;
-jmethodID getNamespaceRegistryMethodID;
+jmethodID R_GlobalEnvMethodID;
+jmethodID R_BaseEnvMethodID;
+jmethodID R_BaseNamespaceMethodID;
+jmethodID R_NamespaceRegistryMethodID;
 jmethodID isInteractiveMethodID;
-jmethodID getGlobalContextMethodID;
+jmethodID R_GlobalContextMethodID;
 
 // R_GlobalEnv et al are not a variables in FASTR as they are RContext specific
 SEXP FASTR_GlobalEnv() {
 	JNIEnv *env = getEnv();
-	return (*env)->CallStaticObjectMethod(env, CallRFFIHelperClass, getGlobalEnvMethodID);
+	return (*env)->CallObjectMethod(env, UpCallsRFFIObject, R_GlobalEnvMethodID);
 }
 
 SEXP FASTR_BaseEnv() {
 	JNIEnv *env = getEnv();
-	return (*env)->CallStaticObjectMethod(env, CallRFFIHelperClass, getBaseEnvMethodID);
+	return (*env)->CallObjectMethod(env, UpCallsRFFIObject, R_BaseEnvMethodID);
 }
 
 SEXP FASTR_BaseNamespace() {
 	JNIEnv *env = getEnv();
-	return (*env)->CallStaticObjectMethod(env, CallRFFIHelperClass, getBaseNamespaceMethodID);
+	return (*env)->CallObjectMethod(env, UpCallsRFFIObject, R_BaseNamespaceMethodID);
 }
 
 SEXP FASTR_NamespaceRegistry() {
 	JNIEnv *env = getEnv();
-	return (*env)->CallStaticObjectMethod(env, CallRFFIHelperClass, getNamespaceRegistryMethodID);
+	return (*env)->CallObjectMethod(env, UpCallsRFFIObject, R_NamespaceRegistryMethodID);
 }
 
 CTXT FASTR_GlobalContext() {
 	JNIEnv *env = getEnv();
-	CTXT res = (*env)->CallStaticObjectMethod(env, CallRFFIHelperClass, getGlobalContextMethodID);
+	CTXT res = (*env)->CallObjectMethod(env, UpCallsRFFIObject, R_GlobalContextMethodID);
     return addGlobalRef(env, res, 0);
 }
 
@@ -77,12 +77,12 @@ void init_variables(JNIEnv *env, jobjectArray initialValues) {
 	jmethodID doubleValueMethodID = checkGetMethodID(env, doubleClass, "doubleValue", "()D", 0);
 	jmethodID intValueMethodID = checkGetMethodID(env, intClass, "intValue", "()I", 0);
 
-	getGlobalEnvMethodID = checkGetMethodID(env, CallRFFIHelperClass, "getGlobalEnv", "()Ljava/lang/Object;", 1);
-	getBaseEnvMethodID = checkGetMethodID(env, CallRFFIHelperClass, "getBaseEnv", "()Ljava/lang/Object;", 1);
-	getBaseNamespaceMethodID = checkGetMethodID(env, CallRFFIHelperClass, "getBaseNamespace", "()Ljava/lang/Object;", 1);
-	getNamespaceRegistryMethodID = checkGetMethodID(env, CallRFFIHelperClass, "getNamespaceRegistry", "()Ljava/lang/Object;", 1);
-	isInteractiveMethodID = checkGetMethodID(env, CallRFFIHelperClass, "isInteractive", "()I", 1);
-	getGlobalContextMethodID = checkGetMethodID(env, CallRFFIHelperClass, "getGlobalContext", "()Ljava/lang/Object;", 1);
+	R_GlobalEnvMethodID = checkGetMethodID(env, UpCallsRFFIClass, "R_GlobalEnv", "()Ljava/lang/Object;", 0);
+	R_BaseEnvMethodID = checkGetMethodID(env, UpCallsRFFIClass, "R_BaseEnv", "()Ljava/lang/Object;", 0);
+	R_BaseNamespaceMethodID = checkGetMethodID(env, UpCallsRFFIClass, "R_BaseNamespace", "()Ljava/lang/Object;", 0);
+	R_NamespaceRegistryMethodID = checkGetMethodID(env, UpCallsRFFIClass, "R_NamespaceRegistry", "()Ljava/lang/Object;", 0);
+	isInteractiveMethodID = checkGetMethodID(env, UpCallsRFFIClass, "isInteractive", "()I", 0);
+	R_GlobalContextMethodID = checkGetMethodID(env, UpCallsRFFIClass, "R_GlobalContext", "()Ljava/lang/Object;", 0);
 
 	int length = (*env)->GetArrayLength(env, initialValues);
 	int index;
diff --git a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jni/CallRFFIHelper.java b/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/JavaUpCallsRFFI.java
similarity index 66%
rename from com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jni/CallRFFIHelper.java
rename to com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/JavaUpCallsRFFI.java
index fa73127bce..4f0611ede2 100644
--- a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jni/CallRFFIHelper.java
+++ b/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/JavaUpCallsRFFI.java
@@ -20,12 +20,11 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.runtime.ffi.jni;
+package com.oracle.truffle.r.runtime.ffi;
 
 import java.nio.charset.StandardCharsets;
 import java.util.function.Function;
 
-import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.frame.Frame;
 import com.oracle.truffle.api.frame.FrameInstance.FrameAccess;
 import com.oracle.truffle.api.object.DynamicObject;
@@ -79,17 +78,16 @@ import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
 import com.oracle.truffle.r.runtime.env.REnvironment;
 import com.oracle.truffle.r.runtime.env.REnvironment.PutException;
 import com.oracle.truffle.r.runtime.ffi.DLL.SymbolHandle;
-import com.oracle.truffle.r.runtime.ffi.RFFIUtils;
+import com.oracle.truffle.r.runtime.ffi.ParseResult.ParseStatus;
+import com.oracle.truffle.r.runtime.ffi.jni.TraceUpCallsAdapter;
 import com.oracle.truffle.r.runtime.gnur.SEXPTYPE;
 import com.oracle.truffle.r.runtime.nodes.DuplicationHelper;
 import com.oracle.truffle.r.runtime.nodes.RNode;
 import com.oracle.truffle.r.runtime.rng.RRNG;
 
 /**
- * This class provides methods that match the functionality of the macro/function definitions in the
- * R header files, e.g. {@code Rinternals.h} that are used by C/C++ code. For ease of
- * identification, we use method names that, as far as possible, match the names in the header
- * files. These methods should never be called from normal FastR code.
+ * This class provides a simple Java-based implementation of {@link UpCallsRFFI}, using no Truffle
+ * mechanisms.
  *
  * TODO Many of the implementations here are incomplete and/or duplicate code that exists in the
  * Truffle side of the implementation, i.e., {@link RNode} subclasses. A complete refactoring that
@@ -98,37 +96,16 @@ import com.oracle.truffle.r.runtime.rng.RRNG;
  * is desirable. In some cases it may be possible to "implement" the functions in R (which is a
  * simple way to achieve the above).
  */
-public class CallRFFIHelper {
+public class JavaUpCallsRFFI implements UpCallsRFFI {
 
-    /**
-     * Internally GNU R distinguishes "strings" and "vectors of strings" using the {@code CHARSXP}
-     * and {@code STRSXP} types, respectively. Although this difference is invisible at the R level,
-     * it manifests itself in the R FFI as several functions traffic in the {@code CHARSXP} type.
-     * Since FastR already uses {@code String} to denote a length-1 string vector, it cannot be used
-     * to represent a {@code CHARSXP}, so this class exists to do so.
-     *
-     */
-    public static final class CharSXPWrapper {
-        private final String contents;
-
-        CharSXPWrapper(String contents) {
-            this.contents = contents;
-        }
-
-        public String getContents() {
-            return contents;
-        }
+    private TraceUpCallsAdapter tracer;
 
-        @Override
-        public String toString() {
-            return "CHARSXP(" + contents + ")";
+    public JavaUpCallsRFFI() {
+        if (RFFIUtils.traceEnabled()) {
+            tracer = new TraceUpCallsAdapter();
         }
     }
 
-    public static Object createCharSXP(String contents) {
-        return new CharSXPWrapper(contents);
-    }
-
     private static RuntimeException unimplemented() {
         return unimplemented("");
     }
@@ -169,38 +146,43 @@ public class CallRFFIHelper {
 
     // Checkstyle: stop method name check
 
-    public static RIntVector Rf_ScalarInteger(int value) {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("Rf_ScalarInteger", value);
+    @Override
+    public RIntVector Rf_ScalarInteger(int value) {
+        if (tracer != null) {
+            tracer.Rf_ScalarInteger(value);
         }
         return RDataFactory.createIntVectorFromScalar(value);
     }
 
-    public static RLogicalVector Rf_ScalarLogical(int value) {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("Rf_ScalarLogical", value);
+    @Override
+    public RLogicalVector Rf_ScalarLogical(int value) {
+        if (tracer != null) {
+            tracer.Rf_ScalarLogical(value);
         }
         return RDataFactory.createLogicalVectorFromScalar(value != 0);
     }
 
-    public static RDoubleVector Rf_ScalarDouble(double value) {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("Rf_ScalarDouble", value);
+    @Override
+    public RDoubleVector Rf_ScalarDouble(double value) {
+        if (tracer != null) {
+            tracer.Rf_ScalarDouble(value);
         }
         return RDataFactory.createDoubleVectorFromScalar(value);
     }
 
-    public static RStringVector Rf_ScalarString(Object value) {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("Rf_ScalarString", value);
+    @Override
+    public RStringVector Rf_ScalarString(Object value) {
+        if (tracer != null) {
+            tracer.Rf_ScalarString(value);
         }
         CharSXPWrapper chars = guaranteeInstanceOf(value, CharSXPWrapper.class);
         return RDataFactory.createStringVectorFromScalar(chars.getContents());
     }
 
-    public static int Rf_asInteger(Object x) {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("Rf_asInteger", x);
+    @Override
+    public int Rf_asInteger(Object x) {
+        if (tracer != null) {
+            tracer.Rf_asInteger(x);
         }
         // TODO this is quite incomplete and really should be implemented with CastIntegerNode
         if (x instanceof Integer) {
@@ -217,9 +199,10 @@ public class CallRFFIHelper {
         }
     }
 
-    public static double Rf_asReal(Object x) {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("Rf_asReal", x);
+    @Override
+    public double Rf_asReal(Object x) {
+        if (tracer != null) {
+            tracer.Rf_asReal(x);
         }
         if (x instanceof Double) {
             return ((Double) x).doubleValue();
@@ -231,9 +214,10 @@ public class CallRFFIHelper {
         }
     }
 
-    public static int Rf_asLogical(Object x) {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("Rf_asLogical", x);
+    @Override
+    public int Rf_asLogical(Object x) {
+        if (tracer != null) {
+            tracer.Rf_asLogical(x);
         }
         if (x instanceof Byte) {
             return ((Byte) x).intValue();
@@ -243,14 +227,15 @@ public class CallRFFIHelper {
         }
     }
 
-    public static Object Rf_asChar(Object x) {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("Rf_asChar", x);
+    @Override
+    public Object Rf_asChar(Object x) {
+        if (tracer != null) {
+            tracer.Rf_asChar(x);
         }
         if (x instanceof CharSXPWrapper) {
             return x;
         } else if (x instanceof RSymbol) {
-            return new CharSXPWrapper(((RSymbol) x).getName());
+            return CharSXPWrapper.create(((RSymbol) x).getName());
         }
 
         Object obj = RRuntime.asAbstractVector(x);
@@ -258,34 +243,37 @@ public class CallRFFIHelper {
             RAbstractVector vector = (RAbstractVector) obj;
             if (vector.getLength() > 0) {
                 if (vector instanceof RAbstractStringVector) {
-                    return new CharSXPWrapper(((RAbstractStringVector) vector).getDataAt(0));
+                    return CharSXPWrapper.create(((RAbstractStringVector) vector).getDataAt(0));
                 } else {
                     unimplemented("asChar type " + x.getClass());
                 }
             }
         }
 
-        return new CharSXPWrapper(RRuntime.STRING_NA);
+        return CharSXPWrapper.create(RRuntime.STRING_NA);
     }
 
-    public static Object Rf_mkCharLenCE(byte[] bytes, @SuppressWarnings("unused") int encoding) {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("Rf_mkCharLenCE", bytes);
+    @Override
+    public Object Rf_mkCharLenCE(byte[] bytes, int encoding) {
+        if (tracer != null) {
+            tracer.Rf_mkCharLenCE(bytes, encoding);
         }
         // TODO: handle encoding properly
-        return new CharSXPWrapper(new String(bytes, StandardCharsets.UTF_8));
+        return CharSXPWrapper.create(new String(bytes, StandardCharsets.UTF_8));
     }
 
-    public static Object Rf_cons(Object car, Object cdr) {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("Rf_cons", car, cdr);
+    @Override
+    public Object Rf_cons(Object car, Object cdr) {
+        if (tracer != null) {
+            tracer.Rf_cons(car, cdr);
         }
         return RDataFactory.createPairList(car, cdr);
     }
 
-    public static void Rf_defineVar(Object symbolArg, Object value, Object envArg) {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("Rf_defineVar", symbolArg, value, envArg);
+    @Override
+    public void Rf_defineVar(Object symbolArg, Object value, Object envArg) {
+        if (tracer != null) {
+            tracer.Rf_defineVar(symbolArg, value, envArg);
         }
         REnvironment env = (REnvironment) envArg;
         RSymbol name = (RSymbol) symbolArg;
@@ -296,33 +284,39 @@ public class CallRFFIHelper {
         }
     }
 
-    public static Object R_do_MAKE_CLASS(String clazz) {
+    @Override
+    public Object R_do_MAKE_CLASS(String clazz) {
+        if (tracer != null) {
+            tracer.R_do_MAKE_CLASS(clazz);
+        }
         String name = "getClass";
         RFunction getClass = (RFunction) RContext.getRRuntimeASTAccess().forcePromise(name, REnvironment.getRegisteredNamespace("methods").get(name));
         return RContext.getEngine().evalFunction(getClass, null, RCaller.createInvalid(null), null, clazz);
     }
 
-    public static Object Rf_findVar(Object symbolArg, Object envArg) {
-        // WARNING: argument order reversed from Rf_findVarInFrame!
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("Rf_findVar", symbolArg, envArg);
+    @Override
+    public Object Rf_findVar(Object symbolArg, Object envArg) {
+        if (tracer != null) {
+            tracer.Rf_findVar(symbolArg, envArg);
         }
         return findVarInFrameHelper(envArg, symbolArg, true);
     }
 
-    public static Object Rf_findVarInFrame(Object envArg, Object symbolArg) {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("Rf_findVarInFrame", envArg, symbolArg);
+    @Override
+    public Object Rf_findVarInFrame(Object envArg, Object symbolArg) {
+        if (tracer != null) {
+            tracer.Rf_findVarInFrame(envArg, symbolArg);
         }
         return findVarInFrameHelper(envArg, symbolArg, false);
     }
 
-    public static Object Rf_findVarInFrame3(Object envArg, Object symbolArg, @SuppressWarnings("unused") int doGet) {
+    @Override
+    public Object Rf_findVarInFrame3(Object envArg, Object symbolArg, int doGet) {
+        if (tracer != null) {
+            tracer.Rf_findVarInFrame3(envArg, symbolArg, doGet);
+        }
         // GNU R has code for IS_USER_DATBASE that uses doGet
         // This is a lookup in the single environment (envArg) only, i.e. inherits=false
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("Rf_findVarInFrame3", envArg, symbolArg);
-        }
         return findVarInFrameHelper(envArg, symbolArg, false);
     }
 
@@ -349,9 +343,10 @@ public class CallRFFIHelper {
         return RUnboundValue.instance;
     }
 
-    public static Object Rf_getAttrib(Object obj, Object name) {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("Rf_getAttrib", obj, name);
+    @Override
+    public Object Rf_getAttrib(Object obj, Object name) {
+        if (tracer != null) {
+            tracer.Rf_getAttrib(obj, name);
         }
         Object result = RNull.instance;
         if (obj instanceof RAttributable) {
@@ -368,9 +363,10 @@ public class CallRFFIHelper {
         return result;
     }
 
-    public static void Rf_setAttrib(Object obj, Object name, Object val) {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("Rf_setAttrib", obj, name, val);
+    @Override
+    public void Rf_setAttrib(Object obj, Object name, Object val) {
+        if (tracer != null) {
+            tracer.Rf_setAttrib(obj, name, val);
         }
         if (obj instanceof RAttributable) {
             RAttributable attrObj = (RAttributable) obj;
@@ -415,9 +411,10 @@ public class CallRFFIHelper {
         return result;
     }
 
-    public static int Rf_inherits(Object x, String clazz) {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("Rf_inherits", x, clazz);
+    @Override
+    public int Rf_inherits(Object x, String clazz) {
+        if (tracer != null) {
+            tracer.Rf_inherits(x, clazz);
         }
         int result = 0;
         RStringVector hierarchy = getClassHr(x);
@@ -429,38 +426,43 @@ public class CallRFFIHelper {
         return result;
     }
 
-    public static Object Rf_install(String name) {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("Rf_install", name);
+    @Override
+    public Object Rf_install(String name) {
+        if (tracer != null) {
+            tracer.Rf_install(name);
         }
         return RDataFactory.createSymbolInterned(name);
     }
 
-    public static Object Rf_lengthgets(Object x, int newSize) {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("Rf_lengthgets", x, newSize);
+    @Override
+    public Object Rf_lengthgets(Object x, int newSize) {
+        if (tracer != null) {
+            tracer.Rf_lengthgets(x, newSize);
         }
         RAbstractVector vec = (RAbstractVector) RRuntime.asAbstractVector(x);
         return vec.resize(newSize);
     }
 
-    public static int Rf_isString(Object x) {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("Rf_isString", x);
+    @Override
+    public int Rf_isString(Object x) {
+        if (tracer != null) {
+            tracer.Rf_isString(x);
         }
         return RRuntime.checkType(x, RType.Character) ? 1 : 0;
     }
 
-    public static int Rf_isNull(Object x) {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("Rf_isNull", x);
+    @Override
+    public int Rf_isNull(Object x) {
+        if (tracer != null) {
+            tracer.Rf_isNull(x);
         }
         return x == RNull.instance ? 1 : 0;
     }
 
-    public static Object Rf_PairToVectorList(Object x) {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("Rf_PairToVectorList", x);
+    @Override
+    public Object Rf_PairToVectorList(Object x) {
+        if (tracer != null) {
+            tracer.Rf_PairToVectorList(x);
         }
         if (x == RNull.instance) {
             return RDataFactory.createList();
@@ -469,30 +471,34 @@ public class CallRFFIHelper {
         return pl.toRList();
     }
 
-    public static void Rf_error(String msg) {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("Rf_error", msg);
+    @Override
+    public void Rf_error(String msg) {
+        if (tracer != null) {
+            tracer.Rf_error(msg);
         }
         throw RError.error(RError.SHOW_CALLER2, RError.Message.GENERIC, msg);
     }
 
-    public static void Rf_warning(String msg) {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("Rf_warning", msg);
+    @Override
+    public void Rf_warning(String msg) {
+        if (tracer != null) {
+            tracer.Rf_warning(msg);
         }
         RError.warning(RError.SHOW_CALLER2, RError.Message.GENERIC, msg);
     }
 
-    public static void Rf_warningcall(Object call, String msg) {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("Rf_warningcall", call, msg);
+    @Override
+    public void Rf_warningcall(Object call, String msg) {
+        if (tracer != null) {
+            tracer.Rf_warningcall(call, msg);
         }
         RErrorHandling.warningcallRFFI(call, msg);
     }
 
-    public static Object Rf_allocateVector(int mode, int n) {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("Rf_allocateVector", mode, n);
+    @Override
+    public Object Rf_allocateVector(int mode, int n) {
+        if (tracer != null) {
+            tracer.Rf_allocateVector(mode, n);
         }
         SEXPTYPE type = SEXPTYPE.mapInt(mode);
         if (n < 0) {
@@ -521,9 +527,10 @@ public class CallRFFIHelper {
         }
     }
 
-    public static Object Rf_allocateArray(int mode, Object dimsObj) {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("Rf_allocateArray", mode, dimsObj);
+    @Override
+    public Object Rf_allocateArray(int mode, Object dimsObj) {
+        if (tracer != null) {
+            tracer.Rf_allocateArray(mode, dimsObj);
         }
         RIntVector dims = (RIntVector) dimsObj;
         int n = 1;
@@ -539,9 +546,10 @@ public class CallRFFIHelper {
 
     }
 
-    public static Object Rf_allocateMatrix(int mode, int nrow, int ncol) {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("Rf_allocateMatrix", mode, ncol, nrow);
+    @Override
+    public Object Rf_allocateMatrix(int mode, int nrow, int ncol) {
+        if (tracer != null) {
+            tracer.Rf_allocateMatrix(mode, ncol, nrow);
         }
         SEXPTYPE type = SEXPTYPE.mapInt(mode);
         if (nrow < 0 || ncol < 0) {
@@ -565,23 +573,26 @@ public class CallRFFIHelper {
         }
     }
 
-    public static int Rf_nrows(Object x) {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("Rf_nrows", x);
+    @Override
+    public int Rf_nrows(Object x) {
+        if (tracer != null) {
+            tracer.Rf_nrows(x);
         }
         return RRuntime.nrows(x);
     }
 
-    public static int Rf_ncols(Object x) {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("Rf_ncols", x);
+    @Override
+    public int Rf_ncols(Object x) {
+        if (tracer != null) {
+            tracer.Rf_ncols(x);
         }
         return RRuntime.ncols(x);
     }
 
-    public static int LENGTH(Object x) {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("LENGTH", x);
+    @Override
+    public int LENGTH(Object x) {
+        if (tracer != null) {
+            tracer.LENGTH(x);
         }
         if (x instanceof RAbstractContainer) {
             return ((RAbstractContainer) x).getLength();
@@ -596,26 +607,29 @@ public class CallRFFIHelper {
         }
     }
 
-    public static void SET_STRING_ELT(Object x, int i, Object v) {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("SET_STRING_ELT", x, i, v);
+    @Override
+    public void SET_STRING_ELT(Object x, int i, Object v) {
+        if (tracer != null) {
+            tracer.SET_STRING_ELT(x, i, v);
         }
         RStringVector vector = guaranteeInstanceOf(x, RStringVector.class);
         CharSXPWrapper element = guaranteeInstanceOf(v, CharSXPWrapper.class);
         vector.setElement(i, element.getContents());
     }
 
-    public static void SET_VECTOR_ELT(Object x, int i, Object v) {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("SET_VECTOR_ELT", i, v);
+    @Override
+    public void SET_VECTOR_ELT(Object x, int i, Object v) {
+        if (tracer != null) {
+            tracer.SET_VECTOR_ELT(x, i, v);
         }
         RList list = guaranteeInstanceOf(x, RList.class);
         list.setElement(i, v);
     }
 
-    public static byte[] RAW(Object x) {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("RAW", x);
+    @Override
+    public byte[] RAW(Object x) {
+        if (tracer != null) {
+            tracer.RAW(x);
         }
         if (x instanceof RRawVector) {
             return ((RRawVector) x).getDataWithoutCopying();
@@ -626,9 +640,10 @@ public class CallRFFIHelper {
         }
     }
 
-    public static byte[] LOGICAL(Object x) {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("LOGICAL", x);
+    @Override
+    public byte[] LOGICAL(Object x) {
+        if (tracer != null) {
+            tracer.LOGICAL(x);
         }
         if (x instanceof RLogicalVector) {
             return ((RLogicalVector) x).getDataWithoutCopying();
@@ -639,9 +654,10 @@ public class CallRFFIHelper {
         }
     }
 
-    public static int[] INTEGER(Object x) {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("INTEGER", x);
+    @Override
+    public int[] INTEGER(Object x) {
+        if (tracer != null) {
+            tracer.INTEGER(x);
         }
         if (x instanceof RIntVector) {
             return ((RIntVector) x).getDataWithoutCopying();
@@ -662,9 +678,10 @@ public class CallRFFIHelper {
         }
     }
 
-    public static double[] REAL(Object x) {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("REAL", x);
+    @Override
+    public double[] REAL(Object x) {
+        if (tracer != null) {
+            tracer.REAL(x);
         }
         if (x instanceof RDoubleVector) {
             return ((RDoubleVector) x).getDataWithoutCopying();
@@ -676,33 +693,19 @@ public class CallRFFIHelper {
         }
     }
 
-    /**
-     * Called to possibly update the "complete" status on {@code x}. N.B. {@code x} may not be an
-     * object with a concrete {@code setComplete} method, e.g. see {@link #INTEGER(Object)}.
-     */
-    public static void setComplete(Object x, boolean complete) {
-        // only care about concrete vectors
-        if (x instanceof RVector) {
-            ((RVector<?>) x).setComplete(complete);
-        }
-    }
-
-    public static void logObject(Object x) {
-        System.out.println("object " + x);
-        System.out.println("class " + x.getClass());
-    }
-
-    public static Object STRING_ELT(Object x, int i) {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("STRING_ELT", x, i);
+    @Override
+    public Object STRING_ELT(Object x, int i) {
+        if (tracer != null) {
+            tracer.STRING_ELT(x, i);
         }
         RAbstractStringVector vector = guaranteeInstanceOf(RRuntime.asAbstractVector(x), RAbstractStringVector.class);
-        return new CharSXPWrapper(vector.getDataAt(i));
+        return CharSXPWrapper.create(vector.getDataAt(i));
     }
 
-    public static Object VECTOR_ELT(Object x, int i) {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("VECTOR_ELT", x, i);
+    @Override
+    public Object VECTOR_ELT(Object x, int i) {
+        if (tracer != null) {
+            tracer.VECTOR_ELT(x, i);
         }
         Object vec = x;
         if (vec instanceof RExpression) {
@@ -712,9 +715,10 @@ public class CallRFFIHelper {
         return list.getDataAt(i);
     }
 
-    public static int NAMED(Object x) {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("NAMED", x);
+    @Override
+    public int NAMED(Object x) {
+        if (tracer != null) {
+            tracer.NAMED(x);
         }
         if (x instanceof RShareable) {
             return ((RShareable) x).isShared() ? 1 : 0;
@@ -723,9 +727,10 @@ public class CallRFFIHelper {
         }
     }
 
-    public static Object SET_TYPEOF_FASTR(Object x, int v) {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("SET_TYPEOF_FASTR", x, v);
+    @Override
+    public Object SET_TYPEOF_FASTR(Object x, int v) {
+        if (tracer != null) {
+            tracer.SET_TYPEOF_FASTR(x, v);
         }
         int code = SEXPTYPE.gnuRCodeForObject(x);
         if (code == SEXPTYPE.LISTSXP.code && v == SEXPTYPE.LANGSXP.code) {
@@ -735,9 +740,10 @@ public class CallRFFIHelper {
         }
     }
 
-    public static int TYPEOF(Object x) {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("TYPEOF", x);
+    @Override
+    public int TYPEOF(Object x) {
+        if (tracer != null) {
+            tracer.TYPEOF(x);
         }
         if (x instanceof CharSXPWrapper) {
             return SEXPTYPE.CHARSXP.code;
@@ -746,9 +752,10 @@ public class CallRFFIHelper {
         }
     }
 
-    public static int OBJECT(Object x) {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("OBJECT", x);
+    @Override
+    public int OBJECT(Object x) {
+        if (tracer != null) {
+            tracer.OBJECT(x);
         }
         if (x instanceof RAttributable) {
             return ((RAttributable) x).getAttr(RRuntime.CLASS_ATTR_KEY) == null ? 0 : 1;
@@ -757,9 +764,10 @@ public class CallRFFIHelper {
         }
     }
 
-    public static Object Rf_duplicate(Object x, int deep) {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("Rf_duplicate", x, deep);
+    @Override
+    public Object Rf_duplicate(Object x, int deep) {
+        if (tracer != null) {
+            tracer.Rf_duplicate(x, deep);
         }
         guarantee(x != null, "unexpected type: null instead of " + x.getClass().getSimpleName());
         guarantee(x instanceof RShareable || x instanceof RIntSequence || x instanceof RExternalPtr,
@@ -773,9 +781,10 @@ public class CallRFFIHelper {
         }
     }
 
-    public static int Rf_anyDuplicated(Object x, int fromLast) {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("Rf_anyDuplicated", x, fromLast);
+    @Override
+    public int Rf_anyDuplicated(Object x, int fromLast) {
+        if (tracer != null) {
+            tracer.Rf_anyDuplicated(x, fromLast);
         }
         RAbstractVector vec = (RAbstractVector) x;
         if (vec.getLength() == 0) {
@@ -785,17 +794,19 @@ public class CallRFFIHelper {
         }
     }
 
-    public static Object PRINTNAME(Object x) {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("PRINTNAME", x);
+    @Override
+    public Object PRINTNAME(Object x) {
+        if (tracer != null) {
+            tracer.PRINTNAME(x);
         }
         guaranteeInstanceOf(x, RSymbol.class);
-        return new CharSXPWrapper(((RSymbol) x).getName());
+        return CharSXPWrapper.create(((RSymbol) x).getName());
     }
 
-    public static Object TAG(Object e) {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("TAG", e);
+    @Override
+    public Object TAG(Object e) {
+        if (tracer != null) {
+            tracer.TAG(e);
         }
         if (e instanceof RPairList) {
             return ((RPairList) e).getTag();
@@ -806,9 +817,10 @@ public class CallRFFIHelper {
         }
     }
 
-    public static Object CAR(Object e) {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("CAR", e);
+    @Override
+    public Object CAR(Object e) {
+        if (tracer != null) {
+            tracer.CAR(e);
         }
         guarantee(e != null && (RPairList.class.isInstance(e) || RLanguage.class.isInstance(e)), "CAR only works on pair lists and language objects");
         if (e instanceof RPairList) {
@@ -818,9 +830,10 @@ public class CallRFFIHelper {
         }
     }
 
-    public static Object CDR(Object e) {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("CDR", e);
+    @Override
+    public Object CDR(Object e) {
+        if (tracer != null) {
+            tracer.CDR(e);
         }
         if (e instanceof RLanguage) {
             RLanguage lang = (RLanguage) e;
@@ -832,9 +845,10 @@ public class CallRFFIHelper {
         }
     }
 
-    public static Object CADR(Object e) {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("CADR", e);
+    @Override
+    public Object CADR(Object e) {
+        if (tracer != null) {
+            tracer.CADR(e);
         }
         guarantee(e != null && (RPairList.class.isInstance(e) || RLanguage.class.isInstance(e)), "CADR only works on pair lists and language objects");
         if (e instanceof RPairList) {
@@ -844,9 +858,10 @@ public class CallRFFIHelper {
         }
     }
 
-    public static Object CADDR(Object e) {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("CADDR", e);
+    @Override
+    public Object CADDR(Object e) {
+        if (tracer != null) {
+            tracer.CADDR(e);
         }
         guarantee(e != null && (RPairList.class.isInstance(e) || RLanguage.class.isInstance(e)), "CADDR only works on pair lists and language objects");
         if (e instanceof RPairList) {
@@ -856,9 +871,10 @@ public class CallRFFIHelper {
         }
     }
 
-    public static Object CDDR(Object e) {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("CDDR", e);
+    @Override
+    public Object CDDR(Object e) {
+        if (tracer != null) {
+            tracer.CDDR(e);
         }
         if (e instanceof RLanguage) {
             RLanguage lang = (RLanguage) e;
@@ -870,9 +886,10 @@ public class CallRFFIHelper {
         }
     }
 
-    public static Object SET_TAG(Object x, Object y) {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("SET_TAG", x, y);
+    @Override
+    public Object SET_TAG(Object x, Object y) {
+        if (tracer != null) {
+            tracer.SET_TAG(x, y);
         }
         if (x instanceof RPairList) {
             ((RPairList) x).setTag(y);
@@ -884,32 +901,39 @@ public class CallRFFIHelper {
         return y;
     }
 
-    public static Object SETCAR(Object x, Object y) {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("SETCAR", x, y);
+    @Override
+    public Object SETCAR(Object x, Object y) {
+        if (tracer != null) {
+            tracer.SETCAR(x, y);
         }
         guaranteeInstanceOf(x, RPairList.class);
         ((RPairList) x).setCar(y);
         return y;
     }
 
-    public static Object SETCDR(Object x, Object y) {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("SETCDR", x, y);
+    @Override
+    public Object SETCDR(Object x, Object y) {
+        if (tracer != null) {
+            tracer.SETCDR(x, y);
         }
         guaranteeInstanceOf(x, RPairList.class);
         ((RPairList) x).setCdr(y);
         return y;
     }
 
-    public static Object SETCADR(Object x, Object y) {
+    @Override
+    public Object SETCADR(Object x, Object y) {
+        if (tracer != null) {
+            tracer.SETCADR(x, y);
+        }
         SETCAR(CDR(x), y);
         return y;
     }
 
-    public static Object SYMVALUE(Object x) {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("SYMVALUE", x);
+    @Override
+    public Object SYMVALUE(Object x) {
+        if (tracer != null) {
+            tracer.SYMVALUE(x);
         }
         if (!(x instanceof RSymbol)) {
             throw RInternalError.shouldNotReachHere();
@@ -922,9 +946,10 @@ public class CallRFFIHelper {
         }
     }
 
-    public static void SET_SYMVALUE(Object x, Object v) {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("SET_SYMVALUE", x, v);
+    @Override
+    public void SET_SYMVALUE(Object x, Object v) {
+        if (tracer != null) {
+            tracer.SET_SYMVALUE(x, v);
         }
         if (!(x instanceof RSymbol)) {
             throw RInternalError.shouldNotReachHere();
@@ -932,27 +957,29 @@ public class CallRFFIHelper {
         REnvironment.baseEnv().safePut(((RSymbol) x).getName(), v);
     }
 
-    public static int R_BindingIsLocked(Object sym, Object env) {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("R_BindingIsLocked", sym, env);
+    @Override
+    public int R_BindingIsLocked(Object sym, Object env) {
+        if (tracer != null) {
+            tracer.R_BindingIsLocked(sym, env);
         }
         guaranteeInstanceOf(sym, RSymbol.class);
         guaranteeInstanceOf(env, REnvironment.class);
         return ((REnvironment) env).bindingIsLocked(((RSymbol) sym).getName()) ? 1 : 0;
     }
 
-    public static Object R_FindNamespace(Object name) {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("R_FindNamespace", name);
+    @Override
+    public Object R_FindNamespace(Object name) {
+        if (tracer != null) {
+            tracer.R_FindNamespace(name);
         }
         Object result = RContext.getInstance().stateREnvironment.getNamespaceRegistry().get(RRuntime.asString(name));
         return result;
     }
 
-    @TruffleBoundary
-    public static Object Rf_eval(Object expr, Object env) {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("Rf_eval", expr, env);
+    @Override
+    public Object Rf_eval(Object expr, Object env) {
+        if (tracer != null) {
+            tracer.Rf_eval(expr, env);
         }
         guarantee(env instanceof REnvironment);
         Object result;
@@ -981,9 +1008,10 @@ public class CallRFFIHelper {
         return result;
     }
 
-    public static Object Rf_findfun(Object symbolObj, Object envObj) {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("Rf_findfun", symbolObj, envObj);
+    @Override
+    public Object Rf_findfun(Object symbolObj, Object envObj) {
+        if (tracer != null) {
+            tracer.Rf_findfun(symbolObj, envObj);
         }
         guarantee(envObj instanceof REnvironment);
         REnvironment env = (REnvironment) envObj;
@@ -999,18 +1027,20 @@ public class CallRFFIHelper {
         }
     }
 
-    public static Object Rf_GetOption1(Object tag) {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("Rf_GetOption1", tag);
+    @Override
+    public Object Rf_GetOption1(Object tag) {
+        if (tracer != null) {
+            tracer.Rf_GetOption1(tag);
         }
         guarantee(tag instanceof RSymbol);
         Object result = RContext.getInstance().stateROptions.getValue(((RSymbol) tag).getName());
         return result;
     }
 
-    public static void Rf_gsetVar(Object symbol, Object value, Object rho) {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("Rf_gsetVar", symbol, value, rho);
+    @Override
+    public void Rf_gsetVar(Object symbol, Object value, Object rho) {
+        if (tracer != null) {
+            tracer.Rf_gsetVar(symbol, value, rho);
         }
         guarantee(symbol instanceof RSymbol);
         REnvironment baseEnv = RContext.getInstance().stateREnvironment.getBaseEnv();
@@ -1022,9 +1052,10 @@ public class CallRFFIHelper {
         }
     }
 
-    public static void DUPLICATE_ATTRIB(Object to, Object from) {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("DUPLICATE_ATTRIB", to, from);
+    @Override
+    public void DUPLICATE_ATTRIB(Object to, Object from) {
+        if (tracer != null) {
+            tracer.DUPLICATE_ATTRIB(to, from);
         }
         if (from instanceof RAttributable) {
             guaranteeInstanceOf(to, RAttributable.class);
@@ -1034,9 +1065,10 @@ public class CallRFFIHelper {
         // TODO: copy OBJECT? and S4 attributes
     }
 
-    public static int R_computeIdentical(Object x, Object y, int flags) {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("R_computeIdentical", x, y, flags);
+    @Override
+    public int R_computeIdentical(Object x, Object y, int flags) {
+        if (tracer != null) {
+            tracer.R_computeIdentical(x, y, flags);
         }
         RFunction indenticalBuiltin = RContext.lookupBuiltin("identical");
         Object res = RContext.getEngine().evalFunction(indenticalBuiltin, null, null, null, x, y, RRuntime.asLogical((!((flags & 1) == 0))),
@@ -1044,25 +1076,26 @@ public class CallRFFIHelper {
         return (int) res;
     }
 
-    @SuppressWarnings("unused")
-    public static void Rf_copyListMatrix(Object s, Object t, int byrow) {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("Rf_copyListMatrix", t, byrow);
+    @Override
+    public void Rf_copyListMatrix(Object s, Object t, int byrow) {
+        if (tracer != null) {
+            tracer.Rf_copyListMatrix(s, t, byrow);
         }
         throw unimplemented();
     }
 
-    @SuppressWarnings("unused")
-    public static void Rf_copyMatrix(Object s, Object t, int byrow) {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("Rf_copyMatrix", t, byrow);
+    @Override
+    public void Rf_copyMatrix(Object s, Object t, int byrow) {
+        if (tracer != null) {
+            tracer.Rf_copyMatrix(s, t, byrow);
         }
         throw unimplemented();
     }
 
-    public static Object R_tryEval(Object expr, Object env, boolean silent) {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("R_tryEval", expr, env, silent);
+    @Override
+    public Object R_tryEval(Object expr, Object env, boolean silent) {
+        if (tracer != null) {
+            tracer.R_tryEval(expr, env, silent);
         }
         Object handlerStack = RErrorHandling.getHandlerStack();
         Object restartStack = RErrorHandling.getRestartStack();
@@ -1083,25 +1116,27 @@ public class CallRFFIHelper {
      * a C function is invoked (in the native layer) instead of an R expression. assert: this is
      * ONLY called from R_TopLevelExec prior to calling C function.
      */
-    public static Object resetAndGetErrorHandlerStacks() {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("R_TopLevelExec");
+    @Override
+    public Object R_ToplevelExec() {
+        if (tracer != null) {
+            tracer.R_ToplevelExec();
         }
         return RErrorHandling.resetAndGetHandlerStacks();
     }
 
     /**
-     * Helper function for {@code R_TopLevelExec}, see {@link #resetAndGetErrorHandlerStacks()},
-     * called after C function returns.
+     * Helper function for {@code R_TopLevelExec}, see {@link #R_ToplevelExec()}, called after C
+     * function returns.
      */
-    public static void restoreErrorHandlerStacks(Object stacks) {
+    public void R_ToplevelExecRestoreErrorHandlerStacks(Object stacks) {
         RErrorHandling.HandlerStacks handlerStacks = guaranteeInstanceOf(stacks, RErrorHandling.HandlerStacks.class);
         RErrorHandling.restoreHandlerStacks(handlerStacks);
     }
 
-    public static int RDEBUG(Object x) {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("RDEBUG", x);
+    @Override
+    public int RDEBUG(Object x) {
+        if (tracer != null) {
+            tracer.RDEBUG(x);
         }
         REnvironment env = guaranteeInstanceOf(x, REnvironment.class);
         if (env instanceof REnvironment.Function) {
@@ -1113,9 +1148,10 @@ public class CallRFFIHelper {
         }
     }
 
-    public static void SET_RDEBUG(Object x, int v) {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("SET_RDEBUG", x, v);
+    @Override
+    public void SET_RDEBUG(Object x, int v) {
+        if (tracer != null) {
+            tracer.SET_RDEBUG(x, v);
         }
         REnvironment env = guaranteeInstanceOf(x, REnvironment.class);
         if (env instanceof REnvironment.Function) {
@@ -1129,27 +1165,30 @@ public class CallRFFIHelper {
         }
     }
 
-    public static int RSTEP(Object x) {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("RSTEP", x);
+    @Override
+    public int RSTEP(Object x) {
+        if (tracer != null) {
+            tracer.RSTEP(x);
         }
         @SuppressWarnings("unused")
         REnvironment env = guaranteeInstanceOf(x, REnvironment.class);
         throw RInternalError.unimplemented("RSTEP");
     }
 
-    public static void SET_RSTEP(Object x, int v) {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("SET_RSTEP", x, v);
+    @Override
+    public void SET_RSTEP(Object x, int v) {
+        if (tracer != null) {
+            tracer.SET_RSTEP(x, v);
         }
         @SuppressWarnings("unused")
         REnvironment env = guaranteeInstanceOf(x, REnvironment.class);
         throw RInternalError.unimplemented("SET_RSTEP");
     }
 
-    public static Object ENCLOS(Object x) {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("ENCLOS", x);
+    @Override
+    public Object ENCLOS(Object x) {
+        if (tracer != null) {
+            tracer.ENCLOS(x);
         }
         REnvironment env = guaranteeInstanceOf(x, REnvironment.class);
         Object result = env.getParent();
@@ -1159,35 +1198,19 @@ public class CallRFFIHelper {
         return result;
     }
 
-    public static Object PRVALUE(Object x) {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("PRVALUE", x);
+    @Override
+    public Object PRVALUE(Object x) {
+        if (tracer != null) {
+            tracer.PRVALUE(x);
         }
         RPromise p = guaranteeInstanceOf(x, RPromise.class);
         return p.isEvaluated() ? p.getValue() : RUnboundValue.instance;
     }
 
-    private enum ParseStatus {
-        PARSE_NULL,
-        PARSE_OK,
-        PARSE_INCOMPLETE,
-        PARSE_ERROR,
-        PARSE_EOF
-    }
-
-    private static class ParseResult {
-        @SuppressWarnings("unused") private final int parseStatus;
-        @SuppressWarnings("unused") private final Object expr;
-
-        private ParseResult(int parseStatus, Object expr) {
-            this.parseStatus = parseStatus;
-            this.expr = expr;
-        }
-    }
-
-    public static Object R_ParseVector(Object text, int n, Object srcFile) {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("R_ParseVector", text, n, srcFile);
+    @Override
+    public Object R_ParseVector(Object text, int n, Object srcFile) {
+        if (tracer != null) {
+            tracer.R_ParseVector(text, n, srcFile);
         }
         // TODO general case
         assert n == 1;
@@ -1206,9 +1229,10 @@ public class CallRFFIHelper {
 
     }
 
-    public static Object R_lsInternal3(Object envArg, int allArg, int sortedArg) {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("R_lsInternal3", envArg, allArg, sortedArg);
+    @Override
+    public Object R_lsInternal3(Object envArg, int allArg, int sortedArg) {
+        if (tracer != null) {
+            tracer.R_lsInternal3(envArg, allArg, sortedArg);
         }
         boolean sorted = sortedArg != 0;
         boolean all = allArg != 0;
@@ -1216,29 +1240,28 @@ public class CallRFFIHelper {
         return env.ls(all, null, sorted);
     }
 
-    public static String R_HomeDir() {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("R_HomeDir");
+    @Override
+    public String R_HomeDir() {
+        if (tracer != null) {
+            tracer.R_HomeDir();
         }
         return REnvVars.rHome();
     }
 
-    @SuppressWarnings("unused")
-    private static void R_CleanUp(int sa, int status, int runlast) {
+    @Override
+    public void R_CleanUp(int sa, int status, int runlast) {
+        if (tracer != null) {
+            tracer.R_CleanUp(sa, status, runlast);
+        }
         RCleanUp.stdCleanUp(SA_TYPE.values()[sa], status, runlast != 0);
     }
 
-    // Checkstyle: resume method name check
-
-    public static Object validate(Object x) {
-        return x;
-    }
-
-    public static Object getGlobalContext() {
-        Utils.warn("Potential memory leak (global context object)");
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("getGlobalContext");
+    @Override
+    public Object R_GlobalContext() {
+        if (tracer != null) {
+            tracer.R_GlobalContext();
         }
+        Utils.warn("Potential memory leak (global context object)");
         Frame frame = Utils.getActualCurrentFrame();
         if (frame == null) {
             return RCaller.topLevel;
@@ -1250,80 +1273,94 @@ public class CallRFFIHelper {
         return rCaller == null ? RCaller.topLevel : rCaller;
     }
 
-    public static Object getGlobalEnv() {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("getGlobalEnv");
+    @Override
+    public Object R_GlobalEnv() {
+        if (tracer != null) {
+            tracer.R_GlobalEnv();
         }
         return RContext.getInstance().stateREnvironment.getGlobalEnv();
     }
 
-    public static Object getBaseEnv() {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("getBaseEnv");
+    @Override
+    public Object R_BaseEnv() {
+        if (tracer != null) {
+            tracer.R_BaseEnv();
         }
         return RContext.getInstance().stateREnvironment.getBaseEnv();
     }
 
-    public static Object getBaseNamespace() {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("getBaseNamespace");
+    @Override
+    public Object R_BaseNamespace() {
+        if (tracer != null) {
+            tracer.R_BaseNamespace();
         }
         return RContext.getInstance().stateREnvironment.getBaseNamespace();
     }
 
-    public static Object getNamespaceRegistry() {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("getNamespaceRegistry");
+    @Override
+    public Object R_NamespaceRegistry() {
+        if (tracer != null) {
+            tracer.R_NamespaceRegistry();
         }
         return RContext.getInstance().stateREnvironment.getNamespaceRegistry();
     }
 
-    public static int isInteractive() {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("isInteractive");
+    @Override
+    public int isInteractive() {
+        if (tracer != null) {
+            tracer.isInteractive();
         }
         return RContext.getInstance().isInteractive() ? 1 : 0;
     }
 
-    public static int isS4Object(Object x) {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("isS4Object");
+    @Override
+    public int isS4Object(Object x) {
+        if (tracer != null) {
+            tracer.isS4Object(x);
         }
         return x instanceof RS4Object ? 1 : 0;
     }
 
-    public static void printf(String message) {
+    @Override
+    public void Rprintf(String message) {
+        if (tracer != null) {
+            tracer.Rprintf(message);
+        }
         RContext.getInstance().getConsoleHandler().print(message);
     }
 
-    public static void getRNGstate() {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("getRNGstate");
+    @Override
+    public void GetRNGstate() {
+        if (tracer != null) {
+            tracer.GetRNGstate();
         }
         RRNG.getRNGState();
     }
 
-    public static void putRNGstate() {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("putRNGstate");
+    @Override
+    public void PutRNGstate() {
+        if (tracer != null) {
+            tracer.PutRNGstate();
         }
         RRNG.putRNGState();
     }
 
-    public static double unifRand() {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("unifRand");
+    @Override
+    public double unif_rand() {
+        if (tracer != null) {
+            tracer.unif_rand();
         }
         return RRNG.unifRand();
     }
 
     // Checkstyle: stop method name check
 
-    public static Object R_getGlobalFunctionContext() {
-        Utils.warn("Potential memory leak (global function context object)");
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("getGlobalFunctionContext");
+    @Override
+    public Object R_getGlobalFunctionContext() {
+        if (tracer != null) {
+            tracer.R_getGlobalFunctionContext();
         }
+        Utils.warn("Potential memory leak (global function context object)");
         Frame frame = Utils.getActualCurrentFrame();
         if (frame == null) {
             return RNull.instance;
@@ -1338,11 +1375,12 @@ public class CallRFFIHelper {
         return currentCaller == null || currentCaller == RCaller.topLevel ? RNull.instance : currentCaller;
     }
 
-    public static Object R_getParentFunctionContext(Object c) {
-        Utils.warn("Potential memory leak (parent function context object)");
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("getParentFunctionContext");
+    @Override
+    public Object R_getParentFunctionContext(Object c) {
+        if (tracer != null) {
+            tracer.R_getParentFunctionContext(c);
         }
+        Utils.warn("Potential memory leak (parent function context object)");
         RCaller currentCaller = guaranteeInstanceOf(c, RCaller.class);
         while (true) {
             currentCaller = currentCaller.getParent();
@@ -1354,9 +1392,10 @@ public class CallRFFIHelper {
         return currentCaller == null || currentCaller == RCaller.topLevel ? RNull.instance : currentCaller;
     }
 
-    public static Object R_getContextEnv(Object c) {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("getContextEnv", c);
+    @Override
+    public Object R_getContextEnv(Object c) {
+        if (tracer != null) {
+            tracer.R_getContextEnv(c);
         }
         RCaller rCaller = guaranteeInstanceOf(c, RCaller.class);
         if (rCaller == RCaller.topLevel) {
@@ -1382,9 +1421,10 @@ public class CallRFFIHelper {
         }
     }
 
-    public static Object R_getContextFun(Object c) {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("getContextEnv", c);
+    @Override
+    public Object R_getContextFun(Object c) {
+        if (tracer != null) {
+            tracer.R_getContextFun(c);
         }
         RCaller rCaller = guaranteeInstanceOf(c, RCaller.class);
         if (rCaller == RCaller.topLevel) {
@@ -1410,9 +1450,10 @@ public class CallRFFIHelper {
         }
     }
 
-    public static Object R_getContextCall(Object c) {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("getContextEnv", c);
+    @Override
+    public Object R_getContextCall(Object c) {
+        if (tracer != null) {
+            tracer.R_getContextCall(c);
         }
         RCaller rCaller = guaranteeInstanceOf(c, RCaller.class);
         if (rCaller == RCaller.topLevel) {
@@ -1421,9 +1462,10 @@ public class CallRFFIHelper {
         return RContext.getRRuntimeASTAccess().getSyntaxCaller(rCaller);
     }
 
-    public static Object R_getContextSrcRef(Object c) {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("getContextSrcRef", c);
+    @Override
+    public Object R_getContextSrcRef(Object c) {
+        if (tracer != null) {
+            tracer.R_getContextSrcRef(c);
         }
         Object o = R_getContextFun(c);
         if (!(o instanceof RFunction)) {
@@ -1438,94 +1480,133 @@ public class CallRFFIHelper {
 
     }
 
-    public static int R_insideBrowser() {
+    @Override
+    public int R_insideBrowser() {
+        if (tracer != null) {
+            tracer.R_insideBrowser();
+        }
         return RContext.getInstance().stateInstrumentation.getBrowserState().inBrowser() ? 1 : 0;
     }
 
-    public static int R_isGlobal(Object c) {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("isGlobal", c);
+    @Override
+    public int R_isGlobal(Object c) {
+        if (tracer != null) {
+            tracer.R_isGlobal(c);
         }
         RCaller rCaller = guaranteeInstanceOf(c, RCaller.class);
 
         return rCaller == RCaller.topLevel ? 1 : 0;
     }
 
-    public static int R_isEqual(Object x, Object y) {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("isEqual", x, y);
+    @Override
+    public int R_isEqual(Object x, Object y) {
+        if (tracer != null) {
+            tracer.R_isEqual(x, y);
         }
         return x == y ? 1 : 0;
     }
 
-    public static Object Rf_classgets(Object x, Object y) {
+    @Override
+    public Object Rf_classgets(Object x, Object y) {
+        if (tracer != null) {
+            tracer.Rf_classgets(x, y);
+        }
         RAbstractVector vector = guaranteeInstanceOf(x, RAbstractVector.class);
         vector.setClassAttr(guaranteeInstanceOf(y, RStringVector.class));
         return RNull.instance;
     }
 
-    public static RExternalPtr R_MakeExternalPtr(long addr, Object tag, Object prot) {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("R_MakeExternalPtr", addr, tag, prot);
+    @Override
+    public RExternalPtr R_MakeExternalPtr(long addr, Object tag, Object prot) {
+        if (tracer != null) {
+            tracer.R_MakeExternalPtr(addr, tag, prot);
         }
         return RDataFactory.createExternalPtr(new SymbolHandle(addr), tag, prot);
     }
 
-    public static long R_ExternalPtrAddr(Object x) {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("R_ExternalPtrAddr", x);
+    @Override
+    public long R_ExternalPtrAddr(Object x) {
+        if (tracer != null) {
+            tracer.R_ExternalPtrAddr(x);
         }
         RExternalPtr p = guaranteeInstanceOf(x, RExternalPtr.class);
         return p.getAddr().asAddress();
     }
 
-    public static Object R_ExternalPtrTag(Object x) {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("R_ExternalPtrTag", x);
+    @Override
+    public Object R_ExternalPtrTag(Object x) {
+        if (tracer != null) {
+            tracer.R_ExternalPtrTag(x);
         }
         RExternalPtr p = guaranteeInstanceOf(x, RExternalPtr.class);
         return p.getTag();
     }
 
-    public static Object R_ExternalPtrProt(Object x) {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("R_ExternalPtrProt", x);
+    @Override
+    public Object R_ExternalPtrProt(Object x) {
+        if (tracer != null) {
+            tracer.R_ExternalPtrProt(x);
         }
         RExternalPtr p = guaranteeInstanceOf(x, RExternalPtr.class);
         return p.getProt();
     }
 
-    public static void R_SetExternalPtrAddr(Object x, long addr) {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("R_SetExternalPtrAddr", x);
+    @Override
+    public void R_SetExternalPtrAddr(Object x, long addr) {
+        if (tracer != null) {
+            tracer.R_SetExternalPtrAddr(x, addr);
         }
         RExternalPtr p = guaranteeInstanceOf(x, RExternalPtr.class);
         p.setAddr(new SymbolHandle(addr));
     }
 
-    public static void R_SetExternalPtrTag(Object x, Object tag) {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("R_SetExternalPtrTag", x);
+    @Override
+    public void R_SetExternalPtrTag(Object x, Object tag) {
+        if (tracer != null) {
+            tracer.R_SetExternalPtrTag(x, tag);
         }
         RExternalPtr p = guaranteeInstanceOf(x, RExternalPtr.class);
         p.setTag(tag);
     }
 
-    public static void R_SetExternalPtrProt(Object x, Object prot) {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("R_ExternalPtrProt", x);
+    @Override
+    public void R_SetExternalPtrProt(Object x, Object prot) {
+        if (tracer != null) {
+            tracer.R_SetExternalPtrProt(x, prot);
         }
         RExternalPtr p = guaranteeInstanceOf(x, RExternalPtr.class);
         p.setProt(prot);
     }
 
-    public static REnvironment R_NewHashedEnv(REnvironment parent, String name, boolean hashed, int initialSize) {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("R_NewHashedEnv", parent, name, hashed, initialSize);
+    @Override
+    public REnvironment R_NewHashedEnv(REnvironment parent, String name, boolean hashed, int initialSize) {
+        if (tracer != null) {
+            tracer.R_NewHashedEnv(parent, name, hashed, initialSize);
         }
         REnvironment env = RDataFactory.createNewEnv(name, hashed, initialSize);
         RArguments.initializeEnclosingFrame(env.getFrame(), parent.getFrame());
         return env;
     }
 
+    // Implementation specific support
+
+    /**
+     * Called to possibly update the "complete" status on {@code x}. N.B. {@code x} may not be an
+     * object with a concrete {@code setComplete} method, e.g. see {@link #INTEGER(Object)}.
+     */
+    public void setComplete(Object x, boolean complete) {
+        // only care about concrete vectors
+        if (x instanceof RVector) {
+            ((RVector<?>) x).setComplete(complete);
+        }
+    }
+
+    /**
+     * Called when a {@link CharSXPWrapper} is expected and not found.
+     */
+    public void logNotCharSXPWrapper(Object x) {
+        System.out.println("object " + x);
+        System.out.println("class " + x.getClass());
+    }
+
 }
diff --git a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/JavaUpCallsRFFIFactory.java b/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/JavaUpCallsRFFIFactory.java
new file mode 100644
index 0000000000..d9666616f0
--- /dev/null
+++ b/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/JavaUpCallsRFFIFactory.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2014, 2016, 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;
+
+public class JavaUpCallsRFFIFactory extends UpCallsRFFIFactory {
+
+    @Override
+    public UpCallsRFFI getUpcallsRFFI() {
+        return new JavaUpCallsRFFI();
+    }
+
+}
diff --git a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/RFFIVariables.java b/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/RFFIVariables.java
index 0695d46b3a..77628a0b8d 100644
--- a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/RFFIVariables.java
+++ b/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/RFFIVariables.java
@@ -29,7 +29,6 @@ import com.oracle.truffle.r.runtime.data.RMissing;
 import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.RUnboundValue;
 import com.oracle.truffle.r.runtime.env.REnvironment;
-import com.oracle.truffle.r.runtime.ffi.jni.CallRFFIHelper;
 
 public enum RFFIVariables {
     R_Home(REnvVars.rHome()),
@@ -71,7 +70,7 @@ public enum RFFIVariables {
     R_dot_target(RDataFactory.createSymbol(".target")),
     R_SrcrefSymbol(RDataFactory.createSymbol("srcref")),
     R_SrcfileSymbol(RDataFactory.createSymbol("srcfile")),
-    R_NaString(CallRFFIHelper.createCharSXP(RRuntime.STRING_NA)),
+    R_NaString(CharSXPWrapper.create(RRuntime.STRING_NA)),
     R_NaN(Double.NaN),
     R_PosInf(Double.POSITIVE_INFINITY),
     R_NegInf(Double.NEGATIVE_INFINITY),
diff --git a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jni/JNI_Call.java b/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jni/JNI_Call.java
index 8ea6fac033..2108c61447 100644
--- a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jni/JNI_Call.java
+++ b/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jni/JNI_Call.java
@@ -36,6 +36,8 @@ import com.oracle.truffle.r.runtime.ffi.LibPaths;
 import com.oracle.truffle.r.runtime.ffi.NativeCallInfo;
 import com.oracle.truffle.r.runtime.ffi.RFFIUtils;
 import com.oracle.truffle.r.runtime.ffi.RFFIVariables;
+import com.oracle.truffle.r.runtime.ffi.UpCallsRFFI;
+import com.oracle.truffle.r.runtime.ffi.UpCallsRFFIFactory;
 
 /**
  * The only variety in the signatures for {@code .Call} is the number of arguments. GnuR supports a
@@ -80,7 +82,7 @@ public class JNI_Call implements CallRFFI {
             traceDownCall("initialize");
         }
         try {
-            initialize(RFFIVariables.values());
+            initialize(UpCallsRFFIFactory.getInstance().getUpcallsRFFI(), RFFIVariables.values());
         } finally {
             if (traceEnabled()) {
                 traceDownCallReturn("initialize", null);
@@ -122,7 +124,7 @@ public class JNI_Call implements CallRFFI {
         }
     }
 
-    private static native void initialize(RFFIVariables[] variables);
+    private static native void initialize(UpCallsRFFI upCallRFFI, RFFIVariables[] variables);
 
     private static native void nativeSetTempDir(String tempDir);
 
diff --git a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jni/TraceUpCallsAdapter.java b/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jni/TraceUpCallsAdapter.java
new file mode 100644
index 0000000000..461863208e
--- /dev/null
+++ b/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jni/TraceUpCallsAdapter.java
@@ -0,0 +1,893 @@
+/*
+ * Copyright (c) 2016, 2016, 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.jni;
+
+import com.oracle.truffle.r.runtime.data.RDoubleVector;
+import com.oracle.truffle.r.runtime.data.RExternalPtr;
+import com.oracle.truffle.r.runtime.data.RIntVector;
+import com.oracle.truffle.r.runtime.data.RLogicalVector;
+import com.oracle.truffle.r.runtime.data.RStringVector;
+import com.oracle.truffle.r.runtime.env.REnvironment;
+import com.oracle.truffle.r.runtime.ffi.RFFIUtils;
+import com.oracle.truffle.r.runtime.ffi.UpCallsRFFI;
+
+public class TraceUpCallsAdapter implements UpCallsRFFI {
+    @Override
+    public RIntVector Rf_ScalarInteger(int value) {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("Rf_ScalarInteger", value);
+        }
+        return null;
+    }
+
+    @Override
+    public RLogicalVector Rf_ScalarLogical(int value) {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("Rf_ScalarLogical", value);
+        }
+        return null;
+    }
+
+    @Override
+    public RDoubleVector Rf_ScalarDouble(double value) {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("Rf_ScalarDouble", value);
+        }
+        return null;
+    }
+
+    @Override
+    public RStringVector Rf_ScalarString(Object value) {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("Rf_ScalarString", value);
+        }
+        return null;
+    }
+
+    @Override
+    public int Rf_asInteger(Object x) {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("Rf_asInteger", x);
+        }
+        return 0;
+    }
+
+    @Override
+    public double Rf_asReal(Object x) {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("Rf_asReal", x);
+        }
+        return 0;
+    }
+
+    @Override
+    public int Rf_asLogical(Object x) {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("Rf_asLogical", x);
+        }
+        return 0;
+    }
+
+    @Override
+    public Object Rf_asChar(Object x) {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("Rf_asChar", x);
+        }
+        return null;
+    }
+
+    @Override
+    public Object Rf_mkCharLenCE(byte[] bytes, int encoding) {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("Rf_mkCharLenCE", bytes);
+        }
+        return null;
+    }
+
+    @Override
+    public Object Rf_cons(Object car, Object cdr) {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("Rf_cons", car, cdr);
+        }
+        return null;
+    }
+
+    @Override
+    public void Rf_defineVar(Object symbolArg, Object value, Object envArg) {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("Rf_defineVar", symbolArg, value, envArg);
+        }
+    }
+
+    @Override
+    public Object R_do_MAKE_CLASS(String clazz) {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("R_do_MAKE_CLASS", clazz);
+        }
+        return null;
+    }
+
+    @Override
+    public Object Rf_findVar(Object symbolArg, Object envArg) {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("Rf_findVar", symbolArg, envArg);
+        }
+        return null;
+    }
+
+    @Override
+    public Object Rf_findVarInFrame(Object envArg, Object symbolArg) {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("Rf_findVarInFrame", envArg, symbolArg);
+        }
+        return null;
+    }
+
+    @Override
+    public Object Rf_findVarInFrame3(Object envArg, Object symbolArg, int doGet) {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("Rf_findVarInFrame3", envArg, symbolArg);
+        }
+        return null;
+    }
+
+    @Override
+    public Object Rf_getAttrib(Object obj, Object name) {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("Rf_getAttrib", obj, name);
+        }
+        return null;
+    }
+
+    @Override
+    public void Rf_setAttrib(Object obj, Object name, Object val) {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("Rf_setAttrib", obj, name, val);
+        }
+    }
+
+    @Override
+    public int Rf_inherits(Object x, String clazz) {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("Rf_inherits", x, clazz);
+        }
+        return 0;
+    }
+
+    @Override
+    public Object Rf_install(String name) {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("Rf_install", name);
+        }
+        return null;
+    }
+
+    @Override
+    public Object Rf_lengthgets(Object x, int newSize) {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("Rf_lengthgets", x, newSize);
+        }
+        return null;
+    }
+
+    @Override
+    public int Rf_isString(Object x) {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("Rf_isString", x);
+        }
+        return 0;
+    }
+
+    @Override
+    public int Rf_isNull(Object x) {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("Rf_isNull", x);
+        }
+        return 0;
+    }
+
+    @Override
+    public Object Rf_PairToVectorList(Object x) {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("Rf_PairToVectorList", x);
+        }
+        return null;
+    }
+
+    @Override
+    public void Rf_error(String msg) {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("Rf_error", msg);
+        }
+    }
+
+    @Override
+    public void Rf_warning(String msg) {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("Rf_warning", msg);
+        }
+    }
+
+    @Override
+    public void Rf_warningcall(Object call, String msg) {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("Rf_warningcall", call, msg);
+        }
+    }
+
+    @Override
+    public Object Rf_allocateVector(int mode, int n) {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("Rf_allocateVector", mode, n);
+        }
+        return null;
+    }
+
+    @Override
+    public Object Rf_allocateArray(int mode, Object dimsObj) {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("Rf_allocateArray", mode, dimsObj);
+        }
+        return null;
+    }
+
+    @Override
+    public Object Rf_allocateMatrix(int mode, int nrow, int ncol) {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("Rf_allocateMatrix", mode, ncol, nrow);
+        }
+        return null;
+    }
+
+    @Override
+    public int Rf_nrows(Object x) {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("Rf_nrows", x);
+        }
+        return 0;
+    }
+
+    @Override
+    public int Rf_ncols(Object x) {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("Rf_ncols", x);
+        }
+        return 0;
+    }
+
+    @Override
+    public int LENGTH(Object x) {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("LENGTH", x);
+        }
+        return 0;
+    }
+
+    @Override
+    public void SET_STRING_ELT(Object x, int i, Object v) {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("SET_STRING_ELT", x, i, v);
+        }
+    }
+
+    @Override
+    public void SET_VECTOR_ELT(Object x, int i, Object v) {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("SET_VECTOR_ELT", i, v);
+        }
+    }
+
+    @Override
+    public byte[] RAW(Object x) {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("RAW", x);
+        }
+        return null;
+    }
+
+    @Override
+    public byte[] LOGICAL(Object x) {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("LOGICAL", x);
+        }
+        return null;
+    }
+
+    @Override
+    public int[] INTEGER(Object x) {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("INTEGER", x);
+        }
+        return null;
+    }
+
+    @Override
+    public double[] REAL(Object x) {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("REAL", x);
+        }
+        return null;
+    }
+
+    @Override
+    public Object STRING_ELT(Object x, int i) {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("STRING_ELT", x, i);
+        }
+        return null;
+    }
+
+    @Override
+    public Object VECTOR_ELT(Object x, int i) {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("VECTOR_ELT", x, i);
+        }
+        return null;
+    }
+
+    @Override
+    public int NAMED(Object x) {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("NAMED", x);
+        }
+        return 0;
+    }
+
+    @Override
+    public Object SET_TYPEOF_FASTR(Object x, int v) {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("SET_TYPEOF_FASTR", x, v);
+        }
+        return null;
+    }
+
+    @Override
+    public int TYPEOF(Object x) {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("TYPEOF", x);
+        }
+        return 0;
+    }
+
+    @Override
+    public int OBJECT(Object x) {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("OBJECT", x);
+        }
+        return 0;
+    }
+
+    @Override
+    public Object Rf_duplicate(Object x, int deep) {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("Rf_duplicate", x, deep);
+        }
+        return null;
+    }
+
+    @Override
+    public int Rf_anyDuplicated(Object x, int fromLast) {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("Rf_anyDuplicated", x, fromLast);
+        }
+        return 0;
+    }
+
+    @Override
+    public Object PRINTNAME(Object x) {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("PRINTNAME", x);
+        }
+        return null;
+    }
+
+    @Override
+    public Object TAG(Object e) {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("TAG", e);
+        }
+        return null;
+    }
+
+    @Override
+    public Object CAR(Object e) {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("CAR", e);
+        }
+        return null;
+    }
+
+    @Override
+    public Object CDR(Object e) {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("CDR", e);
+        }
+        return null;
+    }
+
+    @Override
+    public Object CADR(Object e) {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("CADR", e);
+        }
+        return null;
+    }
+
+    @Override
+    public Object CADDR(Object e) {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("CADDR", e);
+        }
+        return null;
+    }
+
+    @Override
+    public Object CDDR(Object e) {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("CDDR", e);
+        }
+        return null;
+    }
+
+    @Override
+    public Object SET_TAG(Object x, Object y) {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("SET_TAG", x, y);
+        }
+        return null;
+    }
+
+    @Override
+    public Object SETCAR(Object x, Object y) {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("SETCAR", x, y);
+        }
+        return null;
+    }
+
+    @Override
+    public Object SETCDR(Object x, Object y) {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("SETCDR", x, y);
+        }
+        return null;
+    }
+
+    @Override
+    public Object SETCADR(Object x, Object y) {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("SETCADR", x);
+        }
+        return null;
+    }
+
+    @Override
+    public Object SYMVALUE(Object x) {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("SYMVALUE", x);
+        }
+        return null;
+    }
+
+    @Override
+    public void SET_SYMVALUE(Object x, Object v) {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("SET_SYMVALUE", x, v);
+        }
+    }
+
+    @Override
+    public int R_BindingIsLocked(Object sym, Object env) {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("R_BindingIsLocked", sym, env);
+        }
+        return 0;
+    }
+
+    @Override
+    public Object R_FindNamespace(Object name) {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("R_FindNamespace", name);
+        }
+        return null;
+    }
+
+    @Override
+    public Object Rf_eval(Object expr, Object env) {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("Rf_eval", expr, env);
+        }
+        return null;
+    }
+
+    @Override
+    public Object Rf_findfun(Object symbolObj, Object envObj) {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("Rf_findfun", symbolObj, envObj);
+        }
+        return null;
+    }
+
+    @Override
+    public Object Rf_GetOption1(Object tag) {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("Rf_GetOption1", tag);
+        }
+        return null;
+    }
+
+    @Override
+    public void Rf_gsetVar(Object symbol, Object value, Object rho) {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("Rf_gsetVar", symbol, value, rho);
+        }
+    }
+
+    @Override
+    public void DUPLICATE_ATTRIB(Object to, Object from) {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("DUPLICATE_ATTRIB", to, from);
+        }
+    }
+
+    @Override
+    public int R_computeIdentical(Object x, Object y, int flags) {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("R_computeIdentical", x, y, flags);
+        }
+        return 0;
+    }
+
+    @Override
+    public void Rf_copyListMatrix(Object s, Object t, int byrow) {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("Rf_copyListMatrix", t, byrow);
+        }
+    }
+
+    @Override
+    public void Rf_copyMatrix(Object s, Object t, int byrow) {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("Rf_copyMatrix", t, byrow);
+        }
+    }
+
+    @Override
+    public Object R_tryEval(Object expr, Object env, boolean silent) {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("R_tryEval", expr, env, silent);
+        }
+        return null;
+    }
+
+    @Override
+    public Object R_ToplevelExec() {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("R_TopLevelExec");
+        }
+        return null;
+    }
+
+    @Override
+    public int RDEBUG(Object x) {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("RDEBUG", x);
+        }
+        return 0;
+    }
+
+    @Override
+    public void SET_RDEBUG(Object x, int v) {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("SET_RDEBUG", x, v);
+        }
+    }
+
+    @Override
+    public int RSTEP(Object x) {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("RSTEP", x);
+        }
+        return 0;
+    }
+
+    @Override
+    public void SET_RSTEP(Object x, int v) {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("SET_RSTEP", x, v);
+        }
+    }
+
+    @Override
+    public Object ENCLOS(Object x) {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("ENCLOS", x);
+        }
+        return null;
+    }
+
+    @Override
+    public Object PRVALUE(Object x) {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("PRVALUE", x);
+        }
+        return null;
+    }
+
+    @Override
+    public Object R_ParseVector(Object text, int n, Object srcFile) {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("R_ParseVector", text, n, srcFile);
+        }
+        return null;
+    }
+
+    @Override
+    public Object R_lsInternal3(Object envArg, int allArg, int sortedArg) {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("R_lsInternal3", envArg, allArg, sortedArg);
+        }
+        return null;
+    }
+
+    @Override
+    public String R_HomeDir() {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("R_HomeDir");
+        }
+        return null;
+    }
+
+    @Override
+    public void R_CleanUp(int sa, int status, int runlast) {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("R_Cleanup", sa, status, runlast);
+        }
+    }
+
+    @Override
+    public Object R_GlobalContext() {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("R_GlobalContext");
+        }
+        return null;
+    }
+
+    @Override
+    public Object R_GlobalEnv() {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("R_GlobalEnv");
+        }
+        return null;
+    }
+
+    @Override
+    public Object R_BaseEnv() {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("R_BaseEnv");
+        }
+        return null;
+    }
+
+    @Override
+    public Object R_BaseNamespace() {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("R_BaseNamespace");
+        }
+        return null;
+    }
+
+    @Override
+    public Object R_NamespaceRegistry() {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("R_NamespaceRegistry");
+        }
+        return null;
+    }
+
+    @Override
+    public int isInteractive() {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("isInteractive");
+        }
+        return 0;
+    }
+
+    @Override
+    public int isS4Object(Object x) {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("isS4Object");
+        }
+        return 0;
+    }
+
+    @Override
+    public void Rprintf(String message) {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("Rprintf", message);
+        }
+    }
+
+    @Override
+    public void GetRNGstate() {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("GetRNGstate");
+        }
+    }
+
+    @Override
+    public void PutRNGstate() {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("PutRNGstate");
+        }
+    }
+
+    @Override
+    public double unif_rand() {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("unif_rand");
+        }
+        return 0;
+    }
+
+    @Override
+    public Object R_getGlobalFunctionContext() {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("R_getGlobalFunctionContext");
+        }
+        return null;
+    }
+
+    @Override
+    public Object R_getParentFunctionContext(Object c) {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("R_getParentFunctionContext");
+        }
+        return null;
+    }
+
+    @Override
+    public Object R_getContextEnv(Object c) {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("R_getContextEnv", c);
+        }
+        return null;
+    }
+
+    @Override
+    public Object R_getContextFun(Object c) {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("R_getContextFun", c);
+        }
+        return null;
+    }
+
+    @Override
+    public Object R_getContextCall(Object c) {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("R_getContextCall", c);
+        }
+        return null;
+    }
+
+    @Override
+    public Object R_getContextSrcRef(Object c) {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("R_getContextSrcRef", c);
+        }
+        return null;
+    }
+
+    @Override
+    public int R_insideBrowser() {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("R_insideBrowser");
+        }
+        return 0;
+    }
+
+    @Override
+    public int R_isGlobal(Object c) {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("R_isGlobal", c);
+        }
+        return 0;
+    }
+
+    @Override
+    public int R_isEqual(Object x, Object y) {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("isEqual", x, y);
+        }
+        return 0;
+    }
+
+    @Override
+    public Object Rf_classgets(Object x, Object y) {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("Rf_classgets", x, y);
+        }
+        return null;
+    }
+
+    @Override
+    public RExternalPtr R_MakeExternalPtr(long addr, Object tag, Object prot) {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("R_MakeExternalPtr", addr, tag, prot);
+        }
+        return null;
+    }
+
+    @Override
+    public long R_ExternalPtrAddr(Object x) {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("R_ExternalPtrAddr", x);
+        }
+        return 0;
+    }
+
+    @Override
+    public Object R_ExternalPtrTag(Object x) {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("R_ExternalPtrTag", x);
+        }
+        return null;
+    }
+
+    @Override
+    public Object R_ExternalPtrProt(Object x) {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("R_ExternalPtrProt", x);
+        }
+        return null;
+    }
+
+    @Override
+    public void R_SetExternalPtrAddr(Object x, long addr) {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("R_SetExternalPtrAddr", x);
+        }
+    }
+
+    @Override
+    public void R_SetExternalPtrTag(Object x, Object tag) {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("R_SetExternalPtrTag", x);
+        }
+    }
+
+    @Override
+    public void R_SetExternalPtrProt(Object x, Object prot) {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("R_ExternalPtrProt", x);
+        }
+    }
+
+    @Override
+    public REnvironment R_NewHashedEnv(REnvironment parent, String name, boolean hashed, int initialSize) {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("R_NewHashedEnv", parent, name, hashed, initialSize);
+        }
+        return null;
+    }
+}
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/CharSXPWrapper.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/CharSXPWrapper.java
new file mode 100644
index 0000000000..f7e83255eb
--- /dev/null
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/CharSXPWrapper.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2014, 2016, 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;
+
+/**
+ * Internally GNU R distinguishes "strings" and "vectors of strings" using the {@code CHARSXP} and
+ * {@code STRSXP} types, respectively. Although this difference is invisible at the R level, it
+ * manifests itself in the R FFI as several functions traffic in the {@code CHARSXP} type. Since
+ * FastR already uses {@code String} to denote a length-1 string vector, it cannot be used to
+ * represent a {@code CHARSXP}, so this class exists to do so.
+ *
+ * N.B. Use limited to RFFI implementations.
+ *
+ */
+public final class CharSXPWrapper {
+    private final String contents;
+
+    private CharSXPWrapper(String contents) {
+        this.contents = contents;
+    }
+
+    public String getContents() {
+        return contents;
+    }
+
+    @Override
+    public String toString() {
+        return "CHARSXP(" + contents + ")";
+    }
+
+    public static Object create(String contents) {
+        return new CharSXPWrapper(contents);
+    }
+
+}
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/ParseResult.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/ParseResult.java
new file mode 100644
index 0000000000..d4c71e01bd
--- /dev/null
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/ParseResult.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2014, 2016, 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;
+
+/**
+ * Used in implementation of {@link UpCallsRFFI#R_ParseVector(Object, int, Object)}.
+ */
+public class ParseResult {
+    public enum ParseStatus {
+        PARSE_NULL,
+        PARSE_OK,
+        PARSE_INCOMPLETE,
+        PARSE_ERROR,
+        PARSE_EOF
+    }
+
+    @SuppressWarnings("unused") private final int parseStatus;
+    @SuppressWarnings("unused") private final Object expr;
+
+    ParseResult(int parseStatus, Object expr) {
+        this.parseStatus = parseStatus;
+        this.expr = expr;
+    }
+}
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 99378fa1af..ccc6224ba1 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
@@ -42,6 +42,9 @@ package com.oracle.truffle.r.runtime.ffi;
  * <li>{@link DLLRFFI}: interface to dll functions, e.g., {@code dlopen}</li>
  * <li>{@link REmbedRFFI}: interface to embedded support</li>
  * <li>{@link MiscRFFI}: interface to miscellaneous native functions</li>
+ * <li>{@link UpCallsRFFI}: interface that defines the set of upcalls from native code (resulting
+ * from {@link CallRFFI}). There is no public access to this interface as it should never be called
+ * from FastR Java code and is always implemented by a specific FFI factory.
  * </ul>
  *
  * These interfaces may be implemented by one or more providers, specified either when the FastR
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/UpCallsRFFI.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/UpCallsRFFI.java
new file mode 100644
index 0000000000..d643ba908f
--- /dev/null
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/UpCallsRFFI.java
@@ -0,0 +1,279 @@
+/*
+ * Copyright (c) 2014, 2016, 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.data.RDoubleVector;
+import com.oracle.truffle.r.runtime.data.RExternalPtr;
+import com.oracle.truffle.r.runtime.data.RIntVector;
+import com.oracle.truffle.r.runtime.data.RLogicalVector;
+import com.oracle.truffle.r.runtime.data.RStringVector;
+import com.oracle.truffle.r.runtime.env.REnvironment;
+
+/**
+ * This class defines methods that match the functionality of the macro/function definitions in the
+ * R header files, e.g. {@code Rinternals.h} that are used by C/C++ code to call into the R
+ * implementation. For ease of identification, we use method names that match the names in the R
+ * header files. These methods should never be called from normal FastR code.
+ *
+ * The set is incomplete; these are the functions that have been found to be used to at this time of
+ * writing. From the GNU R perspective all {@code Object} parameters are {@code SEXP} instances.
+ * Some of the functions are typed with a specific return type but, again, this is a {@code SEXP} in
+ * GNU R terms. The native side does not require a specific Java type.
+ *
+ */
+public interface UpCallsRFFI {
+
+    // Checkstyle: stop method name check
+
+    RIntVector Rf_ScalarInteger(int value);
+
+    RLogicalVector Rf_ScalarLogical(int value);
+
+    RDoubleVector Rf_ScalarDouble(double value);
+
+    RStringVector Rf_ScalarString(Object value);
+
+    int Rf_asInteger(Object x);
+
+    double Rf_asReal(Object x);
+
+    int Rf_asLogical(Object x);
+
+    Object Rf_asChar(Object x);
+
+    Object Rf_mkCharLenCE(byte[] bytes, int encoding);
+
+    Object Rf_cons(Object car, Object cdr);
+
+    void Rf_defineVar(Object symbolArg, Object value, Object envArg);
+
+    Object R_do_MAKE_CLASS(String clazz);
+
+    /**
+     * WARNING: argument order reversed from Rf_findVarInFrame!
+     */
+    Object Rf_findVar(Object symbolArg, Object envArg);
+
+    Object Rf_findVarInFrame(Object envArg, Object symbolArg);
+
+    Object Rf_findVarInFrame3(Object envArg, Object symbolArg, int doGet);
+
+    Object Rf_getAttrib(Object obj, Object name);
+
+    void Rf_setAttrib(Object obj, Object name, Object val);
+
+    int Rf_inherits(Object x, String clazz);
+
+    Object Rf_install(String name);
+
+    Object Rf_lengthgets(Object x, int newSize);
+
+    int Rf_isString(Object x);
+
+    int Rf_isNull(Object x);
+
+    Object Rf_PairToVectorList(Object x);
+
+    void Rf_error(String msg);
+
+    void Rf_warning(String msg);
+
+    void Rf_warningcall(Object call, String msg);
+
+    Object Rf_allocateVector(int mode, int n);
+
+    Object Rf_allocateArray(int mode, Object dimsObj);
+
+    Object Rf_allocateMatrix(int mode, int nrow, int ncol);
+
+    int Rf_nrows(Object x);
+
+    int Rf_ncols(Object x);
+
+    int LENGTH(Object x);
+
+    void SET_STRING_ELT(Object x, int i, Object v);
+
+    void SET_VECTOR_ELT(Object x, int i, Object v);
+
+    byte[] RAW(Object x);
+
+    byte[] LOGICAL(Object x);
+
+    int[] INTEGER(Object x);
+
+    double[] REAL(Object x);
+
+    Object STRING_ELT(Object x, int i);
+
+    Object VECTOR_ELT(Object x, int i);
+
+    int NAMED(Object x);
+
+    Object SET_TYPEOF_FASTR(Object x, int v);
+
+    int TYPEOF(Object x);
+
+    int OBJECT(Object x);
+
+    Object Rf_duplicate(Object x, int deep);
+
+    int Rf_anyDuplicated(Object x, int fromLast);
+
+    Object PRINTNAME(Object x);
+
+    Object TAG(Object e);
+
+    Object CAR(Object e);
+
+    Object CDR(Object e);
+
+    Object CADR(Object e);
+
+    Object CADDR(Object e);
+
+    Object CDDR(Object e);
+
+    Object SET_TAG(Object x, Object y);
+
+    Object SETCAR(Object x, Object y);
+
+    Object SETCDR(Object x, Object y);
+
+    Object SETCADR(Object x, Object y);
+
+    Object SYMVALUE(Object x);
+
+    void SET_SYMVALUE(Object x, Object v);
+
+    int R_BindingIsLocked(Object sym, Object env);
+
+    Object R_FindNamespace(Object name);
+
+    Object Rf_eval(Object expr, Object env);
+
+    Object Rf_findfun(Object symbolObj, Object envObj);
+
+    Object Rf_GetOption1(Object tag);
+
+    void Rf_gsetVar(Object symbol, Object value, Object rho);
+
+    void DUPLICATE_ATTRIB(Object to, Object from);
+
+    int R_computeIdentical(Object x, Object y, int flags);
+
+    void Rf_copyListMatrix(Object s, Object t, int byrow);
+
+    void Rf_copyMatrix(Object s, Object t, int byrow);
+
+    Object R_tryEval(Object expr, Object env, boolean silent);
+
+    Object R_ToplevelExec();
+
+    int RDEBUG(Object x);
+
+    void SET_RDEBUG(Object x, int v);
+
+    int RSTEP(Object x);
+
+    void SET_RSTEP(Object x, int v);
+
+    Object ENCLOS(Object x);
+
+    Object PRVALUE(Object x);
+
+    Object R_ParseVector(Object text, int n, Object srcFile);
+
+    Object R_lsInternal3(Object envArg, int allArg, int sortedArg);
+
+    String R_HomeDir();
+
+    int isInteractive();
+
+    int isS4Object(Object x);
+
+    void Rprintf(String message);
+
+    void GetRNGstate();
+
+    void PutRNGstate();
+
+    double unif_rand();
+
+    Object Rf_classgets(Object x, Object y);
+
+    RExternalPtr R_MakeExternalPtr(long addr, Object tag, Object prot);
+
+    long R_ExternalPtrAddr(Object x);
+
+    Object R_ExternalPtrTag(Object x);
+
+    Object R_ExternalPtrProt(Object x);
+
+    void R_SetExternalPtrAddr(Object x, long addr);
+
+    void R_SetExternalPtrTag(Object x, Object tag);
+
+    void R_SetExternalPtrProt(Object x, Object prot);
+
+    void R_CleanUp(int sa, int status, int runlast);
+
+    REnvironment R_NewHashedEnv(REnvironment parent, String name, boolean hashed, int initialSize);
+
+    /*
+     * The following functions are global variables in the standard R FFI. However, owing to the
+     * support for virtual R sessions (see .fastr.context) in FastR they are remapped as functions.
+     */
+    Object R_GlobalContext();
+
+    Object R_GlobalEnv();
+
+    Object R_BaseEnv();
+
+    Object R_BaseNamespace();
+
+    Object R_NamespaceRegistry();
+
+    /*
+     * The following functions are FastR extensions to support RStudio
+     */
+
+    Object R_getGlobalFunctionContext();
+
+    Object R_getParentFunctionContext(Object c);
+
+    Object R_getContextEnv(Object c);
+
+    Object R_getContextFun(Object c);
+
+    Object R_getContextCall(Object c);
+
+    Object R_getContextSrcRef(Object c);
+
+    int R_insideBrowser();
+
+    int R_isGlobal(Object c);
+
+    int R_isEqual(Object x, Object y);
+
+}
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/UpCallsRFFIFactory.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/UpCallsRFFIFactory.java
new file mode 100644
index 0000000000..ad90095e5d
--- /dev/null
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/UpCallsRFFIFactory.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2016, 2016, 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;
+
+public abstract class UpCallsRFFIFactory {
+    static {
+        final String prop = System.getProperty("fastr.upcalls.factory.class", "com.oracle.truffle.r.runtime.ffi.JavaUpCallsRFFIFactory");
+        try {
+            theInstance = (UpCallsRFFIFactory) Class.forName(prop).newInstance();
+        } catch (Exception ex) {
+            // CheckStyle: stop system..print check
+            System.err.println("Failed to instantiate class: " + prop);
+        }
+    }
+
+    private static UpCallsRFFIFactory theInstance;
+
+    public static UpCallsRFFIFactory getInstance() {
+        return theInstance;
+    }
+
+    public abstract UpCallsRFFI getUpcallsRFFI();
+
+}
-- 
GitLab