Bringing the !keepAlive approach to javax.script presenter as well weakfx
authorJaroslav Tulach <jtulach@netbeans.org>
Thu, 18 Dec 2014 03:25:54 +0100
branchweakfx
changeset 91515af7ebf1d0e
parent 914 684f9cbfb450
child 916 7e1929093f06
Bringing the !keepAlive approach to javax.script presenter as well
boot-script/src/main/java/net/java/html/boot/script/ScriptPresenter.java
json-tck/src/main/java/net/java/html/js/tests/GCBodyTest.java
json-tck/src/main/java/net/java/html/js/tests/Receiver.java
     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 +}