Bringing up-to-date and adding a TCK test to check behavior of asynchronous callback
1.1 --- a/boot/src/main/java/net/java/html/js/JavaScriptBody.java Tue Mar 04 21:57:50 2014 +0100
1.2 +++ b/boot/src/main/java/net/java/html/js/JavaScriptBody.java Wed Mar 05 00:11:49 2014 +0100
1.3 @@ -93,4 +93,6 @@
1.4 * syntax
1.5 */
1.6 public boolean javacall() default false;
1.7 +
1.8 + public boolean wait4js() default true;
1.9 }
2.1 --- a/boot/src/main/java/org/apidesign/html/boot/spi/Fn.java Tue Mar 04 21:57:50 2014 +0100
2.2 +++ b/boot/src/main/java/org/apidesign/html/boot/spi/Fn.java Wed Mar 05 00:11:49 2014 +0100
2.3 @@ -52,6 +52,8 @@
2.4 import java.util.Map;
2.5 import java.util.Set;
2.6 import java.util.concurrent.Executor;
2.7 +import java.util.logging.Level;
2.8 +import java.util.logging.Logger;
2.9 import net.java.html.js.JavaScriptBody;
2.10 import org.netbeans.html.boot.impl.FnContext;
2.11
2.12 @@ -140,6 +142,17 @@
2.13 return new Fn(fn.presenter()) {
2.14 @Override
2.15 public Object invoke(Object thiz, Object... args) throws Exception {
2.16 + loadResource();
2.17 + return fn.invoke(thiz, args);
2.18 + }
2.19 +
2.20 + @Override
2.21 + public void invokeLater(Object thiz, Object... args) throws Exception {
2.22 + loadResource();
2.23 + fn.invokeLater(thiz, args);
2.24 + }
2.25 +
2.26 + private void loadResource() throws Exception {
2.27 Presenter p = presenter();
2.28 if (p == null) {
2.29 p = FnContext.currentPresenter(false);
2.30 @@ -160,10 +173,10 @@
2.31 }
2.32 }
2.33 }
2.34 - return fn.invoke(thiz, args);
2.35 }
2.36 };
2.37 }
2.38 +
2.39
2.40 /** The currently active presenter.
2.41 *
2.42 @@ -190,6 +203,10 @@
2.43 return FnContext.activate(p);
2.44 }
2.45
2.46 + public void invokeLater(Object thiz, Object... args) throws Exception {
2.47 + invoke(this, args);
2.48 + }
2.49 +
2.50 /** Invokes the defined function with specified <code>this</code> and
2.51 * appropriate arguments.
2.52 *
3.1 --- a/boot/src/main/java/org/netbeans/html/boot/impl/FnUtils.java Tue Mar 04 21:57:50 2014 +0100
3.2 +++ b/boot/src/main/java/org/netbeans/html/boot/impl/FnUtils.java Wed Mar 05 00:11:49 2014 +0100
3.3 @@ -467,31 +467,38 @@
3.4 FindInMethod.super.visitInsn(Opcodes.AASTORE);
3.5 }
3.6
3.7 - super.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
3.8 - "org/apidesign/html/boot/spi/Fn", "invoke", "(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;"
3.9 - );
3.10 - switch (sv.returnType.getSort()) {
3.11 - case Type.VOID:
3.12 - super.visitInsn(Opcodes.RETURN);
3.13 - break;
3.14 - case Type.ARRAY:
3.15 - case Type.OBJECT:
3.16 - super.visitTypeInsn(Opcodes.CHECKCAST, sv.returnType.getInternalName());
3.17 - super.visitInsn(Opcodes.ARETURN);
3.18 - break;
3.19 - case Type.BOOLEAN:
3.20 - super.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/Boolean");
3.21 - super.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
3.22 - "java/lang/Boolean", "booleanValue", "()Z"
3.23 - );
3.24 - super.visitInsn(Opcodes.IRETURN);
3.25 - break;
3.26 - default:
3.27 - super.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/Number");
3.28 - super.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
3.29 - "java/lang/Number", sv.returnType.getClassName() + "Value", "()" + sv.returnType.getDescriptor()
3.30 - );
3.31 - super.visitInsn(sv.returnType.getOpcode(Opcodes.IRETURN));
3.32 + if (fia.wait4js) {
3.33 + super.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
3.34 + "org/apidesign/html/boot/spi/Fn", "invoke", "(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;"
3.35 + );
3.36 + switch (sv.returnType.getSort()) {
3.37 + case Type.VOID:
3.38 + super.visitInsn(Opcodes.RETURN);
3.39 + break;
3.40 + case Type.ARRAY:
3.41 + case Type.OBJECT:
3.42 + super.visitTypeInsn(Opcodes.CHECKCAST, sv.returnType.getInternalName());
3.43 + super.visitInsn(Opcodes.ARETURN);
3.44 + break;
3.45 + case Type.BOOLEAN:
3.46 + super.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/Boolean");
3.47 + super.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
3.48 + "java/lang/Boolean", "booleanValue", "()Z"
3.49 + );
3.50 + super.visitInsn(Opcodes.IRETURN);
3.51 + break;
3.52 + default:
3.53 + super.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/Number");
3.54 + super.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
3.55 + "java/lang/Number", sv.returnType.getClassName() + "Value", "()" + sv.returnType.getDescriptor()
3.56 + );
3.57 + super.visitInsn(sv.returnType.getOpcode(Opcodes.IRETURN));
3.58 + }
3.59 + } else {
3.60 + super.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
3.61 + "org/apidesign/html/boot/spi/Fn", "invokeLater", "(Ljava/lang/Object;[Ljava/lang/Object;)V"
3.62 + );
3.63 + super.visitInsn(Opcodes.RETURN);
3.64 }
3.65 if (hasCode) {
3.66 super.visitLabel(noPresenter);
3.67 @@ -522,6 +529,7 @@
3.68 List<String> args = new ArrayList<String>();
3.69 String body;
3.70 boolean javacall = false;
3.71 + boolean wait4js = true;
3.72
3.73 public FindInAnno() {
3.74 super(Opcodes.ASM4);
3.75 @@ -537,6 +545,10 @@
3.76 javacall = (Boolean) value;
3.77 return;
3.78 }
3.79 + if (name.equals("wait4js")) { // NOI18N
3.80 + wait4js = (Boolean) value;
3.81 + return;
3.82 + }
3.83 assert name.equals("body");
3.84 body = (String) value;
3.85 }
4.1 --- a/boot/src/main/java/org/netbeans/html/boot/impl/JavaScriptProcesor.java Tue Mar 04 21:57:50 2014 +0100
4.2 +++ b/boot/src/main/java/org/netbeans/html/boot/impl/JavaScriptProcesor.java Wed Mar 05 00:11:49 2014 +0100
4.3 @@ -129,6 +129,9 @@
4.4 if (params.size() != arr.length) {
4.5 msg.printMessage(Diagnostic.Kind.ERROR, "Number of args arguments does not match real arguments!", e);
4.6 }
4.7 + if (!jsb.wait4js() && ee.getReturnType().getKind() != TypeKind.VOID) {
4.8 + msg.printMessage(Diagnostic.Kind.ERROR, "Methods that don't wait for JavaScript to finish must return void!", e);
4.9 + }
4.10 if (!jsb.javacall() && jsb.body().contains(".@")) {
4.11 msg.printMessage(Diagnostic.Kind.WARNING, "Usage of .@ usually requires javacall=true", e);
4.12 }
5.1 --- a/json-tck/src/main/java/net/java/html/js/tests/Bodies.java Tue Mar 04 21:57:50 2014 +0100
5.2 +++ b/json-tck/src/main/java/net/java/html/js/tests/Bodies.java Wed Mar 05 00:11:49 2014 +0100
5.3 @@ -56,6 +56,9 @@
5.4 @JavaScriptBody(args = {"r"}, javacall = true, body = "r.@java.lang.Runnable::run()();")
5.5 static native void callback(Runnable r);
5.6
5.7 + @JavaScriptBody(args = {"r"}, wait4js = false, javacall = true, body = "r.@java.lang.Runnable::run()();")
5.8 + static native void asyncCallback(Runnable r);
5.9 +
5.10 @JavaScriptBody(args = {"c"}, javacall = true, body = "return c.@java.util.concurrent.Callable::call()();")
5.11 static native Object callback(Callable<? extends Object> c);
5.12
6.1 --- a/json-tck/src/main/java/net/java/html/js/tests/JavaScriptBodyTest.java Tue Mar 04 21:57:50 2014 +0100
6.2 +++ b/json-tck/src/main/java/net/java/html/js/tests/JavaScriptBodyTest.java Wed Mar 05 00:11:49 2014 +0100
6.3 @@ -77,6 +77,18 @@
6.4 assert run.cnt == 1 : "Can call even private implementation classes: " + run.cnt;
6.5 }
6.6
6.7 + private R asyncRun;
6.8 + @KOTest public void asyncCallbackToRunnable() throws InterruptedException {
6.9 + if (asyncRun == null) {
6.10 + asyncRun = new R();
6.11 + Bodies.asyncCallback(asyncRun);
6.12 + }
6.13 + if (asyncRun.cnt == 0) {
6.14 + throw new InterruptedException();
6.15 + }
6.16 + assert asyncRun.cnt == 1 : "Even async callback must arrive once: " + asyncRun.cnt;
6.17 + }
6.18 +
6.19 @KOTest public void typeOfCharacter() {
6.20 String charType = Bodies.typeof('a', false);
6.21 assert "number".equals(charType) : "Expecting number type: " + charType;
7.1 --- a/ko4j/src/main/java/org/netbeans/html/ko4j/Knockout.java Tue Mar 04 21:57:50 2014 +0100
7.2 +++ b/ko4j/src/main/java/org/netbeans/html/ko4j/Knockout.java Wed Mar 05 00:11:49 2014 +0100
7.3 @@ -59,7 +59,9 @@
7.4 */
7.5 @JavaScriptResource("knockout-2.2.1.js")
7.6 final class Knockout {
7.7 - @JavaScriptBody(args = { "model", "prop", "oldValue", "newValue" }, body =
7.8 + @JavaScriptBody(args = { "model", "prop", "oldValue", "newValue" },
7.9 + wait4js = false,
7.10 + body =
7.11 "if (model) {\n"
7.12 + " var koProp = model[prop];\n"
7.13 + " if (koProp && koProp['valueHasMutated']) {\n"
7.14 @@ -75,7 +77,9 @@
7.15 Object model, String prop, Object oldValue, Object newValue
7.16 );
7.17
7.18 - @JavaScriptBody(args = { "bindings" }, body = "ko.applyBindings(bindings);\n")
7.19 + @JavaScriptBody(args = { "bindings" }, wait4js = false, body =
7.20 + "ko.applyBindings(bindings);\n"
7.21 + )
7.22 native static void applyBindings(Object bindings);
7.23
7.24 @JavaScriptBody(args = { "cnt" }, body =
7.25 @@ -87,8 +91,9 @@
7.26
7.27 @JavaScriptBody(
7.28 javacall = true,
7.29 + wait4js = false,
7.30 args = {"ret", "model", "propNames", "propReadOnly", "propValues", "propArr", "funcNames", "funcArr"},
7.31 - body =
7.32 + body =
7.33 "ret['ko-fx.model'] = model;\n"
7.34 + "function koComputed(name, readOnly, value, prop) {\n"
7.35 + " function realGetter() {\n"
7.36 @@ -140,5 +145,8 @@
7.37 );
7.38
7.39 @JavaScriptBody(args = { "o" }, body = "return o['ko-fx.model'] ? o['ko-fx.model'] : o;")
7.40 - static native Object toModel(Object wrapper);
7.41 + private static native Object toModelImpl(Object wrapper);
7.42 + static Object toModel(Object wrapper) {
7.43 + return toModelImpl(wrapper);
7.44 + }
7.45 }