home *** CD-ROM | disk | FTP | other *** search
/ Tools / WinSN5.0Ver.iso / NETSCAP.50 / WIN1998.ZIP / ns / js / jsj / jsjava.c < prev    next >
Encoding:
C/C++ Source or Header  |  1998-04-08  |  121.7 KB  |  4,525 lines

  1. /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  2.  *
  3.  * The contents of this file are subject to the Netscape Public License
  4.  * Version 1.0 (the "NPL"); you may not use this file except in
  5.  * compliance with the NPL.  You may obtain a copy of the NPL at
  6.  * http://www.mozilla.org/NPL/
  7.  *
  8.  * Software distributed under the NPL is distributed on an "AS IS" basis,
  9.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
  10.  * for the specific language governing rights and limitations under the
  11.  * NPL.
  12.  *
  13.  * The Initial Developer of this code under the NPL is Netscape
  14.  * Communications Corporation.  Portions created by Netscape are
  15.  * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
  16.  * Reserved.
  17.  */
  18. /* ** */
  19. /*
  20.  * JS reflection of Java objects and vice versa
  21.  *
  22.  *   javapackage is a java namespace
  23.  *   java is a java class or object
  24.  *   javaarray is a java array
  25.  *   javaslot represents an object+name that may resolve to
  26.  *     either a field or a method depending on later usage.
  27.  *
  28.  *   netscape.javascript.JSObject is a java reflection of a JS object
  29.  */
  30.  
  31. #ifdef JAVA
  32.  
  33. #include <stdlib.h>
  34. #include <string.h>
  35.  
  36. /* java joy */
  37. #include "oobj.h"
  38. #include "interpreter.h"
  39. #include "tree.h"
  40. #include "opcodes.h"
  41. #include "javaString.h"
  42. #include "exceptions.h"
  43. #include "jri.h"
  44.  
  45. /* nspr stuph */
  46. #ifndef NSPR20
  47. #include "prhash.h"
  48. #else
  49. #include "plhash.h"
  50. #endif
  51. #include "prmon.h"      /* for PR_XLock and PR_XUNlock */
  52. #include "prlog.h"
  53. #include "prprf.h"
  54. #include "prgc.h"
  55.  
  56. #include "jsapi.h"
  57. #include "jsjava.h"
  58. #include "jscntxt.h"    /* for cx->savedErrors */
  59. #include "jsscript.h"   /* for script->javaData */
  60. #include "jsobj.h"      /* for OBJ_GET_SLOT and JSSLOT_PRIVATE */
  61. #include "jsfun.h"      /* for JSFUN_BOUND_METHOD */
  62. #include "jslock.h"     /* for JS_LOCK_RUNTIME/JS_UNLOCK_RUNTIME */
  63.  
  64. #ifdef MACLINUX
  65. #define VARARGS_ASSIGN(foo, bar) foo[0] = bar[0]
  66. #else
  67. #define VARARGS_ASSIGN(foo, bar) (foo) = (bar)
  68. #endif /*MACLINUX*/
  69.  
  70. #ifndef NSPR20
  71. #define WAIT_FOREVER LL_MAXINT
  72. #else
  73. #define WAIT_FOREVER PR_INTERVAL_NO_TIMEOUT
  74. /* PR_LOG hacks */
  75. #define debug PR_LOG_MAX
  76. #define error PR_LOG_ERROR
  77. #define warn PR_LOG_WARNING
  78. #endif
  79.  
  80. /*
  81.  * "exception" is defined by some Windows OS headers.
  82.  * Redefine it for our own purposes.
  83.  */
  84. #ifdef exception
  85. #undef exception
  86. #endif
  87.  
  88. /*
  89.  * we don't have access to the classJavaLangObject from the java
  90.  * runtime dll, so we need a different version of this macro
  91.  */
  92. static ClassClass *js_classJavaLangObject = 0;
  93. #undef obj_array_classblock
  94. #define obj_array_classblock(obj) \
  95.     ((obj_flags((obj)) == T_NORMAL_OBJECT) ? (obj)->methods->classdescriptor \
  96.                                            : ObjectClassBlock)
  97.  
  98. /* JSObject generated header */
  99. #define IMPLEMENT_netscape_javascript_JSObject
  100. #include "netscape_javascript_JSObject.h"
  101. #define IMPLEMENT_netscape_javascript_JSException
  102. #ifdef XP_MAC
  103. #include "n_javascript_JSException.h"
  104. #else
  105. #include "netscape_javascript_JSException.h"
  106. #endif
  107. #include "java_lang_Throwable.h"
  108.  
  109. /*
  110.  * types of reflected java objects
  111.  */
  112.  
  113. /* a package is basically just a name, since the jdk doesn't
  114.  * allow you to find out whether a particular name represents
  115.  * a package or not */
  116. typedef struct JSJavaPackage JSJavaPackage;
  117.  
  118. /* either a java class or a java object.
  119.  *   class.field is a static field or method
  120.  *   class(...) constructs an object that is an instance of the class
  121.  *   object.field is a field or method */
  122. typedef struct JSJava JSJava;
  123.  
  124. /* type associated with a JSJava structure */
  125. /* the JSClass java_class uses OBJECT and CLASS
  126.  * javaarray_class uses ARRAY */
  127. typedef enum JSJavaType {
  128.     JAVA_UNDEF,
  129.     JAVA_OBJECT,
  130.     JAVA_CLASS,
  131.     JAVA_ARRAY
  132. } JSJavaType;
  133.  
  134. /* JSJClassData holds the info necessary for js to represent
  135.  * itself to java.  When js calls java, it leaves a call to
  136.  * the method referred to by mb on the java stack.  Each
  137.  * JSScript that calls java contains its own copy of this,
  138.  * though the classloader and class may be shared. */
  139. typedef struct {
  140.     int nrefs;
  141.     jglobal loader;
  142.     jglobal clazz;
  143.     struct methodblock *mb;
  144. } JSJClassData;
  145.  
  146. /* fields and methods are initially represented with a slot object:
  147.  * this allows us to cope with fields and methods that have the same
  148.  * name.  if the slot is used in a function context it will call the
  149.  * appropriate method (dynamically choosing between overloaded methods).
  150.  * if it is used in any other context it will convert itself to the
  151.  * value of the field at the time it was looked up. */
  152. typedef struct JSJavaSlot JSJavaSlot;
  153.  
  154. /*
  155.  * globals for convenience, set up in the init routine
  156.  */
  157. static ClassClass * ObjectClassBlock = 0;
  158. static ClassClass * JSObjectClassBlock = 0;
  159. static ClassClass * JSExceptionClassBlock = 0;
  160. static ClassClass * StringClassBlock = 0;
  161. static ClassClass * BooleanClassBlock = 0;
  162. static ClassClass * DoubleClassBlock = 0;
  163. static ClassClass * ThrowableClassBlock = 0;
  164. static JRIFieldID JSObjectInternalField;
  165.  
  166. /* note that these hash tables are only stable because we
  167.  * use a non-moving garbage collector! */
  168.  
  169. /*
  170.  * this is used to ensure that there is at most one reflection
  171.  * of any java object.    objects are keyed by handle, classes are
  172.  * keyed by classblock pointer.  the value for each is the JS
  173.  * object reflection.  there is a root finder registered with the
  174.  * gc that marks all the java objects (keys).  the JS objects
  175.  * are not in the GC's root set, and are responsible for removing
  176.  * themselves from the table upon finalization.
  177.  */
  178. static PRHashTable *javaReflections = NULL;
  179. static PRMonitor *javaReflectionsMonitor = 0;
  180. /* this jsContext is used when scanning the reflection table */
  181.  
  182. /*
  183.  * similarly for java reflections of JS objects - in this case
  184.  * the keys are JS objects.  when the corresponding java instance
  185.  * is finalized, the entry is removed from the table, and a GC
  186.  * root for the JS object is removed.
  187.  */
  188. static PRHashTable *jsReflections = NULL;
  189. static PRMonitor *jsReflectionsMonitor = 0;
  190.  
  191. /*
  192.  * Because of the magic of windows DLLs we need to get this passed in
  193.  */
  194. static JSJCallbacks *jsj_callbacks = NULL;
  195.  
  196. /*
  197.  * we keep track of the "main" runtime in order to use it for
  198.  * finalization (see js_RemoveReflection).  this depends on there
  199.  * being only one JSRuntime which can run java, which may not be
  200.  * true eventually.
  201.  */
  202. JSRuntime *finalizeRuntime = 0;
  203.  
  204. #ifndef NSPR20
  205. #ifdef MOZILLA_CLIENT
  206. PR_LOG_DEFINE(MojaSrc);
  207. #else
  208. PRLogModuleInfo *MojaSrc;
  209. #endif
  210. #else
  211. PRLogModuleInfo *MojaSrc = NULL;
  212. #endif
  213.  
  214. /* lazy initialization */
  215. static void jsj_FinishInit(JSContext *cx, JRIEnv *env);
  216.  
  217. /* check if a field is static/nonstatic */
  218. /* this now accepts static fields for non-static references */
  219. #define CHECK_STATIC(isStatic, fb) (((fb)->access & ACC_STATIC) \
  220.                     ? (isStatic) : !(isStatic))
  221.  
  222. /* forward declarations */
  223. typedef void (*JSJCallback)(void*);
  224.  
  225. static JSBool
  226. js_CallJava(JSContext *cx, JSJCallback doit, void *d, JSBool pushSafeFrame);
  227.  
  228. static JSBool
  229. js_FindSystemClass(JSContext *cx, char *name, ClassClass **clazz);
  230.  
  231. static ClassClass *
  232. js_FindJavaClass(JSContext *cx, char *name, ClassClass *from);
  233.  
  234. static JSBool
  235. js_ExecuteJavaMethod(JSContext *cx, void *raddr, size_t rsize,
  236.              HObject *ho, char *name, char *sig,
  237.              struct methodblock *mb, JSBool isStaticCall, ...);
  238.  
  239. static HObject *
  240. js_ConstructJava(JSContext *cx, char *name, ClassClass *cb, char *sig, ...);
  241.  
  242. static HObject *
  243. js_ConstructJavaPrivileged(JSContext *cx, char *name, ClassClass *cb,
  244.                char *sig, ...);
  245.  
  246. static JSObject *
  247. js_ReflectJava(JSContext *cx, JSJavaType type, HObject *handle,
  248.            ClassClass *cb, char *sig, JSObject *useMe);
  249.  
  250. static JSBool
  251. js_reflectJavaSlot(JSContext *cx, JSObject *obj, JSString *str, jsval *vp);
  252.  
  253. static JSBool
  254. js_javaMethodWrapper(JSContext *cx, JSObject *obj,
  255.              PRUintn argc, jsval *argv, jsval *rval);
  256.  
  257. static JSBool
  258. js_javaConstructorWrapper(JSContext *cx, JSObject *obj,
  259.               PRUintn argc, jsval *argv, jsval *rval);
  260.  
  261. static JSBool
  262. js_JArrayElementType(HObject *handle, char **sig, ClassClass **classp);
  263.  
  264. static JSBool
  265. js_convertJSValueToJSObject(JSContext *cx, JSObject *mo, ClassClass *paramcb,
  266.                 JSBool checkOnly, HObject **objp);
  267.  
  268. static JSBool
  269. js_convertJSValueToJElement(JSContext *cx, jsval v,
  270.                 char *addr, char *sig, ClassClass *fromclass,
  271.                 char **sigRest);
  272.  
  273. static JSBool
  274. js_convertJSValueToJValue(JSContext *cx, jsval v,
  275.               OBJECT *addr, char *sig, ClassClass *fromclass,
  276.               JSBool checkOnly, char **sigRestPtr,
  277.               int *cost);
  278.  
  279. static JSBool
  280. js_convertJSValueToJField(JSContext *cx, jsval v,
  281.               HObject *ho, struct fieldblock *fb);
  282.  
  283. static JSBool
  284. js_convertJElementToJSValue(JSContext *cx, char *addr, char *sig,
  285.                 jsval *v, JSType desired);
  286.  
  287. static JSBool
  288. js_convertJValueToJSValue(JSContext *cx, OBJECT *addr, char *sig,
  289.               jsval *vp, JSType desired);
  290.  
  291. static JSBool
  292. js_convertJFieldToJSValue(JSContext *cx, HObject *ho, struct fieldblock *fb,
  293.               jsval *vp, JSType desired);
  294.  
  295. static JSBool
  296. js_convertJObjectToJSString(JSContext *cx, HObject *ho, JSBool isClass,
  297.                 jsval *vp);
  298.  
  299. static JSBool
  300. js_convertJObjectToJSNumber(JSContext *cx, HObject *ho, JSBool isClass,
  301.                 jsval *vp);
  302.  
  303. static JSBool
  304. js_convertJObjectToJSBoolean(JSContext *cx, HObject *ho, JSBool isClass,
  305.                  jsval *vp);
  306.  
  307. static JSJClassData *
  308. jsj_MakeClassData(JSContext *cx);
  309.  
  310. static void
  311. jsj_DestroyClassData(JSContext *cx, JSJClassData *data);
  312.  
  313. static JSBool
  314. js_pushSafeFrame(JSContext *cx, ExecEnv *ee, JSJClassData **classData);
  315.  
  316. static void
  317. js_popSafeFrame(JSContext *cx, ExecEnv *ee, JSJClassData *classData);
  318.  
  319. /* handy macro from agent.c */
  320. #define obj_getoffset(o, off) (*(OBJECT *)((char *)unhand(o)+(off)))
  321.  
  322. /* Java threads call this to find the JSContext to use for
  323.  * executing JavaScript */
  324. PR_IMPLEMENT(int)
  325. JSJ_IsEnabled()
  326. {
  327.     return jsj_callbacks->isEnabled();
  328. }
  329.  
  330. /* Java threads call this to find the JSContext to use for
  331.  * executing JavaScript */
  332. PR_IMPLEMENT(JSContext *)
  333. JSJ_CurrentContext(JRIEnv *env, char **errp)
  334. {
  335.     return jsj_callbacks->currentContext(env, errp);
  336. }
  337.  
  338. /* Java threads call this before executing JS code (to preserve
  339.  * run-to-completion in the client */
  340. PR_IMPLEMENT(void)
  341. JSJ_EnterJS(void)
  342. {
  343.     jsj_callbacks->enterJS();
  344. }
  345.  
  346. /* Java threads call this when finished executing JS code */
  347. PR_IMPLEMENT(void)
  348. JSJ_ExitJS(void)
  349. {
  350.     jsj_callbacks->exitJS();
  351. }
  352.  
  353. /* Java threads call this when finished executing JS code */
  354. PR_IMPLEMENT(JSObject *)
  355. JSJ_GetDefaultObject(JRIEnv *env, jobject hint)
  356. {
  357.     return jsj_callbacks->getDefaultObject(env, hint);
  358. }
  359.  
  360. static ExecEnv *
  361. jsj_GetCurrentEE(JSContext *cx)
  362. {
  363.     JRIEnv *env = 0;
  364.     static int js_java_initialized = 0;
  365.  
  366.     if (js_java_initialized != 2) {
  367.     /* use a cached monitor to make sure that we only
  368.      * initialize once */
  369.     PR_CEnterMonitor(&js_java_initialized);
  370.     switch (js_java_initialized) {
  371.           case 0:   /* we're first */
  372.         js_java_initialized = 1;
  373.         PR_CExitMonitor(&js_java_initialized);
  374.  
  375.         /* force both Java and JS to be initialized */
  376.         env = jsj_callbacks->currentEnv(cx);
  377.  
  378.             if (!env)
  379.                 goto out;
  380.  
  381.         /* initialize the hash tables and classes we need */
  382.         jsj_FinishInit(cx, env);
  383.  
  384.         PR_CEnterMonitor(&js_java_initialized);
  385.         js_java_initialized = 2;
  386.         PR_CNotifyAll(&js_java_initialized);
  387.         break;
  388.       case 1:   /* in progress */
  389.         PR_CWait(&js_java_initialized, WAIT_FOREVER);
  390.         break;
  391.       case 2:   /* done */
  392.         break;
  393.     }
  394.     PR_CExitMonitor(&js_java_initialized);
  395.     }
  396.  
  397.     if (!env)
  398.     env = jsj_callbacks->currentEnv(cx);
  399.  
  400.   out:
  401.     if (!env)
  402.         JS_ReportError(cx, "unable to get Java execution context for JavaScript");
  403.  
  404.     return (ExecEnv *) env;
  405. }
  406.  
  407. /****    ****    ****    ****    ****    ****    ****    ****    ****
  408.  *
  409.  *   java packages are just strings
  410.  *
  411.  ****    ****    ****    ****    ****    ****    ****    ****    ****/
  412.  
  413. struct JSJavaPackage {
  414.     char *name;         /* e.g. "java/lang" or 0 if it's the top level */
  415. };
  416.  
  417. /* javapackage uses standard getProperty */
  418.  
  419. static JSBool
  420. javapackage_setProperty(JSContext *cx, JSObject *obj, jsval slot, jsval *vp)
  421. {
  422.     JSJavaPackage *package = JS_GetPrivate(cx, obj);
  423.  
  424.     JS_ReportError(cx, "%s doesn't refer to any Java value",
  425.            package->name);
  426.     return JS_FALSE;
  427. }
  428.  
  429. static JSBool
  430. javapackage_enumerate(JSContext *cx, JSObject *obj)
  431. {
  432.     /* FIXME can't do this without reading directories... */
  433.     return JS_TRUE;
  434. }
  435.  
  436. /* forward declaration */
  437. static JSBool
  438. javapackage_resolve(JSContext *cx, JSObject *obj, jsval id);
  439.  
  440. static JSBool
  441. javapackage_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp)
  442. {
  443.     JSJavaPackage *package = JS_GetPrivate(cx, obj);
  444.     JSString *str;
  445.     char *name, *cp;
  446.  
  447.     switch (type) {
  448.     case JSTYPE_STRING:
  449.         /* convert '/' to '.' so it looks like the entry syntax */
  450.     if (!package->name)
  451.         break;
  452.         name = PR_smprintf("[JavaPackage %s]", package->name);
  453.     if (!name) {
  454.         JS_ReportOutOfMemory(cx);
  455.         return JS_FALSE;
  456.     }
  457.         for (cp = name; *cp != '\0'; cp++)
  458.             if (*cp == '/')
  459.                 *cp = '.';
  460.     str = JS_NewString(cx, name, strlen(name));
  461.     if (!str) {
  462.         free(name);
  463.         JS_ReportOutOfMemory(cx);
  464.         return JS_FALSE;
  465.     }
  466.  
  467.     *vp = STRING_TO_JSVAL(str);
  468.     break;
  469.     case JSTYPE_OBJECT:
  470.     *vp = OBJECT_TO_JSVAL(obj);
  471.     break;
  472.     default:
  473.     break;
  474.     }
  475.     return JS_TRUE;
  476. }
  477.  
  478. static void
  479. javapackage_finalize(JSContext *cx, JSObject *obj)
  480. {
  481.     JSJavaPackage *package = JS_GetPrivate(cx, obj);
  482.  
  483.     /* get rid of the private data */
  484.     if (package->name)
  485.     JS_free(cx, package->name);
  486.     JS_free(cx, package);
  487. }
  488.  
  489. static JSClass javapackage_class = {
  490.     "JavaPackage",
  491.     JSCLASS_HAS_PRIVATE,
  492.     JS_PropertyStub, JS_PropertyStub,
  493.     JS_PropertyStub, javapackage_setProperty, javapackage_enumerate,
  494.     javapackage_resolve, javapackage_convert, javapackage_finalize
  495. };
  496.  
  497. /* needs pointer to javapackage_class */
  498. static JSBool
  499. javapackage_resolve(JSContext *cx, JSObject *obj, jsval id)
  500. {
  501.     JSJavaPackage *package = JS_GetPrivate(cx, obj);
  502.     char *fullname;
  503.     char *name;
  504.     int namelen;
  505.     ClassClass *cb;
  506.     JSObject *mo;
  507.     JSJClassData *classData;
  508.     JRIEnv *env;
  509.  
  510.     if (!JSVAL_IS_STRING(id))
  511.     return JS_TRUE;
  512.  
  513.     name = JS_GetStringBytes(JSVAL_TO_STRING(id));
  514.     namelen = JS_GetStringLength(JSVAL_TO_STRING(id));
  515.  
  516.     if (package->name) {
  517.     int packagelen = strlen(package->name);
  518.     fullname = JS_malloc(cx, packagelen + namelen + 2);
  519.     if (!fullname)
  520.         return JS_FALSE;
  521.     strcpy(fullname, package->name);
  522.         fullname[packagelen] = '/';
  523.     strcpy(fullname + packagelen + 1, name);
  524.     } else {
  525.     fullname = JS_malloc(cx, namelen + 1);
  526.     if (!fullname)
  527.         return JS_FALSE;
  528.     strcpy(fullname, name);
  529.     }
  530.  
  531.     PR_LOG(MojaSrc, debug, ("looking for java class \"%s\"", fullname));
  532.  
  533.     /* get the class whose classloader we use to find more classes */
  534.     if (!jsj_callbacks->jsClassLoader) {
  535.         classData = NULL;
  536.     } else if (!(classData = jsj_MakeClassData(cx))) {
  537.         JS_free(cx, fullname);
  538.     return JS_FALSE;
  539.     }
  540.  
  541.     env = (JRIEnv *) jsj_GetCurrentEE(cx);
  542.     if (!env) {
  543.     JS_free(cx, fullname);
  544.     return JS_FALSE;
  545.     }
  546.  
  547.     /* see if the name is a class */
  548.     cb = js_FindJavaClass(cx, fullname,
  549.               classData
  550.                           ? JRI_GetGlobalRef(env, classData->clazz)
  551.                           : NULL);
  552.  
  553.     if (jsj_callbacks->jsClassLoader)
  554.         jsj_DestroyClassData(cx, classData);
  555.  
  556.     /* if not, it's a package */
  557.     if (!cb) {
  558.     JSJavaPackage *newpackage = JS_malloc(cx, sizeof(JSJavaPackage));
  559.     if (!newpackage) {
  560.         JS_free(cx, fullname);
  561.         return JS_FALSE;
  562.     }
  563.         PR_LOG(MojaSrc, debug, ("creating package %s", fullname));
  564.     newpackage->name = fullname;
  565.     mo = JS_NewObject(cx, &javapackage_class, 0, 0); /* FIXME proto and parent ok? */
  566.     if (!mo) {
  567.         JS_free(cx, fullname);
  568.         JS_free(cx, newpackage);
  569.         return JS_FALSE;
  570.     }
  571.     JS_SetPrivate(cx, mo, newpackage);
  572.     } else {
  573.     /* reflect the Class object */
  574.     mo = js_ReflectJClassToJSObject(cx, cb);
  575.     if (!mo) {
  576.         /* FIXME error message? */
  577.         return JS_FALSE;
  578.     }
  579.     JS_free(cx, fullname);
  580.     }
  581.  
  582.     return JS_DefineProperty(cx, obj, name, OBJECT_TO_JSVAL(mo),
  583.                  0, 0, JSPROP_READONLY);
  584. }
  585.  
  586. /****    ****    ****    ****    ****    ****    ****    ****    ****/
  587.  
  588. struct JSJava {
  589.     JSJavaType    type;            /* object / array / class */
  590.     HObject    *handle;        /* handle to the java Object */
  591.     ClassClass    *cb;            /* classblock, or element cb if array */
  592.     char    *signature;        /* array element signature */
  593. };
  594.  
  595. static struct fieldblock *
  596. java_lookup_field(JSContext *cx, ClassClass *cb, JSBool isStatic,
  597.           const char *name)
  598. {
  599.     while (cb) {
  600.     int i;
  601.     for (i = 0; i < (int)cbFieldsCount(cb); i++) {
  602.         struct fieldblock *fb = cbFields(cb) + i;
  603.         if (CHECK_STATIC(isStatic, fb)
  604.         && !strcmp(fieldname(fb), name)) {
  605.         return fb;
  606.         }
  607.     }
  608.     /* check the parent */
  609.     if (cbSuperclass(cb))
  610.         cb = cbSuperclass(cb);
  611.     else
  612.         cb = 0;
  613.     }
  614.  
  615.     return 0;
  616. }
  617.  
  618. static struct fieldblock *
  619. java_lookup_name(JSContext *cx, ClassClass *cb, JSBool isStatic,
  620.          const char *name)
  621. {
  622.     while (cb) {
  623.     int i;
  624.     for (i = 0; i < (int)cbFieldsCount(cb); i++) {
  625.         struct fieldblock *fb = cbFields(cb) + i;
  626.         if (CHECK_STATIC(isStatic, fb)
  627.         && !strcmp(fieldname(fb), name)) {
  628.         return fb;
  629.         }
  630.     }
  631.     for (i = cbMethodsCount(cb); i--;) {
  632.         struct methodblock *mb = cbMethods(cb) + i;
  633.         if (CHECK_STATIC(isStatic, &mb->fb)
  634.         && !strcmp(fieldname(&mb->fb), name)) {
  635.         return &mb->fb;
  636.         }
  637.     }
  638.     /* check the parent */
  639.     if (cbSuperclass(cb))
  640.         cb = cbSuperclass(cb);
  641.     else
  642.         cb = 0;
  643.     }
  644.     return 0;
  645. }
  646.  
  647. static JSBool
  648. java_getProperty(JSContext *cx, JSObject *obj, jsval slot, jsval *vp)
  649. {
  650.     JSString *str;
  651.     JSJava *java = JS_GetPrivate(cx, obj);
  652.  
  653.     if (!java) {
  654.         JS_ReportError(cx, "illegal operation on Java prototype object");
  655.     return JS_FALSE;
  656.     }
  657.  
  658.     /* FIXME reflect a getter/setter pair as a property! */
  659.  
  660.     if (!JSVAL_IS_STRING(slot)) {
  661.         JS_ReportError(cx, "invalid Java property expression");
  662.     return JS_FALSE;
  663.     }
  664.  
  665.     str = JSVAL_TO_STRING(slot);
  666.     PR_LOG(MojaSrc, debug, ("looked up slot \"%s\"", JS_GetStringBytes(str)));
  667.  
  668.     /* utter hack so that the assign hack doesn't kill us */
  669.     if (slot == STRING_TO_JSVAL(ATOM_TO_STRING(cx->runtime->atomState.assignAtom))) {
  670.     *vp = JSVAL_VOID;
  671.     return JS_TRUE;
  672.     }
  673.  
  674.     return js_reflectJavaSlot(cx, obj, str, vp);
  675. }
  676.  
  677. static JSBool
  678. java_setProperty(JSContext *cx, JSObject *obj, jsval slot, jsval *vp)
  679. {
  680.     JSJava *java = JS_GetPrivate(cx, obj);
  681.     ClassClass *cb;
  682.     struct fieldblock *fb = 0;
  683.     JSString *str;
  684.     const char *name;
  685.  
  686.     if (!java) {
  687.         JS_ReportError(cx, "illegal operation on Java prototype object");
  688.     return JS_FALSE;
  689.     }
  690.  
  691.     cb = java->cb;
  692.  
  693.     if (!JSVAL_IS_STRING(slot)) {
  694.         JS_ReportError(cx, "invalid Java property assignment");
  695.     return JS_FALSE;
  696.     }
  697.  
  698.     str = JSVAL_TO_STRING(slot);
  699.     name = JS_GetStringBytes(str);
  700.     PR_LOG(MojaSrc, debug, ("looked up slot \"%s\"", name));
  701.  
  702.     fb = java_lookup_field(cx, cb, java->type == JAVA_CLASS, name);
  703.     if (!fb) {
  704.         JS_ReportError(cx, "no Java %sfield found with name %s",
  705.                           (java->type == JAVA_CLASS ? "static " : ""),
  706.               name);
  707.     return JS_FALSE;
  708.     }
  709.  
  710.     if (!js_convertJSValueToJField(cx, *vp, java->handle, fb)) {
  711.         JS_ReportError(cx, "can't set Java field %s", name);
  712.     return JS_FALSE;
  713.     }
  714.     return JS_TRUE;
  715. }
  716.  
  717. static JSBool
  718. java_enumerate_property(JSContext *cx, JSObject *obj, struct fieldblock *fb)
  719. {
  720.     JSJava *java = JS_GetPrivate(cx, obj);
  721.     jsval junk;
  722.  
  723.     PR_ASSERT(java->type == JAVA_OBJECT || java->type == JAVA_CLASS);
  724.     if (!(fb->access & ACC_PUBLIC) ||
  725.     !CHECK_STATIC(java->type == JAVA_CLASS, fb)) {
  726.     return JS_TRUE;
  727.     }
  728.  
  729.     return JS_GetProperty(cx, obj, fieldname(fb), &junk);
  730. /*  FIXME this is what i would prefer, but it seems the get is necessary
  731.     return JS_DefineProperty(cx, obj, fieldname(fb), JSVAL_VOID,
  732.                  0, 0, JSPROP_ENUMERATE);
  733.  */
  734. }
  735.  
  736. static JSBool
  737. java_enumerate(JSContext *cx, JSObject *obj)
  738. {
  739.     JSJava *java = JS_GetPrivate(cx, obj);
  740.     ClassClass *cb;
  741.  
  742.     if (!java)
  743.     return JS_TRUE;
  744.  
  745.     cb = java->cb;
  746.  
  747.     while (cb) {
  748.     int i;
  749.     for (i = 0; i < (int)cbFieldsCount(cb); i++) {
  750.         struct fieldblock *fb, *outerfb;
  751.  
  752.         fb = cbFields(cb) + i;
  753.         outerfb = java_lookup_name(cx, java->cb, java->type == JAVA_CLASS,
  754.                     fieldname(fb));
  755.         if (outerfb && outerfb != fb)
  756.         continue;
  757.  
  758.         if (!java_enumerate_property(cx, obj, fb))
  759.         return JS_FALSE;
  760.     }
  761.     for (i = cbMethodsCount(cb); i--;) {
  762.         struct methodblock *mb;
  763.         struct fieldblock *outerfb;
  764.  
  765.         mb = cbMethods(cb) + i;
  766.         outerfb = java_lookup_name(cx, java->cb, java->type == JAVA_CLASS,
  767.                        fieldname(&mb->fb));
  768.         if (outerfb && outerfb != &mb->fb)
  769.         continue;
  770.  
  771.         if (!java_enumerate_property(cx, obj, &mb->fb))
  772.         return JS_FALSE;
  773.     }
  774.     /* check the parent */
  775.     if (cbSuperclass(cb))
  776.         cb = cbSuperclass(cb);
  777.     else
  778.         cb = 0;
  779.     }
  780.     return JS_TRUE;
  781. }
  782.  
  783. PR_STATIC_CALLBACK(JSBool)
  784. java_resolve(JSContext *cx, JSObject *obj, jsval id)
  785. {
  786.     JSJava *java = JS_GetPrivate(cx, obj);
  787.     char *name;
  788.     jsval v;
  789.     JSErrorReporter oldReporter;
  790.     JSBool hasProperty;
  791.  
  792.     if (!java || !JSVAL_IS_STRING(id))
  793.     return JS_TRUE;
  794.  
  795.     name = JS_GetStringBytes(JSVAL_TO_STRING(id));
  796.     PR_LOG(MojaSrc, debug, ("resolve(0x%x, %s) (handle 0x%x)",
  797.              obj, name, java->handle));
  798.  
  799.     oldReporter = JS_SetErrorReporter(cx, NULL);
  800.     hasProperty = java_getProperty(cx, obj, id, &v);
  801.     JS_SetErrorReporter(cx, oldReporter);
  802.     if (!hasProperty)
  803.         return JS_TRUE;
  804.  
  805.     return JS_DefineProperty(cx, obj, name, v, 0, 0, JSPROP_ENUMERATE);
  806. }
  807.  
  808. PR_STATIC_CALLBACK(PRHashNumber)
  809. java_hashHandle(void *key)
  810. {
  811.     PRHashNumber num = (PRHashNumber) key ;    /* help lame MSVC1.5 on Win16 */
  812.     /* win16 compiler can't shift right by 2, but it will do it by 1 twice. */
  813.     num = num >> 1;
  814.     return num >> 1;
  815. }
  816.  
  817. static int
  818. java_pointerEq(void *v1, void *v2)
  819. {
  820.     return v1 == v2;
  821. }
  822.  
  823. PR_STATIC_CALLBACK(void)
  824. java_finalize(JSContext *cx, JSObject *obj)
  825. {
  826.     JSJava *java = JS_GetPrivate(cx, obj);
  827.     void *key;
  828.     PRHashEntry *he, **hep;
  829.  
  830.     if (!java)
  831.     return;
  832.  
  833.     /* remove it from the reflection table */
  834.     if (java->type == JAVA_CLASS) {
  835.         PR_LOG(MojaSrc, debug, ("removing class 0x%x from table", java->cb));
  836.     key = cbName(java->cb);
  837.     } else {
  838.         PR_LOG(MojaSrc, debug, ("removing handle 0x%x from table", java->handle));
  839.     key = java->handle;
  840.     }
  841.     PR_EnterMonitor(javaReflectionsMonitor);
  842.     if (javaReflections) {
  843.     hep = PR_HashTableRawLookup(javaReflections,
  844.                                     java_hashHandle(key), key);
  845.     he = *hep;
  846.     if (he) {
  847.         PR_HashTableRawRemove(javaReflections, hep, he);
  848.     }
  849.     }
  850.     PR_ExitMonitor(javaReflectionsMonitor);
  851.  
  852.     /* get rid of the private data */
  853.     JS_free(cx, java);
  854. }
  855.  
  856. PR_STATIC_CALLBACK(JSBool)
  857. java_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp)
  858. {
  859.     JSJava *java = JS_GetPrivate(cx, obj);
  860.  
  861.     if (!java) {
  862.     if (type == JSTYPE_OBJECT) {
  863.         *vp = OBJECT_TO_JSVAL(obj);
  864.         return JS_TRUE;
  865.     }
  866.  
  867.         JS_ReportError(cx, "illegal operation on Java prototype object");
  868.     return JS_FALSE;
  869.     }
  870.  
  871.     PR_LOG(MojaSrc, debug, ("java_convert to %d", type));
  872.  
  873.     switch (type) {
  874.     case JSTYPE_OBJECT:
  875.     *vp = OBJECT_TO_JSVAL(obj);
  876.     break;
  877.     case JSTYPE_FUNCTION:
  878.     /* only classes convert to functions (constructors) */
  879.     if (java->type != JAVA_CLASS) {
  880.         /* random java objects do not convert to functions */
  881.             JS_ReportError(cx, "can't convert Java object to function");
  882.         return JS_FALSE;
  883.     } else {
  884.         JSString *str;
  885.         JSFunction *fun;
  886.         JSObject *funobj;
  887.         JSObject *globalobj;
  888.         jsval javaPrototype;
  889.  
  890.             PR_LOG(MojaSrc, debug, ("making a constructor\n"));
  891.  
  892.         str = JS_NewStringCopyZ(cx, cbName(java->cb));
  893.         if (!str)
  894.         return JS_FALSE;
  895.  
  896.         fun = JS_NewFunction(cx, js_javaConstructorWrapper, 0, 0, 0,
  897.                  JS_GetStringBytes(str));
  898.  
  899.         /* FIXME a private property of the function object points to the
  900.          * classblock: gc problem? */
  901.         funobj = JS_GetFunctionObject(fun);
  902.             if (!JS_DefineProperty(cx, funobj, "#javaClass",
  903.                    PRIVATE_TO_JSVAL(java->cb), 0, 0,
  904.                    JSPROP_READONLY)) {
  905.         return JS_FALSE;
  906.         }
  907.  
  908.         /* set the prototype so that objects constructed with
  909.              * "new" have the right JSClass */
  910.         if (!(globalobj = JS_GetGlobalObject(cx))
  911.         || !JS_GetProperty(cx, globalobj,
  912.                                    "#javaPrototype", &javaPrototype)
  913.         || !JS_DefineProperty(cx, funobj, "prototype",
  914.                       javaPrototype, 0, 0,
  915.                       JSPROP_READONLY)) {
  916.         return JS_FALSE;
  917.         }
  918.  
  919.         *vp = OBJECT_TO_JSVAL(funobj);
  920.     }
  921.     break;
  922.     case JSTYPE_STRING:
  923.     /* either pull out the string or call toString */
  924.     return js_convertJObjectToJSString(cx, java->handle,
  925.                        java->type==JAVA_CLASS, vp);
  926.     case JSTYPE_NUMBER:
  927.     /* call doubleValue() */
  928.     return js_convertJObjectToJSNumber(cx, java->handle,
  929.                        java->type==JAVA_CLASS, vp);
  930.     case JSTYPE_BOOLEAN:
  931.     /* call booleanValue() */
  932.     return js_convertJObjectToJSBoolean(cx, java->handle,
  933.                         java->type==JAVA_CLASS, vp);
  934.     default:
  935.     break;
  936.     }
  937.     return JS_TRUE;
  938. }
  939.  
  940. static JSClass java_class = {
  941.     "Java",
  942.     JSCLASS_HAS_PRIVATE,
  943.     JS_PropertyStub, JS_PropertyStub,
  944.     java_getProperty, java_setProperty, java_enumerate,
  945.     java_resolve, java_convert, java_finalize
  946. };
  947.  
  948. /****    ****    ****    ****    ****    ****    ****    ****    ****/
  949.  
  950. static JSBool
  951. javaarray_getProperty(JSContext *cx, JSObject *obj, jsval slot, jsval *vp)
  952. {
  953.     JSJava *array = JS_GetPrivate(cx, obj);
  954.     HObject *ho = array->handle;
  955.     PRInt32 index;
  956.     PRInt32 size;
  957.     char *addr;
  958.  
  959.     if (JSVAL_IS_STRING(slot)) {
  960.     char *name = JS_GetStringBytes(JSVAL_TO_STRING(slot));
  961.         if (0 == strcmp(name, "length")) {
  962.         *vp = INT_TO_JSVAL((jsint)obj_length(ho));
  963.         return JS_TRUE;
  964.     }
  965.         JS_ReportError(cx, "invalid Java array element %s", name);
  966.     return JS_FALSE;
  967.     }
  968.  
  969.     if (!JSVAL_IS_INT(slot)) {
  970.         JS_ReportError(cx, "invalid Java array index expression");
  971.     return JS_FALSE;
  972.     }
  973.     index = JSVAL_TO_INT(slot);
  974.     if (index < 0 || index >= (PRInt32) obj_length(ho)) {
  975.         JS_ReportError(cx, "Java array index %d out of range", index);
  976.     return JS_FALSE;
  977.     }
  978.  
  979.     size = sizearray(obj_flags(ho), 1);
  980.     addr = ((char*) unhand(ho)) + index * size;
  981.  
  982.     if (!js_convertJElementToJSValue(cx, addr, array->signature,
  983.                      vp, JSTYPE_VOID)) {
  984.     JS_ReportError(cx,
  985.             "can't convert Java array element %d to JavaScript value", index);
  986.     return JS_FALSE;
  987.     }
  988.  
  989.     return JS_TRUE;
  990. }
  991.  
  992. static JSBool
  993. javaarray_setProperty(JSContext *cx, JSObject *obj, jsval slot, jsval *vp)
  994. {
  995.     JSJava *array = JS_GetPrivate(cx, obj);
  996.     HObject *ho = array->handle;
  997.     PRInt32 index;
  998.     PRInt32 size;
  999.     char *addr;
  1000.  
  1001.     if (JSVAL_IS_STRING(slot)) {
  1002.     char *name = JS_GetStringBytes(JSVAL_TO_STRING(slot));
  1003.         if (0 == strcmp(name, "length")) {
  1004.             JS_ReportError(cx, "can't set length of a Java array");
  1005.         return JS_FALSE;
  1006.     }
  1007.         JS_ReportError(cx, "invalid assignment to Java array element %s", name);
  1008.     return JS_FALSE;
  1009.     }
  1010.  
  1011.     if (!JSVAL_IS_INT(slot)) {
  1012.         JS_ReportError(cx, "invalid Java array index expression");
  1013.     return JS_FALSE;
  1014.     }
  1015.     index = JSVAL_TO_INT(slot);
  1016.     if (index < 0 || index >= (PRInt32) obj_length(ho)) {
  1017.         JS_ReportError(cx, "Java array index %d out of range", index);
  1018.     return JS_FALSE;
  1019.     }
  1020.  
  1021.     size = sizearray(obj_flags(ho), 1);
  1022.     addr = ((char*) unhand(ho)) + index * size;
  1023.  
  1024.     if (!js_convertJSValueToJElement(cx, *vp, addr,
  1025.                      array->signature, array->cb, 0)) {
  1026.         JS_ReportError(cx, "invalid assignment to Java array element %d",
  1027.                index);
  1028.     return JS_FALSE;
  1029.     }
  1030.  
  1031.     return JS_TRUE;
  1032. }
  1033.  
  1034. static JSBool
  1035. javaarray_enumerate(JSContext *cx, JSObject *obj)
  1036. {
  1037.     JSJava *array = JS_GetPrivate(cx, obj);
  1038.     HObject *ho = array->handle;
  1039.     PRUint32 i;
  1040.     jsval v;
  1041.  
  1042.     for (i = 0; i < obj_length(ho); i++) {
  1043.     PRInt32 size = sizearray(obj_flags(ho), 1);
  1044.     char *addr = ((char*) unhand(ho)) + i * size;
  1045.  
  1046.     if (!js_convertJElementToJSValue(cx, addr, array->signature,
  1047.                      &v, JSTYPE_VOID)) {
  1048.         return JS_FALSE;
  1049.     }
  1050.     if (!JS_SetElement(cx, obj, (jsint) i, &v)) {
  1051.         return JS_FALSE;
  1052.     }
  1053.     }
  1054.     return JS_TRUE;
  1055. }
  1056.  
  1057. static JSBool
  1058. javaarray_resolve(JSContext *cx, JSObject *obj, jsval id)
  1059. {
  1060.     JSJava *array = JS_GetPrivate(cx, obj);
  1061.     HObject *ho = array->handle;
  1062.     jsint index;
  1063.  
  1064.     if (!JSVAL_IS_INT(id))
  1065.         return JS_TRUE;
  1066.  
  1067.     index = JSVAL_TO_INT(id);
  1068.     if (index < 0 || index >= (PRInt32) obj_length(ho))
  1069.     return JS_TRUE;
  1070.  
  1071.     return JS_DefineElement(cx, obj, index, JSVAL_VOID,
  1072.                 0, 0, JSPROP_ENUMERATE);
  1073. }
  1074.  
  1075. static void
  1076. javaarray_finalize(JSContext *cx, JSObject *obj)
  1077. {
  1078.     JSJava *array = JS_GetPrivate(cx, obj);
  1079.  
  1080.     /* get rid of the private data */
  1081.     /* array->signature is a static string */ ;
  1082.     array->signature = 0;
  1083.  
  1084.     /* remove it from the reflection table */
  1085.     java_finalize(cx, obj);
  1086. }
  1087.  
  1088. static JSBool
  1089. javaarray_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp)
  1090. {
  1091.     switch (type) {
  1092.     case JSTYPE_OBJECT:
  1093.     *vp = OBJECT_TO_JSVAL(obj);
  1094.     break;
  1095.     case JSTYPE_STRING:
  1096.     /* FIXME how should arrays convert to strings? */
  1097.     default:
  1098.     break;
  1099.     }
  1100.     return JS_TRUE;
  1101. }
  1102.  
  1103.  
  1104. static JSClass javaarray_class = {
  1105.     "JavaArray",
  1106.     JSCLASS_HAS_PRIVATE,
  1107.     JS_PropertyStub, JS_PropertyStub,
  1108.     javaarray_getProperty, javaarray_setProperty, javaarray_enumerate,
  1109.     javaarray_resolve, javaarray_convert, javaarray_finalize
  1110. };
  1111.  
  1112. /****    ****    ****    ****    ****    ****    ****    ****    ****/
  1113.  
  1114. /* this lameness is brought to you by the decision
  1115.  * to use strings for signatures in the JDK */
  1116. static char *
  1117. getSignatureBase(char *sig)
  1118. {
  1119.     int len;
  1120.     char *ret;
  1121.     char end;
  1122.  
  1123.     switch (*sig) {
  1124.     case SIGNATURE_CLASS:
  1125.     end = SIGNATURE_ENDCLASS;
  1126.     break;
  1127.     case SIGNATURE_FUNC:
  1128.     end = SIGNATURE_ENDFUNC;
  1129.     break;
  1130.     default:
  1131.     return 0;
  1132.     break;
  1133.     }
  1134.  
  1135.     len = strchr(sig+1, end) - (sig+1);
  1136.     ret = malloc(len+1);
  1137.     if (!ret)
  1138.     return 0;
  1139.     strncpy(ret, sig+1, len);
  1140.     ret[len] = '\0';
  1141.  
  1142.     return ret;
  1143. }
  1144.  
  1145. static ClassClass *
  1146. getSignatureClass(JSContext *cx, char *sig, ClassClass *from)
  1147. {
  1148.     char *name;
  1149.     ClassClass *cb;
  1150.  
  1151.     if (sig[1] == '\0') {
  1152.         /* special hack: if the signature is "L" it means
  1153.      * just use the from class */
  1154.     return from;
  1155.     }
  1156.     name = getSignatureBase(sig);
  1157.     if (!name) {
  1158.     JS_ReportOutOfMemory(cx);
  1159.     return 0;
  1160.     }
  1161.     cb = js_FindJavaClass(cx, name, from);
  1162.     free(name);
  1163.     return cb;
  1164. }
  1165.  
  1166. /* this doesn't need to run in a java env, but it may throw an
  1167.  * exception so we need a temporary one */
  1168. static JSBool
  1169. js_isSubclassOf(JSContext *cx, ClassClass *cb1, ClassClass *cb2)
  1170. {
  1171.     ExecEnv *ee = jsj_GetCurrentEE(cx);
  1172.     JSBool ret;
  1173.  
  1174.     /* FIXME what to do with the exception? */
  1175.     if (!ee) {
  1176.     return JS_FALSE;
  1177.     }
  1178.  
  1179.     exceptionClear(ee);
  1180.     ret = (JSBool) is_subclass_of(cb1, cb2, ee);
  1181.     if (exceptionOccurred(ee)) {
  1182. #ifdef DEBUG
  1183.     char *message;
  1184.  
  1185.     /* exceptionDescribe(ee); */
  1186.         /* FIXME this could fail: we don't check if it's throwable,
  1187.      * but i assume that is_subclass_of is well-behaved */
  1188.     HString *hdetail =
  1189.       unhand((Hjava_lang_Throwable *)
  1190.          ee->exception.exc)->detailMessage;
  1191.     ClassClass *cb = obj_array_classblock(ee->exception.exc);
  1192.  
  1193.     /*
  1194.      * FIXME JRI_GetStringPlatformChars and JRI_GetStringUTFChars both
  1195.      * return a pointer into an otherwise unreferenced, unrooted Java
  1196.      * array's body, without returning the Java array's handle.  This
  1197.      * requires fairly conservative GC, which we have in NSPR1.0, but
  1198.      * if we lose it, this code may break.
  1199.      */
  1200.     message = (char *)
  1201.         JRI_GetStringPlatformChars((JRIEnv *) ee,
  1202.                        (struct java_lang_String *) hdetail,
  1203.                        (const jbyte *) cx->charSetName,
  1204.                        (jint) cx->charSetNameLength);
  1205.  
  1206.     PR_LOG(MojaSrc, error,
  1207.                ("exception in is_subclass_of %s (\"%s\")",
  1208.         cbName(cb), message));
  1209. #endif
  1210.     exceptionClear(ee);
  1211.     return JS_FALSE;
  1212.     }
  1213.     return ret ? JS_TRUE : JS_FALSE;
  1214. }
  1215.  
  1216. static JSBool
  1217. js_convertJSValueToJSObject(JSContext *cx, JSObject *mo, ClassClass *paramcb,
  1218.                 JSBool checkOnly, HObject **objp)
  1219. {
  1220.     /* check if a JSObject would be an acceptable argument */
  1221.     if (!js_isSubclassOf(cx, JSObjectClassBlock, paramcb))
  1222.     return JS_FALSE;
  1223.  
  1224.     /* JSObject is ok, so convert mo to one.
  1225.      * a JS object which is not really a java object
  1226.      * converts to JSObject */
  1227.     if (!checkOnly) {
  1228.     *objp = js_ReflectJSObjectToJObject(cx, mo);
  1229.     if (!*objp)
  1230.         return JS_FALSE;
  1231.     }
  1232.     return JS_TRUE;
  1233. }
  1234.  
  1235. static JSBool
  1236. js_convertJSValueToJArray(HObject **objp, JSContext *cx,
  1237.               jsval v, char *sig, ClassClass *fromclass,
  1238.               JSBool checkOnly, int *cost)
  1239. {
  1240.     /* FIXME bump the cost counter when necessary */
  1241.     JSJava *java;
  1242.     HArrayOfObject *harr;
  1243.     ClassClass *acb;
  1244.     ClassClass *paramcb;
  1245.     JSObject *mo;
  1246.     char *elementsig;
  1247.     ClassClass *elementClazz;
  1248.  
  1249.     /* the only legal conversions are from null or from a java array */
  1250.     if (!JSVAL_IS_OBJECT(v))
  1251.     return JS_FALSE;
  1252.  
  1253.     /* can always pass null */
  1254.     mo = JSVAL_TO_OBJECT(v);
  1255.     if (!mo) {
  1256.     if (!checkOnly)
  1257.         *objp = 0;
  1258.     return JS_TRUE;
  1259.     }
  1260.  
  1261.     /* otherwise, it must be a JS reflection of a java array */
  1262.     if (! JS_InstanceOf(cx, mo, &javaarray_class, 0))
  1263.     return JS_FALSE;
  1264.  
  1265.     java = JS_GetPrivate(cx, mo);
  1266.  
  1267.     sig++; /* skip to array element type */
  1268.  
  1269.     switch (*sig) {
  1270.     case SIGNATURE_CLASS:    /* object array */
  1271.     paramcb = getSignatureClass(cx, sig, fromclass);
  1272.         PR_LOG(MojaSrc, debug, ("desired array element signature \"%s\"(0x%x)",
  1273.                  sig, paramcb));
  1274.     if (!paramcb) {
  1275.         PR_LOG(MojaSrc, warn,
  1276.                    ("couldn't find class for signature \"%s\"\n", sig));
  1277.         return JS_FALSE;
  1278.     }
  1279.     harr = (HArrayOfObject *) java->handle;
  1280.         acb = (ClassClass *)(unhand(harr)->body[class_offset(harr)]);
  1281.     if (!acb) {
  1282.         PR_LOG(MojaSrc, warn,
  1283.                    ("couldn't find class of array element\n"));
  1284.         return JS_FALSE;
  1285.     }
  1286.  
  1287.     /* elements must convert */
  1288.     if (js_isSubclassOf(cx, acb, paramcb)) {
  1289.         if (!checkOnly)
  1290.         *objp = (HObject *) harr;
  1291.         return JS_TRUE;
  1292.     }
  1293.     break;
  1294.     case SIGNATURE_ARRAY:    /* nested array */
  1295.         /* FIXME nested arrays can't be supported, because the
  1296.      * jdk runtime treats them all as flat
  1297.      *
  1298.      * This just in... Actually, you can get the dimensions of a
  1299.          * java array because they aren't flattened. */
  1300.     /* FIXME throw an exception */
  1301.     break;
  1302.     default:            /* primitive array */
  1303.     /* for any other array, the signature must match exactly */
  1304.     if (js_JArrayElementType(java->handle, &elementsig, &elementClazz)) {
  1305.         if (elementsig[0] == sig[0]) {
  1306.         if (!checkOnly)
  1307.             *objp = java->handle;
  1308.         return JS_TRUE;
  1309.         }
  1310.     }
  1311.     break;
  1312.     }
  1313.  
  1314.     return JS_FALSE;
  1315. }
  1316.  
  1317. PR_IMPLEMENT(JSBool)
  1318. js_convertJSValueToJObject(HObject **objp, JSContext *cx,
  1319.                jsval v, char *sig, ClassClass *fromclass,
  1320.                JSBool checkOnly, int *cost)
  1321. {
  1322.     /* FIXME bump the cost counter when necessary */
  1323.  
  1324.     ClassClass *paramcb;
  1325.  
  1326.     paramcb = sig ? getSignatureClass(cx, sig, fromclass)
  1327.         : ObjectClassBlock;
  1328.  
  1329.     PR_LOG(MojaSrc, debug, ("desired argument class signature \"%s\"(0x%x)",
  1330.         sig, paramcb));
  1331.  
  1332.     if (!paramcb) {
  1333.     PR_LOG(MojaSrc, warn,
  1334.                ("couldn't find class for signature \"%s\"", sig));
  1335.     return JS_FALSE;
  1336.     }
  1337.  
  1338.     /* JS wrappers around java objects do the same check
  1339.      * as the java compiler */
  1340.     /* FIXME except classes, which become JSObject (circular problem?) */
  1341.     if (JSVAL_IS_OBJECT(v)) {
  1342.     JSObject *mo = JSVAL_TO_OBJECT(v);
  1343.     /* null is always a valid object */
  1344.     if (!mo) {
  1345.         if (!checkOnly)
  1346.         *objp = 0;
  1347.         return JS_TRUE;
  1348.     }
  1349.     if (JS_TypeOfValue(cx, v) == JSTYPE_FUNCTION) {
  1350.         if (js_convertJSValueToJSObject(cx, mo, paramcb, checkOnly, objp))
  1351.         return JS_TRUE;
  1352.     } else if (JS_InstanceOf(cx, mo, &java_class, 0) ||
  1353.            JS_InstanceOf(cx, mo, &javaarray_class, 0)) {
  1354.         JSJava *java = JS_GetPrivate(cx, mo);
  1355.         HObject *ho = java->handle;
  1356.         ClassClass *cb;
  1357.  
  1358.         /* class reflections convert to JSObject or String only */
  1359.         if (java->type == JAVA_CLASS) {
  1360.         if (js_convertJSValueToJSObject(cx, mo, paramcb, checkOnly,
  1361.                         objp)) {
  1362.             return JS_TRUE;
  1363.         }
  1364.         } else {
  1365.         /* get the classblock for the java argument type */
  1366.         cb = obj_array_classblock(ho);
  1367.                 PR_LOG(MojaSrc, debug, ("actual argument 0x%x, class 0x%x",
  1368.                      ho, cb));
  1369.  
  1370.         /* check against the expected class */
  1371.         if (js_isSubclassOf(cx, cb, paramcb)) {
  1372.             if (!checkOnly)
  1373.             *objp = ho;
  1374.             return JS_TRUE;
  1375.         }
  1376.         }
  1377.     } else {
  1378.         /* otherwise see if it will take a JSObject */
  1379.         if (js_convertJSValueToJSObject(cx, mo, paramcb, checkOnly, objp))
  1380.         return JS_TRUE;
  1381.     }
  1382.     } else if (JSVAL_IS_NUMBER(v)) {    /* java.lang.Double */
  1383.     if (js_isSubclassOf(cx, DoubleClassBlock, paramcb)) {
  1384.         /* Float is ok */
  1385.         if (!checkOnly)
  1386.                 *objp = js_ConstructJava(cx, "java/lang/Double",
  1387.                                             0, "(D)",
  1388.                         JSVAL_IS_INT(v)
  1389.                         ? (double) JSVAL_TO_INT(v)
  1390.                         : *JSVAL_TO_DOUBLE(v));
  1391.         return JS_TRUE;
  1392.     }
  1393.     } else if (JSVAL_IS_BOOLEAN(v)) {  /* java.lang.Boolean */
  1394.     /* FIXME this should return Boolean.JS_TRUE or Boolean.FALSE
  1395.      * instead of constructing a new one? */
  1396.     if (js_isSubclassOf(cx, BooleanClassBlock, paramcb)) {
  1397.         if (!checkOnly)
  1398.                 *objp = js_ConstructJava(cx, "java/lang/Boolean",
  1399.                                          0, "(Z)", (long) JSVAL_TO_BOOLEAN(v));
  1400.         return JS_TRUE;
  1401.     }
  1402.     }
  1403.  
  1404.     /* last ditch attempt: is a String acceptable? */
  1405.     if (js_isSubclassOf(cx, StringClassBlock, paramcb)) {
  1406.     JSString *str;
  1407.     /* string is ok, convert to one */
  1408.  
  1409.     str = JS_ValueToString(cx, v);
  1410.     if (str) {
  1411.         /* make a java String from str */
  1412.         if (!checkOnly) {
  1413.         ExecEnv *ee = jsj_GetCurrentEE(cx);
  1414.         if (!ee)
  1415.             return JS_FALSE;
  1416.         *objp = (JHandle *)
  1417.           JRI_NewStringPlatform((JRIEnv *) ee,
  1418.                     (const jbyte *) JS_GetStringBytes(str),
  1419.                     (jint) JS_GetStringLength(str),
  1420.                     (const jbyte *) cx->charSetName,
  1421.                     (jint) cx->charSetNameLength);
  1422.         }
  1423.         return JS_TRUE;
  1424.     }
  1425.     }
  1426.     return JS_FALSE;
  1427. }
  1428.  
  1429. PR_IMPLEMENT(JSBool)
  1430. js_convertJObjectToJSValue(JSContext *cx, jsval *vp, HObject *ho)
  1431. {
  1432.     ExecEnv *ee;
  1433.     JRIEnv *env;
  1434.     JSObject *mo;
  1435.  
  1436.     ee = jsj_GetCurrentEE(cx);
  1437.     if (!ee) return JS_FALSE;
  1438.  
  1439.     env = (JRIEnv *) ee;
  1440.  
  1441.     if (!ho) {
  1442.     *vp = OBJECT_TO_JSVAL(0);
  1443.     return JS_TRUE;
  1444.     }
  1445.  
  1446.     /*
  1447.      * If it's a JSObject, pull out the original JS object when
  1448.      * it comes back into the JS world.
  1449.      */
  1450.     if (obj_array_classblock(ho) == JSObjectClassBlock) {
  1451.     jref jso = (jref) ho /* FIXME */;
  1452.     *vp = OBJECT_TO_JSVAL(
  1453.         (JSObject *) JRI_GetFieldInt(env, jso, JSObjectInternalField));
  1454.     return JS_TRUE;
  1455.     }
  1456.  
  1457.     /*
  1458.      * Instances of java.lang.String are wrapped so we can
  1459.      * call methods on them, but they convert to a JS string
  1460.      * if used in a string context.
  1461.      */
  1462.  
  1463.     /* otherwise, wrap it */
  1464.     mo = js_ReflectJObjectToJSObject(cx, ho);
  1465.     if (!mo) {
  1466.         JS_ReportError(cx, "can't convert Java object to JavaScript object");
  1467.     return JS_FALSE;
  1468.     }
  1469.     *vp = OBJECT_TO_JSVAL(mo);
  1470.     return JS_TRUE;
  1471. }
  1472.  
  1473.  
  1474. #define REFLECTION_IN_PROGRESS ((jref)1)
  1475.  
  1476. PR_IMPLEMENT(HObject *)
  1477. js_ReflectJSObjectToJObject(JSContext *cx, JSObject *mo)
  1478. {
  1479.     ExecEnv *ee;
  1480.     JRIEnv *env;
  1481.     jref jso;
  1482.     PRHashEntry *he;
  1483.  
  1484.     ee = jsj_GetCurrentEE(cx);
  1485.     if (!ee) return 0;
  1486.  
  1487.     env = (JRIEnv *) ee;
  1488.  
  1489.     /* see if it's already there */
  1490.     PR_EnterMonitor(jsReflectionsMonitor);
  1491.  
  1492.     while ((jso = PR_HashTableLookup(jsReflections, mo)) != 0) {
  1493.     if (jso != REFLECTION_IN_PROGRESS)
  1494.         break;
  1495.     PR_Wait(jsReflectionsMonitor, WAIT_FOREVER);
  1496.     }
  1497.  
  1498.     if (jso) {
  1499.     PR_ExitMonitor(jsReflectionsMonitor);
  1500.     return (HObject *) jso /*FIXME*/;
  1501.     }
  1502.  
  1503.     /*
  1504.      * Release the monitor temporarily while we call the java constructor,
  1505.      * which could contain arbitrary scariness.  Put a placeholder in the
  1506.      * hash table so anyone racing to reflect mo waits for us to finish.
  1507.      */
  1508.     PR_HashTableAdd(jsReflections, mo, REFLECTION_IN_PROGRESS);
  1509.     PR_ExitMonitor(jsReflectionsMonitor);
  1510.  
  1511.     /* not found, reflect it */
  1512.     jso = (jref) /*FIXME*/
  1513.         js_ConstructJavaPrivileged(cx, 0, JSObjectClassBlock, "()");
  1514.  
  1515.     /* FIXME check for exceptions */
  1516.     if (!jso) {
  1517.     return 0;
  1518.     }
  1519.  
  1520.     /* need to check again since we released the monitor */
  1521.     PR_EnterMonitor(jsReflectionsMonitor);
  1522.  
  1523. #ifdef DEBUG
  1524. {
  1525.     jref tmp = PR_HashTableLookup(jsReflections, mo);
  1526.  
  1527.     PR_ASSERT(!tmp || tmp == REFLECTION_IN_PROGRESS);
  1528.     if (tmp && tmp != REFLECTION_IN_PROGRESS) {
  1529.     PR_ExitMonitor(jsReflectionsMonitor);
  1530.  
  1531.     /* let the one we constructed die in gc and use the one we found */
  1532.     return (HObject *) tmp;
  1533.     }
  1534. }
  1535. #endif
  1536.  
  1537.     JRI_SetFieldInt(env, jso, JSObjectInternalField, (jint) mo);
  1538.  
  1539.     /* add it to the table */
  1540.     he = PR_HashTableAdd(jsReflections, mo, jso);
  1541.     if (he)
  1542.     (void) JS_AddRoot(cx, (void *)&he->key);    /* FIXME be errors? */
  1543.  
  1544.     /* awaken anyone racing and exit monitor */
  1545.     PR_NotifyAll(jsReflectionsMonitor);
  1546.     PR_ExitMonitor(jsReflectionsMonitor);
  1547.  
  1548.     return (HObject *) jso;/*FIXME*/
  1549. }
  1550.  
  1551. PR_IMPLEMENT(void)
  1552. js_RemoveReflection(JSContext *cx, JSObject *mo)
  1553. {
  1554.     PRHashEntry *he, **hep;
  1555.  
  1556.     /* remove it from the reflection table */
  1557.     PR_LOG(MojaSrc, debug, ("removing JS object 0x%x from table", mo));
  1558.     PR_EnterMonitor(jsReflectionsMonitor);
  1559.     hep = PR_HashTableRawLookup(jsReflections, java_hashHandle(mo), mo);
  1560.     he = *hep;
  1561.  
  1562.     if (he) {
  1563.     /* FIXME inline JS_RemoveRoot -- this will go away with GC unification */
  1564.     JS_LOCK_RUNTIME(finalizeRuntime);
  1565.     (void) PR_HashTableRemove(finalizeRuntime->gcRootsHash, (void *)&he->key);
  1566.     JS_UNLOCK_RUNTIME(finalizeRuntime);
  1567.     }
  1568.     PR_HashTableRawRemove(jsReflections, hep, he);
  1569.     PR_ExitMonitor(jsReflectionsMonitor);
  1570. }
  1571.  
  1572.  
  1573.  
  1574. static JSBool
  1575. js_convertJSValueToJValue(JSContext *cx, jsval v,
  1576.               OBJECT *addr, char *sig, ClassClass *fromclass,
  1577.               JSBool checkOnly, char **sigRestPtr,
  1578.               int *cost)
  1579. {
  1580.     Java8 tdub;
  1581.     char *p = sig;
  1582.  
  1583.     switch (*p) {
  1584.     case SIGNATURE_BOOLEAN:
  1585.     if (!JSVAL_IS_BOOLEAN(v)) {
  1586.         /* FIXME we could convert other things to boolean too,
  1587.          * but until cost checking is done this will cause us
  1588.          * to choose the wrong method if there is method overloading */
  1589. #ifndef USE_COSTS
  1590.         return JS_FALSE;
  1591. #else
  1592.         if (!JS_ConvertValue(cx, v, JSTYPE_BOOLEAN, &v))
  1593.         return JS_FALSE;
  1594.         (*cost)++;
  1595. #endif
  1596.     }
  1597.     if (!checkOnly)
  1598.         *(long*)addr = (JSVAL_TO_BOOLEAN(v) == JS_TRUE);
  1599.     break;
  1600.     case SIGNATURE_SHORT:
  1601.     case SIGNATURE_BYTE:
  1602.     case SIGNATURE_CHAR:
  1603.     case SIGNATURE_INT:
  1604.     /* FIXME should really do a range check... */
  1605.     if (!JSVAL_IS_NUMBER(v)) {
  1606. #ifndef USE_COSTS
  1607.         return JS_FALSE;
  1608. #else
  1609.         if (!JS_ConvertValue(cx, v, JSTYPE_NUMBER, &v))
  1610.         return JS_FALSE;
  1611.         (*cost)++;
  1612. #endif
  1613.     }
  1614.     if (!checkOnly) {
  1615.         if (JSVAL_IS_INT(v))
  1616.         *(long*)addr = (long) JSVAL_TO_INT(v);
  1617.         else
  1618.         *(long*)addr = (long) *JSVAL_TO_DOUBLE(v);
  1619.     }
  1620.     break;
  1621.  
  1622.     case SIGNATURE_LONG:
  1623.     if (!JSVAL_IS_NUMBER(v)) {
  1624. #ifndef USE_COSTS
  1625.         return JS_FALSE;
  1626. #else
  1627.         if (!JS_ConvertValue(cx, v, JSTYPE_NUMBER, &v))
  1628.         return JS_FALSE;
  1629.         (*cost)++;
  1630. #endif
  1631.     }
  1632.     if (!checkOnly) {
  1633.         PRInt64 ll;
  1634.         if (JSVAL_IS_INT(v)) {
  1635.         long l = (long) JSVAL_TO_INT(v);
  1636.         LL_I2L(ll, l);
  1637.         } else {
  1638.         double d = (double) *JSVAL_TO_DOUBLE(v);
  1639.         LL_D2L(ll, d);
  1640.         }
  1641.         SET_INT64(tdub, addr, ll);
  1642.     }
  1643.     break;
  1644.  
  1645.     case SIGNATURE_FLOAT:
  1646.     if (!JSVAL_IS_NUMBER(v)) {
  1647. #ifndef USE_COSTS
  1648.         return JS_FALSE;
  1649. #else
  1650.         if (!JS_ConvertValue(cx, v, JSTYPE_NUMBER, &v))
  1651.         return JS_FALSE;
  1652.         (*cost)++;
  1653. #endif
  1654.     }
  1655.     if (!checkOnly) {
  1656.         if (JSVAL_IS_INT(v))
  1657.         *(float*)addr = (float) JSVAL_TO_INT(v);
  1658.         else
  1659.         *(float*)addr = (float) *JSVAL_TO_DOUBLE(v);
  1660.     }
  1661.     break;
  1662.  
  1663.     case SIGNATURE_DOUBLE:
  1664.     if (!JSVAL_IS_NUMBER(v)) {
  1665. #ifndef USE_COSTS
  1666.         return JS_FALSE;
  1667. #else
  1668.         if (!JS_ConvertValue(cx, v, JSTYPE_NUMBER, &v))
  1669.         return JS_FALSE;
  1670.         (*cost)++;
  1671. #endif
  1672.     }
  1673.     if (!checkOnly) {
  1674.         double d;
  1675.         if (JSVAL_IS_INT(v))
  1676.         d = (double) JSVAL_TO_INT(v);
  1677.         else
  1678.         d = (double) *JSVAL_TO_DOUBLE(v);
  1679.         SET_DOUBLE(tdub, addr, d);
  1680.     }
  1681.     break;
  1682.  
  1683.     case SIGNATURE_CLASS:
  1684.     /* FIXME cost? */
  1685.     if (!js_convertJSValueToJObject((HObject **)/*FIXME*/addr,
  1686.                     cx, v, p, fromclass,
  1687.                     checkOnly, cost)) {
  1688.         return JS_FALSE;
  1689.     }
  1690.     while (*p != SIGNATURE_ENDCLASS) p++;
  1691.     break;
  1692.  
  1693.     case SIGNATURE_ARRAY:
  1694.     /* FIXME cost? */
  1695.     if (!js_convertJSValueToJArray((HObject **)/*FIXME*/addr,
  1696.                        cx, v, p, fromclass,
  1697.                        checkOnly, cost)) {
  1698.         return JS_FALSE;
  1699.     }
  1700.     while (*p == SIGNATURE_ARRAY) p++; /* skip array beginning */
  1701.     /* skip the element type */
  1702.     if (*p == SIGNATURE_CLASS) {
  1703.         while (*p != SIGNATURE_ENDCLASS) p++;
  1704.     }
  1705.     break;
  1706.  
  1707.     default:
  1708.         PR_LOG(MojaSrc, warn, ("unknown value signature '%s'", p));
  1709.     return JS_FALSE;
  1710.     }
  1711.  
  1712.     if (sigRestPtr)
  1713.     *sigRestPtr = p+1;
  1714.     return JS_TRUE;
  1715. }
  1716.  
  1717.  
  1718. /*
  1719.  *  this code mimics the method overloading resolution
  1720.  *  done in java, for use in JS->java calls
  1721.  */
  1722.  
  1723.  
  1724. /* push a jsval array onto the java stack for use by
  1725.  *  the given method.
  1726.  * sigRest gets a pointer to the remainder of the signature (the
  1727.  *  rest of the arguments in the list).
  1728.  * if checkOnly is true, don't actually convert, just check that it's ok.
  1729.  */
  1730. static JSBool
  1731. js_convertJSToJArgs(JSContext *cx, stack_item *optop,
  1732.             struct methodblock *mb, int argc, jsval *argv,
  1733.             JSBool checkOnly, char **sigRestPtr,
  1734.             int *cost)
  1735. {
  1736.     struct fieldblock *fb = &mb->fb;
  1737.     char *sig = fieldsig(fb);
  1738.     jsval *vp = argv;
  1739.     int argsleft = argc;
  1740.     char *p;
  1741.     void *addr;
  1742.  
  1743.     *cost = 0;
  1744.  
  1745.     for (p = sig + 1; *p != SIGNATURE_ENDFUNC; vp++, argsleft--) {
  1746.     if (argsleft == 0)        /* not enough arguments passed */
  1747.         return JS_FALSE;
  1748.     if (checkOnly)
  1749.         addr = 0;
  1750.     else switch (*p) {
  1751.     case SIGNATURE_BOOLEAN:
  1752.     case SIGNATURE_SHORT:
  1753.     case SIGNATURE_BYTE:
  1754.     case SIGNATURE_CHAR:
  1755.     case SIGNATURE_INT:
  1756.         addr = &(optop++)->i;
  1757.         break;
  1758.     case SIGNATURE_LONG:
  1759.         addr = optop;
  1760.         optop += 2;
  1761.         break;
  1762.     case SIGNATURE_FLOAT:
  1763.         addr = &(optop++)->f;
  1764.         break;
  1765.     case SIGNATURE_DOUBLE:
  1766.         addr = optop;
  1767.         optop += 2;
  1768.         break;
  1769.     case SIGNATURE_CLASS:
  1770.     case SIGNATURE_ARRAY:
  1771.         addr = &(optop++)->h;
  1772.         break;
  1773.     default:
  1774.         PR_LOG(MojaSrc, warn,
  1775.                    ("Invalid method signature '%s' for method '%s'\n",
  1776.             fieldsig(fb), fieldname(fb)));
  1777.         return JS_FALSE;
  1778.         break;
  1779.     }
  1780.     /* this bumps p to the next argument */
  1781.     if (!js_convertJSValueToJValue(cx, *vp, addr, p, fieldclass(&mb->fb),
  1782.                        checkOnly, &p, cost)) {
  1783.         return JS_FALSE;
  1784.     }
  1785.     }
  1786.     if (argsleft > 0) {
  1787.     /* too many arguments */
  1788.     return JS_FALSE;
  1789.     }
  1790.  
  1791.     if (sigRestPtr)
  1792.     *sigRestPtr = p+1 /* go past the SIGNATURE_ENDFUNC */;
  1793.     return JS_TRUE;
  1794. }
  1795.  
  1796. /* java array elements are packed with smaller sizes */
  1797. static JSBool
  1798. js_convertJSValueToJElement(JSContext *cx, jsval v,
  1799.                 char *addr, char *sig, ClassClass *fromclass,
  1800.                 char **sigRestPtr)
  1801. {
  1802.     long tmp[2];
  1803.     int cost = 0;
  1804.  
  1805.     if (!js_convertJSValueToJValue(cx, v, (OBJECT *)&tmp,
  1806.                    sig, fromclass,
  1807.                    JS_FALSE, sigRestPtr, &cost))
  1808.        return JS_FALSE;
  1809.  
  1810.     switch (sig[0]) {
  1811.     case SIGNATURE_BOOLEAN:
  1812.     *(char*)addr = (char)(*(long*)&tmp);
  1813.     break;
  1814.     case SIGNATURE_BYTE:
  1815.     *(char*)addr = (char)(*(long*)&tmp);
  1816.     break;
  1817.     case SIGNATURE_CHAR:
  1818.     *(unicode*)addr = (unicode)(*(long*)&tmp);
  1819.     break;
  1820.     case SIGNATURE_SHORT:
  1821.     *(signed short*)addr = (signed short)(*(long*)&tmp);
  1822.     break;
  1823.     case SIGNATURE_INT:
  1824.     *(PRInt32*)addr = *(long*)&tmp;
  1825.     break;
  1826.     case SIGNATURE_LONG:
  1827.     *(PRInt64*)addr = *(PRInt64*)&tmp;
  1828.     break;
  1829.     case SIGNATURE_FLOAT:
  1830.     *(float*)addr = *(float*)&tmp;
  1831.     break;
  1832.     case SIGNATURE_DOUBLE:
  1833.     *(double*)addr = *(double*)&tmp;
  1834.     break;
  1835.     case SIGNATURE_CLASS:
  1836.     case SIGNATURE_ARRAY:
  1837.     *(HObject**)addr = *(HObject**)&tmp;
  1838.     break;
  1839.     default:
  1840.         PR_LOG(MojaSrc, warn, ("unknown value signature '%s'\n", sig[0]));
  1841.     return JS_FALSE;
  1842.     }
  1843.     return JS_TRUE;
  1844. }
  1845.  
  1846.  
  1847. static OBJECT *
  1848. getJavaFieldAddress(HObject *ho, struct fieldblock *fb)
  1849. {
  1850.     OBJECT *addr = 0;            /* address of the java value */
  1851.  
  1852.     if (fb->access & ACC_PUBLIC) {
  1853.     if (fb->access & ACC_STATIC) {
  1854.         char *sig = fieldsig(fb);
  1855.  
  1856.         if (sig[0] == SIGNATURE_LONG || sig[0] == SIGNATURE_DOUBLE)
  1857.         addr = (long *)twoword_static_address(fb);
  1858.         else
  1859.         addr = (long *)normal_static_address(fb);
  1860.     } else {
  1861.         addr = &obj_getoffset(ho, fb->u.offset);
  1862.     }
  1863.     }
  1864.     return addr;
  1865. }
  1866.  
  1867. static JSBool
  1868. js_convertJSValueToJField(JSContext *cx, jsval v, HObject *ho,
  1869.               struct fieldblock *fb)
  1870. {
  1871.     OBJECT *addr = getJavaFieldAddress(ho, fb);
  1872.     int cost = 0;
  1873.  
  1874.     if (!addr) {
  1875.         JS_ReportError(cx, "can't access field %s", fieldname(fb));
  1876.     return JS_FALSE;
  1877.     }
  1878.     return js_convertJSValueToJValue(cx, v, addr,
  1879.                      fieldsig(fb), fieldclass(fb),
  1880.                      JS_FALSE, 0, &cost);
  1881. }
  1882.  
  1883. /*
  1884.  * Returns -1 if the given method is not applicable to the arguments,
  1885.  * or a cost if it is.
  1886.  * if argc == -1, match iff the name matches and don't check the
  1887.  *  signature.    this always returns cost 1.
  1888.  */
  1889. static int
  1890. methodIsApplicable(JSContext *cx,
  1891.            struct methodblock *mb, JSBool isStatic,
  1892.            const char *name, int argc, jsval *argv)
  1893. {
  1894.     struct fieldblock *fb = &mb->fb;
  1895.     int cost = 0;
  1896.  
  1897.     /* name and access must match */
  1898.     if (!CHECK_STATIC(isStatic, fb) || strcmp(fieldname(fb), name))
  1899.     return -1;
  1900.  
  1901.     if (argc == -1)
  1902.     return 1;
  1903.  
  1904.     if (!js_convertJSToJArgs(cx, 0, mb, argc, argv,
  1905.                  JS_TRUE /* checkOnly */,
  1906.                  0 /* &sigRest */, &cost)) {
  1907.     return -1;
  1908.     }
  1909.  
  1910.     return cost;
  1911. }
  1912.  
  1913. /* FIXME this routine doesn't work yet - its purpose is to choose
  1914.  * the best method when java methods are overloaded, or to detect
  1915.  * ambiguous method calls */
  1916. static JSBool
  1917. methodIsMoreSpecific(JSContext *cx,
  1918.              struct methodblock *mb1, struct methodblock *mb2)
  1919. {
  1920.     char *sig1 = fieldsig(&mb1->fb) + 1;        /* skip '(' */
  1921.     char *sig2 = fieldsig(&mb2->fb) + 1;
  1922.  
  1923.     /* FIXME fill this in! */
  1924.     return JS_TRUE;
  1925.  
  1926.     /* go through the args */
  1927.     while (*sig1 != SIGNATURE_ENDFUNC && *sig2 != SIGNATURE_ENDFUNC) {
  1928.     if (*sig1 == SIGNATURE_CLASS) {
  1929.         if (*sig2 == SIGNATURE_CLASS) {
  1930.         ClassClass *cb1 =
  1931.           getSignatureClass(cx, sig1, fieldclass(&mb1->fb));
  1932.         ClassClass *cb2 =
  1933.           getSignatureClass(cx, sig2, fieldclass(&mb2->fb));
  1934.  
  1935.         if (! js_isSubclassOf(cx, cb1, cb2))
  1936.             return JS_FALSE;
  1937.  
  1938.         /* next argument */
  1939.         while (*sig1++ != SIGNATURE_ENDCLASS);
  1940.         while (*sig2++ != SIGNATURE_ENDCLASS);
  1941.         }
  1942.     }
  1943.     }
  1944.     if (*sig1 != *sig2) {
  1945.     return JS_FALSE;
  1946.     /* arg number mismatch */
  1947.     }
  1948.     return JS_TRUE;
  1949. }
  1950.  
  1951. /* based on sun.tools.java.Environment */
  1952. /**
  1953.  * Return true if an implicit cast from this type to
  1954.  * the given type is allowed.
  1955.  */
  1956.  
  1957. static JSBool
  1958. js_convertJValueToJSValue(JSContext *cx, OBJECT *addr, char *sig, jsval *vp,
  1959.               JSType desired)
  1960. {
  1961.     Java8 tmp;
  1962.  
  1963.     switch (sig[0]) {
  1964.     case SIGNATURE_VOID:
  1965.     *vp = JSVAL_VOID;
  1966.     break;
  1967.     case SIGNATURE_BYTE:
  1968.     case SIGNATURE_CHAR:
  1969.     case SIGNATURE_SHORT:
  1970.     *vp = INT_TO_JSVAL((jsint) *addr);
  1971.     break;
  1972.     case SIGNATURE_INT:
  1973.     {
  1974.         PRInt32 val = (PRInt32) *addr;
  1975.         if (INT_FITS_IN_JSVAL(val)) {
  1976.         *vp = INT_TO_JSVAL((jsint) *addr);
  1977.         } else {
  1978.         if (!JS_NewDoubleValue(cx, val, vp))
  1979.             return JS_FALSE;
  1980.         }
  1981.     }
  1982.     break;
  1983.     case SIGNATURE_BOOLEAN:
  1984.     *vp = BOOLEAN_TO_JSVAL((JSBool) *addr);
  1985.     break;
  1986.     case SIGNATURE_LONG:
  1987.     {
  1988.         PRInt64 ll;
  1989.         double d;
  1990.  
  1991.         ll = GET_INT64(tmp, addr);
  1992.         LL_L2D(d, ll);
  1993.         if (!JS_NewDoubleValue(cx, d, vp))
  1994.         return JS_FALSE;
  1995.     }
  1996.     break;
  1997.     case SIGNATURE_FLOAT:
  1998.     if (!JS_NewDoubleValue(cx, *(float *)addr, vp))
  1999.         return JS_FALSE;
  2000.     break;
  2001.     case SIGNATURE_DOUBLE:
  2002.     if (!JS_NewDoubleValue(cx, GET_DOUBLE(tmp, addr), vp))
  2003.         return JS_FALSE;
  2004.     break;
  2005.     case SIGNATURE_CLASS:
  2006.     case SIGNATURE_ARRAY:
  2007.     return js_convertJObjectToJSValue(cx, vp, *(HObject **) addr);
  2008.     default:
  2009.         JS_ReportError(cx, "unknown Java signature character '%c'", sig[0]);
  2010.     return JS_FALSE;
  2011.     }
  2012.  
  2013.     if (desired != JSTYPE_VOID)
  2014.     return JS_ConvertValue(cx, *vp, desired, vp);
  2015.     return JS_TRUE;
  2016. }
  2017.  
  2018. /* java array elements are packed with smaller sizes */
  2019. static JSBool
  2020. js_convertJElementToJSValue(JSContext *cx, char *addr, char *sig, jsval *vp,
  2021.                 JSType desired)
  2022. {
  2023.     switch (sig[0]) {
  2024.     case SIGNATURE_BYTE:
  2025.     *vp = INT_TO_JSVAL(*(char*)addr);
  2026.     break;
  2027.     case SIGNATURE_CHAR:
  2028.     *vp = INT_TO_JSVAL(*(unicode*)addr);
  2029.     break;
  2030.     case SIGNATURE_SHORT:
  2031.     *vp = INT_TO_JSVAL(*(signed short*)addr);
  2032.     break;
  2033.     case SIGNATURE_INT:
  2034.     {
  2035.         PRInt32 val = *(PRInt32*)addr;
  2036.         if (INT_FITS_IN_JSVAL(val)) {
  2037.         *vp = INT_TO_JSVAL((jsint)val);
  2038.         } else {
  2039.         if (!JS_NewDoubleValue(cx, val, vp))
  2040.             return JS_FALSE;
  2041.         }
  2042.     }
  2043.     break;
  2044.     case SIGNATURE_BOOLEAN:
  2045.     *vp = BOOLEAN_TO_JSVAL((JSBool) *(PRInt32*)addr);
  2046.     break;
  2047.     default:
  2048.     return js_convertJValueToJSValue(cx, (OBJECT *) addr, sig, vp, desired);
  2049.     }
  2050.  
  2051.     if (desired != JSTYPE_VOID)
  2052.     return JS_ConvertValue(cx, *vp, desired, vp);
  2053.     return JS_TRUE;
  2054. }
  2055.  
  2056. static JSBool
  2057. js_convertJFieldToJSValue(JSContext *cx, HObject *ho, struct fieldblock *fb,
  2058.               jsval *vp, JSType desired)
  2059. {
  2060.     OBJECT *addr = getJavaFieldAddress(ho, fb);
  2061.     char *sig = fieldsig(fb);
  2062.  
  2063.     if (!addr) {
  2064.         JS_ReportError(cx, "can't access Java field %s", fieldname(fb));
  2065.     return JS_FALSE;
  2066.     }
  2067.     return js_convertJValueToJSValue(cx, addr, sig, vp, desired);
  2068. }
  2069.  
  2070. static JSBool
  2071. js_convertJObjectToJSString(JSContext *cx, HObject *ho, JSBool isClass,
  2072.                 jsval *vp)
  2073. {
  2074.     JSString *str;
  2075.     char *cstr;
  2076.  
  2077.     if (isClass) {
  2078.         cstr = PR_smprintf("[JavaClass %s]", cbName((ClassClass*)ho));
  2079.     } else {
  2080.     HString *hstr;
  2081.     ExecEnv *ee;
  2082.  
  2083.     if (!ho)
  2084.         return JS_FALSE;
  2085.  
  2086.     if (obj_classblock(ho) == StringClassBlock) {
  2087.             /* it's a string already */
  2088.         hstr = (HString*) ho;
  2089.     } else {
  2090.         /* call toString() to convert to a string */
  2091.         if (!js_ExecuteJavaMethod(cx, &hstr, sizeof(hstr), ho,
  2092.                                    "toString", "()Ljava/lang/String;",
  2093.                    0, JS_FALSE)) {
  2094.         return JS_FALSE;
  2095.         }
  2096.     }
  2097.  
  2098.     /* FIXME js_ExecuteJavaMethod does this too */
  2099.     ee = jsj_GetCurrentEE(cx);
  2100.     if (!ee)
  2101.         return JS_FALSE;
  2102.  
  2103.     /* convert the java string to a JS string */
  2104.     cstr = (char *)
  2105.         JRI_GetStringPlatformChars((JRIEnv *) ee,
  2106.                        (struct java_lang_String *) hstr,
  2107.                        (const jbyte *) cx->charSetName,
  2108.                        (jint) cx->charSetNameLength);
  2109.     if (cstr)
  2110.         cstr = strdup(cstr);
  2111.     }
  2112.     if (!cstr) {
  2113.     JS_ReportOutOfMemory(cx);
  2114.     return JS_FALSE;
  2115.     }
  2116.     str = JS_NewString(cx, cstr, strlen(cstr));
  2117.     if (!str) {
  2118.     free(cstr);
  2119.     return JS_FALSE;
  2120.     }
  2121.     *vp = STRING_TO_JSVAL(str);
  2122.     return JS_TRUE;
  2123. }
  2124.  
  2125. static JSBool
  2126. js_convertJObjectToJSNumber(JSContext *cx, HObject *ho, JSBool isClass,
  2127.                 jsval *vp)
  2128. {
  2129.     long foo[2], swap;    /* FIXME JRI and JDK disagree */
  2130.     JRI_JDK_Java8 tmp;
  2131.     double d;
  2132.  
  2133.     if (isClass || !ho) {
  2134.         JS_ReportError(cx, "can't convert Java object to number");
  2135.     return JS_FALSE;
  2136.     }
  2137.  
  2138.     if (!js_ExecuteJavaMethod(cx, foo, sizeof(foo), ho,
  2139.                                  "doubleValue", "()D",
  2140.                  0, JS_FALSE))
  2141.     return JS_FALSE;
  2142.  
  2143.     swap = foo[0];
  2144.     foo[0] = foo[1];
  2145.     foo[1] = swap;
  2146.     d = JRI_GET_DOUBLE(tmp, &foo[0]);
  2147.     return JS_NewDoubleValue(cx, d, vp);
  2148. }
  2149.  
  2150. static JSBool
  2151. js_convertJObjectToJSBoolean(JSContext *cx, HObject *ho, JSBool isClass,
  2152.                  jsval *vp)
  2153. {
  2154.     long b;
  2155.  
  2156.     if (isClass) {
  2157.         b = JS_TRUE;
  2158.     goto ok;
  2159.     }
  2160.  
  2161.     if (!ho) {
  2162.         b = JS_FALSE;
  2163.     goto ok;
  2164.     }
  2165.  
  2166.     if (!js_ExecuteJavaMethod(cx, &b, sizeof(b), ho,
  2167.                               "booleanValue", "()Z",
  2168.                   0, JS_FALSE)) {
  2169.         /* failed, probably missing the booleanValue() method */
  2170.         /* by default we convert to true */
  2171.         b = JS_TRUE;
  2172.     }
  2173.  ok:
  2174.     *vp = BOOLEAN_TO_JSVAL(b);
  2175.     return JS_TRUE;
  2176. }
  2177.  
  2178. static JSBool
  2179. java_returnAsJSValue(JSContext *cx, jsval *vp, ExecEnv *ee, char *sig)
  2180. {
  2181.     OBJECT *addr;
  2182.  
  2183.     if ((sig[0] == SIGNATURE_DOUBLE || sig[0] == SIGNATURE_LONG)) {
  2184.     addr = (OBJECT*) &ee->current_frame->optop[-2];
  2185.     } else {
  2186.     addr = (OBJECT *) &ee->current_frame->optop[-1];
  2187.     }
  2188.     return js_convertJValueToJSValue(cx, addr, sig, vp, JSTYPE_VOID);
  2189. }
  2190.  
  2191. static char*
  2192. js_createArgTypeString(JSContext* cx, int argc, jsval* argv)
  2193. {
  2194.     int i;
  2195.     int size = 0;
  2196.     char *p;
  2197.     char** strs;
  2198.     char *str;
  2199.  
  2200.     if (argc < 1) {
  2201.         str = strdup("");
  2202.     return str;
  2203.     }
  2204.  
  2205.     strs = (char**) malloc(argc * sizeof(char*));
  2206.     if (!strs)
  2207.     return 0;
  2208.  
  2209.     for (i = 0; i < argc; i++) {
  2210.     JSType t = JS_TypeOfValue(cx, argv[i]);
  2211.     switch (t) {
  2212.       case JSTYPE_VOID:
  2213.             strs[i] = "void";
  2214.         break;
  2215.       case JSTYPE_OBJECT:
  2216.         if (JSVAL_IS_NULL(argv[i]))
  2217.         strs[i] = "null";
  2218.         else
  2219.         strs[i] = JS_GetClass(JSVAL_TO_OBJECT(argv[i]))->name;
  2220.         break;
  2221.       case JSTYPE_FUNCTION:
  2222.             strs[i] = "function";
  2223.         break;
  2224.       case JSTYPE_STRING:
  2225.             strs[i] = "string";
  2226.         break;
  2227.       case JSTYPE_NUMBER:
  2228.             strs[i] = "number";
  2229.         break;
  2230.       case JSTYPE_BOOLEAN:
  2231.             strs[i] = "boolean";
  2232.         break;
  2233.       default:
  2234.             PR_ASSERT(!"bad arg type");
  2235.     }
  2236.         size += strlen(strs[i]) + 2; /* +2 for ", " */
  2237.     }
  2238.  
  2239.     str = (char*) malloc(size - 1); /* -2 for ", " & +1 for '\0' */
  2240.     if (!str)
  2241.     goto out;
  2242.  
  2243.     strcpy(str, strs[0]);
  2244.     p = str + strlen(strs[0]);
  2245.     for (i = 1; i < argc; i++) {
  2246.         *p++ = ',';
  2247.         *p++ = ' ';
  2248.     strcpy(p, strs[i]);
  2249.     p += strlen(strs[i]);
  2250.     }
  2251.     *p = '\0';
  2252.  
  2253.   out:
  2254.     free(strs);
  2255.     return str;
  2256. }
  2257.  
  2258. /*
  2259.  * find the best method to call for a name and a bunch of JS
  2260.  * arguments.  try each possible method, then find the most
  2261.  * specific of the applicable methods.  "most specific" is
  2262.  * determined without reference to the JS arguments: it is
  2263.  * the same as "most specific" in the java sense of the word.
  2264.  * FIXME this could cause trouble since more methods will be applicable
  2265.  *  to a JS call than for a similar java call!
  2266.  * pass argc == -1 to match any method with the correct name
  2267.  */
  2268. static struct methodblock *
  2269. matchMethod(JSContext *cx, ClassClass *clazz, JSBool isStatic,
  2270.         const char *name, int argc, jsval *argv, JSBool reportErrors)
  2271. {
  2272.     int mindex;
  2273.     int *isApplicable = 0;        /* array holding costs */
  2274.     struct methodblock *bestmb = 0;
  2275.     JSBool isOverloaded = JS_FALSE;
  2276.     ClassClass *cb = clazz;
  2277.  
  2278.     while (cb) {
  2279.     /*
  2280.      * Find all applicable methods.  keep track of which ones are
  2281.      * applicable using the isApplicable array, and the best one
  2282.      * found so far in bestmb.  set isOverloaded if there is more
  2283.      * than one applicable method.
  2284.      */
  2285.     isApplicable = JS_malloc(cx, cbMethodsCount(cb) * sizeof(int));
  2286.     if (!isApplicable)
  2287.         return 0;
  2288.     for (mindex = cbMethodsCount(cb); mindex--;) {
  2289.         struct methodblock *mb = cbMethods(cb) + mindex;
  2290.         struct fieldblock *fb = &mb->fb;
  2291.  
  2292.         isApplicable[mindex] = methodIsApplicable(cx, mb, isStatic,
  2293.                               name, argc, argv);
  2294.         if (isApplicable[mindex] == -1)
  2295.         continue;
  2296.  
  2297.             PR_LOG(MojaSrc, debug, ("found applicable method %s with sig %s",
  2298.                  fieldname(fb), fieldsig(fb)));
  2299.  
  2300.         if (!bestmb) {        /* first one found */
  2301.         bestmb = mb;
  2302.         continue;
  2303.         }
  2304.  
  2305.         isOverloaded = JS_TRUE;;
  2306.  
  2307.         if (methodIsMoreSpecific(cx, mb, bestmb)) {
  2308.         bestmb = mb;
  2309.         }
  2310.     }
  2311.  
  2312.         /* if we've found something applicable in the current class,
  2313.      * no need to go any further */
  2314.     /* this is the only exit from the loop in which isApplicable
  2315.      * is live */
  2316.     if (bestmb)
  2317.         break;
  2318.  
  2319.     /* otherwise, check the parent */
  2320.     if (cbSuperclass(cb))
  2321.         cb = cbSuperclass(cb);
  2322.     else
  2323.         cb = 0;
  2324.     JS_free(cx, isApplicable);
  2325.     isApplicable = 0;
  2326.     }
  2327.  
  2328.     /* second pass: check that bestmb is more specific than all other
  2329.      * applicable methods */
  2330. /* FIXME if mb is equally specific, this is an ambiguous lookup.
  2331.    Hopefully we can disambiguate by the cost */
  2332.  
  2333. /*
  2334.     if (isOverloaded)
  2335.     for (mindex = cb->methods_count; mindex--;) {
  2336.         struct methodblock *mb = cbMethods(cb) + mindex;
  2337.         struct fieldblock *fb = &mb->fb;
  2338.  
  2339.         if (!isApplicable[mindex] || mb == bestmb)
  2340.         continue;
  2341.  
  2342.     }
  2343. */
  2344.  
  2345.     if (isApplicable)
  2346.     JS_free(cx, isApplicable);
  2347.  
  2348.     if (bestmb && (bestmb->fb.access & ACC_PUBLIC))
  2349.     return bestmb;
  2350.     else if (reportErrors) {
  2351.     char buf[512];
  2352.     char *argstr = js_createArgTypeString(cx, argc, argv);
  2353.         char *methodtype = "method";
  2354.  
  2355.         if (0 == strcmp(name, "<init>")) {
  2356.             methodtype = "constructor";
  2357.     }
  2358.  
  2359.     if (!bestmb) {
  2360.         if (argstr) {
  2361.         PR_snprintf(buf, sizeof(buf),
  2362.                             "no Java %s %s.%s matching JavaScript arguments (%s)",
  2363.                 methodtype, cbName(clazz), name, argstr);
  2364.         free(argstr);
  2365.         } else {
  2366.         PR_snprintf(buf, sizeof(buf),
  2367.                             "no Java %s %s.%s matching JavaScript arguments",
  2368.                 methodtype, cbName(clazz), name);
  2369.         }
  2370.     } else {
  2371.         if (argstr) {
  2372.         PR_snprintf(buf, sizeof(buf),
  2373.                             "Java %s %s.%s is not public (%s)",
  2374.                 methodtype, cbName(clazz), name, argstr);
  2375.         free(argstr);
  2376.         } else {
  2377.         PR_snprintf(buf, sizeof(buf),
  2378.                             "Java %s %s.%s is not public",
  2379.                 methodtype, cbName(clazz), name);
  2380.         }
  2381.     }
  2382.     JS_ReportError(cx, buf);
  2383.     }
  2384.     return NULL;
  2385. }
  2386.  
  2387. /* this is the callback to execute java bytecodes in the
  2388.  * appropriate java env */
  2389. typedef struct {
  2390.     JRIEnv  *env;
  2391.     char    *pc;
  2392.     JSBool  ok;
  2393. } js_ExecuteJava_data;
  2394.  
  2395. static void
  2396. js_ExecuteJava_stub(void *d)
  2397. {
  2398.     js_ExecuteJava_data *data = d;
  2399.     PR_EXTERN(bool_t) ExecuteJava(unsigned char  *, ExecEnv *ee);
  2400.  
  2401.     data->ok = (JSBool) ExecuteJava((unsigned char*) data->pc, /*FIXME*/(ExecEnv*)data->env);
  2402. }
  2403.  
  2404. /* this is a copy of do_execute_java_method_vararg modified for
  2405.  *  a jsval* argument list instead of a va_list
  2406.  */
  2407. static JSBool
  2408. do_js_execute_java_method(JSContext *cx, void *obj,
  2409.                  struct methodblock *mb, JSBool isStaticCall,
  2410.                  int argc, jsval *argv,
  2411.                  jsval *rval)
  2412. {
  2413.     ExecEnv *ee = jsj_GetCurrentEE(cx);
  2414.     char *method_name;
  2415.     char *method_signature;
  2416.     JavaFrame *current_frame, *previous_frame;
  2417.     JavaStack *current_stack;
  2418.     char *sigRest;
  2419.     int cost = 0;
  2420.     stack_item *firstarg;
  2421.     JSBool success = JS_TRUE;
  2422.     JSJClassData *classData;
  2423.  
  2424.     unsigned char pc[6];
  2425.     cp_item_type  constant_pool[10];
  2426.     unsigned char cpt[10];
  2427.     JSBool ok;
  2428.  
  2429.  
  2430.     if (!ee) return JS_FALSE;
  2431.  
  2432.     /* push the safety frame before the call frame */
  2433.     if (!js_pushSafeFrame(cx, ee, &classData))
  2434.     return JS_FALSE;
  2435.  
  2436.     method_name = fieldname(&mb->fb);
  2437.     method_signature = fieldsig(&mb->fb);
  2438.  
  2439.     previous_frame = ee->current_frame;
  2440.     if (previous_frame == 0) {
  2441.     /* bottommost frame on this Exec Env. */
  2442.     current_stack = ee->initial_stack;
  2443.     current_frame = (JavaFrame *)(current_stack->data); /* no vars */
  2444.     } else {
  2445.     int args_size = mb->args_size;
  2446.     current_stack = previous_frame->javastack; /* assume same stack */
  2447.     if (previous_frame->current_method) {
  2448.         int size = previous_frame->current_method->maxstack;
  2449.         current_frame = (JavaFrame *)(&previous_frame->ostack[size]);
  2450.     } else {
  2451.             /* The only frames that don't have a mb are pseudo frames like
  2452.              * this one and they don't really touch their stack. */
  2453.         current_frame = (JavaFrame *)(previous_frame->optop + 3);
  2454.     }
  2455.     if (current_frame->ostack + args_size > current_stack->end_data) {
  2456.             /* Ooops.  The current stack isn't big enough.  */
  2457.         if (current_stack->next != 0) {
  2458.         current_stack = current_stack->next;
  2459.         } else {
  2460.         current_stack = CreateNewJavaStack(ee, current_stack);
  2461.         if (current_stack == 0) {
  2462.             JS_ReportOutOfMemory(cx);
  2463.             success = JS_FALSE;
  2464.             goto done;
  2465.         }
  2466.         }
  2467.         /* no vars */
  2468.         current_frame = (JavaFrame *)(current_stack->data);
  2469.     }
  2470.     }
  2471.     current_frame->prev = previous_frame;
  2472.     current_frame->javastack = current_stack;
  2473.     current_frame->optop = current_frame->ostack;
  2474.     current_frame->vars = 0;    /* better not reference any! */
  2475.     current_frame->constant_pool = 0; /* Until we set it up */
  2476.     current_frame->monitor = 0; /* not monitoring anything */
  2477.     current_frame->annotation = 0;
  2478.     current_frame->current_method = 0;
  2479.  
  2480.     /*
  2481.      * Allocate space for all the operands before they are actually
  2482.      * converted, because conversion may need to use this stack.
  2483.      */
  2484.     firstarg = current_frame->optop;
  2485.     current_frame->optop += mb->args_size;
  2486.  
  2487.     ee->current_frame = current_frame;
  2488.  
  2489.     /* Push the target object, if not a static call. */
  2490.     if (!isStaticCall)
  2491.     (firstarg++)->p = obj;
  2492.  
  2493.     /* Now convert the args into the space on the stack. */
  2494.     if (!js_convertJSToJArgs(cx, firstarg, mb, argc, argv,
  2495.                  JS_FALSE /* actually convert */,
  2496.                  &sigRest, &cost)) {
  2497.         /* the method shouldn't have matched if this was going to happen! */
  2498.         JS_ReportError(cx, "internal error: argument conversion failed");
  2499.     success = JS_FALSE;
  2500.     goto done;
  2501.     }
  2502.  
  2503.     /* build the bytecodes and constant table for the call */
  2504.     constant_pool[CONSTANT_POOL_TYPE_TABLE_INDEX].p = cpt;
  2505.     cpt[0] = CONSTANT_POOL_ENTRY_RESOLVED;
  2506.  
  2507.     pc[0] = isStaticCall ? opc_invokestatic_quick
  2508.     : opc_invokenonvirtual_quick;
  2509.     pc[1] = 0; pc[2] = 1;    /* constant pool entry #1 */
  2510.     pc[3] = opc_return;
  2511.  
  2512.     constant_pool[1].p = mb;
  2513.     cpt[1] = CONSTANT_POOL_ENTRY_RESOLVED | CONSTANT_Methodref;
  2514.  
  2515.     current_frame->constant_pool = constant_pool;
  2516.  
  2517.     /* Run the byte codes in java-land catch any exceptions. */
  2518.     ee->exceptionKind = EXCKIND_NONE;
  2519.  
  2520.     {
  2521.     js_ExecuteJava_data data;
  2522.     data.pc = (char*) pc;
  2523.     js_CallJava(cx, js_ExecuteJava_stub, &data,
  2524.                JS_FALSE /* we pushed the safety frame already */);
  2525.     ok = data.ok;
  2526.     }
  2527.  
  2528.     if (ok) {
  2529.     JSBool ret;
  2530.         PR_LOG(MojaSrc, debug, ("method call succeeded\n"));
  2531.     ret = java_returnAsJSValue(cx, rval, ee, sigRest);
  2532.     if (!ret) {
  2533.         JS_ReportError(cx,
  2534.                            "can't convert Java return value with signature %s",
  2535.                sigRest);
  2536.         success = JS_FALSE;
  2537.     }
  2538.     } else {
  2539.     success = JS_FALSE;
  2540.     }
  2541.  
  2542.   done:
  2543.     /* Our caller can look at ee->exceptionKind and ee->exception. */
  2544.     ee->current_frame = previous_frame;
  2545.     /* pop the safety frame */
  2546.     js_popSafeFrame(cx, ee, classData);
  2547.  
  2548.     /* FIXMEold cx->javaEnv = saved; */
  2549.  
  2550.     return success;
  2551. }
  2552.  
  2553. static JSBool
  2554. js_javaMethodWrapper(JSContext *cx, JSObject *obj,
  2555.              PRUintn argc, jsval *argv, jsval *rval)
  2556. {
  2557.     JSFunction *fun;
  2558.     const char *name;
  2559.     JSJava *java;
  2560.     ClassClass *cb;
  2561.     struct methodblock *realmb;
  2562.     JSBool success;
  2563.  
  2564.     PR_ASSERT(JS_TypeOfValue(cx, argv[-2]) == JSTYPE_FUNCTION);
  2565.     if (!JS_InstanceOf(cx, obj, &java_class, argv))
  2566.     return JS_FALSE;
  2567.  
  2568.     fun = JS_GetPrivate(cx, JSVAL_TO_OBJECT(argv[-2]));
  2569.     name = JS_GetFunctionName(fun);
  2570.  
  2571.     java = JS_GetPrivate(cx, obj);
  2572.     cb = java->cb;
  2573.  
  2574.     PR_LOG(MojaSrc, debug, ("entered methodwrap, fun=0x%x, name=\"%s\"(0x%x)",
  2575.              fun, name, name));
  2576.  
  2577.     /* match argc,argv against the signatures of the java methods */
  2578.     realmb = matchMethod(cx, cb, java->type==JAVA_CLASS, name, argc, argv,
  2579.              JS_TRUE /* reportErrors */);
  2580.  
  2581.     if (!realmb) {
  2582.     return JS_FALSE;
  2583.     }
  2584.  
  2585.     PR_LOG(MojaSrc, debug, ("calling %sjava method %s with signature %s",
  2586.                          realmb->fb.access & ACC_STATIC ? "static " : "",
  2587.              realmb->fb.name, realmb->fb.signature));
  2588.  
  2589.     success =
  2590.       do_js_execute_java_method(cx,
  2591.                 realmb->fb.access & ACC_STATIC ? 0 : java->handle,
  2592.                 realmb,
  2593.                 realmb->fb.access & ACC_STATIC /* isStaticCall */,
  2594.                 argc, argv, rval);
  2595.  
  2596.     return success;
  2597. }
  2598.  
  2599.  
  2600. static JSBool
  2601. js_javaConstructorWrapper(JSContext *cx, JSObject *obj,
  2602.               PRUintn argc, jsval *argv,
  2603.               jsval *rval)
  2604. {
  2605.     JSObject *funobj;
  2606.     ClassClass *cb;
  2607.     struct methodblock *realmb;
  2608.     HObject *ho;
  2609.     JSBool success;
  2610.     jsval tmp;
  2611.     ExecEnv *ee;
  2612.  
  2613.     PR_LOG(MojaSrc, debug, ("entered java constructor wrapper\n"));
  2614.  
  2615.     PR_ASSERT(JS_TypeOfValue(cx, argv[-2]) == JSTYPE_FUNCTION);
  2616.     funobj = JSVAL_TO_OBJECT(argv[-2]);
  2617.     if (!JS_GetProperty(cx, funobj, "#javaClass", &tmp))
  2618.     return JS_FALSE;
  2619.     cb = JSVAL_TO_PRIVATE(tmp);
  2620.  
  2621.     if (!JS_InstanceOf(cx, obj, &java_class, 0)
  2622.     || JS_GetPrivate(cx, obj)) {
  2623.     /* not a fresh object! */
  2624.         /* 3.0 allowed you to call a java constructor without using
  2625.          * "new" so we do the same.  setting obj to null causes
  2626.          * ReflectJava to create a new js object rather than using
  2627.          * the "this" argument of the constructor */
  2628.         obj = NULL;
  2629.     }
  2630.  
  2631.     /* FIXME these are copied from interpreter.c without much understanding */
  2632.     if (cbAccess(cb) & (ACC_INTERFACE | ACC_ABSTRACT)) {
  2633.         JS_ReportError(cx, "can't instantiate Java class");
  2634.     return JS_FALSE;
  2635.     }
  2636.     if (!VerifyClassAccess(0, cb, FALSE)) {
  2637.         JS_ReportError(cx, "can't access Java class");
  2638.     return JS_FALSE;
  2639.     }
  2640.  
  2641.     /* match argc,argv against the signatures of the java constructors */
  2642.     realmb = matchMethod(cx, cb, JS_FALSE, "<init>", argc, argv,
  2643.              JS_TRUE /* reportErrors */);
  2644.     if (!realmb) {
  2645.     return JS_FALSE;
  2646.     }
  2647.  
  2648.     if (!VerifyFieldAccess(0, fieldclass(&realmb->fb),
  2649.                realmb->fb.access, FALSE)) {
  2650.         JS_ReportError(cx, "illegal access to Java constructor");
  2651.     return JS_FALSE;
  2652.     }
  2653.  
  2654.     PR_LOG(MojaSrc, debug, ("calling java constructor with signature %s",
  2655.         realmb->fb.signature));
  2656.  
  2657.     /*
  2658.      * Because newobject can fail, and call SignalError, which calls
  2659.      * FindClassFromClass, etc.
  2660.      */
  2661.     /* Allocate the object */
  2662.     ee = jsj_GetCurrentEE(cx);
  2663.     if (!ee)
  2664.     return JS_FALSE;
  2665.     if ((ho = newobject(cb, 0, ee)) == 0) {
  2666.     JS_ReportOutOfMemory(cx);
  2667.     return JS_FALSE;
  2668.     }
  2669.  
  2670.     success = do_js_execute_java_method(cx, ho, realmb,
  2671.                     JS_FALSE /* isStaticCall */,
  2672.                     argc, argv,
  2673.                     &tmp  /* gets set to void */);
  2674.  
  2675.     /* FIXME the interpreter.c code calls cbDecRef(cb) at the end,
  2676.      * why?  should we? */
  2677.  
  2678.     if (success) {
  2679.     JSObject *jso = js_ReflectJava(cx, JAVA_OBJECT, ho, cb, 0, obj);
  2680.     if (jso) {
  2681.         *rval = OBJECT_TO_JSVAL(jso);
  2682.         return JS_TRUE;
  2683.     }
  2684.     }
  2685.     return JS_FALSE;
  2686. }
  2687.  
  2688. /*   *     *   *     *   *     *   *     *   *     *   *     *   *     *   *     *   *     *
  2689.  *
  2690.  *   JS/java reflection tables
  2691.  *
  2692.  *   *     *   *     *   *     *   *     *   *     *   *     *   *     *   *     *   *     */
  2693.  
  2694. typedef struct ScanArgs {
  2695.     GCInfo    *gcInfo;
  2696. } ScanArgs;
  2697.  
  2698. PR_STATIC_CALLBACK (int)
  2699. scanJSJavaReflectionEntry(PRHashEntry *he, int i, void *arg)
  2700. {
  2701.     ScanArgs *args = arg;
  2702.     JSObject *jso = he->value;
  2703.  
  2704.     /* scan the handle */
  2705.     if (1 /* jso != JSO_REFLECTION_IN_PROGRESS */) {
  2706.     JSJava *java = JSVAL_TO_PRIVATE(OBJ_GET_SLOT(jso, JSSLOT_PRIVATE));
  2707.  
  2708.     args->gcInfo->livePointer((void *)java->handle);
  2709.     }
  2710.  
  2711.     return HT_ENUMERATE_NEXT;
  2712. }
  2713.  
  2714. PR_STATIC_CALLBACK (void)
  2715. scanJSJavaReflections(void *runtime)
  2716. {
  2717.     ScanArgs args;
  2718.  
  2719. /*    PR_PROCESS_ROOT_LOG(("Scanning JS reflections of java objects")); */
  2720.  
  2721.     args.gcInfo = PR_GetGCInfo();
  2722.  
  2723.     /* FIXME this is kind of scary long-term access inside the
  2724.      * monitor - is there any alternative? */
  2725. #ifndef NSPR20
  2726.     PR_EnterMonitor(javaReflectionsMonitor);
  2727. #endif
  2728.     if (javaReflections) {
  2729.     PR_HashTableEnumerateEntries(javaReflections,
  2730.                      scanJSJavaReflectionEntry,
  2731.                      &args);
  2732.     }
  2733. #ifndef NSPR20
  2734.     PR_ExitMonitor(javaReflectionsMonitor);
  2735. #endif
  2736. }
  2737.  
  2738. #ifdef NSPR20
  2739. void PR_CALLBACK PrepareJSLocksForGC(GCLockHookArg arg1)
  2740. {
  2741.     PR_ASSERT(arg1 == PR_GCBEGIN || arg1 == PR_GCEND);
  2742.  
  2743.     if (arg1 == PR_GCBEGIN)
  2744.         PR_EnterMonitor(javaReflectionsMonitor);
  2745.     else
  2746.         PR_ExitMonitor(javaReflectionsMonitor);
  2747. }
  2748. #endif
  2749.  
  2750. static JSObject *
  2751. js_ReflectJava(JSContext *cx, JSJavaType type, HObject *handle,
  2752.            ClassClass *cb, char *sig, JSObject *useMe)
  2753. {
  2754.     JSObject *mo;
  2755.     JSJava *java;
  2756.     void *key;
  2757.     PRHashEntry *he;
  2758.  
  2759.     PR_EnterMonitor(javaReflectionsMonitor);
  2760.     if (!javaReflections) goto fail;
  2761.  
  2762.     /* see if it's already been reflected */
  2763.     switch (type) {
  2764.       case JAVA_CLASS:
  2765.     key = (void *) cbName(cb);
  2766.     break;
  2767.       case JAVA_OBJECT:
  2768.     key = (void *) handle;
  2769.     break;
  2770.       case JAVA_ARRAY:
  2771.     key = (void *) handle;
  2772.     break;
  2773.       default:
  2774.     break;
  2775.     }
  2776.  
  2777.     mo = PR_HashTableLookup(javaReflections, key);
  2778.     if (mo)
  2779.     goto succeed;
  2780.  
  2781.     /* if we got this far, it isn't in the hash table.
  2782.      * if useMe is non-null, we were called via "new" and should
  2783.      * use the object that was already constructed if possible */
  2784.     if (useMe) {
  2785.     /* make sure we have the right js class and no private
  2786.      * data yet.  if not, ignore the provided object */
  2787.     if (JS_GetPrivate(cx, useMe))
  2788.         useMe = NULL;
  2789.     else {
  2790.         JSClass *clasp = JS_GetClass(useMe);
  2791.         switch (type) {
  2792.           case JAVA_CLASS:
  2793.           case JAVA_OBJECT:
  2794.         if (clasp != &java_class)
  2795.             useMe = NULL;
  2796.         break;
  2797.           case JAVA_ARRAY:
  2798.         if (clasp != &javaarray_class)
  2799.             useMe = NULL;
  2800.         break;
  2801.         }
  2802.     }
  2803.     }
  2804.     mo = useMe;
  2805.  
  2806.     /* build the private data for the js reflection */
  2807.     java = JS_malloc(cx, sizeof(JSJava));
  2808.     if (!java)
  2809.     goto fail;
  2810.     java->type = type;
  2811.     java->handle = handle;
  2812.     java->cb = cb;
  2813.     java->signature = sig;
  2814.  
  2815.     switch (type) {
  2816.       case JAVA_CLASS:
  2817.       case JAVA_OBJECT:
  2818.     if (!mo)
  2819.         mo = JS_NewObject(cx, &java_class, 0, 0);
  2820.     if (!mo || ! JS_SetPrivate(cx, mo, java))
  2821.         goto fail_free_java;
  2822.     break;
  2823.       case JAVA_ARRAY:
  2824.     if (!mo)
  2825.         mo = JS_NewObject(cx, &javaarray_class, 0, 0);
  2826.     if (!mo || ! JS_SetPrivate(cx, mo, java))
  2827.         goto fail_free_java;
  2828.     if (! JS_DefineProperty(cx, mo, "length",
  2829.                    INT_TO_JSVAL((jsint)obj_length(handle)),
  2830.                    0, 0, JSPROP_READONLY))
  2831.         goto fail;
  2832.     break;
  2833.       default:
  2834.     break;
  2835.     }
  2836.  
  2837.     /* add it to the table */
  2838.     he = PR_HashTableAdd(javaReflections, key, mo);
  2839.     if (!he) {
  2840.     JS_ReportOutOfMemory(cx);
  2841.     mo = 0;
  2842.     }
  2843.  
  2844.   succeed:
  2845.     PR_ExitMonitor(javaReflectionsMonitor);
  2846.     return mo;
  2847.  
  2848.   fail_free_java:
  2849.     JS_free(cx, java);
  2850.   fail:
  2851.     PR_ExitMonitor(javaReflectionsMonitor);
  2852.     return 0;
  2853. }
  2854.  
  2855. /*
  2856.  * Get the element type for a java array.  *classp will be set to null
  2857.  * if it's not of object type.
  2858.  */
  2859. static JSBool
  2860. js_JArrayElementType(HObject *handle, char **sig, ClassClass **classp)
  2861. {
  2862.     /* figure out the signature from the type */
  2863.     unsigned long elementtype = obj_flags(handle);
  2864.     char *elementsig;
  2865.  
  2866.     *sig = 0;
  2867.     *classp = 0;
  2868.  
  2869.     switch (elementtype) {
  2870.       case T_CLASS:
  2871.     *classp = (ClassClass*)
  2872.           unhand((HArrayOfObject *)handle)->body[class_offset(handle)];
  2873.     elementsig = SIGNATURE_CLASS_STRING;
  2874.     break;
  2875.       case T_BOOLEAN:
  2876.     elementsig = SIGNATURE_BOOLEAN_STRING;
  2877.     break;
  2878.       case T_CHAR:
  2879.     elementsig = SIGNATURE_CHAR_STRING;
  2880.     break;
  2881.       case T_FLOAT:
  2882.     elementsig = SIGNATURE_FLOAT_STRING;
  2883.     break;
  2884.       case T_DOUBLE:
  2885.     elementsig = SIGNATURE_DOUBLE_STRING;
  2886.     break;
  2887.       case T_BYTE:
  2888.     elementsig = SIGNATURE_BYTE_STRING;
  2889.     break;
  2890.       case T_SHORT:
  2891.     elementsig = SIGNATURE_SHORT_STRING;
  2892.     break;
  2893.       case T_INT:
  2894.     elementsig = SIGNATURE_INT_STRING;
  2895.     break;
  2896.       case T_LONG:
  2897.     elementsig = SIGNATURE_LONG_STRING;
  2898.     break;
  2899.       default:
  2900.     PR_ASSERT(0);
  2901.     return JS_FALSE;
  2902.     }
  2903.  
  2904.     *sig = elementsig;
  2905.     return JS_TRUE;
  2906. }
  2907.  
  2908. PR_IMPLEMENT(JSObject *)
  2909. js_ReflectJObjectToJSObject(JSContext *cx, HObject *handle)
  2910. {
  2911.     JSObject *mo = 0;
  2912.     ExecEnv *ee;
  2913.  
  2914.     /* force initialization */
  2915.     ee = jsj_GetCurrentEE(cx);
  2916.     if (!ee) return 0;
  2917.  
  2918.     if (handle) {
  2919.     if (obj_flags(handle)) {
  2920.         char *elementSig;
  2921.         ClassClass *elementClazz;
  2922.  
  2923.         if (!js_JArrayElementType(handle, &elementSig, &elementClazz)) {
  2924.         /* FIXMEbe report an error! */
  2925.         return 0;
  2926.         }
  2927.  
  2928.         mo = js_ReflectJava(cx, JAVA_ARRAY, handle,
  2929.                 elementClazz, elementSig, 0);
  2930.             PR_LOG(MojaSrc, debug, ("reflected array[%s] 0x%x as JSObject* 0x%x",
  2931.                  elementSig, handle, mo));
  2932.  
  2933.     } else {
  2934.         mo = js_ReflectJava(cx, JAVA_OBJECT, handle,
  2935.                 obj_classblock(handle), 0, 0);
  2936.             PR_LOG(MojaSrc, debug, ("reflected HObject* 0x%x as JSObject* 0x%x",
  2937.                  handle, mo));
  2938.     }
  2939.     }
  2940.  
  2941.     return mo;
  2942. }
  2943.  
  2944. JSObject *
  2945. js_ReflectJClassToJSObject(JSContext *cx, ClassClass *cb)
  2946. {
  2947.     ExecEnv *ee;
  2948.     JSObject *mo;
  2949.  
  2950.     /* force initialization */
  2951.     ee = jsj_GetCurrentEE(cx);
  2952.     if (!ee) return 0;
  2953.  
  2954.     mo = js_ReflectJava(cx, JAVA_CLASS, (HObject *) cbHandle(cb), cb, 0, 0);
  2955.  
  2956.     PR_LOG(MojaSrc, debug, ("reflected ClassClass* 0x%x as JSObject* 0x%x",
  2957.              cb, mo));
  2958.  
  2959.     return mo;
  2960. }
  2961.  
  2962. /*    *    *    *    *    *    *    *    *    */
  2963.  
  2964. /*
  2965.  * JSJavaSlot is a java slot which will be resolved as a method
  2966.  * or a field depending on context.
  2967.  */
  2968. struct JSJavaSlot {
  2969.     JSObject        *obj;        /* the object or class reflection */
  2970.     jsval        value;        /* the field value when created */
  2971.     JSString        *name;        /* name of the field or method */
  2972.     struct fieldblock    *fb;        /* fieldblock if there is a field */
  2973. };
  2974.  
  2975. /* none of these should ever be called, since javaslot_convert will
  2976.  * first turn the slot into the underlying object if there is one */
  2977. PR_STATIC_CALLBACK(JSBool)
  2978. javaslot_getProperty(JSContext *cx, JSObject *obj, jsval slot,
  2979.               jsval *vp)
  2980. {
  2981.     /* evil so that the assign hack doesn't kill us */
  2982.     if (slot == STRING_TO_JSVAL(ATOM_TO_STRING(cx->runtime->atomState.assignAtom))) {
  2983.     *vp = JSVAL_VOID;
  2984.     return JS_TRUE;
  2985.     }
  2986.  
  2987.     JS_ReportError(cx, "Java slots have no properties");
  2988.     return JS_FALSE;
  2989. }
  2990.  
  2991. PR_STATIC_CALLBACK(JSBool)
  2992. javaslot_setProperty(JSContext *cx, JSObject *obj, jsval slot,
  2993.          jsval *vp)
  2994. {
  2995.     JS_ReportError(cx, "Java slots have no properties");
  2996.     return JS_FALSE;
  2997. }
  2998.  
  2999. PR_STATIC_CALLBACK(JSBool)
  3000. javaslot_resolve(JSContext *cx, JSObject *obj, jsval id)
  3001. {
  3002.     return JS_TRUE;
  3003. }
  3004.  
  3005. PR_STATIC_CALLBACK(void)
  3006. javaslot_finalize(JSContext *cx, JSObject *obj)
  3007. {
  3008.     JSJavaSlot *slot = JS_GetPrivate(cx, obj);
  3009.  
  3010.     /* the value may be holding a reference to something... */
  3011.     JS_RemoveRoot(cx, &slot->value);
  3012.  
  3013.     /* drop the object of which this is a slot */
  3014.     JS_RemoveRoot(cx, &slot->obj);
  3015.  
  3016.     /* drop the name */
  3017.     JS_UnlockGCThing(cx, slot->name);
  3018.  
  3019.     JS_free(cx, slot);
  3020.  
  3021.     PR_LOG(MojaSrc, debug, ("finalizing JSJavaSlot 0x%x", obj));
  3022. }
  3023.  
  3024. PR_STATIC_CALLBACK(JSBool)
  3025. javaslot_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp)
  3026. {
  3027.     JSJavaSlot *slot = JS_GetPrivate(cx, obj);
  3028.     const char *name;
  3029.     JSFunction *fun;
  3030.     JSString *str;
  3031.  
  3032.     name = JS_GetStringBytes(slot->name);
  3033.     switch (type) {
  3034.       case JSTYPE_FUNCTION:
  3035.     fun = JS_NewFunction(cx, js_javaMethodWrapper, 0, JSFUN_BOUND_METHOD,
  3036.                  slot->obj, name);
  3037.     if (!fun)
  3038.         return JS_FALSE;
  3039.  
  3040.     PR_LOG(MojaSrc, debug,
  3041.                ("converted slot to function 0x%x with name %s\n",
  3042.         fun, name));
  3043.  
  3044.     *vp = OBJECT_TO_JSVAL(JS_GetFunctionObject(fun));
  3045.     return JS_TRUE;
  3046.  
  3047.       case JSTYPE_OBJECT:
  3048.         PR_LOG(MojaSrc, debug, ("converting java slot 0x%x to object", obj));
  3049.     /* FALL THROUGH */
  3050.       case JSTYPE_NUMBER:
  3051.       case JSTYPE_BOOLEAN:
  3052.       case JSTYPE_STRING:
  3053.     if (slot->fb) {
  3054.         return JS_ConvertValue(cx, slot->value, type, vp);
  3055.     }
  3056.     if (type == JSTYPE_STRING) {
  3057.         JSJava *java = JS_GetPrivate(cx, slot->obj);
  3058.         char *cstr, *cp;
  3059.  
  3060.         PR_ASSERT(java->type == JAVA_OBJECT || java->type == JAVA_CLASS);
  3061.             cstr = PR_smprintf("[JavaMethod %s.%s]", cbName(java->cb), name);
  3062.         if (!cstr) {
  3063.         JS_ReportOutOfMemory(cx);
  3064.         return JS_FALSE;
  3065.         }
  3066.             for (cp = cstr; *cp != '\0'; cp++)
  3067.                 if (*cp == '/')
  3068.                     *cp = '.';
  3069.         str = JS_NewString(cx, cstr, strlen(cstr));
  3070.         if (!str) {
  3071.                 free(cstr);
  3072.         return JS_FALSE;
  3073.         }
  3074.         *vp = STRING_TO_JSVAL(str);
  3075.         return JS_TRUE;
  3076.     }
  3077.     if (type != JSTYPE_OBJECT) {
  3078.             JS_ReportError(cx, "no field with name \"%s\"", name);
  3079.         return JS_FALSE;
  3080.     }
  3081.     *vp = OBJECT_TO_JSVAL(obj);
  3082.     break;
  3083.       default:
  3084.     break;
  3085.     }
  3086.     return JS_TRUE;
  3087. }
  3088.  
  3089. static JSClass javaslot_class = {
  3090.     "JavaSlot",
  3091.     JSCLASS_HAS_PRIVATE,
  3092.     JS_PropertyStub, JS_PropertyStub,
  3093.     javaslot_getProperty, javaslot_setProperty, JS_EnumerateStub,
  3094.     javaslot_resolve, javaslot_convert, javaslot_finalize
  3095. };
  3096.  
  3097. static JSBool
  3098. js_reflectJavaSlot(JSContext *cx, JSObject *obj, JSString *str,
  3099.            jsval *vp)
  3100. {
  3101.     JSJava *java = JS_GetPrivate(cx, obj);
  3102.     ClassClass *cb = java->cb;
  3103.     struct fieldblock *fb;
  3104.     struct methodblock *mb;
  3105.     JSJavaSlot *slot;
  3106.     JSObject *mo;
  3107.     const char *name = JS_GetStringBytes(str);
  3108.  
  3109.     *vp = JSVAL_VOID;
  3110.  
  3111.     /* PR_ASSERT(obj->clazz == &java_class); */
  3112.  
  3113.     /* if there's a field get its value at reflection time */
  3114.     fb = java_lookup_field(cx, cb, java->type == JAVA_CLASS, name);
  3115.     if (fb) {
  3116.     if (!js_convertJFieldToJSValue(cx, java->handle, fb,
  3117.                        vp, JSTYPE_VOID)) {
  3118.         /*
  3119.              * If this happens, the field had a value that couldn't
  3120.          * be represented in JS.
  3121.          */
  3122.             PR_LOG(MojaSrc, error, ("looking up initial field value failed!"));
  3123.  
  3124.         /* FIXME should really set a flag that will cause an error
  3125.          * only if the slot is accessed as a field.  for now we
  3126.              * make it look like there wasn't any field by that name,
  3127.          * which is less informative */
  3128.         fb = 0;
  3129.     }
  3130.     }
  3131.  
  3132. #ifndef REFLECT_ALL_SLOTS_LAZILY
  3133.     /* match the name against the signatures of the java methods */
  3134.     mb = matchMethod(cx, cb, java->type==JAVA_CLASS, name, -1, 0,
  3135.              JS_FALSE /* reportErrors */);
  3136.  
  3137.     if (!fb) {
  3138.     JSFunction *fun;
  3139.  
  3140.     if (!mb) {
  3141.         /* nothing by that name, report an error */
  3142.             JS_ReportError(cx, "Java object has no field or method named %s",
  3143.                name);
  3144.         return JS_FALSE;
  3145.     }
  3146.  
  3147.         /* if we get here, there's a method but no field by this name */
  3148.     fun = JS_NewFunction(cx, js_javaMethodWrapper, 0,
  3149.                  JSFUN_BOUND_METHOD, obj, name);
  3150.     if (!fun)
  3151.         return JS_FALSE;
  3152.  
  3153.     PR_LOG(MojaSrc, debug,
  3154.                ("eagerly converted slot to function 0x%x with name %s(0x%x)\n",
  3155.         fun, name, name));
  3156.  
  3157.     *vp = OBJECT_TO_JSVAL(JS_GetFunctionObject(fun));
  3158.     return JS_TRUE;
  3159.     } else if (!mb) {
  3160.         /* there's a field but no method by this name */
  3161.     /* looking up the field already set *vp */
  3162.     return JS_TRUE;
  3163.     }
  3164.  
  3165.     /* if we get here there are both fields and methods by this name,
  3166.      * so we create a slot object to delay the binding */
  3167. #endif
  3168.  
  3169.     slot = (JSJavaSlot *) JS_malloc(cx, sizeof(JSJavaSlot));
  3170.     if (!slot) {
  3171.     return JS_FALSE;
  3172.     }
  3173.  
  3174.     /* corresponding removes and unlocks are in javaslot_finalize */
  3175.     slot->obj = obj;
  3176.     if (!JS_AddRoot(cx, &slot->obj)) {
  3177.     JS_free(cx, slot);
  3178.     return JS_FALSE;
  3179.     }
  3180.     slot->value = *vp;
  3181.     if (!JS_AddRoot(cx, &slot->value)) {
  3182.     JS_RemoveRoot(cx, &slot->obj);
  3183.     JS_free(cx, slot);
  3184.     return JS_FALSE;
  3185.     }
  3186.  
  3187.     /* FIXME check return value? */
  3188.     JS_LockGCThing(cx, str);
  3189.     slot->name = str;
  3190.     slot->fb = fb;
  3191.  
  3192.     mo = JS_NewObject(cx, &javaslot_class, 0, 0);
  3193.     JS_SetPrivate(cx, mo, slot);
  3194.  
  3195.     PR_LOG(MojaSrc, debug, ("reflected slot %s of 0x%x as 0x%x",
  3196.         name, java->handle, mo));
  3197.     if (!mo) {
  3198.     JS_RemoveRoot(cx, &slot->obj);
  3199.     JS_RemoveRoot(cx, &slot->value);
  3200.     JS_UnlockGCThing(cx, slot->name);
  3201.     JS_free(cx, slot);
  3202.     return JS_FALSE;
  3203.     }
  3204.     *vp = OBJECT_TO_JSVAL(mo);
  3205.     return JS_TRUE;
  3206. }
  3207.  
  3208. /***********************************************************************/
  3209.  
  3210. /* a saved JS error state */
  3211. typedef struct SavedJSError SavedJSError;
  3212. struct SavedJSError {
  3213.     char *message;
  3214.     JSErrorReport report;
  3215.     SavedJSError *next;
  3216. };
  3217.  
  3218. /*
  3219.  * capture a JS error that occurred in JS code called by java.
  3220.  * makes a copy of the JS error data and hangs it off the JS
  3221.  * environment.  when the JS code returns, this is checked and
  3222.  * used to generate a JSException.  if the JSException is uncaught
  3223.  * and makes it up to another layer of JS, the error will be
  3224.  * reinstated with JS_ReportError
  3225.  */
  3226. PR_IMPLEMENT(void)
  3227. js_JavaErrorReporter(JSContext *cx, const char *message, JSErrorReport *report)
  3228. {
  3229.     /* save the error state */
  3230.     SavedJSError *newerr;
  3231.     newerr = malloc(sizeof(SavedJSError));
  3232.     if (!newerr) {
  3233.     /* FIXME not much we can do here, abort? */
  3234.     return;
  3235.     }
  3236.     newerr->message = 0;
  3237.     if (message) {
  3238.     newerr->message = strdup(message);
  3239.     if (!newerr->message) {
  3240.         /* FIXME not much we can do here, abort? */
  3241.         free(newerr);
  3242.         return;
  3243.     }
  3244.     }
  3245.     newerr->report.filename = 0;
  3246.     newerr->report.lineno = 0;
  3247.     newerr->report.linebuf = 0;
  3248.     newerr->report.tokenptr = 0;
  3249.  
  3250.     if (report) {
  3251.     if (report->filename) {
  3252.         newerr->report.filename = strdup(report->filename);
  3253.         if (!newerr->report.filename) {
  3254.         /* FIXME not much we can do here, abort? */
  3255.         free(newerr->message);
  3256.         free(newerr);
  3257.         return;
  3258.         }
  3259.     }
  3260.     newerr->report.lineno = report->lineno;
  3261.  
  3262.     if (report->linebuf) {
  3263.         newerr->report.linebuf = strdup(report->linebuf);
  3264.         if (!newerr->report.linebuf) {
  3265.         /* FIXME not much we can do here, abort? */
  3266.         free((void*)newerr->report.filename);
  3267.         free(newerr->message);
  3268.         free(newerr);
  3269.         return;
  3270.         }
  3271.         newerr->report.tokenptr = newerr->report.linebuf +
  3272.                       (report->tokenptr - report->linebuf);
  3273.     }
  3274.     }
  3275.  
  3276.     /* push this error */
  3277.     newerr->next = cx->savedErrors;
  3278.     cx->savedErrors = newerr;
  3279. }
  3280.  
  3281. static SavedJSError *
  3282. js_js_FreeError(SavedJSError *err)
  3283. {
  3284.     SavedJSError *next = err->next;
  3285.  
  3286.     free(err->message);
  3287.     free((char*)err->report.filename);/*FIXME*/
  3288.     free((char*)err->report.linebuf);
  3289.     free(err);
  3290.  
  3291.     return next;
  3292. }
  3293.  
  3294. PR_IMPLEMENT(void)
  3295. jsj_ClearSavedErrors(JSContext *cx)
  3296. {
  3297.     while (cx->savedErrors)
  3298.         cx->savedErrors = js_js_FreeError(cx->savedErrors);
  3299. }
  3300.  
  3301. /* this is called upon returning from JS to java.  one possibility
  3302.  * is that the JS error was actually triggered by java at some point -
  3303.  * if so we throw the original java exception.    otherwise, each JS
  3304.  * error will have pushed something on JSContext->savedErrors, so
  3305.  * we convert them all to a string and throw a JSException with that
  3306.  * info.
  3307.  */
  3308. PR_IMPLEMENT(void)
  3309. js_JSErrorToJException(JSContext *cx, ExecEnv *ee)
  3310. {
  3311.     SavedJSError *err = 0;
  3312.  
  3313.     if (!cx->savedErrors) {
  3314.     exceptionClear(ee);
  3315.     PR_LOG(MojaSrc, debug,
  3316.                ("j-m succeeded with no exception cx=0x%x ee=0x%x", cx, ee));
  3317.     return;
  3318.     }
  3319.  
  3320.     /*
  3321.      * If there's a pending exception in the java env, assume it
  3322.      * needs to be propagated (since JS couldn't have caught it
  3323.      * and done something with it).
  3324.      */
  3325.     if (exceptionOccurred(ee)) {
  3326.     PR_LOG(MojaSrc, debug,
  3327.                ("j-m propagated exception through JS cx=0x%x ee=0x%x",
  3328.         cx, ee));
  3329.     return;     /* propagating is easy! */
  3330.     }
  3331.  
  3332.     /* otherwise, throw a JSException */
  3333.     /* get the message from the deepest saved JS error */
  3334.     err = cx->savedErrors;
  3335.     if (err) {
  3336.     while (err->next)
  3337.         err = err->next;
  3338.     }
  3339.  
  3340.     /* propagate any pending JS errors upward with a java exception */
  3341.     {
  3342.     JRIEnv *env = (JRIEnv*) ee;
  3343.     struct java_lang_String* message =
  3344.       JRI_NewStringUTF(env, err->message,
  3345.                strlen(err->message));
  3346.     struct java_lang_String* filename = err->report.filename
  3347.       ? JRI_NewStringUTF(env, err->report.filename,
  3348.                   strlen(err->report.filename))
  3349.       : NULL;
  3350.     int lineno = err->report.lineno;
  3351.     struct java_lang_String* source = err->report.linebuf
  3352.       ? JRI_NewStringUTF(env, err->report.linebuf,
  3353.                   strlen(err->report.linebuf))
  3354.       : NULL;
  3355.     int index = err->report.linebuf
  3356.       ? err->report.tokenptr - err->report.linebuf
  3357.       : 0;
  3358.     jref exc = (jref)
  3359.      execute_java_constructor((ExecEnv *)env,
  3360.                   NULL, JSExceptionClassBlock,
  3361.                                   "(Ljava/lang/String;Ljava/lang/String;ILjava/lang/String;I)",
  3362.                   message, filename, (int32_t)lineno, source, (int32_t)index);
  3363.  
  3364.     exceptionThrow(ee, (HObject *)exc);
  3365.     PR_LOG(MojaSrc, debug,
  3366.                ("j-m raised JSException \"%s\" cx=0x%x ee=0x%x",
  3367.         err->message, cx, ee));
  3368.     }
  3369. }
  3370.  
  3371. static JSBool
  3372. js_isJSException(ExecEnv *ee, HObject *exc)
  3373. {
  3374.     return strcmp(cbName(obj_array_classblock(exc)),
  3375.                   "netscape/javascript/JSException")
  3376.       ? JS_TRUE : JS_FALSE;
  3377. }
  3378.  
  3379. /*
  3380.  * This is called after returning from java to JS.  if the exception
  3381.  * is actually a JSException, we pull the original JS error state out
  3382.  * of the JSContext and use that.  Otherwise we turn the JSException
  3383.  * into a string and pass it up as a JS error
  3384.  */
  3385. static JSBool
  3386. js_JExceptionToJSError(JSContext *cx, ExecEnv *ee)
  3387. {
  3388.     SavedJSError *err = 0;
  3389.     char *message;
  3390.     JSBool success;
  3391.     JHandle *exc;
  3392.  
  3393.     /*
  3394.      * Get rid of any JS errors so far, but save the deepest one
  3395.      * in case this was a JSException and we re-report it.
  3396.      */
  3397.     /* FIXME the deepest one is the most interesting? */
  3398.     err = cx->savedErrors;
  3399.     if (err) {
  3400.     while (err->next)
  3401.         err = js_js_FreeError(err);
  3402.     }
  3403.  
  3404.     /* if no exception reached us, continue on our merry way */
  3405.     if (!exceptionOccurred(ee)) {
  3406.     PR_LOG(MojaSrc, debug,
  3407.                ("m-j succeeded, no exceptions cx=0x%x ee=0x%x",
  3408.         cx, ee));
  3409.     success = JS_TRUE;
  3410.     goto done;
  3411.     }
  3412.  
  3413.     /* if we got this far there was an error for sure */
  3414.     success = JS_FALSE;
  3415.  
  3416.     switch (ee->exceptionKind) {
  3417.       case EXCKIND_THROW:
  3418.         /* save the exception while we poke around in java */
  3419.         exc = ee->exception.exc;
  3420.         exceptionClear(ee);
  3421.  
  3422.     if (err && js_isJSException(ee, exc)) {
  3423.         js_ReportErrorAgain(cx, err->message, &err->report);
  3424.         PR_LOG(MojaSrc, debug,
  3425.                    ("m-j re-reported error \"%s\" cx=0x%x ee=0x%x",
  3426.             err->message, cx, ee));
  3427.     }
  3428.     /* otherwise, describe the exception to a string */
  3429.     else if (js_isSubclassOf(cx,
  3430.                  obj_array_classblock(exc),
  3431.                  ThrowableClassBlock)) {
  3432.         HString *hdetail =
  3433.           unhand((Hjava_lang_Throwable *)exc)->detailMessage;
  3434.         ClassClass *cb = obj_array_classblock(exc);
  3435.  
  3436.         message = (char *)
  3437.         JRI_GetStringPlatformChars((JRIEnv *) ee,
  3438.                        (struct java_lang_String *) hdetail,
  3439.                        (const jbyte *) cx->charSetName,
  3440.                        (jint) cx->charSetNameLength);
  3441.         PR_LOG(MojaSrc, debug,
  3442.                    ("m-j converted exception %s, \"%s\" to error cx=0x%x ee=0x%x",
  3443.             cbName(cb), message, cx, ee));
  3444.         /* pass the string to JS_ReportError */
  3445.             JS_ReportError(cx, "uncaught Java exception %s (\"%s\")",
  3446.                cbName(cb), message);
  3447.     }
  3448.  
  3449.         /* it's not a Throwable, somebody in java-land is being lame */
  3450.     else {
  3451.         ClassClass *cb = obj_array_classblock(exc);
  3452.             JS_ReportError(cx, "uncaught Java exception of class %s",
  3453.                   cbName(cb));
  3454.         PR_LOG(MojaSrc, debug,
  3455.                    ("m-j converted exception %s to error cx=0x%x ee=0x%x",
  3456.             cbName(cb), cx, ee));
  3457.     }
  3458.     break;
  3459.       case EXCKIND_STKOVRFLW:
  3460.         JS_ReportError(cx, "Java stack overflow, pc=0x%x", ee->exception.addr);
  3461.     break;
  3462.       default:
  3463.     JS_ReportError(cx,
  3464.                        "internal error: Java exception of unknown type %d",
  3465.                ee->exceptionKind);
  3466.     break;
  3467.     }
  3468.  
  3469.   done:
  3470.     if (err) {
  3471.     js_js_FreeError(err);
  3472.     cx->savedErrors = 0;
  3473.     }
  3474.     PR_LOG(MojaSrc, debug, ("m-j cleared JS errors cx=0x%x", cx));
  3475.     return success;
  3476. }
  3477.  
  3478. /***********************************************************************/
  3479.  
  3480. static JSBool
  3481. js_FindSystemClass(JSContext *cx, char *name, ClassClass **clazz)
  3482. {
  3483.     ExecEnv *ee;
  3484.  
  3485.     ee = jsj_GetCurrentEE(cx);
  3486.  
  3487.     if (!ee) return JS_FALSE;
  3488.  
  3489.     *clazz = FindClassFromClass(ee, name, TRUE, NULL);
  3490.  
  3491.     return js_JExceptionToJSError(cx, ee);
  3492. }
  3493.  
  3494. /*
  3495.  * All js_CallJava calls must use a data pointer that starts like
  3496.  * this one:
  3497.  */
  3498. typedef struct {
  3499.     JRIEnv *env;
  3500. } js_CallJava_data;
  3501.  
  3502. static JSBool
  3503. js_CallJava(JSContext *cx, JSJCallback doit, void *d, JSBool pushSafeFrame)
  3504. {
  3505.     js_CallJava_data *data = d;
  3506.     JSBool success = JS_TRUE;
  3507.     ExecEnv *ee;
  3508.     JSJClassData *classData;
  3509.  
  3510.     ee = jsj_GetCurrentEE(cx);
  3511.  
  3512.     if (!ee) return JS_FALSE;
  3513.  
  3514.     data->env = (JRIEnv *) ee;
  3515.  
  3516.     /* security: push the safety frame onto the java stack */
  3517.     if (pushSafeFrame)
  3518.     if (!js_pushSafeFrame(cx, ee, &classData)) {
  3519.         return JS_FALSE;
  3520.     }
  3521.  
  3522.     PR_LOG_BEGIN(MojaSrc, debug, ("entering java ee=0x%x cx=0x%x", ee, cx));
  3523.     /* FIXME this should be restructured: there could be a prolog and
  3524.      * epilog to set up and tear down the JS->java call stuff */
  3525.     doit(data);
  3526.  
  3527.     PR_LOG_END(MojaSrc, debug, ("left java ee=0x%x cx=0x%x", ee, cx));
  3528.  
  3529.     /* it's only safe to call this on the mozilla thread */
  3530.     success = js_JExceptionToJSError(cx, ee);
  3531.  
  3532.     /* pop the safety frame */
  3533.     if (pushSafeFrame)
  3534.     js_popSafeFrame(cx, ee, classData);
  3535.  
  3536.     return success;
  3537. }
  3538.  
  3539.  
  3540. /***********************************************************************/
  3541.  
  3542. typedef struct {
  3543.     JRIEnv *env;
  3544.     HObject *self;
  3545.     char *name;
  3546.     char *sig;
  3547.     struct methodblock *mb;
  3548.     JSBool isStaticCall;
  3549.     va_list args;
  3550.     long *raddr;
  3551.     size_t rsize;
  3552. } js_ExecuteJavaMethod_data;
  3553.  
  3554. static void
  3555. js_ExecuteJavaMethod_stub(void *d)
  3556. {
  3557.     js_ExecuteJavaMethod_data *data = (js_ExecuteJavaMethod_data *) d;
  3558.  
  3559.     data->raddr[0] =
  3560.     do_execute_java_method_vararg(/*FIXME*/(ExecEnv*)data->env,
  3561.                       data->self,
  3562.                       data->name, data->sig,
  3563.                       data->mb, (bool_t) data->isStaticCall,
  3564.                       data->args,
  3565.                       (data->rsize > sizeof(long))
  3566.                       ? &data->raddr[1] : NULL,
  3567.                       FALSE);
  3568. }
  3569.  
  3570. static JSBool
  3571. js_ExecuteJavaMethod(JSContext *cx, void *raddr, size_t rsize,
  3572.             HObject *ho, char *name, char *sig,
  3573.             struct methodblock *mb, JSBool isStaticCall, ...)
  3574. {
  3575.     js_ExecuteJavaMethod_data data;
  3576.     JSBool success;
  3577.     va_list args;
  3578.  
  3579. #if defined(XP_MAC) && !defined(NSPR20)
  3580.  /* Metrowerks va_start() doesn't handle one-byte parameters properly. FIX this when va_start() works again. */
  3581.     args = &isStaticCall+1;
  3582. #else
  3583.     va_start(args, isStaticCall);
  3584. #endif
  3585.  
  3586.     data.self = ho;
  3587.     data.name = name;
  3588.     data.sig = sig;
  3589.     data.mb = mb;
  3590.     data.isStaticCall = isStaticCall;
  3591.     VARARGS_ASSIGN(data.args, args);
  3592.     data.raddr = raddr;
  3593.     data.rsize = rsize;
  3594.  
  3595.     success = js_CallJava(cx, js_ExecuteJavaMethod_stub, &data, JS_TRUE);
  3596.     va_end(args);
  3597.  
  3598.     return success;
  3599. }
  3600.  
  3601. /***********************************************************************/
  3602.  
  3603. typedef struct {
  3604.     JRIEnv *env;
  3605.     JSContext *cx;
  3606.     char *name;
  3607.     ClassClass *fromclass;
  3608.     ClassClass *ret;
  3609.     char *errstr;
  3610. } js_FindJavaClass_data;
  3611.  
  3612. static void
  3613. js_FindJavaClass_stub(void *d)
  3614. {
  3615.     js_FindJavaClass_data *data = d;
  3616.     ExecEnv *ee = /*FIXME*/(ExecEnv*)data->env;
  3617.  
  3618.     exceptionClear(ee);
  3619.  
  3620.     /* FIXME need to push a stack frame with the classloader
  3621.      * of data->fromclass or the security check on opening
  3622.      * the url for the class will fail */
  3623.  
  3624.     data->ret = FindClassFromClass(ee, data->name, TRUE, data->fromclass);
  3625.  
  3626.     /* we clear the exception state, because when JS
  3627.      * fails to find a class it assumes it's a package instead
  3628.      * of an error. */
  3629.     /* FIXME can we report an error if the problem is accessing
  3630.      * bogus-codebase? */
  3631.  
  3632.     if (exceptionOccurred(ee)) {
  3633.     ClassClass *cb = obj_array_classblock(ee->exception.exc);
  3634.  
  3635. #ifdef DEBUG
  3636.     char *message;
  3637.         /* FIXME this could fail: we don't check if it's Throwable,
  3638.      * but i assume that FindClass is well-behaved */
  3639.     HString *hdetail =
  3640.       unhand((Hjava_lang_Throwable *)
  3641.          ee->exception.exc)->detailMessage;
  3642.  
  3643.     message = (char *)
  3644.         JRI_GetStringPlatformChars((JRIEnv *) ee,
  3645.                        (struct java_lang_String *) hdetail,
  3646.                        (const jbyte *) data->cx->charSetName,
  3647.                        (jint) data->cx->charSetNameLength);
  3648.  
  3649.     PR_LOG(MojaSrc, debug,
  3650.                ("exception in is_subclass_of %s (\"%s\")",
  3651.         cbName(cb), message));
  3652. #endif
  3653.  
  3654. #ifdef DEBUG_JSJ
  3655.     /* take a look at the exception to see if we can narrow
  3656.      * down the kinds of failures that cause a package to
  3657.      * be created? */
  3658.     exceptionDescribe(ee);
  3659. #endif
  3660.         /* FIXME other exceptions don't matter? narrow this down... */
  3661.     exceptionClear(ee);
  3662.     }
  3663. }
  3664.  
  3665. /*
  3666.  * This can call arbitrary java code in class initialization.
  3667.  * pass 0 for the "from" argument for system classes, but if
  3668.  * you want to check the applet first pass its class.
  3669.  */
  3670. static ClassClass *
  3671. js_FindJavaClass(JSContext *cx, char *name, ClassClass *from)
  3672. {
  3673.     js_FindJavaClass_data data;
  3674.  
  3675.     data.cx = cx;
  3676.     data.name = name;
  3677.     data.fromclass = from;
  3678.     data.errstr = 0;
  3679.     if (!js_CallJava(cx, js_FindJavaClass_stub, &data, JS_TRUE))
  3680.     return 0;
  3681.     if (data.errstr) {
  3682.         JS_ReportError(cx, "%s", data. errstr);
  3683.     free(data.errstr);
  3684.     /* FIXME need to propagate error condition differently */
  3685.     return 0;
  3686.     }
  3687.     return data.ret;
  3688. }
  3689.  
  3690. /***********************************************************************/
  3691.  
  3692.  
  3693. typedef struct {
  3694.     JRIEnv *env;
  3695.     JSBool privileged;
  3696.     char *name;
  3697.     ClassClass *cb;
  3698.     char *sig;
  3699.     va_list args;
  3700.     HObject *ret;
  3701. } js_ConstructJava_data;
  3702.  
  3703. static void
  3704. js_ConstructJava_stub(void *d)
  3705. {
  3706.     js_ConstructJava_data *data = d;
  3707.     ExecEnv *ee = /*FIXME*/(ExecEnv*)data->env;
  3708.  
  3709.     if (data->privileged) {
  3710.     /* FIXME extremely lame - there should be a security
  3711.      * flag to execute_java_constructor_vararg instead.
  3712.      * the effect of this is that the JSObject constructor may
  3713.          * get called on the wrong thread, but this probably won't
  3714.      * do any damage.  JRI will fix this, right? */
  3715.     ee = PRIVILEGED_EE;
  3716.     }
  3717.     data->ret =
  3718.       execute_java_constructor_vararg(ee,
  3719.                       data->name, data->cb,
  3720.                       data->sig, data->args);
  3721. }
  3722.  
  3723. /* this can call arbitrary java code in class initialization */
  3724. static HObject *
  3725. js_ConstructJava(JSContext *cx, char *name, ClassClass *cb,
  3726.             char *sig, ...)
  3727. {
  3728.     js_ConstructJava_data data;
  3729.     va_list args;
  3730.     JSBool success;
  3731.  
  3732.     va_start(args, sig);
  3733.     data.name = name;
  3734.     data.privileged = JS_FALSE;
  3735.     data.cb = cb;
  3736.     data.sig = sig;
  3737.     VARARGS_ASSIGN(data.args, args);
  3738.     data.env = JRI_GetCurrentEnv();
  3739.  
  3740.     success = js_CallJava(cx, js_ConstructJava_stub, &data, JS_TRUE);
  3741.     va_end(args);
  3742.  
  3743.     if (success) return data.ret;
  3744.     else return 0;
  3745. }
  3746.  
  3747. /* for private constructors, i.e. JSObject */
  3748. static HObject *
  3749. js_ConstructJavaPrivileged(JSContext *cx, char *name, ClassClass *cb,
  3750.                   char *sig, ...)
  3751. {
  3752.     js_ConstructJava_data data;
  3753.     va_list args;
  3754.     JSBool success;
  3755.  
  3756.     va_start(args, sig);
  3757.     data.privileged = JS_TRUE;
  3758.     data.name = name;
  3759.     data.cb = cb;
  3760.     data.sig = sig;
  3761.     VARARGS_ASSIGN(data.args, args);
  3762.     data.env = JRI_GetCurrentEnv();
  3763.  
  3764.     success = js_CallJava(cx, js_ConstructJava_stub, &data, JS_TRUE);
  3765.     va_end(args);
  3766.  
  3767.     if (success) return data.ret;
  3768.     else return 0;
  3769. }
  3770.  
  3771.  
  3772. /***********************************************************************/
  3773.  
  3774. static JSBool jsj_enabled = JS_TRUE;
  3775.  
  3776. /*
  3777.  * most of the initialization is done lazily by this function,
  3778.  * which is only called through jsj_GetCurrentEE().  it is
  3779.  * only called once - this is enforced in jsj_GetCurrentEE().
  3780.  */
  3781. static void
  3782. jsj_FinishInit(JSContext *cx, JRIEnv *env)
  3783. {
  3784.     char *name;
  3785.     ExecEnv *ee;
  3786.  
  3787.     PR_LOG(MojaSrc, debug, ("jsj_FinishInit()\n"));
  3788.  
  3789.     /* initialize the reflection tables */
  3790.     javaReflections =
  3791.       PR_NewHashTable(256, (PRHashFunction) java_hashHandle,
  3792.               (PRHashComparator) java_pointerEq,
  3793.               (PRHashComparator) java_pointerEq, 0, 0);
  3794. #ifdef NSPR20
  3795.     PR_RegisterGCLockHook((GCLockHookFunc*) PrepareJSLocksForGC, 0);
  3796. #endif
  3797.     PR_RegisterRootFinder(scanJSJavaReflections,
  3798.                           "scan JS reflections of java objects",
  3799.               JS_GetRuntime(cx));
  3800.     if (javaReflectionsMonitor == NULL)
  3801.     javaReflectionsMonitor = PR_NewNamedMonitor("javaReflections");
  3802.     jsReflections =
  3803.       PR_NewHashTable(256, (PRHashFunction) java_hashHandle,
  3804.               (PRHashComparator) java_pointerEq,
  3805.               (PRHashComparator) java_pointerEq, 0, 0);
  3806.     if (jsReflectionsMonitor == NULL)
  3807.     jsReflectionsMonitor = PR_NewNamedMonitor("jsReflections");
  3808.  
  3809.     ee = (ExecEnv *) env;
  3810.  
  3811.     exceptionClear(ee);
  3812.  
  3813.     name = "java/lang/Object";
  3814.     if (! (ObjectClassBlock = FindClassFromClass(ee, name, TRUE, 0)))
  3815.     goto badclass;
  3816.     MakeClassSticky(ObjectClassBlock);
  3817.  
  3818.     name = "netscape/javascript/JSObject";
  3819.     if (! (JSObjectClassBlock = FindClassFromClass(ee, name, TRUE, 0)))
  3820.     goto badclass;
  3821.     MakeClassSticky(JSObjectClassBlock);
  3822.     JSObjectInternalField = JRI_GetFieldID(env,
  3823.                        (struct java_lang_Class*)cbHandle(JSObjectClassBlock),
  3824.                                            "internal", "I");
  3825.  
  3826.     name = "netscape/javascript/JSException";
  3827.     if (! (JSExceptionClassBlock = FindClassFromClass(ee, name, TRUE, 0)))
  3828.     goto badclass;
  3829.     MakeClassSticky(JSExceptionClassBlock);
  3830.  
  3831.     name = "java/lang/String";
  3832.     if (! (StringClassBlock = FindClassFromClass(ee, name, TRUE, 0)))
  3833.     goto badclass;
  3834.     MakeClassSticky(StringClassBlock);
  3835.  
  3836.     name = "java/lang/Boolean";
  3837.     if (! (BooleanClassBlock = FindClassFromClass(ee, name, TRUE, 0)))
  3838.     goto badclass;
  3839.     MakeClassSticky(BooleanClassBlock);
  3840.  
  3841.     name = "java/lang/Double";
  3842.     if (! (DoubleClassBlock = FindClassFromClass(ee, name, TRUE, 0)))
  3843.     goto badclass;
  3844.     MakeClassSticky(DoubleClassBlock);
  3845.  
  3846.     name = "java/lang/Throwable";
  3847.     if (! (ThrowableClassBlock = FindClassFromClass(ee, name, TRUE, 0)))
  3848.     goto badclass;
  3849.     MakeClassSticky(ThrowableClassBlock);
  3850.  
  3851.     return;
  3852.  
  3853.   badclass:
  3854.     PR_LOG(MojaSrc, error, ("couldn't find class \"%s\"\n", name));
  3855.     JS_ReportError(cx, "Unable to initialize LiveConnect: missing \"%s\"",
  3856.            name);
  3857.     jsj_enabled = JS_FALSE;
  3858.     return;
  3859. }
  3860.  
  3861. PR_STATIC_CALLBACK(int)
  3862. jsj_TrashJSReflectionEntry(PRHashEntry *he, int i, void *arg)
  3863. {
  3864.     JRIEnv *env = arg;
  3865.     jref jso = (jref) he->value;
  3866.  
  3867.     JRI_SetFieldInt(env, jso, JSObjectInternalField, (jint) 0);
  3868.     /* FIXME inline JS_RemoveRoot -- this will go away with GC unification */
  3869.     JS_LOCK_RUNTIME(finalizeRuntime);
  3870.     (void) PR_HashTableRemove(finalizeRuntime->gcRootsHash, (void *)&he->key);
  3871.     JS_UNLOCK_RUNTIME(finalizeRuntime);
  3872.  
  3873.     return HT_ENUMERATE_REMOVE;
  3874. }
  3875.  
  3876. PR_EXTERN(void)
  3877. JSJ_Finish(void)
  3878. {
  3879.     JRIEnv *env = 0;
  3880.  
  3881.     /* PR_ASSERT(we_have_libmocha_lock) */
  3882.  
  3883.     jsj_enabled = JS_FALSE;
  3884.  
  3885.     if (!javaReflectionsMonitor)
  3886.     return;
  3887.     PR_EnterMonitor(javaReflectionsMonitor);
  3888.     if (javaReflections) {
  3889.     PR_HashTableDestroy(javaReflections);
  3890.     javaReflections = NULL;
  3891.     }
  3892.     PR_ExitMonitor(javaReflectionsMonitor);
  3893.  
  3894.     env = JRI_GetCurrentEnv();
  3895.  
  3896.     if (!env) {
  3897. #ifdef DEBUG_JSJ
  3898.         PR_ASSERT(env);
  3899. #else
  3900.         return;
  3901. #endif
  3902.     }
  3903.  
  3904.     /* FIXME assert that lm_lock is held */
  3905.     PR_EnterMonitor(jsReflectionsMonitor);
  3906.     if (jsReflections) {
  3907.         PR_HashTableEnumerateEntries(jsReflections,
  3908.                                      jsj_TrashJSReflectionEntry,
  3909.                                      (void*) env);
  3910.     PR_HashTableDestroy(jsReflections);
  3911.     jsReflections = NULL;
  3912.     }
  3913.     PR_ExitMonitor(jsReflectionsMonitor);
  3914. }
  3915.  
  3916. /*
  3917.  * Get the java class associated with an instance, useful for access
  3918.  * to static fields and methods of applets.
  3919.  */
  3920. static JSBool
  3921. js_getJavaClass(JSContext *cx, JSObject *obj, PRUintn argc, jsval *argv,
  3922.         jsval *rval)
  3923. {
  3924.     JSObject *mo;
  3925.     JSObject *moclass;
  3926.     JSJava *java;
  3927.  
  3928.     /* FIXME this could accept strings as well i suppose */
  3929.     if (argc != 1 ||
  3930.     !JSVAL_IS_OBJECT(argv[0]) ||
  3931.     !(mo = JSVAL_TO_OBJECT(argv[0])) ||
  3932.     !JS_InstanceOf(cx, mo, &java_class, 0) ||
  3933.     (java = (JSJava *) JS_GetPrivate(cx, mo))->type != JAVA_OBJECT) {
  3934.         JS_ReportError(cx, "getClass expects a Java object argument");
  3935.     return JS_FALSE;
  3936.     }
  3937.  
  3938.     if (!(moclass = js_ReflectJClassToJSObject(cx, java->cb))) {
  3939.         JS_ReportError(cx, "getClass can't find Java class reflection");
  3940.     return JS_FALSE;
  3941.     }
  3942.  
  3943.     *rval = OBJECT_TO_JSVAL(moclass);
  3944.     return JS_TRUE;
  3945. }
  3946.  
  3947. static JSObject *
  3948. js_DefineJavaPackage(JSContext *cx, JSObject *obj, char *jsname, char *package)
  3949. {
  3950.     JSJavaPackage *pack;
  3951.     JSObject *pobj;
  3952.  
  3953.     pack = JS_malloc(cx, sizeof(JSJavaPackage));
  3954.     if (!pack)
  3955.     return 0;
  3956.  
  3957.     if (package) {
  3958.     pack->name = JS_strdup(cx, package);
  3959.     if (!pack->name) {
  3960.         JS_free(cx, pack);
  3961.         return 0;
  3962.     }
  3963.     } else {
  3964.     pack->name = 0;
  3965.     }
  3966.  
  3967.     /* FIXME can we make the package read-only? */
  3968.     pobj = JS_DefineObject(cx, obj, jsname, &javapackage_class, 0, 0);
  3969.     if (pobj && !JS_SetPrivate(cx, pobj, pack))
  3970.     (void) JS_DeleteProperty(cx, obj, jsname);
  3971.     return pobj;
  3972. }
  3973.  
  3974. /* hook from js_DestroyContext */
  3975. static void
  3976. jsj_DestroyJSContextHook(JSContext *cx)
  3977. {
  3978.     /* FIXME do anything with the env? */
  3979.     /* FIXMEold cx->javaEnv = 0; */
  3980. }
  3981.  
  3982. /* FIXME make sure this is called from jsscript.c:js_DestroyScript !*/
  3983. static void
  3984. jsj_DestroyScriptHook(JSContext *cx, JSScript *script)
  3985. {
  3986.     JSJClassData *data = script->javaData;
  3987.  
  3988.     if (!data)
  3989.     return;
  3990.  
  3991.     jsj_DestroyClassData(cx, data);
  3992.  
  3993.     script->javaData = 0;
  3994. }
  3995.  
  3996. static void
  3997. jsj_DestroyFrameHook(JSContext *cx, JSStackFrame *frame) {
  3998.     if (frame->annotation) {
  3999.         JRIEnv *env = (JRIEnv *) jsj_GetCurrentEE(cx);
  4000.         if (!env) return;
  4001.  
  4002.         JRI_DisposeGlobalRef(env, frame->annotation);
  4003.     }
  4004. }
  4005.  
  4006. /* we need to hook into the js interpreter in a few special places */
  4007. JSInterpreterHooks js_Hooks = {
  4008.     jsj_DestroyJSContextHook,
  4009.     jsj_DestroyScriptHook,
  4010.     jsj_DestroyFrameHook
  4011. };
  4012.  
  4013. extern void _java_javascript_init(void);
  4014.  
  4015. /*
  4016.  * Initialize JS<->Java glue
  4017.  */
  4018. PR_IMPLEMENT(JSBool)
  4019. JSJ_Init(JSJCallbacks *callbacks)
  4020. {
  4021.     static JSBool initialized = JS_FALSE;
  4022.  
  4023.     /* JSJ_Init may be called twice: it is called with default
  4024.      * hooks when the netscape.javascript.JSObject class is
  4025.      * initialized, but it also may be called by the client
  4026.      * or server.  in this case, the first to call it gets
  4027.      * to set the hooks */
  4028.     if (initialized)
  4029.         return JS_FALSE;
  4030.     initialized = JS_TRUE;
  4031.  
  4032.     _java_javascript_init();    /* stupid linker tricks */
  4033.  
  4034.     js_SetInterpreterHooks(&js_Hooks);
  4035.  
  4036.     if (!callbacks)
  4037.     return JS_TRUE;
  4038.  
  4039. #ifdef NSPR20
  4040.     if (MojaSrc == NULL)
  4041.         MojaSrc = PR_NewLogModule("MojaSrc");
  4042. #endif
  4043.  
  4044.     jsj_callbacks = callbacks;
  4045.  
  4046.     return JS_TRUE;
  4047. }
  4048.  
  4049. /*
  4050.  * Initialize a javascript context and toplevel object for use with JSJ
  4051.  */
  4052. PR_IMPLEMENT(JSBool)
  4053. JSJ_InitContext(JSContext *cx, JSObject *obj)
  4054. {
  4055.     JSObject *mo;
  4056.  
  4057.     /* grab the main runtime from the context if we haven't yet */
  4058.     if (!finalizeRuntime)
  4059.         finalizeRuntime = JS_GetRuntime(cx);
  4060.  
  4061.     /* define the top of the java package namespace as "Packages" */
  4062.     mo = js_DefineJavaPackage(cx, obj, "Packages", 0);
  4063.  
  4064.     /* some convenience packages */
  4065.     /* FIXME these should be properties of the top-level package
  4066.      * too.  as it is there will be two different objects for
  4067.      * "java" and "Packages.java" which is unfortunate but mostly
  4068.      * invisible */
  4069.  
  4070.     /* FIXMEbe Use new JS_AliasProperty API call.
  4071.      * have to lookup Packages.java first... */
  4072.  
  4073.     js_DefineJavaPackage(cx, obj, "java", "java");
  4074.     js_DefineJavaPackage(cx, obj, "sun", "sun");
  4075.     js_DefineJavaPackage(cx, obj, "netscape", "netscape");
  4076.  
  4077.     JS_DefineFunction(cx, obj, "getClass", js_getJavaClass, 0, JSPROP_READONLY);
  4078.     /* create the prototype object for java objects and classes */
  4079.     mo = JS_DefineObject(cx, obj, "#javaPrototype", &java_class, 0,
  4080.                JSPROP_READONLY | JSPROP_PERMANENT);
  4081.     /* FIXME set up the private data? */
  4082.  
  4083.     /* any initialization that depends on java running is done in
  4084.      * js_FinishInitJava */
  4085.  
  4086.     return JS_TRUE;
  4087. }
  4088.  
  4089. #ifdef NEVER
  4090. /* This is really a big comment describing the JavaScript class file,
  4091.    never intended to be compiled. */
  4092. /* FIXME should this be in a package? */
  4093.  
  4094. /**
  4095.  * Internal class for secure JavaScript->Java calls.
  4096.  * We load this class with a crippled ClassLoader, and make sure
  4097.  * that one of the methods from this class is on the Java stack
  4098.  * when JavaScript calls into Java.
  4099.  * The call to System.err.println is necessary because otherwise
  4100.  * sj doesn't account for the arguments in mb->maxstack, and
  4101.  * the jsContext argument gets wiped out when the next stack
  4102.  * frame is constructed.
  4103.  */
  4104.  
  4105. package netscape.javascript;
  4106.  
  4107. class JavaScript {
  4108.     /* can't be constructed */
  4109.     private JavaScript() {};
  4110.  
  4111.     /**
  4112.      * this is the method that will be on the Java stack when
  4113.      * JavaScript calls Java.  it is never actually called,
  4114.      * but we put a reference to it on the stack as if it had
  4115.      * been.  the mochaContext argument is a native pointer to
  4116.      * the current JavaScript context so that we can figure
  4117.      * that out from Java.
  4118.      */
  4119.     static void callJava(int jsContext) {
  4120.     /* this call is here to make sure the compiler
  4121.      * allocates enough stack for us - previously we
  4122.      * were getting mb->maxstack == 0, which would
  4123.      * cause the jsContext argument to be overwritten */
  4124.     System.err.println(jsContext);
  4125.     };
  4126. }
  4127.  
  4128. #endif /* NEVER */
  4129.  
  4130. static unsigned char JavaScript_class_bytes[] = {
  4131.  "\312\376\272\276\000\003\000-\000\035\007\000\026\007\000\030\007\000"
  4132.  "\020\007\000\033\012\000\003\000\012\012\000\002\000\010\011\000\004\000"
  4133.  "\011\014\000\031\000\034\014\000\015\000\032\014\000\013\000\014\001\000"
  4134.  "\007println\001\000\004(I)V\001\000\003err\001\000\015ConstantValue\001"
  4135.  "\000\010callJava\001\000\023java/io/PrintStream\001\000\012Exceptions"
  4136.  "\001\000\017LineNumberTable\001\000\012SourceFile\001\000\016LocalVar"
  4137.  "iables\001\000\004Code\001\000\036netscape/javascript/JavaScript\001\000"
  4138.  "\017JavaScript.java\001\000\020java/lang/Object\001\000\006<init>\001"
  4139.  "\000\025Ljava/io/PrintStream;\001\000\020java/lang/System\001\000\003"
  4140.  "()V\000\000\000\001\000\002\000\000\000\000\000\002\000\002\000\031\000"
  4141.  "\034\000\001\000\025\000\000\000\035\000\001\000\001\000\000\000\005*"
  4142.  "\267\000\006\261\000\000\000\001\000\022\000\000\000\006\000\001\000\000"
  4143.  "\000\020\000\010\000\017\000\014\000\001\000\025\000\000\000$\000\002"
  4144.  "\000\001\000\000\000\010\262\000\007\032\266\000\005\261\000\000\000\001"
  4145.  "\000\022\000\000\000\012\000\002\000\000\000\037\000\007\000\032\000\001"
  4146.  "\000\023\000\000\000\002\000\027"
  4147. };
  4148.  
  4149. /* now something to make a classloader and get a JavaScript object
  4150.  * from it. */
  4151.  
  4152. /* FIXME garbage collection of these classes?  how should they be
  4153.  * protected? */
  4154.  
  4155. #include "java_lang_ClassLoader.h"
  4156.  
  4157. #define USELESS_CODEBASE_URL "http://javascript-of-unknown-origin.netscape.com/"
  4158.  
  4159. static void
  4160. jsj_DestroyClassData(JSContext *cx, JSJClassData *data)
  4161. {
  4162.     ExecEnv *ee;
  4163.     JRIEnv *env;
  4164.     HObject *loader;
  4165.  
  4166.     /* FIXME locking! */
  4167.     if (--data->nrefs > 0)
  4168.     return;
  4169.  
  4170.     ee = jsj_GetCurrentEE(cx);
  4171.     if (!ee) {
  4172.         /* FIXME memory leak - if we can't find a java execution
  4173.          * env we can't free the classloader and class */
  4174.         PR_ASSERT(!"can't find java env to free JSScript.javaData");
  4175.     return;
  4176.     }
  4177.  
  4178.     env = (JRIEnv *) ee;
  4179.  
  4180.     if (data->clazz) {
  4181.     JRI_DisposeGlobalRef(env, data->clazz);
  4182.     data->clazz = NULL;
  4183.     }
  4184.  
  4185.     if (data->loader) {
  4186.     loader = JRI_GetGlobalRef(env, data->loader);
  4187.     /* comment out call to releaseClassLoader because it is obsolete.
  4188.     execute_java_dynamic_method(ee, loader,
  4189.                                     "releaseClassLoader", "()V");
  4190.     if (exceptionOccurred(ee)) {
  4191.     */
  4192.                 /* FIXME memory leak - if we can't find a java execution
  4193.                  * env we can't free the classloader and class */
  4194.     /*
  4195.             PR_ASSERT(!"failed releasing javascript classloader");
  4196.         goto done;
  4197.     }
  4198.     */
  4199.     JRI_DisposeGlobalRef(env, data->loader);
  4200.     data->loader = NULL;
  4201.     }
  4202.  
  4203.     data->mb = NULL;
  4204.  
  4205.   done:
  4206.     free(data);
  4207. }
  4208.  
  4209. /* security: this method returns a methodblock which can be
  4210.  * used for secure JS->java calls.  the methodblock is
  4211.  * guaranteed to have an associated AppletClassLoader which will
  4212.  * allow the SecurityManager to determine what permissions to
  4213.  * give to the Java code being called.
  4214.  */
  4215. static JSJClassData *
  4216. jsj_MakeClassData(JSContext *cx)
  4217. {
  4218.     int i;
  4219.     ExecEnv *ee;
  4220.     JRIEnv *env;
  4221.     JSScript *script;
  4222.     JSStackFrame *fp;
  4223.     PRInt32 length;
  4224.     HArrayOfByte *bytes;
  4225.     const char *origin_url;
  4226.     struct Hjava_lang_ClassLoader *loader;
  4227.     ClassClass *clazz;
  4228.     struct methodblock *mb;
  4229.     JSJClassData *data;
  4230.  
  4231.     ee = jsj_GetCurrentEE(cx);
  4232.     if (!ee) return 0;
  4233.  
  4234.     /* see if this script already has a classloader */
  4235.     script = NULL;
  4236.     for (fp = cx->fp; fp; fp = fp->down) {
  4237.     script = fp->script;
  4238.     if (script)
  4239.         break;
  4240.     }
  4241.     if (script &&
  4242.     (data = (JSJClassData *) script->javaData)) {
  4243.     /* FIXME locking! */
  4244.     data->nrefs++;
  4245.     return data;
  4246.     }
  4247.  
  4248.     data = (JSJClassData *) malloc(sizeof(JSJClassData));
  4249.     if (!data) {
  4250.     JS_ReportOutOfMemory(cx);
  4251.     return 0;
  4252.     }
  4253.     data->nrefs = 1;
  4254.     data->loader = NULL;
  4255.     data->clazz = NULL;
  4256.     data->mb = NULL;
  4257.  
  4258.     if (script && script->filename
  4259.         && strcmp("[unknown origin]", script->filename)) {
  4260.     origin_url = script->filename;
  4261.     } else {
  4262.     origin_url = USELESS_CODEBASE_URL;
  4263.     }
  4264.  
  4265.     loader = jsj_callbacks->jsClassLoader(cx, origin_url);
  4266.  
  4267.     if (exceptionOccurred(ee) || !loader) {
  4268.         JS_ReportError(cx, "couldn't create ClassLoader for JavaScript");
  4269.     free(data);
  4270.     return 0;
  4271.     }
  4272.  
  4273.     env = (JRIEnv *) ee;
  4274.     data->loader = JRI_NewGlobalRef(env, loader);
  4275.  
  4276.     /* FIXME should save and re-use this array in a jglobal */
  4277.     /* make the array of bytes for class JavaScript */
  4278.     length = sizeof(JavaScript_class_bytes);
  4279.     bytes = (HArrayOfByte *) ArrayAlloc(T_BYTE, length);
  4280.     if (!bytes) {
  4281.     jsj_DestroyClassData(cx, data);
  4282.     JS_ReportError(cx,
  4283.                        "couldn't allocate bytes for JavaScript.class");
  4284.     return 0;
  4285.     }
  4286.     memmove(unhand(bytes)->body, JavaScript_class_bytes, (size_t)length);
  4287.  
  4288.     /* FIXME lock to avoid race condition between this and defineClass */
  4289.     clazz = FindLoadedClass("netscape/javascript/JavaScript", loader);
  4290.  
  4291.     if (!clazz) {
  4292.     /* make class JavaScript */
  4293.     clazz = (ClassClass*)
  4294.         do_execute_java_method(ee, loader,
  4295.                                    "defineClass", "(Ljava/lang/String;[BII)Ljava/lang/Class;",
  4296.                    0, FALSE, NULL /* no required name */, bytes, 0L, length);
  4297.  
  4298.     if (exceptionOccurred(ee) || !clazz) {
  4299.         jsj_DestroyClassData(cx, data);
  4300.             JS_ReportError(cx, "couldn't load class JavaScript");
  4301.         return 0;
  4302.     }
  4303.  
  4304.         (void)
  4305.             do_execute_java_method(ee, loader,
  4306.                                    "setPrincipalAry",
  4307.                                    "(Ljava/lang/Class;Ljava/lang/String;)Z",
  4308.                                    0, FALSE,
  4309.                                    clazz, NULL /* don't lookup */);
  4310.  
  4311.     if (exceptionOccurred(ee) || !clazz) {
  4312.         jsj_DestroyClassData(cx, data);
  4313.             JS_ReportError(cx, "couldn't set principal for class JavaScript");
  4314.         return 0;
  4315.     }
  4316.     }
  4317.  
  4318.     data->clazz = JRI_NewGlobalRef(env, cbHandle(clazz));
  4319.  
  4320.     /* FIXME set up the signatures on the class here */
  4321.  
  4322.     /* find the static method callJava() for the class */
  4323.     for (i = 0; i < (int)cbMethodsCount(clazz); i++) {
  4324.     mb = cbMethods(clazz) + i;
  4325.         if (!strcmp(fieldname(&mb->fb), "callJava")
  4326.             && !strcmp(fieldsig(&mb->fb), "(I)V")) {
  4327.         /* found it... */
  4328.         data->mb = mb;
  4329.         if (script) {
  4330.         /* FIXME locking! */
  4331.         data->nrefs++;
  4332.         script->javaData = data;
  4333.         }
  4334.         return data;
  4335.     }
  4336.     }
  4337.  
  4338.     jsj_DestroyClassData(cx, data);
  4339.     JS_ReportError(cx, "can't find method JavaScript.callJava");
  4340.  
  4341.     return 0;
  4342. }
  4343.  
  4344. /*
  4345.  * tell whether a partiular methodblock is part of a safety frame
  4346.  *
  4347.  * the security here depends on the fact that users are forbidden
  4348.  * to define packages in netscape.javascript.  the consequence
  4349.  * of breaking this security would be that the user could get
  4350.  * a java int dereferenced as a pointer, most likely crashing
  4351.  * the program.
  4352.  *
  4353.  * embedding some sort of secret into the JavaScript class
  4354.  *  (pointer to itself?) would also secure this.
  4355.  */
  4356. PR_IMPLEMENT(JSBool)
  4357. JSJ_IsSafeMethod(struct methodblock *mb)
  4358. {
  4359.     ClassClass *cb;
  4360.  
  4361.     if (!mb)
  4362.     return JS_FALSE;
  4363.  
  4364.     cb = fieldclass(&mb->fb);
  4365.  
  4366.     if (!cb ||
  4367.         strcmp(cbName(cb), "netscape/javascript/JavaScript"))
  4368.     return JS_FALSE;
  4369.  
  4370.     return JS_TRUE;
  4371. }
  4372.  
  4373. /*
  4374.  * push a frame onto the java stack which does nothing except
  4375.  * provide a classloader for the security manager
  4376.  */
  4377. static JSBool
  4378. js_pushSafeFrame(JSContext *cx, ExecEnv *ee, JSJClassData **classData)
  4379. {
  4380.     JavaFrame *current_frame, *previous_frame;
  4381.     JavaStack *current_stack;
  4382.     struct methodblock *mb;
  4383.  
  4384.     if (!jsj_callbacks->jsClassLoader) {
  4385.         *classData = NULL;
  4386.         return JS_TRUE;
  4387.     }
  4388.  
  4389.     if (!(*classData = jsj_MakeClassData(cx)))
  4390.     return JS_FALSE;
  4391.     mb = (*classData)->mb;
  4392.  
  4393.     previous_frame = ee->current_frame;
  4394.     if (previous_frame == 0) {
  4395.     /* bottommost frame on this Exec Env. */
  4396.     current_stack = ee->initial_stack;
  4397.     current_frame = (JavaFrame *)(current_stack->data); /* no vars */
  4398.     } else {
  4399.     int args_size = mb->args_size;
  4400.     current_stack = previous_frame->javastack; /* assume same stack */
  4401.     if (previous_frame->current_method) {
  4402.         int size = previous_frame->current_method->maxstack;
  4403.         current_frame = (JavaFrame *)(&previous_frame->ostack[size]);
  4404.     } else {
  4405.             /* The only frames that don't have a mb are pseudo frames like
  4406.              * this one and they don't really touch their stack. */
  4407.         current_frame = (JavaFrame *)(previous_frame->optop + 3);
  4408.     }
  4409.     if (current_frame->ostack + args_size > current_stack->end_data) {
  4410.             /* Ooops.  The current stack isn't big enough.  */
  4411.         if (current_stack->next != 0) {
  4412.         current_stack = current_stack->next;
  4413.         } else {
  4414.         current_stack = CreateNewJavaStack(ee, current_stack);
  4415.         if (current_stack == 0) {
  4416.             JS_ReportOutOfMemory(cx);
  4417.             return JS_FALSE;
  4418.         }
  4419.         }
  4420.         /* no vars */
  4421.         current_frame = (JavaFrame *)(current_stack->data);
  4422.     }
  4423.     }
  4424.     current_frame->prev = previous_frame;
  4425.     current_frame->javastack = current_stack;
  4426.     current_frame->optop = current_frame->ostack;
  4427.     current_frame->vars = 0;    /* better not reference any! */
  4428.     current_frame->monitor = 0; /* not monitoring anything */
  4429.     current_frame->annotation = 0;
  4430.  
  4431.     /* make this be a method with the JS classloader */
  4432.     current_frame->current_method = mb;
  4433.  
  4434.     /* FIXME push the current mochaContext as an integer */
  4435.     current_frame->optop[0].i = (int32_t) cx;
  4436.     current_frame->optop += current_frame->current_method->args_size;
  4437.     current_frame->constant_pool = 0;
  4438.  
  4439.     ee->current_frame = current_frame;
  4440.  
  4441.     return JS_TRUE;
  4442. }
  4443.  
  4444. /*
  4445.  * pop the safety frame from the java stack
  4446.  */
  4447. static void
  4448. js_popSafeFrame(JSContext *cx, ExecEnv *ee, JSJClassData *classData)
  4449. {
  4450.     if (!classData)
  4451.         return;
  4452.  
  4453.     ee->current_frame = ee->current_frame->prev;
  4454.     jsj_DestroyClassData(cx, classData);
  4455. }
  4456.  
  4457. /*
  4458.  * look up the stack for the most recent safety frame and extract
  4459.  * the JSContext from it.  return NULL if no such frame was found.
  4460.  */
  4461. PR_IMPLEMENT(void)
  4462. JSJ_FindCurrentJSContext(JRIEnv *env, JSContext **cxp, void **loaderp)
  4463. {
  4464.     ExecEnv *ee = (ExecEnv *) env;
  4465.     JavaFrame *frame, frame_buf;
  4466.     ClassClass    *cb;
  4467.  
  4468. #define NEXT_FRAME(frame) \
  4469.     (((frame)->current_method && (frame)->current_method->fb.access & ACC_MACHINE_COMPILED) ? \
  4470.      CompiledFramePrev(frame, &frame_buf) \
  4471.      : frame->prev)
  4472.  
  4473.     *cxp = 0;
  4474.     *loaderp = 0;
  4475.  
  4476.     /* search the stack upward */
  4477.     for (frame = ee->current_frame; frame; frame = NEXT_FRAME(frame)) {
  4478.     struct methodblock *mb = frame->current_method;
  4479.     if (mb) {
  4480.         cb = fieldclass(&frame->current_method->fb);
  4481.         *loaderp = cbLoader(cb);
  4482.         if (*loaderp) {
  4483.         if (JSJ_IsSafeMethod(mb)) {
  4484.             /* extract the mochacontext here */
  4485.             *cxp = (JSContext *)frame->ostack[0].i;
  4486.         } else
  4487.             *cxp = 0;
  4488.         return;
  4489.         }
  4490.     }
  4491.     }
  4492.     return;
  4493. }
  4494.  
  4495. PR_IMPLEMENT(JSBool)
  4496. JSJ_IsCalledFromJava(JSContext *cx)
  4497. {
  4498.     ExecEnv *ee;
  4499.  
  4500.     ee = (ExecEnv *) JRI_GetCurrentEnv();
  4501.     if (ee == NULL)
  4502.         return JS_FALSE;
  4503.  
  4504.     return ee->current_frame != NULL;
  4505. }
  4506.  
  4507. /* extract the javascript object from a netscape.javascript.JSObject */
  4508. PR_IMPLEMENT(JSObject *)
  4509. JSJ_ExtractInternalJSObject(JRIEnv *env, HObject* jso)
  4510. {
  4511.     PR_ASSERT(obj_array_classblock((HObject*)jso) == JSObjectClassBlock);
  4512.     
  4513.     return (JSObject *) JRI_GetFieldInt(env, (netscape_javascript_JSObject*)jso,
  4514.                                         JSObjectInternalField);
  4515. }
  4516.  
  4517. PR_IMPLEMENT(JSPrincipals *)
  4518. js_GetJSPrincipalsFromJavaCaller(JSContext *cx, int callerDepth)
  4519. {
  4520.     return jsj_callbacks->getJSPrincipalsFromJavaCaller(cx, callerDepth);
  4521. }
  4522.  
  4523.  
  4524. #endif /* defined(JAVA) */
  4525.