/* Completes a lazy connection to the host Java VM. */ static JSBool jsj_ConnectToJavaVM(JSJavaVM *jsjava_vm) { if (!jsjava_vm->java_vm) { JSBool ok; JS_ASSERT(JSJ_callbacks->create_java_vm); JS_ASSERT(JSJ_callbacks->destroy_java_vm); ok = JSJ_callbacks->create_java_vm(&jsjava_vm->java_vm, &jsjava_vm->main_thread_env, jsjava_vm->init_args); if (!ok) { jsj_LogError("Failed to create Java VM\n"); return JS_FALSE; } /* Remember that we created the VM so that we know to destroy it later */ jsjava_vm->jsj_created_java_vm = JS_TRUE; } if (!jsjava_vm->jsj_inited_java_vm) { /* * JVM initialization for netscape.javascript.JSObject is performed * independently of the other classes that are initialized in * init_java_VM_reflection, because we allow it to fail. In the case * of failure, LiveConnect is still operative, but only when calling * from JS to Java and not vice-versa. */ init_netscape_java_classes(jsjava_vm, jsjava_vm->main_thread_env); /* Load the Java classes, and the method and field descriptors required for Java reflection. */ if (!init_java_VM_reflection(jsjava_vm, jsjava_vm->main_thread_env) || !jsj_InitJavaObjReflectionsTable()) { jsj_LogError("LiveConnect was unable to reflect one or more components of the Java runtime.\nGo to http://bugzilla.mozilla.org/show_bug.cgi?id=5369 for details.\n"); /* This function crashes when called from here. Check that all the preconditions for this call are satisfied before making it. [jd] JSJ_DisconnectFromJavaVM(jsjava_vm); */ return JS_FALSE; } jsjava_vm->jsj_inited_java_vm = JS_TRUE; } return JS_TRUE; }
/* This shutdown routine discards all JNI references to Java objects that have been reflected into JS, even if there are still references to them from JS. */ void jsj_DiscardJavaClassReflections(JNIEnv *jEnv) { JSJavaThreadState *jsj_env; char *err_msg; JSContext *cx; /* Get the per-thread state corresponding to the current Java thread */ jsj_env = jsj_MapJavaThreadToJSJavaThreadState(jEnv, &err_msg); JS_ASSERT(jsj_env); if (!jsj_env) goto error; /* Get the JSContext that we're supposed to use for this Java thread */ cx = jsj_env->cx; if (!cx) { /* We called spontaneously into JS from Java, rather than from JS into Java and back into JS. Invoke a callback to obtain/create a JSContext for us to use. */ if (JSJ_callbacks->map_jsj_thread_to_js_context) { #ifdef OJI cx = JSJ_callbacks->map_jsj_thread_to_js_context(jsj_env, NULL, /* FIXME: What should this argument be ? */ jEnv, &err_msg); #else cx = JSJ_callbacks->map_jsj_thread_to_js_context(jsj_env, jEnv, &err_msg); #endif if (!cx) goto error; } else { err_msg = JS_smprintf("Unable to find/create JavaScript execution " "context for JNI thread 0x%08x", jEnv); goto error; } } if (java_class_reflections) { JSJ_HashTableEnumerateEntries(java_class_reflections, enumerate_remove_java_class, (void*)jsj_env); JSJ_HashTableDestroy(java_class_reflections); java_class_reflections = NULL; } return; error: JS_ASSERT(!cx); if (err_msg) { jsj_LogError(err_msg); JS_smprintf_free(err_msg); } }
/* * Called once per Java VM, this function initializes the classes, fields, and * methods required for Java reflection. If java_vm is NULL, a new Java VM is * created, using the provided classpath in addition to any default classpath. * The classpath argument is ignored, however, if java_vm_arg is non-NULL. */ JSJavaVM * JSJ_ConnectToJavaVM(SystemJavaVM *java_vm_arg, void* initargs) { SystemJavaVM* java_vm; JSJavaVM *jsjava_vm; JNIEnv *jEnv; JS_ASSERT(JSJ_callbacks); JS_ASSERT(JSJ_callbacks->attach_current_thread); JS_ASSERT(JSJ_callbacks->detach_current_thread); JS_ASSERT(JSJ_callbacks->get_java_vm); jsjava_vm = (JSJavaVM*)malloc(sizeof(JSJavaVM)); if (!jsjava_vm) return NULL; memset(jsjava_vm, 0, sizeof(JSJavaVM)); java_vm = java_vm_arg; /* If a Java VM was passed in, try to attach to it on the current thread. */ if (java_vm) { jEnv = JSJ_callbacks->attach_current_thread(java_vm); if (jEnv == NULL) { jsj_LogError("Failed to attach to Java VM thread\n"); free(jsjava_vm); return NULL; } jsjava_vm->java_vm = java_vm; jsjava_vm->main_thread_env = jEnv; } else { jsjava_vm->init_args = initargs; } #ifdef JSJ_THREADSAFE if (jsjava_vm_list == NULL) { thread_list_monitor = (struct PRMonitor *) PR_NewMonitor(); } #endif /* JSJ_THREADSAFE */ /* Put this VM on the list of all created VMs */ jsjava_vm->next = jsjava_vm_list; jsjava_vm_list = jsjava_vm; return jsjava_vm; }
/* * At certain times during initialization, there may be no JavaScript context * available to direct error reports to, in which case the error messages * are sent to this function. The caller is responsible for free'ing * the js_error_msg argument. */ static void report_java_initialization_error(JNIEnv *jEnv, const char *js_error_msg) { const char *error_msg, *java_error_msg; java_error_msg = NULL; if (jEnv) { java_error_msg = jsj_GetJavaErrorMessage(jEnv); (*jEnv)->ExceptionClear(jEnv); } if (java_error_msg) { error_msg = JS_smprintf("initialization error: %s (%s)\n", js_error_msg, java_error_msg); free((void*)java_error_msg); } else { error_msg = JS_smprintf("initialization error: %s\n", js_error_msg); } jsj_LogError(error_msg); free((void*)error_msg); }
/* * This utility function is called just prior to returning into Java from JS. */ JSBool jsj_exit_js(JSContext *cx, JSJavaThreadState *jsj_env, JSErrorReporter original_reporter) { JNIEnv *jEnv; #ifdef JSJ_THREADSAFE JS_EndRequest(cx); #endif /* Restore the JS error reporter */ JS_SetErrorReporter(cx, original_reporter); jEnv = jsj_env->jEnv; #ifdef DEBUG /* Any Java exceptions should have been noticed and reported already */ if ((*jEnv)->ExceptionOccurred(jEnv)) { JS_ASSERT(0); jsj_LogError("Unhandled Java exception detected"); return JS_FALSE; } #endif /* * Convert reported JS errors to JSExceptions, unless the errors were * themselves the result of Java exceptions, in which case the original * Java exception is simply propagated. */ throw_any_pending_js_error_as_a_java_exception(jsj_env); /* Invoke callback, presumably used to implement concurrency constraints */ if (JSJ_callbacks && JSJ_callbacks->exit_js) JSJ_callbacks->exit_js(jEnv, cx); return JS_TRUE; }
JSJavaThreadState * jsj_enter_js(JNIEnv *jEnv, void* applet_obj, jobject java_wrapper_obj, JSContext **cxp, JSObject **js_objp, JSErrorReporter *old_error_reporterp, void **pNSIPrincipaArray, int numPrincipals, void *pNSISecurityContext) { JSContext *cx; char *err_msg; JSObject *js_obj; JSJavaThreadState *jsj_env; cx = NULL; err_msg = NULL; /* Invoke callback, presumably used to implement concurrency constraints */ if (JSJ_callbacks && JSJ_callbacks->enter_js_from_java) { #ifdef OJI if (!JSJ_callbacks->enter_js_from_java(jEnv, &err_msg, pNSIPrincipaArray, numPrincipals, pNSISecurityContext,applet_obj)) #else if (!JSJ_callbacks->enter_js_from_java(jEnv, &err_msg)) #endif goto entry_failure; } /* Check the JSObject pointer in the wrapper object. */ if (js_objp) { #ifdef PRESERVE_JSOBJECT_IDENTITY #if JS_BYTES_PER_LONG == 8 js_obj = (JSObject *)((*jEnv)->GetLongField(jEnv, java_wrapper_obj, njJSObject_long_internal)); #else js_obj = (JSObject *)((*jEnv)->GetIntField(jEnv, java_wrapper_obj, njJSObject_internal)); #endif #else /* !PRESERVE_JSOBJECT_IDENTITY */ js_obj = jsj_UnwrapJSObjectWrapper(jEnv, java_wrapper_obj); #endif /* PRESERVE_JSOBJECT_IDENTITY */ JS_ASSERT(js_obj); if (!js_obj) goto error; *js_objp = js_obj; } /* Get the per-thread state corresponding to the current Java thread */ jsj_env = jsj_MapJavaThreadToJSJavaThreadState(jEnv, &err_msg); if (!jsj_env) goto error; /* Get the JSContext that we're supposed to use for this Java thread */ cx = jsj_env->cx; if (!cx) { /* We called spontaneously into JS from Java, rather than from JS into Java and back into JS. Invoke a callback to obtain/create a JSContext for us to use. */ if (JSJ_callbacks && JSJ_callbacks->map_jsj_thread_to_js_context) { #ifdef OJI cx = JSJ_callbacks->map_jsj_thread_to_js_context(jsj_env, applet_obj, jEnv, &err_msg); #else cx = JSJ_callbacks->map_jsj_thread_to_js_context(jsj_env, jEnv, &err_msg); #endif if (!cx) goto error; } else { err_msg = JS_smprintf("Unable to find/create JavaScript execution " "context for JNI thread 0x%08x", jEnv); goto error; } } *cxp = cx; /* * Capture all JS error reports so that they can be thrown into the Java * caller as an instance of netscape.javascript.JSException. */ *old_error_reporterp = JS_SetErrorReporter(cx, capture_js_error_reports_for_java); #ifdef JSJ_THREADSAFE JS_BeginRequest(cx); #endif return jsj_env; error: /* Invoke callback, presumably used to implement concurrency constraints */ if (JSJ_callbacks && JSJ_callbacks->exit_js) JSJ_callbacks->exit_js(jEnv, cx); entry_failure: if (err_msg) { if (cx) JS_ReportError(cx, err_msg); else jsj_LogError(err_msg); free(err_msg); } return NULL; }
/* * This is called upon returning from JS back into Java. Any JS errors * reported during that time will be converted into Java exceptions. It's * possible that a JS error was actually triggered by Java at some point, in * which case the original Java exception is thrown. */ static void throw_any_pending_js_error_as_a_java_exception(JSJavaThreadState *jsj_env) { CapturedJSError *error; JNIEnv *jEnv; jstring message_jstr, linebuf_jstr, filename_jstr; jint index, lineno; JSErrorReport *report; JSContext *cx; jsval pending_exception; jobject java_obj; int dummy_cost; JSBool is_local_refp; JSType primitive_type; jthrowable java_exception; message_jstr = linebuf_jstr = filename_jstr = java_exception = NULL; /* Get the Java JNI environment */ jEnv = jsj_env->jEnv; cx = jsj_env->cx; /* Get the pending JS exception if it exists */ if (cx&&JS_IsExceptionPending(cx)) { if (!JS_GetPendingException(cx, &pending_exception)) goto out_of_memory; /* Find out the JSTYPE of this jsval. */ primitive_type = JS_TypeOfValue(cx, pending_exception); /* Convert jsval exception to a java object and then use it to create an instance of JSException. */ if (!jsj_ConvertJSValueToJavaObject(cx, jEnv, pending_exception, jsj_get_jlObject_descriptor(cx, jEnv), &dummy_cost, &java_obj, &is_local_refp)) goto done; java_exception = (*jEnv)->NewObject(jEnv, njJSException, njJSException_JSException_wrap, primitive_type, java_obj); if (is_local_refp) (*jEnv)->DeleteLocalRef(jEnv, java_obj); if (!java_exception) goto out_of_memory; /* Throw the newly-created JSException */ if ((*jEnv)->Throw(jEnv, java_exception) < 0) { JS_ASSERT(0); jsj_LogError("Couldn't throw JSException\n"); goto done; } JS_ClearPendingException(cx); return; } if (!jsj_env->pending_js_errors) { #ifdef DEBUG /* Any exception should be cleared as soon as it's detected, so there shouldn't be any pending. */ if ((*jEnv)->ExceptionOccurred(jEnv)) { /* A Java exception occurred, but nobody in JS-land noticed. */ JS_ASSERT(0); (*jEnv)->ExceptionClear(jEnv); } #endif return; } /* Get the deepest (oldest) saved JS error */ /* XXX - What's the right thing to do about newer errors ? For now we just throw them away */ error = jsj_env->pending_js_errors; while (error->next) error = error->next; /* * If the JS error was originally the result of a Java exception, rethrow * the original exception. */ if (error->java_exception) { (*jEnv)->Throw(jEnv, error->java_exception); goto done; } /* Propagate any JS errors that did not originate as Java exceptions into Java as an instance of netscape.javascript.JSException */ /* First, marshall the arguments to the JSException constructor */ message_jstr = NULL; if (error->message) { message_jstr = (*jEnv)->NewStringUTF(jEnv, error->message); if (!message_jstr) goto out_of_memory; } report = &error->report; filename_jstr = NULL; if (report->filename) { filename_jstr = (*jEnv)->NewStringUTF(jEnv, report->filename); if (!filename_jstr) goto out_of_memory; } linebuf_jstr = NULL; if (report->linebuf) { linebuf_jstr = (*jEnv)->NewStringUTF(jEnv, report->linebuf); if (!linebuf_jstr) goto out_of_memory; } lineno = report->lineno; index = report->linebuf ? report->tokenptr - report->linebuf : 0; /* Call the JSException constructor */ java_exception = (*jEnv)->NewObject(jEnv, njJSException, njJSException_JSException, message_jstr, filename_jstr, lineno, linebuf_jstr, index); if (!java_exception) goto out_of_memory; /* Throw the newly-created JSException */ if ((*jEnv)->Throw(jEnv, java_exception) < 0) { JS_ASSERT(0); jsj_UnexpectedJavaError(cx, jEnv, "Couldn't throw JSException\n"); } goto done; out_of_memory: /* No recovery possible */ JS_ASSERT(0); jsj_LogError("Out of memory while attempting to throw JSException\n"); done: jsj_ClearPendingJSErrors(jsj_env); /* * Release local references to Java objects, since some JVMs seem reticent * about collecting them otherwise. */ if (message_jstr) (*jEnv)->DeleteLocalRef(jEnv, message_jstr); if (filename_jstr) (*jEnv)->DeleteLocalRef(jEnv, filename_jstr); if (linebuf_jstr) (*jEnv)->DeleteLocalRef(jEnv, linebuf_jstr); if (java_exception) (*jEnv)->DeleteLocalRef(jEnv, java_exception); }
/* * This is a wrapper around JS_ReportError(), useful when an error condition * is the result of a JVM failure or exception condition. It appends the * message associated with the pending Java exception to the passed in * printf-style format string and arguments. */ static void vreport_java_error(JSContext *cx, JNIEnv *jEnv, const char *format, va_list ap) { jobject java_obj; jclass java_class; JavaClassDescriptor *class_descriptor; jthrowable java_exception; JSType wrapped_exception_type; jsval js_exception; java_obj = NULL; class_descriptor = NULL; /* Get the exception out of the java environment. */ java_exception = (*jEnv)->ExceptionOccurred(jEnv); if (!java_exception) { JSString *err_jsstr; char *err = JS_vsmprintf(format, ap); if (!err) return; err_jsstr = JS_NewString(cx, err, strlen(err)); if (!err_jsstr) return; JS_SetPendingException(cx, STRING_TO_JSVAL(err_jsstr)); return; } (*jEnv)->ExceptionClear(jEnv); /* Check for JSException */ if (njJSException && (*jEnv)->IsInstanceOf(jEnv, java_exception, njJSException)) { wrapped_exception_type = (*jEnv)->GetIntField(jEnv, java_exception, njJSException_wrappedExceptionType); /* (int) to suppress warning */ if ((int)wrapped_exception_type != JSTYPE_EMPTY) { java_obj = (*jEnv)->GetObjectField(jEnv, java_exception, njJSException_wrappedException); if ((java_obj == NULL) && (wrapped_exception_type == JSTYPE_OBJECT)) { js_exception = JSVAL_NULL; } else { java_class = (*jEnv)->GetObjectClass(jEnv, java_obj); class_descriptor = jsj_GetJavaClassDescriptor(cx, jEnv, java_class); /* OK to delete ref, since above call adds global ref */ (*jEnv)->DeleteLocalRef(jEnv, java_class); /* Convert native JS values back to native types. */ switch(wrapped_exception_type) { case JSTYPE_NUMBER: if (!jsj_ConvertJavaObjectToJSNumber(cx, jEnv, class_descriptor, java_obj, &js_exception)) goto error; break; case JSTYPE_BOOLEAN: if (!jsj_ConvertJavaObjectToJSBoolean(cx, jEnv, class_descriptor, java_obj, &js_exception)) goto error; break; case JSTYPE_STRING: if (!jsj_ConvertJavaObjectToJSString(cx, jEnv, class_descriptor, java_obj, &js_exception)) goto error; break; case JSTYPE_VOID: js_exception = JSVAL_VOID; break; case JSTYPE_OBJECT: case JSTYPE_FUNCTION: default: if ((*jEnv)->IsInstanceOf(jEnv, java_obj, njJSObject)) { js_exception = OBJECT_TO_JSVAL(jsj_UnwrapJSObjectWrapper(jEnv, java_obj)); if (!js_exception) goto error; } else { if (!jsj_ConvertJavaObjectToJSValue(cx, jEnv, java_obj, &js_exception)) goto error; } } } } /* Check for internal exception */ } else { if (!JSJ_ConvertJavaObjectToJSValue(cx, java_exception, &js_exception)) { goto error; } } /* Set pending JS exception and clear the java exception. */ JS_SetPendingException(cx, js_exception); goto done; error: JS_ASSERT(0); jsj_LogError("Out of memory while attempting to throw JSException\n"); done: if (class_descriptor) jsj_ReleaseJavaClassDescriptor(cx, jEnv, class_descriptor); if (java_obj) (*jEnv)->DeleteLocalRef(jEnv, java_obj); if (java_exception) (*jEnv)->DeleteLocalRef(jEnv, java_exception); }