From 28549242ec6a6c525c0e8669eee0d32f4b2cb1d5 Mon Sep 17 00:00:00 2001 From: fur Date: Sat, 20 Jun 1998 17:46:42 +0000 Subject: [PATCH] [Not in the mozilla build] This new vendor-neutral version of LiveConnect is designed to replace the older one in the js/jsj directory, which only works with the Netscape JVM. It is part of the OJI initiative. git-svn-id: svn://10.0.0.236/trunk@4202 18797224-902f-48f8-a5cc-f745e15eee43 --- mozilla/js/ref/liveconnect/LiveConnect.dsp | 140 +++ .../js/ref/liveconnect/LiveConnectShell.dsp | 106 ++ .../js/ref/liveconnect/LiveConnectShell.dsw | 44 + mozilla/js/ref/liveconnect/README.html | 288 +++++ mozilla/js/ref/liveconnect/jsj.c | 737 +++++++++++ mozilla/js/ref/liveconnect/jsj_JSObject.c | 1089 ++++++++++++++++ mozilla/js/ref/liveconnect/jsj_JavaArray.c | 447 +++++++ mozilla/js/ref/liveconnect/jsj_JavaClass.c | 604 +++++++++ mozilla/js/ref/liveconnect/jsj_JavaObject.c | 752 +++++++++++ mozilla/js/ref/liveconnect/jsj_JavaPackage.c | 438 +++++++ mozilla/js/ref/liveconnect/jsj_array.c | 180 +++ mozilla/js/ref/liveconnect/jsj_class.c | 572 +++++++++ mozilla/js/ref/liveconnect/jsj_convert.c | 658 ++++++++++ mozilla/js/ref/liveconnect/jsj_field.c | 383 ++++++ mozilla/js/ref/liveconnect/jsj_method.c | 1105 +++++++++++++++++ mozilla/js/ref/liveconnect/jsj_private.h | 508 ++++++++ mozilla/js/ref/liveconnect/jsj_utils.c | 343 +++++ mozilla/js/ref/liveconnect/jsjava.h | 234 ++++ .../netscape_javascript_JSObject.h | 101 ++ 19 files changed, 8729 insertions(+) create mode 100644 mozilla/js/ref/liveconnect/LiveConnect.dsp create mode 100644 mozilla/js/ref/liveconnect/LiveConnectShell.dsp create mode 100644 mozilla/js/ref/liveconnect/LiveConnectShell.dsw create mode 100644 mozilla/js/ref/liveconnect/README.html create mode 100644 mozilla/js/ref/liveconnect/jsj.c create mode 100644 mozilla/js/ref/liveconnect/jsj_JSObject.c create mode 100644 mozilla/js/ref/liveconnect/jsj_JavaArray.c create mode 100644 mozilla/js/ref/liveconnect/jsj_JavaClass.c create mode 100644 mozilla/js/ref/liveconnect/jsj_JavaObject.c create mode 100644 mozilla/js/ref/liveconnect/jsj_JavaPackage.c create mode 100644 mozilla/js/ref/liveconnect/jsj_array.c create mode 100644 mozilla/js/ref/liveconnect/jsj_class.c create mode 100644 mozilla/js/ref/liveconnect/jsj_convert.c create mode 100644 mozilla/js/ref/liveconnect/jsj_field.c create mode 100644 mozilla/js/ref/liveconnect/jsj_method.c create mode 100644 mozilla/js/ref/liveconnect/jsj_private.h create mode 100644 mozilla/js/ref/liveconnect/jsj_utils.c create mode 100644 mozilla/js/ref/liveconnect/jsjava.h create mode 100644 mozilla/js/ref/liveconnect/netscape_javascript_JSObject.h 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. +

+ +

+Naming and coding conventions:

+ + + +

+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.hLiveConnect's only public header file.  Defines all public API +entry points, callbacks and types. 
jsj_private.hLiveConnect internal header file for intra-module sharing of functions +and types
jsj.cPublic API entry points and initialization code
jsj_array.cRead and write elements of a Java array using the JNI.
jsj_class.cConstruct 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.cConvert between Java and JavaScript values of all types, including +the wrapping of JS objects as Java objects and vice-versa.
jsj_field.cReflect Java fields as properties of JavaObject objects and implement +getter/setter access to those fields.
jsj_JavaArray.cImplementation of the JavaScript JavaArray class.  Instances of +JavaArray are used to reflect Java arrays.
jsj_JavaClass.cImplementation of the JavaScript JavaClass class.   Instances +of JavaClass are used to reflect Java classes.
jsj_JavaObject.cImplementation 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.cImplementation of the JavaScript JavaPackage class.   Instances +of JavaPackage are used to reflect Java packages.
jsj_JSObject.cImplementation 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.cReflect 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.cLow-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