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;
}