/* * Convert a Java object to a boolean by attempting to call the * booleanValue() method on a Java object to get a boolean result. * This usually only works on instances of java.lang.Boolean, but the code * is generalized to work with any Java object that supports this method. * * Returns JS_TRUE if the call was successful. * Returns JS_FALSE if conversion is not possible or an error occurs. */ extern JSBool jsj_ConvertJavaObjectToJSBoolean(JSContext *cx, JNIEnv *jEnv, JavaClassDescriptor *class_descriptor, jobject java_obj, jsval *vp) { jboolean b; jmethodID booleanValue; jclass java_class; /* Null converts to false. */ if (!java_obj) { *vp = JSVAL_FALSE; return JS_TRUE; } java_class = class_descriptor->java_class; booleanValue = (*jEnv)->GetMethodID(jEnv, java_class, "booleanValue", "()Z"); /* Non-null Java object does not have a booleanValue() method, so it converts to true. */ if (!booleanValue) { (*jEnv)->ExceptionClear(jEnv); *vp = JSVAL_TRUE; return JS_TRUE; } b = (*jEnv)->CallBooleanMethod(jEnv, java_obj, booleanValue); if ((*jEnv)->ExceptionOccurred(jEnv)) { jsj_UnexpectedJavaError(cx, jEnv, "booleanValue() method failed"); return JS_FALSE; } *vp = BOOLEAN_TO_JSVAL(b); return JS_TRUE; }
/* * Convert a Java object to a number by attempting to call the * doubleValue() method on a Java object to get a double result. * This usually only works on instances of java.lang.Double, but the code * is generalized to work with any Java object that supports this method. * * Returns JS_TRUE if the call was successful. * Returns JS_FALSE if conversion is not possible or an error occurs. */ JSBool jsj_ConvertJavaObjectToJSNumber(JSContext *cx, JNIEnv *jEnv, JavaClassDescriptor *class_descriptor, jobject java_obj, jsval *vp) { jdouble d; jmethodID doubleValue; jclass java_class; java_class = class_descriptor->java_class; doubleValue = (*jEnv)->GetMethodID(jEnv, java_class, "doubleValue", "()D"); if (!doubleValue) { /* There is no doubleValue() method for the object. Try toString() instead and the JS engine will attempt to convert the result to a number. */ (*jEnv)->ExceptionClear(jEnv); return jsj_ConvertJavaObjectToJSString(cx, jEnv, class_descriptor, java_obj, vp); } d = (*jEnv)->CallDoubleMethod(jEnv, java_obj, doubleValue); if ((*jEnv)->ExceptionOccurred(jEnv)) { jsj_UnexpectedJavaError(cx, jEnv, "doubleValue() method failed"); return JS_FALSE; } return JS_NewDoubleValue(cx, d, vp); }
JSBool JavaStringToId(JSContext *cx, JNIEnv *jEnv, jstring jstr, jsid *idp) { const jschar *ucs2; JSString *jsstr; jsize ucs2_len; jsval val; ucs2 = (*jEnv)->GetStringChars(jEnv, jstr, 0); if (!ucs2) { jsj_UnexpectedJavaError(cx, jEnv, "Couldn't obtain Unicode characters" "from Java string"); return JS_FALSE; } ucs2_len = (*jEnv)->GetStringLength(jEnv, jstr); jsstr = JS_InternUCStringN(cx, ucs2, ucs2_len); (*jEnv)->ReleaseStringChars(jEnv, jstr, ucs2); if (!jsstr) return JS_FALSE; val = STRING_TO_JSVAL(jsstr); JS_ValueToId(cx, STRING_TO_JSVAL(jsstr), idp); return JS_TRUE; }
/* * Given a JVM handle to a java.lang.Class object, malloc a C-string * containing the UTF8 encoding of the fully qualified name of the class. * It's the caller's responsibility to free the returned string. * * If an error occurs, NULL is returned and the error reporter called. */ const char * jsj_GetJavaClassName(JSContext *cx, JNIEnv *jEnv, jclass java_class) { jstring java_class_name_jstr; const char *java_class_name; /* Get java.lang.String object containing class name */ java_class_name_jstr = (*jEnv)->CallObjectMethod(jEnv, java_class, jlClass_getName); if (!java_class_name_jstr) goto error; /* Fix bugzilla #183092 * It is necessary to check Exception from JPI * even though java_class_name_jstr != null */ #ifdef XP_UNIX if ((*jEnv)->ExceptionOccurred(jEnv)) goto error; #endif /* Convert to UTF8 encoding and copy */ java_class_name = jsj_DupJavaStringUTF(cx, jEnv, java_class_name_jstr); (*jEnv)->DeleteLocalRef(jEnv, java_class_name_jstr); return java_class_name; error: jsj_UnexpectedJavaError(cx, jEnv, "Can't get Java class name using" "java.lang.Class.getName()"); return NULL; }
jstring jsj_ConvertJSStringToJavaString(JSContext *cx, JNIEnv *jEnv, JSString *js_str) { jstring result; result = (*jEnv)->NewString(jEnv, JS_GetStringChars(js_str), JS_GetStringLength(js_str)); if (!result) { jsj_UnexpectedJavaError(cx, jEnv, "Couldn't construct instance " "of java.lang.String"); } return result; }
jsize jsj_GetJavaArrayLength(JSContext *cx, JNIEnv *jEnv, jarray java_array) { jsize array_length = (*jEnv)->GetArrayLength(jEnv, java_array); jthrowable java_exception = (*jEnv)->ExceptionOccurred(jEnv); if (java_exception) { jsj_UnexpectedJavaError(cx, jEnv, "Couldn't obtain array length"); (*jEnv)->DeleteLocalRef(jEnv, java_exception); return -1; } return array_length; }
/* * Return the class of a Java array's component type. This is not the same * as the array's element type. For example, the component type of an array * of type SomeType[][][] is SomeType[][], but its element type is SomeType. * * If an error occurs, NULL is returned and an error reported. */ static jclass get_java_array_component_class(JSContext *cx, JNIEnv *jEnv, jclass java_class) { jclass result; result = (*jEnv)->CallObjectMethod(jEnv, java_class, jlClass_getComponentType); if (!result) { jsj_UnexpectedJavaError(cx, jEnv, "Can't get Java array component class using " "java.lang.Class.getComponentType()"); return NULL; } return result; }
/* * Convert a Java object to a number by attempting to call the * doubleValue() method on a Java object to get a double result. * This usually only works on instances of java.lang.Double, but the code * is generalized to work with any Java object that supports this method. * * Returns JS_TRUE if the call was successful. * Returns JS_FALSE if conversion is not possible or an error occurs. */ JSBool jsj_ConvertJavaObjectToJSNumber(JSContext *cx, JNIEnv *jEnv, JavaClassDescriptor *class_descriptor, jobject java_obj, jsval *vp) { jdouble d; jmethodID doubleValue; jclass java_class; java_class = class_descriptor->java_class; doubleValue = (*jEnv)->GetMethodID(jEnv, java_class, "doubleValue", "()D"); if (!doubleValue) { /* There is no doubleValue() method for the object. Try toString() instead and the JS engine will attempt to convert the result to a number. */ (*jEnv)->ExceptionClear(jEnv); return jsj_ConvertJavaObjectToJSString(cx, jEnv, class_descriptor, java_obj, vp); } /* * Sun Java-Plugin team work around bug to be fixed in JRE1.5, where GetMethodID * called with a non-existent method name returns a non-null result. * See Mozilla bug 201164. */ if ((*jEnv)->ExceptionOccurred(jEnv)) { jsj_UnexpectedJavaError(cx, jEnv, "No doubleValue() method for class %s!", class_descriptor->name); return JS_FALSE; } d = (*jEnv)->CallDoubleMethod(jEnv, java_obj, doubleValue); if ((*jEnv)->ExceptionOccurred(jEnv)) { jsj_UnexpectedJavaError(cx, jEnv, "doubleValue() method failed"); return JS_FALSE; } return JS_NewDoubleValue(cx, d, vp); }
/* * Return a UTF8, null-terminated encoding of a Java string. The string must * be free'ed by the caller. * * If an error occurs, returns NULL and calls the JS error reporter. */ const char * jsj_DupJavaStringUTF(JSContext *cx, JNIEnv *jEnv, jstring jstr) { const char *str, *retval; str = (*jEnv)->GetStringUTFChars(jEnv, jstr, 0); if (!str) { jsj_UnexpectedJavaError(cx, jEnv, "Can't get UTF8 characters from " "Java string"); return NULL; } retval = JS_strdup(cx, str); (*jEnv)->ReleaseStringUTFChars(jEnv, jstr, str); return retval; }
/* * Attempt to obtain a JS string representation of a Java object. * The java_obj argument must be of type java.lang.Object or a subclass. * If java_obj is a Java string, it's value is simply extracted and * copied into a JS string. Otherwise, the toString() method is called * on java_obj. */ JSBool jsj_ConvertJavaObjectToJSString(JSContext *cx, JNIEnv *jEnv, JavaClassDescriptor *class_descriptor, jobject java_obj, jsval *vp) { JSString *js_str; jstring java_str; jmethodID toString; jclass java_class; /* Create a Java string, unless java_obj is already a java.lang.String */ if ((*jEnv)->IsInstanceOf(jEnv, java_obj, jlString)) { /* Extract Unicode from java.lang.String instance and convert to JS string */ js_str = jsj_ConvertJavaStringToJSString(cx, jEnv, java_obj); if (!js_str) return JS_FALSE; *vp = STRING_TO_JSVAL(js_str); return JS_TRUE; } java_class = class_descriptor->java_class; toString = (*jEnv)->GetMethodID(jEnv, java_class, "toString", "()Ljava/lang/String;"); if (!toString) { /* All Java objects have a toString method */ jsj_UnexpectedJavaError(cx, jEnv, "No toString() method for class %s!", class_descriptor->name); return JS_FALSE; } java_str = (*jEnv)->CallObjectMethod(jEnv, java_obj, toString); if (!java_str) { jsj_ReportJavaError(cx, jEnv, "toString() method failed"); return JS_FALSE; } /* Extract Unicode from java.lang.String instance and convert to JS string */ js_str = jsj_ConvertJavaStringToJSString(cx, jEnv, java_str); if (!js_str) { (*jEnv)->DeleteLocalRef(jEnv, java_str); return JS_FALSE; } *vp = STRING_TO_JSVAL(js_str); (*jEnv)->DeleteLocalRef(jEnv, java_str); return JS_TRUE; }
/* * Return, as a C string, the JVM stack trace associated with a Java * exception, as would be printed by java.lang.Throwable.printStackTrace(). * The caller is responsible for free'ing the returned string. * * Returns NULL if an error occurs. */ static const char * get_java_stack_trace(JSContext *cx, JNIEnv *jEnv, jthrowable java_exception) { const char *backtrace; jstring backtrace_jstr; backtrace = NULL; if (java_exception && njJSUtil_getStackTrace) { backtrace_jstr = (*jEnv)->CallStaticObjectMethod(jEnv, njJSUtil, njJSUtil_getStackTrace, java_exception); if (!backtrace_jstr) { jsj_UnexpectedJavaError(cx, jEnv, "Unable to get exception stack trace"); return NULL; } backtrace = jsj_DupJavaStringUTF(cx, jEnv, backtrace_jstr); (*jEnv)->DeleteLocalRef(jEnv, backtrace_jstr); } return backtrace; }
/* * The caller must call DeleteLocalRef() on the returned object when no more * references remain. */ jobject jsj_WrapJSObject(JSContext *cx, JNIEnv *jEnv, JSObject *js_obj) { jobject java_wrapper_obj; JSObjectHandle *handle; /* Create a tiny stub object to act as the GC root that points to the JSObject from its netscape.javascript.JSObject counterpart. */ handle = (JSObjectHandle*)JS_malloc(cx, sizeof(JSObjectHandle)); if (!handle) return NULL; handle->js_obj = js_obj; handle->rt = JS_GetRuntime(cx); /* Create a new Java object that wraps the JavaScript object by storing its address in a private integer field. */ #ifndef OJI #if JS_BYTES_PER_LONG == 8 java_wrapper_obj = (*jEnv)->NewObject(jEnv, njJSObject, njJSObject_JSObject, (jlong)handle); #else java_wrapper_obj = (*jEnv)->NewObject(jEnv, njJSObject, njJSObject_JSObject, (jint)handle); #endif #else if (JSJ_callbacks && JSJ_callbacks->get_java_wrapper != NULL) { java_wrapper_obj = JSJ_callbacks->get_java_wrapper(jEnv, (jint)handle); } #endif /*! OJI */ if (!java_wrapper_obj) { jsj_UnexpectedJavaError(cx, jEnv, "Couldn't create new instance of " "netscape.javascript.JSObject"); goto done; } JS_AddNamedRoot(cx, &handle->js_obj, "&handle->js_obj"); done: return java_wrapper_obj; }
static JavaClassDescriptor * new_class_descriptor(JSContext *cx, JNIEnv *jEnv, jclass java_class) { JavaClassDescriptor *class_descriptor; class_descriptor = (JavaClassDescriptor *)JS_malloc(cx, sizeof(JavaClassDescriptor)); if (!class_descriptor) return NULL; memset(class_descriptor, 0, sizeof(JavaClassDescriptor)); class_descriptor->name = jsj_GetJavaClassName(cx, jEnv, java_class); if (!class_descriptor->name) goto error; java_class = (*jEnv)->NewGlobalRef(jEnv, java_class); if (!java_class) { jsj_UnexpectedJavaError(cx, jEnv, "Unable to reference Java class"); goto error; } class_descriptor->java_class = java_class; if (!compute_java_class_signature(cx, jEnv, class_descriptor)) goto error; class_descriptor->modifiers = (*jEnv)->CallIntMethod(jEnv, java_class, jlClass_getModifiers); class_descriptor->ref_count = 1; if (!JSJ_HashTableAdd(java_class_reflections, java_class, class_descriptor, (void*)jEnv)) goto error; return class_descriptor; error: destroy_class_descriptor(cx, jEnv, class_descriptor); return NULL; }
/* * A utility routine to create a JavaScript Unicode string from a * java.lang.String (Unicode) string. */ JSString * jsj_ConvertJavaStringToJSString(JSContext *cx, JNIEnv *jEnv, jstring java_str) { JSString *js_str; jboolean is_copy; const jchar *ucs2_str; jsize ucs2_str_len; ucs2_str_len = (*jEnv)->GetStringLength(jEnv, java_str); ucs2_str = (*jEnv)->GetStringChars(jEnv, java_str, &is_copy); if (!ucs2_str) { jsj_UnexpectedJavaError(cx, jEnv, "Unable to extract native Unicode from Java string"); return NULL; } /* The string data passed into JS_NewUCString() is not copied, so make a copy of the Unicode character vector. */ js_str = JS_NewUCStringCopyN(cx, ucs2_str, ucs2_str_len); (*jEnv)->ReleaseStringChars(jEnv, java_str, ucs2_str); return js_str; }
/* * Convert a JS value to an instance of java.lang.Object or one of its subclasses, * performing any necessary type coercion. If non-trivial coercion is required, * the cost value is incremented. If the java_value pass-by-reference argument * is non-NULL, the resulting Java value is stored there. * * Returns JS_TRUE if the conversion is possible, JS_FALSE otherwise */ JSBool jsj_ConvertJSValueToJavaObject(JSContext *cx, JNIEnv *jEnv, jsval v, JavaSignature *signature, int *cost, jobject *java_value, JSBool *is_local_refp) { JSString *jsstr; jclass target_java_class; JS_ASSERT(IS_REFERENCE_TYPE(signature->type)); /* Initialize to default case, in which no new Java object is synthesized to perform the conversion and, therefore, no JNI local references are being held. */ *is_local_refp = JS_FALSE; /* Get the Java type of the target value */ target_java_class = signature->java_class; if (JSVAL_IS_OBJECT(v)) { JSObject *js_obj = JSVAL_TO_OBJECT(v); /* JS null is always assignable to a Java object */ if (!js_obj) { if (java_value) *java_value = NULL; return JS_TRUE; } if (JS_InstanceOf(cx, js_obj, &JavaObject_class, 0) || JS_InstanceOf(cx, js_obj, &JavaArray_class, 0)) { /* The source value is a Java object wrapped inside a JavaScript object. Unwrap the JS object and return the original Java object if it's class makes it assignment-compatible with the target class using Java's assignability rules. */ JavaObjectWrapper *java_wrapper = JS_GetPrivate(cx, js_obj); jobject java_obj = java_wrapper->java_obj; if ((*jEnv)->IsInstanceOf(jEnv, java_obj, target_java_class)) { if (java_value) *java_value = java_obj; return JS_TRUE; } /* Fall through, to attempt conversion to a Java string */ } else if (JS_InstanceOf(cx, js_obj, &JavaClass_class, 0)) { /* We're dealing with the reflection of a Java class */ JavaClassDescriptor *java_class_descriptor = JS_GetPrivate(cx, js_obj); /* Check if target type is java.lang.Class class */ if ((*jEnv)->IsAssignableFrom(jEnv, jlClass, target_java_class)) { if (java_value) *java_value = java_class_descriptor->java_class; return JS_TRUE; } /* Check if target type is netscape.javascript.JSObject wrapper class */ if (convert_js_obj_to_JSObject_wrapper(cx, jEnv, js_obj, signature, cost, java_value)) { if (java_value && *java_value) *is_local_refp = JS_TRUE; return JS_TRUE; } /* Fall through, to attempt conversion to a Java string */ } else if (JS_InstanceOf(cx, js_obj, &JavaMember_class, 0)) { if (!JS_ConvertValue(cx, v, JSTYPE_OBJECT, &v)) return JS_FALSE; return jsj_ConvertJSValueToJavaObject(cx, jEnv, v, signature, cost, java_value, is_local_refp); /* JS Arrays are converted, element by element, to Java arrays */ } else if (JS_IsArrayObject(cx, js_obj) && (signature->type == JAVA_SIGNATURE_ARRAY)) { if (convert_js_array_to_java_array(cx, jEnv, js_obj, signature, java_value)) { if (java_value && *java_value) *is_local_refp = JS_TRUE; return JS_TRUE; } return JS_FALSE; } else { /* Otherwise, see if the target type is the netscape.javascript.JSObject wrapper class or one of its subclasses, in which case a reference is passed to the original JS object by wrapping it inside an instance of netscape.javascript.JSObject */ if (convert_js_obj_to_JSObject_wrapper(cx, jEnv, js_obj, signature, cost, java_value)) { if (java_value && *java_value) *is_local_refp = JS_TRUE; return JS_TRUE; } /* Fall through, to attempt conversion to a Java string */ } } else if (JSVAL_IS_NUMBER(v)) { /* JS numbers, integral or not, can be converted to instances of java.lang.Double */ if ((*jEnv)->IsAssignableFrom(jEnv, jlDouble, target_java_class)) { if (java_value) { jsdouble d; if (!JS_ValueToNumber(cx, v, &d)) goto conversion_error; *java_value = (*jEnv)->NewObject(jEnv, jlDouble, jlDouble_Double, d); if (*java_value) { *is_local_refp = JS_TRUE; } else { jsj_UnexpectedJavaError(cx, jEnv, "Couldn't construct instance of java.lang.Double"); return JS_FALSE; } } return JS_TRUE; } /* Fall through, to attempt conversion to a java.lang.String ... */ } else if (JSVAL_IS_BOOLEAN(v)) { /* JS boolean values can be converted to instances of java.lang.Boolean */ if ((*jEnv)->IsAssignableFrom(jEnv, jlBoolean, target_java_class)) { if (java_value) { JSBool b; if (!JS_ValueToBoolean(cx, v, &b)) goto conversion_error; *java_value = (*jEnv)->NewObject(jEnv, jlBoolean, jlBoolean_Boolean, b); if (*java_value) { *is_local_refp = JS_TRUE; } else { jsj_UnexpectedJavaError(cx, jEnv, "Couldn't construct instance " "of java.lang.Boolean"); return JS_FALSE; } } return JS_TRUE; } /* Fall through, to attempt conversion to a java.lang.String ... */ } /* If the source JS type is either a string or undefined, or if no conversion is possible from a number, boolean or JS object, see if the target type is java.lang.String */ if ((*jEnv)->IsAssignableFrom(jEnv, jlString, target_java_class)) { /* Convert to JS string, if necessary, and then to a Java Unicode string */ jsstr = JS_ValueToString(cx, v); if (jsstr) { if (java_value) { *java_value = jsj_ConvertJSStringToJavaString(cx, jEnv, jsstr); if (*java_value) { *is_local_refp = JS_TRUE; } else { return JS_FALSE; } } return JS_TRUE; } } conversion_error: return JS_FALSE; }
/* * This function is called up returning from Java back into JS as a result of * a thrown netscape.javascript.JSException, which itself must have been caused * by a JS error when Java called into JS. The original JS error is * reconstituted from the JSException and re-reported as a JS error. * * Returns JS_FALSE if an internal error occurs, JS_TRUE otherwise. */ JSBool jsj_ReportUncaughtJSException(JSContext *cx, JNIEnv *jEnv, jthrowable java_exception) { JSBool success; JSErrorReport report; const char *linebuf, *filename, *message, *tokenptr; jint lineno, token_index; jstring linebuf_jstr, filename_jstr, message_jstr; /* Initialize everything to NULL */ memset(&report, 0, sizeof(JSErrorReport)); success = JS_FALSE; filename_jstr = linebuf_jstr = message_jstr = NULL; filename = message = linebuf = tokenptr = NULL; lineno = (*jEnv)->GetIntField(jEnv, java_exception, njJSException_lineno); report.lineno = lineno; filename_jstr = (*jEnv)->GetObjectField(jEnv, java_exception, njJSException_filename); if ((*jEnv)->ExceptionOccurred(jEnv)) { jsj_UnexpectedJavaError(cx, jEnv, "Unable to access filename field of a JSException"); goto done; } if (filename_jstr) filename = (*jEnv)->GetStringUTFChars(jEnv, filename_jstr, 0); report.filename = filename; linebuf_jstr = (*jEnv)->GetObjectField(jEnv, java_exception, njJSException_source); if ((*jEnv)->ExceptionOccurred(jEnv)) { jsj_UnexpectedJavaError(cx, jEnv, "Unable to access source field of a JSException"); goto done; } if (linebuf_jstr) linebuf = (*jEnv)->GetStringUTFChars(jEnv, linebuf_jstr, 0); report.linebuf = linebuf; token_index = (*jEnv)->GetIntField(jEnv, java_exception, njJSException_lineno); report.tokenptr = linebuf + token_index; message_jstr = (*jEnv)->CallObjectMethod(jEnv, java_exception, jlThrowable_getMessage); if ((*jEnv)->ExceptionOccurred(jEnv)) { jsj_UnexpectedJavaError(cx, jEnv, "Unable to access message of a JSException"); goto done; } if (message_jstr) message = (*jEnv)->GetStringUTFChars(jEnv, message_jstr, 0); js_ReportErrorAgain(cx, message, &report); success = JS_TRUE; done: if (filename_jstr && filename) (*jEnv)->ReleaseStringUTFChars(jEnv, filename_jstr, filename); if (linebuf_jstr && linebuf) (*jEnv)->ReleaseStringUTFChars(jEnv, linebuf_jstr, linebuf); if (message_jstr && message) (*jEnv)->ReleaseStringUTFChars(jEnv, message_jstr, message); return success; }
/* * 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); }
/* * Add a JavaMemberDescriptor to the collection of members in class_descriptor * for every public field of the identified Java class. (A separate collection * is kept in class_descriptor for static and instance members.) * If reflect_only_static_fields is set, instance fields are not reflected. If * it isn't set, only instance fields are reflected and static fields are not * reflected. * * Returns JS_TRUE on success. Otherwise, returns JS_FALSE and reports an error. */ JSBool jsj_ReflectJavaFields(JSContext *cx, JNIEnv *jEnv, JavaClassDescriptor *class_descriptor, JSBool reflect_only_static_fields) { int i; JSBool ok; jint modifiers; jobject java_field; jstring field_name_jstr; jarray joFieldArray; jsize num_fields; jclass java_class; /* Get a java array of java.lang.reflect.Field objects, by calling java.lang.Class.getFields(). */ java_class = class_descriptor->java_class; joFieldArray = (*jEnv)->CallObjectMethod(jEnv, java_class, jlClass_getFields); if (!joFieldArray) { jsj_UnexpectedJavaError(cx, jEnv, "Can't determine Java object's fields " "using java.lang.Class.getFields()"); return JS_FALSE; } /* Iterate over the class fields */ num_fields = (*jEnv)->GetArrayLength(jEnv, joFieldArray); for (i = 0; i < num_fields; i++) { /* Get the i'th reflected field */ java_field = (*jEnv)->GetObjectArrayElement(jEnv, joFieldArray, i); if (!java_field) { jsj_UnexpectedJavaError(cx, jEnv, "Can't access a Field[] array"); return JS_FALSE; } /* Get the field modifiers, e.g. static, public, private, etc. */ modifiers = (*jEnv)->CallIntMethod(jEnv, java_field, jlrField_getModifiers); if ((*jEnv)->ExceptionOccurred(jEnv)) { jsj_UnexpectedJavaError(cx, jEnv, "Can't access a Field's modifiers using" "java.lang.reflect.Field.getModifiers()"); return JS_FALSE; } /* Don't allow access to private or protected Java fields. */ if (!(modifiers & ACC_PUBLIC)) goto no_reflect; /* Reflect all instance fields or all static fields, but not both */ if (reflect_only_static_fields != ((modifiers & ACC_STATIC) != 0)) goto no_reflect; /* Determine the unqualified name of the field */ field_name_jstr = (*jEnv)->CallObjectMethod(jEnv, java_field, jlrField_getName); if (!field_name_jstr) { jsj_UnexpectedJavaError(cx, jEnv, "Can't obtain a Field's name" "java.lang.reflect.Field.getName()"); return JS_FALSE; } /* Add a JavaFieldSpec object to the JavaClassDescriptor */ ok = add_java_field_to_class_descriptor(cx, jEnv, class_descriptor, field_name_jstr, java_field, modifiers); if (!ok) return JS_FALSE; (*jEnv)->DeleteLocalRef(jEnv, field_name_jstr); field_name_jstr = NULL; no_reflect: (*jEnv)->DeleteLocalRef(jEnv, java_field); java_field = NULL; } (*jEnv)->DeleteLocalRef(jEnv, joFieldArray); /* Success */ return JS_TRUE; }
jobject jsj_WrapJSObject(JSContext *cx, JNIEnv *jEnv, JSObject *js_obj) { jobject java_wrapper_obj; JSHashEntry *he, **hep; java_wrapper_obj = NULL; #ifdef JSJ_THREADSAFE PR_EnterMonitor(js_obj_reflections_monitor); #endif /* First, look in the hash table for an existing reflection of the same JavaScript object. If one is found, return it. */ hep = JS_HashTableRawLookup(js_obj_reflections, (JSHashNumber)js_obj, js_obj); /* If the same JSObject is reflected into Java more than once then we should return the same Java object, both for efficiency and so that the '==' operator works as expected in Java when comparing two JSObjects. However, it is not possible to hold a reference to a Java object without inhibiting GC of that object, at least not in a portable way, i.e. a weak reference. So, for now, JSObject identity is broken. */ he = *hep; if (he) { java_wrapper_obj = (jobject)he->value; JS_ASSERT(java_wrapper_obj); if (java_wrapper_obj) goto done; } /* No existing reflection found, so create a new Java object that wraps the JavaScript object by storing its address in a private integer field. */ #ifndef OJI java_wrapper_obj = (*jEnv)->NewObject(jEnv, njJSObject, njJSObject_JSObject, (jint)js_obj); #else if (JSJ_callbacks && JSJ_callbacks->get_java_wrapper != NULL) { java_wrapper_obj = JSJ_callbacks->get_java_wrapper(jEnv, (jint)handle); } #endif /*! OJI */ if (!java_wrapper_obj) { jsj_UnexpectedJavaError(cx, jEnv, "Couldn't create new instance of " "netscape.javascript.JSObject"); goto done; } /* Add the new reflection to the hash table. */ he = JS_HashTableRawAdd(js_obj_reflections, hep, (JSHashNumber)js_obj, js_obj, java_wrapper_obj); if (he) { /* Tell the JavaScript GC about this object since the only reference to it may be in Java-land. */ JS_AddNamedRoot(cx, (void*)&he->key, "&he->key"); } else { JS_ReportOutOfMemory(cx); /* No need to delete java_wrapper_obj because Java GC will reclaim it */ java_wrapper_obj = NULL; } /* * Release local reference to wrapper object, since some JVMs seem reticent * about collecting it otherwise. */ /* FIXME -- beard: this seems to make calls into Java with JSObject's fail. */ /* We should really be creating a global ref if we are putting it in a hash table. */ /* (*jEnv)->DeleteLocalRef(jEnv, java_wrapper_obj); */ done: #ifdef JSJ_THREADSAFE PR_ExitMonitor(js_obj_reflections_monitor); #endif return java_wrapper_obj; }
/* * Add a single field, described by java_field, to the JavaMemberDescriptor * named by field_name within the given JavaClassDescriptor. * * Returns JS_TRUE on success. Otherwise, returns JS_FALSE and reports an error. */ static JSBool add_java_field_to_class_descriptor(JSContext *cx, JNIEnv *jEnv, JavaClassDescriptor *class_descriptor, jstring field_name_jstr, jobject java_field, /* a java.lang.reflect.Field */ jint modifiers) { jclass fieldType; jfieldID fieldID; jclass java_class; JSBool is_static_field; JavaMemberDescriptor *member_descriptor = NULL; const char *sig_cstr = NULL; const char *field_name = NULL; JavaSignature *signature = NULL; JavaFieldSpec *field_spec = NULL; is_static_field = modifiers & ACC_STATIC; if (is_static_field) { member_descriptor = jsj_GetJavaStaticMemberDescriptor(cx, jEnv, class_descriptor, field_name_jstr); } else { member_descriptor = jsj_GetJavaMemberDescriptor(cx, jEnv, class_descriptor, field_name_jstr); } if (!member_descriptor) goto error; field_spec = (JavaFieldSpec*)JS_malloc(cx, sizeof(JavaFieldSpec)); if (!field_spec) goto error; field_spec->modifiers = modifiers; /* Get the Java class corresponding to the type of the field */ fieldType = (*jEnv)->CallObjectMethod(jEnv, java_field, jlrField_getType); if (!fieldType) { jsj_UnexpectedJavaError(cx, jEnv, "Unable to determine type of field using" " java.lang.reflect.Field.getType()"); goto error; } signature = jsj_GetJavaClassDescriptor(cx, jEnv, fieldType); (*jEnv)->DeleteLocalRef(jEnv, fieldType); if (!signature) goto error; field_spec->signature = signature; field_name = jsj_DupJavaStringUTF(cx, jEnv, field_name_jstr); if (!field_name) goto error; field_spec->name = field_name; /* Compute the JNI-style (string-based) signature of the field type */ sig_cstr = jsj_ConvertJavaSignatureToString(cx, signature); if (!sig_cstr) goto error; /* Compute the JNI fieldID and cache it for quick field access */ java_class = class_descriptor->java_class; if (is_static_field) fieldID = (*jEnv)->GetStaticFieldID(jEnv, java_class, field_name, sig_cstr); else fieldID = (*jEnv)->GetFieldID(jEnv, java_class, field_name, sig_cstr); if (!fieldID) { jsj_UnexpectedJavaError(cx, jEnv, "Can't get Java field ID for class %s, field %s (sig=%s)", class_descriptor->name, field_name, sig_cstr); goto error; } field_spec->fieldID = fieldID; JS_free(cx, (char*)sig_cstr); member_descriptor->field = field_spec; /* Success */ return JS_TRUE; error: if (field_spec) { JS_FREE_IF(cx, (char*)field_spec->name); JS_free(cx, field_spec); } JS_FREE_IF(cx, (char*)sig_cstr); if (signature) jsj_ReleaseJavaClassDescriptor(cx, jEnv, signature); return JS_FALSE; }