All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apidesign.bck2brwsr.launcher.JSLauncher Maven / Gradle / Ivy

The newest version!
/**
 * Back 2 Browser Bytecode Translator
 * Copyright (C) 2012-2018 Jaroslav Tulach 
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, version 2 of the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. Look for COPYING file in the top folder.
 * If not, see http://opensource.org/licenses/GPL-2.0.
 */
package org.apidesign.bck2brwsr.launcher;

import org.apidesign.bck2brwsr.launcher.impl.Console;
import java.io.IOException;
import java.io.InputStream;
import java.net.JarURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.script.Bindings;
import javax.script.Invocable;
import javax.script.ScriptContext;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import org.apidesign.bck2brwsr.core.ExtraJavaScript;
import org.apidesign.vm4brwsr.Bck2Brwsr;

/**
 * Tests execution in Java's internal scripting engine.
 */
@ExtraJavaScript(processByteCode = false, resource="")
final class JSLauncher extends Launcher {
    private static final Logger LOG = Logger.getLogger(JSLauncher.class.getName());

    private Set loaders = new LinkedHashSet<>();
    private Invocable code;
    private List codeSeq = new LinkedList<>();
    private Object console;
    private final List executed = new ArrayList<>();

    JSLauncher() {
        addClassLoader(Bck2Brwsr.class.getClassLoader());
    }

    @Override InvocationContext runMethod(InvocationContext mi) {
        loaders.add(mi.clazz.getClassLoader());
        executed.add(mi);
        try {
            long time = System.currentTimeMillis();
            LOG.log(Level.FINE, "Invoking {0}.{1}", new Object[]{mi.clazz.getName(), mi.methodName});
            String res = code.invokeMethod(
                console,
                "invokeTest__Ljava_lang_String_2Ljava_lang_String_2Ljava_lang_String_2",
                mi.clazz.getName(), mi.methodName).toString();
            time = System.currentTimeMillis() - time;
            LOG.log(Level.FINE, "Resut of {0}.{1} = {2} in {3} ms", new Object[]{mi.clazz.getName(), mi.methodName, res, time});
            mi.result(res, (int)time, null);
        } catch (ScriptException | NoSuchMethodException ex) {
            mi.result(null, -1, ex);
        }
        return mi;
    }

    public void addClassLoader(ClassLoader url) {
        this.loaders.add(url);
    }

    @Override
    public void initialize() throws IOException {
        try {
            initRhino();
        } catch (Exception ex) {
            if (ex instanceof IOException) {
                throw (IOException)ex;
            }
            if (ex instanceof RuntimeException) {
                throw (RuntimeException)ex;
            }
            throw new IOException(ex);
        }
    }

    private static ScriptEngine createEngine() {
        ScriptEngineManager sem = new ScriptEngineManager();
        ScriptEngine truffleJS = sem.getEngineByName("Graal.js");
        if (truffleJS != null) {
            Bindings b = truffleJS.getBindings(ScriptContext.ENGINE_SCOPE);
            b.put("polyglot.js.allowHostAccess", true);
            return truffleJS;
        }
        ScriptEngine js = sem.getEngineByExtension("js");
        return js;
    }

    private void initRhino() throws IOException, ScriptException, NoSuchMethodException {
        StringBuilder sb = new StringBuilder();
        Bck2Brwsr.newCompiler()
            .standalone(false)
            .resources(new Res())
            .generate(sb);

        ScriptEngine mach = createEngine();
        if (!(mach instanceof Invocable)) {
            throw new IOException("It is invocable object: " + mach);
        }
        code = (Invocable) mach;

        sb.append(
              "\n"
            + "function initVM(console) {\n"
            + "  return new bck2brwsr(function(res, skip) {\n"
            + "    if (!console) { return null; }\n"
            + "    return console.read(res, skip);\n"
            + "  });\n"
            + "};\n"
        );
        sb.append("\nthis.atob || (this.atob = function(s) { return new String(org.apidesign.bck2brwsr.launcher.impl.Console.parseBase64Binary(s)); })\n");
        codeSeq.add(new Eval(null, sb));

        Object vm = code.invokeFunction("initVM", new Ldr());
        console = code.invokeMethod(vm, "loadClass", Console.class.getName());
    }

    @Override
    public void shutdown() throws IOException {
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        for (Seq seq : codeSeq) {
            sb.append(seq.toString());
        }

        sb.append("(function() {\n");
        sb.append("  function register(name, fn) {\n");
        sb.append("    var element = document.createElement('button');\n");
        sb.append("    element.innerHTML = name;\n");
        sb.append("    document.body.appendChild(element);\n");
        sb.append("    element.addEventListener('click', fn);\n");
        sb.append("  }\n");
        sb.append("  var outVM = initVM();\n");
        for (InvocationContext mi : executed) {
            sb.append("  register('").append(mi.methodName).append("', function () {\n");
            sb.append("    var c = outVM.loadClass('").append(Console.class.getName()).append("');\n");
            sb.append("    c.invokeTest__Ljava_lang_String_2Ljava_lang_String_2Ljava_lang_String_2(\n");
            sb.append("      '").append(mi.clazz.getName()).append("',\n");
            sb.append("      '").append(mi.methodName).append("'\n");
            sb.append("    );\n");
            sb.append("  });\n");
        }
        sb.append("})(this);\n");

        return sb.toString();
    }

    private static URL getResource(String resource, int skip) throws IOException {
        URL u = null;
        Enumeration en = Console.class.getClassLoader().getResources(resource);
        while (en.hasMoreElements()) {
            final URL now = en.nextElement();
            if (--skip < 0) {
                u = now;
                break;
            }
        }
        return u;
    }

    public final class Ldr {
        public byte[] read(String name, int skip) throws IOException, ScriptException {
            URL u = null;
            if (!name.endsWith(".class")) {
                u = getResource(name, skip);
                if (u == null) {
                    return null;
                }
                try (InputStream is = u.openStream()) {
                    byte[] arr;
                    arr = new byte[is.available()];
                    int offset = 0;
                    while (offset < arr.length) {
                        int len = is.read(arr, offset, arr.length - offset);
                        if (len == -1) {
                            throw new IOException("Can't read " + name);
                        }
                        offset += len;
                    }
                    return arr;
                }
            }

            Enumeration en = Console.class.getClassLoader().getResources(name);
            while (en.hasMoreElements()) {
                u = en.nextElement();
            }
            if (u == null) {
                throw new IOException("Can't find " + name);
            }
            if ("jar".equals(u.getProtocol())) {
                u = ((JarURLConnection)u.openConnection()).getJarFileURL();
            }
            String compile = CompileCP.compileFromClassPath(u, null);
            LOG.fine("// eval: " + u);
            LOG.log(Level.FINEST, compile);
            Eval eval = new Eval(u, compile);
            codeSeq.add(eval);
            LOG.finer("// end of " + u);
            return null;
        }
    }

    private interface Seq {
    }

    private class Eval implements Seq {
        public final URL src;
        public final String txt;

        public Eval(URL src, Object c) throws ScriptException {
            this.src = src;
            this.txt = c.toString();
            ((ScriptEngine)JSLauncher.this.code).eval(this.txt);
        }

        @Override
        public String toString() {
            StringBuilder sb = new StringBuilder();
            if (src != null) {
                sb.append("// loading ").append(src).append("\n");
            }
            sb.append(txt);
            if (src != null) {
                sb.append("// end of ").append(src).append("\n");
            }
            return sb.toString();
        }


    }

    private class Res implements Bck2Brwsr.Resources {
        @Override
        public InputStream get(String resource) throws IOException {
            for (ClassLoader l : loaders) {
                URL u = null;
                Enumeration en = l.getResources(resource);
                while (en.hasMoreElements()) {
                    u = en.nextElement();
                }
                if (u != null) {
                    if (u.toExternalForm().contains("/rt.jar")) {
                        LOG.log(Level.WARNING, "No fallback to bootclasspath for {0}", u);
                        return null;
                    }
                    return u.openStream();
                }
            }
            throw new IOException("Can't find " + resource);
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy