Back to index

lightning-sunbird  0.9+nobinonly
jsj_JavaClass.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  * It contains the native code implementation of JS's JavaClass class.
00043  *
00044  * A JavaClass is JavaScript's representation of a Java class.
00045  * Its parent JS object is always a JavaPackage object.  A JavaClass is not an
00046  * exact reflection of Java's corresponding java.lang.Class object.  Rather,
00047  * the properties of a JavaClass are the static methods and properties of the
00048  * corresponding Java class.
00049  *
00050  * Note that there is no runtime equivalent to the JavaClass class in Java.
00051  * (Although there are instances of java.lang.String and there are static
00052  * methods of java.lang.String that can be invoked, there's no such thing as
00053  * a first-class object that can be referenced simply as "java.lang.String".)
00054  */
00055 
00056 #include <stdlib.h>
00057 #include <string.h>
00058 
00059 #include "jsj_private.h"        /* LiveConnect internals */
00060 #include "jscntxt.h"            /* for error reporting */
00061 
00062 JS_STATIC_DLL_CALLBACK(JSBool)
00063 JavaClass_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp)
00064 {
00065     char *name;
00066     JSString *str;
00067 
00068     JavaClassDescriptor *class_descriptor;
00069     
00070     class_descriptor = JS_GetPrivate(cx, obj);
00071     if (!class_descriptor)
00072         return JS_FALSE;
00073 
00074     switch(type) {
00075 
00076 
00077     case JSTYPE_STRING:
00078         /* Convert '/' to '.' so that it looks like Java language syntax. */
00079         if (!class_descriptor->name)
00080             break;
00081         name = JS_smprintf("[JavaClass %s]", class_descriptor->name);
00082         if (!name) {
00083             JS_ReportOutOfMemory(cx);
00084             return JS_FALSE;
00085         }
00086 
00087         str = JS_NewString(cx, name, strlen(name));
00088         if (!str) {
00089             free(name);
00090             /* It's not necessary to call JS_ReportOutOfMemory(), as
00091                JS_NewString() will do so on failure. */
00092             return JS_FALSE;
00093         }
00094 
00095         *vp = STRING_TO_JSVAL(str);
00096         return JS_TRUE;
00097 
00098     default:
00099       break;
00100     }
00101     return JS_TRUE;
00102 }
00103 
00104 static JSBool
00105 lookup_static_member_by_id(JSContext *cx, JNIEnv *jEnv, JSObject *obj,
00106                            JavaClassDescriptor **class_descriptorp,
00107                            jsid id, JavaMemberDescriptor **memberp)
00108 {
00109     jsval idval;
00110     JavaMemberDescriptor *member_descriptor;
00111     const char *member_name;
00112     JavaClassDescriptor *class_descriptor;
00113 
00114     class_descriptor = JS_GetPrivate(cx, obj);
00115     if (!class_descriptor) {
00116         *class_descriptorp = NULL;
00117         *memberp = NULL;
00118         return JS_TRUE;
00119     }
00120     
00121     if (class_descriptorp)
00122         *class_descriptorp = class_descriptor;
00123     
00124     member_descriptor = jsj_LookupJavaStaticMemberDescriptorById(cx, jEnv, class_descriptor, id);
00125     if (!member_descriptor) {
00126         JS_IdToValue(cx, id, &idval);
00127         if (!JSVAL_IS_STRING(idval)) {
00128             JS_ReportErrorNumber(cx, jsj_GetErrorMessage, NULL, 
00129                                             JSJMSG_BAD_JCLASS_EXPR);
00130             return JS_FALSE;
00131         }
00132 
00133         member_name = JS_GetStringBytes(JSVAL_TO_STRING(idval));
00134         
00135         /*
00136          * See if the property looks like the explicit resolution of an
00137          * overloaded method, e.g. "max(double,double)".
00138          */
00139         member_descriptor =
00140             jsj_ResolveExplicitMethod(cx, jEnv, class_descriptor, id, JS_TRUE);
00141         if (member_descriptor)
00142             goto done;
00143 
00144         /* Why do we have to do this ? */
00145         if (!strcmp(member_name, "prototype")) {
00146             *memberp = NULL;
00147             return JS_TRUE;
00148         }
00149 
00150         JS_ReportErrorNumber(cx, jsj_GetErrorMessage, NULL, 
00151                         JSJMSG_MISSING_NAME,
00152                        class_descriptor->name, member_name);
00153         return JS_FALSE;
00154     }
00155 
00156 done:
00157     if (memberp)
00158         *memberp = member_descriptor;
00159     return JS_TRUE;
00160 }
00161 
00162 JS_STATIC_DLL_CALLBACK(JSBool)
00163 JavaClass_getPropertyById(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
00164 {
00165     jsval idval;
00166     jclass java_class;
00167     const char *member_name;
00168     JavaClassDescriptor *class_descriptor;
00169     JavaMemberDescriptor *member_descriptor;
00170     JNIEnv *jEnv;
00171     JSJavaThreadState *jsj_env;
00172     JSBool result;
00173 
00174     /* printf("In JavaClass_getProperty\n"); */
00175     
00176     /* Get the Java per-thread environment pointer for this JSContext */
00177     jsj_env = jsj_EnterJava(cx, &jEnv);
00178     if (!jEnv)
00179         return JS_FALSE;
00180 
00181     if (!lookup_static_member_by_id(cx, jEnv, obj, &class_descriptor, id, &member_descriptor)) {
00182        jsj_ExitJava(jsj_env);
00183         return JS_FALSE;
00184     }
00185     if (!member_descriptor) {
00186         *vp = JSVAL_VOID;
00187        jsj_ExitJava(jsj_env);
00188         return JS_TRUE;
00189     }
00190 
00191     java_class = class_descriptor->java_class;
00192 
00193     if (member_descriptor->field) {
00194         if (!member_descriptor->methods) {
00195             result = jsj_GetJavaFieldValue(cx, jEnv, member_descriptor->field, java_class, vp);
00196            jsj_ExitJava(jsj_env);
00197            return result;
00198         } else {
00199             JS_ASSERT(0);
00200         }
00201     } else {
00202         JSFunction *function;
00203         
00204         /* TODO - eliminate JSFUN_BOUND_METHOD */
00205         if (member_descriptor->methods->is_alias) {
00206             /* If this is an explicit resolution of an overloaded method,
00207                use the fully-qualified method name as the name of the
00208                resulting JS function, i.e. "myMethod(int,long)" */
00209             JS_IdToValue(cx, id, &idval);
00210             member_name = JS_GetStringBytes(JSVAL_TO_STRING(idval));
00211         } else {
00212             /* Either not explicit resolution of overloaded method or
00213                explicit resolution was unnecessary because method was
00214                not overloaded. */
00215             member_name = member_descriptor->name;
00216         }
00217         function = JS_NewFunction(cx, jsj_JavaStaticMethodWrapper, 0,
00218                                   JSFUN_BOUND_METHOD, obj, member_name);
00219         if (!function) {
00220            jsj_ExitJava(jsj_env);
00221             return JS_FALSE;
00222        }
00223 
00224         *vp = OBJECT_TO_JSVAL(JS_GetFunctionObject(function));
00225     }
00226 
00227     jsj_ExitJava(jsj_env);
00228     return JS_TRUE;
00229 }
00230 
00231 JS_STATIC_DLL_CALLBACK(JSBool)
00232 JavaClass_setPropertyById(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
00233 {
00234     jclass java_class;
00235     const char *member_name;
00236     JavaClassDescriptor *class_descriptor;
00237     JavaMemberDescriptor *member_descriptor;
00238     jsval idval;
00239     JNIEnv *jEnv;
00240     JSJavaThreadState *jsj_env;
00241     JSBool result;
00242 
00243     /* printf("In JavaClass_setProperty\n"); */
00244 
00245     /* Get the Java per-thread environment pointer for this JSContext */
00246     jsj_env = jsj_EnterJava(cx, &jEnv);
00247     if (!jEnv)
00248         return JS_FALSE;
00249     
00250     if (!lookup_static_member_by_id(cx, jEnv, obj, &class_descriptor, id, &member_descriptor)) {
00251        jsj_ExitJava(jsj_env);
00252         return JS_FALSE;
00253     }
00254 
00255     /* Check for the case where there is a method with the given name, but no field
00256        with that name */
00257     if (!member_descriptor->field)
00258         goto no_such_field;
00259 
00260     /* Silently fail if field value is final (immutable), as required by ECMA spec */
00261     if (member_descriptor->field->modifiers & ACC_FINAL) {
00262        jsj_ExitJava(jsj_env);
00263         return JS_TRUE;
00264     }
00265 
00266     java_class = class_descriptor->java_class;
00267     result = jsj_SetJavaFieldValue(cx, jEnv, member_descriptor->field, java_class, *vp);
00268     jsj_ExitJava(jsj_env);
00269     return result;
00270 
00271 no_such_field:
00272     JS_IdToValue(cx, id, &idval);
00273     member_name = JS_GetStringBytes(JSVAL_TO_STRING(idval));
00274     JS_ReportErrorNumber(cx, jsj_GetErrorMessage, NULL, 
00275                    JSJMSG_MISSING_STATIC,
00276                    member_name, class_descriptor->name);
00277     jsj_ExitJava(jsj_env);
00278     return JS_FALSE;
00279 }
00280 
00281 /*
00282  * Free the private native data associated with the JavaPackage object.
00283  */
00284 JS_STATIC_DLL_CALLBACK(void)
00285 JavaClass_finalize(JSContext *cx, JSObject *obj)
00286 {
00287     JNIEnv *jEnv;
00288     JSJavaThreadState *jsj_env;
00289 
00290     JavaClassDescriptor *class_descriptor = JS_GetPrivate(cx, obj);
00291     if (!class_descriptor)
00292         return;
00293     
00294     /* Get the Java per-thread environment pointer for this JSContext */
00295     jsj_env = jsj_EnterJava(cx, &jEnv);
00296     if (!jEnv)
00297         return;
00298 
00299     /* printf("Finalizing %s\n", class_descriptor->name); */
00300     jsj_ReleaseJavaClassDescriptor(cx, jEnv, class_descriptor);
00301     jsj_ExitJava(jsj_env);
00302 }
00303 
00304 
00305 JS_STATIC_DLL_CALLBACK(JSBool)
00306 JavaClass_lookupProperty(JSContext *cx, JSObject *obj, jsid id,
00307                          JSObject **objp, JSProperty **propp)
00308 {
00309     JNIEnv *jEnv;
00310     JSErrorReporter old_reporter;
00311     JSJavaThreadState *jsj_env;
00312 
00313     /* printf("In JavaClass_lookupProperty()\n"); */
00314     
00315     /* Get the Java per-thread environment pointer for this JSContext */
00316     jsj_env = jsj_EnterJava(cx, &jEnv);
00317     if (!jEnv)
00318         return JS_FALSE;
00319 
00320     old_reporter = JS_SetErrorReporter(cx, NULL);
00321     if (lookup_static_member_by_id(cx, jEnv, obj, NULL, id, NULL)) {
00322         *objp = obj;
00323         *propp = (JSProperty*)1;
00324     } else {
00325         *objp = NULL;
00326         *propp = NULL;
00327     }
00328 
00329     JS_SetErrorReporter(cx, old_reporter);
00330     jsj_ExitJava(jsj_env);
00331     return JS_TRUE;
00332 }
00333 
00334 JS_STATIC_DLL_CALLBACK(JSBool)
00335 JavaClass_defineProperty(JSContext *cx, JSObject *obj, jsid id, jsval value,
00336                          JSPropertyOp getter, JSPropertyOp setter,
00337                          uintN attrs, JSProperty **propp)
00338 {
00339     JavaClassDescriptor *class_descriptor;
00340     
00341     class_descriptor = JS_GetPrivate(cx, obj);
00342 
00343     /* Check for prototype JavaClass object */
00344     if (!class_descriptor)
00345        return JS_TRUE;
00346 
00347     JS_ReportErrorNumber(cx, jsj_GetErrorMessage, NULL, 
00348                          JSJMSG_JCLASS_PROP_DEFINE);
00349     return JS_FALSE;
00350 }
00351 
00352 JS_STATIC_DLL_CALLBACK(JSBool)
00353 JavaClass_getAttributes(JSContext *cx, JSObject *obj, jsid id,
00354                         JSProperty *prop, uintN *attrsp)
00355 {
00356     /* We don't maintain JS property attributes for Java class members */
00357     *attrsp = JSPROP_PERMANENT|JSPROP_ENUMERATE;
00358     return JS_FALSE;
00359 }
00360 
00361 JS_STATIC_DLL_CALLBACK(JSBool)
00362 JavaClass_setAttributes(JSContext *cx, JSObject *obj, jsid id,
00363                         JSProperty *prop, uintN *attrsp)
00364 {
00365     /* We don't maintain JS property attributes for Java class members */
00366     if (*attrsp != (JSPROP_PERMANENT|JSPROP_ENUMERATE)) {
00367         JS_ASSERT(0);
00368         return JS_FALSE;
00369     }
00370 
00371     /* Silently ignore all setAttribute attempts */
00372     return JS_TRUE;
00373 }
00374 
00375 JS_STATIC_DLL_CALLBACK(JSBool)
00376 JavaClass_deleteProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
00377 {
00378     JSVersion version = JS_GetVersion(cx);
00379     
00380     *vp = JSVAL_FALSE;
00381 
00382     if (!JSVERSION_IS_ECMA(version)) {
00383         JS_ReportErrorNumber(cx, jsj_GetErrorMessage, NULL,
00384                                             JSJMSG_JCLASS_PROP_DELETE);
00385         return JS_FALSE;
00386     } else {
00387         /* Attempts to delete permanent properties are silently ignored
00388            by ECMAScript. */
00389         return JS_TRUE;
00390     }
00391 }
00392 
00393 JS_STATIC_DLL_CALLBACK(JSBool)
00394 JavaClass_defaultValue(JSContext *cx, JSObject *obj, JSType type, jsval *vp)
00395 {
00396     /* printf("In JavaClass_defaultValue()\n"); */
00397     return JavaClass_convert(cx, obj, JSTYPE_STRING, vp);
00398 }
00399 
00400 JS_STATIC_DLL_CALLBACK(JSBool)
00401 JavaClass_newEnumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
00402                        jsval *statep, jsid *idp)
00403 {
00404     JavaMemberDescriptor *member_descriptor;
00405     JavaClassDescriptor *class_descriptor;
00406     JNIEnv *jEnv;
00407     JSJavaThreadState *jsj_env;
00408     
00409     class_descriptor = JS_GetPrivate(cx, obj);
00410 
00411     /* Check for prototype JavaClass object */
00412     if (!class_descriptor) {
00413         *statep = JSVAL_NULL;
00414         if (idp)
00415             *idp = INT_TO_JSVAL(0);
00416         return JS_TRUE;
00417     }
00418     
00419     switch(enum_op) {
00420     case JSENUMERATE_INIT:
00421         /* Get the Java per-thread environment pointer for this JSContext */
00422         jsj_env = jsj_EnterJava(cx, &jEnv);
00423         if (!jEnv)
00424             return JS_FALSE;
00425         member_descriptor = jsj_GetClassStaticMembers(cx, jEnv, class_descriptor);
00426         *statep = PRIVATE_TO_JSVAL(member_descriptor);
00427         if (idp)
00428             *idp = INT_TO_JSVAL(class_descriptor->num_instance_members);
00429        jsj_ExitJava(jsj_env);
00430         return JS_TRUE;
00431         
00432     case JSENUMERATE_NEXT:
00433         member_descriptor = JSVAL_TO_PRIVATE(*statep);
00434         if (member_descriptor) {
00435 
00436             /* Don't enumerate explicit-signature methods, i.e. enumerate toValue,
00437                but not toValue(int), toValue(double), etc. */
00438             while (member_descriptor->methods && member_descriptor->methods->is_alias) {
00439                 member_descriptor = member_descriptor->next;
00440                 if (!member_descriptor) {
00441                     *statep = JSVAL_NULL;
00442                     return JS_TRUE;
00443                 }
00444             }
00445 
00446             *idp = member_descriptor->id;
00447             *statep = PRIVATE_TO_JSVAL(member_descriptor->next);
00448             return JS_TRUE;
00449         }
00450         /* Fall through ... */
00451 
00452     case JSENUMERATE_DESTROY:
00453         *statep = JSVAL_NULL;
00454         return JS_TRUE;
00455 
00456     default:
00457         JS_ASSERT(0);
00458         return JS_FALSE;
00459     }
00460 }
00461 
00462 JS_STATIC_DLL_CALLBACK(JSBool)
00463 JavaClass_checkAccess(JSContext *cx, JSObject *obj, jsid id,
00464                       JSAccessMode mode, jsval *vp, uintN *attrsp)
00465 {
00466     switch (mode) {
00467     case JSACC_WATCH:
00468         JS_ReportErrorNumber(cx, jsj_GetErrorMessage, NULL, 
00469                                             JSJMSG_JCLASS_PROP_WATCH);
00470         return JS_FALSE;
00471 
00472     case JSACC_IMPORT:
00473         JS_ReportErrorNumber(cx, jsj_GetErrorMessage, NULL, 
00474                                             JSJMSG_JCLASS_PROP_EXPORT);
00475         return JS_FALSE;
00476 
00477     default:
00478         return JS_TRUE;
00479     }
00480 }
00481 
00482 /*
00483  * Implement the JavaScript instanceof operator for JavaClass objects by using
00484  * the equivalent Java instanceof operation.
00485  */
00486 JS_STATIC_DLL_CALLBACK(JSBool)
00487 JavaClass_hasInstance(JSContext *cx, JSObject *obj, jsval candidate_jsval,
00488                       JSBool *has_instancep)
00489 {
00490     JavaClassDescriptor *class_descriptor;
00491     JavaObjectWrapper *java_wrapper;
00492     JSClass *js_class;
00493     JSBool has_instance;
00494     JSObject *candidate_obj;
00495     jclass java_class;
00496     jobject java_obj;
00497     JNIEnv *jEnv;
00498     JSJavaThreadState *jsj_env;
00499     
00500     has_instance = JS_FALSE;
00501     class_descriptor = JS_GetPrivate(cx, obj);
00502     if (!class_descriptor) {
00503         JS_ReportErrorNumber(cx, jsj_GetErrorMessage, NULL, 
00504                                                     JSJMSG_BAD_OP_JCLASS);
00505         return JS_FALSE;
00506     }
00507 
00508     /*
00509      * Make sure that the thing to the left of the instanceof operator is a
00510      * Java object.
00511      */
00512     if (!JSVAL_IS_OBJECT(candidate_jsval))
00513         goto done;
00514     candidate_obj = JSVAL_TO_OBJECT(candidate_jsval);
00515 #ifdef JS_THREADSAFE
00516     js_class = JS_GetClass(cx, candidate_obj);
00517 #else
00518     js_class = JS_GetClass(candidate_obj);
00519 #endif
00520     if ((js_class != &JavaObject_class) && (js_class != &JavaArray_class))
00521         goto done;
00522 
00523     java_class = class_descriptor->java_class;
00524     java_wrapper = JS_GetPrivate(cx, candidate_obj);
00525     if (!java_wrapper) {
00526         JS_ReportErrorNumber(cx, jsj_GetErrorMessage, NULL, 
00527                                                 JSJMSG_BAD_OP_PROTO);
00528         return JS_FALSE;
00529     }
00530     java_obj = java_wrapper->java_obj;
00531     /* Get JNI pointer */
00532     jsj_env = jsj_EnterJava(cx, &jEnv);
00533     has_instance = (*jEnv)->IsInstanceOf(jEnv, java_obj, java_class);
00534     jsj_ExitJava(jsj_env);
00535 
00536 done:
00537     *has_instancep = has_instance;
00538     return JS_TRUE;
00539 }
00540 
00541 JSObjectOps JavaClass_ops = {
00542     /* Mandatory non-null function pointer members. */
00543     jsj_wrapper_newObjectMap,       /* newObjectMap */
00544     jsj_wrapper_destroyObjectMap,   /* destroyObjectMap */
00545     JavaClass_lookupProperty,
00546     JavaClass_defineProperty,
00547     JavaClass_getPropertyById,      /* getProperty */
00548     JavaClass_setPropertyById,      /* setProperty */
00549     JavaClass_getAttributes,
00550     JavaClass_setAttributes,
00551     JavaClass_deleteProperty,
00552     JavaClass_defaultValue,
00553     JavaClass_newEnumerate,
00554     JavaClass_checkAccess,
00555 
00556     /* Optionally non-null members start here. */
00557     NULL,                           /* thisObject */
00558     NULL,                           /* dropProperty */
00559     jsj_JavaConstructorWrapper,     /* call */
00560     jsj_JavaConstructorWrapper,     /* construct */
00561     NULL,                           /* xdrObject */
00562     JavaClass_hasInstance,          /* hasInstance */
00563     NULL,                           /* setProto */
00564     NULL,                           /* setParent */
00565     NULL,                           /* mark */
00566     NULL,                           /* clear */
00567     jsj_wrapper_getRequiredSlot,    /* getRequiredSlot */
00568     jsj_wrapper_setRequiredSlot     /* setRequiredSlot */
00569 };
00570 
00571 JS_STATIC_DLL_CALLBACK(JSObjectOps *)
00572 JavaClass_getObjectOps(JSContext *cx, JSClass *clazz)
00573 {
00574     return &JavaClass_ops;
00575 }
00576 
00577 JSClass JavaClass_class = {
00578     "JavaClass", JSCLASS_HAS_PRIVATE,
00579     NULL, NULL, NULL, NULL,
00580     NULL, NULL, JavaClass_convert, JavaClass_finalize,
00581 
00582     /* Optionally non-null members start here. */
00583     JavaClass_getObjectOps,
00584     NULL,
00585     NULL,
00586     NULL,
00587     NULL,
00588     NULL,
00589     NULL,
00590     0,
00591 };
00592 
00593 static JSObject *
00594 jsj_new_JavaClass(JSContext *cx, JNIEnv *jEnv, JSObject* parent_obj,
00595                   JavaClassDescriptor *class_descriptor)
00596 {
00597     JSObject *JavaClass_obj;
00598 
00599     JavaClass_obj = JS_NewObject(cx, &JavaClass_class, 0, parent_obj);
00600     if (!JavaClass_obj)
00601         return NULL;
00602 
00603     JS_SetPrivate(cx, JavaClass_obj, (void *)class_descriptor);
00604 
00605 #ifdef DEBUG
00606     /*    printf("JavaClass \'%s\' created\n", class_descriptor->name); */
00607 #endif
00608 
00609     return JavaClass_obj;
00610 }
00611 
00612 JSObject *
00613 jsj_define_JavaClass(JSContext *cx, JNIEnv *jEnv, JSObject* parent_obj,
00614                      const char *simple_class_name,
00615                      jclass java_class)
00616 {
00617     JavaClassDescriptor *class_descriptor;
00618     JSObject *JavaClass_obj;
00619 
00620     class_descriptor = jsj_GetJavaClassDescriptor(cx, jEnv, java_class);
00621     if (!class_descriptor)
00622         return NULL;
00623 
00624     JavaClass_obj = jsj_new_JavaClass(cx, jEnv, parent_obj, class_descriptor);
00625     if (!JavaClass_obj)
00626         return NULL;
00627 
00628     if (!JS_DefineProperty(cx, parent_obj, simple_class_name,
00629                            OBJECT_TO_JSVAL(JavaClass_obj), 0, 0,
00630                            JSPROP_PERMANENT|JSPROP_READONLY|JSPROP_ENUMERATE))
00631         return NULL;
00632     return JavaClass_obj;
00633 }
00634 
00635 
00636 /*
00637  * The getClass() native JS method is defined as a property of the global
00638  * object.  Given a JavaObject it returns the corresponding JavaClass.  This
00639  * is useful for accessing static methods and fields.
00640  *
00641  *    js> getClass(new java.lang.String("foo"))
00642  *    [JavaClass java.lang.String]
00643  */
00644 JS_STATIC_DLL_CALLBACK(JSBool)
00645 getClass(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
00646 {
00647     JSObject *obj_arg, *JavaClass_obj;
00648     JavaObjectWrapper *java_wrapper;
00649     JavaClassDescriptor *class_descriptor;
00650     JNIEnv *jEnv;
00651     JSJavaThreadState *jsj_env;
00652 
00653     if (argc != 1 ||
00654     !JSVAL_IS_OBJECT(argv[0]) ||
00655     !(obj_arg = JSVAL_TO_OBJECT(argv[0])) ||
00656     (!JS_InstanceOf(cx, obj_arg, &JavaObject_class, 0) &&
00657          !JS_InstanceOf(cx, obj_arg, &JavaArray_class, 0))) {
00658         JS_ReportErrorNumber(cx, jsj_GetErrorMessage, NULL, 
00659                                                 JSJMSG_NEED_JOBJECT_ARG);
00660         return JS_FALSE;
00661     }
00662 
00663     java_wrapper = JS_GetPrivate(cx, obj_arg);
00664     if (!java_wrapper) {
00665         JS_ReportErrorNumber(cx, jsj_GetErrorMessage, NULL, 
00666                                                 JSJMSG_PROTO_GETCLASS);
00667         return JS_FALSE;
00668     }
00669 
00670     jsj_env = jsj_EnterJava(cx, &jEnv);
00671     if (!jEnv)
00672         return JS_FALSE;
00673     
00674     class_descriptor = java_wrapper->class_descriptor;
00675 
00676     JavaClass_obj = jsj_new_JavaClass(cx, jEnv, NULL, class_descriptor);
00677     if (!JavaClass_obj) {
00678        jsj_ExitJava(jsj_env);
00679         return JS_FALSE;
00680     }
00681 
00682     *rval = OBJECT_TO_JSVAL(JavaClass_obj);
00683     jsj_ExitJava(jsj_env);
00684     return JS_TRUE;
00685 }
00686 
00687 JS_STATIC_DLL_CALLBACK(JSBool)
00688 JavaClass_construct(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
00689 {
00690     JSObject *obj_arg, *JavaClass_obj;
00691     JavaObjectWrapper *java_wrapper;
00692     JavaClassDescriptor *class_descriptor;
00693     JNIEnv *jEnv;
00694     JSJavaThreadState *jsj_env;
00695 
00696     if (argc != 1 ||
00697        !JSVAL_IS_OBJECT(argv[0]) ||
00698        !(obj_arg = JSVAL_TO_OBJECT(argv[0])) ||
00699        !JS_InstanceOf(cx, obj_arg, &JavaObject_class, 0) ||
00700        ((java_wrapper = JS_GetPrivate(cx, obj_arg)) == NULL)) {
00701         JS_ReportErrorNumber(cx, jsj_GetErrorMessage, NULL, 
00702                              JSJMSG_NEED_JCLASS_ARG);
00703         return JS_FALSE;
00704     }
00705 
00706     jsj_env = jsj_EnterJava(cx, &jEnv);
00707     if (!jEnv)
00708         return JS_FALSE;
00709 
00710     class_descriptor = java_wrapper->class_descriptor;
00711     if (!(*jEnv)->IsSameObject(jEnv, class_descriptor->java_class, jlClass)) {
00712        JS_ReportErrorNumber(cx, jsj_GetErrorMessage, NULL, 
00713                              JSJMSG_NEED_JCLASS_ARG);
00714        jsj_ExitJava(jsj_env);
00715         return JS_FALSE;
00716     }
00717 
00718     class_descriptor = jsj_GetJavaClassDescriptor(cx, jEnv, java_wrapper->java_obj);
00719     JavaClass_obj = jsj_new_JavaClass(cx, jEnv, NULL, class_descriptor);
00720     if (!JavaClass_obj) {
00721        jsj_ExitJava(jsj_env);
00722         return JS_FALSE;
00723     }
00724 
00725     *rval = OBJECT_TO_JSVAL(JavaClass_obj);
00726     jsj_ExitJava(jsj_env);
00727     return JS_TRUE;
00728 }
00729 
00730 extern JS_IMPORT_DATA(JSObjectOps) js_ObjectOps;
00731 
00732 JSBool
00733 jsj_init_JavaClass(JSContext *cx, JSObject *global_obj)
00734 {
00735     /* Define JavaClass class */
00736     if (!JS_InitClass(cx, global_obj, 0, &JavaClass_class, JavaClass_construct, 0, 0, 0, 0, 0))
00737         return JS_FALSE;
00738 
00739     if (!JS_DefineFunction(cx, global_obj, "getClass", getClass, 0,
00740                            JSPROP_READONLY))
00741         return JS_FALSE;
00742 
00743     return jsj_InitJavaClassReflectionsTable();
00744 }
00745