jaroslav@106: /** jaroslav@106: * Back 2 Browser Bytecode Translator jaroslav@106: * Copyright (C) 2012 Jaroslav Tulach jaroslav@106: * jaroslav@106: * This program is free software: you can redistribute it and/or modify jaroslav@106: * it under the terms of the GNU General Public License as published by jaroslav@106: * the Free Software Foundation, version 2 of the License. jaroslav@106: * jaroslav@106: * This program is distributed in the hope that it will be useful, jaroslav@106: * but WITHOUT ANY WARRANTY; without even the implied warranty of jaroslav@106: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the jaroslav@106: * GNU General Public License for more details. jaroslav@106: * jaroslav@106: * You should have received a copy of the GNU General Public License jaroslav@106: * along with this program. Look for COPYING file in the top folder. jaroslav@106: * If not, see http://opensource.org/licenses/GPL-2.0. jaroslav@106: */ jaroslav@29: package org.apidesign.vm4brwsr; jaroslav@29: jaroslav@29: import java.io.BufferedWriter; jaroslav@29: import java.io.FileWriter; jaroslav@29: import java.io.IOException; jaroslav@29: import java.io.InputStream; jaroslav@29: import java.io.Writer; jaroslav@93: import java.net.URL; jaroslav@29: import java.util.Arrays; jaroslav@97: import java.util.Collections; jaroslav@93: import java.util.Enumeration; jaroslav@97: import java.util.HashMap; jaroslav@97: import java.util.Iterator; jaroslav@29: import java.util.LinkedList; jaroslav@29: import java.util.List; jaroslav@97: import java.util.Map; jaroslav@29: jaroslav@29: /** Generator of JavaScript from bytecode of classes on classpath of the VM. jaroslav@29: * jaroslav@29: * @author Jaroslav Tulach jaroslav@29: */ jaroslav@29: final class GenJS { jaroslav@29: private GenJS() {} jaroslav@29: jaroslav@29: public static void main(String... args) throws IOException { jaroslav@29: if (args.length < 2) { jaroslav@29: System.err.println("Usage: java -cp ... -jar ... java/lang/Class org/your/App ..."); jaroslav@29: return; jaroslav@29: } jaroslav@29: jaroslav@29: Writer w = new BufferedWriter(new FileWriter(args[0])); jaroslav@29: List classes = Arrays.asList(args).subList(1, args.length); jaroslav@29: compile(w, classes); jaroslav@29: w.close(); jaroslav@29: } jaroslav@29: jaroslav@29: static void compile(Appendable out, String... names) throws IOException { jaroslav@29: compile(out, Arrays.asList(names)); jaroslav@29: } jaroslav@29: static void compile(Appendable out, List names) throws IOException { jaroslav@97: for (String baseClass : names) { jaroslav@97: Map processed = new HashMap(); jaroslav@97: LinkedList toProcess = new LinkedList(); jaroslav@97: toProcess.add(baseClass); jaroslav@97: for (;;) { jaroslav@97: String name = null; jaroslav@97: Iterator it = toProcess.iterator(); jaroslav@97: while (it.hasNext() && name == null) { jaroslav@97: String n = it.next(); jaroslav@97: if (processed.get(n) != null) { jaroslav@97: continue; jaroslav@97: } jaroslav@97: name = n; jaroslav@97: } jaroslav@97: if (name == null) { jaroslav@97: break; jaroslav@97: } jaroslav@97: if (name.startsWith("java/") jaroslav@97: && !name.equals("java/lang/Object") jaroslav@97: && !name.equals("java/lang/Class") jaroslav@104: && !name.equals("java/lang/Math") jaroslav@97: && !name.equals("java/lang/Number") jaroslav@97: && !name.equals("java/lang/Integer") jaroslav@104: && !name.equals("java/lang/Float") jaroslav@104: && !name.equals("java/lang/Double") jaroslav@97: && !name.equals("java/lang/Throwable") jaroslav@97: && !name.equals("java/lang/Exception") jaroslav@97: && !name.equals("java/lang/RuntimeException") jaroslav@97: && !name.equals("java/lang/UnsupportedOperationException") jaroslav@97: && !name.equals("java/lang/String") jaroslav@97: && !name.equals("java/lang/String$CaseInsensitiveComparator") jaroslav@97: && !name.equals("java/lang/StringBuilder") jaroslav@97: && !name.equals("java/lang/AbstractStringBuilder") jaroslav@97: ) { jaroslav@97: processed.put(name, ""); jaroslav@97: continue; jaroslav@97: } jaroslav@97: InputStream is = loadClass(name); jaroslav@97: if (is == null) { jaroslav@97: throw new IOException("Can't find class " + name); jaroslav@97: } jaroslav@97: LinkedList scripts = new LinkedList(); jaroslav@97: try { jaroslav@97: String initCode = ByteCodeToJavaScript.compile(is, out, toProcess, scripts); jaroslav@97: processed.put(name, initCode == null ? "" : initCode); jaroslav@97: } catch (RuntimeException ex) { jaroslav@97: if (out instanceof CharSequence) { jaroslav@97: CharSequence seq = (CharSequence)out; jaroslav@97: int lastBlock = seq.length(); jaroslav@97: while (lastBlock-- >= 0) { jaroslav@97: if (seq.charAt(lastBlock) == '{') { jaroslav@97: break; jaroslav@97: } jaroslav@29: } jaroslav@97: throw new IOException("Error while compiling " + name + "\n" jaroslav@97: + seq.subSequence(lastBlock + 1, seq.length()), ex jaroslav@97: ); jaroslav@97: } else { jaroslav@97: throw new IOException("Error while compiling " + name + "\n" jaroslav@97: + out, ex jaroslav@97: ); jaroslav@29: } jaroslav@97: } jaroslav@97: for (String resource : scripts) { jaroslav@97: InputStream emul = GenJS.class.getResourceAsStream(resource); jaroslav@97: if (emul == null) { jaroslav@97: throw new IOException("Can't find " + resource); jaroslav@97: } jaroslav@97: readResource(emul, out); jaroslav@29: } jaroslav@29: } jaroslav@97: jaroslav@97: Collections.reverse(toProcess); jaroslav@97: jaroslav@97: for (String clazz : toProcess) { jaroslav@97: String initCode = processed.remove(clazz); jaroslav@97: if (initCode != null) { jaroslav@97: out.append(initCode).append("\n"); jaroslav@91: } jaroslav@91: } jaroslav@29: } jaroslav@29: } jaroslav@90: private static void readResource(InputStream emul, Appendable out) throws IOException { jaroslav@90: try { jaroslav@90: int state = 0; jaroslav@90: for (;;) { jaroslav@90: int ch = emul.read(); jaroslav@90: if (ch == -1) { jaroslav@90: break; jaroslav@90: } jaroslav@90: if (ch < 0 || ch > 255) { jaroslav@90: throw new IOException("Invalid char in emulation " + ch); jaroslav@90: } jaroslav@90: switch (state) { jaroslav@90: case 0: jaroslav@90: if (ch == '/') { jaroslav@90: state = 1; jaroslav@90: } else { jaroslav@90: out.append((char)ch); jaroslav@90: } jaroslav@90: break; jaroslav@90: case 1: jaroslav@90: if (ch == '*') { jaroslav@90: state = 2; jaroslav@90: } else { jaroslav@90: out.append('/').append((char)ch); jaroslav@90: state = 0; jaroslav@90: } jaroslav@90: break; jaroslav@90: case 2: jaroslav@90: if (ch == '*') { jaroslav@90: state = 3; jaroslav@90: } jaroslav@90: break; jaroslav@90: case 3: jaroslav@90: if (ch == '/') { jaroslav@90: state = 0; jaroslav@90: } else { jaroslav@90: state = 2; jaroslav@90: } jaroslav@90: break; jaroslav@90: } jaroslav@90: } jaroslav@90: } finally { jaroslav@90: emul.close(); jaroslav@90: } jaroslav@90: } jaroslav@93: jaroslav@93: private static InputStream loadClass(String name) throws IOException { jaroslav@93: Enumeration en = ClassLoader.getSystemClassLoader().getResources(name + ".class"); jaroslav@93: URL u = null; jaroslav@93: while (en.hasMoreElements()) { jaroslav@93: u = en.nextElement(); jaroslav@93: } jaroslav@93: if (u == null) { jaroslav@93: throw new IOException("Can't find " + name); jaroslav@93: } jaroslav@93: return u.openStream(); jaroslav@93: } jaroslav@29: jaroslav@29: }