diff --git a/teavm-jso/src/main/java/org/teavm/jso/JSBody.java b/teavm-jso/src/main/java/org/teavm/jso/JSBody.java index 3a893979d..fae89add3 100644 --- a/teavm-jso/src/main/java/org/teavm/jso/JSBody.java +++ b/teavm-jso/src/main/java/org/teavm/jso/JSBody.java @@ -23,10 +23,102 @@ import java.lang.annotation.Target; /** *
Indicates that method is to have native JavaScript implementation. * Method only can take and return primitive values and {@link JSObject}s. - * JSBody script can't call Java methods, but you can pass callbacks wrapped into {@link JSFunctor}. * Note that unless method is static, it must belong to class that implements {@link JSObject}. * If applied to non-native method, original Java body will be overwritten by JavaScript.
* + *Example:
+ * + *+ * {@literal @}JSBody(params = { "message" }, script = "window.alert(message);") + * public static native void alert(String message); + *+ * + *
The motivation for params
field is the following: Java can avoid inclusion of parameter names
+ * into bytecode (for example, you can compile with no debug information). In order the JSO implementation
+ * with no access to original source code could work properly, JSO forces developer to specify parameter
+ * names explicitly.
A method marked with JSBody annotation is restricted to take parameters of allowed types. + * A type is allowed if it is either:
+ * + *boolean
, byte
, short
, char
,
+ * int
, long
, float
or double
;java.lang.String
class;Java primitives are converted to corresponding JavaScript primitives, except for char
,
+ * which does not have corresponding JavaScript representation. JSO implementation converts Java chars to JavaScript
+ * numbers, and expects a JavaScript number when converting to a Java character.
Java arrays are converted to JavaScript arrays and vice versa. Arrays are passed by copy.
+ * + *java.lang.String
objects are converted to JavaScript string.
Overlay types are passed as is.
+ * + * + *Sometimes JavaScript functions expect you to pass a function as a parameter value. JSO allows you to + * pass special form of overlay objects as functions. These overlay objects must be interfaces with exactly one + * parameter and marked with {@link JSFunctor} annotation. Example:
+ * + *+ * {@literal @}JSFunctor + * interface TimerHandler extends JSObject { + * void onTimer(); + * } + * + * {@literal @}JSBody(params = { "handler", "delay" }, script = "return window.setTimeout(handler, delay);") + * public static native int setTimeout(TimerHandler handler, int delay); + *+ * + * + *
You can call Java methods from JSBody script. You should use the following notation: + * + *
javaMethods.get('method reference').invoke([instance, ] param1 [, param2 ...]);+ * + *
The method reference has the following format:
+ * + *+ * (PackageName .)* ClassName . MethodName MethodDescriptor + *+ * + *
where
+ * + *+ * PackageName = Identifier + * ClassName = Identifier + * MethodName = Identifier + * MethodDescriptor = ( TypeDescriptor ) V) + * | ( TypeDescriptor ) TypeDescriptor + * TypeDescriptor = Z | B | C | S | I | J | F | D + * | L QualifiedClassName ; | [ TypeDescriptor. + *+ * + *
that is similar to method + * descriptors augmented with class and method names.
+ * + *For example,
+ * + *+ * {@literal @}JSBody(params = { "message" }, script = "javaMethods.get('org.teavm.jso.browser.Window" + * + ".alert(Ljava/lang/String;)V').invoke(message);") + * public static native void alertCaller(String message); + *+ * + *
Note that get
method must take string constant. Dynamic resolution of Java methods may
+ * not work on some platforms.
JavaScript implementation.
+ *JavaScript code.
*/ String script(); } diff --git a/teavm-jso/src/main/java/org/teavm/jso/JSIndexer.java b/teavm-jso/src/main/java/org/teavm/jso/JSIndexer.java index 7f07f22ec..fd94230b7 100644 --- a/teavm-jso/src/main/java/org/teavm/jso/JSIndexer.java +++ b/teavm-jso/src/main/java/org/teavm/jso/JSIndexer.java @@ -26,16 +26,16 @@ import java.lang.annotation.Target; *Getter indexer is a method that returns value and takes exactly one parameter. In * this case annotation is equivalent to this:
* - *{@code - * @JSBody(params = "index", script = "return this[index];") - * }+ *
+ * {@literal @}JSBody(params = "index", script = "return this[index];") + ** *
Setter indexer is a method that takes two parameter and does not return any value. * Ins this case annotation is equivalent to the following:
* - *{@code - * @JSBody(params = { "index", "value" }, script = "this[index] = value;") - * }+ *
+ * {@literal @}JSBody(params = { "index", "value" }, script = "this[index] = value;") + ** * @author Alexey Andreev */ diff --git a/teavm-jso/src/main/java/org/teavm/jso/JSMethod.java b/teavm-jso/src/main/java/org/teavm/jso/JSMethod.java index 9029492ec..de2abc5af 100644 --- a/teavm-jso/src/main/java/org/teavm/jso/JSMethod.java +++ b/teavm-jso/src/main/java/org/teavm/jso/JSMethod.java @@ -23,13 +23,16 @@ import java.lang.annotation.Target; /** *
Marks abstract member method as a JavaScript method. This is equivalent to the following:
* - *{@code - * @JSBody(params = ..., script = "return new this.methodName(...);") - * }+ *
+ * {@literal @}JSBody(params = ..., script = "return new this.methodName(...);") + ** *
where methodName
is method's name by default or a name, directly specified by
* this annotation.
JSMethod can be avoided. This means that if you define abstract method on overlay class or interface, + * and don't specify any annotations, this method is treated as marked by JSMethod.
+ * * @author Alexey Andreev */ @Retention(RetentionPolicy.RUNTIME) diff --git a/teavm-jso/src/main/java/org/teavm/jso/JSObject.java b/teavm-jso/src/main/java/org/teavm/jso/JSObject.java index 66d0841f4..a3f5d74b6 100644 --- a/teavm-jso/src/main/java/org/teavm/jso/JSObject.java +++ b/teavm-jso/src/main/java/org/teavm/jso/JSObject.java @@ -16,7 +16,46 @@ package org.teavm.jso; /** + *The base type for all overlay types. Overlay types are Java types that represent JavaScript object, + * and therefore can be passed to and from JavaScript code.
* + *An overlay type is an abstract class or an interface that extends/implements JSObject. An overlay type + * has following restrictions:
+ * + *To simplify creation of overlay objects, you can use shortcut annotations instead of {@link JSBody}: + * {@link JSMethod}, {@link JSProperty} and {@link JSIndexer}.
+ * + *Example:
+ * + *+ * public abstract class Int32Array implements JSObject { + * {@literal @}JSBody(params = {}, script = "return this.length;") + * public native int getLength(); + * + * {@literal @}JSIndexer + * public abstract int get(int index); + * + * {@literal @}JSIndexer + * public abstract void set(int index, int value); + * + * {@literal @}JSBody(params = "length", script = "return new Int32Array(length);") + * public static native ArrayBuffer create(int length); + * + * {@literal @}JSBody(params = "buffer", script = "return new Int32Array(buffer);") + * public static native ArrayBuffer create(ArrayBuffer buffer); + * + * {@literal @}JSBody(params = { "buffer", "offset", "length" }, + * script = "return new Int32Array(buffer, offset, length);") + * public static native ArrayBuffer create(ArrayBuffer buffer, int offset, int length); + * } + *+ * + * @see JSBody * @author Alexey Andreev */ public interface JSObject { diff --git a/teavm-jso/src/main/java/org/teavm/jso/JSProperty.java b/teavm-jso/src/main/java/org/teavm/jso/JSProperty.java index 7cdfc6b24..90a4d74d3 100644 --- a/teavm-jso/src/main/java/org/teavm/jso/JSProperty.java +++ b/teavm-jso/src/main/java/org/teavm/jso/JSProperty.java @@ -27,17 +27,17 @@ import java.lang.annotation.Target; * (or
is
in case of boolean getter). It must not take any parameters and must return a value.
* For getter annotation is equivalent to the following:
*
- * {@code - * @JSBody(params = {}, script = "return this.propertyName;") - * }+ *
+ * {@literal @}JSBody(params = {}, script = "return this.propertyName;") + ** *
Setter's name must conform the Java Beans specification, i.e. start with set
prefix
* It must take exactly one parameter and must not return a value.
* For setter annotation is equivalent to the following:
{@code - * @JSBody(params = "value", script = "this.propertyName = value;") - * }+ *
+ * {@literal @}JSBody(params = "value", script = "this.propertyName = value;") + ** *
By default propertyName
is calculated from method's name according to Java Beans specification,
* otherwise the name specified by annotation is taken.
JSO is a specification that describes how Java platform can interact with JavaScript code. + * There are two parts of this specification: JSO core, that defines mapping between Java and JavaScript + * objects and JSO APIs, that define Java wrappers around various JavaScript and HTML5 APIs. + * The latter part is simply application of the former one. JSO implementor must implement only the first part, + * and it may ignore the second part, since it should work properly this way. However, it may implement + * some of the APIs itself in some cases, for example to improve performance.
+ * + *The first part of JSO is directly in this package. All subpackages declare the second part of JSO.
+ * + *JSO does not do anything itself. It is only a set of interfaces that define interaction. + * To use JSO in your application, you should include one of its implementations, that may exist for + * different platforms, such as JVM, RoboVM, Android, TeaVM or bck2brwsr.
+ * + * + *The easiest way to invoke JavaScript code from Java is to define native method marked with the + * {@link org.teavm.jso.JSBody} annotation that contains the JavaScript code.
+ * + *Example:
+ * + *+ * {@literal @}JSBody(params = { "message" }, script = "window.alert(message);") + * public static native void alert(String message); + *+ * + * + *
Often you need to pass complex values between Java and JavaScript. Primitives are usually insufficient for + * this purposed. JSO comes with concept of overlay types, that are usable from both Java and JavaScript. + * For detailed description, see {@link org.teavm.jso.JSObject} interface.
+ * + *When wrapping JavaScript APIs in Java classes, you usually write boilerplate {@link org.teavm.jso.JSBody} like + * this:
+ * + *+ * {@literal @}JSBody(params = "newChild", script = "return this.appendChild(newChild);") + * Node appendChild(Node newChild); + *+ * + *
JSO offers shortcut annotations that help to avoid such boilerplate. They are: {@link org.teavm.jso.JSMethod}, + * {@link org.teavm.jso.JSProperty}, {@link org.teavm.jso.JSIndexer}. + * + */ +package org.teavm.jso;