mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-25 19:25:43 +00:00
Merge changes from SpiderMonkey140_BRANCH between
JS_STABLE_DROP_04261999 and JS_STABLE_DROP_06221999
This commit is contained in:
parent
e18b5e37c8
commit
05603647c8
@ -23,132 +23,133 @@ Table of Contents</h2>
|
||||
|
||||
<h2>
|
||||
<a NAME="Introduction"></a>Introduction</h2>
|
||||
LiveConnect allows JavaScript and Java virtual machines to interoperate.
|
||||
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. 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>. 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). 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.
|
||||
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. 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>. 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). 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.
|
||||
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.
|
||||
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.
|
||||
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. 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. 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.
|
||||
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.
|
||||
Informally, the method with Java parameter types that most 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. 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.
|
||||
If necessary, it is possible to bypass the method overload resolution process
|
||||
and explicitly specify the method to be invoked:</li>
|
||||
|
||||
<br>
|
||||
<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. (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> s = new java.lang.String("foo")</tt>
|
||||
<br><tt> s = s + "";
|
||||
|
||||
</tt>// s is now a JS string
|
||||
<br><tt> 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.
|
||||
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. Now, when JavaScript
|
||||
calls into Java, any Java exceptions are converted to JS exceptions which
|
||||
can be caught using JS try-catch statements. 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. 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. 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.
|
||||
Informally, the method with Java parameter types that most 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). 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. If necessary, it is now possible to
|
||||
bypass the method overload resolution process and explicitly specify the method
|
||||
to be invoked:</li>
|
||||
<BR>
|
||||
<br>
|
||||
|
||||
<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. (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> s = new java.lang.String("foo")</tt> // s contains
|
||||
a Java string<br>
|
||||
<tt> s = s + "";
|
||||
</tt>// s is now a JS string <br>
|
||||
<tt> 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.
|
||||
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. Now, when JavaScript
|
||||
calls into Java, any Java exceptions are converted to JS exceptions which
|
||||
can be caught using JS try-catch statements. 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> (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. (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> (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. (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. 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.
|
||||
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>
|
||||
<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>. (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.) For
|
||||
example, the following is now possible:</li>
|
||||
<p>Note that this change has caused the string representation of JavaArray objects
|
||||
to change. Previously, the JavaArray toString() method always printed
|
||||
"<tt><font size=+1>[object JavaArray]"</font></tt> for all <tt>JavaArray</tt>'s.
|
||||
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>
|
||||
|
||||
<li> A one-character string is now an acceptable match for an argument to a
|
||||
Java method of type <tt>char</tt>. (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.) For example, the following is now
|
||||
possible:</li>
|
||||
|
||||
<p><tt><font size=-1>c = new java.lang.Character("F")</font></tt>
|
||||
<br>
|
||||
<p><tt><font size=-1>c = new java.lang.Character("F")</font></tt><BR>
|
||||
<br>
|
||||
|
||||
<li>
|
||||
A JavaClass object is now an acceptable match for an argument to a Java
|
||||
method of type <i>java.lang.Class</i>. For example, you can now write:</li>
|
||||
@ -156,154 +157,152 @@ method of type <i>java.lang.Class</i>. 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> </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>
|
||||
|
||||
</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. The
|
||||
variants of LiveConnect that appeared in Navigator versions 3.x and 4.x
|
||||
all behave much the same, modulo bugs. For brevity we refer to this
|
||||
classic version of LiveConnect as "LC1" (LiveConnect version 1) and this
|
||||
most recent release as "LC3". (There was an intermediate LiveConnect
|
||||
release known as "LC2" in 7/98, but it was not used in any products.
|
||||
"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. The variants of LiveConnect that appeared in Navigator versions
|
||||
3.x and 4.x all behave much the same, modulo bugs. For brevity we refer
|
||||
to this classic version of LiveConnect as "LC1" (LiveConnect version 1) and this
|
||||
most recent release as "LC3". 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>.
|
||||
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. 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>.
|
||||
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. The use of weak references
|
||||
is not portable, however. It is not part of the JNI or the JDK and
|
||||
it is not provided on all JVMs. The JDK1.2 release will include standard
|
||||
support for weak references.
|
||||
<br>
|
||||
<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. LC1 and LC2 resolved these ambiguities
|
||||
in a simplistic manner, by simply invoking whatever method was enumerated
|
||||
first by the JVM. The enumeration order of reflected methods using
|
||||
<i>java.lang.reflect</i>
|
||||
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.
|
||||
Hence, the Java method chosen when there is more than one compatible method
|
||||
may vary depending on the JVM. 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. Informally,
|
||||
the method with Java parameter types that most closely match the
|
||||
JavaScript types is chosen. You can read all the gorey details in
|
||||
the <a href="http://www.mozilla.org/js/liveconnect/lc3_method_overloading.html">spec</a>.
|
||||
<br>
|
||||
<li>
|
||||
There are several minor changes in error handling to make LiveConnect more
|
||||
conformant to ECMAScript. These include, for example, making any
|
||||
attempt to delete JavaObject, JavaClass or JavaPackage properties fail
|
||||
silently, rather than causing an error. Also, some error messages
|
||||
have been changed to be more informative. 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>.
|
||||
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. 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>.
|
||||
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. The use of weak references is not portable,
|
||||
however. It is not part of the JNI or JDK 1.1 and it is not provided
|
||||
on all JVMs. The JDK1.2 release includes standard support for weak references.]<BR>
|
||||
<br>
|
||||
|
||||
<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. LC1 and LC2 resolved these ambiguities
|
||||
in a simplistic manner, by simply invoking whatever method was enumerated
|
||||
first by the JVM. The enumeration order of reflected methods using <i>java.lang.reflect</i>
|
||||
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. Hence,
|
||||
the Java method chosen when there is more than one compatible method may vary
|
||||
depending on the JVM. 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. Informally,
|
||||
the method with Java parameter types that most closely match the JavaScript
|
||||
types is chosen. 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>
|
||||
|
||||
<li> There are several minor changes in error handling to make LiveConnect more
|
||||
conformant to ECMAScript. These include, for example, making any attempt
|
||||
to delete JavaObject, JavaClass or JavaPackage properties fail silently, rather
|
||||
than causing an error. Also, some error messages have been changed to
|
||||
be more informative. 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.
|
||||
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. In this way, the behavior of
|
||||
the JS '==' and '===' operators are preserved. Unfortunately, the
|
||||
hash table may grow quite large (objects are only removed from the hash
|
||||
table when finalized). 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. 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(). 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). However, the API is not fully implemented.
|
||||
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.
|
||||
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. In this way, the behavior
|
||||
of the JS '==' and '===' operators are preserved. Unfortunately, the
|
||||
hash table may grow quite large (objects are only removed from the hash table
|
||||
when finalized). 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. 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(). 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). However, the API is not fully implemented.
|
||||
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. 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. Another
|
||||
downside of operating without these classes is that Java error messages
|
||||
will not include a Java stack trace, when one is available. 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. 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. (NSPR implements a portable threading library,
|
||||
among other things. 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>.)
|
||||
Next, you must define <tt>JS_THREADSAFE</tt> when building LiveConnect,
|
||||
either on the command-line (gmake/nmake) or in a universal header file.
|
||||
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. 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.
|
||||
Another downside of operating without these classes is that Java error messages
|
||||
will not include a Java stack trace, when one is available. 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. 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. (NSPR implements a portable threading library,
|
||||
among other things. 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>.)
|
||||
Next, you must define <tt>JS_THREADSAFE</tt> when building LiveConnect, either
|
||||
on the command-line (gmake/nmake) or in a universal header file. 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>. This is used to establish paths for header
|
||||
file inclusion, linking and execution. If you are not using Sun's
|
||||
JVM, the project files may require manual tweaking to set these paths correctly.</li>
|
||||
|
||||
<li>
|
||||
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>. This is
|
||||
used to establish paths for header file inclusion, linking and execution.
|
||||
If you are not using Sun's JVM, the project files may require manual tweaking
|
||||
to set these paths correctly.</li>
|
||||
|
||||
<li>
|
||||
Use MSDEV5.0 with the <tt>LiveConnectShell.dsw</tt> project file.
|
||||
<font color="#993300">NOTE: makefile.win is an nmake file used only for
|
||||
building the JS-engine in the Mozilla browser. 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.
|
||||
<font color="#993300">NOTE: makefile.win is an nmake file used only for
|
||||
building the JS-engine in the Mozilla browser. 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. (See above)<br>
|
||||
<li> Update your JVM's <tt>CLASSPATH</tt> to point to the <tt>js\src\liveconnect\classes</tt>
|
||||
subdirectory. (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>. </font><font color="#990000">NOTE:
|
||||
Do not attempt to use <tt>Makefile</tt> to build. 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. The configuration file specifies the JVM headers/libraries
|
||||
used and allows for customization of command-line options. 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. (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>. </font><font color="#990000">NOTE:
|
||||
Do not attempt to use <tt>Makefile</tt> to build. 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. The configuration file specifies the JVM headers/libraries
|
||||
used and allows for customization of command-line options. 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. (See above)</li>
|
||||
</ul>
|
||||
</ul>
|
||||
|
||||
<h2>
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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:
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user