Back to index

lightning-sunbird  0.9+nobinonly
jsj_convert.c
Go to the documentation of this file.
00001 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
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  * Below is the code that converts between Java and JavaScript values of all
00043  * types.
00044  */
00045 
00046 #include <stdlib.h>
00047 #include <string.h>
00048 
00049 #ifdef OJI
00050 #include "prtypes.h"          /* Need platform-dependent definition of HAVE_LONG_LONG
00051                                  for non-standard jri_md.h file */
00052 #endif
00053 
00054 #include "jsj_private.h"      /* LiveConnect internals */
00055 
00056 /* Floating-point double utilities, stolen from jsnum.h */
00057 #ifdef IS_LITTLE_ENDIAN
00058 #define JSDOUBLE_HI32(x)        (((uint32 *)&(x))[1])
00059 #define JSDOUBLE_LO32(x)        (((uint32 *)&(x))[0])
00060 #else
00061 #define JSDOUBLE_HI32(x)        (((uint32 *)&(x))[0])
00062 #define JSDOUBLE_LO32(x)        (((uint32 *)&(x))[1])
00063 #endif
00064 #define JSDOUBLE_HI32_SIGNBIT   0x80000000
00065 #define JSDOUBLE_HI32_EXPMASK   0x7ff00000
00066 #define JSDOUBLE_HI32_MANTMASK  0x000fffff
00067 
00068 #define JSDOUBLE_IS_NaN(x)                                                    \
00069     ((JSDOUBLE_HI32(x) & JSDOUBLE_HI32_EXPMASK) == JSDOUBLE_HI32_EXPMASK &&   \
00070      (JSDOUBLE_LO32(x) || (JSDOUBLE_HI32(x) & JSDOUBLE_HI32_MANTMASK)))
00071 
00072 #define JSDOUBLE_IS_INFINITE(x)                                               \
00073     ((JSDOUBLE_HI32(x) & ~JSDOUBLE_HI32_SIGNBIT) == JSDOUBLE_HI32_EXPMASK &&  \
00074      !JSDOUBLE_LO32(x))
00075 
00076 static JSBool
00077 convert_js_obj_to_JSObject_wrapper(JSContext *cx, JNIEnv *jEnv, JSObject *js_obj,
00078                                    JavaSignature *signature,
00079                                    int *cost, jobject *java_value)
00080 {
00081     if (!njJSObject) {
00082         if (java_value)
00083             JS_ReportErrorNumber(cx, jsj_GetErrorMessage, NULL, 
00084                                                 JSJMSG_CANT_LOAD_JSOBJECT);
00085         return JS_FALSE;
00086     }
00087 
00088     if (!(*jEnv)->IsAssignableFrom(jEnv, njJSObject, signature->java_class))
00089         return JS_FALSE;
00090 
00091     if (!java_value)
00092         return JS_TRUE;
00093 
00094     *java_value = jsj_WrapJSObject(cx, jEnv, js_obj);
00095 
00096     return (*java_value != NULL);   
00097 }
00098 
00099 /* Copy an array from JS to Java;  Create a new Java array and populate its
00100    elements, one by one, with the result of converting each JS array element
00101    to the type of the array component. */
00102 static JSBool
00103 convert_js_array_to_java_array(JSContext *cx, JNIEnv *jEnv, JSObject *js_array,
00104                                JavaSignature *signature,
00105                                jobject *java_valuep)
00106 {
00107     jsuint i;
00108     jsval js_val;
00109     jsuint length;
00110     jclass component_class;
00111     jarray java_array;
00112     JavaSignature *array_component_signature;
00113 
00114     if (!JS_GetArrayLength(cx, js_array, &length))
00115         return JS_FALSE;
00116 
00117     /* Get the Java class of each element of the array */
00118     array_component_signature = signature->array_component_signature;
00119     component_class = array_component_signature->java_class;
00120 
00121     /* Create a new empty Java array with the same length as the JS array */
00122     java_array = (*jEnv)->CallStaticObjectMethod(jEnv, jlrArray, jlrArray_newInstance,
00123                                                  component_class, length);
00124     if (!java_array) {
00125         jsj_ReportJavaError(cx, jEnv, "Error while constructing empty array of %s",
00126                             jsj_GetJavaClassName(cx, jEnv, component_class));
00127         return JS_FALSE;
00128     }
00129 
00130     /* Convert each element of the JS array to an element of the Java array.
00131        If an error occurs, there is no need to worry about releasing the
00132        individual elements of the Java array - they will eventually be GC'ed
00133        by the JVM. */
00134     for (i = 0; i < length; i++) {
00135         if (!JS_LookupElement(cx, js_array, i, &js_val))
00136             return JS_FALSE;
00137 
00138         if (!jsj_SetJavaArrayElement(cx, jEnv, java_array, i, array_component_signature, js_val))
00139             return JS_FALSE;
00140     }
00141 
00142     /* Return the result array */
00143     *java_valuep = java_array;
00144     return JS_TRUE;
00145 }
00146 
00147 jstring
00148 jsj_ConvertJSStringToJavaString(JSContext *cx, JNIEnv *jEnv, JSString *js_str)
00149 {
00150     jstring result;
00151     result = (*jEnv)->NewString(jEnv, JS_GetStringChars(js_str),
00152                                 JS_GetStringLength(js_str));
00153     if (!result) {
00154         jsj_UnexpectedJavaError(cx, jEnv, "Couldn't construct instance "
00155                                           "of java.lang.String");
00156     }
00157     return result;
00158 }
00159 
00160 /*
00161  * Convert a JS value to an instance of java.lang.Object or one of its subclasses, 
00162  * performing any necessary type coercion.  If non-trivial coercion is required,
00163  * the cost value is incremented.  If the java_value pass-by-reference argument
00164  * is non-NULL, the resulting Java value is stored there.
00165  *
00166  * Returns JS_TRUE if the conversion is possible, JS_FALSE otherwise
00167  */
00168 JSBool
00169 jsj_ConvertJSValueToJavaObject(JSContext *cx, JNIEnv *jEnv, jsval v, JavaSignature *signature,
00170                                int *cost, jobject *java_value, JSBool *is_local_refp)
00171 {
00172     JSString *jsstr;
00173     jclass target_java_class;
00174     
00175     JS_ASSERT(IS_REFERENCE_TYPE(signature->type));
00176 
00177     /* Initialize to default case, in which no new Java object is
00178        synthesized to perform the conversion and, therefore, no JNI local
00179        references are being held. */
00180     *is_local_refp = JS_FALSE;
00181 
00182     /* Get the Java type of the target value */
00183     target_java_class = signature->java_class;
00184     
00185     if (JSVAL_IS_OBJECT(v)) {
00186         JSObject *js_obj = JSVAL_TO_OBJECT(v);
00187         
00188         /* JS null is always assignable to a Java object */
00189         if (!js_obj) {
00190             if (java_value)
00191                 *java_value = NULL;
00192             return JS_TRUE;
00193         }
00194         
00195         if (JS_InstanceOf(cx, js_obj, &JavaObject_class, 0) ||
00196             JS_InstanceOf(cx, js_obj, &JavaArray_class, 0)) {
00197             
00198             /* The source value is a Java object wrapped inside a JavaScript
00199                object.  Unwrap the JS object and return the original Java
00200                object if it's class makes it assignment-compatible with the
00201                target class using Java's assignability rules. */
00202             JavaObjectWrapper *java_wrapper = JS_GetPrivate(cx, js_obj);
00203             jobject java_obj = java_wrapper->java_obj;
00204             
00205             if ((*jEnv)->IsInstanceOf(jEnv, java_obj, target_java_class)) {
00206                 if (java_value)
00207                     *java_value = java_obj;
00208                 return JS_TRUE;
00209             }
00210             
00211             /* Fall through, to attempt conversion to a Java string */
00212             
00213         } else if (JS_InstanceOf(cx, js_obj, &JavaClass_class, 0)) {
00214             /* We're dealing with the reflection of a Java class */
00215             JavaClassDescriptor *java_class_descriptor = JS_GetPrivate(cx, js_obj);
00216             
00217             /* Check if target type is java.lang.Class class */
00218             if ((*jEnv)->IsAssignableFrom(jEnv, jlClass, target_java_class)) {
00219                 if (java_value)
00220                     *java_value = java_class_descriptor->java_class;
00221                 return JS_TRUE;
00222             }
00223             
00224             /* Check if target type is netscape.javascript.JSObject wrapper class */
00225             if (convert_js_obj_to_JSObject_wrapper(cx, jEnv, js_obj, signature, cost, java_value)) {
00226                 if (java_value && *java_value)
00227                     *is_local_refp = JS_TRUE;
00228                 return JS_TRUE;
00229             }
00230             
00231             /* Fall through, to attempt conversion to a Java string */
00232             
00233         } else if (JS_InstanceOf(cx, js_obj, &JavaMember_class, 0)) {
00234 
00235             if (!JS_ConvertValue(cx, v, JSTYPE_OBJECT, &v))
00236                 return JS_FALSE;
00237             return jsj_ConvertJSValueToJavaObject(cx, jEnv, v, signature, cost,
00238                                                   java_value, is_local_refp);
00239 
00240         /* JS Arrays are converted, element by element, to Java arrays */
00241         } else if (JS_IsArrayObject(cx, js_obj) && (signature->type == JAVA_SIGNATURE_ARRAY)) {
00242             if (convert_js_array_to_java_array(cx, jEnv, js_obj, signature, java_value)) {
00243                 if (java_value && *java_value)
00244                     *is_local_refp = JS_TRUE;
00245                 return JS_TRUE;
00246             }
00247             return JS_FALSE;
00248 
00249         } else {
00250             /* Otherwise, see if the target type is the  netscape.javascript.JSObject
00251                wrapper class or one of its subclasses, in which case a
00252                reference is passed to the original JS object by wrapping it
00253                inside an instance of netscape.javascript.JSObject */
00254             if (convert_js_obj_to_JSObject_wrapper(cx, jEnv, js_obj, signature, cost, java_value))             {
00255                 if (java_value && *java_value)
00256                     *is_local_refp = JS_TRUE;
00257                 return JS_TRUE;
00258             }
00259             
00260             /* Fall through, to attempt conversion to a Java string */
00261         }
00262         
00263     } else if (JSVAL_IS_NUMBER(v)) {
00264         /* JS numbers, integral or not, can be converted to instances of java.lang.Double */
00265         if ((*jEnv)->IsAssignableFrom(jEnv, jlDouble, target_java_class)) {
00266             if (java_value) {
00267                 jsdouble d;
00268                 if (!JS_ValueToNumber(cx, v, &d))
00269                     goto conversion_error;
00270                 *java_value = (*jEnv)->NewObject(jEnv, jlDouble, jlDouble_Double, d);
00271                 if (*java_value) {
00272                     *is_local_refp = JS_TRUE;
00273                 } else {
00274                     jsj_UnexpectedJavaError(cx, jEnv,
00275                         "Couldn't construct instance of java.lang.Double");
00276                     return JS_FALSE;
00277                 }
00278             }
00279 
00280             return JS_TRUE;
00281         }
00282         /* Fall through, to attempt conversion to a java.lang.String ... */
00283         
00284     } else if (JSVAL_IS_BOOLEAN(v)) {
00285         /* JS boolean values can be converted to instances of java.lang.Boolean */
00286         if ((*jEnv)->IsAssignableFrom(jEnv, jlBoolean, target_java_class)) {
00287             if (java_value) {
00288                 JSBool b;
00289                 if (!JS_ValueToBoolean(cx, v, &b))
00290                     goto conversion_error;
00291                 *java_value =
00292                     (*jEnv)->NewObject(jEnv, jlBoolean, jlBoolean_Boolean, b);
00293                 if (*java_value) {
00294                     *is_local_refp = JS_TRUE;
00295                 } else {
00296                     jsj_UnexpectedJavaError(cx, jEnv, "Couldn't construct instance " 
00297                         "of java.lang.Boolean");
00298                     return JS_FALSE;
00299                 }
00300             }
00301 
00302             return JS_TRUE;
00303         }
00304         /* Fall through, to attempt conversion to a java.lang.String ... */
00305     }
00306     
00307     /* If the source JS type is either a string or undefined, or if no conversion
00308        is possible from a number, boolean or JS object, see if the target type is
00309        java.lang.String */
00310     if ((*jEnv)->IsAssignableFrom(jEnv, jlString, target_java_class)) {
00311         
00312         /* Convert to JS string, if necessary, and then to a Java Unicode string */
00313         jsstr = JS_ValueToString(cx, v);
00314         if (jsstr) {
00315             if (java_value) {
00316                 *java_value = jsj_ConvertJSStringToJavaString(cx, jEnv, jsstr);
00317                 if (*java_value) {
00318                     *is_local_refp = JS_TRUE;
00319                 } else {
00320                     return JS_FALSE;
00321                 }
00322             }
00323 
00324             return JS_TRUE;
00325         }
00326     }
00327     
00328 conversion_error:
00329     return JS_FALSE;
00330 }
00331 
00332 /* Valid ranges for Java numeric types */
00333 #define jbyte_MAX_VALUE   127.0
00334 #define jbyte_MIN_VALUE  -128.0
00335 #define jchar_MAX_VALUE   65535.0
00336 #define jchar_MIN_VALUE   0.0
00337 #define jshort_MAX_VALUE  32767.0
00338 #define jshort_MIN_VALUE -32768.0
00339 #define jint_MAX_VALUE    2147483647.0
00340 #define jint_MIN_VALUE   -2147483648.0
00341 #define jlong_MAX_VALUE   9223372036854775807.0
00342 #define jlong_MIN_VALUE  -9223372036854775808.0
00343 
00344 /* Utility macro for jsj_ConvertJSValueToJavaValue(), below */
00345 #define JSVAL_TO_INTEGRAL_JVALUE(type_name, member_name, member_type, jsval, java_value) \
00346     if (!JSVAL_IS_NUMBER(v)) {                                           \
00347         if (!JS_ConvertValue(cx, v, JSTYPE_NUMBER, &v))                  \
00348             goto conversion_error;                                       \
00349         (*cost)++;                                                       \
00350     }                                                                    \
00351     {                                                                    \
00352         member_type member_name;                                         \
00353                                                                          \
00354         if (JSVAL_IS_INT(v)) {                                           \
00355             jsint ival = JSVAL_TO_INT(v);                                \
00356             member_name = (member_type) ival;                            \
00357                                                                          \
00358             /* Check to see if the jsval's magnitude is too large to be  \
00359                representable in the target java type */                  \
00360             if (member_name != ival)                                     \
00361                 goto numeric_conversion_error;                           \
00362         } else {                                                         \
00363             jdouble dval = *JSVAL_TO_DOUBLE(v);                          \
00364                                                                          \
00365             /* NaN becomes zero when converted to integral value */      \
00366             if (JSDOUBLE_IS_NaN(dval))                                   \
00367                 goto numeric_conversion_error;                           \
00368                                                                          \
00369             /* Unrepresentably large numbers, including infinities, */   \
00370             /* cause an error. */                                        \
00371             else if ((dval >= member_type ## _MAX_VALUE + 1) ||          \
00372                      (dval <= member_type ## _MIN_VALUE - 1)) {          \
00373                 goto numeric_conversion_error;                           \
00374             } else                                                       \
00375                 member_name = (member_type) dval;                        \
00376                                                                          \
00377             /* Don't allow a non-integral number to be converted         \
00378                to an integral type */                                    \
00379             /* Actually, we have to allow this for LC1 compatibility */  \
00380             /* if ((jdouble)member_name != dval)                         \
00381                 (*cost)++; */                                            \
00382         }                                                                \
00383         if (java_value)                                                  \
00384             java_value->member_name = member_name;                       \
00385     }
00386 
00387 #ifdef XP_OS2
00388 
00389 /* OS2 utility macro for jsj_ConvertJSValueToJavaValue(), below             */
00390 /* jlong is a structure, see jri_md.h, where the jlong_ macros are defined. */
00391 #define JSVAL_TO_JLONG_JVALUE(member_name, member_type, jsvalue, java_value) \
00392    if (!JSVAL_IS_NUMBER(jsvalue)) {                                      \
00393       if (!JS_ConvertValue(cx, jsvalue, JSTYPE_NUMBER, &jsvalue))        \
00394          goto conversion_error;                                          \
00395       (*cost)++;                                                         \
00396    }                                                                     \
00397    {                                                                     \
00398       member_type member_name;                                           \
00399                                                                          \
00400       if (JSVAL_IS_INT(jsvalue)) {                                       \
00401           jsint ival = JSVAL_TO_INT(jsvalue);                            \
00402           jlong_I2L(member_name,ival);                                   \
00403                                                                          \
00404        } else {                                                          \
00405             jdouble dval = *JSVAL_TO_DOUBLE(jsvalue);                    \
00406                                                                          \
00407             /* NaN becomes zero when converted to integral value */      \
00408             if (JSDOUBLE_IS_NaN(dval))                                   \
00409                 jlong_I2L(member_name,0);                                \
00410                                                                          \
00411             /* Unrepresentably large numbers, including infinities, */   \
00412             /* cause an error. */                                        \
00413             else if ((dval > member_type ## _MAX_VALUE) ||               \
00414                      (dval < member_type ## _MIN_VALUE)) {               \
00415                 goto numeric_conversion_error;                           \
00416             } else                                                       \
00417                 jlong_D2L(member_name,dval);                             \
00418                                                                          \
00419             /* Don't allow a non-integral number to be converted         \
00420                to an integral type */                                    \
00421             /* Actually, we have to allow this for LC1 compatibility */  \
00422             /*if (jlong_to_jdouble(member_name) != dval)                 \
00423                 (*cost)++;*/                                             \
00424         }                                                                \
00425         if (java_value)                                                  \
00426             java_value->member_name = member_name;                       \
00427     }
00428 
00429 static jdouble jlong_to_jdouble(jlong lvalue)
00430 {
00431    jdouble d;
00432    jlong_L2D(d,lvalue);
00433    return d;
00434 }
00435 
00436 #else
00437 
00438 #define jlong_to_jdouble(lvalue) ((jdouble) lvalue)
00439 
00440 #endif
00441 
00442 /*
00443  * Convert a JS value to a Java value of the given type signature.  The cost
00444  * variable is incremented if coercion is required, e.g. the source value is
00445  * a string, but the target type is a boolean.
00446  *
00447  * Returns JS_FALSE if no conversion is possible, either because the jsval has
00448  * a type that is wholly incompatible with the Java value, or because a scalar
00449  * jsval can't be represented in a variable of the target type without loss of
00450  * precision, e.g. the source value is "4.2" but the destination type is byte.
00451  * If conversion is not possible and java_value is non-NULL, the JS error
00452  * reporter is called with an appropriate message.
00453  */  
00454 JSBool
00455 jsj_ConvertJSValueToJavaValue(JSContext *cx, JNIEnv *jEnv, jsval v_arg,
00456                               JavaSignature *signature,
00457                               int *cost, jvalue *java_value, JSBool *is_local_refp)
00458 {
00459     JavaSignatureChar type;
00460     jsval v;
00461     JSBool success = JS_FALSE;
00462 
00463     /* Initialize to default case, in which no new Java object is
00464        synthesized to perform the conversion and, therefore, no JNI local
00465        references are being held. */
00466     *is_local_refp = JS_FALSE;   
00467     
00468     type = signature->type;
00469     v = v_arg;
00470     switch (type) {
00471     case JAVA_SIGNATURE_BOOLEAN:
00472         if (!JSVAL_IS_BOOLEAN(v)) {
00473             if (!JS_ConvertValue(cx, v, JSTYPE_BOOLEAN, &v))
00474                 goto conversion_error;
00475             if (JSVAL_IS_VOID(v))
00476                 goto conversion_error;
00477             (*cost)++;
00478         }
00479         if (java_value)
00480             java_value->z = (jboolean)(JSVAL_TO_BOOLEAN(v) == JS_TRUE);
00481         break;
00482 
00483     case JAVA_SIGNATURE_SHORT:
00484         JSVAL_TO_INTEGRAL_JVALUE(short, s, jshort, v, java_value);
00485         break;
00486 
00487     case JAVA_SIGNATURE_BYTE:
00488         JSVAL_TO_INTEGRAL_JVALUE(byte, b, jbyte, v, java_value);
00489         break;
00490 
00491     case JAVA_SIGNATURE_CHAR:
00492         /* A one-character string can be converted into a character */
00493         if (JSVAL_IS_STRING(v) && (JS_GetStringLength(JSVAL_TO_STRING(v)) == 1)) {
00494             v = INT_TO_JSVAL(*JS_GetStringChars(JSVAL_TO_STRING(v)));
00495         }
00496         JSVAL_TO_INTEGRAL_JVALUE(char, c, jchar, v, java_value);
00497         break;
00498 
00499     case JAVA_SIGNATURE_INT:
00500         JSVAL_TO_INTEGRAL_JVALUE(int, i, jint, v, java_value);
00501         break;
00502 
00503     case JAVA_SIGNATURE_LONG:
00504 #if (defined(XP_OS2) && !defined(HAVE_LONG_LONG))
00505         JSVAL_TO_JLONG_JVALUE(j, jlong, v, java_value);
00506 #else
00507         JSVAL_TO_INTEGRAL_JVALUE(long, j, jlong, v, java_value);
00508 #endif
00509         break;
00510     
00511     case JAVA_SIGNATURE_FLOAT:
00512         if (!JSVAL_IS_NUMBER(v)) {
00513             if (!JS_ConvertValue(cx, v, JSTYPE_NUMBER, &v))
00514                 goto conversion_error;
00515             (*cost)++;
00516         }
00517         if (java_value) {
00518             if (JSVAL_IS_INT(v))
00519                 java_value->f = (jfloat) JSVAL_TO_INT(v);
00520             else
00521                 java_value->f = (jfloat) *JSVAL_TO_DOUBLE(v);
00522         }
00523         break;
00524 
00525     case JAVA_SIGNATURE_DOUBLE:
00526         if (!JSVAL_IS_NUMBER(v)) {
00527             if (!JS_ConvertValue(cx, v, JSTYPE_NUMBER, &v))
00528                 goto conversion_error;
00529             (*cost)++;
00530         }
00531         if (java_value) {
00532             if (JSVAL_IS_INT(v))
00533                 java_value->d = (jdouble) JSVAL_TO_INT(v);
00534             else
00535                 java_value->d = (jdouble) *JSVAL_TO_DOUBLE(v);
00536         }
00537         break;
00538 
00539     /* Non-primitive (reference) type */
00540     default:
00541         JS_ASSERT(IS_REFERENCE_TYPE(type));
00542         if (!jsj_ConvertJSValueToJavaObject(cx, jEnv, v, signature, cost,
00543             &java_value->l, is_local_refp))
00544             goto conversion_error;
00545         break;
00546 
00547     case JAVA_SIGNATURE_UNKNOWN:
00548     case JAVA_SIGNATURE_VOID:
00549         JS_ASSERT(0);
00550         return JS_FALSE;
00551     }
00552 
00553     /* Success */
00554     return JS_TRUE;
00555 
00556 numeric_conversion_error:
00557     success = JS_TRUE;
00558     /* Fall through ... */
00559 
00560 conversion_error:
00561 
00562     if (java_value) {
00563         const char *jsval_string;
00564         const char *class_name;
00565         JSString *jsstr;
00566 
00567         jsval_string = NULL;
00568         jsstr = JS_ValueToString(cx, v_arg);
00569         if (jsstr)
00570             jsval_string = JS_GetStringBytes(jsstr);
00571         if (!jsval_string)
00572             jsval_string = "";
00573         
00574         class_name = jsj_ConvertJavaSignatureToHRString(cx, signature);
00575         JS_ReportErrorNumber(cx, jsj_GetErrorMessage, NULL, 
00576                              JSJMSG_CANT_CONVERT_JS, jsval_string,
00577                              class_name);
00578 
00579         return JS_FALSE;
00580     }
00581     return success;
00582 }
00583 
00584 /*
00585  * A utility routine to create a JavaScript Unicode string from a
00586  * java.lang.String (Unicode) string.
00587  */
00588 JSString *
00589 jsj_ConvertJavaStringToJSString(JSContext *cx, JNIEnv *jEnv, jstring java_str)
00590 {
00591     JSString *js_str;
00592     jboolean is_copy;
00593     const jchar *ucs2_str;
00594     jsize ucs2_str_len;
00595 
00596     ucs2_str_len = (*jEnv)->GetStringLength(jEnv, java_str);
00597     ucs2_str = (*jEnv)->GetStringChars(jEnv, java_str, &is_copy);
00598     if (!ucs2_str) {
00599         jsj_UnexpectedJavaError(cx, jEnv,
00600                                 "Unable to extract native Unicode from Java string");
00601         return NULL;
00602     }
00603 
00604     /* The string data passed into JS_NewUCString() is
00605        not copied, so make a copy of the Unicode character vector. */
00606     js_str = JS_NewUCStringCopyN(cx, ucs2_str, ucs2_str_len);
00607 
00608     (*jEnv)->ReleaseStringChars(jEnv, java_str, ucs2_str);
00609     return js_str;
00610 }
00611 
00612 /*
00613  * Attempt to obtain a JS string representation of a Java object.
00614  * The java_obj argument must be of type java.lang.Object or a subclass.
00615  * If java_obj is a Java string, it's value is simply extracted and
00616  * copied into a JS string.  Otherwise, the toString() method is called
00617  * on java_obj.
00618  */
00619 JSBool
00620 jsj_ConvertJavaObjectToJSString(JSContext *cx,
00621                                 JNIEnv *jEnv,
00622                                 JavaClassDescriptor *class_descriptor,
00623                                 jobject java_obj, jsval *vp)
00624 {
00625     JSString *js_str;
00626     jstring java_str;
00627     jmethodID toString;
00628     jclass java_class;
00629     
00630     /* Create a Java string, unless java_obj is already a java.lang.String */
00631     if ((*jEnv)->IsInstanceOf(jEnv, java_obj, jlString)) {
00632 
00633         /* Extract Unicode from java.lang.String instance and convert to JS string */
00634         js_str = jsj_ConvertJavaStringToJSString(cx, jEnv, java_obj);
00635         if (!js_str)
00636             return JS_FALSE;
00637         *vp = STRING_TO_JSVAL(js_str);
00638         return JS_TRUE;
00639     }
00640     
00641     java_class = class_descriptor->java_class;
00642     toString = (*jEnv)->GetMethodID(jEnv, java_class, "toString",
00643         "()Ljava/lang/String;");
00644     if (!toString) {
00645         /* All Java objects have a toString method */
00646         jsj_UnexpectedJavaError(cx, jEnv, "No toString() method for class %s!",
00647             class_descriptor->name);
00648         return JS_FALSE;
00649     }
00650     java_str = (*jEnv)->CallObjectMethod(jEnv, java_obj, toString);
00651     if (!java_str) {
00652         jsj_ReportJavaError(cx, jEnv, "toString() method failed");
00653         return JS_FALSE;
00654     }
00655     
00656     /* Extract Unicode from java.lang.String instance and convert to JS string */
00657     js_str = jsj_ConvertJavaStringToJSString(cx, jEnv, java_str);
00658     if (!js_str) {
00659         (*jEnv)->DeleteLocalRef(jEnv, java_str);
00660         return JS_FALSE;
00661     }
00662 
00663     *vp = STRING_TO_JSVAL(js_str);
00664     (*jEnv)->DeleteLocalRef(jEnv, java_str);
00665     return JS_TRUE;
00666 }
00667 
00668 /*
00669  * Convert a Java object to a number by attempting to call the
00670  * doubleValue() method on a Java object to get a double result.
00671  * This usually only works on instances of java.lang.Double, but the code
00672  * is generalized to work with any Java object that supports this method.
00673  *
00674  * Returns JS_TRUE if the call was successful.
00675  * Returns JS_FALSE if conversion is not possible or an error occurs.
00676  */
00677 JSBool
00678 jsj_ConvertJavaObjectToJSNumber(JSContext *cx, JNIEnv *jEnv,
00679                                 JavaClassDescriptor *class_descriptor,
00680                                 jobject java_obj, jsval *vp)
00681 {
00682     jdouble d;
00683     jmethodID doubleValue;
00684     jclass java_class;
00685 
00686     java_class = class_descriptor->java_class;
00687     doubleValue = (*jEnv)->GetMethodID(jEnv, java_class, "doubleValue", "()D");
00688     if (!doubleValue) {
00689         /* There is no doubleValue() method for the object.  Try toString()
00690            instead and the JS engine will attempt to convert the result to
00691            a number. */
00692         (*jEnv)->ExceptionClear(jEnv);
00693         return jsj_ConvertJavaObjectToJSString(cx, jEnv, class_descriptor,
00694                                                java_obj, vp);
00695     }
00696     /*
00697      * Sun Java-Plugin team work around bug to be fixed in JRE1.5, where GetMethodID 
00698      * called with a non-existent method name returns a non-null result.
00699      * See Mozilla bug 201164.
00700      */
00701     if ((*jEnv)->ExceptionOccurred(jEnv)) {
00702         jsj_UnexpectedJavaError(cx, jEnv, "No doubleValue() method for class %s!",
00703             class_descriptor->name);
00704         return JS_FALSE;
00705     }
00706     
00707     d = (*jEnv)->CallDoubleMethod(jEnv, java_obj, doubleValue);
00708     if ((*jEnv)->ExceptionOccurred(jEnv)) {
00709         jsj_UnexpectedJavaError(cx, jEnv, "doubleValue() method failed");
00710         return JS_FALSE;
00711     }
00712     return JS_NewDoubleValue(cx, d, vp);
00713 }
00714 
00715 /*
00716  * Convert a Java object to a boolean by attempting to call the
00717  * booleanValue() method on a Java object to get a boolean result.
00718  * This usually only works on instances of java.lang.Boolean, but the code
00719  * is generalized to work with any Java object that supports this method.
00720  *
00721  * Returns JS_TRUE if the call was successful.
00722  * Returns JS_FALSE if conversion is not possible or an error occurs.
00723  */
00724 extern JSBool
00725 jsj_ConvertJavaObjectToJSBoolean(JSContext *cx, JNIEnv *jEnv,
00726                                  JavaClassDescriptor *class_descriptor,
00727                                  jobject java_obj, jsval *vp)
00728 {
00729     jboolean b;
00730     jmethodID booleanValue;
00731     jclass java_class;
00732     
00733     /* Null converts to false. */
00734     if (!java_obj) {
00735         *vp = JSVAL_FALSE;
00736         return JS_TRUE;
00737     }
00738     java_class = class_descriptor->java_class;
00739     booleanValue = (*jEnv)->GetMethodID(jEnv, java_class, "booleanValue", "()Z");
00740 
00741     /* Non-null Java object does not have a booleanValue() method, so
00742        it converts to true. */
00743     if (!booleanValue) {
00744         (*jEnv)->ExceptionClear(jEnv);
00745         *vp = JSVAL_TRUE;
00746         return JS_TRUE;
00747     }
00748 
00749     b = (*jEnv)->CallBooleanMethod(jEnv, java_obj, booleanValue);
00750     if ((*jEnv)->ExceptionOccurred(jEnv)) {
00751         jsj_UnexpectedJavaError(cx, jEnv, "booleanValue() method failed");
00752         return JS_FALSE;
00753     }
00754     *vp = BOOLEAN_TO_JSVAL(b);
00755     return JS_TRUE;
00756 }
00757 
00758 /*
00759  * Reflect a Java object into a JS value.  The source object, java_obj, must
00760  * be of type java.lang.Object or a subclass and may, therefore, be an array.
00761  */
00762 JSBool
00763 jsj_ConvertJavaObjectToJSValue(JSContext *cx, JNIEnv *jEnv,
00764                                jobject java_obj, jsval *vp)
00765 {
00766     jclass java_class;
00767     JSObject *js_obj;
00768 
00769     /* A null in Java-land is also null in JS */
00770     if (!java_obj) {
00771         *vp = JSVAL_NULL;
00772         return JS_TRUE;
00773     }
00774 
00775     java_class = (*jEnv)->GetObjectClass(jEnv, java_obj);
00776 
00777     /*
00778      * If it's an instance of netscape.javascript.JSObject, i.e. a wrapper
00779      * around a JS object that has been passed into the Java world, unwrap
00780      * it to obtain the original JS object.
00781      */
00782      if (njJSObject && (*jEnv)->IsInstanceOf(jEnv, java_obj, njJSObject)) {
00783 #ifdef PRESERVE_JSOBJECT_IDENTITY
00784 #if JS_BYTES_PER_LONG == 8
00785         js_obj = (JSObject *)((*jEnv)->GetLongField(jEnv, java_obj, njJSObject_long_internal));
00786 #else
00787         js_obj = (JSObject *)((*jEnv)->GetIntField(jEnv, java_obj, njJSObject_internal));
00788 #endif
00789 #else
00790         js_obj = jsj_UnwrapJSObjectWrapper(jEnv, java_obj);
00791 #endif
00792               /* NULL is actually a valid value. It means 'null'.
00793 
00794         JS_ASSERT(js_obj);
00795         if (!js_obj)
00796             goto error;
00797               */
00798 
00799         *vp = OBJECT_TO_JSVAL(js_obj);
00800         goto done;
00801      }
00802 
00803     /* otherwise, wrap it inside a JavaObject */
00804     js_obj = jsj_WrapJavaObject(cx, jEnv, java_obj, java_class);
00805     if (!js_obj)
00806         goto error;
00807     *vp = OBJECT_TO_JSVAL(js_obj);
00808 
00809 done:
00810     (*jEnv)->DeleteLocalRef(jEnv, java_class);
00811     return JS_TRUE;
00812 
00813 error:
00814     (*jEnv)->DeleteLocalRef(jEnv, java_class);
00815     return JS_FALSE;
00816 }
00817 
00818 /*
00819  * Convert a Java value (primitive or object) to a JS value.
00820  *
00821  * This is usually an infallible operation, but JS_FALSE is returned
00822  * on an out-of-memory condition and the error reporter is called.
00823  */
00824 JSBool
00825 jsj_ConvertJavaValueToJSValue(JSContext *cx, JNIEnv *jEnv,
00826                               JavaSignature *signature,
00827                               jvalue *java_value,
00828                               jsval *vp)
00829 {
00830     int32 ival32;
00831 
00832     switch (signature->type) {
00833     case JAVA_SIGNATURE_VOID:
00834         *vp = JSVAL_VOID;
00835         return JS_TRUE;
00836 
00837     case JAVA_SIGNATURE_BYTE:
00838         *vp = INT_TO_JSVAL((jsint)java_value->b);
00839         return JS_TRUE;
00840 
00841     case JAVA_SIGNATURE_CHAR:
00842         *vp = INT_TO_JSVAL((jsint)java_value->c);
00843         return JS_TRUE;
00844 
00845     case JAVA_SIGNATURE_SHORT:
00846         *vp = INT_TO_JSVAL((jsint)java_value->s);
00847         return JS_TRUE;
00848 
00849     case JAVA_SIGNATURE_INT:
00850         ival32 = java_value->i;
00851         if (INT_FITS_IN_JSVAL(ival32)) {
00852             *vp = INT_TO_JSVAL((jsint) ival32);
00853             return JS_TRUE;
00854         } else {
00855             return JS_NewDoubleValue(cx, ival32, vp);
00856         }
00857 
00858     case JAVA_SIGNATURE_BOOLEAN:
00859         *vp = BOOLEAN_TO_JSVAL((JSBool) java_value->z);
00860         return JS_TRUE;
00861 
00862     case JAVA_SIGNATURE_LONG:
00863         return JS_NewDoubleValue(cx, jlong_to_jdouble(java_value->j), vp);
00864   
00865     case JAVA_SIGNATURE_FLOAT:
00866         return JS_NewDoubleValue(cx, java_value->f, vp);
00867 
00868     case JAVA_SIGNATURE_DOUBLE:
00869         return JS_NewDoubleValue(cx, java_value->d, vp);
00870 
00871     case JAVA_SIGNATURE_UNKNOWN:
00872         JS_ASSERT(0);
00873         return JS_FALSE;
00874         
00875     /* Non-primitive (reference) type */
00876     default:
00877         JS_ASSERT(IS_REFERENCE_TYPE(signature->type));
00878         return jsj_ConvertJavaObjectToJSValue(cx, jEnv, java_value->l, vp);
00879 
00880     }
00881 }
00882