/* * 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); } }
/* * Convert a JS value to a Java value of the given type signature. The cost * variable is incremented if coercion is required, e.g. the source value is * a string, but the target type is a boolean. * * Returns JS_FALSE if no conversion is possible, either because the jsval has * a type that is wholly incompatible with the Java value, or because a scalar * jsval can't be represented in a variable of the target type without loss of * precision, e.g. the source value is "4.2" but the destination type is byte. * If conversion is not possible and java_value is non-NULL, the JS error * reporter is called with an appropriate message. */ JSBool jsj_ConvertJSValueToJavaValue(JSContext *cx, JNIEnv *jEnv, jsval v_arg, JavaSignature *signature, int *cost, jvalue *java_value, JSBool *is_local_refp) { JavaSignatureChar type; jsval v; JSBool success = JS_FALSE; /* 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; type = signature->type; v = v_arg; switch (type) { case JAVA_SIGNATURE_BOOLEAN: if (!JSVAL_IS_BOOLEAN(v)) { if (!JS_ConvertValue(cx, v, JSTYPE_BOOLEAN, &v)) goto conversion_error; if (JSVAL_IS_VOID(v)) goto conversion_error; (*cost)++; } if (java_value) java_value->z = (jboolean)(JSVAL_TO_BOOLEAN(v) == JS_TRUE); break; case JAVA_SIGNATURE_SHORT: JSVAL_TO_INTEGRAL_JVALUE(short, s, jshort, v, java_value); break; case JAVA_SIGNATURE_BYTE: JSVAL_TO_INTEGRAL_JVALUE(byte, b, jbyte, v, java_value); break; case JAVA_SIGNATURE_CHAR: /* A one-character string can be converted into a character */ if (JSVAL_IS_STRING(v) && (JS_GetStringLength(JSVAL_TO_STRING(v)) == 1)) { v = INT_TO_JSVAL(*JS_GetStringChars(JSVAL_TO_STRING(v))); } JSVAL_TO_INTEGRAL_JVALUE(char, c, jchar, v, java_value); break; case JAVA_SIGNATURE_INT: JSVAL_TO_INTEGRAL_JVALUE(int, i, jint, v, java_value); break; case JAVA_SIGNATURE_LONG: #if defined(XP_MAC) || (defined(XP_OS2) && !defined(HAVE_LONG_LONG)) JSVAL_TO_JLONG_JVALUE(j, jlong, v, java_value); #else JSVAL_TO_INTEGRAL_JVALUE(long, j, jlong, v, java_value); #endif break; case JAVA_SIGNATURE_FLOAT: if (!JSVAL_IS_NUMBER(v)) { if (!JS_ConvertValue(cx, v, JSTYPE_NUMBER, &v)) goto conversion_error; (*cost)++; } if (java_value) { if (JSVAL_IS_INT(v)) java_value->f = (jfloat) JSVAL_TO_INT(v); else java_value->f = (jfloat) *JSVAL_TO_DOUBLE(v); } break; case JAVA_SIGNATURE_DOUBLE: if (!JSVAL_IS_NUMBER(v)) { if (!JS_ConvertValue(cx, v, JSTYPE_NUMBER, &v)) goto conversion_error; (*cost)++; } if (java_value) { if (JSVAL_IS_INT(v)) java_value->d = (jdouble) JSVAL_TO_INT(v); else java_value->d = (jdouble) *JSVAL_TO_DOUBLE(v); } break; /* Non-primitive (reference) type */ default: JS_ASSERT(IS_REFERENCE_TYPE(type)); if (!jsj_ConvertJSValueToJavaObject(cx, jEnv, v, signature, cost, &java_value->l, is_local_refp)) goto conversion_error; break; case JAVA_SIGNATURE_UNKNOWN: case JAVA_SIGNATURE_VOID: JS_ASSERT(0); return JS_FALSE; } /* Success */ return JS_TRUE; numeric_conversion_error: success = JS_TRUE; /* Fall through ... */ conversion_error: if (java_value) { const char *jsval_string; const char *class_name; JSString *jsstr; jsval_string = NULL; jsstr = JS_ValueToString(cx, v_arg); if (jsstr) jsval_string = JS_GetStringBytes(jsstr); if (!jsval_string) jsval_string = ""; class_name = jsj_ConvertJavaSignatureToHRString(cx, signature); JS_ReportErrorNumber(cx, jsj_GetErrorMessage, NULL, JSJMSG_CANT_CONVERT_JS, jsval_string, class_name); return JS_FALSE; } return success; }
JSBool jsj_SetJavaFieldValue(JSContext *cx, JNIEnv *jEnv, JavaFieldSpec *field_spec, jclass java_obj, jsval js_val) { JSBool is_static_field, is_local_ref; int dummy_cost; jvalue java_value; JavaSignature *signature; JavaSignatureChar field_type; jfieldID fieldID = field_spec->fieldID; is_static_field = field_spec->modifiers & ACC_STATIC; #define SET_JAVA_FIELD(Type,member) \ JS_BEGIN_MACRO \ if (is_static_field) { \ (*jEnv)->SetStatic##Type##Field(jEnv, java_obj, fieldID, \ java_value.member); \ } else { \ (*jEnv)->Set##Type##Field(jEnv, java_obj, fieldID,java_value.member);\ } \ if ((*jEnv)->ExceptionOccurred(jEnv)) { \ jsj_UnexpectedJavaError(cx, jEnv, "Error assigning to Java field"); \ return JS_FALSE; \ } \ JS_END_MACRO signature = field_spec->signature; if (!jsj_ConvertJSValueToJavaValue(cx, jEnv, js_val, signature, &dummy_cost, &java_value, &is_local_ref)) return JS_FALSE; field_type = signature->type; switch(field_type) { case JAVA_SIGNATURE_BYTE: SET_JAVA_FIELD(Byte,b); break; case JAVA_SIGNATURE_CHAR: SET_JAVA_FIELD(Char,c); break; case JAVA_SIGNATURE_SHORT: SET_JAVA_FIELD(Short,s); break; case JAVA_SIGNATURE_INT: SET_JAVA_FIELD(Int,i); break; case JAVA_SIGNATURE_BOOLEAN: SET_JAVA_FIELD(Boolean,z); break; case JAVA_SIGNATURE_LONG: SET_JAVA_FIELD(Long,j); break; case JAVA_SIGNATURE_FLOAT: SET_JAVA_FIELD(Float,f); break; case JAVA_SIGNATURE_DOUBLE: SET_JAVA_FIELD(Double,d); break; /* Non-primitive (reference) type */ default: JS_ASSERT(IS_REFERENCE_TYPE(field_type)); SET_JAVA_FIELD(Object,l); if (is_local_ref) (*jEnv)->DeleteLocalRef(jEnv, java_value.l); break; case JAVA_SIGNATURE_UNKNOWN: case JAVA_SIGNATURE_VOID: JS_ASSERT(0); /* Unknown java type signature */ return JS_FALSE; } #undef SET_JAVA_FIELD return JS_TRUE; }
/* * 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; }
/* * 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); }