1.1 --- a/boot-script/src/main/java/net/java/html/boot/script/ScriptPresenter.java Tue Aug 26 18:13:30 2014 +0200
1.2 +++ b/boot-script/src/main/java/net/java/html/boot/script/ScriptPresenter.java Thu Dec 18 03:25:54 2014 +0100
1.3 @@ -45,6 +45,7 @@
1.4 import java.io.Closeable;
1.5 import java.io.IOException;
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.List;
1.11 @@ -70,8 +71,8 @@
1.12 *
1.13 * @author Jaroslav Tulach
1.14 */
1.15 -final class ScriptPresenter
1.16 -implements Presenter, Fn.FromJavaScript, Fn.ToJavaScript, Executor {
1.17 +final class ScriptPresenter implements Fn.KeepAlive,
1.18 +Presenter, Fn.FromJavaScript, Fn.ToJavaScript, Executor {
1.19 private static final Logger LOG = Logger.getLogger(ScriptPresenter.class.getName());
1.20 private final ScriptEngine eng;
1.21 private final Executor exc;
1.22 @@ -90,9 +91,14 @@
1.23
1.24 @Override
1.25 public Fn defineFn(String code, String... names) {
1.26 - return defineImpl(code, names);
1.27 + return defineImpl(code, names, null);
1.28 }
1.29 - private FnImpl defineImpl(String code, String... names) {
1.30 +
1.31 + @Override
1.32 + public Fn defineFn(String code, String[] names, boolean[] keepAlive) {
1.33 + return defineImpl(code, names, keepAlive);
1.34 + }
1.35 + private FnImpl defineImpl(String code, String[] names, boolean[] keepAlive) {
1.36 StringBuilder sb = new StringBuilder();
1.37 sb.append("(function() {");
1.38 sb.append(" return function(");
1.39 @@ -112,7 +118,7 @@
1.40 } catch (ScriptException ex) {
1.41 throw new IllegalStateException(ex);
1.42 }
1.43 - return new FnImpl(this, fn);
1.44 + return new FnImpl(this, fn, keepAlive);
1.45 }
1.46
1.47 @Override
1.48 @@ -150,7 +156,7 @@
1.49 private FnImpl wrapArrFn() {
1.50 if (wrapArrImpl == null) {
1.51 try {
1.52 - wrapArrImpl = defineImpl("return Array.prototype.slice.call(arguments);");
1.53 + wrapArrImpl = defineImpl("return Array.prototype.slice.call(arguments);", null, null);
1.54 } catch (Exception ex) {
1.55 throw new IllegalStateException(ex);
1.56 }
1.57 @@ -181,7 +187,7 @@
1.58 + " var l = arr.length;\n"
1.59 + " for (var i = 0; i < l; i++) to[i] = arr[i];\n"
1.60 + " return l;\n"
1.61 - + "}", "arr", "to"
1.62 + + "}", new String[] { "arr", "to" }, null
1.63 );
1.64 } catch (Exception ex) {
1.65 throw new IllegalStateException(ex);
1.66 @@ -192,6 +198,9 @@
1.67
1.68 @Override
1.69 public Object toJava(Object jsArray) {
1.70 + if (jsArray instanceof Weak) {
1.71 + jsArray = ((Weak)jsArray).get();
1.72 + }
1.73 try {
1.74 return checkArray(jsArray);
1.75 } catch (Exception ex) {
1.76 @@ -239,10 +248,12 @@
1.77 private class FnImpl extends Fn {
1.78
1.79 private final Object fn;
1.80 + private final boolean[] keepAlive;
1.81
1.82 - public FnImpl(Presenter presenter, Object fn) {
1.83 + public FnImpl(Presenter presenter, Object fn, boolean[] keepAlive) {
1.84 super(presenter);
1.85 this.fn = fn;
1.86 + this.keepAlive = keepAlive;
1.87 }
1.88
1.89 @Override
1.90 @@ -254,19 +265,28 @@
1.91 List<Object> all = new ArrayList<>(args.length + 1);
1.92 all.add(thiz == null ? fn : thiz);
1.93 for (int i = 0; i < args.length; i++) {
1.94 + Object conv = args[i];
1.95 if (arrayChecks) {
1.96 if (args[i] instanceof Object[]) {
1.97 Object[] arr = (Object[]) args[i];
1.98 - Object conv = ((ScriptPresenter)presenter()).convertArrays(arr);
1.99 - args[i] = conv;
1.100 + conv = ((ScriptPresenter) presenter()).convertArrays(arr);
1.101 }
1.102 - if (args[i] instanceof Character) {
1.103 - args[i] = (int)((Character)args[i]);
1.104 + if (conv != null && keepAlive != null
1.105 + && !keepAlive[i] && !isJSReady(conv)
1.106 + && !conv.getClass().getSimpleName().equals("$JsCallbacks$") // NOI18N
1.107 + ) {
1.108 + conv = new Weak(conv);
1.109 + }
1.110 + if (conv instanceof Character) {
1.111 + conv = (int)(Character)conv;
1.112 }
1.113 }
1.114 - all.add(args[i]);
1.115 + all.add(conv);
1.116 }
1.117 Object ret = ((Invocable)eng).invokeMethod(fn, "call", all.toArray()); // NOI18N
1.118 + if (ret instanceof Weak) {
1.119 + ret = ((Weak)ret).get();
1.120 + }
1.121 if (ret == fn) {
1.122 return null;
1.123 }
1.124 @@ -277,4 +297,31 @@
1.125 }
1.126 }
1.127
1.128 + private static boolean isJSReady(Object obj) {
1.129 + if (obj == null) {
1.130 + return true;
1.131 + }
1.132 + if (obj instanceof String) {
1.133 + return true;
1.134 + }
1.135 + if (obj instanceof Number) {
1.136 + return true;
1.137 + }
1.138 + final String cn = obj.getClass().getName();
1.139 + if (cn.startsWith("jdk.nashorn") || ( // NOI18N
1.140 + cn.contains(".mozilla.") && cn.contains(".Native") // NOI18N
1.141 + )) {
1.142 + return true;
1.143 + }
1.144 + if (obj instanceof Character) {
1.145 + return true;
1.146 + }
1.147 + return false;
1.148 + }
1.149 +
1.150 + private static final class Weak extends WeakReference<Object> {
1.151 + public Weak(Object referent) {
1.152 + super(referent);
1.153 + }
1.154 + }
1.155 }