/*
* 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 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.
*/
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);
}
}