+ public void debugToString(StringBuffer sb, String constructorName) {
+ // This is intentionally a local variable so it can be removed by gcclass
+ final String[] OP_NAMES = new String[]{
+ "nop", "aconst_null", "iconst_m1", "iconst_0", "iconst_1", "iconst_2",
+ "iconst_3", "iconst_4", "iconst_5", "lconst_0", "lconst_1", "fconst_0",
+ "fconst_1", "fconst_2", "dconst_0", "dconst_1", "bipush", "sipush",
+ "ldc", "ldc_w", "ldc2_w", "iload", "lload", "fload",
+ "dload", "aload", "iload_0", "iload_1", "iload_2", "iload_3",
+ "lload_0", "lload_1", "lload_2", "lload_3", "fload_0", "fload_1",
+ "fload_2", "fload_3", "dload_0", "dload_1", "dload_2", "dload_3",
+ "aload_0", "aload_1", "aload_2", "aload_3", "iaload", "laload",
+ "faload", "daload", "aaload", "baload", "caload", "saload",
+ "istore", "lstore", "fstore", "dstore", "astore", "istore_0",
+ "istore_1", "istore_2", "istore_3", "lstore_0", "lstore_1", "lstore_2",
+ "lstore_3", "fstore_0", "fstore_1", "fstore_2", "fstore_3", "dstore_0",
+ "dstore_1", "dstore_2", "dstore_3", "astore_0", "astore_1", "astore_2",
+ "astore_3", "iastore", "lastore", "fastore", "dastore", "aastore",
+ "bastore", "castore", "sastore", "pop", "pop2", "dup",
+ "dup_x1", "dup_x2", "dup2", "dup2_x1", "dup2_x2", "swap",
+ "iadd", "ladd", "fadd", "dadd", "isub", "lsub",
+ "fsub", "dsub", "imul", "lmul", "fmul", "dmul",
+ "idiv", "ldiv", "fdiv", "ddiv", "irem", "lrem",
+ "frem", "drem", "ineg", "lneg", "fneg", "dneg",
+ "ishl", "lshl", "ishr", "lshr", "iushr", "lushr",
+ "iand", "land", "ior", "lor", "ixor", "lxor",
+ "iinc", "i2l", "i2f", "i2d", "l2i", "l2f",
+ "l2d", "f2i", "f2l", "f2d", "d2i", "d2l",
+ "d2f", "i2b", "i2c", "i2s", "lcmp", "fcmpl",
+ "fcmpg", "dcmpl", "dcmpg", "ifeq", "ifne", "iflt",
+ "ifge", "ifgt", "ifle", "if_icmpeq", "if_icmpne", "if_icmplt",
+ "if_icmpge", "if_icmpgt", "if_icmple", "if_acmpeq", "if_acmpne", "goto",
+ "jsr", "ret", "tableswitch", "lookupswitch", "ireturn", "lreturn",
+ "freturn", "dreturn", "areturn", "return", "getstatic", "putstatic",
+ "getfield", "putfield", "invokevirtual", "invokespecial", "invokestatic", "invokeinterface",
+ "", "new", "newarray", "anewarray", "arraylength", "athrow",
+ "checkcast", "instanceof", "monitorenter", "monitorexit", "wide", "multianewarray",
+ "ifnull", "ifnonnull", "goto_w", "jsr_w", "", "",
+ "", "", "", "", "", "",
+ "", "", "", "", "", "",
+ "", "", "", "", "", "",
+ "", "", "", "", "", "",
+ "", "", "", "", "", "",
+ "", "", "", "", "", "",
+ "", "", "", "", "", "",
+ "", "", "", "", "", "",
+ "", "", "", ""
+ };
+
+ sb.append(" ").append(ClassFile.flagsToString(flags,false));
+ sb.append(method.debugToString());
+ if(thrownExceptions.size() > 0) {
+ sb.append("throws");
+ for(Enumeration e = thrownExceptions.keys();e.hasMoreElements();)
+ sb.append(" ").append(((Type.Class)e.nextElement()).debugToString()).append(",");
+ sb.setLength(sb.length()-1);
+ sb.append(" ");
+ }
+ if((flags & (NATIVE|ABSTRACT))==0) {
+ sb.append("{\n");
+ for(int i=0;i<size();i++) {
+ sb.append(" ");
+ for(int j=i==0?1:i;j<10000;j*=10) sb.append(" ");
+ sb.append(i).append(": ");
+ sb.append(OP_NAMES[op[i]&0xff]);
+ String s = null;
+ if(arg[i] instanceof Type) s = ((Type)arg[i]).debugToString();
+ else if(arg[i] instanceof Type.Class.Member) s = ((Type.Class.Member)arg[i]).toString();
+ else if(arg[i] instanceof String) s = "\"" + s + "\"";
+ else if(arg[i] != null) s = arg[i].toString();
+ if(s != null) sb.append(" ").append(s);
+ sb.append("\n");
+ }
+ sb.append(" }\n");
+ } else {
+ sb.append(";");
+ }
+ }
+
+ MethodGen(Type.Class c, DataInput in, ConstantPool cp) throws IOException {
+ this.flags = in.readShort();
+ if ((flags & ~VALID_METHOD_FLAGS) != 0) throw new ClassFile.ClassReadExn("invalid flags");
+ String name = cp.getUtf8KeyByIndex(in.readShort());
+ this.method = c.method(name+cp.getUtf8KeyByIndex(in.readShort()));
+ this.attrs = new ClassFile.AttrGen(in,cp);
+
+ if((flags & (NATIVE|ABSTRACT))==0) {
+ byte[] codeAttr = (byte[]) attrs.get("Code");
+ if(codeAttr == null) throw new ClassFile.ClassReadExn("code attr expected");
+ DataInputStream ci = new DataInputStream(new ByteArrayInputStream(codeAttr));
+ maxStack = ci.readUnsignedShort();
+ maxLocals = ci.readUnsignedShort();
+ int codeLen = ci.readInt();
+ int[] bytecodeMap = parseCode(ci,codeLen,cp);
+ int numExns = ci.readUnsignedShort();
+ while(numExns-- > 0)
+ exnTable.addElement(new ExnTableEnt(ci,cp,bytecodeMap));
+ codeAttrs = new ClassFile.AttrGen(ci,cp);
+ // FEATURE: Support these
+ // NOTE: Until we can support them properly we HAVE to delete them,
+ // they'll be incorrect after we rewrite the constant pool, etc
+ codeAttrs.remove("LineNumberTable");
+ codeAttrs.remove("LocalVariableTable");
+
+ } else {
+ codeAttrs = new ClassFile.AttrGen();
+ }
+
+ if(attrs.contains("Exceptions")) {
+ DataInputStream ei = new DataInputStream(new ByteArrayInputStream((byte[]) attrs.get("Exceptions")));
+ int exnCount = ei.readUnsignedShort();
+ while(exnCount-- > 0) {
+ Type.Class t = (Type.Class) cp.getKeyByIndex(ei.readUnsignedShort());
+ thrownExceptions.put(t,t);
+ }
+ }
+ }
+
+ final int[] parseCode(DataInputStream in, int codeLen, ConstantPool cp) throws IOException {
+ int[] map = new int[codeLen];
+ int pc;
+ for(pc=0;pc<map.length;pc++) map[pc] = -1;
+ for(pc=0;pc<codeLen;) {
+ byte op = in.readByte();
+ int opdata = OP_DATA[op&0xff];
+ //System.err.println("Processing " + Integer.toString(op&0xff,16) + " at " + pc);
+ if((opdata&OP_VALID_FLAG)==0) throw new ClassFile.ClassReadExn("invalid bytecode " + (op&0xff));
+ int argLength = opdata & OP_ARG_LENGTH_MASK;
+ int mypc = pc;
+ map[mypc] = size();
+ pc += 1 + (argLength == 7 ? 0 : argLength);
+ if(argLength == 0) { add(op); continue; }
+ Object arg;
+ switch(op) {
+ case IINC:
+ arg = new Pair(in.readUnsignedByte(),in.readByte());
+ break;
+ case TABLESWITCH:
+ case LOOKUPSWITCH:
+ SI si;
+ for(;(pc&3) != 0;pc++) if(in.readByte() != 0) throw new ClassFile.ClassReadExn("invalid padding");
+ int def = in.readInt() + mypc;
+ pc += 4;
+ if(op == LOOKUPSWITCH) {
+ LSI lsi = new LSI(in.readInt());
+ pc += 4;
+ for(int i=0;i<lsi.size();i++) {
+ lsi.setVal(i,in.readInt());
+ lsi.setTarget(i,in.readInt() + mypc);
+ pc += 8;
+ }
+ si = lsi;
+ } else {
+ int lo = in.readInt();
+ int hi = in.readInt();
+ pc += 8;
+ TSI tsi = new TSI(lo,hi);
+ for(int i=0;i<tsi.size();i++) { tsi.setTarget(i,in.readInt() + mypc); pc += 4; }
+ si = tsi;
+ }
+ si.setDefaultTarget(def);
+ arg = si;
+ break;
+ case WIDE: {
+ byte wideop = in.readByte();
+ arg = wideop == IINC
+ ? new Wide(wideop,in.readUnsignedShort(),in.readShort())
+ : new Wide(wideop,in.readUnsignedShort());
+ pc += wideop == IINC ? 5 : 3;
+ break;
+ }
+ case MULTIANEWARRAY:
+ arg = new MultiANewArray((Type.Class)cp.getKeyByIndex(in.readUnsignedShort()),in.readUnsignedByte());
+ break;
+ case INVOKEINTERFACE: {
+ ConstantPool.Ent ent = cp.getByIndex(in.readUnsignedShort());
+ if(ent.tag != CONSTANT_INTERFACEMETHODREF) throw new ClassFile.ClassReadExn("illegal argument to bytecode");
+ arg = ((ConstantPool.InterfaceMethodKey)ent.key()).method;
+ if(in.readByte() == 0 || in.readByte() != 0) throw new ClassFile.ClassReadExn("illegal count or 0 arg to invokeinterface");
+ break;
+ }
+ default:
+ if((opdata&OP_CPENT_FLAG)!=0) {
+ ConstantPool.Ent ent = cp.getByIndex(argLength == 2 ? in.readUnsignedShort() : argLength == 1 ? in.readUnsignedByte() : -1);
+ int tag = ent.tag;
+ Object key = ent.key();
+ switch(op) {
+ case LDC:
+ case LDC_W:
+ case LDC2_W:
+ switch(tag) {
+ case CONSTANT_INTEGER:
+ case CONSTANT_FLOAT:
+ case CONSTANT_LONG:
+ case CONSTANT_DOUBLE:
+ case CONSTANT_STRING:
+ case CONSTANT_CLASS:
+ break;
+ default:
+ throw new ClassFile.ClassReadExn("illegal argument to bytecode 0x" + Integer.toString(op&0xff,16));
+ }
+ break;
+ case GETSTATIC:
+ case PUTSTATIC:
+ case GETFIELD:
+ case PUTFIELD:
+ if(tag != CONSTANT_FIELDREF) throw new ClassFile.ClassReadExn("illegal argument to bytecode 0x" + Integer.toString(op&0xff,16));
+ break;
+ case INVOKEVIRTUAL:
+ case INVOKESPECIAL:
+ case INVOKESTATIC:
+ if(tag != CONSTANT_METHODREF) throw new ClassFile.ClassReadExn("illegal argument to bytecode 0x" + Integer.toString(op&0xff,16));
+ break;
+ case NEW:
+ case ANEWARRAY:
+ case CHECKCAST:
+ case INSTANCEOF:
+ if(tag != CONSTANT_CLASS) throw new ClassFile.ClassReadExn("illegal argument to bytecode 0x" + Integer.toString(op&0xff,16));
+ break;
+ default:
+ throw new Error("should never happen");
+ }
+ arg = key;
+ } else {
+ // treat everything else (including branches for now) as plain old ints
+ int n;
+ boolean unsigned = (opdata&OP_UNSIGNED_FLAG)!=0;
+ if(argLength == 1) n = unsigned ? in.readUnsignedByte() : in.readByte();
+ else if(argLength == 2) n = unsigned ? in.readUnsignedShort() : in.readShort();
+ else throw new Error("should never happen");
+ if((opdata&OP_BRANCH_FLAG)!=0) n += mypc;
+ arg = N(n);
+ }
+ break;
+ }
+ add(op,arg);
+ }
+ if(pc != codeLen)
+ throw new ClassFile.ClassReadExn("didn't read enough code (" + pc + "/" + codeLen + " in " + method.name + ")");
+ for(int i=0;i<size();i++) {
+ switch(op[i]) {
+ case TABLESWITCH:
+ case LOOKUPSWITCH:
+ {
+ SI si = (SI) arg[i];
+
+ int pos = map[si.getDefaultTarget()];
+ if(pos < 0) throw new ClassFile.ClassReadExn("default target points to invalid bytecode: " + si.getDefaultTarget());
+ si.setDefaultTarget(pos);
+
+ for(int j=0;j<si.size();j++) {
+ pos = map[si.getTarget(j)];
+ if(pos < 0) throw new ClassFile.ClassReadExn("target points to invalid bytecode");
+ si.setTarget(j,pos);
+ }
+ break;
+ }
+ default:
+ if(OP_BRANCH(op[i])) {
+ int pos = map[((Integer)arg[i]).intValue()];
+ if(pos < 0) throw new ClassFile.ClassReadExn("branch points to invalid bytecode");
+ arg[i] = N(pos);
+ }
+ break;
+ }
+ }
+ return map;
+ }
+
+ MethodGen(Type.Class.Method method, int flags) {
+ if ((flags & ~VALID_METHOD_FLAGS) != 0) throw new IllegalArgumentException("invalid flags");
+ this.method = method;