javassist.bytecode.stackmap.Tracer Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ehcache Show documentation
Show all versions of ehcache Show documentation
Ehcache is an open source, standards-based cache used to boost performance,
offload the database and simplify scalability. Ehcache is robust, proven and full-featured and
this has made it the most widely-used Java-based cache.
/*
* 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.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; // 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.
*
* @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(), 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 {}
/**
* @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];
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);
}
}