boot-truffle/src/main/java/net/java/html/boot/truffle/TrufflePresenter.java
author Jaroslav Tulach <jtulach@netbeans.org>
Sat, 16 Jul 2016 08:11:49 +0200
branchTruffle
changeset 1098 20246cbd02c3
parent 1097 62f1d7c47a60
child 1102 86f56d464357
permissions -rw-r--r--
Non-execute messages cannot be sent via JavaInterop.asJavaFunction currently. Sending them directly with help of RootNode/CallTarget.
     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.source.Source;
    51 import com.oracle.truffle.api.vm.PolyglotEngine;
    52 import java.io.Closeable;
    53 import java.io.IOException;
    54 import java.io.Reader;
    55 import java.net.URL;
    56 import java.util.ArrayList;
    57 import java.util.List;
    58 import java.util.concurrent.Executor;
    59 import org.netbeans.html.boot.spi.Fn;
    60 import org.netbeans.html.boot.spi.Fn.Presenter;
    61 
    62 /**
    63  * Implementation of {@link Presenter} that delegates to Truffle.
    64  *
    65  * @author Jaroslav Tulach
    66  */
    67 final class TrufflePresenter implements Fn.KeepAlive,
    68     Presenter, Fn.FromJavaScript, Fn.ToJavaScript, Executor {
    69 
    70     private Eval eval;
    71     private WrapArray copy;
    72     private final Executor exc;
    73     private final CallTarget isNull;
    74     private final CallTarget isArray;
    75 
    76     TrufflePresenter(Executor exc, TruffleObject eval) {
    77         this.exc = exc;
    78         this.eval = eval == null ? null : JavaInterop.asJavaFunction(Eval.class, eval);
    79         this.isNull = Truffle.getRuntime().createCallTarget(new IsNullNode());
    80         this.isArray = Truffle.getRuntime().createCallTarget(new IsArrayNode());
    81     }
    82 
    83     @Override
    84     public Fn defineFn(String code, String... names) {
    85         return defineImpl(code, names, null);
    86     }
    87 
    88     @Override
    89     public Fn defineFn(String code, String[] names, boolean[] keepAlive) {
    90         return defineImpl(code, names, keepAlive);
    91     }
    92 
    93     private FnImpl defineImpl(String code, String[] names, boolean[] keepAlive) {
    94         StringBuilder sb = new StringBuilder();
    95         sb.append("(function() {\n");
    96         sb.append("  return function(");
    97         String sep = "";
    98         if (names != null) {
    99             for (String n : names) {
   100                 sb.append(sep).append(n);
   101                 sep = ",";
   102             }
   103         }
   104         sb.append(") {\n");
   105         sb.append(code);
   106         sb.append("\n  };\n");
   107         sb.append("})()\n");
   108 
   109         TruffleObject fn = (TruffleObject) getEval().eval(sb.toString());
   110         return new FnImpl(this, fn, names.length);
   111     }
   112 
   113     @Override
   114     public void displayPage(URL page, Runnable onPageLoad) {
   115         if (onPageLoad != null) {
   116             onPageLoad.run();
   117         }
   118     }
   119 
   120     @Override
   121     public void loadScript(Reader code) throws Exception {
   122         Source src = Source.fromReader(code, "unknown.js");
   123         getEval().eval(src.getCode());
   124     }
   125 
   126     interface WrapArray {
   127         public Object copy(Object arr);
   128     }
   129 
   130     interface Eval {
   131         public Object eval(String code);
   132     }
   133 
   134     final Object checkArray(Object val) throws Exception {
   135         if (val instanceof TruffleObject) {
   136             final TruffleObject truffleObj = (TruffleObject)val;
   137             boolean hasSize = (boolean) isArray.call(truffleObj);
   138             if (hasSize) {
   139                 List<?> list = JavaInterop.asJavaObject(List.class, truffleObj);
   140                 Object[] arr = list.toArray();
   141                 for (int i = 0; i < arr.length; i++) {
   142                     arr[i] = toJava(arr[i]);
   143                 }
   144                 return arr;
   145             }
   146         }
   147         return val;
   148     }
   149 
   150     @Override
   151     public Object toJava(Object jsArray) {
   152         if (jsArray instanceof JavaValue) {
   153             jsArray = ((JavaValue) jsArray).get();
   154         }
   155         if (jsArray instanceof TruffleObject) {
   156             boolean checkNull = (boolean) isNull.call(jsArray);
   157             if (checkNull) {
   158                 return null;
   159             }
   160         }
   161         try {
   162             return checkArray(jsArray);
   163         } catch (Exception ex) {
   164             throw new IllegalStateException(ex);
   165         }
   166     }
   167 
   168     @Override
   169     public Object toJavaScript(Object conv) {
   170         return JavaValue.toJavaScript(conv, getWrap());
   171     }
   172 
   173     @Override
   174     public void execute(final Runnable command) {
   175         if (Fn.activePresenter() == this) {
   176             command.run();
   177             return;
   178         }
   179 
   180         class Wrap implements Runnable {
   181 
   182             public void run() {
   183                 try (Closeable c = Fn.activate(TrufflePresenter.this)) {
   184                     command.run();
   185                 } catch (IOException ex) {
   186                     throw new IllegalStateException(ex);
   187                 }
   188             }
   189         }
   190         final Runnable wrap = new Wrap();
   191         if (exc == null) {
   192             wrap.run();
   193         } else {
   194             exc.execute(wrap);
   195         }
   196     }
   197 
   198     private Eval getEval() {
   199         if (eval == null) {
   200             try {
   201                 PolyglotEngine engine = PolyglotEngine.newBuilder().build();
   202                 TruffleObject fn = (TruffleObject) engine.eval(
   203                     Source.fromText("eval.bind(this)", "eval.js").withMimeType("text/javascript")
   204                 ).get();
   205                 eval = JavaInterop.asJavaFunction(Eval.class, fn);
   206             } catch (IOException ex) {
   207                 throw new IllegalStateException(ex);
   208             }
   209         }
   210         return eval;
   211     }
   212 
   213     private WrapArray getWrap() {
   214         if (copy == null) {
   215             TruffleObject fn = (TruffleObject) getEval().eval("(function(arr) {\n"
   216                 + "  var n = [];\n"
   217                 + "  for (var i = 0; i < arr.length; i++) {\n"
   218                 + "    n[i] = arr[i];\n"
   219                 + "  }\n"
   220                 + "  return n;\n"
   221                 + "}).bind(this)"
   222             );
   223             copy = JavaInterop.asJavaFunction(WrapArray.class, fn);
   224         }
   225         return copy;
   226     }
   227 
   228     private class FnImpl extends Fn {
   229 
   230         private final CallTarget fn;
   231         private final boolean[] keepAlive;
   232 
   233         public FnImpl(Presenter presenter, TruffleObject fn, int arity) {
   234             super(presenter);
   235             this.fn = Truffle.getRuntime().createCallTarget(new FnRootNode(fn, arity));
   236             this.keepAlive = null;
   237         }
   238 
   239         @Override
   240         public Object invoke(Object thiz, Object... args) throws Exception {
   241             List<Object> all = new ArrayList<>(args.length + 1);
   242             all.add(thiz == null ? fn : toJavaScript(thiz));
   243             for (int i = 0; i < args.length; i++) {
   244                 Object conv = args[i];
   245                 conv = toJavaScript(conv);
   246                 all.add(conv);
   247             }
   248             Object ret = fn.call(all.toArray());
   249             if (ret instanceof JavaValue) {
   250                 ret = ((JavaValue)ret).get();
   251             }
   252             if (ret == fn) {
   253                 return null;
   254             }
   255             return toJava(ret);
   256         }
   257     }
   258 
   259     static abstract class JavaLang extends TruffleLanguage<Object> {
   260     }
   261 }