From 30f6fcad122228e79eedee272fd29537e76b2dd5 Mon Sep 17 00:00:00 2001 From: artdeell Date: Fri, 15 Mar 2024 13:24:34 +0300 Subject: [PATCH] DO NOT USE. Feat[launcher]: start implementing custom JVM launcher Why do this: 1. This will allow us to fully move argument parsing to Java and skips headroom of parsing the arguments again by the Java's libjli launcher. 2. This removes a lot of implementation quirks of JLI, like the ones related to JVM path resolution. TODO for this to be complete: 1. Transform module-related arguemnts (they need to be in their `=` form for the JVM) 2. Handle `-XX:` style arguments --- .../java/com/oracle/dalvik/VMLauncher.java | 2 +- .../pojavlaunch/JavaGUILauncherActivity.java | 2 +- .../main/java/net/kdt/pojavlaunch/Tools.java | 19 +- .../net/kdt/pojavlaunch/utils/JREUtils.java | 19 +- .../src/main/jni/input_bridge_v3.c | 17 +- app_pojavlauncher/src/main/jni/jre_launcher.c | 355 +++++++++++------- app_pojavlauncher/src/main/jni/utils.c | 13 + app_pojavlauncher/src/main/jni/utils.h | 6 + 8 files changed, 274 insertions(+), 159 deletions(-) diff --git a/app_pojavlauncher/src/main/java/com/oracle/dalvik/VMLauncher.java b/app_pojavlauncher/src/main/java/com/oracle/dalvik/VMLauncher.java index 9586e15fc..5d0b24665 100644 --- a/app_pojavlauncher/src/main/java/com/oracle/dalvik/VMLauncher.java +++ b/app_pojavlauncher/src/main/java/com/oracle/dalvik/VMLauncher.java @@ -3,5 +3,5 @@ package com.oracle.dalvik; public final class VMLauncher { private VMLauncher() { } - public static native int launchJVM(String[] args); + public static native void launchJVM(String jvmPath, String[] vmArgs, String classpath, String mainClass, String[] applicationArgs); } diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/JavaGUILauncherActivity.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/JavaGUILauncherActivity.java index 6d559e30c..fab9aaa3d 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/JavaGUILauncherActivity.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/JavaGUILauncherActivity.java @@ -327,7 +327,7 @@ public class JavaGUILauncherActivity extends BaseActivity implements View.OnTouc Logger.appendToLog("Info: Java arguments: " + Arrays.toString(javaArgList.toArray(new String[0]))); - JREUtils.launchJavaVM(this, runtime,null,javaArgList, LauncherPreferences.PREF_CUSTOM_JAVA_ARGS); + //JREUtils.launchJavaVM(this, runtime,null,javaArgList, LauncherPreferences.PREF_CUSTOM_JAVA_ARGS); } catch (Throwable th) { Tools.showError(this, th, true); } diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/Tools.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/Tools.java index 294f0c925..0573cf195 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/Tools.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/Tools.java @@ -210,16 +210,25 @@ public final class Tools { javaArgList.add("-Dlog4j.configurationFile=" + configFile); } javaArgList.addAll(Arrays.asList(getMinecraftJVMArgs(versionId, gamedir))); - javaArgList.add("-cp"); - javaArgList.add(getLWJGL3ClassPath() + ":" + launchClassPath); + //javaArgList.add("-cp"); + //javaArgList.add(getLWJGL3ClassPath() + ":" + launchClassPath); - javaArgList.add(versionInfo.mainClass); - javaArgList.addAll(Arrays.asList(launchArgs)); + //javaArgList.add(versionInfo.mainClass); + //javaArgList.addAll(Arrays.asList(launchArgs)); // ctx.appendlnToLog("full args: "+javaArgList.toString()); String args = LauncherPreferences.PREF_CUSTOM_JAVA_ARGS; if(Tools.isValidString(minecraftProfile.javaArgs)) args = minecraftProfile.javaArgs; FFmpegPlugin.discover(activity); - JREUtils.launchJavaVM(activity, runtime, gamedir, javaArgList, args); + JREUtils.launchJavaVM( + activity, + runtime, + gamedir, + javaArgList, + getLWJGL3ClassPath() + ":" + launchClassPath, + versionInfo.mainClass, + launchArgs, + args + ); // If we returned, this means that the JVM exit dialog has been shown and we don't need to be active anymore. // We never return otherwise. The process will be killed anyway, and thus we will become inactive } diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/utils/JREUtils.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/utils/JREUtils.java index a145609b1..c0306135d 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/utils/JREUtils.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/utils/JREUtils.java @@ -273,7 +273,14 @@ public class JREUtils { // return ldLibraryPath; } - public static void launchJavaVM(final AppCompatActivity activity, final Runtime runtime, File gameDirectory, final List JVMArgs, final String userArgsString) throws Throwable { + public static void launchJavaVM(final AppCompatActivity activity, + final Runtime runtime, + File gameDirectory, + final List JVMArgs, + final String classpath, + final String mainClass, + final String[] appArgs, + final String userArgsString) throws Throwable { String runtimeHome = MultiRTUtils.getRuntimeHome(runtime.name).getAbsolutePath(); JREUtils.relocateLibPath(runtime, runtimeHome); @@ -306,17 +313,9 @@ public class JREUtils { initJavaRuntime(runtimeHome); setupExitTrap(activity.getApplication()); chdir(gameDirectory == null ? Tools.DIR_GAME_NEW : gameDirectory.getAbsolutePath()); - userArgs.add(0,"java"); //argv[0] is the program name according to C standard. - final int exitCode = VMLauncher.launchJVM(userArgs.toArray(new String[0])); - Logger.appendToLog("Java Exit code: " + exitCode); - if (exitCode != 0) { - LifecycleAwareAlertDialog.DialogCreator dialogCreator = (dialog, builder)-> - builder.setMessage(activity.getString(R.string.mcn_exit_title, exitCode)) - .setPositiveButton(R.string.main_share_logs, (dialogInterface, which)-> shareLog(activity)); + VMLauncher.launchJVM(jvmLibraryPath+"/libjvm.so", userArgs.toArray(new String[0]), classpath, mainClass.replace('.', '/'), appArgs); - LifecycleAwareAlertDialog.haltOnDialog(activity.getLifecycle(), activity, dialogCreator); - } MainActivity.fullyExit(); } diff --git a/app_pojavlauncher/src/main/jni/input_bridge_v3.c b/app_pojavlauncher/src/main/jni/input_bridge_v3.c index e877e57ef..d8424d7b7 100644 --- a/app_pojavlauncher/src/main/jni/input_bridge_v3.c +++ b/app_pojavlauncher/src/main/jni/input_bridge_v3.c @@ -32,6 +32,7 @@ #define EVENT_TYPE_SCROLL 1007 #define EVENT_TYPE_WINDOW_SIZE 1008 + jint (*orig_ProcessImpl_forkAndExec)(JNIEnv *env, jobject process, jint mode, jbyteArray helperpath, jbyteArray prog, jbyteArray argBlock, jint argc, jbyteArray envBlock, jint envc, jbyteArray dir, jintArray std_fds, jboolean redirectErrorStream); static void registerFunctions(JNIEnv *env); @@ -344,9 +345,10 @@ JNIEXPORT jstring JNICALL Java_org_lwjgl_glfw_CallbackBridge_nativeClipboard(JNI #ifdef DEBUG LOGD("Debug: Clipboard access is going on\n", pojav_environ->isUseStackQueueCall); #endif +#define dalvikEnv (scope.env) + scope_t scope; + scopeAttach(pojav_environ->dalvikJavaVMPtr, &scope); - JNIEnv *dalvikEnv; - (*pojav_environ->dalvikJavaVMPtr)->AttachCurrentThread(pojav_environ->dalvikJavaVMPtr, &dalvikEnv, NULL); assert(dalvikEnv != NULL); assert(pojav_environ->bridgeClazz != NULL); @@ -365,8 +367,9 @@ JNIEXPORT jstring JNICALL Java_org_lwjgl_glfw_CallbackBridge_nativeClipboard(JNI (*dalvikEnv)->DeleteLocalRef(dalvikEnv, copyDst); (*env)->ReleaseByteArrayElements(env, copySrc, (jbyte *)copySrcC, 0); } - (*pojav_environ->dalvikJavaVMPtr)->DetachCurrentThread(pojav_environ->dalvikJavaVMPtr); + scopeDetach(pojav_environ->dalvikJavaVMPtr, &scope); return pasteDst; +#undef dalvikEnv } JNIEXPORT jboolean JNICALL JavaCritical_org_lwjgl_glfw_CallbackBridge_nativeSetInputReady(jboolean inputReady) { @@ -383,11 +386,13 @@ JNIEXPORT jboolean JNICALL Java_org_lwjgl_glfw_CallbackBridge_nativeSetInputRead } JNIEXPORT void JNICALL Java_org_lwjgl_glfw_CallbackBridge_nativeSetGrabbing(__attribute__((unused)) JNIEnv* env, __attribute__((unused)) jclass clazz, jboolean grabbing) { - JNIEnv *dalvikEnv; - (*pojav_environ->dalvikJavaVMPtr)->AttachCurrentThread(pojav_environ->dalvikJavaVMPtr, &dalvikEnv, NULL); +#define dalvikEnv (scope.env) + scope_t scope; + scopeAttach(pojav_environ->dalvikJavaVMPtr, &scope); (*dalvikEnv)->CallStaticVoidMethod(dalvikEnv, pojav_environ->bridgeClazz, pojav_environ->method_onGrabStateChanged, grabbing); - (*pojav_environ->dalvikJavaVMPtr)->DetachCurrentThread(pojav_environ->dalvikJavaVMPtr); + scopeDetach(pojav_environ->dalvikJavaVMPtr, &scope); pojav_environ->isGrabbing = grabbing; +#undef dalvikEnv } jboolean critical_send_char(jchar codepoint) { diff --git a/app_pojavlauncher/src/main/jni/jre_launcher.c b/app_pojavlauncher/src/main/jni/jre_launcher.c index 6662ba383..a95f1a490 100644 --- a/app_pojavlauncher/src/main/jni/jre_launcher.c +++ b/app_pojavlauncher/src/main/jni/jre_launcher.c @@ -37,149 +37,232 @@ #include "utils.h" #include "environ/environ.h" -// Uncomment to try redirect signal handling to JVM -// #define TRY_SIG2JVM +typedef jint (*JNI_CreateJavaVM_t)(JavaVM**, JNIEnv**, void*); -// PojavLancher: fixme: are these wrong? -#define FULL_VERSION "1.8.0-internal" -#define DOT_VERSION "1.8" +typedef struct { + void* handle; + JNI_CreateJavaVM_t JNI_CreateJavaVM; +} jvm_library_t; -static const char* const_progname = "java"; -static const char* const_launcher = "openjdk"; -static const char** const_jargs = NULL; -static const char** const_appclasspath = NULL; -static const jboolean const_javaw = JNI_FALSE; -static const jboolean const_cpwildcard = JNI_TRUE; -static const jint const_ergo_class = 0; // DEFAULT_POLICY -static struct sigaction old_sa[NSIG]; - -void (*__old_sa)(int signal, siginfo_t *info, void *reserved); -int (*JVM_handle_linux_signal)(int signo, siginfo_t* siginfo, void* ucontext, int abort_if_unrecognized); - -void android_sigaction(int signal, siginfo_t *info, void *reserved) { - printf("process killed with signal %d code %p addr %p\n", signal,info->si_code,info->si_addr); - if (JVM_handle_linux_signal == NULL) { // should not happen, but still - __old_sa = old_sa[signal].sa_sigaction; - __old_sa(signal,info,reserved); - exit(1); - } else { - // Based on https://github.com/PojavLauncherTeam/openjdk-multiarch-jdk8u/blob/aarch64-shenandoah-jdk8u272-b10/hotspot/src/os/linux/vm/os_linux.cpp#L4688-4693 - int orig_errno = errno; // Preserve errno value over signal handler. - JVM_handle_linux_signal(signal, info, reserved, true); - errno = orig_errno; - } -} -typedef jint JNI_CreateJavaVM_func(JavaVM **pvm, void **penv, void *args); - -typedef jint JLI_Launch_func(int argc, char ** argv, /* main argc, argc */ - int jargc, const char** jargv, /* java args */ - int appclassc, const char** appclassv, /* app classpath */ - const char* fullversion, /* full version defined */ - const char* dotversion, /* dot version defined */ - const char* pname, /* program name */ - const char* lname, /* launcher name */ - jboolean javaargs, /* JAVA_ARGS */ - jboolean cpwildcard, /* classpath wildcard*/ - jboolean javaw, /* windows-only javaw */ - jint ergo /* ergonomics class policy */ -); - -static jint launchJVM(int margc, char** margv) { - void* libjli = dlopen("libjli.so", RTLD_LAZY | RTLD_GLOBAL); - - // Boardwalk: silence - // LOGD("JLI lib = %x", (int)libjli); - if (NULL == libjli) { - LOGE("JLI lib = NULL: %s", dlerror()); - return -1; - } - LOGD("Found JLI lib"); - - JLI_Launch_func *pJLI_Launch = - (JLI_Launch_func *)dlsym(libjli, "JLI_Launch"); - // Boardwalk: silence - // LOGD("JLI_Launch = 0x%x", *(int*)&pJLI_Launch); - - if (NULL == pJLI_Launch) { - LOGE("JLI_Launch = NULL"); - return -1; - } - - LOGD("Calling JLI_Launch"); - - return pJLI_Launch(margc, margv, - 0, NULL, // sizeof(const_jargs) / sizeof(char *), const_jargs, - 0, NULL, // sizeof(const_appclasspath) / sizeof(char *), const_appclasspath, - FULL_VERSION, - DOT_VERSION, - *margv, // (const_progname != NULL) ? const_progname : *margv, - *margv, // (const_launcher != NULL) ? const_launcher : *margv, - (const_jargs != NULL) ? JNI_TRUE : JNI_FALSE, - const_cpwildcard, const_javaw, const_ergo_class); -/* - return pJLI_Launch(argc, argv, - 0, NULL, 0, NULL, FULL_VERSION, - DOT_VERSION, *margv, *margv, // "java", "openjdk", - JNI_FALSE, JNI_TRUE, JNI_FALSE, 0); -*/ +bool load_vm_library(JNIEnv *env, jstring path, jvm_library_t* library) { + void* handle; + if(path) { + const char* jvm_path = (*env)->GetStringUTFChars(env, path, NULL); + if(jvm_path == NULL) { + printf("failed to get UTF contents of jvm_path\n"); + return false; + } + handle = dlopen(jvm_path, RTLD_NOW); + (*env)->ReleaseStringUTFChars(env, path, jvm_path); + } else { + handle = dlopen("libjvm.so", RTLD_NOW); + } + if(!handle) { + printf("failed to load JVM: %s\n", dlerror()); + return false; + } + library->JNI_CreateJavaVM = dlsym(handle, "JNI_CreateJavaVM"); + if(!library->JNI_CreateJavaVM) { + printf("failed to load JVM: %s\n", dlerror()); + dlclose(handle); + return false; + } + library->handle = handle; + return true; } -/* - * Class: com_oracle_dalvik_VMLauncher - * Method: launchJVM - * Signature: ([Ljava/lang/String;)I - */ -JNIEXPORT jint JNICALL Java_com_oracle_dalvik_VMLauncher_launchJVM(JNIEnv *env, jclass clazz, jobjectArray argsArray) { -#ifdef TRY_SIG2JVM - void* libjvm = dlopen("libjvm.so", RTLD_LAZY | RTLD_GLOBAL); - if (NULL == libjvm) { - LOGE("JVM lib = NULL: %s", dlerror()); - return -1; - } - JVM_handle_linux_signal = dlsym(libjvm, "JVM_handle_linux_signal"); -#endif +void unload_vm_library(jvm_library_t* library) { + dlclose(library->handle); +} - jint res = 0; - // int i; - //Prepare the signal trapper - struct sigaction catcher; - memset(&catcher,0,sizeof(sigaction)); - catcher.sa_sigaction = android_sigaction; - catcher.sa_flags = SA_SIGINFO|SA_RESTART; - // SA_RESETHAND; -#define CATCHSIG(X) sigaction(X, &catcher, &old_sa[X]) - CATCHSIG(SIGILL); - //CATCHSIG(SIGABRT); - CATCHSIG(SIGBUS); - CATCHSIG(SIGFPE); -#ifdef TRY_SIG2JVM - CATCHSIG(SIGSEGV); -#endif - CATCHSIG(SIGSTKFLT); - CATCHSIG(SIGPIPE); - CATCHSIG(SIGXFSZ); - //Signal trapper ready +bool transfer_arg(JNIEnv* env, jstring argJString, const char** argStringp) { + jsize strlen = (*env)->GetStringUTFLength(env, argJString); + char* argString = malloc((strlen + 1) * sizeof(char)); + if(argString == NULL) return false; + (*env)->GetStringUTFRegion(env, argJString, 0, strlen, argString); + // Add null terminator just in case if the implementation does not null terminate strings + argString[strlen] = 0; + *argStringp = argString; + return true; +} - // Save dalvik JNIEnv pointer for JVM launch thread - pojav_environ->dalvikJNIEnvPtr_ANDROID = env; +void free_vm_args(JavaVMOption* args, jsize length) { + for(jsize i = 0; i < length; i++) { + if(args[i].optionString != NULL) free((void*)args[i].optionString); + } +} - if (argsArray == NULL) { - LOGE("Args array null, returning"); - //handle error - return 0; +static void exceptionCheck(JNIEnv *env) { + if(!(*env)->ExceptionCheck(env)) return; + (*env)->ExceptionDescribe(env); + (*env)->ExceptionClear(env); +} + +bool initialize_vm_args(JNIEnv* env, jobjectArray vmArgs, jsize length, JavaVMOption* args) { + char* failureReason; + if(args == NULL) { + failureReason = "Out of memory (JavaVMOption* allocation)"; + goto fail; + } + if((*env)->PushLocalFrame(env, length) < 0) { + exceptionCheck(env); + failureReason = "Out of memory (local frame allocation)"; + goto fail; } - int argc = (*env)->GetArrayLength(env, argsArray); - char **argv = convert_to_char_array(env, argsArray); - - LOGD("Done processing args"); - - res = launchJVM(argc, argv); - - LOGD("Going to free args"); - free_char_array(env, argsArray, argv); - - LOGD("Free done"); - - return res; + for(jsize i = 0; i < length; i++) { + jstring argJString = (*env)->GetObjectArrayElement(env, vmArgs, i); + if(argJString == NULL) { + exceptionCheck(env); + failureReason = "One of the argument strings is NULL"; + goto fail_frame; + } + if(!transfer_arg(env, argJString, &(args[i].optionString))) { + exceptionCheck(env); + failureReason = "Argument string transfer failed"; + goto fail_frame; + } + } + (*env)->PopLocalFrame(env, NULL); + return true; + fail_frame: + (*env)->PopLocalFrame(env, NULL); + fail: + free_vm_args(args, length); + printf("%s\n",failureReason); + return false; +} + +bool transfer_app_args(JNIEnv* host_env, JNIEnv* guest_env, jobjectArray hostArray, jobjectArray guestArray, jsize length) { + char* failureReason; + if((*host_env)->PushLocalFrame(host_env, length) < 0) { + failureReason = "Out of memory (host local frame allocation)"; + goto fail; + } + if((*guest_env)->PushLocalFrame(guest_env, length) < 0) { + failureReason = "Out of memory (guest local frame allocation)"; + goto fail1; + } + for(jsize i = 0; i < length; i++) { + jstring hostEntry = (*host_env)->GetObjectArrayElement(host_env, hostArray, i); + // All entries are initialized to null by default, so we can just skip null host entries + if(hostEntry == NULL) continue; + const char* hostEntryUTF = (*host_env)->GetStringUTFChars(host_env, hostEntry, NULL); + if(hostEntryUTF == NULL) { + failureReason = "Unable to get UTF contents of application argument string"; + goto fail2; + } + jstring guestEntry = (*guest_env)->NewStringUTF(guest_env, hostEntryUTF); + if(guestEntry == NULL) { + failureReason = "Out of memory (guest argument string allocation)"; + goto fail2; + } + (*guest_env)->SetObjectArrayElement(guest_env, guestArray, i, guestEntry); + } + (*guest_env)->PopLocalFrame(guest_env, NULL); + (*host_env)->PopLocalFrame(host_env, NULL); + return true; + fail2: + exceptionCheck(guest_env); + (*guest_env)->PopLocalFrame(guest_env, NULL); + fail1: + exceptionCheck(guest_env); + (*host_env)->PopLocalFrame(host_env, NULL); + fail: + exceptionCheck(guest_env); + printf("%s\n", failureReason); + return false; +} + +#define CLASSPATH_ARG "-Djava.class.path=" +#define CLASSPATH_ARG_LEN 18 + +void setup_classpath(JNIEnv* env, jstring jclasspath, jsize length, char* classpath) { + memcpy(classpath, CLASSPATH_ARG, CLASSPATH_ARG_LEN); + (*env)->GetStringUTFRegion(env, jclasspath, 0, length, &classpath[CLASSPATH_ARG_LEN]); +} + +void vmh_exit(int result) { + printf("VM_EXIT %i\n", result); +} + +#define EXIT(x) { \ +printf("%s\n", x); \ +goto exit; \ +} + +#define EXITVM(x) { \ +printf("%s\n", x); \ +exceptionCheck(guest_env); \ +goto exit_destroyvm; \ +} + +JNIEXPORT void JNICALL Java_com_oracle_dalvik_VMLauncher_launchJVM(JNIEnv *env, + __attribute__((unused)) jclass clazz, + jstring vmPath, + jobjectArray vmArgs, + jstring classpath, + jstring mainClass, + jobjectArray appArgs) { + jvm_library_t library; + if(!load_vm_library(env, vmPath, &library)) return; + + + + jsize inSize = (*env)->GetArrayLength(env, vmArgs); + jsize realSize = inSize + 2; + JavaVMOption options[realSize]; + + jsize classpathLength = (*env)->GetStringUTFLength(env, classpath); + size_t totalLength = CLASSPATH_ARG_LEN + classpathLength + 1; + char classpath_c[totalLength]; + setup_classpath(env, classpath, classpathLength, classpath_c); + classpath_c[totalLength - 1] = 0; // Null terminate in case if setup_classpath doesn't do it. + options[inSize].optionString = classpath_c; + + const char* cmainClass = (*env)->GetStringUTFChars(env, mainClass, NULL); + if(cmainClass == NULL) EXIT("Failed to get classname string") + + if(!initialize_vm_args(env, vmArgs, inSize, options)) goto exit; + + options[inSize+1].optionString = "exit"; + options[inSize+1].extraInfo = vmh_exit; + + JavaVMInitArgs initArgs; + initArgs.options = options; + initArgs.nOptions = realSize; + initArgs.ignoreUnrecognized = JNI_FALSE; + initArgs.version = JNI_VERSION_1_6; + + for(jsize i = 0; i < initArgs.nOptions; i++) { + printf("%s\n", initArgs.options[i].optionString); + } + + JavaVM *guest_jvm; + JNIEnv *guest_env; + jint res = library.JNI_CreateJavaVM(&guest_jvm, &guest_env, (void*)&initArgs); + free_vm_args(options, inSize); + if(res < 0) EXIT("Failed to initialize the Java VM") + + jclass guest_main_class = (*guest_env)->FindClass(guest_env, cmainClass); + (*env)->ReleaseStringUTFChars(env, mainClass, cmainClass); + if(guest_main_class == NULL) EXITVM("Failed to locate main class") + + jmethodID guest_main_method = (*guest_env)->GetStaticMethodID(guest_env, guest_main_class, "main", "([Ljava/lang/String;)V"); + if(guest_main_method == NULL) EXITVM("Failed to locate main method") + + jclass guest_string_class = (*guest_env)->FindClass(guest_env, "java/lang/String"); + if(guest_string_class == NULL) EXITVM("Failed to locate String class") + + jsize appArgsCount = (*env)->GetArrayLength(env, appArgs); + jobjectArray guestArgsArray = (*guest_env)->NewObjectArray(guest_env, appArgsCount, guest_string_class, NULL); + if(!transfer_app_args(env, guest_env, appArgs, guestArgsArray, appArgsCount)) goto exit_destroyvm; + + (*guest_env)->CallStaticVoidMethod(guest_env, guest_main_class, guest_main_method, guestArgsArray); + exceptionCheck(guest_env); + exit_destroyvm: + (*guest_jvm)->DetachCurrentThread(guest_jvm); + (*guest_jvm)->DestroyJavaVM(guest_jvm); + exit: + unload_vm_library(&library); } diff --git a/app_pojavlauncher/src/main/jni/utils.c b/app_pojavlauncher/src/main/jni/utils.c index 571bc7254..18de6388b 100644 --- a/app_pojavlauncher/src/main/jni/utils.c +++ b/app_pojavlauncher/src/main/jni/utils.c @@ -13,6 +13,19 @@ typedef void (*android_update_LD_LIBRARY_PATH_t)(char*); long shared_awt_surface; +bool scopeAttach(JavaVM* vm, scope_t *scope) { + jint err = (*vm)->GetEnv(vm, (void**)&scope->env, JNI_VERSION_1_6); + if(err == JNI_EDETACHED) { + err = (*vm)->AttachCurrentThread(vm, &scope->env, NULL); + if(err == JNI_OK) scope->detach = true; + } + return err == JNI_OK; +} + +void scopeDetach(JavaVM* vm, scope_t *scope) { + if(scope->detach) (*vm)->DetachCurrentThread(vm); +} + char** convert_to_char_array(JNIEnv *env, jobjectArray jstringArray) { int num_rows = (*env)->GetArrayLength(env, jstringArray); char **cArray = (char **) malloc(num_rows * sizeof(char*)); diff --git a/app_pojavlauncher/src/main/jni/utils.h b/app_pojavlauncher/src/main/jni/utils.h index 4a03c726a..1e5d9329c 100644 --- a/app_pojavlauncher/src/main/jni/utils.h +++ b/app_pojavlauncher/src/main/jni/utils.h @@ -2,7 +2,13 @@ #include +typedef struct { + bool detach; + JNIEnv *env; +} scope_t; +bool scopeAttach(JavaVM*, scope_t*); +void scopeDetach(JavaVM*, scope_t*); char** convert_to_char_array(JNIEnv *env, jobjectArray jstringArray); jobjectArray convert_from_char_array(JNIEnv *env, char **charArray, int num_rows);