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@22: package org.apidesign.vm4brwsr; jaroslav@0: jaroslav@0: import java.io.IOException; jaroslav@0: import java.io.InputStream; jtulach@167: import org.apidesign.javap.AnnotationParser; jtulach@167: import org.apidesign.javap.ClassData; jtulach@167: import org.apidesign.javap.FieldData; jtulach@167: import org.apidesign.javap.MethodData; jtulach@167: import static org.apidesign.javap.RuntimeConstants.*; jaroslav@0: jaroslav@0: /** Translator of the code inside class files to JavaScript. jaroslav@0: * jaroslav@0: * @author Jaroslav Tulach jaroslav@0: */ jtulach@162: public abstract class ByteCodeToJavaScript { jtulach@162: private ClassData jc; jaroslav@0: private final Appendable out; jaroslav@0: jtulach@162: protected ByteCodeToJavaScript(Appendable out) { jaroslav@0: this.out = out; jaroslav@0: } jtulach@162: jtulach@162: /* Collects additional required resources. jtulach@162: * jtulach@162: * @param internalClassName classes that were referenced and should be loaded in order the jtulach@162: * generated JavaScript code works properly. The names are in internal jtulach@162: * JVM form so String is java/lang/String. jtulach@162: */ jtulach@162: protected abstract boolean requireReference(String internalClassName); jtulach@162: jtulach@162: /* jtulach@162: * @param resourcePath name of resources to read jtulach@162: */ jtulach@162: protected abstract void requireScript(String resourcePath); jaroslav@213: jaroslav@213: /** Allows subclasses to redefine what field a function representing a jaroslav@213: * class gets assigned. By default it returns the suggested name followed jaroslav@213: * by " = "; jaroslav@213: * jaroslav@213: * @param className suggested name of the class jaroslav@213: */ jaroslav@213: protected String assignClass(String className) { jaroslav@213: return className + " = "; jaroslav@213: } jaroslav@18: jaroslav@18: /** jaroslav@18: * Converts a given class file to a JavaScript version. jaroslav@18: * jaroslav@0: * @param classFile input stream with code of the .class file jaroslav@97: * @return the initialization code for this class, if any. Otherwise null jaroslav@91: * jaroslav@0: * @throws IOException if something goes wrong during read or write or translating jaroslav@0: */ jaroslav@18: jtulach@162: public String compile(InputStream classFile) throws IOException { jtulach@162: this.jc = new ClassData(classFile); jaroslav@152: byte[] arrData = jc.findAnnotationData(true); jaroslav@170: String[] arr = findAnnotation(arrData, jc, jaroslav@170: "org.apidesign.bck2brwsr.core.ExtraJavaScript", jaroslav@170: "resource", "processByteCode" jaroslav@170: ); jaroslav@152: if (arr != null) { jtulach@162: requireScript(arr[0]); jaroslav@152: if ("0".equals(arr[1])) { jaroslav@97: return null; jaroslav@91: } jaroslav@91: } jtulach@162: StringArray toInitilize = new StringArray(); jaroslav@151: final String className = className(jc); jaroslav@213: out.append("\n\n").append(assignClass(className)); jaroslav@213: out.append("function CLS() {"); jaroslav@213: out.append("\n if (!CLS.prototype.$instOf_").append(className).append(") {"); jtulach@191: for (FieldData v : jc.getFields()) { jtulach@191: if (v.isStatic()) { jaroslav@213: out.append("\n CLS.").append(v.getName()).append(initField(v)); jtulach@191: } jtulach@191: } jaroslav@151: // ClassName sc = jc.getSuperClass(); jaroslav@151: String sc = jc.getSuperClassName(); // with _ jaroslav@13: if (sc != null) { jaroslav@213: out.append("\n var p = CLS.prototype = "). jaroslav@205: append(sc.replace('/', '_')).append("(true);"); jtulach@130: } else { jaroslav@213: out.append("\n var p = CLS.prototype;"); jaroslav@13: } jaroslav@151: for (MethodData m : jc.getMethods()) { jaroslav@203: if (m.isStatic()) { jaroslav@204: generateStaticMethod("\n p.", m, toInitilize); jaroslav@203: } else { jaroslav@204: generateInstanceMethod("\n p.", m); jaroslav@38: } jaroslav@38: } jaroslav@204: out.append("\n p.$instOf_").append(className).append(" = true;"); jaroslav@151: for (String superInterface : jc.getSuperInterfaces()) { jaroslav@204: out.append("\n p.$instOf_").append(superInterface.replace('/', '_')).append(" = true;"); jaroslav@40: } jaroslav@225: out.append("\n CLS.$class = java_lang_Class(true);"); jaroslav@225: out.append("\n CLS.$class.jvmName = '").append(jc.getClassName()).append("';"); jaroslav@210: out.append("\n if (arguments.length === 0) {"); jaroslav@213: out.append("\n return new CLS();"); jaroslav@210: out.append("\n }"); jaroslav@204: out.append("\n }"); jaroslav@205: out.append("\n if (arguments.length === 0) {"); jaroslav@205: for (FieldData v : jc.getFields()) { jaroslav@205: if (!v.isStatic()) { jaroslav@205: out.append("\n this.fld_"). jaroslav@205: append(v.getName()).append(initField(v)); jaroslav@205: } jaroslav@205: } jaroslav@205: out.append("\n return this;"); jaroslav@205: out.append("\n }"); jaroslav@213: out.append("\n return new CLS;"); jaroslav@98: out.append("\n}"); jaroslav@97: StringBuilder sb = new StringBuilder(); jtulach@162: for (String init : toInitilize.toArray()) { jaroslav@97: sb.append("\n").append(init).append("();"); jaroslav@21: } jaroslav@97: return sb.toString(); jaroslav@0: } jaroslav@203: private void generateStaticMethod(String prefix, MethodData m, StringArray toInitilize) throws IOException { jaroslav@203: if (javaScriptBody(prefix, m, true)) { jaroslav@94: return; jaroslav@94: } jtulach@156: StringBuilder argsCnt = new StringBuilder(); jaroslav@151: final String mn = findMethodName(m, argsCnt); jaroslav@203: out.append(prefix).append(mn).append(" = function"); jaroslav@21: if (mn.equals("classV")) { jaroslav@205: toInitilize.add(className(jc) + "(true)." + mn); jaroslav@21: } jaroslav@0: out.append('('); jaroslav@0: String space = ""; jtulach@156: for (int index = 0, i = 0; i < argsCnt.length(); i++) { jaroslav@0: out.append(space); jaroslav@2: out.append("arg").append(String.valueOf(index)); jaroslav@0: space = ","; jaroslav@151: final String desc = null;// XXX findDescriptor(args.get(i).getDescriptor()); jtulach@156: if (argsCnt.charAt(i) == '1') { jaroslav@2: index += 2; jaroslav@2: } else { jaroslav@2: index++; jaroslav@2: } jaroslav@0: } jaroslav@5: out.append(") {").append("\n"); jaroslav@151: final byte[] code = m.getCode(); jaroslav@18: if (code != null) { jaroslav@151: int len = m.getMaxLocals(); jtulach@156: for (int index = argsCnt.length(), i = argsCnt.length(); i < len; i++) { jaroslav@18: out.append(" var "); jaroslav@18: out.append("arg").append(String.valueOf(i)).append(";\n"); jaroslav@18: } jaroslav@220: out.append(" var s = new Array();\n"); jaroslav@151: produceCode(code); jaroslav@18: } else { jaroslav@151: out.append(" /* no code found for ").append(m.getInternalSig()).append(" */\n"); jaroslav@0: } jaroslav@203: out.append("};"); jaroslav@10: } jaroslav@10: jaroslav@203: private void generateInstanceMethod(String prefix, MethodData m) throws IOException { jaroslav@203: if (javaScriptBody(prefix, m, false)) { jaroslav@94: return; jaroslav@94: } jtulach@156: StringBuilder argsCnt = new StringBuilder(); jaroslav@203: final String mn = findMethodName(m, argsCnt); jaroslav@203: out.append(prefix).append(mn).append(" = function"); jaroslav@10: out.append("(arg0"); jaroslav@10: String space = ","; jtulach@156: for (int index = 1, i = 0; i < argsCnt.length(); i++) { jaroslav@10: out.append(space); jaroslav@10: out.append("arg").append(String.valueOf(index)); jtulach@156: if (argsCnt.charAt(i) == '1') { jaroslav@10: index += 2; jaroslav@10: } else { jaroslav@10: index++; jaroslav@10: } jaroslav@10: } jaroslav@10: out.append(") {").append("\n"); jaroslav@151: final byte[] code = m.getCode(); jaroslav@18: if (code != null) { jaroslav@151: int len = m.getMaxLocals(); jtulach@156: for (int index = argsCnt.length(), i = argsCnt.length(); i < len; i++) { jaroslav@18: out.append(" var "); jaroslav@18: out.append("arg").append(String.valueOf(i + 1)).append(";\n"); jaroslav@18: } jaroslav@220: out.append(";\n var s = new Array();\n"); jaroslav@151: produceCode(code); jaroslav@18: } else { jaroslav@151: out.append(" /* no code found for ").append(m.getInternalSig()).append(" */\n"); jaroslav@10: } jaroslav@203: out.append("};"); jaroslav@0: } jaroslav@0: jaroslav@0: private void produceCode(byte[] byteCodes) throws IOException { jaroslav@10: out.append("\n var gt = 0;\n for(;;) switch(gt) {\n"); jaroslav@0: for (int i = 0; i < byteCodes.length; i++) { jaroslav@0: int prev = i; jaroslav@10: out.append(" case " + i).append(": "); jtulach@128: final int c = readByte(byteCodes, i); jaroslav@0: switch (c) { jaroslav@151: case opc_aload_0: jaroslav@151: case opc_iload_0: jaroslav@151: case opc_lload_0: jaroslav@151: case opc_fload_0: jaroslav@151: case opc_dload_0: jaroslav@220: out.append("s.push(arg0);"); jaroslav@0: break; jaroslav@151: case opc_aload_1: jaroslav@151: case opc_iload_1: jaroslav@151: case opc_lload_1: jaroslav@151: case opc_fload_1: jaroslav@151: case opc_dload_1: jaroslav@220: out.append("s.push(arg1);"); jaroslav@0: break; jaroslav@151: case opc_aload_2: jaroslav@151: case opc_iload_2: jaroslav@151: case opc_lload_2: jaroslav@151: case opc_fload_2: jaroslav@151: case opc_dload_2: jaroslav@220: out.append("s.push(arg2);"); jaroslav@2: break; jaroslav@151: case opc_aload_3: jaroslav@151: case opc_iload_3: jaroslav@151: case opc_lload_3: jaroslav@151: case opc_fload_3: jaroslav@151: case opc_dload_3: jaroslav@220: out.append("s.push(arg3);"); jaroslav@3: break; jaroslav@151: case opc_iload: jaroslav@151: case opc_lload: jaroslav@151: case opc_fload: jaroslav@151: case opc_dload: jaroslav@151: case opc_aload: { jtulach@128: final int indx = readByte(byteCodes, ++i); jaroslav@220: out.append("s.push(arg").append(indx + ");"); jaroslav@3: break; jaroslav@3: } jaroslav@151: case opc_istore: jaroslav@151: case opc_lstore: jaroslav@151: case opc_fstore: jaroslav@151: case opc_dstore: jaroslav@151: case opc_astore: { jtulach@128: final int indx = readByte(byteCodes, ++i); jaroslav@220: out.append("arg" + indx).append(" = s.pop();"); jaroslav@31: break; jaroslav@31: } jaroslav@151: case opc_astore_0: jaroslav@151: case opc_istore_0: jaroslav@151: case opc_lstore_0: jaroslav@151: case opc_fstore_0: jaroslav@151: case opc_dstore_0: jaroslav@220: out.append("arg0 = s.pop();"); jaroslav@5: break; jaroslav@151: case opc_astore_1: jaroslav@151: case opc_istore_1: jaroslav@151: case opc_lstore_1: jaroslav@151: case opc_fstore_1: jaroslav@151: case opc_dstore_1: jaroslav@220: out.append("arg1 = s.pop();"); jaroslav@5: break; jaroslav@151: case opc_astore_2: jaroslav@151: case opc_istore_2: jaroslav@151: case opc_lstore_2: jaroslav@151: case opc_fstore_2: jaroslav@151: case opc_dstore_2: jaroslav@220: out.append("arg2 = s.pop();"); jaroslav@5: break; jaroslav@151: case opc_astore_3: jaroslav@151: case opc_istore_3: jaroslav@151: case opc_lstore_3: jaroslav@151: case opc_fstore_3: jaroslav@151: case opc_dstore_3: jaroslav@220: out.append("arg3 = s.pop();"); jaroslav@5: break; jaroslav@151: case opc_iadd: jaroslav@151: case opc_ladd: jaroslav@151: case opc_fadd: jaroslav@151: case opc_dadd: jaroslav@220: out.append("s.push(s.pop() + s.pop());"); jaroslav@0: break; jaroslav@151: case opc_isub: jaroslav@151: case opc_lsub: jaroslav@151: case opc_fsub: jaroslav@151: case opc_dsub: jaroslav@220: out.append("{ var tmp = s.pop(); s.push(s.pop() - tmp); }"); jaroslav@2: break; jaroslav@151: case opc_imul: jaroslav@151: case opc_lmul: jaroslav@151: case opc_fmul: jaroslav@151: case opc_dmul: jaroslav@220: out.append("s.push(s.pop() * s.pop());"); jaroslav@1: break; jaroslav@151: case opc_idiv: jaroslav@151: case opc_ldiv: jaroslav@220: out.append("{ var tmp = s.pop(); s.push(Math.floor(s.pop() / tmp)); }"); jaroslav@3: break; jaroslav@151: case opc_fdiv: jaroslav@151: case opc_ddiv: jaroslav@220: out.append("{ var tmp = s.pop(); s.push(s.pop() / tmp); }"); jaroslav@3: break; jaroslav@178: case opc_irem: jaroslav@178: case opc_lrem: jaroslav@178: case opc_frem: jaroslav@178: case opc_drem: jaroslav@220: out.append("{ var d = s.pop(); s.push(s.pop() % d); }"); jaroslav@178: break; jaroslav@151: case opc_iand: jaroslav@151: case opc_land: jaroslav@220: out.append("s.push(s.pop() & s.pop());"); jaroslav@7: break; jaroslav@151: case opc_ior: jaroslav@151: case opc_lor: jaroslav@220: out.append("s.push(s.pop() | s.pop());"); jaroslav@7: break; jaroslav@151: case opc_ixor: jaroslav@151: case opc_lxor: jaroslav@220: out.append("s.push(s.pop() ^ s.pop());"); jaroslav@6: break; jaroslav@151: case opc_ineg: jaroslav@151: case opc_lneg: jaroslav@151: case opc_fneg: jaroslav@151: case opc_dneg: jaroslav@220: out.append("s.push(- s.pop());"); jaroslav@93: break; jaroslav@151: case opc_ishl: jaroslav@151: case opc_lshl: jaroslav@220: out.append("{ var v = s.pop(); s.push(s.pop() << v); }"); jaroslav@93: break; jaroslav@151: case opc_ishr: jaroslav@151: case opc_lshr: jaroslav@220: out.append("{ var v = s.pop(); s.push(s.pop() >> v); }"); jaroslav@93: break; jaroslav@151: case opc_iushr: jaroslav@151: case opc_lushr: jaroslav@220: out.append("{ var v = s.pop(); s.push(s.pop() >>> v); }"); jaroslav@93: break; jaroslav@151: case opc_iinc: { jtulach@128: final int varIndx = readByte(byteCodes, ++i); jaroslav@104: final int incrBy = byteCodes[++i]; jaroslav@5: if (incrBy == 1) { jaroslav@5: out.append("arg" + varIndx).append("++;"); jaroslav@5: } else { jaroslav@5: out.append("arg" + varIndx).append(" += " + incrBy).append(";"); jaroslav@5: } jaroslav@5: break; jaroslav@5: } jaroslav@151: case opc_return: jaroslav@10: out.append("return;"); jaroslav@10: break; jaroslav@151: case opc_ireturn: jaroslav@151: case opc_lreturn: jaroslav@151: case opc_freturn: jaroslav@151: case opc_dreturn: jaroslav@151: case opc_areturn: jaroslav@220: out.append("return s.pop();"); jaroslav@1: break; jaroslav@151: case opc_i2l: jaroslav@151: case opc_i2f: jaroslav@151: case opc_i2d: jaroslav@151: case opc_l2i: jaroslav@3: // max int check? jaroslav@151: case opc_l2f: jaroslav@151: case opc_l2d: jaroslav@151: case opc_f2d: jaroslav@151: case opc_d2f: jaroslav@3: out.append("/* number conversion */"); jaroslav@3: break; jaroslav@151: case opc_f2i: jaroslav@151: case opc_f2l: jaroslav@151: case opc_d2i: jaroslav@151: case opc_d2l: jaroslav@220: out.append("s.push(Math.floor(s.pop()));"); jaroslav@3: break; jaroslav@151: case opc_i2b: jaroslav@151: case opc_i2c: jaroslav@151: case opc_i2s: jaroslav@2: out.append("/* number conversion */"); jaroslav@2: break; jaroslav@151: case opc_aconst_null: jaroslav@220: out.append("s.push(null);"); jaroslav@46: break; jaroslav@151: case opc_iconst_m1: jaroslav@220: out.append("s.push(-1);"); jaroslav@48: break; jaroslav@151: case opc_iconst_0: jaroslav@151: case opc_dconst_0: jaroslav@151: case opc_lconst_0: jaroslav@151: case opc_fconst_0: jaroslav@220: out.append("s.push(0);"); jaroslav@4: break; jaroslav@151: case opc_iconst_1: jaroslav@151: case opc_lconst_1: jaroslav@151: case opc_fconst_1: jaroslav@151: case opc_dconst_1: jaroslav@220: out.append("s.push(1);"); jaroslav@4: break; jaroslav@151: case opc_iconst_2: jaroslav@151: case opc_fconst_2: jaroslav@220: out.append("s.push(2);"); jaroslav@4: break; jaroslav@151: case opc_iconst_3: jaroslav@220: out.append("s.push(3);"); jaroslav@4: break; jaroslav@151: case opc_iconst_4: jaroslav@220: out.append("s.push(4);"); jaroslav@4: break; jaroslav@151: case opc_iconst_5: jaroslav@220: out.append("s.push(5);"); jaroslav@4: break; jaroslav@151: case opc_ldc: { jtulach@128: int indx = readByte(byteCodes, ++i); jaroslav@151: String v = encodeConstant(indx); jaroslav@220: out.append("s.push(").append(v).append(");"); jaroslav@20: break; jaroslav@20: } jaroslav@151: case opc_ldc_w: jaroslav@151: case opc_ldc2_w: { jaroslav@8: int indx = readIntArg(byteCodes, i); jaroslav@8: i += 2; jaroslav@151: String v = encodeConstant(indx); jaroslav@220: out.append("s.push(").append(v).append(");"); jaroslav@8: break; jaroslav@8: } jaroslav@151: case opc_lcmp: jaroslav@151: case opc_fcmpl: jaroslav@151: case opc_fcmpg: jaroslav@151: case opc_dcmpl: jaroslav@151: case opc_dcmpg: { jaroslav@220: out.append("{ var delta = s.pop() - s.pop(); s.push(delta < 0 ?-1 : (delta == 0 ? 0 : 1)); }"); jaroslav@20: break; jaroslav@20: } jaroslav@151: case opc_if_acmpeq: jaroslav@104: i = generateIf(byteCodes, i, "==="); jaroslav@104: break; jaroslav@151: case opc_if_acmpne: jaroslav@104: i = generateIf(byteCodes, i, "!="); jaroslav@104: break; jaroslav@151: case opc_if_icmpeq: { jaroslav@4: i = generateIf(byteCodes, i, "=="); jaroslav@4: break; jaroslav@4: } jaroslav@151: case opc_ifeq: { jaroslav@7: int indx = i + readIntArg(byteCodes, i); jaroslav@220: out.append("if (s.pop() == 0) { gt = " + indx); jaroslav@7: out.append("; continue; }"); jaroslav@7: i += 2; jaroslav@7: break; jaroslav@7: } jaroslav@151: case opc_ifne: { jaroslav@20: int indx = i + readIntArg(byteCodes, i); jaroslav@220: out.append("if (s.pop() != 0) { gt = " + indx); jaroslav@20: out.append("; continue; }"); jaroslav@20: i += 2; jaroslav@20: break; jaroslav@20: } jaroslav@151: case opc_iflt: { jaroslav@20: int indx = i + readIntArg(byteCodes, i); jaroslav@220: out.append("if (s.pop() < 0) { gt = " + indx); jaroslav@20: out.append("; continue; }"); jaroslav@20: i += 2; jaroslav@20: break; jaroslav@20: } jaroslav@151: case opc_ifle: { jaroslav@20: int indx = i + readIntArg(byteCodes, i); jaroslav@220: out.append("if (s.pop() <= 0) { gt = " + indx); jaroslav@20: out.append("; continue; }"); jaroslav@20: i += 2; jaroslav@20: break; jaroslav@20: } jaroslav@151: case opc_ifgt: { jaroslav@20: int indx = i + readIntArg(byteCodes, i); jaroslav@220: out.append("if (s.pop() > 0) { gt = " + indx); jaroslav@20: out.append("; continue; }"); jaroslav@20: i += 2; jaroslav@20: break; jaroslav@20: } jaroslav@151: case opc_ifge: { jaroslav@20: int indx = i + readIntArg(byteCodes, i); jaroslav@220: out.append("if (s.pop() >= 0) { gt = " + indx); jaroslav@20: out.append("; continue; }"); jaroslav@20: i += 2; jaroslav@20: break; jaroslav@20: } jaroslav@151: case opc_ifnonnull: { jaroslav@16: int indx = i + readIntArg(byteCodes, i); jaroslav@220: out.append("if (s.pop() !== null) { gt = " + indx); jaroslav@16: out.append("; continue; }"); jaroslav@16: i += 2; jaroslav@16: break; jaroslav@16: } jaroslav@151: case opc_ifnull: { jaroslav@16: int indx = i + readIntArg(byteCodes, i); jaroslav@220: out.append("if (s.pop() === null) { gt = " + indx); jaroslav@16: out.append("; continue; }"); jaroslav@16: i += 2; jaroslav@16: break; jaroslav@16: } jaroslav@151: case opc_if_icmpne: jaroslav@4: i = generateIf(byteCodes, i, "!="); jaroslav@4: break; jaroslav@151: case opc_if_icmplt: jaroslav@4: i = generateIf(byteCodes, i, ">"); jaroslav@4: break; jaroslav@151: case opc_if_icmple: jaroslav@4: i = generateIf(byteCodes, i, ">="); jaroslav@4: break; jaroslav@151: case opc_if_icmpgt: jaroslav@4: i = generateIf(byteCodes, i, "<"); jaroslav@4: break; jaroslav@151: case opc_if_icmpge: jaroslav@4: i = generateIf(byteCodes, i, "<="); jaroslav@4: break; jaroslav@151: case opc_goto: { jaroslav@5: int indx = i + readIntArg(byteCodes, i); jaroslav@5: out.append("gt = " + indx).append("; continue;"); jaroslav@5: i += 2; jaroslav@5: break; jaroslav@5: } jaroslav@151: case opc_lookupswitch: { jtulach@128: int table = i / 4 * 4 + 4; jaroslav@115: int dflt = i + readInt4(byteCodes, table); jaroslav@115: table += 4; jaroslav@115: int n = readInt4(byteCodes, table); jaroslav@115: table += 4; jaroslav@220: out.append("switch (s.pop()) {\n"); jaroslav@115: while (n-- > 0) { jaroslav@115: int cnstnt = readInt4(byteCodes, table); jaroslav@115: table += 4; jaroslav@115: int offset = i + readInt4(byteCodes, table); jaroslav@115: table += 4; jaroslav@115: out.append(" case " + cnstnt).append(": gt = " + offset).append("; continue;\n"); jaroslav@115: } jaroslav@115: out.append(" default: gt = " + dflt).append("; continue;\n}"); jaroslav@115: i = table - 1; jaroslav@115: break; jaroslav@115: } jaroslav@151: case opc_tableswitch: { jtulach@128: int table = i / 4 * 4 + 4; jaroslav@115: int dflt = i + readInt4(byteCodes, table); jaroslav@115: table += 4; jaroslav@115: int low = readInt4(byteCodes, table); jaroslav@115: table += 4; jaroslav@115: int high = readInt4(byteCodes, table); jaroslav@115: table += 4; jaroslav@220: out.append("switch (s.pop()) {\n"); jaroslav@115: while (low <= high) { jaroslav@115: int offset = i + readInt4(byteCodes, table); jaroslav@115: table += 4; jaroslav@115: out.append(" case " + low).append(": gt = " + offset).append("; continue;\n"); jaroslav@115: low++; jaroslav@115: } jaroslav@115: out.append(" default: gt = " + dflt).append("; continue;\n}"); jaroslav@115: i = table - 1; jaroslav@115: break; jaroslav@115: } jaroslav@151: case opc_invokeinterface: { jaroslav@46: i = invokeVirtualMethod(byteCodes, i) + 2; jaroslav@46: break; jaroslav@46: } jaroslav@151: case opc_invokevirtual: jaroslav@12: i = invokeVirtualMethod(byteCodes, i); jaroslav@12: break; jaroslav@151: case opc_invokespecial: jaroslav@10: i = invokeStaticMethod(byteCodes, i, false); jaroslav@4: break; jaroslav@151: case opc_invokestatic: jaroslav@10: i = invokeStaticMethod(byteCodes, i, true); jaroslav@10: break; jaroslav@151: case opc_new: { jaroslav@8: int indx = readIntArg(byteCodes, i); jaroslav@151: String ci = jc.getClassName(indx); jaroslav@220: out.append("s.push("); jaroslav@151: out.append("new ").append(ci.replace('/','_')); jaroslav@10: out.append(");"); jaroslav@151: addReference(ci); jaroslav@8: i += 2; jaroslav@8: break; jaroslav@8: } jaroslav@151: case opc_newarray: { jaroslav@21: int type = byteCodes[i++]; jaroslav@220: out.append("s.push(new Array(s.pop()).fillNulls());"); jaroslav@21: break; jaroslav@21: } jaroslav@151: case opc_anewarray: { jaroslav@21: i += 2; // skip type of array jaroslav@220: out.append("s.push(new Array(s.pop()).fillNulls());"); jaroslav@21: break; jaroslav@21: } jaroslav@151: case opc_multianewarray: { jtulach@128: i += 2; jtulach@128: int dim = readByte(byteCodes, ++i); jaroslav@220: out.append("{ var a0 = new Array(s.pop()).fillNulls();"); jtulach@128: for (int d = 1; d < dim; d++) { jaroslav@220: out.append("\n var l" + d).append(" = s.pop();"); jtulach@128: out.append("\n for (var i" + d).append (" = 0; i" + d). jtulach@128: append(" < a" + (d - 1)). jtulach@128: append(".length; i" + d).append("++) {"); jtulach@128: out.append("\n var a" + d). jaroslav@172: append (" = new Array(l" + d).append(").fillNulls();"); jtulach@128: out.append("\n a" + (d - 1)).append("[i" + d).append("] = a" + d). jtulach@128: append(";"); jtulach@128: } jtulach@128: for (int d = 1; d < dim; d++) { jtulach@128: out.append("\n }"); jtulach@128: } jaroslav@220: out.append("\ns.push(a0); }"); jtulach@128: break; jtulach@128: } jaroslav@151: case opc_arraylength: jaroslav@220: out.append("s.push(s.pop().length);"); jaroslav@21: break; jaroslav@151: case opc_iastore: jaroslav@151: case opc_lastore: jaroslav@151: case opc_fastore: jaroslav@151: case opc_dastore: jaroslav@151: case opc_aastore: jaroslav@151: case opc_bastore: jaroslav@151: case opc_castore: jaroslav@151: case opc_sastore: { jaroslav@220: out.append("{ var value = s.pop(); var indx = s.pop(); s.pop()[indx] = value; }"); jaroslav@21: break; jaroslav@21: } jaroslav@151: case opc_iaload: jaroslav@151: case opc_laload: jaroslav@151: case opc_faload: jaroslav@151: case opc_daload: jaroslav@151: case opc_aaload: jaroslav@151: case opc_baload: jaroslav@151: case opc_caload: jaroslav@151: case opc_saload: { jaroslav@220: out.append("{ var indx = s.pop(); s.push(s.pop()[indx]); }"); jaroslav@21: break; jaroslav@21: } jaroslav@151: case opc_pop2: jaroslav@220: out.append("s.pop();"); jaroslav@151: case opc_pop: jaroslav@220: out.append("s.pop();"); jaroslav@93: break; jaroslav@151: case opc_dup: jaroslav@220: out.append("s.push(s[s.length - 1]);"); jaroslav@8: break; jaroslav@170: case opc_dup_x1: jaroslav@220: out.append("{ var v1 = s.pop(); var v2 = s.pop(); s.push(v1); s.push(v2); s.push(v1); }"); jaroslav@170: break; jaroslav@170: case opc_dup_x2: jaroslav@220: out.append("{ var v1 = s.pop(); var v2 = s.pop(); var v3 = s.pop(); s.push(v1); s.push(v3); s.push(v2); s.push(v1); }"); jaroslav@170: break; jaroslav@151: case opc_bipush: jaroslav@220: out.append("s.push(" + byteCodes[++i] + ");"); jaroslav@8: break; jaroslav@151: case opc_sipush: jaroslav@220: out.append("s.push(" + readIntArg(byteCodes, i) + ");"); jaroslav@31: i += 2; jaroslav@31: break; jaroslav@151: case opc_getfield: { jaroslav@8: int indx = readIntArg(byteCodes, i); jaroslav@151: String[] fi = jc.getFieldInfoName(indx); jaroslav@220: out.append("s.push(s.pop().fld_"). jaroslav@151: append(fi[1]).append(");"); jaroslav@8: i += 2; jaroslav@8: break; jaroslav@8: } jaroslav@151: case opc_getstatic: { jaroslav@9: int indx = readIntArg(byteCodes, i); jaroslav@151: String[] fi = jc.getFieldInfoName(indx); jaroslav@220: out.append("s.push(").append(fi[0].replace('/', '_')); jtulach@191: out.append('.').append(fi[1]).append(");"); jaroslav@9: i += 2; jaroslav@151: addReference(fi[0]); jaroslav@9: break; jaroslav@9: } jaroslav@151: case opc_putstatic: { jaroslav@9: int indx = readIntArg(byteCodes, i); jaroslav@151: String[] fi = jc.getFieldInfoName(indx); jaroslav@151: out.append(fi[0].replace('/', '_')); jaroslav@220: out.append('.').append(fi[1]).append(" = s.pop();"); jaroslav@9: i += 2; jaroslav@151: addReference(fi[0]); jaroslav@9: break; jaroslav@9: } jaroslav@151: case opc_putfield: { jaroslav@10: int indx = readIntArg(byteCodes, i); jaroslav@151: String[] fi = jc.getFieldInfoName(indx); jaroslav@220: out.append("{ var v = s.pop(); s.pop().fld_") jaroslav@151: .append(fi[1]).append(" = v; }"); jaroslav@10: i += 2; jaroslav@10: break; jaroslav@10: } jaroslav@151: case opc_checkcast: { jaroslav@30: int indx = readIntArg(byteCodes, i); jaroslav@151: final String type = jc.getClassName(indx); jaroslav@42: if (!type.startsWith("[")) { jaroslav@42: // no way to check arrays right now jaroslav@220: out.append("if(s[s.length - 1].$instOf_") jaroslav@42: .append(type.replace('/', '_')) jaroslav@42: .append(" != 1) throw {};"); // XXX proper exception jaroslav@42: } jaroslav@30: i += 2; jaroslav@30: break; jaroslav@30: } jaroslav@151: case opc_instanceof: { jaroslav@17: int indx = readIntArg(byteCodes, i); jaroslav@151: final String type = jc.getClassName(indx); jaroslav@220: out.append("s.push(s.pop().$instOf_") jaroslav@151: .append(type.replace('/', '_')) jaroslav@17: .append(" ? 1 : 0);"); jaroslav@17: i += 2; jaroslav@30: break; jaroslav@17: } jaroslav@151: case opc_athrow: { jaroslav@220: out.append("{ var t = s.pop(); s = new Array(1); s[0] = t; throw t; }"); jaroslav@104: break; jaroslav@104: } jaroslav@104: default: { jaroslav@104: out.append("throw 'unknown bytecode " + c + "';"); jaroslav@104: } jaroslav@8: jaroslav@0: } jaroslav@39: out.append(" //"); jaroslav@0: for (int j = prev; j <= i; j++) { jaroslav@0: out.append(" "); jtulach@128: final int cc = readByte(byteCodes, j); jaroslav@0: out.append(Integer.toString(cc)); jaroslav@0: } jaroslav@39: out.append("\n"); jaroslav@0: } jaroslav@10: out.append(" }\n"); jaroslav@4: } jaroslav@4: jaroslav@4: private int generateIf(byte[] byteCodes, int i, final String test) throws IOException { jaroslav@4: int indx = i + readIntArg(byteCodes, i); jaroslav@220: out.append("if (s.pop() ").append(test).append(" s.pop()) { gt = " + indx); jaroslav@4: out.append("; continue; }"); jaroslav@4: return i + 2; jaroslav@4: } jaroslav@4: jaroslav@4: private int readIntArg(byte[] byteCodes, int offsetInstruction) { jaroslav@5: final int indxHi = byteCodes[offsetInstruction + 1] << 8; jaroslav@5: final int indxLo = byteCodes[offsetInstruction + 2]; jaroslav@5: return (indxHi & 0xffffff00) | (indxLo & 0xff); jaroslav@4: } jaroslav@115: private int readInt4(byte[] byteCodes, int offsetInstruction) { jaroslav@115: final int d = byteCodes[offsetInstruction + 0] << 24; jaroslav@115: final int c = byteCodes[offsetInstruction + 1] << 16; jaroslav@115: final int b = byteCodes[offsetInstruction + 2] << 8; jaroslav@115: final int a = byteCodes[offsetInstruction + 3]; jaroslav@115: return (d & 0xff000000) | (c & 0xff0000) | (b & 0xff00) | (a & 0xff); jaroslav@115: } jtulach@128: private int readByte(byte[] byteCodes, int offsetInstruction) { jtulach@128: return (byteCodes[offsetInstruction] + 256) % 256; jtulach@128: } jaroslav@4: jtulach@156: private static void countArgs(String descriptor, boolean[] hasReturnType, StringBuilder sig, StringBuilder cnt) { jaroslav@4: int i = 0; jaroslav@4: Boolean count = null; jaroslav@32: boolean array = false; jaroslav@10: int firstPos = sig.length(); jaroslav@4: while (i < descriptor.length()) { jaroslav@4: char ch = descriptor.charAt(i++); jaroslav@4: switch (ch) { jaroslav@4: case '(': jaroslav@4: count = true; jaroslav@4: continue; jaroslav@4: case ')': jaroslav@4: count = false; jaroslav@4: continue; jaroslav@32: case 'A': jaroslav@32: array = true; jaroslav@32: break; jaroslav@4: case 'B': jaroslav@4: case 'C': jaroslav@4: case 'D': jaroslav@4: case 'F': jaroslav@4: case 'I': jaroslav@4: case 'J': jaroslav@4: case 'S': jaroslav@4: case 'Z': jaroslav@4: if (count) { jaroslav@32: if (array) { jaroslav@32: sig.append('A'); jaroslav@32: } jaroslav@4: sig.append(ch); jtulach@156: if (ch == 'J' || ch == 'D') { jtulach@156: cnt.append('1'); jtulach@156: } else { jtulach@156: cnt.append('0'); jtulach@156: } jaroslav@4: } else { jaroslav@4: hasReturnType[0] = true; jaroslav@10: sig.insert(firstPos, ch); jaroslav@32: if (array) { jaroslav@32: sig.insert(firstPos, 'A'); jaroslav@32: } jaroslav@4: } jaroslav@93: array = false; jaroslav@4: continue; jaroslav@4: case 'V': jaroslav@4: assert !count; jaroslav@4: hasReturnType[0] = false; jaroslav@10: sig.insert(firstPos, 'V'); jaroslav@4: continue; jaroslav@4: case 'L': jaroslav@16: int next = descriptor.indexOf(';', i); jaroslav@4: if (count) { jaroslav@32: if (array) { jaroslav@32: sig.append('A'); jaroslav@32: } jaroslav@16: sig.append(ch); jaroslav@16: sig.append(descriptor.substring(i, next).replace('/', '_')); jtulach@156: cnt.append('0'); jaroslav@4: } else { jaroslav@16: sig.insert(firstPos, descriptor.substring(i, next).replace('/', '_')); jaroslav@16: sig.insert(firstPos, ch); jaroslav@32: if (array) { jaroslav@33: sig.insert(firstPos, 'A'); jaroslav@32: } jaroslav@4: hasReturnType[0] = true; jaroslav@4: } jaroslav@16: i = next + 1; jaroslav@4: continue; jaroslav@4: case '[': jaroslav@4: //arrays++; jaroslav@4: continue; jaroslav@4: default: jaroslav@4: break; // invalid character jaroslav@4: } jaroslav@4: } jaroslav@0: } jaroslav@9: jtulach@156: private String findMethodName(MethodData m, StringBuilder cnt) { jaroslav@42: StringBuilder name = new StringBuilder(); jaroslav@10: if ("".equals(m.getName())) { // NOI18N jaroslav@42: name.append("cons"); // NOI18N jaroslav@19: } else if ("".equals(m.getName())) { // NOI18N jaroslav@42: name.append("class"); // NOI18N jaroslav@10: } else { jaroslav@42: name.append(m.getName()); jaroslav@10: } jaroslav@42: jaroslav@42: boolean hasReturn[] = { false }; jtulach@156: countArgs(findDescriptor(m.getInternalSig()), hasReturn, name, cnt); jaroslav@42: return name.toString(); jaroslav@10: } jaroslav@10: jtulach@156: private String findMethodName(String[] mi, StringBuilder cnt, boolean[] hasReturn) { jaroslav@10: StringBuilder name = new StringBuilder(); jaroslav@151: String descr = mi[2];//mi.getDescriptor(); jaroslav@151: String nm= mi[1]; jaroslav@151: if ("".equals(nm)) { // NOI18N jaroslav@10: name.append("cons"); // NOI18N jaroslav@10: } else { jaroslav@151: name.append(nm); jaroslav@10: } jtulach@156: countArgs(findDescriptor(descr), hasReturn, name, cnt); jaroslav@10: return name.toString(); jaroslav@10: } jaroslav@10: jaroslav@10: private int invokeStaticMethod(byte[] byteCodes, int i, boolean isStatic) jaroslav@10: throws IOException { jaroslav@10: int methodIndex = readIntArg(byteCodes, i); jaroslav@151: String[] mi = jc.getFieldInfoName(methodIndex); jaroslav@10: boolean[] hasReturn = { false }; jtulach@156: StringBuilder cnt = new StringBuilder(); jaroslav@10: String mn = findMethodName(mi, cnt, hasReturn); jaroslav@11: out.append("{ "); jtulach@156: for (int j = cnt.length() - 1; j >= 0; j--) { jaroslav@220: out.append("var v" + j).append(" = s.pop(); "); jaroslav@11: } jaroslav@11: jaroslav@10: if (hasReturn[0]) { jaroslav@220: out.append("s.push("); jaroslav@10: } jaroslav@151: final String in = mi[0]; jaroslav@18: out.append(in.replace('/', '_')); jaroslav@209: out.append("(true)."); jaroslav@10: out.append(mn); jaroslav@10: out.append('('); jaroslav@10: String sep = ""; jaroslav@10: if (!isStatic) { jaroslav@220: out.append("s.pop()"); jaroslav@10: sep = ", "; jaroslav@10: } jtulach@156: for (int j = 0; j < cnt.length(); j++) { jaroslav@10: out.append(sep); jaroslav@11: out.append("v" + j); jaroslav@10: sep = ", "; jaroslav@10: } jaroslav@10: out.append(")"); jaroslav@10: if (hasReturn[0]) { jaroslav@10: out.append(")"); jaroslav@10: } jaroslav@11: out.append("; }"); jaroslav@10: i += 2; jaroslav@18: addReference(in); jaroslav@10: return i; jaroslav@10: } jaroslav@12: private int invokeVirtualMethod(byte[] byteCodes, int i) jaroslav@12: throws IOException { jaroslav@12: int methodIndex = readIntArg(byteCodes, i); jaroslav@151: String[] mi = jc.getFieldInfoName(methodIndex); jaroslav@12: boolean[] hasReturn = { false }; jtulach@156: StringBuilder cnt = new StringBuilder(); jaroslav@12: String mn = findMethodName(mi, cnt, hasReturn); jaroslav@12: out.append("{ "); jtulach@156: for (int j = cnt.length() - 1; j >= 0; j--) { jaroslav@220: out.append("var v" + j).append(" = s.pop(); "); jaroslav@12: } jaroslav@220: out.append("var self = s.pop(); "); jaroslav@12: if (hasReturn[0]) { jaroslav@220: out.append("s.push("); jaroslav@12: } jaroslav@12: out.append("self."); jaroslav@12: out.append(mn); jaroslav@12: out.append('('); jaroslav@12: out.append("self"); jtulach@156: for (int j = 0; j < cnt.length(); j++) { jaroslav@12: out.append(", "); jaroslav@12: out.append("v" + j); jaroslav@12: } jaroslav@12: out.append(")"); jaroslav@12: if (hasReturn[0]) { jaroslav@12: out.append(")"); jaroslav@12: } jaroslav@12: out.append("; }"); jaroslav@12: i += 2; jaroslav@12: return i; jaroslav@12: } jaroslav@18: jaroslav@103: private void addReference(String cn) throws IOException { jtulach@162: if (requireReference(cn)) { jtulach@162: out.append(" /* needs ").append(cn).append(" */"); jaroslav@18: } jaroslav@18: } jaroslav@16: jaroslav@33: private void outType(String d, StringBuilder out) { jaroslav@33: int arr = 0; jaroslav@33: while (d.charAt(0) == '[') { jaroslav@33: out.append('A'); jaroslav@33: d = d.substring(1); jaroslav@33: } jaroslav@16: if (d.charAt(0) == 'L') { jaroslav@16: assert d.charAt(d.length() - 1) == ';'; jaroslav@16: out.append(d.replace('/', '_').substring(0, d.length() - 1)); jaroslav@16: } else { jaroslav@16: out.append(d); jaroslav@16: } jaroslav@16: } jaroslav@21: jaroslav@151: private String encodeConstant(int entryIndex) { jaroslav@151: String s = jc.stringValue(entryIndex, true); jaroslav@151: return s; jaroslav@21: } jaroslav@32: jaroslav@32: private String findDescriptor(String d) { jaroslav@32: return d.replace('[', 'A'); jaroslav@32: } jaroslav@94: jaroslav@203: private boolean javaScriptBody(String prefix, MethodData m, boolean isStatic) throws IOException { jaroslav@152: byte[] arr = m.findAnnotationData(true); jaroslav@152: if (arr == null) { jaroslav@152: return false; jaroslav@152: } jaroslav@200: final String jvmType = "Lorg/apidesign/bck2brwsr/core/JavaScriptBody;"; jaroslav@152: class P extends AnnotationParser { jaroslav@152: int cnt; jaroslav@152: String[] args = new String[30]; jaroslav@152: String body; jaroslav@94: jaroslav@152: @Override jaroslav@152: protected void visitAttr(String type, String attr, String value) { jaroslav@152: if (type.equals(jvmType)) { jaroslav@152: if ("body".equals(attr)) { jaroslav@152: body = value; jaroslav@152: } else if ("args".equals(attr)) { jaroslav@152: args[cnt++] = value; jaroslav@152: } else { jaroslav@152: throw new IllegalArgumentException(attr); jaroslav@152: } jaroslav@152: } jaroslav@94: } jaroslav@94: } jaroslav@152: P p = new P(); jaroslav@152: p.parse(arr, jc); jaroslav@152: if (p.body == null) { jaroslav@152: return false; jaroslav@152: } jtulach@156: StringBuilder cnt = new StringBuilder(); jaroslav@203: out.append(prefix).append(findMethodName(m, cnt)); jaroslav@203: out.append(" = function("); jaroslav@152: String space; jaroslav@152: int index; jaroslav@152: if (!isStatic) { jaroslav@152: out.append(p.args[0]); jaroslav@152: space = ","; jaroslav@152: index = 1; jaroslav@152: } else { jaroslav@152: space = ""; jaroslav@152: index = 0; jaroslav@152: } jtulach@156: for (int i = 0; i < cnt.length(); i++) { jaroslav@152: out.append(space); jaroslav@152: out.append(p.args[index]); jaroslav@152: index++; jaroslav@152: space = ","; jaroslav@152: } jaroslav@152: out.append(") {").append("\n"); jaroslav@152: out.append(p.body); jaroslav@152: out.append("\n}\n"); jaroslav@152: return true; jaroslav@151: } jaroslav@151: private static String className(ClassData jc) { jaroslav@151: //return jc.getName().getInternalName().replace('/', '_'); jaroslav@151: return jc.getClassName().replace('/', '_'); jaroslav@94: } jaroslav@152: jaroslav@152: private static String[] findAnnotation( jaroslav@152: byte[] arr, ClassData cd, final String className, jaroslav@152: final String... attrNames jaroslav@152: ) throws IOException { jaroslav@152: if (arr == null) { jaroslav@152: return null; jaroslav@152: } jaroslav@152: final String[] values = new String[attrNames.length]; jaroslav@152: final boolean[] found = { false }; jaroslav@152: final String jvmType = "L" + className.replace('.', '/') + ";"; jaroslav@152: AnnotationParser ap = new AnnotationParser() { jaroslav@152: @Override jaroslav@152: protected void visitAttr(String type, String attr, String value) { jaroslav@152: if (type.equals(jvmType)) { jaroslav@152: found[0] = true; jaroslav@152: for (int i = 0; i < attrNames.length; i++) { jaroslav@152: if (attrNames[i].equals(attr)) { jaroslav@152: values[i] = value; jaroslav@152: } jaroslav@152: } jaroslav@152: } jaroslav@152: } jaroslav@152: jaroslav@152: }; jaroslav@152: ap.parse(arr, cd); jaroslav@152: return found[0] ? values : null; jaroslav@152: } jaroslav@173: jaroslav@173: private CharSequence initField(FieldData v) { jaroslav@173: final String is = v.getInternalSig(); jaroslav@173: if (is.length() == 1) { jaroslav@173: switch (is.charAt(0)) { jaroslav@173: case 'S': jaroslav@173: case 'J': jaroslav@173: case 'B': jaroslav@173: case 'Z': jaroslav@173: case 'C': jaroslav@173: case 'I': return " = 0;"; jaroslav@173: case 'F': jaroslav@180: case 'D': return " = 0.0;"; jaroslav@173: default: jaroslav@173: throw new IllegalStateException(is); jaroslav@173: } jaroslav@173: } jaroslav@173: return " = null;"; jaroslav@173: } jaroslav@0: }