JavaClass_newEnumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op, jsval *statep, jsid *idp) { JavaMemberDescriptor *member_descriptor; JavaClassDescriptor *class_descriptor; JNIEnv *jEnv; JSJavaThreadState *jsj_env; class_descriptor = JS_GetPrivate(cx, obj); /* Check for prototype JavaClass object */ if (!class_descriptor) { *statep = JSVAL_NULL; if (idp) *idp = INT_TO_JSVAL(0); return JS_TRUE; } switch(enum_op) { case JSENUMERATE_INIT: /* Get the Java per-thread environment pointer for this JSContext */ jsj_env = jsj_EnterJava(cx, &jEnv); if (!jEnv) return JS_FALSE; member_descriptor = jsj_GetClassStaticMembers(cx, jEnv, class_descriptor); *statep = PRIVATE_TO_JSVAL(member_descriptor); if (idp) *idp = INT_TO_JSVAL(class_descriptor->num_instance_members); jsj_ExitJava(jsj_env); return JS_TRUE; case JSENUMERATE_NEXT: member_descriptor = JSVAL_TO_PRIVATE(*statep); if (member_descriptor) { /* Don't enumerate explicit-signature methods, i.e. enumerate toValue, but not toValue(int), toValue(double), etc. */ while (member_descriptor->methods && member_descriptor->methods->is_alias) { member_descriptor = member_descriptor->next; if (!member_descriptor) { *statep = JSVAL_NULL; return JS_TRUE; } } *idp = member_descriptor->id; *statep = PRIVATE_TO_JSVAL(member_descriptor->next); return JS_TRUE; } /* Fall through ... */ case JSENUMERATE_DESTROY: *statep = JSVAL_NULL; return JS_TRUE; default: JS_ASSERT(0); return JS_FALSE; } }
static JSBool JavaArray_lookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, JSProperty **propp) { JNIEnv *jEnv; JSErrorReporter old_reporter; JSJavaThreadState *jsj_env; jsj_env = jsj_EnterJava(cx, &jEnv); if (!jEnv) return JS_FALSE; old_reporter = JS_SetErrorReporter(cx, NULL); if (access_java_array_element(cx, jEnv, obj, id, NULL, JS_FALSE)) { *objp = obj; *propp = (JSProperty*)1; js_SetObjectWeakRoot(cx, obj); } else { *objp = NULL; *propp = NULL; } JS_SetErrorReporter(cx, old_reporter); jsj_ExitJava(jsj_env); return JS_TRUE; }
JavaClass_lookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, JSProperty **propp #if defined JS_THREADSAFE && defined DEBUG , const char *file, uintN line #endif ) { JNIEnv *jEnv; JSErrorReporter old_reporter; JSJavaThreadState *jsj_env; /* printf("In JavaClass_lookupProperty()\n"); */ /* Get the Java per-thread environment pointer for this JSContext */ jsj_env = jsj_EnterJava(cx, &jEnv); if (!jEnv) return JS_FALSE; old_reporter = JS_SetErrorReporter(cx, NULL); if (lookup_static_member_by_id(cx, jEnv, obj, NULL, id, NULL)) { *objp = obj; *propp = (JSProperty*)1; } else { *objp = NULL; *propp = NULL; } JS_SetErrorReporter(cx, old_reporter); jsj_ExitJava(jsj_env); return JS_TRUE; }
JavaClass_hasInstance(JSContext *cx, JSObject *obj, jsval candidate_jsval, JSBool *has_instancep) { JavaClassDescriptor *class_descriptor; JavaObjectWrapper *java_wrapper; JSClass *js_class; JSBool has_instance; JSObject *candidate_obj; jclass java_class; jobject java_obj; JNIEnv *jEnv; JSJavaThreadState *jsj_env; has_instance = JS_FALSE; class_descriptor = JS_GetPrivate(cx, obj); if (!class_descriptor) { JS_ReportErrorNumber(cx, jsj_GetErrorMessage, NULL, JSJMSG_BAD_OP_JCLASS); return JS_FALSE; } /* * Make sure that the thing to the left of the instanceof operator is a * Java object. */ if (!JSVAL_IS_OBJECT(candidate_jsval)) goto done; candidate_obj = JSVAL_TO_OBJECT(candidate_jsval); #ifdef JS_THREADSAFE js_class = JS_GetClass(cx, candidate_obj); #else js_class = JS_GetClass(candidate_obj); #endif if ((js_class != &JavaObject_class) && (js_class != &JavaArray_class)) goto done; java_class = class_descriptor->java_class; java_wrapper = JS_GetPrivate(cx, candidate_obj); if (!java_wrapper) { JS_ReportErrorNumber(cx, jsj_GetErrorMessage, NULL, JSJMSG_BAD_OP_PROTO); return JS_FALSE; } java_obj = java_wrapper->java_obj; /* Get JNI pointer */ jsj_env = jsj_EnterJava(cx, &jEnv); has_instance = (*jEnv)->IsInstanceOf(jEnv, java_obj, java_class); jsj_ExitJava(jsj_env); done: *has_instancep = has_instance; return JS_TRUE; }
static JSBool JavaArray_setPropertyById(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { JNIEnv *jEnv; JSJavaThreadState *jsj_env; JSBool result; jsj_env = jsj_EnterJava(cx, &jEnv); if (!jEnv) return JS_FALSE; result = access_java_array_element(cx, jEnv, obj, id, vp, JS_TRUE); jsj_ExitJava(jsj_env); return result; }
JavaClass_setPropertyById(JSContext *cx, JSObject *obj, jsid id, jsval *vp) { jclass java_class; const char *member_name; JavaClassDescriptor *class_descriptor; JavaMemberDescriptor *member_descriptor; jsval idval; JNIEnv *jEnv; JSJavaThreadState *jsj_env; JSBool result; /* printf("In JavaClass_setProperty\n"); */ /* Get the Java per-thread environment pointer for this JSContext */ jsj_env = jsj_EnterJava(cx, &jEnv); if (!jEnv) return JS_FALSE; if (!lookup_static_member_by_id(cx, jEnv, obj, &class_descriptor, id, &member_descriptor)) { jsj_ExitJava(jsj_env); return JS_FALSE; } /* Check for the case where there is a method with the given name, but no field with that name */ if (!member_descriptor->field) goto no_such_field; /* Silently fail if field value is final (immutable), as required by ECMA spec */ if (member_descriptor->field->modifiers & ACC_FINAL) { jsj_ExitJava(jsj_env); return JS_TRUE; } java_class = class_descriptor->java_class; result = jsj_SetJavaFieldValue(cx, jEnv, member_descriptor->field, java_class, *vp); jsj_ExitJava(jsj_env); return result; no_such_field: JS_IdToValue(cx, id, &idval); member_name = JS_GetStringBytes(JSVAL_TO_STRING(idval)); JS_ReportErrorNumber(cx, jsj_GetErrorMessage, NULL, JSJMSG_MISSING_STATIC, member_name, class_descriptor->name); jsj_ExitJava(jsj_env); return JS_FALSE; }
/* 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; }
JavaClass_finalize(JSContext *cx, JSObject *obj) { JNIEnv *jEnv; JSJavaThreadState *jsj_env; JavaClassDescriptor *class_descriptor = JS_GetPrivate(cx, obj); if (!class_descriptor) return; /* Get the Java per-thread environment pointer for this JSContext */ jsj_env = jsj_EnterJava(cx, &jEnv); if (!jEnv) return; /* printf("Finalizing %s\n", class_descriptor->name); */ jsj_ReleaseJavaClassDescriptor(cx, jEnv, class_descriptor); jsj_ExitJava(jsj_env); }
JavaClass_construct(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSObject *obj_arg, *JavaClass_obj; JavaObjectWrapper *java_wrapper; JavaClassDescriptor *class_descriptor; JNIEnv *jEnv; JSJavaThreadState *jsj_env; if (argc != 1 || !JSVAL_IS_OBJECT(argv[0]) || !(obj_arg = JSVAL_TO_OBJECT(argv[0])) || !JS_InstanceOf(cx, obj_arg, &JavaObject_class, 0) || ((java_wrapper = JS_GetPrivate(cx, obj_arg)) == NULL)) { JS_ReportErrorNumber(cx, jsj_GetErrorMessage, NULL, JSJMSG_NEED_JCLASS_ARG); return JS_FALSE; } jsj_env = jsj_EnterJava(cx, &jEnv); if (!jEnv) return JS_FALSE; class_descriptor = java_wrapper->class_descriptor; if (!(*jEnv)->IsSameObject(jEnv, class_descriptor->java_class, jlClass)) { JS_ReportErrorNumber(cx, jsj_GetErrorMessage, NULL, JSJMSG_NEED_JCLASS_ARG); jsj_ExitJava(jsj_env); return JS_FALSE; } class_descriptor = jsj_GetJavaClassDescriptor(cx, jEnv, java_wrapper->java_obj); JavaClass_obj = jsj_new_JavaClass(cx, jEnv, NULL, class_descriptor); if (!JavaClass_obj) { jsj_ExitJava(jsj_env); return JS_FALSE; } *rval = OBJECT_TO_JSVAL(JavaClass_obj); jsj_ExitJava(jsj_env); return JS_TRUE; }
getClass(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSObject *obj_arg, *JavaClass_obj; JavaObjectWrapper *java_wrapper; JavaClassDescriptor *class_descriptor; JNIEnv *jEnv; JSJavaThreadState *jsj_env; if (argc != 1 || !JSVAL_IS_OBJECT(argv[0]) || !(obj_arg = JSVAL_TO_OBJECT(argv[0])) || (!JS_InstanceOf(cx, obj_arg, &JavaObject_class, 0) && !JS_InstanceOf(cx, obj_arg, &JavaArray_class, 0))) { JS_ReportErrorNumber(cx, jsj_GetErrorMessage, NULL, JSJMSG_NEED_JOBJECT_ARG); return JS_FALSE; } java_wrapper = JS_GetPrivate(cx, obj_arg); if (!java_wrapper) { JS_ReportErrorNumber(cx, jsj_GetErrorMessage, NULL, JSJMSG_PROTO_GETCLASS); return JS_FALSE; } jsj_env = jsj_EnterJava(cx, &jEnv); if (!jEnv) return JS_FALSE; class_descriptor = java_wrapper->class_descriptor; JavaClass_obj = jsj_new_JavaClass(cx, jEnv, NULL, class_descriptor); if (!JavaClass_obj) { jsj_ExitJava(jsj_env); return JS_FALSE; } *rval = OBJECT_TO_JSVAL(JavaClass_obj); jsj_ExitJava(jsj_env); return JS_TRUE; }
static JSBool JavaArray_newEnumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op, jsval *statep, jsid *idp) { JavaObjectWrapper *java_wrapper; JSJavaThreadState *jsj_env; JNIEnv *jEnv; jsize array_length, index; JSBool ok = JS_TRUE; java_wrapper = JS_GetPrivate(cx, obj); /* Check for prototype object */ if (!java_wrapper) { *statep = JSVAL_NULL; if (idp) *idp = INT_TO_JSVAL(0); return JS_TRUE; } /* Get the Java per-thread environment pointer for this JSContext */ jsj_env = jsj_EnterJava(cx, &jEnv); if (!jEnv) return JS_FALSE; array_length = jsj_GetJavaArrayLength(cx, jEnv, java_wrapper->java_obj); if (array_length < 0) { jsj_ExitJava(jsj_env); return JS_FALSE; } switch(enum_op) { case JSENUMERATE_INIT: *statep = INT_TO_JSVAL(0); if (idp) *idp = INT_TO_JSVAL(array_length); break; case JSENUMERATE_NEXT: index = JSVAL_TO_INT(*statep); if (index < array_length) { JS_ValueToId(cx, INT_TO_JSVAL(index), idp); index++; *statep = INT_TO_JSVAL(index); break; } /* Fall through ... */ case JSENUMERATE_DESTROY: *statep = JSVAL_NULL; break; default: JS_ASSERT(0); ok = JS_FALSE; break; } jsj_ExitJava(jsj_env); return ok; }
capture_js_error_reports_for_java(JSContext *cx, const char *message, JSErrorReport *report) { CapturedJSError *new_error; JSJavaThreadState *jsj_env; jthrowable java_exception, tmp_exception; JNIEnv *jEnv; /* Warnings are not propagated as Java exceptions - they are simply ignored. Ditto for exceptions that are duplicated in the form of error reports. */ if (report && (report->flags & (JSREPORT_WARNING | JSREPORT_EXCEPTION))) return; /* Create an empty struct to hold the saved JS error state */ new_error = malloc(sizeof(CapturedJSError)); if (!new_error) goto out_of_memory; memset(new_error, 0, sizeof(CapturedJSError)); /* Copy all the error info out of the original report into a private copy */ if (message) { new_error->message = strdup(message); if (!new_error->message) goto out_of_memory; } if (report) { new_error->report.lineno = report->lineno; if (report->filename) { new_error->report.filename = strdup(report->filename); if (!new_error->report.filename) goto out_of_memory; } if (report->linebuf) { new_error->report.linebuf = strdup(report->linebuf); if (!new_error->report.linebuf) goto out_of_memory; new_error->report.tokenptr = new_error->report.linebuf + (report->tokenptr - report->linebuf); } } /* Get the head of the list of pending JS errors */ jsj_env = jsj_EnterJava(cx, &jEnv); if (!jsj_env) goto abort; /* If there's a Java exception associated with this error, save it too. */ java_exception = (*jEnv)->ExceptionOccurred(jEnv); if (java_exception) { (*jEnv)->ExceptionClear(jEnv); tmp_exception = java_exception; /* Make a copy */ java_exception = (*jEnv)->NewGlobalRef(jEnv, java_exception); new_error->java_exception = java_exception; (*jEnv)->DeleteLocalRef(jEnv, tmp_exception); } /* Push this error onto the list of pending JS errors */ new_error->next = jsj_env->pending_js_errors; jsj_env->pending_js_errors = new_error; jsj_ExitJava(jsj_env); return; abort: out_of_memory: /* No recovery action possible */ JS_ASSERT(0); destroy_saved_js_error(jEnv, new_error); return; }
JavaPackage_resolve(JSContext *cx, JSObject *obj, jsval id) { JavaPackage_Private *package; JSBool ok = JS_TRUE; jclass jclazz; char *subPath, *newPath; const char *path; JNIEnv *jEnv; JSJavaThreadState *jsj_env; /* Painful hack for pre_define_java_packages() */ if (quiet_resolve_failure) return JS_FALSE; package = (JavaPackage_Private *)JS_GetPrivate(cx, obj); if (!package) return JS_TRUE; if (!JSVAL_IS_STRING(id)) return JS_TRUE; subPath = JS_GetStringBytes(JSVAL_TO_STRING(id)); /* * There will be an attempt to invoke the toString() method when producing * the string representation of a JavaPackage. When this occurs, avoid * creating a bogus toString package. (This means that no one can ever * create a package with the simple name "toString", but we'll live with * that limitation.) */ if (!strcmp(subPath, "toString")) return JS_FALSE; path = package->path; newPath = JS_smprintf("%s%s%s", path, (path[0] ? "/" : ""), subPath); if (!newPath) { JS_ReportOutOfMemory(cx); return JS_FALSE; } jsj_env = jsj_EnterJava(cx, &jEnv); if (!jEnv) { ok = JS_FALSE; goto out; } /* Unfortunately, Java provides no way to find out whether a particular name is a package or not. The only way to tell is to try to load the name as a class file and, if that fails, assume it's a package. This makes things work as expected for the most part, but it has three noticeable problems that keep coming up: - You can refer to a package like java.lang.i.buried.paul without generating a complaint. Of course, you'll never be able to refer to any classes through it. - An annoying consequence of the above is that misspelling a class name results in a cryptic error about packages. - In a browser context, i.e. where applets are involved, figuring out whether something is a class may require looking for it over the net using the current classloader. This means that the first time you refer to java.lang.System in a js context, there will be an attempt to search for [[DOCBASE]]/java.class on the server. A solution is to explicitly tell jsjava the names of all the (local) packages on the CLASSPATH. (Not implemented yet.) */ jclazz = (*jEnv)->FindClass(jEnv, newPath); if (jclazz) { JSObject *newClass; newClass = jsj_define_JavaClass(cx, jEnv, obj, subPath, jclazz); (*jEnv)->DeleteLocalRef(jEnv, jclazz); if (!newClass) { ok = JS_FALSE; goto out; } } else { /* We assume that any failed attempt to load a class is because it doesn't exist. If we wanted to do a better job, we would check the exception type and make sure that it's NoClassDefFoundError */ (*jEnv)->ExceptionClear(jEnv); /* * If there's no class of the given name, then we must be referring to * a package. However, don't allow bogus sub-packages of pre-defined * system packages to be created. */ if (JS_InstanceOf(cx, obj, &JavaPackage_class, NULL)) { JavaPackage_Private *current_package; current_package = JS_GetPrivate(cx, obj); if (current_package->flags & PKG_SYSTEM) { char *msg, *cp; msg = JS_strdup(cx, newPath); /* Check for OOM */ if (msg) { /* Convert package of form "java/lang" to "java.lang" */ for (cp = msg; *cp != '\0'; cp++) if (*cp == '/') *cp = '.'; JS_ReportErrorNumber(cx, jsj_GetErrorMessage, NULL, JSJMSG_MISSING_PACKAGE, msg); free((char*)msg); } ok = JS_FALSE; goto out; } } if (!define_JavaPackage(cx, obj, subPath, newPath, 0, JSPROP_READONLY)) { ok = JS_FALSE; goto out; } #ifdef DEBUG /* printf("JavaPackage \'%s\' created\n", newPath); */ #endif } out: free(newPath); jsj_ExitJava(jsj_env); return ok; }
JavaClass_getPropertyById(JSContext *cx, JSObject *obj, jsid id, jsval *vp) { jsval idval; jclass java_class; const char *member_name; JavaClassDescriptor *class_descriptor; JavaMemberDescriptor *member_descriptor; JNIEnv *jEnv; JSJavaThreadState *jsj_env; JSBool result; /* printf("In JavaClass_getProperty\n"); */ /* Get the Java per-thread environment pointer for this JSContext */ jsj_env = jsj_EnterJava(cx, &jEnv); if (!jEnv) return JS_FALSE; if (!lookup_static_member_by_id(cx, jEnv, obj, &class_descriptor, id, &member_descriptor)) { jsj_ExitJava(jsj_env); return JS_FALSE; } if (!member_descriptor) { *vp = JSVAL_VOID; jsj_ExitJava(jsj_env); return JS_TRUE; } java_class = class_descriptor->java_class; if (member_descriptor->field) { if (!member_descriptor->methods) { result = jsj_GetJavaFieldValue(cx, jEnv, member_descriptor->field, java_class, vp); jsj_ExitJava(jsj_env); return result; } else { JS_ASSERT(0); } } else { JSFunction *function; /* TODO - eliminate JSFUN_BOUND_METHOD */ if (member_descriptor->methods->is_alias) { /* If this is an explicit resolution of an overloaded method, use the fully-qualified method name as the name of the resulting JS function, i.e. "myMethod(int,long)" */ JS_IdToValue(cx, id, &idval); member_name = JS_GetStringBytes(JSVAL_TO_STRING(idval)); } else { /* Either not explicit resolution of overloaded method or explicit resolution was unnecessary because method was not overloaded. */ member_name = member_descriptor->name; } function = JS_NewFunction(cx, jsj_JavaStaticMethodWrapper, 0, JSFUN_BOUND_METHOD, obj, member_name); if (!function) { jsj_ExitJava(jsj_env); return JS_FALSE; } *vp = OBJECT_TO_JSVAL(JS_GetFunctionObject(function)); } jsj_ExitJava(jsj_env); return JS_TRUE; }