boot-truffle/src/main/java/net/java/html/boot/truffle/TrufflePresenter.java
author Jaroslav Tulach <jtulach@netbeans.org>
Sat, 02 Jul 2016 09:35:05 +0200
branchTruffle
changeset 1096 28e373ac1576
parent 1094 47cd2110ed8d
child 1097 62f1d7c47a60
permissions -rw-r--r--
Run most of the tests against GraalVM 0.12 successfully
     1 /**
     2  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     3  *
     4  * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
     5  *
     6  * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
     7  * Other names may be trademarks of their respective owners.
     8  *
     9  * The contents of this file are subject to the terms of either the GNU
    10  * General Public License Version 2 only ("GPL") or the Common
    11  * Development and Distribution License("CDDL") (collectively, the
    12  * "License"). You may not use this file except in compliance with the
    13  * License. You can obtain a copy of the License at
    14  * http://www.netbeans.org/cddl-gplv2.html
    15  * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
    16  * specific language governing permissions and limitations under the
    17  * License.  When distributing the software, include this License Header
    18  * Notice in each file and include the License file at
    19  * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
    20  * particular file as subject to the "Classpath" exception as provided
    21  * by Oracle in the GPL Version 2 section of the License file that
    22  * accompanied this code. If applicable, add the following below the
    23  * License Header, with the fields enclosed by brackets [] replaced by
    24  * your own identifying information:
    25  * "Portions Copyrighted [year] [name of copyright owner]"
    26  *
    27  * Contributor(s):
    28  *
    29  * The Original Software is NetBeans. The Initial Developer of the Original
    30  * Software is Oracle. Portions Copyright 2013-2014 Oracle. All Rights Reserved.
    31  *
    32  * If you wish your version of this file to be governed by only the CDDL
    33  * or only the GPL Version 2, indicate your decision by adding
    34  * "[Contributor] elects to include this software in this distribution
    35  * under the [CDDL or GPL Version 2] license." If you do not indicate a
    36  * single choice of license, a recipient has the option to distribute
    37  * your version of this file under either the CDDL, the GPL Version 2 or
    38  * to extend the choice of license to its licensees as provided above.
    39  * However, if you add GPL Version 2 code and therefore, elected the GPL
    40  * Version 2 license, then the option applies only if the new code is
    41  * made subject to such option by the copyright holder.
    42  */
    43 package net.java.html.boot.truffle;
    44 
    45 import com.oracle.truffle.api.CallTarget;
    46 import com.oracle.truffle.api.Truffle;
    47 import com.oracle.truffle.api.TruffleLanguage;
    48 import com.oracle.truffle.api.interop.TruffleObject;
    49 import com.oracle.truffle.api.interop.java.JavaInterop;
    50 import com.oracle.truffle.api.interop.java.MethodMessage;
    51 import com.oracle.truffle.api.source.Source;
    52 import com.oracle.truffle.api.vm.PolyglotEngine;
    53 import java.io.Closeable;
    54 import java.io.IOException;
    55 import java.io.Reader;
    56 import java.net.URL;
    57 import java.util.ArrayList;
    58 import java.util.List;
    59 import java.util.concurrent.Executor;
    60 import org.netbeans.html.boot.spi.Fn;
    61 import org.netbeans.html.boot.spi.Fn.Presenter;
    62 
    63 /**
    64  * Implementation of {@link Presenter} that delegates to Truffle.
    65  *
    66  * @author Jaroslav Tulach
    67  */
    68 final class TrufflePresenter implements Fn.KeepAlive,
    69     Presenter, Fn.FromJavaScript, Fn.ToJavaScript, Executor {
    70 
    71     private Eval eval;
    72     private WrapArray copy;
    73     private final Executor exc;
    74 
    75     TrufflePresenter(Executor exc, TruffleObject eval) {
    76         this.exc = exc;
    77         this.eval = eval == null ? null : JavaInterop.asJavaFunction(Eval.class, eval);
    78     }
    79 
    80     @Override
    81     public Fn defineFn(String code, String... names) {
    82         return defineImpl(code, names, null);
    83     }
    84 
    85     @Override
    86     public Fn defineFn(String code, String[] names, boolean[] keepAlive) {
    87         return defineImpl(code, names, keepAlive);
    88     }
    89 
    90     private FnImpl defineImpl(String code, String[] names, boolean[] keepAlive) {
    91         StringBuilder sb = new StringBuilder();
    92         sb.append("(function() {\n");
    93         sb.append("  return function(");
    94         String sep = "";
    95         if (names != null) {
    96             for (String n : names) {
    97                 sb.append(sep).append(n);
    98                 sep = ",";
    99             }
   100         }
   101         sb.append(") {\n");
   102         sb.append(code);
   103         sb.append("\n  };\n");
   104         sb.append("})()\n");
   105 
   106         TruffleObject fn = (TruffleObject) getEval().eval(sb.toString());
   107         return new FnImpl(this, fn, names.length);
   108     }
   109 
   110     @Override
   111     public void displayPage(URL page, Runnable onPageLoad) {
   112         if (onPageLoad != null) {
   113             onPageLoad.run();
   114         }
   115     }
   116 
   117     @Override
   118     public void loadScript(Reader code) throws Exception {
   119         Source src = Source.fromReader(code, "unknown.js");
   120         getEval().eval(src.getCode());
   121     }
   122 
   123     interface IsArray {
   124         @MethodMessage(message = "HAS_SIZE")
   125         public boolean hasSize();
   126     }
   127 
   128     interface IsNull {
   129         @MethodMessage(message = "IS_NULL")
   130         public boolean isNull();
   131     }
   132 
   133     interface WrapArray {
   134         public Object copy(Object arr);
   135     }
   136 
   137     interface Eval {
   138         public Object eval(String code);
   139     }
   140 
   141     final Object checkArray(Object val) throws Exception {
   142         if (val instanceof TruffleObject) {
   143             final TruffleObject truffleObj = (TruffleObject)val;
   144             IsArray arrayTest = JavaInterop.asJavaObject(IsArray.class, truffleObj);
   145             try {
   146                 if (arrayTest.hasSize()) {
   147                     List<?> list = JavaInterop.asJavaObject(List.class, truffleObj);
   148                     return list.toArray();
   149                 }
   150             } catch (NegativeArraySizeException ex) {
   151                 // swallow
   152             }
   153         }
   154         return val;
   155     }
   156 
   157     @Override
   158     public Object toJava(Object jsArray) {
   159         if (jsArray instanceof JavaValue) {
   160             jsArray = ((JavaValue) jsArray).get();
   161         }
   162         if (jsArray instanceof TruffleObject) {
   163             IsNull checkNull = JavaInterop.asJavaFunction(IsNull.class, (TruffleObject)jsArray);
   164             try {
   165                 if (checkNull.isNull()) {
   166                     return null;
   167                 }
   168             } catch (NegativeArraySizeException ex) {
   169                 System.err.println("negative size for " + jsArray);
   170                 ex.printStackTrace();
   171             }
   172         }
   173         try {
   174             return checkArray(jsArray);
   175         } catch (Exception ex) {
   176             throw new IllegalStateException(ex);
   177         }
   178     }
   179 
   180     @Override
   181     public Object toJavaScript(Object conv) {
   182         return JavaValue.toJavaScript(conv, getWrap());
   183     }
   184 
   185     @Override
   186     public void execute(final Runnable command) {
   187         if (Fn.activePresenter() == this) {
   188             command.run();
   189             return;
   190         }
   191 
   192         class Wrap implements Runnable {
   193 
   194             public void run() {
   195                 try (Closeable c = Fn.activate(TrufflePresenter.this)) {
   196                     command.run();
   197                 } catch (IOException ex) {
   198                     throw new IllegalStateException(ex);
   199                 }
   200             }
   201         }
   202         final Runnable wrap = new Wrap();
   203         if (exc == null) {
   204             wrap.run();
   205         } else {
   206             exc.execute(wrap);
   207         }
   208     }
   209 
   210     private Eval getEval() {
   211         if (eval == null) {
   212             try {
   213                 PolyglotEngine engine = PolyglotEngine.newBuilder().build();
   214                 TruffleObject fn = (TruffleObject) engine.eval(
   215                     Source.fromText("eval.bind(this)", "eval.js").withMimeType("text/javascript")
   216                 ).get();
   217                 eval = JavaInterop.asJavaFunction(Eval.class, fn);
   218             } catch (IOException ex) {
   219                 throw new IllegalStateException(ex);
   220             }
   221         }
   222         return eval;
   223     }
   224 
   225     private WrapArray getWrap() {
   226         if (copy == null) {
   227             TruffleObject fn = (TruffleObject) getEval().eval("(function(arr) {\n"
   228                 + "  var n = [];\n"
   229                 + "  for (var i = 0; i < arr.length; i++) {\n"
   230                 + "    n[i] = arr[i];\n"
   231                 + "  }\n"
   232                 + "  return n;\n"
   233                 + "}).bind(this)"
   234             );
   235             copy = JavaInterop.asJavaFunction(WrapArray.class, fn);
   236         }
   237         return copy;
   238     }
   239 
   240     private class FnImpl extends Fn {
   241 
   242         private final CallTarget fn;
   243         private final boolean[] keepAlive;
   244 
   245         public FnImpl(Presenter presenter, TruffleObject fn, int arity) {
   246             super(presenter);
   247             this.fn = Truffle.getRuntime().createCallTarget(new FnRootNode(fn, arity));
   248             this.keepAlive = null;
   249         }
   250 
   251         @Override
   252         public Object invoke(Object thiz, Object... args) throws Exception {
   253             List<Object> all = new ArrayList<>(args.length + 1);
   254             all.add(thiz == null ? fn : toJavaScript(thiz));
   255             for (int i = 0; i < args.length; i++) {
   256                 Object conv = args[i];
   257                 conv = toJavaScript(conv);
   258                 all.add(conv);
   259             }
   260             Object ret = fn.call(all.toArray());
   261             if (ret instanceof JavaValue) {
   262                 ret = ((JavaValue)ret).get();
   263             }
   264             if (ret == fn) {
   265                 return null;
   266             }
   267             return toJava(ret);
   268         }
   269     }
   270 
   271     static abstract class JavaLang extends TruffleLanguage<Object> {
   272     }
   273 }