From a48d0180c52db47c488628fc61f132c12a20ece9 Mon Sep 17 00:00:00 2001 From: stepan <stepan.sindelar@oracle.com> Date: Wed, 21 Feb 2018 17:44:15 +0100 Subject: [PATCH] REmbedded API: support main loop initialization in Rf_initEmbeddedR Additionally restructured the Rembedded.c file to logical blocks, add visual dividers, clean-up the code. --- .../truffle/r/engine/shell/REmbedded.java | 22 +- .../fficall/src/truffle_nfi/Rembedded.c | 463 ++++++++++-------- 2 files changed, 263 insertions(+), 222 deletions(-) diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/REmbedded.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/REmbedded.java index afb26eabdb..a207d8247f 100644 --- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/REmbedded.java +++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/REmbedded.java @@ -71,7 +71,7 @@ public class REmbedded { * initialize FastR as we cannot do that until the embedding system has had a chance to adjust * the {@link RStartParams}, which happens after this call returns. */ - private static void initializeR(String[] args) { + private static void initializeR(String[] args, boolean initMainLoop) { assert context == null; RContext.setEmbedded(); RCmdOptions options = RCmdOptions.parseArguments(RCmdOptions.Client.R, args, false); @@ -86,6 +86,13 @@ public class REmbedded { context = Context.newBuilder().allowHostAccess(true).arguments("R", options.getArguments()).in(input).out(stdOut).err(stdErr).build(); consoleHandler.setContext(context); context.eval(INIT); + + if (initMainLoop) { + context.enter(); + RContext.getInstance().completeEmbeddedInitialization(); + // TODO: push callbacks + // stay in the context TODO should we? + } } /** @@ -110,12 +117,10 @@ public class REmbedded { */ private static final Source INIT = Source.newBuilder("R", "1", "<embedded>").buildLiteral(); - /** - * GnuR distinguishes {@code setup_Rmainloop} and {@code run_Rmainloop}. Currently we don't have - * the equivalent separation in FastR. - */ - private static void setupRmainloop() { - // nothing to do + private static void endRmainloop(int status) { + context.leave(); + context.close(); + Utils.systemExit(status); } /** @@ -135,8 +140,7 @@ public class REmbedded { * Testing vehicle, emulates a native upcall. */ public static void main(String[] args) { - initializeR(args); - setupRmainloop(); + initializeR(args, false); runRmainloop(); } diff --git a/com.oracle.truffle.r.native/fficall/src/truffle_nfi/Rembedded.c b/com.oracle.truffle.r.native/fficall/src/truffle_nfi/Rembedded.c index 2ee6783729..001cdcd36e 100644 --- a/com.oracle.truffle.r.native/fficall/src/truffle_nfi/Rembedded.c +++ b/com.oracle.truffle.r.native/fficall/src/truffle_nfi/Rembedded.c @@ -44,116 +44,90 @@ int R_DirtyImage; // TODO update this void *R_GlobalContext; // TODO what? SA_TYPE SaveAction; // ?? -typedef jint (JNICALL *JNI_CreateJavaVMFunc)(JavaVM **pvm, void **penv, void *args); - -static void *dlopen_jvmlib(char *libpath) { - void *handle = dlopen(libpath, RTLD_GLOBAL | RTLD_NOW); - if (handle == NULL) { - fprintf(stderr, "Rf_initialize_R: cannot dlopen %s: %s\n", libpath, dlerror()); - exit(1); - } - return handle; -} - -// -------------------- -// JNI helpers - -static JNIEnv* getEnv() { - return jniEnv; -} - -static jmethodID checkGetMethodID(JNIEnv *env, jclass klass, const char *name, const char *sig, int isStatic) { - jmethodID methodID = isStatic ? (*env)->GetStaticMethodID(env, klass, name, sig) : (*env)->GetMethodID(env, klass, name, sig); - if (methodID == NULL) { - char buf[1024]; - strcpy(buf, "failed to find "); - strcat(buf, isStatic ? "static" : "instance"); - strcat(buf, " method "); - strcat(buf, name); - strcat(buf, "("); - strcat(buf, sig); - strcat(buf, ")"); - (*env)->FatalError(env, buf); - } - return methodID; -} +int R_wait_usec; // TODO: necessary to resolve externals? otherwise dead code -jclass checkFindClass(JNIEnv *env, const char *name) { - jclass klass = (*env)->FindClass(env, name); - if (klass == NULL) { - char buf[1024]; - strcpy(buf, "failed to find class "); - strcat(buf, name); - strcat(buf, ".\nDid you set R_HOME to the correct location?"); - (*env)->FatalError(env, buf); - } - return (*env)->NewGlobalRef(env, klass); -} +typedef jint (JNICALL *JNI_CreateJavaVMFunc)(JavaVM **pvm, void **penv, void *args); -// --------------------- -// UpCalls +// ------------------------------------------------------ +// Forward declarations of static helper functions -// IDE and tools up-calls +static void setupOverrides(void); -CTXT R_getGlobalFunctionContext() { - return ((call_R_getGlobalFunctionContext) callbacks[R_getGlobalFunctionContext_x])(); -} +static void *dlopen_jvmlib(char *libpath); +static JNIEnv* getEnv(); +static jmethodID checkGetMethodID(JNIEnv *env, jclass klass, const char *name, const char *sig, int isStatic); +static jclass checkFindClass(JNIEnv *env, const char *name); +static int process_vmargs(int argc, char *argv[], char *vmargv[], char *uargv[]); +static char **update_environ_with_java_home(void); +static void print_environ(char **env); +static char *get_classpath(char *r_home); -CTXT R_getParentFunctionContext(CTXT c) { - return ((call_R_getParentFunctionContext) callbacks[R_getParentFunctionContext_x])(c); -} -SEXP R_getContextEnv(CTXT c) { - return ((call_R_getContextEnv) callbacks[R_getContextEnv_x])(c); -} +// ---------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------- +// The embedding API +// So far it seems that GNU R can be embedded by invoking either: +// 1) Rf_initEmbeddedR: initializing the main loop without actually running it after which the user +// can e.g. eval code using Rf_eval, finish with Rf_endEmbeddedR +// 2) Rf_initialize_R & Rf_mainloop: initializing and runnning the main loop -SEXP R_getContextFun(CTXT c) { - return ((call_R_getContextFun) callbacks[R_getContextFun_x])(c); -} +// Does the heavy work of starting up the JVM and invoking REmbedded#initializeR, which should be the +// only upcall to Java made via JNI. If setupRmainloop != 0, then also initialized the main loop (does not run it). +static int initializeFastR(int argc, char *argv[], int setupRmainloop); -SEXP R_getContextCall(CTXT c) { - return ((call_R_getContextCall) callbacks[R_getContextCall_x])(c); +// initializes R, but user is expected (TODO: probably) to invoke the main loop after that +int Rf_initialize_R(int argc, char *argv[]) { + return initializeFastR(argc, argv, 0); } -SEXP R_getContextSrcRef(CTXT c) { - return ((call_R_getContextSrcRef) callbacks[R_getContextSrcRef_x])(c); +// initializes R and the main loop without running it +int Rf_initEmbeddedR(int argc, char *argv[]) { + return initializeFastR(argc, argv, 1); } -int R_insideBrowser() { - return ((call_R_insideBrowser) callbacks[R_insideBrowser_x])(); +void R_DefParams(Rstart rs) { + // These are the GnuR defaults and correspond to the settings in RStartParams + // None of the size params make any sense for FastR + rs->R_Quiet = FALSE; + rs->R_Slave = FALSE; + rs->R_Interactive = TRUE; + rs->R_Verbose = FALSE; + rs->RestoreAction = SA_RESTORE; + rs->SaveAction = SA_SAVEASK; + rs->LoadSiteFile = TRUE; + rs->LoadInitFile = TRUE; + rs->DebugInitFile = FALSE; + rs->NoRenviron = FALSE; } -int R_isGlobal(CTXT c) { - return ((call_R_isGlobal) callbacks[R_isGlobal_x])(c); +// Allows to set-up some params before the main loop is initialized +// This call has to be made via JNI as we are not in a down-call, i.e. in truffle context, when this gets executed. +void R_SetParams(Rstart rs) { + JNIEnv *jniEnv = getEnv(); + jmethodID setParamsMethodID = checkGetMethodID(jniEnv, rembeddedClass, "setParams", "(ZZZZZZZIIZ)V", 1); + (*jniEnv)->CallStaticVoidMethod(jniEnv, rStartParamsClass, setParamsMethodID, rs->R_Quiet, rs->R_Slave, rs->R_Interactive, + rs->R_Verbose, rs->LoadSiteFile, rs->LoadInitFile, rs->DebugInitFile, + rs->RestoreAction, rs->SaveAction, rs->NoRenviron); } -int R_isEqual(void* x, void* y) { - return ((call_R_isEqual) callbacks[R_isEqual_x])(x, y); +// Runs the main REPL loop +void Rf_mainloop(void) { + JNIEnv *jniEnv = getEnv(); + setupOverrides(); + jmethodID mainloopMethod = checkGetMethodID(jniEnv, rembeddedClass, "runRmainloop", "()V", 1); + (*jniEnv)->CallStaticVoidMethod(jniEnv, rembeddedClass, mainloopMethod); } +void R_Suicide(const char *s) { ptr_R_Suicide(s); } -// separate vm args from user args -static int process_vmargs(int argc, char *argv[], char *vmargv[], char *uargv[]) { - int vcount = 0; - int ucount = 0; - for (int i = 0; i < argc; i++) { - char *arg = argv[i]; - if ((arg[0] == '-' && arg[1] == 'X') || (arg[0] == '-' && arg[1] == 'D')) { - vmargv[vcount++] = arg; - } else { - uargv[ucount++] = arg; - } - } - return vcount; +void Rf_endEmbeddedR(int fatal) { + // TODO: invoke com.oracle.truffle.r.engine.shell.REmbedded#endRmainloop + (*javaVM)->DestroyJavaVM(javaVM); + //TODO fatal } -// Forward declarations of helper functions -static char **update_environ_with_java_home(void); -static void print_environ(char **env); -static char *get_classpath(char *r_home); - -int Rf_initialize_R(int argc, char *argv[]) { +static int initializeFastR(int argc, char *argv[], int setupRmainloop) { if (initialized) { fprintf(stderr, "%s", "R is already initialized\n"); exit(1); @@ -242,104 +216,107 @@ int Rf_initialize_R(int argc, char *argv[]) { rInterfaceCallbacksClass = checkFindClass(jniEnv, "com/oracle/truffle/r/runtime/RInterfaceCallbacks"); rembeddedClass = checkFindClass(jniEnv, "com/oracle/truffle/r/engine/shell/REmbedded"); jclass stringClass = checkFindClass(jniEnv, "java/lang/String"); - jmethodID initializeMethod = checkGetMethodID(jniEnv, rembeddedClass, "initializeR", "([Ljava/lang/String;)V", 1); + jmethodID initializeMethod = checkGetMethodID(jniEnv, rembeddedClass, "initializeR", "([Ljava/lang/String;Z)V", 1); jobjectArray argsArray = (*jniEnv)->NewObjectArray(jniEnv, argc, stringClass, NULL); for (int i = 0; i < argc; i++) { jstring arg = (*jniEnv)->NewStringUTF(jniEnv, argv[i]); (*jniEnv)->SetObjectArrayElement(jniEnv, argsArray, i, arg); } + if (setupRmainloop) { + setupOverrides(); + } // Can't TRACE this upcall as system not initialized - (*jniEnv)->CallStaticObjectMethod(jniEnv, rembeddedClass, initializeMethod, argsArray); + (*jniEnv)->CallStaticObjectMethod(jniEnv, rembeddedClass, initializeMethod, argsArray, setupRmainloop); initialized++; return 0; } +// ----------------------------------------------------------------------------------------------- +// ----------------------------------------------------------------------------------------------- +// UpCalls from native to Java (IDE and tools up-calls) + char *R_HomeDir(void) { - // TODO: find out if this function could be invoked before the JVM and FastR get initialized - // in which case the access to callbacks will cause SIGSEGV + // TODO: why here? is it not implemented by RFFI already? return ((call_R_HomeDir) callbacks[R_HomeDir_x])(); } -void R_SaveGlobalEnvToFile(const char *f) { - unimplemented("R_SaveGlobalEnvToFile"); +CTXT R_getGlobalFunctionContext() { + return ((call_R_getGlobalFunctionContext) callbacks[R_getGlobalFunctionContext_x])(); } -void R_Suicide(const char *s) { ptr_R_Suicide(s); } +CTXT R_getParentFunctionContext(CTXT c) { + return ((call_R_getParentFunctionContext) callbacks[R_getParentFunctionContext_x])(c); +} -void R_DefParams(Rstart rs) { - // These are the GnuR defaults and correspond to the settings in RStartParams - // None of the size params make any sense for FastR - rs->R_Quiet = FALSE; - rs->R_Slave = FALSE; - rs->R_Interactive = TRUE; - rs->R_Verbose = FALSE; - rs->RestoreAction = SA_RESTORE; - rs->SaveAction = SA_SAVEASK; - rs->LoadSiteFile = TRUE; - rs->LoadInitFile = TRUE; - rs->DebugInitFile = FALSE; -// rs->vsize = R_VSIZE; -// rs->nsize = R_NSIZE; -// rs->max_vsize = R_SIZE_T_MAX; -// rs->max_nsize = R_SIZE_T_MAX; -// rs->ppsize = R_PPSSIZE; - rs->NoRenviron = FALSE; -// R_SizeFromEnv(Rp); +SEXP R_getContextEnv(CTXT c) { + return ((call_R_getContextEnv) callbacks[R_getContextEnv_x])(c); } -// This call has to be made via JNI as we are not in a down-call, i.e. in truffle context, when this gets executed. -void R_SetParams(Rstart rs) { - JNIEnv *jniEnv = getEnv(); - jmethodID setParamsMethodID = checkGetMethodID(jniEnv, rembeddedClass, "setParams", "(ZZZZZZZIIZ)V", 1); - (*jniEnv)->CallStaticVoidMethod(jniEnv, rStartParamsClass, setParamsMethodID, rs->R_Quiet, rs->R_Slave, rs->R_Interactive, - rs->R_Verbose, rs->LoadSiteFile, rs->LoadInitFile, rs->DebugInitFile, - rs->RestoreAction, rs->SaveAction, rs->NoRenviron); +SEXP R_getContextFun(CTXT c) { + return ((call_R_getContextFun) callbacks[R_getContextFun_x])(c); } -void R_SizeFromEnv(Rstart rs) { - unimplemented("R_SizeFromEnv"); +SEXP R_getContextCall(CTXT c) { + return ((call_R_getContextCall) callbacks[R_getContextCall_x])(c); } -void R_common_command_line(int *a, char **b, Rstart rs) { - unimplemented("R_common_command_line"); +SEXP R_getContextSrcRef(CTXT c) { + return ((call_R_getContextSrcRef) callbacks[R_getContextSrcRef_x])(c); } -void R_set_command_line_arguments(int argc, char **argv) { - unimplemented("R_set_command_line_arguments"); +int R_insideBrowser() { + return ((call_R_insideBrowser) callbacks[R_insideBrowser_x])(); } +int R_isGlobal(CTXT c) { + return ((call_R_isGlobal) callbacks[R_isGlobal_x])(c); +} -int Rf_initEmbeddedR(int argc, char *argv[]) { - Rf_initialize_R(argc, argv); -// R_Interactive = TRUE; - setup_Rmainloop(); - return 1; +int R_isEqual(void* x, void* y) { + return ((call_R_isEqual) callbacks[R_isEqual_x])(x, y); } -void Rf_endEmbeddedR(int fatal) { - (*javaVM)->DestroyJavaVM(javaVM); - //TODO fatal +// ----------------------------------------------------------------------------------------------- +// ----------------------------------------------------------------------------------------------- +// Downcalls from Java. We invoke these functions via REmbedRFFI + +static void writeConsoleImpl(char *cbuf, int len, int otype) { + if (ptr_R_WriteConsole == NULL) { + // otype gives std (0) or err (1) + (*ptr_R_WriteConsoleEx)(cbuf, len, otype); + } else { + (*ptr_R_WriteConsole)(cbuf, len); + } } -static void setupOverrides(void); +void rembedded_cleanup(int x, int y, int z) { + ptr_R_CleanUp(x, y, z); +} -void Rf_mainloop(void) { - JNIEnv *jniEnv = getEnv(); - setupOverrides(); - jmethodID mainloopMethod = checkGetMethodID(jniEnv, rembeddedClass, "runRmainloop", "()V", 1); - (*jniEnv)->CallStaticVoidMethod(jniEnv, rembeddedClass, mainloopMethod); +void rembedded_suicide(char* msg) { + ptr_R_Suicide(msg); } -// functions that can be assigned by an embedded client to change behavior +void rembedded_write_console(char *cbuf, int len) { + writeConsoleImpl(cbuf, len, 0); +} -// Note: pointer to this function is typically saved by the user to be called from that -// user's R_Suicide override to actually really commit the suicide. We invoke this through -// JNI intentionally to avoid any potential problems with NFI being called while destroying the VM. -void uR_Suicide(const char *x) { - JNIEnv *jniEnv = getEnv(); - jstring msg = (*jniEnv)->NewStringUTF(jniEnv, x); - jmethodID suicideMethod = checkGetMethodID(jniEnv, rembeddedClass, "R_Suicide", "(Ljava/lang/String;)V", 1); - (*jniEnv)->CallStaticVoidMethod(jniEnv, rembeddedClass, suicideMethod, msg); +void rembedded_write_err_console(char *cbuf, int len) { + writeConsoleImpl(cbuf, len, 1); +} + +char* rembedded_read_console(const char* prompt) { + unsigned char* cbuf = malloc(sizeof(char) * 1024); + int n = (*ptr_R_ReadConsole)(prompt, cbuf, 1024, 0); + return cbuf; +} + +// ----------------------------------------------------------------------------------------------- +// ----------------------------------------------------------------------------------------------- +// Unimplemented API functions (to make the linker happy and as a TODO list) + +void R_SaveGlobalEnvToFile(const char *f) { + unimplemented("R_SaveGlobalEnvToFile"); } void uR_ShowMessage(const char *x) { @@ -347,7 +324,8 @@ void uR_ShowMessage(const char *x) { } int uR_ReadConsole(const char *a, unsigned char *b, int c, int d) { - return (int) unimplemented("R_ReadConsole"); + unimplemented("R_ReadConsole"); + return 0; } void uR_WriteConsole(const char *x, int y) { @@ -374,17 +352,32 @@ void uR_Busy(int x) { unimplemented("R_Busy"); } +void R_SizeFromEnv(Rstart rs) { + unimplemented("R_SizeFromEnv"); +} + +void R_common_command_line(int *a, char **b, Rstart rs) { + unimplemented("R_common_command_line"); +} + +void R_set_command_line_arguments(int argc, char **argv) { + unimplemented("R_set_command_line_arguments"); +} + int uR_ShowFiles(int a, const char **b, const char **c, const char *d, Rboolean e, const char *f) { - return (int) unimplemented("R_ShowFiles"); + unimplemented("R_ShowFiles"); + return 0; } int uR_ChooseFile(int a, char *b, int c) { - return (int) unimplemented("R_ChooseFile"); + unimplemented("R_ChooseFile"); + return 0; } int uR_EditFile(const char *a) { - return (int) unimplemented("R_EditFile"); + unimplemented("R_EditFile"); + return 0; } void uR_loadhistory(SEXP a, SEXP b, SEXP c, SEXP d) { @@ -399,8 +392,9 @@ void uR_addhistory(SEXP a, SEXP b, SEXP c, SEXP d) { unimplemented("R_addhistory"); } -int uR_EditFiles(int a, const char **b, const char **c, const char *d) { - return (int)unimplemented(""); +int uR_EditFiles(int a, const char **b, const char **c, const char *d) { + unimplemented("uR_EditFiles"); + return 0; } SEXP udo_selectlist(SEXP a, SEXP b, SEXP c, SEXP d) { @@ -419,6 +413,49 @@ void uR_ProcessEvents(void) { unimplemented("R_ProcessEvents"); } +void uR_PolledEvents(void) { + unimplemented("R_PolledEvents"); +} + +void Rf_jump_to_toplevel() { + unimplemented("Rf_jump_to_toplevel"); +} + +#include <R_ext/eventloop.h> + +fd_set *R_checkActivity(int usec, int ignore_stdin) { + unimplemented("R_checkActivity"); + return NULL; +} + +void R_runHandlers(InputHandler *handlers, fd_set *mask) { + unimplemented("R_runHandlers"); +} + +// ----------------------------------------------------------------------------------------------- +// ----------------------------------------------------------------------------------------------- +// Functions that can be assigned by an embedded client to change behavior. +// On the Java side the RInterfaceCallbacks remembers for each callback whether its default +// value (pointer to the default implementation) was changed for some user defined function +// and in such case Java down-calls to that function if the event occures. Moreover the default +// values of those pointers (and functions they point to) should be also functional (not all are yet), +// because the user code sometimes saves the original value before overriding it to invoke it in the +// user's overridden version. 'setupOverrides' updates the Java side enum via JNI and is invoked from +// public functions that initialize the R embedding at the point where those pointers should be +// already overridden by the user. (We may reconsider that and always down-call if some embedding +// applications override those pointers after initialization). + + +// Note: pointer to this function is typically saved by the user to be called from that +// user's R_Suicide override to actually really commit the suicide. We invoke this through +// JNI intentionally to avoid any potential problems with NFI being called while destroying the VM. +void uR_Suicide(const char *x) { + JNIEnv *jniEnv = getEnv(); + jstring msg = (*jniEnv)->NewStringUTF(jniEnv, x); + jmethodID suicideMethod = checkGetMethodID(jniEnv, rembeddedClass, "R_Suicide", "(Ljava/lang/String;)V", 1); + (*jniEnv)->CallStaticVoidMethod(jniEnv, rembeddedClass, suicideMethod, msg); +} + void uR_CleanUp(SA_TYPE x, int y, int z) { ((call_R_CleanUp) callbacks[R_CleanUp_x])(x, y, z); } @@ -447,9 +484,9 @@ SEXP (*ptr_do_selectlist)(SEXP, SEXP, SEXP, SEXP) = udo_selectlist; SEXP (*ptr_do_dataentry)(SEXP, SEXP, SEXP, SEXP) = udo_dataentry; SEXP (*ptr_do_dataviewer)(SEXP, SEXP, SEXP, SEXP) = udo_dataviewer; void (*ptr_R_ProcessEvents)() = uR_ProcessEvents; +void (* R_PolledEvents)(void) = uR_PolledEvents; -// This call cannot be made via NFI because it is invoked from Rf_mainloop, -// which is exported C function expected to be invoked by the embedded before actually starting R engine. +// This call cannot be made via callbacks array because it may be invoked before FastR is fully initialized. void setupOverrides(void) { JNIEnv *jniEnv = getEnv(); jmethodID ovrMethodID = checkGetMethodID(jniEnv, rInterfaceCallbacksClass, "override", "(Ljava/lang/String;)V", 1); @@ -472,74 +509,74 @@ void setupOverrides(void) { } } -static void writeConsoleImpl(char *cbuf, int len, int otype) { - if (ptr_R_WriteConsole == NULL) { - // otype gives std (0) or err (1) - (*ptr_R_WriteConsoleEx)(cbuf, len, otype); - } else { - (*ptr_R_WriteConsole)(cbuf, len); - } -} - -void uR_PolledEvents(void) { - unimplemented("R_PolledEvents"); -} - -void (* R_PolledEvents)(void) = uR_PolledEvents; - -void Rf_jump_to_toplevel() { - unimplemented("Rf_jump_to_toplevel"); -} - -#include <R_ext/eventloop.h> - -fd_set *R_checkActivity(int usec, int ignore_stdin) { - return (fd_set*) unimplemented("R_checkActivity"); -} - -void R_runHandlers(InputHandler *handlers, fd_set *mask) { - unimplemented("R_runHandlers"); -} - // ----------------------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------------------- -// Downcalls from Java. We invoke these functions via REmbedRFFI +// Helpers -void rembedded_cleanup(int x, int y, int z) { - ptr_R_CleanUp(x, y, z); +// separate vm args from user args +static int process_vmargs(int argc, char *argv[], char *vmargv[], char *uargv[]) { + int vcount = 0; + int ucount = 0; + for (int i = 0; i < argc; i++) { + char *arg = argv[i]; + if ((arg[0] == '-' && arg[1] == 'X') || (arg[0] == '-' && arg[1] == 'D')) { + vmargv[vcount++] = arg; + } else { + uargv[ucount++] = arg; + } + } + return vcount; } -void rembedded_suicide(char* msg) { - ptr_R_Suicide(msg); -} +#include <sys/types.h> +#include <sys/wait.h> +#include <unistd.h> +#include <errno.h> -void rembedded_write_console(char *cbuf, int len) { - writeConsoleImpl(cbuf, len, 0); +static void perror_exit(char *msg) { + perror(msg); + exit(1); } -void rembedded_write_err_console(char *cbuf, int len) { - writeConsoleImpl(cbuf, len, 1); +static void *dlopen_jvmlib(char *libpath) { + void *handle = dlopen(libpath, RTLD_GLOBAL | RTLD_NOW); + if (handle == NULL) { + fprintf(stderr, "Rf_initialize_R: cannot dlopen %s: %s\n", libpath, dlerror()); + exit(1); + } + return handle; } -char* rembedded_read_console(const char* prompt) { - unsigned char* cbuf = malloc(sizeof(char) * 1024); - int n = (*ptr_R_ReadConsole)(prompt, cbuf, 1024, 0); - return cbuf; +static JNIEnv* getEnv() { + return jniEnv; } -// ----------------------------------------------------------------------------------------------- -// ----------------------------------------------------------------------------------------------- -// Helpers - - -int R_wait_usec; // TODO: necessary to resolve externals? otherwise dead code - -#include <unistd.h> -#include <errno.h> +static jmethodID checkGetMethodID(JNIEnv *env, jclass klass, const char *name, const char *sig, int isStatic) { + jmethodID methodID = isStatic ? (*env)->GetStaticMethodID(env, klass, name, sig) : (*env)->GetMethodID(env, klass, name, sig); + if (methodID == NULL) { + char buf[1024]; + strcpy(buf, "failed to find "); + strcat(buf, isStatic ? "static" : "instance"); + strcat(buf, " method "); + strcat(buf, name); + strcat(buf, "("); + strcat(buf, sig); + strcat(buf, ")"); + (*env)->FatalError(env, buf); + } + return methodID; +} -static void perror_exit(char *msg) { - perror(msg); - exit(1); +static jclass checkFindClass(JNIEnv *env, const char *name) { + jclass klass = (*env)->FindClass(env, name); + if (klass == NULL) { + char buf[1024]; + strcpy(buf, "failed to find class "); + strcat(buf, name); + strcat(buf, ".\nDid you set R_HOME to the correct location?"); + (*env)->FatalError(env, buf); + } + return (*env)->NewGlobalRef(env, klass); } // support for getting the correct classpath for the VM -- GitLab