+ Changed the way JS wrapper functions for Java instance methods are constructed. Previously, these were computed the first time that an instance method was accessed for a particular JavaObject and cached in the native, private portion of that JavaObject. However, the required call to JS_AddRoot() causes an root to appear as a link in a cyclical graph, leading to uncollectible objects, i.e. the JavaObject has a root pointer to the function object and the function has a parent that points back to the JavaObject. Now, we compute the functions at the time a class is reflected and use JS_CloneFunctionObject() each time a JS wrapper function is needed, which is slower, but avoids this GC problem. git-svn-id: svn://10.0.0.236/trunk@5005 18797224-902f-48f8-a5cc-f745e15eee43
673 lines
20 KiB
C
673 lines
20 KiB
C
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
|
*
|
|
* The contents of this file are subject to the Netscape Public License
|
|
* Version 1.0 (the "License"); you may not use this file except in
|
|
* compliance with the License. You may obtain a copy of the License at
|
|
* http://www.mozilla.org/NPL/
|
|
*
|
|
* Software distributed under the License is distributed on an "AS IS"
|
|
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
|
|
* the License for the specific language governing rights and limitations
|
|
* under the License.
|
|
*
|
|
* The Original Code is Mozilla Communicator client code.
|
|
*
|
|
* The Initial Developer of the Original Code is Netscape Communications
|
|
* Corporation. Portions created by Netscape are Copyright (C) 1998
|
|
* Netscape Communications Corporation. All Rights Reserved.
|
|
*/
|
|
|
|
/*
|
|
* This file is part of the Java-vendor-neutral implementation of LiveConnect
|
|
*
|
|
* It contains the native code implementation of JS's JavaObject class.
|
|
*
|
|
* An instance of JavaObject is the JavaScript reflection of a Java object.
|
|
*
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include "prassert.h"
|
|
|
|
#include "jsj_private.h" /* LiveConnect internals */
|
|
#include "jsj_hash.h" /* Hash table with Java object as key */
|
|
|
|
|
|
/*
|
|
* This is a hash table that maps from Java objects to JS objects.
|
|
* It is used to ensure that the same JS object is obtained when a Java
|
|
* object is reflected more than once, so that JS object equality tests
|
|
* work in the expected manner, i.e. the "==" and "===" operators.
|
|
*
|
|
* The table entry keys are Java objects (of type jobject) and the entry values
|
|
* are JSObject pointers. Because the jobject type is an opaque handle and
|
|
* not necessarily a pointer, the hashing and key comparison functions must
|
|
* invoke the appropriate JVM functions.
|
|
*
|
|
* When the corresponding JS object instance is finalized, the entry is
|
|
* removed from the table, and a Java GC root for the Java object is removed.
|
|
*/
|
|
static JSJHashTable *java_obj_reflections = NULL;
|
|
|
|
#ifdef JS_THREADSAFE
|
|
static PRMonitor *java_obj_reflections_monitor = NULL;
|
|
#endif
|
|
|
|
static JSBool
|
|
init_java_obj_reflections_table()
|
|
{
|
|
java_obj_reflections =
|
|
JSJ_NewHashTable(512, jsj_HashJavaObject, jsj_JavaObjectComparator,
|
|
NULL, NULL, NULL);
|
|
if (!java_obj_reflections)
|
|
return JS_FALSE;
|
|
|
|
#ifdef JS_THREADSAFE
|
|
java_obj_reflections_monitor = PR_NewNamedMonitor("java_obj_reflections");
|
|
if (!java_obj_reflections_monitor) {
|
|
PR_HashTableDestroy(java_obj_reflections);
|
|
return JS_FALSE;
|
|
}
|
|
#endif
|
|
|
|
return JS_TRUE;
|
|
}
|
|
|
|
JSObject *
|
|
jsj_WrapJavaObject(JSContext *cx,
|
|
JNIEnv *jEnv,
|
|
jobject java_obj,
|
|
jclass java_class)
|
|
{
|
|
JSJHashNumber hash_code;
|
|
JSClass *js_class;
|
|
JSObject *js_wrapper_obj;
|
|
JavaObjectWrapper *java_wrapper;
|
|
JavaClassDescriptor *class_descriptor;
|
|
JSJHashEntry *he, **hep;
|
|
|
|
js_wrapper_obj = NULL;
|
|
|
|
hash_code = jsj_HashJavaObject((void*)java_obj, (void*)jEnv);
|
|
|
|
#ifdef JS_THREADSAFE
|
|
PR_EnterMonitor(java_obj_reflections_monitor);
|
|
#endif
|
|
|
|
hep = JSJ_HashTableRawLookup(java_obj_reflections,
|
|
hash_code, java_obj, (void*)jEnv);
|
|
he = *hep;
|
|
if (he) {
|
|
js_wrapper_obj = (JSObject *)he->value;
|
|
if (js_wrapper_obj)
|
|
goto done;
|
|
}
|
|
|
|
/* No existing reflection found. Construct a new one */
|
|
class_descriptor = jsj_GetJavaClassDescriptor(cx, jEnv, java_class);
|
|
if (!class_descriptor)
|
|
goto done;
|
|
if (class_descriptor->type == JAVA_SIGNATURE_ARRAY) {
|
|
js_class = &JavaArray_class;
|
|
} else {
|
|
PR_ASSERT(class_descriptor->type == JAVA_SIGNATURE_CLASS);
|
|
js_class = &JavaObject_class;
|
|
}
|
|
|
|
/* Create new JS object to reflect Java object */
|
|
js_wrapper_obj = JS_NewObject(cx, js_class, NULL, NULL);
|
|
if (!js_wrapper_obj)
|
|
goto done;
|
|
|
|
/* Create private, native portion of JavaObject */
|
|
java_wrapper =
|
|
(JavaObjectWrapper *)JS_malloc(cx, sizeof(JavaObjectWrapper));
|
|
if (!java_wrapper) {
|
|
jsj_ReleaseJavaClassDescriptor(cx, jEnv, class_descriptor);
|
|
goto done;
|
|
}
|
|
JS_SetPrivate(cx, js_wrapper_obj, java_wrapper);
|
|
java_wrapper->class_descriptor = class_descriptor;
|
|
|
|
java_obj = (*jEnv)->NewGlobalRef(jEnv, java_obj);
|
|
java_wrapper->java_obj = java_obj;
|
|
if (!java_obj)
|
|
goto out_of_memory;
|
|
|
|
/* Add the JavaObject to the hash table */
|
|
he = JSJ_HashTableRawAdd(java_obj_reflections, hep, hash_code,
|
|
java_obj, js_wrapper_obj, (void*)jEnv);
|
|
if (!he) {
|
|
(*jEnv)->DeleteGlobalRef(jEnv, java_obj);
|
|
goto out_of_memory;
|
|
}
|
|
|
|
done:
|
|
#ifdef JS_THREADSAFE
|
|
PR_ExitMonitor(java_obj_reflections_monitor);
|
|
#endif
|
|
|
|
return js_wrapper_obj;
|
|
|
|
out_of_memory:
|
|
/* No need to free js_wrapper_obj, as it will be finalized by GC. */
|
|
JS_ReportOutOfMemory(cx);
|
|
js_wrapper_obj = NULL;
|
|
goto done;
|
|
}
|
|
|
|
static void
|
|
remove_java_obj_reflection_from_hashtable(jobject java_obj, JNIEnv *jEnv)
|
|
{
|
|
JSJHashNumber hash_code;
|
|
JSJHashEntry *he, **hep;
|
|
|
|
hash_code = jsj_HashJavaObject((void*)java_obj, (void*)jEnv);
|
|
|
|
#ifdef JS_THREADSAFE
|
|
PR_EnterMonitor(java_obj_reflections_monitor);
|
|
#endif
|
|
|
|
hep = JSJ_HashTableRawLookup(java_obj_reflections, hash_code,
|
|
java_obj, (void*)jEnv);
|
|
he = *hep;
|
|
|
|
PR_ASSERT(he);
|
|
if (he)
|
|
JSJ_HashTableRawRemove(java_obj_reflections, hep, he, (void*)jEnv);
|
|
|
|
#ifdef JS_THREADSAFE
|
|
PR_ExitMonitor(java_obj_reflections_monitor);
|
|
#endif
|
|
}
|
|
|
|
void
|
|
JavaObject_finalize(JSContext *cx, JSObject *obj)
|
|
{
|
|
JavaObjectWrapper *java_wrapper;
|
|
jobject java_obj;
|
|
JNIEnv *jEnv;
|
|
|
|
jsj_MapJSContextToJSJThread(cx, &jEnv);
|
|
if (!jEnv)
|
|
return;
|
|
|
|
java_wrapper = JS_GetPrivate(cx, obj);
|
|
if (!java_wrapper)
|
|
return;
|
|
java_obj = java_wrapper->java_obj;
|
|
|
|
if (java_obj) {
|
|
remove_java_obj_reflection_from_hashtable(java_obj, jEnv);
|
|
(*jEnv)->DeleteGlobalRef(jEnv, java_obj);
|
|
}
|
|
jsj_ReleaseJavaClassDescriptor(cx, jEnv, java_wrapper->class_descriptor);
|
|
JS_free(cx, java_wrapper);
|
|
}
|
|
|
|
/* Trivial helper for jsj_DiscardJavaObjReflections(), below */
|
|
static PRIntn
|
|
enumerate_remove_java_obj(JSJHashEntry *he, PRIntn i, void *arg)
|
|
{
|
|
JNIEnv *jEnv = (JNIEnv*)arg;
|
|
jobject java_obj;
|
|
JavaObjectWrapper *java_wrapper;
|
|
JSObject *java_wrapper_obj;
|
|
|
|
java_wrapper_obj = (JSObject *)he->value;
|
|
java_wrapper = JS_GetPrivate(NULL, java_wrapper_obj);
|
|
java_obj = java_wrapper->java_obj;
|
|
(*jEnv)->DeleteGlobalRef(jEnv, java_obj);
|
|
java_wrapper->java_obj = NULL;
|
|
return HT_ENUMERATE_REMOVE;
|
|
}
|
|
|
|
/* 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_DiscardJavaObjReflections(JNIEnv *jEnv)
|
|
{
|
|
JSJ_HashTableEnumerateEntries(java_obj_reflections,
|
|
enumerate_remove_java_obj,
|
|
(void*)jEnv);
|
|
}
|
|
|
|
PR_CALLBACK JSBool
|
|
JavaObject_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp)
|
|
{
|
|
JavaObjectWrapper *java_wrapper;
|
|
JavaClassDescriptor *class_descriptor;
|
|
jobject java_obj;
|
|
JNIEnv *jEnv;
|
|
|
|
/* Get the Java per-thread environment pointer for this JSContext */
|
|
jsj_MapJSContextToJSJThread(cx, &jEnv);
|
|
if (!jEnv)
|
|
return JS_FALSE;
|
|
|
|
java_wrapper = JS_GetPrivate(cx, obj);
|
|
if (!java_wrapper) {
|
|
if (type == JSTYPE_OBJECT) {
|
|
*vp = OBJECT_TO_JSVAL(obj);
|
|
return JS_TRUE;
|
|
}
|
|
|
|
JS_ReportError(cx, "illegal operation on JavaObject prototype object");
|
|
return JS_FALSE;
|
|
}
|
|
|
|
java_obj = java_wrapper->java_obj;
|
|
class_descriptor = java_wrapper->class_descriptor;
|
|
|
|
switch (type) {
|
|
case JSTYPE_OBJECT:
|
|
*vp = OBJECT_TO_JSVAL(obj);
|
|
return JS_TRUE;
|
|
|
|
case JSTYPE_FUNCTION:
|
|
JS_ReportError(cx, "can't convert Java object to function");
|
|
return JS_FALSE;
|
|
|
|
case JSTYPE_VOID:
|
|
case JSTYPE_STRING:
|
|
/* Either extract a C-string from the java.lang.String object
|
|
or call the Java toString() method */
|
|
return jsj_ConvertJavaObjectToJSString(cx, jEnv, class_descriptor, java_obj, vp);
|
|
|
|
case JSTYPE_NUMBER:
|
|
/* Call Java doubleValue() method, if applicable */
|
|
return jsj_ConvertJavaObjectToJSNumber(cx, jEnv, java_obj, vp);
|
|
|
|
case JSTYPE_BOOLEAN:
|
|
/* Call booleanValue() method, if applicable */
|
|
return jsj_ConvertJavaObjectToJSBoolean(cx, jEnv, java_obj, vp);
|
|
|
|
default:
|
|
PR_ASSERT(0);
|
|
return JS_FALSE;
|
|
}
|
|
}
|
|
|
|
static JSBool
|
|
lookup_member_by_id(JSContext *cx, JNIEnv *jEnv, JSObject *obj,
|
|
JavaObjectWrapper **java_wrapperp,
|
|
jsid id,
|
|
JavaMemberDescriptor **member_descriptorp)
|
|
{
|
|
jsval idval;
|
|
JavaObjectWrapper *java_wrapper;
|
|
JavaMemberDescriptor *member_descriptor;
|
|
const char *member_name, *property_name;
|
|
JavaClassDescriptor *class_descriptor;
|
|
|
|
java_wrapper = JS_GetPrivate(cx, obj);
|
|
if (!java_wrapper) {
|
|
if (JS_IdToValue(cx, id, &idval) && JSVAL_IS_STRING(idval) &&
|
|
(property_name = JS_GetStringBytes(JSVAL_TO_STRING(idval))) != NULL) {
|
|
if (!strcmp(property_name, "constructor")) {
|
|
*java_wrapperp = NULL;
|
|
*member_descriptorp = NULL;
|
|
return JS_TRUE;
|
|
}
|
|
}
|
|
JS_ReportError(cx, "illegal operation on JavaObject prototype object");
|
|
return JS_FALSE;
|
|
}
|
|
|
|
class_descriptor = java_wrapper->class_descriptor;
|
|
PR_ASSERT(class_descriptor->type == JAVA_SIGNATURE_CLASS ||
|
|
class_descriptor->type == JAVA_SIGNATURE_ARRAY);
|
|
|
|
member_descriptor = jsj_LookupJavaMemberDescriptorById(cx, jEnv, class_descriptor, id);
|
|
if (!member_descriptor) {
|
|
JS_IdToValue(cx, id, &idval);
|
|
if (!JSVAL_IS_STRING(idval)) {
|
|
JS_ReportError(cx, "invalid JavaObject property expression. "
|
|
"(methods and field properties of a JavaObject object can only be strings)");
|
|
return JS_FALSE;
|
|
}
|
|
|
|
member_name = JS_GetStringBytes(JSVAL_TO_STRING(idval));
|
|
|
|
JS_ReportError(cx, "Java class %s has no public instance field or "
|
|
"method named \"%s\"",
|
|
class_descriptor->name, member_name);
|
|
return JS_FALSE;
|
|
}
|
|
|
|
/* Success. Handle the multiple return values */
|
|
if (java_wrapperp)
|
|
*java_wrapperp = java_wrapper;
|
|
if (member_descriptorp)
|
|
*member_descriptorp = member_descriptor;
|
|
return JS_TRUE;
|
|
}
|
|
|
|
PR_CALLBACK JSBool
|
|
JavaObject_getPropertyById(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
|
|
{
|
|
jobject java_obj;
|
|
JavaMemberDescriptor *member_descriptor;
|
|
JavaObjectWrapper *java_wrapper;
|
|
JNIEnv *jEnv;
|
|
JSObject *funobj;
|
|
|
|
/* printf("In JavaObject_getProperty\n"); */
|
|
|
|
/* Get the Java per-thread environment pointer for this JSContext */
|
|
jsj_MapJSContextToJSJThread(cx, &jEnv);
|
|
if (!jEnv)
|
|
return JS_FALSE;
|
|
|
|
if (!lookup_member_by_id(cx, jEnv, obj, &java_wrapper, id, &member_descriptor))
|
|
return JS_FALSE;
|
|
|
|
/* Handle access to "constructor" property of prototype object with
|
|
silent failure. */
|
|
if (!member_descriptor) {
|
|
*vp = JSVAL_VOID;
|
|
return JS_TRUE;
|
|
}
|
|
|
|
java_obj = java_wrapper->java_obj;
|
|
if (member_descriptor->field) {
|
|
if (!member_descriptor->methods) {
|
|
return jsj_GetJavaFieldValue(cx, jEnv, member_descriptor->field, java_obj, vp);
|
|
} else {
|
|
PR_ASSERT(0);
|
|
}
|
|
} else {
|
|
/* Create a function object with this JavaObject as its parent, so that
|
|
JSFUN_BOUND_METHOD binds it as the default 'this' for the function. */
|
|
funobj = JS_CloneFunctionObject(cx, member_descriptor->invoke_func_obj, obj);
|
|
if (funobj) {
|
|
*vp = OBJECT_TO_JSVAL(funobj);
|
|
return JS_TRUE;
|
|
}
|
|
return JS_FALSE;
|
|
}
|
|
}
|
|
|
|
PR_STATIC_CALLBACK(JSBool)
|
|
JavaObject_setPropertyById(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
|
|
{
|
|
jobject java_obj;
|
|
const char *member_name;
|
|
JavaObjectWrapper *java_wrapper;
|
|
JavaClassDescriptor *class_descriptor;
|
|
JavaMemberDescriptor *member_descriptor;
|
|
jsval idval;
|
|
JNIEnv *jEnv;
|
|
|
|
/* printf("In JavaObject_setProperty\n"); */
|
|
|
|
/* Get the Java per-thread environment pointer for this JSContext */
|
|
jsj_MapJSContextToJSJThread(cx, &jEnv);
|
|
if (!jEnv)
|
|
return JS_FALSE;
|
|
|
|
if (!lookup_member_by_id(cx, jEnv, obj, &java_wrapper, id, &member_descriptor))
|
|
return JS_FALSE;
|
|
|
|
/* Check for the case where there is a method with the give 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)
|
|
return JS_TRUE;
|
|
|
|
java_obj = java_wrapper->java_obj;
|
|
return jsj_SetJavaFieldValue(cx, jEnv, member_descriptor->field, java_obj, *vp);
|
|
|
|
no_such_field:
|
|
JS_IdToValue(cx, id, &idval);
|
|
member_name = JS_GetStringBytes(JSVAL_TO_STRING(idval));
|
|
class_descriptor = java_wrapper->class_descriptor;
|
|
JS_ReportError(cx, "No instance field named \"%s\" in Java class %s",
|
|
member_name, class_descriptor->name);
|
|
return JS_FALSE;
|
|
}
|
|
|
|
PR_CALLBACK JSBool
|
|
JavaObject_enumerate(JSContext *cx, JSObject *obj)
|
|
{
|
|
JavaObjectWrapper *java_wrapper;
|
|
JavaClassDescriptor *class_descriptor;
|
|
JavaMemberDescriptor *member_descriptor;
|
|
JNIEnv *jEnv;
|
|
|
|
/* printf("In JavaObject_enumerate\n"); */
|
|
|
|
java_wrapper = JS_GetPrivate(cx, obj);
|
|
|
|
/* Check if this is the prototype object */
|
|
if (!java_wrapper)
|
|
return JS_TRUE;
|
|
|
|
/* Get the Java per-thread environment pointer for this JSContext */
|
|
jsj_MapJSContextToJSJThread(cx, &jEnv);
|
|
if (!jEnv)
|
|
return JS_FALSE;
|
|
|
|
class_descriptor = java_wrapper->class_descriptor;
|
|
member_descriptor = jsj_GetClassInstanceMembers(cx, jEnv, class_descriptor);
|
|
while (member_descriptor) {
|
|
JS_DefineProperty(cx, obj, member_descriptor->name, JSVAL_VOID, 0, 0,
|
|
JSPROP_PERMANENT|JSPROP_ENUMERATE);
|
|
member_descriptor = member_descriptor->next;
|
|
}
|
|
return JS_TRUE;
|
|
}
|
|
|
|
|
|
static JSBool
|
|
JavaObject_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;
|
|
|
|
/* printf("In JavaObject_lookupProperty()\n"); */
|
|
|
|
/* Get the Java per-thread environment pointer for this JSContext */
|
|
jsj_MapJSContextToJSJThread(cx, &jEnv);
|
|
if (!jEnv)
|
|
return JS_FALSE;
|
|
|
|
if (!lookup_member_by_id(cx, jEnv, obj, NULL, id, NULL))
|
|
return JS_FALSE;
|
|
*objp = obj;
|
|
*propp = (JSProperty*)1;
|
|
return JS_TRUE;
|
|
}
|
|
|
|
static JSBool
|
|
JavaObject_defineProperty(JSContext *cx, JSObject *obj, jsid id, jsval value,
|
|
JSPropertyOp getter, JSPropertyOp setter,
|
|
uintN attrs, JSProperty **propp)
|
|
{
|
|
JS_ReportError(cx, "Cannot define a new property in a JavaObject");
|
|
return JS_FALSE;
|
|
}
|
|
|
|
static JSBool
|
|
JavaObject_getAttributes(JSContext *cx, JSObject *obj, jsid id,
|
|
JSProperty *prop, uintN *attrsp)
|
|
{
|
|
/* We don't maintain JS property attributes for Java class members */
|
|
*attrsp = JSPROP_PERMANENT|JSPROP_ENUMERATE;
|
|
return JS_FALSE;
|
|
}
|
|
|
|
static JSBool
|
|
JavaObject_setAttributes(JSContext *cx, JSObject *obj, jsid id,
|
|
JSProperty *prop, uintN *attrsp)
|
|
{
|
|
/* We don't maintain JS property attributes for Java class members */
|
|
if (*attrsp != JSPROP_PERMANENT|JSPROP_ENUMERATE) {
|
|
PR_ASSERT(0);
|
|
return JS_FALSE;
|
|
}
|
|
|
|
/* Silently ignore all setAttribute attempts */
|
|
return JS_TRUE;
|
|
}
|
|
|
|
static JSBool
|
|
JavaObject_deleteProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
|
|
{
|
|
JSVersion version = JS_GetVersion(cx);
|
|
|
|
*vp = JSVAL_FALSE;
|
|
|
|
if (!JSVERSION_IS_ECMA(version)) {
|
|
JS_ReportError(cx, "Properties of JavaObject objects may not be deleted");
|
|
return JS_FALSE;
|
|
} else {
|
|
/* Attempts to delete permanent properties are silently ignored
|
|
by ECMAScript. */
|
|
return JS_TRUE;
|
|
}
|
|
}
|
|
|
|
static JSBool
|
|
JavaObject_defaultValue(JSContext *cx, JSObject *obj, JSType type, jsval *vp)
|
|
{
|
|
/* printf("In JavaObject_defaultValue()\n"); */
|
|
return JavaObject_convert(cx, obj, JSTYPE_STRING, vp);
|
|
}
|
|
|
|
static JSBool
|
|
JavaObject_newEnumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
|
|
jsval *statep, jsid *idp)
|
|
{
|
|
JavaObjectWrapper *java_wrapper;
|
|
JavaMemberDescriptor *member_descriptor;
|
|
JavaClassDescriptor *class_descriptor;
|
|
JNIEnv *jEnv;
|
|
|
|
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;
|
|
}
|
|
|
|
class_descriptor = java_wrapper->class_descriptor;
|
|
|
|
switch(enum_op) {
|
|
case JSENUMERATE_INIT:
|
|
|
|
/* Get the Java per-thread environment pointer for this JSContext */
|
|
jsj_MapJSContextToJSJThread(cx, &jEnv);
|
|
if (!jEnv)
|
|
return JS_FALSE;
|
|
|
|
member_descriptor = jsj_GetClassInstanceMembers(cx, jEnv, class_descriptor);
|
|
*statep = PRIVATE_TO_JSVAL(member_descriptor);
|
|
if (idp)
|
|
*idp = INT_TO_JSVAL(class_descriptor->num_instance_members);
|
|
return JS_TRUE;
|
|
|
|
case JSENUMERATE_NEXT:
|
|
member_descriptor = JSVAL_TO_PRIVATE(*statep);
|
|
if (member_descriptor) {
|
|
*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:
|
|
PR_ASSERT(0);
|
|
return JS_FALSE;
|
|
}
|
|
}
|
|
|
|
static JSBool
|
|
JavaObject_checkAccess(JSContext *cx, JSObject *obj, jsid id,
|
|
JSAccessMode mode, jsval *vp, uintN *attrsp)
|
|
{
|
|
switch (mode) {
|
|
case JSACC_WATCH:
|
|
JS_ReportError(cx, "Cannot place watchpoints on JavaObject object properties");
|
|
return JS_FALSE;
|
|
|
|
case JSACC_IMPORT:
|
|
JS_ReportError(cx, "Cannot export a JavaObject object's properties");
|
|
return JS_FALSE;
|
|
|
|
default:
|
|
return JS_TRUE;
|
|
}
|
|
}
|
|
|
|
JSObjectOps JavaObject_ops = {
|
|
/* Mandatory non-null function pointer members. */
|
|
NULL, /* newObjectMap */
|
|
NULL, /* destroyObjectMap */
|
|
JavaObject_lookupProperty,
|
|
JavaObject_defineProperty,
|
|
JavaObject_getPropertyById, /* getProperty */
|
|
JavaObject_setPropertyById, /* setProperty */
|
|
JavaObject_getAttributes,
|
|
JavaObject_setAttributes,
|
|
JavaObject_deleteProperty,
|
|
JavaObject_defaultValue,
|
|
JavaObject_newEnumerate,
|
|
JavaObject_checkAccess,
|
|
|
|
/* Optionally non-null members start here. */
|
|
NULL, /* thisObject */
|
|
NULL, /* dropProperty */
|
|
NULL, /* call */
|
|
NULL, /* construct */
|
|
NULL, /* xdrObject */
|
|
NULL, /* hasInstance */
|
|
};
|
|
|
|
static JSObjectOps *
|
|
JavaObject_getObjectOps(JSContext *cx, JSClass *clazz)
|
|
{
|
|
return &JavaObject_ops;
|
|
}
|
|
|
|
JSClass JavaObject_class = {
|
|
"JavaObject", JSCLASS_HAS_PRIVATE,
|
|
NULL, NULL, NULL, NULL,
|
|
NULL, NULL, JavaObject_convert, JavaObject_finalize,
|
|
JavaObject_getObjectOps,
|
|
};
|
|
|
|
extern PR_IMPORT_DATA(JSObjectOps) js_ObjectOps;
|
|
|
|
|
|
JSBool
|
|
jsj_init_JavaObject(JSContext *cx, JSObject *global_obj)
|
|
{
|
|
JavaObject_ops.newObjectMap = js_ObjectOps.newObjectMap;
|
|
JavaObject_ops.destroyObjectMap = js_ObjectOps.destroyObjectMap;
|
|
|
|
if (!JS_InitClass(cx, global_obj,
|
|
0, &JavaObject_class, 0, 0,
|
|
0, 0,
|
|
0, 0))
|
|
return JS_FALSE;
|
|
|
|
return init_java_obj_reflections_table();
|
|
}
|