diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/main/javassist/CtBehavior.java | 9 | ||||
-rw-r--r-- | src/main/javassist/CtField.java | 11 | ||||
-rw-r--r-- | src/main/javassist/bytecode/ClassFileWriter.java | 16 | ||||
-rw-r--r-- | src/main/javassist/bytecode/SignatureAttribute.java | 22 | ||||
-rw-r--r-- | src/main/javassist/bytecode/stackmap/BasicBlock.java | 160 | ||||
-rw-r--r-- | src/main/javassist/bytecode/stackmap/StackAnalyzer.java | 846 |
6 files changed, 1057 insertions, 7 deletions
diff --git a/src/main/javassist/CtBehavior.java b/src/main/javassist/CtBehavior.java index 6f4777e8..a71b4830 100644 --- a/src/main/javassist/CtBehavior.java +++ b/src/main/javassist/CtBehavior.java @@ -262,7 +262,16 @@ public abstract class CtBehavior extends CtMember { * and the return type, <code>getSignature()</code> returns the * same string (the return type of constructors is <code>void</code>). * + * <p>Note that the returned string is not the type signature + * contained in the <code>SignatureAttirbute</code>. It is + * a descriptor. To obtain a type signature, call the following + * methods: + * + * <ul><pre>getMethodInfo().getAttribute(SignatureAttribute.tag) + * </pre></ul> + * * @see javassist.bytecode.Descriptor + * @see javassist.bytecode.SignatureAttribute */ public String getSignature() { return methodInfo.getDescriptor(); diff --git a/src/main/javassist/CtField.java b/src/main/javassist/CtField.java index c8d62f00..1219ecb0 100644 --- a/src/main/javassist/CtField.java +++ b/src/main/javassist/CtField.java @@ -285,6 +285,17 @@ public class CtField extends CtMember { * Returns the character string representing the type of the field. * If two fields have the same type, * <code>getSignature()</code> returns the same string. + * + * <p>Note that the returned string is not the type signature + * contained in the <code>SignatureAttirbute</code>. It is + * a descriptor. To obtain a type signature, call the following + * methods: + * + * <ul><pre>getFieldInfo().getAttribute(SignatureAttribute.tag) + * </pre></ul> + * + * @see javassist.bytecode.Descriptor + * @see javassist.bytecode.SignatureAttribute */ public String getSignature() { return fieldInfo.getDescriptor(); diff --git a/src/main/javassist/bytecode/ClassFileWriter.java b/src/main/javassist/bytecode/ClassFileWriter.java index 3ed2d571..528d98af 100644 --- a/src/main/javassist/bytecode/ClassFileWriter.java +++ b/src/main/javassist/bytecode/ClassFileWriter.java @@ -68,7 +68,7 @@ public class ClassFileWriter { out.println(Modifier.toString(AccessFlag.toModifier(acc)) + " " + finfo.getName() + "\t" + finfo.getDescriptor()); - printAttributes(finfo.getAttributes(), out, false); + printAttributes(finfo.getAttributes(), out, 'f'); } out.println(); @@ -80,15 +80,15 @@ public class ClassFileWriter { out.println(Modifier.toString(AccessFlag.toModifier(acc)) + " " + minfo.getName() + "\t" + minfo.getDescriptor()); - printAttributes(minfo.getAttributes(), out, false); + printAttributes(minfo.getAttributes(), out, 'm'); out.println(); } out.println(); - printAttributes(cf.getAttributes(), out, true); + printAttributes(cf.getAttributes(), out, 'c'); } - static void printAttributes(List list, PrintWriter out, boolean forClass) { + static void printAttributes(List list, PrintWriter out, char kind) { if (list == null) return; @@ -104,7 +104,7 @@ public class ClassFileWriter { + ", " + ca.getExceptionTable().size() + " catch blocks"); out.println("<code attribute begin>"); - printAttributes(ca.getAttributes(), out, forClass); + printAttributes(ca.getAttributes(), out, kind); out.println("<code attribute end>"); } else if (ai instanceof StackMapTable) { @@ -118,10 +118,12 @@ public class ClassFileWriter { out.println("signature: " + sig); try { String s; - if (forClass) + if (kind == 'c') s = SignatureAttribute.toClassSignature(sig).toString(); - else + else if (kind == 'm') s = SignatureAttribute.toMethodSignature(sig).toString(); + else + s = SignatureAttribute.toFieldSignature(sig).toString(); out.println(" " + s); } diff --git a/src/main/javassist/bytecode/SignatureAttribute.java b/src/main/javassist/bytecode/SignatureAttribute.java index 39f4e35b..808809ed 100644 --- a/src/main/javassist/bytecode/SignatureAttribute.java +++ b/src/main/javassist/bytecode/SignatureAttribute.java @@ -489,6 +489,9 @@ public class SignatureAttribute extends AttributeInfo { } } + /** + * Type variables. + */ public static class TypeVariable extends ObjectType { String name; @@ -516,6 +519,7 @@ public class SignatureAttribute extends AttributeInfo { * * @param sig the signature. * @throws BadBytecode thrown when a syntactical error is found. + * @since 3.5 */ public static ClassSignature toClassSignature(String sig) throws BadBytecode { try { @@ -531,6 +535,7 @@ public class SignatureAttribute extends AttributeInfo { * * @param sig the signature. * @throws BadBytecode thrown when a syntactical error is found. + * @since 3.5 */ public static MethodSignature toMethodSignature(String sig) throws BadBytecode { try { @@ -541,6 +546,23 @@ public class SignatureAttribute extends AttributeInfo { } } + /** + * Parses the given signature string as a field type signature. + * + * @param sig the signature string. + * @return the field type signature. + * @throws BadBytecode thrown when a syntactical error is found. + * @since 3.5 + */ + public static ObjectType toFieldSignature(String sig) throws BadBytecode { + try { + return parseObjectType(sig, new Cursor(), false); + } + catch (IndexOutOfBoundsException e) { + throw error(sig); + } + } + private static ClassSignature parseSig(String sig) throws BadBytecode, IndexOutOfBoundsException { diff --git a/src/main/javassist/bytecode/stackmap/BasicBlock.java b/src/main/javassist/bytecode/stackmap/BasicBlock.java new file mode 100644 index 00000000..d7076152 --- /dev/null +++ b/src/main/javassist/bytecode/stackmap/BasicBlock.java @@ -0,0 +1,160 @@ +package javassist.bytecode.stackmap; + +import javassist.bytecode.*; + +public class BasicBlock { + public int position; + public int stackTop; + public int[] stackTypes, localsTypes; + public Object[] stackData, localsData; + + private BasicBlock(int pos) { + position = pos; + } + + public void set(int st, int[] stypes, Object[] sdata, int[] ltypes, Object[] ldata) + throws BadBytecode + { + if (stackTypes == null) { + stackTop = st; + stackTypes = copy(stypes); + stackData = copy(sdata); + localsTypes = copy(ltypes); + localsData = copy(ldata); + } + else { + if (st != stackTop) + throw new BadBytecode("verification failure"); + + int n = ltypes.length; + for (int i = 0; i < n; i++) + if (ltypes[i] != localsTypes[i]) { + localsTypes[i] = StackAnalyzer.EMPTY; + localsData[i] = null; + } + else if (ltypes[i] == StackAnalyzer.OBJECT + && !ldata[i].equals(localsData[i])) + ; // localsData[i] = ??; + } + } + + private static int[] copy(int[] a) { + int[] b = new int[a.length]; + System.arraycopy(a, 0, b, 0, a.length); + return b; + } + + private static Object[] copy(Object[] a) { + Object[] b = new Object[a.length]; + System.arraycopy(a, 0, b, 0, a.length); + return b; + } + + public static BasicBlock find(BasicBlock[] blocks, int pos) throws BadBytecode { + int n = blocks.length; + for (int i = 0; i < n; i++) + if (blocks[i].position == pos) + return blocks[i]; + + throw new BadBytecode("no basic block: " + pos); + } + + public static BasicBlock[] makeBlocks(CodeIterator ci, ExceptionTable et) + throws BadBytecode + { + ci.begin(); + int[] targets = new int[16]; + int size = 0; + while (ci.hasNext()) { + int index = ci.next(); + int op = ci.byteAt(index); + if ((Opcode.IFEQ <= op && op <= Opcode.IF_ACMPNE) + || op == Opcode.IFNULL || op == Opcode.IFNONNULL) + targets = add(targets, size++, index + ci.s16bitAt(index + 1)); + else if (Opcode.GOTO <= op && op <= Opcode.LOOKUPSWITCH) + switch (op) { + case Opcode.GOTO : + targets = add(targets, size++, index + ci.s16bitAt(index + 1)); + break; + case Opcode.JSR : + case Opcode.RET : + throw new BadBytecode("jsr/ret at " + index); + case Opcode.TABLESWITCH : { + int pos = (index & ~3) + 4; + targets = add(targets, size++, index + ci.s32bitAt(pos)); // default offset + int low = ci.s32bitAt(pos + 4); + int high = ci.s32bitAt(pos + 8); + int p = pos + 12; + int n = p + (high - low + 1) * 4; + while (p < n) { + targets = add(targets, size++, index + ci.s32bitAt(p)); + p += 4; + } + break; } + case Opcode.LOOKUPSWITCH : { + int pos = (index & ~3) + 4; + targets = add(targets, size++, index + ci.s32bitAt(pos)); // default offset + int p = pos + 8 + 4; + int n = p + ci.s32bitAt(pos + 4) * 8; + while (p < n) { + targets = add(targets, size++, index + ci.s32bitAt(p)); + p += 8; + } + break; } + } + else if (op == Opcode.GOTO_W) + targets = add(targets, size++, index + ci.s32bitAt(index + 1)); + else if (op == Opcode.JSR_W) + throw new BadBytecode("jsr_w at " + index); + } + + if (et != null) { + int i = et.size(); + while (--i >= 0) { + targets = add(targets, size++, et.startPc(i)); + targets = add(targets, size++, et.handlerPc(i)); + } + } + + return trimArray(targets); + } + + private static int[] add(int[] targets, int size, int value) { + if (targets.length >= size) { + int[] a = new int[size << 1]; + System.arraycopy(targets, 0, a, 0, targets.length); + targets = a; + } + + targets[size++] = value; + return targets; + } + + private static BasicBlock[] trimArray(int[] targets) { + int size = targets.length; + java.util.Arrays.sort(targets); + int s = 0; + int t0 = 0; + for (int i = 0; i < size; i++) { + int t = targets[i]; + if (t != t0) { + s++; + t0 = t; + } + } + + BasicBlock[] results = new BasicBlock[s + 1]; + results[0] = new BasicBlock(0); + t0 = 0; + for (int i = 0, j = 1; i < size; i++) { + int t = targets[i]; + if (t != t0) { + BasicBlock b = new BasicBlock(t); + results[j++] = b; + t0 = t; + } + } + + return results; + } +} diff --git a/src/main/javassist/bytecode/stackmap/StackAnalyzer.java b/src/main/javassist/bytecode/stackmap/StackAnalyzer.java new file mode 100644 index 00000000..12b4892d --- /dev/null +++ b/src/main/javassist/bytecode/stackmap/StackAnalyzer.java @@ -0,0 +1,846 @@ +package javassist.bytecode.stackmap; + +import javassist.bytecode.StackMapTable; +import javassist.bytecode.ByteArray; +import javassist.bytecode.Opcode; +import javassist.bytecode.ConstPool; +import javassist.bytecode.Descriptor; +import javassist.bytecode.BadBytecode; + +public class StackAnalyzer { + private ConstPool cpool; + private int stackTop; + private int[] stackTypes; + private Object[] stackData; // String or Integer + + private int[] localsTypes; + private boolean[] localsUpdated; + private Object[] localsData; // String or Integer + + static final int EMPTY = -1; + static final int TOP = StackMapTable.TOP; + static final int INTEGER = StackMapTable.INTEGER; + static final int FLOAT = StackMapTable.FLOAT; + static final int DOUBLE = StackMapTable.DOUBLE; + static final int LONG = StackMapTable.LONG; + static final int NULL = StackMapTable.NULL; + static final int THIS = StackMapTable.THIS; + static final int OBJECT = StackMapTable.OBJECT; + static final int UNINIT = StackMapTable.UNINIT; + + public StackAnalyzer(ConstPool cp, int maxStack, int maxLocals) { + cpool = cp; + stackTop = 0; + stackTypes = null; + localsTypes = null; + growStack(maxStack); + growLocals(maxLocals); + } + + public void clearUpdatedFlags() { + boolean[] updated = localsUpdated; + for (int i = 0; i < updated.length; i++) + updated[i] = false; + } + + public void growStack(int size) { + int oldSize; + int[] types = new int[size]; + Object[] data = new Object[size]; + if (stackTypes == null) + oldSize = 0; + else { + oldSize = stackTypes.length; + System.arraycopy(stackTypes, 0, types, 0, oldSize); + System.arraycopy(stackData, 0, data, 0, oldSize); + } + + stackTypes = types; + stackData = data; + for (int i = oldSize; i < size; i++) + stackTypes[i] = EMPTY; + } + + public void growLocals(int size) { + int oldSize; + int[] types = new int[size]; + boolean[] updated = new boolean[size]; + Object[] data = new Object[size]; + if (localsTypes == null) + oldSize = 0; + else { + oldSize = localsTypes.length; + System.arraycopy(localsTypes, 0, types, 0, oldSize); + System.arraycopy(localsUpdated, 0, updated, 0, oldSize); + System.arraycopy(localsData, 0, data, 0, oldSize); + } + + localsTypes = types; + localsUpdated = updated; + localsData = data; + for (int i = oldSize; i < size; i++) + localsTypes[i] = EMPTY; + } + + public void pushType(int type, Object data) { + stackTypes[stackTop] = type; + stackData[stackTop++] = data; + } + + /** + * @return the size of the instruction at POS. + */ + protected int doOpcode(int pos, byte[] code) throws BadBytecode { + int op = code[pos] & 0xff; + if (op < 96) + if (op < 54) + return doOpcode0_53(pos, code, op); + else + return doOpcode54_95(pos, code, op); + else + if (op < 148) + return doOpcode96_147(pos, code, op); + else + return doOpcode148_201(pos, code, op); + } + + protected void visitBranch(int pos, byte[] code, int offset) {} + protected void visitGoto(int pos, byte[] code, int offset) {} + protected void visitTableSwitch(int pos, byte[] code, int n, int offsetPos, int defaultByte) {} + protected void visitLookupSwitch(int pos, byte[] code, int n, int pairsPos, int defaultByte) {} + protected void visitReturn(int pos, byte[] code) {} + protected void visitThrow(int pos, byte[] code) {} + + /** + * Invoked when the visited instruction is jsr. + */ + protected void visitJSR(int pos, byte[] code) throws BadBytecode { + throwBadBytecode(pos, "jsr"); + } + + /** + * Invoked when the visited instruction is ret. + */ + protected void visitRET(int pos, byte[] code) throws BadBytecode { + throwBadBytecode(pos, "ret"); + } + + private void throwBadBytecode(int pos, String name) throws BadBytecode { + throw new BadBytecode(name + " at " + pos); + } + + private int doOpcode0_53(int pos, byte[] code, int op) throws BadBytecode { + int[] stackTypes = this.stackTypes; + switch (op) { + case Opcode.NOP : + break; + case Opcode.ACONST_NULL : + stackTypes[stackTop++] = NULL; + break; + case Opcode.ICONST_M1 : + case Opcode.ICONST_0 : + case Opcode.ICONST_1 : + case Opcode.ICONST_2 : + case Opcode.ICONST_3 : + case Opcode.ICONST_4 : + case Opcode.ICONST_5 : + stackTypes[stackTop++] = INTEGER; + break; + case Opcode.LCONST_0 : + case Opcode.LCONST_1 : + stackTypes[stackTop++] = LONG; + stackTypes[stackTop++] = TOP; + break; + case Opcode.FCONST_0 : + case Opcode.FCONST_1 : + case Opcode.FCONST_2 : + stackTypes[stackTop++] = FLOAT; + break; + case Opcode.DCONST_0 : + case Opcode.DCONST_1 : + stackTypes[stackTop++] = DOUBLE; + stackTypes[stackTop++] = TOP; + break; + case Opcode.BIPUSH : + case Opcode.SIPUSH : + stackTypes[stackTop++] = INTEGER; + return op == Opcode.SIPUSH ? 3 : 2; + case Opcode.LDC : + doLDC(code[pos + 1] & 0xff); + return 2; + case Opcode.LDC_W : + case Opcode.LDC2_W : + doLDC(ByteArray.readU16bit(code, pos + 1)); + return 3; + case Opcode.ILOAD : + return doXLOAD(INTEGER); + case Opcode.LLOAD : + return doXLOAD(LONG); + case Opcode.FLOAD : + return doXLOAD(FLOAT); + case Opcode.DLOAD : + return doXLOAD(DOUBLE); + case Opcode.ALOAD : + stackTypes[stackTop] = OBJECT; + stackData[stackTop++] = localsData[code[pos + 1] & 0xff]; + return 2; + case Opcode.ILOAD_0 : + case Opcode.ILOAD_1 : + case Opcode.ILOAD_2 : + case Opcode.ILOAD_3 : + stackTypes[stackTop++] = INTEGER; + break; + case Opcode.LLOAD_0 : + case Opcode.LLOAD_1 : + case Opcode.LLOAD_2 : + case Opcode.LLOAD_3 : + stackTypes[stackTop++] = LONG; + stackTypes[stackTop++] = TOP; + break; + case Opcode.FLOAD_0 : + case Opcode.FLOAD_1 : + case Opcode.FLOAD_2 : + case Opcode.FLOAD_3 : + stackTypes[stackTop++] = FLOAT; + break; + case Opcode.DLOAD_0 : + case Opcode.DLOAD_1 : + case Opcode.DLOAD_2 : + case Opcode.DLOAD_3 : + stackTypes[stackTop++] = DOUBLE; + stackTypes[stackTop++] = TOP; + break; + case Opcode.ALOAD_0 : + case Opcode.ALOAD_1 : + case Opcode.ALOAD_2 : + case Opcode.ALOAD_3 : + stackTypes[stackTop] = OBJECT; + stackData[stackTop++] = localsData[op - Opcode.ALOAD_0]; + break; + case Opcode.IALOAD : + stackTypes[--stackTop - 1] = INTEGER; + break; + case Opcode.LALOAD : + stackTypes[stackTop - 2] = LONG; + stackTypes[stackTop - 1] = TOP; + break; + case Opcode.FALOAD : + stackTypes[--stackTop - 1] = FLOAT; + break; + case Opcode.DALOAD : + stackTypes[stackTop - 2] = DOUBLE; + stackTypes[stackTop - 1] = TOP; + break; + case Opcode.AALOAD : { + int s = --stackTop - 1; + stackTypes[s] = OBJECT; + Object data = stackData[s]; + if (data != null && data instanceof String) + stackData[s] = getDerefType((String)data); + else + throw new BadBytecode("bad AALOAD"); + + break; } + case Opcode.BALOAD : + case Opcode.CALOAD : + case Opcode.SALOAD : + stackTypes[--stackTop - 1] = INTEGER; + break; + default : + throw new RuntimeException("fatal"); + } + + return 1; + } + + private void doLDC(int index) { + int[] stackTypes = this.stackTypes; + int tag = cpool.getTag(index); + if (tag == ConstPool.CONST_String) { + stackTypes[stackTop] = OBJECT; + stackData[stackTop++] = "java/lang/String"; + } + else if (tag == ConstPool.CONST_Integer) + stackTypes[stackTop++] = INTEGER; + else if (tag == ConstPool.CONST_Float) + stackTypes[stackTop++] = FLOAT; + else if (tag == ConstPool.CONST_Long) { + stackTypes[stackTop++] = LONG; + stackTypes[stackTop++] = TOP; + } + else if (tag == ConstPool.CONST_Double) { + stackTypes[stackTop++] = DOUBLE; + stackTypes[stackTop++] = TOP; + } + else if (tag == ConstPool.CONST_Class) { + stackTypes[stackTop] = OBJECT; + stackData[stackTop++] = "java/lang/Class"; + } + else + throw new RuntimeException("bad LDC: " + tag); + } + + private int doXLOAD(int type) { + int[] stackTypes = this.stackTypes; + stackTypes[stackTop++] = type; + if (type == LONG || type == DOUBLE) + stackTypes[stackTop++] = TOP; + + return 2; + } + + private String getDerefType(String type) throws BadBytecode { + if (type.charAt(0) == '[') { + String type2 = type.substring(1); + if (type2.length() > 0) { + char c = type2.charAt(0); + if (c == '[') + return type2; + else if (c == 'L') + return type2.substring(1, type.length() - 2); + else + return type2; + } + } + + throw new BadBytecode("bad array type for AALOAD: " + type); + } + + private int doOpcode54_95(int pos, byte[] code, int op) { + int[] localsTypes = this.localsTypes; + int[] stackTypes = this.stackTypes; + switch (op) { + case Opcode.ISTORE : + return doXSTORE(pos, code, INTEGER); + case Opcode.LSTORE : + return doXSTORE(pos, code, LONG); + case Opcode.FSTORE : + return doXSTORE(pos, code, FLOAT); + case Opcode.DSTORE : + return doXSTORE(pos, code, DOUBLE); + case Opcode.ASTORE : + return doXSTORE(pos, code, OBJECT); + case Opcode.ISTORE_0 : + case Opcode.ISTORE_1 : + case Opcode.ISTORE_2 : + case Opcode.ISTORE_3 : + { int var = op - Opcode.ISTORE_0; + localsTypes[var] = INTEGER; + localsUpdated[var] = true; } + stackTop--; + break; + case Opcode.LSTORE_0 : + case Opcode.LSTORE_1 : + case Opcode.LSTORE_2 : + case Opcode.LSTORE_3 : + { int var = op - Opcode.LSTORE_0; + localsTypes[var] = LONG; + localsTypes[var + 1] = TOP; + localsUpdated[var] = true; } + stackTop -= 2; + break; + case Opcode.FSTORE_0 : + case Opcode.FSTORE_1 : + case Opcode.FSTORE_2 : + case Opcode.FSTORE_3 : + { int var = op - Opcode.FSTORE_0; + localsTypes[var] = FLOAT; + localsUpdated[var] = true; } + stackTop--; + break; + case Opcode.DSTORE_0 : + case Opcode.DSTORE_1 : + case Opcode.DSTORE_2 : + case Opcode.DSTORE_3 : + { int var = op - Opcode.DSTORE_0; + localsTypes[var] = DOUBLE; + localsTypes[var + 1] = TOP; + localsUpdated[var] = true; } + stackTop -= 2; + break; + case Opcode.ASTORE_0 : + case Opcode.ASTORE_1 : + case Opcode.ASTORE_2 : + case Opcode.ASTORE_3 : + { int var = op - Opcode.ASTORE_0; + localsTypes[var] = OBJECT; + localsUpdated[var] = true; + localsData[var] = stackData[--stackTop]; } + break; + case Opcode.IASTORE : + case Opcode.LASTORE : + case Opcode.FASTORE : + case Opcode.DASTORE : + case Opcode.AASTORE : + case Opcode.BASTORE : + case Opcode.CASTORE : + case Opcode.SASTORE : + stackTop -= (op == Opcode.LASTORE || op == Opcode.DASTORE) ? 4 : 3; + break; + case Opcode.POP : + stackTop--; + break; + case Opcode.POP2 : + stackTop -= 2; + break; + case Opcode.DUP : { + int sp = stackTop; + stackTypes[sp] = stackTypes[sp - 1]; + stackData[sp] = stackData[sp - 1]; + stackTop = sp + 1; + break; } + case Opcode.DUP_X1 : + case Opcode.DUP_X2 : { + int len = op - Opcode.DUP_X1 + 2; + doDUP_XX(1, len); + int sp = stackTop; + stackTypes[sp - len] = stackTypes[sp]; + stackData[sp - len] = stackData[sp]; + stackTop = sp + 1; + break; } + case Opcode.DUP2 : + doDUP_XX(2, 2); + stackTop += 2; + break; + case Opcode.DUP2_X1 : + case Opcode.DUP2_X2 : { + int len = op - Opcode.DUP2_X1 + 3; + doDUP_XX(2, len); + Object[] stackData = this.stackData; + int sp = stackTop; + stackTypes[sp - len] = stackTypes[sp]; + stackData[sp - len] = stackData[sp]; + stackTypes[sp - len + 1] = stackTypes[sp + 1]; + stackData[sp - len + 1] = stackData[sp + 1]; + stackTop = sp + 2; + break; } + case Opcode.SWAP : { + Object[] stackData = this.stackData; + int sp = stackTop - 1; + int t = stackTypes[sp]; + Object d = stackData[sp]; + stackTypes[sp] = stackTypes[sp - 1]; + stackData[sp] = stackData[sp - 1]; + stackTypes[sp - 1] = t; + stackData[sp - 1] = d; + break; } + default : + throw new RuntimeException("fatal"); + } + + return 1; + } + + private int doXSTORE(int pos, byte[] code, int type) { + int index = code[pos + 1] & 0xff; + return doXSTORE(index, type); + } + + private int doXSTORE(int index, int type) { + stackTop--; + localsTypes[index] = type; + localsUpdated[index] = true; + if (type == LONG || type == DOUBLE) { + stackTop--; + localsTypes[index + 1] = TOP; + } + else if (type == OBJECT) + localsData[index] = stackData[stackTop]; + + return 2; + } + + private void doDUP_XX(int delta, int len) { + int types[] = stackTypes; + Object data[] = stackData; + int sp = stackTop; + int end = sp - len; + while (sp > end) { + types[sp + delta] = types[sp]; + data[sp + delta] = data[sp]; + sp--; + } + } + + private int doOpcode96_147(int pos, byte[] code, int op) { + if (op <= Opcode.LXOR) { // IADD...LXOR + stackTop -= Opcode.STACK_GROW[op]; + return 1; + } + + switch (op) { + case Opcode.IINC : + return 3; + case Opcode.I2L : + stackTypes[stackTop] = LONG; + stackTypes[stackTop - 1] = TOP; + stackTop++; + break; + case Opcode.I2F : + stackTypes[stackTop - 1] = FLOAT; + break; + case Opcode.I2D : + stackTypes[stackTop] = DOUBLE; + stackTypes[stackTop - 1] = TOP; + stackTop++; + break; + case Opcode.L2I : + stackTypes[--stackTop - 1] = INTEGER; + break; + case Opcode.L2F : + stackTypes[--stackTop - 1] = FLOAT; + break; + case Opcode.L2D : + stackTypes[stackTop - 1] = DOUBLE; + break; + case Opcode.F2I : + stackTypes[stackTop - 1] = INTEGER; + break; + case Opcode.F2L : + stackTypes[stackTop - 1] = TOP; + stackTypes[stackTop++] = LONG; + break; + case Opcode.F2D : + stackTypes[stackTop - 1] = TOP; + stackTypes[stackTop++] = DOUBLE; + break; + case Opcode.D2I : + stackTypes[--stackTop - 1] = INTEGER; + break; + case Opcode.D2L : + stackTypes[stackTop - 1] = LONG; + break; + case Opcode.D2F : + stackTypes[--stackTop - 1] = FLOAT; + break; + case Opcode.I2B : + case Opcode.I2C : + case Opcode.I2S : + break; + default : + throw new RuntimeException("fatal"); + } + + return 1; + } + + private int doOpcode148_201(int pos, byte[] code, int op) throws BadBytecode { + switch (op) { + case Opcode.LCMP : + stackTypes[stackTop - 4] = INTEGER; + stackTop -= 3; + break; + case Opcode.FCMPL : + case Opcode.FCMPG : + stackTypes[--stackTop - 1] = INTEGER; + break; + case Opcode.DCMPL : + case Opcode.DCMPG : + stackTypes[stackTop - 4] = INTEGER; + stackTop -= 3; + break; + case Opcode.IFEQ : + case Opcode.IFNE : + case Opcode.IFLT : + case Opcode.IFGE : + case Opcode.IFGT : + case Opcode.IFLE : + stackTop--; // branch + visitBranch(pos, code, ByteArray.readS16bit(code, pos + 1)); + return 3; + case Opcode.IF_ICMPEQ : + case Opcode.IF_ICMPNE : + case Opcode.IF_ICMPLT : + case Opcode.IF_ICMPGE : + case Opcode.IF_ICMPGT : + case Opcode.IF_ICMPLE : + case Opcode.IF_ACMPEQ : + case Opcode.IF_ACMPNE : + stackTop -= 2; // branch + visitBranch(pos, code, ByteArray.readS16bit(code, pos + 1)); + return 3; + case Opcode.GOTO : + visitGoto(pos, code, ByteArray.readS16bit(code, pos + 1)); + return 3; // branch + case Opcode.JSR : + stackTypes[stackTop++] = TOP; // not allowed? + visitJSR(pos, code); + return 3; // branch + case Opcode.RET : + visitRET(pos, code); + return 2; // not allowed? + case Opcode.TABLESWITCH : { + stackTop--; // branch + int pos2 = (pos & ~3) + 8; + int low = ByteArray.read32bit(code, pos2); + int high = ByteArray.read32bit(code, pos2 + 4); + int n = high - low + 1; + visitTableSwitch(pos, code, n, pos2 + 8, ByteArray.read32bit(code, pos2 - 4)); + return n * 4 + 16 - (pos & 3); } + case Opcode.LOOKUPSWITCH : { + stackTop--; // branch + int pos2 = (pos & ~3) + 8; + int n = ByteArray.read32bit(code, pos2); + visitLookupSwitch(pos, code, n, pos2 + 4, ByteArray.read32bit(code, pos2 - 4)); + return n * 8 + 12 - (pos & 3); } + case Opcode.IRETURN : + stackTop--; + visitReturn(pos, code); + break; + case Opcode.LRETURN : + stackTop -= 2; + visitReturn(pos, code); + break; + case Opcode.FRETURN : + stackTop--; + visitReturn(pos, code); + break; + case Opcode.DRETURN : + stackTop -= 2; + visitReturn(pos, code); + break; + case Opcode.ARETURN : + stackTop--; + visitReturn(pos, code); + break; + case Opcode.RETURN : + visitReturn(pos, code); + break; + case Opcode.GETSTATIC : + return doFieldAccess(pos, code, true); + case Opcode.PUTSTATIC : + return doFieldAccess(pos, code, false); + case Opcode.GETFIELD : + stackTop--; + return doFieldAccess(pos, code, true); + case Opcode.PUTFIELD : + stackTop--; + return doFieldAccess(pos, code, false); + case Opcode.INVOKEVIRTUAL : + case Opcode.INVOKESPECIAL : + return doInvokeMethod(pos, code, 1); + case Opcode.INVOKESTATIC : + return doInvokeMethod(pos, code, 0); + case Opcode.INVOKEINTERFACE : + return doInvokeIntfMethod(pos, code, 1); + case 186 : + throw new RuntimeException("bad opcode 186"); + case Opcode.NEW : { + int i = ByteArray.readU16bit(code, pos + 1); + stackTypes[stackTop - 1] = UNINIT; + stackData[stackTop - 1] = new Integer(pos); + return 3; } + case Opcode.NEWARRAY : + return doNEWARRAY(pos, code); + case Opcode.ANEWARRAY : { + int i = ByteArray.readU16bit(code, pos + 1); + stackTypes[stackTop - 1] = OBJECT; + stackData[stackTop - 1] + = "[L" + cpool.getClassInfo(i).replace('.', '/') + ";"; + return 3; } + case Opcode.ARRAYLENGTH : + stackTypes[stackTop - 1] = INTEGER; + break; + case Opcode.ATHROW : + stackTop--; // branch? + visitThrow(pos, code); + break; + case Opcode.CHECKCAST : { + int i = ByteArray.readU16bit(code, pos + 1); + stackData[stackTop - 1] = cpool.getClassInfo(i); + return 3; } + case Opcode.INSTANCEOF : + stackTypes[stackTop - 1] = INTEGER; + return 3; + case Opcode.MONITORENTER : + case Opcode.MONITOREXIT : + stackTop--; + break; + case Opcode.WIDE : + return doWIDE(pos, code); + case Opcode.MULTIANEWARRAY : + return doMultiANewArray(pos, code); + case Opcode.IFNULL : + case Opcode.IFNONNULL : + stackTop--; // branch + visitBranch(pos, code, ByteArray.readS16bit(code, pos + 1)); + return 3; + case Opcode.GOTO_W : + visitGoto(pos, code, ByteArray.read32bit(code, pos + 1)); + return 5; // branch + case Opcode.JSR_W : + stackTypes[stackTop++] = TOP; // not allowed? + visitJSR(pos, code); + return 5; + } + return 1; + } + + private int doWIDE(int pos, byte[] code) { + int op = code[pos + 1] & 0xff; + switch (op) { + case Opcode.ILOAD : + doXLOAD(INTEGER); + break; + case Opcode.LLOAD : + doXLOAD(LONG); + break; + case Opcode.FLOAD : + doXLOAD(FLOAT); + break; + case Opcode.DLOAD : + doXLOAD(DOUBLE); + break; + case Opcode.ALOAD : + stackTypes[stackTop] = OBJECT; + stackData[stackTop++] = localsData[ByteArray.readU16bit(code, pos)]; + break; + case Opcode.ISTORE : + return doWIDE_STORE(pos, code, INTEGER); + case Opcode.LSTORE : + return doWIDE_STORE(pos, code, LONG); + case Opcode.FSTORE : + return doWIDE_STORE(pos, code, FLOAT); + case Opcode.DSTORE : + return doWIDE_STORE(pos, code, DOUBLE); + case Opcode.ASTORE : + return doWIDE_STORE(pos, code, OBJECT); + case Opcode.IINC : + return 6; + case Opcode.RET : + break; + default : + throw new RuntimeException("bad WIDE instruction: " + op); + } + + return 4; + } + + private int doWIDE_STORE(int pos, byte[] code, int type) { + int index = ByteArray.readU16bit(code, pos); + return doXSTORE(index, type); + } + + private int doFieldAccess(int pos, byte[] code, boolean isGet) { + int index = ByteArray.readU16bit(code, pos + 1); + String desc = cpool.getFieldrefType(index); + if (isGet) + pushMemberType(desc); + else + stackTop -= Descriptor.dataSize(desc); + + return 3; + } + + private int doNEWARRAY(int pos, byte[] code) { + int s = stackTop - 1; + stackTypes[s] = OBJECT; + String type; + switch (code[pos + 1] & 0xff) { + case Opcode.T_BOOLEAN : + type = "[Z"; + break; + case Opcode.T_CHAR : + type = "[C"; + break; + case Opcode.T_FLOAT : + type = "[F"; + break; + case Opcode.T_DOUBLE : + type = "[D"; + break; + case Opcode.T_BYTE : + type = "[B"; + break; + case Opcode.T_SHORT : + type = "[S"; + break; + case Opcode.T_INT : + type = "[I"; + break; + case Opcode.T_LONG : + type = "[J"; + break; + default : + throw new RuntimeException("bad newarray"); + } + + stackData[s] = type; + return 2; + } + + private int doMultiANewArray(int pos, byte[] code) { + int i = ByteArray.readU16bit(code, pos + 1); + int dim = code[pos + 3] & 0xff; + stackTop -= dim - 1; + String type = cpool.getClassInfo(i); + StringBuffer sbuf = new StringBuffer(); + while (dim-- > 0) + sbuf.append('['); + + sbuf.append('L').append(type.replace('.', '/')).append(';'); + stackTypes[stackTop - 1] = OBJECT; + stackData[stackTop - 1] = sbuf.toString(); + return 4; + } + + private int doInvokeMethod(int pos, byte[] code, int targetSize) { + int i = ByteArray.readU16bit(code, pos + 1); + String desc = cpool.getMethodrefType(i); + stackTop -= Descriptor.paramSize(desc) + targetSize; + pushMemberType(desc); + return 3; + } + + private int doInvokeIntfMethod(int pos, byte[] code, int targetSize) { + int i = ByteArray.readU16bit(code, pos + 1); + String desc = cpool.getInterfaceMethodrefType(i); + stackTop -= Descriptor.paramSize(desc) + targetSize; + pushMemberType(desc); + return 5; + } + + private void pushMemberType(String descriptor) { + int top = 0; + if (descriptor.charAt(0) == '(') { + top = descriptor.indexOf(')') + 1; + if (top < 1) + throw new IndexOutOfBoundsException("bad descriptor: " + + descriptor); + } + + int[] types = stackTypes; + int index = stackTop; + switch (descriptor.charAt(top)) { + case '[' : + types[index] = OBJECT; + stackData[index] = descriptor.substring(top); + break; + case 'L' : + types[index] = OBJECT; + stackData[index] = descriptor.substring(top + 1, + descriptor.indexOf(';', top)); + break; + case 'J' : + types[index] = LONG; + types[index + 1] = TOP; + stackTop += 2; + return; + case 'F' : + types[index] = FLOAT; + break; + case 'D' : + types[index] = DOUBLE; + types[index + 1] = TOP; + stackTop += 2; + return; + case 'V' : + return; + default : // C, B, S, I, Z + types[index] = INTEGER; + break; + } + + stackTop++; + } +} |