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