Back to index

lightning-sunbird  0.9+nobinonly
jsj_JSObject.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 /* This file is part of the Java-vendor-neutral implementation of LiveConnect
00041  *
00042  * It contains the implementation of the native methods for the 
00043  * netscape.javascript.JSObject Java class, which are used for calling into
00044  * JavaScript from Java.  It also contains the code that handles propagation
00045  * of exceptions both into and out of Java.
00046  *
00047  */
00048 
00049 #include <stdlib.h>
00050 #include <string.h>
00051 
00052 #include "jsj_private.h"
00053 #include "jsjava.h"
00054 
00055 #include "jscntxt.h"        /* For js_ReportErrorAgain().
00056                                TODO - get rid of private header */
00057 
00058 #include "netscape_javascript_JSObject.h"   /* javah-generated headers */
00059 
00060 
00061 /* A captured JavaScript error, created when JS_ReportError() is called while
00062    running JavaScript code that is itself called from Java. */
00063 struct CapturedJSError {
00064     char *              message;
00065     JSErrorReport       report;         /* Line # of error, etc. */
00066     jthrowable          java_exception; /* Java exception, error, or null */
00067     CapturedJSError *   next;           /* Next oldest captured JS error */
00068 };
00069 
00070 
00071 /*********************** Reflection of JSObjects ****************************/
00072 
00073 #ifdef PRESERVE_JSOBJECT_IDENTITY
00074 /*
00075  * This is a hash table that maps from JS objects to Java objects.
00076  * It is used to ensure that the same Java object results when a JS
00077  * object is reflected more than once, so that Java object equality
00078  * tests work in the expected manner.
00079  *
00080  * The entry keys are JSObject pointers and the entry values are Java objects
00081  * (of type jobject).  When the corresponding Java instance is finalized,
00082  * the entry is removed from the table, and a JavaScript GC root for the JS
00083  * object is removed.
00084  * 
00085  * This code is disabled because cycles in GC roots between the two systems
00086  * cause every reflected JS object to become uncollectable.  This can only
00087  * be solved by using weak links, a feature not available in JDK1.1.
00088  */
00089 static JSHashTable *js_obj_reflections = NULL;
00090 
00091 #ifdef JSJ_THREADSAFE
00092 /*
00093  * Used to protect the js_obj_reflections hashtable from simultaneous
00094  * read/write or * write/write access.
00095  */
00096 static PRMonitor *js_obj_reflections_monitor = NULL;
00097 #endif  /* JSJ_THREADSAFE */
00098 #endif  /* PRESERVE_JSOBJECT_IDENTITY */
00099 
00100 JSBool
00101 jsj_init_js_obj_reflections_table()
00102 {
00103 #ifdef PRESERVE_JSOBJECT_IDENTITY
00104     if(js_obj_reflections != NULL)
00105     {
00106       return JS_TRUE;
00107     }
00108     js_obj_reflections = JS_NewHashTable(128, NULL, JS_CompareValues,
00109                                          JS_CompareValues, NULL, NULL);
00110     if (!js_obj_reflections)
00111         return JS_FALSE;
00112 
00113 #ifdef JSJ_THREADSAFE
00114     js_obj_reflections_monitor = PR_NewMonitor();
00115     if (!js_obj_reflections_monitor) {
00116         JS_HashTableDestroy(js_obj_reflections);
00117         return JS_FALSE;
00118     }
00119 #endif  /* JSJ_THREADSAFE */
00120 #endif  /* PRESERVE_JSOBJECT_IDENTITY */
00121 
00122     return JS_TRUE;
00123 }
00124 
00125 /*
00126  * Return a Java object that "wraps" a given JS object by storing the address
00127  * of the JS object in a private field of the Java object.  A hash table is
00128  * used to ensure that the mapping of JS objects to Java objects is done on
00129  * one-to-one basis.
00130  *
00131  * If an error occurs, returns NULL and reports an error.
00132  */
00133 
00134 #ifdef PRESERVE_JSOBJECT_IDENTITY
00135 
00136 jobject
00137 jsj_WrapJSObject(JSContext *cx, JNIEnv *jEnv, JSObject *js_obj)
00138 {
00139     jobject java_wrapper_obj;
00140     JSHashEntry *he, **hep;
00141 
00142     java_wrapper_obj = NULL;
00143 
00144 #ifdef JSJ_THREADSAFE
00145     PR_EnterMonitor(js_obj_reflections_monitor);
00146 #endif
00147 
00148     /* First, look in the hash table for an existing reflection of the same
00149        JavaScript object.  If one is found, return it. */
00150     hep = JS_HashTableRawLookup(js_obj_reflections, (JSHashNumber)js_obj, js_obj);
00151 
00152     /* If the same JSObject is reflected into Java more than once then we should
00153        return the same Java object, both for efficiency and so that the '=='
00154        operator works as expected in Java when comparing two JSObjects.
00155        However, it is not possible to hold a reference to a Java object without
00156        inhibiting GC of that object, at least not in a portable way, i.e.
00157        a weak reference. So, for now, JSObject identity is broken. */
00158 
00159     he = *hep;
00160     if (he) {
00161         java_wrapper_obj = (jobject)he->value;
00162         JS_ASSERT(java_wrapper_obj);
00163         if (java_wrapper_obj)
00164             goto done;
00165     }
00166 
00167     /* No existing reflection found, so create a new Java object that wraps
00168        the JavaScript object by storing its address in a private integer field. */
00169 #ifndef OJI
00170     java_wrapper_obj =
00171         (*jEnv)->NewObject(jEnv, njJSObject, njJSObject_JSObject, (jint)js_obj);
00172 #else
00173     if (JSJ_callbacks && JSJ_callbacks->get_java_wrapper != NULL) {
00174         java_wrapper_obj = JSJ_callbacks->get_java_wrapper(jEnv, (jint)handle);
00175     }
00176 #endif 
00178     if (!java_wrapper_obj) {
00179         jsj_UnexpectedJavaError(cx, jEnv, "Couldn't create new instance of "
00180                                           "netscape.javascript.JSObject");
00181         goto done;
00182     }
00183 
00184     /* Add the new reflection to the hash table. */
00185     he = JS_HashTableRawAdd(js_obj_reflections, hep, (JSHashNumber)js_obj,
00186                             js_obj, java_wrapper_obj);
00187     if (he) {
00188         /* Tell the JavaScript GC about this object since the only reference
00189            to it may be in Java-land. */
00190         JS_AddNamedRoot(cx, (void*)&he->key, "&he->key");
00191     } else {
00192         JS_ReportOutOfMemory(cx);
00193         /* No need to delete java_wrapper_obj because Java GC will reclaim it */
00194         java_wrapper_obj = NULL;
00195     }
00196     
00197     /*
00198      * Release local reference to wrapper object, since some JVMs seem reticent
00199      * about collecting it otherwise.
00200      */
00201     /* FIXME -- beard: this seems to make calls into Java with JSObject's fail. */
00202     /* We should really be creating a global ref if we are putting it in a hash table. */
00203     /* (*jEnv)->DeleteLocalRef(jEnv, java_wrapper_obj); */
00204 
00205 done:
00206 #ifdef JSJ_THREADSAFE
00207         PR_ExitMonitor(js_obj_reflections_monitor);
00208 #endif
00209         
00210     return java_wrapper_obj;
00211 }
00212 
00213 /*
00214  * Remove the mapping of a JS object from the hash table that maps JS objects
00215  * to Java objects.  This is called from the finalizer of an instance of
00216  * netscape.javascript.JSObject.
00217  */
00218 JSBool
00219 jsj_remove_js_obj_reflection_from_hashtable(JSContext *cx, JSObject *js_obj)
00220 {
00221     JSHashEntry *he, **hep;
00222     JSBool success = JS_FALSE;
00223 
00224 #ifdef JSJ_THREADSAFE
00225     PR_EnterMonitor(js_obj_reflections_monitor);
00226 #endif
00227 
00228     /* Get the hash-table entry for this wrapper object */
00229     hep = JS_HashTableRawLookup(js_obj_reflections, (JSHashNumber)js_obj, js_obj);
00230     he = *hep;
00231 
00232     JS_ASSERT(he);
00233     if (he) {
00234         /* Tell the JS GC that Java no longer keeps a reference to this
00235            JS object. */
00236         success = JS_RemoveRoot(cx, (void *)&he->key);
00237 
00238         JS_HashTableRawRemove(js_obj_reflections, hep, he);
00239     }
00240 
00241 #ifdef JSJ_THREADSAFE
00242     PR_ExitMonitor(js_obj_reflections_monitor);
00243 #endif
00244 
00245     return success;
00246 }
00247 
00248 #else /* !PRESERVE_JSOBJECT_IDENTITY */
00249 
00250 
00251 /*
00252  * The caller must call DeleteLocalRef() on the returned object when no more
00253  * references remain.
00254  */
00255 jobject
00256 jsj_WrapJSObject(JSContext *cx, JNIEnv *jEnv, JSObject *js_obj)
00257 {
00258     jobject java_wrapper_obj;
00259     JSObjectHandle *handle;
00260 
00261     /* Create a tiny stub object to act as the GC root that points to the
00262        JSObject from its netscape.javascript.JSObject counterpart. */
00263     handle = (JSObjectHandle*)JS_malloc(cx, sizeof(JSObjectHandle));
00264     if (!handle)
00265         return NULL;
00266     handle->js_obj = js_obj;
00267     handle->rt = JS_GetRuntime(cx);
00268 
00269     /* Create a new Java object that wraps the JavaScript object by storing its
00270        address in a private integer field. */
00271 #ifndef OJI
00272     java_wrapper_obj =
00273         (*jEnv)->NewObject(jEnv, njJSObject, njJSObject_JSObject, (lcjsobject)handle);
00274 #else
00275     if (JSJ_callbacks && JSJ_callbacks->get_java_wrapper != NULL) {
00276         java_wrapper_obj = JSJ_callbacks->get_java_wrapper(jEnv, (lcjsobject)handle);
00277     } else  {
00278         java_wrapper_obj = NULL;
00279     }
00280 #endif 
00281     if (!java_wrapper_obj) {
00282         jsj_UnexpectedJavaError(cx, jEnv, "Couldn't create new instance of "
00283                                           "netscape.javascript.JSObject");
00284         goto done;
00285     }
00286  
00287     JS_AddNamedRoot(cx, &handle->js_obj, "&handle->js_obj");
00288 
00289 done:
00290         
00291     return java_wrapper_obj;
00292 }
00293 
00294 JSObject *
00295 jsj_UnwrapJSObjectWrapper(JNIEnv *jEnv, jobject java_wrapper_obj)
00296 {
00297     JSObjectHandle *handle;
00298 
00299 #ifndef OJI
00300 #if JS_BYTES_PER_LONG == 8
00301     handle = (JSObjectHandle*)((*jEnv)->GetLongField(jEnv, java_wrapper_obj, njJSObject_long_internal));
00302 #else
00303     handle = (JSObjectHandle*)((*jEnv)->GetIntField(jEnv, java_wrapper_obj, njJSObject_internal));
00304 #endif
00305 #else
00306     /* Unwrapping this wrapper requires knowledge of the structure of the object. This is privileged
00307        information that only the object implementor can know. In this case the object implementor
00308        is the java plugin (such as the Sun plugin class sun.plugin.javascript.navig5.JSObject. 
00309           Since the plugin owns this structure, we defer to it to unwrap the object. If the plugin 
00310           does not implement this callback, then it should be set to null. In that case we try something 
00311           that works with Sun's plugin assuming that it has not yet been implemented yet. This 'else' 
00312           case should be removed as soon as the unwrap function is supported by the Sun JPI. */
00313 
00314     if (JSJ_callbacks && JSJ_callbacks->unwrap_java_wrapper != NULL) {
00315         handle = (JSObjectHandle*)JSJ_callbacks->unwrap_java_wrapper(jEnv, java_wrapper_obj);
00316     }
00317     else {
00318         jclass   cid = (*jEnv)->GetObjectClass(jEnv, java_wrapper_obj);
00319 #if JS_BYTES_PER_LONG == 8
00320         jfieldID fid = (*jEnv)->GetFieldID(jEnv, cid, "nativeJSObject", "J");
00321         handle = (JSObjectHandle*)((*jEnv)->GetLongField(jEnv, java_wrapper_obj, fid));
00322 #else
00323         jfieldID fid = (*jEnv)->GetFieldID(jEnv, cid, "nativeJSObject", "I");
00324         handle = (JSObjectHandle*)((*jEnv)->GetIntField(jEnv, java_wrapper_obj, fid));
00325 #endif
00326     }
00327 #endif
00328     
00329        /* JNI returns a NULL handle for a Java 'null' */
00330     if (!handle)
00331         return NULL;
00332 
00333     return handle->js_obj;
00334        
00335 }
00336 
00337 #endif  /* !PRESERVE_JSOBJECT_IDENTITY */
00338 
00339 /*************** Handling of Java exceptions in JavaScript ******************/
00340 
00341 /*
00342  * Free up a JavaScript error that has been stored by the
00343  * capture_js_error_reports_for_java() function.
00344  */
00345 static CapturedJSError *
00346 destroy_saved_js_error(JNIEnv *jEnv, CapturedJSError *error)
00347 {
00348     CapturedJSError *next_error;
00349     if (!error)
00350         return NULL;
00351     next_error = error->next;
00352 
00353     if (error->java_exception)
00354         (*jEnv)->DeleteGlobalRef(jEnv, error->java_exception);
00355     if (error->message)
00356         free((char*)error->message);
00357     if (error->report.filename)
00358         free((char*)error->report.filename);
00359     if (error->report.linebuf)
00360         free((char*)error->report.linebuf);
00361     free(error);
00362 
00363     return next_error;
00364 }
00365 
00366 /*
00367  * Capture a JS error that has been reported using JS_ReportError() by JS code
00368  * that has itself been called from Java.  A copy of the JS error data is made
00369  * and hung off the JSJ environment.  When JS completes and returns to its Java
00370  * caller, this data is used to synthesize an instance of
00371  * netscape.javascript.JSException.  If the resulting JSException is not caught
00372  * within Java, it may be propagated up the stack beyond the Java caller back
00373  * into JavaScript, in which case the error will be re-reported as a JavaScript
00374  * error.
00375  */
00376 JS_STATIC_DLL_CALLBACK(void)
00377 capture_js_error_reports_for_java(JSContext *cx, const char *message,
00378                                   JSErrorReport *report)
00379 {
00380     CapturedJSError *new_error;
00381     JSJavaThreadState *jsj_env;
00382     jthrowable java_exception, tmp_exception;
00383     JNIEnv *jEnv;
00384 
00385     /* Warnings are not propagated as Java exceptions - they are simply
00386        ignored.  Ditto for exceptions that are duplicated in the form
00387        of error reports. */
00388     if (report && (report->flags & (JSREPORT_WARNING | JSREPORT_EXCEPTION)))
00389         return;
00390 
00391     /* Create an empty struct to hold the saved JS error state */
00392     new_error = malloc(sizeof(CapturedJSError));
00393     if (!new_error)
00394         goto out_of_memory;
00395     memset(new_error, 0, sizeof(CapturedJSError));
00396 
00397     /* Copy all the error info out of the original report into a private copy */
00398     if (message) {
00399         new_error->message = strdup(message);
00400         if (!new_error->message)
00401             goto out_of_memory;
00402     }
00403     if (report) {
00404         new_error->report.lineno = report->lineno;
00405 
00406         if (report->filename) {
00407             new_error->report.filename = strdup(report->filename);
00408             if (!new_error->report.filename)
00409                 goto out_of_memory;
00410         }
00411 
00412         if (report->linebuf) {
00413             new_error->report.linebuf = strdup(report->linebuf);
00414             if (!new_error->report.linebuf)
00415                 goto out_of_memory;
00416             new_error->report.tokenptr =
00417                 new_error->report.linebuf + (report->tokenptr - report->linebuf);
00418         }
00419     }
00420 
00421     /* Get the head of the list of pending JS errors */
00422     jsj_env = jsj_EnterJava(cx, &jEnv);
00423     if (!jsj_env)
00424         goto abort;
00425 
00426     /* If there's a Java exception associated with this error, save it too. */
00427     java_exception = (*jEnv)->ExceptionOccurred(jEnv);
00428     if (java_exception) {
00429         (*jEnv)->ExceptionClear(jEnv);
00430         tmp_exception = java_exception;     /* Make a copy */
00431         java_exception = (*jEnv)->NewGlobalRef(jEnv, java_exception);
00432         new_error->java_exception = java_exception;
00433         (*jEnv)->DeleteLocalRef(jEnv, tmp_exception);
00434     }
00435 
00436     /* Push this error onto the list of pending JS errors */
00437     new_error->next = jsj_env->pending_js_errors;
00438     jsj_env->pending_js_errors = new_error;
00439     jsj_ExitJava(jsj_env);
00440     return;
00441 
00442 abort:
00443 out_of_memory:
00444     /* No recovery action possible */
00445     JS_ASSERT(0);
00446     destroy_saved_js_error(jEnv, new_error);
00447     return;
00448 }
00449 
00450 void
00451 jsj_ClearPendingJSErrors(JSJavaThreadState *jsj_env)
00452 {
00453     while (jsj_env->pending_js_errors)
00454         jsj_env->pending_js_errors = destroy_saved_js_error(jsj_env->jEnv, jsj_env->pending_js_errors);
00455 }
00456 
00457 /*
00458  * This is called upon returning from JS back into Java.  Any JS errors
00459  * reported during that time will be converted into Java exceptions.  It's
00460  * possible that a JS error was actually triggered by Java at some point, in
00461  * which case the original Java exception is thrown.
00462  */
00463 static void
00464 throw_any_pending_js_error_as_a_java_exception(JSJavaThreadState *jsj_env)
00465 {
00466     CapturedJSError *error;
00467     JNIEnv *jEnv;
00468     jstring message_jstr, linebuf_jstr, filename_jstr;
00469     jint index, lineno;
00470     JSErrorReport *report;
00471     JSContext *cx;
00472     jsval pending_exception; 
00473     jobject java_obj;
00474     int dummy_cost;
00475     JSBool is_local_refp;
00476     JSType primitive_type;
00477     jthrowable java_exception;
00478 
00479     message_jstr = linebuf_jstr = filename_jstr = java_exception = NULL;
00480 
00481     /* Get the Java JNI environment */
00482     jEnv = jsj_env->jEnv;
00483 
00484     cx = jsj_env->cx;
00485     /* Get the pending JS exception if it exists */
00486     if (cx&&JS_IsExceptionPending(cx)) {
00487         if (!JS_GetPendingException(cx, &pending_exception))
00488             goto out_of_memory;
00489 
00490         /* Find out the JSTYPE of this jsval. */
00491         primitive_type = JS_TypeOfValue(cx, pending_exception);
00492         
00493         /* Convert jsval exception to a java object and then use it to
00494            create an instance of JSException. */ 
00495         if (!jsj_ConvertJSValueToJavaObject(cx, jEnv, 
00496                                             pending_exception, 
00497                                             jsj_get_jlObject_descriptor(cx, jEnv),
00498                                             &dummy_cost, &java_obj, 
00499                                             &is_local_refp))
00500             goto done;
00501         
00502         java_exception = (*jEnv)->NewObject(jEnv, njJSException, 
00503                                             njJSException_JSException_wrap,
00504                                             primitive_type, java_obj);
00505         if (is_local_refp)
00506             (*jEnv)->DeleteLocalRef(jEnv, java_obj);
00507         if (!java_exception) 
00508             goto out_of_memory;
00509         
00510         /* Throw the newly-created JSException */
00511         if ((*jEnv)->Throw(jEnv, java_exception) < 0) {
00512             JS_ASSERT(0);
00513             jsj_LogError("Couldn't throw JSException\n");
00514             goto done;
00515         }    
00516         JS_ClearPendingException(cx);
00517         return;
00518     }
00519     if (!jsj_env->pending_js_errors) {
00520 #ifdef DEBUG
00521         /* Any exception should be cleared as soon as it's detected, so there
00522            shouldn't be any pending. */
00523         if ((*jEnv)->ExceptionOccurred(jEnv)) {
00524             /* A Java exception occurred, but nobody in JS-land noticed. */
00525             JS_ASSERT(0);
00526             (*jEnv)->ExceptionClear(jEnv);
00527         }
00528 #endif
00529         return;
00530     }
00531 
00532     /* Get the deepest (oldest) saved JS error */
00533     /* XXX - What's the right thing to do about newer errors ?
00534        For now we just throw them away */
00535     error = jsj_env->pending_js_errors;
00536     while (error->next)
00537         error = error->next;
00538     
00539     /*
00540      * If the JS error was originally the result of a Java exception, rethrow
00541      * the original exception.
00542      */
00543     if (error->java_exception) {
00544         (*jEnv)->Throw(jEnv, error->java_exception);
00545         goto done;
00546     }
00547 
00548     /* Propagate any JS errors that did not originate as Java exceptions into
00549        Java as an instance of netscape.javascript.JSException */
00550 
00551     /* First, marshall the arguments to the JSException constructor */
00552     message_jstr = NULL;
00553     if (error->message) {
00554         message_jstr = (*jEnv)->NewStringUTF(jEnv, error->message);
00555         if (!message_jstr)
00556             goto out_of_memory;
00557     }
00558 
00559     report = &error->report;
00560 
00561     filename_jstr = NULL;
00562     if (report->filename) {
00563         filename_jstr = (*jEnv)->NewStringUTF(jEnv, report->filename);
00564         if (!filename_jstr)
00565             goto out_of_memory;
00566     }
00567 
00568     linebuf_jstr = NULL;
00569     if (report->linebuf) {
00570         linebuf_jstr = (*jEnv)->NewStringUTF(jEnv, report->linebuf);
00571         if (!linebuf_jstr)
00572             goto out_of_memory;
00573     }
00574 
00575     lineno = report->lineno;
00576     index = report->linebuf ? report->tokenptr - report->linebuf : 0;
00577 
00578     /* Call the JSException constructor */
00579     java_exception = (*jEnv)->NewObject(jEnv, njJSException, njJSException_JSException,
00580                                         message_jstr, filename_jstr, lineno, linebuf_jstr, index);
00581     if (!java_exception)
00582         goto out_of_memory;
00583 
00584     /* Throw the newly-created JSException */
00585     if ((*jEnv)->Throw(jEnv, java_exception) < 0) {
00586         JS_ASSERT(0);
00587         jsj_UnexpectedJavaError(cx, jEnv, "Couldn't throw JSException\n");
00588     }
00589     goto done;
00590 
00591 out_of_memory:
00592     /* No recovery possible */
00593     JS_ASSERT(0);
00594     jsj_LogError("Out of memory while attempting to throw JSException\n");
00595 
00596 done:
00597     jsj_ClearPendingJSErrors(jsj_env);
00598     /*
00599      * Release local references to Java objects, since some JVMs seem reticent
00600      * about collecting them otherwise.
00601      */
00602     if (message_jstr)
00603         (*jEnv)->DeleteLocalRef(jEnv, message_jstr);
00604     if (filename_jstr)
00605         (*jEnv)->DeleteLocalRef(jEnv, filename_jstr);
00606     if (linebuf_jstr)
00607         (*jEnv)->DeleteLocalRef(jEnv, linebuf_jstr);
00608     if (java_exception)
00609         (*jEnv)->DeleteLocalRef(jEnv, java_exception);
00610 }
00611 
00612 /*
00613  * This function is called up returning from Java back into JS as a result of
00614  * a thrown netscape.javascript.JSException, which itself must have been caused
00615  * by a JS error when Java called into JS.  The original JS error is
00616  * reconstituted from the JSException and re-reported as a JS error.
00617  *
00618  * Returns JS_FALSE if an internal error occurs, JS_TRUE otherwise.
00619  */
00620 JSBool
00621 jsj_ReportUncaughtJSException(JSContext *cx, JNIEnv *jEnv, jthrowable java_exception)
00622 {
00623     JSBool success;
00624     JSErrorReport report;
00625     const char *linebuf, *filename, *message, *tokenptr;
00626     jint lineno, token_index;
00627     jstring linebuf_jstr, filename_jstr, message_jstr;
00628 
00629     /* Initialize everything to NULL */
00630     memset(&report, 0, sizeof(JSErrorReport));
00631     success = JS_FALSE;
00632     filename_jstr = linebuf_jstr = message_jstr = NULL;
00633     filename = message = linebuf = tokenptr = NULL;
00634 
00635     lineno = (*jEnv)->GetIntField(jEnv, java_exception, njJSException_lineno);
00636     report.lineno = lineno;
00637 
00638     filename_jstr = (*jEnv)->GetObjectField(jEnv, java_exception, njJSException_filename);
00639     if ((*jEnv)->ExceptionOccurred(jEnv)) {
00640         jsj_UnexpectedJavaError(cx, jEnv, "Unable to access filename field of a JSException");
00641         goto done;
00642     }
00643     if (filename_jstr)
00644         filename = (*jEnv)->GetStringUTFChars(jEnv, filename_jstr, 0);
00645     report.filename = filename;
00646 
00647     linebuf_jstr = (*jEnv)->GetObjectField(jEnv, java_exception, njJSException_source);
00648     if ((*jEnv)->ExceptionOccurred(jEnv)) {
00649         jsj_UnexpectedJavaError(cx, jEnv, "Unable to access source field of a JSException");
00650         goto done;
00651     }
00652     if (linebuf_jstr)
00653         linebuf = (*jEnv)->GetStringUTFChars(jEnv, linebuf_jstr, 0);
00654     report.linebuf = linebuf;
00655 
00656     token_index = (*jEnv)->GetIntField(jEnv, java_exception, njJSException_lineno);
00657     report.tokenptr = linebuf + token_index;
00658 
00659     message_jstr = (*jEnv)->CallObjectMethod(jEnv, java_exception, jlThrowable_getMessage);
00660     if ((*jEnv)->ExceptionOccurred(jEnv)) {
00661         jsj_UnexpectedJavaError(cx, jEnv, "Unable to access message of a JSException");
00662         goto done;
00663     }
00664     if (message_jstr)
00665         message = (*jEnv)->GetStringUTFChars(jEnv, message_jstr, 0);
00666 
00667     js_ReportErrorAgain(cx, message, &report);
00668 
00669     success = JS_TRUE;
00670 
00671 done:
00672 
00673     if (filename_jstr && filename)
00674         (*jEnv)->ReleaseStringUTFChars(jEnv, filename_jstr, filename);
00675     if (linebuf_jstr && linebuf)
00676         (*jEnv)->ReleaseStringUTFChars(jEnv, linebuf_jstr, linebuf);
00677     if (message_jstr && message)
00678         (*jEnv)->ReleaseStringUTFChars(jEnv, message_jstr, message);
00679     return success;
00680 }
00681 
00682 
00683 
00684 /*************** Utilities for calling JavaScript from Java *****************/
00685 
00686 /*
00687  * This routine is called just prior to invoking any JS API function from
00688  * Java.  It performs a common set of chores, such as obtaining the LiveConnect
00689  * state for the invoking Java thread and determining the JSContext to be
00690  * used for this thread.
00691  *
00692  * Returns NULL on failure.
00693  */
00694 
00695 
00696 JSJavaThreadState *
00697 jsj_enter_js(JNIEnv *jEnv, void* applet_obj, jobject java_wrapper_obj,
00698              JSContext **cxp, JSObject **js_objp, JSErrorReporter *old_error_reporterp,
00699              void **pNSIPrincipaArray, int numPrincipals, void *pNSISecurityContext)
00700 {
00701     JSContext *cx;
00702     char *err_msg;
00703     JSObject *js_obj;
00704     JSJavaThreadState *jsj_env;
00705 
00706     cx = NULL;
00707     err_msg = NULL;
00708 
00709     /* Invoke callback, presumably used to implement concurrency constraints */
00710     if (JSJ_callbacks && JSJ_callbacks->enter_js_from_java) {
00711 #ifdef OJI
00712         if (!JSJ_callbacks->enter_js_from_java(jEnv, &err_msg, pNSIPrincipaArray, numPrincipals, pNSISecurityContext,applet_obj))
00713 #else
00714         if (!JSJ_callbacks->enter_js_from_java(jEnv, &err_msg))
00715 #endif
00716             goto entry_failure;
00717     }
00718 
00719     /* Check the JSObject pointer in the wrapper object. */
00720     if (js_objp) {
00721 
00722 #ifdef PRESERVE_JSOBJECT_IDENTITY
00723 #if JS_BYTES_PER_LONG == 8
00724         js_obj = (JSObject *)((*jEnv)->GetLongField(jEnv, java_wrapper_obj, njJSObject_long_internal));
00725 #else
00726         js_obj = (JSObject *)((*jEnv)->GetIntField(jEnv, java_wrapper_obj, njJSObject_internal));
00727 #endif
00728 #else   /* !PRESERVE_JSOBJECT_IDENTITY */
00729         js_obj = jsj_UnwrapJSObjectWrapper(jEnv, java_wrapper_obj);
00730 #endif  /* PRESERVE_JSOBJECT_IDENTITY */
00731 
00732         JS_ASSERT(js_obj);
00733         if (!js_obj)
00734             goto error;
00735         *js_objp = js_obj;
00736     }
00737 
00738     /* Get the per-thread state corresponding to the current Java thread */
00739     jsj_env = jsj_MapJavaThreadToJSJavaThreadState(jEnv, &err_msg);
00740     if (!jsj_env)
00741         goto error;
00742 
00743     /* Get the JSContext that we're supposed to use for this Java thread */
00744     cx = jsj_env->cx;
00745     if (!cx) {
00746         /* We called spontaneously into JS from Java, rather than from JS into
00747            Java and back into JS.  Invoke a callback to obtain/create a
00748            JSContext for us to use. */
00749         if (JSJ_callbacks && JSJ_callbacks->map_jsj_thread_to_js_context) {
00750 #ifdef OJI
00751             cx = JSJ_callbacks->map_jsj_thread_to_js_context(jsj_env,
00752                                                              applet_obj,
00753                                                              jEnv, &err_msg);
00754 #else
00755             cx = JSJ_callbacks->map_jsj_thread_to_js_context(jsj_env,
00756                                                              jEnv, &err_msg);
00757 #endif
00758             if (!cx)
00759                 goto error;
00760         } else {
00761             err_msg = JS_smprintf("Unable to find/create JavaScript execution "
00762                                   "context for JNI thread 0x%08x", jEnv);
00763             goto error;
00764         }
00765     }
00766     *cxp = cx;
00767 
00768     /*
00769      * Capture all JS error reports so that they can be thrown into the Java
00770      * caller as an instance of netscape.javascript.JSException.
00771      */
00772     *old_error_reporterp =
00773         JS_SetErrorReporter(cx, capture_js_error_reports_for_java);
00774 
00775 #ifdef JSJ_THREADSAFE
00776     JS_BeginRequest(cx);
00777 #endif
00778 
00779     return jsj_env;
00780 
00781 error:
00782     /* Invoke callback, presumably used to implement concurrency constraints */
00783     if (JSJ_callbacks && JSJ_callbacks->exit_js)
00784         JSJ_callbacks->exit_js(jEnv, cx);
00785 
00786 entry_failure:
00787     if (err_msg) {
00788         if (cx)
00789             JS_ReportError(cx, err_msg);
00790         else
00791             jsj_LogError(err_msg);
00792         free(err_msg);
00793     }
00794 
00795     return NULL;
00796 }
00797 
00798 /*
00799  * This utility function is called just prior to returning into Java from JS.
00800  */
00801 JSBool
00802 jsj_exit_js(JSContext *cx, JSJavaThreadState *jsj_env, JSErrorReporter original_reporter)
00803 {
00804     JNIEnv *jEnv;
00805 
00806 #ifdef JSJ_THREADSAFE
00807     JS_EndRequest(cx);
00808 #endif
00809 
00810     /* Restore the JS error reporter */
00811     JS_SetErrorReporter(cx, original_reporter);
00812 
00813     jEnv = jsj_env->jEnv;
00814 
00815 #ifdef DEBUG
00816     /* Any Java exceptions should have been noticed and reported already */
00817     if ((*jEnv)->ExceptionOccurred(jEnv)) {
00818         JS_ASSERT(0);
00819         jsj_LogError("Unhandled Java exception detected");
00820         return JS_FALSE;
00821     }
00822 #endif
00823 
00824     /*
00825      * Convert reported JS errors to JSExceptions, unless the errors were
00826      * themselves the result of Java exceptions, in which case the original
00827      * Java exception is simply propagated.
00828      */
00829     throw_any_pending_js_error_as_a_java_exception(jsj_env);
00830 
00831     /* Invoke callback, presumably used to implement concurrency constraints */
00832     if (JSJ_callbacks && JSJ_callbacks->exit_js)
00833         JSJ_callbacks->exit_js(jEnv, cx);
00834 
00835     return JS_TRUE;
00836 }
00837 
00838 
00839 /* Get the JavaClassDescriptor that corresponds to java.lang.Object */
00840 JavaClassDescriptor *
00841 jsj_get_jlObject_descriptor(JSContext *cx, JNIEnv *jEnv)
00842 {
00843     /* The JavaClassDescriptor for java.lang.Object */
00844     static JavaClassDescriptor *jlObject_descriptor = NULL;
00845 
00846     if (!jlObject_descriptor)
00847         jlObject_descriptor = jsj_GetJavaClassDescriptor(cx, jEnv, jlObject);
00848     return jlObject_descriptor;
00849 }
00850 
00851 /****************** Implementation of methods of JSObject *******************/
00852 
00853 /*
00854  * Class:     netscape_javascript_JSObject
00855  * Method:    initClass
00856  * Signature: ()V
00857  */
00858 JNIEXPORT void JNICALL
00859 Java_netscape_javascript_JSObject_initClass(JNIEnv *jEnv, jclass java_class)
00860 {
00861     jsj_init_js_obj_reflections_table();
00862 }
00863 
00864 /*
00865  * Class:     netscape_javascript_JSObject
00866  * Method:    getMember
00867  * Signature: (Ljava/lang/String;)Ljava/lang/Object;
00868  */
00869 JNIEXPORT jobject JNICALL
00870 Java_netscape_javascript_JSObject_getMember(JNIEnv *jEnv,
00871                                             jobject java_wrapper_obj,
00872                                             jstring property_name_jstr)
00873 {
00874     JSContext *cx = NULL;
00875     JSObject *js_obj;
00876     jsval js_val;
00877     int dummy_cost;
00878     JSBool dummy_bool;
00879     const jchar *property_name_ucs2;
00880     jsize property_name_len;
00881     JSErrorReporter saved_reporter;
00882     jobject member;
00883     jboolean is_copy;
00884     JSJavaThreadState *jsj_env;
00885     
00886     jsj_env = jsj_enter_js(jEnv, NULL, java_wrapper_obj, &cx, &js_obj, &saved_reporter, NULL, 0, NULL);
00887     if (!jsj_env)
00888         return NULL;
00889 
00890     property_name_ucs2 = NULL;
00891     if (!property_name_jstr) {
00892         JS_ReportErrorNumber(cx, jsj_GetErrorMessage, NULL,
00893                              JSJMSG_NULL_MEMBER_NAME);
00894         member = NULL;
00895         goto done;
00896     }
00897 
00898     /* Get the Unicode string for the JS property name */
00899     property_name_ucs2 = (*jEnv)->GetStringChars(jEnv, property_name_jstr, &is_copy);
00900     if (!property_name_ucs2) {
00901         JS_ASSERT(0);
00902         goto done;
00903     }
00904     property_name_len = (*jEnv)->GetStringLength(jEnv, property_name_jstr);
00905     
00906     if (!JS_GetUCProperty(cx, js_obj, property_name_ucs2, property_name_len, &js_val))
00907         goto done;
00908 
00909     jsj_ConvertJSValueToJavaObject(cx, jEnv, js_val, 
00910                                    jsj_get_jlObject_descriptor(cx, jEnv),
00911                                    &dummy_cost, &member, &dummy_bool);
00912 
00913 done:
00914     if (property_name_ucs2)
00915         (*jEnv)->ReleaseStringChars(jEnv, property_name_jstr, property_name_ucs2);
00916     if (!jsj_exit_js(cx, jsj_env, saved_reporter))
00917         return NULL;
00918     
00919     return member;
00920 }
00921 
00922 /*
00923  * Class:     netscape_javascript_JSObject
00924  * Method:    getSlot
00925  * Signature: (I)Ljava/lang/Object;
00926  */
00927 JNIEXPORT jobject JNICALL
00928 Java_netscape_javascript_JSObject_getSlot(JNIEnv *jEnv,
00929                                           jobject java_wrapper_obj,
00930                                           jint slot)
00931 {
00932     JSContext *cx = NULL;
00933     JSObject *js_obj;
00934     jsval js_val;
00935     int dummy_cost;
00936     JSBool dummy_bool;
00937     JSErrorReporter saved_reporter;
00938     jobject member;
00939     JSJavaThreadState *jsj_env;
00940     
00941     jsj_env = jsj_enter_js(jEnv, NULL, java_wrapper_obj, &cx, &js_obj, &saved_reporter, NULL, 0, NULL);
00942     if (!jsj_env)
00943         return NULL;
00944     
00945 
00946     if (!JS_GetElement(cx, js_obj, slot, &js_val))
00947         goto done;
00948     if (!jsj_ConvertJSValueToJavaObject(cx, jEnv, js_val, jsj_get_jlObject_descriptor(cx, jEnv),
00949                                         &dummy_cost, &member, &dummy_bool))
00950         goto done;
00951 
00952 done:
00953     if (!jsj_exit_js(cx, jsj_env, saved_reporter))
00954         return NULL;
00955     
00956     return member;
00957 }
00958 
00959 /*
00960  * Class:     netscape_javascript_JSObject
00961  * Method:    setMember
00962  * Signature: (Ljava/lang/String;Ljava/lang/Object;)V
00963  */
00964 JNIEXPORT void JNICALL
00965 Java_netscape_javascript_JSObject_setMember(JNIEnv *jEnv,
00966                                             jobject java_wrapper_obj,
00967                                             jstring property_name_jstr,
00968                                             jobject java_obj)
00969 {
00970     JSContext *cx = NULL;
00971     JSObject *js_obj;
00972     jsval js_val;
00973     const jchar *property_name_ucs2;
00974     jsize property_name_len;
00975     JSErrorReporter saved_reporter;
00976     jboolean is_copy;
00977     JSJavaThreadState *jsj_env;
00978     
00979     jsj_env = jsj_enter_js(jEnv, NULL, java_wrapper_obj, &cx, &js_obj, &saved_reporter, NULL, 0, NULL);
00980     if (!jsj_env)
00981         return;
00982     
00983     property_name_ucs2 = NULL;
00984     if (!property_name_jstr) {
00985         JS_ReportErrorNumber(cx, jsj_GetErrorMessage, NULL,
00986                                             JSJMSG_NULL_MEMBER_NAME);
00987         goto done;
00988     }
00989 
00990     /* Get the Unicode string for the JS property name */
00991     property_name_ucs2 = (*jEnv)->GetStringChars(jEnv, property_name_jstr, &is_copy);
00992     if (!property_name_ucs2) {
00993         JS_ASSERT(0);
00994         goto done;
00995     }
00996     property_name_len = (*jEnv)->GetStringLength(jEnv, property_name_jstr);
00997     
00998     if (!jsj_ConvertJavaObjectToJSValue(cx, jEnv, java_obj, &js_val))
00999         goto done;
01000 
01001     JS_SetUCProperty(cx, js_obj, property_name_ucs2, property_name_len, &js_val);
01002 
01003 done:
01004     if (property_name_ucs2)
01005         (*jEnv)->ReleaseStringChars(jEnv, property_name_jstr, property_name_ucs2);
01006     jsj_exit_js(cx, jsj_env, saved_reporter);
01007 }
01008 
01009 /*
01010  * Class:     netscape_javascript_JSObject
01011  * Method:    setSlot
01012  * Signature: (ILjava/lang/Object;)V
01013  */
01014 JNIEXPORT void JNICALL
01015 Java_netscape_javascript_JSObject_setSlot(JNIEnv *jEnv,
01016                                           jobject java_wrapper_obj,
01017                                           jint slot,
01018                                           jobject java_obj)
01019 {
01020     JSContext *cx = NULL;
01021     JSObject *js_obj;
01022     jsval js_val;
01023     JSErrorReporter saved_reporter;
01024     JSJavaThreadState *jsj_env;
01025     
01026     jsj_env = jsj_enter_js(jEnv, NULL, java_wrapper_obj, &cx, &js_obj, &saved_reporter, NULL, 0, NULL);
01027     if (!jsj_env)
01028         return;
01029     
01030     if (!jsj_ConvertJavaObjectToJSValue(cx, jEnv, java_obj, &js_val))
01031         goto done;
01032     JS_SetElement(cx, js_obj, slot, &js_val);
01033 
01034 done:
01035     jsj_exit_js(cx, jsj_env, saved_reporter);
01036 }
01037 
01038 /*
01039  * Class:     netscape_javascript_JSObject
01040  * Method:    removeMember
01041  * Signature: (Ljava/lang/String;)V
01042  */
01043 JNIEXPORT void JNICALL
01044 Java_netscape_javascript_JSObject_removeMember(JNIEnv *jEnv,
01045                                                jobject java_wrapper_obj,
01046                                                jstring property_name_jstr)
01047 {
01048     JSContext *cx = NULL;
01049     JSObject *js_obj;
01050     jsval js_val;
01051     const jchar *property_name_ucs2;
01052     jsize property_name_len;
01053     JSErrorReporter saved_reporter;
01054     jboolean is_copy;
01055     JSJavaThreadState *jsj_env;
01056     
01057     jsj_env = jsj_enter_js(jEnv, NULL, java_wrapper_obj, &cx, &js_obj, &saved_reporter, NULL, 0, NULL);
01058     if (!jsj_env)
01059         return;
01060     
01061     if (!property_name_jstr) {
01062         JS_ReportErrorNumber(cx, jsj_GetErrorMessage, NULL,
01063                                             JSJMSG_NULL_MEMBER_NAME);
01064         goto done;
01065     }
01066     /* Get the Unicode string for the JS property name */
01067     property_name_ucs2 = (*jEnv)->GetStringChars(jEnv, property_name_jstr, &is_copy);
01068     if (!property_name_ucs2) {
01069         JS_ASSERT(0);
01070         goto done;
01071     }
01072     property_name_len = (*jEnv)->GetStringLength(jEnv, property_name_jstr);
01073     
01074     JS_DeleteUCProperty2(cx, js_obj, property_name_ucs2, property_name_len, &js_val);
01075 
01076     (*jEnv)->ReleaseStringChars(jEnv, property_name_jstr, property_name_ucs2);
01077 
01078 done:
01079     jsj_exit_js(cx, jsj_env, saved_reporter);
01080     return;
01081 }
01082 
01083 /*
01084  * Class:     netscape_javascript_JSObject
01085  * Method:    call
01086  * Signature: (Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/Object;
01087  */
01088 JNIEXPORT jobject JNICALL
01089 Java_netscape_javascript_JSObject_call(JNIEnv *jEnv, jobject java_wrapper_obj,
01090                                        jstring function_name_jstr, jobjectArray java_args)
01091 {
01092     int i, argc, arg_num;
01093     jsval *argv;
01094     JSContext *cx = NULL;
01095     JSObject *js_obj;
01096     jsval js_val, function_val;
01097     int dummy_cost;
01098     JSBool dummy_bool;
01099     const jchar *function_name_ucs2;
01100     jsize function_name_len;
01101     JSErrorReporter saved_reporter;
01102     jboolean is_copy;
01103     jobject result;
01104     JSJavaThreadState *jsj_env;
01105     
01106     jsj_env = jsj_enter_js(jEnv, NULL, java_wrapper_obj, &cx, &js_obj, &saved_reporter, NULL, 0, NULL);
01107     if (!jsj_env)
01108         return NULL;
01109     
01110     function_name_ucs2 = NULL;
01111     result = NULL;
01112     if (!function_name_jstr) {
01113         JS_ReportErrorNumber(cx, jsj_GetErrorMessage, NULL,
01114                                                     JSJMSG_NULL_FUNCTION_NAME);
01115         goto done;
01116     }
01117 
01118     /* Get the function name to eval as raw Unicode characters */
01119     function_name_ucs2 = (*jEnv)->GetStringChars(jEnv, function_name_jstr, &is_copy);
01120     if (!function_name_ucs2) {
01121         JS_ASSERT(0);
01122         goto done;
01123     }
01124     function_name_len = (*jEnv)->GetStringLength(jEnv, function_name_jstr);
01125     
01126     /* Allocate space for JS arguments */
01127     if (java_args) {
01128         argc = (*jEnv)->GetArrayLength(jEnv, java_args);
01129         argv = (jsval*)JS_malloc(cx, argc * sizeof(jsval));
01130     } else {
01131         argc = 0;
01132         argv = 0;
01133     }
01134 
01135     /* Convert arguments from Java to JS values */
01136     for (arg_num = 0; arg_num < argc; arg_num++) {
01137         jobject arg = (*jEnv)->GetObjectArrayElement(jEnv, java_args, arg_num);
01138 
01139         if (!jsj_ConvertJavaObjectToJSValue(cx, jEnv, arg, &argv[arg_num]))
01140             goto cleanup_argv;
01141         JS_AddNamedRoot(cx, &argv[arg_num], "&argv[arg_num]");
01142     }
01143 
01144     if (!JS_GetUCProperty(cx, js_obj, function_name_ucs2, function_name_len,
01145                           &function_val))
01146         goto cleanup_argv;
01147 
01148     if (!JS_CallFunctionValue(cx, js_obj, function_val, argc, argv, &js_val))
01149         goto cleanup_argv;
01150 
01151     jsj_ConvertJSValueToJavaObject(cx, jEnv, js_val, jsj_get_jlObject_descriptor(cx, jEnv),
01152                                    &dummy_cost, &result, &dummy_bool);
01153 
01154 cleanup_argv:
01155     if (argv) {
01156         for (i = 0; i < arg_num; i++)
01157             JS_RemoveRoot(cx, &argv[i]);
01158         JS_free(cx, argv);
01159     }
01160 
01161 done:
01162     if (function_name_ucs2)
01163         (*jEnv)->ReleaseStringChars(jEnv, function_name_jstr, function_name_ucs2);
01164     if (!jsj_exit_js(cx, jsj_env, saved_reporter))
01165         return NULL;
01166     
01167     return result;
01168 }
01169 
01170 /*
01171  * Class:     netscape_javascript_JSObject
01172  * Method:    eval
01173  * Signature: (Ljava/lang/String;)Ljava/lang/Object;
01174  */
01175 JNIEXPORT jobject JNICALL
01176 Java_netscape_javascript_JSObject_eval(JNIEnv *jEnv,
01177                                        jobject java_wrapper_obj,
01178                                        jstring eval_jstr)
01179 {
01180     const char *codebase;
01181     JSPrincipals *principals;
01182     JSContext *cx = NULL;
01183     JSBool eval_succeeded;
01184     JSObject *js_obj;
01185     jsval js_val;
01186     int dummy_cost;
01187     JSBool dummy_bool;
01188     const jchar *eval_ucs2;
01189     jsize eval_len;
01190     JSErrorReporter saved_reporter;
01191     jboolean is_copy;
01192     jobject result;
01193     JSJavaThreadState *jsj_env;
01194     
01195     jsj_env = jsj_enter_js(jEnv, NULL, java_wrapper_obj, &cx, &js_obj, &saved_reporter, NULL, 0, NULL);
01196     if (!jsj_env)
01197         return NULL;
01198     
01199     result = NULL;
01200     eval_ucs2 = NULL;
01201     if (!eval_jstr) {
01202         JS_ReportErrorNumber(cx, jsj_GetErrorMessage, NULL, 
01203                                                 JSJMSG_NULL_EVAL_ARG);
01204         goto done;
01205     }
01206 
01207     /* Get the string to eval as raw Unicode characters */
01208     eval_ucs2 = (*jEnv)->GetStringChars(jEnv, eval_jstr, &is_copy);
01209     if (!eval_ucs2) {
01210         JS_ASSERT(0);
01211         goto done;
01212     }
01213     eval_len = (*jEnv)->GetStringLength(jEnv, eval_jstr);
01214     
01215     /* Set up security stuff */
01216     principals = NULL;
01217     if (JSJ_callbacks && JSJ_callbacks->get_JSPrincipals_from_java_caller)
01218         principals = JSJ_callbacks->get_JSPrincipals_from_java_caller(jEnv, cx, NULL, 0, NULL);
01219     codebase = principals ? principals->codebase : NULL;
01220 
01221     /* Have the JS engine evaluate the unicode string */
01222     eval_succeeded = JS_EvaluateUCScriptForPrincipals(cx, js_obj, principals,
01223                                                       eval_ucs2, eval_len,
01224                                                       codebase, 0, &js_val);
01225     if (!eval_succeeded)
01226         goto done;
01227 
01228     /* Convert result to a subclass of java.lang.Object */
01229     jsj_ConvertJSValueToJavaObject(cx, jEnv, js_val, jsj_get_jlObject_descriptor(cx, jEnv),
01230                                    &dummy_cost, &result, &dummy_bool);
01231 
01232 done:
01233     if (eval_ucs2)
01234         (*jEnv)->ReleaseStringChars(jEnv, eval_jstr, eval_ucs2);
01235     if (!jsj_exit_js(cx, jsj_env, saved_reporter))
01236         return NULL;
01237     
01238     return result;
01239 }
01240 
01241 /*
01242  * Class:     netscape_javascript_JSObject
01243  * Method:    toString
01244  * Signature: ()Ljava/lang/String;
01245  */
01246 JNIEXPORT jstring JNICALL
01247 Java_netscape_javascript_JSObject_toString(JNIEnv *jEnv,
01248                                            jobject java_wrapper_obj)
01249 {
01250     jstring result;
01251     JSContext *cx = NULL;
01252     JSObject *js_obj;
01253     JSString *jsstr;
01254     JSErrorReporter saved_reporter;
01255     JSJavaThreadState *jsj_env;
01256     
01257     jsj_env = jsj_enter_js(jEnv, NULL, java_wrapper_obj, &cx, &js_obj, &saved_reporter, NULL, 0, NULL);
01258     if (!jsj_env)
01259         return NULL;
01260     
01261     result = NULL;
01262     jsstr = JS_ValueToString(cx, OBJECT_TO_JSVAL(js_obj));
01263     if (jsstr)
01264         result = jsj_ConvertJSStringToJavaString(cx, jEnv, jsstr);
01265     if (!result)
01266         result = (*jEnv)->NewStringUTF(jEnv, "*JavaObject*");
01267 
01268     if (!jsj_exit_js(cx, jsj_env, saved_reporter))
01269         return NULL;
01270     
01271     return result;
01272 }
01273 
01274 /*
01275  * Class:     netscape_javascript_JSObject
01276  * Method:    getWindow
01277  * Signature: (Ljava/applet/Applet;)Lnetscape/javascript/JSObject;
01278  */
01279 JNIEXPORT jobject JNICALL
01280 Java_netscape_javascript_JSObject_getWindow(JNIEnv *jEnv,
01281                                             jclass js_object_class,
01282                                             jobject java_applet_obj)
01283 {
01284     char *err_msg;
01285     JSContext *cx = NULL;
01286     JSObject *js_obj = NULL;
01287     jsval js_val;
01288     int dummy_cost;
01289     JSBool dummy_bool;
01290     JSErrorReporter saved_reporter;
01291     jobject java_obj;
01292     JSJavaThreadState *jsj_env;
01293     
01294     jsj_env = jsj_enter_js(jEnv, java_applet_obj, NULL, &cx, NULL, &saved_reporter, NULL, 0, NULL);
01295     if (!jsj_env)
01296         return NULL;
01297     
01298     err_msg = NULL;
01299     java_obj = NULL;
01300     if (JSJ_callbacks && JSJ_callbacks->map_java_object_to_js_object)
01301         js_obj = JSJ_callbacks->map_java_object_to_js_object(jEnv, java_applet_obj, &err_msg);
01302     if (!js_obj) {
01303         if (err_msg) {
01304             JS_ReportError(cx, err_msg);
01305             free(err_msg);
01306         }
01307         goto done;
01308     }
01309     js_val = OBJECT_TO_JSVAL(js_obj);
01310     jsj_ConvertJSValueToJavaObject(cx, jEnv, js_val, jsj_get_jlObject_descriptor(cx, jEnv),
01311                                    &dummy_cost, &java_obj, &dummy_bool);
01312 done:
01313     if (!jsj_exit_js(cx, jsj_env, saved_reporter))
01314         return NULL;
01315     
01316     return java_obj;
01317 }
01318 
01319 /*
01320  * Class:     netscape_javascript_JSObject
01321  * Method:    finalize
01322  * Signature: ()V
01323  */
01324 JNIEXPORT void JNICALL
01325 Java_netscape_javascript_JSObject_finalize(JNIEnv *jEnv, jobject java_wrapper_obj)
01326 {
01327     JSBool success;
01328     JSObjectHandle *handle;
01329 
01330     success = JS_FALSE;
01331 
01332 #if JS_BYTES_PER_LONG == 8
01333     handle = (JSObjectHandle *)((*jEnv)->GetLongField(jEnv, java_wrapper_obj, njJSObject_long_internal));
01334 #else    
01335     handle = (JSObjectHandle *)((*jEnv)->GetIntField(jEnv, java_wrapper_obj, njJSObject_internal));
01336 #endif
01337     JS_ASSERT(handle);
01338     if (!handle)
01339         return;
01340 
01341     success = JS_RemoveRootRT(handle->rt, &handle->js_obj);
01342     free(handle);
01343 
01344     JS_ASSERT(success);
01345 }
01346 
01347 /*
01348  * Class:     netscape_javascript_JSObject
01349  * Method:    equals
01350  * Signature: (Ljava/lang/Object;)Z
01351  */
01352 
01353 JNIEXPORT jboolean JNICALL
01354 Java_netscape_javascript_JSObject_equals(JNIEnv *jEnv,
01355                                          jobject java_wrapper_obj,
01356                                          jobject comparison_obj)
01357 {
01358 #ifdef PRESERVE_JSOBJECT_IDENTITY
01359 #    error "Missing code should be added here"
01360 #else
01361     JSObject *js_obj1, *js_obj2;
01362 
01363     /* Check that we're comparing with another netscape.javascript.JSObject */
01364     if (!comparison_obj)
01365         return 0;
01366     if (!(*jEnv)->IsInstanceOf(jEnv, comparison_obj, njJSObject))
01367         return 0;
01368 
01369     js_obj1 = jsj_UnwrapJSObjectWrapper(jEnv, java_wrapper_obj);
01370     js_obj2 = jsj_UnwrapJSObjectWrapper(jEnv, comparison_obj);
01371 
01372     return (js_obj1 == js_obj2);
01373 #endif  /* PRESERVE_JSOBJECT_IDENTITY */
01374 }
01375