1.1 --- a/boot/src/main/java/net/java/html/js/JavaScriptBody.java Tue Jun 25 13:19:12 2013 +0200
1.2 +++ b/boot/src/main/java/net/java/html/js/JavaScriptBody.java Tue Jun 25 15:34:37 2013 +0200
1.3 @@ -51,4 +51,17 @@
1.4 * <code>this</code>
1.5 */
1.6 public String body();
1.7 +
1.8 + /** Should a special syntax for calling back into Java object be turned on?
1.9 + * The syntax begins with <b>{@code @}</b> followed by fully qualified
1.10 + * package name of the class. Now followed by <b>::</b> and a method in
1.11 + * the class followed by its parameters enclosed inside <b>(...)</b>.
1.12 + * This is the syntax one can use to call <code>run()</code>
1.13 + * method of {@link Runnable}:
1.14 + * <pre>r.@java.lang.Runnable::run()()</pre>.
1.15 + *
1.16 + * @return true, if the script should be scanned for special callback
1.17 + * syntax
1.18 + */
1.19 + public boolean javacall() default false;
1.20 }
2.1 --- a/boot/src/main/java/org/apidesign/html/boot/impl/FnUtils.java Tue Jun 25 13:19:12 2013 +0200
2.2 +++ b/boot/src/main/java/org/apidesign/html/boot/impl/FnUtils.java Tue Jun 25 15:34:37 2013 +0200
2.3 @@ -20,11 +20,15 @@
2.4 */
2.5 package org.apidesign.html.boot.impl;
2.6
2.7 +import java.lang.reflect.Method;
2.8 import java.net.URL;
2.9 import java.util.ArrayList;
2.10 import java.util.Collections;
2.11 import java.util.Enumeration;
2.12 import java.util.List;
2.13 +import java.util.logging.Level;
2.14 +import java.util.logging.Logger;
2.15 +import java.util.regex.Pattern;
2.16 import org.apidesign.html.boot.spi.Fn;
2.17
2.18 /**
2.19 @@ -66,5 +70,56 @@
2.20 }
2.21 };
2.22 }
2.23 +
2.24 + static String callback(String body, ClassLoader loader) {
2.25 + try {
2.26 + return callbackImpl(body, loader);
2.27 + } catch (ClassNotFoundException ex) {
2.28 + throw new IllegalStateException("Can't parse " + body, ex);
2.29 + } catch (NoSuchMethodException ex) {
2.30 + throw new IllegalStateException("Can't parse " + body, ex);
2.31 + }
2.32 + }
2.33 +
2.34 + private static String callbackImpl(String body, ClassLoader loader)
2.35 + throws ClassNotFoundException, NoSuchMethodException {
2.36 + StringBuilder sb = new StringBuilder();
2.37 + int pos = 0;
2.38 + for (;;) {
2.39 + int next = body.indexOf(".@", pos);
2.40 + if (next == -1) {
2.41 + sb.append(body.substring(pos));
2.42 + return sb.toString();
2.43 + }
2.44 + sb.append(body.substring(pos, next));
2.45 +
2.46 + int sigBeg = body.indexOf('(', next);
2.47 + int sigEnd = body.indexOf(')', sigBeg);
2.48 +
2.49 + int colon4 = body.indexOf("::", next);
2.50 +
2.51 + if (sigBeg == -1 || sigEnd == -1 || colon4 == -1) {
2.52 + throw new IllegalStateException("Malformed body " + body);
2.53 + }
2.54 +
2.55 + String fqn = body.substring(next + 2, colon4);
2.56 + String method = body.substring(colon4 + 2, sigBeg);
2.57 + String params = body.substring(sigBeg + 1, sigEnd);
2.58 +
2.59 + Class<?> clazz = Class.forName(fqn, false, loader);
2.60 + Method m = clazz.getMethod(method);
2.61 +
2.62 + sb.append("['").append(m.getName()).append("(");
2.63 + String sep = "";
2.64 + for (Class<?> pt : m.getParameterTypes()) {
2.65 + sb.append(sep).append(pt.getName());
2.66 + sep = ",";
2.67 + }
2.68 + sb.append(")']");
2.69 +
2.70 + pos = sigEnd + 1;
2.71 + }
2.72 + }
2.73 +
2.74
2.75 }
3.1 --- a/boot/src/main/java/org/apidesign/html/boot/impl/JsClassLoader.java Tue Jun 25 13:19:12 2013 +0200
3.2 +++ b/boot/src/main/java/org/apidesign/html/boot/impl/JsClassLoader.java Tue Jun 25 15:34:37 2013 +0200
3.3 @@ -27,6 +27,8 @@
3.4 import java.util.ArrayList;
3.5 import java.util.Enumeration;
3.6 import java.util.List;
3.7 +import java.util.logging.Level;
3.8 +import java.util.logging.Logger;
3.9 import org.objectweb.asm.AnnotationVisitor;
3.10 import org.objectweb.asm.ClassReader;
3.11 import org.objectweb.asm.ClassVisitor;
3.12 @@ -125,7 +127,7 @@
3.13 protected abstract Fn defineFn(String code, String... names);
3.14
3.15
3.16 - private static final class FindInClass extends ClassVisitor {
3.17 + private final class FindInClass extends ClassVisitor {
3.18 private String name;
3.19 private int found;
3.20
3.21 @@ -352,6 +354,7 @@
3.22 private final class FindInAnno extends AnnotationVisitor {
3.23 private List<String> args = new ArrayList<String>();
3.24 private String body;
3.25 + private boolean javacall = false;
3.26
3.27 public FindInAnno() {
3.28 super(Opcodes.ASM4);
3.29 @@ -363,6 +366,10 @@
3.30 args.add((String) value);
3.31 return;
3.32 }
3.33 + if (name.equals("javacall")) { // NOI18N
3.34 + javacall = (Boolean)value;
3.35 + return;
3.36 + }
3.37 assert name.equals("body");
3.38 body = (String) value;
3.39 }
3.40 @@ -375,7 +382,10 @@
3.41 @Override
3.42 public void visitEnd() {
3.43 if (body != null) {
3.44 - generateJSBody(args, body);
3.45 + generateJSBody(args, javacall ?
3.46 + FnUtils.callback(body, JsClassLoader.this) :
3.47 + body
3.48 + );
3.49 }
3.50 }
3.51 }
4.1 --- a/boot/src/test/java/org/apidesign/html/boot/impl/JsClassLoaderBase.java Tue Jun 25 13:19:12 2013 +0200
4.2 +++ b/boot/src/test/java/org/apidesign/html/boot/impl/JsClassLoaderBase.java Tue Jun 25 15:34:37 2013 +0200
4.3 @@ -106,4 +106,21 @@
4.4 assertEquals(st.invoke(null), Boolean.TRUE, "Can return boolean");
4.5 }
4.6
4.7 + @Test public void callback() throws Throwable {
4.8 + class R implements Runnable {
4.9 + int cnt;
4.10 +
4.11 + @Override
4.12 + public void run() {
4.13 + cnt++;
4.14 + }
4.15 + }
4.16 + R r = new R();
4.17 +
4.18 + Method inc = methodClass.getMethod("callback", Runnable.class);
4.19 + inc.invoke(null, r);
4.20 +
4.21 + assertEquals(r.cnt, 1, "Callback happened");
4.22 + }
4.23 +
4.24 }
4.25 \ No newline at end of file
5.1 --- a/boot/src/test/java/org/apidesign/html/boot/impl/JsMethods.java Tue Jun 25 13:19:12 2013 +0200
5.2 +++ b/boot/src/test/java/org/apidesign/html/boot/impl/JsMethods.java Tue Jun 25 15:34:37 2013 +0200
5.3 @@ -51,4 +51,7 @@
5.4 public static boolean truth() {
5.5 return false;
5.6 }
5.7 +
5.8 + @JavaScriptBody(args = { "r" }, javacall=true, body = "r.@java.lang.Runnable::run()()")
5.9 + public static native void callback(Runnable r);
5.10 }