mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-01 14:45:29 +00:00
4549 lines
118 KiB
C
4549 lines
118 KiB
C
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
|
*
|
|
* The contents of this file are subject to the Netscape Public License
|
|
* Version 1.0 (the "NPL"); you may not use this file except in
|
|
* compliance with the NPL. You may obtain a copy of the NPL at
|
|
* http://www.mozilla.org/NPL/
|
|
*
|
|
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
|
* for the specific language governing rights and limitations under the
|
|
* NPL.
|
|
*
|
|
* The Initial Developer of this code under the NPL is Netscape
|
|
* Communications Corporation. Portions created by Netscape are
|
|
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
|
* Reserved.
|
|
*/
|
|
/* ** */
|
|
/*
|
|
* JS reflection of Java objects and vice versa
|
|
*
|
|
* javapackage is a java namespace
|
|
* java is a java class or object
|
|
* javaarray is a java array
|
|
* javaslot represents an object+name that may resolve to
|
|
* either a field or a method depending on later usage.
|
|
*
|
|
* netscape.javascript.JSObject is a java reflection of a JS object
|
|
*/
|
|
|
|
#ifdef JAVA
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
/* java joy */
|
|
#include "oobj.h"
|
|
#include "interpreter.h"
|
|
#include "tree.h"
|
|
#include "opcodes.h"
|
|
#include "javaString.h"
|
|
#include "exceptions.h"
|
|
#include "jri.h"
|
|
|
|
/* nspr stuph */
|
|
#ifndef NSPR20
|
|
#include "prhash.h"
|
|
#else
|
|
#include "plhash.h"
|
|
#endif
|
|
#include "prmon.h" /* for PR_XLock and PR_XUNlock */
|
|
#include "prlog.h"
|
|
#include "prprf.h"
|
|
#include "prgc.h"
|
|
|
|
#include "jsapi.h"
|
|
#include "jsjava.h"
|
|
#include "jscntxt.h" /* for cx->savedErrors */
|
|
#include "jsscript.h" /* for script->javaData */
|
|
#include "jsobj.h" /* for OBJ_GET_SLOT and JSSLOT_PRIVATE */
|
|
#include "jsfun.h" /* for JSFUN_BOUND_METHOD */
|
|
#include "jslock.h" /* for JS_LOCK_RUNTIME/JS_UNLOCK_RUNTIME */
|
|
|
|
#ifdef MKLINUX
|
|
#define VARARGS_ASSIGN(foo, bar) foo[0] = bar[0]
|
|
#else
|
|
#define VARARGS_ASSIGN(foo, bar) (foo) = (bar)
|
|
#endif /*MKLINUX*/
|
|
|
|
#ifndef NSPR20
|
|
#define WAIT_FOREVER LL_MAXINT
|
|
#else
|
|
#define WAIT_FOREVER PR_INTERVAL_NO_TIMEOUT
|
|
/* PR_LOG hacks */
|
|
#define debug PR_LOG_MAX
|
|
#define error PR_LOG_ERROR
|
|
#define warn PR_LOG_WARNING
|
|
#endif
|
|
|
|
/*
|
|
* "exception" is defined by some Windows OS headers.
|
|
* Redefine it for our own purposes.
|
|
*/
|
|
#ifdef exception
|
|
#undef exception
|
|
#endif
|
|
|
|
/*
|
|
* we don't have access to the classJavaLangObject from the java
|
|
* runtime dll, so we need a different version of this macro
|
|
*/
|
|
static ClassClass *js_classJavaLangObject = 0;
|
|
#undef obj_array_classblock
|
|
#define obj_array_classblock(obj) \
|
|
((obj_flags((obj)) == T_NORMAL_OBJECT) ? (obj)->methods->classdescriptor \
|
|
: ObjectClassBlock)
|
|
|
|
/* JSObject generated header */
|
|
#define IMPLEMENT_netscape_javascript_JSObject
|
|
#include "netscape_javascript_JSObject.h"
|
|
#define IMPLEMENT_netscape_javascript_JSException
|
|
#ifdef XP_MAC
|
|
#include "n_javascript_JSException.h"
|
|
#else
|
|
#include "netscape_javascript_JSException.h"
|
|
#endif
|
|
#include "java_lang_Throwable.h"
|
|
|
|
/*
|
|
* types of reflected java objects
|
|
*/
|
|
|
|
/* a package is basically just a name, since the jdk doesn't
|
|
* allow you to find out whether a particular name represents
|
|
* a package or not */
|
|
typedef struct JSJavaPackage JSJavaPackage;
|
|
|
|
/* either a java class or a java object.
|
|
* class.field is a static field or method
|
|
* class(...) constructs an object that is an instance of the class
|
|
* object.field is a field or method */
|
|
typedef struct JSJava JSJava;
|
|
|
|
/* type associated with a JSJava structure */
|
|
/* the JSClass java_class uses OBJECT and CLASS
|
|
* javaarray_class uses ARRAY */
|
|
typedef enum JSJavaType {
|
|
JAVA_UNDEF,
|
|
JAVA_OBJECT,
|
|
JAVA_CLASS,
|
|
JAVA_ARRAY
|
|
} JSJavaType;
|
|
|
|
/* JSJClassData holds the info necessary for js to represent
|
|
* itself to java. When js calls java, it leaves a call to
|
|
* the method referred to by mb on the java stack. Each
|
|
* JSScript that calls java contains its own copy of this,
|
|
* though the classloader and class may be shared. */
|
|
typedef struct {
|
|
int nrefs;
|
|
jglobal loader;
|
|
jglobal clazz;
|
|
struct methodblock *mb;
|
|
} JSJClassData;
|
|
|
|
/* fields and methods are initially represented with a slot object:
|
|
* this allows us to cope with fields and methods that have the same
|
|
* name. if the slot is used in a function context it will call the
|
|
* appropriate method (dynamically choosing between overloaded methods).
|
|
* if it is used in any other context it will convert itself to the
|
|
* value of the field at the time it was looked up. */
|
|
typedef struct JSJavaSlot JSJavaSlot;
|
|
|
|
/*
|
|
* globals for convenience, set up in the init routine
|
|
*/
|
|
static ClassClass * ObjectClassBlock = 0;
|
|
static ClassClass * JSObjectClassBlock = 0;
|
|
static ClassClass * JSExceptionClassBlock = 0;
|
|
static ClassClass * StringClassBlock = 0;
|
|
static ClassClass * BooleanClassBlock = 0;
|
|
static ClassClass * DoubleClassBlock = 0;
|
|
static ClassClass * ThrowableClassBlock = 0;
|
|
static JRIFieldID JSObjectInternalField;
|
|
|
|
/* note that these hash tables are only stable because we
|
|
* use a non-moving garbage collector! */
|
|
|
|
/*
|
|
* this is used to ensure that there is at most one reflection
|
|
* of any java object. objects are keyed by handle, classes are
|
|
* keyed by classblock pointer. the value for each is the JS
|
|
* object reflection. there is a root finder registered with the
|
|
* gc that marks all the java objects (keys). the JS objects
|
|
* are not in the GC's root set, and are responsible for removing
|
|
* themselves from the table upon finalization.
|
|
*/
|
|
static PRHashTable *javaReflections = NULL;
|
|
static PRMonitor *javaReflectionsMonitor = 0;
|
|
/* this jsContext is used when scanning the reflection table */
|
|
|
|
/*
|
|
* similarly for java reflections of JS objects - in this case
|
|
* the keys are JS objects. when the corresponding java instance
|
|
* is finalized, the entry is removed from the table, and a GC
|
|
* root for the JS object is removed.
|
|
*/
|
|
static PRHashTable *jsReflections = NULL;
|
|
static PRMonitor *jsReflectionsMonitor = 0;
|
|
|
|
/*
|
|
* Because of the magic of windows DLLs we need to get this passed in
|
|
*/
|
|
static JSJCallbacks *jsj_callbacks = NULL;
|
|
|
|
/*
|
|
* we keep track of the "main" runtime in order to use it for
|
|
* finalization (see js_RemoveReflection). this depends on there
|
|
* being only one JSRuntime which can run java, which may not be
|
|
* true eventually.
|
|
*/
|
|
JSRuntime *finalizeRuntime = 0;
|
|
|
|
#ifndef NSPR20
|
|
#ifdef MOZILLA_CLIENT
|
|
PR_LOG_DEFINE(MojaSrc);
|
|
#else
|
|
PRLogModuleInfo *MojaSrc;
|
|
#endif
|
|
#else
|
|
PRLogModuleInfo *MojaSrc = NULL;
|
|
#endif
|
|
|
|
/* lazy initialization */
|
|
static void jsj_FinishInit(JSContext *cx, JRIEnv *env);
|
|
|
|
/* check if a field is static/nonstatic */
|
|
/* this now accepts static fields for non-static references */
|
|
#define CHECK_STATIC(isStatic, fb) (((fb)->access & ACC_STATIC) \
|
|
? (isStatic) : !(isStatic))
|
|
|
|
/* forward declarations */
|
|
typedef void (*JSJCallback)(void*);
|
|
|
|
static JSBool
|
|
js_CallJava(JSContext *cx, JSJCallback doit, void *d, JSBool pushSafeFrame);
|
|
|
|
static JSBool
|
|
js_FindSystemClass(JSContext *cx, char *name, ClassClass **clazz);
|
|
|
|
static ClassClass *
|
|
js_FindJavaClass(JSContext *cx, char *name, ClassClass *from);
|
|
|
|
static JSBool
|
|
js_ExecuteJavaMethod(JSContext *cx, void *raddr, size_t rsize,
|
|
HObject *ho, char *name, char *sig,
|
|
struct methodblock *mb, JSBool isStaticCall, ...);
|
|
|
|
static HObject *
|
|
js_ConstructJava(JSContext *cx, char *name, ClassClass *cb, char *sig, ...);
|
|
|
|
static HObject *
|
|
js_ConstructJavaPrivileged(JSContext *cx, char *name, ClassClass *cb,
|
|
char *sig, ...);
|
|
|
|
static JSObject *
|
|
js_ReflectJava(JSContext *cx, JSJavaType type, HObject *handle,
|
|
ClassClass *cb, char *sig, JSObject *useMe);
|
|
|
|
static JSBool
|
|
js_reflectJavaSlot(JSContext *cx, JSObject *obj, JSString *str, jsval *vp);
|
|
|
|
static JSBool
|
|
js_javaMethodWrapper(JSContext *cx, JSObject *obj,
|
|
PRUintn argc, jsval *argv, jsval *rval);
|
|
|
|
static JSBool
|
|
js_javaConstructorWrapper(JSContext *cx, JSObject *obj,
|
|
PRUintn argc, jsval *argv, jsval *rval);
|
|
|
|
static JSBool
|
|
js_JArrayElementType(HObject *handle, char **sig, ClassClass **classp);
|
|
|
|
static JSBool
|
|
js_convertJSValueToJSObject(JSContext *cx, JSObject *mo, ClassClass *paramcb,
|
|
JSBool checkOnly, HObject **objp);
|
|
|
|
static JSBool
|
|
js_convertJSValueToJElement(JSContext *cx, jsval v,
|
|
char *addr, char *sig, ClassClass *fromclass,
|
|
char **sigRest);
|
|
|
|
static JSBool
|
|
js_convertJSValueToJValue(JSContext *cx, jsval v,
|
|
OBJECT *addr, char *sig, ClassClass *fromclass,
|
|
JSBool checkOnly, char **sigRestPtr,
|
|
int *cost);
|
|
|
|
static JSBool
|
|
js_convertJSValueToJField(JSContext *cx, jsval v,
|
|
HObject *ho, struct fieldblock *fb);
|
|
|
|
static JSBool
|
|
js_convertJElementToJSValue(JSContext *cx, char *addr, char *sig,
|
|
jsval *v, JSType desired);
|
|
|
|
static JSBool
|
|
js_convertJValueToJSValue(JSContext *cx, OBJECT *addr, char *sig,
|
|
jsval *vp, JSType desired);
|
|
|
|
static JSBool
|
|
js_convertJFieldToJSValue(JSContext *cx, HObject *ho, struct fieldblock *fb,
|
|
jsval *vp, JSType desired);
|
|
|
|
static JSBool
|
|
js_convertJObjectToJSString(JSContext *cx, HObject *ho, JSBool isClass,
|
|
jsval *vp);
|
|
|
|
static JSBool
|
|
js_convertJObjectToJSNumber(JSContext *cx, HObject *ho, JSBool isClass,
|
|
jsval *vp);
|
|
|
|
static JSBool
|
|
js_convertJObjectToJSBoolean(JSContext *cx, HObject *ho, JSBool isClass,
|
|
jsval *vp);
|
|
|
|
static JSJClassData *
|
|
jsj_MakeClassData(JSContext *cx);
|
|
|
|
static void
|
|
jsj_DestroyClassData(JSContext *cx, JSJClassData *data);
|
|
|
|
static JSBool
|
|
js_pushSafeFrame(JSContext *cx, ExecEnv *ee, JSJClassData **classData);
|
|
|
|
static void
|
|
js_popSafeFrame(JSContext *cx, ExecEnv *ee, JSJClassData *classData);
|
|
|
|
/* handy macro from agent.c */
|
|
#define obj_getoffset(o, off) (*(OBJECT *)((char *)unhand(o)+(off)))
|
|
|
|
/* Java threads call this to find the JSContext to use for
|
|
* executing JavaScript */
|
|
PR_IMPLEMENT(int)
|
|
JSJ_IsEnabled()
|
|
{
|
|
return jsj_callbacks->isEnabled();
|
|
}
|
|
|
|
/* Java threads call this to find the JSContext to use for
|
|
* executing JavaScript */
|
|
PR_IMPLEMENT(JSContext *)
|
|
JSJ_CurrentContext(JRIEnv *env, char **errp)
|
|
{
|
|
return jsj_callbacks->currentContext(env, errp);
|
|
}
|
|
|
|
/* Java threads call this before executing JS code (to preserve
|
|
* run-to-completion in the client */
|
|
PR_IMPLEMENT(void)
|
|
JSJ_EnterJS(void)
|
|
{
|
|
jsj_callbacks->enterJS();
|
|
}
|
|
|
|
/* Java threads call this when finished executing JS code */
|
|
PR_IMPLEMENT(void)
|
|
JSJ_ExitJS(void)
|
|
{
|
|
jsj_callbacks->exitJS();
|
|
}
|
|
|
|
/* Java threads call this when finished executing JS code */
|
|
PR_IMPLEMENT(JSObject *)
|
|
JSJ_GetDefaultObject(JRIEnv *env, jobject hint)
|
|
{
|
|
return jsj_callbacks->getDefaultObject(env, hint);
|
|
}
|
|
|
|
static ExecEnv *
|
|
jsj_GetCurrentEE(JSContext *cx)
|
|
{
|
|
JRIEnv *env = 0;
|
|
static int js_java_initialized = 0;
|
|
|
|
if (js_java_initialized != 2) {
|
|
/* use a cached monitor to make sure that we only
|
|
* initialize once */
|
|
PR_CEnterMonitor(&js_java_initialized);
|
|
switch (js_java_initialized) {
|
|
case 0: /* we're first */
|
|
js_java_initialized = 1;
|
|
PR_CExitMonitor(&js_java_initialized);
|
|
|
|
/* force both Java and JS to be initialized */
|
|
env = jsj_callbacks->currentEnv(cx);
|
|
|
|
if (!env)
|
|
goto out;
|
|
|
|
/* initialize the hash tables and classes we need */
|
|
jsj_FinishInit(cx, env);
|
|
|
|
PR_CEnterMonitor(&js_java_initialized);
|
|
js_java_initialized = 2;
|
|
PR_CNotifyAll(&js_java_initialized);
|
|
break;
|
|
case 1: /* in progress */
|
|
PR_CWait(&js_java_initialized, WAIT_FOREVER);
|
|
break;
|
|
case 2: /* done */
|
|
break;
|
|
}
|
|
PR_CExitMonitor(&js_java_initialized);
|
|
}
|
|
|
|
if (!env)
|
|
env = jsj_callbacks->currentEnv(cx);
|
|
|
|
out:
|
|
if (!env)
|
|
JS_ReportError(cx, "unable to get Java execution context for JavaScript");
|
|
|
|
return (ExecEnv *) env;
|
|
}
|
|
|
|
/**** **** **** **** **** **** **** **** ****
|
|
*
|
|
* java packages are just strings
|
|
*
|
|
**** **** **** **** **** **** **** **** ****/
|
|
|
|
struct JSJavaPackage {
|
|
char *name; /* e.g. "java/lang" or 0 if it's the top level */
|
|
};
|
|
|
|
/* javapackage uses standard getProperty */
|
|
|
|
static JSBool
|
|
javapackage_setProperty(JSContext *cx, JSObject *obj, jsval slot, jsval *vp)
|
|
{
|
|
JSJavaPackage *package = JS_GetPrivate(cx, obj);
|
|
|
|
JS_ReportError(cx, "%s doesn't refer to any Java value",
|
|
package->name);
|
|
return JS_FALSE;
|
|
}
|
|
|
|
static JSBool
|
|
javapackage_enumerate(JSContext *cx, JSObject *obj)
|
|
{
|
|
/* FIXME can't do this without reading directories... */
|
|
return JS_TRUE;
|
|
}
|
|
|
|
/* forward declaration */
|
|
static JSBool
|
|
javapackage_resolve(JSContext *cx, JSObject *obj, jsval id);
|
|
|
|
static JSBool
|
|
javapackage_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp)
|
|
{
|
|
JSJavaPackage *package = JS_GetPrivate(cx, obj);
|
|
JSString *str;
|
|
char *name, *cp;
|
|
|
|
switch (type) {
|
|
case JSTYPE_STRING:
|
|
/* convert '/' to '.' so it looks like the entry syntax */
|
|
if (!package->name)
|
|
break;
|
|
name = PR_smprintf("[JavaPackage %s]", package->name);
|
|
if (!name) {
|
|
JS_ReportOutOfMemory(cx);
|
|
return JS_FALSE;
|
|
}
|
|
for (cp = name; *cp != '\0'; cp++)
|
|
if (*cp == '/')
|
|
*cp = '.';
|
|
str = JS_NewString(cx, name, strlen(name));
|
|
if (!str) {
|
|
free(name);
|
|
JS_ReportOutOfMemory(cx);
|
|
return JS_FALSE;
|
|
}
|
|
|
|
*vp = STRING_TO_JSVAL(str);
|
|
break;
|
|
case JSTYPE_OBJECT:
|
|
*vp = OBJECT_TO_JSVAL(obj);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return JS_TRUE;
|
|
}
|
|
|
|
static void
|
|
javapackage_finalize(JSContext *cx, JSObject *obj)
|
|
{
|
|
JSJavaPackage *package = JS_GetPrivate(cx, obj);
|
|
|
|
/* get rid of the private data */
|
|
if (package->name)
|
|
JS_free(cx, package->name);
|
|
JS_free(cx, package);
|
|
}
|
|
|
|
static JSClass javapackage_class = {
|
|
"JavaPackage",
|
|
JSCLASS_HAS_PRIVATE,
|
|
JS_PropertyStub, JS_PropertyStub,
|
|
JS_PropertyStub, javapackage_setProperty, javapackage_enumerate,
|
|
javapackage_resolve, javapackage_convert, javapackage_finalize
|
|
};
|
|
|
|
/* needs pointer to javapackage_class */
|
|
static JSBool
|
|
javapackage_resolve(JSContext *cx, JSObject *obj, jsval id)
|
|
{
|
|
JSJavaPackage *package = JS_GetPrivate(cx, obj);
|
|
char *fullname;
|
|
char *name;
|
|
int namelen;
|
|
ClassClass *cb;
|
|
JSObject *mo;
|
|
JSJClassData *classData;
|
|
JRIEnv *env;
|
|
|
|
if (!JSVAL_IS_STRING(id))
|
|
return JS_TRUE;
|
|
|
|
name = JS_GetStringBytes(JSVAL_TO_STRING(id));
|
|
namelen = JS_GetStringLength(JSVAL_TO_STRING(id));
|
|
|
|
if (package->name) {
|
|
int packagelen = strlen(package->name);
|
|
fullname = JS_malloc(cx, packagelen + namelen + 2);
|
|
if (!fullname)
|
|
return JS_FALSE;
|
|
strcpy(fullname, package->name);
|
|
fullname[packagelen] = '/';
|
|
strcpy(fullname + packagelen + 1, name);
|
|
} else {
|
|
fullname = JS_malloc(cx, namelen + 1);
|
|
if (!fullname)
|
|
return JS_FALSE;
|
|
strcpy(fullname, name);
|
|
}
|
|
|
|
PR_LOG(MojaSrc, debug, ("looking for java class \"%s\"", fullname));
|
|
|
|
/* get the class whose classloader we use to find more classes */
|
|
if (!jsj_callbacks->jsClassLoader) {
|
|
classData = NULL;
|
|
} else if (!(classData = jsj_MakeClassData(cx))) {
|
|
JS_free(cx, fullname);
|
|
return JS_FALSE;
|
|
}
|
|
|
|
env = (JRIEnv *) jsj_GetCurrentEE(cx);
|
|
if (!env) {
|
|
JS_free(cx, fullname);
|
|
return JS_FALSE;
|
|
}
|
|
|
|
/* see if the name is a class */
|
|
cb = js_FindJavaClass(cx, fullname,
|
|
classData
|
|
? JRI_GetGlobalRef(env, classData->clazz)
|
|
: NULL);
|
|
|
|
if (jsj_callbacks->jsClassLoader)
|
|
jsj_DestroyClassData(cx, classData);
|
|
|
|
/* if not, it's a package */
|
|
if (!cb) {
|
|
JSJavaPackage *newpackage = JS_malloc(cx, sizeof(JSJavaPackage));
|
|
if (!newpackage) {
|
|
JS_free(cx, fullname);
|
|
return JS_FALSE;
|
|
}
|
|
PR_LOG(MojaSrc, debug, ("creating package %s", fullname));
|
|
newpackage->name = fullname;
|
|
mo = JS_NewObject(cx, &javapackage_class, 0, 0); /* FIXME proto and parent ok? */
|
|
if (!mo) {
|
|
JS_free(cx, fullname);
|
|
JS_free(cx, newpackage);
|
|
return JS_FALSE;
|
|
}
|
|
JS_SetPrivate(cx, mo, newpackage);
|
|
} else {
|
|
/* reflect the Class object */
|
|
mo = js_ReflectJClassToJSObject(cx, cb);
|
|
if (!mo) {
|
|
/* FIXME error message? */
|
|
return JS_FALSE;
|
|
}
|
|
JS_free(cx, fullname);
|
|
}
|
|
|
|
return JS_DefineProperty(cx, obj, name, OBJECT_TO_JSVAL(mo),
|
|
0, 0, JSPROP_READONLY);
|
|
}
|
|
|
|
/**** **** **** **** **** **** **** **** ****/
|
|
|
|
struct JSJava {
|
|
JSJavaType type; /* object / array / class */
|
|
HObject *handle; /* handle to the java Object */
|
|
ClassClass *cb; /* classblock, or element cb if array */
|
|
char *signature; /* array element signature */
|
|
};
|
|
|
|
static struct fieldblock *
|
|
java_lookup_field(JSContext *cx, ClassClass *cb, JSBool isStatic,
|
|
const char *name)
|
|
{
|
|
while (cb) {
|
|
int i;
|
|
for (i = 0; i < (int)cbFieldsCount(cb); i++) {
|
|
struct fieldblock *fb = cbFields(cb) + i;
|
|
if (CHECK_STATIC(isStatic, fb)
|
|
&& !strcmp(fieldname(fb), name)) {
|
|
return fb;
|
|
}
|
|
}
|
|
/* check the parent */
|
|
if (cbSuperclass(cb))
|
|
cb = cbSuperclass(cb);
|
|
else
|
|
cb = 0;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct fieldblock *
|
|
java_lookup_name(JSContext *cx, ClassClass *cb, JSBool isStatic,
|
|
const char *name)
|
|
{
|
|
while (cb) {
|
|
int i;
|
|
for (i = 0; i < (int)cbFieldsCount(cb); i++) {
|
|
struct fieldblock *fb = cbFields(cb) + i;
|
|
if (CHECK_STATIC(isStatic, fb)
|
|
&& !strcmp(fieldname(fb), name)) {
|
|
return fb;
|
|
}
|
|
}
|
|
for (i = cbMethodsCount(cb); i--;) {
|
|
struct methodblock *mb = cbMethods(cb) + i;
|
|
if (CHECK_STATIC(isStatic, &mb->fb)
|
|
&& !strcmp(fieldname(&mb->fb), name)) {
|
|
return &mb->fb;
|
|
}
|
|
}
|
|
/* check the parent */
|
|
if (cbSuperclass(cb))
|
|
cb = cbSuperclass(cb);
|
|
else
|
|
cb = 0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static JSBool
|
|
java_getProperty(JSContext *cx, JSObject *obj, jsval slot, jsval *vp)
|
|
{
|
|
JSString *str;
|
|
JSJava *java = JS_GetPrivate(cx, obj);
|
|
|
|
if (!java) {
|
|
JS_ReportError(cx, "illegal operation on Java prototype object");
|
|
return JS_FALSE;
|
|
}
|
|
|
|
/* FIXME reflect a getter/setter pair as a property! */
|
|
|
|
if (!JSVAL_IS_STRING(slot)) {
|
|
JS_ReportError(cx, "invalid Java property expression");
|
|
return JS_FALSE;
|
|
}
|
|
|
|
str = JSVAL_TO_STRING(slot);
|
|
PR_LOG(MojaSrc, debug, ("looked up slot \"%s\"", JS_GetStringBytes(str)));
|
|
|
|
/* utter hack so that the assign hack doesn't kill us */
|
|
if (slot == STRING_TO_JSVAL(ATOM_TO_STRING(cx->runtime->atomState.assignAtom))) {
|
|
*vp = JSVAL_VOID;
|
|
return JS_TRUE;
|
|
}
|
|
|
|
return js_reflectJavaSlot(cx, obj, str, vp);
|
|
}
|
|
|
|
static JSBool
|
|
java_setProperty(JSContext *cx, JSObject *obj, jsval slot, jsval *vp)
|
|
{
|
|
JSJava *java = JS_GetPrivate(cx, obj);
|
|
ClassClass *cb;
|
|
struct fieldblock *fb = 0;
|
|
JSString *str;
|
|
const char *name;
|
|
|
|
if (!java) {
|
|
JS_ReportError(cx, "illegal operation on Java prototype object");
|
|
return JS_FALSE;
|
|
}
|
|
|
|
cb = java->cb;
|
|
|
|
if (!JSVAL_IS_STRING(slot)) {
|
|
JS_ReportError(cx, "invalid Java property assignment");
|
|
return JS_FALSE;
|
|
}
|
|
|
|
str = JSVAL_TO_STRING(slot);
|
|
name = JS_GetStringBytes(str);
|
|
PR_LOG(MojaSrc, debug, ("looked up slot \"%s\"", name));
|
|
|
|
fb = java_lookup_field(cx, cb, java->type == JAVA_CLASS, name);
|
|
if (!fb) {
|
|
JS_ReportError(cx, "no Java %sfield found with name %s",
|
|
(java->type == JAVA_CLASS ? "static " : ""),
|
|
name);
|
|
return JS_FALSE;
|
|
}
|
|
|
|
if (!js_convertJSValueToJField(cx, *vp, java->handle, fb)) {
|
|
JS_ReportError(cx, "can't set Java field %s", name);
|
|
return JS_FALSE;
|
|
}
|
|
return JS_TRUE;
|
|
}
|
|
|
|
static JSBool
|
|
java_enumerate_property(JSContext *cx, JSObject *obj, struct fieldblock *fb)
|
|
{
|
|
JSJava *java = JS_GetPrivate(cx, obj);
|
|
jsval junk;
|
|
|
|
PR_ASSERT(java->type == JAVA_OBJECT || java->type == JAVA_CLASS);
|
|
if (!(fb->access & ACC_PUBLIC) ||
|
|
!CHECK_STATIC(java->type == JAVA_CLASS, fb)) {
|
|
return JS_TRUE;
|
|
}
|
|
|
|
return JS_GetProperty(cx, obj, fieldname(fb), &junk);
|
|
/* FIXME this is what i would prefer, but it seems the get is necessary
|
|
return JS_DefineProperty(cx, obj, fieldname(fb), JSVAL_VOID,
|
|
0, 0, JSPROP_ENUMERATE);
|
|
*/
|
|
}
|
|
|
|
static JSBool
|
|
java_enumerate(JSContext *cx, JSObject *obj)
|
|
{
|
|
JSJava *java = JS_GetPrivate(cx, obj);
|
|
ClassClass *cb;
|
|
|
|
if (!java)
|
|
return JS_TRUE;
|
|
|
|
cb = java->cb;
|
|
|
|
while (cb) {
|
|
int i;
|
|
for (i = 0; i < (int)cbFieldsCount(cb); i++) {
|
|
struct fieldblock *fb, *outerfb;
|
|
|
|
fb = cbFields(cb) + i;
|
|
outerfb = java_lookup_name(cx, java->cb, java->type == JAVA_CLASS,
|
|
fieldname(fb));
|
|
if (outerfb && outerfb != fb)
|
|
continue;
|
|
|
|
if (!java_enumerate_property(cx, obj, fb))
|
|
return JS_FALSE;
|
|
}
|
|
for (i = cbMethodsCount(cb); i--;) {
|
|
struct methodblock *mb;
|
|
struct fieldblock *outerfb;
|
|
|
|
mb = cbMethods(cb) + i;
|
|
outerfb = java_lookup_name(cx, java->cb, java->type == JAVA_CLASS,
|
|
fieldname(&mb->fb));
|
|
if (outerfb && outerfb != &mb->fb)
|
|
continue;
|
|
|
|
if (!java_enumerate_property(cx, obj, &mb->fb))
|
|
return JS_FALSE;
|
|
}
|
|
/* check the parent */
|
|
if (cbSuperclass(cb))
|
|
cb = cbSuperclass(cb);
|
|
else
|
|
cb = 0;
|
|
}
|
|
return JS_TRUE;
|
|
}
|
|
|
|
PR_STATIC_CALLBACK(JSBool)
|
|
java_resolve(JSContext *cx, JSObject *obj, jsval id)
|
|
{
|
|
JSJava *java = JS_GetPrivate(cx, obj);
|
|
char *name;
|
|
jsval v;
|
|
JSErrorReporter oldReporter;
|
|
JSBool hasProperty;
|
|
|
|
if (!java || !JSVAL_IS_STRING(id))
|
|
return JS_TRUE;
|
|
|
|
name = JS_GetStringBytes(JSVAL_TO_STRING(id));
|
|
PR_LOG(MojaSrc, debug, ("resolve(0x%x, %s) (handle 0x%x)",
|
|
obj, name, java->handle));
|
|
|
|
oldReporter = JS_SetErrorReporter(cx, NULL);
|
|
hasProperty = java_getProperty(cx, obj, id, &v);
|
|
JS_SetErrorReporter(cx, oldReporter);
|
|
if (!hasProperty)
|
|
return JS_TRUE;
|
|
|
|
return JS_DefineProperty(cx, obj, name, v, 0, 0, JSPROP_ENUMERATE);
|
|
}
|
|
|
|
PR_STATIC_CALLBACK(PRHashNumber)
|
|
java_hashHandle(void *key)
|
|
{
|
|
PRHashNumber num = (PRHashNumber) key ; /* help lame MSVC1.5 on Win16 */
|
|
/* win16 compiler can't shift right by 2, but it will do it by 1 twice. */
|
|
num = num >> 1;
|
|
return num >> 1;
|
|
}
|
|
|
|
static int
|
|
java_pointerEq(void *v1, void *v2)
|
|
{
|
|
return v1 == v2;
|
|
}
|
|
|
|
PR_STATIC_CALLBACK(void)
|
|
java_finalize(JSContext *cx, JSObject *obj)
|
|
{
|
|
JSJava *java = JS_GetPrivate(cx, obj);
|
|
void *key;
|
|
PRHashEntry *he, **hep;
|
|
|
|
if (!java)
|
|
return;
|
|
|
|
/* remove it from the reflection table */
|
|
if (java->type == JAVA_CLASS) {
|
|
PR_LOG(MojaSrc, debug, ("removing class 0x%x from table", java->cb));
|
|
key = cbName(java->cb);
|
|
} else {
|
|
PR_LOG(MojaSrc, debug, ("removing handle 0x%x from table", java->handle));
|
|
key = java->handle;
|
|
}
|
|
PR_EnterMonitor(javaReflectionsMonitor);
|
|
if (javaReflections) {
|
|
hep = PR_HashTableRawLookup(javaReflections,
|
|
java_hashHandle(key), key);
|
|
he = *hep;
|
|
if (he) {
|
|
PR_HashTableRawRemove(javaReflections, hep, he);
|
|
}
|
|
}
|
|
PR_ExitMonitor(javaReflectionsMonitor);
|
|
|
|
/* get rid of the private data */
|
|
JS_free(cx, java);
|
|
}
|
|
|
|
PR_STATIC_CALLBACK(JSBool)
|
|
java_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp)
|
|
{
|
|
JSJava *java = JS_GetPrivate(cx, obj);
|
|
|
|
if (!java) {
|
|
if (type == JSTYPE_OBJECT) {
|
|
*vp = OBJECT_TO_JSVAL(obj);
|
|
return JS_TRUE;
|
|
}
|
|
|
|
JS_ReportError(cx, "illegal operation on Java prototype object");
|
|
return JS_FALSE;
|
|
}
|
|
|
|
PR_LOG(MojaSrc, debug, ("java_convert to %d", type));
|
|
|
|
switch (type) {
|
|
case JSTYPE_OBJECT:
|
|
*vp = OBJECT_TO_JSVAL(obj);
|
|
break;
|
|
case JSTYPE_FUNCTION:
|
|
/* only classes convert to functions (constructors) */
|
|
if (java->type != JAVA_CLASS) {
|
|
/* random java objects do not convert to functions */
|
|
JS_ReportError(cx, "can't convert Java object to function");
|
|
return JS_FALSE;
|
|
} else {
|
|
JSString *str;
|
|
JSFunction *fun;
|
|
JSObject *funobj;
|
|
JSObject *globalobj;
|
|
jsval javaPrototype;
|
|
|
|
PR_LOG(MojaSrc, debug, ("making a constructor\n"));
|
|
|
|
str = JS_NewStringCopyZ(cx, cbName(java->cb));
|
|
if (!str)
|
|
return JS_FALSE;
|
|
|
|
fun = JS_NewFunction(cx, js_javaConstructorWrapper, 0, 0, 0,
|
|
JS_GetStringBytes(str));
|
|
|
|
/* FIXME a private property of the function object points to the
|
|
* classblock: gc problem? */
|
|
funobj = JS_GetFunctionObject(fun);
|
|
if (!JS_DefineProperty(cx, funobj, "#javaClass",
|
|
PRIVATE_TO_JSVAL(java->cb), 0, 0,
|
|
JSPROP_READONLY)) {
|
|
return JS_FALSE;
|
|
}
|
|
|
|
/* set the prototype so that objects constructed with
|
|
* "new" have the right JSClass */
|
|
if (!(globalobj = JS_GetGlobalObject(cx))
|
|
|| !JS_GetProperty(cx, globalobj,
|
|
"#javaPrototype", &javaPrototype)
|
|
|| !JS_DefineProperty(cx, funobj, "prototype",
|
|
javaPrototype, 0, 0,
|
|
JSPROP_READONLY)) {
|
|
return JS_FALSE;
|
|
}
|
|
|
|
*vp = OBJECT_TO_JSVAL(funobj);
|
|
}
|
|
break;
|
|
case JSTYPE_STRING:
|
|
/* either pull out the string or call toString */
|
|
return js_convertJObjectToJSString(cx, java->handle,
|
|
java->type==JAVA_CLASS, vp);
|
|
case JSTYPE_NUMBER:
|
|
/* call doubleValue() */
|
|
return js_convertJObjectToJSNumber(cx, java->handle,
|
|
java->type==JAVA_CLASS, vp);
|
|
case JSTYPE_BOOLEAN:
|
|
/* call booleanValue() */
|
|
return js_convertJObjectToJSBoolean(cx, java->handle,
|
|
java->type==JAVA_CLASS, vp);
|
|
default:
|
|
break;
|
|
}
|
|
return JS_TRUE;
|
|
}
|
|
|
|
static JSClass java_class = {
|
|
"Java",
|
|
JSCLASS_HAS_PRIVATE,
|
|
JS_PropertyStub, JS_PropertyStub,
|
|
java_getProperty, java_setProperty, java_enumerate,
|
|
java_resolve, java_convert, java_finalize
|
|
};
|
|
|
|
/**** **** **** **** **** **** **** **** ****/
|
|
|
|
static JSBool
|
|
javaarray_getProperty(JSContext *cx, JSObject *obj, jsval slot, jsval *vp)
|
|
{
|
|
JSJava *array = JS_GetPrivate(cx, obj);
|
|
HObject *ho = array->handle;
|
|
PRInt32 index;
|
|
PRInt32 size;
|
|
char *addr;
|
|
|
|
if (JSVAL_IS_STRING(slot)) {
|
|
char *name = JS_GetStringBytes(JSVAL_TO_STRING(slot));
|
|
if (0 == strcmp(name, "length")) {
|
|
*vp = INT_TO_JSVAL((jsint)obj_length(ho));
|
|
return JS_TRUE;
|
|
}
|
|
JS_ReportError(cx, "invalid Java array element %s", name);
|
|
return JS_FALSE;
|
|
}
|
|
|
|
if (!JSVAL_IS_INT(slot)) {
|
|
JS_ReportError(cx, "invalid Java array index expression");
|
|
return JS_FALSE;
|
|
}
|
|
index = JSVAL_TO_INT(slot);
|
|
if (index < 0 || index >= (PRInt32) obj_length(ho)) {
|
|
JS_ReportError(cx, "Java array index %d out of range", index);
|
|
return JS_FALSE;
|
|
}
|
|
|
|
size = sizearray(obj_flags(ho), 1);
|
|
addr = ((char*) unhand(ho)) + index * size;
|
|
|
|
if (!js_convertJElementToJSValue(cx, addr, array->signature,
|
|
vp, JSTYPE_VOID)) {
|
|
JS_ReportError(cx,
|
|
"can't convert Java array element %d to JavaScript value", index);
|
|
return JS_FALSE;
|
|
}
|
|
|
|
return JS_TRUE;
|
|
}
|
|
|
|
static JSBool
|
|
javaarray_setProperty(JSContext *cx, JSObject *obj, jsval slot, jsval *vp)
|
|
{
|
|
JSJava *array = JS_GetPrivate(cx, obj);
|
|
HObject *ho = array->handle;
|
|
PRInt32 index;
|
|
PRInt32 size;
|
|
char *addr;
|
|
|
|
if (JSVAL_IS_STRING(slot)) {
|
|
char *name = JS_GetStringBytes(JSVAL_TO_STRING(slot));
|
|
if (0 == strcmp(name, "length")) {
|
|
JS_ReportError(cx, "can't set length of a Java array");
|
|
return JS_FALSE;
|
|
}
|
|
JS_ReportError(cx, "invalid assignment to Java array element %s", name);
|
|
return JS_FALSE;
|
|
}
|
|
|
|
if (!JSVAL_IS_INT(slot)) {
|
|
JS_ReportError(cx, "invalid Java array index expression");
|
|
return JS_FALSE;
|
|
}
|
|
index = JSVAL_TO_INT(slot);
|
|
if (index < 0 || index >= (PRInt32) obj_length(ho)) {
|
|
JS_ReportError(cx, "Java array index %d out of range", index);
|
|
return JS_FALSE;
|
|
}
|
|
|
|
size = sizearray(obj_flags(ho), 1);
|
|
addr = ((char*) unhand(ho)) + index * size;
|
|
|
|
if (!js_convertJSValueToJElement(cx, *vp, addr,
|
|
array->signature, array->cb, 0)) {
|
|
JS_ReportError(cx, "invalid assignment to Java array element %d",
|
|
index);
|
|
return JS_FALSE;
|
|
}
|
|
|
|
return JS_TRUE;
|
|
}
|
|
|
|
static JSBool
|
|
javaarray_enumerate(JSContext *cx, JSObject *obj)
|
|
{
|
|
JSJava *array = JS_GetPrivate(cx, obj);
|
|
HObject *ho = array->handle;
|
|
PRUint32 i;
|
|
jsval v;
|
|
|
|
for (i = 0; i < obj_length(ho); i++) {
|
|
PRInt32 size = sizearray(obj_flags(ho), 1);
|
|
char *addr = ((char*) unhand(ho)) + i * size;
|
|
|
|
if (!js_convertJElementToJSValue(cx, addr, array->signature,
|
|
&v, JSTYPE_VOID)) {
|
|
return JS_FALSE;
|
|
}
|
|
if (!JS_SetElement(cx, obj, (jsint) i, &v)) {
|
|
return JS_FALSE;
|
|
}
|
|
}
|
|
return JS_TRUE;
|
|
}
|
|
|
|
static JSBool
|
|
javaarray_resolve(JSContext *cx, JSObject *obj, jsval id)
|
|
{
|
|
JSJava *array = JS_GetPrivate(cx, obj);
|
|
HObject *ho = array->handle;
|
|
jsint index;
|
|
|
|
if (!JSVAL_IS_INT(id))
|
|
return JS_TRUE;
|
|
|
|
index = JSVAL_TO_INT(id);
|
|
if (index < 0 || index >= (PRInt32) obj_length(ho))
|
|
return JS_TRUE;
|
|
|
|
return JS_DefineElement(cx, obj, index, JSVAL_VOID,
|
|
0, 0, JSPROP_ENUMERATE);
|
|
}
|
|
|
|
static void
|
|
javaarray_finalize(JSContext *cx, JSObject *obj)
|
|
{
|
|
JSJava *array = JS_GetPrivate(cx, obj);
|
|
|
|
/* get rid of the private data */
|
|
/* array->signature is a static string */ ;
|
|
array->signature = 0;
|
|
|
|
/* remove it from the reflection table */
|
|
java_finalize(cx, obj);
|
|
}
|
|
|
|
static JSBool
|
|
javaarray_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp)
|
|
{
|
|
switch (type) {
|
|
case JSTYPE_OBJECT:
|
|
*vp = OBJECT_TO_JSVAL(obj);
|
|
break;
|
|
case JSTYPE_STRING:
|
|
/* FIXME how should arrays convert to strings? */
|
|
default:
|
|
break;
|
|
}
|
|
return JS_TRUE;
|
|
}
|
|
|
|
|
|
static JSClass javaarray_class = {
|
|
"JavaArray",
|
|
JSCLASS_HAS_PRIVATE,
|
|
JS_PropertyStub, JS_PropertyStub,
|
|
javaarray_getProperty, javaarray_setProperty, javaarray_enumerate,
|
|
javaarray_resolve, javaarray_convert, javaarray_finalize
|
|
};
|
|
|
|
/**** **** **** **** **** **** **** **** ****/
|
|
|
|
/* this lameness is brought to you by the decision
|
|
* to use strings for signatures in the JDK */
|
|
static char *
|
|
getSignatureBase(char *sig)
|
|
{
|
|
int len;
|
|
char *ret;
|
|
char end;
|
|
|
|
switch (*sig) {
|
|
case SIGNATURE_CLASS:
|
|
end = SIGNATURE_ENDCLASS;
|
|
break;
|
|
case SIGNATURE_FUNC:
|
|
end = SIGNATURE_ENDFUNC;
|
|
break;
|
|
default:
|
|
return 0;
|
|
break;
|
|
}
|
|
|
|
len = strchr(sig+1, end) - (sig+1);
|
|
ret = malloc(len+1);
|
|
if (!ret)
|
|
return 0;
|
|
strncpy(ret, sig+1, len);
|
|
ret[len] = '\0';
|
|
|
|
return ret;
|
|
}
|
|
|
|
static ClassClass *
|
|
getSignatureClass(JSContext *cx, char *sig, ClassClass *from)
|
|
{
|
|
char *name;
|
|
ClassClass *cb;
|
|
|
|
if (sig[1] == '\0') {
|
|
/* special hack: if the signature is "L" it means
|
|
* just use the from class */
|
|
return from;
|
|
}
|
|
name = getSignatureBase(sig);
|
|
if (!name) {
|
|
JS_ReportOutOfMemory(cx);
|
|
return 0;
|
|
}
|
|
cb = js_FindJavaClass(cx, name, from);
|
|
free(name);
|
|
return cb;
|
|
}
|
|
|
|
/* this doesn't need to run in a java env, but it may throw an
|
|
* exception so we need a temporary one */
|
|
static JSBool
|
|
js_isSubclassOf(JSContext *cx, ClassClass *cb1, ClassClass *cb2)
|
|
{
|
|
ExecEnv *ee = jsj_GetCurrentEE(cx);
|
|
JSBool ret;
|
|
|
|
/* FIXME what to do with the exception? */
|
|
if (!ee) {
|
|
return JS_FALSE;
|
|
}
|
|
|
|
exceptionClear(ee);
|
|
ret = (JSBool) is_subclass_of(cb1, cb2, ee);
|
|
if (exceptionOccurred(ee)) {
|
|
#ifdef DEBUG
|
|
char *message;
|
|
|
|
/* exceptionDescribe(ee); */
|
|
/* FIXME this could fail: we don't check if it's throwable,
|
|
* but i assume that is_subclass_of is well-behaved */
|
|
HString *hdetail =
|
|
unhand((Hjava_lang_Throwable *)
|
|
ee->exception.exc)->detailMessage;
|
|
ClassClass *cb = obj_array_classblock(ee->exception.exc);
|
|
|
|
/*
|
|
* FIXME JRI_GetStringPlatformChars and JRI_GetStringUTFChars both
|
|
* return a pointer into an otherwise unreferenced, unrooted Java
|
|
* array's body, without returning the Java array's handle. This
|
|
* requires fairly conservative GC, which we have in NSPR1.0, but
|
|
* if we lose it, this code may break.
|
|
*/
|
|
message = (char *)
|
|
JRI_GetStringPlatformChars((JRIEnv *) ee,
|
|
(struct java_lang_String *) hdetail,
|
|
NULL, 0);
|
|
|
|
/* XXX - temporarily replace arguments so we can compile
|
|
(const jbyte *) cx->charSetName,
|
|
(jint) cx->charSetNameLength);
|
|
*/
|
|
|
|
PR_LOG(MojaSrc, error,
|
|
("exception in is_subclass_of %s (\"%s\")",
|
|
cbName(cb), message));
|
|
#endif
|
|
exceptionClear(ee);
|
|
return JS_FALSE;
|
|
}
|
|
return ret ? JS_TRUE : JS_FALSE;
|
|
}
|
|
|
|
static JSBool
|
|
js_convertJSValueToJSObject(JSContext *cx, JSObject *mo, ClassClass *paramcb,
|
|
JSBool checkOnly, HObject **objp)
|
|
{
|
|
/* check if a JSObject would be an acceptable argument */
|
|
if (!js_isSubclassOf(cx, JSObjectClassBlock, paramcb))
|
|
return JS_FALSE;
|
|
|
|
/* JSObject is ok, so convert mo to one.
|
|
* a JS object which is not really a java object
|
|
* converts to JSObject */
|
|
if (!checkOnly) {
|
|
*objp = js_ReflectJSObjectToJObject(cx, mo);
|
|
if (!*objp)
|
|
return JS_FALSE;
|
|
}
|
|
return JS_TRUE;
|
|
}
|
|
|
|
static JSBool
|
|
js_convertJSValueToJArray(HObject **objp, JSContext *cx,
|
|
jsval v, char *sig, ClassClass *fromclass,
|
|
JSBool checkOnly, int *cost)
|
|
{
|
|
/* FIXME bump the cost counter when necessary */
|
|
JSJava *java;
|
|
HArrayOfObject *harr;
|
|
ClassClass *acb;
|
|
ClassClass *paramcb;
|
|
JSObject *mo;
|
|
char *elementsig;
|
|
ClassClass *elementClazz;
|
|
|
|
/* the only legal conversions are from null or from a java array */
|
|
if (!JSVAL_IS_OBJECT(v))
|
|
return JS_FALSE;
|
|
|
|
/* can always pass null */
|
|
mo = JSVAL_TO_OBJECT(v);
|
|
if (!mo) {
|
|
if (!checkOnly)
|
|
*objp = 0;
|
|
return JS_TRUE;
|
|
}
|
|
|
|
/* otherwise, it must be a JS reflection of a java array */
|
|
if (! JS_InstanceOf(cx, mo, &javaarray_class, 0))
|
|
return JS_FALSE;
|
|
|
|
java = JS_GetPrivate(cx, mo);
|
|
|
|
sig++; /* skip to array element type */
|
|
|
|
switch (*sig) {
|
|
case SIGNATURE_CLASS: /* object array */
|
|
paramcb = getSignatureClass(cx, sig, fromclass);
|
|
PR_LOG(MojaSrc, debug, ("desired array element signature \"%s\"(0x%x)",
|
|
sig, paramcb));
|
|
if (!paramcb) {
|
|
PR_LOG(MojaSrc, warn,
|
|
("couldn't find class for signature \"%s\"\n", sig));
|
|
return JS_FALSE;
|
|
}
|
|
harr = (HArrayOfObject *) java->handle;
|
|
acb = (ClassClass *)(unhand(harr)->body[class_offset(harr)]);
|
|
if (!acb) {
|
|
PR_LOG(MojaSrc, warn,
|
|
("couldn't find class of array element\n"));
|
|
return JS_FALSE;
|
|
}
|
|
|
|
/* elements must convert */
|
|
if (js_isSubclassOf(cx, acb, paramcb)) {
|
|
if (!checkOnly)
|
|
*objp = (HObject *) harr;
|
|
return JS_TRUE;
|
|
}
|
|
break;
|
|
case SIGNATURE_ARRAY: /* nested array */
|
|
/* FIXME nested arrays can't be supported, because the
|
|
* jdk runtime treats them all as flat
|
|
*
|
|
* This just in... Actually, you can get the dimensions of a
|
|
* java array because they aren't flattened. */
|
|
/* FIXME throw an exception */
|
|
break;
|
|
default: /* primitive array */
|
|
/* for any other array, the signature must match exactly */
|
|
if (js_JArrayElementType(java->handle, &elementsig, &elementClazz)) {
|
|
if (elementsig[0] == sig[0]) {
|
|
if (!checkOnly)
|
|
*objp = java->handle;
|
|
return JS_TRUE;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
return JS_FALSE;
|
|
}
|
|
|
|
PR_IMPLEMENT(JSBool)
|
|
js_convertJSValueToJObject(HObject **objp, JSContext *cx,
|
|
jsval v, char *sig, ClassClass *fromclass,
|
|
JSBool checkOnly, int *cost)
|
|
{
|
|
/* FIXME bump the cost counter when necessary */
|
|
|
|
ClassClass *paramcb;
|
|
|
|
paramcb = sig ? getSignatureClass(cx, sig, fromclass)
|
|
: ObjectClassBlock;
|
|
|
|
PR_LOG(MojaSrc, debug, ("desired argument class signature \"%s\"(0x%x)",
|
|
sig, paramcb));
|
|
|
|
if (!paramcb) {
|
|
PR_LOG(MojaSrc, warn,
|
|
("couldn't find class for signature \"%s\"", sig));
|
|
return JS_FALSE;
|
|
}
|
|
|
|
/* JS wrappers around java objects do the same check
|
|
* as the java compiler */
|
|
/* FIXME except classes, which become JSObject (circular problem?) */
|
|
if (JSVAL_IS_OBJECT(v)) {
|
|
JSObject *mo = JSVAL_TO_OBJECT(v);
|
|
/* null is always a valid object */
|
|
if (!mo) {
|
|
if (!checkOnly)
|
|
*objp = 0;
|
|
return JS_TRUE;
|
|
}
|
|
if (JS_TypeOfValue(cx, v) == JSTYPE_FUNCTION) {
|
|
if (js_convertJSValueToJSObject(cx, mo, paramcb, checkOnly, objp))
|
|
return JS_TRUE;
|
|
} else if (JS_InstanceOf(cx, mo, &java_class, 0) ||
|
|
JS_InstanceOf(cx, mo, &javaarray_class, 0)) {
|
|
JSJava *java = JS_GetPrivate(cx, mo);
|
|
HObject *ho = java->handle;
|
|
ClassClass *cb;
|
|
|
|
/* class reflections convert to JSObject or String only */
|
|
if (java->type == JAVA_CLASS) {
|
|
if (js_convertJSValueToJSObject(cx, mo, paramcb, checkOnly,
|
|
objp)) {
|
|
return JS_TRUE;
|
|
}
|
|
} else {
|
|
/* get the classblock for the java argument type */
|
|
cb = obj_array_classblock(ho);
|
|
PR_LOG(MojaSrc, debug, ("actual argument 0x%x, class 0x%x",
|
|
ho, cb));
|
|
|
|
/* check against the expected class */
|
|
if (js_isSubclassOf(cx, cb, paramcb)) {
|
|
if (!checkOnly)
|
|
*objp = ho;
|
|
return JS_TRUE;
|
|
}
|
|
}
|
|
} else {
|
|
/* otherwise see if it will take a JSObject */
|
|
if (js_convertJSValueToJSObject(cx, mo, paramcb, checkOnly, objp))
|
|
return JS_TRUE;
|
|
}
|
|
} else if (JSVAL_IS_NUMBER(v)) { /* java.lang.Double */
|
|
if (js_isSubclassOf(cx, DoubleClassBlock, paramcb)) {
|
|
/* Float is ok */
|
|
if (!checkOnly)
|
|
*objp = js_ConstructJava(cx, "java/lang/Double",
|
|
0, "(D)",
|
|
JSVAL_IS_INT(v)
|
|
? (double) JSVAL_TO_INT(v)
|
|
: *JSVAL_TO_DOUBLE(v));
|
|
return JS_TRUE;
|
|
}
|
|
} else if (JSVAL_IS_BOOLEAN(v)) { /* java.lang.Boolean */
|
|
/* FIXME this should return Boolean.JS_TRUE or Boolean.FALSE
|
|
* instead of constructing a new one? */
|
|
if (js_isSubclassOf(cx, BooleanClassBlock, paramcb)) {
|
|
if (!checkOnly)
|
|
*objp = js_ConstructJava(cx, "java/lang/Boolean",
|
|
0, "(Z)", (long) JSVAL_TO_BOOLEAN(v));
|
|
return JS_TRUE;
|
|
}
|
|
}
|
|
|
|
/* last ditch attempt: is a String acceptable? */
|
|
if (js_isSubclassOf(cx, StringClassBlock, paramcb)) {
|
|
JSString *str;
|
|
/* string is ok, convert to one */
|
|
|
|
str = JS_ValueToString(cx, v);
|
|
if (str) {
|
|
/* make a java String from str */
|
|
if (!checkOnly) {
|
|
ExecEnv *ee = jsj_GetCurrentEE(cx);
|
|
if (!ee)
|
|
return JS_FALSE;
|
|
*objp = (JHandle *)
|
|
JRI_NewStringPlatform((JRIEnv *) ee,
|
|
(const jbyte *) JS_GetStringBytes(str),
|
|
(jint) JS_GetStringLength(str),
|
|
NULL, 0);
|
|
|
|
/* XXX - temporarily replace arguments so we can compile
|
|
(const jbyte *) cx->charSetName,
|
|
(jint) cx->charSetNameLength);
|
|
*/
|
|
}
|
|
return JS_TRUE;
|
|
}
|
|
}
|
|
return JS_FALSE;
|
|
}
|
|
|
|
PR_IMPLEMENT(JSBool)
|
|
js_convertJObjectToJSValue(JSContext *cx, jsval *vp, HObject *ho)
|
|
{
|
|
ExecEnv *ee;
|
|
JRIEnv *env;
|
|
JSObject *mo;
|
|
|
|
ee = jsj_GetCurrentEE(cx);
|
|
if (!ee) return JS_FALSE;
|
|
|
|
env = (JRIEnv *) ee;
|
|
|
|
if (!ho) {
|
|
*vp = OBJECT_TO_JSVAL(0);
|
|
return JS_TRUE;
|
|
}
|
|
|
|
/*
|
|
* If it's a JSObject, pull out the original JS object when
|
|
* it comes back into the JS world.
|
|
*/
|
|
if (obj_array_classblock(ho) == JSObjectClassBlock) {
|
|
jref jso = (jref) ho /* FIXME */;
|
|
*vp = OBJECT_TO_JSVAL(
|
|
(JSObject *) JRI_GetFieldInt(env, jso, JSObjectInternalField));
|
|
return JS_TRUE;
|
|
}
|
|
|
|
/*
|
|
* Instances of java.lang.String are wrapped so we can
|
|
* call methods on them, but they convert to a JS string
|
|
* if used in a string context.
|
|
*/
|
|
|
|
/* otherwise, wrap it */
|
|
mo = js_ReflectJObjectToJSObject(cx, ho);
|
|
if (!mo) {
|
|
JS_ReportError(cx, "can't convert Java object to JavaScript object");
|
|
return JS_FALSE;
|
|
}
|
|
*vp = OBJECT_TO_JSVAL(mo);
|
|
return JS_TRUE;
|
|
}
|
|
|
|
|
|
#define REFLECTION_IN_PROGRESS ((jref)1)
|
|
|
|
PR_IMPLEMENT(HObject *)
|
|
js_ReflectJSObjectToJObject(JSContext *cx, JSObject *mo)
|
|
{
|
|
ExecEnv *ee;
|
|
JRIEnv *env;
|
|
jref jso;
|
|
PRHashEntry *he;
|
|
|
|
ee = jsj_GetCurrentEE(cx);
|
|
if (!ee) return 0;
|
|
|
|
env = (JRIEnv *) ee;
|
|
|
|
/* see if it's already there */
|
|
PR_EnterMonitor(jsReflectionsMonitor);
|
|
|
|
while ((jso = PR_HashTableLookup(jsReflections, mo)) != 0) {
|
|
if (jso != REFLECTION_IN_PROGRESS)
|
|
break;
|
|
PR_Wait(jsReflectionsMonitor, WAIT_FOREVER);
|
|
}
|
|
|
|
if (jso) {
|
|
PR_ExitMonitor(jsReflectionsMonitor);
|
|
return (HObject *) jso /*FIXME*/;
|
|
}
|
|
|
|
/*
|
|
* Release the monitor temporarily while we call the java constructor,
|
|
* which could contain arbitrary scariness. Put a placeholder in the
|
|
* hash table so anyone racing to reflect mo waits for us to finish.
|
|
*/
|
|
PR_HashTableAdd(jsReflections, mo, REFLECTION_IN_PROGRESS);
|
|
PR_ExitMonitor(jsReflectionsMonitor);
|
|
|
|
/* not found, reflect it */
|
|
jso = (jref) /*FIXME*/
|
|
js_ConstructJavaPrivileged(cx, 0, JSObjectClassBlock, "()");
|
|
|
|
/* FIXME check for exceptions */
|
|
if (!jso) {
|
|
return 0;
|
|
}
|
|
|
|
/* need to check again since we released the monitor */
|
|
PR_EnterMonitor(jsReflectionsMonitor);
|
|
|
|
#ifdef DEBUG
|
|
{
|
|
jref tmp = PR_HashTableLookup(jsReflections, mo);
|
|
|
|
PR_ASSERT(!tmp || tmp == REFLECTION_IN_PROGRESS);
|
|
if (tmp && tmp != REFLECTION_IN_PROGRESS) {
|
|
PR_ExitMonitor(jsReflectionsMonitor);
|
|
|
|
/* let the one we constructed die in gc and use the one we found */
|
|
return (HObject *) tmp;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
JRI_SetFieldInt(env, jso, JSObjectInternalField, (jint) mo);
|
|
|
|
/* add it to the table */
|
|
he = PR_HashTableAdd(jsReflections, mo, jso);
|
|
if (he)
|
|
(void) JS_AddRoot(cx, (void *)&he->key); /* FIXME be errors? */
|
|
|
|
/* awaken anyone racing and exit monitor */
|
|
PR_NotifyAll(jsReflectionsMonitor);
|
|
PR_ExitMonitor(jsReflectionsMonitor);
|
|
|
|
return (HObject *) jso;/*FIXME*/
|
|
}
|
|
|
|
PR_IMPLEMENT(void)
|
|
js_RemoveReflection(JSContext *cx, JSObject *mo)
|
|
{
|
|
PRHashEntry *he, **hep;
|
|
|
|
/* remove it from the reflection table */
|
|
PR_LOG(MojaSrc, debug, ("removing JS object 0x%x from table", mo));
|
|
PR_EnterMonitor(jsReflectionsMonitor);
|
|
hep = PR_HashTableRawLookup(jsReflections, java_hashHandle(mo), mo);
|
|
he = *hep;
|
|
|
|
if (he) {
|
|
/* FIXME inline JS_RemoveRoot -- this will go away with GC unification */
|
|
JS_LOCK_RUNTIME(finalizeRuntime);
|
|
(void) PR_HashTableRemove(finalizeRuntime->gcRootsHash, (void *)&he->key);
|
|
JS_UNLOCK_RUNTIME(finalizeRuntime);
|
|
}
|
|
PR_HashTableRawRemove(jsReflections, hep, he);
|
|
PR_ExitMonitor(jsReflectionsMonitor);
|
|
}
|
|
|
|
|
|
|
|
static JSBool
|
|
js_convertJSValueToJValue(JSContext *cx, jsval v,
|
|
OBJECT *addr, char *sig, ClassClass *fromclass,
|
|
JSBool checkOnly, char **sigRestPtr,
|
|
int *cost)
|
|
{
|
|
Java8 tdub;
|
|
char *p = sig;
|
|
|
|
switch (*p) {
|
|
case SIGNATURE_BOOLEAN:
|
|
if (!JSVAL_IS_BOOLEAN(v)) {
|
|
/* FIXME we could convert other things to boolean too,
|
|
* but until cost checking is done this will cause us
|
|
* to choose the wrong method if there is method overloading */
|
|
#ifndef USE_COSTS
|
|
return JS_FALSE;
|
|
#else
|
|
if (!JS_ConvertValue(cx, v, JSTYPE_BOOLEAN, &v))
|
|
return JS_FALSE;
|
|
(*cost)++;
|
|
#endif
|
|
}
|
|
if (!checkOnly)
|
|
*(long*)addr = (JSVAL_TO_BOOLEAN(v) == JS_TRUE);
|
|
break;
|
|
case SIGNATURE_SHORT:
|
|
case SIGNATURE_BYTE:
|
|
case SIGNATURE_CHAR:
|
|
case SIGNATURE_INT:
|
|
/* FIXME should really do a range check... */
|
|
if (!JSVAL_IS_NUMBER(v)) {
|
|
#ifndef USE_COSTS
|
|
return JS_FALSE;
|
|
#else
|
|
if (!JS_ConvertValue(cx, v, JSTYPE_NUMBER, &v))
|
|
return JS_FALSE;
|
|
(*cost)++;
|
|
#endif
|
|
}
|
|
if (!checkOnly) {
|
|
if (JSVAL_IS_INT(v))
|
|
*(long*)addr = (long) JSVAL_TO_INT(v);
|
|
else
|
|
*(long*)addr = (long) *JSVAL_TO_DOUBLE(v);
|
|
}
|
|
break;
|
|
|
|
case SIGNATURE_LONG:
|
|
if (!JSVAL_IS_NUMBER(v)) {
|
|
#ifndef USE_COSTS
|
|
return JS_FALSE;
|
|
#else
|
|
if (!JS_ConvertValue(cx, v, JSTYPE_NUMBER, &v))
|
|
return JS_FALSE;
|
|
(*cost)++;
|
|
#endif
|
|
}
|
|
if (!checkOnly) {
|
|
PRInt64 ll;
|
|
if (JSVAL_IS_INT(v)) {
|
|
long l = (long) JSVAL_TO_INT(v);
|
|
LL_I2L(ll, l);
|
|
} else {
|
|
double d = (double) *JSVAL_TO_DOUBLE(v);
|
|
LL_D2L(ll, d);
|
|
}
|
|
SET_INT64(tdub, addr, ll);
|
|
}
|
|
break;
|
|
|
|
case SIGNATURE_FLOAT:
|
|
if (!JSVAL_IS_NUMBER(v)) {
|
|
#ifndef USE_COSTS
|
|
return JS_FALSE;
|
|
#else
|
|
if (!JS_ConvertValue(cx, v, JSTYPE_NUMBER, &v))
|
|
return JS_FALSE;
|
|
(*cost)++;
|
|
#endif
|
|
}
|
|
if (!checkOnly) {
|
|
if (JSVAL_IS_INT(v))
|
|
*(float*)addr = (float) JSVAL_TO_INT(v);
|
|
else
|
|
*(float*)addr = (float) *JSVAL_TO_DOUBLE(v);
|
|
}
|
|
break;
|
|
|
|
case SIGNATURE_DOUBLE:
|
|
if (!JSVAL_IS_NUMBER(v)) {
|
|
#ifndef USE_COSTS
|
|
return JS_FALSE;
|
|
#else
|
|
if (!JS_ConvertValue(cx, v, JSTYPE_NUMBER, &v))
|
|
return JS_FALSE;
|
|
(*cost)++;
|
|
#endif
|
|
}
|
|
if (!checkOnly) {
|
|
double d;
|
|
if (JSVAL_IS_INT(v))
|
|
d = (double) JSVAL_TO_INT(v);
|
|
else
|
|
d = (double) *JSVAL_TO_DOUBLE(v);
|
|
SET_DOUBLE(tdub, addr, d);
|
|
}
|
|
break;
|
|
|
|
case SIGNATURE_CLASS:
|
|
/* FIXME cost? */
|
|
if (!js_convertJSValueToJObject((HObject **)/*FIXME*/addr,
|
|
cx, v, p, fromclass,
|
|
checkOnly, cost)) {
|
|
return JS_FALSE;
|
|
}
|
|
while (*p != SIGNATURE_ENDCLASS) p++;
|
|
break;
|
|
|
|
case SIGNATURE_ARRAY:
|
|
/* FIXME cost? */
|
|
if (!js_convertJSValueToJArray((HObject **)/*FIXME*/addr,
|
|
cx, v, p, fromclass,
|
|
checkOnly, cost)) {
|
|
return JS_FALSE;
|
|
}
|
|
while (*p == SIGNATURE_ARRAY) p++; /* skip array beginning */
|
|
/* skip the element type */
|
|
if (*p == SIGNATURE_CLASS) {
|
|
while (*p != SIGNATURE_ENDCLASS) p++;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
PR_LOG(MojaSrc, warn, ("unknown value signature '%s'", p));
|
|
return JS_FALSE;
|
|
}
|
|
|
|
if (sigRestPtr)
|
|
*sigRestPtr = p+1;
|
|
return JS_TRUE;
|
|
}
|
|
|
|
|
|
/*
|
|
* this code mimics the method overloading resolution
|
|
* done in java, for use in JS->java calls
|
|
*/
|
|
|
|
|
|
/* push a jsval array onto the java stack for use by
|
|
* the given method.
|
|
* sigRest gets a pointer to the remainder of the signature (the
|
|
* rest of the arguments in the list).
|
|
* if checkOnly is true, don't actually convert, just check that it's ok.
|
|
*/
|
|
static JSBool
|
|
js_convertJSToJArgs(JSContext *cx, stack_item *optop,
|
|
struct methodblock *mb, int argc, jsval *argv,
|
|
JSBool checkOnly, char **sigRestPtr,
|
|
int *cost)
|
|
{
|
|
struct fieldblock *fb = &mb->fb;
|
|
char *sig = fieldsig(fb);
|
|
jsval *vp = argv;
|
|
int argsleft = argc;
|
|
char *p;
|
|
void *addr;
|
|
|
|
*cost = 0;
|
|
|
|
for (p = sig + 1; *p != SIGNATURE_ENDFUNC; vp++, argsleft--) {
|
|
if (argsleft == 0) /* not enough arguments passed */
|
|
return JS_FALSE;
|
|
if (checkOnly)
|
|
addr = 0;
|
|
else switch (*p) {
|
|
case SIGNATURE_BOOLEAN:
|
|
case SIGNATURE_SHORT:
|
|
case SIGNATURE_BYTE:
|
|
case SIGNATURE_CHAR:
|
|
case SIGNATURE_INT:
|
|
addr = &(optop++)->i;
|
|
break;
|
|
case SIGNATURE_LONG:
|
|
addr = optop;
|
|
optop += 2;
|
|
break;
|
|
case SIGNATURE_FLOAT:
|
|
addr = &(optop++)->f;
|
|
break;
|
|
case SIGNATURE_DOUBLE:
|
|
addr = optop;
|
|
optop += 2;
|
|
break;
|
|
case SIGNATURE_CLASS:
|
|
case SIGNATURE_ARRAY:
|
|
addr = &(optop++)->h;
|
|
break;
|
|
default:
|
|
PR_LOG(MojaSrc, warn,
|
|
("Invalid method signature '%s' for method '%s'\n",
|
|
fieldsig(fb), fieldname(fb)));
|
|
return JS_FALSE;
|
|
break;
|
|
}
|
|
/* this bumps p to the next argument */
|
|
if (!js_convertJSValueToJValue(cx, *vp, addr, p, fieldclass(&mb->fb),
|
|
checkOnly, &p, cost)) {
|
|
return JS_FALSE;
|
|
}
|
|
}
|
|
if (argsleft > 0) {
|
|
/* too many arguments */
|
|
return JS_FALSE;
|
|
}
|
|
|
|
if (sigRestPtr)
|
|
*sigRestPtr = p+1 /* go past the SIGNATURE_ENDFUNC */;
|
|
return JS_TRUE;
|
|
}
|
|
|
|
/* java array elements are packed with smaller sizes */
|
|
static JSBool
|
|
js_convertJSValueToJElement(JSContext *cx, jsval v,
|
|
char *addr, char *sig, ClassClass *fromclass,
|
|
char **sigRestPtr)
|
|
{
|
|
long tmp[2];
|
|
int cost = 0;
|
|
|
|
if (!js_convertJSValueToJValue(cx, v, (OBJECT *)&tmp,
|
|
sig, fromclass,
|
|
JS_FALSE, sigRestPtr, &cost))
|
|
return JS_FALSE;
|
|
|
|
switch (sig[0]) {
|
|
case SIGNATURE_BOOLEAN:
|
|
*(char*)addr = (char)(*(long*)&tmp);
|
|
break;
|
|
case SIGNATURE_BYTE:
|
|
*(char*)addr = (char)(*(long*)&tmp);
|
|
break;
|
|
case SIGNATURE_CHAR:
|
|
*(unicode*)addr = (unicode)(*(long*)&tmp);
|
|
break;
|
|
case SIGNATURE_SHORT:
|
|
*(signed short*)addr = (signed short)(*(long*)&tmp);
|
|
break;
|
|
case SIGNATURE_INT:
|
|
*(PRInt32*)addr = *(long*)&tmp;
|
|
break;
|
|
case SIGNATURE_LONG:
|
|
*(PRInt64*)addr = *(PRInt64*)&tmp;
|
|
break;
|
|
case SIGNATURE_FLOAT:
|
|
*(float*)addr = *(float*)&tmp;
|
|
break;
|
|
case SIGNATURE_DOUBLE:
|
|
*(double*)addr = *(double*)&tmp;
|
|
break;
|
|
case SIGNATURE_CLASS:
|
|
case SIGNATURE_ARRAY:
|
|
*(HObject**)addr = *(HObject**)&tmp;
|
|
break;
|
|
default:
|
|
PR_LOG(MojaSrc, warn, ("unknown value signature '%s'\n", sig[0]));
|
|
return JS_FALSE;
|
|
}
|
|
return JS_TRUE;
|
|
}
|
|
|
|
|
|
static OBJECT *
|
|
getJavaFieldAddress(HObject *ho, struct fieldblock *fb)
|
|
{
|
|
OBJECT *addr = 0; /* address of the java value */
|
|
|
|
if (fb->access & ACC_PUBLIC) {
|
|
if (fb->access & ACC_STATIC) {
|
|
char *sig = fieldsig(fb);
|
|
|
|
if (sig[0] == SIGNATURE_LONG || sig[0] == SIGNATURE_DOUBLE)
|
|
addr = (long *)twoword_static_address(fb);
|
|
else
|
|
addr = (long *)normal_static_address(fb);
|
|
} else {
|
|
addr = &obj_getoffset(ho, fb->u.offset);
|
|
}
|
|
}
|
|
return addr;
|
|
}
|
|
|
|
static JSBool
|
|
js_convertJSValueToJField(JSContext *cx, jsval v, HObject *ho,
|
|
struct fieldblock *fb)
|
|
{
|
|
OBJECT *addr = getJavaFieldAddress(ho, fb);
|
|
int cost = 0;
|
|
|
|
if (!addr) {
|
|
JS_ReportError(cx, "can't access field %s", fieldname(fb));
|
|
return JS_FALSE;
|
|
}
|
|
return js_convertJSValueToJValue(cx, v, addr,
|
|
fieldsig(fb), fieldclass(fb),
|
|
JS_FALSE, 0, &cost);
|
|
}
|
|
|
|
/*
|
|
* Returns -1 if the given method is not applicable to the arguments,
|
|
* or a cost if it is.
|
|
* if argc == -1, match iff the name matches and don't check the
|
|
* signature. this always returns cost 1.
|
|
*/
|
|
static int
|
|
methodIsApplicable(JSContext *cx,
|
|
struct methodblock *mb, JSBool isStatic,
|
|
const char *name, int argc, jsval *argv)
|
|
{
|
|
struct fieldblock *fb = &mb->fb;
|
|
int cost = 0;
|
|
|
|
/* name and access must match */
|
|
if (!CHECK_STATIC(isStatic, fb) || strcmp(fieldname(fb), name))
|
|
return -1;
|
|
|
|
if (argc == -1)
|
|
return 1;
|
|
|
|
if (!js_convertJSToJArgs(cx, 0, mb, argc, argv,
|
|
JS_TRUE /* checkOnly */,
|
|
0 /* &sigRest */, &cost)) {
|
|
return -1;
|
|
}
|
|
|
|
return cost;
|
|
}
|
|
|
|
/* FIXME this routine doesn't work yet - its purpose is to choose
|
|
* the best method when java methods are overloaded, or to detect
|
|
* ambiguous method calls */
|
|
static JSBool
|
|
methodIsMoreSpecific(JSContext *cx,
|
|
struct methodblock *mb1, struct methodblock *mb2)
|
|
{
|
|
char *sig1 = fieldsig(&mb1->fb) + 1; /* skip '(' */
|
|
char *sig2 = fieldsig(&mb2->fb) + 1;
|
|
|
|
/* FIXME fill this in! */
|
|
return JS_TRUE;
|
|
|
|
/* go through the args */
|
|
while (*sig1 != SIGNATURE_ENDFUNC && *sig2 != SIGNATURE_ENDFUNC) {
|
|
if (*sig1 == SIGNATURE_CLASS) {
|
|
if (*sig2 == SIGNATURE_CLASS) {
|
|
ClassClass *cb1 =
|
|
getSignatureClass(cx, sig1, fieldclass(&mb1->fb));
|
|
ClassClass *cb2 =
|
|
getSignatureClass(cx, sig2, fieldclass(&mb2->fb));
|
|
|
|
if (! js_isSubclassOf(cx, cb1, cb2))
|
|
return JS_FALSE;
|
|
|
|
/* next argument */
|
|
while (*sig1++ != SIGNATURE_ENDCLASS);
|
|
while (*sig2++ != SIGNATURE_ENDCLASS);
|
|
}
|
|
}
|
|
}
|
|
if (*sig1 != *sig2) {
|
|
return JS_FALSE;
|
|
/* arg number mismatch */
|
|
}
|
|
return JS_TRUE;
|
|
}
|
|
|
|
/* based on sun.tools.java.Environment */
|
|
/**
|
|
* Return true if an implicit cast from this type to
|
|
* the given type is allowed.
|
|
*/
|
|
|
|
static JSBool
|
|
js_convertJValueToJSValue(JSContext *cx, OBJECT *addr, char *sig, jsval *vp,
|
|
JSType desired)
|
|
{
|
|
Java8 tmp;
|
|
|
|
switch (sig[0]) {
|
|
case SIGNATURE_VOID:
|
|
*vp = JSVAL_VOID;
|
|
break;
|
|
case SIGNATURE_BYTE:
|
|
case SIGNATURE_CHAR:
|
|
case SIGNATURE_SHORT:
|
|
*vp = INT_TO_JSVAL((jsint) *addr);
|
|
break;
|
|
case SIGNATURE_INT:
|
|
{
|
|
PRInt32 val = (PRInt32) *addr;
|
|
if (INT_FITS_IN_JSVAL(val)) {
|
|
*vp = INT_TO_JSVAL((jsint) *addr);
|
|
} else {
|
|
if (!JS_NewDoubleValue(cx, val, vp))
|
|
return JS_FALSE;
|
|
}
|
|
}
|
|
break;
|
|
case SIGNATURE_BOOLEAN:
|
|
*vp = BOOLEAN_TO_JSVAL((JSBool) *addr);
|
|
break;
|
|
case SIGNATURE_LONG:
|
|
{
|
|
PRInt64 ll;
|
|
double d;
|
|
|
|
ll = GET_INT64(tmp, addr);
|
|
LL_L2D(d, ll);
|
|
if (!JS_NewDoubleValue(cx, d, vp))
|
|
return JS_FALSE;
|
|
}
|
|
break;
|
|
case SIGNATURE_FLOAT:
|
|
if (!JS_NewDoubleValue(cx, *(float *)addr, vp))
|
|
return JS_FALSE;
|
|
break;
|
|
case SIGNATURE_DOUBLE:
|
|
if (!JS_NewDoubleValue(cx, GET_DOUBLE(tmp, addr), vp))
|
|
return JS_FALSE;
|
|
break;
|
|
case SIGNATURE_CLASS:
|
|
case SIGNATURE_ARRAY:
|
|
return js_convertJObjectToJSValue(cx, vp, *(HObject **) addr);
|
|
default:
|
|
JS_ReportError(cx, "unknown Java signature character '%c'", sig[0]);
|
|
return JS_FALSE;
|
|
}
|
|
|
|
if (desired != JSTYPE_VOID)
|
|
return JS_ConvertValue(cx, *vp, desired, vp);
|
|
return JS_TRUE;
|
|
}
|
|
|
|
/* java array elements are packed with smaller sizes */
|
|
static JSBool
|
|
js_convertJElementToJSValue(JSContext *cx, char *addr, char *sig, jsval *vp,
|
|
JSType desired)
|
|
{
|
|
switch (sig[0]) {
|
|
case SIGNATURE_BYTE:
|
|
*vp = INT_TO_JSVAL(*(char*)addr);
|
|
break;
|
|
case SIGNATURE_CHAR:
|
|
*vp = INT_TO_JSVAL(*(unicode*)addr);
|
|
break;
|
|
case SIGNATURE_SHORT:
|
|
*vp = INT_TO_JSVAL(*(signed short*)addr);
|
|
break;
|
|
case SIGNATURE_INT:
|
|
{
|
|
PRInt32 val = *(PRInt32*)addr;
|
|
if (INT_FITS_IN_JSVAL(val)) {
|
|
*vp = INT_TO_JSVAL((jsint)val);
|
|
} else {
|
|
if (!JS_NewDoubleValue(cx, val, vp))
|
|
return JS_FALSE;
|
|
}
|
|
}
|
|
break;
|
|
case SIGNATURE_BOOLEAN:
|
|
*vp = BOOLEAN_TO_JSVAL((JSBool) *(PRInt32*)addr);
|
|
break;
|
|
default:
|
|
return js_convertJValueToJSValue(cx, (OBJECT *) addr, sig, vp, desired);
|
|
}
|
|
|
|
if (desired != JSTYPE_VOID)
|
|
return JS_ConvertValue(cx, *vp, desired, vp);
|
|
return JS_TRUE;
|
|
}
|
|
|
|
static JSBool
|
|
js_convertJFieldToJSValue(JSContext *cx, HObject *ho, struct fieldblock *fb,
|
|
jsval *vp, JSType desired)
|
|
{
|
|
OBJECT *addr = getJavaFieldAddress(ho, fb);
|
|
char *sig = fieldsig(fb);
|
|
|
|
if (!addr) {
|
|
JS_ReportError(cx, "can't access Java field %s", fieldname(fb));
|
|
return JS_FALSE;
|
|
}
|
|
return js_convertJValueToJSValue(cx, addr, sig, vp, desired);
|
|
}
|
|
|
|
static JSBool
|
|
js_convertJObjectToJSString(JSContext *cx, HObject *ho, JSBool isClass,
|
|
jsval *vp)
|
|
{
|
|
JSString *str;
|
|
char *cstr;
|
|
|
|
if (isClass) {
|
|
cstr = PR_smprintf("[JavaClass %s]", cbName((ClassClass*)ho));
|
|
} else {
|
|
HString *hstr;
|
|
ExecEnv *ee;
|
|
|
|
if (!ho)
|
|
return JS_FALSE;
|
|
|
|
if (obj_classblock(ho) == StringClassBlock) {
|
|
/* it's a string already */
|
|
hstr = (HString*) ho;
|
|
} else {
|
|
/* call toString() to convert to a string */
|
|
if (!js_ExecuteJavaMethod(cx, &hstr, sizeof(hstr), ho,
|
|
"toString", "()Ljava/lang/String;",
|
|
0, JS_FALSE)) {
|
|
return JS_FALSE;
|
|
}
|
|
}
|
|
|
|
/* FIXME js_ExecuteJavaMethod does this too */
|
|
ee = jsj_GetCurrentEE(cx);
|
|
if (!ee)
|
|
return JS_FALSE;
|
|
|
|
/* convert the java string to a JS string */
|
|
cstr = (char *)
|
|
JRI_GetStringPlatformChars((JRIEnv *) ee,
|
|
(struct java_lang_String *) hstr,
|
|
NULL, 0);
|
|
|
|
/* XXX - temporarily replace arguments so we can compile
|
|
(const jbyte *) cx->charSetName,
|
|
(jint) cx->charSetNameLength);
|
|
*/
|
|
|
|
if (cstr)
|
|
cstr = strdup(cstr);
|
|
}
|
|
if (!cstr) {
|
|
JS_ReportOutOfMemory(cx);
|
|
return JS_FALSE;
|
|
}
|
|
str = JS_NewString(cx, cstr, strlen(cstr));
|
|
if (!str) {
|
|
free(cstr);
|
|
return JS_FALSE;
|
|
}
|
|
*vp = STRING_TO_JSVAL(str);
|
|
return JS_TRUE;
|
|
}
|
|
|
|
static JSBool
|
|
js_convertJObjectToJSNumber(JSContext *cx, HObject *ho, JSBool isClass,
|
|
jsval *vp)
|
|
{
|
|
long foo[2], swap; /* FIXME JRI and JDK disagree */
|
|
JRI_JDK_Java8 tmp;
|
|
double d;
|
|
|
|
if (isClass || !ho) {
|
|
JS_ReportError(cx, "can't convert Java object to number");
|
|
return JS_FALSE;
|
|
}
|
|
|
|
if (!js_ExecuteJavaMethod(cx, foo, sizeof(foo), ho,
|
|
"doubleValue", "()D",
|
|
0, JS_FALSE))
|
|
return JS_FALSE;
|
|
|
|
swap = foo[0];
|
|
foo[0] = foo[1];
|
|
foo[1] = swap;
|
|
d = JRI_GET_DOUBLE(tmp, &foo[0]);
|
|
return JS_NewDoubleValue(cx, d, vp);
|
|
}
|
|
|
|
static JSBool
|
|
js_convertJObjectToJSBoolean(JSContext *cx, HObject *ho, JSBool isClass,
|
|
jsval *vp)
|
|
{
|
|
long b;
|
|
|
|
if (isClass) {
|
|
b = JS_TRUE;
|
|
goto ok;
|
|
}
|
|
|
|
if (!ho) {
|
|
b = JS_FALSE;
|
|
goto ok;
|
|
}
|
|
|
|
if (!js_ExecuteJavaMethod(cx, &b, sizeof(b), ho,
|
|
"booleanValue", "()Z",
|
|
0, JS_FALSE)) {
|
|
/* failed, probably missing the booleanValue() method */
|
|
/* by default we convert to true */
|
|
b = JS_TRUE;
|
|
}
|
|
ok:
|
|
*vp = BOOLEAN_TO_JSVAL(b);
|
|
return JS_TRUE;
|
|
}
|
|
|
|
static JSBool
|
|
java_returnAsJSValue(JSContext *cx, jsval *vp, ExecEnv *ee, char *sig)
|
|
{
|
|
OBJECT *addr;
|
|
|
|
if ((sig[0] == SIGNATURE_DOUBLE || sig[0] == SIGNATURE_LONG)) {
|
|
addr = (OBJECT*) &ee->current_frame->optop[-2];
|
|
} else {
|
|
addr = (OBJECT *) &ee->current_frame->optop[-1];
|
|
}
|
|
return js_convertJValueToJSValue(cx, addr, sig, vp, JSTYPE_VOID);
|
|
}
|
|
|
|
static char*
|
|
js_createArgTypeString(JSContext* cx, int argc, jsval* argv)
|
|
{
|
|
int i;
|
|
int size = 0;
|
|
char *p;
|
|
char** strs;
|
|
char *str;
|
|
|
|
if (argc < 1) {
|
|
str = strdup("");
|
|
return str;
|
|
}
|
|
|
|
strs = (char**) malloc(argc * sizeof(char*));
|
|
if (!strs)
|
|
return 0;
|
|
|
|
for (i = 0; i < argc; i++) {
|
|
JSType t = JS_TypeOfValue(cx, argv[i]);
|
|
switch (t) {
|
|
case JSTYPE_VOID:
|
|
strs[i] = "void";
|
|
break;
|
|
case JSTYPE_OBJECT:
|
|
if (JSVAL_IS_NULL(argv[i]))
|
|
strs[i] = "null";
|
|
else
|
|
strs[i] = JS_GetClass(JSVAL_TO_OBJECT(argv[i]))->name;
|
|
break;
|
|
case JSTYPE_FUNCTION:
|
|
strs[i] = "function";
|
|
break;
|
|
case JSTYPE_STRING:
|
|
strs[i] = "string";
|
|
break;
|
|
case JSTYPE_NUMBER:
|
|
strs[i] = "number";
|
|
break;
|
|
case JSTYPE_BOOLEAN:
|
|
strs[i] = "boolean";
|
|
break;
|
|
default:
|
|
PR_ASSERT(!"bad arg type");
|
|
}
|
|
size += strlen(strs[i]) + 2; /* +2 for ", " */
|
|
}
|
|
|
|
str = (char*) malloc(size - 1); /* -2 for ", " & +1 for '\0' */
|
|
if (!str)
|
|
goto out;
|
|
|
|
strcpy(str, strs[0]);
|
|
p = str + strlen(strs[0]);
|
|
for (i = 1; i < argc; i++) {
|
|
*p++ = ',';
|
|
*p++ = ' ';
|
|
strcpy(p, strs[i]);
|
|
p += strlen(strs[i]);
|
|
}
|
|
*p = '\0';
|
|
|
|
out:
|
|
free(strs);
|
|
return str;
|
|
}
|
|
|
|
/*
|
|
* find the best method to call for a name and a bunch of JS
|
|
* arguments. try each possible method, then find the most
|
|
* specific of the applicable methods. "most specific" is
|
|
* determined without reference to the JS arguments: it is
|
|
* the same as "most specific" in the java sense of the word.
|
|
* FIXME this could cause trouble since more methods will be applicable
|
|
* to a JS call than for a similar java call!
|
|
* pass argc == -1 to match any method with the correct name
|
|
*/
|
|
static struct methodblock *
|
|
matchMethod(JSContext *cx, ClassClass *clazz, JSBool isStatic,
|
|
const char *name, int argc, jsval *argv, JSBool reportErrors)
|
|
{
|
|
int mindex;
|
|
int *isApplicable = 0; /* array holding costs */
|
|
struct methodblock *bestmb = 0;
|
|
JSBool isOverloaded = JS_FALSE;
|
|
ClassClass *cb = clazz;
|
|
|
|
while (cb) {
|
|
/*
|
|
* Find all applicable methods. keep track of which ones are
|
|
* applicable using the isApplicable array, and the best one
|
|
* found so far in bestmb. set isOverloaded if there is more
|
|
* than one applicable method.
|
|
*/
|
|
isApplicable = JS_malloc(cx, cbMethodsCount(cb) * sizeof(int));
|
|
if (!isApplicable)
|
|
return 0;
|
|
for (mindex = cbMethodsCount(cb); mindex--;) {
|
|
struct methodblock *mb = cbMethods(cb) + mindex;
|
|
struct fieldblock *fb = &mb->fb;
|
|
|
|
isApplicable[mindex] = methodIsApplicable(cx, mb, isStatic,
|
|
name, argc, argv);
|
|
if (isApplicable[mindex] == -1)
|
|
continue;
|
|
|
|
PR_LOG(MojaSrc, debug, ("found applicable method %s with sig %s",
|
|
fieldname(fb), fieldsig(fb)));
|
|
|
|
if (!bestmb) { /* first one found */
|
|
bestmb = mb;
|
|
continue;
|
|
}
|
|
|
|
isOverloaded = JS_TRUE;;
|
|
|
|
if (methodIsMoreSpecific(cx, mb, bestmb)) {
|
|
bestmb = mb;
|
|
}
|
|
}
|
|
|
|
/* if we've found something applicable in the current class,
|
|
* no need to go any further */
|
|
/* this is the only exit from the loop in which isApplicable
|
|
* is live */
|
|
if (bestmb)
|
|
break;
|
|
|
|
/* otherwise, check the parent */
|
|
if (cbSuperclass(cb))
|
|
cb = cbSuperclass(cb);
|
|
else
|
|
cb = 0;
|
|
JS_free(cx, isApplicable);
|
|
isApplicable = 0;
|
|
}
|
|
|
|
/* second pass: check that bestmb is more specific than all other
|
|
* applicable methods */
|
|
/* FIXME if mb is equally specific, this is an ambiguous lookup.
|
|
Hopefully we can disambiguate by the cost */
|
|
|
|
/*
|
|
if (isOverloaded)
|
|
for (mindex = cb->methods_count; mindex--;) {
|
|
struct methodblock *mb = cbMethods(cb) + mindex;
|
|
struct fieldblock *fb = &mb->fb;
|
|
|
|
if (!isApplicable[mindex] || mb == bestmb)
|
|
continue;
|
|
|
|
}
|
|
*/
|
|
|
|
if (isApplicable)
|
|
JS_free(cx, isApplicable);
|
|
|
|
if (bestmb && (bestmb->fb.access & ACC_PUBLIC))
|
|
return bestmb;
|
|
else if (reportErrors) {
|
|
char buf[512];
|
|
char *argstr = js_createArgTypeString(cx, argc, argv);
|
|
char *methodtype = "method";
|
|
|
|
if (0 == strcmp(name, "<init>")) {
|
|
methodtype = "constructor";
|
|
}
|
|
|
|
if (!bestmb) {
|
|
if (argstr) {
|
|
PR_snprintf(buf, sizeof(buf),
|
|
"no Java %s %s.%s matching JavaScript arguments (%s)",
|
|
methodtype, cbName(clazz), name, argstr);
|
|
free(argstr);
|
|
} else {
|
|
PR_snprintf(buf, sizeof(buf),
|
|
"no Java %s %s.%s matching JavaScript arguments",
|
|
methodtype, cbName(clazz), name);
|
|
}
|
|
} else {
|
|
if (argstr) {
|
|
PR_snprintf(buf, sizeof(buf),
|
|
"Java %s %s.%s is not public (%s)",
|
|
methodtype, cbName(clazz), name, argstr);
|
|
free(argstr);
|
|
} else {
|
|
PR_snprintf(buf, sizeof(buf),
|
|
"Java %s %s.%s is not public",
|
|
methodtype, cbName(clazz), name);
|
|
}
|
|
}
|
|
JS_ReportError(cx, buf);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/* this is the callback to execute java bytecodes in the
|
|
* appropriate java env */
|
|
typedef struct {
|
|
JRIEnv *env;
|
|
char *pc;
|
|
JSBool ok;
|
|
} js_ExecuteJava_data;
|
|
|
|
static void
|
|
js_ExecuteJava_stub(void *d)
|
|
{
|
|
js_ExecuteJava_data *data = d;
|
|
PR_EXTERN(bool_t) ExecuteJava(unsigned char *, ExecEnv *ee);
|
|
|
|
data->ok = (JSBool) ExecuteJava((unsigned char*) data->pc, /*FIXME*/(ExecEnv*)data->env);
|
|
}
|
|
|
|
/* this is a copy of do_execute_java_method_vararg modified for
|
|
* a jsval* argument list instead of a va_list
|
|
*/
|
|
static JSBool
|
|
do_js_execute_java_method(JSContext *cx, void *obj,
|
|
struct methodblock *mb, JSBool isStaticCall,
|
|
int argc, jsval *argv,
|
|
jsval *rval)
|
|
{
|
|
ExecEnv *ee = jsj_GetCurrentEE(cx);
|
|
char *method_name;
|
|
char *method_signature;
|
|
JavaFrame *current_frame, *previous_frame;
|
|
JavaStack *current_stack;
|
|
char *sigRest;
|
|
int cost = 0;
|
|
stack_item *firstarg;
|
|
JSBool success = JS_TRUE;
|
|
JSJClassData *classData;
|
|
|
|
unsigned char pc[6];
|
|
cp_item_type constant_pool[10];
|
|
unsigned char cpt[10];
|
|
JSBool ok;
|
|
|
|
|
|
if (!ee) return JS_FALSE;
|
|
|
|
/* push the safety frame before the call frame */
|
|
if (!js_pushSafeFrame(cx, ee, &classData))
|
|
return JS_FALSE;
|
|
|
|
method_name = fieldname(&mb->fb);
|
|
method_signature = fieldsig(&mb->fb);
|
|
|
|
previous_frame = ee->current_frame;
|
|
if (previous_frame == 0) {
|
|
/* bottommost frame on this Exec Env. */
|
|
current_stack = ee->initial_stack;
|
|
current_frame = (JavaFrame *)(current_stack->data); /* no vars */
|
|
} else {
|
|
int args_size = mb->args_size;
|
|
current_stack = previous_frame->javastack; /* assume same stack */
|
|
if (previous_frame->current_method) {
|
|
int size = previous_frame->current_method->maxstack;
|
|
current_frame = (JavaFrame *)(&previous_frame->ostack[size]);
|
|
} else {
|
|
/* The only frames that don't have a mb are pseudo frames like
|
|
* this one and they don't really touch their stack. */
|
|
current_frame = (JavaFrame *)(previous_frame->optop + 3);
|
|
}
|
|
if (current_frame->ostack + args_size > current_stack->end_data) {
|
|
/* Ooops. The current stack isn't big enough. */
|
|
if (current_stack->next != 0) {
|
|
current_stack = current_stack->next;
|
|
} else {
|
|
current_stack = CreateNewJavaStack(ee, current_stack);
|
|
if (current_stack == 0) {
|
|
JS_ReportOutOfMemory(cx);
|
|
success = JS_FALSE;
|
|
goto done;
|
|
}
|
|
}
|
|
/* no vars */
|
|
current_frame = (JavaFrame *)(current_stack->data);
|
|
}
|
|
}
|
|
current_frame->prev = previous_frame;
|
|
current_frame->javastack = current_stack;
|
|
current_frame->optop = current_frame->ostack;
|
|
current_frame->vars = 0; /* better not reference any! */
|
|
current_frame->constant_pool = 0; /* Until we set it up */
|
|
current_frame->monitor = 0; /* not monitoring anything */
|
|
current_frame->annotation = 0;
|
|
current_frame->current_method = 0;
|
|
|
|
/*
|
|
* Allocate space for all the operands before they are actually
|
|
* converted, because conversion may need to use this stack.
|
|
*/
|
|
firstarg = current_frame->optop;
|
|
current_frame->optop += mb->args_size;
|
|
|
|
ee->current_frame = current_frame;
|
|
|
|
/* Push the target object, if not a static call. */
|
|
if (!isStaticCall)
|
|
(firstarg++)->p = obj;
|
|
|
|
/* Now convert the args into the space on the stack. */
|
|
if (!js_convertJSToJArgs(cx, firstarg, mb, argc, argv,
|
|
JS_FALSE /* actually convert */,
|
|
&sigRest, &cost)) {
|
|
/* the method shouldn't have matched if this was going to happen! */
|
|
JS_ReportError(cx, "internal error: argument conversion failed");
|
|
success = JS_FALSE;
|
|
goto done;
|
|
}
|
|
|
|
/* build the bytecodes and constant table for the call */
|
|
constant_pool[CONSTANT_POOL_TYPE_TABLE_INDEX].p = cpt;
|
|
cpt[0] = CONSTANT_POOL_ENTRY_RESOLVED;
|
|
|
|
pc[0] = isStaticCall ? opc_invokestatic_quick
|
|
: opc_invokenonvirtual_quick;
|
|
pc[1] = 0; pc[2] = 1; /* constant pool entry #1 */
|
|
pc[3] = opc_return;
|
|
|
|
constant_pool[1].p = mb;
|
|
cpt[1] = CONSTANT_POOL_ENTRY_RESOLVED | CONSTANT_Methodref;
|
|
|
|
current_frame->constant_pool = constant_pool;
|
|
|
|
/* Run the byte codes in java-land catch any exceptions. */
|
|
ee->exceptionKind = EXCKIND_NONE;
|
|
|
|
{
|
|
js_ExecuteJava_data data;
|
|
data.pc = (char*) pc;
|
|
js_CallJava(cx, js_ExecuteJava_stub, &data,
|
|
JS_FALSE /* we pushed the safety frame already */);
|
|
ok = data.ok;
|
|
}
|
|
|
|
if (ok) {
|
|
JSBool ret;
|
|
PR_LOG(MojaSrc, debug, ("method call succeeded\n"));
|
|
ret = java_returnAsJSValue(cx, rval, ee, sigRest);
|
|
if (!ret) {
|
|
JS_ReportError(cx,
|
|
"can't convert Java return value with signature %s",
|
|
sigRest);
|
|
success = JS_FALSE;
|
|
}
|
|
} else {
|
|
success = JS_FALSE;
|
|
}
|
|
|
|
done:
|
|
/* Our caller can look at ee->exceptionKind and ee->exception. */
|
|
ee->current_frame = previous_frame;
|
|
/* pop the safety frame */
|
|
js_popSafeFrame(cx, ee, classData);
|
|
|
|
/* FIXMEold cx->javaEnv = saved; */
|
|
|
|
return success;
|
|
}
|
|
|
|
static JSBool
|
|
js_javaMethodWrapper(JSContext *cx, JSObject *obj,
|
|
PRUintn argc, jsval *argv, jsval *rval)
|
|
{
|
|
JSFunction *fun;
|
|
const char *name;
|
|
JSJava *java;
|
|
ClassClass *cb;
|
|
struct methodblock *realmb;
|
|
JSBool success;
|
|
|
|
PR_ASSERT(JS_TypeOfValue(cx, argv[-2]) == JSTYPE_FUNCTION);
|
|
if (!JS_InstanceOf(cx, obj, &java_class, argv))
|
|
return JS_FALSE;
|
|
|
|
fun = JS_GetPrivate(cx, JSVAL_TO_OBJECT(argv[-2]));
|
|
name = JS_GetFunctionName(fun);
|
|
|
|
java = JS_GetPrivate(cx, obj);
|
|
cb = java->cb;
|
|
|
|
PR_LOG(MojaSrc, debug, ("entered methodwrap, fun=0x%x, name=\"%s\"(0x%x)",
|
|
fun, name, name));
|
|
|
|
/* match argc,argv against the signatures of the java methods */
|
|
realmb = matchMethod(cx, cb, java->type==JAVA_CLASS, name, argc, argv,
|
|
JS_TRUE /* reportErrors */);
|
|
|
|
if (!realmb) {
|
|
return JS_FALSE;
|
|
}
|
|
|
|
PR_LOG(MojaSrc, debug, ("calling %sjava method %s with signature %s",
|
|
realmb->fb.access & ACC_STATIC ? "static " : "",
|
|
realmb->fb.name, realmb->fb.signature));
|
|
|
|
success =
|
|
do_js_execute_java_method(cx,
|
|
realmb->fb.access & ACC_STATIC ? 0 : java->handle,
|
|
realmb,
|
|
realmb->fb.access & ACC_STATIC /* isStaticCall */,
|
|
argc, argv, rval);
|
|
|
|
return success;
|
|
}
|
|
|
|
|
|
static JSBool
|
|
js_javaConstructorWrapper(JSContext *cx, JSObject *obj,
|
|
PRUintn argc, jsval *argv,
|
|
jsval *rval)
|
|
{
|
|
JSObject *funobj;
|
|
ClassClass *cb;
|
|
struct methodblock *realmb;
|
|
HObject *ho;
|
|
JSBool success;
|
|
jsval tmp;
|
|
ExecEnv *ee;
|
|
|
|
PR_LOG(MojaSrc, debug, ("entered java constructor wrapper\n"));
|
|
|
|
PR_ASSERT(JS_TypeOfValue(cx, argv[-2]) == JSTYPE_FUNCTION);
|
|
funobj = JSVAL_TO_OBJECT(argv[-2]);
|
|
if (!JS_GetProperty(cx, funobj, "#javaClass", &tmp))
|
|
return JS_FALSE;
|
|
cb = JSVAL_TO_PRIVATE(tmp);
|
|
|
|
if (!JS_InstanceOf(cx, obj, &java_class, 0)
|
|
|| JS_GetPrivate(cx, obj)) {
|
|
/* not a fresh object! */
|
|
/* 3.0 allowed you to call a java constructor without using
|
|
* "new" so we do the same. setting obj to null causes
|
|
* ReflectJava to create a new js object rather than using
|
|
* the "this" argument of the constructor */
|
|
obj = NULL;
|
|
}
|
|
|
|
/* FIXME these are copied from interpreter.c without much understanding */
|
|
if (cbAccess(cb) & (ACC_INTERFACE | ACC_ABSTRACT)) {
|
|
JS_ReportError(cx, "can't instantiate Java class");
|
|
return JS_FALSE;
|
|
}
|
|
if (!VerifyClassAccess(0, cb, FALSE)) {
|
|
JS_ReportError(cx, "can't access Java class");
|
|
return JS_FALSE;
|
|
}
|
|
|
|
/* match argc,argv against the signatures of the java constructors */
|
|
realmb = matchMethod(cx, cb, JS_FALSE, "<init>", argc, argv,
|
|
JS_TRUE /* reportErrors */);
|
|
if (!realmb) {
|
|
return JS_FALSE;
|
|
}
|
|
|
|
if (!VerifyFieldAccess(0, fieldclass(&realmb->fb),
|
|
realmb->fb.access, FALSE)) {
|
|
JS_ReportError(cx, "illegal access to Java constructor");
|
|
return JS_FALSE;
|
|
}
|
|
|
|
PR_LOG(MojaSrc, debug, ("calling java constructor with signature %s",
|
|
realmb->fb.signature));
|
|
|
|
/*
|
|
* Because newobject can fail, and call SignalError, which calls
|
|
* FindClassFromClass, etc.
|
|
*/
|
|
/* Allocate the object */
|
|
ee = jsj_GetCurrentEE(cx);
|
|
if (!ee)
|
|
return JS_FALSE;
|
|
if ((ho = newobject(cb, 0, ee)) == 0) {
|
|
JS_ReportOutOfMemory(cx);
|
|
return JS_FALSE;
|
|
}
|
|
|
|
success = do_js_execute_java_method(cx, ho, realmb,
|
|
JS_FALSE /* isStaticCall */,
|
|
argc, argv,
|
|
&tmp /* gets set to void */);
|
|
|
|
/* FIXME the interpreter.c code calls cbDecRef(cb) at the end,
|
|
* why? should we? */
|
|
|
|
if (success) {
|
|
JSObject *jso = js_ReflectJava(cx, JAVA_OBJECT, ho, cb, 0, obj);
|
|
if (jso) {
|
|
*rval = OBJECT_TO_JSVAL(jso);
|
|
return JS_TRUE;
|
|
}
|
|
}
|
|
return JS_FALSE;
|
|
}
|
|
|
|
/* * * * * * * * * * * * * * * * * * *
|
|
*
|
|
* JS/java reflection tables
|
|
*
|
|
* * * * * * * * * * * * * * * * * * */
|
|
|
|
typedef struct ScanArgs {
|
|
GCInfo *gcInfo;
|
|
} ScanArgs;
|
|
|
|
PR_STATIC_CALLBACK (int)
|
|
scanJSJavaReflectionEntry(PRHashEntry *he, int i, void *arg)
|
|
{
|
|
ScanArgs *args = arg;
|
|
JSObject *jso = he->value;
|
|
|
|
/* scan the handle */
|
|
if (1 /* jso != JSO_REFLECTION_IN_PROGRESS */) {
|
|
/* XXX - fur - temporary change to allow compilation
|
|
JSJava *java = JSVAL_TO_PRIVATE(OBJ_GET_SLOT(jso, JSSLOT_PRIVATE)); */
|
|
JSJava *java = JSVAL_TO_PRIVATE(jso->slots[JSSLOT_PRIVATE]);
|
|
|
|
args->gcInfo->livePointer((void *)java->handle);
|
|
}
|
|
|
|
return HT_ENUMERATE_NEXT;
|
|
}
|
|
|
|
PR_STATIC_CALLBACK (void)
|
|
scanJSJavaReflections(void *runtime)
|
|
{
|
|
ScanArgs args;
|
|
|
|
/* PR_PROCESS_ROOT_LOG(("Scanning JS reflections of java objects")); */
|
|
|
|
args.gcInfo = PR_GetGCInfo();
|
|
|
|
/* FIXME this is kind of scary long-term access inside the
|
|
* monitor - is there any alternative? */
|
|
#ifndef NSPR20
|
|
PR_EnterMonitor(javaReflectionsMonitor);
|
|
#endif
|
|
if (javaReflections) {
|
|
PR_HashTableEnumerateEntries(javaReflections,
|
|
scanJSJavaReflectionEntry,
|
|
&args);
|
|
}
|
|
#ifndef NSPR20
|
|
PR_ExitMonitor(javaReflectionsMonitor);
|
|
#endif
|
|
}
|
|
|
|
#ifdef NSPR20
|
|
void PR_CALLBACK PrepareJSLocksForGC(GCLockHookArg arg1)
|
|
{
|
|
PR_ASSERT(arg1 == PR_GCBEGIN || arg1 == PR_GCEND);
|
|
|
|
if (arg1 == PR_GCBEGIN)
|
|
PR_EnterMonitor(javaReflectionsMonitor);
|
|
else
|
|
PR_ExitMonitor(javaReflectionsMonitor);
|
|
}
|
|
#endif
|
|
|
|
static JSObject *
|
|
js_ReflectJava(JSContext *cx, JSJavaType type, HObject *handle,
|
|
ClassClass *cb, char *sig, JSObject *useMe)
|
|
{
|
|
JSObject *mo;
|
|
JSJava *java;
|
|
void *key;
|
|
PRHashEntry *he;
|
|
|
|
PR_EnterMonitor(javaReflectionsMonitor);
|
|
if (!javaReflections) goto fail;
|
|
|
|
/* see if it's already been reflected */
|
|
switch (type) {
|
|
case JAVA_CLASS:
|
|
key = (void *) cbName(cb);
|
|
break;
|
|
case JAVA_OBJECT:
|
|
key = (void *) handle;
|
|
break;
|
|
case JAVA_ARRAY:
|
|
key = (void *) handle;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
mo = PR_HashTableLookup(javaReflections, key);
|
|
if (mo)
|
|
goto succeed;
|
|
|
|
/* if we got this far, it isn't in the hash table.
|
|
* if useMe is non-null, we were called via "new" and should
|
|
* use the object that was already constructed if possible */
|
|
if (useMe) {
|
|
/* make sure we have the right js class and no private
|
|
* data yet. if not, ignore the provided object */
|
|
if (JS_GetPrivate(cx, useMe))
|
|
useMe = NULL;
|
|
else {
|
|
JSClass *clasp = JS_GetClass(useMe);
|
|
switch (type) {
|
|
case JAVA_CLASS:
|
|
case JAVA_OBJECT:
|
|
if (clasp != &java_class)
|
|
useMe = NULL;
|
|
break;
|
|
case JAVA_ARRAY:
|
|
if (clasp != &javaarray_class)
|
|
useMe = NULL;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
mo = useMe;
|
|
|
|
/* build the private data for the js reflection */
|
|
java = JS_malloc(cx, sizeof(JSJava));
|
|
if (!java)
|
|
goto fail;
|
|
java->type = type;
|
|
java->handle = handle;
|
|
java->cb = cb;
|
|
java->signature = sig;
|
|
|
|
switch (type) {
|
|
case JAVA_CLASS:
|
|
case JAVA_OBJECT:
|
|
if (!mo)
|
|
mo = JS_NewObject(cx, &java_class, 0, 0);
|
|
if (!mo || ! JS_SetPrivate(cx, mo, java))
|
|
goto fail_free_java;
|
|
break;
|
|
case JAVA_ARRAY:
|
|
if (!mo)
|
|
mo = JS_NewObject(cx, &javaarray_class, 0, 0);
|
|
if (!mo || ! JS_SetPrivate(cx, mo, java))
|
|
goto fail_free_java;
|
|
if (! JS_DefineProperty(cx, mo, "length",
|
|
INT_TO_JSVAL((jsint)obj_length(handle)),
|
|
0, 0, JSPROP_READONLY))
|
|
goto fail;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
/* add it to the table */
|
|
he = PR_HashTableAdd(javaReflections, key, mo);
|
|
if (!he) {
|
|
JS_ReportOutOfMemory(cx);
|
|
mo = 0;
|
|
}
|
|
|
|
succeed:
|
|
PR_ExitMonitor(javaReflectionsMonitor);
|
|
return mo;
|
|
|
|
fail_free_java:
|
|
JS_free(cx, java);
|
|
fail:
|
|
PR_ExitMonitor(javaReflectionsMonitor);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Get the element type for a java array. *classp will be set to null
|
|
* if it's not of object type.
|
|
*/
|
|
static JSBool
|
|
js_JArrayElementType(HObject *handle, char **sig, ClassClass **classp)
|
|
{
|
|
/* figure out the signature from the type */
|
|
unsigned long elementtype = obj_flags(handle);
|
|
char *elementsig;
|
|
|
|
*sig = 0;
|
|
*classp = 0;
|
|
|
|
switch (elementtype) {
|
|
case T_CLASS:
|
|
*classp = (ClassClass*)
|
|
unhand((HArrayOfObject *)handle)->body[class_offset(handle)];
|
|
elementsig = SIGNATURE_CLASS_STRING;
|
|
break;
|
|
case T_BOOLEAN:
|
|
elementsig = SIGNATURE_BOOLEAN_STRING;
|
|
break;
|
|
case T_CHAR:
|
|
elementsig = SIGNATURE_CHAR_STRING;
|
|
break;
|
|
case T_FLOAT:
|
|
elementsig = SIGNATURE_FLOAT_STRING;
|
|
break;
|
|
case T_DOUBLE:
|
|
elementsig = SIGNATURE_DOUBLE_STRING;
|
|
break;
|
|
case T_BYTE:
|
|
elementsig = SIGNATURE_BYTE_STRING;
|
|
break;
|
|
case T_SHORT:
|
|
elementsig = SIGNATURE_SHORT_STRING;
|
|
break;
|
|
case T_INT:
|
|
elementsig = SIGNATURE_INT_STRING;
|
|
break;
|
|
case T_LONG:
|
|
elementsig = SIGNATURE_LONG_STRING;
|
|
break;
|
|
default:
|
|
PR_ASSERT(0);
|
|
return JS_FALSE;
|
|
}
|
|
|
|
*sig = elementsig;
|
|
return JS_TRUE;
|
|
}
|
|
|
|
PR_IMPLEMENT(JSObject *)
|
|
js_ReflectJObjectToJSObject(JSContext *cx, HObject *handle)
|
|
{
|
|
JSObject *mo = 0;
|
|
ExecEnv *ee;
|
|
|
|
/* force initialization */
|
|
ee = jsj_GetCurrentEE(cx);
|
|
if (!ee) return 0;
|
|
|
|
if (handle) {
|
|
if (obj_flags(handle)) {
|
|
char *elementSig;
|
|
ClassClass *elementClazz;
|
|
|
|
if (!js_JArrayElementType(handle, &elementSig, &elementClazz)) {
|
|
/* FIXMEbe report an error! */
|
|
return 0;
|
|
}
|
|
|
|
mo = js_ReflectJava(cx, JAVA_ARRAY, handle,
|
|
elementClazz, elementSig, 0);
|
|
PR_LOG(MojaSrc, debug, ("reflected array[%s] 0x%x as JSObject* 0x%x",
|
|
elementSig, handle, mo));
|
|
|
|
} else {
|
|
mo = js_ReflectJava(cx, JAVA_OBJECT, handle,
|
|
obj_classblock(handle), 0, 0);
|
|
PR_LOG(MojaSrc, debug, ("reflected HObject* 0x%x as JSObject* 0x%x",
|
|
handle, mo));
|
|
}
|
|
}
|
|
|
|
return mo;
|
|
}
|
|
|
|
JSObject *
|
|
js_ReflectJClassToJSObject(JSContext *cx, ClassClass *cb)
|
|
{
|
|
ExecEnv *ee;
|
|
JSObject *mo;
|
|
|
|
/* force initialization */
|
|
ee = jsj_GetCurrentEE(cx);
|
|
if (!ee) return 0;
|
|
|
|
mo = js_ReflectJava(cx, JAVA_CLASS, (HObject *) cbHandle(cb), cb, 0, 0);
|
|
|
|
PR_LOG(MojaSrc, debug, ("reflected ClassClass* 0x%x as JSObject* 0x%x",
|
|
cb, mo));
|
|
|
|
return mo;
|
|
}
|
|
|
|
/* * * * * * * * * */
|
|
|
|
/*
|
|
* JSJavaSlot is a java slot which will be resolved as a method
|
|
* or a field depending on context.
|
|
*/
|
|
struct JSJavaSlot {
|
|
JSObject *obj; /* the object or class reflection */
|
|
jsval value; /* the field value when created */
|
|
JSString *name; /* name of the field or method */
|
|
struct fieldblock *fb; /* fieldblock if there is a field */
|
|
};
|
|
|
|
/* none of these should ever be called, since javaslot_convert will
|
|
* first turn the slot into the underlying object if there is one */
|
|
PR_STATIC_CALLBACK(JSBool)
|
|
javaslot_getProperty(JSContext *cx, JSObject *obj, jsval slot,
|
|
jsval *vp)
|
|
{
|
|
/* evil so that the assign hack doesn't kill us */
|
|
if (slot == STRING_TO_JSVAL(ATOM_TO_STRING(cx->runtime->atomState.assignAtom))) {
|
|
*vp = JSVAL_VOID;
|
|
return JS_TRUE;
|
|
}
|
|
|
|
JS_ReportError(cx, "Java slots have no properties");
|
|
return JS_FALSE;
|
|
}
|
|
|
|
PR_STATIC_CALLBACK(JSBool)
|
|
javaslot_setProperty(JSContext *cx, JSObject *obj, jsval slot,
|
|
jsval *vp)
|
|
{
|
|
JS_ReportError(cx, "Java slots have no properties");
|
|
return JS_FALSE;
|
|
}
|
|
|
|
PR_STATIC_CALLBACK(JSBool)
|
|
javaslot_resolve(JSContext *cx, JSObject *obj, jsval id)
|
|
{
|
|
return JS_TRUE;
|
|
}
|
|
|
|
PR_STATIC_CALLBACK(void)
|
|
javaslot_finalize(JSContext *cx, JSObject *obj)
|
|
{
|
|
JSJavaSlot *slot = JS_GetPrivate(cx, obj);
|
|
|
|
/* the value may be holding a reference to something... */
|
|
JS_RemoveRoot(cx, &slot->value);
|
|
|
|
/* drop the object of which this is a slot */
|
|
JS_RemoveRoot(cx, &slot->obj);
|
|
|
|
/* drop the name */
|
|
JS_UnlockGCThing(cx, slot->name);
|
|
|
|
JS_free(cx, slot);
|
|
|
|
PR_LOG(MojaSrc, debug, ("finalizing JSJavaSlot 0x%x", obj));
|
|
}
|
|
|
|
PR_STATIC_CALLBACK(JSBool)
|
|
javaslot_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp)
|
|
{
|
|
JSJavaSlot *slot = JS_GetPrivate(cx, obj);
|
|
const char *name;
|
|
JSFunction *fun;
|
|
JSString *str;
|
|
|
|
name = JS_GetStringBytes(slot->name);
|
|
switch (type) {
|
|
case JSTYPE_FUNCTION:
|
|
fun = JS_NewFunction(cx, js_javaMethodWrapper, 0, JSFUN_BOUND_METHOD,
|
|
slot->obj, name);
|
|
if (!fun)
|
|
return JS_FALSE;
|
|
|
|
PR_LOG(MojaSrc, debug,
|
|
("converted slot to function 0x%x with name %s\n",
|
|
fun, name));
|
|
|
|
*vp = OBJECT_TO_JSVAL(JS_GetFunctionObject(fun));
|
|
return JS_TRUE;
|
|
|
|
case JSTYPE_OBJECT:
|
|
PR_LOG(MojaSrc, debug, ("converting java slot 0x%x to object", obj));
|
|
/* FALL THROUGH */
|
|
case JSTYPE_NUMBER:
|
|
case JSTYPE_BOOLEAN:
|
|
case JSTYPE_STRING:
|
|
if (slot->fb) {
|
|
return JS_ConvertValue(cx, slot->value, type, vp);
|
|
}
|
|
if (type == JSTYPE_STRING) {
|
|
JSJava *java = JS_GetPrivate(cx, slot->obj);
|
|
char *cstr, *cp;
|
|
|
|
PR_ASSERT(java->type == JAVA_OBJECT || java->type == JAVA_CLASS);
|
|
cstr = PR_smprintf("[JavaMethod %s.%s]", cbName(java->cb), name);
|
|
if (!cstr) {
|
|
JS_ReportOutOfMemory(cx);
|
|
return JS_FALSE;
|
|
}
|
|
for (cp = cstr; *cp != '\0'; cp++)
|
|
if (*cp == '/')
|
|
*cp = '.';
|
|
str = JS_NewString(cx, cstr, strlen(cstr));
|
|
if (!str) {
|
|
free(cstr);
|
|
return JS_FALSE;
|
|
}
|
|
*vp = STRING_TO_JSVAL(str);
|
|
return JS_TRUE;
|
|
}
|
|
if (type != JSTYPE_OBJECT) {
|
|
JS_ReportError(cx, "no field with name \"%s\"", name);
|
|
return JS_FALSE;
|
|
}
|
|
*vp = OBJECT_TO_JSVAL(obj);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return JS_TRUE;
|
|
}
|
|
|
|
static JSClass javaslot_class = {
|
|
"JavaSlot",
|
|
JSCLASS_HAS_PRIVATE,
|
|
JS_PropertyStub, JS_PropertyStub,
|
|
javaslot_getProperty, javaslot_setProperty, JS_EnumerateStub,
|
|
javaslot_resolve, javaslot_convert, javaslot_finalize
|
|
};
|
|
|
|
static JSBool
|
|
js_reflectJavaSlot(JSContext *cx, JSObject *obj, JSString *str,
|
|
jsval *vp)
|
|
{
|
|
JSJava *java = JS_GetPrivate(cx, obj);
|
|
ClassClass *cb = java->cb;
|
|
struct fieldblock *fb;
|
|
struct methodblock *mb;
|
|
JSJavaSlot *slot;
|
|
JSObject *mo;
|
|
const char *name = JS_GetStringBytes(str);
|
|
|
|
*vp = JSVAL_VOID;
|
|
|
|
/* PR_ASSERT(obj->clazz == &java_class); */
|
|
|
|
/* if there's a field get its value at reflection time */
|
|
fb = java_lookup_field(cx, cb, java->type == JAVA_CLASS, name);
|
|
if (fb) {
|
|
if (!js_convertJFieldToJSValue(cx, java->handle, fb,
|
|
vp, JSTYPE_VOID)) {
|
|
/*
|
|
* If this happens, the field had a value that couldn't
|
|
* be represented in JS.
|
|
*/
|
|
PR_LOG(MojaSrc, error, ("looking up initial field value failed!"));
|
|
|
|
/* FIXME should really set a flag that will cause an error
|
|
* only if the slot is accessed as a field. for now we
|
|
* make it look like there wasn't any field by that name,
|
|
* which is less informative */
|
|
fb = 0;
|
|
}
|
|
}
|
|
|
|
#ifndef REFLECT_ALL_SLOTS_LAZILY
|
|
/* match the name against the signatures of the java methods */
|
|
mb = matchMethod(cx, cb, java->type==JAVA_CLASS, name, -1, 0,
|
|
JS_FALSE /* reportErrors */);
|
|
|
|
if (!fb) {
|
|
JSFunction *fun;
|
|
|
|
if (!mb) {
|
|
/* nothing by that name, report an error */
|
|
JS_ReportError(cx, "Java object has no field or method named %s",
|
|
name);
|
|
return JS_FALSE;
|
|
}
|
|
|
|
/* if we get here, there's a method but no field by this name */
|
|
fun = JS_NewFunction(cx, js_javaMethodWrapper, 0,
|
|
JSFUN_BOUND_METHOD, obj, name);
|
|
if (!fun)
|
|
return JS_FALSE;
|
|
|
|
PR_LOG(MojaSrc, debug,
|
|
("eagerly converted slot to function 0x%x with name %s(0x%x)\n",
|
|
fun, name, name));
|
|
|
|
*vp = OBJECT_TO_JSVAL(JS_GetFunctionObject(fun));
|
|
return JS_TRUE;
|
|
} else if (!mb) {
|
|
/* there's a field but no method by this name */
|
|
/* looking up the field already set *vp */
|
|
return JS_TRUE;
|
|
}
|
|
|
|
/* if we get here there are both fields and methods by this name,
|
|
* so we create a slot object to delay the binding */
|
|
#endif
|
|
|
|
slot = (JSJavaSlot *) JS_malloc(cx, sizeof(JSJavaSlot));
|
|
if (!slot) {
|
|
return JS_FALSE;
|
|
}
|
|
|
|
/* corresponding removes and unlocks are in javaslot_finalize */
|
|
slot->obj = obj;
|
|
if (!JS_AddRoot(cx, &slot->obj)) {
|
|
JS_free(cx, slot);
|
|
return JS_FALSE;
|
|
}
|
|
slot->value = *vp;
|
|
if (!JS_AddRoot(cx, &slot->value)) {
|
|
JS_RemoveRoot(cx, &slot->obj);
|
|
JS_free(cx, slot);
|
|
return JS_FALSE;
|
|
}
|
|
|
|
/* FIXME check return value? */
|
|
JS_LockGCThing(cx, str);
|
|
slot->name = str;
|
|
slot->fb = fb;
|
|
|
|
mo = JS_NewObject(cx, &javaslot_class, 0, 0);
|
|
JS_SetPrivate(cx, mo, slot);
|
|
|
|
PR_LOG(MojaSrc, debug, ("reflected slot %s of 0x%x as 0x%x",
|
|
name, java->handle, mo));
|
|
if (!mo) {
|
|
JS_RemoveRoot(cx, &slot->obj);
|
|
JS_RemoveRoot(cx, &slot->value);
|
|
JS_UnlockGCThing(cx, slot->name);
|
|
JS_free(cx, slot);
|
|
return JS_FALSE;
|
|
}
|
|
*vp = OBJECT_TO_JSVAL(mo);
|
|
return JS_TRUE;
|
|
}
|
|
|
|
/***********************************************************************/
|
|
|
|
/* a saved JS error state */
|
|
typedef struct SavedJSError SavedJSError;
|
|
struct SavedJSError {
|
|
char *message;
|
|
JSErrorReport report;
|
|
SavedJSError *next;
|
|
};
|
|
|
|
/*
|
|
* capture a JS error that occurred in JS code called by java.
|
|
* makes a copy of the JS error data and hangs it off the JS
|
|
* environment. when the JS code returns, this is checked and
|
|
* used to generate a JSException. if the JSException is uncaught
|
|
* and makes it up to another layer of JS, the error will be
|
|
* reinstated with JS_ReportError
|
|
*/
|
|
PR_IMPLEMENT(void)
|
|
js_JavaErrorReporter(JSContext *cx, const char *message, JSErrorReport *report)
|
|
{
|
|
/* save the error state */
|
|
SavedJSError *newerr;
|
|
newerr = malloc(sizeof(SavedJSError));
|
|
if (!newerr) {
|
|
/* FIXME not much we can do here, abort? */
|
|
return;
|
|
}
|
|
newerr->message = 0;
|
|
if (message) {
|
|
newerr->message = strdup(message);
|
|
if (!newerr->message) {
|
|
/* FIXME not much we can do here, abort? */
|
|
free(newerr);
|
|
return;
|
|
}
|
|
}
|
|
newerr->report.filename = 0;
|
|
newerr->report.lineno = 0;
|
|
newerr->report.linebuf = 0;
|
|
newerr->report.tokenptr = 0;
|
|
|
|
if (report) {
|
|
if (report->filename) {
|
|
newerr->report.filename = strdup(report->filename);
|
|
if (!newerr->report.filename) {
|
|
/* FIXME not much we can do here, abort? */
|
|
free(newerr->message);
|
|
free(newerr);
|
|
return;
|
|
}
|
|
}
|
|
newerr->report.lineno = report->lineno;
|
|
|
|
if (report->linebuf) {
|
|
newerr->report.linebuf = strdup(report->linebuf);
|
|
if (!newerr->report.linebuf) {
|
|
/* FIXME not much we can do here, abort? */
|
|
free((void*)newerr->report.filename);
|
|
free(newerr->message);
|
|
free(newerr);
|
|
return;
|
|
}
|
|
newerr->report.tokenptr = newerr->report.linebuf +
|
|
(report->tokenptr - report->linebuf);
|
|
}
|
|
}
|
|
|
|
/* push this error */
|
|
newerr->next = cx->savedErrors;
|
|
cx->savedErrors = newerr;
|
|
}
|
|
|
|
static SavedJSError *
|
|
js_js_FreeError(SavedJSError *err)
|
|
{
|
|
SavedJSError *next = err->next;
|
|
|
|
free(err->message);
|
|
free((char*)err->report.filename);/*FIXME*/
|
|
free((char*)err->report.linebuf);
|
|
free(err);
|
|
|
|
return next;
|
|
}
|
|
|
|
PR_IMPLEMENT(void)
|
|
jsj_ClearSavedErrors(JSContext *cx)
|
|
{
|
|
while (cx->savedErrors)
|
|
cx->savedErrors = js_js_FreeError(cx->savedErrors);
|
|
}
|
|
|
|
/* this is called upon returning from JS to java. one possibility
|
|
* is that the JS error was actually triggered by java at some point -
|
|
* if so we throw the original java exception. otherwise, each JS
|
|
* error will have pushed something on JSContext->savedErrors, so
|
|
* we convert them all to a string and throw a JSException with that
|
|
* info.
|
|
*/
|
|
PR_IMPLEMENT(void)
|
|
js_JSErrorToJException(JSContext *cx, ExecEnv *ee)
|
|
{
|
|
SavedJSError *err = 0;
|
|
|
|
if (!cx->savedErrors) {
|
|
exceptionClear(ee);
|
|
PR_LOG(MojaSrc, debug,
|
|
("j-m succeeded with no exception cx=0x%x ee=0x%x", cx, ee));
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* If there's a pending exception in the java env, assume it
|
|
* needs to be propagated (since JS couldn't have caught it
|
|
* and done something with it).
|
|
*/
|
|
if (exceptionOccurred(ee)) {
|
|
PR_LOG(MojaSrc, debug,
|
|
("j-m propagated exception through JS cx=0x%x ee=0x%x",
|
|
cx, ee));
|
|
return; /* propagating is easy! */
|
|
}
|
|
|
|
/* otherwise, throw a JSException */
|
|
/* get the message from the deepest saved JS error */
|
|
err = cx->savedErrors;
|
|
if (err) {
|
|
while (err->next)
|
|
err = err->next;
|
|
}
|
|
|
|
/* propagate any pending JS errors upward with a java exception */
|
|
{
|
|
JRIEnv *env = (JRIEnv*) ee;
|
|
struct java_lang_String* message =
|
|
JRI_NewStringUTF(env, err->message,
|
|
strlen(err->message));
|
|
struct java_lang_String* filename = err->report.filename
|
|
? JRI_NewStringUTF(env, err->report.filename,
|
|
strlen(err->report.filename))
|
|
: NULL;
|
|
int lineno = err->report.lineno;
|
|
struct java_lang_String* source = err->report.linebuf
|
|
? JRI_NewStringUTF(env, err->report.linebuf,
|
|
strlen(err->report.linebuf))
|
|
: NULL;
|
|
int index = err->report.linebuf
|
|
? err->report.tokenptr - err->report.linebuf
|
|
: 0;
|
|
jref exc = (jref)
|
|
execute_java_constructor((ExecEnv *)env,
|
|
NULL, JSExceptionClassBlock,
|
|
"(Ljava/lang/String;Ljava/lang/String;ILjava/lang/String;I)",
|
|
message, filename, (int32_t)lineno, source, (int32_t)index);
|
|
|
|
exceptionThrow(ee, (HObject *)exc);
|
|
PR_LOG(MojaSrc, debug,
|
|
("j-m raised JSException \"%s\" cx=0x%x ee=0x%x",
|
|
err->message, cx, ee));
|
|
}
|
|
}
|
|
|
|
static JSBool
|
|
js_isJSException(ExecEnv *ee, HObject *exc)
|
|
{
|
|
return strcmp(cbName(obj_array_classblock(exc)),
|
|
"netscape/javascript/JSException")
|
|
? JS_TRUE : JS_FALSE;
|
|
}
|
|
|
|
/*
|
|
* This is called after returning from java to JS. if the exception
|
|
* is actually a JSException, we pull the original JS error state out
|
|
* of the JSContext and use that. Otherwise we turn the JSException
|
|
* into a string and pass it up as a JS error
|
|
*/
|
|
static JSBool
|
|
js_JExceptionToJSError(JSContext *cx, ExecEnv *ee)
|
|
{
|
|
SavedJSError *err = 0;
|
|
char *message;
|
|
JSBool success;
|
|
JHandle *exc;
|
|
|
|
/*
|
|
* Get rid of any JS errors so far, but save the deepest one
|
|
* in case this was a JSException and we re-report it.
|
|
*/
|
|
/* FIXME the deepest one is the most interesting? */
|
|
err = cx->savedErrors;
|
|
if (err) {
|
|
while (err->next)
|
|
err = js_js_FreeError(err);
|
|
}
|
|
|
|
/* if no exception reached us, continue on our merry way */
|
|
if (!exceptionOccurred(ee)) {
|
|
PR_LOG(MojaSrc, debug,
|
|
("m-j succeeded, no exceptions cx=0x%x ee=0x%x",
|
|
cx, ee));
|
|
success = JS_TRUE;
|
|
goto done;
|
|
}
|
|
|
|
/* if we got this far there was an error for sure */
|
|
success = JS_FALSE;
|
|
|
|
switch (ee->exceptionKind) {
|
|
case EXCKIND_THROW:
|
|
/* save the exception while we poke around in java */
|
|
exc = ee->exception.exc;
|
|
exceptionClear(ee);
|
|
|
|
if (err && js_isJSException(ee, exc)) {
|
|
js_ReportErrorAgain(cx, err->message, &err->report);
|
|
PR_LOG(MojaSrc, debug,
|
|
("m-j re-reported error \"%s\" cx=0x%x ee=0x%x",
|
|
err->message, cx, ee));
|
|
}
|
|
/* otherwise, describe the exception to a string */
|
|
else if (js_isSubclassOf(cx,
|
|
obj_array_classblock(exc),
|
|
ThrowableClassBlock)) {
|
|
HString *hdetail =
|
|
unhand((Hjava_lang_Throwable *)exc)->detailMessage;
|
|
ClassClass *cb = obj_array_classblock(exc);
|
|
|
|
message = (char *)
|
|
JRI_GetStringPlatformChars((JRIEnv *) ee,
|
|
(struct java_lang_String *) hdetail,
|
|
NULL, 0);
|
|
|
|
/* XXX - temporarily replace arguments so we can compile
|
|
(const jbyte *) cx->charSetName,
|
|
(jint) cx->charSetNameLength);
|
|
*/
|
|
|
|
PR_LOG(MojaSrc, debug,
|
|
("m-j converted exception %s, \"%s\" to error cx=0x%x ee=0x%x",
|
|
cbName(cb), message, cx, ee));
|
|
/* pass the string to JS_ReportError */
|
|
JS_ReportError(cx, "uncaught Java exception %s (\"%s\")",
|
|
cbName(cb), message);
|
|
}
|
|
|
|
/* it's not a Throwable, somebody in java-land is being lame */
|
|
else {
|
|
ClassClass *cb = obj_array_classblock(exc);
|
|
JS_ReportError(cx, "uncaught Java exception of class %s",
|
|
cbName(cb));
|
|
PR_LOG(MojaSrc, debug,
|
|
("m-j converted exception %s to error cx=0x%x ee=0x%x",
|
|
cbName(cb), cx, ee));
|
|
}
|
|
break;
|
|
case EXCKIND_STKOVRFLW:
|
|
JS_ReportError(cx, "Java stack overflow, pc=0x%x", ee->exception.addr);
|
|
break;
|
|
default:
|
|
JS_ReportError(cx,
|
|
"internal error: Java exception of unknown type %d",
|
|
ee->exceptionKind);
|
|
break;
|
|
}
|
|
|
|
done:
|
|
if (err) {
|
|
js_js_FreeError(err);
|
|
cx->savedErrors = 0;
|
|
}
|
|
PR_LOG(MojaSrc, debug, ("m-j cleared JS errors cx=0x%x", cx));
|
|
return success;
|
|
}
|
|
|
|
/***********************************************************************/
|
|
|
|
static JSBool
|
|
js_FindSystemClass(JSContext *cx, char *name, ClassClass **clazz)
|
|
{
|
|
ExecEnv *ee;
|
|
|
|
ee = jsj_GetCurrentEE(cx);
|
|
|
|
if (!ee) return JS_FALSE;
|
|
|
|
*clazz = FindClassFromClass(ee, name, TRUE, NULL);
|
|
|
|
return js_JExceptionToJSError(cx, ee);
|
|
}
|
|
|
|
/*
|
|
* All js_CallJava calls must use a data pointer that starts like
|
|
* this one:
|
|
*/
|
|
typedef struct {
|
|
JRIEnv *env;
|
|
} js_CallJava_data;
|
|
|
|
static JSBool
|
|
js_CallJava(JSContext *cx, JSJCallback doit, void *d, JSBool pushSafeFrame)
|
|
{
|
|
js_CallJava_data *data = d;
|
|
JSBool success = JS_TRUE;
|
|
ExecEnv *ee;
|
|
JSJClassData *classData;
|
|
|
|
ee = jsj_GetCurrentEE(cx);
|
|
|
|
if (!ee) return JS_FALSE;
|
|
|
|
data->env = (JRIEnv *) ee;
|
|
|
|
/* security: push the safety frame onto the java stack */
|
|
if (pushSafeFrame)
|
|
if (!js_pushSafeFrame(cx, ee, &classData)) {
|
|
return JS_FALSE;
|
|
}
|
|
|
|
PR_LOG_BEGIN(MojaSrc, debug, ("entering java ee=0x%x cx=0x%x", ee, cx));
|
|
/* FIXME this should be restructured: there could be a prolog and
|
|
* epilog to set up and tear down the JS->java call stuff */
|
|
doit(data);
|
|
|
|
PR_LOG_END(MojaSrc, debug, ("left java ee=0x%x cx=0x%x", ee, cx));
|
|
|
|
/* it's only safe to call this on the mozilla thread */
|
|
success = js_JExceptionToJSError(cx, ee);
|
|
|
|
/* pop the safety frame */
|
|
if (pushSafeFrame)
|
|
js_popSafeFrame(cx, ee, classData);
|
|
|
|
return success;
|
|
}
|
|
|
|
|
|
/***********************************************************************/
|
|
|
|
typedef struct {
|
|
JRIEnv *env;
|
|
HObject *self;
|
|
char *name;
|
|
char *sig;
|
|
struct methodblock *mb;
|
|
JSBool isStaticCall;
|
|
va_list args;
|
|
long *raddr;
|
|
size_t rsize;
|
|
} js_ExecuteJavaMethod_data;
|
|
|
|
static void
|
|
js_ExecuteJavaMethod_stub(void *d)
|
|
{
|
|
js_ExecuteJavaMethod_data *data = (js_ExecuteJavaMethod_data *) d;
|
|
|
|
data->raddr[0] =
|
|
do_execute_java_method_vararg(/*FIXME*/(ExecEnv*)data->env,
|
|
data->self,
|
|
data->name, data->sig,
|
|
data->mb, (bool_t) data->isStaticCall,
|
|
data->args,
|
|
(data->rsize > sizeof(long))
|
|
? &data->raddr[1] : NULL,
|
|
FALSE);
|
|
}
|
|
|
|
static JSBool
|
|
js_ExecuteJavaMethod(JSContext *cx, void *raddr, size_t rsize,
|
|
HObject *ho, char *name, char *sig,
|
|
struct methodblock *mb, JSBool isStaticCall, ...)
|
|
{
|
|
js_ExecuteJavaMethod_data data;
|
|
JSBool success;
|
|
va_list args;
|
|
|
|
#if defined(XP_MAC) && !defined(NSPR20)
|
|
/* Metrowerks va_start() doesn't handle one-byte parameters properly. FIX this when va_start() works again. */
|
|
args = &isStaticCall+1;
|
|
#else
|
|
va_start(args, isStaticCall);
|
|
#endif
|
|
|
|
data.self = ho;
|
|
data.name = name;
|
|
data.sig = sig;
|
|
data.mb = mb;
|
|
data.isStaticCall = isStaticCall;
|
|
VARARGS_ASSIGN(data.args, args);
|
|
data.raddr = raddr;
|
|
data.rsize = rsize;
|
|
|
|
success = js_CallJava(cx, js_ExecuteJavaMethod_stub, &data, JS_TRUE);
|
|
va_end(args);
|
|
|
|
return success;
|
|
}
|
|
|
|
/***********************************************************************/
|
|
|
|
typedef struct {
|
|
JRIEnv *env;
|
|
JSContext *cx;
|
|
char *name;
|
|
ClassClass *fromclass;
|
|
ClassClass *ret;
|
|
char *errstr;
|
|
} js_FindJavaClass_data;
|
|
|
|
static void
|
|
js_FindJavaClass_stub(void *d)
|
|
{
|
|
js_FindJavaClass_data *data = d;
|
|
ExecEnv *ee = /*FIXME*/(ExecEnv*)data->env;
|
|
|
|
exceptionClear(ee);
|
|
|
|
/* FIXME need to push a stack frame with the classloader
|
|
* of data->fromclass or the security check on opening
|
|
* the url for the class will fail */
|
|
|
|
data->ret = FindClassFromClass(ee, data->name, TRUE, data->fromclass);
|
|
|
|
/* we clear the exception state, because when JS
|
|
* fails to find a class it assumes it's a package instead
|
|
* of an error. */
|
|
/* FIXME can we report an error if the problem is accessing
|
|
* bogus-codebase? */
|
|
|
|
if (exceptionOccurred(ee)) {
|
|
ClassClass *cb = obj_array_classblock(ee->exception.exc);
|
|
|
|
#ifdef DEBUG
|
|
char *message;
|
|
/* FIXME this could fail: we don't check if it's Throwable,
|
|
* but i assume that FindClass is well-behaved */
|
|
HString *hdetail =
|
|
unhand((Hjava_lang_Throwable *)
|
|
ee->exception.exc)->detailMessage;
|
|
|
|
message = (char *)
|
|
JRI_GetStringPlatformChars((JRIEnv *) ee,
|
|
(struct java_lang_String *) hdetail,
|
|
NULL, 0);
|
|
|
|
/* XXX - temporarily replace arguments so we can compile
|
|
(const jbyte *) cx->charSetName,
|
|
(jint) cx->charSetNameLength);
|
|
*/
|
|
|
|
PR_LOG(MojaSrc, debug,
|
|
("exception in is_subclass_of %s (\"%s\")",
|
|
cbName(cb), message));
|
|
#endif
|
|
|
|
#ifdef DEBUG_JSJ
|
|
/* take a look at the exception to see if we can narrow
|
|
* down the kinds of failures that cause a package to
|
|
* be created? */
|
|
exceptionDescribe(ee);
|
|
#endif
|
|
/* FIXME other exceptions don't matter? narrow this down... */
|
|
exceptionClear(ee);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* This can call arbitrary java code in class initialization.
|
|
* pass 0 for the "from" argument for system classes, but if
|
|
* you want to check the applet first pass its class.
|
|
*/
|
|
static ClassClass *
|
|
js_FindJavaClass(JSContext *cx, char *name, ClassClass *from)
|
|
{
|
|
js_FindJavaClass_data data;
|
|
|
|
data.cx = cx;
|
|
data.name = name;
|
|
data.fromclass = from;
|
|
data.errstr = 0;
|
|
if (!js_CallJava(cx, js_FindJavaClass_stub, &data, JS_TRUE))
|
|
return 0;
|
|
if (data.errstr) {
|
|
JS_ReportError(cx, "%s", data. errstr);
|
|
free(data.errstr);
|
|
/* FIXME need to propagate error condition differently */
|
|
return 0;
|
|
}
|
|
return data.ret;
|
|
}
|
|
|
|
/***********************************************************************/
|
|
|
|
|
|
typedef struct {
|
|
JRIEnv *env;
|
|
JSBool privileged;
|
|
char *name;
|
|
ClassClass *cb;
|
|
char *sig;
|
|
va_list args;
|
|
HObject *ret;
|
|
} js_ConstructJava_data;
|
|
|
|
static void
|
|
js_ConstructJava_stub(void *d)
|
|
{
|
|
js_ConstructJava_data *data = d;
|
|
ExecEnv *ee = /*FIXME*/(ExecEnv*)data->env;
|
|
|
|
if (data->privileged) {
|
|
/* FIXME extremely lame - there should be a security
|
|
* flag to execute_java_constructor_vararg instead.
|
|
* the effect of this is that the JSObject constructor may
|
|
* get called on the wrong thread, but this probably won't
|
|
* do any damage. JRI will fix this, right? */
|
|
ee = PRIVILEGED_EE;
|
|
}
|
|
data->ret =
|
|
execute_java_constructor_vararg(ee,
|
|
data->name, data->cb,
|
|
data->sig, data->args);
|
|
}
|
|
|
|
/* this can call arbitrary java code in class initialization */
|
|
static HObject *
|
|
js_ConstructJava(JSContext *cx, char *name, ClassClass *cb,
|
|
char *sig, ...)
|
|
{
|
|
js_ConstructJava_data data;
|
|
va_list args;
|
|
JSBool success;
|
|
|
|
va_start(args, sig);
|
|
data.name = name;
|
|
data.privileged = JS_FALSE;
|
|
data.cb = cb;
|
|
data.sig = sig;
|
|
VARARGS_ASSIGN(data.args, args);
|
|
data.env = JRI_GetCurrentEnv();
|
|
|
|
success = js_CallJava(cx, js_ConstructJava_stub, &data, JS_TRUE);
|
|
va_end(args);
|
|
|
|
if (success) return data.ret;
|
|
else return 0;
|
|
}
|
|
|
|
/* for private constructors, i.e. JSObject */
|
|
static HObject *
|
|
js_ConstructJavaPrivileged(JSContext *cx, char *name, ClassClass *cb,
|
|
char *sig, ...)
|
|
{
|
|
js_ConstructJava_data data;
|
|
va_list args;
|
|
JSBool success;
|
|
|
|
va_start(args, sig);
|
|
data.privileged = JS_TRUE;
|
|
data.name = name;
|
|
data.cb = cb;
|
|
data.sig = sig;
|
|
VARARGS_ASSIGN(data.args, args);
|
|
data.env = JRI_GetCurrentEnv();
|
|
|
|
success = js_CallJava(cx, js_ConstructJava_stub, &data, JS_TRUE);
|
|
va_end(args);
|
|
|
|
if (success) return data.ret;
|
|
else return 0;
|
|
}
|
|
|
|
|
|
/***********************************************************************/
|
|
|
|
static JSBool jsj_enabled = JS_TRUE;
|
|
|
|
/*
|
|
* most of the initialization is done lazily by this function,
|
|
* which is only called through jsj_GetCurrentEE(). it is
|
|
* only called once - this is enforced in jsj_GetCurrentEE().
|
|
*/
|
|
static void
|
|
jsj_FinishInit(JSContext *cx, JRIEnv *env)
|
|
{
|
|
char *name;
|
|
ExecEnv *ee;
|
|
|
|
PR_LOG(MojaSrc, debug, ("jsj_FinishInit()\n"));
|
|
|
|
/* initialize the reflection tables */
|
|
javaReflections =
|
|
PR_NewHashTable(256, (PRHashFunction) java_hashHandle,
|
|
(PRHashComparator) java_pointerEq,
|
|
(PRHashComparator) java_pointerEq, 0, 0);
|
|
#ifdef NSPR20
|
|
PR_RegisterGCLockHook((GCLockHookFunc*) PrepareJSLocksForGC, 0);
|
|
#endif
|
|
PR_RegisterRootFinder(scanJSJavaReflections,
|
|
"scan JS reflections of java objects",
|
|
JS_GetRuntime(cx));
|
|
if (javaReflectionsMonitor == NULL)
|
|
javaReflectionsMonitor = PR_NewNamedMonitor("javaReflections");
|
|
jsReflections =
|
|
PR_NewHashTable(256, (PRHashFunction) java_hashHandle,
|
|
(PRHashComparator) java_pointerEq,
|
|
(PRHashComparator) java_pointerEq, 0, 0);
|
|
if (jsReflectionsMonitor == NULL)
|
|
jsReflectionsMonitor = PR_NewNamedMonitor("jsReflections");
|
|
|
|
ee = (ExecEnv *) env;
|
|
|
|
exceptionClear(ee);
|
|
|
|
name = "java/lang/Object";
|
|
if (! (ObjectClassBlock = FindClassFromClass(ee, name, TRUE, 0)))
|
|
goto badclass;
|
|
MakeClassSticky(ObjectClassBlock);
|
|
|
|
name = "netscape/javascript/JSObject";
|
|
if (! (JSObjectClassBlock = FindClassFromClass(ee, name, TRUE, 0)))
|
|
goto badclass;
|
|
MakeClassSticky(JSObjectClassBlock);
|
|
JSObjectInternalField = JRI_GetFieldID(env,
|
|
(struct java_lang_Class*)cbHandle(JSObjectClassBlock),
|
|
"internal", "I");
|
|
|
|
name = "netscape/javascript/JSException";
|
|
if (! (JSExceptionClassBlock = FindClassFromClass(ee, name, TRUE, 0)))
|
|
goto badclass;
|
|
MakeClassSticky(JSExceptionClassBlock);
|
|
|
|
name = "java/lang/String";
|
|
if (! (StringClassBlock = FindClassFromClass(ee, name, TRUE, 0)))
|
|
goto badclass;
|
|
MakeClassSticky(StringClassBlock);
|
|
|
|
name = "java/lang/Boolean";
|
|
if (! (BooleanClassBlock = FindClassFromClass(ee, name, TRUE, 0)))
|
|
goto badclass;
|
|
MakeClassSticky(BooleanClassBlock);
|
|
|
|
name = "java/lang/Double";
|
|
if (! (DoubleClassBlock = FindClassFromClass(ee, name, TRUE, 0)))
|
|
goto badclass;
|
|
MakeClassSticky(DoubleClassBlock);
|
|
|
|
name = "java/lang/Throwable";
|
|
if (! (ThrowableClassBlock = FindClassFromClass(ee, name, TRUE, 0)))
|
|
goto badclass;
|
|
MakeClassSticky(ThrowableClassBlock);
|
|
|
|
return;
|
|
|
|
badclass:
|
|
PR_LOG(MojaSrc, error, ("couldn't find class \"%s\"\n", name));
|
|
JS_ReportError(cx, "Unable to initialize LiveConnect: missing \"%s\"",
|
|
name);
|
|
jsj_enabled = JS_FALSE;
|
|
return;
|
|
}
|
|
|
|
PR_STATIC_CALLBACK(int)
|
|
jsj_TrashJSReflectionEntry(PRHashEntry *he, int i, void *arg)
|
|
{
|
|
JRIEnv *env = arg;
|
|
jref jso = (jref) he->value;
|
|
|
|
JRI_SetFieldInt(env, jso, JSObjectInternalField, (jint) 0);
|
|
/* FIXME inline JS_RemoveRoot -- this will go away with GC unification */
|
|
JS_LOCK_RUNTIME(finalizeRuntime);
|
|
(void) PR_HashTableRemove(finalizeRuntime->gcRootsHash, (void *)&he->key);
|
|
JS_UNLOCK_RUNTIME(finalizeRuntime);
|
|
|
|
return HT_ENUMERATE_REMOVE;
|
|
}
|
|
|
|
PR_EXTERN(void)
|
|
JSJ_Finish(void)
|
|
{
|
|
JRIEnv *env = 0;
|
|
|
|
/* PR_ASSERT(we_have_libmocha_lock) */
|
|
|
|
jsj_enabled = JS_FALSE;
|
|
|
|
if (!javaReflectionsMonitor)
|
|
return;
|
|
PR_EnterMonitor(javaReflectionsMonitor);
|
|
if (javaReflections) {
|
|
PR_HashTableDestroy(javaReflections);
|
|
javaReflections = NULL;
|
|
}
|
|
PR_ExitMonitor(javaReflectionsMonitor);
|
|
|
|
env = JRI_GetCurrentEnv();
|
|
|
|
if (!env) {
|
|
#ifdef DEBUG_JSJ
|
|
PR_ASSERT(env);
|
|
#else
|
|
return;
|
|
#endif
|
|
}
|
|
|
|
/* FIXME assert that lm_lock is held */
|
|
PR_EnterMonitor(jsReflectionsMonitor);
|
|
if (jsReflections) {
|
|
PR_HashTableEnumerateEntries(jsReflections,
|
|
jsj_TrashJSReflectionEntry,
|
|
(void*) env);
|
|
PR_HashTableDestroy(jsReflections);
|
|
jsReflections = NULL;
|
|
}
|
|
PR_ExitMonitor(jsReflectionsMonitor);
|
|
}
|
|
|
|
/*
|
|
* Get the java class associated with an instance, useful for access
|
|
* to static fields and methods of applets.
|
|
*/
|
|
static JSBool
|
|
js_getJavaClass(JSContext *cx, JSObject *obj, PRUintn argc, jsval *argv,
|
|
jsval *rval)
|
|
{
|
|
JSObject *mo;
|
|
JSObject *moclass;
|
|
JSJava *java;
|
|
|
|
/* FIXME this could accept strings as well i suppose */
|
|
if (argc != 1 ||
|
|
!JSVAL_IS_OBJECT(argv[0]) ||
|
|
!(mo = JSVAL_TO_OBJECT(argv[0])) ||
|
|
!JS_InstanceOf(cx, mo, &java_class, 0) ||
|
|
(java = (JSJava *) JS_GetPrivate(cx, mo))->type != JAVA_OBJECT) {
|
|
JS_ReportError(cx, "getClass expects a Java object argument");
|
|
return JS_FALSE;
|
|
}
|
|
|
|
if (!(moclass = js_ReflectJClassToJSObject(cx, java->cb))) {
|
|
JS_ReportError(cx, "getClass can't find Java class reflection");
|
|
return JS_FALSE;
|
|
}
|
|
|
|
*rval = OBJECT_TO_JSVAL(moclass);
|
|
return JS_TRUE;
|
|
}
|
|
|
|
static JSObject *
|
|
js_DefineJavaPackage(JSContext *cx, JSObject *obj, char *jsname, char *package)
|
|
{
|
|
JSJavaPackage *pack;
|
|
JSObject *pobj;
|
|
|
|
pack = JS_malloc(cx, sizeof(JSJavaPackage));
|
|
if (!pack)
|
|
return 0;
|
|
|
|
if (package) {
|
|
pack->name = JS_strdup(cx, package);
|
|
if (!pack->name) {
|
|
JS_free(cx, pack);
|
|
return 0;
|
|
}
|
|
} else {
|
|
pack->name = 0;
|
|
}
|
|
|
|
/* FIXME can we make the package read-only? */
|
|
pobj = JS_DefineObject(cx, obj, jsname, &javapackage_class, 0, 0);
|
|
if (pobj && !JS_SetPrivate(cx, pobj, pack))
|
|
(void) JS_DeleteProperty(cx, obj, jsname);
|
|
return pobj;
|
|
}
|
|
|
|
/* hook from js_DestroyContext */
|
|
static void
|
|
jsj_DestroyJSContextHook(JSContext *cx)
|
|
{
|
|
/* FIXME do anything with the env? */
|
|
/* FIXMEold cx->javaEnv = 0; */
|
|
}
|
|
|
|
/* FIXME make sure this is called from jsscript.c:js_DestroyScript !*/
|
|
static void
|
|
jsj_DestroyScriptHook(JSContext *cx, JSScript *script)
|
|
{
|
|
JSJClassData *data = script->javaData;
|
|
|
|
if (!data)
|
|
return;
|
|
|
|
jsj_DestroyClassData(cx, data);
|
|
|
|
script->javaData = 0;
|
|
}
|
|
|
|
static void
|
|
jsj_DestroyFrameHook(JSContext *cx, JSStackFrame *frame) {
|
|
if (frame->annotation) {
|
|
JRIEnv *env = (JRIEnv *) jsj_GetCurrentEE(cx);
|
|
if (!env) return;
|
|
|
|
JRI_DisposeGlobalRef(env, frame->annotation);
|
|
}
|
|
}
|
|
|
|
/* we need to hook into the js interpreter in a few special places */
|
|
JSInterpreterHooks js_Hooks = {
|
|
jsj_DestroyJSContextHook,
|
|
jsj_DestroyScriptHook,
|
|
jsj_DestroyFrameHook
|
|
};
|
|
|
|
extern void _java_javascript_init(void);
|
|
|
|
/*
|
|
* Initialize JS<->Java glue
|
|
*/
|
|
PR_IMPLEMENT(JSBool)
|
|
JSJ_Init(JSJCallbacks *callbacks)
|
|
{
|
|
static JSBool initialized = JS_FALSE;
|
|
|
|
/* JSJ_Init may be called twice: it is called with default
|
|
* hooks when the netscape.javascript.JSObject class is
|
|
* initialized, but it also may be called by the client
|
|
* or server. in this case, the first to call it gets
|
|
* to set the hooks */
|
|
if (initialized)
|
|
return JS_FALSE;
|
|
initialized = JS_TRUE;
|
|
|
|
_java_javascript_init(); /* stupid linker tricks */
|
|
|
|
js_SetInterpreterHooks(&js_Hooks);
|
|
|
|
if (!callbacks)
|
|
return JS_TRUE;
|
|
|
|
#ifdef NSPR20
|
|
if (MojaSrc == NULL)
|
|
MojaSrc = PR_NewLogModule("MojaSrc");
|
|
#endif
|
|
|
|
jsj_callbacks = callbacks;
|
|
|
|
return JS_TRUE;
|
|
}
|
|
|
|
/*
|
|
* Initialize a javascript context and toplevel object for use with JSJ
|
|
*/
|
|
PR_IMPLEMENT(JSBool)
|
|
JSJ_InitContext(JSContext *cx, JSObject *obj)
|
|
{
|
|
JSObject *mo;
|
|
|
|
/* grab the main runtime from the context if we haven't yet */
|
|
if (!finalizeRuntime)
|
|
finalizeRuntime = JS_GetRuntime(cx);
|
|
|
|
/* define the top of the java package namespace as "Packages" */
|
|
mo = js_DefineJavaPackage(cx, obj, "Packages", 0);
|
|
|
|
/* some convenience packages */
|
|
/* FIXME these should be properties of the top-level package
|
|
* too. as it is there will be two different objects for
|
|
* "java" and "Packages.java" which is unfortunate but mostly
|
|
* invisible */
|
|
|
|
/* FIXMEbe Use new JS_AliasProperty API call.
|
|
* have to lookup Packages.java first... */
|
|
|
|
js_DefineJavaPackage(cx, obj, "java", "java");
|
|
js_DefineJavaPackage(cx, obj, "sun", "sun");
|
|
js_DefineJavaPackage(cx, obj, "netscape", "netscape");
|
|
|
|
JS_DefineFunction(cx, obj, "getClass", js_getJavaClass, 0, JSPROP_READONLY);
|
|
/* create the prototype object for java objects and classes */
|
|
mo = JS_DefineObject(cx, obj, "#javaPrototype", &java_class, 0,
|
|
JSPROP_READONLY | JSPROP_PERMANENT);
|
|
/* FIXME set up the private data? */
|
|
|
|
/* any initialization that depends on java running is done in
|
|
* js_FinishInitJava */
|
|
|
|
return JS_TRUE;
|
|
}
|
|
|
|
#ifdef NEVER
|
|
/* This is really a big comment describing the JavaScript class file,
|
|
never intended to be compiled. */
|
|
/* FIXME should this be in a package? */
|
|
|
|
/**
|
|
* Internal class for secure JavaScript->Java calls.
|
|
* We load this class with a crippled ClassLoader, and make sure
|
|
* that one of the methods from this class is on the Java stack
|
|
* when JavaScript calls into Java.
|
|
* The call to System.err.println is necessary because otherwise
|
|
* sj doesn't account for the arguments in mb->maxstack, and
|
|
* the jsContext argument gets wiped out when the next stack
|
|
* frame is constructed.
|
|
*/
|
|
|
|
package netscape.javascript;
|
|
|
|
class JavaScript {
|
|
/* can't be constructed */
|
|
private JavaScript() {};
|
|
|
|
/**
|
|
* this is the method that will be on the Java stack when
|
|
* JavaScript calls Java. it is never actually called,
|
|
* but we put a reference to it on the stack as if it had
|
|
* been. the mochaContext argument is a native pointer to
|
|
* the current JavaScript context so that we can figure
|
|
* that out from Java.
|
|
*/
|
|
static void callJava(int jsContext) {
|
|
/* this call is here to make sure the compiler
|
|
* allocates enough stack for us - previously we
|
|
* were getting mb->maxstack == 0, which would
|
|
* cause the jsContext argument to be overwritten */
|
|
System.err.println(jsContext);
|
|
};
|
|
}
|
|
|
|
#endif /* NEVER */
|
|
|
|
static unsigned char JavaScript_class_bytes[] = {
|
|
"\312\376\272\276\000\003\000-\000\035\007\000\026\007\000\030\007\000"
|
|
"\020\007\000\033\012\000\003\000\012\012\000\002\000\010\011\000\004\000"
|
|
"\011\014\000\031\000\034\014\000\015\000\032\014\000\013\000\014\001\000"
|
|
"\007println\001\000\004(I)V\001\000\003err\001\000\015ConstantValue\001"
|
|
"\000\010callJava\001\000\023java/io/PrintStream\001\000\012Exceptions"
|
|
"\001\000\017LineNumberTable\001\000\012SourceFile\001\000\016LocalVar"
|
|
"iables\001\000\004Code\001\000\036netscape/javascript/JavaScript\001\000"
|
|
"\017JavaScript.java\001\000\020java/lang/Object\001\000\006<init>\001"
|
|
"\000\025Ljava/io/PrintStream;\001\000\020java/lang/System\001\000\003"
|
|
"()V\000\000\000\001\000\002\000\000\000\000\000\002\000\002\000\031\000"
|
|
"\034\000\001\000\025\000\000\000\035\000\001\000\001\000\000\000\005*"
|
|
"\267\000\006\261\000\000\000\001\000\022\000\000\000\006\000\001\000\000"
|
|
"\000\020\000\010\000\017\000\014\000\001\000\025\000\000\000$\000\002"
|
|
"\000\001\000\000\000\010\262\000\007\032\266\000\005\261\000\000\000\001"
|
|
"\000\022\000\000\000\012\000\002\000\000\000\037\000\007\000\032\000\001"
|
|
"\000\023\000\000\000\002\000\027"
|
|
};
|
|
|
|
/* now something to make a classloader and get a JavaScript object
|
|
* from it. */
|
|
|
|
/* FIXME garbage collection of these classes? how should they be
|
|
* protected? */
|
|
|
|
#include "java_lang_ClassLoader.h"
|
|
|
|
#define USELESS_CODEBASE_URL "http://javascript-of-unknown-origin.netscape.com/"
|
|
|
|
static void
|
|
jsj_DestroyClassData(JSContext *cx, JSJClassData *data)
|
|
{
|
|
ExecEnv *ee;
|
|
JRIEnv *env;
|
|
HObject *loader;
|
|
|
|
/* FIXME locking! */
|
|
if (--data->nrefs > 0)
|
|
return;
|
|
|
|
ee = jsj_GetCurrentEE(cx);
|
|
if (!ee) {
|
|
/* FIXME memory leak - if we can't find a java execution
|
|
* env we can't free the classloader and class */
|
|
PR_ASSERT(!"can't find java env to free JSScript.javaData");
|
|
return;
|
|
}
|
|
|
|
env = (JRIEnv *) ee;
|
|
|
|
if (data->clazz) {
|
|
JRI_DisposeGlobalRef(env, data->clazz);
|
|
data->clazz = NULL;
|
|
}
|
|
|
|
if (data->loader) {
|
|
loader = JRI_GetGlobalRef(env, data->loader);
|
|
/* comment out call to releaseClassLoader because it is obsolete.
|
|
execute_java_dynamic_method(ee, loader,
|
|
"releaseClassLoader", "()V");
|
|
if (exceptionOccurred(ee)) {
|
|
*/
|
|
/* FIXME memory leak - if we can't find a java execution
|
|
* env we can't free the classloader and class */
|
|
/*
|
|
PR_ASSERT(!"failed releasing javascript classloader");
|
|
goto done;
|
|
}
|
|
*/
|
|
JRI_DisposeGlobalRef(env, data->loader);
|
|
data->loader = NULL;
|
|
}
|
|
|
|
data->mb = NULL;
|
|
|
|
done:
|
|
free(data);
|
|
}
|
|
|
|
/* security: this method returns a methodblock which can be
|
|
* used for secure JS->java calls. the methodblock is
|
|
* guaranteed to have an associated AppletClassLoader which will
|
|
* allow the SecurityManager to determine what permissions to
|
|
* give to the Java code being called.
|
|
*/
|
|
static JSJClassData *
|
|
jsj_MakeClassData(JSContext *cx)
|
|
{
|
|
int i;
|
|
ExecEnv *ee;
|
|
JRIEnv *env;
|
|
JSScript *script;
|
|
JSStackFrame *fp;
|
|
PRInt32 length;
|
|
HArrayOfByte *bytes;
|
|
const char *origin_url;
|
|
struct Hjava_lang_ClassLoader *loader;
|
|
ClassClass *clazz;
|
|
struct methodblock *mb;
|
|
JSJClassData *data;
|
|
|
|
ee = jsj_GetCurrentEE(cx);
|
|
if (!ee) return 0;
|
|
|
|
/* see if this script already has a classloader */
|
|
script = NULL;
|
|
for (fp = cx->fp; fp; fp = fp->down) {
|
|
script = fp->script;
|
|
if (script)
|
|
break;
|
|
}
|
|
if (script &&
|
|
(data = (JSJClassData *) script->javaData)) {
|
|
/* FIXME locking! */
|
|
data->nrefs++;
|
|
return data;
|
|
}
|
|
|
|
data = (JSJClassData *) malloc(sizeof(JSJClassData));
|
|
if (!data) {
|
|
JS_ReportOutOfMemory(cx);
|
|
return 0;
|
|
}
|
|
data->nrefs = 1;
|
|
data->loader = NULL;
|
|
data->clazz = NULL;
|
|
data->mb = NULL;
|
|
|
|
if (script && script->filename
|
|
&& strcmp("[unknown origin]", script->filename)) {
|
|
origin_url = script->filename;
|
|
} else {
|
|
origin_url = USELESS_CODEBASE_URL;
|
|
}
|
|
|
|
loader = jsj_callbacks->jsClassLoader(cx, origin_url);
|
|
|
|
if (exceptionOccurred(ee) || !loader) {
|
|
JS_ReportError(cx, "couldn't create ClassLoader for JavaScript");
|
|
free(data);
|
|
return 0;
|
|
}
|
|
|
|
env = (JRIEnv *) ee;
|
|
data->loader = JRI_NewGlobalRef(env, loader);
|
|
|
|
/* FIXME should save and re-use this array in a jglobal */
|
|
/* make the array of bytes for class JavaScript */
|
|
length = sizeof(JavaScript_class_bytes);
|
|
bytes = (HArrayOfByte *) ArrayAlloc(T_BYTE, length);
|
|
if (!bytes) {
|
|
jsj_DestroyClassData(cx, data);
|
|
JS_ReportError(cx,
|
|
"couldn't allocate bytes for JavaScript.class");
|
|
return 0;
|
|
}
|
|
memmove(unhand(bytes)->body, JavaScript_class_bytes, (size_t)length);
|
|
|
|
/* FIXME lock to avoid race condition between this and defineClass */
|
|
clazz = FindLoadedClass("netscape/javascript/JavaScript", loader);
|
|
|
|
if (!clazz) {
|
|
/* make class JavaScript */
|
|
clazz = (ClassClass*)
|
|
do_execute_java_method(ee, loader,
|
|
"defineClass", "(Ljava/lang/String;[BII)Ljava/lang/Class;",
|
|
0, FALSE, NULL /* no required name */, bytes, 0L, length);
|
|
|
|
if (exceptionOccurred(ee) || !clazz) {
|
|
jsj_DestroyClassData(cx, data);
|
|
JS_ReportError(cx, "couldn't load class JavaScript");
|
|
return 0;
|
|
}
|
|
|
|
(void)
|
|
do_execute_java_method(ee, loader,
|
|
"setPrincipalAry",
|
|
"(Ljava/lang/Class;Ljava/lang/String;)Z",
|
|
0, FALSE,
|
|
clazz, NULL /* don't lookup */);
|
|
|
|
if (exceptionOccurred(ee) || !clazz) {
|
|
jsj_DestroyClassData(cx, data);
|
|
JS_ReportError(cx, "couldn't set principal for class JavaScript");
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
data->clazz = JRI_NewGlobalRef(env, cbHandle(clazz));
|
|
|
|
/* FIXME set up the signatures on the class here */
|
|
|
|
/* find the static method callJava() for the class */
|
|
for (i = 0; i < (int)cbMethodsCount(clazz); i++) {
|
|
mb = cbMethods(clazz) + i;
|
|
if (!strcmp(fieldname(&mb->fb), "callJava")
|
|
&& !strcmp(fieldsig(&mb->fb), "(I)V")) {
|
|
/* found it... */
|
|
data->mb = mb;
|
|
if (script) {
|
|
/* FIXME locking! */
|
|
data->nrefs++;
|
|
script->javaData = data;
|
|
}
|
|
return data;
|
|
}
|
|
}
|
|
|
|
jsj_DestroyClassData(cx, data);
|
|
JS_ReportError(cx, "can't find method JavaScript.callJava");
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* tell whether a partiular methodblock is part of a safety frame
|
|
*
|
|
* the security here depends on the fact that users are forbidden
|
|
* to define packages in netscape.javascript. the consequence
|
|
* of breaking this security would be that the user could get
|
|
* a java int dereferenced as a pointer, most likely crashing
|
|
* the program.
|
|
*
|
|
* embedding some sort of secret into the JavaScript class
|
|
* (pointer to itself?) would also secure this.
|
|
*/
|
|
PR_IMPLEMENT(JSBool)
|
|
JSJ_IsSafeMethod(struct methodblock *mb)
|
|
{
|
|
ClassClass *cb;
|
|
|
|
if (!mb)
|
|
return JS_FALSE;
|
|
|
|
cb = fieldclass(&mb->fb);
|
|
|
|
if (!cb ||
|
|
strcmp(cbName(cb), "netscape/javascript/JavaScript"))
|
|
return JS_FALSE;
|
|
|
|
return JS_TRUE;
|
|
}
|
|
|
|
/*
|
|
* push a frame onto the java stack which does nothing except
|
|
* provide a classloader for the security manager
|
|
*/
|
|
static JSBool
|
|
js_pushSafeFrame(JSContext *cx, ExecEnv *ee, JSJClassData **classData)
|
|
{
|
|
JavaFrame *current_frame, *previous_frame;
|
|
JavaStack *current_stack;
|
|
struct methodblock *mb;
|
|
|
|
if (!jsj_callbacks->jsClassLoader) {
|
|
*classData = NULL;
|
|
return JS_TRUE;
|
|
}
|
|
|
|
if (!(*classData = jsj_MakeClassData(cx)))
|
|
return JS_FALSE;
|
|
mb = (*classData)->mb;
|
|
|
|
previous_frame = ee->current_frame;
|
|
if (previous_frame == 0) {
|
|
/* bottommost frame on this Exec Env. */
|
|
current_stack = ee->initial_stack;
|
|
current_frame = (JavaFrame *)(current_stack->data); /* no vars */
|
|
} else {
|
|
int args_size = mb->args_size;
|
|
current_stack = previous_frame->javastack; /* assume same stack */
|
|
if (previous_frame->current_method) {
|
|
int size = previous_frame->current_method->maxstack;
|
|
current_frame = (JavaFrame *)(&previous_frame->ostack[size]);
|
|
} else {
|
|
/* The only frames that don't have a mb are pseudo frames like
|
|
* this one and they don't really touch their stack. */
|
|
current_frame = (JavaFrame *)(previous_frame->optop + 3);
|
|
}
|
|
if (current_frame->ostack + args_size > current_stack->end_data) {
|
|
/* Ooops. The current stack isn't big enough. */
|
|
if (current_stack->next != 0) {
|
|
current_stack = current_stack->next;
|
|
} else {
|
|
current_stack = CreateNewJavaStack(ee, current_stack);
|
|
if (current_stack == 0) {
|
|
JS_ReportOutOfMemory(cx);
|
|
return JS_FALSE;
|
|
}
|
|
}
|
|
/* no vars */
|
|
current_frame = (JavaFrame *)(current_stack->data);
|
|
}
|
|
}
|
|
current_frame->prev = previous_frame;
|
|
current_frame->javastack = current_stack;
|
|
current_frame->optop = current_frame->ostack;
|
|
current_frame->vars = 0; /* better not reference any! */
|
|
current_frame->monitor = 0; /* not monitoring anything */
|
|
current_frame->annotation = 0;
|
|
|
|
/* make this be a method with the JS classloader */
|
|
current_frame->current_method = mb;
|
|
|
|
/* FIXME push the current mochaContext as an integer */
|
|
current_frame->optop[0].i = (int32_t) cx;
|
|
current_frame->optop += current_frame->current_method->args_size;
|
|
current_frame->constant_pool = 0;
|
|
|
|
ee->current_frame = current_frame;
|
|
|
|
return JS_TRUE;
|
|
}
|
|
|
|
/*
|
|
* pop the safety frame from the java stack
|
|
*/
|
|
static void
|
|
js_popSafeFrame(JSContext *cx, ExecEnv *ee, JSJClassData *classData)
|
|
{
|
|
if (!classData)
|
|
return;
|
|
|
|
ee->current_frame = ee->current_frame->prev;
|
|
jsj_DestroyClassData(cx, classData);
|
|
}
|
|
|
|
/*
|
|
* look up the stack for the most recent safety frame and extract
|
|
* the JSContext from it. return NULL if no such frame was found.
|
|
*/
|
|
PR_IMPLEMENT(void)
|
|
JSJ_FindCurrentJSContext(JRIEnv *env, JSContext **cxp, void **loaderp)
|
|
{
|
|
ExecEnv *ee = (ExecEnv *) env;
|
|
JavaFrame *frame, frame_buf;
|
|
ClassClass *cb;
|
|
|
|
#define NEXT_FRAME(frame) \
|
|
(((frame)->current_method && (frame)->current_method->fb.access & ACC_MACHINE_COMPILED) ? \
|
|
CompiledFramePrev(frame, &frame_buf) \
|
|
: frame->prev)
|
|
|
|
*cxp = 0;
|
|
*loaderp = 0;
|
|
|
|
/* search the stack upward */
|
|
for (frame = ee->current_frame; frame; frame = NEXT_FRAME(frame)) {
|
|
struct methodblock *mb = frame->current_method;
|
|
if (mb) {
|
|
cb = fieldclass(&frame->current_method->fb);
|
|
*loaderp = cbLoader(cb);
|
|
if (*loaderp) {
|
|
if (JSJ_IsSafeMethod(mb)) {
|
|
/* extract the mochacontext here */
|
|
*cxp = (JSContext *)frame->ostack[0].i;
|
|
} else
|
|
*cxp = 0;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
PR_IMPLEMENT(JSBool)
|
|
JSJ_IsCalledFromJava(JSContext *cx)
|
|
{
|
|
ExecEnv *ee;
|
|
|
|
ee = (ExecEnv *) JRI_GetCurrentEnv();
|
|
if (ee == NULL)
|
|
return JS_FALSE;
|
|
|
|
return ee->current_frame != NULL;
|
|
}
|
|
|
|
/* extract the javascript object from a netscape.javascript.JSObject */
|
|
PR_IMPLEMENT(JSObject *)
|
|
JSJ_ExtractInternalJSObject(JRIEnv *env, HObject* jso)
|
|
{
|
|
PR_ASSERT(obj_array_classblock((HObject*)jso) == JSObjectClassBlock);
|
|
|
|
return (JSObject *) JRI_GetFieldInt(env, (netscape_javascript_JSObject*)jso,
|
|
JSObjectInternalField);
|
|
}
|
|
|
|
PR_IMPLEMENT(JSPrincipals *)
|
|
js_GetJSPrincipalsFromJavaCaller(JSContext *cx, int callerDepth)
|
|
{
|
|
return jsj_callbacks->getJSPrincipalsFromJavaCaller(cx, callerDepth);
|
|
}
|
|
|
|
|
|
#endif /* defined(JAVA) */
|