1.1 --- a/launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/fximpl/JVMBridge.java Sun Aug 31 22:36:54 2014 +0200
1.2 +++ b/launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/fximpl/JVMBridge.java Mon Dec 22 20:57:23 2014 +0100
1.3 @@ -19,12 +19,15 @@
1.4
1.5 import java.io.BufferedReader;
1.6 import java.io.Reader;
1.7 +import java.lang.ref.WeakReference;
1.8 import java.net.URL;
1.9 import java.util.ArrayList;
1.10 +import java.util.Arrays;
1.11 import java.util.Collection;
1.12 import java.util.List;
1.13 import java.util.TooManyListenersException;
1.14 import java.util.concurrent.Executor;
1.15 +import java.util.logging.Level;
1.16 import java.util.logging.Logger;
1.17 import javafx.application.Platform;
1.18 import javafx.beans.value.ChangeListener;
1.19 @@ -78,8 +81,8 @@
1.20 return Class.forName(name, true, cl);
1.21 }
1.22
1.23 - private final class WebPresenter
1.24 - implements FindResources, Fn.Presenter, Fn.ToJavaScript, Fn.FromJavaScript, Executor {
1.25 + private final class WebPresenter implements Fn.Presenter,
1.26 + FindResources, Fn.ToJavaScript, Fn.FromJavaScript, Executor, Fn.KeepAlive {
1.27 @Override
1.28 public void findResources(String name, Collection<? super URL> results, boolean oneIsEnough) {
1.29 if (ldrs != null) for (ClassLoader l : ldrs) {
1.30 @@ -92,14 +95,20 @@
1.31
1.32 @Override
1.33 public Fn defineFn(String code, String... names) {
1.34 - return defineJSFn(code, names);
1.35 + return defineJSFn(code, names, null);
1.36 }
1.37 - private JSFn defineJSFn(String code, String... names) {
1.38 +
1.39 + @Override
1.40 + public Fn defineFn(String code, String[] names, boolean[] keepAlive) {
1.41 + return defineJSFn(code, names, keepAlive);
1.42 + }
1.43 +
1.44 + private JSFn defineJSFn(String code, String[] names, boolean[] keepAlive) {
1.45 StringBuilder sb = new StringBuilder();
1.46 sb.append("(function() {");
1.47 sb.append(" return function(");
1.48 String sep = "";
1.49 - for (String n : names) {
1.50 + if (names != null) for (String n : names) {
1.51 sb.append(sep).append(n);
1.52 sep = ",";
1.53 }
1.54 @@ -109,7 +118,7 @@
1.55 sb.append("})()");
1.56
1.57 JSObject x = (JSObject) engine.executeScript(sb.toString());
1.58 - return new JSFn(this, x);
1.59 + return new JSFn(this, x, keepAlive);
1.60 }
1.61
1.62 @Override
1.63 @@ -133,6 +142,9 @@
1.64
1.65 @Override
1.66 public Object toJava(Object js) {
1.67 + if (js instanceof Weak) {
1.68 + js = ((Weak)js).get();
1.69 + }
1.70 return checkArray(js);
1.71 }
1.72
1.73 @@ -172,7 +184,7 @@
1.74 + " k.array= function() {"
1.75 + " return Array.prototype.slice.call(arguments);"
1.76 + " };"
1.77 - + " return k;"
1.78 + + " return k;", null, null
1.79 ).invokeImpl(null, false);
1.80 } catch (Exception ex) {
1.81 throw new IllegalStateException(ex);
1.82 @@ -206,7 +218,7 @@
1.83 + " return l;"
1.84 + " }"
1.85 + " };"
1.86 - + " return k;"
1.87 + + " return k;", null, null
1.88 ).invokeImpl(null, false);
1.89 } catch (Exception ex) {
1.90 throw new IllegalStateException(ex);
1.91 @@ -214,15 +226,17 @@
1.92 }
1.93 return arraySize;
1.94 }
1.95 -
1.96 +
1.97 }
1.98
1.99 private static final class JSFn extends Fn {
1.100 private final JSObject fn;
1.101 + private final boolean[] keepAlive;
1.102
1.103 - private JSFn(WebPresenter cl, JSObject fn) {
1.104 + private JSFn(WebPresenter cl, JSObject fn, boolean[] keepAlive) {
1.105 super(cl);
1.106 this.fn = fn;
1.107 + this.keepAlive = keepAlive;
1.108 }
1.109
1.110 @Override
1.111 @@ -235,14 +249,25 @@
1.112 List<Object> all = new ArrayList<Object>(args.length + 1);
1.113 all.add(thiz == null ? fn : thiz);
1.114 for (int i = 0; i < args.length; i++) {
1.115 - if (arrayChecks && args[i] instanceof Object[]) {
1.116 - Object[] arr = (Object[]) args[i];
1.117 - Object conv = ((WebPresenter) presenter()).convertArrays(arr);
1.118 - args[i] = conv;
1.119 + Object conv = args[i];
1.120 + if (arrayChecks) {
1.121 + if (args[i] instanceof Object[]) {
1.122 + Object[] arr = (Object[]) args[i];
1.123 + conv = ((WebPresenter) presenter()).convertArrays(arr);
1.124 + }
1.125 + if (conv != null && keepAlive != null
1.126 + && !keepAlive[i] && !isJSReady(conv)
1.127 + && !conv.getClass().getSimpleName().equals("$JsCallbacks$") // NOI18N
1.128 + ) {
1.129 + conv = new Weak(conv);
1.130 + }
1.131 }
1.132 - all.add(args[i]);
1.133 + all.add(conv);
1.134 }
1.135 Object ret = fn.call("call", all.toArray()); // NOI18N
1.136 + if (ret instanceof Weak) {
1.137 + ret = ((Weak) ret).get();
1.138 + }
1.139 if (ret == fn) {
1.140 return null;
1.141 }
1.142 @@ -259,4 +284,30 @@
1.143 }
1.144 }
1.145 }
1.146 +
1.147 + private static boolean isJSReady(Object obj) {
1.148 + if (obj == null) {
1.149 + return true;
1.150 + }
1.151 + if (obj instanceof String) {
1.152 + return true;
1.153 + }
1.154 + if (obj instanceof Number) {
1.155 + return true;
1.156 + }
1.157 + if (obj instanceof JSObject) {
1.158 + return true;
1.159 + }
1.160 + if (obj instanceof Character) {
1.161 + return true;
1.162 + }
1.163 + return false;
1.164 + }
1.165 +
1.166 + private static final class Weak extends WeakReference<Object> {
1.167 + public Weak(Object referent) {
1.168 + super(referent);
1.169 + assert !(referent instanceof Weak);
1.170 + }
1.171 + } // end of Weak
1.172 }