/** * set member of a Native JSObject for a given index. * * @param jEnv - JNIEnv on which the call is being made. * @param obj - A Native JS Object. * @param index - Index of a member. * @param jobj - Value to set. If this is a basic data type, it is converted * using standard JNI calls but if it is a wrapper to a JSObject * then a internal mapping is consulted to convert to a NJSObject. */ NS_METHOD nsCLiveconnect::SetSlot(JNIEnv *jEnv, lcjsobject obj, jint slot, jobject java_obj, void* principalsArray[], int numPrincipals, nsISupports *securitySupports) { if(jEnv == NULL || obj == 0) { return NS_ERROR_FAILURE; } JSJavaThreadState *jsj_env = NULL; JSObjectHandle *handle = (JSObjectHandle*)obj; JSObject *js_obj = handle->js_obj; JSContext *cx = NULL; jsval js_val; JSErrorReporter saved_state = NULL; jsj_env = jsj_enter_js(jEnv, mJavaClient, NULL, &cx, NULL, &saved_state, principalsArray, numPrincipals, securitySupports); if (!jsj_env) return NS_ERROR_FAILURE; AutoPushJSContext autopush(securitySupports, cx); if (NS_FAILED(autopush.ResultOfPush())) goto done; if (!jsj_ConvertJavaObjectToJSValue(cx, jEnv, java_obj, &js_val)) goto done; JS_SetElement(cx, js_obj, slot, &js_val); done: jsj_exit_js(cx, jsj_env, saved_state); return NS_OK; }
/* * Convert a Java value (primitive or object) to a JS value. * * This is usually an infallible operation, but JS_FALSE is returned * on an out-of-memory condition and the error reporter is called. */ JSBool jsj_ConvertJavaValueToJSValue(JSContext *cx, JNIEnv *jEnv, JavaSignature *signature, jvalue *java_value, jsval *vp) { int32 ival32; switch (signature->type) { case JAVA_SIGNATURE_VOID: *vp = JSVAL_VOID; return JS_TRUE; case JAVA_SIGNATURE_BYTE: *vp = INT_TO_JSVAL((jsint)java_value->b); return JS_TRUE; case JAVA_SIGNATURE_CHAR: *vp = INT_TO_JSVAL((jsint)java_value->c); return JS_TRUE; case JAVA_SIGNATURE_SHORT: *vp = INT_TO_JSVAL((jsint)java_value->s); return JS_TRUE; case JAVA_SIGNATURE_INT: ival32 = java_value->i; if (INT_FITS_IN_JSVAL(ival32)) { *vp = INT_TO_JSVAL((jsint) ival32); return JS_TRUE; } else { return JS_NewDoubleValue(cx, ival32, vp); } case JAVA_SIGNATURE_BOOLEAN: *vp = BOOLEAN_TO_JSVAL((JSBool) java_value->z); return JS_TRUE; case JAVA_SIGNATURE_LONG: return JS_NewDoubleValue(cx, jlong_to_jdouble(java_value->j), vp); case JAVA_SIGNATURE_FLOAT: return JS_NewDoubleValue(cx, java_value->f, vp); case JAVA_SIGNATURE_DOUBLE: return JS_NewDoubleValue(cx, java_value->d, vp); case JAVA_SIGNATURE_UNKNOWN: JS_ASSERT(0); return JS_FALSE; /* Non-primitive (reference) type */ default: JS_ASSERT(IS_REFERENCE_TYPE(signature->type)); return jsj_ConvertJavaObjectToJSValue(cx, jEnv, java_value->l, vp); } }
/* Utility routine to wrap a Java object inside a JS object, having a a result type of either JavaObject or JavaArray. */ JSBool JSJ_ConvertJavaObjectToJSValue(JSContext *cx, jobject java_obj, jsval *vp) { JNIEnv *jEnv; JSBool result; JSJavaThreadState *jsj_env; /* Get the Java per-thread environment pointer for this JSContext */ jsj_env = jsj_EnterJava(cx, &jEnv); if (!jEnv) return JS_FALSE; result = jsj_ConvertJavaObjectToJSValue(cx, jEnv, java_obj, vp); jsj_ExitJava(jsj_env); return result; }
/* * Class: netscape_javascript_JSObject * Method: setMember * Signature: (Ljava/lang/String;Ljava/lang/Object;)V */ JNIEXPORT void JNICALL Java_netscape_javascript_JSObject_setMember(JNIEnv *jEnv, jobject java_wrapper_obj, jstring property_name_jstr, jobject java_obj) { JSContext *cx = NULL; JSObject *js_obj; jsval js_val; const jchar *property_name_ucs2; jsize property_name_len; JSErrorReporter saved_reporter; jboolean is_copy; JSJavaThreadState *jsj_env; jsj_env = jsj_enter_js(jEnv, NULL, java_wrapper_obj, &cx, &js_obj, &saved_reporter, NULL, 0, NULL); if (!jsj_env) return; property_name_ucs2 = NULL; if (!property_name_jstr) { JS_ReportErrorNumber(cx, jsj_GetErrorMessage, NULL, JSJMSG_NULL_MEMBER_NAME); goto done; } /* Get the Unicode string for the JS property name */ property_name_ucs2 = (*jEnv)->GetStringChars(jEnv, property_name_jstr, &is_copy); if (!property_name_ucs2) { JS_ASSERT(0); goto done; } property_name_len = (*jEnv)->GetStringLength(jEnv, property_name_jstr); if (!jsj_ConvertJavaObjectToJSValue(cx, jEnv, java_obj, &js_val)) goto done; JS_SetUCProperty(cx, js_obj, property_name_ucs2, property_name_len, &js_val); done: if (property_name_ucs2) (*jEnv)->ReleaseStringChars(jEnv, property_name_jstr, property_name_ucs2); jsj_exit_js(cx, jsj_env, saved_reporter); }
/** * set member of a Native JSObject for a given name. * * @param jEnv - JNIEnv on which the call is being made. * @param obj - A Native JS Object. * @param name - Name of a member. * @param jobj - Value to set. If this is a basic data type, it is converted * using standard JNI calls but if it is a wrapper to a JSObject * then a internal mapping is consulted to convert to a NJSObject. */ NS_METHOD nsCLiveconnect::SetMember(JNIEnv *jEnv, lcjsobject obj, const jchar *name, jsize length, jobject java_obj, void* principalsArray[], int numPrincipals, nsISupports *securitySupports) { if(jEnv == NULL || obj == 0) { return NS_ERROR_FAILURE; } JSJavaThreadState *jsj_env = NULL; JSObjectHandle *handle = (JSObjectHandle*)obj; JSObject *js_obj = handle->js_obj; JSContext *cx = NULL; jsval js_val; JSErrorReporter saved_state = NULL; jsj_env = jsj_enter_js(jEnv, mJavaClient, NULL, &cx, NULL, &saved_state, principalsArray, numPrincipals, securitySupports); if (!jsj_env) return NS_ERROR_FAILURE; AutoPushJSContext autopush(securitySupports, cx); if (NS_FAILED(autopush.ResultOfPush())) goto done; if (!name) { JS_ReportError(cx, "illegal null member name"); goto done; } if (!jsj_ConvertJavaObjectToJSValue(cx, jEnv, java_obj, &js_val)) goto done; JS_SetUCProperty(cx, js_obj, name, length, &js_val); done: jsj_exit_js(cx, jsj_env, saved_state); return NS_OK; }
/* * Class: netscape_javascript_JSObject * Method: setSlot * Signature: (ILjava/lang/Object;)V */ JNIEXPORT void JNICALL Java_netscape_javascript_JSObject_setSlot(JNIEnv *jEnv, jobject java_wrapper_obj, jint slot, jobject java_obj) { JSContext *cx = NULL; JSObject *js_obj; jsval js_val; JSErrorReporter saved_reporter; JSJavaThreadState *jsj_env; jsj_env = jsj_enter_js(jEnv, NULL, java_wrapper_obj, &cx, &js_obj, &saved_reporter, NULL, 0, NULL); if (!jsj_env) return; if (!jsj_ConvertJavaObjectToJSValue(cx, jEnv, java_obj, &js_val)) goto done; JS_SetElement(cx, js_obj, slot, &js_val); done: jsj_exit_js(cx, jsj_env, saved_reporter); }
/* * Class: netscape_javascript_JSObject * Method: call * Signature: (Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/Object; */ JNIEXPORT jobject JNICALL Java_netscape_javascript_JSObject_call(JNIEnv *jEnv, jobject java_wrapper_obj, jstring function_name_jstr, jobjectArray java_args) { int i, argc, arg_num; jsval *argv; JSContext *cx = NULL; JSObject *js_obj; jsval js_val, function_val; int dummy_cost; JSBool dummy_bool; const jchar *function_name_ucs2; jsize function_name_len; JSErrorReporter saved_reporter; jboolean is_copy; jobject result; JSJavaThreadState *jsj_env; jsj_env = jsj_enter_js(jEnv, NULL, java_wrapper_obj, &cx, &js_obj, &saved_reporter, NULL, 0, NULL); if (!jsj_env) return NULL; function_name_ucs2 = NULL; result = NULL; if (!function_name_jstr) { JS_ReportErrorNumber(cx, jsj_GetErrorMessage, NULL, JSJMSG_NULL_FUNCTION_NAME); goto done; } /* Get the function name to eval as raw Unicode characters */ function_name_ucs2 = (*jEnv)->GetStringChars(jEnv, function_name_jstr, &is_copy); if (!function_name_ucs2) { JS_ASSERT(0); goto done; } function_name_len = (*jEnv)->GetStringLength(jEnv, function_name_jstr); /* Allocate space for JS arguments */ if (java_args) { argc = (*jEnv)->GetArrayLength(jEnv, java_args); argv = (jsval*)JS_malloc(cx, argc * sizeof(jsval)); } else { argc = 0; argv = 0; } /* Convert arguments from Java to JS values */ for (arg_num = 0; arg_num < argc; arg_num++) { jobject arg = (*jEnv)->GetObjectArrayElement(jEnv, java_args, arg_num); if (!jsj_ConvertJavaObjectToJSValue(cx, jEnv, arg, &argv[arg_num])) goto cleanup_argv; JS_AddNamedRoot(cx, &argv[arg_num], "&argv[arg_num]"); } if (!JS_GetUCProperty(cx, js_obj, function_name_ucs2, function_name_len, &function_val)) goto cleanup_argv; if (!JS_CallFunctionValue(cx, js_obj, function_val, argc, argv, &js_val)) goto cleanup_argv; jsj_ConvertJSValueToJavaObject(cx, jEnv, js_val, jsj_get_jlObject_descriptor(cx, jEnv), &dummy_cost, &result, &dummy_bool); cleanup_argv: if (argv) { for (i = 0; i < arg_num; i++) JS_RemoveRoot(cx, &argv[i]); JS_free(cx, argv); } done: if (function_name_ucs2) (*jEnv)->ReleaseStringChars(jEnv, function_name_jstr, function_name_ucs2); if (!jsj_exit_js(cx, jsj_env, saved_reporter)) return NULL; return result; }
/* * 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); }
/* * Read the value of a Java field and return it as a JavaScript value. * If the field is static, then java_obj is a Java class, otherwise * it's a Java instance object. * * Returns JS_TRUE on success. Otherwise, returns JS_FALSE and reports an error. */ JSBool jsj_GetJavaFieldValue(JSContext *cx, JNIEnv *jEnv, JavaFieldSpec *field_spec, jobject java_obj, jsval *vp) { JSBool is_static_field, success; jvalue java_value; JavaSignature *signature; JavaSignatureChar field_type; jfieldID fieldID = field_spec->fieldID; is_static_field = field_spec->modifiers & ACC_STATIC; #define GET_JAVA_FIELD(Type,member) \ JS_BEGIN_MACRO \ if (is_static_field) \ java_value.member = \ (*jEnv)->GetStatic##Type##Field(jEnv, (*jEnv)->GetObjectClass(jEnv, java_obj), fieldID); \ else \ java_value.member = \ (*jEnv)->Get##Type##Field(jEnv, java_obj, fieldID); \ if ((*jEnv)->ExceptionOccurred(jEnv)) { \ jsj_UnexpectedJavaError(cx, jEnv, "Error reading Java field"); \ return JS_FALSE; \ } \ JS_END_MACRO signature = field_spec->signature; field_type = signature->type; switch(field_type) { case JAVA_SIGNATURE_BYTE: GET_JAVA_FIELD(Byte,b); break; case JAVA_SIGNATURE_CHAR: GET_JAVA_FIELD(Char,c); break; case JAVA_SIGNATURE_SHORT: GET_JAVA_FIELD(Short,s); break; case JAVA_SIGNATURE_INT: GET_JAVA_FIELD(Int,i); break; case JAVA_SIGNATURE_BOOLEAN: GET_JAVA_FIELD(Boolean,z); break; case JAVA_SIGNATURE_LONG: GET_JAVA_FIELD(Long,j); break; case JAVA_SIGNATURE_FLOAT: GET_JAVA_FIELD(Float,f); break; case JAVA_SIGNATURE_DOUBLE: GET_JAVA_FIELD(Double,d); break; case JAVA_SIGNATURE_UNKNOWN: case JAVA_SIGNATURE_VOID: JS_ASSERT(0); /* Unknown java type signature */ return JS_FALSE; /* Non-primitive (reference) type */ default: JS_ASSERT(IS_REFERENCE_TYPE(field_type)); GET_JAVA_FIELD(Object,l); success = jsj_ConvertJavaObjectToJSValue(cx, jEnv, java_value.l, vp); (*jEnv)->DeleteLocalRef(jEnv, java_value.l); return success; } #undef GET_JAVA_FIELD return jsj_ConvertJavaValueToJSValue(cx, jEnv, signature, &java_value, vp); }
/** * call a method of Native JSObject. * * @param jEnv - JNIEnv on which the call is being made. * @param obj - A Native JS Object. * @param name - Name of a method. * @param jobjArr - Array of jobjects representing parameters of method being caled. * @param pjobj - return value. */ NS_METHOD nsCLiveconnect::Call(JNIEnv *jEnv, lcjsobject obj, const jchar *name, jsize length, jobjectArray java_args, void* principalsArray[], int numPrincipals, nsISupports *securitySupports, jobject *pjobj) { if(jEnv == NULL || obj == 0) { return NS_ERROR_FAILURE; } int i = 0; int argc = 0; int arg_num = 0; jsval *argv = 0; JSJavaThreadState *jsj_env = NULL; JSObjectHandle *handle = (JSObjectHandle*)obj; JSObject *js_obj = handle->js_obj; JSContext *cx = NULL; jsval js_val; jsval function_val = 0; int dummy_cost = 0; JSBool dummy_bool = PR_FALSE; JSErrorReporter saved_state = NULL; jobject result = NULL; jsj_env = jsj_enter_js(jEnv, mJavaClient, NULL, &cx, NULL, &saved_state, principalsArray, numPrincipals, securitySupports); if (!jsj_env) return NS_ERROR_FAILURE; result = NULL; AutoPushJSContext autopush(securitySupports, cx); if (NS_FAILED(autopush.ResultOfPush())) goto done; if (!name) { JS_ReportError(cx, "illegal null JavaScript function name"); goto done; } /* Allocate space for JS arguments */ argc = java_args ? jEnv->GetArrayLength(java_args) : 0; if (argc) { argv = (jsval*)JS_malloc(cx, argc * sizeof(jsval)); if (!argv) goto done; } else { argv = 0; } /* Convert arguments from Java to JS values */ for (arg_num = 0; arg_num < argc; arg_num++) { jobject arg = jEnv->GetObjectArrayElement(java_args, arg_num); JSBool ret = jsj_ConvertJavaObjectToJSValue(cx, jEnv, arg, &argv[arg_num]); jEnv->DeleteLocalRef(arg); if (!ret) goto cleanup_argv; JS_AddRoot(cx, &argv[arg_num]); } if (!JS_GetUCProperty(cx, js_obj, name, length, &function_val)) goto cleanup_argv; if (!JS_CallFunctionValue(cx, js_obj, function_val, argc, argv, &js_val)) goto cleanup_argv; jsj_ConvertJSValueToJavaObject(cx, jEnv, js_val, jsj_get_jlObject_descriptor(cx, jEnv), &dummy_cost, &result, &dummy_bool); cleanup_argv: if (argv) { for (i = 0; i < arg_num; i++) JS_RemoveRoot(cx, &argv[i]); JS_free(cx, argv); } done: if (!jsj_exit_js(cx, jsj_env, saved_state)) return NS_ERROR_FAILURE; *pjobj = result; return NS_OK; }