Cleanup properties of the JavaScript object once its model gets garbage collected gc
authorJaroslav Tulach <jtulach@netbeans.org>
Mon, 08 Dec 2014 21:04:35 +0100
branchgc
changeset 87775fff3637abe
parent 876 0bd358b6d0a7
child 890 5bea583947da
Cleanup properties of the JavaScript object once its model gets garbage collected
ko4j/src/main/java/org/netbeans/html/ko4j/FXContext.java
ko4j/src/main/java/org/netbeans/html/ko4j/Knockout.java
     1.1 --- a/ko4j/src/main/java/org/netbeans/html/ko4j/FXContext.java	Mon Dec 08 20:59:41 2014 +0100
     1.2 +++ b/ko4j/src/main/java/org/netbeans/html/ko4j/FXContext.java	Mon Dec 08 21:04:35 2014 +0100
     1.3 @@ -43,7 +43,6 @@
     1.4  package org.netbeans.html.ko4j;
     1.5  
     1.6  import java.io.ByteArrayOutputStream;
     1.7 -import java.io.Closeable;
     1.8  import java.io.IOException;
     1.9  import java.io.InputStream;
    1.10  import java.io.InputStreamReader;
    1.11 @@ -90,7 +89,7 @@
    1.12              funcNames[i] = funcArr[i].getFunctionName();
    1.13          }
    1.14          Object ret = getJSObject();
    1.15 -        Knockout.wrapModel(new Knockout(model, propArr, funcArr),
    1.16 +        Knockout.wrapModel(new Knockout(model, ret, propArr, funcArr),
    1.17              ret, 
    1.18              propNames, propReadOnly, propValues,
    1.19              funcNames
    1.20 @@ -101,11 +100,16 @@
    1.21      private Object getJSObject() {
    1.22          int len = 64;
    1.23          if (jsObjects != null && jsIndex < (len = jsObjects.length)) {
    1.24 -            return jsObjects[jsIndex++];
    1.25 +            Object ret = jsObjects[jsIndex];
    1.26 +            jsObjects[jsIndex] = null;
    1.27 +            jsIndex++;
    1.28 +            return ret;
    1.29          }
    1.30          jsObjects = Knockout.allocJS(len * 2);
    1.31          jsIndex = 1;
    1.32 -        return jsObjects[0];
    1.33 +        Object ret = jsObjects[0];
    1.34 +        jsObjects[0] = null;
    1.35 +        return ret;
    1.36      }
    1.37      
    1.38      @Override
    1.39 @@ -120,11 +124,13 @@
    1.40  
    1.41      @Override
    1.42      public void valueHasMutated(Object data, String propertyName) {
    1.43 +        Knockout.cleanUp();
    1.44          Knockout.valueHasMutated(data, propertyName, null, null);
    1.45      }
    1.46      
    1.47      @Override
    1.48      public void valueHasMutated(Object data, String propertyName, Object oldValue, Object newValue) {
    1.49 +        Knockout.cleanUp();
    1.50          Knockout.valueHasMutated(data, propertyName, oldValue, newValue);
    1.51      }
    1.52  
     2.1 --- a/ko4j/src/main/java/org/netbeans/html/ko4j/Knockout.java	Mon Dec 08 20:59:41 2014 +0100
     2.2 +++ b/ko4j/src/main/java/org/netbeans/html/ko4j/Knockout.java	Mon Dec 08 21:04:35 2014 +0100
     2.3 @@ -42,6 +42,7 @@
     2.4   */
     2.5  package org.netbeans.html.ko4j;
     2.6  
     2.7 +import java.lang.ref.ReferenceQueue;
     2.8  import java.lang.ref.WeakReference;
     2.9  import net.java.html.js.JavaScriptBody;
    2.10  import net.java.html.js.JavaScriptResource;
    2.11 @@ -60,12 +61,16 @@
    2.12   */
    2.13  @JavaScriptResource("knockout-3.2.0.debug.js")
    2.14  final class Knockout extends WeakReference<Object> {
    2.15 -    private final PropertyBinding[] props;
    2.16 -    private final FunctionBinding[] funcs;
    2.17 +    private static final ReferenceQueue<Object> QUEUE = new ReferenceQueue();
    2.18 +    
    2.19 +    private PropertyBinding[] props;
    2.20 +    private FunctionBinding[] funcs;
    2.21 +    private Object js;
    2.22      private Object strong;
    2.23  
    2.24 -    public Knockout(Object model, PropertyBinding[] props, FunctionBinding[] funcs) {
    2.25 -        super(model);
    2.26 +    public Knockout(Object model, Object js, PropertyBinding[] props, FunctionBinding[] funcs) {
    2.27 +        super(model, QUEUE);
    2.28 +        this.js = js;
    2.29          this.props = new PropertyBinding[props.length];
    2.30          for (int i = 0; i < props.length; i++) {
    2.31              this.props[i] = props[i].weak();
    2.32 @@ -76,6 +81,19 @@
    2.33          }
    2.34      }
    2.35      
    2.36 +    static void cleanUp() {
    2.37 +        for (;;) {
    2.38 +            Knockout ko = (Knockout)QUEUE.poll();
    2.39 +            if (ko == null) {
    2.40 +                return;
    2.41 +            }
    2.42 +            clean(ko.js);
    2.43 +            ko.js = null;
    2.44 +            ko.props = null;
    2.45 +            ko.funcs = null;
    2.46 +        }
    2.47 +    }
    2.48 +    
    2.49      final void hold() {
    2.50          strong = get();
    2.51      }
    2.52 @@ -130,14 +148,15 @@
    2.53      @JavaScriptBody(
    2.54          javacall = true,
    2.55          wait4js = false,
    2.56 -        args = { "self", "ret", "propNames", "propReadOnly", "propValues", "funcNames" },
    2.57 +        args = { "thiz", "ret", "propNames", "propReadOnly", "propValues", "funcNames" },
    2.58          body = 
    2.59 -          "Object.defineProperty(ret, 'ko4j', { value : self });\n"
    2.60 +          "Object.defineProperty(ret, 'ko4j', { value : thiz });\n"
    2.61          + "function koComputed(index, name, readOnly, value) {\n"
    2.62          + "  var trigger = ko['observable']()['extend']({'notify':'always'});"
    2.63          + "  function realGetter() {\n"
    2.64 +        + "    var self = ret['ko4j'];\n"
    2.65          + "    try {\n"
    2.66 -        + "      var v = self.@org.netbeans.html.ko4j.Knockout::getValue(I)(index);\n"
    2.67 +        + "      var v = self ? self.@org.netbeans.html.ko4j.Knockout::getValue(I)(index) : null;\n"
    2.68          + "      return v;\n"
    2.69          + "    } catch (e) {\n"
    2.70          + "      alert(\"Cannot call getValue on \" + self + \" prop: \" + name + \" error: \" + e);\n"
    2.71 @@ -156,6 +175,8 @@
    2.72          + "  };\n"
    2.73          + "  if (!readOnly) {\n"
    2.74          + "    bnd['write'] = function(val) {\n"
    2.75 +        + "      var self = ret['ko4j'];\n"
    2.76 +        + "      if (!self) return;\n"
    2.77          + "      var model = val['ko4j'];\n"
    2.78          + "      self.@org.netbeans.html.ko4j.Knockout::setValue(ILjava/lang/Object;)(index, model ? model : val);\n"
    2.79          + "    };\n"
    2.80 @@ -172,6 +193,8 @@
    2.81          + "}\n"
    2.82          + "function koExpose(index, name) {\n"
    2.83          + "  ret[name] = function(data, ev) {\n"
    2.84 +        + "    var self = ret['ko4j'];\n"
    2.85 +        + "    if (!self) return;\n"
    2.86          + "    self.@org.netbeans.html.ko4j.Knockout::call(ILjava/lang/Object;Ljava/lang/Object;)(index, data, ev);\n"
    2.87          + "  };\n"
    2.88          + "}\n"
    2.89 @@ -186,6 +209,15 @@
    2.90          String[] funcNames
    2.91      );
    2.92      
    2.93 +    @JavaScriptBody(args = { "js" }, wait4js = false, body = 
    2.94 +        "delete js['ko4j'];\n" +
    2.95 +        "for (var p in js) {\n" +
    2.96 +        "  delete js[p];\n" +
    2.97 +        "};\n" +
    2.98 +        "\n"
    2.99 +    )
   2.100 +    private static native void clean(Object js);
   2.101 +    
   2.102      @JavaScriptBody(args = { "o" }, body = "return o['ko4j'] ? o['ko4j'] : o;")
   2.103      private static native Object toModelImpl(Object wrapper);
   2.104      static Object toModel(Object wrapper) {