Attempt to standardize asynchronous execution of JavaScript code snippets async
authorJaroslav Tulach <jaroslav.tulach@netbeans.org>
Tue, 04 Mar 2014 10:12:22 +0100
branchasync
changeset 5700e831501e072
parent 568 f0b64a5d6517
child 577 f23fdccd1827
Attempt to standardize asynchronous execution of JavaScript code snippets
boot/src/main/java/net/java/html/js/JavaScriptBody.java
boot/src/main/java/org/apidesign/html/boot/spi/Fn.java
boot/src/main/java/org/netbeans/html/boot/impl/FnUtils.java
boot/src/main/java/org/netbeans/html/boot/impl/JavaScriptProcesor.java
ko4j/src/main/java/org/netbeans/html/ko4j/Knockout.java
     1.1 --- a/boot/src/main/java/net/java/html/js/JavaScriptBody.java	Mon Mar 03 15:14:46 2014 +0100
     1.2 +++ b/boot/src/main/java/net/java/html/js/JavaScriptBody.java	Tue Mar 04 10:12:22 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	Mon Mar 03 15:14:46 2014 +0100
     2.2 +++ b/boot/src/main/java/org/apidesign/html/boot/spi/Fn.java	Tue Mar 04 10:12:22 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 @@ -190,6 +192,10 @@
    2.13          return FnContext.activate(p);
    2.14      }
    2.15      
    2.16 +    public void invokeLater(Object thiz, Object... args) throws Exception {
    2.17 +        invoke(this, args);
    2.18 +    }
    2.19 +    
    2.20      /** Invokes the defined function with specified <code>this</code> and
    2.21       * appropriate arguments.
    2.22       * 
     3.1 --- a/boot/src/main/java/org/netbeans/html/boot/impl/FnUtils.java	Mon Mar 03 15:14:46 2014 +0100
     3.2 +++ b/boot/src/main/java/org/netbeans/html/boot/impl/FnUtils.java	Tue Mar 04 10:12:22 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	Mon Mar 03 15:14:46 2014 +0100
     4.2 +++ b/boot/src/main/java/org/netbeans/html/boot/impl/JavaScriptProcesor.java	Tue Mar 04 10:12:22 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/ko4j/src/main/java/org/netbeans/html/ko4j/Knockout.java	Mon Mar 03 15:14:46 2014 +0100
     5.2 +++ b/ko4j/src/main/java/org/netbeans/html/ko4j/Knockout.java	Tue Mar 04 10:12:22 2014 +0100
     5.3 @@ -45,6 +45,7 @@
     5.4  import net.java.html.js.JavaScriptBody;
     5.5  import net.java.html.js.JavaScriptResource;
     5.6  import net.java.html.json.Model;
     5.7 +import org.apidesign.html.boot.spi.Fn;
     5.8  import org.apidesign.html.json.spi.FunctionBinding;
     5.9  import org.apidesign.html.json.spi.PropertyBinding;
    5.10  
    5.11 @@ -59,7 +60,9 @@
    5.12   */
    5.13  @JavaScriptResource("knockout-2.2.1.js")
    5.14  final class Knockout {
    5.15 -    @JavaScriptBody(args = { "model", "prop", "oldValue", "newValue" }, body =
    5.16 +    @JavaScriptBody(args = { "model", "prop", "oldValue", "newValue" }, 
    5.17 +        wait4js = false,
    5.18 +        body =
    5.19            "if (model) {\n"
    5.20          + "  var koProp = model[prop];\n"
    5.21          + "  if (koProp && koProp['valueHasMutated']) {\n"
    5.22 @@ -75,15 +78,17 @@
    5.23          Object model, String prop, Object oldValue, Object newValue
    5.24      );
    5.25  
    5.26 -    @JavaScriptBody(args = { "bindings" }, body = "ko.applyBindings(bindings);\n")
    5.27 +    @JavaScriptBody(args = { "bindings" }, wait4js = false, body = 
    5.28 +        "ko.applyBindings(bindings);\n"
    5.29 +    )
    5.30      native static void applyBindings(Object bindings);
    5.31      
    5.32      @JavaScriptBody(
    5.33          javacall = true,
    5.34 -        args = {"model", "propNames", "propReadOnly", "propValues", "propArr", "funcNames", "funcArr"},
    5.35 -        body
    5.36 -        = "var ret = {};\n"
    5.37 -        + "ret['ko-fx.model'] = model;\n"
    5.38 +        wait4js = false,
    5.39 +        args = {"ret", "model", "propNames", "propReadOnly", "propValues", "propArr", "funcNames", "funcArr"},
    5.40 +        body = 
    5.41 +          "ret['ko-fx.model'] = model;\n"
    5.42          + "function koComputed(name, readOnly, value, prop) {\n"
    5.43          + "  function realGetter() {\n"
    5.44          + "    try {\n"
    5.45 @@ -126,14 +131,50 @@
    5.46          + "for (var i = 0; i < funcNames.length; i++) {\n"
    5.47          + "  koExpose(funcNames[i], funcArr[i]);\n"
    5.48          + "}\n"
    5.49 -        + "return ret;\n"
    5.50          )
    5.51 -    static native Object wrapModel(
    5.52 -        Object model,
    5.53 +    private static native void wrapImpl(
    5.54 +        Object jsObject, Object model,
    5.55          String[] propNames, boolean[] propReadOnly, Object propValues, PropertyBinding[] propArr,
    5.56          String[] funcNames, FunctionBinding[] funcArr
    5.57      );
    5.58      
    5.59 +    @JavaScriptBody(args = { "cnt" }, body = 
    5.60 +        "var ret = new Array();\n"
    5.61 +      + "while (cnt-- > 0) {\n"
    5.62 +      + "  ret.push(new Object());\n"
    5.63 +      + "}\n"
    5.64 +      + "return ret;\n"
    5.65 +    )
    5.66 +    private static native Object[] newObjs(int cnt);
    5.67 +    
    5.68 +    private static Fn.Presenter prev;
    5.69 +    private static int objCounter;
    5.70 +    private static Object[] objs;
    5.71 +    
    5.72 +    private static synchronized Object createJSObj() {
    5.73 +        if (prev != Fn.activePresenter()) {
    5.74 +            objs = null;
    5.75 +        }
    5.76 +        if (objs == null || objs.length <= objCounter) {
    5.77 +            objs = newObjs(128);
    5.78 +            objCounter = 0;
    5.79 +        }
    5.80 +        return objs[objCounter++];
    5.81 +    }
    5.82 +    
    5.83 +    static Object wrapModel(
    5.84 +        Object model,
    5.85 +        String[] propNames, boolean[] propReadOnly, Object propValues, PropertyBinding[] propArr,
    5.86 +        String[] funcNames, FunctionBinding[] funcArr
    5.87 +    ) {
    5.88 +        Object js = createJSObj();
    5.89 +        wrapImpl(js, model, propNames, propReadOnly, propValues, propArr, funcNames, funcArr);
    5.90 +        return js;
    5.91 +    }
    5.92 +    
    5.93      @JavaScriptBody(args = { "o" }, body = "return o['ko-fx.model'] ? o['ko-fx.model'] : o;")
    5.94 -    static native Object toModel(Object wrapper);
    5.95 +    private static native Object toModelImpl(Object wrapper);
    5.96 +    static Object toModel(Object wrapper) {
    5.97 +        return toModelImpl(wrapper);
    5.98 +    }
    5.99  }