Back to index

lightning-sunbird  0.9+nobinonly
jsj.c
Go to the documentation of this file.
00001 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
00002  *
00003  * ***** BEGIN LICENSE BLOCK *****
00004  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00005  *
00006  * The contents of this file are subject to the Mozilla Public License Version
00007  * 1.1 (the "License"); you may not use this file except in compliance with
00008  * the License. You may obtain a copy of the License at
00009  * http://www.mozilla.org/MPL/
00010  *
00011  * Software distributed under the License is distributed on an "AS IS" basis,
00012  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00013  * for the specific language governing rights and limitations under the
00014  * License.
00015  *
00016  * The Original Code is Mozilla Communicator client code, released
00017  * March 31, 1998.
00018  *
00019  * The Initial Developer of the Original Code is
00020  * Netscape Communications Corporation.
00021  * Portions created by the Initial Developer are Copyright (C) 1998
00022  * the Initial Developer. All Rights Reserved.
00023  *
00024  * Contributor(s):
00025  *
00026  * Alternatively, the contents of this file may be used under the terms of
00027  * either of the GNU General Public License Version 2 or later (the "GPL"),
00028  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00029  * in which case the provisions of the GPL or the LGPL are applicable instead
00030  * of those above. If you wish to allow use of your version of this file only
00031  * under the terms of either the GPL or the LGPL, and not to allow others to
00032  * use your version of this file under the terms of the MPL, indicate your
00033  * decision by deleting the provisions above and replace them with the notice
00034  * and other provisions required by the GPL or the LGPL. If you do not delete
00035  * the provisions above, a recipient may use your version of this file under
00036  * the terms of any one of the MPL, the GPL or the LGPL.
00037  *
00038  * ***** END LICENSE BLOCK ***** */
00039  
00040 /*
00041  * This file is part of the Java-vendor-neutral implementation of LiveConnect
00042  *
00043  * It contains the top-level initialization code and the implementation of the
00044  * public API.
00045  *
00046  */
00047 
00048 #include <stdlib.h>
00049 #include <string.h>
00050 
00051 #include "jsj_private.h"        /* LiveConnect internals */
00052 #include "jsjava.h"             /* LiveConnect external API */
00053 
00054 #ifdef JSJ_THREADSAFE
00055 #    include "prmon.h"
00056 #endif
00057 
00058 JSBool jsj_JSIsCallingApplet = JS_FALSE;
00059 
00060 /*
00061  * At certain times during initialization, there may be no JavaScript context
00062  * available to direct error reports to, in which case the error messages
00063  * are sent to this function.  The caller is responsible for free'ing
00064  * the js_error_msg argument.
00065  */
00066 static void
00067 report_java_initialization_error(JNIEnv *jEnv, const char *js_error_msg)
00068 {
00069     const char *error_msg, *java_error_msg;
00070 
00071     java_error_msg = NULL;
00072 
00073     if (jEnv) {
00074         java_error_msg = jsj_GetJavaErrorMessage(jEnv);
00075         (*jEnv)->ExceptionClear(jEnv);
00076     }
00077 
00078     if (java_error_msg) { 
00079         error_msg = JS_smprintf("initialization error: %s (%s)\n",
00080                                 js_error_msg, java_error_msg);
00081         free((void*)java_error_msg);
00082     } else {
00083         error_msg = JS_smprintf("initialization error: %s\n",
00084                                 js_error_msg);
00085     }
00086 
00087     jsj_LogError(error_msg);
00088     free((void*)error_msg);
00089 }
00090 
00091 /*
00092  * Opaque JVM handles to Java classes and methods required for Java reflection.
00093  * These are computed and cached during initialization.
00094  */
00095 
00096 jclass jlObject;                        /* java.lang.Object */
00097 jclass jlrMethod;                       /* java.lang.reflect.Method */
00098 jclass jlrField;                        /* java.lang.reflect.Field */
00099 jclass jlrArray;                        /* java.lang.reflect.Array */
00100 jclass jlVoid;                          /* java.lang.Void */
00101 jclass jlrConstructor;                  /* java.lang.reflect.Constructor */
00102 jclass jlThrowable;                     /* java.lang.Throwable */
00103 jclass jlSystem;                        /* java.lang.System */
00104 jclass jlClass;                         /* java.lang.Class */
00105 jclass jlBoolean;                       /* java.lang.Boolean */
00106 jclass jlDouble;                        /* java.lang.Double */
00107 jclass jlString;                        /* java.lang.String */
00108 jclass jaApplet;                        /* java.applet.Applet */
00109 jclass njJSObject;                      /* netscape.javascript.JSObject */
00110 jclass njJSException;                   /* netscape.javascript.JSException */
00111 jclass njJSUtil;                        /* netscape.javascript.JSUtil */
00112 
00113 jmethodID jlClass_getMethods;           /* java.lang.Class.getMethods() */
00114 jmethodID jlClass_getConstructors;      /* java.lang.Class.getConstructors() */
00115 jmethodID jlClass_getFields;            /* java.lang.Class.getFields() */
00116 jmethodID jlClass_getName;              /* java.lang.Class.getName() */
00117 jmethodID jlClass_getComponentType;     /* java.lang.Class.getComponentType() */
00118 jmethodID jlClass_getModifiers;         /* java.lang.Class.getModifiers() */
00119 jmethodID jlClass_isArray;              /* java.lang.Class.isArray() */
00120 
00121 jmethodID jlrMethod_getName;            /* java.lang.reflect.Method.getName() */
00122 jmethodID jlrMethod_getParameterTypes;  /* java.lang.reflect.Method.getParameterTypes() */
00123 jmethodID jlrMethod_getReturnType;      /* java.lang.reflect.Method.getReturnType() */
00124 jmethodID jlrMethod_getModifiers;       /* java.lang.reflect.Method.getModifiers() */
00125 
00126 jmethodID jlrConstructor_getParameterTypes; /* java.lang.reflect.Constructor.getParameterTypes() */
00127 jmethodID jlrConstructor_getModifiers;  /* java.lang.reflect.Constructor.getModifiers() */
00128 
00129 jmethodID jlrField_getName;             /* java.lang.reflect.Field.getName() */
00130 jmethodID jlrField_getType;             /* java.lang.reflect.Field.getType() */
00131 jmethodID jlrField_getModifiers;        /* java.lang.reflect.Field.getModifiers() */
00132 
00133 jmethodID jlrArray_newInstance;         /* java.lang.reflect.Array.newInstance() */
00134 
00135 jmethodID jlBoolean_Boolean;            /* java.lang.Boolean constructor */
00136 jmethodID jlBoolean_booleanValue;       /* java.lang.Boolean.booleanValue() */
00137 jmethodID jlDouble_Double;              /* java.lang.Double constructor */
00138 jmethodID jlDouble_doubleValue;         /* java.lang.Double.doubleValue() */
00139 
00140 jmethodID jlThrowable_toString;         /* java.lang.Throwable.toString() */
00141 jmethodID jlThrowable_getMessage;       /* java.lang.Throwable.getMessage() */
00142 
00143 jmethodID jlSystem_identityHashCode;    /* java.lang.System.identityHashCode() */
00144 
00145 jobject jlVoid_TYPE;                    /* java.lang.Void.TYPE value */
00146 
00147 jmethodID njJSException_JSException;    /* netscape.javascript.JSException constructor */
00148 jmethodID njJSException_JSException_wrap;/*netscape.javascript.JSException alternate constructor */
00149 jmethodID njJSObject_JSObject;          /* netscape.javascript.JSObject constructor */
00150 jmethodID njJSUtil_getStackTrace;       /* netscape.javascript.JSUtil.getStackTrace() */
00151 jfieldID njJSObject_internal;           /* netscape.javascript.JSObject.internal */
00152 jfieldID njJSObject_long_internal;      /* netscape.javascript.JSObject.long_internal */
00153 jfieldID njJSException_lineno;          /* netscape.javascript.JSException.lineno */
00154 jfieldID njJSException_tokenIndex;      /* netscape.javascript.JSException.tokenIndex */
00155 jfieldID njJSException_source;          /* netscape.javascript.JSException.source */
00156 jfieldID njJSException_filename;        /* netscape.javascript.JSException.filename */
00157 jfieldID njJSException_wrappedExceptionType;        /* netscape.javascript.JSException.wrappedExceptionType */
00158 jfieldID njJSException_wrappedException;        /* netscape.javascript.JSException.wrappedException */
00159 
00160 /* Obtain a reference to a Java class */
00161 #define LOAD_CLASS(qualified_name, class)                                    \
00162     {                                                                        \
00163         jclass _##class = (*jEnv)->FindClass(jEnv, #qualified_name);         \
00164         if (_##class == 0) {                                                 \
00165             (*jEnv)->ExceptionClear(jEnv);                                   \
00166             report_java_initialization_error(jEnv,                           \
00167                 "Can't load class " #qualified_name);                        \
00168             return JS_FALSE;                                                 \
00169         }                                                                    \
00170         class = (*jEnv)->NewGlobalRef(jEnv, _##class);                       \
00171         (*jEnv)->DeleteLocalRef(jEnv, _##class);                             \
00172     }
00173 
00174 /* Obtain a methodID reference to a Java method or constructor */
00175 #define _LOAD_METHOD(qualified_class, method, mvar, signature, class, is_static)\
00176     if (is_static) {                                                         \
00177         class##_##mvar =                                                     \
00178             (*jEnv)->GetStaticMethodID(jEnv, class, #method, signature);     \
00179     } else {                                                                 \
00180         class##_##mvar =                                                     \
00181             (*jEnv)->GetMethodID(jEnv, class, #method, signature);           \
00182     }                                                                        \
00183     if (class##_##mvar == 0) {                                               \
00184             (*jEnv)->ExceptionClear(jEnv);                                   \
00185         report_java_initialization_error(jEnv,                               \
00186                "Can't get mid for " #qualified_class "." #method "()");      \
00187         return JS_FALSE;                                                     \
00188     }
00189 
00190 /* Obtain a methodID reference to a Java instance method */
00191 #define LOAD_METHOD(qualified_class, method, signature, class)               \
00192     _LOAD_METHOD(qualified_class, method, method, signature, class, JS_FALSE)
00193 
00194 /* Obtain a methodID reference to a Java static method */
00195 #define LOAD_STATIC_METHOD(qualified_class, method, signature, class)        \
00196     _LOAD_METHOD(qualified_class, method, method, signature, class, JS_TRUE)
00197 
00198 /* Obtain a methodID reference to a Java constructor */
00199 #define LOAD_CONSTRUCTOR(qualified_class, method, signature, class)          \
00200     _LOAD_METHOD(qualified_class,<init>, method, signature, class, JS_FALSE)
00201 
00202 /* Obtain a fieldID reference to a Java instance or static field */
00203 #define _LOAD_FIELDID(qualified_class, field, signature, class, is_static)   \
00204     if (is_static) {                                                         \
00205         class##_##field = (*jEnv)->GetStaticFieldID(jEnv, class, #field, signature);\
00206     } else {                                                                 \
00207         class##_##field = (*jEnv)->GetFieldID(jEnv, class, #field, signature);\
00208     }                                                                        \
00209     if (class##_##field == 0) {                                              \
00210             (*jEnv)->ExceptionClear(jEnv);                                   \
00211         report_java_initialization_error(jEnv,                               \
00212                 "Can't get fid for " #qualified_class "." #field);           \
00213         return JS_FALSE;                                                     \
00214     }
00215 
00216 /* Obtain a fieldID reference to a Java instance field */
00217 #define LOAD_FIELDID(qualified_class, field, signature, class)               \
00218     _LOAD_FIELDID(qualified_class, field, signature, class, JS_FALSE)
00219 
00220 /* Obtain the value of a static field in a Java class */
00221 #define LOAD_FIELD_VAL(qualified_class, field, signature, class, type)       \
00222     {                                                                        \
00223         jfieldID field_id;                                                   \
00224         field_id = (*jEnv)->GetStaticFieldID(jEnv, class, #field, signature);\
00225         if (field_id == 0) {                                                 \
00226             report_java_initialization_error(jEnv,                           \
00227                 "Can't get fid for " #qualified_class "." #field);           \
00228             return JS_FALSE;                                                 \
00229         }                                                                    \
00230         class##_##field =                                                    \
00231             (*jEnv)->GetStatic##type##Field(jEnv, class, field_id);          \
00232         if (class##_##field == 0) {                                          \
00233             (*jEnv)->ExceptionClear(jEnv);                                   \
00234             report_java_initialization_error(jEnv,                           \
00235                 "Can't read static field " #qualified_class "." #field);     \
00236             return JS_FALSE;                                                 \
00237         }                                                                    \
00238     }
00239 
00240 /* Obtain the value of a static field in a Java class, which is known to
00241    contain an object value. */
00242 #define LOAD_FIELD_OBJ(qualified_class, field, signature, class)             \
00243     LOAD_FIELD_VAL(qualified_class, field, signature, class, Object);        \
00244     class##_##field = (*jEnv)->NewGlobalRef(jEnv, class##_##field);
00245 
00246 /*
00247  * Load the Java classes, and the method and field descriptors required for Java reflection.
00248  * Returns JS_TRUE on success, JS_FALSE on failure.
00249  */
00250 static JSBool
00251 init_java_VM_reflection(JSJavaVM *jsjava_vm, JNIEnv *jEnv)
00252 {
00253     /* Load Java system classes and method, including java.lang.reflect classes */
00254     LOAD_CLASS(java/lang/Object,                jlObject);
00255     LOAD_CLASS(java/lang/Class,                 jlClass);
00256     LOAD_CLASS(java/lang/reflect/Method,        jlrMethod);
00257     LOAD_CLASS(java/lang/reflect/Constructor,   jlrConstructor);
00258     LOAD_CLASS(java/lang/reflect/Field,         jlrField);
00259     LOAD_CLASS(java/lang/reflect/Array,         jlrArray);
00260     LOAD_CLASS(java/lang/Throwable,             jlThrowable);
00261     LOAD_CLASS(java/lang/System,                jlSystem);
00262     LOAD_CLASS(java/lang/Boolean,               jlBoolean);
00263     LOAD_CLASS(java/lang/Double,                jlDouble);
00264     LOAD_CLASS(java/lang/String,                jlString);
00265     LOAD_CLASS(java/lang/Void,                  jlVoid);
00266 
00267     LOAD_CLASS(java/applet/Applet,              jaApplet);
00268 
00269     LOAD_METHOD(java.lang.Class,            getMethods,         "()[Ljava/lang/reflect/Method;",jlClass);
00270     LOAD_METHOD(java.lang.Class,            getConstructors,    "()[Ljava/lang/reflect/Constructor;",jlClass);
00271     LOAD_METHOD(java.lang.Class,            getFields,          "()[Ljava/lang/reflect/Field;", jlClass);
00272     LOAD_METHOD(java.lang.Class,            getName,            "()Ljava/lang/String;",         jlClass);
00273     LOAD_METHOD(java.lang.Class,            isArray,            "()Z",                          jlClass);
00274     LOAD_METHOD(java.lang.Class,            getComponentType,   "()Ljava/lang/Class;",          jlClass);
00275     LOAD_METHOD(java.lang.Class,            getModifiers,       "()I",                          jlClass);
00276 
00277     LOAD_METHOD(java.lang.reflect.Method,   getName,            "()Ljava/lang/String;",         jlrMethod);
00278     LOAD_METHOD(java.lang.reflect.Method,   getParameterTypes,  "()[Ljava/lang/Class;",         jlrMethod);
00279     LOAD_METHOD(java.lang.reflect.Method,   getReturnType,      "()Ljava/lang/Class;",          jlrMethod);
00280     LOAD_METHOD(java.lang.reflect.Method,   getModifiers,       "()I",                          jlrMethod);
00281 
00282     LOAD_METHOD(java.lang.reflect.Constructor,  getParameterTypes,  "()[Ljava/lang/Class;",     jlrConstructor);
00283     LOAD_METHOD(java.lang.reflect.Constructor,  getModifiers,       "()I",                      jlrConstructor);
00284     
00285     LOAD_METHOD(java.lang.reflect.Field,    getName,            "()Ljava/lang/String;",         jlrField);
00286     LOAD_METHOD(java.lang.reflect.Field,    getType,            "()Ljava/lang/Class;",          jlrField);
00287     LOAD_METHOD(java.lang.reflect.Field,    getModifiers,       "()I",                          jlrField);
00288 
00289     LOAD_STATIC_METHOD(java.lang.reflect.Array,
00290                                             newInstance,        "(Ljava/lang/Class;I)Ljava/lang/Object;",jlrArray);
00291 
00292     LOAD_METHOD(java.lang.Throwable,        toString,           "()Ljava/lang/String;",         jlThrowable);
00293     LOAD_METHOD(java.lang.Throwable,        getMessage,         "()Ljava/lang/String;",         jlThrowable);
00294 
00295     LOAD_METHOD(java.lang.Double,           doubleValue,        "()D",                          jlDouble);
00296 
00297     LOAD_METHOD(java.lang.Boolean,          booleanValue,       "()Z",                          jlBoolean);
00298     
00299     LOAD_STATIC_METHOD(java.lang.System,    identityHashCode,   "(Ljava/lang/Object;)I",        jlSystem);
00300 
00301     LOAD_CONSTRUCTOR(java.lang.Boolean,     Boolean,            "(Z)V",                         jlBoolean);
00302     LOAD_CONSTRUCTOR(java.lang.Double,      Double,             "(D)V",                         jlDouble);
00303 
00304     LOAD_FIELD_OBJ(java.lang.Void,          TYPE,               "Ljava/lang/Class;",            jlVoid);
00305   
00306     return JS_TRUE;
00307 }
00308 
00309 #if !defined(OJI) 
00310 
00315 #include "netscape_javascript_JSObject.h"
00316 
00317 /* Manually load the required native methods. */
00318 static JSBool
00319 JSObject_RegisterNativeMethods(JNIEnv* jEnv)
00320 {
00321 
00322     static JNINativeMethod nativeMethods[] = {
00323         {"initClass", "()V", (void*)&Java_netscape_javascript_JSObject_initClass},
00324 
00325 #ifndef OJI
00326         {"getMember", "(Ljava/lang/String;)Ljava/lang/Object;", (void*)&Java_netscape_javascript_JSObject_getMember},
00327         {"getSlot", "(I)Ljava/lang/Object;", (void*)&Java_netscape_javascript_JSObject_getSlot},
00328         {"setMember", "(Ljava/lang/String;Ljava/lang/Object;)V", (void*)&Java_netscape_javascript_JSObject_setMember},
00329         {"setSlot", "(ILjava/lang/Object;)V", (void*)&Java_netscape_javascript_JSObject_setSlot},
00330         {"removeMember", "(Ljava/lang/String;)V", (void*)&Java_netscape_javascript_JSObject_removeMember},
00331         {"call", "(Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/Object;", (void*)&Java_netscape_javascript_JSObject_call},
00332         {"eval", "(Ljava/lang/String;)Ljava/lang/Object;", (void*)&Java_netscape_javascript_JSObject_eval},
00333         
00334         {"toString", "()Ljava/lang/String;", (void*)&Java_netscape_javascript_JSObject_toString},
00335         {"getWindow", "(Ljava/applet/Applet;)Lnetscape/javascript/JSObject;", (void*)&Java_netscape_javascript_JSObject_getWindow},
00336         {"finalize", "()V", (void*)&Java_netscape_javascript_JSObject_finalize},
00337         {"equals", "(Ljava/lang/Object;)Z", (void*)&Java_netscape_javascript_JSObject_equals}
00338 #endif  /* !OJI */
00339 
00340     };
00341     (*jEnv)->RegisterNatives(jEnv, njJSObject, nativeMethods, sizeof(nativeMethods) / sizeof(JNINativeMethod));
00342     if ((*jEnv)->ExceptionOccurred(jEnv)) {
00343         report_java_initialization_error(jEnv, "Couldn't initialize JSObject native methods.");
00344         (*jEnv)->ExceptionClear(jEnv);
00345         return JS_FALSE;
00346     }
00347     /* Call the initClass method */
00348     Java_netscape_javascript_JSObject_initClass(jEnv, njJSObject);
00349     return JS_TRUE;
00350 }
00351 
00352 #endif
00353 
00354 /* Load Netscape-specific Java extension classes, methods, and fields */
00355 static JSBool
00356 init_netscape_java_classes(JSJavaVM *jsjava_vm, JNIEnv *jEnv)
00357 {
00358     LOAD_CLASS(netscape/javascript/JSObject,    njJSObject);
00359     LOAD_CLASS(netscape/javascript/JSException, njJSException);
00360     LOAD_CLASS(netscape/javascript/JSUtil,      njJSUtil);
00361 
00362 #if !defined(OJI) 
00363     JSObject_RegisterNativeMethods(jEnv);
00364 #endif
00365 
00366 #ifndef OJI
00367     LOAD_CONSTRUCTOR(netscape.javascript.JSObject,
00368                                             JSObject,           "(I)V",                         njJSObject);
00369 #endif
00370     LOAD_CONSTRUCTOR(netscape.javascript.JSException,
00371                                             JSException,        "(Ljava/lang/String;Ljava/lang/String;ILjava/lang/String;I)V",
00372                                                                                                 njJSException);
00373 
00374     /* Load second constructor for wrapping JS exception objects inside JSExceptions */
00375     _LOAD_METHOD(netscape.javascript.JSException,<init>,
00376                  JSException_wrap, "(ILjava/lang/Object;)V",        
00377                  njJSException, JS_FALSE);
00378 
00379 #ifndef OJI
00380     LOAD_FIELDID(netscape.javascript.JSObject,  
00381                                             internal,           "I",                            njJSObject);
00382 #endif
00383     LOAD_FIELDID(netscape.javascript.JSException,  
00384                                             lineno,             "I",                            njJSException);
00385     LOAD_FIELDID(netscape.javascript.JSException,  
00386                                             tokenIndex,         "I",                            njJSException);
00387     LOAD_FIELDID(netscape.javascript.JSException,  
00388                                             source,             "Ljava/lang/String;",           njJSException);
00389     LOAD_FIELDID(netscape.javascript.JSException,  
00390                                             filename,           "Ljava/lang/String;",           njJSException);
00391     LOAD_FIELDID(netscape.javascript.JSException, wrappedExceptionType, "I",
00392                  njJSException);
00393     LOAD_FIELDID(netscape.javascript.JSException, wrappedException,
00394                  "Ljava/lang/Object;", njJSException);
00395 
00396     LOAD_STATIC_METHOD(netscape.javascript.JSUtil,
00397                                             getStackTrace,      "(Ljava/lang/Throwable;)Ljava/lang/String;",
00398                                                                                                 njJSUtil);
00399 
00400     return JS_TRUE;
00401 }
00402 
00403 JSJavaVM *jsjava_vm_list = NULL;
00404 
00405 static JSJavaThreadState *thread_list = NULL;
00406 
00407 #ifdef JSJ_THREADSAFE
00408 static PRMonitor *thread_list_monitor = NULL;
00409 #endif
00410 
00411 /*
00412  * Called once per Java VM, this function initializes the classes, fields, and
00413  * methods required for Java reflection.  If java_vm is NULL, a new Java VM is
00414  * created, using the provided classpath in addition to any default classpath.
00415  * The classpath argument is ignored, however, if java_vm_arg is non-NULL.
00416  */
00417 JSJavaVM *
00418 JSJ_ConnectToJavaVM(SystemJavaVM *java_vm_arg, void* initargs)
00419 {
00420     SystemJavaVM* java_vm;
00421     JSJavaVM *jsjava_vm;
00422     JNIEnv *jEnv;
00423 
00424     JS_ASSERT(JSJ_callbacks);
00425     JS_ASSERT(JSJ_callbacks->attach_current_thread);
00426     JS_ASSERT(JSJ_callbacks->detach_current_thread);
00427     JS_ASSERT(JSJ_callbacks->get_java_vm);
00428 
00429     jsjava_vm = (JSJavaVM*)malloc(sizeof(JSJavaVM));
00430     if (!jsjava_vm)
00431         return NULL;
00432     memset(jsjava_vm, 0, sizeof(JSJavaVM));
00433 
00434     java_vm = java_vm_arg;
00435 
00436     /* If a Java VM was passed in, try to attach to it on the current thread. */
00437     if (java_vm) {
00438         jEnv = JSJ_callbacks->attach_current_thread(java_vm);
00439         if (jEnv == NULL) {
00440             jsj_LogError("Failed to attach to Java VM thread\n");
00441             free(jsjava_vm);
00442             return NULL;
00443         }
00444 
00445         jsjava_vm->java_vm = java_vm;
00446         jsjava_vm->main_thread_env = jEnv;
00447     } else {
00448         jsjava_vm->init_args = initargs;
00449     }
00450        
00451 #ifdef JSJ_THREADSAFE
00452     if (jsjava_vm_list == NULL) {
00453         thread_list_monitor =
00454             (struct PRMonitor *) PR_NewMonitor();
00455     }
00456 #endif /* JSJ_THREADSAFE */
00457 
00458     /* Put this VM on the list of all created VMs */
00459     jsjava_vm->next = jsjava_vm_list;
00460     jsjava_vm_list = jsjava_vm;
00461 
00462     return jsjava_vm;
00463 }
00464 
00465 /* Completes a lazy connection to the host Java VM. */
00466 static JSBool
00467 jsj_ConnectToJavaVM(JSJavaVM *jsjava_vm)
00468 {
00469     if (!jsjava_vm->java_vm) {
00470         JSBool ok;
00471         JS_ASSERT(JSJ_callbacks->create_java_vm);
00472         JS_ASSERT(JSJ_callbacks->destroy_java_vm);
00473 
00474         ok = JSJ_callbacks->create_java_vm(&jsjava_vm->java_vm,
00475                                            &jsjava_vm->main_thread_env,
00476                                            jsjava_vm->init_args);
00477         if (!ok) {
00478             jsj_LogError("Failed to create Java VM\n");
00479             return JS_FALSE;
00480         }
00481 
00482         /* Remember that we created the VM so that we know to destroy it later */
00483         jsjava_vm->jsj_created_java_vm = JS_TRUE;
00484     }
00485     
00486     if (!jsjava_vm->jsj_inited_java_vm) {
00487         /*
00488          * JVM initialization for netscape.javascript.JSObject is performed
00489          * independently of the other classes that are initialized in
00490          * init_java_VM_reflection, because we allow it to fail.  In the case
00491          * of failure, LiveConnect is still operative, but only when calling
00492          * from JS to Java and not vice-versa.
00493          */
00494         init_netscape_java_classes(jsjava_vm, jsjava_vm->main_thread_env);
00495 
00496         /* Load the Java classes, and the method and field descriptors required for
00497            Java reflection. */
00498         if (!init_java_VM_reflection(jsjava_vm, jsjava_vm->main_thread_env) || 
00499             !jsj_InitJavaObjReflectionsTable()) {
00500             jsj_LogError("LiveConnect was unable to reflect one or more components of the Java runtime.\nGo to http://bugzilla.mozilla.org/show_bug.cgi?id=5369 for details.\n");
00501             /* This function crashes when called from here.
00502                Check that all the preconditions for this
00503                call are satisfied before making it. [jd]
00504                JSJ_DisconnectFromJavaVM(jsjava_vm); */
00505             return JS_FALSE;
00506         }
00507         
00508         jsjava_vm->jsj_inited_java_vm = JS_TRUE;
00509     }
00510 
00511     return JS_TRUE;
00512 }
00513 
00514 JSJCallbacks *JSJ_callbacks = NULL;
00515 
00516 /* Called once to set up callbacks for all instances of LiveConnect */
00517 void
00518 JSJ_Init(JSJCallbacks *callbacks)
00519 {
00520     JS_ASSERT(callbacks);
00521     JSJ_callbacks = callbacks;
00522 }
00523 
00524 /*
00525  * Initialize the provided JSContext by setting up the JS classes necessary for
00526  * reflection and by defining JavaPackage objects for the default Java packages
00527  * as properties of global_obj.  Additional packages may be pre-defined by
00528  * setting the predefined_packages argument.  (Pre-defining a Java package at
00529  * initialization time is not necessary, but it will make package lookup faster
00530  * and, more importantly, will avoid unnecessary network accesses if classes
00531  * are being loaded over the network.)
00532  */
00533 JSBool
00534 JSJ_InitJSContext(JSContext *cx, JSObject *global_obj,
00535                   JavaPackageDef *predefined_packages)
00536 {
00537     /* Initialize the JavaScript classes used for reflection */
00538     if (!jsj_init_JavaObject(cx, global_obj))
00539         return JS_FALSE;
00540     
00541 /*    if (!jsj_init_JavaMember(cx, global_obj))
00542         return JS_FALSE; */
00543     
00544     if (!jsj_init_JavaPackage(cx, global_obj, predefined_packages))
00545         return JS_FALSE;
00546 
00547     if (!jsj_init_JavaClass(cx, global_obj))
00548         return JS_FALSE;
00549 
00550     if (!jsj_init_JavaArray(cx, global_obj))
00551         return JS_FALSE;
00552 
00553     if (!jsj_init_JavaMember(cx, global_obj))
00554         return JS_FALSE;
00555     
00556     return JS_TRUE;
00557 }
00558 
00559 /* Eliminate a reference to a Java class */
00560 #define UNLOAD_CLASS(qualified_name, class)                                  \
00561     if (class) {                                                             \
00562         (*jEnv)->DeleteGlobalRef(jEnv, class);                               \
00563         class = NULL;                                                        \
00564     }
00565 
00566 /*
00567  * This routine severs the connection to a Java VM, freeing all related resources.
00568  * It shouldn't be called until the global scope has been cleared in all related
00569  * JSContexts (so that all LiveConnect objects are finalized) and a JavaScript
00570  * GC is performed.  Otherwise, accessed to free'ed memory could result.
00571  */
00572 void
00573 JSJ_DisconnectFromJavaVM(JSJavaVM *jsjava_vm)
00574 {
00575     SystemJavaVM *java_vm;
00576     JSJavaVM *j, **jp;
00577 
00578     /* Since JSJ_ConnectToJavaVM is now lazy */
00579     java_vm = jsjava_vm->java_vm;
00580     if (java_vm) {
00581         JNIEnv *jEnv = jsjava_vm->main_thread_env;
00582 
00583         /* Drop all references to Java objects and classes */
00584         jsj_DiscardJavaObjReflections(jEnv);
00585         jsj_DiscardJavaClassReflections(jEnv);
00586 
00587         if (jsjava_vm->jsj_created_java_vm) { 
00588             (void)JSJ_callbacks->destroy_java_vm(java_vm, jEnv);
00589         } else {
00590             UNLOAD_CLASS(java/lang/Object,                jlObject);
00591             UNLOAD_CLASS(java/lang/Class,                 jlClass);
00592             UNLOAD_CLASS(java/lang/reflect/Method,        jlrMethod);
00593             UNLOAD_CLASS(java/lang/reflect/Constructor,   jlrConstructor);
00594             UNLOAD_CLASS(java/lang/reflect/Field,         jlrField);
00595             UNLOAD_CLASS(java/lang/reflect/Array,         jlrArray);
00596             UNLOAD_CLASS(java/lang/Throwable,             jlThrowable);
00597             UNLOAD_CLASS(java/lang/System,                jlSystem);
00598             UNLOAD_CLASS(java/lang/Boolean,               jlBoolean);
00599             UNLOAD_CLASS(java/lang/Double,                jlDouble);
00600             UNLOAD_CLASS(java/lang/String,                jlString);
00601             UNLOAD_CLASS(java/lang/Void,                  jlVoid);
00602             UNLOAD_CLASS(java/applet/Applet,              jaApplet);
00603             UNLOAD_CLASS(netscape/javascript/JSObject,    njJSObject);
00604             UNLOAD_CLASS(netscape/javascript/JSException, njJSException);
00605             UNLOAD_CLASS(netscape/javascript/JSUtil,      njJSUtil);
00606         }
00607     }
00608 
00609     /* Remove this VM from the list of all JSJavaVM objects. */
00610     for (jp = &jsjava_vm_list; (j = *jp) != NULL; jp = &j->next) {
00611         if (j == jsjava_vm) {
00612             *jp = jsjava_vm->next;
00613             break;
00614         }
00615     }
00616     JS_ASSERT(j); /* vm not found in list */
00617 
00618 #ifdef JSJ_THREADSAFE
00619     if (jsjava_vm_list == NULL) {
00620         PR_DestroyMonitor(thread_list_monitor);
00621         thread_list_monitor = NULL;
00622     }
00623 #endif /* JSJ_THREADSAFE */
00624     
00625     free(jsjava_vm);
00626 }
00627 
00628 static JSJavaThreadState *
00629 new_jsjava_thread_state(JSJavaVM *jsjava_vm, const char *thread_name, JNIEnv *jEnv)
00630 {
00631     JSJavaThreadState *jsj_env;
00632 
00633     jsj_env = (JSJavaThreadState *)malloc(sizeof(JSJavaThreadState));
00634     if (!jsj_env)
00635         return NULL;
00636     memset(jsj_env, 0, sizeof(JSJavaThreadState));
00637 
00638     jsj_env->jEnv = jEnv;
00639     jsj_env->jsjava_vm = jsjava_vm;
00640     if (thread_name)
00641         jsj_env->name = strdup(thread_name);
00642 
00643 #ifdef JSJ_THREADSAFE
00644     PR_EnterMonitor(thread_list_monitor);
00645 #endif
00646 
00647     jsj_env->next = thread_list;
00648     thread_list = jsj_env;
00649 
00650 #ifdef JSJ_THREADSAFE
00651     PR_ExitMonitor(thread_list_monitor);
00652 #endif
00653 
00654     return jsj_env;
00655 }
00656 
00657 static JSJavaThreadState *
00658 find_jsjava_thread(JNIEnv *jEnv)
00659 {
00660     JSJavaThreadState *e, **p, *jsj_env;
00661     jsj_env = NULL;
00662 
00663 #ifdef JSJ_THREADSAFE
00664     PR_EnterMonitor(thread_list_monitor);
00665 #endif
00666 
00667     /* Search for the thread state among the list of all created
00668        LiveConnect threads */
00669     for (p = &thread_list; (e = *p) != NULL; p = &(e->next)) {
00670         if (e->jEnv == jEnv) {
00671             jsj_env = e;
00672             break;
00673         }
00674     }
00675 
00676     /* Move a found thread to head of list for faster search next time. */
00677     if (jsj_env && p != &thread_list) {
00678         *p = jsj_env->next;
00679         jsj_env->next = thread_list;
00680         thread_list = jsj_env;
00681     }
00682     
00683 #ifdef JSJ_THREADSAFE
00684     PR_ExitMonitor(thread_list_monitor);
00685 #endif
00686 
00687     return jsj_env;
00688 }
00689 
00690 JS_EXPORT_API(JSJavaThreadState *)
00691 JSJ_AttachCurrentThreadToJava(JSJavaVM *jsjava_vm, const char *name, JNIEnv **java_envp)
00692 {
00693     JNIEnv *jEnv;
00694     SystemJavaVM *java_vm;
00695     JSJavaThreadState *jsj_env;
00696     
00697     /* Make sure we're fully connected to the Java VM */
00698     if (!jsj_ConnectToJavaVM(jsjava_vm))
00699         return NULL;
00700 
00701     /* Try to attach a Java thread to the current native thread */
00702     java_vm = jsjava_vm->java_vm;
00703     if (JSJ_callbacks && JSJ_callbacks->attach_current_thread)
00704         jEnv = JSJ_callbacks->attach_current_thread(java_vm);
00705     else
00706         return NULL;
00707     if (jEnv == NULL) 
00708         return NULL;
00709 
00710     /* If we found an existing thread state, just return it. */
00711     jsj_env = find_jsjava_thread(jEnv);
00712     if (jsj_env)
00713         return jsj_env;
00714 
00715     /* Create a new wrapper around the thread/VM state */
00716     jsj_env = new_jsjava_thread_state(jsjava_vm, name, jEnv);
00717 
00718     if (java_envp)
00719         *java_envp = jEnv;
00720     return jsj_env;
00721 }
00722 
00723 static JSJavaVM *
00724 map_java_vm_to_jsjava_vm(SystemJavaVM *java_vm)
00725 {
00726     JSJavaVM *v;
00727     for (v = jsjava_vm_list; v; v = v->next) {
00728         if (!jsj_ConnectToJavaVM(v))
00729             return NULL;
00730         if (v->java_vm == java_vm)
00731             return v;
00732     }
00733     return NULL;
00734 }
00735 
00736 /*
00737  * Unfortunately, there's no standard means to associate any private data with
00738  * a JNI thread environment, so we need to use the Java environment pointer as
00739  * the key in a lookup table that maps it to a JSJavaThreadState structure, 
00740  * where we store all our per-thread private data.  If no existing thread state
00741  * is found, a new one is created.
00742  *
00743  * If an error occurs, returns NULL and sets the errp argument to an error
00744  * message, which the caller is responsible for free'ing.
00745  */
00746 JSJavaThreadState *
00747 jsj_MapJavaThreadToJSJavaThreadState(JNIEnv *jEnv, char **errp)
00748 {
00749     JSJavaThreadState *jsj_env;
00750     SystemJavaVM *java_vm;
00751     JSJavaVM *jsjava_vm;
00752 
00753     /* If we found an existing thread state, just return it. */
00754     jsj_env = find_jsjava_thread(jEnv);
00755     if (jsj_env)
00756         return jsj_env;
00757 
00758     /* No one set up a LiveConnect thread state for a given Java thread.
00759        Invoke the callback to create one on-the-fly. */
00760 
00761     /* First, figure out which Java VM is calling us */
00762     if (JSJ_callbacks && JSJ_callbacks->get_java_vm)
00763         java_vm = JSJ_callbacks->get_java_vm(jEnv);
00764     else
00765         return NULL;
00766     if (java_vm == NULL)
00767         return NULL;
00768 
00769     /* Get our private JavaVM data */
00770     jsjava_vm = map_java_vm_to_jsjava_vm(java_vm);
00771     if (!jsjava_vm) {
00772         *errp = JS_smprintf("Total weirdness:   No JSJavaVM wrapper ever created "
00773                             "for JavaVM 0x%08x", java_vm);
00774         return NULL;
00775     }
00776 
00777     jsj_env = new_jsjava_thread_state(jsjava_vm, NULL, jEnv);
00778     if (!jsj_env)
00779         return NULL;
00780 
00781     return jsj_env;
00782 }
00783 
00784 /*
00785  * This function is used to specify a particular JSContext as *the* JavaScript
00786  * execution environment to be used when LiveConnect is accessed from the given
00787  * Java thread, i.e. by using one of the methods of netscape.javascript.JSObject.
00788  * (There can only be one such JS context for a given Java thread.  To
00789  * multiplex JSContexts among a single thread, this function must be called
00790  * before Java is invoked on that thread.)  The return value is the previous
00791  * context associated with the given Java thread.
00792  */
00793 JS_EXPORT_API(JSContext *)
00794 JSJ_SetDefaultJSContextForJavaThread(JSContext *cx, JSJavaThreadState *jsj_env)
00795 {
00796     JSContext *old_context;
00797     old_context = jsj_env->cx;
00798     jsj_env->cx = cx;
00799 
00800     /* The following line prevents clearing of jsj_env->cx by jsj_ExitJava() */
00801     jsj_env->recursion_depth++;
00802     return old_context;
00803 }
00804 
00805 JS_EXPORT_API(JSBool)
00806 JSJ_DetachCurrentThreadFromJava(JSJavaThreadState *jsj_env)
00807 {
00808     SystemJavaVM *java_vm;
00809     JNIEnv* jEnv;
00810     JSJavaThreadState *e, **p;
00811 
00812     /* Disassociate the current native thread from its corresponding Java thread */
00813     java_vm = jsj_env->jsjava_vm->java_vm;
00814     jEnv = jsj_env->jEnv;
00815 
00816 #ifdef JSJ_THREADSAFE
00817     PR_EnterMonitor(thread_list_monitor);
00818 #endif /* JSJ_THREADSAFE */
00819 
00820     if (!JSJ_callbacks->detach_current_thread(java_vm, jEnv)) {
00821 
00822 #ifdef JSJ_THREADSAFE
00823         PR_ExitMonitor(thread_list_monitor);
00824 #endif /* JSJ_THREADSAFE */
00825 
00826         return JS_FALSE;
00827     }
00828 
00829     /* Destroy the LiveConnect execution environment passed in */
00830     jsj_ClearPendingJSErrors(jsj_env);
00831 
00832     for (p = &thread_list; (e = *p) != NULL; p = &(e->next)) {
00833         if (e == jsj_env) {
00834             *p = jsj_env->next;
00835             break;
00836         }
00837     }
00838 
00839     JS_ASSERT(e);
00840 
00841 #ifdef JSJ_THREADSAFE
00842     PR_ExitMonitor(thread_list_monitor);
00843 #endif /* JSJ_THREADSAFE */
00844 
00845     free(jsj_env);
00846     return JS_TRUE;
00847 }
00848 
00849 /* Utility routine to wrap a Java object inside a JS object, having a 
00850    a result type of either JavaObject or JavaArray. */
00851 JSBool
00852 JSJ_ConvertJavaObjectToJSValue(JSContext *cx, jobject java_obj, jsval *vp)
00853 {
00854     JNIEnv *jEnv;
00855     JSBool result;
00856     JSJavaThreadState *jsj_env;
00857             
00858     /* Get the Java per-thread environment pointer for this JSContext */
00859     jsj_env = jsj_EnterJava(cx, &jEnv);
00860     if (!jEnv)
00861         return JS_FALSE;
00862 
00863     result = jsj_ConvertJavaObjectToJSValue(cx, jEnv, java_obj, vp);
00864     jsj_ExitJava(jsj_env);
00865     return result;
00866 }
00867 
00868 
00869 JS_EXPORT_API(JSBool)
00870 JSJ_ConvertJSValueToJavaObject(JSContext *cx, jsval v, jobject *vp)
00871 {
00872     if (!JSVAL_IS_PRIMITIVE(v)) {
00873         JSObject *js_obj = JSVAL_TO_OBJECT(v);
00874         JavaObjectWrapper *java_wrapper = JS_GetPrivate(cx, js_obj);
00875         *vp = java_wrapper->java_obj;
00876         return JS_TRUE;
00877     }
00878     return JS_FALSE;
00879 }
00880 
00881 
00882 JS_EXPORT_API(JSBool)
00883 JSJ_IsJSCallApplet()
00884 {
00885     return jsj_JSIsCallingApplet;
00886 }