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 61431ad1d6d0b6c2717a6d7d17d8babc84c26456..af38cfcc846d04ece15ebe0120913ee60a101c23 100644
--- a/com.oracle.truffle.r.native/fficall/src/jni/Rembedded.c
+++ b/com.oracle.truffle.r.native/fficall/src/jni/Rembedded.c
@@ -406,14 +406,19 @@ void setupOverrides(void) {
 }
 
 static void REmbed_nativeWriteConsole(JNIEnv *jniEnv, jclass c, jstring string, int otype) {
-	int len = (*jniEnv)->GetStringUTFLength(jniEnv, string);
-	const char *cbuf =  (*jniEnv)->GetStringUTFChars(jniEnv, string, NULL);
-	if (ptr_R_WriteConsole == NULL) {
-		(*ptr_R_WriteConsoleEx)(cbuf, len, otype);
-	} else {
-	    (*ptr_R_WriteConsole)(cbuf, len);
+	jmp_buf error_jmpbuf;
+	callEnter(jniEnv, &error_jmpbuf);
+	if (!setjmp(error_jmpbuf)) {
+		int len = (*jniEnv)->GetStringUTFLength(jniEnv, string);
+		const char *cbuf =  (*jniEnv)->GetStringUTFChars(jniEnv, string, NULL);
+		if (ptr_R_WriteConsole == NULL) {
+			(*ptr_R_WriteConsoleEx)(cbuf, len, otype);
+		} else {
+			(*ptr_R_WriteConsole)(cbuf, len);
+		}
+		(*jniEnv)->ReleaseStringUTFChars(jniEnv, string, cbuf);
 	}
-	(*jniEnv)->ReleaseStringUTFChars(jniEnv, string, cbuf);
+	callExit(jniEnv);
 }
 
 JNIEXPORT void JNICALL Java_com_oracle_truffle_r_runtime_ffi_jnr_JNI_1REmbed_nativeWriteConsole(JNIEnv *jniEnv, jclass c, jstring string) {
@@ -425,22 +430,37 @@ JNIEXPORT void JNICALL Java_com_oracle_truffle_r_runtime_ffi_jnr_JNI_1REmbed_nat
 }
 
 JNIEXPORT jstring JNICALL Java_com_oracle_truffle_r_runtime_ffi_jnr_JNI_1REmbed_nativeReadConsole(JNIEnv *jniEnv, jclass c, jstring prompt) {
-	const char *cprompt =  (*jniEnv)->GetStringUTFChars(jniEnv, prompt, NULL);
-	unsigned char cbuf[1024];
-	int n = (*ptr_R_ReadConsole)(cprompt, cbuf, 1024, 0);
-	jstring result;
-	result = (*jniEnv)->NewStringUTF(jniEnv, (const char *)cbuf);
-	(*jniEnv)->ReleaseStringUTFChars(jniEnv, prompt, cprompt);
+	jmp_buf error_jmpbuf;
+	jstring result = NULL;
+	callEnter(jniEnv, &error_jmpbuf);
+	if (!setjmp(error_jmpbuf)) {
+		const char *cprompt =  (*jniEnv)->GetStringUTFChars(jniEnv, prompt, NULL);
+		unsigned char cbuf[1024];
+		int n = (*ptr_R_ReadConsole)(cprompt, cbuf, 1024, 0);
+		result = (*jniEnv)->NewStringUTF(jniEnv, (const char *)cbuf);
+		(*jniEnv)->ReleaseStringUTFChars(jniEnv, prompt, cprompt);
+	}
+	callExit(jniEnv);
 	return result;
 }
 
 JNIEXPORT void JNICALL Java_com_oracle_truffle_r_runtime_ffi_jnr_JNI_1REmbed_nativeCleanUp(JNIEnv *jniEnv, jclass c, jint x, jint y, jint z) {
+	jmp_buf error_jmpbuf;
+	callEnter(jniEnv, &error_jmpbuf);
+	if (!setjmp(error_jmpbuf)) {
 	(*ptr_R_CleanUp)(x, y, z);
+	}
+	callExit(jniEnv);
 }
 
 JNIEXPORT void JNICALL Java_com_oracle_truffle_r_runtime_ffi_jnr_JNI_1REmbed_nativeSuicide(JNIEnv *jniEnv, jclass c, jstring string) {
-	const char *cbuf =  (*jniEnv)->GetStringUTFChars(jniEnv, string, NULL);
-	(*ptr_R_Suicide)(cbuf);
+	jmp_buf error_jmpbuf;
+	callEnter(jniEnv, &error_jmpbuf);
+	if (!setjmp(error_jmpbuf)) {
+		const char *cbuf =  (*jniEnv)->GetStringUTFChars(jniEnv, string, NULL);
+		(*ptr_R_Suicide)(cbuf);
+	}
+	callExit(jniEnv);
 }
 
 void uR_PolledEvents(void) {
@@ -582,7 +602,7 @@ CTXT R_getGlobalFunctionContext() {
 	jmethodID methodID = checkGetMethodID(jniEnv, CallRFFIHelperClass, "R_getGlobalFunctionContext", "()Ljava/lang/Object;", 1);
     CTXT result = (*jniEnv)->CallStaticObjectMethod(jniEnv, CallRFFIHelperClass, methodID);
     result = checkRef(jniEnv, result);
-    return result == R_NilValue ? NULL : result;
+    return result == R_NilValue ? NULL : addGlobalRef(jniEnv, result, 0);
 }
 
 CTXT R_getParentFunctionContext(CTXT c) {
@@ -590,7 +610,7 @@ CTXT R_getParentFunctionContext(CTXT c) {
 	jmethodID methodID = checkGetMethodID(jniEnv, CallRFFIHelperClass, "R_getParentFunctionContext", "(Ljava/lang/Object;)Ljava/lang/Object;", 1);
     CTXT result = (*jniEnv)->CallStaticObjectMethod(jniEnv, CallRFFIHelperClass, methodID, c);
     result = checkRef(jniEnv, result);
-    return result == R_NilValue ? NULL : result;
+    return result == R_NilValue ? NULL : addGlobalRef(jniEnv, result, 0);
 }
 
 SEXP R_getContextEnv(CTXT context) {
@@ -627,3 +647,15 @@ int R_insideBrowser() {
 	jmethodID methodID = checkGetMethodID(jniEnv, CallRFFIHelperClass, "R_insideBrowser", "()I", 1);
     return (*jniEnv)->CallStaticIntMethod(jniEnv, CallRFFIHelperClass, 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);
+}
+
+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);
+}
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 14227cbd329b3acd95354e84d88609a379ecbaee..5c701bef8db98b376449c6192b9b8b8491e476c1 100644
--- a/com.oracle.truffle.r.native/fficall/src/jni/Rinternals.c
+++ b/com.oracle.truffle.r.native/fficall/src/jni/Rinternals.c
@@ -350,6 +350,7 @@ SEXP Rf_dimnamesgets(SEXP x, SEXP y) {
 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);
     return checkRef(thisenv, result);
 }
@@ -849,7 +850,7 @@ SEXP CADR(SEXP e) {
 }
 
 SEXP CDDR(SEXP e) {
-    TRACE(TARG1, e);
+    TRACE(TARGp, e);
     JNIEnv *thisenv = getEnv();
     SEXP result = (*thisenv)->CallStaticObjectMethod(thisenv, CallRFFIHelperClass, CDDR_MethodID, e);
     return checkRef(thisenv, result);
@@ -1455,6 +1456,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);
 	// If tryResult is NULL, an error occurred
 	if (ErrorOccurred) {
@@ -1609,12 +1611,13 @@ int R_check_class_etc (SEXP x, const char **valid) {
 	return (int) unimplemented("R_check_class_etc");
 }
 
-void R_PreserveObject(SEXP x) {
-	// Not applicable
+SEXP R_PreserveObject(SEXP x) {
+	// convert to a JNI global ref until explicitly released
+	return createGlobalRef(getEnv(), x, 0);
 }
 
 void R_ReleaseObject(SEXP x) {
-	// Not applicable
+	releaseGlobalRef(getEnv(), x);
 }
 
 void R_dot_Last(void) {
diff --git a/com.oracle.truffle.r.native/fficall/src/jni/rfficall.c b/com.oracle.truffle.r.native/fficall/src/jni/rfficall.c
index a8d153d4a0c39265d82c94e63c739c61e1c7a777..9b2a545370781553d0f7157366b688ea3dae1496 100644
--- a/com.oracle.truffle.r.native/fficall/src/jni/rfficall.c
+++ b/com.oracle.truffle.r.native/fficall/src/jni/rfficall.c
@@ -42,8 +42,6 @@ Java_com_oracle_truffle_r_runtime_ffi_jnr_JNI_1CallRFFI_nativeSetTempDir(JNIEnv
 	setTempDir(env, tempDir);
 }
 
-static jmp_buf error_jmpbuf;
-
 // Boilerplate methods for the actual calls
 
 typedef SEXP (*call0func)();
@@ -343,6 +341,7 @@ typedef SEXP (*call64func)(SEXP arg1, SEXP arg2, SEXP arg3, SEXP arg4, SEXP arg5
 
 JNIEXPORT jobject JNICALL
 Java_com_oracle_truffle_r_runtime_ffi_jnr_JNI_1CallRFFI_call0(JNIEnv *env, jclass c, jlong address) {
+	jmp_buf error_jmpbuf;
 	jobject result = NULL;
 	callEnter(env, &error_jmpbuf);
 	if (!setjmp(error_jmpbuf)) {
@@ -355,6 +354,7 @@ Java_com_oracle_truffle_r_runtime_ffi_jnr_JNI_1CallRFFI_call0(JNIEnv *env, jclas
 
 JNIEXPORT jobject JNICALL
 Java_com_oracle_truffle_r_runtime_ffi_jnr_JNI_1CallRFFI_call1(JNIEnv *env, jclass c, jlong address, jobject arg1) {
+	jmp_buf error_jmpbuf;
 	jobject result = NULL;
 	callEnter(env, &error_jmpbuf);
 	if (!setjmp(error_jmpbuf)) {
@@ -367,6 +367,7 @@ Java_com_oracle_truffle_r_runtime_ffi_jnr_JNI_1CallRFFI_call1(JNIEnv *env, jclas
 
 JNIEXPORT jobject JNICALL
 Java_com_oracle_truffle_r_runtime_ffi_jnr_JNI_1CallRFFI_call2(JNIEnv *env, jclass c, jlong address, jobject arg1, jobject arg2) {
+	jmp_buf error_jmpbuf;
 	jobject result = NULL;
 	callEnter(env, &error_jmpbuf);
 	if (!setjmp(error_jmpbuf)) {
@@ -380,6 +381,7 @@ Java_com_oracle_truffle_r_runtime_ffi_jnr_JNI_1CallRFFI_call2(JNIEnv *env, jclas
 JNIEXPORT jobject JNICALL
 Java_com_oracle_truffle_r_runtime_ffi_jnr_JNI_1CallRFFI_call3(JNIEnv *env, jclass c, jlong address, jobject arg1, jobject arg2,
 		jobject arg3) {
+	jmp_buf error_jmpbuf;
 	jobject result = NULL;
 	callEnter(env, &error_jmpbuf);
 	if (!setjmp(error_jmpbuf)) {
@@ -393,6 +395,7 @@ Java_com_oracle_truffle_r_runtime_ffi_jnr_JNI_1CallRFFI_call3(JNIEnv *env, jclas
 JNIEXPORT jobject JNICALL
 Java_com_oracle_truffle_r_runtime_ffi_jnr_JNI_1CallRFFI_call4(JNIEnv *env, jclass c, jlong address, jobject arg1, jobject arg2,
 		jobject arg3, jobject arg4) {
+	jmp_buf error_jmpbuf;
 	jobject result = NULL;
 	callEnter(env, &error_jmpbuf);
 	if (!setjmp(error_jmpbuf)) {
@@ -406,6 +409,7 @@ Java_com_oracle_truffle_r_runtime_ffi_jnr_JNI_1CallRFFI_call4(JNIEnv *env, jclas
 JNIEXPORT jobject JNICALL
 Java_com_oracle_truffle_r_runtime_ffi_jnr_JNI_1CallRFFI_call5(JNIEnv *env, jclass c, jlong address, jobject arg1, jobject arg2,
 		jobject arg3, jobject arg4, jobject arg5) {
+	jmp_buf error_jmpbuf;
 	jobject result = NULL;
 	callEnter(env, &error_jmpbuf);
 	if (!setjmp(error_jmpbuf)) {
@@ -419,6 +423,7 @@ Java_com_oracle_truffle_r_runtime_ffi_jnr_JNI_1CallRFFI_call5(JNIEnv *env, jclas
 JNIEXPORT jobject JNICALL
 Java_com_oracle_truffle_r_runtime_ffi_jnr_JNI_1CallRFFI_call6(JNIEnv *env, jclass c, jlong address, jobject arg1, jobject arg2,
 		jobject arg3, jobject arg4, jobject arg5, jobject arg6) {
+	jmp_buf error_jmpbuf;
 	jobject result = NULL;
 	callEnter(env, &error_jmpbuf);
 	if (!setjmp(error_jmpbuf)) {
@@ -432,6 +437,7 @@ Java_com_oracle_truffle_r_runtime_ffi_jnr_JNI_1CallRFFI_call6(JNIEnv *env, jclas
 JNIEXPORT jobject JNICALL
 Java_com_oracle_truffle_r_runtime_ffi_jnr_JNI_1CallRFFI_call7(JNIEnv *env, jclass c, jlong address, jobject arg1, jobject arg2,
 		jobject arg3, jobject arg4, jobject arg5, jobject arg6, jobject arg7) {
+	jmp_buf error_jmpbuf;
 	jobject result = NULL;
 	callEnter(env, &error_jmpbuf);
 	if (!setjmp(error_jmpbuf)) {
@@ -445,6 +451,7 @@ Java_com_oracle_truffle_r_runtime_ffi_jnr_JNI_1CallRFFI_call7(JNIEnv *env, jclas
 JNIEXPORT jobject JNICALL
 Java_com_oracle_truffle_r_runtime_ffi_jnr_JNI_1CallRFFI_call8(JNIEnv *env, jclass c, jlong address, jobject arg1, jobject arg2,
 		jobject arg3, jobject arg4, jobject arg5, jobject arg6, jobject arg7, jobject arg8) {
+	jmp_buf error_jmpbuf;
 	jobject result = NULL;
 	callEnter(env, &error_jmpbuf);
 	if (!setjmp(error_jmpbuf)) {
@@ -458,6 +465,7 @@ Java_com_oracle_truffle_r_runtime_ffi_jnr_JNI_1CallRFFI_call8(JNIEnv *env, jclas
 JNIEXPORT jobject JNICALL
 Java_com_oracle_truffle_r_runtime_ffi_jnr_JNI_1CallRFFI_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) {
+	jmp_buf error_jmpbuf;
 	jobject result = NULL;
 	callEnter(env, &error_jmpbuf);
 	if (!setjmp(error_jmpbuf)) {
@@ -470,6 +478,7 @@ Java_com_oracle_truffle_r_runtime_ffi_jnr_JNI_1CallRFFI_call9(JNIEnv *env, jclas
 
 JNIEXPORT jobject JNICALL
 Java_com_oracle_truffle_r_runtime_ffi_jnr_JNI_1CallRFFI_call(JNIEnv *env, jclass c, jlong address, jobjectArray args) {
+	jmp_buf error_jmpbuf;
 	jobject result = NULL;
 	callEnter(env, &error_jmpbuf);
 	jsize len = (*env)->GetArrayLength(env, args);
@@ -1214,6 +1223,7 @@ typedef void (*callVoid1func)(SEXP arg1);
 
 JNIEXPORT void JNICALL
 Java_com_oracle_truffle_r_runtime_ffi_jnr_JNI_1CallRFFI_callVoid1(JNIEnv *env, jclass c, jlong address, jobject arg1) {
+	jmp_buf error_jmpbuf;
 	callEnter(env, &error_jmpbuf);
 	if (!setjmp(error_jmpbuf)) {
 		callVoid1func call1 = (callVoid1func) address;
@@ -1226,6 +1236,7 @@ typedef void (*callVoid0func)();
 
 JNIEXPORT void JNICALL
 Java_com_oracle_truffle_r_runtime_ffi_jnr_JNI_1CallRFFI_callVoid0(JNIEnv *env, jclass c, jlong address) {
+	jmp_buf error_jmpbuf;
 	callEnter(env, &error_jmpbuf);
 	if (!setjmp(error_jmpbuf)) {
 		callVoid0func call1 = (callVoid0func) address;
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 43f13021a505226076c73c73e19162c98ce54186..5f9425d8a7ffd68d8607ad2c3cc9864671065fb9 100644
--- a/com.oracle.truffle.r.native/fficall/src/jni/rffiutils.c
+++ b/com.oracle.truffle.r.native/fficall/src/jni/rffiutils.c
@@ -43,14 +43,18 @@ jmethodID createSymbolMethodID;
 static jmethodID validateMethodID;
 
 static JNIEnv *curenv = NULL;
-jmp_buf *callErrorJmpBuf;
 
 // default for trace output when enabled
 FILE *traceFile = NULL;
 
-static int alwaysUseGlobal = 0;
+typedef struct globalRefTable_struct {
+	int permanent;
+	SEXP gref;         // The jobject (SEXP) global ref
+} GlobalRefElem;
+
 #define CACHED_GLOBALREFS_INITIAL_SIZE 64
-static SEXP *cachedGlobalRefs;
+static GlobalRefElem *cachedGlobalRefs;
+static int cachedGlobalRefsHwm;
 static int cachedGlobalRefsLength;
 
 // Data structure for managing the required copying of
@@ -71,7 +75,7 @@ static NativeArrayElem *nativeArrayTable;
 // hwm of nativeArrayTable
 static int nativeArrayTableHwm;
 static int nativeArrayTableLength;
-static void releaseNativeArray(JNIEnv *env, int index);
+static void releaseNativeArray(JNIEnv *env, int index, int freedata);
 
 static int isEmbedded = 0;
 void setEmbedded() {
@@ -79,10 +83,14 @@ void setEmbedded() {
 }
 
 // native down call depth, indexes nativeArrayTableHwmStack
-int callDepth;
+int callDepth = 0;
+
+#define CALLDEPTH_STACK_SIZE 16
+static int nativeArrayTableHwmStack[CALLDEPTH_STACK_SIZE];
+
+// stack of jmp_buf ptrs for non-local control transfer on error
+static jmp_buf* callErrorJmpBufTable[CALLDEPTH_STACK_SIZE];
 
-#define NATIVE_ARRAY_TABLE_HWM_STACK_SIZE 16
-int nativeArrayTableHwmStack[NATIVE_ARRAY_TABLE_HWM_STACK_SIZE] ;
 
 void init_utils(JNIEnv *env) {
 	curenv = env;
@@ -113,8 +121,9 @@ void init_utils(JNIEnv *env) {
 	unimplementedMethodID = checkGetMethodID(env, RInternalErrorClass, "unimplemented", "(Ljava/lang/String;)Ljava/lang/RuntimeException;", 1);
 	createSymbolMethodID = checkGetMethodID(env, RDataFactoryClass, "createSymbolInterned", "(Ljava/lang/String;)Lcom/oracle/truffle/r/runtime/data/RSymbol;", 1);
     validateMethodID = checkGetMethodID(env, CallRFFIHelperClass, "validate", "(Ljava/lang/Object;)Ljava/lang/Object;", 1);
-    cachedGlobalRefs = calloc(CACHED_GLOBALREFS_INITIAL_SIZE, sizeof(SEXP));
+    cachedGlobalRefs = calloc(CACHED_GLOBALREFS_INITIAL_SIZE, sizeof(GlobalRefElem));
     cachedGlobalRefsLength = CACHED_GLOBALREFS_INITIAL_SIZE;
+    cachedGlobalRefsHwm = 0;
 	nativeArrayTable = calloc(NATIVE_ARRAY_TABLE_INITIAL_SIZE, sizeof(NativeArrayElem));
 	nativeArrayTableLength = NATIVE_ARRAY_TABLE_INITIAL_SIZE;
 	nativeArrayTableHwm = 0;
@@ -134,8 +143,9 @@ const char *stringToChars(JNIEnv *jniEnv, jstring string) {
 
 void callEnter(JNIEnv *env, jmp_buf *jmpbuf) {
 	setEnv(env);
-	callErrorJmpBuf = jmpbuf;
-	if (callDepth >= NATIVE_ARRAY_TABLE_HWM_STACK_SIZE) {
+	//printf("callEnter: callDepth %d, jmpbufptr %p\n", callDepth, jmpbuf);
+	callErrorJmpBufTable[callDepth] = jmpbuf;
+	if (callDepth >= CALLDEPTH_STACK_SIZE) {
 		fatalError("call stack overflow\n");
 	}
 	nativeArrayTableHwmStack[callDepth] = nativeArrayTableHwm;
@@ -143,27 +153,27 @@ void callEnter(JNIEnv *env, jmp_buf *jmpbuf) {
 }
 
 jmp_buf *getErrorJmpBuf() {
-	return callErrorJmpBuf;
+	// printf("getErrorJmpBuf: callDepth %d, jmpbufptr %p\n", callDepth, callErrorJmpBufTable[callDepth - 1]);
+	return callErrorJmpBufTable[callDepth - 1];
 }
 
 void callExit(JNIEnv *env) {
 	int oldHwm = nativeArrayTableHwmStack[callDepth - 1];
 	for (int i = oldHwm; i < nativeArrayTableHwm; i++) {
-		releaseNativeArray(env, i);
+               releaseNativeArray(env, i, 1);
 	}
 	nativeArrayTableHwm = oldHwm;
 	callDepth--;
 }
 
 void invalidateNativeArray(JNIEnv *env, SEXP oldObj) {
-	int i;
-	for (i = 0; i < nativeArrayTableHwm; i++) {
+	for (int i = 0; i < nativeArrayTableHwm; i++) {
 		NativeArrayElem cv = nativeArrayTable[i];
 		if ((*env)->IsSameObject(env, cv.obj, oldObj)) {
 #if TRACE_NATIVE_ARRAYS
 			fprintf(traceFile, "invalidateNativeArray(%p): found\n", oldObj);
 #endif
-			releaseNativeArray(env, &cv);
+			releaseNativeArray(env, i, 1);
 			nativeArrayTable[i].obj = NULL;
 		}
 	}
@@ -172,6 +182,14 @@ void invalidateNativeArray(JNIEnv *env, SEXP oldObj) {
 #endif
 }
 
+void updateNativeArrays(JNIEnv *env) {
+	int oldHwm = nativeArrayTableHwmStack[callDepth - 1];
+	for (int i = oldHwm; i < nativeArrayTableHwm; i++) {
+        releaseNativeArray(env, i, 0);
+	}
+}
+
+
 static void *findNativeArray(JNIEnv *env, SEXP x) {
 	int i;
 	for (i = 0; i < nativeArrayTableHwm; i++) {
@@ -270,16 +288,16 @@ void *getNativeArray(JNIEnv *thisenv, SEXP x, SEXPTYPE type) {
 	return data;
 }
 
-static void releaseNativeArray(JNIEnv *env, int i) {
+static void releaseNativeArray(JNIEnv *env, int i, int freedata) {
 	NativeArrayElem cv = nativeArrayTable[i];
 #if TRACE_NATIVE_ARRAYS
-		fprintf(traceFile, "releaseNativeArray(x=%p, ix=%d)\n", cv.obj, i);
+               fprintf(traceFile, "releaseNativeArray(x=%p, ix=%d, freedata=%d)\n", cv.obj, i, freedata);
 #endif
 	if (cv.obj != NULL) {
 		switch (cv.type) {
 		case INTSXP: {
 			jintArray intArray = (jintArray) cv.jArray;
-			(*env)->ReleaseIntArrayElements(env, intArray, (jint *)cv.data, 0);
+			(*env)->ReleaseIntArrayElements(env, intArray, (jint *)cv.data, freedata ? 0 : JNI_COMMIT);
 			break;
 		}
 
@@ -293,78 +311,109 @@ static void releaseNativeArray(JNIEnv *env, int i) {
 				internalData[i] = data[i] == NA_INTEGER ? 255 : (jbyte) data[i];
 			}
 			(*env)->ReleaseByteArrayElements(env, byteArray, internalData, 0);
-			free(data); // was malloc'ed in addNativeArray
+                       if (freedata){
+                           free(data); // was malloc'ed in addNativeArray
+                       }
 			break;
 		}
 
 		case REALSXP: {
 			jdoubleArray doubleArray = (jdoubleArray) cv.jArray;
-			(*env)->ReleaseDoubleArrayElements(env, doubleArray, (jdouble *)cv.data, 0);
+			(*env)->ReleaseDoubleArrayElements(env, doubleArray, (jdouble *)cv.data, freedata ? 0 : JNI_COMMIT);
 			break;
 
 		}
 
 		case RAWSXP: {
 			jbyteArray byteArray = (jbyteArray) cv.jArray;
-			(*env)->ReleaseByteArrayElements(env, byteArray, (jbyte *)cv.data, 0);
+			(*env)->ReleaseByteArrayElements(env, byteArray, (jbyte *)cv.data, freedata ? 0 : JNI_COMMIT);
 			break;
 
 		}
 		default:
 			fatalError("releaseNativeArray type");
 		}
-		// free up the slot
-		cv.obj = NULL;
+               if (freedata) {
+                   // free up the slot
+		    cv.obj = NULL;
+               }
 	}
 }
 
-static SEXP checkCachedGlobalRef(JNIEnv *env, SEXP obj, int useGlobal) {
-	int i;
-	for (i = 0; i < cachedGlobalRefsLength; i++) {
-		SEXP ref = cachedGlobalRefs[i];
-		if (ref == NULL) {
-			break;
+static SEXP findCachedGlobalRef(JNIEnv *env, SEXP obj) {
+	for (int i = 0; i < cachedGlobalRefsHwm; i++) {
+		GlobalRefElem elem = cachedGlobalRefs[i];
+		if (elem.gref == NULL) {
+			continue;
 		}
-		if ((*env)->IsSameObject(env, ref, obj)) {
+		if ((*env)->IsSameObject(env, elem.gref, obj)) {
 #if TRACE_REF_CACHE
 			fprintf(traceFile, "gref: cache hit: %d\n", i);
 #endif
-			return ref;
+			return elem.gref;
 		}
 	}
-	SEXP result;
-	if (useGlobal) {
-		if (i >= cachedGlobalRefsLength) {
-			int newLength = cachedGlobalRefsLength * 2;
+	return NULL;
+}
+
+SEXP addGlobalRef(JNIEnv *env, SEXP obj, int permanent) {
+	SEXP gref;
+	if (cachedGlobalRefsHwm >= cachedGlobalRefsLength) {
+		int newLength = cachedGlobalRefsLength * 2;
 #if TRACE_REF_CACHE
-			fprintf(traceFile, "gref: extending table to %d\n", newLength);
+		fprintf(traceFile, "gref: extending table to %d\n", newLength);
 #endif
-			SEXP newCachedGlobalRefs = calloc(newLength, sizeof(SEXP));
-			if (newCachedGlobalRefs == NULL) {
-				fatalError("FFI global refs table expansion failure");
-			}
-			memcpy(newCachedGlobalRefs, cachedGlobalRefs, cachedGlobalRefsLength * sizeof(SEXP));
-			free(cachedGlobalRefs);
-			cachedGlobalRefs = newCachedGlobalRefs;
-			cachedGlobalRefsLength = newLength;
+		SEXP newCachedGlobalRefs = calloc(newLength, sizeof(GlobalRefElem));
+		if (newCachedGlobalRefs == NULL) {
+			fatalError("FFI global refs table expansion failure");
 		}
-		result = (*env)->NewGlobalRef(env, obj);
-		cachedGlobalRefs[i] = result;
-	} else {
-		result = obj;
+		memcpy(newCachedGlobalRefs, cachedGlobalRefs, cachedGlobalRefsLength * sizeof(GlobalRefElem));
+		free(cachedGlobalRefs);
+		cachedGlobalRefs = newCachedGlobalRefs;
+		cachedGlobalRefsLength = newLength;
 	}
-	return result;
+	gref = (*env)->NewGlobalRef(env, obj);
+	cachedGlobalRefs[cachedGlobalRefsHwm].gref = gref;
+	cachedGlobalRefs[cachedGlobalRefsHwm].permanent = permanent;
+#if TRACE_REF_CACHE
+			fprintf(traceFile, "gref: add: index %d, ref %p\n", cachedGlobalRefsHwm), gref;
+#endif
+	cachedGlobalRefsHwm++;
+	return gref;
 }
 
 SEXP checkRef(JNIEnv *env, SEXP obj) {
-	SEXP result = checkCachedGlobalRef(env, obj, alwaysUseGlobal);
-	TRACE(TARGp, result);
-	return result;
+	SEXP gref = findCachedGlobalRef(env, obj);
+	TRACE(TARGpp, obj, global);
+	if (gref == NULL) {
+		return obj;
+	} else {
+	    return gref;
+	}
 }
 
-SEXP mkNamedGlobalRef(JNIEnv *env, SEXP obj) {
-	SEXP result = checkCachedGlobalRef(env, obj, 1);
-	return result;
+SEXP createGlobalRef(JNIEnv *env, SEXP obj, int permanent) {
+	SEXP gref = findCachedGlobalRef(env, obj);
+	if (gref == NULL) {
+		gref = addGlobalRef(env, obj, permanent);
+	}
+	return gref;
+}
+
+void releaseGlobalRef(JNIEnv *env, SEXP obj) {
+	for (int i = 0; i < cachedGlobalRefsHwm; i++) {
+		GlobalRefElem elem = cachedGlobalRefs[i];
+		if (elem.gref == NULL || elem.permanent) {
+			continue;
+		}
+		if ((*env)->IsSameObject(env, elem.gref, obj)) {
+#if TRACE_REF_CACHE
+			fprintf(traceFile, "gref: release: index %d, gref: %p\n", i, elem.gref);
+#endif
+			(*env)->DeleteGlobalRef(env, elem.gref);
+			cachedGlobalRefs[i].gref = NULL;
+		}
+	}
 }
 
 void validateRef(JNIEnv *env, SEXP x, const char *msg) {
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 62da16cdcf08af4579691d82e5b5d7b427c627f7..78bbe21b9fe3c27f7113b2f213ac5500e5721929 100644
--- a/com.oracle.truffle.r.native/fficall/src/jni/rffiutils.h
+++ b/com.oracle.truffle.r.native/fficall/src/jni/rffiutils.h
@@ -46,10 +46,14 @@ void *unimplemented(char *msg);
 void fatalError(char *msg);
 // makes a call to the VM with x as an argument (for debugger validation)
 void validate(SEXP x);
-// checks x against the list of canonical (named) refs, returning the canonical version if a match
+// checks x against the list of global JNI refs, returning the global version if x matches (IsSameObject)
 SEXP checkRef(JNIEnv *env, SEXP x);
-// creates a canonical (named) JNI global ref from x
-SEXP mkNamedGlobalRef(JNIEnv *env, SEXP x);
+// creates a global JNI global ref from x. If permanent is non-zero, calls to
+// releaseGlobalRef are ignored and the global ref persists for the entire execution
+// (used for the R global variables such as R_NilValue).
+SEXP createGlobalRef(JNIEnv *env, SEXP x, int permanent);
+// release a previously created JNI global ref
+void releaseGlobalRef(JNIEnv *env, SEXP x);
 // validate a JNI reference
 void validateRef(JNIEnv *env, SEXP x, const char *msg);
 
@@ -60,6 +64,7 @@ void callExit(JNIEnv *env);
 // called by callExit to deallocate transient memory
 void allocExit();
 
+// returns the jmp_buf at the current call depth
 jmp_buf *getErrorJmpBuf();
 
 // Given the x denotes an R vector type, return a pointer to
@@ -68,6 +73,9 @@ void *getNativeArray(JNIEnv *env, SEXP x, SEXPTYPE type);
 // Rare case where an operation changes the internal
 // data and thus the old C array should be invalidated
 void invalidateNativeArray(JNIEnv *env, SEXP oldObj);
+void updateNativeArrays(JNIEnv *env);
+
+SEXP addGlobalRef(JNIEnv *env, SEXP obj, int permanent);
 
 void init_rmath(JNIEnv *env);
 void init_variables(JNIEnv *env, jobjectArray initialValues);
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 328b067bd7ec76b0c201480912bc57097a77b9ae..ca381df5e2e84d8b6568bca259034d09d8227244 100644
--- a/com.oracle.truffle.r.native/fficall/src/jni/variables.c
+++ b/com.oracle.truffle.r.native/fficall/src/jni/variables.c
@@ -61,7 +61,8 @@ SEXP FASTR_NamespaceRegistry() {
 
 CTXT FASTR_GlobalContext() {
 	JNIEnv *env = getEnv();
-	return (*env)->CallStaticObjectMethod(env, CallRFFIHelperClass, getGlobalContextMethodID);
+	CTXT res = (*env)->CallStaticObjectMethod(env, CallRFFIHelperClass, getGlobalContextMethodID);
+    return addGlobalRef(env, res, 0);
 }
 
 void init_variables(JNIEnv *env, jobjectArray initialValues) {
@@ -104,7 +105,7 @@ void init_variables(JNIEnv *env, jobjectArray initialValues) {
 			} else if (strcmp(nameChars, "R_NaInt") == 0) {
 				R_NaInt = (*env)->CallIntMethod(env, value, intValueMethodID);
 			} else {
-				SEXP ref = mkNamedGlobalRef(env, value);
+				SEXP ref = createGlobalRef(env, value, 1);
 				if (strcmp(nameChars, "R_EmptyEnv") == 0) {
 					R_EmptyEnv = ref;
 				} else if (strcmp(nameChars, "R_NilValue") == 0) {
diff --git a/com.oracle.truffle.r.native/include/ed_Rinterface_gcntx b/com.oracle.truffle.r.native/include/ed_Rinterface_gcntx
index f9cf0613c24ce9f66f6d1c864deb23a289a24224..88fe94a62a6d39d815458b68669f08a7f1c49529 100644
--- a/com.oracle.truffle.r.native/include/ed_Rinterface_gcntx
+++ b/com.oracle.truffle.r.native/include/ed_Rinterface_gcntx
@@ -12,6 +12,8 @@ extern SEXP R_getContextFun(CTXT);
 extern SEXP R_getContextCall(CTXT);
 extern SEXP R_getContextSrcRef(CTXT);
 extern int R_insideBrowser();
+extern int R_isGlobal(CTXT);
+extern int R_isEqual(void*, void*);
 #else
 .
 +1
diff --git a/com.oracle.truffle.r.native/include/ed_Rinternals b/com.oracle.truffle.r.native/include/ed_Rinternals
index 7aaafb1cd67144dfa7bfab280f75ba96bc8861f8..e4be8532d85dbe199c91ab944fd300e2f46ed299 100644
--- a/com.oracle.truffle.r.native/include/ed_Rinternals
+++ b/com.oracle.truffle.r.native/include/ed_Rinternals
@@ -64,4 +64,14 @@ LibExtern SEXP FASTR_NamespaceRegistry();
 a
 #endif
 .
+/R_PreserveObject/
+i
+#ifdef FASTR
+SEXP R_PreserveObject(SEXP);
+#else
+.
++1
+a
+#endif
+.
 w Rinternals.h
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/helpers/BrowserInteractNode.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/helpers/BrowserInteractNode.java
index 408009cdde54b9733d2917ab6f83e63f7cd56fa1..0cac8ae1caf25989e975c4cf4876315e0741de98 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/helpers/BrowserInteractNode.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/helpers/BrowserInteractNode.java
@@ -26,8 +26,11 @@ import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.frame.MaterializedFrame;
 import com.oracle.truffle.api.frame.VirtualFrame;
+import com.oracle.truffle.r.nodes.RASTBuilder;
+import com.oracle.truffle.r.nodes.access.variables.ReadVariableNode;
 import com.oracle.truffle.r.runtime.JumpToTopLevelException;
 import com.oracle.truffle.r.runtime.RArguments;
+import com.oracle.truffle.r.runtime.RCaller;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.RSource;
 import com.oracle.truffle.r.runtime.RSrcref;
@@ -44,6 +47,7 @@ import com.oracle.truffle.r.runtime.data.RStringVector;
 import com.oracle.truffle.r.runtime.env.REnvironment;
 import com.oracle.truffle.r.runtime.instrument.InstrumentationState.BrowserState;
 import com.oracle.truffle.r.runtime.nodes.RNode;
+import com.oracle.truffle.r.runtime.nodes.RSyntaxNode;
 
 /**
  * The interactive component of the {@code browser} function.
@@ -58,6 +62,9 @@ import com.oracle.truffle.r.runtime.nodes.RNode;
  */
 public abstract class BrowserInteractNode extends RNode {
 
+    // it's never meant to be executed
+    private static final RSyntaxNode browserCall = new RASTBuilder().call(RSyntaxNode.INTERNAL, ReadVariableNode.create("browser"));
+
     public static final int STEP = 0;
     public static final int NEXT = 1;
     public static final int CONTINUE = 2;
@@ -75,8 +82,13 @@ public abstract class BrowserInteractNode extends RNode {
         // we may be at top level where there is not caller
         boolean callerIsDebugged = callerFunction == null ? false : DebugHandling.isDebugged(callerFunction);
         int exitMode = NEXT;
+        RCaller currentCaller = RArguments.getCall(mFrame);
+        if (currentCaller == null) {
+            currentCaller = RCaller.topLevel;
+        }
+        RCaller browserCaller = RCaller.create(null, currentCaller, browserCall);
         try {
-            browserState.setInBrowser(true);
+            browserState.setInBrowser(browserCaller);
             LW: while (true) {
                 String input = ch.readLine();
                 if (input != null) {
@@ -145,7 +157,7 @@ public abstract class BrowserInteractNode extends RNode {
             }
         } finally {
             ch.setPrompt(savedPrompt);
-            browserState.setInBrowser(false);
+            browserState.setInBrowser(null);
         }
         return exitMode;
     }
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 8b1ae9741ca36934671297541093e59ed73e8074..c838d1a4caea37d395247dce3386331281fde0d5 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
@@ -902,18 +902,19 @@ public class CallRFFIHelper {
         if (expr instanceof RPromise) {
             result = RContext.getRRuntimeASTAccess().forcePromise(null, expr);
         } else if (expr instanceof RExpression) {
-            result = RContext.getEngine().eval((RExpression) expr, (REnvironment) env, topLevel);
+            result = RContext.getEngine().eval((RExpression) expr, (REnvironment) env, RCaller.topLevel);
         } else if (expr instanceof RLanguage) {
-            result = RContext.getEngine().eval((RLanguage) expr, (REnvironment) env, topLevel);
+            result = RContext.getEngine().eval((RLanguage) expr, (REnvironment) env, RCaller.topLevel);
         } else if (expr instanceof RPairList) {
             RPairList l = (RPairList) expr;
             RFunction f = (RFunction) l.car();
             Object args = l.cdr();
             if (args == RNull.instance) {
-                result = RContext.getEngine().evalFunction(f, env == REnvironment.globalEnv() ? null : ((REnvironment) env).getFrame(), topLevel, null, new Object[0]);
+                result = RContext.getEngine().evalFunction(f, env == REnvironment.globalEnv() ? null : ((REnvironment) env).getFrame(), RCaller.topLevel, null, new Object[0]);
             } else {
                 RList argsList = ((RPairList) args).toRList();
-                result = RContext.getEngine().evalFunction(f, env == REnvironment.globalEnv() ? null : ((REnvironment) env).getFrame(), topLevel, argsList.getNames(), argsList.getDataNonShared());
+                result = RContext.getEngine().evalFunction(f, env == REnvironment.globalEnv() ? null : ((REnvironment) env).getFrame(), RCaller.topLevel, argsList.getNames(),
+                                argsList.getDataNonShared());
             }
 
         } else {
@@ -1165,18 +1166,20 @@ public class CallRFFIHelper {
         return x;
     }
 
-    private static RCaller topLevel = RCaller.createInvalid(null);
-
     public static Object getGlobalContext() {
+        Utils.warn("Potential memory leak (global context object)");
         if (RFFIUtils.traceEnabled()) {
             RFFIUtils.traceUpCall("getGlobalContext");
         }
         Frame frame = Utils.getActualCurrentFrame();
         if (frame == null) {
-            return topLevel;
+            return RCaller.topLevel;
+        }
+        if (RContext.getInstance().stateInstrumentation.getBrowserState().inBrowser()) {
+            return RContext.getInstance().stateInstrumentation.getBrowserState().getInBrowserCaller();
         }
         RCaller rCaller = RArguments.getCall(frame);
-        return rCaller == null ? topLevel : rCaller;
+        return rCaller == null ? RCaller.topLevel : rCaller;
     }
 
     public static Object getGlobalEnv() {
@@ -1249,6 +1252,7 @@ public class CallRFFIHelper {
     // 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");
         }
@@ -1258,26 +1262,28 @@ public class CallRFFIHelper {
         }
         RCaller currentCaller = RArguments.getCall(frame);
         while (currentCaller != null) {
-            if (!currentCaller.isPromise() && currentCaller.isValidCaller()) {
+            if (!currentCaller.isPromise() && currentCaller.isValidCaller() && currentCaller != RContext.getInstance().stateInstrumentation.getBrowserState().getInBrowserCaller()) {
                 break;
             }
             currentCaller = currentCaller.getParent();
         }
-        return currentCaller == null || currentCaller == topLevel ? RNull.instance : currentCaller;
+        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");
         }
         RCaller currentCaller = guaranteeInstanceOf(c, RCaller.class);
         while (true) {
             currentCaller = currentCaller.getParent();
-            if (currentCaller == null || (!currentCaller.isPromise() && currentCaller.isValidCaller())) {
+            if (currentCaller == null ||
+                            (!currentCaller.isPromise() && currentCaller.isValidCaller() && currentCaller != RContext.getInstance().stateInstrumentation.getBrowserState().getInBrowserCaller())) {
                 break;
             }
         }
-        return currentCaller == null || currentCaller == topLevel ? RNull.instance : currentCaller;
+        return currentCaller == null || currentCaller == RCaller.topLevel ? RNull.instance : currentCaller;
     }
 
     public static Object R_getContextEnv(Object c) {
@@ -1285,7 +1291,7 @@ public class CallRFFIHelper {
             RFFIUtils.traceUpCall("getContextEnv", c);
         }
         RCaller rCaller = guaranteeInstanceOf(c, RCaller.class);
-        if (rCaller == topLevel) {
+        if (rCaller == RCaller.topLevel) {
             return RContext.getInstance().stateREnvironment.getGlobalEnv();
         }
         Frame frame = Utils.getActualCurrentFrame();
@@ -1313,7 +1319,7 @@ public class CallRFFIHelper {
             RFFIUtils.traceUpCall("getContextEnv", c);
         }
         RCaller rCaller = guaranteeInstanceOf(c, RCaller.class);
-        if (rCaller == topLevel) {
+        if (rCaller == RCaller.topLevel) {
             return RNull.instance;
         }
         Frame frame = Utils.getActualCurrentFrame();
@@ -1341,27 +1347,10 @@ public class CallRFFIHelper {
             RFFIUtils.traceUpCall("getContextEnv", c);
         }
         RCaller rCaller = guaranteeInstanceOf(c, RCaller.class);
-        if (rCaller == topLevel) {
+        if (rCaller == RCaller.topLevel) {
             return RNull.instance;
         }
-        Frame frame = Utils.getActualCurrentFrame();
-        if (RArguments.getCall(frame) == rCaller) {
-            return RContext.getRRuntimeASTAccess().getSyntaxCaller(rCaller);
-        } else {
-            Object result = Utils.iterateRFrames(FrameAccess.READ_ONLY, new Function<Frame, Object>() {
-
-                @Override
-                public Object apply(Frame f) {
-                    RCaller currentCaller = RArguments.getCall(f);
-                    if (currentCaller == rCaller) {
-                        return RContext.getRRuntimeASTAccess().getSyntaxCaller(rCaller);
-                    } else {
-                        return null;
-                    }
-                }
-            });
-            return result;
-        }
+        return RContext.getRRuntimeASTAccess().getSyntaxCaller(rCaller);
     }
 
     public static Object R_getContextSrcRef(Object c) {
@@ -1384,4 +1373,21 @@ public class CallRFFIHelper {
     public static int R_insideBrowser() {
         return RContext.getInstance().stateInstrumentation.getBrowserState().inBrowser() ? 1 : 0;
     }
+
+    public static int R_isGlobal(Object c) {
+        if (RFFIUtils.traceEnabled()) {
+            RFFIUtils.traceUpCall("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);
+        }
+        return x == y ? 1 : 0;
+    }
+
 }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RCaller.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RCaller.java
index a8dd3a444a1376e122400b57b0f2698e5a64728f..6de384ca59ac9e204f606368dfef7b05be3b566d 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RCaller.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RCaller.java
@@ -33,6 +33,8 @@ import com.oracle.truffle.r.runtime.nodes.RSyntaxNode;
  */
 public final class RCaller {
 
+    public static final RCaller topLevel = RCaller.createInvalid(null);
+
     private static final Object PROMISE_MARKER = new Object();
 
     private final int depth;
@@ -96,6 +98,11 @@ public final class RCaller {
         return new RCaller(callingFrame, node);
     }
 
+    public static RCaller create(Frame callingFrame, RCaller parent, RSyntaxNode node) {
+        assert node != null;
+        return new RCaller(depthFromFrame(callingFrame), parent, node);
+    }
+
     public static RCaller create(Frame callingFrame, Supplier<RSyntaxNode> supplier) {
         assert supplier != null;
         return new RCaller(callingFrame, supplier);
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RDeparse.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RDeparse.java
index 065806b2fccacffc006817fc0a33d1f2d6b0e08c..5084d3acdce63c75618129a4a0b5e6385ce5de66 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RDeparse.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RDeparse.java
@@ -1064,7 +1064,7 @@ public class RDeparse {
                 root = list.getDataAtAsObject(0);
             }
         }
-        return new DeparseVisitor(false, 80, true, 0, -1, constants).process(root).getContents();
+        return new DeparseVisitor(false, 80, true, SHOWATTRIBUTES, -1, constants).process(root).getContents();
     }
 
     @TruffleBoundary
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/instrument/InstrumentationState.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/instrument/InstrumentationState.java
index b76b2d347e9f4a5cbea64068bd04acec3bf89e6e..b1a55172ce2f496442b27013b8366f929c0f189e 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/instrument/InstrumentationState.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/instrument/InstrumentationState.java
@@ -35,6 +35,7 @@ import com.oracle.truffle.api.instrumentation.ExecutionEventListener;
 import com.oracle.truffle.api.instrumentation.Instrumenter;
 import com.oracle.truffle.api.source.SourceSection;
 import com.oracle.truffle.api.vm.PolyglotEngine;
+import com.oracle.truffle.r.runtime.RCaller;
 import com.oracle.truffle.r.runtime.RCleanUp;
 import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.tools.Profiler;
@@ -134,16 +135,16 @@ public final class InstrumentationState implements RContext.ContextState {
             }
         }
 
-        private boolean inBrowser;
+        private RCaller caller;
         private String lastEmptyLineCommand = "n";
         private ArrayList<HelperState> helperStateList = new ArrayList<>();
 
-        public void setInBrowser(boolean state) {
-            this.inBrowser = state;
+        public void setInBrowser(RCaller caller) {
+            this.caller = caller;
         }
 
         public boolean inBrowser() {
-            return inBrowser;
+            return caller != null;
         }
 
         public void setLastEmptyLineCommand(String s) {
@@ -154,6 +155,10 @@ public final class InstrumentationState implements RContext.ContextState {
             return lastEmptyLineCommand;
         }
 
+        public RCaller getInBrowserCaller() {
+            return caller;
+        }
+
         @TruffleBoundary
         public void push(HelperState helperState) {
             helperStateList.add(helperState);
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 767a8c3cbcd9b43a42eed4a917dcaba28353adaf..6f53923b5377add006be17e501fd321463e2ffff 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
@@ -30,8 +30,8 @@ rffi.TYPEOF <- function(x) {
 	.Call("invoke_TYPEOF", x, PACKAGE = "testrffi")
 }
 
-rffi.error <- function() {
-	.Call("invoke_error", PACKAGE = "testrffi")
+rffi.error <- function(msg = "invoke_error in testrffi") {
+	.Call("invoke_error", msg, PACKAGE = "testrffi")
 }
 
 rffi.dotExternalAccessArgs <- function(...) {
@@ -95,3 +95,18 @@ rffi.iterate_iptr <- function(x) {
 	.Call("iterate_iptr", x, PACKAGE = "testrffi")
 }
 
+rffi.preserve_object <- function() {
+	.Call("preserve_object", PACKAGE = "testrffi")
+}
+
+rffi.release_object <- function(x) {
+	invisible(.Call("release_object", x, PACKAGE = "testrffi"))
+}
+
+rffi.findvar <- function(x, env) {
+	if (is.character(x)) {
+		x = as.symbol(x)
+	}
+	.Call("findvar", x, env, 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 0d6d6674ffcd3812a1b859494df934aebf536b1c..61f4862f88ee9e9ae9ea2f93e8c919517571f6c5 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
@@ -89,8 +89,8 @@ SEXP invoke_TYPEOF(SEXP x) {
 	return ScalarInteger(TYPEOF(x));
 }
 
-SEXP invoke_error() {
-	error("invoke_error in testrffi");
+SEXP invoke_error(SEXP msg) {
+	error(R_CHAR(STRING_ELT(msg, 0)));
 }
 
 // returns a
@@ -258,3 +258,25 @@ SEXP iterate_iptr(SEXP x) {
     UNPROTECT(1);
     return v;
 }
+
+SEXP preserve_object(void) {
+	SEXP v;
+	v = allocVector(INTSXP, 1);
+	R_PreserveObject(v);
+	return v;
+}
+
+SEXP release_object(SEXP x) {
+	R_ReleaseObject(x);
+    return R_NilValue;
+}
+
+SEXP findvar(SEXP x, SEXP env) {
+	SEXP v = Rf_findVar(x, env);
+	if (v == R_UnboundValue) {
+		Rf_error("'%s' not found", R_CHAR(PRINTNAME(x)));
+	} else {
+		return v;
+	}
+}
+