Merge changes from SpiderMonkey140_BRANCH between

JS_STABLE_DROP_04261999 and
 JS_STABLE_DROP_06221999
This commit is contained in:
fur%netscape.com 1999-06-24 05:22:41 +00:00
parent e18b5e37c8
commit 05603647c8
15 changed files with 783 additions and 519 deletions

View File

@ -23,132 +23,133 @@ Table of Contents</h2>
<h2>
<a NAME="Introduction"></a>Introduction</h2>
LiveConnect allows JavaScript and Java virtual machines to interoperate.&nbsp;
Specifically, it enables JavaScript to access Java fields, invoke Java
methods and makes it possible for Java to access JavaScript object properties
and evaluate arbitrary JavaScript.&nbsp; More information on LiveConnect
can be found by <a href="http://developer.netscape.com/find/find.cgi?scope=LiveConnect&browse-category=&ui=sr&chunk-size=&page=1&taxonomy=DevEdge+Online">searching
the index on Netscape's DevEdge site</a>.&nbsp; This README assumes basic
familiarity with <a href="http://cvs-mirror.mozilla.org/webtools/lxr/source/js/src/README.html">JSRef</a>,
the reference implementation of JavaScript, and with the LiveConnect technology.
<p>JSRef project/makefiles build a library or DLL containing the JavaScript
runtime (compiler, interpreter, decompiler, garbage collector, atom manager,
standard classes).&nbsp; The LiveConnect project/makefiles build a library
that links both with JSRef and with any Java Virtual Machine (JVM) that
implements the Java Native Interface (JNI), as specified by JavaSoft.&nbsp;
It then compiles a small "shell" example program and links that with the
library to make an interpreter that can be used interactively and with
test scripts.&nbsp; See the <a href="#sample_shell_interaction">sample
shell interactions</a>.
<span CLASS=LXRLONGDESC> <span CLASS=LXRSHORTDESC>LiveConnect is a library that
permits JavaScript and Java virtual machines to interoperate.</span> Specifically,
it enables JavaScript to access Java fields, invoke Java methods and enables Java
to access JavaScript object properties and evaluate arbitrary JavaScript.</span>
LiveConnect was originally an integrated feature of both the Netscape Navigator
browser and Netscape's server-side JavaScript. Now, it is a standalone library
that can be embedded within other projects, such as the Mozilla browser. More
information on LiveConnect can be found by <a href="http://developer.netscape.com/find/find.cgi?scope=LiveConnect&browse-category=&ui=sr&chunk-size=&page=1&taxonomy=DevEdge+Online">searching
the index on Netscape's DevEdge site</a>.&nbsp; This README assumes basic familiarity
with <a href="http://cvs-mirror.mozilla.org/webtools/lxr/source/js/src/README.html">JSRef</a>,
the reference implementation of JavaScript, and with the LiveConnect technology.
<p>The JSRef project/makefiles (located in another directory) build a library
or DLL containing the JavaScript runtime (compiler, interpreter, decompiler,
garbage collector, atom manager, standard classes).&nbsp; The LiveConnect project/makefiles
build a library that links with both JSRef and any Java Virtual Machine (JVM)
that implements the Java Native Interface (JNI), as specified by JavaSoft.&nbsp;
It then compiles a small "shell" example program and links that with the library
to make an interpreter that can be used interactively and with test scripts.&nbsp;
See the <a href="#sample_shell_interaction">sample shell interactions</a>.
<p><i>Scott Furman, 10/31/98</i>
<h2>
<a NAME="New_Features"></a>New features</h2>
The following features were not available in the versions of LiveConnect
that were integrated with Netscape Navigator versions 4.x and earlier.&nbsp;
For information on LiveConnect version 1, which was used in Navigator 3
and 4 and Enterprise Server 3, see <a href="http://developer.netscape.com/find/find.cgi?scope=LiveConnect&browse-category=&ui=sr&chunk-size=&page=1&taxonomy=DevEdge+Online">Netscape's
DevEdge site</a> or any number of 3rd-party publications.)
The following features were not available in the versions of LiveConnect that
were integrated with Netscape Navigator versions 4.x and earlier.&nbsp; For information
on LiveConnect version 1, which was used in Navigator versions 3 and 4, and Enterprise
Server 3, see <a href="http://developer.netscape.com/find/find.cgi?scope=LiveConnect&browse-category=&ui=sr&chunk-size=&page=1&taxonomy=DevEdge+Online">Netscape's
DevEdge site</a> or any number of 3rd-party publications.)
<h4>
LiveConnect version 3 (10/31/98)</h4>
<ul>
<li>
In previous versions of LiveConnect, when more than one overloaded Java
method was compatible with the types of arguments in an invocation from
JS, the choice of Java method was made arbitrarily, by using the first
one enumerated by the Java reflection APIs.&nbsp; Unfortunately, the ordering
of methods when enumerating is not governed by any specification, so differences
between JVM vendors could lead to inconsistencies in LiveConnect behavior.&nbsp;
Now, a <a href="http://www.mozilla.org/js/liveconnect/lc3_method_overloading.html">JVM-independent
set of rules</a> is used to choose among a set of overloaded methods.&nbsp;&nbsp;
Informally, the method with Java parameter types that most&nbsp; closely
match the JavaScript types is chosen.</li>
<li>
The weak correspondence between the JS language typing system and Java's
may result in ambiguity and/or shadowing when resolving among overloaded
Java methods, even when using LC3's improved method overload resolution
algorithm.&nbsp; For example, JS's number type can map to any of Java's
floating-point or integral types: byte, char, short, int, long, float,
double.&nbsp;&nbsp;
If necessary, it is possible to bypass the method overload resolution process
and explicitly specify the method to be invoked:</li>
<br>&nbsp;
<ul><tt>myPrintMethod = java.io.PrintStream["print(double)"];</tt>
<br><tt>myPrintMethod(13);</tt></ul>
<br>
<li>
Static methods can now be invoked using either the class name or a reference
to an instance of the class.&nbsp; (Older versions of LiveConnect allow
only the former.)</li>
<li>
It is no longer necessary to convert Java Strings to JS strings before
using them as the receivers of JS string methods, which is typically done
by appending an empty string to the Java string, e.g.</li>
<p><br><tt>&nbsp;&nbsp;&nbsp; s = new java.lang.String("foo")</tt>
<br><tt>&nbsp;&nbsp;&nbsp; s = s + "";&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
</tt>// s is now a JS string
<br><tt>&nbsp;&nbsp;&nbsp; m = s.match(/o?/)</tt>
<p>The explicit conversion to a JS string is no longer required because
<i>java.lang.String</i>
objects are treated as a special case that "inherit" all the methods of
JS strings, i.e. so that the second statement in the example above is now
superfluous.
<li>
Similarly, JavaArray objects "inherit" the methods of JS's <tt>Array.prototype</tt>,
so it is possible to appy many of the JS array utility methods such as
<tt>reverse()</tt>
and <tt>join()</tt> to JavaArray objects.</li>
<li>
There is now support for the <tt>instanceof</tt> and <tt>in</tt> operators.&nbsp;
These operators are currently proposed for inclusion in the ECMA-2 standard.</li>
<li>
LiveConnect has been extended to take advantage of JavaScript exceptions,
a language feature that debuted in JavaScript 1.4.&nbsp; Now, when JavaScript
calls into Java, any Java exceptions are converted to JS exceptions which
can be caught using JS try-catch statements.&nbsp; Similarly, JS exceptions
are propagated to Java wrapped in an instance of <i>netscape.javascript.JSException</i>.</li>
<li> In previous versions of LiveConnect, when more than one overloaded Java
method was compatible with the types of arguments in an invocation from JS,
the choice of Java method was made arbitrarily, by using the first one enumerated
by the Java reflection APIs.&nbsp; Unfortunately, the ordering of methods
when enumerating is not governed by any specification, so differences between
JVM vendors could lead to inconsistencies in LiveConnect behavior.&nbsp; Now,
a <a href="http://www.mozilla.org/js/liveconnect/lc3_method_overloading.html">JVM-independent
set of rules</a> is used to choose among a set of overloaded methods.&nbsp;&nbsp;
Informally, the method with Java parameter types that most&nbsp; closely match
the JavaScript types is chosen.<BR>
<BR>
</li>
<li> The weak correspondence between the JS language typing system and Java's
may result in ambiguity and/or shadowing when resolving among overloaded Java
methods, even when using LC3's improved method overload resolution algorithm
(see above).&nbsp; For example, JS's number type can map to a Java method
argument that has any floating-point or integral types: byte, char, short,
int, long, float, double.&nbsp;&nbsp; If necessary, it is now possible to
bypass the method overload resolution process and explicitly specify the method
to be invoked:</li>
<BR>
<br>
&nbsp;
<ul>
<tt>myPrintMethod = java.io.PrintStream["print(double)"];</tt> <br>
<tt>myPrintMethod(13);</tt>
</ul>
<br>
<li> Static methods can now be invoked using either the class name or a reference
to an instance of the class.&nbsp; (Older versions of LiveConnect allow only
the former.)<BR>
<BR>
</li>
<li> It is no longer necessary to convert Java Strings to JS strings before
using them as the receivers of JS string methods, which is typically done
by appending an empty string to the Java string, e.g.
<BLOCKQUOTE>
<P> <tt>&nbsp;&nbsp;&nbsp; s = new java.lang.String("foo")</tt> // s contains
a Java string<br>
<tt>&nbsp;&nbsp;&nbsp; s = s + "";&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
</tt>// s is now a JS string <br>
<tt>&nbsp;&nbsp;&nbsp; m = s.match(/o?/)</tt> </P>
</BLOCKQUOTE>
</li>
<p>The explicit conversion to a JS string is no longer required because <i>java.lang.String</i>
objects are treated as a special case that "inherit" all the methods of JS
strings, i.e. so that the second statement in the example above is now superfluous.<BR>
<BR>
<li> Similarly, JavaArray objects "inherit" the methods of JS's <tt>Array.prototype</tt>,
so it is possible to apply many, though not all, of the JS array utility methods
such as <tt>reverse()</tt> and <tt>join()</tt> to JavaArray objects.<BR>
<BR>
</li>
<li> There is now support for the <tt>instanceof</tt> and <tt>in</tt> operators.&nbsp;
These operators are currently proposed for inclusion in the ECMA-2 standard.<BR>
<BR>
</li>
<li> LiveConnect has been extended to take advantage of JavaScript exceptions,
a language feature that debuted in JavaScript 1.4.&nbsp; Now, when JavaScript
calls into Java, any Java exceptions are converted to JS exceptions which
can be caught using JS try-catch statements.&nbsp; Similarly, JS exceptions
are propagated to Java wrapped in an instance of <i>netscape.javascript.JSException</i>.</li>
</ul>
<h4>
LiveConnect version 2 (7/31/98)</h4>
<blockquote>
<li>
The Java methods of <i>java.lang.Object</i> are now invokeable methods
of <tt><font size=+1>JavaArray</font></tt> objects, matching the behavior
of arrays when accessed from Java<i>.</i>&nbsp; (Java arrays are a subclass
of <i>java.lang.Object</i>.) For example, Java's <tt><font size=+1>getClass()</font></tt>
and <tt><font size=+1>hashCode()</font></tt> methods can now be called
on <tt><font size=+1>JavaArray</font></tt> objects.&nbsp; (In prior versions
of LiveConnect, the methods of <i>java.lang.Object</i> were only inherited
by non-array Java objects.)</li>
<li> The Java methods of <i>java.lang.Object</i> are now invokeable methods
of <tt><font size=+1>JavaArray</font></tt> objects, matching the behavior
of arrays when accessed from Java<i>.</i>&nbsp; (Java arrays are a subclass
of <i>java.lang.Object</i>.) For example, Java's <tt>getClass()</tt> and <tt>hashCode()</tt>
methods can now be called on <tt>JavaArray</tt> objects.&nbsp; (In prior versions
of LiveConnect, the methods of <i>java.lang.Object</i> were only inherited
by non-array Java objects.)</li>
<p>Note that this change has caused the string representation of JavaArray
objects to change.&nbsp; Previously, the JavaArray toString() method always
printed "<tt><font size=+1>[object JavaArray]"</font></tt> for all <tt><font size=+1>JavaArray</font></tt>'s.&nbsp;
Now, the Java <tt><font size=+1>java.lang.Object.toString()</font></tt>
method is called to convert JavaArray objects to strings, just as with
other, non-array Java objects that are accessible via LiveConnect. <tt><font size=+1>java.lang.Object.toString()</font></tt>is
defined in the <i>Java Language Specification</i> to return the value of
the following expression:
<p><tt><font size=-1>getClass().getName() + '@' + Integer.toHexString(hashCode())</font></tt>
<br>&nbsp;
<li>
A one-character string is now an acceptable match for an argument to a
Java method of type <tt><font size=+1>char</font></tt>.&nbsp; (In earlier
versions of LiveConnect, the only acceptable match for a <tt><font size=+1>char</font></tt>
had to be a JavaScript value that was convertible to a number.)&nbsp; For
example, the following is now possible:</li>
<p>Note that this change has caused the string representation of JavaArray objects
to change.&nbsp; Previously, the JavaArray toString() method always printed
"<tt><font size=+1>[object JavaArray]"</font></tt> for all <tt>JavaArray</tt>'s.&nbsp;
Now, the Java <tt>java.lang.Object.toString()</tt> method is called to convert
JavaArray objects to strings, just as with other, non-array Java objects that
are accessible via LiveConnect. <tt>java.lang.Object.toString()</tt>is defined
in the <i>Java Language Specification</i> to return the value of the following
expression:
<p><tt><font size=-1>getClass().getName() + '@' + Integer.toHexString(hashCode())</font></tt><BR>
<br>
&nbsp;
<li> A one-character string is now an acceptable match for an argument to a
Java method of type <tt>char</tt>.&nbsp; (In earlier versions of LiveConnect,
the only acceptable match for a <tt>char</tt> had to be a JavaScript value
that was convertible to a number.)&nbsp; For example, the following is now
possible:</li>
<p><tt><font size=-1>c = new java.lang.Character("F")</font></tt>
<br>&nbsp;
<p><tt><font size=-1>c = new java.lang.Character("F")</font></tt><BR>
<br>
&nbsp;
<li>
A JavaClass object is now an acceptable match for an argument to a Java
method of type <i>java.lang.Class</i>.&nbsp; For example, you can now write:</li>
@ -156,154 +157,152 @@ method of type <i>java.lang.Class</i>.&nbsp; For example, you can now write:</li
<p><tt><font size=-1>java.lang.reflect.Array.newInstance(java.lang.String,
3)</font></tt>
<p>instead of the more verbose:
<p><tt><font size=-1>jls = java.lang.Class.forName("java.lang.String")</font></tt>
<br><tt><font size=-1>java.lang.reflect.Array.newInstance(jls, 3)</font></tt>
<br>&nbsp;</blockquote>
<p><tt><font size=-1>jls = java.lang.Class.forName("java.lang.String")</font></tt>
<br>
<tt><font size=-1>java.lang.reflect.Array.newInstance(jls, 3)</font></tt>
<p><br>
&nbsp;
</blockquote>
<h2>
<a NAME="Compatibility"></a>Compatibility</h2>
Unlike this standalone/component release, all previous versions of LiveConnect
appeared only as an integrated feature of Netscape Navigator.&nbsp; The
variants of LiveConnect that appeared in Navigator versions 3.x and 4.x
all behave much the same, modulo bugs.&nbsp; For brevity we refer to this
classic version of LiveConnect as "LC1" (LiveConnect version 1) and this
most recent release as "LC3".&nbsp; (There was an intermediate LiveConnect
release known as "LC2" in 7/98, but it was not used in any products.&nbsp;
"LC3" provides a superset of LC2 features.)
Unlike this standalone/component release, all previous versions of LiveConnect
appeared only as an integrated feature of Netscape Navigator or the Enterprise
Server.&nbsp; The variants of LiveConnect that appeared in Navigator versions
3.x and 4.x all behave much the same, modulo bugs.&nbsp; For brevity we refer
to this classic version of LiveConnect as "LC1" (LiveConnect version 1) and this
most recent release as "LC3".&nbsp; With a few exceptions LC3 provides a superset
of LC1 features. (There was an intermediate LiveConnect release known as "LC2"
in 7/98, but it was not used in any products.)
<ul>
<li>
As in LC1, JavaScript objects appear to Java as instances of <i>netscape.javascript.JSObject</i>.&nbsp;
In LC1, two <i>JSObject</i>'s could be tested for equality, i.e. to see
if they refer to the same instance, by using the `==' operator.&nbsp; Instead,
developers must now use the <tt>equals()</tt>method of <i>netscape.javascript.JSObject</i>
for comparison, a method that overrides <tt>java.lang.Object.equals()</tt>.&nbsp;
Using <tt>equals()</tt> instead of `==' should work the same in all versions
of LiveConnect.</li>
<p>It is not possible to replicate the identity behavior of the `==' operator
that LC1 provides without the use of "weak" references, i.e. references
that do not contribute to making a Java object reachable for purposes of
garbage collection, but which nonetheless allow reference to an object
as long as it is reachable by other means.&nbsp; The use of weak references
is not portable, however.&nbsp; It is not part of the JNI or the JDK and
it is not provided on all JVMs.&nbsp; The JDK1.2 release will include standard
support for weak references.
<br>&nbsp;
<li>
It's possible that, in a set of overloaded Java methods, more than one
method is compatible with the types of the actual arguments in a call from
JavaScript via LiveConnect.&nbsp; LC1 and LC2 resolved these ambiguities
in a simplistic manner, by simply invoking whatever method was enumerated
first by the JVM.&nbsp; The enumeration order of reflected methods using
<i>java.lang.reflect</i>&nbsp;
is not specified by Sun and may differ among vendor's JVMs, i.e. enumeration
could be in order of classfile appearance, hashtable order, etc.&nbsp;
Hence, the Java method chosen when there is more than one compatible method
may vary depending on the JVM.&nbsp; With the Netscape and Sun JVMs, it
is possible to change the behavior of an LC1/LC2 program by changing the
order that Java methods appear in a source file, thus changing the method
enumeration order.</li>
<p>In LC3, a new method overload resolution algorithm is used.&nbsp; Informally,
the method with Java parameter types that most&nbsp; closely match the
JavaScript types is chosen.&nbsp; You can read all the gorey details in
the <a href="http://www.mozilla.org/js/liveconnect/lc3_method_overloading.html">spec</a>.
<br>&nbsp;
<li>
There are several minor changes in error handling to make LiveConnect more
conformant to ECMAScript.&nbsp; These include, for example, making any
attempt to delete JavaObject, JavaClass or JavaPackage properties fail
silently, rather than causing an error.&nbsp; Also, some error messages
have been changed to be more informative.&nbsp; These changes should generally
be backward-compatible with LC1 because few programs that use LiveConnect
will depend on the exact behavior of LiveConnect when handling errors.</li>
<li> As in LC1, JavaScript objects appear to Java as instances of <i>netscape.javascript.JSObject</i>.&nbsp;
In LC1, two <i>JSObject</i>'s could be tested for equality, i.e. to see if
they refer to the same instance, by using the `==' operator.&nbsp; Instead,
developers must now use the <tt>equals()</tt>method of <i>netscape.javascript.JSObject</i>
for comparison, a method that overrides <tt>java.lang.Object.equals()</tt>.&nbsp;
Note that using <tt>equals()</tt> instead of `==' will work the same in all
versions of LiveConnect, including LC3</li>
<p>[It is not possible to replicate the identity behavior of the `==' operator
that LC1 provides without the use of "weak" references, i.e. references that
do not contribute to making a Java object reachable for purposes of garbage
collection, but which nonetheless allow reference to an object as long as
it is reachable by other means.&nbsp; The use of weak references is not portable,
however.&nbsp; It is not part of the JNI or JDK 1.1 and it is not provided
on all JVMs.&nbsp; The JDK1.2 release includes standard support for weak references.]<BR>
<br>
&nbsp;
<li> It's possible that, in a set of overloaded Java methods, more than one
method is compatible with the types of the actual arguments in a call from
JavaScript to Java via LiveConnect.&nbsp; LC1 and LC2 resolved these ambiguities
in a simplistic manner, by simply invoking whatever method was enumerated
first by the JVM.&nbsp; The enumeration order of reflected methods using <i>java.lang.reflect</i>&nbsp;
is not specified by Sun and may differ among vendor's JVMs, i.e. enumeration
could be in order of classfile appearance, hashtable order, etc.&nbsp; Hence,
the Java method chosen when there is more than one compatible method may vary
depending on the JVM.&nbsp; With the Netscape and Sun JVMs, it is possible
to change the behavior of an LC1/LC2 program by changing the order that Java
methods appear in a source file, thus changing the method enumeration order.</li>
<p>In LC3, a new method overload resolution algorithm is used.&nbsp; Informally,
the method with Java parameter types that most&nbsp; closely match the JavaScript
types is chosen.&nbsp; You can read all the gorey details in the <a href="http://www.mozilla.org/js/liveconnect/lc3_method_overloading.html">spec</a>.<BR>
<br>
&nbsp;
<li> There are several minor changes in error handling to make LiveConnect more
conformant to ECMAScript.&nbsp; These include, for example, making any attempt
to delete JavaObject, JavaClass or JavaPackage properties fail silently, rather
than causing an error.&nbsp; Also, some error messages have been changed to
be more informative.&nbsp; These changes should generally be backward-compatible
with LC1 because few programs that use LiveConnect will depend on the exact
behavior of LiveConnect when handling errors.</li>
</ul>
<h2>
<a NAME="Limitations"></a>Limitations/Bugs/To-Do</h2>
<h2> <a NAME="Limitations"></a>Limitations/Bugs/To-Do<BR>
</h2>
<ul>
<li>
There is no way to explicitly resolve overloaded constructors, i.e. in
the same way that can be done with instance and static methods.</li>
<li>
The efficiency of calling Java methods leaves something to be desired,
due to the convoluted nature of implementing native methods for JS.&nbsp;
JS_CloneFunctionObject() is called for every Java method invocation and
the inability to store private data in a JSFunction object requires that
the method table be searched twice instead of once.</li>
<li>
When Java objects are referenced from JS, they are entered into a hash
table, so as to ensure that the same JS Object wrapper is used every time
a particular Java object is reflected into JS.&nbsp; In this way, the behavior of
the JS '==' and '===' operators are preserved.&nbsp; Unfortunately, the
hash table may grow quite large (objects are only removed from the hash
table when finalized).&nbsp; One solution would be to make it possible
to overload JS's equality-test operators, so that the hash table would
no longer be required.</li>
<li>
Initially, JavaClassDescriptors were reference-counted to permit free'ing
of unused descriptors.&nbsp; However, it's relatively common to develop
cycles in the graph of JavaClassDescriptors, which leads to unused JavaClassDescriptors
that are not free'ed until JSJ_Shutdown().&nbsp; Luckily, the amount of
memory used by JavaClassDescriptors tends to be relatively small.</li>
<li>
The LiveConnect API is designed to allow multiple JVMs to be used simultaneously
in the same executable (although each JSContext is limited to interaction
with at most one JVM).&nbsp; However, the API is not fully implemented.&nbsp;
For example, many global variables will need to become members of the JSJavaVM
struct so that they are stored on a per-JVM basis.</li>
<li>
Java and JavaScript use independent garbage collection systems. A reference between the two worlds must, therefore, take the form of a GC root. It's possible to create uncollectable objects when cyclic graphs cross the boundary between JS and Java, e.g. a JS object that refers to a Java object that refers back to the original JS object. There is no simple solution to this dual-GC problem. Luckily, such cyclic object graphs are extremely rare.
</li>
<li> The efficiency of calling Java methods leaves something to be desired,
due to the convoluted nature of implementing native methods for JS.&nbsp;
JS_CloneFunctionObject() is called for every Java method invocation and the
inability to store private data in a JSFunction object requires that the method
table be searched twice instead of once for every invocation.<BR>
<BR>
</li>
<li> When Java objects are referenced from JS, they are entered into a hash
table, so as to ensure that the same JS Object wrapper is used every time
a particular Java object is reflected into JS.&nbsp; In this way, the behavior
of the JS '==' and '===' operators are preserved.&nbsp; Unfortunately, the
hash table may grow quite large (objects are only removed from the hash table
when finalized).&nbsp; In thread-safe systems, the hash table must be locked
when accessed, leading to slow performance. One alternate solution would be
to make it possible to overload JS's equality-test operators, so that the
hash table would no longer be required.<BR>
<BR>
</li>
<li> Initially, JavaClassDescriptor objectswere reference-counted to permit
free'ing of unused descriptors.&nbsp; However, it's relatively common to develop
cycles in the graph of JavaClassDescriptors, which leads to unused JavaClassDescriptors
that have non-zero reference counts. For that reason, JavaClassDescriptors
are not free'ed until JSJ_Shutdown().&nbsp; Luckily, the amount of memory
used by JavaClassDescriptors tends to be relatively small.<BR>
<BR>
</li>
<li> The LiveConnect API is designed to allow multiple JVMs to be used simultaneously
in the same executable (although each JSContext is limited to interaction
with at most one JVM).&nbsp; However, the API is not fully implemented.&nbsp;
For example, many global variables will need to become members of the JSJavaVM
struct so that they are stored on a per-JVM basis.<BR>
<BR>
</li>
<li> Java and JavaScript use independent garbage collection systems. A reference
between the two worlds must, therefore, take the form of a GC root. It's possible
to create uncollectable objects when cyclic graphs cross the boundary between
JS and Java, e.g. a JS object that refers to a Java object that refers back
to the original JS object. There is no simple solution to this dual-GC problem.
Luckily, such cyclic object graphs are extremely rare. </li>
</ul>
<h2>
<a NAME="Build_conventions"></a>Build conventions</h2>
Update your JVM's <tt><font size=+1>CLASSPATH</font></tt> to point to the
<tt>js/src/liveconnect/classes</tt>
subdirectory.&nbsp; If you do not, LiveConnect will still operate but with
the limitation that JS objects may not be passed as arguments of Java methods
and it will not be possible to call from Java into JavaScript, i.e. the
<i>netscape.javascript.JSObject</i> class will be inaccessible.&nbsp; Another
downside of operating without these classes is that Java error messages
will not include a Java stack trace, when one is available.&nbsp; If your
<tt><font size=+1>CLASSPATH</font></tt>
is set improperly, you will see a message like, "<tt>initialization error:
Can't load class netscape/javascript/JSObject</tt>" when starting a LiveConnect
debug build.
<p>By default, all platforms build a version of LiveConnect that is <i>not</i>
threadsafe.&nbsp; If you require thread-safety, you must also populate
the <tt>mozilla/dist</tt> directory with <a href="http://www.mozilla.org/docs/tplist/catCode/nsprdesc.htm">NSPR</a>
headers and libraries.&nbsp; (NSPR implements a portable threading library,
among other things.&nbsp; The source is downloadable via <a href="http://www.mozilla.org/cvs.html">CVS</a>
from <tt><a href="http://cvs-mirror.mozilla.org/webtools/lxr/source/nsprpub">mozilla/nsprpub</a></tt>.)&nbsp;
Next, you must define <tt>JS_THREADSAFE</tt> when building LiveConnect,
either on the command-line (gmake/nmake) or in a universal header file.&nbsp;
Note that JSRef must also be built with <tt><font size=+1>JS_THREADSAFE</font></tt>.
<P>The following directions are for building the standalone version of LiveConnect.
To build the version that's used in the Mozilla browser, see the <A HREF="http://www.mozilla.org/docs/">Mozilla
build documentation</A>.</P>
<P>On all platforms, you must update your JVM's <tt>CLASSPATH</tt> to point to
the <tt>js/src/liveconnect/classes</tt> subdirectory.&nbsp; If you do not, LiveConnect
will still operate but with the limitation that JS objects may not be passed
as arguments of Java methods and it will not be possible to call from Java into
JavaScript, i.e. the <i>netscape.javascript.JSObject</i> class will be inaccessible.&nbsp;
Another downside of operating without these classes is that Java error messages
will not include a Java stack trace, when one is available.&nbsp; If your <tt>CLASSPATH</tt>
is set improperly, you will see a message like, "<tt>initialization error: Can't
load class netscape/javascript/JSObject</tt>" when starting a LiveConnect debug
build. </P>
<p>By default, all platforms build a version of LiveConnect that is <i>not</i>
threadsafe.&nbsp; If you require thread-safety, you must also populate the <tt>mozilla/dist</tt>
directory with <a href="http://www.mozilla.org/docs/tplist/catCode/nsprdesc.htm">NSPR</a>
headers and libraries.&nbsp; (NSPR implements a portable threading library,
among other things.&nbsp; The source is downloadable via <a href="http://www.mozilla.org/cvs.html">CVS</a>
from <tt><a href="http://cvs-mirror.mozilla.org/webtools/lxr/source/nsprpub">mozilla/nsprpub</a></tt>.)&nbsp;
Next, you must define <tt>JS_THREADSAFE</tt> when building LiveConnect, either
on the command-line (gmake/nmake) or in a universal header file.&nbsp; Note
that JSRef must also be built with <tt>JS_THREADSAFE</tt>.
<p>One important note about building on Windows: There are two independent build
systems (in addition to the Mozilla browser build system). One of them uses
the IDE project files and the other uses gmake and makefiles. The former will
be preferred by most for debugging and the latter is more complete, since it
builds the necessary Java classes in addition to compiling the LiveConnect C
code.
<ul><b>Windows</b>
<ul>
<li>
Build the JS runtime and interpreter, <tt>js32.dll</tt>, by using the <a href="http://cvs-mirror.mozilla.org/webtools/lxr/source/js/src/README.html#Build">normal
JSRef build procedure</a>.</li>
JSRef build procedure</a>.</li> <li> Set the <tt>JDK</tt> environment variable to point to the top-level JDK directory,
e.g. <tt>D:\jdk1.1.5</tt>.&nbsp; This is used to establish paths for header
file inclusion, linking and execution.&nbsp; If you are not using Sun's
JVM, the project files may require manual tweaking to set these paths correctly.</li>
<li>
Set the <tt><font size=+1>JDK</font></tt> environment variable to point
to the top-level JDK directory, e.g. <tt>D:\jdk1.1.5</tt>.&nbsp; This is
used to establish paths for header file inclusion, linking and execution.&nbsp;
If you are not using Sun's JVM, the project files may require manual tweaking
to set these paths correctly.</li>
<li>
Use MSDEV5.0 with the <tt>LiveConnectShell.dsw</tt> project file.&nbsp;
<font color="#993300">NOTE: makefile.win is an nmake file used only for
building the JS-engine in the Mozilla browser.&nbsp; Don't attempt to use
it to build the standalone JS-engine.</font></li>
<li> Use MSVC 5 or MSVC 6 with the <tt>LiveConnectShell.dsw</tt> project file.&nbsp;
<font color="#993300">NOTE: makefile.win is an nmake file used only for
building the JS-engine in the Mozilla browser.&nbsp; Don't attempt to use
it to build the standalone JS-engine.</font></li>
<li>
The output files (DLLs and executables) are placed in either the <tt>js\src\liveconnect\Debug</tt>
@ -322,10 +321,8 @@ in the JDK's bin directory, e.g. <tt>D:\jdk1.1.5\bin\javai_g.dll</tt>.</li>
Use any Java compiler to compile the java source files in the <tt>js\src\liveconnect\classes\netscape\javascript</tt>
directory.</li>
<li>
Update your JVM's <tt><font size=+1>CLASSPATH</font></tt> to point to the
<tt>js\src\liveconnect\classes</tt>
subdirectory.&nbsp; (See above)<br>
<li> Update your JVM's <tt>CLASSPATH</tt> to point to the <tt>js\src\liveconnect\classes</tt>
subdirectory.&nbsp; (See above)<br>
<BR></li>
</ul>
<b>Mac OS</b>
@ -355,33 +352,23 @@ Make an alias to <tt>liveconnect.jar</tt> and place it in "<tt>{SystemFolder}Ext
Libraries:MRJClasses</tt>".<br>
<BR></li>
</ul>
<b>Unix</b>
<ul>
<li>
<font color="#000000">Use '<tt>gmake -f Makefile.ref</tt>' to build. To
compile optimized code, pass <tt>BUILD_OPT=1</tt> on the gmake command
line or preset it in the environment or <tt>Makefile.ref</tt>.&nbsp; </font><font color="#990000">NOTE:
Do not attempt to use <tt>Makefile</tt> to build.&nbsp; This file is used
only for building LiveConnect in the Mozilla browser.</font></li>
<li>
<font color="#000000">Each platform on which LiveConnect is built must
have a *.mk configuration file in the <tt>js/src/liveconnect/config</tt>
directory.&nbsp; The configuration file specifies the JVM headers/libraries
used and allows for customization of command-line options.&nbsp; To date,
the build system has been tested on Solaris, AIX, HP/UX, OSF, IRIX, x86
Linux and Windows NT. Most platforms will work with either the vendor compiler
or gcc.</font></li>
<li>
Use any Java compiler to compile the java source files in the <tt>js/src/liveconnect/classes/netscape/javascript</tt>
directory.</li>
<li>
Update your JVM's <tt><font size=+1>CLASSPATH</font></tt> to point to the
<tt>js/src/liveconnect/classes</tt>
subdirectory.&nbsp; (See above)</li>
</ul>
<b>Unix</b> (also works on Windows)
<ul>
<li> <font color="#000000">Use '<tt>gmake -f Makefile.ref</tt>' to build.
To compile optimized code, pass <tt>BUILD_OPT=1</tt> on the gmake command
line or preset it in the environment or <tt>Makefile.ref</tt>.&nbsp; </font><font color="#990000">NOTE:
Do not attempt to use <tt>Makefile</tt> to build.&nbsp; This file is used
only for building LiveConnect in the Mozilla browser.</font></li>
<li> <font color="#000000">Each platform on which LiveConnect is built must
have a *.mk configuration file in the <tt>js/src/liveconnect/config</tt>
directory.&nbsp; The configuration file specifies the JVM headers/libraries
used and allows for customization of command-line options.&nbsp; To date,
the build system has been tested on Solaris, AIX, HP/UX, OSF, IRIX, x86
Linux and Windows NT. Most platforms will work with either the vendor compiler
or gcc.</font></li>
<li>Update your JVM's <tt><font size=+1>CLASSPATH</font></tt> to point to
the <tt>js/src/liveconnect/classes</tt> subdirectory.&nbsp; (See above)</li>
</ul>
</ul>
<h2>

View File

@ -17,9 +17,10 @@
# JDK_DIR should be the directory you put the JDK in, and should have
# the appropriate lib/ and include/ dirs on it.
# If you're not using the `Blackdown' JDK, try changing the following line:
JDK=/usr/lib/jdk-1.1.5
JDK=/share/builds/components/jdk/1.1.7/Linux
export THREADS_FLAG=native
INCLUDES += -I$(JDK)/include -I$(JDK)/include/md \
-I$(JDK)/include/genunix
OTHER_LIBS += -L$(JDK)/lib/i386/green_threads -ljava
OTHER_LIBS += -L$(JDK)/lib/i386/native_threads -ljava

View File

@ -19,3 +19,5 @@ JDK = /share/builds/components/jdk/1.1.6/OSF1
INCLUDES += -I$(JDK)/include/java -I$(JDK)/include/java/alpha
OTHER_LIBS += -L$(JDK)/lib/alpha -ljava
XLDFLAGS += -taso

View File

@ -466,7 +466,7 @@ JSJ_ConnectToJavaVM(SystemJavaVM *java_vm_arg, void* initargs)
thread_list_monitor =
(struct PRMonitor *) PR_NewMonitor();
}
#endif JSJ_THREADSAFE
#endif /* JSJ_THREADSAFE */
/* Put this VM on the list of all created VMs */
jsjava_vm->next = jsjava_vm_list;
@ -580,7 +580,7 @@ JSJ_DisconnectFromJavaVM(JSJavaVM *jsjava_vm)
PR_DestroyMonitor(thread_list_monitor);
thread_list_monitor = NULL;
}
#endif JSJ_THREADSAFE
#endif /* JSJ_THREADSAFE */
free(jsjava_vm);
}
@ -600,14 +600,14 @@ new_jsjava_thread_state(JSJavaVM *jsjava_vm, const char *thread_name, JNIEnv *jE
if (thread_name)
jsj_env->name = strdup(thread_name);
#ifdef JSJ_THREAD_SAFE
#ifdef JSJ_THREADSAFE
PR_EnterMonitor(thread_list_monitor);
#endif
jsj_env->next = thread_list;
thread_list = jsj_env;
#ifdef JSJ_THREAD_SAFE
#ifdef JSJ_THREADSAFE
PR_ExitMonitor(thread_list_monitor);
#endif
@ -620,6 +620,10 @@ find_jsjava_thread(JNIEnv *jEnv)
JSJavaThreadState *e, **p, *jsj_env;
jsj_env = NULL;
#ifdef JSJ_THREADSAFE
PR_EnterMonitor(thread_list_monitor);
#endif
/* Search for the thread state among the list of all created
LiveConnect threads */
for (p = &thread_list; (e = *p) != NULL; p = &(e->next)) {
@ -631,19 +635,15 @@ find_jsjava_thread(JNIEnv *jEnv)
/* Move a found thread to head of list for faster search next time. */
if (jsj_env && p != &thread_list) {
#ifdef JSJ_THREAD_SAFE
PR_EnterMonitor(thread_list_monitor);
#endif
/* First, check to make sure list hasn't mutated since we searched */
if (*p == jsj_env) {
*p = jsj_env->next;
thread_list = jsj_env;
}
#ifdef JSJ_THREAD_SAFE
PR_ExitMonitor(thread_list_monitor);
#endif
*p = jsj_env->next;
jsj_env->next = thread_list;
thread_list = jsj_env;
}
#ifdef JSJ_THREADSAFE
PR_ExitMonitor(thread_list_monitor);
#endif
return jsj_env;
}
@ -744,6 +744,9 @@ JSJ_SetDefaultJSContextForJavaThread(JSContext *cx, JSJavaThreadState *jsj_env)
JSContext *old_context;
old_context = jsj_env->cx;
jsj_env->cx = cx;
/* The following line prevents clearing of jsj_env->cx by jsj_ExitJava() */
jsj_env->recursion_depth++;
return old_context;
}
@ -757,15 +760,22 @@ JSJ_DetachCurrentThreadFromJava(JSJavaThreadState *jsj_env)
/* Disassociate the current native thread from its corresponding Java thread */
java_vm = jsj_env->jsjava_vm->java_vm;
jEnv = jsj_env->jEnv;
if (!JSJ_callbacks->detach_current_thread(java_vm, jEnv))
return JS_FALSE;
/* Destroy the LiveConnect execution environment passed in */
jsj_ClearPendingJSErrors(jsj_env);
#ifdef JSJ_THREADSAFE
PR_EnterMonitor(thread_list_monitor);
#endif JSJ_THREADSAFE
#endif /* JSJ_THREADSAFE */
if (!JSJ_callbacks->detach_current_thread(java_vm, jEnv)) {
#ifdef JSJ_THREADSAFE
PR_ExitMonitor(thread_list_monitor);
#endif /* JSJ_THREADSAFE */
return JS_FALSE;
}
/* Destroy the LiveConnect execution environment passed in */
jsj_ClearPendingJSErrors(jsj_env);
for (p = &thread_list; (e = *p) != NULL; p = &(e->next)) {
if (e == jsj_env) {
@ -774,9 +784,11 @@ JSJ_DetachCurrentThreadFromJava(JSJavaThreadState *jsj_env)
}
}
JS_ASSERT(e);
#ifdef JSJ_THREADSAFE
PR_ExitMonitor(thread_list_monitor);
#endif JSJ_THREADSAFE
#endif /* JSJ_THREADSAFE */
free(jsj_env);
return JS_TRUE;
@ -788,13 +800,17 @@ JSBool
JSJ_ConvertJavaObjectToJSValue(JSContext *cx, jobject java_obj, jsval *vp)
{
JNIEnv *jEnv;
JSBool result;
JSJavaThreadState *jsj_env;
/* Get the Java per-thread environment pointer for this JSContext */
jsj_MapJSContextToJSJThread(cx, &jEnv);
jsj_env = jsj_EnterJava(cx, &jEnv);
if (!jEnv)
return JS_FALSE;
return jsj_ConvertJavaObjectToJSValue(cx, jEnv, java_obj, vp);
result = jsj_ConvertJavaObjectToJSValue(cx, jEnv, java_obj, vp);
jsj_ExitJava(jsj_env);
return result;
}

View File

@ -244,7 +244,7 @@ jsj_WrapJSObject(JSContext *cx, JNIEnv *jEnv, JSObject *js_obj)
if (!handle)
return NULL;
handle->js_obj = js_obj;
handle->cx = cx;
handle->rt = JS_GetRuntime(cx);
/* Create a new Java object that wraps the JavaScript object by storing its
address in a private integer field. */
@ -366,7 +366,7 @@ capture_js_error_reports_for_java(JSContext *cx, const char *message,
}
/* Get the head of the list of pending JS errors */
jsj_env = jsj_MapJSContextToJSJThread(cx, &jEnv);
jsj_env = jsj_EnterJava(cx, &jEnv);
if (!jsj_env)
goto abort;
@ -383,6 +383,7 @@ capture_js_error_reports_for_java(JSContext *cx, const char *message,
/* 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;
jsj_ExitJava(jsj_env);
return;
abort:
@ -693,7 +694,14 @@ jsj_enter_js(JNIEnv *jEnv, void* applet_obj, jobject java_wrapper_obj,
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, applet_obj, jEnv, &err_msg);
#ifdef OJI
cx = JSJ_callbacks->map_jsj_thread_to_js_context(jsj_env,
applet_obj,
jEnv, &err_msg);
#else
cx = JSJ_callbacks->map_jsj_thread_to_js_context(jsj_env,
jEnv, &err_msg);
#endif
if (!cx)
goto error;
} else {
@ -704,7 +712,6 @@ jsj_enter_js(JNIEnv *jEnv, void* applet_obj, jobject java_wrapper_obj,
jsj_env->cx = cx;
}
*cxp = cx;
jsj_env->recursion_depth++;
/*
* Capture all JS error reports so that they can be thrown into the Java
@ -713,6 +720,10 @@ jsj_enter_js(JNIEnv *jEnv, void* applet_obj, jobject java_wrapper_obj,
*old_error_reporterp =
JS_SetErrorReporter(cx, capture_js_error_reports_for_java);
#ifdef JSJ_THREADSAFE
JS_BeginRequest(cx);
#endif
return jsj_env;
error:
@ -740,6 +751,10 @@ jsj_exit_js(JSContext *cx, JSJavaThreadState *jsj_env, JSErrorReporter original_
{
JNIEnv *jEnv;
#ifdef JSJ_THREADSAFE
JS_EndRequest(cx);
#endif
/* Restore the JS error reporter */
JS_SetErrorReporter(cx, original_reporter);
@ -765,9 +780,6 @@ jsj_exit_js(JSContext *cx, JSJavaThreadState *jsj_env, JSErrorReporter original_
if (JSJ_callbacks->exit_js)
JSJ_callbacks->exit_js(jEnv);
jsj_env->recursion_depth--;
if (!jsj_env->recursion_depth)
jsj_env->cx = NULL;
return JS_TRUE;
}
@ -811,7 +823,7 @@ Java_netscape_javascript_JSObject_getMember(JNIEnv *jEnv,
jobject java_wrapper_obj,
jstring property_name_jstr)
{
JSContext *cx;
JSContext *cx = NULL;
JSObject *js_obj;
jsval js_val;
int dummy_cost;
@ -869,7 +881,7 @@ Java_netscape_javascript_JSObject_getSlot(JNIEnv *jEnv,
jobject java_wrapper_obj,
jint slot)
{
JSContext *cx;
JSContext *cx = NULL;
JSObject *js_obj;
jsval js_val;
int dummy_cost;
@ -907,7 +919,7 @@ Java_netscape_javascript_JSObject_setMember(JNIEnv *jEnv,
jstring property_name_jstr,
jobject java_obj)
{
JSContext *cx;
JSContext *cx = NULL;
JSObject *js_obj;
jsval js_val;
const jchar *property_name_ucs2;
@ -957,7 +969,7 @@ Java_netscape_javascript_JSObject_setSlot(JNIEnv *jEnv,
jint slot,
jobject java_obj)
{
JSContext *cx;
JSContext *cx = NULL;
JSObject *js_obj;
jsval js_val;
JSErrorReporter saved_reporter;
@ -985,7 +997,7 @@ Java_netscape_javascript_JSObject_removeMember(JNIEnv *jEnv,
jobject java_wrapper_obj,
jstring property_name_jstr)
{
JSContext *cx;
JSContext *cx = NULL;
JSObject *js_obj;
jsval js_val;
const jchar *property_name_ucs2;
@ -1031,7 +1043,7 @@ Java_netscape_javascript_JSObject_call(JNIEnv *jEnv, jobject java_wrapper_obj,
{
int i, argc, arg_num;
jsval *argv;
JSContext *cx;
JSContext *cx = NULL;
JSObject *js_obj;
jsval js_val, function_val;
int dummy_cost;
@ -1119,7 +1131,7 @@ Java_netscape_javascript_JSObject_eval(JNIEnv *jEnv,
{
const char *codebase;
JSPrincipals *principals;
JSContext *cx;
JSContext *cx = NULL;
JSBool eval_succeeded;
JSObject *js_obj;
jsval js_val;
@ -1188,7 +1200,7 @@ Java_netscape_javascript_JSObject_toString(JNIEnv *jEnv,
jobject java_wrapper_obj)
{
jstring result;
JSContext *cx;
JSContext *cx = NULL;
JSObject *js_obj;
JSString *jsstr;
JSErrorReporter saved_reporter;
@ -1222,7 +1234,7 @@ Java_netscape_javascript_JSObject_getWindow(JNIEnv *jEnv,
jobject java_applet_obj)
{
char *err_msg;
JSContext *cx;
JSContext *cx = NULL;
JSObject *js_obj;
jsval js_val;
int dummy_cost;
@ -1264,19 +1276,17 @@ JNIEXPORT void JNICALL
Java_netscape_javascript_JSObject_finalize(JNIEnv *jEnv, jobject java_wrapper_obj)
{
JSBool success;
JSContext *cx;
JSObjectHandle *handle;
success = JS_FALSE;
handle = (JSObjectHandle *)((*jEnv)->GetIntField(jEnv, java_wrapper_obj, njJSObject_internal));
JS_ASSERT(handle);
if (!handle)
return;
cx = handle->cx;
success = JS_RemoveRoot(cx, &handle->js_obj);
JS_free(cx, handle);
success = JS_RemoveRootRT(handle->rt, &handle->js_obj);
free(handle);
JS_ASSERT(success);
}

View File

@ -94,7 +94,8 @@ access_java_array_element(JSContext *cx,
if (JS_IdToValue(cx, id, &idval) && JSVAL_IS_STRING(idval) &&
(property_name = JS_GetStringBytes(JSVAL_TO_STRING(idval))) != NULL) {
if (!strcmp(property_name, "constructor")) {
*vp = JSVAL_VOID;
if (vp)
*vp = JSVAL_VOID;
return JS_TRUE;
}
}
@ -132,7 +133,8 @@ access_java_array_element(JSContext *cx,
JSJMSG_CANT_WRITE_JARRAY, member_name);
return JS_FALSE;
} else {
*vp = JSVAL_VOID;
if (vp)
*vp = JSVAL_VOID;
return JS_TRUE;
}
} else {
@ -190,20 +192,30 @@ JS_STATIC_DLL_CALLBACK(JSBool)
JavaArray_getPropertyById(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
JNIEnv *jEnv;
jsj_MapJSContextToJSJThread(cx, &jEnv);
JSJavaThreadState *jsj_env;
JSBool result;
jsj_env = jsj_EnterJava(cx, &jEnv);
if (!jEnv)
return JS_FALSE;
return access_java_array_element(cx, jEnv, obj, id, vp, JS_FALSE);
result = access_java_array_element(cx, jEnv, obj, id, vp, JS_FALSE);
jsj_ExitJava(jsj_env);
return result;
}
JS_STATIC_DLL_CALLBACK(JSBool)
JavaArray_setPropertyById(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
JNIEnv *jEnv;
jsj_MapJSContextToJSJThread(cx, &jEnv);
JSJavaThreadState *jsj_env;
JSBool result;
jsj_env = jsj_EnterJava(cx, &jEnv);
if (!jEnv)
return JS_FALSE;
return access_java_array_element(cx, jEnv, obj, id, vp, JS_TRUE);
result = access_java_array_element(cx, jEnv, obj, id, vp, JS_TRUE);
jsj_ExitJava(jsj_env);
return result;
}
static JSBool
@ -217,13 +229,16 @@ JavaArray_lookupProperty(JSContext *cx, JSObject *obj, jsid id,
JNIEnv *jEnv;
JSBool result;
JSErrorReporter old_reporter;
jsj_MapJSContextToJSJThread(cx, &jEnv);
JSJavaThreadState *jsj_env;
jsj_env = jsj_EnterJava(cx, &jEnv);
if (!jEnv)
return JS_FALSE;
old_reporter = JS_SetErrorReporter(cx, NULL);
result = access_java_array_element(cx, jEnv, obj, id, NULL, JS_FALSE);
JS_SetErrorReporter(cx, old_reporter);
jsj_ExitJava(jsj_env);
return result;
}
@ -232,9 +247,13 @@ JavaArray_defineProperty(JSContext *cx, JSObject *obj, jsid id, jsval value,
JSPropertyOp getter, JSPropertyOp setter,
uintN attrs, JSProperty **propp)
{
JS_ReportErrorNumber(cx, jsj_GetErrorMessage, NULL,
JSJMSG_JARRAY_PROP_DEFINE);
return JS_FALSE;
jsval *vp = &value;
if (propp)
return JS_FALSE;
if (attrs & ~(JSPROP_PERMANENT|JSPROP_ENUMERATE))
return JS_FALSE;
return JavaArray_setPropertyById(cx, obj, id, vp);
}
static JSBool
@ -290,6 +309,7 @@ JavaArray_newEnumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
jsval *statep, jsid *idp)
{
JavaObjectWrapper *java_wrapper;
JSJavaThreadState *jsj_env;
JNIEnv *jEnv;
jsize array_length, index;
@ -303,13 +323,15 @@ JavaArray_newEnumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
}
/* Get the Java per-thread environment pointer for this JSContext */
jsj_MapJSContextToJSJThread(cx, &jEnv);
jsj_env = jsj_EnterJava(cx, &jEnv);
if (!jEnv)
return JS_FALSE;
array_length = jsj_GetJavaArrayLength(cx, jEnv, java_wrapper->java_obj);
if (array_length < 0)
if (array_length < 0) {
jsj_ExitJava(jsj_env);
return JS_FALSE;
}
switch(enum_op) {
case JSENUMERATE_INIT:
@ -317,6 +339,7 @@ JavaArray_newEnumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
if (idp)
*idp = INT_TO_JSVAL(array_length);
jsj_ExitJava(jsj_env);
return JS_TRUE;
case JSENUMERATE_NEXT:
@ -332,10 +355,12 @@ JavaArray_newEnumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
case JSENUMERATE_DESTROY:
*statep = JSVAL_NULL;
jsj_ExitJava(jsj_env);
return JS_TRUE;
default:
JS_ASSERT(0);
jsj_ExitJava(jsj_env);
return JS_FALSE;
}
}

View File

@ -148,18 +148,23 @@ JavaClass_getPropertyById(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
JavaClassDescriptor *class_descriptor;
JavaMemberDescriptor *member_descriptor;
JNIEnv *jEnv;
JSJavaThreadState *jsj_env;
JSBool result;
/* printf("In JavaClass_getProperty\n"); */
/* Get the Java per-thread environment pointer for this JSContext */
jsj_MapJSContextToJSJThread(cx, &jEnv);
jsj_env = jsj_EnterJava(cx, &jEnv);
if (!jEnv)
return JS_FALSE;
if (!lookup_static_member_by_id(cx, jEnv, obj, &class_descriptor, id, &member_descriptor))
if (!lookup_static_member_by_id(cx, jEnv, obj, &class_descriptor, id, &member_descriptor)) {
jsj_ExitJava(jsj_env);
return JS_FALSE;
}
if (!member_descriptor) {
*vp = JSVAL_VOID;
jsj_ExitJava(jsj_env);
return JS_TRUE;
}
@ -167,7 +172,9 @@ JavaClass_getPropertyById(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
if (member_descriptor->field) {
if (!member_descriptor->methods) {
return jsj_GetJavaFieldValue(cx, jEnv, member_descriptor->field, java_class, vp);
result = jsj_GetJavaFieldValue(cx, jEnv, member_descriptor->field, java_class, vp);
jsj_ExitJava(jsj_env);
return result;
} else {
JS_ASSERT(0);
}
@ -189,11 +196,15 @@ JavaClass_getPropertyById(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
}
function = JS_NewFunction(cx, jsj_JavaStaticMethodWrapper, 0,
JSFUN_BOUND_METHOD, obj, member_name);
if (!function)
if (!function) {
jsj_ExitJava(jsj_env);
return JS_FALSE;
}
*vp = OBJECT_TO_JSVAL(JS_GetFunctionObject(function));
}
jsj_ExitJava(jsj_env);
return JS_TRUE;
}
@ -206,16 +217,20 @@ JavaClass_setPropertyById(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
JavaMemberDescriptor *member_descriptor;
jsval idval;
JNIEnv *jEnv;
JSJavaThreadState *jsj_env;
JSBool result;
/* printf("In JavaClass_setProperty\n"); */
/* Get the Java per-thread environment pointer for this JSContext */
jsj_MapJSContextToJSJThread(cx, &jEnv);
jsj_env = jsj_EnterJava(cx, &jEnv);
if (!jEnv)
return JS_FALSE;
if (!lookup_static_member_by_id(cx, jEnv, obj, &class_descriptor, id, &member_descriptor))
if (!lookup_static_member_by_id(cx, jEnv, obj, &class_descriptor, id, &member_descriptor)) {
jsj_ExitJava(jsj_env);
return JS_FALSE;
}
/* Check for the case where there is a method with the given name, but no field
with that name */
@ -223,11 +238,15 @@ JavaClass_setPropertyById(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
goto no_such_field;
/* Silently fail if field value is final (immutable), as required by ECMA spec */
if (member_descriptor->field->modifiers & ACC_FINAL)
if (member_descriptor->field->modifiers & ACC_FINAL) {
jsj_ExitJava(jsj_env);
return JS_TRUE;
}
java_class = class_descriptor->java_class;
return jsj_SetJavaFieldValue(cx, jEnv, member_descriptor->field, java_class, *vp);
result = jsj_SetJavaFieldValue(cx, jEnv, member_descriptor->field, java_class, *vp);
jsj_ExitJava(jsj_env);
return result;
no_such_field:
JS_IdToValue(cx, id, &idval);
@ -235,6 +254,7 @@ no_such_field:
JS_ReportErrorNumber(cx, jsj_GetErrorMessage, NULL,
JSJMSG_MISSING_STATIC,
member_name, class_descriptor->name);
jsj_ExitJava(jsj_env);
return JS_FALSE;
}
@ -245,18 +265,20 @@ JS_STATIC_DLL_CALLBACK(void)
JavaClass_finalize(JSContext *cx, JSObject *obj)
{
JNIEnv *jEnv;
JSJavaThreadState *jsj_env;
JavaClassDescriptor *class_descriptor = JS_GetPrivate(cx, obj);
if (!class_descriptor)
return;
/* Get the Java per-thread environment pointer for this JSContext */
jsj_MapJSContextToJSJThread(cx, &jEnv);
jsj_env = jsj_EnterJava(cx, &jEnv);
if (!jEnv)
return;
/* printf("Finalizing %s\n", class_descriptor->name); */
jsj_ReleaseJavaClassDescriptor(cx, jEnv, class_descriptor);
jsj_ExitJava(jsj_env);
}
@ -270,11 +292,12 @@ JavaClass_lookupProperty(JSContext *cx, JSObject *obj, jsid id,
{
JNIEnv *jEnv;
JSErrorReporter old_reporter;
JSJavaThreadState *jsj_env;
/* printf("In JavaClass_lookupProperty()\n"); */
/* Get the Java per-thread environment pointer for this JSContext */
jsj_MapJSContextToJSJThread(cx, &jEnv);
jsj_env = jsj_EnterJava(cx, &jEnv);
if (!jEnv)
return JS_FALSE;
@ -288,6 +311,7 @@ JavaClass_lookupProperty(JSContext *cx, JSObject *obj, jsid id,
}
JS_SetErrorReporter(cx, old_reporter);
jsj_ExitJava(jsj_env);
return JS_TRUE;
}
@ -364,6 +388,7 @@ JavaClass_newEnumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
JavaMemberDescriptor *member_descriptor;
JavaClassDescriptor *class_descriptor;
JNIEnv *jEnv;
JSJavaThreadState *jsj_env;
class_descriptor = JS_GetPrivate(cx, obj);
@ -378,13 +403,14 @@ JavaClass_newEnumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
switch(enum_op) {
case JSENUMERATE_INIT:
/* Get the Java per-thread environment pointer for this JSContext */
jsj_MapJSContextToJSJThread(cx, &jEnv);
jsj_env = jsj_EnterJava(cx, &jEnv);
if (!jEnv)
return JS_FALSE;
member_descriptor = jsj_GetClassStaticMembers(cx, jEnv, class_descriptor);
*statep = PRIVATE_TO_JSVAL(member_descriptor);
if (idp)
*idp = INT_TO_JSVAL(class_descriptor->num_instance_members);
jsj_ExitJava(jsj_env);
return JS_TRUE;
case JSENUMERATE_NEXT:
@ -453,6 +479,7 @@ JavaClass_hasInstance(JSContext *cx, JSObject *obj, jsval candidate_jsval,
jclass java_class;
jobject java_obj;
JNIEnv *jEnv;
JSJavaThreadState *jsj_env;
has_instance = JS_FALSE;
class_descriptor = JS_GetPrivate(cx, obj);
@ -486,8 +513,9 @@ JavaClass_hasInstance(JSContext *cx, JSObject *obj, jsval candidate_jsval,
}
java_obj = java_wrapper->java_obj;
/* Get JNI pointer */
jsj_MapJSContextToJSJThread(cx, &jEnv);
jsj_env = jsj_EnterJava(cx, &jEnv);
has_instance = (*jEnv)->IsInstanceOf(jEnv, java_obj, java_class);
jsj_ExitJava(jsj_env);
done:
*has_instancep = has_instance;
@ -589,10 +617,7 @@ getClass(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
JavaObjectWrapper *java_wrapper;
JavaClassDescriptor *class_descriptor;
JNIEnv *jEnv;
jsj_MapJSContextToJSJThread(cx, &jEnv);
if (!jEnv)
return JS_FALSE;
JSJavaThreadState *jsj_env;
if (argc != 1 ||
!JSVAL_IS_OBJECT(argv[0]) ||
@ -611,12 +636,20 @@ getClass(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
return JS_FALSE;
}
jsj_env = jsj_EnterJava(cx, &jEnv);
if (!jEnv)
return JS_FALSE;
class_descriptor = java_wrapper->class_descriptor;
JavaClass_obj = jsj_new_JavaClass(cx, jEnv, NULL, class_descriptor);
if (!JavaClass_obj)
if (!JavaClass_obj) {
jsj_ExitJava(jsj_env);
return JS_FALSE;
}
*rval = OBJECT_TO_JSVAL(JavaClass_obj);
jsj_ExitJava(jsj_env);
return JS_TRUE;
}
@ -627,10 +660,7 @@ JavaClass_construct(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval
JavaObjectWrapper *java_wrapper;
JavaClassDescriptor *class_descriptor;
JNIEnv *jEnv;
jsj_MapJSContextToJSJThread(cx, &jEnv);
if (!jEnv)
return JS_FALSE;
JSJavaThreadState *jsj_env;
if (argc != 1 ||
!JSVAL_IS_OBJECT(argv[0]) ||
@ -642,18 +672,27 @@ JavaClass_construct(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval
return JS_FALSE;
}
jsj_env = jsj_EnterJava(cx, &jEnv);
if (!jEnv)
return JS_FALSE;
class_descriptor = java_wrapper->class_descriptor;
if (!(*jEnv)->IsSameObject(jEnv, class_descriptor->java_class, jlClass)) {
JS_ReportErrorNumber(cx, jsj_GetErrorMessage, NULL,
JSJMSG_NEED_JCLASS_ARG);
jsj_ExitJava(jsj_env);
return JS_FALSE;
}
class_descriptor = jsj_GetJavaClassDescriptor(cx, jEnv, java_wrapper->java_obj);
JavaClass_obj = jsj_new_JavaClass(cx, jEnv, NULL, class_descriptor);
if (!JavaClass_obj)
if (!JavaClass_obj) {
jsj_ExitJava(jsj_env);
return JS_FALSE;
}
*rval = OBJECT_TO_JSVAL(JavaClass_obj);
jsj_ExitJava(jsj_env);
return JS_TRUE;
}

View File

@ -62,16 +62,11 @@ static void
JavaMember_finalize(JSContext *cx, JSObject *obj)
{
JavaMethodOrFieldValue *member_val;
JNIEnv *jEnv;
member_val = JS_GetPrivate(cx, obj);
if (!member_val)
return;
jsj_MapJSContextToJSJThread(cx, &jEnv);
if (!jEnv)
return;
JS_RemoveRoot(cx, &member_val->method_val);
if (JSVAL_IS_GCTHING(member_val->method_val))
JS_RemoveRoot(cx, &member_val->method_val);

View File

@ -54,6 +54,7 @@ static JSJHashTable *java_obj_reflections = NULL;
#ifdef JSJ_THREADSAFE
static PRMonitor *java_obj_reflections_monitor = NULL;
static int java_obj_reflections_mutation_count = 0;
#endif
JSBool
@ -92,6 +93,10 @@ jsj_WrapJavaObject(JSContext *cx,
JavaClassDescriptor *class_descriptor;
JSJHashEntry *he, **hep;
#ifdef JSJ_THREADSAFE
int mutation_count;
#endif
js_wrapper_obj = NULL;
hash_code = jsj_HashJavaObject((void*)java_obj, (void*)jEnv);
@ -103,16 +108,26 @@ jsj_WrapJavaObject(JSContext *cx,
hep = JSJ_HashTableRawLookup(java_obj_reflections,
hash_code, java_obj, (void*)jEnv);
he = *hep;
#ifdef JSJ_THREADSAFE
/* Track mutations to hash table */
mutation_count = java_obj_reflections_mutation_count;
/* We must temporarily release this monitor so as to avoid
deadlocks with the JS GC. See Bugsplat #354852 */
PR_ExitMonitor(java_obj_reflections_monitor);
#endif
if (he) {
js_wrapper_obj = (JSObject *)he->value;
if (js_wrapper_obj)
goto done;
return js_wrapper_obj;
}
/* No existing reflection found. Construct a new one */
class_descriptor = jsj_GetJavaClassDescriptor(cx, jEnv, java_class);
if (!class_descriptor)
goto done;
return NULL;
if (class_descriptor->type == JAVA_SIGNATURE_ARRAY) {
js_class = &JavaArray_class;
} else {
@ -123,17 +138,40 @@ jsj_WrapJavaObject(JSContext *cx,
/* Create new JS object to reflect Java object */
js_wrapper_obj = JS_NewObject(cx, js_class, NULL, NULL);
if (!js_wrapper_obj)
goto done;
return NULL;
/* 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;
return NULL;
}
JS_SetPrivate(cx, js_wrapper_obj, java_wrapper);
java_wrapper->class_descriptor = class_descriptor;
java_wrapper->java_obj = NULL;
#ifdef JSJ_THREADSAFE
PR_EnterMonitor(java_obj_reflections_monitor);
/* We may need to do the hash table lookup again, since some other
thread may have updated it while the lock wasn't being held. */
if (mutation_count != java_obj_reflections_mutation_count) {
hep = JSJ_HashTableRawLookup(java_obj_reflections,
hash_code, java_obj, (void*)jEnv);
he = *hep;
if (he) {
js_wrapper_obj = (JSObject *)he->value;
if (js_wrapper_obj) {
PR_ExitMonitor(java_obj_reflections_monitor);
return js_wrapper_obj;
}
}
}
java_obj_reflections_mutation_count++;
#endif
java_obj = (*jEnv)->NewGlobalRef(jEnv, java_obj);
java_wrapper->java_obj = java_obj;
@ -143,23 +181,21 @@ jsj_WrapJavaObject(JSContext *cx,
/* Add the JavaObject to the hash table */
he = JSJ_HashTableRawAdd(java_obj_reflections, hep, hash_code,
java_obj, js_wrapper_obj, (void*)jEnv);
#ifdef JSJ_THREADSAFE
PR_ExitMonitor(java_obj_reflections_monitor);
#endif
if (!he) {
(*jEnv)->DeleteGlobalRef(jEnv, java_obj);
goto out_of_memory;
}
done:
#ifdef JSJ_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;
return NULL;
}
static void
@ -183,6 +219,8 @@ remove_java_obj_reflection_from_hashtable(jobject java_obj, JNIEnv *jEnv)
JSJ_HashTableRawRemove(java_obj_reflections, hep, he, (void*)jEnv);
#ifdef JSJ_THREADSAFE
java_obj_reflections_mutation_count++;
PR_ExitMonitor(java_obj_reflections_monitor);
#endif
}
@ -193,13 +231,14 @@ JavaObject_finalize(JSContext *cx, JSObject *obj)
JavaObjectWrapper *java_wrapper;
jobject java_obj;
JNIEnv *jEnv;
JSJavaThreadState *jsj_env;
java_wrapper = JS_GetPrivate(cx, obj);
if (!java_wrapper)
return;
java_obj = java_wrapper->java_obj;
jsj_MapJSContextToJSJThread(cx, &jEnv);
jsj_env = jsj_EnterJava(cx, &jEnv);
if (!jEnv)
return;
@ -209,6 +248,7 @@ JavaObject_finalize(JSContext *cx, JSObject *obj)
}
jsj_ReleaseJavaClassDescriptor(cx, jEnv, java_wrapper->class_descriptor);
JS_free(cx, java_wrapper);
jsj_ExitJava(jsj_env);
}
/* Trivial helper for jsj_DiscardJavaObjReflections(), below */
@ -255,11 +295,8 @@ JavaObject_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp)
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;
JSJavaThreadState *jsj_env;
JSBool result;
java_wrapper = JS_GetPrivate(cx, obj);
if (!java_wrapper) {
@ -269,10 +306,10 @@ JavaObject_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp)
}
JS_ReportErrorNumber(cx, jsj_GetErrorMessage, NULL,
JSJMSG_BAD_OP_JOBJECT);
JSJMSG_BAD_OP_JOBJECT);
return JS_FALSE;
}
java_obj = java_wrapper->java_obj;
class_descriptor = java_wrapper->class_descriptor;
@ -283,22 +320,43 @@ JavaObject_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp)
case JSTYPE_FUNCTION:
JS_ReportErrorNumber(cx, jsj_GetErrorMessage, NULL,
JSJMSG_CONVERT_TO_FUNC);
JSJMSG_CONVERT_TO_FUNC);
return JS_FALSE;
case JSTYPE_VOID:
case JSTYPE_STRING:
/* Get the Java per-thread environment pointer for this JSContext */
jsj_env = jsj_EnterJava(cx, &jEnv);
if (!jEnv)
return JS_FALSE;
/* 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);
result = jsj_ConvertJavaObjectToJSString(cx, jEnv, class_descriptor, java_obj, vp);
jsj_ExitJava(jsj_env);
return result;
case JSTYPE_NUMBER:
/* Call Java doubleValue() method, if applicable */
return jsj_ConvertJavaObjectToJSNumber(cx, jEnv, class_descriptor, java_obj, vp);
/* Get the Java per-thread environment pointer for this JSContext */
jsj_env = jsj_EnterJava(cx, &jEnv);
if (!jEnv)
return JS_FALSE;
/* Call Java doubleValue() method, if applicable */
result = jsj_ConvertJavaObjectToJSNumber(cx, jEnv, class_descriptor, java_obj, vp);
jsj_ExitJava(jsj_env);
return result;
case JSTYPE_BOOLEAN:
/* Get the Java per-thread environment pointer for this JSContext */
jsj_env = jsj_EnterJava(cx, &jEnv);
if (!jEnv)
return JS_FALSE;
/* Call booleanValue() method, if applicable */
return jsj_ConvertJavaObjectToJSBoolean(cx, jEnv, class_descriptor, java_obj, vp);
result = jsj_ConvertJavaObjectToJSBoolean(cx, jEnv, class_descriptor, java_obj, vp);
jsj_ExitJava(jsj_env);
return result;
default:
JS_ASSERT(0);
@ -460,23 +518,28 @@ JavaObject_getPropertyById(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
JSObject *funobj;
jsval field_val, method_val;
JSBool success;
JSJavaThreadState *jsj_env;
/* printf("In JavaObject_getProperty\n"); */
/* Get the Java per-thread environment pointer for this JSContext */
jsj_MapJSContextToJSJThread(cx, &jEnv);
jsj_env = jsj_EnterJava(cx, &jEnv);
if (!jEnv)
return JS_FALSE;
if (vp)
*vp = JSVAL_VOID;
if (!lookup_member_by_id(cx, jEnv, obj, &java_wrapper, id, &member_descriptor, vp))
if (!lookup_member_by_id(cx, jEnv, obj, &java_wrapper, id, &member_descriptor, vp)) {
jsj_ExitJava(jsj_env);
return JS_FALSE;
}
/* Handle access to special, non-Java properties of JavaObjects, e.g. the
"constructor" property of the prototype object */
if (!member_descriptor)
if (!member_descriptor) {
jsj_ExitJava(jsj_env);
return JS_TRUE;
}
java_obj = java_wrapper->java_obj;
field_val = method_val = JSVAL_VOID;
@ -484,8 +547,10 @@ JavaObject_getPropertyById(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
/* If a field member, get the value of the field */
if (member_descriptor->field) {
success = jsj_GetJavaFieldValue(cx, jEnv, member_descriptor->field, java_obj, &field_val);
if (!success)
if (!success) {
jsj_ExitJava(jsj_env);
return JS_FALSE;
}
}
/* If a method member, build a wrapper around the Java method */
@ -493,16 +558,20 @@ JavaObject_getPropertyById(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
/* Create a function object with this JavaObject as its parent, so that
JSFUN_BOUND_METHOD binds it as the default 'this' for the function. */
funobj = JS_CloneFunctionObject(cx, member_descriptor->invoke_func_obj, obj);
if (!funobj)
if (!funobj) {
jsj_ExitJava(jsj_env);
return JS_FALSE;
}
method_val = OBJECT_TO_JSVAL(funobj);
}
#if TEST_JAVAMEMBER
/* Always create a JavaMember object, even though it's inefficient */
obj = jsj_CreateJavaMember(cx, method_val, field_val);
if (!obj)
if (!obj) {
jsj_ExitJava(jsj_env);
return JS_FALSE;
}
*vp = OBJECT_TO_JSVAL(obj);
#else /* !TEST_JAVAMEMBER */
@ -516,8 +585,10 @@ JavaObject_getPropertyById(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
In Java, such ambiguity is not possible because the compiler
can statically determine which is being accessed. */
obj = jsj_CreateJavaMember(cx, method_val, field_val);
if (!obj)
if (!obj) {
jsj_ExitJava(jsj_env);
return JS_FALSE;
}
*vp = OBJECT_TO_JSVAL(obj);
}
@ -527,7 +598,8 @@ JavaObject_getPropertyById(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
}
#endif /* !TEST_JAVAMEMBER */
jsj_ExitJava(jsj_env);
return JS_TRUE;
}
@ -541,16 +613,20 @@ JavaObject_setPropertyById(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
JavaMemberDescriptor *member_descriptor;
jsval idval;
JNIEnv *jEnv;
JSJavaThreadState *jsj_env;
JSBool result;
/* printf("In JavaObject_setProperty\n"); */
/* Get the Java per-thread environment pointer for this JSContext */
jsj_MapJSContextToJSJThread(cx, &jEnv);
jsj_env = jsj_EnterJava(cx, &jEnv);
if (!jEnv)
return JS_FALSE;
if (!lookup_member_by_id(cx, jEnv, obj, &java_wrapper, id, &member_descriptor, NULL))
if (!lookup_member_by_id(cx, jEnv, obj, &java_wrapper, id, &member_descriptor, NULL)) {
jsj_ExitJava(jsj_env);
return JS_FALSE;
}
/* Could be assignment to magic JS __proto__ property rather than a Java field */
if (!member_descriptor) {
@ -563,9 +639,11 @@ JavaObject_setPropertyById(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
if (!JSVAL_IS_OBJECT(*vp)) {
JS_ReportErrorNumber(cx, jsj_GetErrorMessage, NULL,
JSJMSG_BAD_PROTO_ASSIGNMENT);
jsj_ExitJava(jsj_env);
return JS_FALSE;
}
JS_SetPrototype(cx, obj, JSVAL_TO_OBJECT(*vp));
jsj_ExitJava(jsj_env);
return JS_TRUE;
}
@ -575,11 +653,15 @@ JavaObject_setPropertyById(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
goto no_such_field;
/* Silently fail if field value is final (immutable), as required by ECMA spec */
if (member_descriptor->field->modifiers & ACC_FINAL)
if (member_descriptor->field->modifiers & ACC_FINAL) {
jsj_ExitJava(jsj_env);
return JS_TRUE;
}
java_obj = java_wrapper->java_obj;
return jsj_SetJavaFieldValue(cx, jEnv, member_descriptor->field, java_obj, *vp);
result = jsj_SetJavaFieldValue(cx, jEnv, member_descriptor->field, java_obj, *vp);
jsj_ExitJava(jsj_env);
return result;
no_such_field:
JS_IdToValue(cx, id, &idval);
@ -588,6 +670,7 @@ no_such_field:
JS_ReportErrorNumber(cx, jsj_GetErrorMessage, NULL,
JSJMSG_NO_NAME_IN_CLASS,
member_name, class_descriptor->name);
jsj_ExitJava(jsj_env);
return JS_FALSE;
}
@ -602,11 +685,12 @@ JavaObject_lookupProperty(JSContext *cx, JSObject *obj, jsid id,
JNIEnv *jEnv;
JSErrorReporter old_reporter;
jsval dummy_val;
JSJavaThreadState *jsj_env;
/* printf("In JavaObject_lookupProperty()\n"); */
/* Get the Java per-thread environment pointer for this JSContext */
jsj_MapJSContextToJSJThread(cx, &jEnv);
jsj_env = jsj_EnterJava(cx, &jEnv);
if (!jEnv)
return JS_FALSE;
@ -624,6 +708,7 @@ JavaObject_lookupProperty(JSContext *cx, JSObject *obj, jsid id,
}
JS_SetErrorReporter(cx, old_reporter);
jsj_ExitJava(jsj_env);
return JS_TRUE;
}
@ -693,6 +778,7 @@ JavaObject_newEnumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
JavaMemberDescriptor *member_descriptor;
JavaClassDescriptor *class_descriptor;
JNIEnv *jEnv;
JSJavaThreadState *jsj_env;
java_wrapper = JS_GetPrivate(cx, obj);
/* Check for prototype object */
@ -709,7 +795,7 @@ JavaObject_newEnumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
case JSENUMERATE_INIT:
/* Get the Java per-thread environment pointer for this JSContext */
jsj_MapJSContextToJSJThread(cx, &jEnv);
jsj_env = jsj_EnterJava(cx, &jEnv);
if (!jEnv)
return JS_FALSE;
@ -717,6 +803,7 @@ JavaObject_newEnumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
*statep = PRIVATE_TO_JSVAL(member_descriptor);
if (idp)
*idp = INT_TO_JSVAL(class_descriptor->num_instance_members);
jsj_ExitJava(jsj_env);
return JS_TRUE;
case JSENUMERATE_NEXT:

View File

@ -118,6 +118,7 @@ JavaPackage_resolve(JSContext *cx, JSObject *obj, jsval id)
char *subPath, *newPath;
const char *path;
JNIEnv *jEnv;
JSJavaThreadState *jsj_env;
/* Painful hack for pre_define_java_packages() */
if (quiet_resolve_failure)
@ -148,7 +149,7 @@ JavaPackage_resolve(JSContext *cx, JSObject *obj, jsval id)
return JS_FALSE;
}
jsj_MapJSContextToJSJThread(cx, &jEnv);
jsj_env = jsj_EnterJava(cx, &jEnv);
if (!jEnv)
return JS_FALSE;
@ -235,6 +236,7 @@ JavaPackage_resolve(JSContext *cx, JSObject *obj, jsval id)
out:
free(newPath);
jsj_ExitJava(jsj_env);
return ok;
}

View File

@ -396,10 +396,41 @@ jsj_DiscardJavaClassReflections(JNIEnv *jEnv)
{
JSJavaThreadState *jsj_env;
char *err_msg;
JSContext *cx;
/* Get the per-thread state corresponding to the current Java thread */
jsj_env = jsj_MapJavaThreadToJSJavaThreadState(jEnv, &err_msg);
JS_ASSERT(jsj_env);
if (!jsj_env)
return;
/* 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) {
#ifdef OJI
cx = JSJ_callbacks->map_jsj_thread_to_js_context(jsj_env,
NULL /* FIXME: What should this argument be ? */
jEnv, &err_msg);
#else
cx = JSJ_callbacks->map_jsj_thread_to_js_context(jsj_env,
jEnv, &err_msg);
#endif
JS_ASSERT(cx);
if (!cx)
return;
} else {
err_msg = JS_smprintf("Unable to find/create JavaScript execution "
"context for JNI thread 0x%08x", jEnv);
jsj_LogError(err_msg);
free(err_msg);
return;
}
jsj_env->cx = cx;
}
if (java_class_reflections) {
JSJ_HashTableEnumerateEntries(java_class_reflections,
@ -470,19 +501,22 @@ reflect_java_methods_and_fields(JSContext *cx,
success = JS_TRUE; /* optimism */
#ifdef JSJ_THREAD_SAFE
#ifdef JSJ_THREADSAFE
PR_EnterMonitor(java_reflect_monitor);
#endif
/* See if we raced with another thread to reflect members of this class */
/* See if we raced with another thread to reflect members of this class.
If the status is REFLECT_COMPLETE, another thread beat us to it. If
the status is REFLECT_IN_PROGRESS, we've recursively called this
function within a single thread. Either way, we're done. */
if (reflect_statics_only) {
if (class_descriptor->static_members_reflected)
if (class_descriptor->static_members_reflected != REFLECT_NO)
goto done;
class_descriptor->static_members_reflected = JS_TRUE;
class_descriptor->static_members_reflected = REFLECT_IN_PROGRESS;
} else {
if (class_descriptor->instance_members_reflected)
if (class_descriptor->instance_members_reflected != REFLECT_NO)
goto done;
class_descriptor->instance_members_reflected = JS_TRUE;
class_descriptor->instance_members_reflected = REFLECT_IN_PROGRESS;
}
if (!jsj_ReflectJavaMethods(cx, jEnv, class_descriptor, reflect_statics_only))
@ -496,16 +530,18 @@ reflect_java_methods_and_fields(JSContext *cx,
class_descriptor->num_static_members++;
member_descriptor = member_descriptor->next;
}
class_descriptor->static_members_reflected = REFLECT_COMPLETE;
} else {
member_descriptor = class_descriptor->instance_members;
while (member_descriptor) {
class_descriptor->num_instance_members++;
member_descriptor = member_descriptor->next;
}
class_descriptor->instance_members_reflected = REFLECT_COMPLETE;
}
done:
#ifdef JSJ_THREAD_SAFE
#ifdef JSJ_THREADSAFE
PR_ExitMonitor(java_reflect_monitor);
#endif
return success;
@ -520,7 +556,7 @@ jsj_GetClassStaticMembers(JSContext *cx,
JNIEnv *jEnv,
JavaClassDescriptor *class_descriptor)
{
if (!class_descriptor->static_members_reflected)
if (class_descriptor->static_members_reflected != REFLECT_COMPLETE)
reflect_java_methods_and_fields(cx, jEnv, class_descriptor, JS_TRUE);
return class_descriptor->static_members;
}
@ -530,7 +566,7 @@ jsj_GetClassInstanceMembers(JSContext *cx,
JNIEnv *jEnv,
JavaClassDescriptor *class_descriptor)
{
if (!class_descriptor->instance_members_reflected)
if (class_descriptor->instance_members_reflected != REFLECT_COMPLETE)
reflect_java_methods_and_fields(cx, jEnv, class_descriptor, JS_FALSE);
return class_descriptor->instance_members;
}
@ -615,7 +651,7 @@ JavaMemberDescriptor *
jsj_LookupJavaClassConstructors(JSContext *cx, JNIEnv *jEnv,
JavaClassDescriptor *class_descriptor)
{
if (!class_descriptor->static_members_reflected)
if (class_descriptor->static_members_reflected != REFLECT_COMPLETE)
reflect_java_methods_and_fields(cx, jEnv, class_descriptor, JS_TRUE);
return class_descriptor->constructors;
}

View File

@ -1088,13 +1088,16 @@ preferred_conversion(JSContext *cx, JNIEnv *jEnv, jsval js_val,
js_type = compute_jsj_type(cx, js_val);
rank1 = rank_table[js_type][(int)descriptor1->type - 2];
rank2 = rank_table[js_type][(int)descriptor2->type - 2];
/* Fast path for conversion from most JS types */
if (rank1 < rank2)
return JSJPREF_FIRST_ARG;
/*
* Special logic is required for matching the classes of wrapped
* Java objects.
*/
if (((js_type == JSJTYPE_JAVAOBJECT) || (js_type == JSJTYPE_JAVAARRAY)) &&
IS_REFERENCE_TYPE(descriptor2->type)) {
if (rank2 == 0) {
java_class1 = descriptor1->java_class;
java_class2 = descriptor2->java_class;
@ -1110,7 +1113,7 @@ preferred_conversion(JSContext *cx, JNIEnv *jEnv, jsval js_val,
* For JavaObject arguments, any compatible reference type is preferable
* to any primitive Java type or to java.lang.String.
*/
if (rank2 < rank1)
if (rank1 != 0)
return JSJPREF_SECOND_ARG;
/*
@ -1127,10 +1130,6 @@ preferred_conversion(JSContext *cx, JNIEnv *jEnv, jsval js_val,
return JSJPREF_AMBIGUOUS;
}
/* Fast path for conversion from most JS types */
if (rank1 < rank2)
return JSJPREF_FIRST_ARG;
if (rank1 > rank2)
return JSJPREF_SECOND_ARG;
@ -1394,6 +1393,12 @@ invoke_java_method(JSContext *cx, JSJavaThreadState *jsj_env,
}
}
/* Prevent deadlocking if we re-enter JS on another thread as a result of a Java
method call and that new thread wants to perform a GC. */
#ifdef JSJ_THREADSAFE
JS_EndRequest(cx);
#endif
#define CALL_JAVA_METHOD(type, member) \
JS_BEGIN_MACRO \
if (is_static_method) { \
@ -1458,7 +1463,8 @@ invoke_java_method(JSContext *cx, JSJavaThreadState *jsj_env,
case JAVA_SIGNATURE_UNKNOWN:
JS_ASSERT(0);
return JS_FALSE;
error_occurred = JS_TRUE;
goto out;
/* Non-primitive (reference) type */
default:
@ -1468,6 +1474,7 @@ invoke_java_method(JSContext *cx, JSJavaThreadState *jsj_env,
}
out:
JSJ_SetDefaultJSContextForJavaThread(old_cx, jsj_env);
if (localv) {
@ -1480,6 +1487,10 @@ out:
if (jargv)
JS_free(cx, jargv);
#ifdef JSJ_THREADSAFE
JS_BeginRequest(cx);
#endif
if (!error_occurred) {
success = jsj_ConvertJavaValueToJSValue(cx, jEnv, return_val_signature, &java_value, vp);
if (IS_REFERENCE_TYPE(return_val_signature->type))
@ -1556,9 +1567,19 @@ invoke_java_constructor(JSContext *cx,
}
#endif
/* Prevent deadlocking if we re-enter JS on another thread as a result of a Java
method call and that new thread wants to perform a GC. */
#ifdef JSJ_THREADSAFE
JS_EndRequest(cx);
#endif
/* Call the constructor */
java_object = (*jEnv)->NewObjectA(jEnv, java_class, methodID, jargv);
#ifdef JSJ_THREADSAFE
JS_BeginRequest(cx);
#endif
JSJ_SetDefaultJSContextForJavaThread(old_cx, jsj_env);
if (!java_object) {
@ -1655,19 +1676,23 @@ jsj_JavaConstructorWrapper(JSContext *cx, JSObject *obj,
JavaMemberDescriptor *member_descriptor;
JSJavaThreadState *jsj_env;
JNIEnv *jEnv;
JSBool result;
/* 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);
JS_ASSERT(class_descriptor);
if (!class_descriptor)
return JS_FALSE;
/* Get the Java per-thread environment pointer for this JSContext */
jsj_env = jsj_EnterJava(cx, &jEnv);
if (!jEnv)
return JS_FALSE;
member_descriptor = jsj_LookupJavaClassConstructors(cx, jEnv, class_descriptor);
return java_constructor_wrapper(cx, jsj_env, member_descriptor,
class_descriptor, argc, argv, vp);
result = java_constructor_wrapper(cx, jsj_env, member_descriptor,
class_descriptor, argc, argv, vp);
jsj_ExitJava(jsj_env);
return result;
}
@ -1709,22 +1734,25 @@ jsj_JavaStaticMethodWrapper(JSContext *cx, JSObject *obj,
jsval idval;
JNIEnv *jEnv;
JSJavaThreadState *jsj_env;
/* Get the Java per-thread environment pointer for this JSContext */
jsj_env = jsj_MapJSContextToJSJThread(cx, &jEnv);
if (!jEnv)
return JS_FALSE;
JSBool result;
class_descriptor = JS_GetPrivate(cx, obj);
if (!class_descriptor)
return JS_FALSE;
/* Get the Java per-thread environment pointer for this JSContext */
jsj_env = jsj_EnterJava(cx, &jEnv);
if (!jEnv)
return JS_FALSE;
JS_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);
return static_method_wrapper(cx, jsj_env, class_descriptor, id, argc, argv, vp);
result = static_method_wrapper(cx, jsj_env, class_descriptor, id, argc, argv, vp);
jsj_ExitJava(jsj_env);
return result;
}
JS_DLL_CALLBACK JSBool
@ -1740,11 +1768,7 @@ jsj_JavaInstanceMethodWrapper(JSContext *cx, JSObject *obj,
JSJavaThreadState *jsj_env;
JNIEnv *jEnv;
jobject java_obj;
/* Get the Java per-thread environment pointer for this JSContext */
jsj_env = jsj_MapJSContextToJSJThread(cx, &jEnv);
if (!jEnv)
return JS_FALSE;
JSBool result;
java_wrapper = JS_GetPrivate(cx, obj);
if (!java_wrapper)
@ -1757,14 +1781,22 @@ jsj_JavaInstanceMethodWrapper(JSContext *cx, JSObject *obj,
JS_ValueToId(cx, idval, &id);
class_descriptor = java_wrapper->class_descriptor;
/* Get the Java per-thread environment pointer for this JSContext */
jsj_env = jsj_EnterJava(cx, &jEnv);
if (!jEnv)
return JS_FALSE;
/* Try to find an instance method with the given name first */
member_descriptor = jsj_LookupJavaMemberDescriptorById(cx, jEnv, class_descriptor, id);
if (member_descriptor)
return invoke_overloaded_java_method(cx, jsj_env, member_descriptor,
JS_FALSE, java_obj,
class_descriptor, argc, argv, vp);
result = invoke_overloaded_java_method(cx, jsj_env, member_descriptor,
JS_FALSE, java_obj,
class_descriptor, argc, argv, vp);
/* If no instance method was found, try for a static method or constructor */
return static_method_wrapper(cx, jsj_env, class_descriptor, id, argc, argv, vp);
else
result = static_method_wrapper(cx, jsj_env, class_descriptor, id, argc, argv, vp);
jsj_ExitJava(jsj_env);
return result;
}

View File

@ -151,6 +151,13 @@ struct JavaMemberDescriptor {
JSObject * invoke_func_obj; /* If non-null, JSFunction obj to invoke method */
};
/* Status of Class member reflection. See JavaClassDescriptor. */
typedef enum {
REFLECT_NO,
REFLECT_IN_PROGRESS,
REFLECT_COMPLETE
} ReflectStatus;
/* This is the native portion of a reflected Java class */
struct JavaClassDescriptor {
const char * name; /* Name of class, e.g. "java.lang.Byte" */
@ -158,9 +165,9 @@ struct JavaClassDescriptor {
jclass java_class; /* Opaque JVM handle to corresponding java.lang.Class */
int num_instance_members;
int num_static_members;
JSBool instance_members_reflected;
volatile ReflectStatus instance_members_reflected;
JavaMemberDescriptor * instance_members;
JSBool static_members_reflected;
volatile ReflectStatus static_members_reflected;
JavaMemberDescriptor * static_members;
JavaMemberDescriptor * constructors;
int modifiers; /* Class declaration qualifiers,
@ -204,7 +211,7 @@ struct JSJavaThreadState {
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 */
int recursion_depth;/* # transitions into JS from Java */
int recursion_depth;/* # transitions into Java from JS */
JSJavaThreadState * next; /* next thread state among all created threads */
};
@ -219,7 +226,7 @@ typedef struct JavaToJSSavedState JavaToJSSavedState;
objects hold a reference to native JSObjects. */
struct JSObjectHandle {
JSObject *js_obj;
JSContext *cx; /* Creating context, needed for finalization */
JSRuntime *rt;
};
typedef struct JSObjectHandle JSObjectHandle;
@ -578,8 +585,11 @@ 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);
extern JSJavaThreadState *
jsj_EnterJava(JSContext *cx, JNIEnv **envp);
extern void
jsj_ExitJava(JSJavaThreadState *jsj_env);
#ifdef DEBUG
#define DEBUG_LOG(args) printf args

View File

@ -221,88 +221,97 @@ vreport_java_error(JSContext *cx, JNIEnv *jEnv, const char *format, va_list ap)
/* Get the exception out of the java environment. */
java_exception = (*jEnv)->ExceptionOccurred(jEnv);
if (!java_exception) {
JSString *err_jsstr;
char *err = JS_vsmprintf(format, ap);
if (!err)
return;
err_jsstr = JS_NewString(cx, err, strlen(err));
if (!err_jsstr)
return;
JS_SetPendingException(cx, STRING_TO_JSVAL(err_jsstr));
return;
}
if (java_exception) {
(*jEnv)->ExceptionClear(jEnv);
(*jEnv)->ExceptionClear(jEnv);
/* Check for JSException */
if (njJSException &&
(*jEnv)->IsInstanceOf(jEnv, java_exception, njJSException)) {
/* Check for JSException */
if (njJSException &&
(*jEnv)->IsInstanceOf(jEnv, java_exception, njJSException)) {
wrapped_exception_type =
(*jEnv)->GetIntField(jEnv, java_exception,
njJSException_wrappedExceptionType);
wrapped_exception_type =
(*jEnv)->GetIntField(jEnv, java_exception,
njJSException_wrappedExceptionType);
if (wrapped_exception_type != JSTYPE_EMPTY) {
java_obj =
(*jEnv)->GetObjectField(jEnv, java_exception,
njJSException_wrappedException);
if ((java_obj == NULL) &&
(wrapped_exception_type == JSTYPE_OBJECT)) {
js_exception = JSVAL_NULL;
} else {
java_class = (*jEnv)->GetObjectClass(jEnv, java_obj);
class_descriptor = jsj_GetJavaClassDescriptor(cx, jEnv, java_class);
/* OK to delete ref, since above call adds global ref */
(*jEnv)->DeleteLocalRef(jEnv, java_class);
if (wrapped_exception_type != JSTYPE_EMPTY) {
java_obj =
(*jEnv)->GetObjectField(jEnv, java_exception,
njJSException_wrappedException);
if ((java_obj == NULL) &&
(wrapped_exception_type == JSTYPE_OBJECT)) {
js_exception = JSVAL_NULL;
} else {
java_class = (*jEnv)->GetObjectClass(jEnv, java_obj);
class_descriptor = jsj_GetJavaClassDescriptor(cx, jEnv, java_class);
/* OK to delete ref, since above call adds global ref */
(*jEnv)->DeleteLocalRef(jEnv, java_class);
/* Convert native JS values back to native types. */
switch(wrapped_exception_type) {
case JSTYPE_NUMBER:
if (!jsj_ConvertJavaObjectToJSNumber(cx, jEnv,
class_descriptor,
java_obj,
&js_exception))
goto do_report;
break;
case JSTYPE_BOOLEAN:
if (!jsj_ConvertJavaObjectToJSBoolean(cx, jEnv,
class_descriptor,
java_obj,
&js_exception))
goto do_report;
break;
case JSTYPE_STRING:
if (!jsj_ConvertJavaObjectToJSString(cx, jEnv,
class_descriptor,
java_obj,
&js_exception))
goto do_report;
break;
case JSTYPE_VOID:
js_exception = JSVAL_VOID;
break;
case JSTYPE_OBJECT:
case JSTYPE_FUNCTION:
default:
if ((*jEnv)->IsInstanceOf(jEnv, java_obj, njJSObject)) {
js_exception = OBJECT_TO_JSVAL(jsj_UnwrapJSObjectWrapper(jEnv, java_obj));
if (!js_exception)
goto do_report;
} else {
if (!jsj_ConvertJavaObjectToJSValue(cx, jEnv, java_obj,
&js_exception))
goto do_report;
}
/* Convert native JS values back to native types. */
switch(wrapped_exception_type) {
case JSTYPE_NUMBER:
if (!jsj_ConvertJavaObjectToJSNumber(cx, jEnv,
class_descriptor,
java_obj,
&js_exception))
goto error;
break;
case JSTYPE_BOOLEAN:
if (!jsj_ConvertJavaObjectToJSBoolean(cx, jEnv,
class_descriptor,
java_obj,
&js_exception))
goto error;
break;
case JSTYPE_STRING:
if (!jsj_ConvertJavaObjectToJSString(cx, jEnv,
class_descriptor,
java_obj,
&js_exception))
goto error;
break;
case JSTYPE_VOID:
js_exception = JSVAL_VOID;
break;
case JSTYPE_OBJECT:
case JSTYPE_FUNCTION:
default:
if ((*jEnv)->IsInstanceOf(jEnv, java_obj, njJSObject)) {
js_exception = OBJECT_TO_JSVAL(jsj_UnwrapJSObjectWrapper(jEnv, java_obj));
if (!js_exception)
goto error;
} else {
if (!jsj_ConvertJavaObjectToJSValue(cx, jEnv, java_obj,
&js_exception))
goto error;
}
}
}
/* Check for internal exception */
} else {
if (!JSJ_ConvertJavaObjectToJSValue(cx, java_exception,
&js_exception)) {
goto do_report;
}
}
/* Set pending JS exception and clear the java exception. */
JS_SetPendingException(cx, js_exception);
goto done;
/* Check for internal exception */
} else {
if (!JSJ_ConvertJavaObjectToJSValue(cx, java_exception,
&js_exception)) {
goto error;
}
}
/* Set pending JS exception and clear the java exception. */
JS_SetPendingException(cx, js_exception);
goto done;
do_report:
error:
JS_ASSERT(0);
jsj_LogError("Out of memory while attempting to throw JSException\n");
@ -411,7 +420,7 @@ jsj_GetJavaArrayLength(JSContext *cx, JNIEnv *jEnv, jarray java_array)
static JSJavaThreadState *the_java_jsj_env = NULL;
JSJavaThreadState *
jsj_MapJSContextToJSJThread(JSContext *cx, JNIEnv **envp)
jsj_EnterJava(JSContext *cx, JNIEnv **envp)
{
JSJavaThreadState *jsj_env;
char *err_msg;
@ -429,13 +438,24 @@ jsj_MapJSContextToJSJThread(JSContext *cx, JNIEnv **envp)
}
return NULL;
}
/* need to assign the context field. */
JS_ASSERT((jsj_env->recursion_depth == 0) || (jsj_env->cx == cx));
jsj_env->recursion_depth++;
jsj_env->cx = cx;
if (envp)
*envp = jsj_env->jEnv;
return jsj_env;
}
extern void
jsj_ExitJava(JSJavaThreadState *jsj_env)
{
JS_ASSERT(jsj_env->recursion_depth > 0);
if (--jsj_env->recursion_depth == 0)
jsj_env->cx = NULL;
}
/**
* Since only one Java thread is allowed to enter JavaScript, this function is
* used to enforce the use of that thread's state. The static global the_java_jsj_env

View File

@ -78,7 +78,9 @@ typedef struct JSJCallbacks {
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,
#ifdef OJI
void *java_applet_obj,
#endif
JNIEnv *jEnv,
char **errp);