jaroslav@0: /* jaroslav@0: Java 4 Browser Bytecode Translator jaroslav@0: Copyright (C) 2012-2012 Jaroslav Tulach jaroslav@0: jaroslav@0: This program is free software: you can redistribute it and/or modify jaroslav@0: it under the terms of the GNU General Public License as published by jaroslav@0: the Free Software Foundation, version 2 of the License. jaroslav@0: jaroslav@0: This program is distributed in the hope that it will be useful, jaroslav@0: but WITHOUT ANY WARRANTY; without even the implied warranty of jaroslav@0: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the jaroslav@0: GNU General Public License for more details. jaroslav@0: jaroslav@0: You should have received a copy of the GNU General Public License jaroslav@0: along with this program. Look for COPYING file in the top folder. jaroslav@0: If not, see http://opensource.org/licenses/GPL-2.0. jaroslav@0: */ jaroslav@22: package org.apidesign.vm4brwsr; jaroslav@0: jaroslav@0: import java.io.IOException; jaroslav@0: import java.io.InputStream; jaroslav@21: import java.util.ArrayList; jaroslav@18: import java.util.Collection; jaroslav@0: import java.util.List; jaroslav@2: import static org.netbeans.modules.classfile.ByteCodes.*; jaroslav@8: import org.netbeans.modules.classfile.CPClassInfo; jaroslav@8: import org.netbeans.modules.classfile.CPEntry; jaroslav@8: import org.netbeans.modules.classfile.CPFieldInfo; jaroslav@4: import org.netbeans.modules.classfile.CPMethodInfo; jaroslav@21: import org.netbeans.modules.classfile.CPStringInfo; jaroslav@0: import org.netbeans.modules.classfile.ClassFile; jaroslav@13: import org.netbeans.modules.classfile.ClassName; jaroslav@0: import org.netbeans.modules.classfile.Code; jaroslav@0: import org.netbeans.modules.classfile.Method; jaroslav@0: import org.netbeans.modules.classfile.Parameter; jaroslav@9: import org.netbeans.modules.classfile.Variable; jaroslav@0: jaroslav@0: /** Translator of the code inside class files to JavaScript. jaroslav@0: * jaroslav@0: * @author Jaroslav Tulach jaroslav@0: */ jaroslav@0: public final class ByteCodeToJavaScript { jaroslav@0: private final ClassFile jc; jaroslav@0: private final Appendable out; jaroslav@18: private final Collection references; jaroslav@0: jaroslav@18: private ByteCodeToJavaScript( jaroslav@18: ClassFile jc, Appendable out, Collection references jaroslav@18: ) { jaroslav@0: this.jc = jc; jaroslav@0: this.out = out; jaroslav@18: this.references = references; jaroslav@0: } 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@0: * @param out a {@link StringBuilder} or similar to generate the output to jaroslav@18: * @param references a write only collection where the system adds list of jaroslav@18: * other classes that were referenced and should be loaded in order the jaroslav@18: * generated JavaScript code works properly. The names are in internal jaroslav@18: * JVM form so String is java/lang/String. Can be null jaroslav@18: * if one is not interested in knowing references jaroslav@0: * @throws IOException if something goes wrong during read or write or translating jaroslav@0: */ jaroslav@18: jaroslav@18: public static void compile( jaroslav@18: InputStream classFile, Appendable out, jaroslav@18: Collection references jaroslav@18: ) throws IOException { jaroslav@0: ClassFile jc = new ClassFile(classFile, true); jaroslav@18: ByteCodeToJavaScript compiler = new ByteCodeToJavaScript( jaroslav@18: jc, out, references jaroslav@18: ); jaroslav@21: List toInitilize = new ArrayList(); jaroslav@0: for (Method m : jc.getMethods()) { jaroslav@0: if (m.isStatic()) { jaroslav@21: compiler.generateStaticMethod(m, toInitilize); jaroslav@10: } else { jaroslav@10: compiler.generateInstanceMethod(m); jaroslav@0: } jaroslav@0: } jaroslav@9: for (Variable v : jc.getVariables()) { jaroslav@9: if (v.isStatic()) { jaroslav@9: compiler.generateStaticField(v); jaroslav@9: } jaroslav@9: } jaroslav@10: jaroslav@18: final String className = jc.getName().getInternalName().replace('/', '_'); jaroslav@13: out.append("\nfunction ").append(className); jaroslav@10: out.append("() {"); jaroslav@10: for (Method m : jc.getMethods()) { jaroslav@10: if (!m.isStatic()) { jaroslav@12: compiler.generateMethodReference(m); jaroslav@10: } jaroslav@10: } jaroslav@10: for (Variable v : jc.getVariables()) { jaroslav@10: if (!v.isStatic()) { jaroslav@10: out.append("\n this." + v.getName() + " = 0;"); jaroslav@10: } jaroslav@10: } jaroslav@17: out.append("\n this.$instOf_").append(className).append(" = true;"); jaroslav@10: out.append("\n}"); jaroslav@13: ClassName sc = jc.getSuperClass(); jaroslav@13: if (sc != null) { jaroslav@21: out.append("\n").append(className) jaroslav@18: .append(".prototype = new ").append(sc.getInternalName().replace('/', '_')); jaroslav@13: } jaroslav@21: for (String init : toInitilize) { jaroslav@21: out.append("\n").append(init).append("();"); jaroslav@21: } jaroslav@0: } jaroslav@21: private void generateStaticMethod(Method m, List toInitilize) throws IOException { jaroslav@21: final String mn = findMethodName(m); jaroslav@1: out.append("\nfunction ").append( jaroslav@18: jc.getName().getInternalName().replace('/', '_') jaroslav@21: ).append('_').append(mn); jaroslav@21: if (mn.equals("classV")) { jaroslav@21: toInitilize.add(jc.getName().getInternalName().replace('/', '_') + '_' + mn); jaroslav@21: } jaroslav@0: out.append('('); jaroslav@0: String space = ""; jaroslav@10: List args = m.getParameters(); jaroslav@2: for (int index = 0, i = 0; i < args.size(); i++) { jaroslav@0: out.append(space); jaroslav@2: out.append("arg").append(String.valueOf(index)); jaroslav@0: space = ","; jaroslav@2: final String desc = args.get(i).getDescriptor(); jaroslav@3: if ("D".equals(desc) || "J".equals(desc)) { jaroslav@2: index += 2; jaroslav@2: } else { jaroslav@2: index++; jaroslav@2: } jaroslav@0: } jaroslav@5: out.append(") {").append("\n"); jaroslav@0: final Code code = m.getCode(); jaroslav@18: if (code != null) { jaroslav@18: int len = code.getMaxLocals(); jaroslav@18: for (int index = args.size(), i = args.size(); i < len; i++) { jaroslav@18: out.append(" var "); jaroslav@18: out.append("arg").append(String.valueOf(i)).append(";\n"); jaroslav@18: } jaroslav@21: out.append(" var stack = new Array();\n"); jaroslav@18: produceCode(code.getByteCodes()); jaroslav@18: } else { jaroslav@18: out.append(" /* no code found for ").append(m.getTypeSignature()).append(" */\n"); jaroslav@0: } jaroslav@10: out.append("}"); jaroslav@10: } jaroslav@10: jaroslav@12: private void generateMethodReference(Method m) throws IOException { jaroslav@12: final String name = findMethodName(m); jaroslav@12: out.append("\n this.").append(name).append(" = ") jaroslav@18: .append(jc.getName().getInternalName().replace('/', '_')) jaroslav@12: .append('_').append(name).append(";"); jaroslav@12: } jaroslav@12: jaroslav@10: private void generateInstanceMethod(Method m) throws IOException { jaroslav@10: out.append("\nfunction ").append( jaroslav@18: jc.getName().getInternalName().replace('/', '_') jaroslav@10: ).append('_').append(findMethodName(m)); jaroslav@10: out.append("(arg0"); jaroslav@10: String space = ","; jaroslav@10: List args = m.getParameters(); jaroslav@10: for (int index = 1, i = 0; i < args.size(); i++) { jaroslav@10: out.append(space); jaroslav@10: out.append("arg").append(String.valueOf(index)); jaroslav@10: final String desc = args.get(i).getDescriptor(); jaroslav@10: if ("D".equals(desc) || "J".equals(desc)) { jaroslav@10: index += 2; jaroslav@10: } else { jaroslav@10: index++; jaroslav@10: } jaroslav@10: } jaroslav@10: out.append(") {").append("\n"); jaroslav@10: final Code code = m.getCode(); jaroslav@18: if (code != null) { jaroslav@18: int len = code.getMaxLocals(); jaroslav@18: for (int index = args.size(), i = args.size(); i < len; i++) { jaroslav@18: out.append(" var "); jaroslav@18: out.append("arg").append(String.valueOf(i + 1)).append(";\n"); jaroslav@18: } jaroslav@18: out.append(";\n var stack = new Array("); jaroslav@18: out.append(Integer.toString(code.getMaxStack())); jaroslav@18: out.append(");\n"); jaroslav@18: produceCode(code.getByteCodes()); jaroslav@18: } else { jaroslav@18: out.append(" /* no code found for ").append(m.getTypeSignature()).append(" */\n"); jaroslav@10: } jaroslav@0: 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(": "); jaroslav@0: final int c = (byteCodes[i] + 256) % 256; jaroslav@0: switch (c) { jaroslav@2: case bc_aload_0: jaroslav@2: case bc_iload_0: jaroslav@2: case bc_lload_0: jaroslav@2: case bc_fload_0: jaroslav@2: case bc_dload_0: jaroslav@0: out.append("stack.push(arg0);"); jaroslav@0: break; jaroslav@2: case bc_aload_1: jaroslav@2: case bc_iload_1: jaroslav@2: case bc_lload_1: jaroslav@2: case bc_fload_1: jaroslav@2: case bc_dload_1: jaroslav@0: out.append("stack.push(arg1);"); jaroslav@0: break; jaroslav@2: case bc_aload_2: jaroslav@2: case bc_iload_2: jaroslav@2: case bc_lload_2: jaroslav@2: case bc_fload_2: jaroslav@2: case bc_dload_2: jaroslav@2: out.append("stack.push(arg2);"); jaroslav@2: break; jaroslav@3: case bc_aload_3: jaroslav@3: case bc_iload_3: jaroslav@3: case bc_lload_3: jaroslav@3: case bc_fload_3: jaroslav@3: case bc_dload_3: jaroslav@3: out.append("stack.push(arg3);"); jaroslav@3: break; jaroslav@3: case bc_iload: jaroslav@3: case bc_lload: jaroslav@3: case bc_fload: jaroslav@3: case bc_dload: jaroslav@3: case bc_aload: { jaroslav@3: final int indx = (byteCodes[++i] + 256) % 256; jaroslav@3: out.append("stack.push(arg").append(indx + ");"); jaroslav@3: break; jaroslav@3: } jaroslav@8: case bc_astore_0: jaroslav@5: case bc_istore_0: jaroslav@5: case bc_lstore_0: jaroslav@5: case bc_fstore_0: jaroslav@5: case bc_dstore_0: jaroslav@5: out.append("arg0 = stack.pop();"); jaroslav@5: break; jaroslav@8: case bc_astore_1: jaroslav@5: case bc_istore_1: jaroslav@5: case bc_lstore_1: jaroslav@5: case bc_fstore_1: jaroslav@5: case bc_dstore_1: jaroslav@5: out.append("arg1 = stack.pop();"); jaroslav@5: break; jaroslav@8: case bc_astore_2: jaroslav@5: case bc_istore_2: jaroslav@5: case bc_lstore_2: jaroslav@5: case bc_fstore_2: jaroslav@5: case bc_dstore_2: jaroslav@5: out.append("arg2 = stack.pop();"); jaroslav@5: break; jaroslav@8: case bc_astore_3: jaroslav@5: case bc_istore_3: jaroslav@5: case bc_lstore_3: jaroslav@5: case bc_fstore_3: jaroslav@5: case bc_dstore_3: jaroslav@5: out.append("arg3 = stack.pop();"); jaroslav@5: break; jaroslav@2: case bc_iadd: jaroslav@2: case bc_ladd: jaroslav@2: case bc_fadd: jaroslav@2: case bc_dadd: jaroslav@0: out.append("stack.push(stack.pop() + stack.pop());"); jaroslav@0: break; jaroslav@2: case bc_isub: jaroslav@2: case bc_lsub: jaroslav@2: case bc_fsub: jaroslav@2: case bc_dsub: jaroslav@3: out.append("{ var tmp = stack.pop(); stack.push(stack.pop() - tmp); }"); jaroslav@2: break; jaroslav@2: case bc_imul: jaroslav@2: case bc_lmul: jaroslav@2: case bc_fmul: jaroslav@2: case bc_dmul: jaroslav@1: out.append("stack.push(stack.pop() * stack.pop());"); jaroslav@1: break; jaroslav@3: case bc_idiv: jaroslav@3: case bc_ldiv: jaroslav@3: out.append("{ var tmp = stack.pop(); stack.push(Math.floor(stack.pop() / tmp)); }"); jaroslav@3: break; jaroslav@3: case bc_fdiv: jaroslav@3: case bc_ddiv: jaroslav@3: out.append("{ var tmp = stack.pop(); stack.push(stack.pop() / tmp); }"); jaroslav@3: break; jaroslav@7: case bc_iand: jaroslav@7: case bc_land: jaroslav@7: out.append("stack.push(stack.pop() & stack.pop());"); jaroslav@7: break; jaroslav@7: case bc_ior: jaroslav@7: case bc_lor: jaroslav@7: out.append("stack.push(stack.pop() | stack.pop());"); jaroslav@7: break; jaroslav@6: case bc_ixor: jaroslav@6: case bc_lxor: jaroslav@6: out.append("stack.push(stack.pop() ^ stack.pop());"); jaroslav@6: break; jaroslav@5: case bc_iinc: { jaroslav@5: final int varIndx = (byteCodes[++i] + 256) % 256; jaroslav@5: final int incrBy = (byteCodes[++i] + 256) % 256; 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@10: case bc_return: jaroslav@10: out.append("return;"); jaroslav@10: break; jaroslav@2: case bc_ireturn: jaroslav@2: case bc_lreturn: jaroslav@2: case bc_freturn: jaroslav@2: case bc_dreturn: jaroslav@10: case bc_areturn: jaroslav@0: out.append("return stack.pop();"); jaroslav@1: break; jaroslav@2: case bc_i2l: jaroslav@2: case bc_i2f: jaroslav@2: case bc_i2d: jaroslav@2: case bc_l2i: jaroslav@3: // max int check? jaroslav@2: case bc_l2f: jaroslav@2: case bc_l2d: jaroslav@3: case bc_f2d: jaroslav@3: case bc_d2f: jaroslav@3: out.append("/* number conversion */"); jaroslav@3: break; jaroslav@2: case bc_f2i: jaroslav@2: case bc_f2l: jaroslav@2: case bc_d2i: jaroslav@2: case bc_d2l: jaroslav@3: out.append("stack.push(Math.floor(stack.pop()));"); jaroslav@3: break; jaroslav@2: case bc_i2b: jaroslav@2: case bc_i2c: jaroslav@2: case bc_i2s: jaroslav@2: out.append("/* number conversion */"); jaroslav@2: break; jaroslav@4: case bc_iconst_0: jaroslav@4: case bc_dconst_0: jaroslav@4: case bc_lconst_0: jaroslav@4: case bc_fconst_0: jaroslav@4: out.append("stack.push(0);"); jaroslav@4: break; jaroslav@4: case bc_iconst_1: jaroslav@4: case bc_lconst_1: jaroslav@4: case bc_fconst_1: jaroslav@4: case bc_dconst_1: jaroslav@4: out.append("stack.push(1);"); jaroslav@4: break; jaroslav@4: case bc_iconst_2: jaroslav@4: case bc_fconst_2: jaroslav@4: out.append("stack.push(2);"); jaroslav@4: break; jaroslav@4: case bc_iconst_3: jaroslav@4: out.append("stack.push(3);"); jaroslav@4: break; jaroslav@4: case bc_iconst_4: jaroslav@4: out.append("stack.push(4);"); jaroslav@4: break; jaroslav@4: case bc_iconst_5: jaroslav@4: out.append("stack.push(5);"); jaroslav@4: break; jaroslav@20: case bc_ldc: { jaroslav@21: int indx = byteCodes[++i]; jaroslav@20: CPEntry entry = jc.getConstantPool().get(indx); jaroslav@21: String v = encodeConstant(entry); jaroslav@21: out.append("stack.push(").append(v).append(");"); jaroslav@20: break; jaroslav@20: } jaroslav@8: case bc_ldc_w: jaroslav@8: case bc_ldc2_w: { jaroslav@8: int indx = readIntArg(byteCodes, i); jaroslav@8: CPEntry entry = jc.getConstantPool().get(indx); jaroslav@8: i += 2; jaroslav@21: String v = encodeConstant(entry); jaroslav@21: out.append("stack.push(").append(v).append(");"); jaroslav@8: break; jaroslav@8: } jaroslav@20: case bc_lcmp: jaroslav@20: case bc_fcmpl: jaroslav@20: case bc_fcmpg: jaroslav@20: case bc_dcmpl: jaroslav@20: case bc_dcmpg: { jaroslav@20: out.append("{ var delta = stack.pop() - stack.pop(); stack.push(delta < 0 ?-1 : (delta == 0 ? 0 : 1)); }"); jaroslav@20: break; jaroslav@20: } jaroslav@4: case bc_if_icmpeq: { jaroslav@4: i = generateIf(byteCodes, i, "=="); jaroslav@4: break; jaroslav@4: } jaroslav@7: case bc_ifeq: { jaroslav@7: int indx = i + readIntArg(byteCodes, i); jaroslav@7: out.append("if (stack.pop() == 0) { gt = " + indx); jaroslav@7: out.append("; continue; }"); jaroslav@7: i += 2; jaroslav@7: break; jaroslav@7: } jaroslav@20: case bc_ifne: { jaroslav@20: int indx = i + readIntArg(byteCodes, i); jaroslav@20: out.append("if (stack.pop() != 0) { gt = " + indx); jaroslav@20: out.append("; continue; }"); jaroslav@20: i += 2; jaroslav@20: break; jaroslav@20: } jaroslav@20: case bc_iflt: { jaroslav@20: int indx = i + readIntArg(byteCodes, i); jaroslav@20: out.append("if (stack.pop() < 0) { gt = " + indx); jaroslav@20: out.append("; continue; }"); jaroslav@20: i += 2; jaroslav@20: break; jaroslav@20: } jaroslav@20: case bc_ifle: { jaroslav@20: int indx = i + readIntArg(byteCodes, i); jaroslav@20: out.append("if (stack.pop() <= 0) { gt = " + indx); jaroslav@20: out.append("; continue; }"); jaroslav@20: i += 2; jaroslav@20: break; jaroslav@20: } jaroslav@20: case bc_ifgt: { jaroslav@20: int indx = i + readIntArg(byteCodes, i); jaroslav@20: out.append("if (stack.pop() > 0) { gt = " + indx); jaroslav@20: out.append("; continue; }"); jaroslav@20: i += 2; jaroslav@20: break; jaroslav@20: } jaroslav@20: case bc_ifge: { jaroslav@20: int indx = i + readIntArg(byteCodes, i); jaroslav@20: out.append("if (stack.pop() >= 0) { gt = " + indx); jaroslav@20: out.append("; continue; }"); jaroslav@20: i += 2; jaroslav@20: break; jaroslav@20: } jaroslav@16: case bc_ifnonnull: { jaroslav@16: int indx = i + readIntArg(byteCodes, i); jaroslav@16: out.append("if (stack.pop()) { gt = " + indx); jaroslav@16: out.append("; continue; }"); jaroslav@16: i += 2; jaroslav@16: break; jaroslav@16: } jaroslav@16: case bc_ifnull: { jaroslav@16: int indx = i + readIntArg(byteCodes, i); jaroslav@16: out.append("if (!stack.pop()) { gt = " + indx); jaroslav@16: out.append("; continue; }"); jaroslav@16: i += 2; jaroslav@16: break; jaroslav@16: } jaroslav@4: case bc_if_icmpne: jaroslav@4: i = generateIf(byteCodes, i, "!="); jaroslav@4: break; jaroslav@4: case bc_if_icmplt: jaroslav@4: i = generateIf(byteCodes, i, ">"); jaroslav@4: break; jaroslav@4: case bc_if_icmple: jaroslav@4: i = generateIf(byteCodes, i, ">="); jaroslav@4: break; jaroslav@4: case bc_if_icmpgt: jaroslav@4: i = generateIf(byteCodes, i, "<"); jaroslav@4: break; jaroslav@4: case bc_if_icmpge: jaroslav@4: i = generateIf(byteCodes, i, "<="); jaroslav@4: break; jaroslav@5: case bc_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@15: case bc_invokeinterface: jaroslav@12: case bc_invokevirtual: jaroslav@12: i = invokeVirtualMethod(byteCodes, i); jaroslav@12: break; jaroslav@10: case bc_invokespecial: jaroslav@10: i = invokeStaticMethod(byteCodes, i, false); jaroslav@4: break; jaroslav@10: case bc_invokestatic: jaroslav@10: i = invokeStaticMethod(byteCodes, i, true); jaroslav@10: break; jaroslav@8: case bc_new: { jaroslav@8: int indx = readIntArg(byteCodes, i); jaroslav@8: CPClassInfo ci = jc.getConstantPool().getClass(indx); jaroslav@8: out.append("stack.push("); jaroslav@18: out.append("new ").append(ci.getClassName().getInternalName().replace('/','_')); jaroslav@10: out.append(");"); jaroslav@18: addReference(ci.getClassName().getInternalName()); jaroslav@8: i += 2; jaroslav@8: break; jaroslav@8: } jaroslav@21: case bc_newarray: { jaroslav@21: int type = byteCodes[i++]; jaroslav@21: out.append("stack.push(new Array(stack.pop()));"); jaroslav@21: break; jaroslav@21: } jaroslav@21: case bc_anewarray: { jaroslav@21: i += 2; // skip type of array jaroslav@21: out.append("stack.push(new Array(stack.pop()));"); jaroslav@21: break; jaroslav@21: } jaroslav@21: case bc_arraylength: jaroslav@21: out.append("stack.push(stack.pop().length);"); jaroslav@21: break; jaroslav@21: case bc_iastore: jaroslav@21: case bc_lastore: jaroslav@21: case bc_fastore: jaroslav@21: case bc_dastore: jaroslav@21: case bc_aastore: jaroslav@21: case bc_bastore: jaroslav@21: case bc_castore: jaroslav@21: case bc_sastore: { jaroslav@21: out.append("{ var value = stack.pop(); var indx = stack.pop(); stack.pop()[indx] = value; }"); jaroslav@21: break; jaroslav@21: } jaroslav@21: case bc_iaload: jaroslav@21: case bc_laload: jaroslav@21: case bc_faload: jaroslav@21: case bc_daload: jaroslav@21: case bc_aaload: jaroslav@21: case bc_baload: jaroslav@21: case bc_caload: jaroslav@21: case bc_saload: { jaroslav@21: out.append("{ var indx = stack.pop(); stack.push(stack.pop()[indx]); }"); jaroslav@21: break; jaroslav@21: } jaroslav@8: case bc_dup: jaroslav@8: out.append("stack.push(stack[stack.length - 1]);"); jaroslav@8: break; jaroslav@8: case bc_bipush: jaroslav@8: out.append("stack.push(" + byteCodes[++i] + ");"); jaroslav@8: break; jaroslav@8: case bc_getfield: { jaroslav@8: int indx = readIntArg(byteCodes, i); jaroslav@8: CPFieldInfo fi = (CPFieldInfo) jc.getConstantPool().get(indx); jaroslav@21: out.append("stack.push(stack.pop().").append(fi.getFieldName()).append(");"); jaroslav@8: i += 2; jaroslav@8: break; jaroslav@8: } jaroslav@9: case bc_getstatic: { jaroslav@9: int indx = readIntArg(byteCodes, i); jaroslav@9: CPFieldInfo fi = (CPFieldInfo) jc.getConstantPool().get(indx); jaroslav@18: final String in = fi.getClassName().getInternalName(); jaroslav@18: out.append("stack.push(").append(in.replace('/', '_')); jaroslav@9: out.append('_').append(fi.getFieldName()).append(");"); jaroslav@9: i += 2; jaroslav@18: addReference(in); jaroslav@9: break; jaroslav@9: } jaroslav@9: case bc_putstatic: { jaroslav@9: int indx = readIntArg(byteCodes, i); jaroslav@9: CPFieldInfo fi = (CPFieldInfo) jc.getConstantPool().get(indx); jaroslav@18: final String in = fi.getClassName().getInternalName(); jaroslav@18: out.append(in.replace('/', '_')); jaroslav@9: out.append('_').append(fi.getFieldName()).append(" = stack.pop();"); jaroslav@9: i += 2; jaroslav@18: addReference(in); jaroslav@9: break; jaroslav@9: } jaroslav@10: case bc_putfield: { jaroslav@10: int indx = readIntArg(byteCodes, i); jaroslav@10: CPFieldInfo fi = (CPFieldInfo) jc.getConstantPool().get(indx); jaroslav@10: out.append("{ var v = stack.pop(); stack.pop().") jaroslav@10: .append(fi.getFieldName()).append(" = v; }"); jaroslav@10: i += 2; jaroslav@10: break; jaroslav@10: } jaroslav@17: case bc_instanceof: { jaroslav@17: int indx = readIntArg(byteCodes, i); jaroslav@17: CPClassInfo ci = jc.getConstantPool().getClass(indx); jaroslav@17: out.append("stack.push(stack.pop().$instOf_") jaroslav@18: .append(ci.getClassName().getInternalName().replace('/', '_')) jaroslav@17: .append(" ? 1 : 0);"); jaroslav@17: i += 2; jaroslav@17: } jaroslav@8: jaroslav@0: } jaroslav@3: out.append(" /*"); jaroslav@0: for (int j = prev; j <= i; j++) { jaroslav@0: out.append(" "); jaroslav@0: final int cc = (byteCodes[j] + 256) % 256; jaroslav@0: out.append(Integer.toString(cc)); jaroslav@0: } jaroslav@0: 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@4: out.append("if (stack.pop() ").append(test).append(" stack.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@4: jaroslav@4: private static int countArgs(String descriptor, boolean[] hasReturnType, StringBuilder sig) { jaroslav@4: int cnt = 0; jaroslav@4: int i = 0; jaroslav@4: Boolean count = null; 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@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@4: cnt++; jaroslav@4: sig.append(ch); jaroslav@4: } else { jaroslav@4: hasReturnType[0] = true; jaroslav@10: sig.insert(firstPos, ch); jaroslav@4: } 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@4: cnt++; jaroslav@16: sig.append(ch); jaroslav@16: sig.append(descriptor.substring(i, next).replace('/', '_')); jaroslav@4: } else { jaroslav@16: sig.insert(firstPos, descriptor.substring(i, next).replace('/', '_')); jaroslav@16: sig.insert(firstPos, ch); 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@4: return cnt; jaroslav@0: } jaroslav@9: jaroslav@9: private void generateStaticField(Variable v) throws IOException { jaroslav@9: out.append("\nvar ") jaroslav@18: .append(jc.getName().getInternalName().replace('/', '_')) jaroslav@9: .append('_').append(v.getName()).append(" = 0;"); jaroslav@9: } jaroslav@10: jaroslav@10: private String findMethodName(Method m) { jaroslav@19: StringBuilder tmp = new StringBuilder(); jaroslav@10: if ("".equals(m.getName())) { // NOI18N jaroslav@19: tmp.append("consV"); // NOI18N jaroslav@19: } else if ("".equals(m.getName())) { // NOI18N jaroslav@19: tmp.append("classV"); // NOI18N jaroslav@10: } else { jaroslav@19: tmp.append(m.getName()); jaroslav@19: outType(m.getReturnType(), tmp); jaroslav@10: } jaroslav@10: List args = m.getParameters(); jaroslav@10: for (Parameter t : args) { jaroslav@19: outType(t.getDescriptor(), tmp); jaroslav@10: } jaroslav@19: return tmp.toString(); jaroslav@10: } jaroslav@10: jaroslav@10: private String findMethodName(CPMethodInfo mi, int[] cnt, boolean[] hasReturn) { jaroslav@10: StringBuilder name = new StringBuilder(); jaroslav@10: if ("".equals(mi.getName())) { // NOI18N jaroslav@10: name.append("cons"); // NOI18N jaroslav@10: } else { jaroslav@10: name.append(mi.getName()); jaroslav@10: } jaroslav@10: cnt[0] = countArgs(mi.getDescriptor(), hasReturn, name); 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@10: CPMethodInfo mi = (CPMethodInfo) jc.getConstantPool().get(methodIndex); jaroslav@10: boolean[] hasReturn = { false }; jaroslav@10: int[] cnt = { 0 }; jaroslav@10: String mn = findMethodName(mi, cnt, hasReturn); jaroslav@11: out.append("{ "); jaroslav@11: for (int j = cnt[0] - 1; j >= 0; j--) { jaroslav@11: out.append("var v" + j).append(" = stack.pop(); "); jaroslav@11: } jaroslav@11: jaroslav@10: if (hasReturn[0]) { jaroslav@10: out.append("stack.push("); jaroslav@10: } jaroslav@18: final String in = mi.getClassName().getInternalName(); jaroslav@18: out.append(in.replace('/', '_')); jaroslav@10: out.append('_'); jaroslav@10: out.append(mn); jaroslav@10: out.append('('); jaroslav@10: String sep = ""; jaroslav@10: if (!isStatic) { jaroslav@10: out.append("stack.pop()"); jaroslav@10: sep = ", "; jaroslav@10: } jaroslav@10: for (int j = 0; j < cnt[0]; 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@12: CPMethodInfo mi = (CPMethodInfo) jc.getConstantPool().get(methodIndex); jaroslav@12: boolean[] hasReturn = { false }; jaroslav@12: int[] cnt = { 0 }; jaroslav@12: String mn = findMethodName(mi, cnt, hasReturn); jaroslav@12: out.append("{ "); jaroslav@12: for (int j = cnt[0] - 1; j >= 0; j--) { jaroslav@12: out.append("var v" + j).append(" = stack.pop(); "); jaroslav@12: } jaroslav@12: out.append("var self = stack.pop(); "); jaroslav@12: if (hasReturn[0]) { jaroslav@12: out.append("stack.push("); jaroslav@12: } jaroslav@12: out.append("self."); jaroslav@12: out.append(mn); jaroslav@12: out.append('('); jaroslav@12: out.append("self"); jaroslav@12: for (int j = 0; j < cnt[0]; 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@18: private void addReference(String cn) { jaroslav@18: if (references != null) { jaroslav@18: references.add(cn); jaroslav@18: } jaroslav@18: } jaroslav@16: jaroslav@16: private void outType(final String d, StringBuilder out) { 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@21: private String encodeConstant(CPEntry entry) { jaroslav@21: final String v; jaroslav@21: if (entry instanceof CPStringInfo) { jaroslav@21: v = "\"" + entry.getValue().toString().replace("\"", "\\\"") + "\""; jaroslav@21: } else { jaroslav@21: v = entry.getValue().toString(); jaroslav@21: } jaroslav@21: return v; jaroslav@21: } jaroslav@0: }