2 * Back 2 Browser Bytecode Translator
3 * Copyright (C) 2012-2015 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, version 2 of the License.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. Look for COPYING file in the top folder.
16 * If not, see http://opensource.org/licenses/GPL-2.0.
18 package org.apidesign.bck2brwsr.launcher.fximpl;
20 import java.io.BufferedReader;
21 import java.io.Reader;
22 import java.lang.ref.WeakReference;
24 import java.util.ArrayList;
25 import java.util.Arrays;
26 import java.util.Collection;
27 import java.util.List;
28 import java.util.TooManyListenersException;
29 import java.util.concurrent.Executor;
30 import java.util.logging.Level;
31 import java.util.logging.Logger;
32 import javafx.application.Platform;
33 import javafx.beans.value.ChangeListener;
34 import javafx.scene.web.WebEngine;
35 import netscape.javascript.JSObject;
36 import org.netbeans.html.boot.impl.FindResources;
37 import org.netbeans.html.boot.impl.FnUtils;
38 import org.netbeans.html.boot.spi.Fn;
42 * @author Jaroslav Tulach <jtulach@netbeans.org>
44 public final class JVMBridge {
45 static final Logger LOG = Logger.getLogger(JVMBridge.class.getName());
47 private final WebEngine engine;
48 private final ClassLoader cl;
49 private final WebPresenter presenter;
51 private static ClassLoader[] ldrs;
52 private static ChangeListener<Void> onBck2BrwsrLoad;
54 JVMBridge(WebEngine eng) {
56 final ClassLoader p = JVMBridge.class.getClassLoader().getParent();
57 this.presenter = new WebPresenter();
58 this.cl = FnUtils.newLoader(presenter, presenter, p);
61 public static void registerClassLoaders(ClassLoader[] loaders) {
62 ldrs = loaders.clone();
65 public static void addBck2BrwsrLoad(ChangeListener<Void> l) throws TooManyListenersException {
66 if (onBck2BrwsrLoad != null) {
67 throw new TooManyListenersException();
72 public static void onBck2BrwsrLoad() {
73 ChangeListener<Void> l = onBck2BrwsrLoad;
75 l.changed(null, null, null);
79 public Class<?> loadClass(String name) throws ClassNotFoundException {
80 Fn.activate(presenter);
81 return Class.forName(name, true, cl);
84 private final class WebPresenter implements Fn.Presenter,
85 FindResources, Fn.ToJavaScript, Fn.FromJavaScript, Executor, Fn.KeepAlive {
87 public void findResources(String name, Collection<? super URL> results, boolean oneIsEnough) {
88 if (ldrs != null) for (ClassLoader l : ldrs) {
89 URL u = l.getResource(name);
97 public Fn defineFn(String code, String... names) {
98 return defineJSFn(code, names, null);
102 public Fn defineFn(String code, String[] names, boolean[] keepAlive) {
103 return defineJSFn(code, names, keepAlive);
106 private JSFn defineJSFn(String code, String[] names, boolean[] keepAlive) {
107 StringBuilder sb = new StringBuilder();
108 sb.append("(function() {");
109 sb.append(" return function(");
111 if (names != null) for (String n : names) {
112 sb.append(sep).append(n);
120 JSObject x = (JSObject) engine.executeScript(sb.toString());
121 return new JSFn(this, x, keepAlive);
125 public void displayPage(URL page, Runnable onPageLoad) {
126 throw new UnsupportedOperationException("Not supported yet.");
130 public void loadScript(Reader code) throws Exception {
131 BufferedReader r = new BufferedReader(code);
132 StringBuilder sb = new StringBuilder();
134 String l = r.readLine();
138 sb.append(l).append('\n');
140 engine.executeScript(sb.toString());
144 public Object toJava(Object js) {
145 if (js instanceof Weak) {
146 js = ((Weak)js).get();
148 return checkArray(js);
152 public Object toJavaScript(Object toReturn) {
153 if (toReturn instanceof Object[]) {
154 return convertArrays((Object[]) toReturn);
160 public void execute(Runnable command) {
161 if (Platform.isFxApplicationThread()) {
164 Platform.runLater(command);
168 final JSObject convertArrays(Object[] arr) {
169 for (int i = 0; i < arr.length; i++) {
170 if (arr[i] instanceof Object[]) {
171 arr[i] = convertArrays((Object[]) arr[i]);
174 final JSObject wrapArr = (JSObject) wrapArrFn().call("array", arr); // NOI18N
178 private JSObject wrapArrImpl;
180 private final JSObject wrapArrFn() {
181 if (wrapArrImpl == null) {
183 wrapArrImpl = (JSObject) defineJSFn(" var k = {};"
184 + " k.array= function() {"
185 + " return Array.prototype.slice.call(arguments);"
187 + " return k;", null, null
188 ).invokeImpl(null, false);
189 } catch (Exception ex) {
190 throw new IllegalStateException(ex);
196 final Object checkArray(Object val) {
197 int length = ((Number) arraySizeFn().call("array", val, null)).intValue();
201 Object[] arr = new Object[length];
202 arraySizeFn().call("array", val, arr);
205 private JSObject arraySize;
207 private final JSObject arraySizeFn() {
208 if (arraySize == null) {
210 arraySize = (JSObject) defineJSFn(" var k = {};"
211 + " k.array = function(arr, to) {"
212 + " if (to === null) {"
213 + " if (Object.prototype.toString.call(arr) === '[object Array]') return arr.length;"
216 + " var l = arr.length;"
217 + " for (var i = 0; i < l; i++) to[i] = arr[i];"
221 + " return k;", null, null
222 ).invokeImpl(null, false);
223 } catch (Exception ex) {
224 throw new IllegalStateException(ex);
232 private static final class JSFn extends Fn {
233 private final JSObject fn;
234 private final boolean[] keepAlive;
236 private JSFn(WebPresenter cl, JSObject fn, boolean[] keepAlive) {
239 this.keepAlive = keepAlive;
243 public Object invoke(Object thiz, Object... args) throws Exception {
244 return invokeImpl(thiz, true, args);
247 final Object invokeImpl(Object thiz, boolean arrayChecks, Object... args) throws Exception {
249 List<Object> all = new ArrayList<Object>(args.length + 1);
250 all.add(thiz == null ? fn : thiz);
251 for (int i = 0; i < args.length; i++) {
252 Object conv = args[i];
254 if (args[i] instanceof Object[]) {
255 Object[] arr = (Object[]) args[i];
256 conv = ((WebPresenter) presenter()).convertArrays(arr);
258 if (conv != null && keepAlive != null
259 && !keepAlive[i] && !isJSReady(conv)
260 && !conv.getClass().getSimpleName().equals("$JsCallbacks$") // NOI18N
262 conv = new Weak(conv);
267 Object ret = fn.call("call", all.toArray()); // NOI18N
268 if (ret instanceof Weak) {
269 ret = ((Weak) ret).get();
277 return ((WebPresenter) presenter()).checkArray(ret);
281 } catch (Exception t) {
288 private static boolean isJSReady(Object obj) {
292 if (obj instanceof String) {
295 if (obj instanceof Number) {
298 if (obj instanceof JSObject) {
301 if (obj instanceof Character) {
307 private static final class Weak extends WeakReference<Object> {
308 public Weak(Object referent) {
310 assert !(referent instanceof Weak);