1.1 --- a/boot-script/src/main/java/net/java/html/boot/script/ScriptPresenter.java Wed Dec 17 22:00:41 2014 +0100
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 }
2.1 --- a/json-tck/src/main/java/net/java/html/js/tests/GCBodyTest.java Wed Dec 17 22:00:41 2014 +0100
2.2 +++ b/json-tck/src/main/java/net/java/html/js/tests/GCBodyTest.java Thu Dec 18 03:25:54 2014 +0100
2.3 @@ -89,6 +89,20 @@
2.4 assert obj != null : "Object is still present";
2.5 }
2.6
2.7 + @KOTest public void strongReceiverBehavior() {
2.8 + Object v = new EmptyInstance();
2.9 + Receiver r = new Receiver(v);
2.10 + r.apply();
2.11 + assert v == r.value : "Value is as expected";
2.12 + }
2.13 +
2.14 + @KOTest public void gcReceiverBehavior() throws InterruptedException {
2.15 + Receiver r = new Receiver(new EmptyInstance());
2.16 + assertGC(r.ref, "The empty instance can be GCed even when referenced from JS");
2.17 + r.apply();
2.18 + assert r.value == null : "Setter called with null value";
2.19 + }
2.20 +
2.21 private static Reference<?> sendRunnable(final int[] arr) {
2.22 Runnable r = new Runnable() {
2.23 @Override
3.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
3.2 +++ b/json-tck/src/main/java/net/java/html/js/tests/Receiver.java Thu Dec 18 03:25:54 2014 +0100
3.3 @@ -0,0 +1,80 @@
3.4 +/**
3.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3.6 + *
3.7 + * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
3.8 + *
3.9 + * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
3.10 + * Other names may be trademarks of their respective owners.
3.11 + *
3.12 + * The contents of this file are subject to the terms of either the GNU
3.13 + * General Public License Version 2 only ("GPL") or the Common
3.14 + * Development and Distribution License("CDDL") (collectively, the
3.15 + * "License"). You may not use this file except in compliance with the
3.16 + * License. You can obtain a copy of the License at
3.17 + * http://www.netbeans.org/cddl-gplv2.html
3.18 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
3.19 + * specific language governing permissions and limitations under the
3.20 + * License. When distributing the software, include this License Header
3.21 + * Notice in each file and include the License file at
3.22 + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
3.23 + * particular file as subject to the "Classpath" exception as provided
3.24 + * by Oracle in the GPL Version 2 section of the License file that
3.25 + * accompanied this code. If applicable, add the following below the
3.26 + * License Header, with the fields enclosed by brackets [] replaced by
3.27 + * your own identifying information:
3.28 + * "Portions Copyrighted [year] [name of copyright owner]"
3.29 + *
3.30 + * Contributor(s):
3.31 + *
3.32 + * The Original Software is NetBeans. The Initial Developer of the Original
3.33 + * Software is Oracle. Portions Copyright 2013-2014 Oracle. All Rights Reserved.
3.34 + *
3.35 + * If you wish your version of this file to be governed by only the CDDL
3.36 + * or only the GPL Version 2, indicate your decision by adding
3.37 + * "[Contributor] elects to include this software in this distribution
3.38 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
3.39 + * single choice of license, a recipient has the option to distribute
3.40 + * your version of this file under either the CDDL, the GPL Version 2 or
3.41 + * to extend the choice of license to its licensees as provided above.
3.42 + * However, if you add GPL Version 2 code and therefore, elected the GPL
3.43 + * Version 2 license, then the option applies only if the new code is
3.44 + * made subject to such option by the copyright holder.
3.45 + */
3.46 +package net.java.html.js.tests;
3.47 +
3.48 +import java.lang.ref.Reference;
3.49 +import java.lang.ref.WeakReference;
3.50 +import net.java.html.js.JavaScriptBody;
3.51 +
3.52 +/**
3.53 + */
3.54 +public final class Receiver {
3.55 + private final Object fn;
3.56 + Object value;
3.57 + final Reference<Object> ref;
3.58 +
3.59 + public Receiver(Object v) {
3.60 + this.fn = initFn(v);
3.61 + this.ref = new WeakReference<Object>(v);
3.62 + this.value = this;
3.63 + }
3.64 +
3.65 + public void apply() {
3.66 + fnApply(fn, this);
3.67 + }
3.68 +
3.69 + void set(Object v) {
3.70 + value = v;
3.71 + }
3.72 +
3.73 + @JavaScriptBody(args = { "v" }, keepAlive = false, javacall = true,
3.74 + body = "return function(rec) {\n"
3.75 + + " rec.@net.java.html.js.tests.Receiver::set(Ljava/lang/Object;)(v);\n"
3.76 + + "};\n")
3.77 + private static native Object initFn(Object v);
3.78 +
3.79 + @JavaScriptBody(args = { "fn", "thiz" }, body =
3.80 + "fn(thiz);"
3.81 + )
3.82 + private static native void fnApply(Object fn, Receiver thiz);
3.83 +}