This new vendor-neutral version of LiveConnect is designed to replace the older
one in the js/jsj directory, which only works with the Netscape JVM.  It is part
of the OJI initiative.
This commit is contained in:
fur 1998-06-20 17:46:42 +00:00
Родитель ee281ae46f
Коммит e871f15495
19 изменённых файлов: 8579 добавлений и 0 удалений

Просмотреть файл

@ -0,0 +1,140 @@
# Microsoft Developer Studio Project File - Name="LiveConnect" - Package Owner=<4>
# Microsoft Developer Studio Generated Build File, Format Version 5.00
# ** DO NOT EDIT **
# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
CFG=LiveConnect - Win32 Debug
!MESSAGE This is not a valid makefile. To build this project using NMAKE,
!MESSAGE use the Export Makefile command and run
!MESSAGE
!MESSAGE NMAKE /f "LiveConnect.mak".
!MESSAGE
!MESSAGE You can specify a configuration when running NMAKE
!MESSAGE by defining the macro CFG on the command line. For example:
!MESSAGE
!MESSAGE NMAKE /f "LiveConnect.mak" CFG="LiveConnect - Win32 Debug"
!MESSAGE
!MESSAGE Possible choices for configuration are:
!MESSAGE
!MESSAGE "LiveConnect - Win32 Release" (based on\
"Win32 (x86) Dynamic-Link Library")
!MESSAGE "LiveConnect - Win32 Debug" (based on\
"Win32 (x86) Dynamic-Link Library")
!MESSAGE
# Begin Project
# PROP Scc_ProjName ""
# PROP Scc_LocalPath ""
CPP=cl.exe
MTL=midl.exe
RSC=rc.exe
!IF "$(CFG)" == "LiveConnect - Win32 Release"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 0
# PROP BASE Output_Dir "Release"
# PROP BASE Intermediate_Dir "Release"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 0
# PROP Output_Dir "Release"
# PROP Intermediate_Dir "Release"
# PROP Ignore_Export_Lib 0
# PROP Target_Dir ""
# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /FD /c
# ADD CPP /nologo /MD /W3 /GX /Zi /O2 /I ".." /I "$(JDK)\include" /I "$(JDK)\include\win32" /D "NDEBUG" /D "XP_PC" /D "JSFILE" /D "WIN32" /D "_WINDOWS" /YX /FD /c
# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o NUL /win32
# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o NUL /win32
# ADD BASE RSC /l 0x409 /d "NDEBUG"
# ADD RSC /l 0x409 /d "NDEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LINK32=link.exe
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib ..\Release\js32.lib $(JDK)\lib\javai.lib /nologo /subsystem:windows /dll /debug /machine:I386
!ELSEIF "$(CFG)" == "LiveConnect - Win32 Debug"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 1
# PROP BASE Output_Dir "Debug"
# PROP BASE Intermediate_Dir "Debug"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 1
# PROP Output_Dir "Debug"
# PROP Intermediate_Dir "Debug"
# PROP Ignore_Export_Lib 0
# PROP Target_Dir ""
# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /FD /c
# ADD CPP /nologo /MDd /W3 /Gm /GX /Zi /Od /I ".." /I "$(JDK)\include" /I "$(JDK)\include\win32" /D "_DEBUG" /D "_CONSOLE" /D "DEBUG" /D "XP_PC" /D "JSFILE" /D "WIN32" /D "_WINDOWS" /FR /YX /FD /c
# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o NUL /win32
# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o NUL /win32
# ADD BASE RSC /l 0x409 /d "_DEBUG"
# ADD RSC /l 0x409 /d "_DEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LINK32=link.exe
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386 /pdbtype:sept
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib ..\Debug\js32.lib $(JDK)\lib\javai_g.lib /nologo /subsystem:windows /dll /debug /machine:I386 /out:"Debug/LiveConnect_g.dll" /pdbtype:sept
!ENDIF
# Begin Target
# Name "LiveConnect - Win32 Release"
# Name "LiveConnect - Win32 Debug"
# Begin Source File
SOURCE=..\liveconnect\jsj.c
# End Source File
# Begin Source File
SOURCE=..\liveconnect\jsj_array.c
# End Source File
# Begin Source File
SOURCE=..\liveconnect\jsj_class.c
# End Source File
# Begin Source File
SOURCE=..\liveconnect\jsj_convert.c
# End Source File
# Begin Source File
SOURCE=..\liveconnect\jsj_field.c
# End Source File
# Begin Source File
SOURCE=.\jsj_JavaArray.c
# End Source File
# Begin Source File
SOURCE=.\jsj_JavaClass.c
# End Source File
# Begin Source File
SOURCE=.\jsj_JavaObject.c
# End Source File
# Begin Source File
SOURCE=.\jsj_JavaPackage.c
# End Source File
# Begin Source File
SOURCE=.\jsj_JSObject.c
# End Source File
# Begin Source File
SOURCE=..\liveconnect\jsj_method.c
# End Source File
# Begin Source File
SOURCE=..\liveconnect\jsj_utils.c
# End Source File
# End Target
# End Project

Просмотреть файл

Просмотреть файл

Просмотреть файл

@ -0,0 +1,288 @@
<HTML>
<HEAD>
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
<META NAME="Author" CONTENT="Scott Furman">
<META NAME="GENERATOR" CONTENT="Mozilla/4.05 [en] (WinNT; I) [Netscape]">
<TITLE>README for LiveConnect</TITLE>
</HEAD>
<BODY>
<CENTER>
<H2>
<FONT COLOR="#CC0000"><FONT SIZE=+3>WARNING:</FONT></FONT></H2></CENTER>
<CENTER>
<H2>
<FONT COLOR="#CC0000">This document is a skeleton, still&nbsp;<BR>
very much under construction.</FONT></H2></CENTER>
<H2>
Introduction</H2>
This is the README file for the JavaScript LiveConnect implementation.&nbsp;
It consists of build conventions and instructions, source code conventions,
a design walk-through, and a brief file-by-file description of the source.
<P>This document assumes basic familiarity with JSRef, the reference implementation
of JavaScript, and with the LiveConnect technology&nbsp; (LiveConnect allows
JavaScript and Java virtual machines to be connected.&nbsp; It enables
JavaScript to access Java fields, invoke Java methods and makes it possible
for Java to access JavaScript object properties and evaluate JavaScript.&nbsp;
More information on LiveConnect can be found by searching the index on
Netscape's <A HREF="http://developer.netscape.com">DevEdge site</A>.)
<P>JSRef builds a library or DLL containing the JavaScript runtime (compiler,
interpreter, decompiler, garbage collector, atom manager, standard classes).&nbsp;
The LiveConnect project/makefiles build a library that links both with
JSRef and with a Java Virtual Machine (JVM) that implements the Java Native
Interface (JNI), as specified by JavaSoft.&nbsp; It then compiles a small
"shell" program and links that with the library to make an interpreter
that can be used interactively and with test scripts.
<P><I>Scott Furman, 4/8/98</I>
<H2>
Build conventions</H2>
To enable multi-threaded execution, define the <TT><FONT SIZE=+1>JS_THREADSAFE</FONT></TT>
cpp macro and flesh out the stubs and required headers in jslock.c/.h.&nbsp;
See the JS API docs for more.&nbsp; JSRef must also be built with <TT><FONT SIZE=+1>JS_THREADSAFE</FONT></TT>.
<P>Update your JVM's <TT><FONT SIZE=+1>CLASSPATH</FONT></TT> to point to
the <TT><FONT SIZE=+1>liveconnect/classes</FONT></TT> subdirectory.&nbsp;
If you do not, LiveConnect will still operate but with the limitation that
JS objects may not be passed as arguments of Java methods and it will not
be possible to call from Java into JavaScript, i.e. the <I>netscape.javascript.JSObject</I>
class will be inaccessible.
<UL><B>Windows</B>
<UL>
<LI>
Use MSDEV5.0 (LiveConnectShell.dsw).</LI>
<LI>
You must first build the JS runtime, js32.dll, by using the normal JSRef
build procedure.</LI>
<LI>
Identify the JVM that you are linking against by setting the <TT><FONT SIZE=+1>JDK</FONT></TT>
environment variable to point to the top-level JDK directory, e.g. <TT><FONT SIZE=+1>D:\jdk1.1.5</FONT></TT>.&nbsp;
This is used to establish paths for header file inclusion, linking and
execution.&nbsp; If you are not using Sun's JVM, the project files may
require manual tweaking to set these paths correctly.</LI>
<LI>
The output files (DLLs and executables) are placed in the <TT><FONT SIZE=+1>js/ref/liveconnect/Debug</FONT></TT>
or <TT><FONT SIZE=+1>js/ref/liveconnect/Release</FONT></TT> directories.</LI>
<LI>
The LiveConnect-enabled shell is named <TT><FONT SIZE=+1>lcshell.exe</FONT></TT>
and appears in the output directory.</LI>
<LI>
You must have the JVM DLL in your <TT><FONT SIZE=+1>PATH</FONT></TT> environment
variable in order to run.&nbsp; If you are using the Sun JDK, the DLL appears
in the JDK's bin directory.</LI>
</UL>
<B>Mac</B>
<UL>
<LI>
<FONT COLOR="#FF0000">LiveConnect is known to build on the Mac, but no
project files created yet</FONT></LI>
<LI>
Use CodeWarrior 1.x (???.prj.hqx) or 2 (???.prj2.hqx).</LI>
</UL>
<B>Unix</B>
<UL>
<LI>
<FONT COLOR="#FF0000">No Makefiles created yet</FONT></LI>
<LI>
Use vendor cc or gcc (ftp://prep.ai.mit.edu/pub/gnu) for compiling,&nbsp;
and use gmake for building.</LI>
<LI>
To compile optimized code, set BUILD_OPT=1 on the nmake/gmake command line
or preset it in the environment or makefile.&nbsp; The C preprocessor macro
DEBUG&nbsp; will be undefined, and NDEBUG (archaic Unix-ism for "No Debugging")
will be defined.&nbsp; Without BUILD_OPT, DEBUG is predefined and NDEBUG
is undefined.</LI>
<LI>
Your own debug flag, DEBUG_$USER, will be defined or undefined as BUILD_OPT
is unset or set.</LI>
<LI>
To add C compiler options from the make command line, set XCFLAGS=-Dfoo.</LI>
<LI>
To predefine -D or -U options in the makefile, set DEFINES.</LI>
<LI>
To predefine -I options in the makefile, set INCLUDES.</LI>
</UL>
</UL>
<H2>
Naming and coding conventions:</H2>
<UL>
<LI>
Public function names begin with JSJ_ followed by capitalized "intercaps",&nbsp;
e.g. JSJ_ConnectToJavaVM.</LI>
<LI>
Extern but library-private function names use a jsj_ prefix and mixed case,
e.g. jsj_LookupSymbol.</LI>
<LI>
Most static function names have unprefixed, underscore-separated names:
get_char.</LI>
<LI>
But static native methods of JS objects have intercaps names, e.g., JavaObject_getProperty().</LI>
<LI>
And library-private and static data use underscores, not intercaps (but
library-private data do use a js_ prefix).</LI>
<LI>
Scalar type names are lowercase and js-prefixed: jsdouble.</LI>
<LI>
Aggregate type names are JS-prefixed and mixed-case: JSObject.</LI>
<LI>
Macros are generally ALL_CAPS and underscored, to call out potential side
effects, multiple uses of a formal argument, etc.</LI>
<LI>
Four spaces of indentation per statement nesting level.&nbsp; The files
are space-filled, so adjusting of your tab setting should be unnecessary.</LI>
<LI>
DLL entry points have their return type expanded within a PR_PUBLIC_API()&nbsp;
macro call, to get the right Windows secret type qualifiers in the right
places for both 16- and 32-bit builds.</LI>
<LI>
Callback functions that might be called from a DLL are similarly macroized
with PR_STATIC_CALLBACK (if the function otherwise would be static to hide
its name) or PR_CALLBACK (this macro takes no type argument; it should
be used after the return type and before the function name).</LI>
</UL>
<H2>
The LiveConnect API</H2>
All public LiveConnect entry points and callbacks are documented in jsjava.h,
the header file that exports those functions.
<H2>
Design walk-through</H2>
&nbsp;
<BR>&nbsp;
<H2>
File walk-through</H2>
&nbsp;
<TABLE BORDER=3 CELLSPACING=0 CELLPADDING=4 >
<TR>
<TD>jsjava.h</TD>
<TD>LiveConnect's only public header file.&nbsp; Defines all public API
entry points, callbacks and types.&nbsp;</TD>
</TR>
<TR>
<TD>jsj_private.h</TD>
<TD>LiveConnect internal header file for intra-module sharing of functions
and types</TD>
</TR>
<TR>
<TD>jsj.c</TD>
<TD>Public API entry points and initialization code</TD>
</TR>
<TR>
<TD>jsj_array.c</TD>
<TD>Read and write elements of a Java array using the JNI.</TD>
</TR>
<TR>
<TD>jsj_class.c</TD>
<TD>Construct and manipulate JavaClassDescriptor structs, which are the
native wrappers for Java classes.&nbsp; JavaClassDescriptors are used to
describe the signatures of methods and fields.&nbsp; There is a JavaClassDescriptor
associated with the reflection of each Java Object.</TD>
</TR>
<TR>
<TD>jsj_convert.c</TD>
<TD>Convert between Java and JavaScript values of all types, including
the wrapping of JS objects as Java objects and vice-versa.</TD>
</TR>
<TR>
<TD>jsj_field.c</TD>
<TD>Reflect Java fields as properties of JavaObject objects and implement
getter/setter access to those fields.</TD>
</TR>
<TR>
<TD>jsj_JavaArray.c</TD>
<TD>Implementation of the JavaScript JavaArray class.&nbsp; Instances of
JavaArray are used to reflect Java arrays.</TD>
</TR>
<TR>
<TD>jsj_JavaClass.c</TD>
<TD>Implementation of the JavaScript JavaClass class.&nbsp;&nbsp; Instances
of JavaClass are used to reflect Java classes.</TD>
</TR>
<TR>
<TD>jsj_JavaObject.c</TD>
<TD>Implementation of the JavaScript JavaObject class.&nbsp;&nbsp; Instances
of JavaObject are used to reflect Java objects, except for Java arrays,
although some of the code in this file is used by the JavaArray code.</TD>
</TR>
<TR>
<TD>jsj_JavaPackage.c</TD>
<TD>Implementation of the JavaScript JavaPackage class.&nbsp;&nbsp; Instances
of JavaPackage are used to reflect Java packages.</TD>
</TR>
<TR>
<TD>jsj_JSObject.c</TD>
<TD>Implementation of the native methods for the&nbsp; <I>netscape.javascript.JSObject</I>
Java class, which are used for calling into JavaScript from Java.&nbsp;
It also contains the code that handles propagation of exceptions both into
and out of Java.</TD>
</TR>
<TR>
<TD>jsj_method.c</TD>
<TD>Reflect Java methods as properties of JavaObject objects and make it
possible to invoke those methods.&nbsp; Includes overloaded method resolution
and argument/return-value conversion code.</TD>
</TR>
<TR>
<TD>jsj_utils.c</TD>
<TD>Low-level utility code fo reporting errors, etc.</TD>
</TR>
</TABLE>
&nbsp;
<BR>&nbsp;
</BODY>
</HTML>

737
js/ref/liveconnect/jsj.c Normal file
Просмотреть файл

@ -0,0 +1,737 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (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/NPL/
*
* 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.
*
* The Initial Developer of the Original Code is Netscape Communications
* Corporation. Portions created by Netscape are Copyright (C) 1998
* Netscape Communications Corporation. All Rights Reserved.
*/
/*
* This file is part of the Java-vendor-neutral implementation of LiveConnect
*
* It contains the top-level initialization code and the implementation of the
* public API.
*
*/
#include <stdlib.h>
#include <string.h>
#include "prtypes.h"
#include "prassert.h"
#include "prprintf.h"
#include "jsj_private.h" /* LiveConnect internals */
#include "jsjava.h" /* LiveConnect external API */
/* FIXME - The JNI environment should not be a global. It needs to be in thread-local storage. */
JNIEnv *jENV;
/*
* At certain times during initialization, there may be no JavaScript context
* available to direct error reports to, in which case the error messages
* are sent to this function. The caller is responsible for free'ing
* the js_error_msg argument.
*/
static void
report_java_initialization_error(JNIEnv *jEnv, const char *js_error_msg)
{
const char *error_msg, *java_error_msg;
java_error_msg = NULL;
if (jEnv) {
java_error_msg = jsj_GetJavaErrorMessage(jEnv);
(*jEnv)->ExceptionClear(jEnv);
}
if (java_error_msg) {
error_msg = PR_smprintf("initialization error: %s (%s)\n",
js_error_msg, java_error_msg);
free((void*)java_error_msg);
} else {
error_msg = PR_smprintf("initialization error: %s\n",
js_error_msg);
}
jsj_LogError(error_msg);
}
/*
* Opaque JVM handles to Java classes and methods required for Java reflection.
* These are computed and cached at initialization.
*/
jclass jlObject; /* java.lang.Object */
jclass jlrMethod; /* java.lang.reflect.Method */
jclass jlrField; /* java.lang.reflect.Field */
jclass jlVoid; /* java.lang.Void */
jclass jlrConstructor; /* java.lang.reflect.Constructor */
jclass jlThrowable; /* java.lang.Throwable */
jclass jlSystem; /* java.lang.System */
jclass jlClass; /* java.lang.Class */
jclass jlBoolean; /* java.lang.Boolean */
jclass jlDouble; /* java.lang.Double */
jclass jlString; /* java.lang.String */
jclass njJSObject; /* netscape.javascript.JSObject */
jclass njJSException; /* netscape.javascript.JSException */
jclass njJSUtil; /* netscape.javascript.JSUtil */
jmethodID jlClass_getMethods; /* java.lang.Class.getMethods() */
jmethodID jlClass_getConstructors; /* java.lang.Class.getConstructors() */
jmethodID jlClass_getFields; /* java.lang.Class.getFields() */
jmethodID jlClass_getName; /* java.lang.Class.getName() */
jmethodID jlClass_getComponentType; /* java.lang.Class.getComponentType() */
jmethodID jlClass_getModifiers; /* java.lang.Class.getModifiers() */
jmethodID jlClass_isArray; /* java.lang.Class.isArray() */
jmethodID jlrMethod_getName; /* java.lang.reflect.Method.getName() */
jmethodID jlrMethod_getParameterTypes; /* java.lang.reflect.Method.getParameterTypes() */
jmethodID jlrMethod_getReturnType; /* java.lang.reflect.Method.getReturnType() */
jmethodID jlrMethod_getModifiers; /* java.lang.reflect.Method.getModifiers() */
jmethodID jlrConstructor_getParameterTypes; /* java.lang.reflect.Constructor.getParameterTypes() */
jmethodID jlrConstructor_getModifiers; /* java.lang.reflect.Constructor.getModifiers() */
jmethodID jlrField_getName; /* java.lang.reflect.Field.getName() */
jmethodID jlrField_getType; /* java.lang.reflect.Field.getType() */
jmethodID jlrField_getModifiers; /* java.lang.reflect.Field.getModifiers() */
jmethodID jlBoolean_Boolean; /* java.lang.Boolean constructor */
jmethodID jlBoolean_booleanValue; /* java.lang.Boolean.booleanValue() */
jmethodID jlDouble_Double; /* java.lang.Double constructor */
jmethodID jlDouble_doubleValue; /* java.lang.Double.doubleValue() */
jmethodID jlThrowable_toString; /* java.lang.Throwable.toString() */
jmethodID jlThrowable_getMessage; /* java.lang.Throwable.getMessage() */
jmethodID jlSystem_identityHashCode; /* java.lang.System.identityHashCode() */
jobject jlVoid_TYPE; /* java.lang.Void.TYPE value */
jmethodID njJSException_JSException; /* netscape.javascript.JSexception constructor */
jmethodID njJSObject_JSObject; /* netscape.javascript.JSObject constructor */
jmethodID njJSUtil_getStackTrace; /* netscape.javascript.JSUtil.getStackTrace() */
jfieldID njJSObject_internal; /* netscape.javascript.JSObject.internal */
jfieldID njJSException_lineno; /* netscape.javascript.JSException.lineno */
jfieldID njJSException_tokenIndex; /* netscape.javascript.JSException.tokenIndex */
jfieldID njJSException_source; /* netscape.javascript.JSException.source */
jfieldID njJSException_filename; /* netscape.javascript.JSException.filename */
/* Obtain a reference to a Java class */
#define LOAD_CLASS(qualified_name, class) \
{ \
jclass _##class = (*jEnv)->FindClass(jEnv, #qualified_name); \
if (_##class == 0) { \
report_java_initialization_error(jEnv, \
"Can't load class " #qualified_name); \
return JS_FALSE; \
} \
class = (*jEnv)->NewGlobalRef(jEnv, _##class); \
}
/* Obtain a methodID reference to a Java method or constructor */
#define _LOAD_METHOD(qualified_class, method, mvar, signature, class, is_static)\
if (is_static) { \
class##_##mvar = \
(*jEnv)->GetStaticMethodID(jEnv, class, #method, signature); \
} else { \
class##_##mvar = \
(*jEnv)->GetMethodID(jEnv, class, #method, signature); \
} \
if (class##_##mvar == 0) { \
report_java_initialization_error(jEnv, \
"Can't get mid for " #qualified_class "." #method "()"); \
return JS_FALSE; \
}
/* Obtain a methodID reference to a Java instance method */
#define LOAD_METHOD(qualified_class, method, signature, class) \
_LOAD_METHOD(qualified_class, method, method, signature, class, JS_FALSE)
/* Obtain a methodID reference to a Java static method */
#define LOAD_STATIC_METHOD(qualified_class, method, signature, class) \
_LOAD_METHOD(qualified_class, method, method, signature, class, JS_TRUE)
/* Obtain a methodID reference to a Java constructor */
#define LOAD_CONSTRUCTOR(qualified_class, method, signature, class) \
_LOAD_METHOD(qualified_class,<init>, method, signature, class, JS_FALSE)
/* Obtain a fieldID reference to a Java instance or static field */
#define _LOAD_FIELDID(qualified_class, field, signature, class, is_static) \
if (is_static) { \
class##_##field = (*jEnv)->GetStaticFieldID(jEnv, class, #field, signature);\
} else { \
class##_##field = (*jEnv)->GetFieldID(jEnv, class, #field, signature);\
} \
if (class##_##field == 0) { \
report_java_initialization_error(jEnv, \
"Can't get fid for " #qualified_class "." #field); \
return JS_FALSE; \
}
/* Obtain a fieldID reference to a Java instance field */
#define LOAD_FIELDID(qualified_class, field, signature, class) \
_LOAD_FIELDID(qualified_class, field, signature, class, JS_FALSE)
/* Obtain the value of a static field in a Java class */
#define LOAD_FIELD_VAL(qualified_class, field, signature, class, type) \
{ \
jfieldID field_id; \
field_id = (*jEnv)->GetStaticFieldID(jEnv, class, #field, signature);\
if (field_id == 0) { \
report_java_initialization_error(jEnv, \
"Can't get fid for " #qualified_class "." #field); \
return JS_FALSE; \
} \
class##_##field = \
(*jEnv)->GetStatic##type##Field(jEnv, class, field_id); \
if (class##_##field == 0) { \
report_java_initialization_error(jEnv, \
"Can't read static field " #qualified_class "." #field); \
return JS_FALSE; \
} \
}
/* Obtain the value of a static field in a Java class, which is known to
contain an object value. */
#define LOAD_FIELD_OBJ(qualified_class, field, signature, class) \
LOAD_FIELD_VAL(qualified_class, field, signature, class, Object); \
class##_##field = (*jEnv)->NewGlobalRef(jEnv, class##_##field);
/*
* Load the Java classes, and the method and field descriptors required for Java reflection.
* Returns JS_TRUE on success, JS_FALSE on failure.
*/
static JSBool
init_java_VM_reflection(JSJavaVM *jsjava_vm, JNIEnv *jEnv)
{
/* Load Java system classes and method, including java.lang.reflect classes */
LOAD_CLASS(java/lang/Object, jlObject);
LOAD_CLASS(java/lang/Class, jlClass);
LOAD_CLASS(java/lang/reflect/Method, jlrMethod);
LOAD_CLASS(java/lang/reflect/Constructor, jlrConstructor);
LOAD_CLASS(java/lang/reflect/Field, jlrField);
LOAD_CLASS(java/lang/Throwable, jlThrowable);
LOAD_CLASS(java/lang/System, jlSystem);
LOAD_CLASS(java/lang/Boolean, jlBoolean);
LOAD_CLASS(java/lang/Double, jlDouble);
LOAD_CLASS(java/lang/String, jlString);
LOAD_CLASS(java/lang/Void, jlVoid);
LOAD_METHOD(java.lang.Class, getMethods, "()[Ljava/lang/reflect/Method;",jlClass);
LOAD_METHOD(java.lang.Class, getConstructors, "()[Ljava/lang/reflect/Constructor;",jlClass);
LOAD_METHOD(java.lang.Class, getFields, "()[Ljava/lang/reflect/Field;", jlClass);
LOAD_METHOD(java.lang.Class, getName, "()Ljava/lang/String;", jlClass);
LOAD_METHOD(java.lang.Class, isArray, "()Z", jlClass);
LOAD_METHOD(java.lang.Class, getComponentType, "()Ljava/lang/Class;", jlClass);
LOAD_METHOD(java.lang.Class, getModifiers, "()I", jlClass);
LOAD_METHOD(java.lang.reflect.Method, getName, "()Ljava/lang/String;", jlrMethod);
LOAD_METHOD(java.lang.reflect.Method, getParameterTypes, "()[Ljava/lang/Class;", jlrMethod);
LOAD_METHOD(java.lang.reflect.Method, getReturnType, "()Ljava/lang/Class;", jlrMethod);
LOAD_METHOD(java.lang.reflect.Method, getModifiers, "()I", jlrMethod);
LOAD_METHOD(java.lang.reflect.Constructor, getParameterTypes, "()[Ljava/lang/Class;", jlrConstructor);
LOAD_METHOD(java.lang.reflect.Constructor, getModifiers, "()I", jlrConstructor);
LOAD_METHOD(java.lang.reflect.Field, getName, "()Ljava/lang/String;", jlrField);
LOAD_METHOD(java.lang.reflect.Field, getType, "()Ljava/lang/Class;", jlrField);
LOAD_METHOD(java.lang.reflect.Field, getModifiers, "()I", jlrField);
LOAD_METHOD(java.lang.Throwable, toString, "()Ljava/lang/String;", jlThrowable);
LOAD_METHOD(java.lang.Throwable, getMessage, "()Ljava/lang/String;", jlThrowable);
LOAD_METHOD(java.lang.Double, doubleValue, "()D", jlDouble);
LOAD_METHOD(java.lang.Boolean, booleanValue, "()Z", jlBoolean);
LOAD_STATIC_METHOD(java.lang.System, identityHashCode, "(Ljava/lang/Object;)I", jlSystem);
LOAD_CONSTRUCTOR(java.lang.Boolean, Boolean, "(Z)V", jlBoolean);
LOAD_CONSTRUCTOR(java.lang.Double, Double, "(D)V", jlDouble);
LOAD_FIELD_OBJ(java.lang.Void, TYPE, "Ljava/lang/Class;", jlVoid);
}
/* Load Netscape-specific Java extension classes, methods, and fields */
static JSBool
init_netscape_java_classes(JSJavaVM *jsjava_vm, JNIEnv *jEnv)
{
LOAD_CLASS(netscape/javascript/JSObject, njJSObject);
LOAD_CLASS(netscape/javascript/JSException, njJSException);
LOAD_CLASS(netscape/javascript/JSUtil, njJSUtil);
LOAD_CONSTRUCTOR(netscape.javascript.JSObject,
JSObject, "(I)V", njJSObject);
LOAD_CONSTRUCTOR(netscape.javascript.JSException,
JSException, "(Ljava/lang/String;Ljava/lang/String;ILjava/lang/String;I)V",
njJSException);
LOAD_FIELDID(netscape.javascript.JSObject,
internal, "I", njJSObject);
LOAD_FIELDID(netscape.javascript.JSException,
lineno, "I", njJSException);
LOAD_FIELDID(netscape.javascript.JSException,
tokenIndex, "I", njJSException);
LOAD_FIELDID(netscape.javascript.JSException,
source, "Ljava/lang/String;", njJSException);
LOAD_FIELDID(netscape.javascript.JSException,
filename, "Ljava/lang/String;", njJSException);
LOAD_STATIC_METHOD(netscape.javascript.JSUtil,
getStackTrace, "(Ljava/lang/Throwable;)Ljava/lang/String;",
njJSUtil);
return JS_TRUE;
}
JSJavaVM *jsjava_vm_list = NULL;
/*
* Called once per Java VM, this function initializes the classes, fields, and
* methods required for Java reflection. If java_vm is NULL, a new Java VM is
* created, using the provided classpath in addition to any default classpath.
* The classpath argument is ignored, however, if java_vm_arg is non-NULL.
*/
JSJavaVM *
JSJ_ConnectToJavaVM(JavaVM *java_vm_arg, const char *user_classpath)
{
JavaVM *java_vm;
JSJavaVM *jsjava_vm;
const char *full_classpath;
jsjava_vm = (JSJavaVM*)malloc(sizeof(JSJavaVM));
if (!jsjava_vm)
return NULL;
memset(jsjava_vm, 0, sizeof(JSJavaVM));
java_vm = java_vm_arg;
/* If a Java VM was passed in, try to attach to it on the current thread. */
if (java_vm) {
if ((*java_vm)->AttachCurrentThread(java_vm, &jENV, NULL) < 0) {
jsj_LogError("Failed to attach to Java VM thread\n");
free(jsjava_vm);
return NULL;
}
} else {
/* No Java VM supplied, so create our own */
JDK1_1InitArgs vm_args;
/* Magic constant indicates JRE version 1.1 */
vm_args.version = 0x00010001;
JNI_GetDefaultJavaVMInitArgs(&vm_args);
/* Prepend the classpath argument to the default JVM classpath */
if (user_classpath) {
full_classpath = PR_smprintf("%s;%s", user_classpath, vm_args.classpath);
if (!full_classpath) {
free(jsjava_vm);
return NULL;
}
vm_args.classpath = (char*)full_classpath;
}
/* Attempt to create our own VM */
if (JNI_CreateJavaVM(&java_vm, &jENV, &vm_args) < 0) {
jsj_LogError("Failed to create Java VM\n");
free(jsjava_vm);
return NULL;
}
/* Remember that we created the VM so that we know to destroy it later */
jsjava_vm->jsj_created_java_vm = JS_TRUE;
}
jsjava_vm->java_vm = java_vm;
jsjava_vm->main_thread_env = jENV;
/* Load the Java classes, and the method and field descriptors required for
Java reflection. */
if (!init_java_VM_reflection(jsjava_vm, jENV)) {
JSJ_DisconnectFromJavaVM(jsjava_vm);
return NULL;
}
/*
* JVM initialization for netscape.javascript.JSObject is performed
* independently of the other classes that are initialized in
* init_java_VM_reflection, because we allow it to fail. In the case
* of failure, LiveConnect is still operative, but only when calling
* from JS to Java and not vice-versa.
*/
init_netscape_java_classes(jsjava_vm, jENV);
/* Put this VM on the list of all created VMs */
jsjava_vm->next = jsjava_vm_list;
jsjava_vm_list = jsjava_vm;
return jsjava_vm;
}
JSJCallbacks *JSJ_callbacks = NULL;
/* Called once to set up callbacks for all instances of LiveConnect */
void
JSJ_Init(JSJCallbacks *callbacks)
{
PR_ASSERT(callbacks);
JSJ_callbacks = callbacks;
}
/*
* Initialize the provided JSContext by setting up the JS classes necessary for
* reflection and by defining JavaPackage objects for the default Java packages
* as properties of global_obj. Additional packages may be pre-defined by
* setting the predefined_packages argument. (Pre-defining a Java package at
* initialization time is not necessary, but it will make package lookup faster
* and, more importantly, will avoid unnecessary network accesses if classes
* are being loaded over the network.)
*/
JSJ_InitJSContext(JSContext *cx, JSObject *global_obj,
JavaPackageDef *predefined_packages)
{
/* Initialize the JavaScript classes used for reflection */
if (!jsj_init_JavaObject(cx, global_obj))
return JS_FALSE;
/* if (!jsj_init_JavaMember(cx, global_obj))
return JS_FALSE; */
if (!jsj_init_JavaPackage(cx, global_obj, predefined_packages))
return JS_FALSE;
if (!jsj_init_JavaClass(cx, global_obj))
return JS_FALSE;
if (!jsj_init_JavaArray(cx, global_obj))
return JS_FALSE;
return JS_TRUE;
}
/* Obtain a reference to a Java class */
#define UNLOAD_CLASS(qualified_name, class) \
if (class) { \
(*jENV)->DeleteGlobalRef(jENV, class); \
class = NULL; \
}
/*
* This routine severs the connection to a Java VM, freeing all related resources.
* It shouldn't be called until the global scope has been cleared in all related
* JSContexts (so that all LiveConnect objects are finalized) and a JavaScript
* GC is performed. Otherwise, accessed to free'ed memory could result.
*/
void
JSJ_DisconnectFromJavaVM(JSJavaVM *jsjava_vm)
{
/* FIXME - Clean up the various hash tables */
if (jsjava_vm->jsj_created_java_vm) {
JavaVM *java_vm = jsjava_vm->java_vm;
(*java_vm)->DestroyJavaVM(java_vm);
} else {
UNLOAD_CLASS(java/lang/Object, jlObject);
UNLOAD_CLASS(java/lang/Class, jlClass);
UNLOAD_CLASS(java/lang/reflect/Method, jlrMethod);
UNLOAD_CLASS(java/lang/reflect/Constructor, jlrConstructor);
UNLOAD_CLASS(java/lang/reflect/Field, jlrField);
UNLOAD_CLASS(java/lang/Throwable, jlThrowable);
UNLOAD_CLASS(java/lang/System, jlSystem);
UNLOAD_CLASS(java/lang/Boolean, jlBoolean);
UNLOAD_CLASS(java/lang/Double, jlDouble);
UNLOAD_CLASS(java/lang/String, jlString);
UNLOAD_CLASS(java/lang/Void, jlVoid);
UNLOAD_CLASS(netscape/javascript/JSObject, njJSObject);
UNLOAD_CLASS(netscape/javascript/JSException, njJSException);
UNLOAD_CLASS(netscape/javascript/JSUtil, njJSUtil);
}
}
static JSJavaThreadState *thread_list = NULL;
static JSJavaThreadState *
new_jsjava_thread_state(JSJavaVM *jsjava_vm, const char *thread_name, JNIEnv *jEnv)
{
JSJavaThreadState *jsj_env;
jsj_env = (JSJavaThreadState *)malloc(sizeof(JSJavaThreadState));
if (!jsj_env)
return NULL;
memset(jsj_env, 0, sizeof(JSJavaThreadState));
jsj_env->jEnv = jEnv;
jsj_env->jsjava_vm = jsjava_vm;
if (thread_name)
jsj_env->name = strdup(thread_name);
/* FIXME - need to protect against races */
jsj_env->next = thread_list;
thread_list = jsj_env;
return jsj_env;
}
static JSJavaThreadState *
find_jsjava_thread(JNIEnv *jEnv)
{
JSJavaThreadState *e, **p, *jsj_env;
jsj_env = NULL;
/* FIXME - need to protect against races in manipulating the thread list */
/* Search for the thread state among the list of all created
LiveConnect threads */
for (p=&thread_list; e = *p; p = &(e->next)) {
if (e->jEnv == jEnv) {
jsj_env = e;
*p = jsj_env->next;
break;
}
}
/* Move a found thread to head of list for faster search next time. */
if (jsj_env)
thread_list = jsj_env;
return jsj_env;
}
PR_PUBLIC_API(JSJavaThreadState *)
JSJ_AttachCurrentThreadToJava(JSJavaVM *jsjava_vm, const char *name, JNIEnv **java_envp)
{
JNIEnv *jEnv;
JavaVM *java_vm;
JSJavaThreadState *jsj_env;
/* Try to attach a Java thread to the current native thread */
java_vm = jsjava_vm->java_vm;
if ((*java_vm)->AttachCurrentThread(java_vm, &jEnv, NULL) < 0)
return NULL;
/* If we found an existing thread state, just return it. */
jsj_env = find_jsjava_thread(jEnv);
if (jsj_env)
return jsj_env;
/* Create a new wrapper around the thread/VM state */
jsj_env = new_jsjava_thread_state(jsjava_vm, name, jEnv);
if (java_envp)
*java_envp = jEnv;
return jsj_env;
}
static JSJavaVM *
map_java_vm_to_jsjava_vm(JavaVM *java_vm)
{
JSJavaVM *v;
for (v = jsjava_vm_list; v; v = v->next) {
if (v->java_vm == java_vm)
return v;
}
return NULL;
}
/*
* Unfortunately, there's no standard means to associate any private data with
* a JNI thread environment, so we need to use the Java environment pointer as
* the key in a lookup table that maps it to a JSJavaThreadState structure,
* where we store all our per-thread private data. If no existing thread state
* is found, a new one is created.
*
* If an error occurs, returns NULL and sets the errp argument to an error
* message, which the caller is responsible for free'ing.
*/
JSJavaThreadState *
jsj_MapJavaThreadToJSJavaThreadState(JNIEnv *jEnv, char **errp)
{
JSJavaThreadState *jsj_env;
JavaVM *java_vm;
JSJavaVM *jsjava_vm;
/* If we found an existing thread state, just return it. */
jsj_env = find_jsjava_thread(jEnv);
if (jsj_env)
return jsj_env;
/* No one set up a LiveConnect thread state for a given Java thread.
Invoke the callback to create one on-the-fly. */
/* First, figure out which Java VM is calling us */
if ((*jEnv)->GetJavaVM(jEnv, &java_vm) < 0)
return NULL;
/* Get our private JavaVM data */
jsjava_vm = map_java_vm_to_jsjava_vm(java_vm);
if (!jsjava_vm) {
*errp = PR_smprintf("Total weirdness: No JSJavaVM wrapper ever created "
"for JavaVM 0x%08x", java_vm);
return NULL;
}
jsj_env = new_jsjava_thread_state(jsjava_vm, NULL, jEnv);
if (!jsj_env)
return NULL;
return jsj_env;
}
/*
* This function is used to specify a particular JSContext as *the* JavaScript
* execution environment to be used when LiveConnect is accessed from the given
* Java thread, i.e. by using one of the methods of netscape.javascript.JSObject.
* (There can only be one such JS context for a given Java thread. To
* multiplex JSContexts among a single thread, this function must be called
* before Java is invoked on that thread.) The return value is the previous
* context associated with the given Java thread.
*/
PR_PUBLIC_API(JSContext *)
JSJ_SetDefaultJSContextForJavaThread(JSContext *cx, JSJavaThreadState *jsj_env)
{
JSContext *old_context;
old_context = jsj_env->cx;
jsj_env->cx = cx;
return old_context;
}
PR_PUBLIC_API(JSBool)
JSJ_DetachCurrentThreadFromJava(JSJavaThreadState *jsj_env)
{
JavaVM *java_vm;
JSJavaThreadState *e, **p;
/* Disassociate the current native thread from its corresponding Java thread */
java_vm = jsj_env->jsjava_vm->java_vm;
if ((*java_vm)->DetachCurrentThread(java_vm) < 0)
return JS_FALSE;
/* Destroy the LiveConnect execution environment passed in */
jsj_ClearPendingJSErrors(jsj_env);
/* FIXME - need to protect against races */
for (p=&thread_list; e = *p; p = &(e->next)) {
if (e == jsj_env) {
*p = jsj_env->next;
break;
}
}
free(jsj_env);
return JS_TRUE;
}
JSBool
JSJ_ConvertJavaObjectToJSValue(JSContext *cx, jobject java_obj, jsval *vp)
{
JNIEnv *jEnv;
/* Get the Java per-thread environment pointer for this JSContext */
jsj_MapJSContextToJSJThread(cx, &jEnv);
if (!jEnv)
return JS_FALSE;
jsj_ConvertJavaObjectToJSValue(cx, jEnv, java_obj, vp);
}
/*===========================================================================*/
/* The convenience functions below present a complete, but simplified
LiveConnect API which is designed to handle the special case of a single
Java-VM, single-threaded operation, and use of only one JSContext. */
/* We can get away with global variables in our single-threaded,
single-JSContext case. */
static JSJavaVM * the_jsj_vm = NULL;
static JSContext * the_cx = NULL;
static JSJavaThreadState * the_jsj_thread = NULL;
static JSObject * the_global_js_obj = NULL;
/* Trivial implementation of callback function */
static JSJavaThreadState *
default_map_js_context_to_jsj_thread(JSContext *cx, char **errp)
{
return the_jsj_thread;
}
/* Trivial implementation of callback function */
static JSContext *
default_map_jsj_thread_to_js_context(JSJavaThreadState *jsj_env, char **errp)
{
return the_cx;
}
/* Trivial implementation of callback function */
JSObject *
default_map_java_object_to_js_object(JNIEnv *jEnv, jobject hint, char **errp)
{
return the_global_js_obj;
}
/* Trivial implementations of callback functions */
JSJCallbacks jsj_default_callbacks = {
default_map_jsj_thread_to_js_context,
default_map_js_context_to_jsj_thread,
default_map_java_object_to_js_object
};
/*
* Initialize the provided JSContext by setting up the JS classes necessary for
* reflection and by defining JavaPackage objects for the default Java packages
* as properties of global_obj. If java_vm is NULL, a new Java VM is
* created, using the provided classpath in addition to any default classpath.
* The classpath argument is ignored, however, if java_vm is non-NULL.
*/
JSBool
JSJ_SimpleInit(JSContext *cx, JSObject *global_obj, JavaVM *java_vm, const char *classpath)
{
PR_ASSERT(!the_jsj_vm);
the_jsj_vm = JSJ_ConnectToJavaVM(java_vm, classpath);
if (!the_jsj_vm)
return JS_FALSE;
JSJ_Init(&jsj_default_callbacks);
if (!JSJ_InitJSContext(cx, global_obj, NULL))
goto error;
the_cx = cx;
the_global_js_obj = global_obj;
the_jsj_thread = JSJ_AttachCurrentThreadToJava(the_jsj_vm, "main thread", &jENV);
if (!the_jsj_thread)
goto error;
return JS_TRUE;
error:
JSJ_SimpleShutdown();
return JS_FALSE;
}
/*
* Free up all LiveConnect resources. Destroy the Java VM if it was
* created by LiveConnect.
*/
PR_PUBLIC_API(void)
JSJ_SimpleShutdown()
{
PR_ASSERT(the_jsj_vm);
JSJ_DisconnectFromJavaVM(the_jsj_vm);
the_jsj_vm = NULL;
the_cx = NULL;
the_global_js_obj = NULL;
the_jsj_thread = NULL;
}

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -0,0 +1,447 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (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/NPL/
*
* 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.
*
* The Initial Developer of the Original Code is Netscape Communications
* Corporation. Portions created by Netscape are Copyright (C) 1998
* Netscape Communications Corporation. All Rights Reserved.
*/
/*
* This file is part of the Java-vendor-neutral implementation of LiveConnect
*
* It contains the definition of the JavaScript JavaArray class.
* Instances of JavaArray are used to reflect Java arrays.
*/
#include <stdlib.h>
#include <string.h>
#include "prassert.h"
#include "jsj_private.h" /* LiveConnect internals */
static JSBool
access_java_array_element(JSContext *cx,
JNIEnv *jEnv,
JSObject *obj,
jsid id,
jsval *vp,
JSBool do_assignment)
{
jsval idval;
jarray java_array;
JavaClassDescriptor *class_descriptor;
JavaObjectWrapper *java_wrapper;
jsize array_length, index;
JavaSignature *array_component_signature;
/* printf("In JavaArray_getProperty\n"); */
java_wrapper = JS_GetPrivate(cx, obj);
if (!java_wrapper) {
const char *property_name;
if (JS_IdToValue(cx, id, &idval) && JSVAL_IS_STRING(idval) &&
(property_name = JS_GetStringBytes(JSVAL_TO_STRING(idval)))) {
if (!strcmp(property_name, "constructor")) {
*vp = JSVAL_VOID;
return JS_TRUE;
}
}
JS_ReportError(cx, "illegal operation on JavaArray prototype object");
return JS_FALSE;
}
class_descriptor = java_wrapper->class_descriptor;
java_array = java_wrapper->java_obj;
PR_ASSERT(class_descriptor->type == JAVA_SIGNATURE_ARRAY);
JS_IdToValue(cx, id, &idval);
if (!JSVAL_IS_INT(idval)) {
/*
* Usually, properties of JavaArray objects are indexed by integers, but
* Java arrays also inherit all the methods of java.lang.Object, so a
* string-valued property is also possible.
*/
if (JSVAL_IS_STRING(idval)) {
const char *member_name;
member_name = JS_GetStringBytes(JSVAL_TO_STRING(idval));
if (do_assignment) {
JS_ReportError(cx, "Attempt to write to invalid Java array "
"element \"%s\"", member_name);
return JS_FALSE;
} else {
if (!strcmp(member_name, "length")) {
array_length = jsj_GetJavaArrayLength(cx, jEnv, java_array);
if (array_length < 0)
return JS_FALSE;
if (vp)
*vp = INT_TO_JSVAL(array_length);
return JS_TRUE;
}
/* Check to see if we're reflecting a Java array method */
return JavaObject_getPropertyById(cx, obj, id, vp);
}
}
JS_ReportError(cx, "invalid Java array index expression");
return JS_FALSE;
}
index = JSVAL_TO_INT(idval);
#if 0
array_length = jsj_GetJavaArrayLength(cx, jEnv, java_array);
if (array_length < 0)
return JS_FALSE;
/* Just let Java throw an exception instead of checking array bounds here */
if (index < 0 || index >= array_length) {
JS_ReportError(cx, "Java array index %d out of range", index);
return JS_FALSE;
}
#endif
array_component_signature = class_descriptor->array_component_signature;
if (!vp)
return JS_TRUE;
if (do_assignment) {
return jsj_SetJavaArrayElement(cx, jEnv, java_array, index,
array_component_signature, *vp);
} else {
return jsj_GetJavaArrayElement(cx, jEnv, java_array, index,
array_component_signature, vp);
}
}
PR_STATIC_CALLBACK(JSBool)
JavaArray_getPropertyById(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
JNIEnv *jEnv;
jsj_MapJSContextToJSJThread(cx, &jEnv);
if (!jEnv)
return JS_FALSE;
return access_java_array_element(cx, jEnv, obj, id, vp, JS_FALSE);
}
PR_STATIC_CALLBACK(JSBool)
JavaArray_setPropertyById(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
JNIEnv *jEnv;
jsj_MapJSContextToJSJThread(cx, &jEnv);
if (!jEnv)
return JS_FALSE;
return access_java_array_element(cx, jEnv, obj, id, vp, JS_TRUE);
}
#ifdef NO_JSOBJECTOPS
PR_STATIC_CALLBACK(JSBool)
JavaArray_enumerate(JSContext *cx, JSObject *obj)
{
jarray java_array;
JavaClassDescriptor *class_descriptor;
JavaObjectWrapper *java_wrapper;
jsize array_length, index;
/* printf("In JavaArray_enumerate\n"); */
java_wrapper = JS_GetPrivate(cx, obj);
/* Check if this is the prototype object */
if (!java_wrapper)
return JS_TRUE;
class_descriptor = java_wrapper->class_descriptor;
java_array = java_wrapper->java_obj;
PR_ASSERT(class_descriptor->type == JAVA_SIGNATURE_ARRAY);
array_length = (*jEnv)->GetArrayLength(jEnv, java_array);
for (index = 0; index < array_length; index++) {
if (!JS_DefineElement(cx, obj, index, JSVAL_VOID,
0, 0, JSPROP_ENUMERATE))
return JS_FALSE;
}
return JavaObject_enumerate(cx, obj);
}
JSBool
JavaArray_getProperty(JSContext *cx, JSObject *obj, jsval idval, jsval *vp)
{
jsid id;
JS_ValueToId(cx, idval, &id);
return JavaArray_getPropertyById(cx, obj, id, vp);
}
PR_STATIC_CALLBACK(JSBool)
JavaArray_setProperty(JSContext *cx, JSObject *obj, jsval idval, jsval *vp)
{
jsid id;
JS_ValueToId(cx, idval, &id);
return JavaArray_setPropertyById(cx, obj, id, vp);
}
/*
* Resolve a component name to be either the name of a static field or a static method
*/
PR_STATIC_CALLBACK(JSBool)
JavaArray_resolve(JSContext *cx, JSObject *obj, jsval id)
{
jarray java_array;
const char *member_name;
JavaClassDescriptor *class_descriptor;
JavaObjectWrapper *java_wrapper;
jsize array_length, index;
/* printf("In JavaArray_resolve\n"); */
java_wrapper = JS_GetPrivate(cx, obj);
/* Check for prototype object */
if (!java_wrapper)
return JS_FALSE;
class_descriptor = java_wrapper->class_descriptor;
java_array = java_wrapper->java_obj;
PR_ASSERT(class_descriptor->type == JAVA_SIGNATURE_ARRAY);
array_length = (*jEnv)->GetArrayLength(jEnv, java_array);
if (JSVAL_IS_STRING(id)) {
member_name = JS_GetStringBytes(JSVAL_TO_STRING(id));
if (!strcmp(member_name, "length")) {
JS_DefineProperty(cx, obj, "length", INT_TO_JSVAL(array_length),
0, 0, JSPROP_READONLY);
return JS_TRUE;
}
return JavaObject_resolve(cx, obj, id);
}
if (!JSVAL_IS_INT(id)) {
JS_ReportError(cx, "invalid Java array index expression");
return JS_FALSE;
}
index = JSVAL_TO_INT(id);
return JS_DefineElement(cx, obj, index, JSVAL_VOID,
0, 0, JSPROP_ENUMERATE);
}
/* The definition of the JS class JavaArray */
JSClass JavaArray_class = {
"JavaArray", JSCLASS_HAS_PRIVATE,
JS_PropertyStub, JS_PropertyStub, JavaArray_getProperty, JavaArray_setProperty,
JavaArray_enumerate, JavaArray_resolve,
JavaObject_convert, JavaObject_finalize
};
#else
static JSBool
JavaArray_lookupProperty(JSContext *cx, JSObject *obj, jsid id,
JSObject **objp, JSProperty **propp
#if defined JS_THREADSAFE && defined DEBUG
, const char *file, uintN line
#endif
)
{
JNIEnv *jEnv;
jsj_MapJSContextToJSJThread(cx, &jEnv);
if (!jEnv)
return JS_FALSE;
return access_java_array_element(cx, jEnv, obj, id, NULL, JS_FALSE);
}
static JSBool
JavaArray_defineProperty(JSContext *cx, JSObject *obj, jsid id, jsval value,
JSPropertyOp getter, JSPropertyOp setter,
uintN attrs, JSProperty **propp)
{
JS_ReportError(cx, "Elements of JavaArray objects may not be deleted");
return JS_FALSE;
}
static JSBool
JavaArray_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;
}
static JSBool
JavaArray_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) {
PR_ASSERT(0);
return JS_FALSE;
}
/* Silently ignore all setAttribute attempts */
return JS_TRUE;
}
static JSBool
JavaArray_deleteProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
{
JS_ReportError(cx, "Elements of JavaArray objects may not be deleted");
return JS_FALSE;
}
static JSBool
JavaArray_defaultValue(JSContext *cx, JSObject *obj, JSType type, jsval *vp)
{
printf("In JavaArray_defaultValue()\n");
return JavaObject_convert(cx, obj, JSTYPE_STRING, vp);
}
static JSBool
JavaArray_newEnumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
jsval *statep, jsid *idp)
{
JavaObjectWrapper *java_wrapper;
JNIEnv *jEnv;
jsize array_length, index;
java_wrapper = JS_GetPrivate(cx, obj);
/* Check for prototype object */
if (!java_wrapper) {
*statep = JSVAL_NULL;
if (idp)
*idp = INT_TO_JSVAL(0);
return JS_TRUE;
}
/* Get the Java per-thread environment pointer for this JSContext */
jsj_MapJSContextToJSJThread(cx, &jEnv);
if (!jEnv)
return JS_FALSE;
array_length = jsj_GetJavaArrayLength(cx, jEnv, java_wrapper->java_obj);
if (array_length < 0)
return JS_FALSE;
switch(enum_op) {
case JSENUMERATE_INIT:
*statep = INT_TO_JSVAL(0);
if (idp)
*idp = INT_TO_JSVAL(array_length);
return JS_TRUE;
case JSENUMERATE_NEXT:
index = JSVAL_TO_INT(*statep);
if (index < array_length) {
JS_ValueToId(cx, INT_TO_JSVAL(index), idp);
index++;
*statep = INT_TO_JSVAL(index);
return JS_TRUE;
}
/* Fall through ... */
case JSENUMERATE_DESTROY:
*statep = JSVAL_NULL;
return JS_TRUE;
default:
PR_ASSERT(0);
return JS_FALSE;
}
}
static JSBool
JavaArray_checkAccess(JSContext *cx, JSObject *obj, jsid id,
JSAccessMode mode, jsval *vp, uintN *attrsp)
{
switch (mode) {
case JSACC_WATCH:
JS_ReportError(cx, "Cannot place watchpoints on JavaArray object properties");
return JS_FALSE;
case JSACC_IMPORT:
JS_ReportError(cx, "Cannot export a JavaArray object's properties");
return JS_FALSE;
default:
return JS_TRUE;
}
}
JSObjectOps JavaArray_ops = {
/* Mandatory non-null function pointer members. */
NULL, /* newObjectMap */
NULL, /* destroyObjectMap */
JavaArray_lookupProperty,
JavaArray_defineProperty,
JavaArray_getPropertyById, /* getProperty */
JavaArray_setPropertyById, /* setProperty */
JavaArray_getAttributes,
JavaArray_setAttributes,
JavaArray_deleteProperty,
JavaArray_defaultValue,
JavaArray_newEnumerate,
JavaArray_checkAccess,
/* Optionally non-null members start here. */
NULL, /* thisObject */
NULL, /* dropProperty */
NULL, /* call */
NULL, /* construct */
NULL, /* xdrObject */
NULL /* hasInstance */
};
JSObjectOps *
JavaArray_getObjectOps(JSContext *cx, JSClass *clazz)
{
return &JavaArray_ops;
}
JSClass JavaArray_class = {
"JavaArray", JSCLASS_HAS_PRIVATE,
NULL, NULL, NULL, NULL,
NULL, NULL, JavaObject_convert, JavaObject_finalize,
JavaArray_getObjectOps,
};
extern PR_IMPORT_DATA(JSObjectOps) js_ObjectOps;
#endif /* NO_JSOBJECTOPS */
/* Initialize the JS JavaArray class */
JSBool
jsj_init_JavaArray(JSContext *cx, JSObject *global_obj)
{
#ifndef NO_JSOBJECTOPS
JavaArray_ops.newObjectMap = js_ObjectOps.newObjectMap;
JavaArray_ops.destroyObjectMap = js_ObjectOps.destroyObjectMap;
#endif
if (!JS_InitClass(cx, global_obj,
0, &JavaArray_class, 0, 0,
0, 0, 0, 0))
return JS_FALSE;
return JS_TRUE;
}

Просмотреть файл

@ -0,0 +1,604 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (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/NPL/
*
* 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.
*
* The Initial Developer of the Original Code is Netscape Communications
* Corporation. Portions created by Netscape are Copyright (C) 1998
* Netscape Communications Corporation. All Rights Reserved.
*/
/* 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 "prtypes.h"
#include "prprintf.h"
#include "prassert.h"
#include "jsj_private.h" /* LiveConnect internals */
static 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) {
#ifdef NO_JSOBJECTOPS
Is this really needed ?
case JSTYPE_FUNCTION:
{
JSFunction *function;
JSObject *function_obj;
function = JS_NewFunction(cx, jsj_JavaConstructorWrapper, 0,
JSFUN_BOUND_METHOD, obj, "<init>");
if (!function)
return JS_FALSE;
function_obj = JS_GetFunctionObject(function);
*vp = OBJECT_TO_JSVAL(function_obj);
return JS_TRUE;
}
#endif
case JSTYPE_STRING:
/* Convert '/' to '.' so that it looks like Java language syntax. */
if (!class_descriptor->name)
break;
name = PR_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:
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) {
JS_ReportError(cx, "illegal operation on JavaClass prototype object");
return JS_FALSE;
}
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_ReportError(cx, "invalid JavaClass property expression. "
"(methods and fields of a JavaClass object can only be identified by their name)");
return JS_FALSE;
}
member_name = JS_GetStringBytes(JSVAL_TO_STRING(idval));
/* Why do we have to do this ? */
if (!strcmp(member_name, "prototype")) {
*memberp = NULL;
return JS_TRUE;
}
JS_ReportError(cx, "Java class %s has no public static field or method named \"%s\"",
class_descriptor->name, member_name);
return JS_FALSE;
}
if (memberp)
*memberp = member_descriptor;
return JS_TRUE;
}
PR_STATIC_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;
/* printf("In JavaClass_getProperty\n"); */
/* Get the Java per-thread environment pointer for this JSContext */
jsj_MapJSContextToJSJThread(cx, &jEnv);
if (!jEnv)
return JS_FALSE;
if (!lookup_static_member_by_id(cx, jEnv, obj, &class_descriptor, id, &member_descriptor))
return JS_FALSE;
if (!member_descriptor) {
*vp = JSVAL_VOID;
return JS_TRUE;
}
java_class = class_descriptor->java_class;
if (member_descriptor->field) {
if (!member_descriptor->methods) {
return jsj_GetJavaFieldValue(cx, jEnv, member_descriptor->field, java_class, vp);
} else {
PR_ASSERT(0);
}
} else {
JSFunction *function;
/* FIXME - eliminate JSFUN_BOUND_METHOD */
JS_IdToValue(cx, id, &idval);
member_name = JS_GetStringBytes(JSVAL_TO_STRING(idval));
function = JS_NewFunction(cx, jsj_JavaStaticMethodWrapper, 0,
JSFUN_BOUND_METHOD, obj, member_name);
if (!function)
return JS_FALSE;
*vp = OBJECT_TO_JSVAL(JS_GetFunctionObject(function));
return JS_TRUE;
}
}
PR_STATIC_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;
printf("In JavaClass_setProperty\n");
/* Get the Java per-thread environment pointer for this JSContext */
jsj_MapJSContextToJSJThread(cx, &jEnv);
if (!jEnv)
return JS_FALSE;
if (!lookup_static_member_by_id(cx, jEnv, obj, &class_descriptor, id, &member_descriptor))
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)
return JS_TRUE;
java_class = class_descriptor->java_class;
return jsj_SetJavaFieldValue(cx, jEnv, member_descriptor->field, java_class, *vp);
no_such_field:
JS_IdToValue(cx, id, &idval);
member_name = JS_GetStringBytes(JSVAL_TO_STRING(idval));
JS_ReportError(cx, "No static field named \"%s\" in Java class %s",
member_name, class_descriptor->name);
return JS_FALSE;
}
/*
* Free the private native data associated with the JavaPackage object.
*/
PR_STATIC_CALLBACK(void)
JavaClass_finalize(JSContext *cx, JSObject *obj)
{
JNIEnv *jEnv;
JavaClassDescriptor *class_descriptor = JS_GetPrivate(cx, obj);
if (!class_descriptor)
return;
/* Get the Java per-thread environment pointer for this JSContext */
jsj_MapJSContextToJSJThread(cx, &jEnv);
if (!jEnv)
return;
printf("Finalizing %s\n", class_descriptor->name);
jsj_ReleaseJavaClassDescriptor(cx, jEnv, class_descriptor);
}
#ifdef NO_JSOBJECTOPS
PR_STATIC_CALLBACK(JSBool)
JavaClass_getProperty(JSContext *cx, JSObject *obj, jsval idval, jsval *vp)
{
jsid id;
JS_ValueToId(cx, idval, &id);
return JavaClass_getPropertyById(cx, obj, id, vp);
}
PR_STATIC_CALLBACK(JSBool)
JavaClass_setProperty(JSContext *cx, JSObject *obj, jsval idval, jsval *vp)
{
jsid id;
JS_ValueToId(cx, idval, &id);
return JavaClass_setPropertyById(cx, obj, id, vp);
}
PR_STATIC_CALLBACK(JSBool)
JavaClass_enumerate(JSContext *cx, JSObject *obj)
{
JavaMemberDescriptor *member_descriptor;
JavaClassDescriptor *class_descriptor;
JNIEnv *jEnv;
/* printf("In JavaClass_enumerate\n");*/
/* Get the Java per-thread environment pointer for this JSContext */
jsj_MapJSContextToJSJThread(cx, &jEnv);
if (!jEnv)
return JS_FALSE;
class_descriptor = JS_GetPrivate(cx, obj);
if (!class_descriptor)
return JS_FALSE;
member_descriptor = jsj_GetClassStaticMembers(cx, jEnv, class_descriptor);
while (member_descriptor) {
JS_DefineProperty(cx, obj, member_descriptor->name, JSVAL_VOID, 0, 0,
JSPROP_PERMANENT|JSPROP_ENUMERATE);
member_descriptor = member_descriptor->next;
}
return JS_TRUE;
}
/*
* Resolve a component name to be either the name of a static field or a static method
*/
PR_STATIC_CALLBACK(JSBool)
JavaClass_resolve(JSContext *cx, JSObject *obj, jsval idval)
{
' const char *member_name;
jsid id;
/* Get the Java per-thread environment pointer for this JSContext */
jsj_MapJSContextToJSJThread(cx, &jEnv);
if (!jEnv)
return JS_FALSE;
JS_ValueToId(cx, idval, &id);
if (!lookup_static_member_by_id(cx, obj, NULL, id, NULL))
return JS_FALSE;
member_name = JS_GetStringBytes(JSVAL_TO_STRING(idval));
return JS_DefineProperty(cx, obj, member_name, JSVAL_VOID, 0, 0,
JSPROP_PERMANENT|JSPROP_ENUMERATE);
}
#else /* !NO_JSOBJECTOPS */
static JSBool
JavaClass_lookupProperty(JSContext *cx, JSObject *obj, jsid id,
JSObject **objp, JSProperty **propp
#if defined JS_THREADSAFE && defined DEBUG
, const char *file, uintN line
#endif
)
{
JNIEnv *jEnv;
printf("In JavaClass_lookupProperty()\n");
/* Get the Java per-thread environment pointer for this JSContext */
jsj_MapJSContextToJSJThread(cx, &jEnv);
if (!jEnv)
return JS_FALSE;
if (!lookup_static_member_by_id(cx, jEnv, obj, NULL, id, NULL))
return JS_FALSE;
*objp = obj;
*propp = (JSProperty*)1;
return JS_TRUE;
}
static JSBool
JavaClass_defineProperty(JSContext *cx, JSObject *obj, jsid id, jsval value,
JSPropertyOp getter, JSPropertyOp setter,
uintN attrs, JSProperty **propp)
{
JS_ReportError(cx, "Properties of JavaClass objects may not be deleted");
return JS_FALSE;
}
static 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;
}
static 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) {
PR_ASSERT(0);
return JS_FALSE;
}
/* Silently ignore all setAttribute attempts */
return JS_TRUE;
}
static JSBool
JavaClass_deleteProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
{
JS_ReportError(cx, "Properties of JavaClass objects may not be deleted");
return JS_FALSE;
}
static JSBool
JavaClass_defaultValue(JSContext *cx, JSObject *obj, JSType type, jsval *vp)
{
printf("In JavaClass_defaultValue()\n");
return JavaClass_convert(cx, obj, JSTYPE_STRING, vp);
}
static JSBool
JavaClass_newEnumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
jsval *statep, jsid *idp)
{
JavaMemberDescriptor *member_descriptor;
JavaClassDescriptor *class_descriptor;
JNIEnv *jEnv;
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_MapJSContextToJSJThread(cx, &jEnv);
if (!jEnv)
return JS_FALSE;
member_descriptor = jsj_GetClassInstanceMembers(cx, jEnv, class_descriptor);
*statep = PRIVATE_TO_JSVAL(member_descriptor);
if (idp)
*idp = INT_TO_JSVAL(class_descriptor->num_instance_members);
return JS_TRUE;
case JSENUMERATE_NEXT:
member_descriptor = JSVAL_TO_PRIVATE(*statep);
if (member_descriptor) {
*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:
PR_ASSERT(0);
return JS_FALSE;
}
}
static JSBool
JavaClass_checkAccess(JSContext *cx, JSObject *obj, jsid id,
JSAccessMode mode, jsval *vp, uintN *attrsp)
{
switch (mode) {
case JSACC_WATCH:
JS_ReportError(cx, "Cannot place watchpoints on JavaClass object properties");
return JS_FALSE;
case JSACC_IMPORT:
JS_ReportError(cx, "Cannot export a JavaClass object's properties");
return JS_FALSE;
default:
return JS_TRUE;
}
}
/*
* Implement the JavaScript instanceof operator for JavaClass objects by using
* the equivalent Java instanceof operation.
*/
static 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;
has_instance = JS_FALSE;
class_descriptor = JS_GetPrivate(cx, obj);
if (!class_descriptor) {
JS_ReportError(cx, "illegal operation on JavaClass prototype object");
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);
js_class = JS_GetClass(candidate_obj);
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_ReportError(cx, "illegal operation on prototype object");
return JS_FALSE;
}
java_obj = java_wrapper->java_obj;
/* Get JNI pointer */
jsj_MapJSContextToJSJThread(cx, &jEnv);
has_instance = (*jEnv)->IsInstanceOf(jEnv, java_obj, java_class);
done:
*has_instancep = has_instance;
return JS_TRUE;
}
JSObjectOps JavaClass_ops = {
/* Mandatory non-null function pointer members. */
NULL, /* newObjectMap */
NULL, /* 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 */
};
JSObjectOps *
JavaClass_getObjectOps(JSContext *cx, JSClass *clazz)
{
return &JavaClass_ops;
}
#endif /* !NO_JSOBJECTOPS */
JSClass JavaClass_class = {
"JavaClass", JSCLASS_HAS_PRIVATE,
NULL, NULL, NULL, NULL,
NULL, NULL, JavaClass_convert, JavaClass_finalize,
JavaClass_getObjectOps,
};
extern PR_IMPORT_DATA(JSObjectOps) js_ObjectOps;
JSBool
jsj_init_JavaClass(JSContext *cx, JSObject *global_obj)
{
JavaClass_ops.newObjectMap = js_ObjectOps.newObjectMap;
JavaClass_ops.destroyObjectMap = js_ObjectOps.destroyObjectMap;
/* Define JavaClass class */
if (!JS_InitClass(cx, global_obj, 0, &JavaClass_class, 0, 0, 0, 0, 0, 0))
return JS_FALSE;
return jsj_InitJavaClassReflectionsTable();
}
JSObject *
jsj_define_JavaClass(JSContext *cx, JNIEnv *jEnv, JSObject* parent_obj,
const char *simple_class_name,
const char *fully_qualified_class_name,
jclass java_class)
{
JSObject *JavaClass_obj;
JavaClassDescriptor *class_descriptor;
JavaClass_obj = JS_DefineObject(cx, parent_obj, simple_class_name,
&JavaClass_class, 0,
JSPROP_PERMANENT | JSPROP_READONLY);
if (!JavaClass_obj)
return NULL;
/* Set the prototype so that objects constructed with
"new" have the right JSClass
if (!JS_DefineProperty(cx, JavaClass_obj, "prototype",
JSVAL_NULL, 0, 0, JSPROP_PERMANENT | JSPROP_READONLY))
goto error; */
/* Attach private, native data to the JS object */
class_descriptor = jsj_GetJavaClassDescriptor(cx, jEnv, java_class);
if (!class_descriptor)
goto error;
JS_SetPrivate(cx, JavaClass_obj, (void *)class_descriptor);
#ifdef DEBUG
/* printf("JavaClass \'%s\' created\n", class_descriptor->name); */
#endif
return JavaClass_obj;
error:
JS_DeleteProperty(cx, parent_obj, simple_class_name);
return NULL;
}

Просмотреть файл

@ -0,0 +1,752 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (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/NPL/
*
* 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.
*
* The Initial Developer of the Original Code is Netscape Communications
* Corporation. Portions created by Netscape are Copyright (C) 1998
* Netscape Communications Corporation. All Rights Reserved.
*/
/*
* This file is part of the Java-vendor-neutral implementation of LiveConnect
*
* It contains the native code implementation of JS's JavaObject class.
*
* An instance of JavaObject is the JavaScript reflection of a Java object.
*
*/
#include <stdlib.h>
#include <string.h>
#include "prassert.h"
#include "jsj_private.h" /* LiveConnect internals */
/*
* This is a hash table that maps from Java objects to JS objects.
* It is used to ensure that the same JS object is obtained when a Java
* object is reflected more than once, so that JS object equality tests
* work in the expected manner, i.e. the "==" and "===" operators.
*
* The table entry keys are Java objects (of type jobject) and the entry values
* are JSObject pointers. Because the jobject type is an opaque handle and
* not necessarily a pointer, the hashing and key comparison functions must
* invoke the appropriate JVM functions.
*
* When the corresponding JS object instance is finalized, the entry is
* removed from the table, and a Java GC root for the Java object is removed.
*/
static PRHashTable *java_obj_reflections = NULL;
#ifdef JS_THREADSAFE
static PRMonitor *java_obj_reflections_monitor = NULL;
#endif
static JSBool
init_java_obj_reflections_table()
{
java_obj_reflections =
PR_NewHashTable(512, jsj_HashJavaObject, jsj_JavaObjectComparator,
NULL, NULL, NULL);
if (!java_obj_reflections)
return JS_FALSE;
#ifdef JS_THREADSAFE
java_obj_reflections_monitor = PR_NewNamedMonitor("java_obj_reflections");
if (!java_obj_reflections_monitor) {
PR_HashTableDestroy(java_obj_reflections);
return JS_FALSE;
}
#endif
return JS_TRUE;
}
JSObject *
jsj_WrapJavaObject(JSContext *cx,
JNIEnv *jEnv,
jobject java_obj,
jclass java_class)
{
prhashcode hash_code;
JSClass *js_class;
JSObject *js_wrapper_obj;
JavaObjectWrapper *java_wrapper;
JavaClassDescriptor *class_descriptor;
PRHashEntry *he, **hep;
js_wrapper_obj = NULL;
hash_code = jsj_HashJavaObject((void*)java_obj);
#ifdef JS_THREADSAFE
PR_EnterMonitor(java_obj_reflections_monitor);
#endif
hep = PR_HashTableRawLookup(java_obj_reflections,
hash_code, java_obj);
he = *hep;
if (he) {
js_wrapper_obj = (JSObject *)he->value;
if (js_wrapper_obj)
goto done;
}
/* No existing reflection found. Construct a new one */
class_descriptor = jsj_GetJavaClassDescriptor(cx, jEnv, java_class);
if (!class_descriptor)
goto done;
if (class_descriptor->type == JAVA_SIGNATURE_ARRAY) {
js_class = &JavaArray_class;
} else {
PR_ASSERT(class_descriptor->type == JAVA_SIGNATURE_CLASS);
js_class = &JavaObject_class;
}
/* Create new JS object to reflect Java object */
js_wrapper_obj = JS_NewObject(cx, js_class, NULL, NULL);
if (!js_wrapper_obj)
goto done;
/* Create private, native portion of JavaObject */
java_wrapper =
(JavaObjectWrapper *)JS_malloc(cx, sizeof(JavaObjectWrapper));
if (!java_wrapper) {
jsj_ReleaseJavaClassDescriptor(cx, jEnv, class_descriptor);
goto done;
}
JS_SetPrivate(cx, js_wrapper_obj, java_wrapper);
java_wrapper->class_descriptor = class_descriptor;
java_wrapper->members = NULL;
/* Add the JavaObject to the hash table */
he = PR_HashTableRawAdd(java_obj_reflections, hep, hash_code,
java_obj, js_wrapper_obj);
if (he) {
java_wrapper->java_obj = (*jEnv)->NewGlobalRef(jEnv, java_obj);
if (!java_wrapper->java_obj)
goto out_of_memory;
} else {
goto out_of_memory;
}
done:
#ifdef JS_THREADSAFE
PR_ExitMonitor(java_obj_reflections_monitor);
#endif
return js_wrapper_obj;
out_of_memory:
/* No need to free js_wrapper_obj, as it will be finalized by GC. */
JS_ReportOutOfMemory(cx);
js_wrapper_obj = NULL;
goto done;
}
static void
remove_java_obj_reflection_from_hashtable(jobject java_obj)
{
prhashcode hash_code;
PRHashEntry *he, **hep;
hash_code = jsj_HashJavaObject((void*)java_obj);
#ifdef JS_THREADSAFE
PR_EnterMonitor(java_obj_reflections_monitor);
#endif
hep = PR_HashTableRawLookup(java_obj_reflections, hash_code, java_obj);
he = *hep;
PR_ASSERT(he);
if (he)
PR_HashTableRawRemove(java_obj_reflections, hep, he);
#ifdef JS_THREADSAFE
PR_ExitMonitor(java_obj_reflections_monitor);
#endif
}
void
JavaObject_finalize(JSContext *cx, JSObject *obj)
{
JavaObjectWrapper *java_wrapper;
jobject java_obj;
JNIEnv *jEnv;
jsj_MapJSContextToJSJThread(cx, &jEnv);
if (!jEnv)
return;
java_wrapper = JS_GetPrivate(cx, obj);
if (!java_wrapper)
return;
java_obj = java_wrapper->java_obj;
remove_java_obj_reflection_from_hashtable(java_obj);
(*jEnv)->DeleteGlobalRef(jEnv, java_obj);
jsj_ReleaseJavaClassDescriptor(cx, jEnv, java_wrapper->class_descriptor);
/* FIXME - Delete JavaMemberValues */
/* if (java_wrapper->invoke_java_method_func_obj)
JS_RemoveRoot(cx, &java_wrapper->invoke_java_method_func_obj); */
JS_free(cx, java_wrapper);
}
/*
static JSBool
JavaObject_toString(JSContext *cx, JSObject *obj,
uintN argc, jsval *argv, jsval *vp)
{
jobject java_obj;
JavaObjectWrapper *java_wrapper;
JavaClassDescriptor *class_descriptor;
java_wrapper = JS_GetPrivate(cx, obj);
if (!java_wrapper) {
JS_ReportError(cx, "illegal operation on JavaObject prototype object");
return JS_FALSE;
}
class_descriptor = java_wrapper->class_descriptor;
java_obj = java_wrapper->java_obj;
return jsj_ConvertJavaObjectToJSString(cx, class_descriptor, java_obj, vp);
}
*/
PR_CALLBACK JSBool
JavaObject_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp)
{
JavaObjectWrapper *java_wrapper;
JavaClassDescriptor *class_descriptor;
jobject java_obj;
JNIEnv *jEnv;
/* Get the Java per-thread environment pointer for this JSContext */
jsj_MapJSContextToJSJThread(cx, &jEnv);
if (!jEnv)
return JS_FALSE;
java_wrapper = JS_GetPrivate(cx, obj);
if (!java_wrapper) {
if (type == JSTYPE_OBJECT) {
*vp = OBJECT_TO_JSVAL(obj);
return JS_TRUE;
}
JS_ReportError(cx, "illegal operation on JavaObject prototype object");
return JS_FALSE;
}
java_obj = java_wrapper->java_obj;
class_descriptor = java_wrapper->class_descriptor;
switch (type) {
case JSTYPE_OBJECT:
*vp = OBJECT_TO_JSVAL(obj);
return JS_TRUE;
case JSTYPE_FUNCTION:
JS_ReportError(cx, "can't convert Java object to function");
return JS_FALSE;
case JSTYPE_VOID:
case JSTYPE_STRING:
/* Either extract a C-string from the java.lang.String object
or call the Java toString() method */
return jsj_ConvertJavaObjectToJSString(cx, jEnv, class_descriptor, java_obj, vp);
case JSTYPE_NUMBER:
/* Call Java doubleValue() method, if applicable */
return jsj_ConvertJavaObjectToJSNumber(cx, jEnv, java_obj, vp);
case JSTYPE_BOOLEAN:
/* Call booleanValue() method, if applicable */
return jsj_ConvertJavaObjectToJSBoolean(cx, jEnv, java_obj, vp);
default:
PR_ASSERT(0);
return JS_FALSE;
}
}
static JSBool
lookup_member_by_id(JSContext *cx, JNIEnv *jEnv, JSObject *obj,
JavaObjectWrapper **java_wrapperp,
jsid id, JavaMemberVal **memberp,
JavaMemberDescriptor **member_descriptorp)
{
jsval idval;
JavaMemberVal *member, **prev_memberp;
JavaObjectWrapper *java_wrapper;
JavaMemberDescriptor *member_descriptor;
const char *member_name, *property_name;
JavaClassDescriptor *class_descriptor;
java_wrapper = JS_GetPrivate(cx, obj);
if (!java_wrapper) {
if (JS_IdToValue(cx, id, &idval) && JSVAL_IS_STRING(idval) &&
(property_name = JS_GetStringBytes(JSVAL_TO_STRING(idval)))) {
if (!strcmp(property_name, "constructor")) {
*java_wrapperp = NULL;
*member_descriptorp = NULL;
return JS_TRUE;
}
}
JS_ReportError(cx, "illegal operation on JavaObject prototype object");
return JS_FALSE;
}
class_descriptor = java_wrapper->class_descriptor;
PR_ASSERT(class_descriptor->type == JAVA_SIGNATURE_CLASS ||
class_descriptor->type == JAVA_SIGNATURE_ARRAY);
/* FIXME - not thread-safe */
prev_memberp = &java_wrapper->members;
for (member = *prev_memberp; member; member = member->next) {
member_descriptor = member->descriptor;
if (member_descriptor->id == id) {
*prev_memberp = member->next;
member->next = java_wrapper->members;
break;
}
}
if (!member) {
JSFunction *function;
JSObject *function_obj;
member_descriptor = jsj_LookupJavaMemberDescriptorById(cx, jEnv, class_descriptor, id);
if (member_descriptor && member_descriptor->methods) {
member = (JavaMemberVal*)JS_malloc(cx, sizeof(JavaMemberVal));
if (!member)
return JS_FALSE;
JS_IdToValue(cx, id, &idval);
member_name = JS_GetStringBytes(JSVAL_TO_STRING(idval));
/* printf("Adding %s\n", member_name); */
/* FIXME - eliminate JSFUN_BOUND_METHOD */
/* TODO - Use JS_CloneFunction() to save memory */
function = JS_NewFunction(cx, jsj_JavaInstanceMethodWrapper, 0,
JSFUN_BOUND_METHOD, obj, member_name);
if (!function) {
JS_free(cx, member);
return JS_FALSE;
}
function_obj = JS_GetFunctionObject(function);
member->invoke_method_func_val = OBJECT_TO_JSVAL(function_obj);
member->descriptor = member_descriptor;
member->next = NULL;
JS_AddRoot(cx, &member->invoke_method_func_val);
}
}
/* Place member at head of list of members for faster access next time */
if (member) {
member->next = java_wrapper->members;
java_wrapper->members = member;
}
if (!member_descriptor) {
JS_IdToValue(cx, id, &idval);
if (!JSVAL_IS_STRING(idval)) {
JS_ReportError(cx, "invalid JavaObject property expression. "
"(methods and field properties of a JavaObject object can only be strings)");
return JS_FALSE;
}
member_name = JS_GetStringBytes(JSVAL_TO_STRING(idval));
JS_ReportError(cx, "Java class %s has no public instance field or "
"method named \"%s\"",
class_descriptor->name, member_name);
return JS_FALSE;
}
/* Success. Handle the multiple return values */
if (java_wrapperp)
*java_wrapperp = java_wrapper;
if (memberp)
*memberp = member;
if (member_descriptorp)
*member_descriptorp = member_descriptor;
return JS_TRUE;
}
PR_CALLBACK JSBool
JavaObject_getPropertyById(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
{
jobject java_obj;
JavaMemberDescriptor *member_descriptor;
JavaMemberVal *member;
JavaObjectWrapper *java_wrapper;
JNIEnv *jEnv;
/* printf("In JavaObject_getProperty\n"); */
/* Get the Java per-thread environment pointer for this JSContext */
jsj_MapJSContextToJSJThread(cx, &jEnv);
if (!jEnv)
return JS_FALSE;
if (!lookup_member_by_id(cx, jEnv, obj, &java_wrapper, id, &member, &member_descriptor))
return JS_FALSE;
/* Handle access to "constructor" property of prototype object with
silent failure. */
if (!member_descriptor) {
*vp = JSVAL_VOID;
return JS_TRUE;
}
java_obj = java_wrapper->java_obj;
if (member_descriptor->field) {
if (!member_descriptor->methods) {
return jsj_GetJavaFieldValue(cx, jEnv, member_descriptor->field, java_obj, vp);
} else {
PR_ASSERT(0);
}
} else {
*vp = member->invoke_method_func_val;
return JS_TRUE;
}
}
PR_STATIC_CALLBACK(JSBool)
JavaObject_setPropertyById(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
{
jobject java_obj;
const char *member_name;
JavaObjectWrapper *java_wrapper;
JavaClassDescriptor *class_descriptor;
JavaMemberDescriptor *member_descriptor;
jsval idval;
JNIEnv *jEnv;
/* printf("In JavaObject_setProperty\n"); */
/* Get the Java per-thread environment pointer for this JSContext */
jsj_MapJSContextToJSJThread(cx, &jEnv);
if (!jEnv)
return JS_FALSE;
if (!lookup_member_by_id(cx, jEnv, obj, &java_wrapper, id, NULL, &member_descriptor))
return JS_FALSE;
/* Check for the case where there is a method with the give 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)
return JS_TRUE;
java_obj = java_wrapper->java_obj;
return jsj_SetJavaFieldValue(cx, jEnv, member_descriptor->field, java_obj, *vp);
no_such_field:
JS_IdToValue(cx, id, &idval);
member_name = JS_GetStringBytes(JSVAL_TO_STRING(idval));
class_descriptor = java_wrapper->class_descriptor;
JS_ReportError(cx, "No instance field named \"%s\" in Java class %s",
member_name, class_descriptor->name);
return JS_FALSE;
}
PR_CALLBACK JSBool
JavaObject_enumerate(JSContext *cx, JSObject *obj)
{
JavaObjectWrapper *java_wrapper;
JavaClassDescriptor *class_descriptor;
JavaMemberDescriptor *member_descriptor;
JNIEnv *jEnv;
/* printf("In JavaObject_enumerate\n"); */
java_wrapper = JS_GetPrivate(cx, obj);
/* Check if this is the prototype object */
if (!java_wrapper)
return JS_TRUE;
/* Get the Java per-thread environment pointer for this JSContext */
jsj_MapJSContextToJSJThread(cx, &jEnv);
if (!jEnv)
return JS_FALSE;
class_descriptor = java_wrapper->class_descriptor;
member_descriptor = jsj_GetClassInstanceMembers(cx, jEnv, class_descriptor);
while (member_descriptor) {
JS_DefineProperty(cx, obj, member_descriptor->name, JSVAL_VOID, 0, 0,
JSPROP_PERMANENT|JSPROP_ENUMERATE);
member_descriptor = member_descriptor->next;
}
return JS_TRUE;
}
#ifdef NO_JSOBJECTOPS
JSBool
JavaObject_getProperty(JSContext *cx, JSObject *obj, jsval idval, jsval *vp)
{
jsid id;
JS_ValueToId(cx, idval, &id);
return JavaObject_getPropertyById(cx, obj, id, vp);
}
PR_STATIC_CALLBACK(JSBool)
JavaObject_setProperty(JSContext *cx, JSObject *obj, jsval idval, jsval *vp)
{
jsid id;
JS_ValueToId(cx, idval, &id);
return JavaObject_setPropertyById(cx, obj, id, vp);
}
/*
* Resolve a component name to be either the name of a static field or a static method
*/
PR_CALLBACK JSBool
JavaObject_resolve(JSContext *cx, JSObject *obj, jsval idval)
{
const char *member_name;
jsid id;
JNIEnv *jEnv;
/* printf("In JavaObject_resolve\n"); */
/* Prototype object has no properties */
if (!JS_GetPrivate(cx, obj))
return JS_TRUE;
/* Get the Java per-thread environment pointer for this JSContext */
jsj_MapJSContextToJSJThread(cx, &jEnv);
if (!jEnv)
return JS_FALSE;
JS_ValueToId(cx, idval, &id);
if (!lookup_member_by_id(cx, jEnv, obj, NULL, id, NULL))
return JS_FALSE;
member_name = JS_GetStringBytes(JSVAL_TO_STRING(idval));
return JS_DefineProperty(cx, obj, member_name, JSVAL_VOID, 0, 0,
JSPROP_PERMANENT|JSPROP_ENUMERATE);
}
JSClass JavaObject_class = {
"JavaObject", JSCLASS_HAS_PRIVATE,
JS_PropertyStub, JS_PropertyStub, JavaObject_getProperty, JavaObject_setProperty,
JavaObject_enumerate, JavaObject_resolve,
JavaObject_convert, JavaObject_finalize
};
#else /* !NO_JSOBJECTOPS */
static JSBool
JavaObject_lookupProperty(JSContext *cx, JSObject *obj, jsid id,
JSObject **objp, JSProperty **propp
#if defined JS_THREADSAFE && defined DEBUG
, const char *file, uintN line
#endif
)
{
JNIEnv *jEnv;
printf("In JavaObject_lookupProperty()\n");
/* Get the Java per-thread environment pointer for this JSContext */
jsj_MapJSContextToJSJThread(cx, &jEnv);
if (!jEnv)
return JS_FALSE;
if (!lookup_member_by_id(cx, jEnv, obj, NULL, id, NULL, NULL))
return JS_FALSE;
*objp = obj;
*propp = (JSProperty*)1;
return JS_TRUE;
}
static JSBool
JavaObject_defineProperty(JSContext *cx, JSObject *obj, jsid id, jsval value,
JSPropertyOp getter, JSPropertyOp setter,
uintN attrs, JSProperty **propp)
{
JS_ReportError(cx, "Properties of JavaObject objects may not be deleted");
return JS_FALSE;
}
static JSBool
JavaObject_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;
}
static JSBool
JavaObject_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) {
PR_ASSERT(0);
return JS_FALSE;
}
/* Silently ignore all setAttribute attempts */
return JS_TRUE;
}
static JSBool
JavaObject_deleteProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
{
JS_ReportError(cx, "Properties of JavaObject objects may not be deleted");
return JS_FALSE;
}
static JSBool
JavaObject_defaultValue(JSContext *cx, JSObject *obj, JSType type, jsval *vp)
{
/* printf("In JavaObject_defaultValue()\n"); */
return JavaObject_convert(cx, obj, JSTYPE_STRING, vp);
}
static JSBool
JavaObject_newEnumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
jsval *statep, jsid *idp)
{
JavaObjectWrapper *java_wrapper;
JavaMemberDescriptor *member_descriptor;
JavaClassDescriptor *class_descriptor;
JNIEnv *jEnv;
java_wrapper = JS_GetPrivate(cx, obj);
/* Check for prototype object */
if (!java_wrapper) {
*statep = JSVAL_NULL;
if (idp)
*idp = INT_TO_JSVAL(0);
return JS_TRUE;
}
class_descriptor = java_wrapper->class_descriptor;
switch(enum_op) {
case JSENUMERATE_INIT:
/* Get the Java per-thread environment pointer for this JSContext */
jsj_MapJSContextToJSJThread(cx, &jEnv);
if (!jEnv)
return JS_FALSE;
member_descriptor = jsj_GetClassInstanceMembers(cx, jEnv, class_descriptor);
*statep = PRIVATE_TO_JSVAL(member_descriptor);
if (idp)
*idp = INT_TO_JSVAL(class_descriptor->num_instance_members);
return JS_TRUE;
case JSENUMERATE_NEXT:
member_descriptor = JSVAL_TO_PRIVATE(*statep);
if (member_descriptor) {
*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:
PR_ASSERT(0);
return JS_FALSE;
}
}
static JSBool
JavaObject_checkAccess(JSContext *cx, JSObject *obj, jsid id,
JSAccessMode mode, jsval *vp, uintN *attrsp)
{
switch (mode) {
case JSACC_WATCH:
JS_ReportError(cx, "Cannot place watchpoints on JavaObject object properties");
return JS_FALSE;
case JSACC_IMPORT:
JS_ReportError(cx, "Cannot export a JavaObject object's properties");
return JS_FALSE;
default:
return JS_TRUE;
}
}
JSObjectOps JavaObject_ops = {
/* Mandatory non-null function pointer members. */
NULL, /* newObjectMap */
NULL, /* destroyObjectMap */
JavaObject_lookupProperty,
JavaObject_defineProperty,
JavaObject_getPropertyById, /* getProperty */
JavaObject_setPropertyById, /* setProperty */
JavaObject_getAttributes,
JavaObject_setAttributes,
JavaObject_deleteProperty,
JavaObject_defaultValue,
JavaObject_newEnumerate,
JavaObject_checkAccess,
/* Optionally non-null members start here. */
NULL, /* thisObject */
NULL, /* dropProperty */
NULL, /* call */
NULL, /* construct */
NULL, /* xdrObject */
NULL, /* hasInstance */
};
JSObjectOps *
JavaObject_getObjectOps(JSContext *cx, JSClass *clazz)
{
return &JavaObject_ops;
}
JSClass JavaObject_class = {
"JavaObject", JSCLASS_HAS_PRIVATE,
NULL, NULL, NULL, NULL,
NULL, NULL, JavaObject_convert, JavaObject_finalize,
JavaObject_getObjectOps,
};
extern PR_IMPORT_DATA(JSObjectOps) js_ObjectOps;
#endif /* !NO_JSOBJECTOPS */
JSBool
jsj_init_JavaObject(JSContext *cx, JSObject *global_obj)
{
#ifndef NO_JSOBJECTOPS
JavaObject_ops.newObjectMap = js_ObjectOps.newObjectMap;
JavaObject_ops.destroyObjectMap = js_ObjectOps.destroyObjectMap;
#endif
if (!JS_InitClass(cx, global_obj,
0, &JavaObject_class, 0, 0,
0, 0,
0, 0))
return JS_FALSE;
return init_java_obj_reflections_table();
}

Просмотреть файл

@ -0,0 +1,438 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (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/NPL/
*
* 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.
*
* The Initial Developer of the Original Code is Netscape Communications
* Corporation. Portions created by Netscape are Copyright (C) 1998
* Netscape Communications Corporation. All Rights Reserved.
*/
/* This file is part of the Java-vendor-neutral implementation of LiveConnect
*
* It contains the native code implementation of the JavaPackage class.
*
* A JavaPackage is JavaScript's representation of a Java package. The
* JavaPackage object contains only a string, which is the path to the package,
* e.g. "java/lang". The JS properties of a JavaPackage are either nested packages
* or a JavaClass object, which represents the path to a Java class.
*
* Note that there is no equivalent to a JavaPackage object in Java. Example:
* 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 java.lang
* object in Java that exists at run time.
*
*/
#include <stdlib.h>
#include <string.h>
#include "prtypes.h"
#include "prprintf.h"
#include "jsj_private.h" /* LiveConnect internals */
#include "jsjava.h"
JSClass JavaPackage_class; /* Forward declaration */
/*
* The native part of a JavaPackage object. It gets stored in the object's
* private slot.
*/
typedef struct {
const char * path; /* e.g. "java/lang" or NULL if top level package */
int flags; /* e.g. PKG_SYSTEM, PKG_CLASS */
} JavaPackage_Private;
JSObject *
define_JavaPackage(JSContext *cx, JSObject *parent_obj,
const char *obj_name, const char *path, int flags)
{
JSObject *package_obj;
JavaPackage_Private *package;
package_obj = JS_DefineObject(cx, parent_obj, obj_name, &JavaPackage_class, 0,
JSPROP_PERMANENT | JSPROP_READONLY);
if (!package_obj)
return NULL;
/* Attach private, native data to the JS object */
package = (JavaPackage_Private *)JS_malloc(cx, sizeof(JavaPackage_Private));
JS_SetPrivate(cx, package_obj, (void *)package);
if (path)
package->path = JS_strdup(cx, path);
else
package->path = "";
package->flags = flags;
/* Check for OOM */
if (!package->path) {
JS_DeleteProperty(cx, parent_obj, obj_name);
JS_free(cx, package);
return NULL;
}
return package_obj;
}
/* JavaPackage uses standard JS getProperty */
/*
* Don't allow user-defined properties to be set on Java package objects, e.g.
* it is illegal to write "java.lang.myProperty = 4". We probably could relax
* this restriction, but it's potentially confusing and not clearly useful.
*/
static JSBool
JavaPackage_setProperty(JSContext *cx, JSObject *obj, jsval slot, jsval *vp)
{
JavaPackage_Private *package = JS_GetPrivate(cx, obj);
if (!package) {
JS_ReportError(cx, "illegal attempt to add property to "
"JavaPackage prototype object");
return JS_FALSE;
}
JS_ReportError(cx, "You may not add properties to %s, "
"as it is not a JavaScript object", package->path);
return JS_FALSE;
}
static JSBool quiet_resolve_failure;
/*
* Resolve a component name to be either the name of a class or a package.
*/
static JSBool
JavaPackage_resolve(JSContext *cx, JSObject *obj, jsval id)
{
JavaPackage_Private *package;
JSBool ok = JS_TRUE;
jclass jclazz;
char *subPath, *newPath;
const char *path;
JNIEnv *jEnv;
package = (JavaPackage_Private *)JS_GetPrivate(cx, obj);
if (!package) {
fprintf(stderr, "JavaPackage_resolve: no private data!\n");
return JS_FALSE;
}
path = package->path;
subPath = JS_GetStringBytes(JSVAL_TO_STRING(id));
newPath = PR_smprintf("%s%s%s", path, (path[0] ? "/" : ""), subPath);
if (!newPath) {
JS_ReportOutOfMemory(cx);
return JS_FALSE;
}
jsj_MapJSContextToJSJThread(cx, &jEnv);
if (!jEnv)
return JS_FALSE;
/*
Unfortunately, Java provides no way to find out whether a particular
name is a package or not. The only way to tell is to try to load the
name as a class file and, if that fails, assume it's a package. This
makes things work as expected for the most part, but it has three
noticeable problems that keep coming up:
- You can refer to a package like java.lang.i.buried.paul without
generating a complaint. Of course, you'll never be able to refer to
any classes through it.
- An annoying consequence of the above is that misspelling a class name
results in a cryptic error about packages.
- In a browser context, i.e. where applets are involved, figuring out
whether something is a class may require looking for it over the net
using the current classloader. This means that the first time you
refer to java.lang.System in a js context, there will be an attempt
to search for [[DOCBASE]]/java.class on the server.
A solution is to explicitly tell jsjava the names of all the (local)
packages on the CLASSPATH. (Not implemented yet.)
*/
jclazz = (*jEnv)->FindClass(jEnv, newPath);
if (jclazz) {
JSObject *newClass;
newClass = jsj_define_JavaClass(cx, jEnv, obj, subPath, newPath, jclazz);
if (!newClass) {
ok = JS_FALSE;
goto out;
}
} else {
/*
* If there's no class of the given name, then we must be referring to
* a package. However, don't allow bogus sub-packages of pre-defined
* system packages to be created.
*/
if (JS_InstanceOf(cx, obj, &JavaPackage_class, NULL)) {
JavaPackage_Private *package;
package = JS_GetPrivate(cx, obj);
if (package->flags & PKG_SYSTEM) {
char *msg, *cp;
/* Painful hack for pre_define_java_packages() */
if (quiet_resolve_failure)
return JS_FALSE;
msg = PR_smprintf("No Java system package with name \"%s\" was identified "
"at initialization time and no Java class "
"with that name exists either", newPath);
/* Check for OOM */
if (msg) {
/* Convert package of form "java/lang" to "java.lang" */
for (cp = msg; *cp != '\0'; cp++)
if (*cp == '/')
*cp = '.';
JS_ReportError(cx, msg);
free((char*)msg);
}
return JS_FALSE;
}
}
if (!define_JavaPackage(cx, obj, subPath, newPath, 0)) {
ok = JS_FALSE;
goto out;
}
#ifdef DEBUG
printf("JavaPackage \'%s/%s\' created\n", subPath, newPath);
#endif
}
out:
free(newPath);
return ok;
}
static JSBool
JavaPackage_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp)
{
JSString *str;
char *name, *cp;
JavaPackage_Private *package = JS_GetPrivate(cx, obj);
if (!package) {
fprintf(stderr, "JavaPackage_resolve: no private data!\n");
return JS_FALSE;
}
switch (type) {
/* Pretty-printing of JavaPackage */
case JSTYPE_STRING:
/* Convert '/' to '.' so that it looks like Java language syntax. */
if (!package->path)
break;
name = PR_smprintf("[JavaPackage %s]", package->path);
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);
/* It's not necessary to call JS_ReportOutOfMemory(), as
JS_NewString() will do so on failure. */
return JS_FALSE;
}
*vp = STRING_TO_JSVAL(str);
break;
case JSTYPE_OBJECT:
*vp = OBJECT_TO_JSVAL(obj);
break;
default:
break;
}
return JS_TRUE;
}
/*
* Free the private native data associated with the JavaPackage object.
*/
static void
JavaPackage_finalize(JSContext *cx, JSObject *obj)
{
JavaPackage_Private *package = JS_GetPrivate(cx, obj);
if (!package)
return;
if (package->path)
JS_free(cx, (char *)package->path);
JS_free(cx, package);
}
/*
* The definition of the JavaPackage class
*/
JSClass JavaPackage_class = {
"JavaPackage", JSCLASS_HAS_PRIVATE,
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JavaPackage_setProperty,
JS_EnumerateStub, JavaPackage_resolve,
JavaPackage_convert, JavaPackage_finalize
};
JavaPackageDef
standard_java_packages[] = {
{"java", NULL, PKG_SYSTEM},
{"java.awt", NULL, PKG_SYSTEM},
{"java.awt.event", NULL, PKG_SYSTEM},
{"java.awt.image", NULL, PKG_SYSTEM},
{"java.awt.peer", NULL, PKG_SYSTEM},
{"java.beans", NULL, PKG_SYSTEM},
{"java.io", NULL, PKG_SYSTEM},
{"java.lang", NULL, PKG_SYSTEM},
{"java.lang.reflect", NULL, PKG_SYSTEM},
{"java.math", NULL, PKG_SYSTEM},
{"java.net", NULL, PKG_SYSTEM},
{"java.text", NULL, PKG_SYSTEM},
{"java.util", NULL, PKG_SYSTEM},
{"java.util.zip", NULL, PKG_SYSTEM},
{"netscape", NULL, PKG_SYSTEM},
{"netscape.javascript", NULL, PKG_SYSTEM},
{"sun", NULL, PKG_USER},
{"Packages", "", PKG_USER},
0
};
/*
* Pre-define a hierarchy of JavaPackage objects.
* Pre-defining a Java package at initialization time is not necessary, but
* it will make package lookup faster and, more importantly, will avoid
* unnecessary network accesses if classes are being loaded over the network.
*/
static JSBool
pre_define_java_packages(JSContext *cx, JSObject *global_obj,
JavaPackageDef *predefined_packages)
{
JSBool package_exists;
JSObject *parent_obj;
JavaPackageDef *package_def;
char *simple_name, *cp, *package_name, *path;
int flags;
if (!predefined_packages)
return JS_TRUE;
/* Iterate over all pre-defined Java packages */
for (package_def = predefined_packages; package_def->name; package_def++) {
package_name = path = NULL;
parent_obj = global_obj;
package_name = strdup(package_def->name);
if (!package_name)
goto out_of_memory;
/* Walk the chain of JavaPackage objects to get to the parent of the
rightmost sub-package in the fully-qualified package name. */
for (simple_name = strtok(package_name, "."); 1; simple_name = strtok(NULL, ".")) {
jsval v;
if (!simple_name) {
JS_ReportError(cx, "Package %s defined twice ?", package_name);
goto error;
}
/* Check to see if the sub-package already exists */
quiet_resolve_failure = JS_TRUE;
package_exists = JS_LookupProperty(cx, parent_obj, simple_name, &v) && JSVAL_IS_OBJECT(v);
quiet_resolve_failure = JS_FALSE;
if (package_exists) {
parent_obj = JSVAL_TO_OBJECT(v);
continue;
} else {
/* New package objects should only be created at the terminal
sub-package in a fully-qualified package-name */
if (strtok(NULL, ".")) {
JS_ReportError(cx, "Illegal predefined package definition for %s",
package_def->name);
goto error;
}
if (package_def->path) {
path = strdup(package_def->path);
if (!path)
goto out_of_memory;
} else {
/*
* The default path is specified, so create it from the
* fully-qualified package name.
*/
path = strdup(package_def->name);
if (!path)
goto out_of_memory;
/* Transform package name, e.g. "java.lang" ==> "java/lang" */
for (cp = path; *cp != '\0'; cp++) {
if (*cp == '.')
*cp = '/';
}
}
flags = package_def->flags;
parent_obj = define_JavaPackage(cx, parent_obj, simple_name, path, flags);
if (!parent_obj)
goto error;
free(path);
break;
}
}
free(package_name);
}
return JS_TRUE;
out_of_memory:
JS_ReportOutOfMemory(cx);
error:
JS_FREE_IF(cx, package_name);
JS_FREE_IF(cx, path);
return JS_FALSE;
}
/*
* One-time initialization for the JavaPackage class. (This is not
* run once per thread, rather it's run once for a given JSContext.)
*/
JSBool
jsj_init_JavaPackage(JSContext *cx, JSObject *global_obj,
JavaPackageDef *additional_predefined_packages) {
/* Define JavaPackage class */
if (!JS_InitClass(cx, global_obj, 0, &JavaPackage_class, 0, 0, 0, 0, 0, 0))
return JS_FALSE;
/* Add top-level packages, e.g. : java, netscape, sun */
if (!pre_define_java_packages(cx, global_obj, standard_java_packages))
return JS_FALSE;
if (!pre_define_java_packages(cx, global_obj, additional_predefined_packages))
return JS_FALSE;
return JS_TRUE;
}

Просмотреть файл

@ -0,0 +1,180 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (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/NPL/
*
* 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.
*
* The Initial Developer of the Original Code is Netscape Communications
* Corporation. Portions created by Netscape are Copyright (C) 1998
* Netscape Communications Corporation. All Rights Reserved.
*/
/*
* This file is part of the Java-vendor-neutral implementation of LiveConnect
*
* It contains the code for reading and writing elements of a Java array.
*/
#include "prtypes.h"
#include "prassert.h"
#include "jsj_private.h" /* LiveConnect internals */
/*
* Read the Java value at a given index into a Java array and convert it
* to a JS value. The array_component_signature describes the type of
* the resulting Java value, which can be a primitive type or an object type.
* More specifically it can be an array type in the case of multidimensional
* arrays.
*/
JSBool
jsj_GetJavaArrayElement(JSContext *cx, JNIEnv *jEnv, jarray java_array, jsize index,
JavaSignature *array_component_signature,
jsval *vp)
{
jvalue java_value;
JavaSignatureChar component_type;
#define GET_ELEMENT_FROM_PRIMITIVE_JAVA_ARRAY(Type,member) \
(*jEnv)->Get##Type##ArrayRegion(jEnv, java_array, index, 1, \
&java_value.member); \
if ((*jEnv)->ExceptionOccurred(jEnv)) { \
jsj_ReportJavaError(cx, jEnv, "Error reading element of " \
"Java primitive array"); \
return JS_FALSE; \
}
component_type = array_component_signature->type;
switch(component_type) {
case JAVA_SIGNATURE_BYTE:
GET_ELEMENT_FROM_PRIMITIVE_JAVA_ARRAY(Byte,b);
break;
case JAVA_SIGNATURE_CHAR:
GET_ELEMENT_FROM_PRIMITIVE_JAVA_ARRAY(Char,c);
break;
case JAVA_SIGNATURE_SHORT:
GET_ELEMENT_FROM_PRIMITIVE_JAVA_ARRAY(Short,s);
break;
case JAVA_SIGNATURE_INT:
GET_ELEMENT_FROM_PRIMITIVE_JAVA_ARRAY(Int,i);
break;
case JAVA_SIGNATURE_BOOLEAN:
GET_ELEMENT_FROM_PRIMITIVE_JAVA_ARRAY(Boolean,z);
break;
case JAVA_SIGNATURE_LONG:
GET_ELEMENT_FROM_PRIMITIVE_JAVA_ARRAY(Long,j);
break;
case JAVA_SIGNATURE_FLOAT:
GET_ELEMENT_FROM_PRIMITIVE_JAVA_ARRAY(Float,f);
break;
case JAVA_SIGNATURE_DOUBLE:
GET_ELEMENT_FROM_PRIMITIVE_JAVA_ARRAY(Double,d);
break;
case JAVA_SIGNATURE_CLASS:
case JAVA_SIGNATURE_ARRAY:
java_value.l = (*jEnv)->GetObjectArrayElement(jEnv, java_array, index);
if ((*jEnv)->ExceptionOccurred(jEnv)) {
jsj_ReportJavaError(cx, jEnv, "Error reading Java object array");
return JS_FALSE;
}
break;
#undef GET_ELEMENT_FROM_PRIMITIVE_JAVA_ARRAY
default:
PR_ASSERT(0); /* Unknown java type signature */
return JS_FALSE;
}
return jsj_ConvertJavaValueToJSValue(cx, jEnv, array_component_signature, &java_value, vp);
}
JSBool
jsj_SetJavaArrayElement(JSContext *cx, JNIEnv *jEnv, jarray java_array, jsize index,
JavaSignature *array_component_signature,
jsval js_val)
{
int dummy_cost;
jvalue java_value;
JavaSignatureChar component_type;
if (!jsj_ConvertJSValueToJavaValue(cx, jEnv, js_val, array_component_signature,
&dummy_cost, &java_value))
return JS_FALSE;
#define SET_ELEMENT_FROM_PRIMITIVE_JAVA_ARRAY(Type,member) \
(*jEnv)->Set##Type##ArrayRegion(jEnv, java_array, index, 1, \
&java_value.member); \
if ((*jEnv)->ExceptionOccurred(jEnv)) { \
jsj_ReportJavaError(cx, jEnv, "Error assigning to element of " \
"Java primitive array"); \
return JS_FALSE; \
}
component_type = array_component_signature->type;
switch(component_type) {
case JAVA_SIGNATURE_BYTE:
SET_ELEMENT_FROM_PRIMITIVE_JAVA_ARRAY(Byte,b);
break;
case JAVA_SIGNATURE_CHAR:
SET_ELEMENT_FROM_PRIMITIVE_JAVA_ARRAY(Char,c);
break;
case JAVA_SIGNATURE_SHORT:
SET_ELEMENT_FROM_PRIMITIVE_JAVA_ARRAY(Short,s);
break;
case JAVA_SIGNATURE_INT:
SET_ELEMENT_FROM_PRIMITIVE_JAVA_ARRAY(Int,i);
break;
case JAVA_SIGNATURE_BOOLEAN:
SET_ELEMENT_FROM_PRIMITIVE_JAVA_ARRAY(Boolean,z);
break;
case JAVA_SIGNATURE_LONG:
SET_ELEMENT_FROM_PRIMITIVE_JAVA_ARRAY(Long,j);
break;
case JAVA_SIGNATURE_FLOAT:
SET_ELEMENT_FROM_PRIMITIVE_JAVA_ARRAY(Float,f);
break;
case JAVA_SIGNATURE_DOUBLE:
SET_ELEMENT_FROM_PRIMITIVE_JAVA_ARRAY(Double,d);
break;
case JAVA_SIGNATURE_CLASS:
case JAVA_SIGNATURE_ARRAY:
(*jEnv)->SetObjectArrayElement(jEnv, java_array, index, java_value.l);
if ((*jEnv)->ExceptionOccurred(jEnv)) {
jsj_ReportJavaError(cx, jEnv, "Error assigning to Java object array");
return JS_FALSE;
}
break;
#undef SET_ELEMENT_FROM_PRIMITIVE_JAVA_ARRAY
default:
PR_ASSERT(0); /* Unknown java type signature */
return JS_FALSE;
}
return JS_TRUE;
}

Просмотреть файл

@ -0,0 +1,572 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (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/NPL/
*
* 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.
*
* The Initial Developer of the Original Code is Netscape Communications
* Corporation. Portions created by Netscape are Copyright (C) 1998
* Netscape Communications Corporation. All Rights Reserved.
*/
/*
* This file is part of the Java-vendor-neutral implementation of LiveConnect
*
* It contains the code that constructs and manipulates JavaClassDescriptor
* structs, which are the native wrappers for Java classes.
* JavaClassDescriptors are used to describe the signatures of methods and
* fields. There is a JavaClassDescriptor associated with the reflection of
* each Java Object.
*/
#include <stdlib.h>
#include <string.h>
#include "prtypes.h"
#include "prprintf.h"
#include "prassert.h"
#include "jsj_private.h" /* LiveConnect internals */
#include "prhash.h" /* Hash tables */
/* A one-to-one mapping between all referenced java.lang.Class objects and
their corresponding JavaClassDescriptor objects */
static PRHashTable *java_class_reflections;
/*
* Given a JVM handle to a java.lang.Class object, malloc a C-string
* containing the UTF8 encoding of the fully qualified name of the class.
* It's the caller's responsibility to free the returned string.
*
* If an error occurs, NULL is returned and the error reporter called.
*/
const char *
jsj_GetJavaClassName(JSContext *cx, JNIEnv *jEnv, jclass java_class)
{
jstring java_class_name_jstr;
const char *java_class_name;
/* Get java.lang.String object containing class name */
java_class_name_jstr =
(*jEnv)->CallObjectMethod(jEnv, java_class, jlClass_getName);
if (!java_class_name_jstr)
goto error;
/* Convert to UTF8 encoding and copy */
java_class_name = jsj_DupJavaStringUTF(cx, jEnv, java_class_name_jstr);
if (!java_class_name)
return NULL;
return java_class_name;
error:
jsj_UnexpectedJavaError(cx, jEnv, "Can't get Java class name using"
"java.lang.Class.getName()");
return NULL;
}
/*
* Convert in-place a string of the form "java.lang.String" into "java/lang/String".
* Though the former style is conventionally used by Java programmers, the latter is
* what the JNI functions require.
*/
void
jsj_MakeJNIClassname(char * class_name)
{
char * c;
for (c = class_name; *c; c++)
if (*c == '.')
*c = '/';
}
/*
* Classify an instance of java.lang.Class as either one of the primitive
* types, e.g. int, char, etc., as an array type or as a non-array object type
* (subclass of java.lang.Object) by returning the appropriate enum member.
*
*/
static JavaSignatureChar
get_signature_type(JSContext *cx, JavaClassDescriptor *class_descriptor)
{
JavaSignatureChar type;
const char *java_class_name;
/* Get UTF8 encoding of class name */
java_class_name = class_descriptor->name;
PR_ASSERT(java_class_name);
if (!java_class_name)
return JAVA_SIGNATURE_UNKNOWN;
if (!strcmp(java_class_name, "byte"))
type = JAVA_SIGNATURE_BYTE;
else if (!strcmp(java_class_name, "char"))
type = JAVA_SIGNATURE_CHAR;
else if (!strcmp(java_class_name, "float"))
type = JAVA_SIGNATURE_FLOAT;
else if (!strcmp(java_class_name, "double"))
type = JAVA_SIGNATURE_DOUBLE;
else if (!strcmp(java_class_name, "int"))
type = JAVA_SIGNATURE_INT;
else if (!strcmp(java_class_name, "long"))
type = JAVA_SIGNATURE_LONG;
else if (!strcmp(java_class_name, "short"))
type = JAVA_SIGNATURE_SHORT;
else if (!strcmp(java_class_name, "boolean"))
type = JAVA_SIGNATURE_BOOLEAN;
else if (!strcmp(java_class_name, "void"))
type = JAVA_SIGNATURE_VOID;
else
/* Well, I guess it's a Java class, then. */
type = JAVA_SIGNATURE_CLASS;
return type;
}
static JSBool
is_java_array_class(JNIEnv *jEnv, jclass java_class)
{
return (*jEnv)->CallBooleanMethod(jEnv, java_class, jlClass_isArray);
}
/*
* Return the class of a Java array's component type. This is not the same
* as the array's element type. For example, the component type of an array
* of type SomeType[][][] is SomeType[][], but its element type is SomeType.
*
* If an error occurs, NULL is returned and an error reported.
*/
static jclass
get_java_array_component_class(JSContext *cx, JNIEnv *jEnv, jclass java_class)
{
jclass result;
result = (*jEnv)->CallObjectMethod(jEnv, java_class, jlClass_getComponentType);
if (!result) {
jsj_UnexpectedJavaError(cx, jEnv,
"Can't get Java array component class using "
"java.lang.Class.getComponentType()");
return NULL;
}
return result;
}
/*
* Given a Java class, fill in the signature structure that describes the class.
* If an error occurs, JS_FALSE is returned and the error reporter called.
*/
static JSBool
compute_java_class_signature(JSContext *cx, JNIEnv *jEnv, JavaSignature *signature)
{
jclass java_class = signature->java_class;
if (is_java_array_class(jEnv, java_class)) {
jclass component_class;
signature->type = JAVA_SIGNATURE_ARRAY;
component_class = get_java_array_component_class(cx, jEnv, java_class);
if (!component_class)
return JS_FALSE;
signature->array_component_signature =
jsj_GetJavaClassDescriptor(cx, jEnv, component_class);
if (!signature->array_component_signature)
return JS_FALSE;
} else {
signature->type = get_signature_type(cx, signature);
}
return JS_TRUE;
}
/*
* Convert a JavaSignature object into a string format as used by
* the JNI functions, e.g. java.lang.Object ==> "Ljava/lang/Object;"
* The caller is responsible for freeing the resulting string.
*
* If an error is encountered, NULL is returned and an error reported.
*/
const char *
jsj_ConvertJavaSignatureToString(JSContext *cx, JavaSignature *signature)
{
char *sig;
if (signature->type == JAVA_SIGNATURE_CLASS) {
/* A non-array object class */
sig = PR_smprintf("L%s;", signature->name);
if (sig)
jsj_MakeJNIClassname(sig);
} else if (signature->type == JAVA_SIGNATURE_ARRAY) {
/* An array class */
const char *component_signature_string;
component_signature_string =
jsj_ConvertJavaSignatureToString(cx, signature->array_component_signature);
if (!component_signature_string)
return NULL;
sig = PR_smprintf("[%s", component_signature_string);
JS_free(cx, (char*)component_signature_string);
} else {
/* A primitive class */
sig = PR_smprintf("%c", (char)signature->type);
}
if (!sig) {
JS_ReportOutOfMemory(cx);
return NULL;
}
return sig;
}
/*
* Convert a JavaSignature object into a human-readable string format as seen
* in Java source files, e.g. "byte", or "int[][]" or "java.lang.String".
* The caller is responsible for freeing the resulting string.
*
* If an error is encountered, NULL is returned and an error reported.
*/
const char *
jsj_ConvertJavaSignatureToHRString(JSContext *cx,
JavaSignature *signature)
{
char *sig;
JavaSignature *acs;
if (signature->type == JAVA_SIGNATURE_ARRAY) {
/* An array class */
const char *component_signature_string;
acs = signature->array_component_signature;
component_signature_string =
jsj_ConvertJavaSignatureToHRString(cx, acs);
if (!component_signature_string)
return NULL;
sig = PR_smprintf("%s[]", component_signature_string);
JS_free(cx, (char*)component_signature_string);
} else {
/* A primitive class or a non-array object class */
sig = JS_strdup(cx, signature->name);
}
if (!sig) {
JS_ReportOutOfMemory(cx);
return NULL;
}
return sig;
}
static void
destroy_java_member_descriptor(JSContext *cx, JNIEnv *jEnv, JavaMemberDescriptor *member_descriptor)
{
JavaMethodSpec *method, *next_method;
if (member_descriptor->field)
jsj_DestroyFieldSpec(cx, jEnv, member_descriptor->field);
method = member_descriptor->methods;
while (method) {
next_method = method->next;
jsj_DestroyMethodSpec(cx, jEnv, method);
method = next_method;
}
}
static void
destroy_class_member_descriptors(JSContext *cx, JNIEnv *jEnv, JavaMemberDescriptor *member_descriptor)
{
JavaMemberDescriptor *next_member;
while (member_descriptor) {
next_member = member_descriptor->next;
destroy_java_member_descriptor(cx, jEnv, member_descriptor);
member_descriptor = next_member;
}
}
static void
destroy_class_descriptor(JSContext *cx, JNIEnv *jEnv, JavaClassDescriptor *class_descriptor)
{
JS_FREE_IF(cx, (char *)class_descriptor->name);
if (class_descriptor->java_class) {
(*jEnv)->DeleteGlobalRef(jEnv, class_descriptor->java_class);
PR_HashTableRemove(java_class_reflections, class_descriptor->java_class);
}
if (class_descriptor->array_component_signature)
jsj_ReleaseJavaClassDescriptor(cx, jEnv, class_descriptor->array_component_signature);
destroy_class_member_descriptors(cx, jEnv, class_descriptor->instance_members);
destroy_class_member_descriptors(cx, jEnv, class_descriptor->static_members);
destroy_class_member_descriptors(cx, jEnv, class_descriptor->constructors);
JS_free(cx, class_descriptor);
}
static JavaClassDescriptor *
new_class_descriptor(JSContext *cx, JNIEnv *jEnv, jclass java_class)
{
JavaClassDescriptor *class_descriptor;
class_descriptor = (JavaClassDescriptor *)JS_malloc(cx, sizeof(JavaClassDescriptor));
if (!class_descriptor)
return NULL;
memset(class_descriptor, 0, sizeof(JavaClassDescriptor));
class_descriptor->name = jsj_GetJavaClassName(cx, jEnv, java_class);
if (!class_descriptor->name)
goto error;
java_class = (*jEnv)->NewGlobalRef(jEnv, java_class);
if (!java_class) {
jsj_ReportJavaError(cx, jEnv, "Unable to reference Java class");
goto error;
}
class_descriptor->java_class = java_class;
if (!compute_java_class_signature(cx, jEnv, class_descriptor))
goto error;
class_descriptor->modifiers =
(*jEnv)->CallIntMethod(jEnv, java_class, jlClass_getModifiers);
class_descriptor->ref_count = 1;
if (!PR_HashTableAdd(java_class_reflections, java_class, class_descriptor))
goto error;
return class_descriptor;
error:
destroy_class_descriptor(cx, jEnv, class_descriptor);
return NULL;
}
extern JavaClassDescriptor *
jsj_GetJavaClassDescriptor(JSContext *cx, JNIEnv *jEnv, jclass java_class)
{
JavaClassDescriptor *class_descriptor;
class_descriptor = PR_HashTableLookup(java_class_reflections,
(const void *)java_class);
if (!class_descriptor)
return new_class_descriptor(cx, jEnv, java_class);
PR_ASSERT(class_descriptor->ref_count > 0);
class_descriptor->ref_count++;
return class_descriptor;
}
void
jsj_ReleaseJavaClassDescriptor(JSContext *cx, JNIEnv *jEnv, JavaClassDescriptor *class_descriptor)
{
if (!--class_descriptor->ref_count)
destroy_class_descriptor(cx, jEnv, class_descriptor);
}
static JSBool
reflect_java_methods_and_fields(JSContext *cx,
JNIEnv *jEnv,
JavaClassDescriptor *class_descriptor,
JSBool reflect_statics_only)
{
JavaMemberDescriptor *member_descriptor;
if (reflect_statics_only)
class_descriptor->static_members_reflected = JS_TRUE;
else
class_descriptor->instance_members_reflected = JS_TRUE;
if (!jsj_ReflectJavaMethods(cx, jEnv, class_descriptor, reflect_statics_only))
return JS_FALSE;
if (!jsj_ReflectJavaFields(cx, jEnv, class_descriptor, reflect_statics_only))
return JS_FALSE;
if (reflect_statics_only) {
member_descriptor = class_descriptor->static_members;
while (member_descriptor) {
class_descriptor->num_static_members++;
member_descriptor = member_descriptor->next;
}
} else {
member_descriptor = class_descriptor->instance_members;
while (member_descriptor) {
class_descriptor->num_instance_members++;
member_descriptor = member_descriptor->next;
}
}
return JS_TRUE;
}
JavaMemberDescriptor *
jsj_GetClassStaticMembers(JSContext *cx,
JNIEnv *jEnv,
JavaClassDescriptor *class_descriptor)
{
if (!class_descriptor->static_members_reflected)
reflect_java_methods_and_fields(cx, jEnv, class_descriptor, JS_TRUE);
return class_descriptor->static_members;
}
JavaMemberDescriptor *
jsj_GetClassInstanceMembers(JSContext *cx,
JNIEnv *jEnv,
JavaClassDescriptor *class_descriptor)
{
if (!class_descriptor->instance_members_reflected)
reflect_java_methods_and_fields(cx, jEnv, class_descriptor, JS_FALSE);
return class_descriptor->instance_members;
}
JavaMemberDescriptor *
jsj_LookupJavaStaticMemberDescriptorById(JSContext *cx,
JNIEnv *jEnv,
JavaClassDescriptor *class_descriptor,
jsid id)
{
JavaMemberDescriptor *member_descriptor;
member_descriptor = jsj_GetClassStaticMembers(cx, jEnv, class_descriptor);
while (member_descriptor) {
if (id == member_descriptor->id)
return member_descriptor;
member_descriptor = member_descriptor->next;
}
return NULL;
}
JavaMemberDescriptor *
jsj_GetJavaStaticMemberDescriptor(JSContext *cx,
JNIEnv *jEnv,
JavaClassDescriptor *class_descriptor,
jstring member_name_jstr)
{
JavaMemberDescriptor *member_descriptor;
jsid id;
if (!JavaStringToId(cx, jEnv, member_name_jstr, &id))
return NULL;
member_descriptor = jsj_LookupJavaStaticMemberDescriptorById(cx, jEnv, class_descriptor, id);
if (member_descriptor)
return member_descriptor;
member_descriptor = JS_malloc(cx, sizeof(JavaMemberDescriptor));
if (!member_descriptor)
return NULL;
memset(member_descriptor, 0, sizeof(JavaMemberDescriptor));
member_descriptor->name = jsj_DupJavaStringUTF(cx, jEnv, member_name_jstr);
if (!member_descriptor->name) {
JS_free(cx, member_descriptor);
return NULL;
}
member_descriptor->id = id;
member_descriptor->next = class_descriptor->static_members;
class_descriptor->static_members = member_descriptor;
return member_descriptor;
}
JavaMemberDescriptor *
jsj_GetJavaClassConstructors(JSContext *cx,
JavaClassDescriptor *class_descriptor)
{
JavaMemberDescriptor *member_descriptor;
if (class_descriptor->constructors)
return class_descriptor->constructors;
member_descriptor = JS_malloc(cx, sizeof(JavaMemberDescriptor));
if (!member_descriptor)
return NULL;
memset(member_descriptor, 0, sizeof(JavaMemberDescriptor));
member_descriptor->name = JS_strdup(cx, "<init>");
if (!member_descriptor->name) {
JS_free(cx, member_descriptor);
return NULL;
}
class_descriptor->constructors = member_descriptor;
return member_descriptor;
}
JavaMemberDescriptor *
jsj_LookupJavaClassConstructors(JSContext *cx, JNIEnv *jEnv,
JavaClassDescriptor *class_descriptor)
{
if (!class_descriptor->static_members_reflected)
reflect_java_methods_and_fields(cx, jEnv, class_descriptor, JS_TRUE);
return class_descriptor->constructors;
}
JavaMemberDescriptor *
jsj_LookupJavaMemberDescriptorById(JSContext *cx, JNIEnv *jEnv,
JavaClassDescriptor *class_descriptor,
jsid id)
{
JavaMemberDescriptor *member_descriptor;
member_descriptor = jsj_GetClassInstanceMembers(cx, jEnv, class_descriptor);
while (member_descriptor) {
if (id == member_descriptor->id)
return member_descriptor;
member_descriptor = member_descriptor->next;
}
return NULL;
}
JavaMemberDescriptor *
jsj_GetJavaMemberDescriptor(JSContext *cx,
JNIEnv *jEnv,
JavaClassDescriptor *class_descriptor,
jstring member_name_jstr)
{
JavaMemberDescriptor *member_descriptor;
jsid id;
if (!JavaStringToId(cx, jEnv, member_name_jstr, &id))
return NULL;
member_descriptor = jsj_LookupJavaMemberDescriptorById(cx, jEnv, class_descriptor, id);
if (member_descriptor)
return member_descriptor;
member_descriptor = JS_malloc(cx, sizeof(JavaMemberDescriptor));
if (!member_descriptor)
return NULL;
memset(member_descriptor, 0, sizeof(JavaMemberDescriptor));
member_descriptor->name = jsj_DupJavaStringUTF(cx, jEnv, member_name_jstr);
if (!member_descriptor->name) {
JS_free(cx, member_descriptor);
return NULL;
}
member_descriptor->id = id;
member_descriptor->next = class_descriptor->instance_members;
class_descriptor->instance_members = member_descriptor;
return member_descriptor;
}
JSBool
jsj_InitJavaClassReflectionsTable()
{
java_class_reflections =
PR_NewHashTable(64, jsj_HashJavaObject, jsj_JavaObjectComparator,
NULL, NULL, NULL);
if (!java_class_reflections)
return JS_FALSE;
return JS_TRUE;
}

Просмотреть файл

@ -0,0 +1,658 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (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/NPL/
*
* 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.
*
* The Initial Developer of the Original Code is Netscape Communications
* Corporation. Portions created by Netscape are Copyright (C) 1998
* Netscape Communications Corporation. All Rights Reserved.
*/
/* This file is part of the Java-vendor-neutral implementation of LiveConnect
*
* Below is the code that converts between Java and JavaScript values of all
* types.
*/
#include <stdlib.h>
#include <string.h>
#include "prtypes.h"
#include "prprintf.h"
#include "prassert.h"
#include "jsj_private.h" /* LiveConnect internals */
static JSBool
convert_js_obj_to_JSObject_wrapper(JSContext *cx, JNIEnv *jEnv, JSObject *js_obj,
JavaSignature *signature,
int *cost, jobject *java_value)
{
if (!njJSObject) {
if (java_value)
JS_ReportError(cx, "Couldn't convert JavaScript object to an "
"instance of netscape.javascript.JSObject "
"because that class could not be loaded.");
return JS_FALSE;
}
if (!(*jEnv)->IsAssignableFrom(jEnv, njJSObject, signature->java_class))
return JS_FALSE;
if (!java_value)
return JS_TRUE;
*java_value = jsj_WrapJSObject(cx, jEnv, js_obj);
return (*java_value != NULL);
}
jstring
jsj_ConvertJSStringToJavaString(JSContext *cx, JNIEnv *jEnv, JSString *js_str)
{
jstring result;
result = (*jEnv)->NewString(jEnv, JS_GetStringChars(js_str),
JS_GetStringLength(js_str));
if (!result) {
jsj_UnexpectedJavaError(cx, jEnv, "Couldn't construct instance "
"of java.lang.String");
}
return result;
}
/*
* Convert a JS value to an instance of java.lang.Object or one of its subclasses,
* performing any necessary type coercion. If non-trivial coercion is required,
* the cost value is incremented. If the java_value pass-by-reference argument
* is non-NULL, the resulting Java value is stored there.
*
* Returns JS_TRUE if the conversion is possible, JS_FALSE otherwise
*/
JSBool
jsj_ConvertJSValueToJavaObject(JSContext *cx, JNIEnv *jEnv, jsval v, JavaSignature *signature,
int *cost, jobject *java_value)
{
JSString *jsstr;
jclass target_java_class;
PR_ASSERT(signature->type == JAVA_SIGNATURE_CLASS ||
signature->type == JAVA_SIGNATURE_ARRAY);
/* Get the Java type of the target value */
target_java_class = signature->java_class;
if (JSVAL_IS_OBJECT(v)) {
JSObject *js_obj = JSVAL_TO_OBJECT(v);
/* JS null is always assignable to a Java object */
if (!js_obj) {
if (java_value)
*java_value = NULL;
return JS_TRUE;
}
/* JS functions can be wrapped as a netscape.javascript.JSObject */
if (JS_TypeOfValue(cx, v) == JSTYPE_FUNCTION) {
if (convert_js_obj_to_JSObject_wrapper(cx, jEnv, js_obj, signature, cost, java_value))
return JS_TRUE;
/* That didn't work, so fall through, to attempt conversion to
a java.lang.String ... */
/* Check for a Java object wrapped inside a JS object */
} else if (JS_InstanceOf(cx, js_obj, &JavaObject_class, 0) ||
JS_InstanceOf(cx, js_obj, &JavaArray_class, 0)) {
/* The source value is a Java object wrapped inside a JavaScript
object. Unwrap the JS object and return the original Java
object if it's class makes it assignment-compatible with the
target class using Java's assignability rules. */
JavaObjectWrapper *java_wrapper = JS_GetPrivate(cx, js_obj);
jobject java_obj = java_wrapper->java_obj;
if ((*jEnv)->IsInstanceOf(jEnv, java_obj, target_java_class)) {
if (java_value)
*java_value = java_obj;
return JS_TRUE;
}
#ifdef LIVECONNECT_IMPROVEMENTS
/* Don't allow wrapped Java objects to be converted to strings */
goto conversion_error;
#else
/* Fall through, to attempt conversion to a Java string */
#endif
} else if (JS_InstanceOf(cx, js_obj, &JavaClass_class, 0)) {
/* We're dealing with the reflection of a Java class */
JavaClassDescriptor *java_class_descriptor = JS_GetPrivate(cx, js_obj);
/* Check if target type is java.lang.Class class */
if ((*jEnv)->IsAssignableFrom(jEnv, jlClass, target_java_class)) {
if (java_value)
*java_value = java_class_descriptor->java_class;
return JS_TRUE;
}
/* Check if target type is netscape.javascript.JSObject wrapper class */
if (convert_js_obj_to_JSObject_wrapper(cx, jEnv, js_obj, signature, cost, java_value))
return JS_TRUE;
/* Fall through, to attempt conversion to a Java string */
} else {
/* Otherwise, see if the target type is the netscape.javascript.JSObject
wrapper class or one of its subclasses, in which case a
reference is passed to the original JS object by wrapping it
inside an instance of netscape.javascript.JSObject */
if (convert_js_obj_to_JSObject_wrapper(cx, jEnv, js_obj, signature, cost, java_value))
return JS_TRUE;
/* Fall through, to attempt conversion to a Java string */
}
} else if (JSVAL_IS_NUMBER(v)) {
/* JS numbers, integral or not, can be converted to instances of java.lang.Double */
if ((*jEnv)->IsAssignableFrom(jEnv, jlDouble, target_java_class)) {
if (java_value) {
jsdouble d;
if (!JS_ValueToNumber(cx, v, &d))
goto conversion_error;
*java_value = (*jEnv)->NewObject(jEnv, jlDouble, jlDouble_Double, d);
if (!*java_value) {
jsj_UnexpectedJavaError(cx, jEnv,
"Couldn't construct instance of java.lang.Double");
return JS_FALSE;
}
}
#ifdef LIVECONNECT_IMPROVEMENTS
(*cost)++;
#endif
return JS_TRUE;
}
/* Fall through, to attempt conversion to a java.lang.String ... */
} else if (JSVAL_IS_BOOLEAN(v)) {
/* JS boolean values can be converted to instances of java.lang.Boolean */
if ((*jEnv)->IsAssignableFrom(jEnv, jlBoolean, target_java_class)) {
if (java_value) {
JSBool b;
if (!JS_ValueToBoolean(cx, v, &b))
goto conversion_error;
*java_value =
(*jEnv)->NewObject(jEnv, jlBoolean, jlBoolean_Boolean, b);
if (!*java_value) {
jsj_UnexpectedJavaError(cx, jEnv, "Couldn't construct instance "
"of java.lang.Boolean");
return JS_FALSE;
}
}
#ifdef LIVECONNECT_IMPROVEMENTS
(*cost)++;
#endif
return JS_TRUE;
}
/* Fall through, to attempt conversion to a java.lang.String ... */
}
/* If no other conversion is possible, see if the target type is java.lang.String */
if ((*jEnv)->IsAssignableFrom(jEnv, jlString, target_java_class)) {
JSBool is_string = JSVAL_IS_STRING(v);
/* Convert to JS string, if necessary, and then to a Java Unicode string */
jsstr = JS_ValueToString(cx, v);
if (jsstr) {
if (java_value) {
*java_value = jsj_ConvertJSStringToJavaString(cx, jEnv, jsstr);
if (!*java_value)
return JS_FALSE;
}
#ifdef LIVECONNECT_IMPROVEMENTS
if (!is_string)
(*cost)++;
#endif
return JS_TRUE;
}
}
conversion_error:
return JS_FALSE;
}
/* Utility macro for jsj_ConvertJSValueToJavaValue(), below */
#define JSVAL_TO_INTEGRAL_JVALUE(type_name, member_name, member_type, jsval, java_value) \
if (!JSVAL_IS_NUMBER(v)) { \
if (!JS_ConvertValue(cx, v, JSTYPE_NUMBER, &v)) \
goto conversion_error; \
(*cost)++; \
} \
{ \
member_type member_name; \
\
if (JSVAL_IS_INT(v)) { \
jsint ival = JSVAL_TO_INT(v); \
member_name = (member_type) ival; \
\
/* Check to see if the jsval's magnitude is too large to be \
representable in the target java type */ \
if (member_name != ival) \
goto conversion_error; \
} else { \
jdouble dval = *JSVAL_TO_DOUBLE(v); \
member_name = (member_type) dval; \
\
/* Don't allow a non-integral number */ \
/* FIXME - should this be an error ? */ \
if ((jdouble)member_name != dval) \
(*cost)++; \
} \
if (java_value) \
java_value->member_name = member_name; \
}
/*
* Convert a JS value to a Java value of the given type signature. The cost
* variable is incremented if coercion is required, e.g. the source value is
* a string, but the target type is a boolean.
*
* Returns JS_FALSE if no conversion is possible, either because the jsval has
* a type that is wholly incompatible with the Java value, or because a scalar
* jsval can't be represented in a variable of the target type without loss of
* precision, e.g. the source value is "4.2" but the destination type is byte.
* If conversion is not possible and java_value is non-NULL, the JS error
* reporter is called with an appropriate message.
*/
JSBool
jsj_ConvertJSValueToJavaValue(JSContext *cx, JNIEnv *jEnv, jsval v,
JavaSignature *signature,
int *cost, jvalue *java_value)
{
JavaSignatureChar type = signature->type;
switch (type) {
case JAVA_SIGNATURE_BOOLEAN:
if (!JSVAL_IS_BOOLEAN(v)) {
if (!JS_ConvertValue(cx, v, JSTYPE_BOOLEAN, &v))
goto conversion_error;
(*cost)++;
}
if (java_value)
java_value->z = (jboolean)(JSVAL_TO_BOOLEAN(v) == JS_TRUE);
break;
case JAVA_SIGNATURE_SHORT:
JSVAL_TO_INTEGRAL_JVALUE(short, s, jshort, v, java_value);
break;
case JAVA_SIGNATURE_BYTE:
JSVAL_TO_INTEGRAL_JVALUE(byte, b, jbyte, v, java_value);
break;
case JAVA_SIGNATURE_CHAR:
/* A one-character string can be converted into a character */
if (JSVAL_IS_STRING(v) && (JS_GetStringLength(JSVAL_TO_STRING(v)) == 1)) {
v = INT_TO_JSVAL(*JS_GetStringChars(JSVAL_TO_STRING(v)));
}
JSVAL_TO_INTEGRAL_JVALUE(char, c, jchar, v, java_value);
break;
case JAVA_SIGNATURE_INT:
JSVAL_TO_INTEGRAL_JVALUE(int, i, jint, v, java_value);
break;
case JAVA_SIGNATURE_LONG:
JSVAL_TO_INTEGRAL_JVALUE(long, j, jlong, v, java_value);
break;
case JAVA_SIGNATURE_FLOAT:
if (!JSVAL_IS_NUMBER(v)) {
if (!JS_ConvertValue(cx, v, JSTYPE_NUMBER, &v))
goto conversion_error;
(*cost)++;
}
if (java_value) {
if (JSVAL_IS_INT(v))
java_value->f = (jfloat) JSVAL_TO_INT(v);
else
java_value->f = (jfloat) *JSVAL_TO_DOUBLE(v);
}
break;
case JAVA_SIGNATURE_DOUBLE:
if (!JSVAL_IS_NUMBER(v)) {
if (!JS_ConvertValue(cx, v, JSTYPE_NUMBER, &v))
goto conversion_error;
(*cost)++;
}
if (java_value) {
if (JSVAL_IS_INT(v))
java_value->d = (jdouble) JSVAL_TO_INT(v);
else
java_value->d = (jdouble) *JSVAL_TO_DOUBLE(v);
}
break;
case JAVA_SIGNATURE_CLASS:
case JAVA_SIGNATURE_ARRAY:
if (!jsj_ConvertJSValueToJavaObject(cx, jEnv, v, signature, cost, &java_value->l))
goto conversion_error;
break;
default:
PR_ASSERT(0);
return JS_FALSE;
}
/* Success */
return JS_TRUE;
conversion_error:
if (java_value) {
const char *jsval_string;
JSString *jsstr;
jsval_string = NULL;
jsstr = JS_ValueToString(cx, v);
if (jsstr)
jsval_string = JS_GetStringBytes(jsstr);
if (!jsval_string)
jsval_string = "";
JS_ReportError(cx, "Unable to convert JavaScript value %s to "
"Java value of type %s",
jsval_string, signature->name);
}
return JS_FALSE;
}
/*
* A utility routine to create a JavaScript Unicode string from a
* java.lang.String (Unicode) string.
*/
JSString *
jsj_ConvertJavaStringToJSString(JSContext *cx, JNIEnv *jEnv, jstring java_str)
{
JSString *js_str;
jboolean is_copy;
const jchar *ucs2_str;
jchar *copy_ucs2_str;
jsize ucs2_str_len, num_bytes;
ucs2_str_len = (*jEnv)->GetStringLength(jEnv, java_str);
ucs2_str = (*jEnv)->GetStringChars(jEnv, java_str, &is_copy);
if (!ucs2_str) {
jsj_UnexpectedJavaError(cx, jEnv,
"Unable to extract native Unicode from Java string");
return NULL;
}
js_str = NULL;
/* Unlike JS_NewString(), the string data passed into JS_NewUCString() is
not copied, so make a copy of the Unicode character vector. */
num_bytes = ucs2_str_len * sizeof(jchar);
copy_ucs2_str = (jchar*)JS_malloc(cx, num_bytes);
if (!copy_ucs2_str)
goto done;
memcpy(copy_ucs2_str, ucs2_str, num_bytes);
js_str = JS_NewUCString(cx, (jschar*)copy_ucs2_str, ucs2_str_len);
done:
(*jEnv)->ReleaseStringChars(jEnv, java_str, ucs2_str);
return js_str;
}
/*
* Attempt to obtain a JS string representation of a Java object.
* The java_obj argument must be of type java.lang.Object or a subclass.
* If java_obj is a Java string, it's value is simply extracted and
* copied into a JS string. Otherwise, the toString() method is called
* on java_obj.
*/
JSBool
jsj_ConvertJavaObjectToJSString(JSContext *cx,
JNIEnv *jEnv,
JavaClassDescriptor *class_descriptor,
jobject java_obj, jsval *vp)
{
JSString *js_str;
jstring java_str;
jmethodID toString;
/* Create a Java string, unless java_obj is already a java.lang.String */
if ((*jEnv)->IsInstanceOf(jEnv, java_obj, jlString)) {
java_str = java_obj;
} else {
jclass java_class;
java_class = class_descriptor->java_class;
toString = (*jEnv)->GetMethodID(jEnv, java_class, "toString",
"()Ljava/lang/String;");
if (!toString) {
/* All Java objects have a toString method */
jsj_UnexpectedJavaError(cx, jEnv, "No toString() method for class %s!",
class_descriptor->name);
return JS_FALSE;
}
java_str = (*jEnv)->CallObjectMethod(jEnv, java_obj, toString);
if (!java_str) {
jsj_ReportJavaError(cx, jEnv, "toString() method failed");
return JS_FALSE;
}
}
/* Extract Unicode from java.lang.String instance and convert to JS string */
js_str = jsj_ConvertJavaStringToJSString(cx, jEnv, java_str);
if (!js_str)
return JS_FALSE;
*vp = STRING_TO_JSVAL(js_str);
return JS_TRUE;
}
/*
* Convert a Java object to a number by attempting to call the
* doubleValue() method on a Java object to get a double result.
* This usually only works on instances of java.lang.Double, but the code
* is generalized to work with any Java object that supports this method.
*
* Returns JS_TRUE if the call was successful.
* Returns JS_FALSE if conversion is not possible or an error occurs.
*/
JSBool
jsj_ConvertJavaObjectToJSNumber(JSContext *cx, JNIEnv *jEnv,
jobject java_obj, jsval *vp)
{
jdouble d;
jmethodID doubleValue;
#ifndef SUN_VM_IS_NOT_GARBAGE
/* Late breaking news: calling GetMethodID() on an object that doesn't
contain the given method may cause the Sun VM to crash. So we only
call the method on instances of java.lang.Double */
JSBool is_Double;
/* Make sure that we have a java.lang.Double */
is_Double = (*jEnv)->IsInstanceOf(jEnv, java_obj, jlDouble);
if (!is_Double)
return JS_FALSE;
doubleValue = jlDouble_doubleValue;
#else
doubleValue = (*jEnv)->GetMethodID(jEnv, java_obj, "doubleValue", "()D");
if (!doubleValue)
return JS_FALSE;
#endif
d = (*jEnv)->CallDoubleMethod(jEnv, java_obj, doubleValue);
if ((*jEnv)->ExceptionOccurred(jEnv)) {
jsj_UnexpectedJavaError(cx, jEnv, "doubleValue() method failed");
return JS_FALSE;
}
return JS_NewDoubleValue(cx, d, vp);
}
/*
* Convert a Java object to a boolean by attempting to call the
* booleanValue() method on a Java object to get a boolean result.
* This usually only works on instances of java.lang.Boolean, but the code
* is generalized to work with any Java object that supports this method.
*
* Returns JS_TRUE if the call was successful.
* Returns JS_FALSE if conversion is not possible or an error occurs.
*/
extern JSBool
jsj_ConvertJavaObjectToJSBoolean(JSContext *cx, JNIEnv *jEnv,
jobject java_obj, jsval *vp)
{
jboolean b;
jmethodID booleanValue;
#ifndef SUN_VM_IS_NOT_GARBAGE
/* Late breaking news: calling GetMethodID() on an object that doesn't
contain the given method may cause the Sun VM to crash. So we only
call the method on instances of java.lang.Boolean */
JSBool is_Boolean;
/* Make sure that we have a java.lang.Boolean */
is_Boolean = (*jEnv)->IsInstanceOf(jEnv, java_obj, jlBoolean);
if (!is_Boolean)
return JS_FALSE;
booleanValue = jlBoolean_booleanValue;
#else
booleanValue = (*jEnv)->GetMethodID(jEnv, java_obj, "booleanValue", "()Z");
if (!booleanValue)
return JS_FALSE;
#endif
b = (*jEnv)->CallBooleanMethod(jEnv, java_obj, booleanValue);
if ((*jEnv)->ExceptionOccurred(jEnv)) {
jsj_UnexpectedJavaError(cx, jEnv, "booleanValue() method failed");
return JS_FALSE;
}
*vp = BOOLEAN_TO_JSVAL(b);
return JS_TRUE;
}
/*
* Reflect a Java object into a JS value. The source object, java_obj, must
* be of type java.lang.Object or a subclass and may, therefore, be an array.
*/
JSBool
jsj_ConvertJavaObjectToJSValue(JSContext *cx, JNIEnv *jEnv,
jobject java_obj, jsval *vp)
{
jclass java_class;
JSObject *js_obj;
/* A null in Java-land is also null in JS */
if (!java_obj) {
*vp = JSVAL_NULL;
return JS_TRUE;
}
java_class = (*jEnv)->GetObjectClass(jEnv, java_obj);
/*
* If it's an instance of netscape.javascript.JSObject, i.e. a wrapper
* around a JS object that has been passed into the Java world, unwrap
* it to obtain the original JS object.
*/
if (njJSObject && (*jEnv)->IsInstanceOf(jEnv, java_obj, njJSObject)) {
js_obj = (JSObject *)((*jEnv)->GetIntField(jEnv, java_obj, njJSObject_internal));
PR_ASSERT(js_obj);
if (!js_obj)
return JS_FALSE;
*vp = OBJECT_TO_JSVAL(js_obj);
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.
*/
/* TODO - let's get rid of this annoying "feature" */
/* otherwise, wrap it inside a JavaObject */
js_obj = jsj_WrapJavaObject(cx, jEnv, java_obj, java_class);
if (!js_obj)
return JS_FALSE;
*vp = OBJECT_TO_JSVAL(js_obj);
return JS_TRUE;
}
/*
* Convert a Java value (primitive or object) to a JS value.
*
* This is usually an infallible operation, but JS_FALSE is returned
* on an out-of-memory condition and the error reporter is called.
*/
JSBool
jsj_ConvertJavaValueToJSValue(JSContext *cx, JNIEnv *jEnv,
JavaSignature *signature,
jvalue *java_value,
jsval *vp)
{
int32 ival32;
switch (signature->type) {
case JAVA_SIGNATURE_VOID:
*vp = JSVAL_VOID;
return JS_TRUE;
case JAVA_SIGNATURE_BYTE:
*vp = INT_TO_JSVAL((jsint)java_value->b);
return JS_TRUE;
case JAVA_SIGNATURE_CHAR:
*vp = INT_TO_JSVAL((jsint)java_value->c);
return JS_TRUE;
case JAVA_SIGNATURE_SHORT:
*vp = INT_TO_JSVAL((jsint)java_value->s);
return JS_TRUE;
case JAVA_SIGNATURE_INT:
ival32 = java_value->i;
if (INT_FITS_IN_JSVAL(ival32)) {
*vp = INT_TO_JSVAL((jsint) ival32);
return JS_TRUE;
} else {
return JS_NewDoubleValue(cx, ival32, vp);
}
case JAVA_SIGNATURE_BOOLEAN:
*vp = BOOLEAN_TO_JSVAL((JSBool) java_value->z);
return JS_TRUE;
case JAVA_SIGNATURE_LONG:
return JS_NewDoubleValue(cx, (jsdouble)java_value->j, vp);
case JAVA_SIGNATURE_FLOAT:
return JS_NewDoubleValue(cx, java_value->f, vp);
case JAVA_SIGNATURE_DOUBLE:
return JS_NewDoubleValue(cx, java_value->d, vp);
case JAVA_SIGNATURE_CLASS:
case JAVA_SIGNATURE_ARRAY:
return jsj_ConvertJavaObjectToJSValue(cx, jEnv, java_value->l, vp);
default:
PR_ASSERT(0);
return JS_FALSE;
}
}

Просмотреть файл

@ -0,0 +1,383 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (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/NPL/
*
* 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.
*
* The Initial Developer of the Original Code is Netscape Communications
* Corporation. Portions created by Netscape are Copyright (C) 1998
* Netscape Communications Corporation. All Rights Reserved.
*/
/*
* This file is part of the Java-vendor-neutral implementation of LiveConnect
*
* It contains the code used to reflect Java fields as properties of
* JavaObject objects and the code to access those fields.
*
*/
#include <stdlib.h>
#include "prtypes.h"
#include "prprintf.h"
#include "prassert.h"
#include "jsj_private.h" /* LiveConnect internals */
/*
* Add a single field, described by java_field, to the JavaMemberDescriptor
* named by field_name within the given JavaClassDescriptor.
*
* Returns JS_TRUE on success. Otherwise, returns JS_FALSE and reports an error.
*/
static JSBool
add_java_field_to_class_descriptor(JSContext *cx,
JNIEnv *jEnv,
JavaClassDescriptor *class_descriptor,
jstring field_name_jstr,
jobject java_field, /* a java.lang.reflect.Field */
jint modifiers)
{
jclass fieldType;
jfieldID fieldID;
jclass java_class;
JSBool is_static_field;
JavaMemberDescriptor *member_descriptor = NULL;
const char *sig_cstr = NULL;
const char *field_name = NULL;
JavaSignature *signature = NULL;
JavaFieldSpec *field_spec = NULL;
is_static_field = modifiers & ACC_STATIC;
if (is_static_field) {
member_descriptor = jsj_GetJavaStaticMemberDescriptor(cx, jEnv, class_descriptor, field_name_jstr);
} else {
member_descriptor = jsj_GetJavaMemberDescriptor(cx, jEnv, class_descriptor, field_name_jstr);
}
if (!member_descriptor)
goto error;
field_spec = (JavaFieldSpec*)JS_malloc(cx, sizeof(JavaFieldSpec));
if (!field_spec)
goto error;
field_spec->modifiers = modifiers;
/* Get the Java class corresponding to the type of the field */
fieldType = (*jEnv)->CallObjectMethod(jEnv, java_field, jlrField_getType);
if (!fieldType) {
jsj_UnexpectedJavaError(cx, jEnv,
"Unable to determine type of field using"
" java.lang.reflect.Field.getType()");
goto error;
}
signature = jsj_GetJavaClassDescriptor(cx, jEnv, fieldType);
if (!signature)
goto error;
field_spec->signature = signature;
field_name = jsj_DupJavaStringUTF(cx, jEnv, field_name_jstr);
if (!field_name)
goto error;
field_spec->name = field_name;
/* Compute the JNI-style (string-based) signature of the field type */
sig_cstr = jsj_ConvertJavaSignatureToString(cx, signature);
if (!sig_cstr)
goto error;
/* Compute the JNI fieldID and cache it for quick field access */
java_class = class_descriptor->java_class;
if (is_static_field)
fieldID = (*jEnv)->GetStaticFieldID(jEnv, java_class, field_name, sig_cstr);
else
fieldID = (*jEnv)->GetFieldID(jEnv, java_class, field_name, sig_cstr);
if (!fieldID) {
jsj_UnexpectedJavaError(cx, jEnv,
"Can't get Java field ID for class %s, field %s (sig=%s)",
class_descriptor->name, field_name, sig_cstr);
goto error;
}
field_spec->fieldID = fieldID;
JS_free(cx, (char*)sig_cstr);
member_descriptor->field = field_spec;
/* Success */
return JS_TRUE;
error:
if (field_spec) {
JS_FREE_IF(cx, (char*)field_spec->name);
JS_free(cx, field_spec);
}
JS_FREE_IF(cx, (char*)sig_cstr);
if (signature)
jsj_ReleaseJavaClassDescriptor(cx, jEnv, signature);
return JS_FALSE;
}
/*
* Free up a JavaFieldSpec and all its resources.
*/
void
jsj_DestroyFieldSpec(JSContext *cx, JNIEnv *jEnv, JavaFieldSpec *field)
{
JS_FREE_IF(cx, (char*)field->name);
jsj_ReleaseJavaClassDescriptor(cx, jEnv, field->signature);
JS_free(cx, field);
}
/*
* Add a JavaMemberDescriptor to the collection of members in class_descriptor
* for every public field of the identified Java class. (A separate collection
* is kept in class_descriptor for static and instance members.)
* If reflect_only_static_fields is set, instance fields are not reflected. If
* it isn't set, only instance fields are reflected and static fields are not
* reflected.
*
* Returns JS_TRUE on success. Otherwise, returns JS_FALSE and reports an error.
*/
JSBool
jsj_ReflectJavaFields(JSContext *cx, JNIEnv *jEnv, JavaClassDescriptor *class_descriptor,
JSBool reflect_only_static_fields)
{
int i;
JSBool ok;
jint modifiers;
jobject java_field;
jstring field_name_jstr;
jarray joFieldArray;
jsize num_fields;
jclass java_class;
/* Get a java array of java.lang.reflect.Field objects, by calling
java.lang.Class.getFields(). */
java_class = class_descriptor->java_class;
joFieldArray = (*jEnv)->CallObjectMethod(jEnv, java_class, jlClass_getFields);
if (!joFieldArray) {
jsj_UnexpectedJavaError(cx, jEnv,
"Can't determine Java object's fields "
"using java.lang.Class.getFields()");
return JS_FALSE;
}
/* Iterate over the class fields */
num_fields = (*jEnv)->GetArrayLength(jEnv, joFieldArray);
for (i = 0; i < num_fields; i++) {
/* Get the i'th reflected field */
java_field = (*jEnv)->GetObjectArrayElement(jEnv, joFieldArray, i);
if (!java_field) {
jsj_UnexpectedJavaError(cx, jEnv, "Can't access a Field[] array");
return JS_FALSE;
}
/* Get the field modifiers, e.g. static, public, private, etc. */
modifiers = (*jEnv)->CallIntMethod(jEnv, java_field, jlrField_getModifiers);
if ((*jEnv)->ExceptionOccurred(jEnv)) {
jsj_UnexpectedJavaError(cx, jEnv,
"Can't access a Field's modifiers using"
"java.lang.reflect.Field.getModifiers()");
return JS_FALSE;
}
/* Don't allow access to private or protected Java fields. */
if (!(modifiers & ACC_PUBLIC))
continue;
/* Reflect all instance fields or all static fields, but not both */
if (reflect_only_static_fields != ((modifiers & ACC_STATIC) != 0))
continue;
/* Determine the unqualified name of the field */
field_name_jstr = (*jEnv)->CallObjectMethod(jEnv, java_field, jlrField_getName);
if (!field_name_jstr) {
jsj_UnexpectedJavaError(cx, jEnv,
"Can't obtain a Field's name"
"java.lang.reflect.Field.getName()");
return JS_FALSE;
}
/* Add a JavaFieldSpec object to the JavaClassDescriptor */
ok = add_java_field_to_class_descriptor(cx, jEnv, class_descriptor, field_name_jstr,
java_field, modifiers);
if (!ok)
return JS_FALSE;
}
/* Success */
return JS_TRUE;
}
/*
* Read the value of a Java field and return it as a JavaScript value.
* If the field is static, then java_obj is a Java class, otherwise
* it's a Java instance object.
*
* Returns JS_TRUE on success. Otherwise, returns JS_FALSE and reports an error.
*/
JSBool
jsj_GetJavaFieldValue(JSContext *cx, JNIEnv *jEnv, JavaFieldSpec *field_spec,
jobject java_obj, jsval *vp)
{
JSBool is_static_field;
jvalue java_value;
JavaSignature *signature;
JavaSignatureChar field_type;
jfieldID fieldID = field_spec->fieldID;
is_static_field = field_spec->modifiers & ACC_STATIC;
#define GET_JAVA_FIELD(Type,member) \
PR_BEGIN_MACRO \
if (is_static_field) \
java_value.member = \
(*jEnv)->GetStatic##Type##Field(jEnv, java_obj, fieldID); \
else \
java_value.member = \
(*jEnv)->Get##Type##Field(jEnv, java_obj, fieldID); \
if ((*jEnv)->ExceptionOccurred(jEnv)) { \
jsj_ReportJavaError(cx, jEnv, "Error reading Java field"); \
return JS_FALSE; \
} \
PR_END_MACRO
signature = field_spec->signature;
field_type = signature->type;
switch(field_type) {
case JAVA_SIGNATURE_BYTE:
GET_JAVA_FIELD(Byte,b);
break;
case JAVA_SIGNATURE_CHAR:
GET_JAVA_FIELD(Char,c);
break;
case JAVA_SIGNATURE_SHORT:
GET_JAVA_FIELD(Short,s);
break;
case JAVA_SIGNATURE_INT:
GET_JAVA_FIELD(Int,i);
break;
case JAVA_SIGNATURE_BOOLEAN:
GET_JAVA_FIELD(Boolean,z);
break;
case JAVA_SIGNATURE_LONG:
GET_JAVA_FIELD(Long,j);
break;
case JAVA_SIGNATURE_FLOAT:
GET_JAVA_FIELD(Float,f);
break;
case JAVA_SIGNATURE_DOUBLE:
GET_JAVA_FIELD(Double,d);
break;
case JAVA_SIGNATURE_CLASS:
case JAVA_SIGNATURE_ARRAY:
GET_JAVA_FIELD(Object,l);
break;
#undef GET_JAVA_FIELD
default:
PR_ASSERT(0); /* Unknown java type signature */
return JS_FALSE;
}
return jsj_ConvertJavaValueToJSValue(cx, jEnv, signature, &java_value, vp);
}
JSBool
jsj_SetJavaFieldValue(JSContext *cx, JNIEnv *jEnv, JavaFieldSpec *field_spec,
jclass java_obj, jsval js_val)
{
JSBool is_static_field;
int dummy_cost;
jvalue java_value;
JavaSignature *signature;
JavaSignatureChar field_type;
jfieldID fieldID = field_spec->fieldID;
is_static_field = field_spec->modifiers & ACC_STATIC;
#define SET_JAVA_FIELD(Type,member) \
PR_BEGIN_MACRO \
if (is_static_field) { \
(*jEnv)->SetStatic##Type##Field(jEnv, java_obj, fieldID, \
java_value.member); \
} else { \
(*jEnv)->Set##Type##Field(jEnv, java_obj, fieldID,java_value.member);\
} \
if ((*jEnv)->ExceptionOccurred(jEnv)) { \
jsj_ReportJavaError(cx, jEnv, "Error assigning to Java field"); \
return JS_FALSE; \
} \
PR_END_MACRO
signature = field_spec->signature;
if (!jsj_ConvertJSValueToJavaValue(cx, jEnv, js_val, signature, &dummy_cost, &java_value))
return JS_FALSE;
field_type = signature->type;
switch(field_type) {
case JAVA_SIGNATURE_BYTE:
SET_JAVA_FIELD(Byte,b);
break;
case JAVA_SIGNATURE_CHAR:
SET_JAVA_FIELD(Char,c);
break;
case JAVA_SIGNATURE_SHORT:
SET_JAVA_FIELD(Short,s);
break;
case JAVA_SIGNATURE_INT:
SET_JAVA_FIELD(Int,i);
break;
case JAVA_SIGNATURE_BOOLEAN:
SET_JAVA_FIELD(Boolean,z);
break;
case JAVA_SIGNATURE_LONG:
SET_JAVA_FIELD(Long,j);
break;
case JAVA_SIGNATURE_FLOAT:
SET_JAVA_FIELD(Float,f);
break;
case JAVA_SIGNATURE_DOUBLE:
SET_JAVA_FIELD(Double,d);
break;
case JAVA_SIGNATURE_CLASS:
case JAVA_SIGNATURE_ARRAY:
SET_JAVA_FIELD(Object,l);
break;
#undef SET_JAVA_FIELD
default:
PR_ASSERT(0); /* Unknown java type signature */
return JS_FALSE;
}
return JS_TRUE;
}

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -0,0 +1,508 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (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/NPL/
*
* 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.
*
* The Initial Developer of the Original Code is Netscape Communications
* Corporation. Portions created by Netscape are Copyright (C) 1998
* Netscape Communications Corporation. All Rights Reserved.
*/
/*
* This file is part of the Java-vendor-neutral implementation of LiveConnect
*
* Declarations of private (internal) functions/data/types for
* JavaScript <==> Java communication.
*
*/
#ifndef _JSJAVA_PVT_H
#define _JSJAVA_PVT_H
#include "prhash.h" /* NSPR hash-tables */
#include "jni.h" /* Java Native Interface */
#include "jsapi.h" /* JavaScript engine API */
/*************************** Type Declarations ******************************/
/* Forward type declarations */
typedef struct JavaMemberDescriptor JavaMemberDescriptor;
typedef struct JavaMethodSpec JavaMethodSpec;
typedef struct JavaClassDescriptor JavaClassDescriptor;
typedef struct JavaClassDescriptor JavaSignature;
typedef struct JSJCallbacks JSJCallbacks;
typedef struct CapturedJSError CapturedJSError;
typedef struct JavaPackageDef JavaPackageDef;
typedef struct JSJavaThreadState JSJavaThreadState;
typedef struct JSJavaVM JSJavaVM;
typedef struct JavaMemberVal JavaMemberVal;
/*
* This enum uses the same character encoding used by the JDK to encode
* Java type signatures, but the enum is easier to debug/compile with.
*/
typedef enum {
JAVA_SIGNATURE_ARRAY = '[',
JAVA_SIGNATURE_BYTE = 'B',
JAVA_SIGNATURE_CHAR = 'C',
JAVA_SIGNATURE_CLASS = 'L',
JAVA_SIGNATURE_FLOAT = 'F',
JAVA_SIGNATURE_DOUBLE = 'D',
JAVA_SIGNATURE_INT = 'I',
JAVA_SIGNATURE_LONG = 'J',
JAVA_SIGNATURE_SHORT = 'S',
JAVA_SIGNATURE_VOID = 'V',
JAVA_SIGNATURE_BOOLEAN = 'Z',
JAVA_SIGNATURE_UNKNOWN = 0
} JavaSignatureChar;
/* The signature of a Java method consists of the signatures of all its
arguments and its return type signature. */
typedef struct JavaMethodSignature {
jsize num_args; /* Length of arg_signatures array */
JavaSignature ** arg_signatures; /* Array of argument signatures */
JavaSignature * return_val_signature; /* Return type signature */
} JavaMethodSignature;
/* A descriptor for the reflection of a single Java field */
typedef struct JavaFieldSpec {
jfieldID fieldID; /* JVM opaque access handle for field */
JavaSignature * signature; /* Java type of field */
int modifiers; /* Bitfield indicating field qualifiers */
const char * name; /* UTF8; TODO - Should support Unicode field names */
} JavaFieldSpec;
/* A descriptor for the reflection of a single Java method.
Each overloaded method has a separate corresponding JavaMethodSpec. */
typedef struct JavaMethodSpec {
jmethodID methodID; /* JVM opaque access handle for method */
JavaMethodSignature signature;
const char * name; /* UTF8; TODO - Should support Unicode method names */
JavaMethodSpec * next; /* next method in chain of overloaded methods */
} JavaMethodSpec;
/*
* A descriptor for the reflection of a single member of a Java object.
* This can represent one or more Java methods and/or a single field.
* (When there is more than one method attached to a single JavaMemberDescriptor
* they are overloaded methods sharing the same simple name.) This same
* descriptor type is used for both static or instance members.
*/
typedef struct JavaMemberDescriptor {
const char * name; /* simple name of field and/or method */
jsid id; /* hashed name for quick JS property lookup */
JavaFieldSpec * field; /* field with the given name, if any */
JavaMethodSpec * methods; /* Overloaded methods which share the same name, if any */
JavaMemberDescriptor * next; /* next descriptor in same defining class */
} JavaMemberDescriptor;
/* This is the native portion of a reflected Java class */
typedef struct JavaClassDescriptor {
const char * name; /* Name of class, e.g. "java/lang/Byte" */
JavaSignatureChar type; /* class category: primitive type, object, array */
jclass java_class; /* Opaque JVM handle to corresponding java.lang.Class */
int num_instance_members;
int num_static_members;
JSBool instance_members_reflected;
JavaMemberDescriptor * instance_members;
JSBool static_members_reflected;
JavaMemberDescriptor * static_members;
JavaMemberDescriptor * constructors;
int modifiers; /* Class declaration qualifiers,
e.g. abstract, private */
int ref_count; /* # of references to this struct */
JavaSignature * array_component_signature; /* Only non-NULL for array classes */
} JavaClassDescriptor;
/* This is the native portion of a reflected Java method or field */
typedef struct JavaMemberVal {
jsval field_val; /* Captured value of Java field */
jsval invoke_method_func_val; /* JSFunction wrapper around Java method invoker */
JavaMemberDescriptor * descriptor;
JavaMemberVal * next;
} JavaMemberVal;
/* This is the native portion of a reflected Java object */
typedef struct {
jobject java_obj; /* Opaque JVM ref to Java object */
JavaClassDescriptor * class_descriptor; /* Java class info */
JavaMemberVal * members; /* Reflected methods and fields */
} JavaObjectWrapper;
/* These are definitions of the Java class/method/field modifier bits.
These really shouldn't be hard-coded here. Rather,
they should be read from java.lang.reflect.Modifier */
#define ACC_PUBLIC 0x0001 /* visible to everyone */
#define ACC_STATIC 0x0008 /* instance variable is static */
#define ACC_FINAL 0x0010 /* no further subclassing,overriding */
#define ACC_INTERFACE 0x0200 /* class is an interface */
#define ACC_ABSTRACT 0x0400 /* no definition provided */
/* A JSJavaVM structure must be created for each Java VM that is accessed
via LiveConnect */
typedef struct JSJavaVM {
/* TODO - all LiveConnect global variables should be migrated into this
structure in order to allow more than one LiveConnect-enabled
Java VM to exist within the same process. */
JavaVM * java_vm;
JNIEnv * main_thread_env; /* Main-thread Java environment */
JSBool jsj_created_java_vm;
int num_attached_threads;
JSJavaVM * next; /* next VM among all created VMs */
} JSJavaVM;
/* Per-thread state that encapsulates the connection to the Java VM */
typedef struct JSJavaThreadState {
const char * name; /* Thread name, for debugging */
JSJavaVM * jsjava_vm; /* All per-JVM state */
JNIEnv * jEnv; /* Per-thread opaque handle to Java VM */
CapturedJSError * pending_js_errors; /* JS errors to be thrown as Java exceptions */
JSContext * cx; /* current JS context for thread */
JSJavaThreadState * next; /* next thread state among all created threads */
} JSJavaThreadState;
/******************************** Globals ***********************************/
extern JNIEnv *jENV;
extern JSJCallbacks *JSJ_callbacks;
/* JavaScript classes that reflect Java objects */
extern JSClass JavaObject_class;
extern JSClass JavaArray_class;
extern JSClass JavaClass_class;
/*
* Opaque JVM handles to Java classes, methods and objects required for
* Java reflection. These are computed and cached during initialization.
* TODO: These should be moved inside the JSJavaVM struct
*/
extern jclass jlObject; /* java.lang.Object */
extern jclass jlrConstructor; /* java.lang.reflect.Constructor */
extern jclass jlThrowable; /* java.lang.Throwable */
extern jclass jlSystem; /* java.lang.System */
extern jclass jlClass; /* java.lang.Class */
extern jclass jlBoolean; /* java.lang.Boolean */
extern jclass jlDouble; /* java.lang.Double */
extern jclass jlString; /* java.lang.String */
extern jclass njJSObject; /* netscape.javascript.JSObject */
extern jclass njJSException; /* netscape.javascript.JSException */
extern jclass njJSUtil; /* netscape.javascript.JSUtil */
extern jmethodID jlClass_getMethods; /* java.lang.Class.getMethods() */
extern jmethodID jlClass_getConstructors; /* java.lang.Class.getConstructors() */
extern jmethodID jlClass_getFields; /* java.lang.Class.getFields() */
extern jmethodID jlClass_getName; /* java.lang.Class.getName() */
extern jmethodID jlClass_getComponentType; /* java.lang.Class.getComponentType() */
extern jmethodID jlClass_getModifiers; /* java.lang.Class.getModifiers() */
extern jmethodID jlClass_isArray; /* java.lang.Class.isArray() */
extern jmethodID jlrMethod_getName; /* java.lang.reflect.Method.getName() */
extern jmethodID jlrMethod_getParameterTypes; /* java.lang.reflect.Method.getParameterTypes() */
extern jmethodID jlrMethod_getReturnType; /* java.lang.reflect.Method.getReturnType() */
extern jmethodID jlrMethod_getModifiers; /* java.lang.reflect.Method.getModifiers() */
extern jmethodID jlrConstructor_getParameterTypes; /* java.lang.reflect.Constructor.getParameterTypes() */
extern jmethodID jlrConstructor_getModifiers; /* java.lang.reflect.Constructor.getModifiers() */
extern jmethodID jlrField_getName; /* java.lang.reflect.Field.getName() */
extern jmethodID jlrField_getType; /* java.lang.reflect.Field.getType() */
extern jmethodID jlrField_getModifiers; /* java.lang.reflect.Field.getModifiers() */
extern jmethodID jlThrowable_getMessage; /* java.lang.Throwable.getMessage() */
extern jmethodID jlThrowable_toString; /* java.lang.Throwable.toString() */
extern jmethodID jlBoolean_Boolean; /* java.lang.Boolean constructor */
extern jmethodID jlBoolean_booleanValue; /* java.lang.Boolean.booleanValue() */
extern jmethodID jlDouble_Double; /* java.lang.Double constructor */
extern jmethodID jlDouble_doubleValue; /* java.lang.Double.doubleValue() */
extern jmethodID jlSystem_identityHashCode; /* java.lang.System.identityHashCode() */
extern jobject jlVoid_TYPE; /* java.lang.Void.TYPE value */
extern jmethodID njJSException_JSException; /* netscape.javascipt.JSexception constructor */
extern jmethodID njJSObject_JSObject; /* netscape.javascript.JSObject constructor */
extern jmethodID njJSUtil_getStackTrace; /* netscape.javascript.JSUtil.getStackTrace() */
extern jfieldID njJSObject_internal; /* netscape.javascript.JSObject.internal */
extern jfieldID njJSException_lineno; /* netscape.javascript.JSException.lineno */
extern jfieldID njJSException_tokenIndex; /* netscape.javascript.JSException.tokenIndex */
extern jfieldID njJSException_source; /* netscape.javascript.JSException.source */
extern jfieldID njJSException_filename; /* netscape.javascript.JSException.filename */
/**************** Java <==> JS conversions and Java types *******************/
extern JSBool
jsj_ComputeJavaClassSignature(JSContext *cx,
JavaSignature *signature,
jclass java_class);
extern const char *
jsj_ConvertJavaSignatureToString(JSContext *cx, JavaSignature *signature);
extern const char *
jsj_ConvertJavaSignatureToHRString(JSContext *cx,
JavaSignature *signature);
extern JavaMethodSignature *
jsj_InitJavaMethodSignature(JSContext *cx, JNIEnv *jEnv, jobject method,
JavaMethodSignature *method_signature);
extern const char *
jsj_ConvertJavaMethodSignatureToString(JSContext *cx,
JavaMethodSignature *method_signature);
extern const char *
jsj_ConvertJavaMethodSignatureToHRString(JSContext *cx,
const char *method_name,
JavaMethodSignature *method_signature);
extern void
jsj_PurgeJavaMethodSignature(JSContext *cx, JNIEnv *jEnv, JavaMethodSignature *signature);
extern JSBool
jsj_ConvertJSValueToJavaValue(JSContext *cx, JNIEnv *jEnv, jsval v, JavaSignature *signature,
int *cost, jvalue *java_value);
extern JSBool
jsj_ConvertJSValueToJavaObject(JSContext *cx, JNIEnv *jEnv, jsval v, JavaSignature *signature,
int *cost, jobject *java_value);
extern jstring
jsj_ConvertJSStringToJavaString(JSContext *cx, JNIEnv *jEnv, JSString *js_str);
extern JSBool
jsj_ConvertJavaValueToJSValue(JSContext *cx, JNIEnv *jEnv, JavaSignature *signature,
jvalue *java_value, jsval *vp);
extern JSBool
jsj_ConvertJavaObjectToJSValue(JSContext *cx, JNIEnv *jEnv,
jobject java_obj, jsval *vp);
extern JSBool
jsj_ConvertJavaObjectToJSString(JSContext *cx, JNIEnv *jEnv,
JavaClassDescriptor *class_descriptor,
jobject java_obj, jsval *vp);
extern JSBool
jsj_ConvertJavaObjectToJSNumber(JSContext *cx, JNIEnv *jEnv,
jobject java_obj, jsval *vp);
extern JSBool
jsj_ConvertJavaObjectToJSBoolean(JSContext *cx, JNIEnv *jEnv,
jobject java_obj, jsval *vp);
/************************ Java package reflection **************************/
extern JSBool
jsj_init_JavaPackage(JSContext *, JSObject *,
JavaPackageDef *predefined_packages);
/************************* Java class reflection ***************************/
extern JSBool
jsj_init_JavaClass(JSContext *cx, JSObject *global_obj);
const char *
jsj_GetJavaClassName(JSContext *cx, JNIEnv *jEnv, jclass java_class);
extern JavaClassDescriptor *
jsj_GetJavaClassDescriptor(JSContext *cx, JNIEnv *jEnv, jclass java_class);
extern void
jsj_ReleaseJavaClassDescriptor(JSContext *cx, JNIEnv *jEnv, JavaClassDescriptor *class_descriptor);
extern JSObject *
jsj_define_JavaClass(JSContext *cx, JNIEnv *jEnv, JSObject *obj,
const char *unqualified_class_name,
const char *fully_qualified_class_name,
jclass jclazz);
extern JavaMemberDescriptor *
jsj_GetJavaMemberDescriptor(JSContext *cx,
JNIEnv *jEnv,
JavaClassDescriptor *class_descriptor,
jstring member_name);
/* extern JavaMemberDescriptor *
jsj_LookupJavaClassMember(JSContext *cx,
JavaClassDescriptor *class_descriptor,
const char *member_name);*/
extern JavaMemberDescriptor *
jsj_LookupJavaMemberDescriptorById(JSContext *cx, JNIEnv *jEnv,
JavaClassDescriptor *class_descriptor,
jsid id);
/* extern JavaMemberDescriptor *
jsj_LookupJavaStaticMemberDescriptor(JSContext *cx,
JavaClassDescriptor *class_descriptor,
jstring member_name); */
extern JavaMemberDescriptor *
jsj_LookupJavaStaticMemberDescriptorById(JSContext *cx, JNIEnv *jEnv,
JavaClassDescriptor *class_descriptor,
jsid id);
extern JavaMemberDescriptor *
jsj_GetJavaStaticMemberDescriptor(JSContext *cx, JNIEnv *jEnv,
JavaClassDescriptor *class_descriptor,
jstring member_name);
extern JavaMemberDescriptor *
jsj_GetJavaClassConstructors(JSContext *cx,
JavaClassDescriptor *class_descriptor);
extern JavaMemberDescriptor *
jsj_LookupJavaClassConstructors(JSContext *cx, JNIEnv *jEnv,
JavaClassDescriptor *class_descriptor);
extern JavaMemberDescriptor *
jsj_GetClassInstanceMembers(JSContext *cx, JNIEnv *jEnv,
JavaClassDescriptor *class_descriptor);
extern JavaMemberDescriptor *
jsj_GetClassStaticMembers(JSContext *cx, JNIEnv *jEnv,
JavaClassDescriptor *class_descriptor);
extern JSBool
jsj_InitJavaClassReflectionsTable();
/************************* Java field reflection ***************************/
extern JSBool
jsj_GetJavaFieldValue(JSContext *cx, JNIEnv *jEnv, JavaFieldSpec *field_spec,
jobject java_obj, jsval *vp);
extern JSBool
jsj_SetJavaFieldValue(JSContext *cx, JNIEnv *jEnv, JavaFieldSpec *field_spec,
jobject java_obj, jsval js_val);
extern JSBool
jsj_ReflectJavaFields(JSContext *cx, JNIEnv *jEnv,
JavaClassDescriptor *class_descriptor,
JSBool reflect_only_static_fields);
extern void
jsj_DestroyFieldSpec(JSContext *cx, JNIEnv *jEnv, JavaFieldSpec *field);
/************************* Java method reflection ***************************/
extern JSBool
jsj_JavaInstanceMethodWrapper(JSContext *cx, JSObject *obj,
uintN argc, jsval *argv, jsval *vp);
extern JSBool
jsj_JavaStaticMethodWrapper(JSContext *cx, JSObject *obj,
uintN argc, jsval *argv, jsval *vp);
extern JSBool
jsj_JavaConstructorWrapper(JSContext *cx, JSObject *obj,
uintN argc, jsval *argv, jsval *vp);
extern JSBool
jsj_ReflectJavaMethods(JSContext *cx, JNIEnv *jEnv,
JavaClassDescriptor *class_descriptor,
JSBool reflect_only_static_methods);
extern void
jsj_DestroyMethodSpec(JSContext *cx, JNIEnv *jEnv, JavaMethodSpec *method_spec);
/************************* Java member reflection ***************************/
extern JSBool
jsj_init_JavaMember(JSContext *, JSObject *);
extern JSBool
jsj_ReflectJavaMethodsAndFields(JSContext *cx, JavaClassDescriptor *class_descriptor,
JSBool reflect_only_statics);
/************************* Java object reflection **************************/
extern JSBool
jsj_init_JavaObject(JSContext *, JSObject *);
extern JSObject *
jsj_WrapJavaObject(JSContext *cx, JNIEnv *jEnv, jobject java_obj, jclass java_class);
extern JSBool
JavaObject_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp);
extern void
JavaObject_finalize(JSContext *cx, JSObject *obj);
extern JSBool
JavaObject_resolve(JSContext *cx, JSObject *obj, jsval id);
extern JSBool
JavaObject_enumerate(JSContext *cx, JSObject *obj);
extern JSBool
JavaObject_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
extern JSBool
JavaObject_getPropertyById(JSContext *cx, JSObject *obj, jsid id, jsval *vp);
/************************* Java array reflection ***************************/
extern JSBool
jsj_init_JavaArray(JSContext *cx, JSObject *global_obj);
extern JSBool
jsj_GetJavaArrayElement(JSContext *cx, JNIEnv *jEnv, jarray java_array,
jsize index, JavaSignature *array_component_signature,
jsval *vp);
extern JSBool
jsj_SetJavaArrayElement(JSContext *cx, JNIEnv *jEnv, jarray java_array,
jsize index, JavaSignature *array_component_signature,
jsval js_val);
/********************* JavaScript object reflection ************************/
extern jobject
jsj_WrapJSObject(JSContext *cx, JNIEnv *jEnv, JSObject *js_obj);
extern void
jsj_ClearPendingJSErrors(JSJavaThreadState *jsj_env);
extern JSBool
jsj_ReportUncaughtJSException(JSContext *cx, JNIEnv *jEnv, jthrowable java_exception);
/**************************** Utilities ************************************/
extern void
jsj_ReportJavaError(JSContext *cx, JNIEnv *env, const char *format, ...);
extern void
jsj_UnexpectedJavaError(JSContext *cx, JNIEnv *env, const char *format, ...);
extern const char *
jsj_GetJavaErrorMessage(JNIEnv *env);
extern void
jsj_LogError(const char *error_msg);
PR_CALLBACK prhashcode
jsj_HashJavaObject(const void *key);
PR_CALLBACK intN
jsj_JavaObjectComparator(const void *v1, const void *v2);
extern JSJavaThreadState *
jsj_MapJavaThreadToJSJavaThreadState(JNIEnv *jEnv, char **errp);
extern void
jsj_MakeJNIClassname(char *jClassName);
extern const char *
jsj_ClassNameOfJavaObject(JSContext *cx, JNIEnv *jEnv, jobject java_object);
extern jsize
jsj_GetJavaArrayLength(JSContext *cx, JNIEnv *jEnv, jarray java_array);
extern JSBool
JavaStringToId(JSContext *cx, JNIEnv *jEnv, jstring jstr, jsid *idp);
extern const char *
jsj_DupJavaStringUTF(JSContext *cx, JNIEnv *jEnv, jstring jstr);
JSJavaThreadState *
jsj_MapJSContextToJSJThread(JSContext *cx, JNIEnv **envp);
#ifdef DEBUG
#define DEBUG_LOG(args) printf args
#endif
#define JS_FREE_IF(cx, x) \
PR_BEGIN_MACRO \
if (x) \
JS_free(cx, x); \
PR_END_MACRO
#endif /* _JSJAVA_PVT_H */

Просмотреть файл

@ -0,0 +1,343 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (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/NPL/
*
* 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.
*
* The Initial Developer of the Original Code is Netscape Communications
* Corporation. Portions created by Netscape are Copyright (C) 1998
* Netscape Communications Corporation. All Rights Reserved.
*/
/*
* This file is part of the Java-vendor-neutral implementation of LiveConnect
*
* It contains low-level utility code.
*
*/
#include <stdlib.h>
#include <string.h>
#include "prtypes.h"
#include "prprintf.h"
#include "prassert.h"
#include "jsj_private.h" /* LiveConnect internals */
#include "jsjava.h" /* External LiveConnect API */
/*
* This is a hash-table utility routine that computes the hash code of a Java
* object by calling java.lang.System.identityHashCode()
*/
PR_CALLBACK prhashcode
jsj_HashJavaObject(const void *key)
{
prhashcode hash_code;
jobject java_obj;
java_obj = (jobject)key;
hash_code = (*jENV)->CallStaticIntMethod(jENV, jlSystem,
jlSystem_identityHashCode, java_obj);
PR_ASSERT(!(*jENV)->ExceptionOccurred(jENV));
return hash_code;
}
/*
* This is a hash-table utility routine for comparing two Java objects.
* It's not possible to use the == operator to directly compare two jobject's,
* since they're opaque references and aren't guaranteed to be simple pointers
* or handles (though they may be in some JVM implementations). Instead,
* use the JNI routine for comparing the two objects.
*/
PR_CALLBACK intN
jsj_JavaObjectComparator(const void *v1, const void *v2)
{
jobject java_obj1, java_obj2;
java_obj1 = (jobject)v1;
java_obj2 = (jobject)v2;
if (java_obj1 == java_obj2)
return 1;
return (*jENV)->IsSameObject(jENV, java_obj1, java_obj2);
}
/*
* Return a UTF8, null-terminated encoding of a Java string. The string must
* be free'ed by the caller.
*
* If an error occurs, returns NULL and calls the JS error reporter.
*/
const char *
jsj_DupJavaStringUTF(JSContext *cx, JNIEnv *jEnv, jstring jstr)
{
const char *str, *retval;
str = (*jEnv)->GetStringUTFChars(jEnv, jstr, 0);
if (!str) {
jsj_UnexpectedJavaError(cx, jEnv, "Can't get UTF8 characters from "
"Java string");
return NULL;
}
retval = JS_strdup(cx, str);
(*jEnv)->ReleaseStringUTFChars(jEnv, jstr, str);
return retval;
}
JSBool
JavaStringToId(JSContext *cx, JNIEnv *jEnv, jstring jstr, jsid *idp)
{
const jschar *ucs2;
JSString *jsstr;
jsize ucs2_len;
jsval val;
ucs2 = (*jEnv)->GetStringChars(jEnv, jstr, 0);
if (!ucs2) {
jsj_UnexpectedJavaError(cx, jEnv, "Couldn't obtain Unicode characters"
"from Java string");
return JS_FALSE;
}
ucs2_len = (*jEnv)->GetStringLength(jEnv, jstr);
jsstr = JS_InternUCStringN(cx, ucs2, ucs2_len);
(*jEnv)->ReleaseStringChars(jEnv, jstr, ucs2);
if (!jsstr)
return JS_FALSE;
val = STRING_TO_JSVAL(jsstr);
#ifndef NO_JSOBJECTOPS
JS_ValueToId(cx, STRING_TO_JSVAL(jsstr), idp);
#else
*valp = val
#endif
return JS_TRUE;
}
/* Not used ?
const char *
jsj_ClassNameOfJavaObject(JSContext *cx, JNIEnv *jEnv, jobject java_object)
{
jobject java_class;
java_class = (*jEnv)->GetObjectClass(jEnv, java_object);
if (!java_class) {
PR_ASSERT(0);
return NULL;
}
return jsj_GetJavaClassName(cx, jEnv, java_class);
}
*/
/*
* Return, as a C string, the error message associated with a Java exception
* that occurred as a result of a JNI call, preceded by the class name of
* the exception. As a special case, if the class of the exception is
* netscape.javascript.JSException, the exception class name is omitted.
*
* NULL is returned if no Java exception is pending. The caller is
* responsible for free'ing the returned string. On exit, the Java exception
* is *not* cleared.
*/
const char *
jsj_GetJavaErrorMessage(JNIEnv *jEnv)
{
const char *java_error_msg;
char *error_msg = NULL;
jthrowable exception;
jstring java_exception_jstring;
exception = (*jEnv)->ExceptionOccurred(jEnv);
if (exception) {
java_exception_jstring =
(*jEnv)->CallObjectMethod(jEnv, exception, jlThrowable_toString);
java_error_msg = (*jEnv)->GetStringUTFChars(jEnv, java_exception_jstring, NULL);
error_msg = strdup((char*)java_error_msg);
(*jEnv)->ReleaseStringUTFChars(jEnv, java_exception_jstring, java_error_msg);
#ifdef DEBUG
/* (*jEnv)->ExceptionDescribe(jEnv); */
#endif
}
return error_msg;
}
/*
* Return, as a C string, the JVM stack trace associated with a Java
* exception, as would be printed by java.lang.Throwable.printStackTrace().
* The caller is responsible for free'ing the returned string.
*
* Returns NULL if an error occurs.
*/
static const char *
get_java_stack_trace(JSContext *cx, JNIEnv *jEnv, jthrowable java_exception)
{
const char *backtrace;
jstring backtrace_jstr;
backtrace = NULL;
if (java_exception && njJSUtil_getStackTrace) {
backtrace_jstr = (*jEnv)->CallStaticObjectMethod(jEnv, njJSUtil,
njJSUtil_getStackTrace,
java_exception);
if (!backtrace_jstr) {
jsj_UnexpectedJavaError(cx, jEnv, "Unable to get exception stack trace");
return NULL;
}
backtrace = jsj_DupJavaStringUTF(cx, jEnv, backtrace_jstr);
}
return backtrace;
}
/* Full Java backtrace when Java exceptions reported to JavaScript */
#define REPORT_JAVA_EXCEPTION_STACK_TRACE
/*
* This is a wrapper around JS_ReportError(), useful when an error condition
* is the result of a JVM failure or exception condition. It appends the
* message associated with the pending Java exception to the passed in
* printf-style format string and arguments.
*/
static void
vreport_java_error(JSContext *cx, JNIEnv *jEnv, const char *format, va_list ap)
{
char *error_msg, *js_error_msg;
const char *java_stack_trace;
const char *java_error_msg;
jthrowable java_exception;
java_error_msg = NULL;
java_exception = (*jEnv)->ExceptionOccurred(jEnv);
if (java_exception && njJSException &&
(*jEnv)->IsInstanceOf(jEnv, java_exception, njJSException)) {
(*jEnv)->ExceptionClear(jEnv);
jsj_ReportUncaughtJSException(cx, jEnv, java_exception);
return;
}
js_error_msg = PR_vsmprintf(format, ap);
if (!js_error_msg) {
PR_ASSERT(0); /* Out-of-memory */
return;
}
#ifdef REPORT_JAVA_EXCEPTION_STACK_TRACE
java_stack_trace = get_java_stack_trace(cx, jEnv, java_exception);
if (java_stack_trace) {
error_msg = PR_smprintf("%s\n%s", js_error_msg, java_stack_trace);
free((char*)java_stack_trace);
if (!error_msg) {
PR_ASSERT(0); /* Out-of-memory */
return;
}
}
#else
java_error_msg = jsj_GetJavaErrorMessage(jEnv);
if (java_error_msg) {
error_msg = PR_smprintf("%s (%s)\n", js_error_msg, java_error_msg);
free((char*)java_error_msg);
free(js_error_msg);
} else {
error_msg = js_error_msg;
}
#endif
JS_ReportError(cx, error_msg);
/* Important: the Java exception must not be cleared until the reporter
has been called, because the capture_js_error_reports_for_java(),
called from JS_ReportError(), needs to read the exception from the JVM */
(*jEnv)->ExceptionClear(jEnv);
free(error_msg);
}
void
jsj_ReportJavaError(JSContext *cx, JNIEnv *env, const char *format, ...)
{
va_list ap;
va_start(ap, format);
vreport_java_error(cx, env, format, ap);
va_end(ap);
}
/*
* Same as jsj_ReportJavaError, except "internal error: " is prepended
* to message.
*/
void
jsj_UnexpectedJavaError(JSContext *cx, JNIEnv *env, const char *format, ...)
{
va_list ap;
const char *format2;
va_start(ap, format);
format2 = PR_smprintf("internal error: %s", format);
if (format2)
vreport_java_error(cx, env, format2, ap);
free((void*)format2);
va_end(ap);
}
/*
* Most LiveConnect errors are signaled by calling JS_ReportError(),
* but in some circumstances, the target JSContext for such errors
* is not determinable, e.g. during initialization. In such cases
* any error messages are routed to this function.
*/
void
jsj_LogError(const char *error_msg)
{
if (JSJ_callbacks && JSJ_callbacks->error_print)
JSJ_callbacks->error_print(error_msg);
else
fputs(error_msg, stderr);
}
jsize
jsj_GetJavaArrayLength(JSContext *cx, JNIEnv *jEnv, jarray java_array)
{
jsize array_length = (*jEnv)->GetArrayLength(jEnv, java_array);
if ((*jEnv)->ExceptionOccurred(jEnv)) {
jsj_UnexpectedJavaError(cx, jEnv, "Couldn't obtain array length");
return -1;
}
return array_length;
}
JSJavaThreadState *
jsj_MapJSContextToJSJThread(JSContext *cx, JNIEnv **envp)
{
JSJavaThreadState *jsj_env;
char *err_msg;
*envp = NULL;
err_msg = NULL;
jsj_env = JSJ_callbacks->map_js_context_to_jsj_thread(cx, &err_msg);
if (!jsj_env) {
if (err_msg) {
JS_ReportError(cx, err_msg);
free(err_msg);
}
return NULL;
}
if (envp)
*envp = jsj_env->jEnv;
return jsj_env;
}

234
js/ref/liveconnect/jsjava.h Normal file
Просмотреть файл

@ -0,0 +1,234 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (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/NPL/
*
* 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.
*
* The Initial Developer of the Original Code is Netscape Communications
* Corporation. Portions created by Netscape are Copyright (C) 1998
* Netscape Communications Corporation. All Rights Reserved.
*/
/*
* This file is part of the Java-vendor-neutral implementation of LiveConnect
*
* Publicly exported functions for JavaScript <==> Java communication.
*
*/
#ifndef _JSJAVA_H
#define _JSJAVA_H
#include "jni.h" /* Java Native Interface */
#include "jsapi.h" /* JavaScript engine API */
/*
* A JSJavaVM structure is a wrapper around a JavaVM which incorporates
* additional LiveConnect state.
*/
typedef struct JSJavaVM JSJavaVM;
/* LiveConnect and Java state, one per thread */
typedef struct JSJavaThreadState JSJavaThreadState;
/*
* This callback table provides hooks to external functions that implement
* functionality specific to the embedding. For example, these callbacks are
* necessary in multi-threaded environments or to implement a security
* policy.
*/
typedef struct JSJCallbacks {
/* This callback is invoked when there is no JavaScript execution
environment (JSContext) associated with the current Java thread and
a call is made from Java into JavaScript. (A JSContext is associated
with a Java thread by calling the JSJ_SetJSContextForJavaThread()
function.) This callback is only invoked when Java spontaneously calls
into JavaScript, i.e. it is not called when JS calls into Java which
calls back into JS.
This callback can be used to create a JSContext lazily, or obtain
one from a pool of available JSContexts. The implementation of this
callback can call JSJ_SetJSContextForJavaThread() to avoid any further
callbacks of this type for this Java thread. */
JSContext * (*map_jsj_thread_to_js_context)(JSJavaThreadState *jsj_env,
char **errp);
/* This callback is invoked whenever a call is made into Java from
JavaScript. It's responsible for mapping from a JavaScript execution
environment (JSContext) to a Java thread. (A JavaContext can only
be associated with one Java thread at a time.) */
JSJavaThreadState * (*map_js_context_to_jsj_thread)(JSContext *cx,
char **errp);
/* This callback implements netscape.javascript.JSObject.getWindow(),
a method named for its behavior in the browser environment, where it
returns the JS "Window" object corresponding to the HTML window that an
applet is embedded within. More generally, it's a way for Java to get
hold of a JS object that has not been explicitly passed to it. */
JSObject * (*map_java_object_to_js_object)(JNIEnv *jEnv, jobject hint,
char **errp);
/* An interim callback function until the LiveConnect security story is
straightened out. This function pointer can be set to NULL. */
JSPrincipals * (*get_JSPrincipals_from_java_caller)(JNIEnv *jEnv);
/* The following two callbacks sandwich any JS evaluation performed
from Java. They may be used to implement concurrency constraints, e.g.
by suspending the current thread until some condition is met. In the
browser embedding, these are used to maintain the run-to-completion
semantics of JavaScript. It is acceptable for either function pointer
to be NULL. */
JSBool (*enter_js_from_java)(char **errp);
void (*exit_js)(void);
/* Most LiveConnect errors are signaled by calling JS_ReportError(), but in
some circumstances, the target JSContext for such errors is not
determinable, e.g. during initialization. In such cases any error
messages are routed to this function. If the function pointer is set to
NULL, error messages are sent to stderr. */
void (*error_print)(const char *error_msg);
/* Reserved for future use */
void * reserved[10];
} JSJCallbacks;
/*===========================================================================*/
/* A flag that denotes that a Java package has no sub-packages other than those
explicitly pre-defined at the time of JSContext initialization. An access
to a simple name within such a package, therefore, must either correspond to
one of these explicitly named sub-packages or to a class within this
package. It is reasonable for LiveConnect to signal an error if a simple
name does not comply with these criteria. */
#define PKG_SYSTEM 1
/* A flag that denotes that a Java package which might contain sub-packages
that are not pre-defined at initialization time, because the sub-packages
may not be the same in all installations. Therefore, an access to a simple
name within such a a package which does not correspond to either a
pre-defined sub-package or to a class, must be assummed to refer to an
unknown sub-package. This behavior may cause bogus JavaPackage objects to be
created if a package name is misspelled, e.g. sun.oi.net. */
#define PKG_USER 2
/* A Java package defined at initialization time. */
typedef struct JavaPackageDef {
const char * name; /* e.g. "java.lang" */
const char * path; /* e.g. "java/lang", or NULL for default */
int flags; /* PKG_USER, PKG_SYSTEM, etc. */
} JavaPackageDef;
/*===========================================================================*/
/* The following two convenience functions present a complete, but simplified
LiveConnect API which is designed to handle the special case of a single
Java-VM, with single-threaded operation, and the use of only one JSContext.
The full API is in the section below. */
/* Initialize the provided JSContext by setting up the JS classes necessary for
reflection and by defining JavaPackage objects for the default Java packages
as properties of global_obj. If java_vm is NULL, a new Java VM is
created, using the provided classpath in addition to any default classpath.
The classpath argument is ignored, however, if java_vm is non-NULL. */
PR_PUBLIC_API(JSBool)
JSJ_SimpleInit(JSContext *cx, JSObject *global_obj,
JavaVM *java_vm, const char *classpath);
/* Free up all resources. Destroy the Java VM if it was created by LiveConnect */
PR_PUBLIC_API(void)
JSJ_SimpleShutdown();
/*===========================================================================*/
/* The "full" LiveConnect API, required when more than one thread, Java VM, or
JSContext is involved. Initialization pseudocode might go roughly like
this:
JSJ_Init() // Setup callbacks
for each JavaVM {
JSJ_ConnectToJavaVM(...)
}
for each JSContext {
JSJ_InitJSContext(...)
}
for each JS evaluation {
run JavaScript code in the JSContext;
}
*/
/* Called once for all instances of LiveConnect to set up callbacks */
PR_PUBLIC_API(void)
JSJ_Init(JSJCallbacks *callbacks);
/* Called once per Java VM, this function initializes the classes, fields, and
methods required for Java reflection. If java_vm is NULL, a new Java VM is
created, using the provided classpath in addition to any default classpath.
The classpath argument is ignored, however, if java_vm is non-NULL. */
PR_PUBLIC_API(JSJavaVM *)
JSJ_ConnectToJavaVM(JavaVM *java_vm, const char *classpath);
/* Initialize the provided JSContext by setting up the JS classes necessary for
reflection and by defining JavaPackage objects for the default Java packages
as properties of global_obj. Additional packages may be pre-defined by
setting the predefined_packages argument. (Pre-defining a Java package at
initialization time is not necessary, but it will make package lookup faster
and, more importantly, will avoid unnecessary network accesses if classes
are being loaded over the network.) */
PR_PUBLIC_API(JSBool)
JSJ_InitJSContext(JSContext *cx, JSObject *global_obj,
JavaPackageDef *predefined_packages);
/* This function returns a structure that encapsulates the Java and JavaScript
execution environment for the current native thread. It is intended to
be called from the embedder's implementation of JSJCallback's
map_js_context_to_jsj_thread() function. The thread_name argument is only
used for debugging purposes and can be set to NULL. The Java JNI
environment associated with this thread is returned through the java_envp
argument if java_envp is non-NULL. */
PR_PUBLIC_API(JSJavaThreadState *)
JSJ_AttachCurrentThreadToJava(JSJavaVM *jsjava_vm, const char *thread_name,
JNIEnv **java_envp);
/* Destructor routine for per-thread JSJavaThreadState structure */
PR_PUBLIC_API(JSBool)
JSJ_DetachCurrentThreadFromJava(JSJavaThreadState *jsj_env);
/* This function is used to specify a particular JSContext as *the* JavaScript
execution environment to be used when LiveConnect is accessed from the given
Java thread, i.e. when one of the methods of netscape.javascript.JSObject
has been called. There can only be one such JS context for any given Java
thread at a time. To multiplex JSContexts among a single thread, this
function could be called before Java is invoked on that thread.) The return
value is the previous JSContext associated with the given Java thread.
If this function has not been called for a thread and a crossing is made
into JavaScript from Java, the map_jsj_thread_to_js_context() callback will
be invoked to determine the JSContext for the thread. The purpose of the
function is to improve performance by avoiding the expense of the callback.
*/
PR_PUBLIC_API(JSContext *)
JSJ_SetDefaultJSContextForJavaThread(JSContext *cx, JSJavaThreadState *jsj_env);
/* This routine severs the connection to a Java VM, freeing all related resources.
It shouldn't be called until the global scope has been cleared in all related
JSContexts (so that all LiveConnect objects are finalized) and a JavaScript
GC is performed. Otherwise, accessed to free'ed memory could result. */
PR_PUBLIC_API(void)
JSJ_DisconnectFromJavaVM(JSJavaVM *);
/*
* Reflect a Java object into a JS value. The source object, java_obj, must
* be of type java.lang.Object or a subclass and may, therefore, be an array.
*/
PR_PUBLIC_API(JSBool)
JSJ_ConvertJavaObjectToJSValue(JSContext *cx, jobject java_obj, jsval *vp);
#endif /* _JSJAVA_H */

Просмотреть файл

@ -0,0 +1,101 @@
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class netscape_javascript_JSObject */
#ifndef _Included_netscape_javascript_JSObject
#define _Included_netscape_javascript_JSObject
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: netscape_javascript_JSObject
* Method: initClass
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_netscape_javascript_JSObject_initClass
(JNIEnv *, jclass);
/*
* Class: netscape_javascript_JSObject
* Method: getMember
* Signature: (Ljava/lang/String;)Ljava/lang/Object;
*/
JNIEXPORT jobject JNICALL Java_netscape_javascript_JSObject_getMember
(JNIEnv *, jobject, jstring);
/*
* Class: netscape_javascript_JSObject
* Method: getSlot
* Signature: (I)Ljava/lang/Object;
*/
JNIEXPORT jobject JNICALL Java_netscape_javascript_JSObject_getSlot
(JNIEnv *, jobject, jint);
/*
* Class: netscape_javascript_JSObject
* Method: setMember
* Signature: (Ljava/lang/String;Ljava/lang/Object;)V
*/
JNIEXPORT void JNICALL Java_netscape_javascript_JSObject_setMember
(JNIEnv *, jobject, jstring, jobject);
/*
* Class: netscape_javascript_JSObject
* Method: setSlot
* Signature: (ILjava/lang/Object;)V
*/
JNIEXPORT void JNICALL Java_netscape_javascript_JSObject_setSlot
(JNIEnv *, jobject, jint, jobject);
/*
* Class: netscape_javascript_JSObject
* Method: removeMember
* Signature: (Ljava/lang/String;)V
*/
JNIEXPORT void JNICALL Java_netscape_javascript_JSObject_removeMember
(JNIEnv *, jobject, jstring);
/*
* Class: netscape_javascript_JSObject
* Method: call
* Signature: (Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/Object;
*/
JNIEXPORT jobject JNICALL Java_netscape_javascript_JSObject_call
(JNIEnv *, jobject, jstring, jobjectArray);
/*
* Class: netscape_javascript_JSObject
* Method: eval
* Signature: (Ljava/lang/String;)Ljava/lang/Object;
*/
JNIEXPORT jobject JNICALL Java_netscape_javascript_JSObject_eval
(JNIEnv *, jobject, jstring);
/*
* Class: netscape_javascript_JSObject
* Method: toString
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_netscape_javascript_JSObject_toString
(JNIEnv *, jobject);
/*
* Class: netscape_javascript_JSObject
* Method: getWindow
* Signature: (Ljava/applet/Applet;)Lnetscape/javascript/JSObject;
*/
JNIEXPORT jobject JNICALL Java_netscape_javascript_JSObject_getWindow
(JNIEnv *, jclass, jobject);
/*
* Class: netscape_javascript_JSObject
* Method: finalize
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_netscape_javascript_JSObject_finalize
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif