From: chiba Date: Fri, 9 Feb 2007 18:05:21 +0000 (+0000) Subject: modified javassist.convert.* to support the stack map table of Java 6. X-Git-Tag: rel_3_17_1_ga~284 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=fa1ebd7549d3f17ce49e7942703bc598f9142081;p=javassist.git modified javassist.convert.* to support the stack map table of Java 6. git-svn-id: http://anonsvn.jboss.org/repos/javassist/trunk@351 30ef5769-5b8d-40dd-aea6-55b5d6557bb3 --- diff --git a/.classpath b/.classpath index deae2f47..21f8fe5a 100644 --- a/.classpath +++ b/.classpath @@ -1,7 +1,6 @@ - - - + + diff --git a/.project b/.project index 20b52261..4d88bda1 100644 --- a/.project +++ b/.project @@ -1,6 +1,6 @@ - javassist + jvst @@ -10,16 +10,6 @@ - - org.eclipse.ui.externaltools.ExternalToolBuilder - full,incremental, - - - LaunchConfigHandle - <project>/.externalToolBuilders/Ant.launch - - - org.eclipse.jdt.core.javanature diff --git a/build.xml b/build.xml index a3dcadaf..214259f0 100644 --- a/build.xml +++ b/build.xml @@ -109,7 +109,7 @@ to ${build.classes.dir}. "); - StackMapTable.Writer.print((StackMapTable)ai, out); + StackMapTable.Printer.print((StackMapTable)ai, out); out.println(""); } else if (ai instanceof SignatureAttribute) { diff --git a/src/main/javassist/bytecode/CodeIterator.java b/src/main/javassist/bytecode/CodeIterator.java index 13948c91..fbad5ee5 100644 --- a/src/main/javassist/bytecode/CodeIterator.java +++ b/src/main/javassist/bytecode/CodeIterator.java @@ -671,6 +671,10 @@ public class CodeIterator implements Opcode { if (vta != null) vta.shiftPc(where, gapLength, exclusive); + StackMapTable smt = (StackMapTable)ca.getAttribute(StackMapTable.tag); + if (smt != null) + smt.shiftPc(where, gapLength, exclusive); + return newcode; } diff --git a/src/main/javassist/bytecode/StackMapTable.java b/src/main/javassist/bytecode/StackMapTable.java index a2fecaef..cb59efa2 100644 --- a/src/main/javassist/bytecode/StackMapTable.java +++ b/src/main/javassist/bytecode/StackMapTable.java @@ -21,6 +21,7 @@ import java.io.ByteArrayOutputStream; import java.io.PrintWriter; import java.io.IOException; import java.util.Map; +import javassist.CannotCompileException; /** * stack_map attribute. @@ -54,8 +55,8 @@ public class StackMapTable extends AttributeInfo { * Makes a copy. * * @exception RuntimeCopyException if a BadBytecode - * exception is thrown, it is - * converted into + * exception is thrown while copying, + * it is converted into * RuntimeCopyException. * */ @@ -136,17 +137,26 @@ public class StackMapTable extends AttributeInfo { /** * A code walker for a StackMapTable attribute. */ - static class Walker { + public static class Walker { byte[] info; int numOfEntries; + /** + * Constructs a walker. + */ + public Walker(StackMapTable smt) { + this(smt.get()); + } + /** * Constructs a walker. * * @param data the info field of the * attribute_info structure. + * It can be obtained by get() + * in the AttributeInfo class. */ - public Walker(byte[] data) { + Walker(byte[] data) { info = data; numOfEntries = ByteArray.readU16bit(data, 0); } @@ -159,7 +169,7 @@ public class StackMapTable extends AttributeInfo { /** * Visits each entry of the stack map frames. */ - public final void parse() throws BadBytecode { + public void parse() throws BadBytecode { int n = numOfEntries; int pos = 2; for (int i = 0; i < n; i++) @@ -213,9 +223,9 @@ public class StackMapTable extends AttributeInfo { * field of attribute_info structure. * @param offsetDelta */ - public void sameFrame(int pos, int offsetDelta) {} + public void sameFrame(int pos, int offsetDelta) throws BadBytecode {} - private int sameLocals(int pos, int type) { + private int sameLocals(int pos, int type) throws BadBytecode { int offset; if (type < 128) offset = type - 64; @@ -247,7 +257,8 @@ public class StackMapTable extends AttributeInfo { * or stack[0].offset * if the tag is UNINIT. */ - public void sameLocals(int pos, int offsetDelta, int stackTag, int stackData) {} + public void sameLocals(int pos, int offsetDelta, int stackTag, int stackData) + throws BadBytecode {} /** * Invoked if the visited frame is a chop_frame. @@ -256,9 +267,9 @@ public class StackMapTable extends AttributeInfo { * @param offsetDelta * @param k the k last locals are absent. */ - public void chopFrame(int pos, int offsetDelta, int k) {} + public void chopFrame(int pos, int offsetDelta, int k) throws BadBytecode {} - private int appendFrame(int pos, int type) { + private int appendFrame(int pos, int type) throws BadBytecode { int k = type - 251; int offset = ByteArray.readU16bit(info, pos + 1); int[] tags = new int[k]; @@ -290,9 +301,10 @@ public class StackMapTable extends AttributeInfo { * @param data locals[i].cpool_index * or locals[i].offset. */ - public void appendFrame(int pos, int offsetDelta, int[] tags, int[] data) {} + public void appendFrame(int pos, int offsetDelta, int[] tags, int[] data) + throws BadBytecode {} - private int fullFrame(int pos) { + private int fullFrame(int pos) throws BadBytecode { int offset = ByteArray.readU16bit(info, pos + 1); int numOfLocals = ByteArray.readU16bit(info, pos + 3); int[] localsTags = new int[numOfLocals]; @@ -319,7 +331,8 @@ public class StackMapTable extends AttributeInfo { * or stack[i].offset */ public void fullFrame(int pos, int offsetDelta, int[] localTags, int[] localData, - int[] stackTags, int[] stackData) {} + int[] stackTags, int[] stackData) + throws BadBytecode {} private int verifyTypeInfo(int pos, int n, int[] tags, int[] data) { for (int i = 0; i < n; i++) { @@ -335,15 +348,12 @@ public class StackMapTable extends AttributeInfo { } } - static class Copier extends Walker { + static class SimpleCopy extends Walker { private Writer writer; - private ConstPool srcPool, destPool; - public Copier(ConstPool src, byte[] data, ConstPool dest) { + public SimpleCopy(byte[] data) { super(data); writer = new Writer(data.length); - srcPool = src; - destPool = dest; } public byte[] doit() throws BadBytecode { @@ -356,10 +366,7 @@ public class StackMapTable extends AttributeInfo { } public void sameLocals(int pos, int offsetDelta, int stackTag, int stackData) { - if (stackTag == OBJECT) - stackData = srcPool.copy(stackData, destPool, null); - - writer.sameLocals(offsetDelta, stackTag, stackData); + writer.sameLocals(offsetDelta, stackTag, copyData(stackTag, stackData)); } public void chopFrame(int pos, int offsetDelta, int k) { @@ -376,7 +383,32 @@ public class StackMapTable extends AttributeInfo { stackTags, copyData(stackTags, stackData)); } - private int[] copyData(int[] tags, int[] data) { + protected int copyData(int tag, int data) { + return data; + } + + protected int[] copyData(int[] tags, int[] data) { + return data; + } + } + + static class Copier extends SimpleCopy { + private ConstPool srcPool, destPool; + + public Copier(ConstPool src, byte[] data, ConstPool dest) { + super(data); + srcPool = src; + destPool = dest; + } + + protected int copyData(int tag, int data) { + if (tag == OBJECT) + return srcPool.copy(data, destPool, null); + else + return data; + } + + protected int[] copyData(int[] tags, int[] data) { int[] newData = new int[data.length]; for (int i = 0; i < data.length; i++) if (tags[i] == OBJECT) @@ -391,22 +423,10 @@ public class StackMapTable extends AttributeInfo { /** * A writer of stack map tables. */ - static class Writer { + public static class Writer { ByteArrayOutputStream output; int numOfEntries; - /** - * Prints the stack table map. - */ - public static void print(StackMapTable smt, PrintWriter writer) { - try { - new Printer(smt.get(), writer).parse(); - } - catch (BadBytecode e) { - writer.println(e.getMessage()); - } - } - /** * Constructs a writer. * @param size the initial buffer size. @@ -522,6 +542,7 @@ public class StackMapTable extends AttributeInfo { writeTypeInfo(localTags[i], localData[i]); n = stackTags.length; + write16(n); for (int i = 0; i < n; i++) writeTypeInfo(stackTags[i], stackData[i]); } @@ -541,6 +562,18 @@ public class StackMapTable extends AttributeInfo { static class Printer extends Walker { private PrintWriter writer; + /** + * Prints the stack table map. + */ + public static void print(StackMapTable smt, PrintWriter writer) { + try { + new Printer(smt.get(), writer).parse(); + } + catch (BadBytecode e) { + writer.println(e.getMessage()); + } + } + Printer(byte[] data, PrintWriter pw) { super(data); writer = pw; @@ -612,4 +645,166 @@ public class StackMapTable extends AttributeInfo { writer.println(msg); } } + + void shiftPc(int where, int gapSize, boolean exclusive) + throws BadBytecode + { + new Shifter(this, where, gapSize, exclusive).doit(); + } + + static class Shifter extends Walker { + private StackMapTable stackMap; + private int where, gap; + private int position; + private byte[] updatedInfo; + private boolean exclusive; + + public Shifter(StackMapTable smt, int where, int gap, boolean exclusive) { + super(smt); + stackMap = smt; + this.where = where; + this.gap = gap; + this.position = 0; + this.updatedInfo = null; + this.exclusive = exclusive; + } + + public void doit() throws BadBytecode { + parse(); + if (updatedInfo != null) + stackMap.set(updatedInfo); + } + + public void sameFrame(int pos, int offsetDelta) { + update(pos, offsetDelta, 0, 251); + } + + public void sameLocals(int pos, int offsetDelta, int stackTag, int stackData) { + update(pos, offsetDelta, 64, 247); + } + + private void update(int pos, int offsetDelta, int base, int entry) { + int oldPos = position; + position = oldPos + offsetDelta + (oldPos == 0 ? 0 : 1); + boolean match; + if (exclusive) + match = oldPos < where && where <= position; + else + match = oldPos <= where && where < position; + + if (match) { + int newDelta = offsetDelta + gap; + position += gap; + if (newDelta < 64) + info[pos] = (byte)(newDelta + base); + else if (offsetDelta < 64) { + byte[] newinfo = insertGap(info, pos, 2); + newinfo[pos] = (byte)entry; + ByteArray.write16bit(newDelta, info, pos + 1); + updatedInfo = newinfo; + } + else + ByteArray.write16bit(newDelta, info, pos + 1); + } + } + + private static byte[] insertGap(byte[] info, int where, int gap) { + int len = info.length; + byte[] newinfo = new byte[len + gap]; + for (int i = 0; i < len; i++) + newinfo[i + (i < where ? 0 : gap)] = info[i]; + + return newinfo; + } + + public void chopFrame(int pos, int offsetDelta, int k) { + update(pos, offsetDelta); + } + + public void appendFrame(int pos, int offsetDelta, int[] tags, int[] data) { + update(pos, offsetDelta); + } + + public void fullFrame(int pos, int offsetDelta, int[] localTags, int[] localData, + int[] stackTags, int[] stackData) { + update(pos, offsetDelta); + } + + private void update(int pos, int offsetDelta) { + int oldPos = position; + position = oldPos + offsetDelta + (oldPos == 0 ? 0 : 1); + boolean match; + if (exclusive) + match = oldPos < where && where <= position; + else + match = oldPos <= where && where < position; + + if (match) { + int newDelta = offsetDelta + gap; + ByteArray.write16bit(newDelta, info, pos + 1); + position += gap; + } + } + } + + /** + * Undocumented method. Do not use; internal-use only. + * + *

This method is for javassist.convert.TransformNew. + * It is called to update the stack map table when + * the NEW opcode (and the following DUP) is removed. + * + * @param where the position of the removed NEW opcode. + */ + public void removeNew(int where) throws CannotCompileException { + try { + byte[] data = new NewRemover(this.get(), where).doit(); + this.set(data); + } + catch (BadBytecode e) { + throw new CannotCompileException("bad stack map table", e); + } + } + + static class NewRemover extends SimpleCopy { + int posOfNew; + + public NewRemover(byte[] data, int pos) { + super(data); + posOfNew = pos; + } + + public void sameLocals(int pos, int offsetDelta, int stackTag, int stackData) { + if (stackTag == UNINIT && stackData == posOfNew) + super.sameFrame(pos, offsetDelta); + else + super.sameLocals(pos, offsetDelta, stackTag, stackData); + } + + public void fullFrame(int pos, int offsetDelta, int[] localTags, int[] localData, + int[] stackTags, int[] stackData) { + int n = stackTags.length - 1; + for (int i = 0; i < n; i++) + if (stackTags[i] == UNINIT && stackData[i] == posOfNew + && stackTags[i + 1] == UNINIT && stackData[i + 1] == posOfNew) { + n++; + int[] stackTags2 = new int[n - 2]; + int[] stackData2 = new int[n - 2]; + int k = 0; + for (int j = 0; j < n; j++) + if (j == i) + j++; + else { + stackTags2[k] = stackTags[j]; + stackData2[k++] = stackData[j]; + } + + stackTags = stackTags2; + stackData = stackData2; + break; + } + + super.fullFrame(pos, offsetDelta, localTags, localData, stackTags, stackData); + } + } } diff --git a/src/main/javassist/bytecode/stackmap/BasicBlock.java b/src/main/javassist/bytecode/stackmap/BasicBlock.java index d7076152..4f0111bf 100644 --- a/src/main/javassist/bytecode/stackmap/BasicBlock.java +++ b/src/main/javassist/bytecode/stackmap/BasicBlock.java @@ -29,10 +29,10 @@ public class BasicBlock { int n = ltypes.length; for (int i = 0; i < n; i++) if (ltypes[i] != localsTypes[i]) { - localsTypes[i] = StackAnalyzer.EMPTY; + localsTypes[i] = StackAnalyzerCore.EMPTY; localsData[i] = null; } - else if (ltypes[i] == StackAnalyzer.OBJECT + else if (ltypes[i] == StackAnalyzerCore.OBJECT && !ldata[i].equals(localsData[i])) ; // localsData[i] = ??; } diff --git a/src/main/javassist/bytecode/stackmap/StackAnalyzer.java b/src/main/javassist/bytecode/stackmap/StackAnalyzer.java index 12b4892d..264ffe86 100644 --- a/src/main/javassist/bytecode/stackmap/StackAnalyzer.java +++ b/src/main/javassist/bytecode/stackmap/StackAnalyzer.java @@ -1,846 +1,10 @@ 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 class StackAnalyzer extends StackAnalyzerCore { 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++; + super(cp, maxStack, maxLocals); } } diff --git a/src/main/javassist/bytecode/stackmap/StackAnalyzerCore.java b/src/main/javassist/bytecode/stackmap/StackAnalyzerCore.java new file mode 100644 index 00000000..c3c79c86 --- /dev/null +++ b/src/main/javassist/bytecode/stackmap/StackAnalyzerCore.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 StackAnalyzerCore { + 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 StackAnalyzerCore(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++; + } +} diff --git a/src/main/javassist/convert/TransformAccessArrayField.java b/src/main/javassist/convert/TransformAccessArrayField.java index 9b08a128..afa944b6 100644 --- a/src/main/javassist/convert/TransformAccessArrayField.java +++ b/src/main/javassist/convert/TransformAccessArrayField.java @@ -14,7 +14,6 @@ */ package javassist.convert; - import javassist.CtClass; import javassist.NotFoundException; import javassist.CodeConverter.ArrayAccessReplacementMethodNames; @@ -25,43 +24,44 @@ import javassist.bytecode.ConstPool; /** * * @author Kabir Khan - * @version $Revision: 1.2 $ + * @version $Revision: 1.3 $ */ -public class TransformAccessArrayField extends Transformer -{ -// CtClass componentType; +public class TransformAccessArrayField extends Transformer { +// CtClass componentType; String methodClassname; ArrayAccessReplacementMethodNames names; - public TransformAccessArrayField(Transformer next, String methodClassname, ArrayAccessReplacementMethodNames names) throws NotFoundException + public TransformAccessArrayField(Transformer next, String methodClassname, + ArrayAccessReplacementMethodNames names) + throws NotFoundException { super(next); this.methodClassname = methodClassname; this.names = names; } - public int transform(CtClass tclazz, int pos, CodeIterator iterator, ConstPool cp) throws BadBytecode + public int transform(CtClass tclazz, int pos, CodeIterator iterator, + ConstPool cp) throws BadBytecode { int c = iterator.byteAt(pos); - if (c == AALOAD || c == BALOAD || c == CALOAD || c == DALOAD || c == FALOAD || c == IALOAD || c == LALOAD || c == SALOAD) - { + if (c == AALOAD || c == BALOAD || c == CALOAD || c == DALOAD + || c == FALOAD || c == IALOAD || c == LALOAD || c == SALOAD) replace(cp, iterator, pos, c, getLoadReplacementSignature(c)); - } - else if (c == AASTORE || c == BASTORE || c == CASTORE || c == DASTORE || c == FASTORE || c == IASTORE || c == LASTORE || c == SASTORE) - { + else if (c == AASTORE || c == BASTORE || c == CASTORE || c == DASTORE + || c == FASTORE || c == IASTORE || c == LASTORE || c == SASTORE) replace(cp, iterator, pos, c, getStoreReplacementSignature(c)); - } return pos; } - private void replace(ConstPool cp, CodeIterator iterator, int pos, int opcode, String signature) throws BadBytecode + private void replace(ConstPool cp, CodeIterator iterator, + int pos, int opcode, String signature) + throws BadBytecode { String methodName = getMethodName(opcode); - if (methodName != null) - { + if (methodName != null) { iterator.insertGap(2); int mi = cp.addClassInfo(methodClassname); int methodref = cp.addMethodrefInfo(mi, methodName, signature); @@ -70,114 +70,112 @@ public class TransformAccessArrayField extends Transformer } } - private String getMethodName(int opcode) - { - String methodName = null; - switch(opcode) - { - case AALOAD: + private String getMethodName(int opcode) { + String methodName = null; + switch (opcode) { + case AALOAD: methodName = names.objectRead(); break; - case BALOAD: + case BALOAD: methodName = names.byteOrBooleanRead(); break; - case CALOAD: + case CALOAD: methodName = names.charRead(); break; - case DALOAD: + case DALOAD: methodName = names.doubleRead(); break; - case FALOAD: + case FALOAD: methodName = names.floatRead(); break; - case IALOAD: + case IALOAD: methodName = names.intRead(); break; - case SALOAD: + case SALOAD: methodName = names.shortRead(); break; - case LALOAD: + case LALOAD: methodName = names.longRead(); break; - case AASTORE: + case AASTORE: methodName = names.objectWrite(); break; - case BASTORE: + case BASTORE: methodName = names.byteOrBooleanWrite(); break; - case CASTORE: + case CASTORE: methodName = names.charWrite(); break; - case DASTORE: + case DASTORE: methodName = names.doubleWrite(); break; - case FASTORE: + case FASTORE: methodName = names.floatWrite(); break; - case IASTORE: + case IASTORE: methodName = names.intWrite(); break; - case SASTORE: + case SASTORE: methodName = names.shortWrite(); break; - case LASTORE: + case LASTORE: methodName = names.longWrite(); break; - } - - if (methodName.equals("")) - { - methodName = null; - } - return methodName; - } + } - private String getLoadReplacementSignature(int opcode) throws BadBytecode + if (methodName.equals("")) + methodName = null; + + return methodName; + } + + private String getLoadReplacementSignature(int opcode) + throws BadBytecode { - switch(opcode) - { - case AALOAD: + switch (opcode) { + case AALOAD: return "(Ljava/lang/Object;I)Ljava/lang/Object;"; - case BALOAD: + case BALOAD: return "(Ljava/lang/Object;I)B"; - case CALOAD: + case CALOAD: return "(Ljava/lang/Object;I)C"; - case DALOAD: + case DALOAD: return "(Ljava/lang/Object;I)D"; - case FALOAD: + case FALOAD: return "(Ljava/lang/Object;I)F"; - case IALOAD: + case IALOAD: return "(Ljava/lang/Object;I)I"; - case SALOAD: + case SALOAD: return "(Ljava/lang/Object;I)S"; - case LALOAD: + case LALOAD: return "(Ljava/lang/Object;I)J"; - } - - throw new BadBytecode(opcode); - } + } + + throw new BadBytecode(opcode); + } - private String getStoreReplacementSignature(int opcode) throws BadBytecode + private String getStoreReplacementSignature(int opcode) + throws BadBytecode { - switch(opcode) - { - case AASTORE: + switch (opcode) { + case AASTORE: return "(Ljava/lang/Object;ILjava/lang/Object;)V"; - case BASTORE: + case BASTORE: return "(Ljava/lang/Object;IB)V"; - case CASTORE: + case CASTORE: return "(Ljava/lang/Object;IC)V"; - case DASTORE: + case DASTORE: return "(Ljava/lang/Object;ID)V"; - case FASTORE: + case FASTORE: return "(Ljava/lang/Object;IF)V"; - case IASTORE: + case IASTORE: return "(Ljava/lang/Object;II)V"; - case SASTORE: + case SASTORE: return "(Ljava/lang/Object;IS)V"; - case LASTORE: + case LASTORE: return "(Ljava/lang/Object;IJ)V"; - } - throw new BadBytecode(opcode); - } + } + + throw new BadBytecode(opcode); + } } diff --git a/src/main/javassist/convert/TransformNew.java b/src/main/javassist/convert/TransformNew.java index c4347b7c..a853d2d5 100644 --- a/src/main/javassist/convert/TransformNew.java +++ b/src/main/javassist/convert/TransformNew.java @@ -64,6 +64,11 @@ final public class TransformNew extends Transformer { iterator.writeByte(NOP, pos + 2); iterator.writeByte(NOP, pos + 3); ++nested; + + StackMapTable smt + = (StackMapTable)iterator.get().getAttribute(StackMapTable.tag); + if (smt != null) + smt.removeNew(pos); } } else if (c == INVOKESPECIAL) { diff --git a/src/test/test/Test.java b/src/test/test/Test.java new file mode 100644 index 00000000..29a08a69 --- /dev/null +++ b/src/test/test/Test.java @@ -0,0 +1,14 @@ +package test; + +import javassist.bytecode.*; + +public class Test { + public static void main(String[] args) { + String[] names = Mnemonic.OPCODE; + for (int i = 0; i < names.length; i++) + if (names[i] == null) + System.out.println(" case " + i + " :"); + else + System.out.println(" case Opcode." + names[i].toUpperCase() + " :"); + } +}