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; jaroslav@93: import java.util.Enumeration; 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@272: class GenJS extends ByteCodeToJavaScript { jtulach@162: public GenJS(Appendable out) { jtulach@162: super(out); jtulach@162: } jaroslav@29: jaroslav@29: static void compile(Appendable out, String... names) throws IOException { jtulach@162: compile(out, StringArray.asList(names)); jaroslav@29: } jtulach@168: static void compile(ClassLoader l, Appendable out, String... names) throws IOException { jtulach@168: compile(l, out, StringArray.asList(names)); jtulach@168: } jtulach@162: static void compile(Appendable out, StringArray names) throws IOException { jaroslav@109: compile(GenJS.class.getClassLoader(), out, names); jaroslav@109: } jtulach@162: static void compile(ClassLoader l, Appendable out, StringArray names) throws IOException { jaroslav@272: new GenJS(out).doCompile(l, names); jaroslav@272: } jaroslav@272: protected void doCompile(ClassLoader l, StringArray names) throws IOException { jtulach@162: StringArray processed = new StringArray(); jtulach@162: StringArray initCode = new StringArray(); jtulach@162: for (String baseClass : names.toArray()) { jaroslav@272: references.add(baseClass); jaroslav@97: for (;;) { jaroslav@97: String name = null; jaroslav@272: for (String n : references.toArray()) { jtulach@162: if (processed.contains(n)) { jaroslav@97: continue; jaroslav@97: } jaroslav@97: name = n; jaroslav@97: } jaroslav@97: if (name == null) { jaroslav@97: break; 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: try { jaroslav@272: String ic = compile(is); jtulach@162: processed.add(name); jtulach@162: initCode.add(ic == null ? "" : ic); jaroslav@97: } catch (RuntimeException ex) { jaroslav@97: if (out instanceof CharSequence) { jaroslav@97: CharSequence seq = (CharSequence)out; jaroslav@97: int lastBlock = seq.length(); jaroslav@151: 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@29: } jaroslav@97: jaroslav@272: for (String resource : scripts.toArray()) { jaroslav@250: while (resource.startsWith("/")) { jaroslav@250: resource = resource.substring(1); jaroslav@250: } jaroslav@250: InputStream emul = l.getResourceAsStream(resource); jaroslav@250: if (emul == null) { jaroslav@250: throw new IOException("Can't find " + resource); jaroslav@250: } jaroslav@250: readResource(emul, out); jaroslav@250: } jaroslav@272: scripts = new StringArray(); jaroslav@250: jaroslav@272: StringArray toInit = StringArray.asList(references.toArray()); jtulach@162: toInit.reverse(); jtulach@129: jtulach@162: for (String ic : toInit.toArray()) { jtulach@162: int indx = processed.indexOf(ic); jtulach@162: if (indx >= 0) { jtulach@162: out.append(initCode.toArray()[indx]).append("\n"); jtulach@162: initCode.toArray()[indx] = ""; 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: } jtulach@162: if (u.toExternalForm().contains("rt.jar!")) { jtulach@162: throw new IOException("No emulation for " + u); jtulach@162: } 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: } jtulach@162: jtulach@162: private StringArray scripts = new StringArray(); jtulach@162: private StringArray references = new StringArray(); jtulach@162: jtulach@162: @Override jtulach@162: protected boolean requireReference(String cn) { jtulach@162: if (references.contains(cn)) { jtulach@162: return false; jtulach@162: } jtulach@162: references.add(cn); jtulach@162: return true; jtulach@162: } jtulach@162: jtulach@162: @Override jtulach@162: protected void requireScript(String resourcePath) { jtulach@162: scripts.add(resourcePath); jtulach@162: } jaroslav@29: }