Back to index

lightning-sunbird  0.9+nobinonly
jsj_JavaArray.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 /*
00041  * This file is part of the Java-vendor-neutral implementation of LiveConnect
00042  *
00043  * It contains the definition of the JavaScript JavaArray class.
00044  * Instances of JavaArray are used to reflect Java arrays.
00045  */
00046 
00047 #include <stdlib.h>
00048 #include <string.h>
00049 
00050 #include "jsj_private.h"      /* LiveConnect internals */
00051 
00052 /* Shorthands for ASCII (7-bit) decimal and hex conversion. */
00053 #define JS7_ISDEC(c)    (((c) >= '0') && ((c) <= '9'))
00054 #define JS7_UNDEC(c)    ((c) - '0')
00055 
00056 /*
00057  * Convert any jsval v to an integer jsval if ToString(v)
00058  * contains a base-10 integer that fits into 31 bits.
00059  * Otherwise return v.
00060  */
00061 static jsval
00062 try_convert_to_jsint(JSContext *cx, jsval idval)
00063 {
00064     const jschar *cp;
00065     JSString *jsstr;
00066     
00067     jsstr = JS_ValueToString(cx, idval);
00068     if (!jsstr)
00069         return idval;
00070 
00071     cp = JS_GetStringChars(jsstr);
00072     if (JS7_ISDEC(*cp)) {
00073         jsuint index = JS7_UNDEC(*cp++);
00074         jsuint oldIndex = 0;
00075         jsuint c = 0;
00076         if (index != 0) {
00077             while (JS7_ISDEC(*cp)) {
00078                 oldIndex = index;
00079                 c = JS7_UNDEC(*cp);
00080                 index = 10*index + c;
00081                 cp++;
00082             }
00083         }
00084         if (*cp == 0 &&
00085             (oldIndex < (JSVAL_INT_MAX / 10) ||
00086             (oldIndex == (JSVAL_INT_MAX / 10) && c < (JSVAL_INT_MAX % 10)))) {
00087             return INT_TO_JSVAL(index);
00088         }
00089     }
00090     return idval;
00091 }
00092 
00093 
00094 static JSBool
00095 access_java_array_element(JSContext *cx,
00096                           JNIEnv *jEnv,
00097                           JSObject *obj,
00098                           jsid id,
00099                           jsval *vp,
00100                           JSBool do_assignment)
00101 {
00102     jsval idval;
00103     jarray java_array;
00104     JavaClassDescriptor *class_descriptor;
00105     JavaObjectWrapper *java_wrapper;
00106     jsize array_length, index;
00107     JavaSignature *array_component_signature;
00108     
00109     /* printf("In JavaArray_getProperty\n"); */
00110     
00111     java_wrapper = JS_GetPrivate(cx, obj);
00112     if (!java_wrapper) {
00113         const char *property_name;
00114         if (JS_IdToValue(cx, id, &idval) && JSVAL_IS_STRING(idval) &&
00115             (property_name = JS_GetStringBytes(JSVAL_TO_STRING(idval))) != NULL) {
00116             if (!strcmp(property_name, "constructor")) {
00117                 if (vp)
00118                     *vp = JSVAL_VOID;
00119                 return JS_TRUE;
00120             }
00121         }
00122         JS_ReportErrorNumber(cx, jsj_GetErrorMessage, NULL, 
00123                                                 JSJMSG_BAD_OP_JARRAY);
00124         return JS_FALSE;
00125     }
00126     class_descriptor = java_wrapper->class_descriptor;
00127     java_array = java_wrapper->java_obj;
00128     
00129     JS_ASSERT(class_descriptor->type == JAVA_SIGNATURE_ARRAY);
00130 
00131     JS_IdToValue(cx, id, &idval);
00132 
00133     if (!JSVAL_IS_INT(idval))
00134         idval = try_convert_to_jsint(cx, idval);
00135 
00136     if (!JSVAL_IS_INT(idval)) {
00137         /*
00138          * Usually, properties of JavaArray objects are indexed by integers, but
00139          * Java arrays also inherit all the methods of java.lang.Object, so a
00140          * string-valued property is also possible.
00141          */
00142         if (JSVAL_IS_STRING(idval)) {
00143             const char *member_name;
00144             
00145             member_name = JS_GetStringBytes(JSVAL_TO_STRING(idval));
00146             
00147             if (do_assignment) {
00148                 JSVersion version = JS_GetVersion(cx);
00149 
00150                 if (!JSVERSION_IS_ECMA(version)) {
00151  
00152                     JS_ReportErrorNumber(cx, jsj_GetErrorMessage, NULL, 
00153                                         JSJMSG_CANT_WRITE_JARRAY, member_name);
00154                     return JS_FALSE;
00155                 } else {
00156                     if (vp)
00157                         *vp = JSVAL_VOID;
00158                     return JS_TRUE;
00159                 }
00160             } else {
00161                 if (!strcmp(member_name, "length")) {
00162                     array_length = jsj_GetJavaArrayLength(cx, jEnv, java_array);
00163                     if (array_length < 0)
00164                         return JS_FALSE;
00165                     if (vp)
00166                         *vp = INT_TO_JSVAL(array_length);
00167                     return JS_TRUE;
00168                 }
00169                 
00170                 /* Check to see if we're reflecting a Java array method */
00171                 return JavaObject_getPropertyById(cx, obj, id, vp);
00172             }
00173         }
00174 
00175         JS_ReportErrorNumber(cx, jsj_GetErrorMessage, NULL, 
00176                                             JSJMSG_BAD_INDEX_EXPR);
00177         return JS_FALSE;
00178     }
00179     
00180     index = JSVAL_TO_INT(idval);
00181 
00182 #if 0
00183     array_length = jsj_GetJavaArrayLength(cx, jEnv, java_array);
00184     if (array_length < 0)
00185         return JS_FALSE;
00186 
00187     /* Just let Java throw an exception instead of checking array bounds here */
00188     if (index < 0 || index >= array_length) {
00189         char numBuf[12];
00190         sprintf(numBuf, "%d", index);
00191         JS_ReportErrorNumber(cx, jsj_GetErrorMessage, NULL,
00192                                             JSJMSG_BAD_JARRAY_INDEX, numBuf);
00193         return JS_FALSE;
00194     }
00195 #endif
00196 
00197     array_component_signature = class_descriptor->array_component_signature;
00198 
00199     if (!vp)
00200         return JS_TRUE;
00201 
00202     if (do_assignment) {
00203         return jsj_SetJavaArrayElement(cx, jEnv, java_array, index,
00204                                        array_component_signature, *vp);
00205     } else {
00206         return jsj_GetJavaArrayElement(cx, jEnv, java_array, index,
00207                                        array_component_signature, vp);
00208     }
00209 }
00210 
00211 JS_STATIC_DLL_CALLBACK(JSBool)
00212 JavaArray_getPropertyById(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
00213 {
00214     JNIEnv *jEnv;
00215     JSJavaThreadState *jsj_env;
00216     JSBool result;
00217 
00218     jsj_env = jsj_EnterJava(cx, &jEnv);
00219     if (!jEnv)
00220         return JS_FALSE;
00221     result = access_java_array_element(cx, jEnv, obj, id, vp, JS_FALSE);
00222     jsj_ExitJava(jsj_env);
00223     return result;
00224 }
00225 
00226 JS_STATIC_DLL_CALLBACK(JSBool)
00227 JavaArray_setPropertyById(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
00228 {
00229     JNIEnv *jEnv;
00230     JSJavaThreadState *jsj_env;
00231     JSBool result;
00232     
00233     jsj_env = jsj_EnterJava(cx, &jEnv);
00234     if (!jEnv)
00235         return JS_FALSE;
00236     result = access_java_array_element(cx, jEnv, obj, id, vp, JS_TRUE);
00237     jsj_ExitJava(jsj_env);
00238     return result;
00239 }
00240 
00241 JS_STATIC_DLL_CALLBACK(JSBool)
00242 JavaArray_lookupProperty(JSContext *cx, JSObject *obj, jsid id,
00243                          JSObject **objp, JSProperty **propp)
00244 {
00245     JNIEnv *jEnv;
00246     JSErrorReporter old_reporter;
00247     JSJavaThreadState *jsj_env;
00248 
00249     jsj_env = jsj_EnterJava(cx, &jEnv);
00250     if (!jEnv)
00251         return JS_FALSE;
00252 
00253     old_reporter = JS_SetErrorReporter(cx, NULL);
00254     if (access_java_array_element(cx, jEnv, obj, id, NULL, JS_FALSE)) {
00255         *objp = obj;
00256         *propp = (JSProperty*)1;
00257     } else {
00258         *objp = NULL;
00259         *propp = NULL;
00260     }
00261     JS_SetErrorReporter(cx, old_reporter);
00262     jsj_ExitJava(jsj_env);
00263     return JS_TRUE;
00264 }
00265 
00266 JS_STATIC_DLL_CALLBACK(JSBool)
00267 JavaArray_defineProperty(JSContext *cx, JSObject *obj, jsid id, jsval value,
00268                          JSPropertyOp getter, JSPropertyOp setter,
00269                          uintN attrs, JSProperty **propp)
00270 {
00271     jsval *vp = &value;
00272     if (propp)
00273         return JS_FALSE;
00274     if (attrs & ~(JSPROP_PERMANENT|JSPROP_ENUMERATE))
00275         return JS_FALSE;
00276 
00277     return JavaArray_setPropertyById(cx, obj, id, vp);
00278 }
00279 
00280 JS_STATIC_DLL_CALLBACK(JSBool)
00281 JavaArray_getAttributes(JSContext *cx, JSObject *obj, jsid id,
00282                         JSProperty *prop, uintN *attrsp)
00283 {
00284     /* We don't maintain JS property attributes for Java class members */
00285     *attrsp = JSPROP_PERMANENT|JSPROP_ENUMERATE;
00286     return JS_FALSE;
00287 }
00288 
00289 JS_STATIC_DLL_CALLBACK(JSBool)
00290 JavaArray_setAttributes(JSContext *cx, JSObject *obj, jsid id,
00291                         JSProperty *prop, uintN *attrsp)
00292 {
00293     /* We don't maintain JS property attributes for Java class members */
00294     if (*attrsp != (JSPROP_PERMANENT|JSPROP_ENUMERATE)) {
00295         JS_ASSERT(0);
00296         return JS_FALSE;
00297     }
00298 
00299     /* Silently ignore all setAttribute attempts */
00300     return JS_TRUE;
00301 }
00302 
00303 JS_STATIC_DLL_CALLBACK(JSBool)
00304 JavaArray_deleteProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
00305 {
00306     JSVersion version = JS_GetVersion(cx);
00307 
00308     *vp = JSVAL_FALSE;
00309     
00310     if (!JSVERSION_IS_ECMA(version)) {
00311         JS_ReportErrorNumber(cx, jsj_GetErrorMessage, NULL, 
00312                                             JSJMSG_JARRAY_PROP_DELETE);
00313         return JS_FALSE;
00314     } else {
00315         /* Attempts to delete permanent properties are silently ignored
00316            by ECMAScript. */
00317         return JS_TRUE;
00318     }
00319 }
00320 
00321 JS_STATIC_DLL_CALLBACK(JSBool)
00322 JavaArray_defaultValue(JSContext *cx, JSObject *obj, JSType type, jsval *vp)
00323 {
00324     /* printf("In JavaArray_defaultValue()\n"); */
00325     return JavaObject_convert(cx, obj, JSTYPE_STRING, vp);
00326 }
00327 
00328 JS_STATIC_DLL_CALLBACK(JSBool)
00329 JavaArray_newEnumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
00330                        jsval *statep, jsid *idp)
00331 {
00332     JavaObjectWrapper *java_wrapper;
00333     JSJavaThreadState *jsj_env;
00334     JNIEnv *jEnv;
00335     jsize array_length, index;
00336     JSBool ok = JS_TRUE;
00337 
00338     java_wrapper = JS_GetPrivate(cx, obj);
00339     /* Check for prototype object */
00340     if (!java_wrapper) {
00341         *statep = JSVAL_NULL;
00342         if (idp)
00343             *idp = INT_TO_JSVAL(0);
00344         return JS_TRUE;
00345     }
00346         
00347     /* Get the Java per-thread environment pointer for this JSContext */
00348     jsj_env = jsj_EnterJava(cx, &jEnv);
00349     if (!jEnv)
00350         return JS_FALSE;
00351 
00352     array_length = jsj_GetJavaArrayLength(cx, jEnv, java_wrapper->java_obj);
00353     if (array_length < 0) {
00354         jsj_ExitJava(jsj_env);
00355         return JS_FALSE;
00356     }
00357 
00358     switch(enum_op) {
00359     case JSENUMERATE_INIT:
00360         *statep = INT_TO_JSVAL(0);
00361 
00362         if (idp)
00363             *idp = INT_TO_JSVAL(array_length);
00364         break;
00365         
00366     case JSENUMERATE_NEXT:
00367         index = JSVAL_TO_INT(*statep);
00368         if (index < array_length) {
00369             JS_ValueToId(cx, INT_TO_JSVAL(index), idp);
00370             index++;
00371             *statep = INT_TO_JSVAL(index);
00372             break;
00373         }
00374 
00375         /* Fall through ... */
00376 
00377     case JSENUMERATE_DESTROY:
00378         *statep = JSVAL_NULL;
00379         break;
00380 
00381     default:
00382         JS_ASSERT(0);
00383         ok = JS_FALSE;
00384         break;
00385     }
00386 
00387     jsj_ExitJava(jsj_env);
00388     return ok;
00389 }
00390 
00391 JS_STATIC_DLL_CALLBACK(JSBool)
00392 JavaArray_checkAccess(JSContext *cx, JSObject *obj, jsid id,
00393                       JSAccessMode mode, jsval *vp, uintN *attrsp)
00394 {
00395     switch (mode) {
00396     case JSACC_WATCH:
00397         JS_ReportErrorNumber(cx, jsj_GetErrorMessage, NULL, 
00398                                             JSJMSG_JARRAY_PROP_WATCH);
00399         return JS_FALSE;
00400 
00401     case JSACC_IMPORT:
00402         JS_ReportErrorNumber(cx, jsj_GetErrorMessage, NULL, 
00403                                             JSJMSG_JARRAY_PROP_EXPORT);
00404         return JS_FALSE;
00405 
00406     default:
00407         return JS_TRUE;
00408     }
00409 }
00410 
00411 JSObjectOps JavaArray_ops = {
00412     /* Mandatory non-null function pointer members. */
00413     jsj_wrapper_newObjectMap,       /* newObjectMap */
00414     jsj_wrapper_destroyObjectMap,   /* destroyObjectMap */
00415     JavaArray_lookupProperty,
00416     JavaArray_defineProperty,
00417     JavaArray_getPropertyById,      /* getProperty */
00418     JavaArray_setPropertyById,      /* setProperty */
00419     JavaArray_getAttributes,
00420     JavaArray_setAttributes,
00421     JavaArray_deleteProperty,
00422     JavaArray_defaultValue,
00423     JavaArray_newEnumerate,
00424     JavaArray_checkAccess,
00425 
00426     /* Optionally non-null members start here. */
00427     NULL,                           /* thisObject */
00428     NULL,                           /* dropProperty */
00429     NULL,                           /* call */
00430     NULL,                           /* construct */
00431     NULL,                           /* xdrObject */
00432     NULL,                           /* hasInstance */
00433     NULL,                           /* setProto */
00434     NULL,                           /* setParent */
00435     NULL,                           /* mark */
00436     NULL,                           /* clear */
00437     jsj_wrapper_getRequiredSlot,    /* getRequiredSlot */
00438     jsj_wrapper_setRequiredSlot     /* setRequiredSlot */
00439 };
00440 
00441 JS_STATIC_DLL_CALLBACK(JSObjectOps *)
00442 JavaArray_getObjectOps(JSContext *cx, JSClass *clazz)
00443 {
00444     return &JavaArray_ops;
00445 }
00446 
00447 JSClass JavaArray_class = {
00448     "JavaArray", JSCLASS_HAS_PRIVATE,
00449     NULL, NULL, NULL, NULL,
00450     NULL, NULL, JavaObject_convert, JavaObject_finalize,
00451 
00452     /* Optionally non-null members start here. */
00453     JavaArray_getObjectOps,
00454     NULL,
00455     NULL,
00456     NULL,
00457     NULL,
00458     NULL,
00459     NULL,
00460     0,
00461 };
00462 
00463 extern JS_IMPORT_DATA(JSObjectOps) js_ObjectOps;
00464 
00465 
00466 /* Initialize the JS JavaArray class */
00467 JSBool
00468 jsj_init_JavaArray(JSContext *cx, JSObject *global_obj)
00469 {
00470     if (!JS_InitClass(cx, global_obj, 
00471         0, &JavaArray_class, 0, 0,
00472         0, 0, 0, 0))
00473         return JS_FALSE;
00474     
00475     return JS_TRUE;
00476 }
00477