You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@netbeans.apache.org by jt...@apache.org on 2017/09/08 21:06:17 UTC

[09/28] incubator-netbeans-html4j git commit: Manage the Java objects on the VM side, without passing them to JavaFX WebView

Manage the Java objects on the VM side, without passing them to JavaFX WebView


Project: http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/commit/e95bb204
Tree: http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/tree/e95bb204
Diff: http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/diff/e95bb204

Branch: refs/heads/master
Commit: e95bb2042e28c609269f33182200072932eb4370
Parents: 323e9ba
Author: Jaroslav Tulach <jt...@netbeans.org>
Authored: Sat Jan 14 05:16:33 2017 +0100
Committer: Jaroslav Tulach <ja...@apidesign.org>
Committed: Fri Sep 8 17:13:55 2017 +0200

----------------------------------------------------------------------
 .../html/boot/fx/AbstractFXPresenter.java       | 331 +++++++++++++++----
 .../java/net/java/html/js/tests/GCBodyTest.java |   1 -
 2 files changed, 268 insertions(+), 64 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/e95bb204/boot-fx/src/main/java/org/netbeans/html/boot/fx/AbstractFXPresenter.java
----------------------------------------------------------------------
diff --git a/boot-fx/src/main/java/org/netbeans/html/boot/fx/AbstractFXPresenter.java b/boot-fx/src/main/java/org/netbeans/html/boot/fx/AbstractFXPresenter.java
index 9164140..063f5af 100644
--- a/boot-fx/src/main/java/org/netbeans/html/boot/fx/AbstractFXPresenter.java
+++ b/boot-fx/src/main/java/org/netbeans/html/boot/fx/AbstractFXPresenter.java
@@ -47,10 +47,16 @@ import java.io.Closeable;
 import java.io.IOException;
 import java.io.Reader;
 import java.lang.ref.WeakReference;
+import java.lang.reflect.Array;
 import java.net.URL;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
+import java.util.NavigableSet;
+import java.util.TreeSet;
 import java.util.concurrent.Executor;
 import java.util.logging.Level;
 import java.util.logging.Logger;
@@ -59,6 +65,7 @@ import javafx.scene.Parent;
 import javafx.scene.layout.BorderPane;
 import javafx.scene.web.WebEngine;
 import javafx.scene.web.WebView;
+import netscape.javascript.JSException;
 import netscape.javascript.JSObject;
 import org.netbeans.html.boot.spi.Fn;
 
@@ -76,7 +83,9 @@ Fn.KeepAlive, Fn.ToJavaScript, Fn.FromJavaScript, Executor, Cloneable {
     // transient - e.g. not cloneable
     private JSObject arraySize;
     private JSObject wrapArrImpl;
+    private JSObject newPOJOImpl;
     private Object undefined;
+    private JavaValues values;
 
     @Override
     protected AbstractFXPresenter clone() {
@@ -85,6 +94,8 @@ Fn.KeepAlive, Fn.ToJavaScript, Fn.FromJavaScript, Executor, Cloneable {
             p.arraySize = null;
             p.wrapArrImpl = null;
             p.undefined = null;
+            p.newPOJOImpl = null;
+            p.values = null;
             return p;
         } catch (CloneNotSupportedException ex) {
             throw new IllegalStateException(ex);
@@ -209,6 +220,13 @@ Fn.KeepAlive, Fn.ToJavaScript, Fn.FromJavaScript, Executor, Cloneable {
         return wrapArr;
     }
 
+    private final JavaValues values() {
+        if (values == null) {
+            values = new JavaValues();
+        }
+        return values;
+    }
+
     private final JSObject wrapArrFn() {
         if (wrapArrImpl == null) {
             try {
@@ -225,6 +243,25 @@ Fn.KeepAlive, Fn.ToJavaScript, Fn.FromJavaScript, Executor, Cloneable {
         return wrapArrImpl;
     }
 
+    JSObject createPOJOWrapper(int hash, int id) {
+        if (newPOJOImpl == null) {
+            try {
+                newPOJOImpl = (JSObject) defineJSFn(
+                    "var k = {};\n" +
+                    "k.fxBrwsrId = function(hash, id) {\n" +
+                    "  return {\n" +
+                    "    'fxBrwsrId' : function(callback) { callback.hashAndId(hash, id); }\n" +
+                    "  }\n" +
+                    "};\n" +
+                    "return k;\n", new String[] { "callback" }, null
+                ).invokeImpl(null, false);
+            } catch (Exception ex) {
+                throw new IllegalStateException(ex);
+            }
+        }
+        return (JSObject) newPOJOImpl.call("fxBrwsrId", hash, id);
+    }
+
     final Object undefined() {
         if (undefined == null) {
             undefined = engine.executeScript("undefined");
@@ -232,29 +269,21 @@ Fn.KeepAlive, Fn.ToJavaScript, Fn.FromJavaScript, Executor, Cloneable {
         return undefined;
     }
 
-    final Object checkArray(Object val) {
-        if (!(val instanceof JSObject)) {
-            return val;
-        }
+    private int getArrayLength(Object val) throws JSException {
         int length = ((Number) arraySizeFn().call("array", val, null)).intValue();
-        if (length == -1) {
-            return val;
-        }
+        return length;
+    }
+
+    private Object[] toArray(int length, Object val) throws JSException {
         Object[] arr = new Object[length];
         arraySizeFn().call("array", val, arr);
-        clearUndefinedArray(arr);
+        checkArray(arr);
         return arr;
     }
 
-    private void clearUndefinedArray(Object[] arr) {
+    private void checkArray(Object[] arr) {
         for (int i = 0; i < arr.length; i++) {
-            if (arr[i] == undefined) {
-                arr[i] = null;
-                continue;
-            }
-            if (arr[i] instanceof Object[]) {
-                clearUndefinedArray((Object[])arr[i]);
-            }
+            arr[i] = toJava(arr[i]);
         }
     }
 
@@ -283,27 +312,63 @@ Fn.KeepAlive, Fn.ToJavaScript, Fn.FromJavaScript, Executor, Cloneable {
 
     @Override
     public Object toJava(Object toJS) {
-        if (toJS instanceof Weak) {
-            toJS = ((Weak)toJS).get();
-        }
         if (toJS == undefined()) {
             return null;
         }
-        return checkArray(toJS);
+        if (!(toJS instanceof JSObject)) {
+            return toJS;
+        }
+        JSObject js = (JSObject) toJS;
+        int length = getArrayLength(toJS);
+        if (length != -1) {
+            Object[] arr = toArray(length, toJS);
+            System.err.println("converter to Java array:");
+            dumpArray(arr, "");
+            return arr;
+        }
+        return values().realValue(js);
     }
 
     @Override
-    public Object toJavaScript(Object toReturn) {
-        if (toReturn instanceof Object[]) {
-            return convertArrays((Object[])toReturn);
-        } else {
-            if (toReturn instanceof Character) {
-                return (int)(Character)toReturn;
+    public Object toJavaScript(Object value) {
+        return toJavaScript(value, true);
+    }
+
+    final Object toJavaScript(Object value, boolean keep) {
+        if (value == null) {
+            return null;
+        }
+        if (value instanceof String) {
+            return value;
+        }
+        if (value instanceof Number) {
+            return value;
+        }
+        if (value instanceof JSObject) {
+            return value;
+        }
+        if (value instanceof Boolean) {
+            return value;
+        }
+        if (value instanceof Character) {
+            return (int) (char) (Character) value;
+        }
+        int len = isArray(value);
+        if (len >= 0) {
+            Object[] copy = new Object[len];
+            for (int i = 0; i < len; i++) {
+                copy[i] = toJavaScript(Array.get(value, i));
             }
-            return toReturn;
+            final JSObject wrapArr = (JSObject)wrapArrFn().call("array", copy); // NOI18N
+            return wrapArr;
         }
+        if (value.getClass().getName().endsWith("$JsCallbacks$")) {
+            return value;
+        }
+        return values().wrap(value, keep);
     }
 
+
     @Override public void execute(final Runnable r) {
         if (Platform.isFxApplicationThread()) {
             Closeable c = Fn.activate(this);
@@ -336,6 +401,15 @@ Fn.KeepAlive, Fn.ToJavaScript, Fn.FromJavaScript, Executor, Cloneable {
         }
     }
 
+    private void dumpArray(Object[] arr, String spaces) {
+        for (int i = 0; i < arr.length; i++) {
+            System.err.println(spaces + "[" + i + "] = " + arr[i]);
+            if (arr[i] instanceof Object[]) {
+                dumpArray((Object[]) arr[i], spaces + "  ");
+            }
+        }
+    }
+
     private static final class JSFn extends Fn {
 
         private final JSObject fn;
@@ -364,37 +438,23 @@ Fn.KeepAlive, Fn.ToJavaScript, Fn.FromJavaScript, Executor, Cloneable {
                     LOG.log(Level.FINER, "  params: {0}", Arrays.asList(args));
                 }
                 List<Object> all = new ArrayList<Object>(args.length + 1);
-                all.add(thiz == null ? fn : thiz);
+                all.add(thiz == null ? presenter.undefined() : thiz);
                 for (int i = 0; i < args.length; i++) {
                     Object conv = args[i];
                     if (arrayChecks) {
-                        if (args[i] instanceof Object[]) {
-                            Object[] arr = (Object[]) args[i];
-                            conv = presenter.convertArrays(arr);
-                        }
-                        if (conv != null && keepAlive != null &&
-                            !keepAlive[i] && !isJSReady(conv) &&
-                            !conv.getClass().getSimpleName().equals("$JsCallbacks$") // NOI18N
-                        ) {
-                            conv = new Weak(conv);
-                        }
-                        if (conv instanceof Character) {
-                            conv = (int)(Character)conv;
-                        }
+                        boolean alive = keepAlive == null || keepAlive[i];
+                        conv = presenter.toJavaScript(conv, alive);
                     }
                     all.add(conv);
                 }
                 Object ret = fn.call("call", all.toArray()); // NOI18N
-                if (ret instanceof Weak) {
-                    ret = ((Weak)ret).get();
-                }
-                if (ret == fn || ret == presenter.undefined()) {
+                if (ret == presenter.undefined()) {
                     return null;
                 }
                 if (!arrayChecks) {
                     return ret;
                 }
-                return presenter.checkArray(ret);
+                return presenter.toJava(ret);
             } catch (Error t) {
                 t.printStackTrace();
                 throw t;
@@ -405,29 +465,174 @@ Fn.KeepAlive, Fn.ToJavaScript, Fn.FromJavaScript, Executor, Cloneable {
         }
     }
 
-    private static boolean isJSReady(Object obj) {
-        if (obj == null) {
-            return true;
+    protected int isArray(Object value) {
+        try {
+            return Array.getLength(value);
+        } catch (IllegalArgumentException ex) {
+            return -1;
+        }
+    }
+
+    private interface Ref extends Comparable<Ref> {
+        Object value();
+        int id();
+        JSObject jsObj();
+    }
+
+    private final class WeakRef extends WeakReference<Object> implements Ref {
+        private final int id;
+        private final JSObject js;
+
+        WeakRef(Object value, int id, JSObject js) {
+            super(value);
+            this.id = id;
+            this.js = js;
+        }
+
+        @Override
+        public Object value() {
+            return get();
+        }
+
+        @Override
+        public int id() {
+            return id;
+        }
+
+        @Override
+        public JSObject jsObj() {
+            return js;
+        }
+
+        @Override
+        public int compareTo(Ref o) {
+            return this.id() - o.id();
+        }
+    }
+
+    private final class StrongRef implements Ref {
+        private final Object value;
+        private final int id;
+        private final JSObject js;
+
+        StrongRef(Object value, int id, JSObject js) {
+            this.value = value;
+            this.id = id;
+            this.js = js;
         }
-        if (obj instanceof String) {
-            return true;
+
+        @Override
+        public Object value() {
+            return value;
         }
-        if (obj instanceof Number) {
-            return true;
+
+        @Override
+        public int id() {
+            return id;
         }
-        if (obj instanceof JSObject) {
-            return true;
+
+        @Override
+        public JSObject jsObj() {
+            return js;
         }
-        if (obj instanceof Character) {
-            return true;
+
+        @Override
+        public int compareTo(Ref o) {
+            return this.id() - o.id();
         }
-        return false;
     }
 
-    private static final class Weak extends WeakReference<Object> {
-        public Weak(Object referent) {
-            super(referent);
-            assert !(referent instanceof Weak);
+    public final class JavaValues {
+        private final Map<Integer,NavigableSet<Ref>> values;
+        private int hash;
+        private int id;
+
+        JavaValues() {
+            this.values = new HashMap<Integer,NavigableSet<Ref>>();
         }
-    } // end of Weak
+
+        synchronized final JSObject wrap(Object pojo, boolean keep) {
+            int hash = System.identityHashCode(pojo);
+            NavigableSet<Ref> refs = values.get(hash);
+            if (refs != null) {
+                for (Ref ref : refs) {
+                    if (ref.value() == pojo) {
+                        return ref.jsObj();
+                    }
+                }
+            } else {
+                refs = new TreeSet<Ref>();
+                values.put(hash, refs);
+            }
+            int id = findId(refs);
+            JSObject js = createPOJOWrapper(hash, id);
+            Ref newRef = keep ? new StrongRef(pojo, id, js) : new WeakRef(pojo, id, js);
+            refs.add(newRef);
+            return newRef.jsObj();
+        }
+
+        private int findId(NavigableSet<Ref> refs) {
+            if (refs.isEmpty()) {
+                return 0;
+            }
+            final Ref first = refs.first();
+            int previous = first.id();
+            if (previous > 0) {
+                return 0;
+            }
+            for (Ref ref : refs.tailSet(first, false)) {
+                int next = ref.id();
+                if (previous + 1 < next) {
+                    return previous + 1;
+                }
+                previous = next;
+            }
+            return previous + 1;
+        }
+
+        public void hashAndId(int hash, int id) {
+            assert this.hash == -1;
+            assert this.id == -1;
+            this.hash = hash;
+            this.id = id;
+        }
+
+        Object realValue(JSObject obj) {
+            Object java = obj.getMember("fxBrwsrId");
+            if (java instanceof JSObject) {
+                for (;;) {
+                    int resultHash;
+                    int resultId;
+                    synchronized (this) {
+                        this.hash = -1;
+                        this.id = -1;
+                        obj.call("fxBrwsrId", this);
+                        assert this.hash != -1;
+                        assert this.id != -1;
+                        resultHash = this.hash;
+                        resultId = this.id;
+                    }
+
+                    final NavigableSet<Ref> refs = values.get(resultHash);
+                    Iterator<Ref> it = refs.iterator();
+                    while (it.hasNext()) {
+                        Ref next = it.next();
+                        Object pojo = next.value();
+                        if (next.id() == resultId) {
+                            return pojo;
+                        }
+                        if (pojo == null) {
+                            it.remove();
+                        }
+                    }
+                    if (refs.isEmpty()) {
+                        values.remove(resultHash);
+                    }
+                }
+            }
+            return obj;
+        }
+    }
+
+
 }

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/e95bb204/json-tck/src/main/java/net/java/html/js/tests/GCBodyTest.java
----------------------------------------------------------------------
diff --git a/json-tck/src/main/java/net/java/html/js/tests/GCBodyTest.java b/json-tck/src/main/java/net/java/html/js/tests/GCBodyTest.java
index 5baa865..cbb4984 100644
--- a/json-tck/src/main/java/net/java/html/js/tests/GCBodyTest.java
+++ b/json-tck/src/main/java/net/java/html/js/tests/GCBodyTest.java
@@ -82,7 +82,6 @@ public class GCBodyTest {
 
         int res = Bodies.sumNow(jsSum, 22, 20);
         assertEquals(res, 42, "Expecting 42");
-        assertGC(ref, "Can disappear!");
         if (gcError != null) {
             throw gcError;
         }