aboutsummaryrefslogtreecommitdiff
path: root/src/main/javassist/bytecode/stackmap/Tracer.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/javassist/bytecode/stackmap/Tracer.java')
-rw-r--r--src/main/javassist/bytecode/stackmap/Tracer.java920
1 files changed, 920 insertions, 0 deletions
diff --git a/src/main/javassist/bytecode/stackmap/Tracer.java b/src/main/javassist/bytecode/stackmap/Tracer.java
new file mode 100644
index 0000000..89e788d
--- /dev/null
+++ b/src/main/javassist/bytecode/stackmap/Tracer.java
@@ -0,0 +1,920 @@
+/*
+ * Javassist, a Java-bytecode translator toolkit.
+ * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. Alternatively, the contents of this file may be used under
+ * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ */
+
+package javassist.bytecode.stackmap;
+
+import javassist.bytecode.ByteArray;
+import javassist.bytecode.Opcode;
+import javassist.bytecode.ConstPool;
+import javassist.bytecode.Descriptor;
+import javassist.bytecode.BadBytecode;
+import javassist.ClassPool;
+
+/*
+ * A class for performing abstract interpretation.
+ * See also MapMaker class.
+ */
+
+public abstract class Tracer implements TypeTag {
+ protected ClassPool classPool;
+ protected ConstPool cpool;
+ protected String returnType;
+
+ protected int stackTop;
+ protected TypeData[] stackTypes;
+ protected TypeData[] localsTypes;
+
+ public Tracer(ClassPool classes, ConstPool cp, int maxStack, int maxLocals,
+ String retType) {
+ classPool = classes;
+ cpool = cp;
+ returnType = retType;
+ stackTop = 0;
+ stackTypes = new TypeData[maxStack];
+ localsTypes = new TypeData[maxLocals];
+ }
+
+ public Tracer(Tracer t, boolean copyStack) {
+ classPool = t.classPool;
+ cpool = t.cpool;
+ returnType = t.returnType;
+
+ stackTop = t.stackTop;
+ int size = t.stackTypes.length;
+ stackTypes = new TypeData[size];
+ if (copyStack)
+ copyFrom(t.stackTop, t.stackTypes, stackTypes);
+
+ int size2 = t.localsTypes.length;
+ localsTypes = new TypeData[size2];
+ copyFrom(size2, t.localsTypes, localsTypes);
+ }
+
+ protected static int copyFrom(int n, TypeData[] srcTypes, TypeData[] destTypes) {
+ int k = -1;
+ for (int i = 0; i < n; i++) {
+ TypeData t = srcTypes[i];
+ destTypes[i] = t == TOP ? TOP : t.getSelf();
+ if (t != TOP)
+ if (t.is2WordType())
+ k = i + 1;
+ else
+ k = i;
+ }
+
+ return k + 1;
+ }
+
+ /**
+ * Does abstract interpretation on the given bytecode instruction.
+ * It records whether or not a local variable (i.e. register) is accessed.
+ * If the instruction requires that a local variable or
+ * a stack element has a more specific type, this method updates the
+ * type of it.
+ *
+ * @param pos the position of the instruction.
+ * @return the size of the instruction at POS.
+ */
+ protected int doOpcode(int pos, byte[] code) throws BadBytecode {
+ try {
+ 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);
+ }
+ catch (ArrayIndexOutOfBoundsException e) {
+ throw new BadBytecode("inconsistent stack height " + e.getMessage());
+ }
+ }
+
+ protected void visitBranch(int pos, byte[] code, int offset) throws BadBytecode {}
+ protected void visitGoto(int pos, byte[] code, int offset) throws BadBytecode {}
+ protected void visitReturn(int pos, byte[] code) throws BadBytecode {}
+ protected void visitThrow(int pos, byte[] code) throws BadBytecode {}
+
+ /**
+ * @param pos the position of TABLESWITCH
+ * @param code bytecode
+ * @param n the number of case labels
+ * @param offsetPos the position of the branch-target table.
+ * @param defaultOffset the offset to the default branch target.
+ */
+ protected void visitTableSwitch(int pos, byte[] code, int n,
+ int offsetPos, int defaultOffset) throws BadBytecode {}
+
+ /**
+ * @param pos the position of LOOKUPSWITCH
+ * @param code bytecode
+ * @param n the number of case labels
+ * @param offsetPos the position of the table of pairs of a value and a branch target.
+ * @param defaultOffset the offset to the default branch target.
+ */
+ protected void visitLookupSwitch(int pos, byte[] code, int n,
+ int pairsPos, int defaultOffset) throws BadBytecode {}
+
+ /**
+ * Invoked when the visited instruction is jsr.
+ * Java6 or later does not allow using RET.
+ */
+ protected void visitJSR(int pos, byte[] code) throws BadBytecode {
+ /* Since JSR pushes a return address onto the operand stack,
+ * the stack map at the entry point of a subroutine is
+ * stackTypes resulting after executing the following code:
+ *
+ * stackTypes[stackTop++] = TOP;
+ */
+ }
+
+ /**
+ * Invoked when the visited instruction is ret or wide ret.
+ * Java6 or later does not allow using RET.
+ */
+ protected void visitRET(int pos, byte[] code) throws BadBytecode {}
+
+ private int doOpcode0_53(int pos, byte[] code, int op) throws BadBytecode {
+ int reg;
+ TypeData[] stackTypes = this.stackTypes;
+ switch (op) {
+ case Opcode.NOP :
+ break;
+ case Opcode.ACONST_NULL :
+ stackTypes[stackTop++] = new TypeData.NullType();
+ 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, code, pos);
+ case Opcode.LLOAD :
+ return doXLOAD(LONG, code, pos);
+ case Opcode.FLOAD :
+ return doXLOAD(FLOAT, code, pos);
+ case Opcode.DLOAD :
+ return doXLOAD(DOUBLE, code, pos);
+ case Opcode.ALOAD :
+ return doALOAD(code[pos + 1] & 0xff);
+ 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 :
+ reg = op - Opcode.ALOAD_0;
+ stackTypes[stackTop++] = localsTypes[reg];
+ 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;
+ TypeData data = stackTypes[s];
+ if (data == null || !data.isObjectType())
+ throw new BadBytecode("bad AALOAD");
+ else
+ stackTypes[s] = new TypeData.ArrayElement(data);
+
+ 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) {
+ TypeData[] stackTypes = this.stackTypes;
+ int tag = cpool.getTag(index);
+ if (tag == ConstPool.CONST_String)
+ stackTypes[stackTop++] = new TypeData.ClassName("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++] = new TypeData.ClassName("java.lang.Class");
+ else
+ throw new RuntimeException("bad LDC: " + tag);
+ }
+
+ private int doXLOAD(TypeData type, byte[] code, int pos) {
+ int localVar = code[pos + 1] & 0xff;
+ return doXLOAD(localVar, type);
+ }
+
+ private int doXLOAD(int localVar, TypeData type) {
+ stackTypes[stackTop++] = type;
+ if (type.is2WordType())
+ stackTypes[stackTop++] = TOP;
+
+ return 2;
+ }
+
+ private int doALOAD(int localVar) { // int localVar, TypeData type) {
+ stackTypes[stackTop++] = localsTypes[localVar];
+ return 2;
+ }
+
+ private int doOpcode54_95(int pos, byte[] code, int op) throws BadBytecode {
+ TypeData[] localsTypes = this.localsTypes;
+ TypeData[] 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 doASTORE(code[pos + 1] & 0xff);
+ 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;
+ 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;
+ 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;
+ 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;
+ 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;
+ doASTORE(var);
+ break; }
+ case Opcode.IASTORE :
+ case Opcode.LASTORE :
+ case Opcode.FASTORE :
+ case Opcode.DASTORE :
+ stackTop -= (op == Opcode.LASTORE || op == Opcode.DASTORE) ? 4 : 3;
+ break;
+ case Opcode.AASTORE :
+ TypeData.setType(stackTypes[stackTop - 1],
+ TypeData.ArrayElement.getElementType(stackTypes[stackTop - 3].getName()),
+ classPool);
+ stackTop -= 3;
+ break;
+ case Opcode.BASTORE :
+ case Opcode.CASTORE :
+ case Opcode.SASTORE :
+ stackTop -= 3;
+ break;
+ case Opcode.POP :
+ stackTop--;
+ break;
+ case Opcode.POP2 :
+ stackTop -= 2;
+ break;
+ case Opcode.DUP : {
+ int sp = stackTop;
+ stackTypes[sp] = stackTypes[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];
+ 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);
+ int sp = stackTop;
+ stackTypes[sp - len] = stackTypes[sp];
+ stackTypes[sp - len + 1] = stackTypes[sp + 1];
+ stackTop = sp + 2;
+ break; }
+ case Opcode.SWAP : {
+ int sp = stackTop - 1;
+ TypeData t = stackTypes[sp];
+ stackTypes[sp] = stackTypes[sp - 1];
+ stackTypes[sp - 1] = t;
+ break; }
+ default :
+ throw new RuntimeException("fatal");
+ }
+
+ return 1;
+ }
+
+ private int doXSTORE(int pos, byte[] code, TypeData type) {
+ int index = code[pos + 1] & 0xff;
+ return doXSTORE(index, type);
+ }
+
+ private int doXSTORE(int index, TypeData type) {
+ stackTop--;
+ localsTypes[index] = type;
+ if (type.is2WordType()) {
+ stackTop--;
+ localsTypes[index + 1] = TOP;
+ }
+
+ return 2;
+ }
+
+ private int doASTORE(int index) {
+ stackTop--;
+ // implicit upcast might be done.
+ localsTypes[index] = stackTypes[stackTop].copy();
+ return 2;
+ }
+
+ private void doDUP_XX(int delta, int len) {
+ TypeData types[] = stackTypes;
+ int sp = stackTop - 1;
+ int end = sp - len;
+ while (sp > end) {
+ types[sp + delta] = types[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 :
+ // this does not call writeLocal().
+ 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 :
+ visitJSR(pos, code);
+ return 3; // branch
+ case Opcode.RET :
+ visitRET(pos, code);
+ return 2;
+ 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 :
+ TypeData.setType(stackTypes[--stackTop], returnType, classPool);
+ visitReturn(pos, code);
+ break;
+ case Opcode.RETURN :
+ visitReturn(pos, code);
+ break;
+ case Opcode.GETSTATIC :
+ return doGetField(pos, code, false);
+ case Opcode.PUTSTATIC :
+ return doPutField(pos, code, false);
+ case Opcode.GETFIELD :
+ return doGetField(pos, code, true);
+ case Opcode.PUTFIELD :
+ return doPutField(pos, code, true);
+ case Opcode.INVOKEVIRTUAL :
+ case Opcode.INVOKESPECIAL :
+ return doInvokeMethod(pos, code, true);
+ case Opcode.INVOKESTATIC :
+ return doInvokeMethod(pos, code, false);
+ case Opcode.INVOKEINTERFACE :
+ return doInvokeIntfMethod(pos, code);
+ case 186 :
+ throw new RuntimeException("bad opcode 186");
+ case Opcode.NEW : {
+ int i = ByteArray.readU16bit(code, pos + 1);
+ stackTypes[stackTop++]
+ = new TypeData.UninitData(pos, cpool.getClassInfo(i));
+ return 3; }
+ case Opcode.NEWARRAY :
+ return doNEWARRAY(pos, code);
+ case Opcode.ANEWARRAY : {
+ int i = ByteArray.readU16bit(code, pos + 1);
+ String type = cpool.getClassInfo(i).replace('.', '/');
+ if (type.charAt(0) == '[')
+ type = "[" + type;
+ else
+ type = "[L" + type + ";";
+
+ stackTypes[stackTop - 1]
+ = new TypeData.ClassName(type);
+ return 3; }
+ case Opcode.ARRAYLENGTH :
+ TypeData.setType(stackTypes[stackTop - 1], "[Ljava.lang.Object;", classPool);
+ stackTypes[stackTop - 1] = INTEGER;
+ break;
+ case Opcode.ATHROW :
+ TypeData.setType(stackTypes[--stackTop], "java.lang.Throwable", classPool);
+ visitThrow(pos, code);
+ break;
+ case Opcode.CHECKCAST : {
+ // TypeData.setType(stackTypes[stackTop - 1], "java.lang.Object", classPool);
+ int i = ByteArray.readU16bit(code, pos + 1);
+ stackTypes[stackTop - 1] = new TypeData.ClassName(cpool.getClassInfo(i));
+ return 3; }
+ case Opcode.INSTANCEOF :
+ // TypeData.setType(stackTypes[stackTop - 1], "java.lang.Object", classPool);
+ stackTypes[stackTop - 1] = INTEGER;
+ return 3;
+ case Opcode.MONITORENTER :
+ case Opcode.MONITOREXIT :
+ stackTop--;
+ // TypeData.setType(stackTypes[stackTop], "java.lang.Object", classPool);
+ 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 :
+ visitJSR(pos, code);
+ return 5;
+ }
+ return 1;
+ }
+
+ private int doWIDE(int pos, byte[] code) throws BadBytecode {
+ int op = code[pos + 1] & 0xff;
+ switch (op) {
+ case Opcode.ILOAD :
+ doWIDE_XLOAD(pos, code, INTEGER);
+ break;
+ case Opcode.LLOAD :
+ doWIDE_XLOAD(pos, code, LONG);
+ break;
+ case Opcode.FLOAD :
+ doWIDE_XLOAD(pos, code, FLOAT);
+ break;
+ case Opcode.DLOAD :
+ doWIDE_XLOAD(pos, code, DOUBLE);
+ break;
+ case Opcode.ALOAD : {
+ int index = ByteArray.readU16bit(code, pos + 2);
+ doALOAD(index);
+ break; }
+ case Opcode.ISTORE :
+ doWIDE_STORE(pos, code, INTEGER);
+ break;
+ case Opcode.LSTORE :
+ doWIDE_STORE(pos, code, LONG);
+ break;
+ case Opcode.FSTORE :
+ doWIDE_STORE(pos, code, FLOAT);
+ break;
+ case Opcode.DSTORE :
+ doWIDE_STORE(pos, code, DOUBLE);
+ break;
+ case Opcode.ASTORE : {
+ int index = ByteArray.readU16bit(code, pos + 2);
+ doASTORE(index);
+ break; }
+ case Opcode.IINC :
+ // this does not call writeLocal().
+ return 6;
+ case Opcode.RET :
+ visitRET(pos, code);
+ break;
+ default :
+ throw new RuntimeException("bad WIDE instruction: " + op);
+ }
+
+ return 4;
+ }
+
+ private void doWIDE_XLOAD(int pos, byte[] code, TypeData type) {
+ int index = ByteArray.readU16bit(code, pos + 2);
+ doXLOAD(index, type);
+ }
+
+ private void doWIDE_STORE(int pos, byte[] code, TypeData type) {
+ int index = ByteArray.readU16bit(code, pos + 2);
+ doXSTORE(index, type);
+ }
+
+ private int doPutField(int pos, byte[] code, boolean notStatic) throws BadBytecode {
+ int index = ByteArray.readU16bit(code, pos + 1);
+ String desc = cpool.getFieldrefType(index);
+ stackTop -= Descriptor.dataSize(desc);
+ char c = desc.charAt(0);
+ if (c == 'L')
+ TypeData.setType(stackTypes[stackTop], getFieldClassName(desc, 0), classPool);
+ else if (c == '[')
+ TypeData.setType(stackTypes[stackTop], desc, classPool);
+
+ setFieldTarget(notStatic, index);
+ return 3;
+ }
+
+ private int doGetField(int pos, byte[] code, boolean notStatic) throws BadBytecode {
+ int index = ByteArray.readU16bit(code, pos + 1);
+ setFieldTarget(notStatic, index);
+ String desc = cpool.getFieldrefType(index);
+ pushMemberType(desc);
+ return 3;
+ }
+
+ private void setFieldTarget(boolean notStatic, int index) throws BadBytecode {
+ if (notStatic) {
+ String className = cpool.getFieldrefClassName(index);
+ TypeData.setType(stackTypes[--stackTop], className, classPool);
+ }
+ }
+
+ private int doNEWARRAY(int pos, byte[] code) {
+ int s = stackTop - 1;
+ 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");
+ }
+
+ stackTypes[s] = new TypeData.ClassName(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).replace('.', '/');
+ stackTypes[stackTop - 1] = new TypeData.ClassName(type);
+ return 4;
+ }
+
+ private int doInvokeMethod(int pos, byte[] code, boolean notStatic) throws BadBytecode {
+ int i = ByteArray.readU16bit(code, pos + 1);
+ String desc = cpool.getMethodrefType(i);
+ checkParamTypes(desc, 1);
+ if (notStatic) {
+ String className = cpool.getMethodrefClassName(i);
+ TypeData.setType(stackTypes[--stackTop], className, classPool);
+ }
+
+ pushMemberType(desc);
+ return 3;
+ }
+
+ private int doInvokeIntfMethod(int pos, byte[] code) throws BadBytecode {
+ int i = ByteArray.readU16bit(code, pos + 1);
+ String desc = cpool.getInterfaceMethodrefType(i);
+ checkParamTypes(desc, 1);
+ String className = cpool.getInterfaceMethodrefClassName(i);
+ TypeData.setType(stackTypes[--stackTop], className, classPool);
+ 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);
+ }
+
+ TypeData[] types = stackTypes;
+ int index = stackTop;
+ switch (descriptor.charAt(top)) {
+ case '[' :
+ types[index] = new TypeData.ClassName(descriptor.substring(top));
+ break;
+ case 'L' :
+ types[index] = new TypeData.ClassName(getFieldClassName(descriptor, 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++;
+ }
+
+ private static String getFieldClassName(String desc, int index) {
+ return desc.substring(index + 1, desc.length() - 1).replace('/', '.');
+ }
+
+ private void checkParamTypes(String desc, int i) throws BadBytecode {
+ char c = desc.charAt(i);
+ if (c == ')')
+ return;
+
+ int k = i;
+ boolean array = false;
+ while (c == '[') {
+ array = true;
+ c = desc.charAt(++k);
+ }
+
+ if (c == 'L') {
+ k = desc.indexOf(';', k) + 1;
+ if (k <= 0)
+ throw new IndexOutOfBoundsException("bad descriptor");
+ }
+ else
+ k++;
+
+ checkParamTypes(desc, k);
+ if (!array && (c == 'J' || c == 'D'))
+ stackTop -= 2;
+ else
+ stackTop--;
+
+ if (array)
+ TypeData.setType(stackTypes[stackTop],
+ desc.substring(i, k), classPool);
+ else if (c == 'L')
+ TypeData.setType(stackTypes[stackTop],
+ desc.substring(i + 1, k - 1).replace('/', '.'), classPool);
+ }
+}