diff --git a/mozilla/js/ref/liveconnect/LiveConnect.dsp b/mozilla/js/ref/liveconnect/LiveConnect.dsp index 705e239c304..ea2492b6a33 100644 --- a/mozilla/js/ref/liveconnect/LiveConnect.dsp +++ b/mozilla/js/ref/liveconnect/LiveConnect.dsp @@ -110,6 +110,10 @@ SOURCE=..\liveconnect\jsj_field.c # End Source File # Begin Source File +SOURCE=.\jsj_hash.c +# End Source File +# Begin Source File + SOURCE=.\jsj_JavaArray.c # End Source File # Begin Source File diff --git a/mozilla/js/ref/liveconnect/jsj.c b/mozilla/js/ref/liveconnect/jsj.c index cdacbbdd7b7..e60bd4c79f5 100644 --- a/mozilla/js/ref/liveconnect/jsj.c +++ b/mozilla/js/ref/liveconnect/jsj.c @@ -35,9 +35,6 @@ #include "jsj_private.h" /* LiveConnect internals */ #include "jsjava.h" /* LiveConnect external API */ -/* FIXME - The JNI environment should not be a global. It needs to be in thread-local storage. */ -JNIEnv *jENV; - /* * At certain times during initialization, there may be no JavaScript context * available to direct error reports to, in which case the error messages @@ -311,6 +308,7 @@ JSJ_ConnectToJavaVM(JavaVM *java_vm_arg, const char *user_classpath) JavaVM *java_vm; JSJavaVM *jsjava_vm; const char *full_classpath; + JNIEnv *jEnv; jsjava_vm = (JSJavaVM*)malloc(sizeof(JSJavaVM)); if (!jsjava_vm) @@ -321,7 +319,7 @@ JSJ_ConnectToJavaVM(JavaVM *java_vm_arg, const char *user_classpath) /* If a Java VM was passed in, try to attach to it on the current thread. */ if (java_vm) { - if ((*java_vm)->AttachCurrentThread(java_vm, &jENV, NULL) < 0) { + if ((*java_vm)->AttachCurrentThread(java_vm, &jEnv, NULL) < 0) { jsj_LogError("Failed to attach to Java VM thread\n"); free(jsjava_vm); return NULL; @@ -345,7 +343,7 @@ JSJ_ConnectToJavaVM(JavaVM *java_vm_arg, const char *user_classpath) } /* Attempt to create our own VM */ - if (JNI_CreateJavaVM(&java_vm, &jENV, &vm_args) < 0) { + if (JNI_CreateJavaVM(&java_vm, &jEnv, &vm_args) < 0) { jsj_LogError("Failed to create Java VM\n"); free(jsjava_vm); return NULL; @@ -355,11 +353,11 @@ JSJ_ConnectToJavaVM(JavaVM *java_vm_arg, const char *user_classpath) jsjava_vm->jsj_created_java_vm = JS_TRUE; } jsjava_vm->java_vm = java_vm; - jsjava_vm->main_thread_env = jENV; + jsjava_vm->main_thread_env = jEnv; /* Load the Java classes, and the method and field descriptors required for Java reflection. */ - if (!init_java_VM_reflection(jsjava_vm, jENV)) { + if (!init_java_VM_reflection(jsjava_vm, jEnv)) { JSJ_DisconnectFromJavaVM(jsjava_vm); return NULL; } @@ -371,7 +369,7 @@ JSJ_ConnectToJavaVM(JavaVM *java_vm_arg, const char *user_classpath) * of failure, LiveConnect is still operative, but only when calling * from JS to Java and not vice-versa. */ - init_netscape_java_classes(jsjava_vm, jENV); + init_netscape_java_classes(jsjava_vm, jEnv); /* Put this VM on the list of all created VMs */ jsjava_vm->next = jsjava_vm_list; @@ -425,7 +423,7 @@ JSJ_InitJSContext(JSContext *cx, JSObject *global_obj, /* Eliminate a reference to a Java class */ #define UNLOAD_CLASS(qualified_name, class) \ if (class) { \ - (*jENV)->DeleteGlobalRef(jENV, class); \ + (*jEnv)->DeleteGlobalRef(jEnv, class); \ class = NULL; \ } @@ -438,10 +436,17 @@ JSJ_InitJSContext(JSContext *cx, JSObject *global_obj, void JSJ_DisconnectFromJavaVM(JSJavaVM *jsjava_vm) { - /* FIXME - Clean up the various hash tables */ + JNIEnv *jEnv; + JavaVM *java_vm; + + java_vm = jsjava_vm->java_vm; + (*java_vm)->AttachCurrentThread(java_vm, &jEnv, NULL); - if (jsjava_vm->jsj_created_java_vm) { - JavaVM *java_vm = jsjava_vm->java_vm; + /* Drop all references to Java objects and classes */ + jsj_DiscardJavaObjReflections(jEnv); + jsj_DiscardJavaClassReflections(jEnv); + + if (jsjava_vm->jsj_created_java_vm) { (*java_vm)->DestroyJavaVM(java_vm); } else { UNLOAD_CLASS(java/lang/Object, jlObject); @@ -478,7 +483,7 @@ new_jsjava_thread_state(JSJavaVM *jsjava_vm, const char *thread_name, JNIEnv *jE if (thread_name) jsj_env->name = strdup(thread_name); - /* FIXME - need to protect against races */ + /* THREADSAFETY - need to protect against races */ jsj_env->next = thread_list; thread_list = jsj_env; @@ -491,7 +496,7 @@ find_jsjava_thread(JNIEnv *jEnv) JSJavaThreadState *e, **p, *jsj_env; jsj_env = NULL; - /* FIXME - need to protect against races in manipulating the thread list */ + /* THREADSAFETY - need to protect against races in manipulating the thread list */ /* Search for the thread state among the list of all created LiveConnect threads */ @@ -622,7 +627,7 @@ JSJ_DetachCurrentThreadFromJava(JSJavaThreadState *jsj_env) /* Destroy the LiveConnect execution environment passed in */ jsj_ClearPendingJSErrors(jsj_env); - /* FIXME - need to protect against races */ + /* THREADSAFETY - need to protect against races */ for (p=&thread_list; e = *p; p = &(e->next)) { if (e == jsj_env) { *p = jsj_env->next; @@ -698,8 +703,9 @@ JSJCallbacks jsj_default_callbacks = { JSBool JSJ_SimpleInit(JSContext *cx, JSObject *global_obj, JavaVM *java_vm, const char *classpath) { - PR_ASSERT(!the_jsj_vm); + JNIEnv *jEnv; + PR_ASSERT(!the_jsj_vm); the_jsj_vm = JSJ_ConnectToJavaVM(java_vm, classpath); if (!the_jsj_vm) return JS_FALSE; @@ -711,7 +717,7 @@ JSJ_SimpleInit(JSContext *cx, JSObject *global_obj, JavaVM *java_vm, const char the_cx = cx; the_global_js_obj = global_obj; - the_jsj_thread = JSJ_AttachCurrentThreadToJava(the_jsj_vm, "main thread", &jENV); + the_jsj_thread = JSJ_AttachCurrentThreadToJava(the_jsj_vm, "main thread", &jEnv); if (!the_jsj_thread) goto error; diff --git a/mozilla/js/ref/liveconnect/jsj_JSObject.c b/mozilla/js/ref/liveconnect/jsj_JSObject.c index 91dd69a7f37..f82dd958659 100644 --- a/mozilla/js/ref/liveconnect/jsj_JSObject.c +++ b/mozilla/js/ref/liveconnect/jsj_JSObject.c @@ -35,7 +35,8 @@ #include "jsj_private.h" #include "jsjava.h" -#include "jscntxt.h" /* For js_ReportErrorAgain(). FIXME - get rid of private header */ +#include "jscntxt.h" /* For js_ReportErrorAgain(). + TODO - get rid of private header */ #include "netscape_javascript_JSObject.h" /* javah-generated headers */ @@ -763,7 +764,6 @@ Java_netscape_javascript_JSObject_setMember(JNIEnv *jEnv, goto done; } - /* FIXME - can property watchers be used to avoid security checks ? */ /* Get the Unicode string for the JS property name */ property_name_ucs2 = (*jEnv)->GetStringChars(jEnv, property_name_jstr, &is_copy); if (!property_name_ucs2) { @@ -898,8 +898,6 @@ Java_netscape_javascript_JSObject_call(JNIEnv *jEnv, jobject java_wrapper_obj, } function_name_len = (*jEnv)->GetStringLength(jEnv, function_name_jstr); - /* FIXME: What about security stuff ? Don't principals need to be set here ? */ - /* Allocate space for JS arguments */ if (java_args) { argc = (*jEnv)->GetArrayLength(jEnv, java_args); diff --git a/mozilla/js/ref/liveconnect/jsj_JavaClass.c b/mozilla/js/ref/liveconnect/jsj_JavaClass.c index 629204c2a69..92556ec15c8 100644 --- a/mozilla/js/ref/liveconnect/jsj_JavaClass.c +++ b/mozilla/js/ref/liveconnect/jsj_JavaClass.c @@ -164,7 +164,7 @@ JavaClass_getPropertyById(JSContext *cx, JSObject *obj, jsid id, jsval *vp) } else { JSFunction *function; - /* FIXME - eliminate JSFUN_BOUND_METHOD */ + /* TODO - eliminate JSFUN_BOUND_METHOD */ JS_IdToValue(cx, id, &idval); member_name = JS_GetStringBytes(JSVAL_TO_STRING(idval)); function = JS_NewFunction(cx, jsj_JavaStaticMethodWrapper, 0, @@ -341,7 +341,7 @@ JavaClass_newEnumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op, jsj_MapJSContextToJSJThread(cx, &jEnv); if (!jEnv) return JS_FALSE; - member_descriptor = jsj_GetClassInstanceMembers(cx, jEnv, class_descriptor); + 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); diff --git a/mozilla/js/ref/liveconnect/jsj_JavaObject.c b/mozilla/js/ref/liveconnect/jsj_JavaObject.c index 305d2274ef9..b12cb4bf379 100644 --- a/mozilla/js/ref/liveconnect/jsj_JavaObject.c +++ b/mozilla/js/ref/liveconnect/jsj_JavaObject.c @@ -31,6 +31,7 @@ #include "prassert.h" #include "jsj_private.h" /* LiveConnect internals */ +#include "jsj_hash.h" /* Hash table with Java object as key */ /* @@ -47,7 +48,7 @@ * 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 PRHashTable *java_obj_reflections = NULL; +static JSJHashTable *java_obj_reflections = NULL; #ifdef JS_THREADSAFE static PRMonitor *java_obj_reflections_monitor = NULL; @@ -57,8 +58,8 @@ static JSBool init_java_obj_reflections_table() { java_obj_reflections = - PR_NewHashTable(512, jsj_HashJavaObject, jsj_JavaObjectComparator, - NULL, NULL, NULL); + JSJ_NewHashTable(512, jsj_HashJavaObject, jsj_JavaObjectComparator, + NULL, NULL, NULL); if (!java_obj_reflections) return JS_FALSE; @@ -84,18 +85,18 @@ jsj_WrapJavaObject(JSContext *cx, JSObject *js_wrapper_obj; JavaObjectWrapper *java_wrapper; JavaClassDescriptor *class_descriptor; - PRHashEntry *he, **hep; + JSJHashEntry *he, **hep; js_wrapper_obj = NULL; - hash_code = jsj_HashJavaObject((void*)java_obj); + hash_code = jsj_HashJavaObject((void*)java_obj, jEnv); #ifdef JS_THREADSAFE PR_EnterMonitor(java_obj_reflections_monitor); #endif - hep = PR_HashTableRawLookup(java_obj_reflections, - hash_code, java_obj); + hep = JSJ_HashTableRawLookup(java_obj_reflections, + hash_code, java_obj, (void*)jEnv); he = *hep; if (he) { js_wrapper_obj = (JSObject *)he->value; @@ -130,14 +131,17 @@ jsj_WrapJavaObject(JSContext *cx, java_wrapper->class_descriptor = class_descriptor; java_wrapper->members = NULL; + 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 = PR_HashTableRawAdd(java_obj_reflections, hep, hash_code, - java_obj, js_wrapper_obj); - if (he) { - java_wrapper->java_obj = (*jEnv)->NewGlobalRef(jEnv, java_obj); - if (!java_wrapper->java_obj) - goto out_of_memory; - } else { + 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; } @@ -156,23 +160,24 @@ out_of_memory: } static void -remove_java_obj_reflection_from_hashtable(jobject java_obj) +remove_java_obj_reflection_from_hashtable(jobject java_obj, JNIEnv *jEnv) { prhashcode hash_code; - PRHashEntry *he, **hep; + JSJHashEntry *he, **hep; - hash_code = jsj_HashJavaObject((void*)java_obj); + hash_code = jsj_HashJavaObject((void*)java_obj, jEnv); #ifdef JS_THREADSAFE PR_EnterMonitor(java_obj_reflections_monitor); #endif - hep = PR_HashTableRawLookup(java_obj_reflections, hash_code, java_obj); + hep = JSJ_HashTableRawLookup(java_obj_reflections, hash_code, + java_obj, (void*)jEnv); he = *hep; PR_ASSERT(he); if (he) - PR_HashTableRawRemove(java_obj_reflections, hep, he); + JSJ_HashTableRawRemove(java_obj_reflections, hep, he, (void*)jEnv); #ifdef JS_THREADSAFE PR_ExitMonitor(java_obj_reflections_monitor); @@ -195,7 +200,7 @@ JavaObject_finalize(JSContext *cx, JSObject *obj) return; java_obj = java_wrapper->java_obj; - remove_java_obj_reflection_from_hashtable(java_obj); + remove_java_obj_reflection_from_hashtable(java_obj, jEnv); (*jEnv)->DeleteGlobalRef(jEnv, java_obj); jsj_ReleaseJavaClassDescriptor(cx, jEnv, java_wrapper->class_descriptor); @@ -205,26 +210,29 @@ JavaObject_finalize(JSContext *cx, JSObject *obj) JS_free(cx, java_wrapper); } -/* -static JSBool -JavaObject_toString(JSContext *cx, JSObject *obj, - uintN argc, jsval *argv, jsval *vp) +/* 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; - JavaClassDescriptor *class_descriptor; - java_wrapper = JS_GetPrivate(cx, obj); - if (!java_wrapper) { - JS_ReportError(cx, "illegal operation on JavaObject prototype object"); - return JS_FALSE; - } - class_descriptor = java_wrapper->class_descriptor; - java_obj = java_wrapper->java_obj; - - return jsj_ConvertJavaObjectToJSString(cx, class_descriptor, java_obj, vp); + java_obj = (jobject)he->key; + (*jEnv)->DeleteGlobalRef(jEnv, java_obj); + 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) { @@ -312,7 +320,7 @@ lookup_member_by_id(JSContext *cx, JNIEnv *jEnv, JSObject *obj, PR_ASSERT(class_descriptor->type == JAVA_SIGNATURE_CLASS || class_descriptor->type == JAVA_SIGNATURE_ARRAY); - /* FIXME - not thread-safe */ + /* THREADSAFETY - not thread-safe */ prev_memberp = &java_wrapper->members; for (member = *prev_memberp; member; member = member->next) { member_descriptor = member->descriptor; @@ -336,7 +344,7 @@ lookup_member_by_id(JSContext *cx, JNIEnv *jEnv, JSObject *obj, /* printf("Adding %s\n", member_name); */ - /* FIXME - eliminate JSFUN_BOUND_METHOD */ + /* TODO - eliminate JSFUN_BOUND_METHOD */ /* TODO - Use JS_CloneFunction() to save memory */ function = JS_NewFunction(cx, jsj_JavaInstanceMethodWrapper, 0, JSFUN_BOUND_METHOD, obj, member_name); diff --git a/mozilla/js/ref/liveconnect/jsj_class.c b/mozilla/js/ref/liveconnect/jsj_class.c index d09e3d34050..6219a0c3d4e 100644 --- a/mozilla/js/ref/liveconnect/jsj_class.c +++ b/mozilla/js/ref/liveconnect/jsj_class.c @@ -36,11 +36,11 @@ #include "jsj_private.h" /* LiveConnect internals */ -#include "prhash.h" /* Hash tables */ +#include "jsj_hash.h" /* Hash tables */ /* A one-to-one mapping between all referenced java.lang.Class objects and their corresponding JavaClassDescriptor objects */ -static PRHashTable *java_class_reflections; +static JSJHashTable *java_class_reflections; /* * Given a JVM handle to a java.lang.Class object, malloc a C-string @@ -299,7 +299,8 @@ destroy_class_descriptor(JSContext *cx, JNIEnv *jEnv, JavaClassDescriptor *class JS_FREE_IF(cx, (char *)class_descriptor->name); if (class_descriptor->java_class) { (*jEnv)->DeleteGlobalRef(jEnv, class_descriptor->java_class); - PR_HashTableRemove(java_class_reflections, class_descriptor->java_class); + JSJ_HashTableRemove(java_class_reflections, + class_descriptor->java_class, (void*)jEnv); } if (class_descriptor->array_component_signature) @@ -339,7 +340,8 @@ new_class_descriptor(JSContext *cx, JNIEnv *jEnv, jclass java_class) (*jEnv)->CallIntMethod(jEnv, java_class, jlClass_getModifiers); class_descriptor->ref_count = 1; - if (!PR_HashTableAdd(java_class_reflections, java_class, class_descriptor)) + if (!JSJ_HashTableAdd(java_class_reflections, java_class, class_descriptor, + (void*)jEnv)) goto error; return class_descriptor; @@ -349,12 +351,36 @@ error: return NULL; } +/* Trivial helper for jsj_DiscardJavaClassReflections(), below */ +static PRIntn +enumerate_remove_java_class(JSJHashEntry *he, PRIntn i, void *arg) +{ + JNIEnv *jEnv = (JNIEnv*)arg; + jclass java_class; + + java_class = (jclass)he->key; + (*jEnv)->DeleteGlobalRef(jEnv, java_class); + 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_DiscardJavaClassReflections(JNIEnv *jEnv) +{ + JSJ_HashTableEnumerateEntries(java_class_reflections, + enumerate_remove_java_class, + (void*)jEnv); +} + extern JavaClassDescriptor * jsj_GetJavaClassDescriptor(JSContext *cx, JNIEnv *jEnv, jclass java_class) { JavaClassDescriptor *class_descriptor; - class_descriptor = PR_HashTableLookup(java_class_reflections, - (const void *)java_class); + class_descriptor = JSJ_HashTableLookup(java_class_reflections, + (const void *)java_class, + (void*)jEnv); if (!class_descriptor) return new_class_descriptor(cx, jEnv, java_class); @@ -563,8 +589,8 @@ JSBool jsj_InitJavaClassReflectionsTable() { java_class_reflections = - PR_NewHashTable(64, jsj_HashJavaObject, jsj_JavaObjectComparator, - NULL, NULL, NULL); + JSJ_NewHashTable(64, jsj_HashJavaObject, jsj_JavaObjectComparator, + NULL, NULL, NULL); if (!java_class_reflections) return JS_FALSE; diff --git a/mozilla/js/ref/liveconnect/jsj_hash.c b/mozilla/js/ref/liveconnect/jsj_hash.c new file mode 100644 index 00000000000..67c42d8630a --- /dev/null +++ b/mozilla/js/ref/liveconnect/jsj_hash.c @@ -0,0 +1,481 @@ +/* -*- 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 is a copy of the NSPR hash-table library, but it has been slightly + * modified to allow an additional argument to be passed into the hash + * key-comparision function. This is used to maintain thread-safety by + * passing in a JNIEnv pointer to the key-comparison function rather + * than storing it in a global. All types,function names, etc. have + * been renamed from their original NSPR names to protect the innocent. + */ + +#include "jsj_hash.h" +#include "prtypes.h" +#include "prassert.h" +#include +#include + +/* Compute the number of buckets in ht */ +#define NBUCKETS(ht) (1 << (JSJ_HASH_BITS - (ht)->shift)) + +/* The smallest table has 16 buckets */ +#define MINBUCKETSLOG2 4 +#define MINBUCKETS (1 << MINBUCKETSLOG2) + +/* Compute the maximum entries given n buckets that we will tolerate, ~90% */ +#define OVERLOADED(n) ((n) - ((n) >> 3)) + +/* Compute the number of entries below which we shrink the table by half */ +#define UNDERLOADED(n) (((n) > MINBUCKETS) ? ((n) >> 2) : 0) + +/* +** Stubs for default hash allocator ops. +*/ +static void * +DefaultAllocTable(void *pool, size_t size) +{ +#if defined(XP_MAC) +#pragma unused (pool) +#endif + + return malloc(size); +} + +static void +DefaultFreeTable(void *pool, void *item) +{ +#if defined(XP_MAC) +#pragma unused (pool) +#endif + + free(item); +} + +static JSJHashEntry * +DefaultAllocEntry(void *pool, const void *key) +{ +#if defined(XP_MAC) +#pragma unused (pool,key) +#endif + + return malloc(sizeof(JSJHashEntry)); +} + +static void +DefaultFreeEntry(void *pool, JSJHashEntry *he, PRUintn flag) +{ +#if defined(XP_MAC) +#pragma unused (pool) +#endif + + if (flag == HT_FREE_ENTRY) + free(he); +} + +static JSJHashAllocOps defaultHashAllocOps = { + DefaultAllocTable, DefaultFreeTable, + DefaultAllocEntry, DefaultFreeEntry +}; + +PR_IMPLEMENT(JSJHashTable *) +JSJ_NewHashTable(PRUint32 n, JSJHashFunction keyHash, + JSJHashComparator keyCompare, JSJHashComparator valueCompare, + JSJHashAllocOps *allocOps, void *allocPriv) +{ + JSJHashTable *ht; + PRUint32 nb; + + if (n <= MINBUCKETS) { + n = MINBUCKETSLOG2; + } else { + n = PR_CeilingLog2(n); + if ((PRInt32)n < 0) + return 0; + } + + if (!allocOps) allocOps = &defaultHashAllocOps; + + ht = (*allocOps->allocTable)(allocPriv, sizeof *ht); + if (!ht) + return 0; + memset(ht, 0, sizeof *ht); + ht->shift = JSJ_HASH_BITS - n; + n = 1 << n; +#if defined(XP_PC) && !defined(_WIN32) + if (n > 16000) { + (*allocOps->freeTable)(allocPriv, ht); + return 0; + } +#endif /* WIN16 */ + nb = n * sizeof(JSJHashEntry *); + ht->buckets = (*allocOps->allocTable)(allocPriv, nb); + if (!ht->buckets) { + (*allocOps->freeTable)(allocPriv, ht); + return 0; + } + memset(ht->buckets, 0, nb); + + ht->keyHash = keyHash; + ht->keyCompare = keyCompare; + ht->valueCompare = valueCompare; + ht->allocOps = allocOps; + ht->allocPriv = allocPriv; + return ht; +} + +PR_IMPLEMENT(void) +JSJ_HashTableDestroy(JSJHashTable *ht) +{ + PRUint32 i, n; + JSJHashEntry *he, *next; + JSJHashAllocOps *allocOps = ht->allocOps; + void *allocPriv = ht->allocPriv; + + n = NBUCKETS(ht); + for (i = 0; i < n; i++) { + for (he = ht->buckets[i]; he; he = next) { + next = he->next; + (*allocOps->freeEntry)(allocPriv, he, HT_FREE_ENTRY); + } + } +#ifdef DEBUG + memset(ht->buckets, 0xDB, n * sizeof ht->buckets[0]); +#endif + (*allocOps->freeTable)(allocPriv, ht->buckets); +#ifdef DEBUG + memset(ht, 0xDB, sizeof *ht); +#endif + (*allocOps->freeTable)(allocPriv, ht); +} + +/* +** Multiplicative hash, from Knuth 6.4. +*/ +#define GOLDEN_RATIO 0x9E3779B9U + +PR_IMPLEMENT(JSJHashEntry **) +JSJ_HashTableRawLookup(JSJHashTable *ht, JSJHashNumber keyHash, const void *key, void *arg) +{ + JSJHashEntry *he, **hep, **hep0; + JSJHashNumber h; + +#ifdef HASHMETER + ht->nlookups++; +#endif + h = keyHash * GOLDEN_RATIO; + h >>= ht->shift; + hep = hep0 = &ht->buckets[h]; + while ((he = *hep) != 0) { + if (he->keyHash == keyHash && (*ht->keyCompare)(key, he->key, arg)) { + /* Move to front of chain if not already there */ + if (hep != hep0) { + *hep = he->next; + he->next = *hep0; + *hep0 = he; + } + return hep0; + } + hep = &he->next; +#ifdef HASHMETER + ht->nsteps++; +#endif + } + return hep; +} + +PR_IMPLEMENT(JSJHashEntry *) +JSJ_HashTableRawAdd(JSJHashTable *ht, JSJHashEntry **hep, + JSJHashNumber keyHash, const void *key, void *value, + void *arg) +{ + PRUint32 i, n; + JSJHashEntry *he, *next, **oldbuckets; + PRUint32 nb; + + /* Grow the table if it is overloaded */ + n = NBUCKETS(ht); + if (ht->nentries >= OVERLOADED(n)) { +#ifdef HASHMETER + ht->ngrows++; +#endif + ht->shift--; + oldbuckets = ht->buckets; +#if defined(XP_PC) && !defined(_WIN32) + if (2 * n > 16000) + return 0; +#endif /* WIN16 */ + nb = 2 * n * sizeof(JSJHashEntry *); + ht->buckets = (*ht->allocOps->allocTable)(ht->allocPriv, nb); + if (!ht->buckets) { + ht->buckets = oldbuckets; + return 0; + } + memset(ht->buckets, 0, nb); + + for (i = 0; i < n; i++) { + for (he = oldbuckets[i]; he; he = next) { + next = he->next; + hep = JSJ_HashTableRawLookup(ht, he->keyHash, he->key, arg); + PR_ASSERT(*hep == 0); + he->next = 0; + *hep = he; + } + } +#ifdef DEBUG + memset(oldbuckets, 0xDB, n * sizeof oldbuckets[0]); +#endif + (*ht->allocOps->freeTable)(ht->allocPriv, oldbuckets); + hep = JSJ_HashTableRawLookup(ht, keyHash, key, arg); + } + + /* Make a new key value entry */ + he = (*ht->allocOps->allocEntry)(ht->allocPriv, key); + if (!he) + return 0; + he->keyHash = keyHash; + he->key = key; + he->value = value; + he->next = *hep; + *hep = he; + ht->nentries++; + return he; +} + +PR_IMPLEMENT(JSJHashEntry *) +JSJ_HashTableAdd(JSJHashTable *ht, const void *key, void *value, void *arg) +{ + JSJHashNumber keyHash; + JSJHashEntry *he, **hep; + + keyHash = (*ht->keyHash)(key, arg); + hep = JSJ_HashTableRawLookup(ht, keyHash, key, arg); + if ((he = *hep) != 0) { + /* Hit; see if values match */ + if ((*ht->valueCompare)(he->value, value, arg)) { + /* key,value pair is already present in table */ + return he; + } + if (he->value) + (*ht->allocOps->freeEntry)(ht->allocPriv, he, HT_FREE_VALUE); + he->value = value; + return he; + } + return JSJ_HashTableRawAdd(ht, hep, keyHash, key, value, arg); +} + +PR_IMPLEMENT(void) +JSJ_HashTableRawRemove(JSJHashTable *ht, JSJHashEntry **hep, JSJHashEntry *he, void *arg) +{ + PRUint32 i, n; + JSJHashEntry *next, **oldbuckets; + PRUint32 nb; + + *hep = he->next; + (*ht->allocOps->freeEntry)(ht->allocPriv, he, HT_FREE_ENTRY); + + /* Shrink table if it's underloaded */ + n = NBUCKETS(ht); + if (--ht->nentries < UNDERLOADED(n)) { +#ifdef HASHMETER + ht->nshrinks++; +#endif + ht->shift++; + oldbuckets = ht->buckets; + nb = n * sizeof(JSJHashEntry*) / 2; + ht->buckets = (*ht->allocOps->allocTable)(ht->allocPriv, nb); + if (!ht->buckets) { + ht->buckets = oldbuckets; + return; + } + memset(ht->buckets, 0, nb); + + for (i = 0; i < n; i++) { + for (he = oldbuckets[i]; he; he = next) { + next = he->next; + hep = JSJ_HashTableRawLookup(ht, he->keyHash, he->key, arg); + PR_ASSERT(*hep == 0); + he->next = 0; + *hep = he; + } + } +#ifdef DEBUG + memset(oldbuckets, 0xDB, n * sizeof oldbuckets[0]); +#endif + (*ht->allocOps->freeTable)(ht->allocPriv, oldbuckets); + } +} + +PR_IMPLEMENT(PRBool) +JSJ_HashTableRemove(JSJHashTable *ht, const void *key, void *arg) +{ + JSJHashNumber keyHash; + JSJHashEntry *he, **hep; + + keyHash = (*ht->keyHash)(key, arg); + hep = JSJ_HashTableRawLookup(ht, keyHash, key, arg); + if ((he = *hep) == 0) + return PR_FALSE; + + /* Hit; remove element */ + JSJ_HashTableRawRemove(ht, hep, he, arg); + return PR_TRUE; +} + +PR_IMPLEMENT(void *) +JSJ_HashTableLookup(JSJHashTable *ht, const void *key, void *arg) +{ + JSJHashNumber keyHash; + JSJHashEntry *he, **hep; + + keyHash = (*ht->keyHash)(key, arg); + hep = JSJ_HashTableRawLookup(ht, keyHash, key, arg); + if ((he = *hep) != 0) { + return he->value; + } + return 0; +} + +/* +** Iterate over the entries in the hash table calling func for each +** entry found. Stop if "f" says to (return value & PR_ENUMERATE_STOP). +** Return a count of the number of elements scanned. +*/ +PR_IMPLEMENT(int) +JSJ_HashTableEnumerateEntries(JSJHashTable *ht, JSJHashEnumerator f, void *arg) +{ + JSJHashEntry *he, **hep; + PRUint32 i, nbuckets; + int rv, n = 0; + JSJHashEntry *todo = 0; + + nbuckets = NBUCKETS(ht); + for (i = 0; i < nbuckets; i++) { + hep = &ht->buckets[i]; + while ((he = *hep) != 0) { + rv = (*f)(he, n, arg); + n++; + if (rv & (HT_ENUMERATE_REMOVE | HT_ENUMERATE_UNHASH)) { + *hep = he->next; + if (rv & HT_ENUMERATE_REMOVE) { + he->next = todo; + todo = he; + } + } else { + hep = &he->next; + } + if (rv & HT_ENUMERATE_STOP) { + goto out; + } + } + } + +out: + hep = &todo; + while ((he = *hep) != 0) { + JSJ_HashTableRawRemove(ht, hep, he, arg); + } + return n; +} + +#ifdef HASHMETER +#include +#include + +PR_IMPLEMENT(void) +JSJ_HashTableDumpMeter(JSJHashTable *ht, JSJHashEnumerator dump, FILE *fp) +{ + double mean, variance; + PRUint32 nchains, nbuckets; + PRUint32 i, n, maxChain, maxChainLen; + JSJHashEntry *he; + + variance = 0; + nchains = 0; + maxChainLen = 0; + nbuckets = NBUCKETS(ht); + for (i = 0; i < nbuckets; i++) { + he = ht->buckets[i]; + if (!he) + continue; + nchains++; + for (n = 0; he; he = he->next) + n++; + variance += n * n; + if (n > maxChainLen) { + maxChainLen = n; + maxChain = i; + } + } + mean = (double)ht->nentries / nchains; + variance = fabs(variance / nchains - mean * mean); + + fprintf(fp, "\nHash table statistics:\n"); + fprintf(fp, " number of lookups: %u\n", ht->nlookups); + fprintf(fp, " number of entries: %u\n", ht->nentries); + fprintf(fp, " number of grows: %u\n", ht->ngrows); + fprintf(fp, " number of shrinks: %u\n", ht->nshrinks); + fprintf(fp, " mean steps per hash: %g\n", (double)ht->nsteps + / ht->nlookups); + fprintf(fp, "mean hash chain length: %g\n", mean); + fprintf(fp, " standard deviation: %g\n", sqrt(variance)); + fprintf(fp, " max hash chain length: %u\n", maxChainLen); + fprintf(fp, " max hash chain: [%u]\n", maxChain); + + for (he = ht->buckets[maxChain], i = 0; he; he = he->next, i++) + if ((*dump)(he, i, fp) != HT_ENUMERATE_NEXT) + break; +} +#endif /* HASHMETER */ + +PR_IMPLEMENT(int) +JSJ_HashTableDump(JSJHashTable *ht, JSJHashEnumerator dump, FILE *fp) +{ + int count; + + count = JSJ_HashTableEnumerateEntries(ht, dump, fp); +#ifdef HASHMETER + JSJ_HashTableDumpMeter(ht, dump, fp); +#endif + return count; +} + +PR_IMPLEMENT(JSJHashNumber) +JSJ_HashString(const void *key) +{ + JSJHashNumber h; + const unsigned char *s; + + h = 0; + for (s = key; *s; s++) + h = (h >> 28) ^ (h << 4) ^ *s; + return h; +} + +PR_IMPLEMENT(int) +JSJ_CompareStrings(const void *v1, const void *v2) +{ + return strcmp(v1, v2) == 0; +} + +PR_IMPLEMENT(int) +JSJ_CompareValues(const void *v1, const void *v2) +{ + return v1 == v2; +} diff --git a/mozilla/js/ref/liveconnect/jsj_hash.h b/mozilla/js/ref/liveconnect/jsj_hash.h new file mode 100644 index 00000000000..e6fb30aa41b --- /dev/null +++ b/mozilla/js/ref/liveconnect/jsj_hash.h @@ -0,0 +1,141 @@ +/* -*- 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 is a copy of the NSPR hash-table library, but it has been slightly + * modified to allow an additional argument to be passed into the hash + * key-comparision function. This is used to maintain thread-safety by + * passing in a JNIEnv pointer to the key-comparison function rather + * than storing it in a global. All types,function names, etc. have + * been renamed from their original NSPR names to protect the innocent. + */ + +#ifndef jsj_hash_h___ +#define jsj_hash_h___ +/* + * API to portable hash table code. + */ +#include +#include +#include "prtypes.h" + +PR_BEGIN_EXTERN_C + +typedef struct JSJHashEntry JSJHashEntry; +typedef struct JSJHashTable JSJHashTable; +typedef PRUint32 JSJHashNumber; +#define JSJ_HASH_BITS 32 +typedef JSJHashNumber (*JSJHashFunction)(const void *key, void *arg); +typedef PRIntn (*JSJHashComparator)(const void *v1, const void *v2, void *arg); +typedef PRIntn (*JSJHashEnumerator)(JSJHashEntry *he, PRIntn i, void *arg); + +/* Flag bits in JSJHashEnumerator's return value */ +#define HT_ENUMERATE_NEXT 0 /* continue enumerating entries */ +#define HT_ENUMERATE_STOP 1 /* stop enumerating entries */ +#define HT_ENUMERATE_REMOVE 2 /* remove and free the current entry */ +#define HT_ENUMERATE_UNHASH 4 /* just unhash the current entry */ + +typedef struct JSJHashAllocOps { + void * (*allocTable)(void *pool, size_t size); + void (*freeTable)(void *pool, void *item); + JSJHashEntry * (*allocEntry)(void *pool, const void *key); + void (*freeEntry)(void *pool, JSJHashEntry *he, PRUintn flag); +} JSJHashAllocOps; + +#define HT_FREE_VALUE 0 /* just free the entry's value */ +#define HT_FREE_ENTRY 1 /* free value and entire entry */ + +struct JSJHashEntry { + JSJHashEntry *next; /* hash chain linkage */ + JSJHashNumber keyHash; /* key hash function result */ + const void *key; /* ptr to opaque key */ + void *value; /* ptr to opaque value */ +}; + +struct JSJHashTable { + JSJHashEntry **buckets; /* vector of hash buckets */ + PRUint32 nentries; /* number of entries in table */ + PRUint32 shift; /* multiplicative hash shift */ + JSJHashFunction keyHash; /* key hash function */ + JSJHashComparator keyCompare; /* key comparison function */ + JSJHashComparator valueCompare; /* value comparison function */ + JSJHashAllocOps *allocOps; /* allocation operations */ + void *allocPriv; /* allocation private data */ +#ifdef HASHMETER + PRUint32 nlookups; /* total number of lookups */ + PRUint32 nsteps; /* number of hash chains traversed */ + PRUint32 ngrows; /* number of table expansions */ + PRUint32 nshrinks; /* number of table contractions */ +#endif +}; + +/* + * Create a new hash table. + * If allocOps is null, use default allocator ops built on top of malloc(). + */ +PR_EXTERN(JSJHashTable *) +JSJ_NewHashTable(PRUint32 n, JSJHashFunction keyHash, + JSJHashComparator keyCompare, JSJHashComparator valueCompare, + JSJHashAllocOps *allocOps, void *allocPriv); + +PR_EXTERN(void) +JSJ_HashTableDestroy(JSJHashTable *ht); + +/* Low level access methods */ +PR_EXTERN(JSJHashEntry **) +JSJ_HashTableRawLookup(JSJHashTable *ht, JSJHashNumber keyHash, const void *key, void *arg); + +PR_EXTERN(JSJHashEntry *) +JSJ_HashTableRawAdd(JSJHashTable *ht, JSJHashEntry **hep, JSJHashNumber keyHash, + const void *key, void *value, void *arg); + +PR_EXTERN(void) +JSJ_HashTableRawRemove(JSJHashTable *ht, JSJHashEntry **hep, JSJHashEntry *he, void *arg); + +/* Higher level access methods */ +PR_EXTERN(JSJHashEntry *) +JSJ_HashTableAdd(JSJHashTable *ht, const void *key, void *value, void *arg); + +PR_EXTERN(PRBool) +JSJ_HashTableRemove(JSJHashTable *ht, const void *key, void *arg); + +PR_EXTERN(PRIntn) +JSJ_HashTableEnumerateEntries(JSJHashTable *ht, JSJHashEnumerator f, void *arg); + +PR_EXTERN(void *) +JSJ_HashTableLookup(JSJHashTable *ht, const void *key, void *arg); + +PR_EXTERN(PRIntn) +JSJ_HashTableDump(JSJHashTable *ht, JSJHashEnumerator dump, FILE *fp); + +/* General-purpose C string hash function. */ +PR_EXTERN(JSJHashNumber) +JSJ_HashString(const void *key); + +/* Compare strings using strcmp(), return true if equal. */ +PR_EXTERN(int) +JSJ_CompareStrings(const void *v1, const void *v2); + +/* Stub function just returns v1 == v2 */ +PR_EXTERN(PRIntn) +JSJ_CompareValues(const void *v1, const void *v2); + +PR_END_EXTERN_C + +#endif /* jsj_hash_h___ */ diff --git a/mozilla/js/ref/liveconnect/jsj_method.c b/mozilla/js/ref/liveconnect/jsj_method.c index 9ac35968856..80aa56ad4e8 100644 --- a/mozilla/js/ref/liveconnect/jsj_method.c +++ b/mozilla/js/ref/liveconnect/jsj_method.c @@ -34,7 +34,7 @@ #include "prosdep.h" #include "jsj_private.h" /* LiveConnect internals */ -#include "jsjava.h" /* LiveConnect external API */ /* FIXME - get rid of this include */ +#include "jsjava.h" /* LiveConnect external API */ /* * A helper function for jsj_ConvertJavaMethodSignatureToString(): diff --git a/mozilla/js/ref/liveconnect/jsj_private.h b/mozilla/js/ref/liveconnect/jsj_private.h index 01bae47c4ae..55afca02f49 100644 --- a/mozilla/js/ref/liveconnect/jsj_private.h +++ b/mozilla/js/ref/liveconnect/jsj_private.h @@ -174,7 +174,6 @@ typedef struct JSJavaThreadState { /******************************** Globals ***********************************/ -extern JNIEnv *jENV; extern JSJCallbacks *JSJ_callbacks; /* JavaScript classes that reflect Java objects */ @@ -301,7 +300,10 @@ jsj_init_JavaPackage(JSContext *, JSObject *, extern JSBool jsj_init_JavaClass(JSContext *cx, JSObject *global_obj); -const char * +extern void +jsj_DiscardJavaClassReflections(JNIEnv *jEnv); + +extern const char * jsj_GetJavaClassName(JSContext *cx, JNIEnv *jEnv, jclass java_class); extern JavaClassDescriptor * @@ -321,21 +323,11 @@ jsj_GetJavaMemberDescriptor(JSContext *cx, JavaClassDescriptor *class_descriptor, jstring member_name); -/* extern JavaMemberDescriptor * -jsj_LookupJavaClassMember(JSContext *cx, - JavaClassDescriptor *class_descriptor, - const char *member_name);*/ - extern JavaMemberDescriptor * jsj_LookupJavaMemberDescriptorById(JSContext *cx, JNIEnv *jEnv, JavaClassDescriptor *class_descriptor, jsid id); -/* extern JavaMemberDescriptor * -jsj_LookupJavaStaticMemberDescriptor(JSContext *cx, - JavaClassDescriptor *class_descriptor, - jstring member_name); */ - extern JavaMemberDescriptor * jsj_LookupJavaStaticMemberDescriptorById(JSContext *cx, JNIEnv *jEnv, JavaClassDescriptor *class_descriptor, @@ -412,6 +404,9 @@ jsj_init_JavaObject(JSContext *, JSObject *); extern JSObject * jsj_WrapJavaObject(JSContext *cx, JNIEnv *jEnv, jobject java_obj, jclass java_class); +extern void +jsj_DiscardJavaObjReflections(JNIEnv *jEnv); + extern JSBool JavaObject_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp); @@ -468,10 +463,10 @@ extern void jsj_LogError(const char *error_msg); PR_CALLBACK prhashcode -jsj_HashJavaObject(const void *key); +jsj_HashJavaObject(const void *key, JNIEnv *jEnv); PR_CALLBACK intN -jsj_JavaObjectComparator(const void *v1, const void *v2); +jsj_JavaObjectComparator(const void *v1, const void *v2, void *arg); extern JSJavaThreadState * jsj_MapJavaThreadToJSJavaThreadState(JNIEnv *jEnv, char **errp); diff --git a/mozilla/js/ref/liveconnect/jsj_utils.c b/mozilla/js/ref/liveconnect/jsj_utils.c index ad6b9a8d928..c9d6c992006 100644 --- a/mozilla/js/ref/liveconnect/jsj_utils.c +++ b/mozilla/js/ref/liveconnect/jsj_utils.c @@ -41,15 +41,15 @@ * object by calling java.lang.System.identityHashCode() */ PR_CALLBACK prhashcode -jsj_HashJavaObject(const void *key) +jsj_HashJavaObject(const void *key, JNIEnv *jEnv) { prhashcode hash_code; jobject java_obj; java_obj = (jobject)key; - hash_code = (*jENV)->CallStaticIntMethod(jENV, jlSystem, + hash_code = (*jEnv)->CallStaticIntMethod(jEnv, jlSystem, jlSystem_identityHashCode, java_obj); - PR_ASSERT(!(*jENV)->ExceptionOccurred(jENV)); + PR_ASSERT(!(*jEnv)->ExceptionOccurred(jEnv)); return hash_code; } @@ -61,16 +61,18 @@ jsj_HashJavaObject(const void *key) * use the JNI routine for comparing the two objects. */ PR_CALLBACK intN -jsj_JavaObjectComparator(const void *v1, const void *v2) +jsj_JavaObjectComparator(const void *v1, const void *v2, void *arg) { jobject java_obj1, java_obj2; + JNIEnv *jEnv; + jEnv = (JNIEnv*)arg; java_obj1 = (jobject)v1; java_obj2 = (jobject)v2; if (java_obj1 == java_obj2) return 1; - return (*jENV)->IsSameObject(jENV, java_obj1, java_obj2); + return (*jEnv)->IsSameObject(jEnv, java_obj1, java_obj2); } /*