Back to index

lightning-sunbird  0.9+nobinonly
jsj_utils.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  * This Original Code has been modified by IBM Corporation. Modifications made
00039  * by IBM described herein are Copyright (c) International Business Machines
00040  * Corporation, 2000.
00041  * Modifications to Mozilla code or documentation identified per MPL Section 3.3
00042  *
00043  * Date             Modified by     Description of modification
00044  * 04/20/2000       IBM Corp.      OS/2 VisualAge build.
00045  *
00046  * ***** END LICENSE BLOCK ***** */
00047 
00048 /*
00049  * This file is part of the Java-vendor-neutral implementation of LiveConnect
00050  *
00051  * It contains low-level utility code.
00052  *
00053  */
00054 
00055 #include <stdlib.h>
00056 #include <string.h>
00057 
00058 #include "jsj_private.h"        /* LiveConnect internals */
00059 #include "jsjava.h"             /* External LiveConnect API */
00060 
00061 
00062 /*
00063  * This is a hash-table utility routine that computes the hash code of a Java
00064  * object by calling java.lang.System.identityHashCode()
00065  */
00066 JSJHashNumber JS_DLL_CALLBACK
00067 jsj_HashJavaObject(const void *key, void* env)
00068 {
00069     JSHashNumber hash_code;
00070     jobject java_obj;
00071     JNIEnv *jEnv;
00072 
00073     java_obj = (jobject)key;
00074     jEnv = (JNIEnv*) env;
00075     JS_ASSERT(!(*jEnv)->ExceptionOccurred(jEnv));
00076     hash_code = (*jEnv)->CallStaticIntMethod(jEnv, jlSystem,
00077                                              jlSystem_identityHashCode, java_obj);
00078 
00079 #ifdef DEBUG
00080     if ((*jEnv)->ExceptionOccurred(jEnv)) {
00081         (*jEnv)->ExceptionDescribe(jEnv);
00082         JS_ASSERT(0);
00083     }
00084 #endif
00085 
00086     return hash_code;
00087 }
00088 
00089 /* 
00090  * This is a hash-table utility routine for comparing two Java objects.
00091  * It's not possible to use the == operator to directly compare two jobject's,
00092  * since they're opaque references and aren't guaranteed to be simple pointers
00093  * or handles (though they may be in some JVM implementations).  Instead,
00094  * use the JNI routine for comparing the two objects.
00095  */
00096 intN JS_DLL_CALLBACK
00097 jsj_JavaObjectComparator(const void *v1, const void *v2, void *arg)
00098 {
00099     jobject java_obj1, java_obj2;
00100     JNIEnv *jEnv;
00101 
00102     jEnv = (JNIEnv*)arg;
00103     java_obj1 = (jobject)v1;
00104     java_obj2 = (jobject)v2;
00105 
00106     if (java_obj1 == java_obj2)
00107         return 1;
00108     if (jEnv)
00109         return (*jEnv)->IsSameObject(jEnv, java_obj1, java_obj2);
00110     return 0;
00111 }
00112 
00113 /*
00114  * Return a UTF8, null-terminated encoding of a Java string.  The string must
00115  * be free'ed by the caller.
00116  *
00117  * If an error occurs, returns NULL and calls the JS error reporter.
00118  */
00119 const char *
00120 jsj_DupJavaStringUTF(JSContext *cx, JNIEnv *jEnv, jstring jstr)
00121 {
00122     const char *str, *retval;
00123 
00124     str = (*jEnv)->GetStringUTFChars(jEnv, jstr, 0);
00125     if (!str) {
00126         jsj_UnexpectedJavaError(cx, jEnv, "Can't get UTF8 characters from "
00127                                           "Java string");
00128         return NULL;
00129     }
00130     retval = JS_strdup(cx, str);
00131     (*jEnv)->ReleaseStringUTFChars(jEnv, jstr, str);
00132     return retval;
00133 }
00134 
00135 JSBool
00136 JavaStringToId(JSContext *cx, JNIEnv *jEnv, jstring jstr, jsid *idp)
00137 {
00138     const jschar *ucs2;
00139     JSString *jsstr;
00140     jsize ucs2_len;
00141     jsval val;
00142     
00143     ucs2 = (*jEnv)->GetStringChars(jEnv, jstr, 0);
00144     if (!ucs2) {
00145         jsj_UnexpectedJavaError(cx, jEnv, "Couldn't obtain Unicode characters"
00146                                 "from Java string");
00147         return JS_FALSE;
00148     }
00149     
00150     ucs2_len = (*jEnv)->GetStringLength(jEnv, jstr);
00151     jsstr = JS_InternUCStringN(cx, ucs2, ucs2_len);
00152     (*jEnv)->ReleaseStringChars(jEnv, jstr, ucs2);
00153     if (!jsstr)
00154         return JS_FALSE;
00155 
00156     val = STRING_TO_JSVAL(jsstr);
00157     JS_ValueToId(cx, STRING_TO_JSVAL(jsstr), idp);
00158     return JS_TRUE;
00159 }
00160 
00161 /*
00162  * Return, as a C string, the error message associated with a Java exception
00163  * that occurred as a result of a JNI call, preceded by the class name of
00164  * the exception.  As a special case, if the class of the exception is
00165  * netscape.javascript.JSException, the exception class name is omitted.
00166  *
00167  * NULL is returned if no Java exception is pending.  The caller is
00168  * responsible for free'ing the returned string.  On exit, the Java exception
00169  * is *not* cleared.
00170  */
00171 const char *
00172 jsj_GetJavaErrorMessage(JNIEnv *jEnv)
00173 {
00174     const char *java_error_msg;
00175     char *error_msg = NULL;
00176     jthrowable exception;
00177     jstring java_exception_jstring;
00178 
00179     exception = (*jEnv)->ExceptionOccurred(jEnv);
00180     if (exception && jlThrowable_toString) {
00181         java_exception_jstring =
00182             (*jEnv)->CallObjectMethod(jEnv, exception, jlThrowable_toString);
00183         if (!java_exception_jstring)
00184             goto done;
00185         java_error_msg = (*jEnv)->GetStringUTFChars(jEnv, java_exception_jstring, NULL);
00186         if (java_error_msg) {
00187             error_msg = strdup((char*)java_error_msg);
00188             (*jEnv)->ReleaseStringUTFChars(jEnv, java_exception_jstring, java_error_msg);
00189         }
00190         (*jEnv)->DeleteLocalRef(jEnv, java_exception_jstring);
00191 
00192 #ifdef DEBUG
00193         /* (*jEnv)->ExceptionDescribe(jEnv); */
00194 #endif
00195     }
00196 done:
00197     if (exception)
00198         (*jEnv)->DeleteLocalRef(jEnv, exception);
00199     return error_msg;
00200 }    
00201 
00202 /*
00203  * Return, as a C string, the JVM stack trace associated with a Java
00204  * exception, as would be printed by java.lang.Throwable.printStackTrace().
00205  * The caller is responsible for free'ing the returned string.
00206  *
00207  * Returns NULL if an error occurs.
00208  */
00209 static const char *
00210 get_java_stack_trace(JSContext *cx, JNIEnv *jEnv, jthrowable java_exception)
00211 {
00212     const char *backtrace;
00213     jstring backtrace_jstr;
00214 
00215     backtrace = NULL;
00216     if (java_exception && njJSUtil_getStackTrace) {
00217         backtrace_jstr = (*jEnv)->CallStaticObjectMethod(jEnv, njJSUtil,
00218                                                          njJSUtil_getStackTrace,
00219                                                          java_exception);
00220         if (!backtrace_jstr) {
00221             jsj_UnexpectedJavaError(cx, jEnv, "Unable to get exception stack trace");
00222             return NULL;
00223         }
00224         backtrace = jsj_DupJavaStringUTF(cx, jEnv, backtrace_jstr);
00225         (*jEnv)->DeleteLocalRef(jEnv, backtrace_jstr);
00226     }
00227     return backtrace;
00228 } 
00229 
00230 /* Full Java backtrace when Java exceptions reported to JavaScript */
00231 #define REPORT_JAVA_EXCEPTION_STACK_TRACE
00232 
00233 /*
00234  * This is a wrapper around JS_ReportError(), useful when an error condition
00235  * is the result of a JVM failure or exception condition.  It appends the
00236  * message associated with the pending Java exception to the passed in
00237  * printf-style format string and arguments.
00238  */
00239 static void
00240 vreport_java_error(JSContext *cx, JNIEnv *jEnv, const char *format, va_list ap)
00241 {
00242     jobject java_obj;
00243     jclass java_class;
00244     JavaClassDescriptor *class_descriptor;
00245     jthrowable java_exception;
00246     JSType wrapped_exception_type;
00247     jsval js_exception;
00248        
00249     java_obj = NULL;
00250     class_descriptor = NULL;
00251 
00252     /* Get the exception out of the java environment. */
00253     java_exception = (*jEnv)->ExceptionOccurred(jEnv);
00254     if (!java_exception) {
00255         JSString *err_jsstr;
00256         char *err = JS_vsmprintf(format, ap);
00257         if (!err)
00258             return;
00259         err_jsstr = JS_NewString(cx, err, strlen(err));
00260         if (!err_jsstr)
00261             return;
00262         JS_SetPendingException(cx, STRING_TO_JSVAL(err_jsstr));
00263         return;
00264     }
00265 
00266     
00267     (*jEnv)->ExceptionClear(jEnv);
00268     
00269     /* Check for JSException */
00270     if (njJSException && 
00271         (*jEnv)->IsInstanceOf(jEnv, java_exception, njJSException)) {
00272         
00273         wrapped_exception_type = 
00274             (*jEnv)->GetIntField(jEnv, java_exception,
00275             njJSException_wrappedExceptionType);
00276         
00277         /* (int) to suppress warning */
00278         if ((int)wrapped_exception_type != JSTYPE_EMPTY) {
00279             java_obj = 
00280                 (*jEnv)->GetObjectField(jEnv, java_exception, 
00281                 njJSException_wrappedException);
00282             
00283             if ((java_obj == NULL) && 
00284                 (wrapped_exception_type == JSTYPE_OBJECT)) {
00285                 js_exception = JSVAL_NULL;
00286             } else { 
00287                 java_class = (*jEnv)->GetObjectClass(jEnv, java_obj); 
00288                 class_descriptor = jsj_GetJavaClassDescriptor(cx, jEnv, java_class);
00289                 /* OK to delete ref, since above call adds global ref */
00290                 (*jEnv)->DeleteLocalRef(jEnv, java_class);  
00291                 
00292                 /* Convert native JS values back to native types. */
00293                 switch(wrapped_exception_type) {
00294                 case JSTYPE_NUMBER:
00295                     if (!jsj_ConvertJavaObjectToJSNumber(cx, jEnv,
00296                         class_descriptor,
00297                         java_obj, 
00298                         &js_exception))
00299                         goto error;
00300                     break;
00301                 case JSTYPE_BOOLEAN:
00302                     if (!jsj_ConvertJavaObjectToJSBoolean(cx, jEnv,
00303                         class_descriptor,
00304                         java_obj, 
00305                         &js_exception))
00306                         goto error;
00307                     break;
00308                 case JSTYPE_STRING:
00309                     if (!jsj_ConvertJavaObjectToJSString(cx, jEnv,
00310                         class_descriptor,
00311                         java_obj, 
00312                         &js_exception))
00313                         goto error;
00314                     break;
00315                 case JSTYPE_VOID:
00316                     js_exception = JSVAL_VOID;
00317                     break;
00318                 case JSTYPE_OBJECT:
00319                 case JSTYPE_FUNCTION:
00320                 default:
00321                     if ((*jEnv)->IsInstanceOf(jEnv, java_obj, njJSObject)) {
00322                         js_exception = OBJECT_TO_JSVAL(jsj_UnwrapJSObjectWrapper(jEnv, java_obj));
00323                         if (!js_exception)
00324                             goto error;                        
00325                     } else {
00326                         if (!jsj_ConvertJavaObjectToJSValue(cx, jEnv, java_obj, 
00327                             &js_exception)) 
00328                             goto error;
00329                     }
00330                 }
00331             }
00332         }
00333         /* Check for internal exception */
00334     } else {
00335         if (!JSJ_ConvertJavaObjectToJSValue(cx, java_exception,
00336             &js_exception)) {
00337             goto error;
00338         }
00339     }
00340     
00341     /* Set pending JS exception and clear the java exception. */
00342     JS_SetPendingException(cx, js_exception);                        
00343     goto done;
00344 
00345 error:
00346     
00347     JS_ASSERT(0);
00348     jsj_LogError("Out of memory while attempting to throw JSException\n");
00349     
00350 done:
00351     if (class_descriptor)
00352         jsj_ReleaseJavaClassDescriptor(cx, jEnv, class_descriptor);
00353     if (java_obj)
00354         (*jEnv)->DeleteLocalRef(jEnv, java_obj);
00355     if (java_exception)
00356         (*jEnv)->DeleteLocalRef(jEnv, java_exception);
00357 }
00358 
00359 void
00360 jsj_ReportJavaError(JSContext *cx, JNIEnv *env, const char *format, ...)
00361 {
00362     va_list ap;
00363 
00364     va_start(ap, format);
00365     vreport_java_error(cx, env, format, ap);
00366     va_end(ap);
00367 }
00368 
00369 /*
00370  * Same as jsj_ReportJavaError, except "internal error: " is prepended
00371  * to message.
00372  */
00373 void
00374 jsj_UnexpectedJavaError(JSContext *cx, JNIEnv *env, const char *format, ...)
00375 {
00376     va_list ap;
00377     const char *format2;
00378 
00379     va_start(ap, format);
00380     format2 = JS_smprintf("internal error: %s", format);
00381     if (format2) {
00382         vreport_java_error(cx, env, format2, ap);
00383         free((void*)format2);
00384     }
00385     va_end(ap);
00386 }
00387 
00388 /*
00389  * Most LiveConnect errors are signaled by calling JS_ReportError(),
00390  * but in some circumstances, the target JSContext for such errors
00391  * is not determinable, e.g. during initialization.  In such cases
00392  * any error messages are routed to this function.
00393  */
00394 void
00395 jsj_LogError(const char *error_msg)
00396 {
00397     if (JSJ_callbacks && JSJ_callbacks->error_print)
00398         JSJ_callbacks->error_print(error_msg);
00399     else
00400         fputs(error_msg, stderr);
00401 }
00402 
00403 /*
00404         Error number handling. 
00405 
00406         jsj_ErrorFormatString is an array of format strings mapped
00407         by error number. It is initialized by the contents of jsj.msg
00408 
00409         jsj_GetErrorMessage is invoked by the engine whenever it wants 
00410         to convert an error number into an error format string.
00411 */
00412 /*
00413         this define needs to go somewhere sensible
00414 */
00415 #define JSJ_HAS_DFLT_MSG_STRINGS 1
00416 
00417 JSErrorFormatString jsj_ErrorFormatString[JSJ_Err_Limit] = {
00418 #if JSJ_HAS_DFLT_MSG_STRINGS
00419 #define MSG_DEF(name, number, count, format) \
00420     { format, count } ,
00421 #else
00422 #define MSG_DEF(name, number, count, format) \
00423     { NULL, count } ,
00424 #endif
00425 #include "jsj.msg"
00426 #undef MSG_DEF
00427 };
00428 
00429 const JSErrorFormatString *
00430 jsj_GetErrorMessage(void *userRef, const char *locale, const uintN errorNumber)
00431 {
00432     if ((errorNumber > 0) && (errorNumber < JSJ_Err_Limit))
00433             return &jsj_ErrorFormatString[errorNumber];
00434         else
00435             return NULL;
00436 }
00437 
00438 jsize
00439 jsj_GetJavaArrayLength(JSContext *cx, JNIEnv *jEnv, jarray java_array)
00440 {
00441     jsize array_length = (*jEnv)->GetArrayLength(jEnv, java_array);
00442     jthrowable java_exception = (*jEnv)->ExceptionOccurred(jEnv);
00443     if (java_exception) {
00444         jsj_UnexpectedJavaError(cx, jEnv, "Couldn't obtain array length");
00445         (*jEnv)->DeleteLocalRef(jEnv, java_exception);
00446         return -1;
00447     }
00448     return array_length;
00449 }
00450 
00451 static JSJavaThreadState *the_java_jsj_env = NULL;
00452 
00453 JSJavaThreadState *
00454 jsj_EnterJava(JSContext *cx, JNIEnv **envp)
00455 {
00456     JSJavaThreadState *jsj_env;
00457     char *err_msg;
00458 
00459     *envp = NULL;
00460     err_msg = NULL;
00461 
00462     jsj_env = the_java_jsj_env;
00463     if (jsj_env == NULL && JSJ_callbacks && JSJ_callbacks->map_js_context_to_jsj_thread)
00464         jsj_env = JSJ_callbacks->map_js_context_to_jsj_thread(cx, &err_msg);
00465     if (!jsj_env) {
00466         if (err_msg) {
00467             JS_ReportError(cx, err_msg);
00468             free(err_msg);
00469         }
00470         return NULL;
00471     }
00472 
00473     /* simultaneous calls from different JSContext are not allowed */
00474     if ((jsj_env->recursion_depth > 0) && (jsj_env->cx != cx))
00475         return NULL;
00476 
00477     jsj_env->recursion_depth++;
00478 
00479     /* bug #60018:  prevent dangling pointer to JSContext */
00480     if (!jsj_env->cx)
00481         jsj_env->cx = cx;
00482 
00483     if (envp)
00484         *envp = jsj_env->jEnv;
00485     return jsj_env;
00486 }
00487 
00488 extern void
00489 jsj_ExitJava(JSJavaThreadState *jsj_env)
00490 {
00491     jsj_JSIsCallingApplet = JS_FALSE;
00492     if (jsj_env) {
00493         JS_ASSERT(jsj_env->recursion_depth > 0);
00494         if (--jsj_env->recursion_depth == 0)
00495             jsj_env->cx = NULL;
00496     }
00497 }
00498 
00507 JSJavaThreadState *
00508 jsj_SetJavaJSJEnv(JSJavaThreadState* java_jsj_env)
00509 {
00510     JSJavaThreadState *old_jsj_env = the_java_jsj_env;
00511     the_java_jsj_env = java_jsj_env;
00512     return old_jsj_env;
00513 }