From fbcaea7c6dc82889034e639d6d332a46517c2c54 Mon Sep 17 00:00:00 2001
From: Mick Jordan <mick.jordan@oracle.com>
Date: Tue, 9 Jun 2015 19:53:32 -0700
Subject: [PATCH] support errors from FFI code

---
 .../fficall/jni/src/rf_functions.c            |  19 ++-
 .../fficall/jni/src/rfficall.c                | 110 ++++++++++++------
 .../fficall/jni/src/rffiutils.c               |  31 ++++-
 .../fficall/jni/src/rffiutils.h               |   5 +-
 .../fficall/jni/src/vectoraccess.c            |  51 +++++---
 .../r/nodes/builtin/base/foreign/DotC.java    |   5 -
 .../base/foreign/ForeignFunctions.java        |   7 +-
 .../r/nodes/instrument/debug/Browser.java     |   9 +-
 .../r/runtime/ffi/gnfi/GNFI_RFFIFactory.java  |   5 +
 .../r/runtime/ffi/jnr/CRFFI_JNR_Invoke.java   |  10 +-
 .../r/runtime/ffi/jnr/CallRFFIHelper.java     |  27 ++++-
 .../r/runtime/ffi/jnr/CallRFFIWithJNI.java    |   9 +-
 .../com/oracle/truffle/r/runtime/RError.java  |   1 -
 .../oracle/truffle/r/runtime/ffi/CRFFI.java   |   5 +-
 .../truffle/r/runtime/ffi/CallRFFI.java       |   8 +-
 .../truffle/r/runtime/ffi/RFFIFactory.java    |   4 +
 .../packages/testrffi/testrffi/R/testrffi.R   |   4 +
 .../packages/testrffi/testrffi/src/testrffi.c |   4 +
 18 files changed, 218 insertions(+), 96 deletions(-)

diff --git a/com.oracle.truffle.r.native/fficall/jni/src/rf_functions.c b/com.oracle.truffle.r.native/fficall/jni/src/rf_functions.c
index 60b33b839e..bd385fff86 100644
--- a/com.oracle.truffle.r.native/fficall/jni/src/rf_functions.c
+++ b/com.oracle.truffle.r.native/fficall/jni/src/rf_functions.c
@@ -42,6 +42,7 @@ static jmethodID Rf_setAttribMethodID;
 static jmethodID Rf_isStringMethodID;
 static jmethodID Rf_isNullMethodID;
 static jmethodID Rf_warningMethodID;
+static jmethodID Rf_errorMethodID;
 static jmethodID Rf_NewHashedEnvMethodID;
 
 void init_rf_functions(JNIEnv *env) {
@@ -56,6 +57,7 @@ void init_rf_functions(JNIEnv *env) {
 	Rf_isStringMethodID = checkGetMethodID(env, CallRFFIHelperClass, "Rf_isString", "(Ljava/lang/Object;)I", 1);
 	Rf_isNullMethodID = checkGetMethodID(env, CallRFFIHelperClass, "Rf_isNull", "(Ljava/lang/Object;)I", 1);
 	Rf_warningMethodID = checkGetMethodID(env, CallRFFIHelperClass, "Rf_warning", "(Ljava/lang/String;)V", 1);
+	Rf_errorMethodID = checkGetMethodID(env, CallRFFIHelperClass, "Rf_error", "(Ljava/lang/String;)V", 1);
 	createIntArrayMethodID = checkGetMethodID(env, RDataFactoryClass, "createIntVector", "(I)Lcom/oracle/truffle/r/runtime/data/RIntVector;", 1);
 	createDoubleArrayMethodID = checkGetMethodID(env, RDataFactoryClass, "createDoubleVector", "(I)Lcom/oracle/truffle/r/runtime/data/RDoubleVector;", 1);
 	createStringArrayMethodID = checkGetMethodID(env, RDataFactoryClass, "createStringVector", "(I)Lcom/oracle/truffle/r/runtime/data/RStringVector;", 1);
@@ -202,7 +204,21 @@ void Rf_unprotect_ptr(SEXP x) {
 }
 
 void Rf_error(const char *msg, ...) {
-	unimplemented("Rf_error");
+	// TODO if msg is a format string how do we get the args given the number is determined by the format?
+	// This is a bit tricky. The usual error handling model in Java is "throw RError.error(...)" but
+	// 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
+	// exception (whatever it is) is observed by JNI, he call to Rf_error will return where we do a
+	// non-local transfer of control back to the entry point (which will cleanup).
+	JNIEnv *thisenv = getEnv();
+	jstring string = (*thisenv)->NewStringUTF(thisenv, msg);
+	// This will set a pending exception
+	(*thisenv)->CallStaticObjectMethod(thisenv, CallRFFIHelperClass, Rf_errorMethodID, string);
+	// just transfer back which will cleanup and exit the entire JNI call
+	longjmp(*getErrorJmpBuf(), 1);
+
 }
 
 void Rf_warningcall(SEXP x, const char *msg, ...) {
@@ -210,6 +226,7 @@ void Rf_warningcall(SEXP x, const char *msg, ...) {
 }
 
 void Rf_warning(const char *msg, ...) {
+	// TODO if msg is a format string how do we get the args given the number is determined by the format?
 	JNIEnv *thisenv = getEnv();
 	jstring string = (*thisenv)->NewStringUTF(thisenv, msg);
 	(*thisenv)->CallStaticObjectMethod(thisenv, CallRFFIHelperClass, Rf_warningMethodID, string);
diff --git a/com.oracle.truffle.r.native/fficall/jni/src/rfficall.c b/com.oracle.truffle.r.native/fficall/jni/src/rfficall.c
index 93090ab33f..a7680e164b 100644
--- a/com.oracle.truffle.r.native/fficall/jni/src/rfficall.c
+++ b/com.oracle.truffle.r.native/fficall/jni/src/rfficall.c
@@ -23,6 +23,7 @@
 
 #include "rffiutils.h"
 #include <string.h>
+#include <setjmp.h>
 
 JNIEXPORT void JNICALL
 Java_com_oracle_truffle_r_runtime_ffi_jnr_CallRFFIWithJNI_initialize(JNIEnv *env, jclass c,
@@ -39,6 +40,7 @@ Java_com_oracle_truffle_r_runtime_ffi_jnr_CallRFFIWithJNI_initialize(JNIEnv *env
 	init_listaccess(env);
 }
 
+static jmp_buf error_jmpbuf;
 
 // Boilerplate methods for the actual calls
 
@@ -56,27 +58,36 @@ typedef SEXP (*call10func)(SEXP arg1, SEXP arg2, SEXP arg3, SEXP arg4, SEXP arg5
 
 JNIEXPORT jobject JNICALL
 Java_com_oracle_truffle_r_runtime_ffi_jnr_CallRFFIWithJNI_call0(JNIEnv *env, jclass c, jlong address) {
-	callEnter(env);
-	call0func call0 = (call0func) address;
-	jobject result = (*call0)();
+	jobject result = NULL;
+	callEnter(env, &error_jmpbuf);
+	if (!setjmp(error_jmpbuf)) {
+		call0func call0 = (call0func) address;
+		result = (*call0)();
+	}
 	callExit(env);
 	return result;
 }
 
 JNIEXPORT jobject JNICALL
 Java_com_oracle_truffle_r_runtime_ffi_jnr_CallRFFIWithJNI_call1(JNIEnv *env, jclass c, jlong address, jobject arg1) {
-	callEnter(env);
-	call1func call1 = (call1func) address;
-	jobject result = (*call1)(arg1);
+	jobject result = NULL;
+	callEnter(env, &error_jmpbuf);
+	if (!setjmp(error_jmpbuf)) {
+		call1func call1 = (call1func) address;
+		result = (*call1)(arg1);
+	}
 	callExit(env);
 	return result;
 }
 
 JNIEXPORT jobject JNICALL
 Java_com_oracle_truffle_r_runtime_ffi_jnr_CallRFFIWithJNI_call2(JNIEnv *env, jclass c, jlong address, jobject arg1, jobject arg2) {
-	callEnter(env);
-	call2func call2 = (call2func) address;
-	jobject result = (*call2)(arg1, arg2);
+	jobject result = NULL;
+	callEnter(env, &error_jmpbuf);
+	if (!setjmp(error_jmpbuf)) {
+		call2func call2 = (call2func) address;
+		result = (*call2)(arg1, arg2);
+	}
 	callExit(env);
 	return result;
 }
@@ -84,9 +95,12 @@ Java_com_oracle_truffle_r_runtime_ffi_jnr_CallRFFIWithJNI_call2(JNIEnv *env, jcl
 JNIEXPORT jobject JNICALL
 Java_com_oracle_truffle_r_runtime_ffi_jnr_CallRFFIWithJNI_call3(JNIEnv *env, jclass c, jlong address, jobject arg1, jobject arg2,
 		jobject arg3) {
-	callEnter(env);
-	call3func call3 = (call3func) address;
-	jobject result = (*call3)(arg1, arg2, arg3);
+	jobject result = NULL;
+	callEnter(env, &error_jmpbuf);
+	if (!setjmp(error_jmpbuf)) {
+		call3func call3 = (call3func) address;
+		result = (*call3)(arg1, arg2, arg3);
+	}
 	callExit(env);
 	return result;
 }
@@ -94,9 +108,12 @@ Java_com_oracle_truffle_r_runtime_ffi_jnr_CallRFFIWithJNI_call3(JNIEnv *env, jcl
 JNIEXPORT jobject JNICALL
 Java_com_oracle_truffle_r_runtime_ffi_jnr_CallRFFIWithJNI_call4(JNIEnv *env, jclass c, jlong address, jobject arg1, jobject arg2,
 		jobject arg3, jobject arg4) {
-	callEnter(env);
-	call4func call4 = (call4func) address;
-	jobject result = (*call4)(arg1, arg2, arg3, arg4);
+	jobject result = NULL;
+	callEnter(env, &error_jmpbuf);
+	if (!setjmp(error_jmpbuf)) {
+		call4func call4 = (call4func) address;
+		result = (*call4)(arg1, arg2, arg3, arg4);
+	}
 	callExit(env);
 	return result;
 }
@@ -104,9 +121,12 @@ Java_com_oracle_truffle_r_runtime_ffi_jnr_CallRFFIWithJNI_call4(JNIEnv *env, jcl
 JNIEXPORT jobject JNICALL
 Java_com_oracle_truffle_r_runtime_ffi_jnr_CallRFFIWithJNI_call5(JNIEnv *env, jclass c, jlong address, jobject arg1, jobject arg2,
 		jobject arg3, jobject arg4, jobject arg5) {
-	setEnv(env);
-	call5func call5 = (call5func) address;
-	jobject result = (*call5)(arg1, arg2, arg3, arg4, arg5);
+	jobject result = NULL;
+	callEnter(env, &error_jmpbuf);
+	if (!setjmp(error_jmpbuf)) {
+		call5func call5 = (call5func) address;
+		result = (*call5)(arg1, arg2, arg3, arg4, arg5);
+	}
 	callExit(env);
 	return result;
 }
@@ -114,9 +134,12 @@ Java_com_oracle_truffle_r_runtime_ffi_jnr_CallRFFIWithJNI_call5(JNIEnv *env, jcl
 JNIEXPORT jobject JNICALL
 Java_com_oracle_truffle_r_runtime_ffi_jnr_CallRFFIWithJNI_call6(JNIEnv *env, jclass c, jlong address, jobject arg1, jobject arg2,
 		jobject arg3, jobject arg4, jobject arg5, jobject arg6) {
-	callEnter(env);
-	call6func call6 = (call6func) address;
-	jobject result = (*call6)(arg1, arg2, arg3, arg4, arg5, arg6);
+	jobject result = NULL;
+	callEnter(env, &error_jmpbuf);
+	if (!setjmp(error_jmpbuf)) {
+		call6func call6 = (call6func) address;
+		result = (*call6)(arg1, arg2, arg3, arg4, arg5, arg6);
+	}
 	callExit(env);
 	return result;
 }
@@ -124,9 +147,12 @@ Java_com_oracle_truffle_r_runtime_ffi_jnr_CallRFFIWithJNI_call6(JNIEnv *env, jcl
 JNIEXPORT jobject JNICALL
 Java_com_oracle_truffle_r_runtime_ffi_jnr_CallRFFIWithJNI_call7(JNIEnv *env, jclass c, jlong address, jobject arg1, jobject arg2,
 		jobject arg3, jobject arg4, jobject arg5, jobject arg6, jobject arg7) {
-	callEnter(env);
-	call7func call7 = (call7func) address;
-	jobject result = (*call7)(arg1, arg2, arg3, arg4, arg5, arg6, arg7);
+	jobject result = NULL;
+	callEnter(env, &error_jmpbuf);
+	if (!setjmp(error_jmpbuf)) {
+		call7func call7 = (call7func) address;
+		result = (*call7)(arg1, arg2, arg3, arg4, arg5, arg6, arg7);
+	}
 	callExit(env);
 	return result;
 }
@@ -134,9 +160,12 @@ Java_com_oracle_truffle_r_runtime_ffi_jnr_CallRFFIWithJNI_call7(JNIEnv *env, jcl
 JNIEXPORT jobject JNICALL
 Java_com_oracle_truffle_r_runtime_ffi_jnr_CallRFFIWithJNI_call8(JNIEnv *env, jclass c, jlong address, jobject arg1, jobject arg2,
 		jobject arg3, jobject arg4, jobject arg5, jobject arg6, jobject arg7, jobject arg8) {
-	callEnter(env);
-	call8func call8 = (call8func) address;
-	jobject result = (*call8)(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8);
+	jobject result = NULL;
+	callEnter(env, &error_jmpbuf);
+	if (!setjmp(error_jmpbuf)) {
+		call8func call8 = (call8func) address;
+		result = (*call8)(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8);
+	}
 	callExit(env);
 	return result;
 }
@@ -144,19 +173,24 @@ Java_com_oracle_truffle_r_runtime_ffi_jnr_CallRFFIWithJNI_call8(JNIEnv *env, jcl
 JNIEXPORT jobject JNICALL
 Java_com_oracle_truffle_r_runtime_ffi_jnr_CallRFFIWithJNI_call9(JNIEnv *env, jclass c, jlong address, jobject arg1, jobject arg2,
 		jobject arg3, jobject arg4, jobject arg5, jobject arg6, jobject arg7, jobject arg8, jobject arg9) {
-	callEnter(env);
-	call9func call9 = (call9func) address;
-	jobject result = (*call9)(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9);
+	jobject result = NULL;
+	callEnter(env, &error_jmpbuf);
+	if (!setjmp(error_jmpbuf)) {
+		call9func call9 = (call9func) address;
+		result = (*call9)(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9);
+	}
 	callExit(env);
 	return result;
 }
 
 JNIEXPORT jobject JNICALL
 Java_com_oracle_truffle_r_runtime_ffi_jnr_CallRFFIWithJNI_call(JNIEnv *env, jclass c, jlong address, jobjectArray args) {
-	callEnter(env);
+	jobject result = NULL;
+	callEnter(env, &error_jmpbuf);
 	jsize len = (*env)->GetArrayLength(env, args);
 	switch (len) {
 	case 10: {
+		// Sadly no GetObjectArrayRegion call, but there has to be a better way!
 		jobject arg1 = (*env)->GetObjectArrayElement(env, args, 0);
 		jobject arg2 = (*env)->GetObjectArrayElement(env, args, 1);
 		jobject arg3 = (*env)->GetObjectArrayElement(env, args, 2);
@@ -167,8 +201,10 @@ Java_com_oracle_truffle_r_runtime_ffi_jnr_CallRFFIWithJNI_call(JNIEnv *env, jcla
 		jobject arg8 = (*env)->GetObjectArrayElement(env, args, 7);
 		jobject arg9 = (*env)->GetObjectArrayElement(env, args, 8);
 		jobject arg10 = (*env)->GetObjectArrayElement(env, args, 9);
-		call10func call10 = (call10func) address;
-		jobject result = (*call10)(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10);
+		if (!setjmp(error_jmpbuf)) {
+			call10func call10 = (call10func) address;
+			result = (*call10)(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10);
+		}
 		callExit(env);
 		return result;
 	}
@@ -183,9 +219,11 @@ typedef void (*callVoid1func)(SEXP arg1);
 
 JNIEXPORT void JNICALL
 Java_com_oracle_truffle_r_runtime_ffi_jnr_CallRFFIWithJNI_callVoid1(JNIEnv *env, jclass c, jlong address, jobject arg1) {
-	callEnter(env);
-	callVoid1func call1 = (callVoid1func) address;
-	(*call1)(arg1);
+	callEnter(env, &error_jmpbuf);
+	if (!setjmp(error_jmpbuf)) {
+		callVoid1func call1 = (callVoid1func) address;
+		(*call1)(arg1);
+	}
 	callExit(env);
 }
 
diff --git a/com.oracle.truffle.r.native/fficall/jni/src/rffiutils.c b/com.oracle.truffle.r.native/fficall/jni/src/rffiutils.c
index e16c70d7ff..46984b1255 100644
--- a/com.oracle.truffle.r.native/fficall/jni/src/rffiutils.c
+++ b/com.oracle.truffle.r.native/fficall/jni/src/rffiutils.c
@@ -25,9 +25,12 @@
 #include <stdlib.h>
 
 /*
- * All calls pass through one of the call(N) methods, which carry the JNIEnv value,
- * which needs to be saved for reuse in the many R functions such as Rf_allocVector.
- * FastR is not currently multi-threaded so the value can safely be stored in a static.
+ * All calls pass through one of the call(N) methods in rfficall.c, which carry the JNIEnv value,
+ * that needs to be saved for reuse in the many R functions such as Rf_allocVector.
+ * Currently only single threaded access is permitted (via a semaphore in CallRFFIWithJNI)
+ * so we are safe to use static variables. TODO Figure out where to store such state
+ * (portably) for MT use. JNI provides no help. N.B. The MT restriction also precludes
+ * recursive calls.
  */
 jclass CallRFFIHelperClass;
 jclass RDataFactoryClass;
@@ -38,6 +41,7 @@ jmethodID createSymbolMethodID;
 static jmethodID validateMethodID;
 
 JNIEnv *curenv = NULL;
+jmp_buf *callErrorJmpBuf;
 
 #define DEBUG_CACHE 0
 #define TRACE_COPIES 0
@@ -77,11 +81,16 @@ void init_utils(JNIEnv *env) {
 	copiedVectorsIndex = 0;
 }
 
-void callEnter(JNIEnv *env) {
+void callEnter(JNIEnv *env, jmp_buf *jmpbuf) {
 	setEnv(env);
+	callErrorJmpBuf = jmpbuf;
 //	printf("callEnter\n");
 }
 
+jmp_buf *getErrorJmpBuf() {
+	return callErrorJmpBuf;
+}
+
 void callExit(JNIEnv *env) {
 //	printf("callExit\n");
 	int i;
@@ -93,6 +102,20 @@ void callExit(JNIEnv *env) {
 			    (*env)->ReleaseIntArrayElements(env, intArray, (jint *)cv.data, 0);
 			    break;
 		    }
+
+		    case REALSXP: {
+			    jdoubleArray doubleArray = (jdoubleArray) cv.jArray;
+			    (*env)->ReleaseDoubleArrayElements(env, doubleArray, (jdouble *)cv.data, 0);
+			    break;
+
+		    }
+
+		    case LGLSXP: case RAWSXP: {
+			    jbyteArray byteArray = (jbyteArray) cv.jArray;
+			    (*env)->ReleaseByteArrayElements(env, byteArray, (jbyte *)cv.data, 0);
+			    break;
+
+		    }
 		    default:
 		    	fatalError("copiedVector type");
 		}
diff --git a/com.oracle.truffle.r.native/fficall/jni/src/rffiutils.h b/com.oracle.truffle.r.native/fficall/jni/src/rffiutils.h
index ac4425e2c8..0e550a5d04 100644
--- a/com.oracle.truffle.r.native/fficall/jni/src/rffiutils.h
+++ b/com.oracle.truffle.r.native/fficall/jni/src/rffiutils.h
@@ -25,6 +25,7 @@
 
 #include <jni.h>
 #include <Rinternals.h>
+#include <setjmp.h>
 
 #define VALIDATE_REFS 1
 
@@ -49,10 +50,12 @@ SEXP mkNamedGlobalRef(JNIEnv *env, int index, SEXP x);
 void validateRef(JNIEnv *env, SEXP x, const char *msg);
 
 // entering a top-level JNI call
-void callEnter(JNIEnv *env);
+void callEnter(JNIEnv *env, jmp_buf *error_exit);
 // exiting a top-level JNI call
 void callExit(JNIEnv *env);
 
+jmp_buf *getErrorJmpBuf();
+
 // find an object for which we have cached the internal rep
 void *findCopiedObject(JNIEnv *env, SEXP x);
 // add a new object to the internal rep cache
diff --git a/com.oracle.truffle.r.native/fficall/jni/src/vectoraccess.c b/com.oracle.truffle.r.native/fficall/jni/src/vectoraccess.c
index b3f0df097f..140906302e 100644
--- a/com.oracle.truffle.r.native/fficall/jni/src/vectoraccess.c
+++ b/com.oracle.truffle.r.native/fficall/jni/src/vectoraccess.c
@@ -25,19 +25,21 @@
 #include <string.h>
 
 jmethodID SET_STRING_ELT_MethodID;
-jmethodID SET_INTEGER_ELT_MethodID;
 jmethodID SET_VECTOR_ELT_MethodID;
 jmethodID RAW_MethodID;
 jmethodID INTEGER_MethodID;
+jmethodID REAL_MethodID;
+jmethodID LOGICAL_MethodID;
 jmethodID STRING_ELT_MethodID;
 jmethodID VECTOR_ELT_MethodID;
 jmethodID LENGTH_MethodID;
 
 void init_vectoraccess(JNIEnv *env) {
 	SET_STRING_ELT_MethodID = checkGetMethodID(env, CallRFFIHelperClass, "SET_STRING_ELT", "(Ljava/lang/Object;ILjava/lang/Object;)V", 1);
-	SET_INTEGER_ELT_MethodID = checkGetMethodID(env, CallRFFIHelperClass, "SET_INTEGER_ELT", "(Ljava/lang/Object;II)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/String;", 1);
 	VECTOR_ELT_MethodID = checkGetMethodID(env, CallRFFIHelperClass, "VECTOR_ELT", "(Ljava/lang/Object;I)Ljava/lang/Object;", 1);
@@ -46,6 +48,7 @@ void init_vectoraccess(JNIEnv *env) {
 
 
 int LENGTH(SEXP x) {
+	TRACE(TARG1, x);
 	JNIEnv *thisenv = getEnv();
 	return (*thisenv)->CallStaticObjectMethod(thisenv, CallRFFIHelperClass, LENGTH_MethodID, x);
 }
@@ -95,12 +98,20 @@ int SETLEVELS(SEXP x, int v){
 }
 
 int *LOGICAL(SEXP x){
-	unimplemented("LOGICAL");
+	TRACE(TARG1, x);
+	JNIEnv *thisenv = getEnv();
+	jbyte *data = (jint *) findCopiedObject(thisenv, x);
+	if (data == NULL) {
+	    jintArray intArray = (*thisenv)->CallStaticObjectMethod(thisenv, CallRFFIHelperClass, LOGICAL_MethodID, x);
+	    int len = (*thisenv)->GetArrayLength(thisenv, intArray);
+	    data = (*thisenv)->GetByteArrayElements(thisenv, intArray, NULL);
+	    addCopiedObject(thisenv, x, LGLSXP, intArray, data);
+	}
+	return data;
 }
 
 int *INTEGER(SEXP x){
 	TRACE(TARG1, x);
-	// TODO This does not support write access, e.g. INTEGER(x)[i]
 	JNIEnv *thisenv = getEnv();
 	jint *data = (jint *) findCopiedObject(thisenv, x);
 	if (data == NULL) {
@@ -114,20 +125,28 @@ int *INTEGER(SEXP x){
 
 
 Rbyte *RAW(SEXP x){
-	// TODO This does not support write access, e.g. RAW(x)[i]
 	JNIEnv *thisenv = getEnv();
-	jbyteArray bytearray = (*thisenv)->CallStaticObjectMethod(thisenv, CallRFFIHelperClass, RAW_MethodID, x);
-	int len = (*thisenv)->GetArrayLength(thisenv, bytearray);
-	jbyte *data = (*thisenv)->GetByteArrayElements(thisenv, bytearray, NULL);
-	void *result = malloc(len);
-	memcpy(result, data, len);
-	(*thisenv)->ReleaseByteArrayElements(thisenv, bytearray, data, JNI_ABORT);
-	return (Rbyte *) result;
+	jbyte *data = (jbyte *) findCopiedObject(thisenv, x);
+	if (data == NULL) {
+	    jbyteArray byteArray = (*thisenv)->CallStaticObjectMethod(thisenv, CallRFFIHelperClass, RAW_MethodID, x);
+	    int len = (*thisenv)->GetArrayLength(thisenv, byteArray);
+	    data = (*thisenv)->GetByteArrayElements(thisenv, byteArray, NULL);
+        addCopiedObject(thisenv, x, RAWSXP, byteArray, data);
+    }
+	return data;
 }
 
 
 double *REAL(SEXP x){
-	unimplemented("REAL");
+	JNIEnv *thisenv = getEnv();
+	jdouble *data = (jdouble *) findCopiedObject(thisenv, x);
+	if (data == NULL) {
+	    jdoubleArray doubleArray = (*thisenv)->CallStaticObjectMethod(thisenv, CallRFFIHelperClass, REAL_MethodID, x);
+	    int len = (*thisenv)->GetArrayLength(thisenv, doubleArray);
+	    data = (*thisenv)->GetDoubleArrayElements(thisenv, doubleArray, NULL);
+        addCopiedObject(thisenv, x, REALSXP, doubleArray, data);
+    }
+	return data;
 }
 
 
@@ -150,12 +169,6 @@ SEXP VECTOR_ELT(SEXP x, R_xlen_t i){
     return checkRef(thisenv, result);
 }
 
-void SET_INTEGER_ELT(SEXP x, R_xlen_t i, int v) {
-	JNIEnv *thisenv = getEnv();
-	(*thisenv)->CallStaticVoidMethod(thisenv, CallRFFIHelperClass, SET_INTEGER_ELT_MethodID, x, i, v);
-
-}
-
 void SET_STRING_ELT(SEXP x, R_xlen_t i, SEXP v){
 	JNIEnv *thisenv = getEnv();
 	(*thisenv)->CallStaticVoidMethod(thisenv, CallRFFIHelperClass, SET_STRING_ELT_MethodID, x, i, v);
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/DotC.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/DotC.java
index 9eebca4455..e8662b7826 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/DotC.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/DotC.java
@@ -125,12 +125,7 @@ public abstract class DotC extends RBuiltinNode {
                 errorProfile.enter();
                 throw RError.error(getEncapsulatingSourceSection(), RError.Message.UNIMPLEMENTED_ARG_TYPE, i + 1);
             }
-        }
-        try {
             RFFIFactory.getRFFI().getCRFFI().invoke(symbolInfo, nativeArgs);
-        } catch (Throwable t) {
-            errorProfile.enter();
-            throw RError.error(getEncapsulatingSourceSection(), RError.Message.NATIVE_CALL_FAILED, t.getMessage());
         }
         // we have to assume that the native method updated everything
         RStringVector listNames = validateArgNames(argValues.length, getSuppliedSignature());
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/ForeignFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/ForeignFunctions.java
index 7546561e61..af368bfecc 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/ForeignFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/ForeignFunctions.java
@@ -200,12 +200,7 @@ public class ForeignFunctions {
                 errorProfile.enter();
                 throw RError.error(getEncapsulatingSourceSection(), Message.C_SYMBOL_NOT_IN_TABLE, name);
             }
-            try {
-                return RFFIFactory.getRFFI().getCallRFFI().invokeCall(symbolInfo, args.getArguments());
-            } catch (Throwable t) {
-                errorProfile.enter();
-                throw RError.error(getEncapsulatingSourceSection(), RError.Message.NATIVE_CALL_FAILED, t.getMessage());
-            }
+            return RFFIFactory.getRFFI().getCallRFFI().invokeCall(symbolInfo, args.getArguments());
         }
 
         @Fallback
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/instrument/debug/Browser.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/instrument/debug/Browser.java
index 653da21853..6acadc538c 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/instrument/debug/Browser.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/instrument/debug/Browser.java
@@ -41,6 +41,7 @@ public class Browser {
     }
 
     private static final String BROWSER_SOURCE = "<browser_input>";
+    private static String lastEmptyLineCommand = "n";
 
     @TruffleBoundary
     public static ExitMode interact(MaterializedFrame frame) {
@@ -50,14 +51,12 @@ public class Browser {
         ExitMode exitMode = ExitMode.NEXT;
         try {
             LW: while (true) {
-                String input = ch.readLine();
+                String input = ch.readLine().trim();
                 if (input.length() == 0) {
                     RLogicalVector browserNLdisabledVec = (RLogicalVector) RContext.getROptionsState().getValue("browserNLdisabled");
                     if (!RRuntime.fromLogical(browserNLdisabledVec.getDataAt(0))) {
-                        break;
+                        input = lastEmptyLineCommand;
                     }
-                } else {
-                    input = input.trim();
                 }
                 switch (input) {
                     case "c":
@@ -66,9 +65,11 @@ public class Browser {
                         break LW;
                     case "n":
                         exitMode = ExitMode.NEXT;
+                        lastEmptyLineCommand = "n";
                         break LW;
                     case "s":
                         exitMode = ExitMode.STEP;
+                        lastEmptyLineCommand = "s";
                         break LW;
                     case "f":
                         exitMode = ExitMode.FINISH;
diff --git a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/gnfi/GNFI_RFFIFactory.java b/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/gnfi/GNFI_RFFIFactory.java
index 958c2f0858..65bb8923bc 100644
--- a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/gnfi/GNFI_RFFIFactory.java
+++ b/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/gnfi/GNFI_RFFIFactory.java
@@ -30,6 +30,7 @@ import java.util.*;
 import com.oracle.nfi.*;
 import com.oracle.nfi.api.*;
 import com.oracle.truffle.r.runtime.*;
+import com.oracle.truffle.r.runtime.RContext.ContextState;
 import com.oracle.truffle.r.runtime.ffi.*;
 
 /**
@@ -206,4 +207,8 @@ public class GNFI_RFFIFactory extends RFFIFactory implements RFFI, BaseRFFI {
         throw Utils.fail("glob not implemented");
     }
 
+    public ContextState newContext(RContext context, Object... objects) {
+        throw RInternalError.unimplemented();
+    }
+
 }
diff --git a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jnr/CRFFI_JNR_Invoke.java b/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jnr/CRFFI_JNR_Invoke.java
index a3a37f0475..b92ca1570f 100644
--- a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jnr/CRFFI_JNR_Invoke.java
+++ b/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jnr/CRFFI_JNR_Invoke.java
@@ -23,6 +23,8 @@
 package com.oracle.truffle.r.runtime.ffi.jnr;
 
 import java.lang.invoke.*;
+
+import com.oracle.truffle.r.runtime.*;
 import com.oracle.truffle.r.runtime.ffi.*;
 
 import jnr.invoke.*;
@@ -37,7 +39,7 @@ public class CRFFI_JNR_Invoke implements CRFFI {
      * array (call by reference for scalars). As we already loaded the library and looked up the
      * symbol address we don't need to use JNR for that.
      */
-    public void invoke(DLL.SymbolInfo symbolInfo, Object[] args) throws Throwable {
+    public void invoke(DLL.SymbolInfo symbolInfo, Object[] args) {
         ParameterType[] parameterTypes = new ParameterType[args.length];
         for (int i = 0; i < args.length; i++) {
             Object arg = args[i];
@@ -53,6 +55,10 @@ public class CRFFI_JNR_Invoke implements CRFFI {
 
         // We already have up the symbol address
         MethodHandle mh = Native.getMethodHandle(sig, new CodeAddress(symbolInfo.address));
-        mh.invokeWithArguments(args);
+        try {
+            mh.invokeWithArguments(args);
+        } catch (Throwable ex) {
+            throw RError.error(RError.Message.GENERIC, ex.getMessage());
+        }
     }
 }
diff --git a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jnr/CallRFFIHelper.java b/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jnr/CallRFFIHelper.java
index 6415c18dfd..6e07a4b2ee 100644
--- a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jnr/CallRFFIHelper.java
+++ b/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jnr/CallRFFIHelper.java
@@ -165,6 +165,10 @@ public class CallRFFIHelper {
         return pl.toRList();
     }
 
+    static void Rf_error(String msg) {
+        throw RError.error(RError.Message.GENERIC, msg);
+    }
+
     static void Rf_warning(String msg) {
         RError.warning(RError.Message.GENERIC, msg);
     }
@@ -185,12 +189,6 @@ public class CallRFFIHelper {
         xv.setElement(i, v);
     }
 
-    static void SET_INTEGER_ELT(Object x, int i, int v) {
-        // TODO error checks
-        RIntVector xv = (RIntVector) x;
-        xv.setElement(i, v);
-    }
-
     static void SET_VECTOR_ELT(Object x, int i, Object v) {
         // TODO error checks
         RList list = (RList) x;
@@ -206,6 +204,15 @@ public class CallRFFIHelper {
 
     }
 
+    static byte[] LOGICAL(Object x) {
+        if (x instanceof RLogicalVector) {
+            return ((RLogicalVector) x).getDataWithoutCopying();
+        } else {
+            throw RInternalError.unimplemented();
+        }
+
+    }
+
     static int[] INTEGER(Object x) {
         if (x instanceof RIntVector) {
             return ((RIntVector) x).getDataWithoutCopying();
@@ -214,6 +221,14 @@ public class CallRFFIHelper {
         }
     }
 
+    static double[] REAL(Object x) {
+        if (x instanceof RDoubleVector) {
+            return ((RDoubleVector) x).getDataWithoutCopying();
+        } else {
+            throw RInternalError.unimplemented();
+        }
+    }
+
     static String STRING_ELT(Object x, int i) {
         if (x instanceof String) {
             assert i == 0;
diff --git a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jnr/CallRFFIWithJNI.java b/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jnr/CallRFFIWithJNI.java
index c279442a7c..fac622ad58 100644
--- a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jnr/CallRFFIWithJNI.java
+++ b/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jnr/CallRFFIWithJNI.java
@@ -79,7 +79,7 @@ public class CallRFFIWithJNI implements CallRFFI {
 
     private static final Semaphore inCritical = new Semaphore(1, false);
 
-    public Object invokeCall(SymbolInfo symbolInfo, Object[] args) throws Throwable {
+    public Object invokeCall(SymbolInfo symbolInfo, Object[] args) {
         long address = symbolInfo.address;
         try {
             inCritical.acquire();
@@ -99,12 +99,14 @@ public class CallRFFIWithJNI implements CallRFFI {
                 return call(address, args);
                 // @formatter:on
             }
+        } catch (InterruptedException ex) {
+            throw RInternalError.shouldNotReachHere();
         } finally {
             inCritical.release();
         }
     }
 
-    public Object invokeExternal(SymbolInfo symbolInfo, Object[] args) throws Throwable {
+    public Object invokeExternal(SymbolInfo symbolInfo, Object[] args) {
         throw RInternalError.unimplemented(".External");
     }
 
@@ -132,7 +134,7 @@ public class CallRFFIWithJNI implements CallRFFI {
 
     private static native Object call9(long address, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7, Object arg8, Object arg9);
 
-    public void invokeVoidCall(SymbolInfo symbolInfo, Object[] args) throws Throwable {
+    public void invokeVoidCall(SymbolInfo symbolInfo, Object[] args) {
         long address = symbolInfo.address;
         try {
             inCritical.acquire();
@@ -143,6 +145,7 @@ public class CallRFFIWithJNI implements CallRFFI {
                 default:
                     throw RInternalError.shouldNotReachHere();
             }
+        } catch (InterruptedException ex) {
         } finally {
             inCritical.release();
         }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RError.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RError.java
index f0c5c535a2..fe5157e93b 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RError.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RError.java
@@ -493,7 +493,6 @@ public final class RError extends RuntimeException {
         NA_NAN_INF_IN_FOREIGN_FUNCTION_CALL("NA/NaN/Inf in foreign function call (arg %d)"),
         INCORRECT_ARG("incorrect arguments to %s"),
         UNIMPLEMENTED_ARG_TYPE("unimplemented argument type (arg %d)"),
-        NATIVE_CALL_FAILED("native call failed: %s"),
         C_SYMBOL_NOT_IN_TABLE("C symbol name \"%s\" not in load table"),
         FORTRAN_SYMBOL_NOT_IN_TABLE("Fortran symbol name \"%s\" not in load table"),
         NOT_THAT_MANY_FRAMES("not that many frames on the stack"),
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/CRFFI.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/CRFFI.java
index dfdb7085ee..3d0b10d8db 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/CRFFI.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/CRFFI.java
@@ -30,10 +30,9 @@ public interface CRFFI {
      * Invoke the native method identified by {@code symbolInfo} passing it the arguments in
      * {@code args}. The values in {@code args} should be native types,e.g., {@code double[]} not
      * {@code RDoubleVector}.
-     * 
+     *
      * @param symbolInfo identifies the symbol and the defining library
      * @param args native arguments
-     * @throws Throwable on any error during the call
      */
-    void invoke(DLL.SymbolInfo symbolInfo, Object[] args) throws Throwable;
+    void invoke(DLL.SymbolInfo symbolInfo, Object[] args);
 }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/CallRFFI.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/CallRFFI.java
index 721fdfd423..49ed64af40 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/CallRFFI.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/CallRFFI.java
@@ -33,14 +33,13 @@ public interface CallRFFI {
      *
      * @param symbolInfo identifies the symbol and the defining library
      * @param args arguments
-     * @throws Throwable on any error during the call
      */
-    Object invokeCall(DLL.SymbolInfo symbolInfo, Object[] args) throws Throwable;
+    Object invokeCall(DLL.SymbolInfo symbolInfo, Object[] args);
 
     /**
      * Variant that does not return a result (primarily for library "init" methods.
      */
-    void invokeVoidCall(DLL.SymbolInfo symbolInfo, Object[] args) throws Throwable;
+    void invokeVoidCall(DLL.SymbolInfo symbolInfo, Object[] args);
 
     /**
      * Variant of {@link #invokeCall} for {@code .External}, where args are wrapped up as a single
@@ -48,8 +47,7 @@ public interface CallRFFI {
      *
      * @param symbolInfo
      * @param args
-     * @throws Throwable
      */
-    Object invokeExternal(DLL.SymbolInfo symbolInfo, Object[] args) throws Throwable;
+    Object invokeExternal(DLL.SymbolInfo symbolInfo, Object[] args);
 
 }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/RFFIFactory.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/RFFIFactory.java
index 9433a030b5..a5bd96357a 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/RFFIFactory.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/RFFIFactory.java
@@ -70,6 +70,10 @@ public abstract class RFFIFactory implements RContext.StateFactory {
         throw missing("Stats");
     }
 
+    public ToolsRFFI getToolsRFFI() {
+        throw missing("Tools");
+    }
+
     public RApplRFFI getRApplRFFI() {
         throw missing("RDerived");
     }
diff --git a/com.oracle.truffle.r.test.native/packages/testrffi/testrffi/R/testrffi.R b/com.oracle.truffle.r.test.native/packages/testrffi/testrffi/R/testrffi.R
index c3e6f9e793..0355ac9fa9 100644
--- a/com.oracle.truffle.r.test.native/packages/testrffi/testrffi/R/testrffi.R
+++ b/com.oracle.truffle.r.test.native/packages/testrffi/testrffi/R/testrffi.R
@@ -21,3 +21,7 @@ rffi.getExternalPtrAddr <- function(eptr) {
 rffi.TYPEOF <- function(x) {
 	.Call("invoke_TYPEOF", x, PACKAGE = "testrffi")
 }
+
+rffi.error <- function() {
+	.Call("invoke_error", PACKAGE = "testrffi")
+}
diff --git a/com.oracle.truffle.r.test.native/packages/testrffi/testrffi/src/testrffi.c b/com.oracle.truffle.r.test.native/packages/testrffi/testrffi/src/testrffi.c
index d87674bc95..4cadd04e57 100644
--- a/com.oracle.truffle.r.test.native/packages/testrffi/testrffi/src/testrffi.c
+++ b/com.oracle.truffle.r.test.native/packages/testrffi/testrffi/src/testrffi.c
@@ -62,3 +62,7 @@ SEXP getExternalPtrAddr(SEXP eptr) {
 SEXP invoke_TYPEOF(SEXP x) {
 	return ScalarInteger(TYPEOF(x));
 }
+
+SEXP invoke_error() {
+	error("invoke_error in testrffi");
+}
-- 
GitLab