RetroZilla/js/src/liveconnect/jsj_JavaClass.c

747 lines
23 KiB
C

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla Communicator client code, released
* March 31, 1998.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1998
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
/* This file is part of the Java-vendor-neutral implementation of LiveConnect
*
* It contains the native code implementation of JS's JavaClass class.
*
* A JavaClass is JavaScript's representation of a Java class.
* Its parent JS object is always a JavaPackage object. A JavaClass is not an
* exact reflection of Java's corresponding java.lang.Class object. Rather,
* the properties of a JavaClass are the static methods and properties of the
* corresponding Java class.
*
* Note that there is no runtime equivalent to the JavaClass class in Java.
* (Although there are instances of java.lang.String and there are static
* methods of java.lang.String that can be invoked, there's no such thing as
* a first-class object that can be referenced simply as "java.lang.String".)
*/
#include <stdlib.h>
#include <string.h>
#include "jsj_private.h" /* LiveConnect internals */
#include "jscntxt.h" /* for error reporting */
JS_STATIC_DLL_CALLBACK(JSBool)
JavaClass_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp)
{
char *name;
JSString *str;
JavaClassDescriptor *class_descriptor;
class_descriptor = JS_GetPrivate(cx, obj);
if (!class_descriptor)
return JS_FALSE;
switch(type) {
case JSTYPE_STRING:
/* Convert '/' to '.' so that it looks like Java language syntax. */
if (!class_descriptor->name)
break;
name = JS_smprintf("[JavaClass %s]", class_descriptor->name);
if (!name) {
JS_ReportOutOfMemory(cx);
return JS_FALSE;
}
str = JS_NewString(cx, name, strlen(name));
if (!str) {
free(name);
/* It's not necessary to call JS_ReportOutOfMemory(), as
JS_NewString() will do so on failure. */
return JS_FALSE;
}
*vp = STRING_TO_JSVAL(str);
return JS_TRUE;
default:
break;
}
return JS_TRUE;
}
static JSBool
lookup_static_member_by_id(JSContext *cx, JNIEnv *jEnv, JSObject *obj,
JavaClassDescriptor **class_descriptorp,
jsid id, JavaMemberDescriptor **memberp)
{
jsval idval;
JavaMemberDescriptor *member_descriptor;
const char *member_name;
JavaClassDescriptor *class_descriptor;
class_descriptor = JS_GetPrivate(cx, obj);
if (!class_descriptor) {
*class_descriptorp = NULL;
*memberp = NULL;
return JS_TRUE;
}
if (class_descriptorp)
*class_descriptorp = class_descriptor;
member_descriptor = jsj_LookupJavaStaticMemberDescriptorById(cx, jEnv, class_descriptor, id);
if (!member_descriptor) {
JS_IdToValue(cx, id, &idval);
if (!JSVAL_IS_STRING(idval)) {
JS_ReportErrorNumber(cx, jsj_GetErrorMessage, NULL,
JSJMSG_BAD_JCLASS_EXPR);
return JS_FALSE;
}
member_name = JS_GetStringBytes(JSVAL_TO_STRING(idval));
/*
* See if the property looks like the explicit resolution of an
* overloaded method, e.g. "max(double,double)".
*/
member_descriptor =
jsj_ResolveExplicitMethod(cx, jEnv, class_descriptor, id, JS_TRUE);
if (member_descriptor)
goto done;
/* Why do we have to do this ? */
if (!strcmp(member_name, "prototype")) {
*memberp = NULL;
return JS_TRUE;
}
JS_ReportErrorNumber(cx, jsj_GetErrorMessage, NULL,
JSJMSG_MISSING_NAME,
class_descriptor->name, member_name);
return JS_FALSE;
}
done:
if (memberp)
*memberp = member_descriptor;
return JS_TRUE;
}
JS_STATIC_DLL_CALLBACK(JSBool)
JavaClass_getPropertyById(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
{
jsval idval;
jclass java_class;
const char *member_name;
JavaClassDescriptor *class_descriptor;
JavaMemberDescriptor *member_descriptor;
JNIEnv *jEnv;
JSJavaThreadState *jsj_env;
JSBool result;
/* printf("In JavaClass_getProperty\n"); */
/* Get the Java per-thread environment pointer for this JSContext */
jsj_env = jsj_EnterJava(cx, &jEnv);
if (!jEnv)
return JS_FALSE;
if (!lookup_static_member_by_id(cx, jEnv, obj, &class_descriptor, id, &member_descriptor)) {
jsj_ExitJava(jsj_env);
return JS_FALSE;
}
if (!member_descriptor) {
*vp = JSVAL_VOID;
jsj_ExitJava(jsj_env);
return JS_TRUE;
}
java_class = class_descriptor->java_class;
if (member_descriptor->field) {
if (!member_descriptor->methods) {
result = jsj_GetJavaFieldValue(cx, jEnv, member_descriptor->field, java_class, vp);
jsj_ExitJava(jsj_env);
return result;
} else {
JS_ASSERT(0);
}
} else {
JSFunction *function;
/* TODO - eliminate JSFUN_BOUND_METHOD */
if (member_descriptor->methods->is_alias) {
/* If this is an explicit resolution of an overloaded method,
use the fully-qualified method name as the name of the
resulting JS function, i.e. "myMethod(int,long)" */
JS_IdToValue(cx, id, &idval);
member_name = JS_GetStringBytes(JSVAL_TO_STRING(idval));
} else {
/* Either not explicit resolution of overloaded method or
explicit resolution was unnecessary because method was
not overloaded. */
member_name = member_descriptor->name;
}
function = JS_NewFunction(cx, jsj_JavaStaticMethodWrapper, 0,
JSFUN_BOUND_METHOD, obj, member_name);
if (!function) {
jsj_ExitJava(jsj_env);
return JS_FALSE;
}
*vp = OBJECT_TO_JSVAL(JS_GetFunctionObject(function));
}
jsj_ExitJava(jsj_env);
return JS_TRUE;
}
JS_STATIC_DLL_CALLBACK(JSBool)
JavaClass_setPropertyById(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
{
jclass java_class;
const char *member_name;
JavaClassDescriptor *class_descriptor;
JavaMemberDescriptor *member_descriptor;
jsval idval;
JNIEnv *jEnv;
JSJavaThreadState *jsj_env;
JSBool result;
/* printf("In JavaClass_setProperty\n"); */
/* Get the Java per-thread environment pointer for this JSContext */
jsj_env = jsj_EnterJava(cx, &jEnv);
if (!jEnv)
return JS_FALSE;
if (!lookup_static_member_by_id(cx, jEnv, obj, &class_descriptor, id, &member_descriptor)) {
jsj_ExitJava(jsj_env);
return JS_FALSE;
}
/* Check for the case where there is a method with the given name, but no field
with that name */
if (!member_descriptor->field)
goto no_such_field;
/* Silently fail if field value is final (immutable), as required by ECMA spec */
if (member_descriptor->field->modifiers & ACC_FINAL) {
jsj_ExitJava(jsj_env);
return JS_TRUE;
}
java_class = class_descriptor->java_class;
result = jsj_SetJavaFieldValue(cx, jEnv, member_descriptor->field, java_class, *vp);
jsj_ExitJava(jsj_env);
return result;
no_such_field:
JS_IdToValue(cx, id, &idval);
member_name = JS_GetStringBytes(JSVAL_TO_STRING(idval));
JS_ReportErrorNumber(cx, jsj_GetErrorMessage, NULL,
JSJMSG_MISSING_STATIC,
member_name, class_descriptor->name);
jsj_ExitJava(jsj_env);
return JS_FALSE;
}
/*
* Free the private native data associated with the JavaPackage object.
*/
JS_STATIC_DLL_CALLBACK(void)
JavaClass_finalize(JSContext *cx, JSObject *obj)
{
JNIEnv *jEnv;
JSJavaThreadState *jsj_env;
JavaClassDescriptor *class_descriptor = JS_GetPrivate(cx, obj);
if (!class_descriptor)
return;
/* Get the Java per-thread environment pointer for this JSContext */
jsj_env = jsj_EnterJava(cx, &jEnv);
if (!jEnv)
return;
/* printf("Finalizing %s\n", class_descriptor->name); */
jsj_ReleaseJavaClassDescriptor(cx, jEnv, class_descriptor);
jsj_ExitJava(jsj_env);
}
JS_STATIC_DLL_CALLBACK(JSBool)
JavaClass_lookupProperty(JSContext *cx, JSObject *obj, jsid id,
JSObject **objp, JSProperty **propp)
{
JNIEnv *jEnv;
JSErrorReporter old_reporter;
JSJavaThreadState *jsj_env;
/* printf("In JavaClass_lookupProperty()\n"); */
/* Get the Java per-thread environment pointer for this JSContext */
jsj_env = jsj_EnterJava(cx, &jEnv);
if (!jEnv)
return JS_FALSE;
old_reporter = JS_SetErrorReporter(cx, NULL);
if (lookup_static_member_by_id(cx, jEnv, obj, NULL, id, NULL)) {
*objp = obj;
*propp = (JSProperty*)1;
js_SetObjectWeakRoot(cx, obj);
} else {
*objp = NULL;
*propp = NULL;
}
JS_SetErrorReporter(cx, old_reporter);
jsj_ExitJava(jsj_env);
return JS_TRUE;
}
JS_STATIC_DLL_CALLBACK(JSBool)
JavaClass_defineProperty(JSContext *cx, JSObject *obj, jsid id, jsval value,
JSPropertyOp getter, JSPropertyOp setter,
uintN attrs, JSProperty **propp)
{
JavaClassDescriptor *class_descriptor;
class_descriptor = JS_GetPrivate(cx, obj);
/* Check for prototype JavaClass object */
if (!class_descriptor)
return JS_TRUE;
JS_ReportErrorNumber(cx, jsj_GetErrorMessage, NULL,
JSJMSG_JCLASS_PROP_DEFINE);
return JS_FALSE;
}
JS_STATIC_DLL_CALLBACK(JSBool)
JavaClass_getAttributes(JSContext *cx, JSObject *obj, jsid id,
JSProperty *prop, uintN *attrsp)
{
/* We don't maintain JS property attributes for Java class members */
*attrsp = JSPROP_PERMANENT|JSPROP_ENUMERATE;
return JS_FALSE;
}
JS_STATIC_DLL_CALLBACK(JSBool)
JavaClass_setAttributes(JSContext *cx, JSObject *obj, jsid id,
JSProperty *prop, uintN *attrsp)
{
/* We don't maintain JS property attributes for Java class members */
if (*attrsp != (JSPROP_PERMANENT|JSPROP_ENUMERATE)) {
JS_ASSERT(0);
return JS_FALSE;
}
/* Silently ignore all setAttribute attempts */
return JS_TRUE;
}
JS_STATIC_DLL_CALLBACK(JSBool)
JavaClass_deleteProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
{
JSVersion version = JS_GetVersion(cx);
*vp = JSVAL_FALSE;
if (!JSVERSION_IS_ECMA(version)) {
JS_ReportErrorNumber(cx, jsj_GetErrorMessage, NULL,
JSJMSG_JCLASS_PROP_DELETE);
return JS_FALSE;
} else {
/* Attempts to delete permanent properties are silently ignored
by ECMAScript. */
return JS_TRUE;
}
}
JS_STATIC_DLL_CALLBACK(JSBool)
JavaClass_defaultValue(JSContext *cx, JSObject *obj, JSType type, jsval *vp)
{
/* printf("In JavaClass_defaultValue()\n"); */
return JavaClass_convert(cx, obj, JSTYPE_STRING, vp);
}
JS_STATIC_DLL_CALLBACK(JSBool)
JavaClass_newEnumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
jsval *statep, jsid *idp)
{
JavaMemberDescriptor *member_descriptor;
JavaClassDescriptor *class_descriptor;
JNIEnv *jEnv;
JSJavaThreadState *jsj_env;
class_descriptor = JS_GetPrivate(cx, obj);
/* Check for prototype JavaClass object */
if (!class_descriptor) {
*statep = JSVAL_NULL;
if (idp)
*idp = INT_TO_JSVAL(0);
return JS_TRUE;
}
switch(enum_op) {
case JSENUMERATE_INIT:
/* Get the Java per-thread environment pointer for this JSContext */
jsj_env = jsj_EnterJava(cx, &jEnv);
if (!jEnv)
return JS_FALSE;
member_descriptor = jsj_GetClassStaticMembers(cx, jEnv, class_descriptor);
*statep = PRIVATE_TO_JSVAL(member_descriptor);
if (idp)
*idp = INT_TO_JSVAL(class_descriptor->num_instance_members);
jsj_ExitJava(jsj_env);
return JS_TRUE;
case JSENUMERATE_NEXT:
member_descriptor = JSVAL_TO_PRIVATE(*statep);
if (member_descriptor) {
/* Don't enumerate explicit-signature methods, i.e. enumerate toValue,
but not toValue(int), toValue(double), etc. */
while (member_descriptor->methods && member_descriptor->methods->is_alias) {
member_descriptor = member_descriptor->next;
if (!member_descriptor) {
*statep = JSVAL_NULL;
return JS_TRUE;
}
}
*idp = member_descriptor->id;
*statep = PRIVATE_TO_JSVAL(member_descriptor->next);
return JS_TRUE;
}
/* Fall through ... */
case JSENUMERATE_DESTROY:
*statep = JSVAL_NULL;
return JS_TRUE;
default:
JS_ASSERT(0);
return JS_FALSE;
}
}
JS_STATIC_DLL_CALLBACK(JSBool)
JavaClass_checkAccess(JSContext *cx, JSObject *obj, jsid id,
JSAccessMode mode, jsval *vp, uintN *attrsp)
{
switch (mode) {
case JSACC_WATCH:
JS_ReportErrorNumber(cx, jsj_GetErrorMessage, NULL,
JSJMSG_JCLASS_PROP_WATCH);
return JS_FALSE;
case JSACC_IMPORT:
JS_ReportErrorNumber(cx, jsj_GetErrorMessage, NULL,
JSJMSG_JCLASS_PROP_EXPORT);
return JS_FALSE;
default:
return JS_TRUE;
}
}
/*
* Implement the JavaScript instanceof operator for JavaClass objects by using
* the equivalent Java instanceof operation.
*/
JS_STATIC_DLL_CALLBACK(JSBool)
JavaClass_hasInstance(JSContext *cx, JSObject *obj, jsval candidate_jsval,
JSBool *has_instancep)
{
JavaClassDescriptor *class_descriptor;
JavaObjectWrapper *java_wrapper;
JSClass *js_class;
JSBool has_instance;
JSObject *candidate_obj;
jclass java_class;
jobject java_obj;
JNIEnv *jEnv;
JSJavaThreadState *jsj_env;
has_instance = JS_FALSE;
class_descriptor = JS_GetPrivate(cx, obj);
if (!class_descriptor) {
JS_ReportErrorNumber(cx, jsj_GetErrorMessage, NULL,
JSJMSG_BAD_OP_JCLASS);
return JS_FALSE;
}
/*
* Make sure that the thing to the left of the instanceof operator is a
* Java object.
*/
if (!JSVAL_IS_OBJECT(candidate_jsval))
goto done;
candidate_obj = JSVAL_TO_OBJECT(candidate_jsval);
#ifdef JS_THREADSAFE
js_class = JS_GetClass(cx, candidate_obj);
#else
js_class = JS_GetClass(candidate_obj);
#endif
if ((js_class != &JavaObject_class) && (js_class != &JavaArray_class))
goto done;
java_class = class_descriptor->java_class;
java_wrapper = JS_GetPrivate(cx, candidate_obj);
if (!java_wrapper) {
JS_ReportErrorNumber(cx, jsj_GetErrorMessage, NULL,
JSJMSG_BAD_OP_PROTO);
return JS_FALSE;
}
java_obj = java_wrapper->java_obj;
/* Get JNI pointer */
jsj_env = jsj_EnterJava(cx, &jEnv);
has_instance = (*jEnv)->IsInstanceOf(jEnv, java_obj, java_class);
jsj_ExitJava(jsj_env);
done:
*has_instancep = has_instance;
return JS_TRUE;
}
JSObjectOps JavaClass_ops = {
/* Mandatory non-null function pointer members. */
jsj_wrapper_newObjectMap, /* newObjectMap */
jsj_wrapper_destroyObjectMap, /* destroyObjectMap */
JavaClass_lookupProperty,
JavaClass_defineProperty,
JavaClass_getPropertyById, /* getProperty */
JavaClass_setPropertyById, /* setProperty */
JavaClass_getAttributes,
JavaClass_setAttributes,
JavaClass_deleteProperty,
JavaClass_defaultValue,
JavaClass_newEnumerate,
JavaClass_checkAccess,
/* Optionally non-null members start here. */
NULL, /* thisObject */
NULL, /* dropProperty */
jsj_JavaConstructorWrapper, /* call */
jsj_JavaConstructorWrapper, /* construct */
NULL, /* xdrObject */
JavaClass_hasInstance, /* hasInstance */
NULL, /* setProto */
NULL, /* setParent */
NULL, /* mark */
NULL, /* clear */
jsj_wrapper_getRequiredSlot, /* getRequiredSlot */
jsj_wrapper_setRequiredSlot /* setRequiredSlot */
};
JS_STATIC_DLL_CALLBACK(JSObjectOps *)
JavaClass_getObjectOps(JSContext *cx, JSClass *clazz)
{
return &JavaClass_ops;
}
JSClass JavaClass_class = {
"JavaClass", JSCLASS_HAS_PRIVATE,
NULL, NULL, NULL, NULL,
NULL, NULL, JavaClass_convert, JavaClass_finalize,
/* Optionally non-null members start here. */
JavaClass_getObjectOps,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
0,
};
static JSObject *
jsj_new_JavaClass(JSContext *cx, JNIEnv *jEnv, JSObject* parent_obj,
JavaClassDescriptor *class_descriptor)
{
JSObject *JavaClass_obj;
JavaClass_obj = JS_NewObject(cx, &JavaClass_class, 0, parent_obj);
if (!JavaClass_obj)
return NULL;
JS_SetPrivate(cx, JavaClass_obj, (void *)class_descriptor);
#ifdef DEBUG
/* printf("JavaClass \'%s\' created\n", class_descriptor->name); */
#endif
return JavaClass_obj;
}
JSObject *
jsj_define_JavaClass(JSContext *cx, JNIEnv *jEnv, JSObject* parent_obj,
const char *simple_class_name,
jclass java_class)
{
JavaClassDescriptor *class_descriptor;
JSObject *JavaClass_obj;
class_descriptor = jsj_GetJavaClassDescriptor(cx, jEnv, java_class);
if (!class_descriptor)
return NULL;
JavaClass_obj = jsj_new_JavaClass(cx, jEnv, parent_obj, class_descriptor);
if (!JavaClass_obj)
return NULL;
if (!JS_DefineProperty(cx, parent_obj, simple_class_name,
OBJECT_TO_JSVAL(JavaClass_obj), 0, 0,
JSPROP_PERMANENT|JSPROP_READONLY|JSPROP_ENUMERATE))
return NULL;
return JavaClass_obj;
}
/*
* The getClass() native JS method is defined as a property of the global
* object. Given a JavaObject it returns the corresponding JavaClass. This
* is useful for accessing static methods and fields.
*
* js> getClass(new java.lang.String("foo"))
* [JavaClass java.lang.String]
*/
JS_STATIC_DLL_CALLBACK(JSBool)
getClass(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
JSObject *obj_arg, *JavaClass_obj;
JavaObjectWrapper *java_wrapper;
JavaClassDescriptor *class_descriptor;
JNIEnv *jEnv;
JSJavaThreadState *jsj_env;
if (argc != 1 ||
!JSVAL_IS_OBJECT(argv[0]) ||
!(obj_arg = JSVAL_TO_OBJECT(argv[0])) ||
(!JS_InstanceOf(cx, obj_arg, &JavaObject_class, 0) &&
!JS_InstanceOf(cx, obj_arg, &JavaArray_class, 0))) {
JS_ReportErrorNumber(cx, jsj_GetErrorMessage, NULL,
JSJMSG_NEED_JOBJECT_ARG);
return JS_FALSE;
}
java_wrapper = JS_GetPrivate(cx, obj_arg);
if (!java_wrapper) {
JS_ReportErrorNumber(cx, jsj_GetErrorMessage, NULL,
JSJMSG_PROTO_GETCLASS);
return JS_FALSE;
}
jsj_env = jsj_EnterJava(cx, &jEnv);
if (!jEnv)
return JS_FALSE;
class_descriptor = java_wrapper->class_descriptor;
JavaClass_obj = jsj_new_JavaClass(cx, jEnv, NULL, class_descriptor);
if (!JavaClass_obj) {
jsj_ExitJava(jsj_env);
return JS_FALSE;
}
*rval = OBJECT_TO_JSVAL(JavaClass_obj);
jsj_ExitJava(jsj_env);
return JS_TRUE;
}
JS_STATIC_DLL_CALLBACK(JSBool)
JavaClass_construct(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
JSObject *obj_arg, *JavaClass_obj;
JavaObjectWrapper *java_wrapper;
JavaClassDescriptor *class_descriptor;
JNIEnv *jEnv;
JSJavaThreadState *jsj_env;
if (argc != 1 ||
!JSVAL_IS_OBJECT(argv[0]) ||
!(obj_arg = JSVAL_TO_OBJECT(argv[0])) ||
!JS_InstanceOf(cx, obj_arg, &JavaObject_class, 0) ||
((java_wrapper = JS_GetPrivate(cx, obj_arg)) == NULL)) {
JS_ReportErrorNumber(cx, jsj_GetErrorMessage, NULL,
JSJMSG_NEED_JCLASS_ARG);
return JS_FALSE;
}
jsj_env = jsj_EnterJava(cx, &jEnv);
if (!jEnv)
return JS_FALSE;
class_descriptor = java_wrapper->class_descriptor;
if (!(*jEnv)->IsSameObject(jEnv, class_descriptor->java_class, jlClass)) {
JS_ReportErrorNumber(cx, jsj_GetErrorMessage, NULL,
JSJMSG_NEED_JCLASS_ARG);
jsj_ExitJava(jsj_env);
return JS_FALSE;
}
class_descriptor = jsj_GetJavaClassDescriptor(cx, jEnv, java_wrapper->java_obj);
JavaClass_obj = jsj_new_JavaClass(cx, jEnv, NULL, class_descriptor);
if (!JavaClass_obj) {
jsj_ExitJava(jsj_env);
return JS_FALSE;
}
*rval = OBJECT_TO_JSVAL(JavaClass_obj);
jsj_ExitJava(jsj_env);
return JS_TRUE;
}
extern JS_IMPORT_DATA(JSObjectOps) js_ObjectOps;
JSBool
jsj_init_JavaClass(JSContext *cx, JSObject *global_obj)
{
/* Define JavaClass class */
if (!JS_InitClass(cx, global_obj, 0, &JavaClass_class, JavaClass_construct, 0, 0, 0, 0, 0))
return JS_FALSE;
if (!JS_DefineFunction(cx, global_obj, "getClass", getClass, 0,
JSPROP_READONLY))
return JS_FALSE;
return jsj_InitJavaClassReflectionsTable();
}