diff --git a/mozilla/js/ref/liveconnect/LiveConnect.dsp b/mozilla/js/ref/liveconnect/LiveConnect.dsp
new file mode 100644
index 00000000000..705e239c304
--- /dev/null
+++ b/mozilla/js/ref/liveconnect/LiveConnect.dsp
@@ -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
diff --git a/mozilla/js/ref/liveconnect/LiveConnectShell.dsp b/mozilla/js/ref/liveconnect/LiveConnectShell.dsp
new file mode 100644
index 00000000000..5c48456437f
--- /dev/null
+++ b/mozilla/js/ref/liveconnect/LiveConnectShell.dsp
@@ -0,0 +1,106 @@
+# Microsoft Developer Studio Project File - Name="LiveConnectShell" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 5.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Console Application" 0x0103
+
+CFG=LiveConnectShell - 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 "LiveConnectShell.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 "LiveConnectShell.mak" CFG="LiveConnectShell - Win32 Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "LiveConnectShell - Win32 Release" (based on\
+ "Win32 (x86) Console Application")
+!MESSAGE "LiveConnectShell - Win32 Debug" (based on\
+ "Win32 (x86) Console Application")
+!MESSAGE
+
+# Begin Project
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "LiveConnectShell - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "LiveConnectShell_"
+# PROP BASE Intermediate_Dir "LiveConnectShell_"
+# 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 /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
+# ADD CPP /nologo /MD /W3 /GX /O2 /I "$(JDK)\include" /I "$(JDK)\include\win32" /I "." /D "_WIN32" /D "WIN32" /D "NDEBUG" /D "XP_PC" /D "_WINDOWS" /D "JSFILE" /D "LIVECONNECT" /YX /FD /c
+# 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:console /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 /nologo /subsystem:console /machine:I386 /out:"Release/lcshell.exe"
+# Begin Special Build Tool
+SOURCE=$(InputPath)
+PostBuild_Desc=Copy DLL(s) to build directory
+PostBuild_Cmds=COPY ..\Release\js32.dll .\Release
+# End Special Build Tool
+
+!ELSEIF "$(CFG)" == "LiveConnectShell - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "LiveConnectShell0"
+# PROP BASE Intermediate_Dir "LiveConnectShell0"
+# 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 /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
+# ADD CPP /nologo /MDd /W3 /Gm /GX /Zi /Od /I "$(JDK)\include" /I "$(JDK)\include\win32" /I "." /D "LIVECONNECT" /D "_WIN32" /D "WIN32" /D "_DEBUG" /D "DEBUG" /D "XP_PC" /D "_WINDOWS" /D "JSFILE" /FR /YX /FD /c
+# SUBTRACT CPP /u
+# 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:console /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 /nologo /subsystem:console /debug /machine:I386 /out:"Debug/lcshell.exe" /pdbtype:sept
+# Begin Special Build Tool
+SOURCE=$(InputPath)
+PostBuild_Desc=Copy DLL(s) to build directory
+PostBuild_Cmds=COPY ..\Debug\js32.dll .\Debug
+# End Special Build Tool
+
+!ENDIF
+
+# Begin Target
+
+# Name "LiveConnectShell - Win32 Release"
+# Name "LiveConnectShell - Win32 Debug"
+# Begin Source File
+
+SOURCE=..\js.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\Debug\js32.lib
+# End Source File
+# End Target
+# End Project
diff --git a/mozilla/js/ref/liveconnect/LiveConnectShell.dsw b/mozilla/js/ref/liveconnect/LiveConnectShell.dsw
new file mode 100644
index 00000000000..4752be78ae4
--- /dev/null
+++ b/mozilla/js/ref/liveconnect/LiveConnectShell.dsw
@@ -0,0 +1,44 @@
+Microsoft Developer Studio Workspace File, Format Version 5.00
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
+
+###############################################################################
+
+Project: "LiveConnect"=.\LiveConnect.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Project: "LiveConnectShell"=.\LiveConnectShell.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+ Begin Project Dependency
+ Project_Dep_Name LiveConnect
+ End Project Dependency
+}}}
+
+###############################################################################
+
+Global:
+
+Package=<5>
+{{{
+}}}
+
+Package=<3>
+{{{
+}}}
+
+###############################################################################
+
diff --git a/mozilla/js/ref/liveconnect/README.html b/mozilla/js/ref/liveconnect/README.html
new file mode 100644
index 00000000000..a61f3e8541d
--- /dev/null
+++ b/mozilla/js/ref/liveconnect/README.html
@@ -0,0 +1,288 @@
+
+
+
+
+
+ README for LiveConnect
+
+
+
+
+
+WARNING:
+
+
+
+This document is a skeleton, still
+very much under construction.
+
+
+Introduction
+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.
+
+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 DevEdge site.)
+
+
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.
+
+
Scott Furman, 4/8/98
+
+Build conventions
+To enable multi-threaded execution, define the JS_THREADSAFE
+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 JS_THREADSAFE.
+
+Update your JVM's CLASSPATH to point to
+the liveconnect/classes 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 netscape.javascript.JSObject
+class will be inaccessible.
+
Windows
+
+-
+Use MSDEV5.0 (LiveConnectShell.dsw).
+
+-
+You must first build the JS runtime, js32.dll, by using the normal JSRef
+build procedure.
+
+-
+Identify the JVM that you are linking against by setting the JDK
+environment variable to point to the top-level JDK directory, e.g. D:\jdk1.1.5.
+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.
+
+-
+The output files (DLLs and executables) are placed in the js/ref/liveconnect/Debug
+or js/ref/liveconnect/Release directories.
+
+-
+The LiveConnect-enabled shell is named lcshell.exe
+and appears in the output directory.
+
+-
+You must have the JVM DLL in your PATH environment
+variable in order to run. If you are using the Sun JDK, the DLL appears
+in the JDK's bin directory.
+
+Mac
+
+-
+LiveConnect is known to build on the Mac, but no
+project files created yet
+
+-
+Use CodeWarrior 1.x (???.prj.hqx) or 2 (???.prj2.hqx).
+
+Unix
+
+-
+No Makefiles created yet
+
+-
+Use vendor cc or gcc (ftp://prep.ai.mit.edu/pub/gnu) for compiling,
+and use gmake for building.
+
+-
+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.
+
+-
+Your own debug flag, DEBUG_$USER, will be defined or undefined as BUILD_OPT
+is unset or set.
+
+-
+To add C compiler options from the make command line, set XCFLAGS=-Dfoo.
+
+-
+To predefine -D or -U options in the makefile, set DEFINES.
+
+-
+To predefine -I options in the makefile, set INCLUDES.
+
+
+
+
+Naming and coding conventions:
+
+
+-
+Public function names begin with JSJ_ followed by capitalized "intercaps",
+e.g. JSJ_ConnectToJavaVM.
+
+-
+Extern but library-private function names use a jsj_ prefix and mixed case,
+e.g. jsj_LookupSymbol.
+
+-
+Most static function names have unprefixed, underscore-separated names:
+get_char.
+
+-
+But static native methods of JS objects have intercaps names, e.g., JavaObject_getProperty().
+
+-
+And library-private and static data use underscores, not intercaps (but
+library-private data do use a js_ prefix).
+
+-
+Scalar type names are lowercase and js-prefixed: jsdouble.
+
+-
+Aggregate type names are JS-prefixed and mixed-case: JSObject.
+
+-
+Macros are generally ALL_CAPS and underscored, to call out potential side
+effects, multiple uses of a formal argument, etc.
+
+-
+Four spaces of indentation per statement nesting level. The files
+are space-filled, so adjusting of your tab setting should be unnecessary.
+
+-
+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.
+
+-
+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).
+
+
+
+The LiveConnect API
+All public LiveConnect entry points and callbacks are documented in jsjava.h,
+the header file that exports those functions.
+
+Design walk-through
+
+
+
+File walk-through
+
+
+
+| jsjava.h |
+
+LiveConnect's only public header file. Defines all public API
+entry points, callbacks and types. |
+
+
+
+| jsj_private.h |
+
+LiveConnect internal header file for intra-module sharing of functions
+and types |
+
+
+
+| jsj.c |
+
+Public API entry points and initialization code |
+
+
+
+| jsj_array.c |
+
+Read and write elements of a Java array using the JNI. |
+
+
+
+| jsj_class.c |
+
+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. |
+
+
+
+| jsj_convert.c |
+
+Convert between Java and JavaScript values of all types, including
+the wrapping of JS objects as Java objects and vice-versa. |
+
+
+
+| jsj_field.c |
+
+Reflect Java fields as properties of JavaObject objects and implement
+getter/setter access to those fields. |
+
+
+
+| jsj_JavaArray.c |
+
+Implementation of the JavaScript JavaArray class. Instances of
+JavaArray are used to reflect Java arrays. |
+
+
+
+| jsj_JavaClass.c |
+
+Implementation of the JavaScript JavaClass class. Instances
+of JavaClass are used to reflect Java classes. |
+
+
+
+| jsj_JavaObject.c |
+
+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. |
+
+
+
+| jsj_JavaPackage.c |
+
+Implementation of the JavaScript JavaPackage class. Instances
+of JavaPackage are used to reflect Java packages. |
+
+
+
+| jsj_JSObject.c |
+
+Implementation of the native methods for the netscape.javascript.JSObject
+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. |
+
+
+
+| jsj_method.c |
+
+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. |
+
+
+
+| jsj_utils.c |
+
+Low-level utility code fo reporting errors, etc. |
+
+
+
+
+
+
diff --git a/mozilla/js/ref/liveconnect/jsj.c b/mozilla/js/ref/liveconnect/jsj.c
new file mode 100644
index 00000000000..6dfda36be02
--- /dev/null
+++ b/mozilla/js/ref/liveconnect/jsj.c
@@ -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
+#include
+#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,, 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;
+}
+
diff --git a/mozilla/js/ref/liveconnect/jsj_JSObject.c b/mozilla/js/ref/liveconnect/jsj_JSObject.c
new file mode 100644
index 00000000000..1947b93ac1f
--- /dev/null
+++ b/mozilla/js/ref/liveconnect/jsj_JSObject.c
@@ -0,0 +1,1089 @@
+/* -*- 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 implementation of the native methods for the
+ * netscape.javascript.JSObject 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.
+ *
+ */
+
+#include
+#include
+#include "prtypes.h"
+#include "prprintf.h"
+#include "prassert.h"
+
+#include "jsj_private.h"
+#include "jsjava.h"
+
+#include "jscntxt.h" /* For js_ReportErrorAgain(). FIXME - get rid of private header */
+
+#include "netscape_javascript_JSObject.h" /* javah-generated headers */
+
+
+/* A captured JavaScript error, created when JS_ReportError() is called while
+ running JavaScript code that is itself called from Java. */
+struct CapturedJSError {
+ char * message;
+ JSErrorReport report; /* Line # of error, etc. */
+ jthrowable java_exception; /* Java exception, error, or null */
+ CapturedJSError * next; /* Next oldest captured JS error */
+};
+
+
+/*********************** Reflection of JSObjects ****************************/
+
+/*
+ * This is a hash table that maps from JS objects to Java objects.
+ * It is used to ensure that the same Java object results when a JS
+ * object is reflected more than once, so that Java object equality
+ * tests work in the expected manner.
+ *
+ * The entry keys are JSObject pointers and the entry values are Java objects
+ * (of type jobject). When the corresponding Java instance is finalized,
+ * the entry is removed from the table, and a JavaScript GC root for the JS
+ * object is removed.
+ */
+static PRHashTable *js_obj_reflections = NULL;
+
+#ifdef JS_THREADSAFE
+/*
+ * Used to protect the js_obj_reflections hashtable from simultaneous
+ * read/write or * write/write access.
+ */
+static PRMonitor *js_obj_reflections_monitor = NULL;
+#endif
+
+static JSBool
+init_js_obj_reflections_table()
+{
+ js_obj_reflections = PR_NewHashTable(128, NULL, PR_CompareValues,
+ PR_CompareValues, NULL, NULL);
+ if (!js_obj_reflections)
+ return JS_FALSE;
+
+#ifdef JS_THREADSAFE
+ js_obj_reflections_monitor = PR_NewNamedMonitor("js_obj_reflections");
+ if (!js_obj_reflections_monitor) {
+ PR_HashTableDestroy(js_obj_reflections);
+ return JS_FALSE;
+ }
+#endif
+
+ return JS_TRUE;
+}
+
+/*
+ * Return a Java object that "wraps" a given JS object by storing the address
+ * of the JS object in a private field of the Java object. A hash table is
+ * used to ensure that the mapping of JS objects to Java objects is done on
+ * one-to-one basis.
+ *
+ * If an error occurs, returns NULL and reports an error.
+ */
+jobject
+jsj_WrapJSObject(JSContext *cx, JNIEnv *jEnv, JSObject *js_obj)
+{
+ jobject java_wrapper_obj;
+ PRHashEntry *he, **hep;
+
+ java_wrapper_obj = NULL;
+
+#ifdef JS_THREADSAFE
+ PR_EnterMonitor(js_obj_reflections_monitor);
+#endif
+
+ /* First, look in the hash table for an existing reflection of the same
+ JavaScript object. If one is found, return it. */
+ hep = PR_HashTableRawLookup(js_obj_reflections, (prhashcode)js_obj, js_obj);
+ he = *hep;
+ if (he) {
+ java_wrapper_obj = (jobject)he->value;
+ PR_ASSERT(java_wrapper_obj);
+ if (java_wrapper_obj)
+ goto done;
+ }
+
+ /* No existing reflection found, so create a new Java object that wraps
+ the JavaScript object by storing its address in a private integer field. */
+ java_wrapper_obj =
+ (*jEnv)->NewObject(jEnv, njJSObject, njJSObject_JSObject, (jint)js_obj);
+ if (!java_wrapper_obj) {
+ jsj_UnexpectedJavaError(cx, jEnv, "Couldn't create new instance of "
+ "netscape.javascript.JSObject");
+ goto done;
+ }
+
+ /* Add the new reflection to the hash table. */
+ he = PR_HashTableRawAdd(js_obj_reflections, hep, (prhashcode)js_obj,
+ js_obj, java_wrapper_obj);
+ if (he) {
+ /* Tell the JavaScript GC about this object since the only reference
+ to it may be in Java-land. */
+ JS_AddRoot(cx, (void*)&he->key);
+ } else {
+ JS_ReportOutOfMemory(cx);
+ /* No need to delete java_wrapper_obj because Java GC will reclaim it */
+ java_wrapper_obj = NULL;
+ }
+
+done:
+#ifdef JS_THREADSAFE
+ PR_ExitMonitor(js_obj_reflections_monitor);
+#endif
+
+ return java_wrapper_obj;
+}
+
+/*
+ * Remove the mapping of a JS object from the hash table that maps JS objects
+ * to Java objects. This is called from the finalizer of an instance of
+ * netscape.javascript.JSObject.
+ */
+static JSBool
+remove_js_obj_reflection_from_hashtable(JSContext *cx, JSObject *js_obj)
+{
+ PRHashEntry *he, **hep;
+ JSBool success = JS_FALSE;
+
+#ifdef JS_THREADSAFE
+ PR_EnterMonitor(js_obj_reflections_monitor);
+#endif
+
+ /* Get the hash-table entry for this wrapper object */
+ hep = PR_HashTableRawLookup(js_obj_reflections, (prhashcode)js_obj, js_obj);
+ he = *hep;
+
+ PR_ASSERT(he);
+ if (he) {
+ /* Tell the JS GC that Java no longer keeps a reference to this
+ JS object. */
+ success = JS_RemoveRoot(cx, (void *)&he->key);
+
+ PR_HashTableRawRemove(js_obj_reflections, hep, he);
+ }
+
+#ifdef JS_THREADSAFE
+ PR_ExitMonitor(js_obj_reflections_monitor);
+#endif
+
+ return success;
+}
+
+/*************** Handling of Java exceptions in JavaScript ******************/
+
+/*
+ * Free up a JavaScript error that has been stored by the
+ * capture_js_error_reports_for_java() function.
+ */
+static CapturedJSError *
+destroy_saved_js_error(JNIEnv *jEnv, CapturedJSError *error)
+{
+ CapturedJSError *next_error;
+ if (!error)
+ return NULL;
+ next_error = error->next;
+
+ if (error->java_exception)
+ (*jEnv)->DeleteGlobalRef(jEnv, error->java_exception);
+ if (error->message)
+ free((char*)error->message);
+ if (error->report.filename)
+ free((char*)error->report.filename);
+ if (error->report.linebuf)
+ free((char*)error->report.linebuf);
+ free(error);
+
+ return next_error;
+}
+
+/*
+ * Capture a JS error that has been reported using JS_ReportError() by JS code
+ * that has itself been called from Java. A copy of the JS error data is made
+ * and hung off the JSJ environment. When JS completes and returns to its Java
+ * caller, this data is used to synthesize an instance of
+ * netscape.javascript.JSException. If the resulting JSException is not caught
+ * within Java, it may be propagated up the stack beyond the Java caller back
+ * into JavaScript, in which case the error will be re-reported as a JavaScript
+ * error.
+ */
+PR_STATIC_CALLBACK(void)
+capture_js_error_reports_for_java(JSContext *cx, const char *message,
+ JSErrorReport *report)
+{
+ CapturedJSError *new_error;
+ JSJavaThreadState *jsj_env;
+ jthrowable java_exception;
+ JNIEnv *jEnv;
+
+ /* Create an empty struct to hold the saved JS error state */
+ new_error = malloc(sizeof(CapturedJSError));
+ if (!new_error)
+ goto out_of_memory;
+ memset(new_error, 0, sizeof(CapturedJSError));
+
+ /* Copy all the error info out of the original report into a private copy */
+ if (message) {
+ new_error->message = strdup(message);
+ if (!new_error->message)
+ goto out_of_memory;
+ }
+ if (report) {
+ new_error->report.lineno = report->lineno;
+
+ if (report->filename) {
+ new_error->report.filename = strdup(report->filename);
+ if (!new_error->report.filename)
+ goto out_of_memory;
+ }
+
+ if (report->linebuf) {
+ new_error->report.linebuf = strdup(report->linebuf);
+ if (!new_error->report.linebuf)
+ goto out_of_memory;
+ new_error->report.tokenptr =
+ new_error->report.linebuf + (report->tokenptr - report->linebuf);
+ }
+ }
+
+ /* Get the head of the list of pending JS errors */
+ jsj_env = jsj_MapJSContextToJSJThread(cx, &jEnv);
+ if (!jsj_env)
+ goto abort;
+
+ /* If there's a Java exception associated with this error, save it too. */
+ java_exception = (*jEnv)->ExceptionOccurred(jEnv);
+ if (java_exception) {
+ (*jEnv)->ExceptionClear(jEnv);
+ java_exception = (*jEnv)->NewGlobalRef(jEnv, java_exception);
+ new_error->java_exception = java_exception;
+ }
+
+ /* Push this error onto the list of pending JS errors */
+ new_error->next = jsj_env->pending_js_errors;
+ jsj_env->pending_js_errors = new_error;
+ return;
+
+abort:
+out_of_memory:
+ /* No recovery action possible */
+ PR_ASSERT(0);
+ destroy_saved_js_error(jEnv, new_error);
+ return;
+}
+
+void
+jsj_ClearPendingJSErrors(JSJavaThreadState *jsj_env)
+{
+ while (jsj_env->pending_js_errors)
+ jsj_env->pending_js_errors = destroy_saved_js_error(jsj_env->jEnv, jsj_env->pending_js_errors);
+}
+
+/*
+ * This is called upon returning from JS back into Java. Any JS errors
+ * reported during that time will be converted into Java exceptions. It's
+ * possible that a JS error was actually triggered by Java at some point, in
+ * which case the original Java exception is thrown.
+ */
+static void
+throw_any_pending_js_error_as_a_java_exception(JSJavaThreadState *jsj_env)
+{
+ CapturedJSError *error;
+ JNIEnv *jEnv;
+ jstring message_jstr, linebuf_jstr, filename_jstr;
+ jint index, lineno;
+ JSErrorReport *report;
+ jthrowable java_exception;
+
+ /* Get the Java JNI environment */
+ jEnv = jsj_env->jEnv;
+
+ if (!jsj_env->pending_js_errors) {
+#ifdef DEBUG
+ /* Any exception should be cleared as soon as it's detected, so there
+ shouldn't be any pending. */
+ if ((*jEnv)->ExceptionOccurred(jEnv)) {
+ /* A Java exception occurred, but nobody in JS-land noticed. */
+ PR_ASSERT(0);
+ (*jEnv)->ExceptionClear(jEnv);
+ }
+#endif
+ return;
+ }
+
+ /* Get the deepest (oldest) saved JS error */
+ /* XXX - What's the right thing to do about newer errors ?
+ For now we just throw them away */
+ error = jsj_env->pending_js_errors;
+ while (error->next)
+ error = error->next;
+
+ /*
+ * If the JS error was originally the result of a Java exception, rethrow
+ * the original exception.
+ */
+ if (error->java_exception) {
+ (*jEnv)->Throw(jEnv, error->java_exception);
+ goto done;
+ }
+
+ /* Propagate any JS errors that did not originate as Java exceptions into
+ Java as an instance of netscape.javascript.JSException */
+
+ /* First, marshall the arguments to the JSException constructor */
+ message_jstr = NULL;
+ if (error->message) {
+ message_jstr = (*jEnv)->NewStringUTF(jEnv, error->message);
+ if (!message_jstr)
+ goto out_of_memory;
+ }
+
+ report = &error->report;
+
+ filename_jstr = NULL;
+ if (report->filename) {
+ filename_jstr = (*jEnv)->NewStringUTF(jEnv, report->filename);
+ if (!filename_jstr)
+ goto out_of_memory;
+ }
+
+ linebuf_jstr = NULL;
+ if (report->linebuf) {
+ linebuf_jstr = (*jEnv)->NewStringUTF(jEnv, report->linebuf);
+ if (!linebuf_jstr)
+ goto out_of_memory;
+ }
+
+ lineno = report->lineno;
+ index = report->linebuf ? report->tokenptr - report->linebuf : 0;
+
+ /* Call the JSException constructor */
+ java_exception = (*jEnv)->NewObject(jEnv, njJSException, njJSException_JSException,
+ message_jstr, filename_jstr, lineno, linebuf_jstr, index);
+ if (!java_exception)
+ goto out_of_memory;
+
+ /* Throw the newly-created JSException */
+ if ((*jEnv)->Throw(jEnv, java_exception) < 0) {
+ PR_ASSERT(0);
+ jsj_LogError("Couldn't throw JSException\n");
+ goto done;
+ }
+
+ goto done;
+
+out_of_memory:
+ /* No recovery possible */
+ PR_ASSERT(0);
+ jsj_LogError("Out of memory while attempting to throw JSException\n");
+
+done:
+ jsj_ClearPendingJSErrors(jsj_env);
+}
+
+/*
+ * This function is called up returning from Java back into JS as a result of
+ * a thrown netscape.javascript.JSException, which itself must have been caused
+ * by a JS error when Java called into JS. The original JS error is
+ * reconstituted from the JSException and re-reported as a JS error.
+ *
+ * Returns JS_FALSE if an internal error occurs, JS_TRUE otherwise.
+ */
+JSBool
+jsj_ReportUncaughtJSException(JSContext *cx, JNIEnv *jEnv, jthrowable java_exception)
+{
+ JSBool success;
+ JSErrorReport report;
+ const char *linebuf, *filename, *message, *tokenptr;
+ jint lineno, token_index;
+ jstring linebuf_jstr, filename_jstr, message_jstr;
+
+ /* Initialize everything to NULL */
+ memset(&report, 0, sizeof(JSErrorReport));
+ success = JS_FALSE;
+ filename_jstr = linebuf_jstr = NULL;
+ filename = message = linebuf = tokenptr = NULL;
+
+ lineno = (*jEnv)->GetIntField(jEnv, java_exception, njJSException_lineno);
+ report.lineno = lineno;
+
+ filename_jstr = (*jEnv)->GetObjectField(jEnv, java_exception, njJSException_filename);
+ if ((*jEnv)->ExceptionOccurred(jEnv)) {
+ jsj_UnexpectedJavaError(cx, jEnv, "Unable to access filename field of a JSException");
+ goto done;
+ }
+ if (filename_jstr)
+ filename = (*jEnv)->GetStringUTFChars(jEnv, filename_jstr, 0);
+ report.filename = filename;
+
+ linebuf_jstr = (*jEnv)->GetObjectField(jEnv, java_exception, njJSException_source);
+ if ((*jEnv)->ExceptionOccurred(jEnv)) {
+ jsj_UnexpectedJavaError(cx, jEnv, "Unable to access source field of a JSException");
+ goto done;
+ }
+ if (linebuf_jstr)
+ linebuf = (*jEnv)->GetStringUTFChars(jEnv, linebuf_jstr, 0);
+ report.linebuf = linebuf;
+
+ token_index = (*jEnv)->GetIntField(jEnv, java_exception, njJSException_lineno);
+ report.tokenptr = linebuf + token_index;
+
+ message_jstr = (*jEnv)->CallObjectMethod(jEnv, java_exception, jlThrowable_getMessage);
+ if ((*jEnv)->ExceptionOccurred(jEnv)) {
+ jsj_UnexpectedJavaError(cx, jEnv, "Unable to access message of a JSException");
+ goto done;
+ }
+ if (message_jstr)
+ message = (*jEnv)->GetStringUTFChars(jEnv, message_jstr, 0);
+
+ js_ReportErrorAgain(cx, message, &report);
+
+ success = JS_TRUE;
+
+done:
+
+ if (filename_jstr && filename)
+ (*jEnv)->ReleaseStringUTFChars(jEnv, filename_jstr, filename);
+ if (linebuf_jstr && linebuf)
+ (*jEnv)->ReleaseStringUTFChars(jEnv, linebuf_jstr, linebuf);
+ if (message_jstr && message)
+ (*jEnv)->ReleaseStringUTFChars(jEnv, message_jstr, message);
+ return success;
+}
+
+
+
+/*************** Utilities for calling JavaScript from Java *****************/
+
+/*
+ * This routine is called just prior to invoking any JS API function from
+ * Java. It performs a common set of chores, such as obtaining the LiveConnect
+ * state for the invoking Java thread and determining the JSContext to be
+ * used for this thread.
+ *
+ * Returns NULL on failure.
+ */
+JSJavaThreadState *
+enter_js(JNIEnv *jEnv, jobject java_wrapper_obj,
+ JSContext **cxp, JSObject **js_objp, JSErrorReporter *old_error_reporterp)
+{
+ JSContext *cx;
+ char *err_msg;
+ JSObject *js_obj;
+ JSJavaThreadState *jsj_env;
+
+ cx = NULL;
+ err_msg = NULL;
+
+ /* Invoke callback, presumably used to implement concurrency constraints */
+ if (JSJ_callbacks->enter_js_from_java) {
+ if (!JSJ_callbacks->enter_js_from_java(&err_msg))
+ goto entry_failure;
+ }
+
+ /* Check the JSObject pointer in the wrapper object for null while holding
+ the JS lock to deal with shutdown issues. */
+ if (js_objp) {
+ js_obj = (JSObject *)((*jEnv)->GetIntField(jEnv, java_wrapper_obj, njJSObject_internal));
+ PR_ASSERT(js_obj);
+ if (!js_obj)
+ goto error;
+ *js_objp = js_obj;
+ }
+
+ /* Get the per-thread state corresponding to the current Java thread */
+ jsj_env = jsj_MapJavaThreadToJSJavaThreadState(jEnv, &err_msg);
+ if (!jsj_env)
+ goto error;
+
+ /* Get the JSContext that we're supposed to use for this Java thread */
+ cx = jsj_env->cx;
+ if (!cx) {
+ /* We called spontaneously into JS from Java, rather than from JS into
+ Java and back into JS. Invoke a callback to obtain/create a
+ JSContext for us to use. */
+ if (JSJ_callbacks->map_jsj_thread_to_js_context) {
+ cx = JSJ_callbacks->map_jsj_thread_to_js_context(jsj_env, &err_msg);
+ if (!cx)
+ goto error;
+ } else {
+ err_msg = PR_smprintf("Unable to find/create JavaScript execution "
+ "context for JNI thread 0x%08x", jEnv);
+ goto error;
+ }
+ }
+ *cxp = cx;
+
+ /*
+ * Capture all JS error reports so that they can be thrown into the Java
+ * caller as an instance of netscape.javascript.JSException.
+ */
+ *old_error_reporterp =
+ JS_SetErrorReporter(cx, capture_js_error_reports_for_java);
+
+ return jsj_env;
+
+error:
+ /* Invoke callback, presumably used to implement concurrency constraints */
+ if (JSJ_callbacks->exit_js)
+ JSJ_callbacks->exit_js();
+
+entry_failure:
+ if (err_msg) {
+ if (cx)
+ JS_ReportError(cx, err_msg);
+ else
+ jsj_LogError(err_msg);
+ free(err_msg);
+ }
+
+ return NULL;
+}
+
+/*
+ * This utility function is called just prior to returning into Java from JS.
+ */
+static JSBool
+exit_js(JSContext *cx, JSJavaThreadState *jsj_env, JSErrorReporter original_reporter)
+{
+ JNIEnv *jEnv;
+
+ /* Restore the JS error reporter */
+ JS_SetErrorReporter(cx, original_reporter);
+
+ jEnv = jsj_env->jEnv;
+
+#ifdef DEBUG
+ /* Any Java exceptions should have been noticed and reported already */
+ if ((*jEnv)->ExceptionOccurred(jEnv)) {
+ PR_ASSERT(0);
+ jsj_LogError("Unhandled Java exception detected");
+ return JS_FALSE;
+ }
+#endif
+
+ /*
+ * Convert reported JS errors to JSExceptions, unless the errors were
+ * themselves the result of Java exceptions, in which case the original
+ * Java exception is simply propagated.
+ */
+ throw_any_pending_js_error_as_a_java_exception(jsj_env);
+
+ /* Invoke callback, presumably used to implement concurrency constraints */
+ if (JSJ_callbacks->exit_js)
+ JSJ_callbacks->exit_js();
+
+ return JS_TRUE;
+}
+
+
+/* Get the JavaClassDescriptor that corresponds to java.lang.Object */
+static JavaClassDescriptor *
+get_jlObject_descriptor(JSContext *cx, JNIEnv *jEnv)
+{
+ /* The JavaClassDescriptor for java.lang.Object */
+ static JavaClassDescriptor *jlObject_descriptor = NULL;
+
+ if (!jlObject_descriptor)
+ jlObject_descriptor = jsj_GetJavaClassDescriptor(cx, jEnv, jlObject);
+ return jlObject_descriptor;
+}
+
+/****************** Implementation of methods of JSObject *******************/
+
+/*
+ * Class: netscape_javascript_JSObject
+ * Method: initClass
+ * Signature: ()V
+ */
+JNIEXPORT void JNICALL
+Java_netscape_javascript_JSObject_initClass(JNIEnv *jEnv, jclass java_class)
+{
+ init_js_obj_reflections_table();
+}
+
+/*
+ * Class: netscape_javascript_JSObject
+ * Method: getMember
+ * Signature: (Ljava/lang/String;)Ljava/lang/Object;
+ */
+JNIEXPORT jobject JNICALL
+Java_netscape_javascript_JSObject_getMember(JNIEnv *jEnv,
+ jobject java_wrapper_obj,
+ jstring property_name_jstr)
+{
+ JSContext *cx;
+ JSObject *js_obj;
+ jsval js_val;
+ int dummy_cost;
+ const jchar *property_name_ucs2;
+ jsize property_name_len;
+ JSErrorReporter saved_reporter;
+ jobject member;
+ jboolean is_copy;
+ JSJavaThreadState *jsj_env;
+
+ jsj_env = enter_js(jEnv, java_wrapper_obj, &cx, &js_obj, &saved_reporter);
+ if (!jsj_env)
+ return NULL;
+
+ property_name_ucs2 = NULL;
+ if (!property_name_jstr) {
+ JS_ReportError(cx, "illegal null member name");
+ member = NULL;
+ goto done;
+ }
+
+ /* Get the Unicode string for the JS property name */
+ property_name_ucs2 = (*jEnv)->GetStringChars(jEnv, property_name_jstr, &is_copy);
+ if (!property_name_ucs2) {
+ PR_ASSERT(0);
+ goto done;
+ }
+ property_name_len = (*jEnv)->GetStringLength(jEnv, property_name_jstr);
+
+ if (!JS_GetUCProperty(cx, js_obj, property_name_ucs2, property_name_len, &js_val))
+ goto done;
+
+ jsj_ConvertJSValueToJavaObject(cx, jEnv, js_val, get_jlObject_descriptor(cx, jEnv),
+ &dummy_cost, &member);
+
+done:
+ if (property_name_ucs2)
+ (*jEnv)->ReleaseStringChars(jEnv, property_name_jstr, property_name_ucs2);
+ if (!exit_js(cx, jsj_env, saved_reporter))
+ return NULL;
+
+ return member;
+}
+
+/*
+ * Class: netscape_javascript_JSObject
+ * Method: getSlot
+ * Signature: (I)Ljava/lang/Object;
+ */
+JNIEXPORT jobject JNICALL
+Java_netscape_javascript_JSObject_getSlot(JNIEnv *jEnv,
+ jobject java_wrapper_obj,
+ jint slot)
+{
+ JSContext *cx;
+ JSObject *js_obj;
+ jsval js_val;
+ int dummy_cost;
+ JSErrorReporter saved_reporter;
+ jobject member;
+ JSJavaThreadState *jsj_env;
+
+ jsj_env = enter_js(jEnv, java_wrapper_obj, &cx, &js_obj, &saved_reporter);
+ if (!jsj_env)
+ return NULL;
+
+
+ if (!JS_GetElement(cx, js_obj, slot, &js_val))
+ goto done;
+ if (!jsj_ConvertJSValueToJavaObject(cx, jEnv, js_val, get_jlObject_descriptor(cx, jEnv),
+ &dummy_cost, &member))
+ goto done;
+
+done:
+ if (!exit_js(cx, jsj_env, saved_reporter))
+ return NULL;
+
+ return member;
+}
+
+/*
+ * Class: netscape_javascript_JSObject
+ * Method: setMember
+ * Signature: (Ljava/lang/String;Ljava/lang/Object;)V
+ */
+JNIEXPORT void JNICALL
+Java_netscape_javascript_JSObject_setMember(JNIEnv *jEnv,
+ jobject java_wrapper_obj,
+ jstring property_name_jstr,
+ jobject java_obj)
+{
+ JSContext *cx;
+ JSObject *js_obj;
+ jsval js_val;
+ const jchar *property_name_ucs2;
+ jsize property_name_len;
+ JSErrorReporter saved_reporter;
+ jboolean is_copy;
+ JSJavaThreadState *jsj_env;
+
+ jsj_env = enter_js(jEnv, java_wrapper_obj, &cx, &js_obj, &saved_reporter);
+ if (!jsj_env)
+ return;
+
+ property_name_ucs2 = NULL;
+ if (!property_name_jstr) {
+ JS_ReportError(cx, "illegal null member name");
+ goto done;
+ }
+
+ /* FIXME - can property watchers be used to avoid security checks ? */
+ /* Get the Unicode string for the JS property name */
+ property_name_ucs2 = (*jEnv)->GetStringChars(jEnv, property_name_jstr, &is_copy);
+ if (!property_name_ucs2) {
+ PR_ASSERT(0);
+ goto done;
+ }
+ property_name_len = (*jEnv)->GetStringLength(jEnv, property_name_jstr);
+
+ if (!jsj_ConvertJavaObjectToJSValue(cx, jEnv, java_obj, &js_val))
+ goto done;
+
+ JS_SetUCProperty(cx, js_obj, property_name_ucs2, property_name_len, &js_val);
+
+done:
+ if (property_name_ucs2)
+ (*jEnv)->ReleaseStringChars(jEnv, property_name_jstr, property_name_ucs2);
+ exit_js(cx, jsj_env, saved_reporter);
+}
+
+/*
+ * Class: netscape_javascript_JSObject
+ * Method: setSlot
+ * Signature: (ILjava/lang/Object;)V
+ */
+JNIEXPORT void JNICALL
+Java_netscape_javascript_JSObject_setSlot(JNIEnv *jEnv,
+ jobject java_wrapper_obj,
+ jint slot,
+ jobject java_obj)
+{
+ JSContext *cx;
+ JSObject *js_obj;
+ jsval js_val;
+ JSErrorReporter saved_reporter;
+ JSJavaThreadState *jsj_env;
+
+ jsj_env = enter_js(jEnv, java_wrapper_obj, &cx, &js_obj, &saved_reporter);
+ if (!jsj_env)
+ return;
+
+ if (!jsj_ConvertJavaObjectToJSValue(cx, jEnv, java_obj, &js_val))
+ goto done;
+ JS_SetElement(cx, js_obj, slot, &js_val);
+
+done:
+ exit_js(cx, jsj_env, saved_reporter);
+}
+
+/*
+ * Class: netscape_javascript_JSObject
+ * Method: removeMember
+ * Signature: (Ljava/lang/String;)V
+ */
+JNIEXPORT void JNICALL
+Java_netscape_javascript_JSObject_removeMember(JNIEnv *jEnv,
+ jobject java_wrapper_obj,
+ jstring property_name_jstr)
+{
+ JSContext *cx;
+ JSObject *js_obj;
+ jsval js_val;
+ const jchar *property_name_ucs2;
+ jsize property_name_len;
+ JSErrorReporter saved_reporter;
+ jboolean is_copy;
+ JSJavaThreadState *jsj_env;
+
+ jsj_env = enter_js(jEnv, java_wrapper_obj, &cx, &js_obj, &saved_reporter);
+ if (!jsj_env)
+ return;
+
+ if (!property_name_jstr) {
+ JS_ReportError(cx, "illegal null member name");
+ goto done;
+ }
+ /* Get the Unicode string for the JS property name */
+ property_name_ucs2 = (*jEnv)->GetStringChars(jEnv, property_name_jstr, &is_copy);
+ if (!property_name_ucs2) {
+ PR_ASSERT(0);
+ goto done;
+ }
+ property_name_len = (*jEnv)->GetStringLength(jEnv, property_name_jstr);
+
+ JS_DeleteUCProperty2(cx, js_obj, property_name_ucs2, property_name_len, &js_val);
+
+ (*jEnv)->ReleaseStringChars(jEnv, property_name_jstr, property_name_ucs2);
+
+done:
+ exit_js(cx, jsj_env, saved_reporter);
+ return;
+}
+
+/*
+ * 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 *jEnv, jobject java_wrapper_obj,
+ jstring function_name_jstr, jobjectArray java_args)
+{
+ int i, argc, arg_num;
+ jsval *argv;
+ JSContext *cx;
+ JSObject *js_obj;
+ jsval js_val, function_val;
+ int dummy_cost;
+ const jchar *function_name_ucs2;
+ jsize function_name_len;
+ JSErrorReporter saved_reporter;
+ jboolean is_copy;
+ jobject result;
+ JSJavaThreadState *jsj_env;
+
+ jsj_env = enter_js(jEnv, java_wrapper_obj, &cx, &js_obj, &saved_reporter);
+ if (!jsj_env)
+ return NULL;
+
+ function_name_ucs2 = NULL;
+ result = NULL;
+ if (!function_name_jstr) {
+ JS_ReportError(cx, "illegal null JavaScript function name");
+ goto done;
+ }
+
+ /* Get the function name to eval as raw Unicode characters */
+ function_name_ucs2 = (*jEnv)->GetStringChars(jEnv, function_name_jstr, &is_copy);
+ if (!function_name_ucs2) {
+ PR_ASSERT(0);
+ goto done;
+ }
+ function_name_len = (*jEnv)->GetStringLength(jEnv, function_name_jstr);
+
+ /* FIXME: What about security stuff ? Don't principals need to be set here ? */
+
+ /* Allocate space for JS arguments */
+ if (java_args) {
+ argc = (*jEnv)->GetArrayLength(jEnv, java_args);
+ argv = (jsval*)JS_malloc(cx, argc * sizeof(jsval));
+ } else {
+ argc = 0;
+ argv = 0;
+ }
+
+ /* Convert arguments from Java to JS values */
+ for (arg_num = 0; arg_num < argc; arg_num++) {
+ jobject arg = (*jEnv)->GetObjectArrayElement(jEnv, java_args, arg_num);
+
+ if (!jsj_ConvertJavaObjectToJSValue(cx, jEnv, arg, &argv[arg_num]))
+ goto cleanup_argv;
+ JS_AddRoot(cx, &argv[arg_num]);
+ }
+
+ if (!JS_GetUCProperty(cx, js_obj, function_name_ucs2, function_name_len,
+ &function_val))
+ goto cleanup_argv;
+
+ if (!JS_CallFunctionValue(cx, js_obj, function_val, argc, argv, &js_val))
+ goto cleanup_argv;
+
+ jsj_ConvertJSValueToJavaObject(cx, jEnv, js_val, get_jlObject_descriptor(cx, jEnv),
+ &dummy_cost, &result);
+
+cleanup_argv:
+ if (argv) {
+ for (i = 0; i < arg_num; i++)
+ JS_RemoveRoot(cx, &argv[i]);
+ JS_free(cx, argv);
+ }
+
+done:
+ if (function_name_ucs2)
+ (*jEnv)->ReleaseStringChars(jEnv, function_name_jstr, function_name_ucs2);
+ if (!exit_js(cx, jsj_env, saved_reporter))
+ return NULL;
+
+ return result;
+}
+
+/*
+ * Class: netscape_javascript_JSObject
+ * Method: eval
+ * Signature: (Ljava/lang/String;)Ljava/lang/Object;
+ */
+JNIEXPORT jobject JNICALL
+Java_netscape_javascript_JSObject_eval(JNIEnv *jEnv,
+ jobject java_wrapper_obj,
+ jstring eval_jstr)
+{
+ const char *codebase;
+ JSPrincipals *principals;
+ JSContext *cx;
+ JSBool eval_succeeded;
+ JSObject *js_obj;
+ jsval js_val;
+ int dummy_cost;
+ const jchar *eval_ucs2;
+ jsize eval_len;
+ JSErrorReporter saved_reporter;
+ jboolean is_copy;
+ jobject result;
+ JSJavaThreadState *jsj_env;
+
+ jsj_env = enter_js(jEnv, java_wrapper_obj, &cx, &js_obj, &saved_reporter);
+ if (!jsj_env)
+ return NULL;
+
+ result = NULL;
+ eval_ucs2 = NULL;
+ if (!eval_jstr) {
+ JS_ReportError(cx, "illegal null string eval argument");
+ goto done;
+ }
+
+ /* Get the string to eval as raw Unicode characters */
+ eval_ucs2 = (*jEnv)->GetStringChars(jEnv, eval_jstr, &is_copy);
+ if (!eval_ucs2) {
+ PR_ASSERT(0);
+ goto done;
+ }
+ eval_len = (*jEnv)->GetStringLength(jEnv, eval_jstr);
+
+ /* Set up security stuff */
+ principals = NULL;
+ if (JSJ_callbacks->get_JSPrincipals_from_java_caller)
+ principals = JSJ_callbacks->get_JSPrincipals_from_java_caller(jEnv);
+ codebase = principals ? principals->codebase : NULL;
+
+ /* Have the JS engine evaluate the unicode string */
+ eval_succeeded = JS_EvaluateUCScriptForPrincipals(cx, js_obj, principals,
+ eval_ucs2, eval_len,
+ codebase, 0, &js_val);
+ if (!eval_succeeded)
+ goto done;
+
+ /* Convert result to a subclass of java.lang.Object */
+ jsj_ConvertJSValueToJavaObject(cx, jEnv, js_val, get_jlObject_descriptor(cx, jEnv),
+ &dummy_cost, &result);
+
+done:
+ if (eval_ucs2)
+ (*jEnv)->ReleaseStringChars(jEnv, eval_jstr, eval_ucs2);
+ if (!exit_js(cx, jsj_env, saved_reporter))
+ return NULL;
+
+ return result;
+}
+
+/*
+ * Class: netscape_javascript_JSObject
+ * Method: toString
+ * Signature: ()Ljava/lang/String;
+ */
+JNIEXPORT jstring JNICALL
+Java_netscape_javascript_JSObject_toString(JNIEnv *jEnv,
+ jobject java_wrapper_obj)
+{
+ jstring result;
+ JSContext *cx;
+ JSObject *js_obj;
+ JSString *jsstr;
+ JSErrorReporter saved_reporter;
+ JSJavaThreadState *jsj_env;
+
+ jsj_env = enter_js(jEnv, java_wrapper_obj, &cx, &js_obj, &saved_reporter);
+ if (!jsj_env)
+ return NULL;
+
+ result = NULL;
+ jsstr = JS_ValueToString(cx, OBJECT_TO_JSVAL(js_obj));
+ if (jsstr)
+ result = jsj_ConvertJSStringToJavaString(cx, jEnv, jsstr);
+ if (!result)
+ result = (*jEnv)->NewStringUTF(jEnv, "*JavaObject*");
+
+ if (!exit_js(cx, jsj_env, saved_reporter))
+ return NULL;
+
+ return result;
+}
+
+/*
+ * Class: netscape_javascript_JSObject
+ * Method: getWindow
+ * Signature: (Ljava/applet/Applet;)Lnetscape/javascript/JSObject;
+ */
+JNIEXPORT jobject JNICALL
+Java_netscape_javascript_JSObject_getWindow(JNIEnv *jEnv,
+ jclass js_object_class,
+ jobject java_applet_obj)
+{
+ char *err_msg;
+ JSContext *cx;
+ JSObject *js_obj;
+ jsval js_val;
+ int dummy_cost;
+ JSErrorReporter saved_reporter;
+ jobject java_obj;
+ JSJavaThreadState *jsj_env;
+
+ jsj_env = enter_js(jEnv, NULL, &cx, NULL, &saved_reporter);
+ if (!jsj_env)
+ return NULL;
+
+ err_msg = NULL;
+ java_obj = NULL;
+ js_obj = JSJ_callbacks->map_java_object_to_js_object(jEnv, java_applet_obj, &err_msg);
+ if (!js_obj) {
+ if (err_msg) {
+ JS_ReportError(cx, err_msg);
+ free(err_msg);
+ }
+ goto done;
+ }
+ js_val = OBJECT_TO_JSVAL(js_obj);
+ jsj_ConvertJSValueToJavaObject(cx, jEnv, js_val, get_jlObject_descriptor(cx, jEnv),
+ &dummy_cost, &java_obj);
+done:
+ if (!exit_js(cx, jsj_env, saved_reporter))
+ return NULL;
+
+ return java_obj;
+}
+
+/*
+ * Class: netscape_javascript_JSObject
+ * Method: finalize
+ * Signature: ()V
+ */
+JNIEXPORT void JNICALL
+Java_netscape_javascript_JSObject_finalize(JNIEnv *jEnv, jobject java_wrapper_obj)
+{
+ JSContext *cx;
+ JSErrorReporter saved_reporter;
+ JSJavaThreadState *jsj_env;
+ JSObject *js_obj;
+
+ jsj_env = enter_js(jEnv, java_wrapper_obj, &cx, &js_obj, &saved_reporter);
+ if (!jsj_env) /* Note: memory leak if we exit here */
+ return;
+ remove_js_obj_reflection_from_hashtable(cx, js_obj);
+ exit_js(cx, jsj_env, saved_reporter);
+}
+
diff --git a/mozilla/js/ref/liveconnect/jsj_JavaArray.c b/mozilla/js/ref/liveconnect/jsj_JavaArray.c
new file mode 100644
index 00000000000..b76141b841d
--- /dev/null
+++ b/mozilla/js/ref/liveconnect/jsj_JavaArray.c
@@ -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
+#include
+#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;
+}
+
diff --git a/mozilla/js/ref/liveconnect/jsj_JavaClass.c b/mozilla/js/ref/liveconnect/jsj_JavaClass.c
new file mode 100644
index 00000000000..b59116a5132
--- /dev/null
+++ b/mozilla/js/ref/liveconnect/jsj_JavaClass.c
@@ -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
+#include
+
+#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, "");
+ 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;
+}
diff --git a/mozilla/js/ref/liveconnect/jsj_JavaObject.c b/mozilla/js/ref/liveconnect/jsj_JavaObject.c
new file mode 100644
index 00000000000..7e1bc188d7f
--- /dev/null
+++ b/mozilla/js/ref/liveconnect/jsj_JavaObject.c
@@ -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
+#include
+#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();
+}
diff --git a/mozilla/js/ref/liveconnect/jsj_JavaPackage.c b/mozilla/js/ref/liveconnect/jsj_JavaPackage.c
new file mode 100644
index 00000000000..cf8b1b2a0f3
--- /dev/null
+++ b/mozilla/js/ref/liveconnect/jsj_JavaPackage.c
@@ -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
+#include
+
+#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;
+}
diff --git a/mozilla/js/ref/liveconnect/jsj_array.c b/mozilla/js/ref/liveconnect/jsj_array.c
new file mode 100644
index 00000000000..fa6eac66c4e
--- /dev/null
+++ b/mozilla/js/ref/liveconnect/jsj_array.c
@@ -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;
+}
+
diff --git a/mozilla/js/ref/liveconnect/jsj_class.c b/mozilla/js/ref/liveconnect/jsj_class.c
new file mode 100644
index 00000000000..d09e3d34050
--- /dev/null
+++ b/mozilla/js/ref/liveconnect/jsj_class.c
@@ -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
+#include
+
+#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, "");
+ 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;
+}
diff --git a/mozilla/js/ref/liveconnect/jsj_convert.c b/mozilla/js/ref/liveconnect/jsj_convert.c
new file mode 100644
index 00000000000..0347db7cd0f
--- /dev/null
+++ b/mozilla/js/ref/liveconnect/jsj_convert.c
@@ -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
+#include
+
+#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;
+ }
+}
+
diff --git a/mozilla/js/ref/liveconnect/jsj_field.c b/mozilla/js/ref/liveconnect/jsj_field.c
new file mode 100644
index 00000000000..e2aee419c9e
--- /dev/null
+++ b/mozilla/js/ref/liveconnect/jsj_field.c
@@ -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
+#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;
+}
diff --git a/mozilla/js/ref/liveconnect/jsj_method.c b/mozilla/js/ref/liveconnect/jsj_method.c
new file mode 100644
index 00000000000..b5c44b683cc
--- /dev/null
+++ b/mozilla/js/ref/liveconnect/jsj_method.c
@@ -0,0 +1,1105 @@
+/* -*- 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 methods as properties of
+ * JavaObject objects and the code to invoke those methods.
+ *
+ */
+
+#include
+#include
+
+#include "prtypes.h"
+#include "prprintf.h"
+#include "prassert.h"
+
+#include "jsj_private.h" /* LiveConnect internals */
+#include "jsjava.h" /* LiveConnect external API */ /* FIXME - get rid of this include */
+
+/*
+ * A helper function for jsj_ConvertJavaMethodSignatureToString():
+ * Compute JNI-style (string) signatures for an array of JavaSignature objects
+ * and concatenate the results into a single string.
+ *
+ * If an error is encountered, NULL is returned and the error reporter is called.
+ */
+static const char *
+convert_java_method_arg_signatures_to_string(JSContext *cx,
+ JavaSignature **arg_signatures,
+ int num_args)
+{
+ const char *first_arg_signature, *rest_arg_signatures, *sig;
+ JavaSignature **rest_args;
+
+ /* Convert the first method argument in the array to a string */
+ first_arg_signature = jsj_ConvertJavaSignatureToString(cx, arg_signatures[0]);
+ if (!first_arg_signature)
+ return NULL;
+
+ /* If this is the last method argument in the array, we're done. */
+ if (num_args == 1)
+ return first_arg_signature;
+
+ /* Convert the remaining method arguments to a string */
+ rest_args = &arg_signatures[1];
+ rest_arg_signatures =
+ convert_java_method_arg_signatures_to_string(cx, rest_args, num_args - 1);
+ if (!rest_arg_signatures) {
+ free((void*)first_arg_signature);
+ return NULL;
+ }
+
+ /* Concatenate the signature string of this argument with the signature
+ strings of all the remaining arguments. */
+ sig = PR_smprintf("%s%s", first_arg_signature, rest_arg_signatures);
+ free((void*)first_arg_signature);
+ free((void*)rest_arg_signatures);
+ if (!sig) {
+ JS_ReportOutOfMemory(cx);
+ return NULL;
+ }
+
+ return sig;
+}
+
+/*
+ * Compute a JNI-style (string) signature for the given method, e.g. the method
+ * "String MyFunc(int, byte)" is translated to "(IB)Ljava/lang/String;".
+ *
+ * If an error is encountered, NULL is returned and the error reporter is called.
+ */
+const char *
+jsj_ConvertJavaMethodSignatureToString(JSContext *cx,
+ JavaMethodSignature *method_signature)
+{
+ JavaSignature **arg_signatures, *return_val_signature;
+ const char *arg_sigs_cstr;
+ const char *return_val_sig_cstr;
+ const char *sig_cstr;
+
+ arg_signatures = method_signature->arg_signatures;
+ return_val_signature = method_signature->return_val_signature;
+
+ /* Convert the method argument signatures to a C-string */
+ arg_sigs_cstr = NULL;
+ if (arg_signatures) {
+ arg_sigs_cstr =
+ convert_java_method_arg_signatures_to_string(cx, arg_signatures,
+ method_signature->num_args);
+ if (!arg_sigs_cstr)
+ return NULL;
+ }
+
+ /* Convert the method return value signature to a C-string */
+ return_val_sig_cstr = jsj_ConvertJavaSignatureToString(cx, return_val_signature);
+ if (!return_val_sig_cstr) {
+ free((void*)arg_sigs_cstr);
+ return NULL;
+ }
+
+ /* Compose method arg signatures string and return val signature string */
+ if (arg_sigs_cstr) {
+ sig_cstr = PR_smprintf("(%s)%s", arg_sigs_cstr, return_val_sig_cstr);
+ free((void*)arg_sigs_cstr);
+ } else {
+ sig_cstr = PR_smprintf("()%s", return_val_sig_cstr);
+ }
+
+ free((void*)return_val_sig_cstr);
+
+ if (!sig_cstr) {
+ JS_ReportOutOfMemory(cx);
+ return NULL;
+ }
+ return sig_cstr;
+}
+
+/*
+ * A helper function for jsj_ConvertJavaMethodSignatureToHRString():
+ * Compute human-readable string signatures for an array of JavaSignatures
+ * and concatenate the results into a single string.
+ *
+ * If an error is encountered, NULL is returned and the error reporter is called.
+ */
+static const char *
+convert_java_method_arg_signatures_to_hr_string(JSContext *cx,
+ JavaSignature **arg_signatures,
+ int num_args)
+{
+ const char *first_arg_signature, *rest_arg_signatures, *sig;
+ JavaSignature **rest_args;
+
+ /* Convert the first method argument in the array to a string */
+ first_arg_signature = jsj_ConvertJavaSignatureToHRString(cx, arg_signatures[0]);
+ if (!first_arg_signature)
+ return NULL;
+
+ /* If this is the last method argument in the array, we're done. */
+ if (num_args == 1)
+ return first_arg_signature;
+
+ /* Convert the remaining method arguments to a string */
+ rest_args = &arg_signatures[1];
+ rest_arg_signatures =
+ convert_java_method_arg_signatures_to_hr_string(cx, rest_args, num_args - 1);
+ if (!rest_arg_signatures) {
+ free((void*)first_arg_signature);
+ return NULL;
+ }
+
+ /* Concatenate the signature string of this argument with the signature
+ strings of all the remaining arguments. */
+ sig = PR_smprintf("%s, %s", first_arg_signature, rest_arg_signatures);
+ free((void*)first_arg_signature);
+ free((void*)rest_arg_signatures);
+ if (!sig) {
+ JS_ReportOutOfMemory(cx);
+ return NULL;
+ }
+
+ return sig;
+}
+/*
+ * Compute a string signature for the given method using the same type names
+ * that appear in Java source files, e.g. "int[][]", rather than the JNI-style
+ * type strings that are provided by the functions above. An example return
+ * value might be "String MyFunc(int, byte, char[][], java.lang.String)".
+ *
+ * If an error is encountered, NULL is returned and the error reporter is called.
+ */
+const char *
+jsj_ConvertJavaMethodSignatureToHRString(JSContext *cx,
+ const char *method_name,
+ JavaMethodSignature *method_signature)
+{
+ JavaSignature **arg_signatures, *return_val_signature;
+ const char *arg_sigs_cstr;
+ const char *return_val_sig_cstr;
+ const char *sig_cstr;
+
+ arg_signatures = method_signature->arg_signatures;
+ return_val_signature = method_signature->return_val_signature;
+
+ /* Convert the method argument signatures to a C-string */
+ arg_sigs_cstr = NULL;
+ if (arg_signatures) {
+ arg_sigs_cstr =
+ convert_java_method_arg_signatures_to_hr_string(cx, arg_signatures,
+ method_signature->num_args);
+ if (!arg_sigs_cstr)
+ return NULL;
+ }
+
+ /* Convert the method return value signature to a C-string */
+ return_val_sig_cstr = jsj_ConvertJavaSignatureToHRString(cx, return_val_signature);
+ if (!return_val_sig_cstr) {
+ free((void*)arg_sigs_cstr);
+ return NULL;
+ }
+
+ /* Compose method arg signatures string and return val signature string */
+ if (arg_sigs_cstr) {
+ sig_cstr = PR_smprintf("%s %s(%s)", return_val_sig_cstr, method_name, arg_sigs_cstr);
+ free((void*)arg_sigs_cstr);
+ } else {
+ sig_cstr = PR_smprintf("%s %s()", return_val_sig_cstr, method_name);
+ }
+
+ free((void*)return_val_sig_cstr);
+
+ if (!sig_cstr) {
+ JS_ReportOutOfMemory(cx);
+ return NULL;
+ }
+ return sig_cstr;
+}
+/*
+ * Destroy the sub-structure of a JavaMethodSignature object without free'ing
+ * the method signature itself.
+ */
+void
+jsj_PurgeJavaMethodSignature(JSContext *cx, JNIEnv *jEnv, JavaMethodSignature *method_signature)
+{
+ int i, num_args;
+ JavaSignature **arg_signatures;
+
+ if (!method_signature) /* Paranoia */
+ return;
+
+ /* Free the method argument signatures */
+ num_args = method_signature->num_args;
+ arg_signatures = method_signature->arg_signatures;
+ for (i = 0; i < num_args; i++)
+ jsj_ReleaseJavaClassDescriptor(cx, jEnv, arg_signatures[i]);
+ if (arg_signatures)
+ JS_free(cx, arg_signatures);
+
+ /* Free the return type signature */
+ if (method_signature->return_val_signature)
+ jsj_ReleaseJavaClassDescriptor(cx, jEnv, method_signature->return_val_signature);
+}
+
+/*
+ * Fill in the members of a JavaMethodSignature object using the method
+ * argument, which can be either an instance of either java.lang.reflect.Method
+ * or java.lang.reflect.Constructor.
+ *
+ * With normal completion, return the original method_signature argument.
+ * If an error occurs, return NULL and call the error reporter.
+ */
+JavaMethodSignature *
+jsj_InitJavaMethodSignature(JSContext *cx, JNIEnv *jEnv,
+ jobject method,
+ JavaMethodSignature *method_signature)
+{
+ int i;
+ jboolean is_constructor;
+ jclass return_val_class;
+ jsize num_args;
+ JavaSignature *return_val_signature;
+ jarray arg_classes;
+ jmethodID getParameterTypes;
+
+ memset(method_signature, 0, sizeof (JavaMethodSignature));
+
+ is_constructor = (*jEnv)->IsInstanceOf(jEnv, method, jlrConstructor);
+
+ /* Get a Java array that lists the class of each of the method's arguments */
+ if (is_constructor)
+ getParameterTypes = jlrConstructor_getParameterTypes;
+ else
+ getParameterTypes = jlrMethod_getParameterTypes;
+ arg_classes = (*jEnv)->CallObjectMethod(jEnv, method, getParameterTypes);
+ if (!arg_classes) {
+ jsj_UnexpectedJavaError(cx, jEnv,
+ "Can't determine argument signature of method");
+ goto error;
+ }
+
+ /* Compute the number of method arguments */
+ num_args = jsj_GetJavaArrayLength(cx, jEnv, arg_classes);
+ if (num_args < 0)
+ goto error;
+ method_signature->num_args = num_args;
+
+ /* Build a JavaSignature array corresponding to the method's arguments */
+ if (num_args) {
+ JavaSignature **arg_signatures;
+
+ /* Allocate an array of JavaSignatures, one for each method argument */
+ size_t arg_signatures_size = num_args * sizeof(JavaSignature *);
+ arg_signatures = (JavaSignature **)JS_malloc(cx, arg_signatures_size);
+ if (!arg_signatures)
+ goto error;
+ memset(arg_signatures, 0, arg_signatures_size);
+ method_signature->arg_signatures = arg_signatures;
+
+ /* Build an array of JavaSignature objects, each of which corresponds
+ to the type of an individual method argument. */
+ for (i = 0; i < num_args; i++) {
+ JavaSignature *a;
+ jclass arg_class = (*jEnv)->GetObjectArrayElement(jEnv, arg_classes, i);
+
+ a = arg_signatures[i] = jsj_GetJavaClassDescriptor(cx, jEnv, arg_class);
+ if (!a) {
+ jsj_ReportJavaError(cx, jEnv, "Could not determine Java class "
+ "signature using java.lang.reflect");
+ goto error;
+ }
+ }
+ }
+
+ /* Get the Java class of the method's return value */
+ if (is_constructor) {
+ /* Constructors always have a "void" return type */
+ return_val_class = jlVoid_TYPE;
+ } else {
+ return_val_class =
+ (*jEnv)->CallObjectMethod(jEnv, method, jlrMethod_getReturnType);
+ if (!return_val_class) {
+ jsj_UnexpectedJavaError(cx, jEnv,
+ "Can't determine return type of method "
+ "using java.lang.reflect.Method.getReturnType()");
+ goto error;
+ }
+ }
+
+ /* Build a JavaSignature object corresponding to the method's return value */
+ return_val_signature = jsj_GetJavaClassDescriptor(cx, jEnv, return_val_class);
+ if (!return_val_signature)
+ goto error;
+ method_signature->return_val_signature = return_val_signature;
+
+ return method_signature;
+
+error:
+
+ jsj_PurgeJavaMethodSignature(cx, jEnv, method_signature);
+ return NULL;
+}
+
+static JSBool
+add_java_method_to_class_descriptor(JSContext *cx, JNIEnv *jEnv,
+ JavaClassDescriptor *class_descriptor,
+ jstring method_name_jstr,
+ jobject java_method,
+ JSBool is_static_method,
+ JSBool is_constructor)
+{
+ jmethodID methodID;
+ jclass java_class = class_descriptor->java_class;
+
+ JavaMemberDescriptor *member_descriptor = NULL;
+ const char *sig_cstr = NULL;
+ const char *method_name = NULL;
+ JavaMethodSignature *signature = NULL;
+ JavaMethodSpec *method_spec = NULL;
+
+ if (is_constructor) {
+ member_descriptor = jsj_GetJavaClassConstructors(cx, class_descriptor);
+ } else {
+ if (is_static_method) {
+ member_descriptor = jsj_GetJavaStaticMemberDescriptor(cx, jEnv, class_descriptor, method_name_jstr);
+ } else {
+ member_descriptor = jsj_GetJavaMemberDescriptor(cx, jEnv, class_descriptor, method_name_jstr);
+ }
+ }
+ if (!member_descriptor)
+ return JS_FALSE;
+
+ method_spec = (JavaMethodSpec*)JS_malloc(cx, sizeof(JavaMethodSpec));
+ if (!method_spec)
+ goto error;
+ memset(method_spec, 0, sizeof(JavaMethodSpec));
+
+ signature = jsj_InitJavaMethodSignature(cx, jEnv, java_method, &method_spec->signature);
+ if (!signature)
+ goto error;
+
+ method_name = JS_strdup(cx, member_descriptor->name);
+ if (!method_name)
+ goto error;
+ method_spec->name = method_name;
+
+ sig_cstr = jsj_ConvertJavaMethodSignatureToString(cx, signature);
+ if (!sig_cstr)
+ goto error;
+
+ if (is_static_method)
+ methodID = (*jEnv)->GetStaticMethodID(jEnv, java_class, method_name, sig_cstr);
+ else
+ methodID = (*jEnv)->GetMethodID(jEnv, java_class, method_name, sig_cstr);
+ method_spec->methodID = methodID;
+
+ if (!methodID) {
+ jsj_UnexpectedJavaError(cx, jEnv,
+ "Can't get Java method ID for %s.%s() (sig=%s)",
+ class_descriptor->name, method_name, sig_cstr);
+ goto error;
+ }
+
+ JS_free(cx, (char*)sig_cstr);
+
+ /* Add method to list of overloaded methods for this class member */
+ method_spec->next = member_descriptor->methods;
+ member_descriptor->methods = method_spec;
+
+ return JS_TRUE;
+
+error:
+ if (method_spec)
+ JS_FREE_IF(cx, (char*)method_spec->name);
+ JS_FREE_IF(cx, (char*)sig_cstr);
+ if (signature)
+ jsj_PurgeJavaMethodSignature(cx, jEnv, signature);
+ JS_FREE_IF(cx, method_spec);
+ return JS_FALSE;
+}
+
+JSBool
+jsj_ReflectJavaMethods(JSContext *cx, JNIEnv *jEnv,
+ JavaClassDescriptor *class_descriptor,
+ JSBool reflect_only_static_methods)
+{
+ jarray joMethodArray, joConstructorArray;
+ jsize num_methods, num_constructors;
+ int i;
+ jclass java_class;
+ JSBool ok, reflect_only_instance_methods;
+
+ reflect_only_instance_methods = !reflect_only_static_methods;
+
+ /* Get a java array of java.lang.reflect.Method objects, by calling
+ java.lang.Class.getMethods(). */
+ java_class = class_descriptor->java_class;
+ joMethodArray = (*jEnv)->CallObjectMethod(jEnv, java_class, jlClass_getMethods);
+ if (!joMethodArray) {
+ jsj_UnexpectedJavaError(cx, jEnv,
+ "Can't determine Java object's methods "
+ "using java.lang.Class.getMethods()");
+ return JS_FALSE;
+ }
+
+ /* Iterate over the class methods */
+ num_methods = (*jEnv)->GetArrayLength(jEnv, joMethodArray);
+ for (i = 0; i < num_methods; i++) {
+ jstring method_name_jstr;
+
+ /* Get the i'th reflected method */
+ jobject java_method = (*jEnv)->GetObjectArrayElement(jEnv, joMethodArray, i);
+
+ /* Get the method modifiers, eg static, public, private, etc. */
+ jint modifiers = (*jEnv)->CallIntMethod(jEnv, java_method, jlrMethod_getModifiers);
+
+ /* Don't allow access to private or protected Java methods. */
+ if (!(modifiers & ACC_PUBLIC))
+ continue;
+
+ /* Abstract methods can't be invoked */
+ if (modifiers & ACC_ABSTRACT)
+ continue;
+
+ /* Reflect all instance methods or all static methods, but not both */
+ if (reflect_only_static_methods != ((modifiers & ACC_STATIC) != 0))
+ continue;
+
+
+ /* Add a JavaMethodSpec object to the JavaClassDescriptor */
+ method_name_jstr = (*jEnv)->CallObjectMethod(jEnv, java_method, jlrMethod_getName);
+ ok = add_java_method_to_class_descriptor(cx, jEnv, class_descriptor, method_name_jstr, java_method,
+ reflect_only_static_methods, JS_FALSE);
+ if (!ok)
+ return JS_FALSE;
+ }
+
+ if (reflect_only_instance_methods)
+ return JS_TRUE;
+
+ joConstructorArray = (*jEnv)->CallObjectMethod(jEnv, java_class, jlClass_getConstructors);
+ if (!joConstructorArray) {
+ jsj_UnexpectedJavaError(cx, jEnv, "internal error: "
+ "Can't determine Java class's constructors "
+ "using java.lang.Class.getConstructors()");
+ return JS_FALSE;
+ }
+
+ /* Iterate over the class constructors */
+ num_constructors = (*jEnv)->GetArrayLength(jEnv, joConstructorArray);
+ for (i = 0; i < num_constructors; i++) {
+ /* Get the i'th reflected constructor */
+ jobject java_constructor =
+ (*jEnv)->GetObjectArrayElement(jEnv, joConstructorArray, i);
+
+ /* Get the method modifiers, eg public, private, etc. */
+ jint modifiers = (*jEnv)->CallIntMethod(jEnv, java_constructor,
+ jlrConstructor_getModifiers);
+
+ /* Don't allow access to private or protected Java methods. */
+ if (!(modifiers & ACC_PUBLIC))
+ continue;
+
+ /* Add a JavaMethodSpec object to the JavaClassDescriptor */
+ ok = add_java_method_to_class_descriptor(cx, jEnv, class_descriptor, NULL,
+ java_constructor,
+ JS_FALSE, JS_TRUE);
+ if (!ok)
+ return JS_FALSE;
+ }
+ return JS_TRUE;
+}
+
+/*
+ * Free up a JavaMethodSpec and all its resources.
+ */
+void
+jsj_DestroyMethodSpec(JSContext *cx, JNIEnv *jEnv, JavaMethodSpec *method_spec)
+{
+ JS_FREE_IF(cx, (char*)method_spec->name);
+ jsj_PurgeJavaMethodSignature(cx, jEnv, &method_spec->signature);
+ JS_free(cx, method_spec);
+}
+
+static JSBool
+method_signature_matches_JS_args(JSContext *cx, JNIEnv *jEnv, uintN argc, jsval *argv,
+ JavaMethodSignature *method_signature, int *cost)
+{
+ uintN i;
+ JavaSignature *arg_signature;
+
+ if (argc != (uintN)method_signature->num_args)
+ return JS_FALSE;
+
+ for (i = 0; i < argc; i++) {
+ arg_signature = method_signature->arg_signatures[i];
+ if (!jsj_ConvertJSValueToJavaValue(cx, jEnv, argv[i], arg_signature, cost, NULL))
+ return JS_FALSE;
+ }
+ return JS_TRUE;
+}
+
+/*
+ * Return the JavaScript types that a JavaScript method was invoked with
+ * as a string, e.g. a JS method invoked like foo(3, 'hey', 'you', true)
+ * would cause a return value of "(number, string, string, boolean)".
+ * The returned string must be free'ed by the caller.
+ * Returns NULL and reports an error if out-of-memory.
+ */
+static const char *
+get_js_arg_types_as_string(JSContext *cx, uintN argc, jsval *argv)
+{
+ uintN i;
+ const char *arg_type, *arg_string, *tmp;
+
+ if (argc == 0)
+ return strdup("()");
+
+ arg_string = strdup("(");
+ if (!arg_string)
+ goto out_of_memory;
+ for (i = 0; i < argc; i++) {
+ arg_type = JS_GetTypeName(cx, JS_TypeOfValue(cx, argv[i]));
+ tmp = PR_smprintf("%s%s%s%s", arg_string, i ? ", " : "", arg_type,
+ (i == argc-1) ? ")" : "");
+ free((char*)arg_string);
+ if (!tmp)
+ goto out_of_memory;
+ arg_string = tmp;
+ }
+
+ return arg_string;
+
+out_of_memory:
+ JS_ReportOutOfMemory(cx);
+ return NULL;
+}
+
+
+static void
+report_method_match_failure(JSContext *cx,
+ JavaMemberDescriptor *member_descriptor,
+ JavaClassDescriptor *class_descriptor,
+ JSBool is_static_method,
+ uintN argc, jsval *argv)
+{
+ const char *err, *js_arg_string, *tmp, *method_str, *method_name;
+ JSBool is_constructor;
+ JavaMethodSpec *method;
+
+ err = NULL;
+ is_constructor = (!strcmp(member_descriptor->name, ""));
+
+ js_arg_string = get_js_arg_types_as_string(cx, argc, argv);
+ if (!js_arg_string)
+ goto out_of_memory;
+
+ if (is_constructor) {
+ err = PR_smprintf("There is no Java constructor for class %s that matches "
+ "JavaScript argument types %s.\n", class_descriptor->name,
+ js_arg_string);
+ method_name = class_descriptor->name;
+ } else {
+ err = PR_smprintf("There is no %sJava method %s.%s that matches "
+ "JavaScript argument types %s.\n",
+ is_static_method ? "static ": "",
+ class_descriptor->name, member_descriptor->name, js_arg_string);
+ method_name = member_descriptor->name;
+ }
+ if (!err)
+ goto out_of_memory;
+
+ tmp = PR_smprintf("%sCandidate methods with the same name are:\n", err);
+ if (!tmp)
+ goto out_of_memory;
+ err = tmp;
+
+ method = member_descriptor->methods;
+ while (method) {
+ method_str =
+ jsj_ConvertJavaMethodSignatureToHRString(cx, method_name, &method->signature);
+ if (!method_str)
+ goto out_of_memory;
+ tmp = PR_smprintf("%s %s\n", err, method_str);
+ free((char*)method_str);
+ if (!tmp)
+ goto out_of_memory;
+ err = tmp;
+
+ method = method->next;
+ }
+
+ JS_ReportError(cx, err);
+ return;
+
+out_of_memory:
+ if (js_arg_string)
+ free((char*)js_arg_string);
+ if (err)
+ free((char*)err);
+}
+
+static JavaMethodSpec *
+resolve_overloaded_method(JSContext *cx, JNIEnv *jEnv, JavaMemberDescriptor *member_descriptor,
+ JavaClassDescriptor *class_descriptor,
+ JSBool is_static_method,
+ uintN argc, jsval *argv)
+{
+ int cost, lowest_cost, num_method_matches;
+ JavaMethodSpec *best_method_match, *method;
+
+ num_method_matches = 0;
+ lowest_cost = 10000;
+ best_method_match = NULL;
+
+ for (method = member_descriptor->methods; method; method = method->next) {
+ cost = 0;
+ if (!method_signature_matches_JS_args(cx, jEnv, argc, argv, &method->signature, &cost))
+ continue;
+
+ if (cost < lowest_cost) {
+ lowest_cost = cost;
+ best_method_match = method;
+ num_method_matches++;
+ }
+ }
+
+ if (!best_method_match)
+ report_method_match_failure(cx, member_descriptor, class_descriptor,
+ is_static_method, argc, argv);
+
+ /*
+#ifdef LIVECONNECT_IMPROVEMENTS
+ if (num_method_matches > 1)
+ return NULL;
+#else
+ if (lowest_cost != 0)
+ return NULL;
+#endif
+*/
+ return best_method_match;
+}
+
+static jvalue *
+convert_JS_method_args_to_java_argv(JSContext *cx, JNIEnv *jEnv, jsval *argv,
+ JavaMethodSpec *method)
+{
+ jvalue *jargv;
+ JSBool ok;
+ uintN i, argc;
+ JavaSignature **arg_signatures;
+ JavaMethodSignature *signature;
+
+
+ signature = &method->signature;
+ argc = signature->num_args;
+ PR_ASSERT(argc != 0);
+ arg_signatures = signature->arg_signatures;
+
+ jargv = (jvalue *)JS_malloc(cx, sizeof(jvalue) * argc);
+
+ if (!jargv)
+ return NULL;
+
+ for (i = 0; i < argc; i++) {
+ int dummy_cost;
+
+ ok = jsj_ConvertJSValueToJavaValue(cx, jEnv, argv[i], arg_signatures[i],
+ &dummy_cost, &jargv[i]);
+ if (!ok) {
+ JS_ReportError(cx, "Internal error: can't convert JS value to Java value");
+ JS_free(cx, jargv);
+ return NULL;
+ }
+ }
+
+ return jargv;
+}
+
+static JSBool
+invoke_java_method(JSContext *cx, JSJavaThreadState *jsj_env,
+ jobject java_class_or_instance,
+ JavaClassDescriptor *class_descriptor,
+ JavaMethodSpec *method,
+ JSBool is_static_method,
+ jsval *argv, jsval *vp)
+{
+ jvalue java_value;
+ jvalue *jargv;
+ uintN argc;
+ jobject java_object;
+ jclass java_class;
+ jmethodID methodID;
+ JavaMethodSignature *signature;
+ JavaSignature *return_val_signature;
+ JSContext *old_cx;
+ JNIEnv *jEnv;
+
+ methodID = method->methodID;
+ signature = &method->signature;
+ argc = signature->num_args;
+
+ jEnv = jsj_env->jEnv;
+
+ if (is_static_method) {
+ java_object = NULL;
+ java_class = java_class_or_instance;
+ } else {
+ java_object = java_class_or_instance;
+ java_class = NULL;
+ }
+
+ jargv = NULL;
+ if (argc) {
+ jargv = convert_JS_method_args_to_java_argv(cx, jEnv, argv, method);
+ if (!jargv)
+ return JS_FALSE;
+ }
+
+#define CALL_JAVA_METHOD(type, member) \
+ PR_BEGIN_MACRO \
+ if (is_static_method) { \
+ java_value.member = (*jEnv)->CallStatic##type##MethodA(jEnv, java_class, methodID, jargv);\
+ } else { \
+ java_value.member = (*jEnv)->Call##type##MethodA(jEnv, java_object, methodID, jargv);\
+ } \
+ if ((*jEnv)->ExceptionOccurred(jEnv)) { \
+ jsj_ReportJavaError(cx, jEnv, "Error calling method %s.%s()", \
+ class_descriptor->name, method->name); \
+ goto error; \
+ } \
+ PR_END_MACRO
+
+ old_cx = JSJ_SetDefaultJSContextForJavaThread(cx, jsj_env);
+ if (old_cx) {
+ JS_ReportError(cx, "Java thread in simultaneous use by more than "
+ "one JSContext ?");
+ }
+
+ return_val_signature = signature->return_val_signature;
+ switch(return_val_signature->type) {
+ case JAVA_SIGNATURE_BYTE:
+ CALL_JAVA_METHOD(Byte, b);
+ break;
+
+ case JAVA_SIGNATURE_CHAR:
+ CALL_JAVA_METHOD(Char, c);
+ break;
+
+ case JAVA_SIGNATURE_FLOAT:
+ CALL_JAVA_METHOD(Float, f);
+ break;
+
+ case JAVA_SIGNATURE_DOUBLE:
+ CALL_JAVA_METHOD(Double, d);
+ break;
+
+ case JAVA_SIGNATURE_INT:
+ CALL_JAVA_METHOD(Int, i);
+ break;
+
+ case JAVA_SIGNATURE_LONG:
+ CALL_JAVA_METHOD(Long, j);
+ break;
+
+ case JAVA_SIGNATURE_SHORT:
+ CALL_JAVA_METHOD(Short, s);
+ break;
+
+ case JAVA_SIGNATURE_BOOLEAN:
+ CALL_JAVA_METHOD(Boolean, z);
+ break;
+
+ case JAVA_SIGNATURE_ARRAY:
+ case JAVA_SIGNATURE_CLASS:
+ CALL_JAVA_METHOD(Object, l);
+ break;
+
+ case JAVA_SIGNATURE_VOID:
+ if (is_static_method)
+ (*jEnv)->CallStaticVoidMethodA(jEnv, java_class, methodID, jargv);
+ else
+ (*jEnv)->CallVoidMethodA(jEnv, java_object, methodID, jargv);
+ if ((*jEnv)->ExceptionOccurred(jEnv)) {
+ jsj_ReportJavaError(cx, jEnv, "Error calling method %s.%s()",
+ class_descriptor->name, method->name);
+ goto error;
+ }
+ break;
+
+ default:
+ PR_ASSERT(0);
+ return JS_FALSE;
+ }
+
+ if (jargv)
+ JS_free(cx, jargv);
+ JSJ_SetDefaultJSContextForJavaThread(old_cx, jsj_env);
+
+ return jsj_ConvertJavaValueToJSValue(cx, jEnv, return_val_signature, &java_value, vp);
+error:
+ if (jargv)
+ JS_free(cx, jargv);
+ JSJ_SetDefaultJSContextForJavaThread(old_cx, jsj_env);
+ return JS_FALSE;
+}
+
+static JSBool
+invoke_overloaded_java_method(JSContext *cx, JSJavaThreadState *jsj_env,
+ JavaMemberDescriptor *member,
+ JSBool is_static_method,
+ jobject java_class_or_instance,
+ JavaClassDescriptor *class_descriptor,
+ uintN argc, jsval *argv,
+ jsval *vp)
+{
+ JavaMethodSpec *method;
+ JNIEnv *jEnv;
+
+ jEnv = jsj_env->jEnv;
+
+ method = resolve_overloaded_method(cx, jEnv, member, class_descriptor,
+ is_static_method, argc, argv);
+ if (!method)
+ return JS_FALSE;
+
+ return invoke_java_method(cx, jsj_env, java_class_or_instance, class_descriptor,
+ method, is_static_method, argv, vp);
+}
+
+PR_CALLBACK JSBool
+jsj_JavaStaticMethodWrapper(JSContext *cx, JSObject *obj,
+ uintN argc, jsval *argv, jsval *vp)
+{
+ JSFunction *function;
+ JavaMemberDescriptor *member_descriptor;
+ JavaClassDescriptor *class_descriptor;
+ jsid id;
+ jsval idval;
+ JNIEnv *jEnv;
+ JSJavaThreadState *jsj_env;
+ jobject java_class;
+
+ /* Get the Java per-thread environment pointer for this JSContext */
+ jsj_env = jsj_MapJSContextToJSJThread(cx, &jEnv);
+ if (!jEnv)
+ return JS_FALSE;
+
+ class_descriptor = JS_GetPrivate(cx, obj);
+ if (!class_descriptor)
+ return JS_FALSE;
+ java_class = class_descriptor->java_class;
+
+ PR_ASSERT(JS_TypeOfValue(cx, argv[-2]) == JSTYPE_FUNCTION);
+ function = JS_GetPrivate(cx, JSVAL_TO_OBJECT(argv[-2]));
+ idval = STRING_TO_JSVAL(JS_InternString(cx, JS_GetFunctionName(function)));
+ JS_ValueToId(cx, idval, &id);
+
+ member_descriptor = jsj_LookupJavaStaticMemberDescriptorById(cx, jEnv, class_descriptor, id);
+ if (!member_descriptor) {
+ PR_ASSERT(0);
+ return JS_FALSE;
+ }
+
+ return invoke_overloaded_java_method(cx, jsj_env, member_descriptor, JS_TRUE,
+ java_class, class_descriptor, argc, argv, vp);
+}
+
+PR_CALLBACK JSBool
+jsj_JavaInstanceMethodWrapper(JSContext *cx, JSObject *obj,
+ uintN argc, jsval *argv, jsval *vp)
+{
+ JSFunction *function;
+ JavaMemberDescriptor *member_descriptor;
+ JavaObjectWrapper *java_wrapper;
+ JavaClassDescriptor *class_descriptor;
+ jsint id;
+ jsval idval;
+ JSJavaThreadState *jsj_env;
+ JNIEnv *jEnv;
+ jobject java_obj, java_class;
+
+ /* Get the Java per-thread environment pointer for this JSContext */
+ jsj_env = jsj_MapJSContextToJSJThread(cx, &jEnv);
+ if (!jEnv)
+ return JS_FALSE;
+
+ java_wrapper = JS_GetPrivate(cx, obj);
+ if (!java_wrapper)
+ return JS_FALSE;
+ java_obj = java_wrapper->java_obj;
+
+ PR_ASSERT(JS_TypeOfValue(cx, argv[-2]) == JSTYPE_FUNCTION);
+ function = JS_GetPrivate(cx, JSVAL_TO_OBJECT(argv[-2]));
+ idval = STRING_TO_JSVAL(JS_InternString(cx, JS_GetFunctionName(function)));
+ JS_ValueToId(cx, idval, &id);
+ class_descriptor = java_wrapper->class_descriptor;
+ member_descriptor = jsj_LookupJavaMemberDescriptorById(cx, jEnv, class_descriptor, id);
+
+ /* If no instance method was found, try for a static method */
+ if (!member_descriptor) {
+ member_descriptor = jsj_LookupJavaStaticMemberDescriptorById(cx, jEnv, class_descriptor, id);
+ if (!member_descriptor) {
+ PR_ASSERT(0);
+ return JS_FALSE;
+ }
+ java_class = class_descriptor->java_class;
+ return invoke_overloaded_java_method(cx, jsj_env, member_descriptor, JS_TRUE,
+ java_class, class_descriptor, argc, argv, vp);
+ }
+
+ return invoke_overloaded_java_method(cx, jsj_env, member_descriptor,
+ JS_FALSE, java_obj,
+ class_descriptor, argc, argv, vp);
+}
+
+static JSBool
+invoke_java_constructor(JSContext *cx,
+ JSJavaThreadState *jsj_env,
+ jclass java_class,
+ JavaMethodSpec *method,
+ jsval *argv, jsval *vp)
+{
+ jvalue *jargv;
+ uintN argc;
+ jobject java_object;
+ jmethodID methodID;
+ JavaMethodSignature *signature;
+ JSContext *old_cx;
+ JNIEnv *jEnv;
+
+ methodID = method->methodID;
+ signature = &method->signature;
+ argc = signature->num_args;
+
+ jEnv = jsj_env->jEnv;
+
+ jargv = NULL;
+ if (argc) {
+ jargv = convert_JS_method_args_to_java_argv(cx, jEnv, argv, method);
+ if (!jargv)
+ return JS_FALSE;
+ }
+
+ old_cx = JSJ_SetDefaultJSContextForJavaThread(cx, jsj_env);
+ if (old_cx) {
+ JS_ReportError(cx, "Java thread in simultaneous use by more than "
+ "one JSContext ?");
+ }
+
+ java_object = (*jEnv)->NewObjectA(jEnv, java_class, methodID, jargv);
+
+ JSJ_SetDefaultJSContextForJavaThread(old_cx, jsj_env);
+
+ if (!java_object) {
+ jsj_ReportJavaError(cx, jEnv, "Error while constructing instance of %s",
+ jsj_GetJavaClassName(cx, jEnv, java_class));
+ goto error;
+ }
+
+ if (jargv)
+ JS_free(cx, jargv);
+
+ return jsj_ConvertJavaObjectToJSValue(cx, jEnv, java_object, vp);
+
+error:
+ if (jargv)
+ JS_free(cx, jargv);
+ return JS_FALSE;
+}
+
+static JSBool
+invoke_overloaded_java_constructor(JSContext *cx,
+ JSJavaThreadState *jsj_env,
+ JavaMemberDescriptor *member,
+ JSObject *obj, uintN argc, jsval *argv,
+ jsval *vp)
+{
+ jclass java_class;
+ JavaMethodSpec *method;
+ JavaClassDescriptor *class_descriptor;
+ JNIEnv *jEnv;
+
+ jEnv = jsj_env->jEnv;
+
+ class_descriptor = JS_GetPrivate(cx, obj);
+ PR_ASSERT(class_descriptor);
+ if (!class_descriptor)
+ return JS_FALSE;
+
+
+ method = resolve_overloaded_method(cx, jEnv, member, class_descriptor, JS_TRUE,
+ argc, argv);
+ if (!method)
+ return JS_FALSE;
+
+ java_class = class_descriptor->java_class;
+ return invoke_java_constructor(cx, jsj_env, java_class, method, argv, vp);
+}
+
+
+PR_CALLBACK JSBool
+jsj_JavaConstructorWrapper(JSContext *cx, JSObject *obj,
+ uintN argc, jsval *argv, jsval *vp)
+{
+ jint modifiers;
+ JavaMemberDescriptor *member_descriptor;
+ JavaClassDescriptor *class_descriptor;
+ JSJavaThreadState *jsj_env;
+ JNIEnv *jEnv;
+
+ /* Get the Java per-thread environment pointer for this JSContext */
+ jsj_env = jsj_MapJSContextToJSJThread(cx, &jEnv);
+ if (!jEnv)
+ return JS_FALSE;
+
+ obj = JSVAL_TO_OBJECT(argv[-2]);
+ class_descriptor = JS_GetPrivate(cx, obj);
+ if (!class_descriptor)
+ return JS_FALSE;
+
+ /* Get class/interface flags and check them */
+ modifiers = class_descriptor->modifiers;
+ if (modifiers & ACC_ABSTRACT) {
+ JS_ReportError(cx, "Java class %s is abstract and therefore may not "
+ "be instantiated", class_descriptor->name);
+ return JS_FALSE;
+ }
+ if (modifiers & ACC_INTERFACE) {
+ JS_ReportError(cx, "%s is a Java interface and therefore may not "
+ "be instantiated", class_descriptor->name);
+ return JS_FALSE;
+ }
+ if ( !(modifiers & ACC_PUBLIC) ) {
+ JS_ReportError(cx, "Java class %s is not public and therefore may not "
+ "be instantiated", class_descriptor->name);
+ return JS_FALSE;
+ }
+
+ member_descriptor = jsj_LookupJavaClassConstructors(cx, jEnv, class_descriptor);
+ if (!member_descriptor) {
+ JS_ReportError(cx, "No public constructors defined for Java class %s",
+ class_descriptor->name);
+ return JS_FALSE;
+ }
+
+ return invoke_overloaded_java_constructor(cx, jsj_env, member_descriptor, obj, argc, argv, vp);
+}
+
diff --git a/mozilla/js/ref/liveconnect/jsj_private.h b/mozilla/js/ref/liveconnect/jsj_private.h
new file mode 100644
index 00000000000..6856d56b005
--- /dev/null
+++ b/mozilla/js/ref/liveconnect/jsj_private.h
@@ -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 */
diff --git a/mozilla/js/ref/liveconnect/jsj_utils.c b/mozilla/js/ref/liveconnect/jsj_utils.c
new file mode 100644
index 00000000000..f6140b77279
--- /dev/null
+++ b/mozilla/js/ref/liveconnect/jsj_utils.c
@@ -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
+#include
+
+#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;
+}
diff --git a/mozilla/js/ref/liveconnect/jsjava.h b/mozilla/js/ref/liveconnect/jsjava.h
new file mode 100644
index 00000000000..2561e18c6aa
--- /dev/null
+++ b/mozilla/js/ref/liveconnect/jsjava.h
@@ -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 */
\ No newline at end of file
diff --git a/mozilla/js/ref/liveconnect/netscape_javascript_JSObject.h b/mozilla/js/ref/liveconnect/netscape_javascript_JSObject.h
new file mode 100644
index 00000000000..f710bbefcbe
--- /dev/null
+++ b/mozilla/js/ref/liveconnect/netscape_javascript_JSObject.h
@@ -0,0 +1,101 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include
+/* 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