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: } lubomir@869: lubomir@869: private VM(Appendable out, ObfuscationDelegate obfuscationDelegate) { lubomir@869: super(out, obfuscationDelegate); lubomir@869: } lubomir@869: jaroslav@277: static { jaroslav@277: // uses VMLazy to load dynamic classes jaroslav@671: boolean assertsOn = false; jaroslav@671: assert assertsOn = true; jaroslav@671: if (assertsOn) { jaroslav@671: VMLazy.init(); jaroslav@671: Zips.init(); jaroslav@671: } jaroslav@277: } jaroslav@399: jaroslav@399: @Override jaroslav@399: boolean debug(String msg) throws IOException { jaroslav@399: return false; jaroslav@399: } 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: } lubomir@869: lubomir@869: static void compile(Bck2Brwsr.Resources l, Appendable out, StringArray names, lubomir@869: ObfuscationDelegate obfuscationDelegate) throws IOException { lubomir@869: new VM(out, obfuscationDelegate).doCompile(l, names); lubomir@869: } lubomir@869: jaroslav@298: protected void doCompile(Bck2Brwsr.Resources l, StringArray names) throws IOException { jaroslav@711: out.append("(function VM(global) {var fillInVMSkeleton = function(vm) {"); jtulach@162: StringArray processed = new StringArray(); jtulach@162: StringArray initCode = new StringArray(); jaroslav@1344: StringArray skipClass = 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()) { jaroslav@1344: if (skipClass.contains(n)) { jaroslav@1344: continue; jaroslav@1344: } 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@1367: lazyReference(out, name); jaroslav@1344: skipClass.add(name); jaroslav@1344: continue; 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) { jaroslav@833: final String theCode = initCode.toArray()[indx]; jaroslav@833: if (!theCode.isEmpty()) { jaroslav@833: out.append(theCode).append("\n"); jaroslav@833: } jtulach@162: initCode.toArray()[indx] = ""; jaroslav@91: } jaroslav@91: } jaroslav@29: } jaroslav@274: out.append( jaroslav@711: " return vm;\n" jaroslav@711: + " };\n" jaroslav@711: + " global.bck2brwsr = function() {\n" jaroslav@729: + " var args = Array.prototype.slice.apply(arguments);\n" jaroslav@711: + " var vm = fillInVMSkeleton({});\n" jaroslav@277: + " var loader = {};\n" jaroslav@277: + " loader.vm = vm;\n" jaroslav@277: + " loader.loadClass = function(name) {\n" jaroslav@442: + " var attr = name.replace__Ljava_lang_String_2CC('.','_');\n" jaroslav@277: + " var fn = vm[attr];\n" jaroslav@277: + " if (fn) return fn(false);\n" jaroslav@277: + " return vm.org_apidesign_vm4brwsr_VMLazy(false).\n" lubomir@407: + " load__Ljava_lang_Object_2Ljava_lang_Object_2Ljava_lang_String_2_3Ljava_lang_Object_2(loader, name, args);\n" jaroslav@277: + " }\n" jaroslav@729: + " if (vm.loadClass) {\n" jaroslav@729: + " throw 'Cannot initialize the bck2brwsr VM twice!';\n" jaroslav@424: + " }\n" jaroslav@729: + " vm.loadClass = loader.loadClass;\n" jaroslav@1373: + " vm._reload = function(name, byteCode) {;\n" jaroslav@1373: + " var attr = name.replace__Ljava_lang_String_2CC('.','_');\n" jaroslav@1373: + " delete vm[attr];\n" jaroslav@1373: + " return vm.org_apidesign_vm4brwsr_VMLazy(false).\n" jaroslav@1373: + " reload__Ljava_lang_Object_2Ljava_lang_Object_2Ljava_lang_String_2_3Ljava_lang_Object_2_3B(loader, name, args, byteCode);\n" jaroslav@1373: + " };\n" jaroslav@729: + " vm.loadBytes = function(name) {\n" jaroslav@729: + " return vm.org_apidesign_vm4brwsr_VMLazy(false).\n" jaroslav@729: + " loadBytes___3BLjava_lang_Object_2Ljava_lang_String_2_3Ljava_lang_Object_2(loader, name, args);\n" jaroslav@729: + " }\n" jaroslav@729: + " vm.java_lang_reflect_Array(false);\n" jaroslav@729: + " vm.org_apidesign_vm4brwsr_VMLazy(false).\n" jaroslav@729: + " loadBytes___3BLjava_lang_Object_2Ljava_lang_String_2_3Ljava_lang_Object_2(loader, null, args);\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: } lubomir@869: lubomir@869: @Override lubomir@869: String getVMObject() { lubomir@869: return "vm"; lubomir@869: } jaroslav@1367: jaroslav@1367: private static void lazyReference(Appendable out, String n) throws IOException { jaroslav@1367: String cls = n.replace('/', '_'); jaroslav@1367: String dot = n.replace('/', '.'); jaroslav@1367: jaroslav@1367: out.append("\nvm.").append(cls).append(" = function() {"); jaroslav@1367: out.append("\n var instance = arguments.length == 0 || arguments[0] === true;"); jaroslav@1367: out.append("\n delete vm.").append(cls).append(";"); jaroslav@1367: out.append("\n var c = vm.loadClass('").append(dot).append("');"); jaroslav@1367: out.append("\n return vm.").append(cls).append("(instance);"); jaroslav@1367: out.append("\n}"); jaroslav@1367: } jaroslav@29: }