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.IOException; jaroslav@29: import java.io.InputStream; jaroslav@93: import java.net.URL; jtulach@129: import java.util.ArrayList; 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; jtulach@129: import java.util.LinkedHashSet; 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: 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@109: compile(GenJS.class.getClassLoader(), out, names); jaroslav@109: } jaroslav@109: static void compile(ClassLoader l, Appendable out, List names) throws IOException { jaroslav@133: final Map processed = new HashMap(); jaroslav@97: for (String baseClass : names) { jtulach@129: LinkedHashSet toProcess = new LinkedHashSet() { jtulach@129: @Override jtulach@129: public boolean add(String e) { jtulach@129: if (processed.containsKey(e)) { jtulach@129: return false; jtulach@129: } jtulach@129: return super.add(e); jtulach@129: } jtulach@129: }; 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: } jtulach@129: if (name.startsWith("sun/")) { jaroslav@97: processed.put(name, ""); jaroslav@97: continue; jaroslav@97: } jaroslav@109: InputStream is = loadClass(l, 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@109: while (resource.startsWith("/")) { jaroslav@109: resource = resource.substring(1); jaroslav@109: } jaroslav@109: InputStream emul = l.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: jtulach@129: List toInit = new ArrayList(toProcess); jtulach@129: Collections.reverse(toInit); jtulach@129: jtulach@129: for (String clazz : toInit) { jaroslav@133: String initCode = processed.get(clazz); jaroslav@133: if (initCode != null && !initCode.isEmpty()) { jaroslav@97: out.append(initCode).append("\n"); jaroslav@133: processed.put(clazz, ""); jaroslav@91: } jaroslav@91: } jaroslav@133: 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@109: private static InputStream loadClass(ClassLoader l, String name) throws IOException { jaroslav@109: Enumeration en = l.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@136: jaroslav@136: static String toString(String name) throws IOException { jaroslav@136: StringBuilder sb = new StringBuilder(); jaroslav@136: compile(sb, name); jaroslav@136: return sb.toString().toString(); jaroslav@136: } jaroslav@29: }