зеркало из https://github.com/mozilla/pjs.git
[Not in the mozilla build]
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:
Родитель
ee281ae46f
Коммит
e871f15495
|
@ -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 <BR>
|
||||||
|
very much under construction.</FONT></H2></CENTER>
|
||||||
|
|
||||||
|
<H2>
|
||||||
|
Introduction</H2>
|
||||||
|
This is the README file for the JavaScript LiveConnect implementation.
|
||||||
|
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 (LiveConnect allows
|
||||||
|
JavaScript and Java virtual machines to be connected. It enables
|
||||||
|
JavaScript to access Java fields, invoke Java methods and makes it possible
|
||||||
|
for Java to access JavaScript object properties and evaluate JavaScript.
|
||||||
|
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).
|
||||||
|
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. 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.
|
||||||
|
See the JS API docs for more. 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.
|
||||||
|
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>.
|
||||||
|
This is used to establish paths for header file inclusion, linking and
|
||||||
|
execution. 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. 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,
|
||||||
|
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. The C preprocessor macro
|
||||||
|
DEBUG will be undefined, and NDEBUG (archaic Unix-ism for "No Debugging")
|
||||||
|
will be defined. 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",
|
||||||
|
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. 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()
|
||||||
|
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>
|
||||||
|
|
||||||
|
<BR>
|
||||||
|
<H2>
|
||||||
|
File walk-through</H2>
|
||||||
|
|
||||||
|
<TABLE BORDER=3 CELLSPACING=0 CELLPADDING=4 >
|
||||||
|
<TR>
|
||||||
|
<TD>jsjava.h</TD>
|
||||||
|
|
||||||
|
<TD>LiveConnect's only public header file. Defines all public API
|
||||||
|
entry points, callbacks and types. </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. JavaClassDescriptors are used to
|
||||||
|
describe the signatures of methods and fields. 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. Instances of
|
||||||
|
JavaArray are used to reflect Java arrays.</TD>
|
||||||
|
</TR>
|
||||||
|
|
||||||
|
<TR>
|
||||||
|
<TD>jsj_JavaClass.c</TD>
|
||||||
|
|
||||||
|
<TD>Implementation of the JavaScript JavaClass class. Instances
|
||||||
|
of JavaClass are used to reflect Java classes.</TD>
|
||||||
|
</TR>
|
||||||
|
|
||||||
|
<TR>
|
||||||
|
<TD>jsj_JavaObject.c</TD>
|
||||||
|
|
||||||
|
<TD>Implementation of the JavaScript JavaObject class. 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. 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 <I>netscape.javascript.JSObject</I>
|
||||||
|
Java class, which are used for calling into JavaScript from Java.
|
||||||
|
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. 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>
|
||||||
|
|
||||||
|
<BR>
|
||||||
|
</BODY>
|
||||||
|
</HTML>
|
|
@ -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;
|
||||||
|
}
|
|
@ -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
|
Загрузка…
Ссылка в новой задаче