JavaScript Native Interface (JSNI)
Mix handwritten JavaScript into your Java classes to access low-level browser functionality.
Contents
- Overview Mix handwritten JavaScript into your Java classes to access low-level browser functionality.
- Writing Native JavaScript Methods Declare a
native
method and write the body into a specially formatted comment. - Accessing Java Methods and Fields from JavaScript Handwritten JavaScript can invoke methods and access fields on Java objects.
- Sharing objects between Java source and JavaScript How Java objects appear to JavaScript code and vice-versa.
- Exceptions and JSNI How JavaScript exceptions interact with Java exceptions and vice-versa.
Overview
Writing JSNI methods is a powerful technique, but should be used sparingly. JSNI code is less portable across browsers, more likely to leak memory, less amenable to Java tools, and hard for the compiler to optimize.
We think of JSNI as the web equivalent of inline assembly code. You can:
- Implement a Java method directly in JavaScript
- Wrap type-safe Java method signatures around existing JavaScript
- Call from JavaScript into Java code and vice-versa
- Throw exceptions across Java/JavaScript boundaries
- Read and write Java fields from JavaScript
- Use hosted mode to debug both Java source (with a Java debugger) and JavaScript (with a script debugger, only in Windows right now)
Writing Native JavaScript Methods
native
and contain JavaScript code in a specially formatted comment block between the end of the parameter list and the trailing semicolon. A JSNI comment block begins with the exact token /*-{
and ends with the exact token }-*/
. JSNI methods are be called just like any normal Java method. They can be static or instance methods. Example
public static native void alert(String msg) /*-{
$wnd.alert(msg);
}-*/;
Accessing Java Methods and Fields from JavaScript
Invoking Java methods from JavaScript
Calling Java methods from JavaScript is somewhat similar to calling Java methods from C code in JNI. In particular, JSNI borrows the JNI mangled method signature approach to distinguish among overloaded methods.JavaScript calls into Java methods are of the form
[instance-expr.]@class-name::method-name(param-signature)(arguments)where
- [instance-expr.]
- must be present when calling an instance method and must be absent when calling a static method
- class-name
- is the fully-qualified name of the class in which the method is declared (or a subclass thereof)
- param-signature
- is the internal Java method signature as specified here but without the trailing signature of the method return type since it isn't needed to choose the overload
- arguments
- the actual argument list to pass to the called method
Accessing Java fields from JavaScript
Static and instance fields can be accessed from handwritten JavaScript. Field references are of the form[instance-expr.]@class-name::field-name
Example
public class JSNIExample {
String myInstanceField;
static int myStaticField;
void instanceFoo(String s) {
// use s
}
static void staticFoo(String s) {
// use s
}
public native void bar(JSNIExample x, String s) /*-{
// Call instance method instanceFoo() on this
this.@com.google.gwt.examples.JSNIExample::instanceFoo(Ljava/lang/String;)(s);
// Call instance method instanceFoo() on x
x.@com.google.gwt.examples.JSNIExample::instanceFoo(Ljava/lang/String;)(s);
// Call static method staticFoo()
@com.google.gwt.examples.JSNIExample::staticFoo(Ljava/lang/String;)(s);
// Read instance field on this
var val = this.@com.google.gwt.examples.JSNIExample::myInstanceField;
// Write instance field on x
x.@com.google.gwt.examples.JSNIExample::myInstanceField = val + " and stuff";
// Read static field (no qualifier)
@com.google.gwt.examples.JSNIExample::myStaticField = val + " and stuff";
}-*/;
}
Sharing objects between Java source and JavaScript
Passing Java values into JavaScript
Incoming Java type | How it appears to JavaScript code |
---|---|
a JavaScript numeric value, as in var x = 42; | |
String | a JavaScript string, as in var s = "my string"; |
boolean | a JavaScript boolean value, as in var b = true; |
a JavaScriptObject that must have originated from JavaScript code, typically as the return value of some other JSNI method | |
an opaque value that can only be passed back into Java code | |
Object | an opaque value accessible through special syntax |
Passing JavaScript values into Java code
Outgoing Java type | What must be passed |
---|---|
a JavaScript numeric value, as in return 19; | |
String | a JavaScript string, as in return "boo"; |
boolean | a JavaScript boolean value, as in return false; |
a native JavaScript object, as in return document.createElement("div") | |
Object (including arrays) | a Java Object of the correct type that must have originated in Java code; Java objects cannot be constructed from "thin air" in JavaScript |
Important Notes
- A Java numeric primitive is one of
byte
,short
,char
,int
,long
,float
, ordouble
. You must ensure the value is appropriate for the declared type. Returning3.7
when the declared type isint
will cause unpredictable behavior. - Java
null
and JavaScriptnull
are identical and always legal values for any non-primitive Java type. JavaScriptundefined
is not identical tonull
; never returnundefined
from a JSNI method or unpredictable behavior will occur. - Violating any of these marshaling rules in hosted mode will generate a
com.google.gwt.dev.shell.HostedModeException
detailing the problem. This exception is not translatable and never thrown in web mode. - JavaScriptObject is a magical type that gets special treatment from the GWT compiler and hosted browser. Its purpose is to provide an opaque representation of native JavaScript objects to Java code.
Exceptions and JSNI
An exception that originates in a JSNI method and escapes into Java code can be caught as a JavaScriptException. Relying on this behavior is discouraged because JavaScript exceptions are not usefully typed. The recommended practice is to handle JavaScript exceptions in JavaScript code and Java exceptions in Java code.
When a JSNI method invokes a Java method, a more complex call chain results. An exception thrown from the inner Java method can safely pass through the sandwiched JSNI method back to the original Java call site, retaining type fidelity. It can be caught as expected. For example,
- Java method
foo()
calls JSNI methodbar()
- JavaScript method
bar()
calls Java methodbaz()
- Java method
baz()
throws an exception
baz()
will propagate through bar()
and can be caught in foo()
.
0 comments:
Post a Comment