jaroslav@1041: /** jaroslav@1041: * Back 2 Browser Bytecode Translator jaroslav@1041: * Copyright (C) 2012 Jaroslav Tulach jaroslav@1041: * jaroslav@1041: * This program is free software: you can redistribute it and/or modify jaroslav@1041: * it under the terms of the GNU General Public License as published by jaroslav@1041: * the Free Software Foundation, version 2 of the License. jaroslav@1041: * jaroslav@1041: * This program is distributed in the hope that it will be useful, jaroslav@1041: * but WITHOUT ANY WARRANTY; without even the implied warranty of jaroslav@1041: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the jaroslav@1041: * GNU General Public License for more details. jaroslav@1041: * jaroslav@1041: * You should have received a copy of the GNU General Public License jaroslav@1041: * along with this program. Look for COPYING file in the top folder. jaroslav@1041: * If not, see http://opensource.org/licenses/GPL-2.0. jaroslav@1041: */ jaroslav@1041: package org.apidesign.bck2brwsr.launcher.fximpl; jaroslav@1041: jaroslav@1233: import java.io.BufferedReader; jaroslav@1227: import java.io.Reader; jaroslav@1174: import java.net.URL; jaroslav@1175: import java.util.ArrayList; jaroslav@1183: import java.util.Arrays; jaroslav@1184: import java.util.Collection; jaroslav@1175: import java.util.List; jaroslav@1041: import java.util.TooManyListenersException; jaroslav@1423: import java.util.concurrent.Executor; jaroslav@1423: import java.util.logging.Level; jaroslav@1423: import java.util.logging.Logger; jaroslav@1423: import javafx.application.Platform; jaroslav@1041: import javafx.beans.value.ChangeListener; jaroslav@1174: import javafx.scene.web.WebEngine; jaroslav@1174: import netscape.javascript.JSObject; jaroslav@1418: import org.apidesign.html.boot.spi.Fn; jaroslav@1418: import org.netbeans.html.boot.impl.FindResources; jaroslav@1418: import org.netbeans.html.boot.impl.FnUtils; jaroslav@1041: jaroslav@1041: /** jaroslav@1041: * jaroslav@1041: * @author Jaroslav Tulach jaroslav@1041: */ jaroslav@1041: public final class JVMBridge { jaroslav@1423: static final Logger LOG = Logger.getLogger(JVMBridge.class.getName()); jaroslav@1423: jaroslav@1174: private final WebEngine engine; jaroslav@1184: private final ClassLoader cl; jaroslav@1282: private final WebPresenter presenter; jaroslav@1174: jaroslav@1041: private static ClassLoader[] ldrs; jaroslav@1041: private static ChangeListener onBck2BrwsrLoad; jaroslav@1174: jaroslav@1174: JVMBridge(WebEngine eng) { jaroslav@1174: this.engine = eng; jaroslav@1184: final ClassLoader p = JVMBridge.class.getClassLoader().getParent(); jaroslav@1282: this.presenter = new WebPresenter(); jaroslav@1282: this.cl = FnUtils.newLoader(presenter, presenter, p); jaroslav@1174: } jaroslav@1041: jaroslav@1041: public static void registerClassLoaders(ClassLoader[] loaders) { jaroslav@1041: ldrs = loaders.clone(); jaroslav@1041: } jaroslav@1041: jaroslav@1041: public static void addBck2BrwsrLoad(ChangeListener l) throws TooManyListenersException { jaroslav@1041: if (onBck2BrwsrLoad != null) { jaroslav@1041: throw new TooManyListenersException(); jaroslav@1041: } jaroslav@1041: onBck2BrwsrLoad = l; jaroslav@1041: } jaroslav@1041: jaroslav@1041: public static void onBck2BrwsrLoad() { jaroslav@1041: ChangeListener l = onBck2BrwsrLoad; jaroslav@1041: if (l != null) { jaroslav@1041: l.changed(null, null, null); jaroslav@1041: } jaroslav@1041: } jaroslav@1041: jaroslav@1041: public Class loadClass(String name) throws ClassNotFoundException { jaroslav@1418: Fn.activate(presenter); jaroslav@1174: return Class.forName(name, true, cl); jaroslav@1174: } jaroslav@1174: jaroslav@1423: private final class WebPresenter jaroslav@1429: implements FindResources, Fn.Presenter, Fn.ToJavaScript, Fn.FromJavaScript, Executor { jaroslav@1174: @Override jaroslav@1184: public void findResources(String name, Collection results, boolean oneIsEnough) { jaroslav@1174: if (ldrs != null) for (ClassLoader l : ldrs) { jaroslav@1174: URL u = l.getResource(name); jaroslav@1174: if (u != null) { jaroslav@1184: results.add(u); jaroslav@1174: } jaroslav@1041: } jaroslav@1175: } jaroslav@1174: jaroslav@1174: @Override jaroslav@1184: public Fn defineFn(String code, String... names) { jaroslav@1423: return defineJSFn(code, names); jaroslav@1423: } jaroslav@1423: private JSFn defineJSFn(String code, String... names) { jaroslav@1174: StringBuilder sb = new StringBuilder(); jaroslav@1174: sb.append("(function() {"); jaroslav@1183: sb.append(" return function("); jaroslav@1174: String sep = ""; jaroslav@1174: for (String n : names) { jaroslav@1174: sb.append(sep).append(n); jaroslav@1174: sep = ","; jaroslav@1174: } jaroslav@1174: sb.append(") {\n"); jaroslav@1174: sb.append(code); jaroslav@1174: sb.append("};"); jaroslav@1174: sb.append("})()"); jaroslav@1174: jaroslav@1174: JSObject x = (JSObject) engine.executeScript(sb.toString()); jaroslav@1282: return new JSFn(this, x); jaroslav@1041: } jaroslav@1184: jaroslav@1184: @Override jaroslav@1184: public void displayPage(URL page, Runnable onPageLoad) { jaroslav@1233: throw new UnsupportedOperationException("Not supported yet."); jaroslav@1184: } jaroslav@1227: jaroslav@1227: @Override jaroslav@1227: public void loadScript(Reader code) throws Exception { jaroslav@1233: BufferedReader r = new BufferedReader(code); jaroslav@1233: StringBuilder sb = new StringBuilder(); jaroslav@1233: for (;;) { jaroslav@1233: String l = r.readLine(); jaroslav@1233: if (l == null) { jaroslav@1233: break; jaroslav@1233: } jaroslav@1233: sb.append(l).append('\n'); jaroslav@1233: } jaroslav@1233: engine.executeScript(sb.toString()); jaroslav@1227: } jaroslav@1423: jaroslav@1423: @Override jaroslav@1429: public Object toJava(Object js) { jaroslav@1429: return checkArray(js); jaroslav@1429: } jaroslav@1429: jaroslav@1429: @Override jaroslav@1423: public Object toJavaScript(Object toReturn) { jaroslav@1423: if (toReturn instanceof Object[]) { jaroslav@1423: return convertArrays((Object[]) toReturn); jaroslav@1423: } jaroslav@1423: return toReturn; jaroslav@1423: } jaroslav@1423: jaroslav@1423: @Override jaroslav@1423: public void execute(Runnable command) { jaroslav@1423: if (Platform.isFxApplicationThread()) { jaroslav@1423: command.run(); jaroslav@1423: } else { jaroslav@1423: Platform.runLater(command); jaroslav@1423: } jaroslav@1423: } jaroslav@1423: jaroslav@1423: final JSObject convertArrays(Object[] arr) { jaroslav@1423: for (int i = 0; i < arr.length; i++) { jaroslav@1423: if (arr[i] instanceof Object[]) { jaroslav@1423: arr[i] = convertArrays((Object[]) arr[i]); jaroslav@1423: } jaroslav@1423: } jaroslav@1423: final JSObject wrapArr = (JSObject) wrapArrFn().call("array", arr); // NOI18N jaroslav@1423: return wrapArr; jaroslav@1423: } jaroslav@1423: jaroslav@1423: private JSObject wrapArrImpl; jaroslav@1423: jaroslav@1423: private final JSObject wrapArrFn() { jaroslav@1423: if (wrapArrImpl == null) { jaroslav@1423: try { jaroslav@1423: wrapArrImpl = (JSObject) defineJSFn(" var k = {};" jaroslav@1423: + " k.array= function() {" jaroslav@1423: + " return Array.prototype.slice.call(arguments);" jaroslav@1423: + " };" jaroslav@1423: + " return k;" jaroslav@1423: ).invokeImpl(null, false); jaroslav@1423: } catch (Exception ex) { jaroslav@1423: throw new IllegalStateException(ex); jaroslav@1423: } jaroslav@1423: } jaroslav@1423: return wrapArrImpl; jaroslav@1423: } jaroslav@1423: jaroslav@1423: final Object checkArray(Object val) { jaroslav@1423: int length = ((Number) arraySizeFn().call("array", val, null)).intValue(); jaroslav@1423: if (length == -1) { jaroslav@1423: return val; jaroslav@1423: } jaroslav@1423: Object[] arr = new Object[length]; jaroslav@1423: arraySizeFn().call("array", val, arr); jaroslav@1423: return arr; jaroslav@1423: } jaroslav@1423: private JSObject arraySize; jaroslav@1423: jaroslav@1423: private final JSObject arraySizeFn() { jaroslav@1423: if (arraySize == null) { jaroslav@1423: try { jaroslav@1423: arraySize = (JSObject) defineJSFn(" var k = {};" jaroslav@1423: + " k.array = function(arr, to) {" jaroslav@1423: + " if (to === null) {" jaroslav@1423: + " if (Object.prototype.toString.call(arr) === '[object Array]') return arr.length;" jaroslav@1423: + " else return -1;" jaroslav@1423: + " } else {" jaroslav@1423: + " var l = arr.length;" jaroslav@1423: + " for (var i = 0; i < l; i++) to[i] = arr[i];" jaroslav@1423: + " return l;" jaroslav@1423: + " }" jaroslav@1423: + " };" jaroslav@1423: + " return k;" jaroslav@1423: ).invokeImpl(null, false); jaroslav@1423: } catch (Exception ex) { jaroslav@1423: throw new IllegalStateException(ex); jaroslav@1423: } jaroslav@1423: } jaroslav@1423: return arraySize; jaroslav@1423: } jaroslav@1423: jaroslav@1174: } jaroslav@1174: jaroslav@1174: private static final class JSFn extends Fn { jaroslav@1174: private final JSObject fn; jaroslav@1174: jaroslav@1282: private JSFn(WebPresenter cl, JSObject fn) { jaroslav@1282: super(cl); jaroslav@1174: this.fn = fn; jaroslav@1174: } jaroslav@1282: jaroslav@1174: @Override jaroslav@1181: public Object invoke(Object thiz, Object... args) throws Exception { jaroslav@1423: return invokeImpl(thiz, true, args); jaroslav@1423: } jaroslav@1423: jaroslav@1423: final Object invokeImpl(Object thiz, boolean arrayChecks, Object... args) throws Exception { jaroslav@1179: try { jaroslav@1183: List all = new ArrayList(args.length + 1); jaroslav@1183: all.add(thiz == null ? fn : thiz); jaroslav@1423: for (int i = 0; i < args.length; i++) { jaroslav@1423: if (arrayChecks && args[i] instanceof Object[]) { jaroslav@1423: Object[] arr = (Object[]) args[i]; jaroslav@1423: Object conv = ((WebPresenter) presenter()).convertArrays(arr); jaroslav@1423: args[i] = conv; jaroslav@1423: } jaroslav@1423: all.add(args[i]); jaroslav@1423: } jaroslav@1183: Object ret = fn.call("call", all.toArray()); // NOI18N jaroslav@1423: if (ret == fn) { jaroslav@1423: return null; jaroslav@1423: } jaroslav@1423: if (!arrayChecks) { jaroslav@1423: return ret; jaroslav@1423: } jaroslav@1423: return ((WebPresenter) presenter()).checkArray(ret); jaroslav@1179: } catch (Error t) { jaroslav@1179: t.printStackTrace(); jaroslav@1179: throw t; jaroslav@1179: } catch (Exception t) { jaroslav@1179: t.printStackTrace(); jaroslav@1179: throw t; jaroslav@1179: } jaroslav@1174: } jaroslav@1041: } jaroslav@1041: }