/*
 * Javassist, a Java-bytecode translator toolkit.
 * Copyright (C) 1999- 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,
 * or the Apache License Version 2.0.
 *
 * 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.ClassPool;
import javassist.bytecode.BadBytecode;
import javassist.bytecode.ByteArray;
import javassist.bytecode.ConstPool;
import javassist.bytecode.Descriptor;
import javassist.bytecode.Opcode;

/*
 * 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;    // used as the type of ARETURN

    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 = TypeData.make(maxStack);
        localsTypes = TypeData.make(maxLocals);
    }

    public Tracer(Tracer t) {
        classPool = t.classPool;
        cpool = t.cpool;
        returnType = t.returnType;
        stackTop = t.stackTop;
        stackTypes = TypeData.make(t.stackTypes.length);
        localsTypes = TypeData.make(t.localsTypes.length);
    }

    
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.
Params:
  • pos – the position of the instruction.
Returns: the size of the instruction at POS.
/** * 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 < 54) return doOpcode0_53(pos, code, op); if (op < 96) return doOpcode54_95(pos, code, op); if (op < 148) return doOpcode96_147(pos, code, op); return doOpcode148_201(pos, code, op); } catch (ArrayIndexOutOfBoundsException e) { throw new BadBytecode("inconsistent stack height " + e.getMessage(), e); } } 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 {}
Params:
  • pos – the position of TABLESWITCH
  • code – bytecode
  • n – the number of case labels
  • offsetPos – the position of the branch-target table.
  • defaultOffset – the offset to the default branch target.
/** * @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 {}
Params:
  • pos – the position of LOOKUPSWITCH
  • code – bytecode
  • n – the number of case labels
  • pairsPos – the position of the table of pairs of a value and a branch target.
  • defaultOffset – the offset to the default branch target.
/** * @param pos the position of LOOKUPSWITCH * @param code bytecode * @param n the number of case labels * @param pairsPos 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.
/** * 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.
/** * 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]; stackTypes[s] = TypeData.ArrayElement.make(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) { stackTypes[stackTop++] = localsTypes[localVar]; return 2; } private int doOpcode54_95(int pos, byte[] code, int op) throws BadBytecode { 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.ArrayElement.aastore(stackTypes[stackTop - 3], stackTypes[stackTop - 1], 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]; 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 - 1] = LONG; stackTypes[stackTop] = TOP; stackTop++; break; case Opcode.I2F : stackTypes[stackTop - 1] = FLOAT; break; case Opcode.I2D : stackTypes[stackTop - 1] = DOUBLE; stackTypes[stackTop] = TOP; stackTop++; break; case Opcode.L2I : stackTypes[--stackTop - 1] = INTEGER; break; case Opcode.L2F : stackTypes[--stackTop - 1] = FLOAT; break; case Opcode.L2D : stackTypes[stackTop - 2] = DOUBLE; break; case Opcode.F2I : stackTypes[stackTop - 1] = INTEGER; break; case Opcode.F2L : stackTypes[stackTop - 1] = LONG; stackTypes[stackTop] = TOP; stackTop++; break; case Opcode.F2D : stackTypes[stackTop - 1] = DOUBLE; stackTypes[stackTop] = TOP; stackTop++; break; case Opcode.D2I : stackTypes[--stackTop - 1] = INTEGER; break; case Opcode.D2L : stackTypes[stackTop - 2] = 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 : stackTypes[--stackTop].setType(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 Opcode.INVOKEDYNAMIC : return doInvokeDynamic(pos, code); 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 : stackTypes[stackTop - 1].setType("[Ljava.lang.Object;", classPool); stackTypes[stackTop - 1] = INTEGER; break; case Opcode.ATHROW : stackTypes[--stackTop].setType("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); String type = cpool.getClassInfo(i); if (type.charAt(0) == '[') type = type.replace('.', '/'); // getClassInfo() may return "[java.lang.Object;". stackTypes[stackTop - 1] = new TypeData.ClassName(type); 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') stackTypes[stackTop].setType(getFieldClassName(desc, 0), classPool); else if (c == '[') stackTypes[stackTop].setType(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); stackTypes[--stackTop].setType(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 target = stackTypes[--stackTop]; if (target instanceof TypeData.UninitTypeVar && target.isUninit()) constructorCalled(target, ((TypeData.UninitTypeVar)target).offset()); else if (target instanceof TypeData.UninitData) constructorCalled(target, ((TypeData.UninitData)target).offset()); target.setType(className, classPool); } pushMemberType(desc); return 3; } /* This is a constructor call on an uninitialized object. * Sets flags of other references to that object. * * @param offset the offset where the object has been created. */ private void constructorCalled(TypeData target, int offset) { target.constructorCalled(offset); for (int i = 0; i < stackTop; i++) stackTypes[i].constructorCalled(offset); for (int i = 0; i < localsTypes.length; i++) localsTypes[i].constructorCalled(offset); } 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); stackTypes[--stackTop].setType(className, classPool); pushMemberType(desc); return 5; } private int doInvokeDynamic(int pos, byte[] code) throws BadBytecode { int i = ByteArray.readU16bit(code, pos + 1); String desc = cpool.getInvokeDynamicType(i); checkParamTypes(desc, 1); // assume CosntPool#REF_invokeStatic /* TypeData target = stackTypes[--stackTop]; if (target instanceof TypeData.UninitTypeVar && target.isUninit()) constructorCalled((TypeData.UninitTypeVar)target); */ 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) stackTypes[stackTop].setType(desc.substring(i, k), classPool); else if (c == 'L') stackTypes[stackTop].setType(desc.substring(i + 1, k - 1).replace('/', '.'), classPool); } }