jaroslav@323: /** jaroslav@323: * Back 2 Browser Bytecode Translator jaroslav@323: * Copyright (C) 2012 Jaroslav Tulach jaroslav@323: * jaroslav@323: * This program is free software: you can redistribute it and/or modify jaroslav@323: * it under the terms of the GNU General Public License as published by jaroslav@323: * the Free Software Foundation, version 2 of the License. jaroslav@323: * jaroslav@323: * This program is distributed in the hope that it will be useful, jaroslav@323: * but WITHOUT ANY WARRANTY; without even the implied warranty of jaroslav@323: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the jaroslav@323: * GNU General Public License for more details. jaroslav@323: * jaroslav@323: * You should have received a copy of the GNU General Public License jaroslav@323: * along with this program. Look for COPYING file in the top folder. jaroslav@323: * If not, see http://opensource.org/licenses/GPL-2.0. jaroslav@323: */ jaroslav@622: package org.apidesign.bck2brwsr.launcher.impl; jaroslav@323: jaroslav@360: import java.io.IOException; jaroslav@360: import java.io.InputStream; jaroslav@332: import java.lang.reflect.InvocationTargetException; jaroslav@323: import java.lang.reflect.Method; jaroslav@412: import java.lang.reflect.Modifier; jaroslav@332: import java.net.URL; jaroslav@360: import java.util.Enumeration; jaroslav@323: import org.apidesign.bck2brwsr.core.JavaScriptBody; jaroslav@323: jaroslav@323: /** jaroslav@323: * jaroslav@323: * @author Jaroslav Tulach jaroslav@323: */ jaroslav@323: public class Console { jaroslav@622: private Console() { jaroslav@622: } jaroslav@517: static { jaroslav@517: turnAssetionStatusOn(); jaroslav@517: } jaroslav@323: jaroslav@323: @JavaScriptBody(args = {"id", "attr"}, body = jaroslav@323: "return window.document.getElementById(id)[attr].toString();") jaroslav@323: private static native Object getAttr(String id, String attr); jaroslav@323: jaroslav@323: @JavaScriptBody(args = {"id", "attr", "value"}, body = jaroslav@323: "window.document.getElementById(id)[attr] = value;") jaroslav@323: private static native void setAttr(String id, String attr, Object value); jaroslav@366: jaroslav@381: @JavaScriptBody(args = {}, body = "return; window.close();") jaroslav@366: private static native void closeWindow(); jaroslav@342: jaroslav@343: private static void log(String newText) { jaroslav@526: String id = "bck2brwsr.result"; jaroslav@343: String attr = "value"; jaroslav@342: setAttr(id, attr, getAttr(id, attr) + "\n" + newText); jaroslav@343: setAttr(id, "scrollTop", getAttr(id, "scrollHeight")); jaroslav@342: } jaroslav@323: jaroslav@323: public static void execute() throws Exception { jaroslav@323: String clazz = (String) getAttr("clazz", "value"); jaroslav@323: String method = (String) getAttr("method", "value"); jaroslav@332: Object res = invokeMethod(clazz, method); jaroslav@526: setAttr("bck2brwsr.result", "value", res); jaroslav@332: } jaroslav@519: jaroslav@519: @JavaScriptBody(args = { "url", "callback", "arr" }, body = "" jaroslav@519: + "var request = new XMLHttpRequest();\n" jaroslav@519: + "request.open('GET', url, true);\n" jaroslav@519: + "request.onreadystatechange = function() {\n" jaroslav@519: + " if (this.readyState!==4) return;\n" jaroslav@519: + " arr[0] = this.responseText;\n" jaroslav@519: + " callback.run__V();\n" jaroslav@519: + "};" jaroslav@519: + "request.send();" jaroslav@519: ) jaroslav@519: private static native void loadText(String url, Runnable callback, String[] arr) throws IOException; jaroslav@332: jaroslav@519: public static void harness(String url) throws IOException { jaroslav@343: log("Connecting to " + url); jaroslav@519: Request r = new Request(url); jaroslav@519: } jaroslav@519: jaroslav@519: private static class Request implements Runnable { jaroslav@519: private final String[] arr = { null }; jaroslav@519: private final String url; jaroslav@519: jaroslav@519: private Request(String url) throws IOException { jaroslav@519: this.url = url; jaroslav@519: loadText(url, this, arr); jaroslav@519: } jaroslav@519: jaroslav@519: @Override jaroslav@519: public void run() { jaroslav@519: try { jaroslav@519: String data = arr[0]; jaroslav@344: log("\nGot \"" + data + "\""); jaroslav@519: jaroslav@519: if (data == null) { jaroslav@519: log("Some error exiting"); jaroslav@519: closeWindow(); jaroslav@519: return; jaroslav@519: } jaroslav@519: jaroslav@342: if (data.isEmpty()) { jaroslav@343: log("No data, exiting"); jaroslav@366: closeWindow(); jaroslav@519: return; jaroslav@342: } jaroslav@342: jaroslav@342: Case c = Case.parseData(data); jaroslav@526: if (c.getHtmlFragment() != null) { jaroslav@526: setAttr("bck2brwsr.fragment", "innerHTML", c.getHtmlFragment()); jaroslav@526: } jaroslav@344: log("Invoking " + c.getClassName() + '.' + c.getMethodName() + " as request: " + c.getRequestId()); jaroslav@342: jaroslav@342: Object result = invokeMethod(c.getClassName(), c.getMethodName()); jaroslav@355: jaroslav@526: setAttr("bck2brwsr.fragment", "innerHTML", ""); jaroslav@344: log("Result: " + result); jaroslav@381: jaroslav@381: result = encodeURL("" + result); jaroslav@381: jaroslav@355: log("Sending back: " + url + "?request=" + c.getRequestId() + "&result=" + result); jaroslav@519: String u = url + "?request=" + c.getRequestId() + "&result=" + result; jaroslav@519: jaroslav@519: loadText(u, this, arr); jaroslav@519: jaroslav@519: } catch (Exception ex) { jaroslav@707: log(ex.getClass().getName() + ":" + ex.getMessage()); jaroslav@342: } jaroslav@332: } jaroslav@332: } jaroslav@356: jaroslav@381: private static String encodeURL(String r) { jaroslav@381: StringBuilder sb = new StringBuilder(); jaroslav@381: for (int i = 0; i < r.length(); i++) { jaroslav@381: int ch = r.charAt(i); jaroslav@381: if (ch < 32 || ch == '%' || ch == '+') { jaroslav@381: sb.append("%").append(("0" + Integer.toHexString(ch)).substring(0, 2)); jaroslav@381: } else { jaroslav@381: if (ch == 32) { jaroslav@381: sb.append("+"); jaroslav@381: } else { jaroslav@381: sb.append((char)ch); jaroslav@381: } jaroslav@381: } jaroslav@381: } jaroslav@381: return sb.toString(); jaroslav@381: } jaroslav@381: jaroslav@412: static String invoke(String clazz, String method) throws ClassNotFoundException, InvocationTargetException, IllegalAccessException, InstantiationException { jaroslav@356: final Object r = invokeMethod(clazz, method); jaroslav@356: return r == null ? "null" : r.toString().toString(); jaroslav@356: } jaroslav@323: jaroslav@360: /** Helper method that inspects the classpath and loads given resource jaroslav@360: * (usually a class file). Used while running tests in Rhino. jaroslav@360: * jaroslav@360: * @param name resource name to find jaroslav@360: * @return the array of bytes in the given resource jaroslav@360: * @throws IOException I/O in case something goes wrong jaroslav@360: */ jaroslav@360: public static byte[] read(String name) throws IOException { jaroslav@360: URL u = null; jaroslav@360: Enumeration en = Console.class.getClassLoader().getResources(name); jaroslav@360: while (en.hasMoreElements()) { jaroslav@360: u = en.nextElement(); jaroslav@360: } jaroslav@360: if (u == null) { jaroslav@360: throw new IOException("Can't find " + name); jaroslav@360: } jaroslav@360: try (InputStream is = u.openStream()) { jaroslav@360: byte[] arr; jaroslav@360: arr = new byte[is.available()]; jaroslav@360: int offset = 0; jaroslav@360: while (offset < arr.length) { jaroslav@360: int len = is.read(arr, offset, arr.length - offset); jaroslav@360: if (len == -1) { jaroslav@360: throw new IOException("Can't read " + name); jaroslav@360: } jaroslav@360: offset += len; jaroslav@360: } jaroslav@360: return arr; jaroslav@360: } jaroslav@360: } jaroslav@360: jaroslav@332: private static Object invokeMethod(String clazz, String method) jaroslav@332: throws ClassNotFoundException, InvocationTargetException, jaroslav@412: SecurityException, IllegalAccessException, IllegalArgumentException, jaroslav@412: InstantiationException { jaroslav@323: Method found = null; jaroslav@323: Class c = Class.forName(clazz); jaroslav@323: for (Method m : c.getMethods()) { jaroslav@323: if (m.getName().equals(method)) { jaroslav@323: found = m; jaroslav@323: } jaroslav@323: } jaroslav@323: Object res; jaroslav@323: if (found != null) { jaroslav@413: try { jaroslav@413: if ((found.getModifiers() & Modifier.STATIC) != 0) { jaroslav@413: res = found.invoke(null); jaroslav@413: } else { jaroslav@413: res = found.invoke(c.newInstance()); jaroslav@413: } jaroslav@531: } catch (Throwable ex) { jaroslav@413: res = ex.getClass().getName() + ":" + ex.getMessage(); jaroslav@412: } jaroslav@323: } else { jaroslav@323: res = "Can't find method " + method + " in " + clazz; jaroslav@323: } jaroslav@332: return res; jaroslav@323: } jaroslav@517: jaroslav@517: @JavaScriptBody(args = {}, body = "vm.desiredAssertionStatus = true;") jaroslav@517: private static void turnAssetionStatusOn() { jaroslav@517: } jaroslav@342: jaroslav@342: private static final class Case { jaroslav@342: private final Object data; jaroslav@342: jaroslav@342: private Case(Object data) { jaroslav@342: this.data = data; jaroslav@342: } jaroslav@342: jaroslav@342: public static Case parseData(String s) { jaroslav@342: return new Case(toJSON(s)); jaroslav@342: } jaroslav@342: jaroslav@342: public String getMethodName() { jaroslav@342: return value("methodName", data); jaroslav@342: } jaroslav@342: jaroslav@342: public String getClassName() { jaroslav@342: return value("className", data); jaroslav@342: } jaroslav@342: jaroslav@342: public String getRequestId() { jaroslav@342: return value("request", data); jaroslav@342: } jaroslav@526: jaroslav@526: public String getHtmlFragment() { jaroslav@526: return value("html", data); jaroslav@526: } jaroslav@342: jaroslav@342: @JavaScriptBody(args = "s", body = "return eval('(' + s + ')');") jaroslav@342: private static native Object toJSON(String s); jaroslav@342: jaroslav@526: @JavaScriptBody(args = {"p", "d"}, body = jaroslav@526: "var v = d[p];\n" jaroslav@526: + "if (typeof v === 'undefined') return null;\n" jaroslav@526: + "return v.toString();" jaroslav@526: ) jaroslav@342: private static native String value(String p, Object d); jaroslav@342: } jaroslav@323: }