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