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@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@298: class VM extends ByteCodeToJavaScript { jaroslav@298: public VM(Appendable out) { jtulach@162: super(out); jtulach@162: } jaroslav@29: jaroslav@277: static { jaroslav@277: // uses VMLazy to load dynamic classes jaroslav@277: VMLazy.init(); jaroslav@277: } jaroslav@277: jaroslav@298: static void compile(Bck2Brwsr.Resources l, Appendable out, StringArray names) throws IOException { jaroslav@298: new VM(out).doCompile(l, names); jaroslav@29: } jaroslav@298: protected void doCompile(Bck2Brwsr.Resources l, StringArray names) throws IOException { jaroslav@274: out.append("(function VM(global) {"); jaroslav@274: out.append("\n var vm = {};"); 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@298: InputStream emul = l.get(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@29: } jaroslav@274: out.append( jaroslav@277: " global.bck2brwsr = function() {\n" jaroslav@277: + " var args = arguments;\n" jaroslav@277: + " var loader = {};\n" jaroslav@277: + " loader.vm = vm;\n" jaroslav@277: + " loader.loadClass = function(name) {\n" jaroslav@277: + " var attr = name.replace__Ljava_lang_String_2CC(name, '.','_');\n" jaroslav@277: + " var fn = vm[attr];\n" jaroslav@277: + " if (fn) return fn(false);\n" jaroslav@300: + " if (!args[0]) throw 'bck2brwsr initialized without loader function, cannot load ' + name;\n" jaroslav@277: + " return vm.org_apidesign_vm4brwsr_VMLazy(false).\n" jaroslav@277: + " load___3Ljava_lang_Object_2Ljava_lang_Object_2Ljava_lang_String_2_3Ljava_lang_Object_2(loader, name, args);\n" jaroslav@277: + " }\n" jaroslav@322: + " if (args[0]) vm.loadClass = loader.loadClass;\n" jaroslav@277: + " return loader;\n" jaroslav@277: + " };\n"); jaroslav@274: out.append("}(this));"); 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@298: private static InputStream loadClass(Bck2Brwsr.Resources l, String name) throws IOException { jaroslav@298: return l.get(name + ".class"); // NOI18N jaroslav@93: } jaroslav@136: jaroslav@136: static String toString(String name) throws IOException { jaroslav@136: StringBuilder sb = new StringBuilder(); jaroslav@298: // 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@274: jaroslav@274: @Override jaroslav@274: String assignClass(String className) { jaroslav@274: return "vm." + className + " = "; jaroslav@274: } jaroslav@274: jaroslav@274: @Override jaroslav@274: String accessClass(String className) { jaroslav@274: return "vm." + className; jaroslav@274: } jaroslav@29: }