git-svn-id: http://anonsvn.jboss.org/repos/javassist/trunk@360 30ef5769-5b8d-40dd-aea6-55b5d6557bb3tags/rel_3_17_1_ga
@@ -432,7 +432,7 @@ public interface Opcode { | |||
0, // newarray, 188 | |||
0, // anewarray, 189 | |||
0, // arraylength, 190 | |||
0, // athrow, 191 stack is cleared | |||
-1, // athrow, 191 stack is cleared | |||
0, // checkcast, 192 | |||
0, // instanceof, 193 | |||
-1, // monitorenter, 194 |
@@ -509,9 +509,10 @@ public class StackMapTable extends AttributeInfo { | |||
} | |||
/** | |||
* Writes a <code>append_frame</code>. | |||
* Writes a <code>append_frame</code>. The number of the appended | |||
* locals is specified by the length of <code>tags</code>. | |||
* | |||
* @param tag <code>locals[].tag</code>. | |||
* @param tags <code>locals[].tag</code>. | |||
* The length of this array must be | |||
* either 1, 2, or 3. | |||
* @param data <code>locals[].cpool_index</code> | |||
@@ -531,6 +532,9 @@ public class StackMapTable extends AttributeInfo { | |||
/** | |||
* Writes a <code>full_frame</code>. | |||
* <code>number_of_locals</code> and <code>number_of_stack_items</code> | |||
* are specified by the the length of <code>localTags</code> and | |||
* <code>stackTags</code>. | |||
* | |||
* @param localTags <code>locals[].tag</code>. | |||
* @param localData <code>locals[].cpool_index</code> |
@@ -24,6 +24,16 @@ public class BasicBlock implements TypeTag, Comparable { | |||
public int stackTop, numLocals; | |||
public TypeData[] stackTypes, localsTypes; | |||
/* The version number of the values of numLocals and localsTypes. | |||
* These values are repeatedly updated while MapMaker#make() | |||
* is running. This field represents when the values are recorded. | |||
*/ | |||
public int version; | |||
/* a flag used by MapMaker#recordUsage() | |||
*/ | |||
public int[] localsUsage; | |||
/* The number of the basic blocks from which a thread of control | |||
* may reach this basic block. The number excludes the preceding | |||
* block. Thus, if it is zero, a thread of control reaches | |||
@@ -32,6 +42,22 @@ public class BasicBlock implements TypeTag, Comparable { | |||
*/ | |||
public int inbound; | |||
public static class Branch { | |||
public Branch next; | |||
public int target; | |||
public int typeIndex; // exception type | |||
public Branch(Branch next, int target, int type) { | |||
this.next = next; | |||
this.target = target; | |||
this.typeIndex = type; | |||
} | |||
} | |||
/* A list of catch clauses that a thread may jump | |||
* from this block to. | |||
*/ | |||
public Branch catchBlocks; | |||
/* public static void main(String[] args) throws Exception { | |||
BasicBlock b = new BasicBlock(0); | |||
b.initFirstBlock(8, 1, args[0], args[1], args[2].equals("static"), args[2].equals("const")); | |||
@@ -44,9 +70,13 @@ public class BasicBlock implements TypeTag, Comparable { | |||
stackTop = numLocals = 0; | |||
stackTypes = localsTypes = null; | |||
inbound = 1; | |||
localsUsage = null; | |||
catchBlocks = null; | |||
} | |||
public boolean alreadySet() { return stackTypes != null; } | |||
public boolean alreadySet(int ver) { | |||
return stackTypes != null && ver == version; | |||
} | |||
/* | |||
* Computes the correct value of numLocals. | |||
@@ -56,8 +86,15 @@ public class BasicBlock implements TypeTag, Comparable { | |||
public void resetNumLocals() { | |||
if (localsTypes != null) { | |||
int nl = numLocals; | |||
while (nl > 0 && localsTypes[nl - 1] == TypeTag.TOP) | |||
while (nl > 0 && localsTypes[nl - 1] == TypeTag.TOP) { | |||
if (nl > 1) { | |||
TypeData td = localsTypes[nl - 2]; | |||
if (td == TypeTag.LONG || td == TypeTag.DOUBLE) | |||
break; | |||
} | |||
--nl; | |||
} | |||
numLocals = nl; | |||
} | |||
@@ -119,9 +156,14 @@ public class BasicBlock implements TypeTag, Comparable { | |||
/** | |||
* Divides the given code fragment into basic blocks. | |||
* It returns null if the given MethodInfo does not include | |||
* a CodeAttribute. | |||
*/ | |||
public static BasicBlock[] makeBlocks(MethodInfo minfo) throws BadBytecode { | |||
CodeAttribute ca = minfo.getCodeAttribute(); | |||
if (ca == null) | |||
return null; | |||
CodeIterator ci = ca.iterator(); | |||
ConstPool pool = minfo.getConstPool(); | |||
BasicBlock[] blocks = makeBlocks(ci, 0, ci.getCodeLength(), ca.getExceptionTable(), 0, pool); | |||
@@ -148,7 +190,9 @@ public class BasicBlock implements TypeTag, Comparable { | |||
ci.begin(); | |||
ci.move(begin); | |||
ArrayList targets = new ArrayList(); | |||
targets.add(new BasicBlock(begin)); | |||
BasicBlock bb0 = new BasicBlock(begin); | |||
bb0.inbound = 0; // the first block is not a branch target. | |||
targets.add(bb0); | |||
while (ci.hasNext()) { | |||
int index = ci.next(); | |||
if (index >= end) | |||
@@ -203,7 +247,9 @@ public class BasicBlock implements TypeTag, Comparable { | |||
} | |||
} | |||
return trimArray(targets, end); | |||
BasicBlock[] blocks = trimArray(targets, end); | |||
markCatch(et, etOffset, blocks); | |||
return blocks; | |||
} | |||
public int compareTo(Object obj) { | |||
@@ -253,6 +299,28 @@ public class BasicBlock implements TypeTag, Comparable { | |||
return results; | |||
} | |||
private static void markCatch(ExceptionTable et, int etOffset, | |||
BasicBlock[] blocks) | |||
{ | |||
if (et == null) | |||
return; | |||
int nblocks = blocks.length; | |||
int n = et.size(); | |||
for (int i = 0; i < n; i++) { | |||
int start = et.startPc(i) + etOffset; | |||
int end = et.endPc(i) + etOffset; | |||
int handler = et.handlerPc(i) + etOffset; | |||
int type = et.catchType(i); | |||
for (int k = 0; k < nblocks; k++) { | |||
BasicBlock bb = blocks[k]; | |||
int p = bb.position; | |||
if (start <= p && p < end) | |||
bb.catchBlocks = new Branch(bb.catchBlocks, handler, type); | |||
} | |||
} | |||
} | |||
/** | |||
* Initializes the first block by the given method descriptor. | |||
* | |||
@@ -280,20 +348,18 @@ public class BasicBlock implements TypeTag, Comparable { | |||
int n = isStatic ? -1 : 0; | |||
int i = 1; | |||
do { | |||
try { | |||
i = descToTag(methodDesc, i, ++n, locals); | |||
} | |||
catch (StringIndexOutOfBoundsException e) { | |||
throw new BadBytecode("bad method descriptor: " | |||
+ methodDesc); | |||
} | |||
} while (i > 0); | |||
try { | |||
while ((i = descToTag(methodDesc, i, ++n, locals)) > 0) | |||
if (locals[n].is2WordType()) | |||
locals[++n] = TOP; | |||
} | |||
catch (StringIndexOutOfBoundsException e) { | |||
throw new BadBytecode("bad method descriptor: " | |||
+ methodDesc); | |||
} | |||
numLocals = n; | |||
localsTypes = locals; | |||
position = 0; | |||
inbound = 0; | |||
} | |||
private static int descToTag(String desc, int i, |
@@ -22,23 +22,34 @@ import javassist.bytecode.*; | |||
* Stack map maker. | |||
*/ | |||
public class MapMaker extends Tracer { | |||
private boolean moveon; | |||
private boolean moveon; // used for returning another value from doOpcode(). | |||
private boolean loopDetected; | |||
private int iteration; | |||
private BasicBlock[] blocks; | |||
public static void main(String[] args) throws Exception { | |||
if (args.length > 1) { | |||
boolean useMain2 = args[0].equals("0"); | |||
if (useMain2 && args.length > 1) { | |||
main2(args); | |||
return; | |||
} | |||
for (int i = 0; i < args.length; i++) | |||
main1(args[i]); | |||
} | |||
public static void main1(String className) throws Exception { | |||
ClassPool cp = ClassPool.getDefault(); | |||
javassist.CtClass cc = cp.get(args[0]); | |||
//javassist.CtClass cc = cp.get(className); | |||
javassist.CtClass cc = cp.makeClass(new java.io.FileInputStream(className)); | |||
System.out.println(className); | |||
ClassFile cf = cc.getClassFile(); | |||
java.util.List minfos = cf.getMethods(); | |||
for (int i = 0; i < minfos.size(); i++) { | |||
MethodInfo minfo = (MethodInfo)minfos.get(i); | |||
CodeAttribute ca = minfo.getCodeAttribute(); | |||
ca.setAttribute(MapMaker.getMap(cp, minfo)); | |||
if (ca != null) | |||
ca.setAttribute(MapMaker.getMap(cp, minfo)); | |||
} | |||
cc.writeFile("tmp"); | |||
@@ -46,19 +57,25 @@ public class MapMaker extends Tracer { | |||
public static void main2(String[] args) throws Exception { | |||
ClassPool cp = ClassPool.getDefault(); | |||
javassist.CtClass cc = cp.get(args[0]); | |||
//javassist.CtClass cc = cp.get(args[1]); | |||
javassist.CtClass cc = cp.makeClass(new java.io.FileInputStream(args[1])); | |||
MethodInfo minfo; | |||
MapMaker mm; | |||
if (args[1].equals("_init_")) | |||
mm = makeMapMaker(cp, cc.getDeclaredConstructors()[0].getMethodInfo()); | |||
if (args[2].equals("_init_")) | |||
// minfo = cc.getDeclaredConstructors()[0].getMethodInfo(); | |||
minfo = cc.getClassInitializer().getMethodInfo(); | |||
else | |||
mm = makeMapMaker(cp, cc.getDeclaredMethod(args[1]).getMethodInfo()); | |||
minfo = cc.getDeclaredMethod(args[2]).getMethodInfo(); | |||
mm = makeMapMaker(cp, minfo); | |||
if (mm == null) | |||
System.out.println("single basic block"); | |||
else { | |||
BasicBlock[] blocks = mm.getBlocks(); | |||
for (int i = 0; i < blocks.length; i++) | |||
System.out.println(blocks[i]); | |||
StackMapTable smt = mm.toStackMap(); | |||
} | |||
} | |||
@@ -77,21 +94,26 @@ public class MapMaker extends Tracer { | |||
return mm.toStackMap(); | |||
} | |||
/* | |||
/** | |||
* Makes basic blocks with stack maps. If the number of the basic blocks | |||
* is one, this method returns null. | |||
* is one, this method returns null. If the given method info does not | |||
* include a code attribute, this method also returns null. | |||
*/ | |||
public static MapMaker makeMapMaker(ClassPool classes, MethodInfo minfo) | |||
throws BadBytecode | |||
{ | |||
CodeAttribute ca = minfo.getCodeAttribute(); | |||
if (ca == null) | |||
return null; | |||
CodeIterator ci = ca.iterator(); | |||
ConstPool pool = minfo.getConstPool(); | |||
ExceptionTable et = ca.getExceptionTable(); | |||
BasicBlock[] blocks = BasicBlock.makeBlocks(ci, 0, ci.getCodeLength(), | |||
et, 0, pool); | |||
if (blocks.length < 2) | |||
return null; | |||
if (blocks.length == 0 || blocks[0].inbound < 1) | |||
return null; | |||
boolean isStatic = (minfo.getAccessFlags() & AccessFlag.STATIC) != 0; | |||
int maxStack = ca.getMaxStack(); | |||
@@ -102,7 +124,7 @@ public class MapMaker extends Tracer { | |||
isStatic, minfo.isConstructor()); | |||
String retType = BasicBlock.getRetType(desc); | |||
MapMaker mm = new MapMaker(classes, pool, maxStack, maxLocals, | |||
blocks, retType, blocks[0]); | |||
blocks, retType, blocks[0], 0); | |||
mm.make(ca.getCode(), et); | |||
return mm; | |||
} | |||
@@ -111,19 +133,22 @@ public class MapMaker extends Tracer { | |||
* Constructs a tracer. | |||
*/ | |||
MapMaker(ClassPool classes, ConstPool cp, | |||
int maxStack, int maxLocals, BasicBlock[] bb, | |||
String retType, BasicBlock init) { | |||
this(classes, cp, maxStack, maxLocals, bb, retType); | |||
int maxStack, int maxLocals, BasicBlock[] bb, | |||
String retType, BasicBlock init, int iterate) | |||
{ | |||
this(classes, cp, maxStack, maxLocals, bb, retType, iterate); | |||
TypeData[] srcTypes = init.localsTypes; | |||
copyFrom(srcTypes.length, srcTypes, this.localsTypes); | |||
} | |||
private MapMaker(ClassPool classes, ConstPool cp, | |||
int maxStack, int maxLocals, BasicBlock[] bb, | |||
String retType) | |||
String retType, int iterateNo) | |||
{ | |||
super(classes, cp, maxStack, maxLocals, retType); | |||
blocks = bb; | |||
loopDetected = false; | |||
iteration = iterateNo; | |||
} | |||
public BasicBlock[] getBlocks() { return blocks; } | |||
@@ -132,41 +157,18 @@ public class MapMaker extends Tracer { | |||
* Runs an analyzer. | |||
*/ | |||
void make(byte[] code, ExceptionTable et) throws BadBytecode { | |||
blocks[0].version = iteration; | |||
make(code, blocks[0]); | |||
traceExceptions(code, et); | |||
if (loopDetected) { | |||
blocks[0].version = ++iteration; | |||
make(code, blocks[0]); | |||
} | |||
int n = blocks.length; | |||
for (int i = 0; i < n; i++) | |||
evalExpected(blocks[i]); | |||
} | |||
private void traceExceptions(byte[] code, ExceptionTable et) | |||
throws BadBytecode | |||
{ | |||
int n = et.size(); | |||
for (int i = 0; i < n; i++) { | |||
int startPc = et.startPc(i); | |||
int handlerPc = et.handlerPc(i); | |||
BasicBlock handler = BasicBlock.find(blocks, handlerPc); | |||
if (handler.alreadySet()) | |||
continue; | |||
BasicBlock thrower = BasicBlock.find(blocks, startPc); | |||
TypeData[] srcTypes = thrower.localsTypes; | |||
copyFrom(srcTypes.length, srcTypes, this.localsTypes); | |||
int typeIndex = et.catchType(i); | |||
String type; | |||
if (typeIndex == 0) | |||
type = "java.lang.Throwable"; | |||
else | |||
type = cpool.getClassInfo(typeIndex); | |||
stackTop = 1; | |||
stackTypes[0] = new TypeData.ClassName(type); | |||
recordStackMap(handler); | |||
make(code, handler); | |||
} | |||
} | |||
// Phase 1: Code Tracing | |||
private void make(byte[] code, BasicBlock bb) | |||
@@ -174,6 +176,7 @@ public class MapMaker extends Tracer { | |||
{ | |||
int pos = bb.position; | |||
int end = pos + bb.length; | |||
traceExceptions(code, bb.catchBlocks); | |||
moveon = true; | |||
while (moveon && pos < end) | |||
pos += doOpcode(pos, code); | |||
@@ -186,16 +189,55 @@ public class MapMaker extends Tracer { | |||
private void nextBlock(int pos, byte[] code, int offset) throws BadBytecode { | |||
BasicBlock bb = BasicBlock.find(blocks, pos + offset); | |||
if (bb.alreadySet()) { | |||
if (bb.alreadySet(iteration)) { | |||
mergeMap(stackTypes, bb.stackTypes); | |||
mergeMap(localsTypes, bb.localsTypes); | |||
mergeUsage(bb); | |||
} | |||
else { | |||
recordStackMap(bb); | |||
bb.version = iteration; | |||
MapMaker maker = new MapMaker(classPool, cpool, stackTypes.length, | |||
localsTypes.length, blocks, returnType); | |||
localsTypes.length, blocks, returnType, iteration); | |||
maker.copyFrom(this); | |||
maker.make(code, bb); | |||
recordUsage(bb, maker); | |||
if (maker.loopDetected) | |||
this.loopDetected = true; | |||
} | |||
} | |||
private void traceExceptions(byte[] code, BasicBlock.Branch branches) | |||
throws BadBytecode | |||
{ | |||
while (branches != null) { | |||
int pos = branches.target; | |||
BasicBlock bb = BasicBlock.find(blocks, pos); | |||
if (bb.alreadySet(iteration)) { | |||
mergeMap(localsTypes, bb.localsTypes); | |||
mergeUsage(bb); | |||
} | |||
else { | |||
recordStackMap(bb, branches.typeIndex); | |||
bb.version = iteration; | |||
MapMaker maker = new MapMaker(classPool, cpool, stackTypes.length, | |||
localsTypes.length, blocks, returnType, iteration); | |||
/* the following code is equivalent to maker.copyFrom(this) | |||
* except stackTypes are not copied. | |||
*/ | |||
maker.stackTypes[0] = bb.stackTypes[0].getSelf(); | |||
maker.stackTop = 1; | |||
TypeData[] srcTypes = this.localsTypes; | |||
copyFrom(srcTypes.length, srcTypes, maker.localsTypes); | |||
maker.make(code, bb); | |||
recordUsage(bb, maker); | |||
if (maker.loopDetected) | |||
this.loopDetected = true; | |||
} | |||
branches = branches.next; | |||
} | |||
} | |||
@@ -234,7 +276,10 @@ public class MapMaker extends Tracer { | |||
TypeData t = srcTypes[i]; | |||
destTypes[i] = t == null ? null : t.getSelf(); | |||
if (t != TOP) | |||
k = i; | |||
if (t.is2WordType()) | |||
k = i + 1; | |||
else | |||
k = i; | |||
} | |||
return k + 1; | |||
@@ -255,13 +300,65 @@ public class MapMaker extends Tracer { | |||
target.setStackMap(st, tStackTypes, k, tLocalsTypes); | |||
} | |||
private void recordStackMap(BasicBlock target, int exceptionType) | |||
throws BadBytecode | |||
{ | |||
int n = localsTypes.length; | |||
TypeData[] tLocalsTypes = new TypeData[n]; | |||
int k = copyFrom(n, localsTypes, tLocalsTypes); | |||
String type; | |||
if (exceptionType == 0) | |||
type = "java.lang.Throwable"; | |||
else | |||
type = cpool.getClassInfo(exceptionType); | |||
TypeData[] tStackTypes = new TypeData[stackTypes.length]; | |||
tStackTypes[0] = new TypeData.ClassName(type); | |||
target.setStackMap(1, tStackTypes, k, tLocalsTypes); | |||
} | |||
private void recordUsage(BasicBlock target, MapMaker next) { | |||
int[] nextUsage = next.localsUsage; | |||
TypeData[] tData = target.localsTypes; | |||
int n = tData.length; | |||
for (int i = blocks[0].numLocals; i < n; i++) | |||
if (nextUsage[i] == READ) | |||
readLocal(i); | |||
else | |||
tData[i] = TOP; | |||
int[] usage = new int[nextUsage.length]; | |||
n = usage.length; | |||
for (int i = 0; i < n; i++) | |||
usage[i] = nextUsage[i]; | |||
target.localsUsage = usage; | |||
} | |||
private void mergeUsage(BasicBlock target) { | |||
int[] usage = target.localsUsage; | |||
if (usage == null) { | |||
// detected a loop. | |||
loopDetected = true; | |||
} | |||
else { | |||
int n = usage.length; | |||
for (int i = 0; i < n; i++) | |||
if (usage[i] == READ) | |||
readLocal(i); | |||
} | |||
} | |||
// Phase 2 | |||
void evalExpected(BasicBlock target) throws BadBytecode { | |||
ClassPool cp = classPool; | |||
evalExpected(cp, target.stackTop, target.stackTypes); | |||
TypeData[] types = target.localsTypes; | |||
evalExpected(cp, types.length, types); | |||
if (types != null) // unless this block is dead code | |||
evalExpected(cp, types.length, types); | |||
} | |||
private static void evalExpected(ClassPool cp, int n, TypeData[] types) | |||
@@ -282,13 +379,18 @@ public class MapMaker extends Tracer { | |||
int n = blocks.length; | |||
BasicBlock prev = blocks[0]; | |||
int offsetDelta = prev.length; | |||
if (prev.inbound > 0) { // the first instruction is a branch target. | |||
writer.sameFrame(0); | |||
offsetDelta--; | |||
} | |||
for (int i = 1; i < n; i++) { | |||
BasicBlock bb = blocks[i]; | |||
if (bb.inbound > 0) { | |||
bb.resetNumLocals(); | |||
int diffL = stackMapDiff(prev.numLocals, prev.localsTypes, | |||
bb.numLocals, bb.localsTypes); | |||
toStackMapBody(writer, bb, diffL, offsetDelta); | |||
toStackMapBody(writer, bb, diffL, offsetDelta, prev); | |||
offsetDelta = bb.length - 1; | |||
prev = bb; | |||
} | |||
@@ -301,7 +403,7 @@ public class MapMaker extends Tracer { | |||
} | |||
private void toStackMapBody(StackMapTable.Writer writer, BasicBlock bb, | |||
int diffL, int offsetDelta) { | |||
int diffL, int offsetDelta, BasicBlock prev) { | |||
// if diffL is -100, two TypeData arrays do not share | |||
// any elements. | |||
@@ -316,17 +418,16 @@ public class MapMaker extends Tracer { | |||
return; | |||
} | |||
else if (0 < diffL && diffL <= 3) { | |||
int[] tags = new int[diffL]; | |||
int[] data = new int[diffL]; | |||
fillStackMap(diffL, bb.numLocals - diffL, tags, data, | |||
bb.localsTypes); | |||
int[] tags = fillStackMap(bb.numLocals - prev.numLocals, | |||
prev.numLocals, data, | |||
bb.localsTypes); | |||
writer.appendFrame(offsetDelta, tags, data); | |||
return; | |||
} | |||
} | |||
else if (stackTop == 1 && diffL == 0) { | |||
TypeData[] types = bb.stackTypes; | |||
TypeData td = types[0]; | |||
TypeData td = bb.stackTypes[0]; | |||
if (td == TOP) | |||
writer.sameLocals(offsetDelta, StackMapTable.TOP, 0); | |||
else | |||
@@ -334,30 +435,45 @@ public class MapMaker extends Tracer { | |||
td.getTypeData(cpool)); | |||
return; | |||
} | |||
else if (stackTop == 2 && diffL == 0) { | |||
TypeData td = bb.stackTypes[0]; | |||
if (td != TOP && td.is2WordType()) { | |||
// bb.stackTypes[1] must be TOP. | |||
writer.sameLocals(offsetDelta, td.getTypeTag(), | |||
td.getTypeData(cpool)); | |||
return; | |||
} | |||
} | |||
int[] stags = new int[stackTop]; | |||
int[] sdata = new int[stackTop]; | |||
int nl = bb.numLocals; | |||
int[] ltags = new int[nl]; | |||
int[] ldata = new int[nl]; | |||
fillStackMap(stackTop, 0, stags, sdata, bb.stackTypes); | |||
fillStackMap(nl, 0, ltags, ldata, bb.localsTypes); | |||
int[] stags = fillStackMap(stackTop, 0, sdata, bb.stackTypes); | |||
int[] ldata = new int[bb.numLocals]; | |||
int[] ltags = fillStackMap(bb.numLocals, 0, ldata, bb.localsTypes); | |||
writer.fullFrame(offsetDelta, ltags, ldata, stags, sdata); | |||
} | |||
private void fillStackMap(int num, int offset, int[] tags, int[] data, TypeData[] types) { | |||
private int[] fillStackMap(int num, int offset, int[] data, TypeData[] types) { | |||
int realNum = diffSize(types, offset, offset + num); | |||
ConstPool cp = cpool; | |||
int[] tags = new int[realNum]; | |||
int j = 0; | |||
for (int i = 0; i < num; i++) { | |||
TypeData td = types[offset + i]; | |||
if (td == TOP) { | |||
tags[i] = StackMapTable.TOP; | |||
data[i] = 0; | |||
tags[j] = StackMapTable.TOP; | |||
data[j] = 0; | |||
} | |||
else { | |||
tags[i] = td.getTypeTag(); | |||
data[i] = td.getTypeData(cp); | |||
tags[j] = td.getTypeTag(); | |||
data[j] = td.getTypeData(cp); | |||
if (td.is2WordType()) | |||
i++; | |||
} | |||
j++; | |||
} | |||
return tags; | |||
} | |||
private static int stackMapDiff(int oldTdLen, TypeData[] oldTd, | |||
@@ -371,7 +487,10 @@ public class MapMaker extends Tracer { | |||
len = newTdLen; | |||
if (stackMapEq(oldTd, newTd, len)) | |||
return diff; | |||
if (diff > 0) | |||
return diffSize(newTd, len, newTdLen); | |||
else | |||
return -diffSize(oldTd, len, oldTdLen); | |||
else | |||
return -100; | |||
} | |||
@@ -379,7 +498,7 @@ public class MapMaker extends Tracer { | |||
private static boolean stackMapEq(TypeData[] oldTd, TypeData[] newTd, int len) { | |||
for (int i = 0; i < len; i++) { | |||
TypeData td = oldTd[i]; | |||
if (td == TOP) { | |||
if (td == TOP) { // the next element to LONG/DOUBLE is TOP. | |||
if (newTd[i] != TOP) | |||
return false; | |||
} | |||
@@ -391,6 +510,18 @@ public class MapMaker extends Tracer { | |||
return true; | |||
} | |||
private static int diffSize(TypeData[] types, int offset, int len) { | |||
int num = 0; | |||
while (offset < len) { | |||
TypeData td = types[offset++]; | |||
num++; | |||
if (td != TOP && td.is2WordType()) | |||
offset++; | |||
} | |||
return num; | |||
} | |||
// Branch actions | |||
protected void visitBranch(int pos, byte[] code, int offset) throws BadBytecode { |
@@ -36,6 +36,11 @@ public abstract class Tracer implements TypeTag { | |||
protected TypeData[] stackTypes; | |||
protected TypeData[] localsTypes; | |||
static final int UNKNOWN = 0; | |||
static final int READ = 1; | |||
static final int UPDATED = 2; | |||
protected int[] localsUsage; | |||
public Tracer(ClassPool classes, ConstPool cp, int maxStack, int maxLocals, | |||
String retType) { | |||
classPool = classes; | |||
@@ -44,15 +49,22 @@ public abstract class Tracer implements TypeTag { | |||
stackTop = 0; | |||
stackTypes = new TypeData[maxStack]; | |||
localsTypes = new TypeData[maxLocals]; | |||
localsUsage = new int[maxLocals]; | |||
} | |||
/* If the type is LONG or DOUBLE, | |||
* the next local variable is also read. | |||
* IINC (or WIDE IINC) calls only readLocal() but it does not call writeLocal(). | |||
*/ | |||
private void readLocal(int reg) {} | |||
protected final void readLocal(int reg) { | |||
if (localsUsage[reg] == UNKNOWN) | |||
localsUsage[reg] = READ; | |||
} | |||
private void writeLocal(int reg) {} | |||
protected final void writeLocal(int reg) { | |||
if (localsUsage[reg] == UNKNOWN) | |||
localsUsage[reg] = UPDATED; | |||
} | |||
/** | |||
* Does abstract interpretation on the given bytecode instruction. | |||
@@ -230,7 +242,7 @@ public abstract class Tracer implements TypeTag { | |||
case Opcode.AALOAD : { | |||
int s = --stackTop - 1; | |||
TypeData data = stackTypes[s]; | |||
if (data == null || data.isBasicType()) | |||
if (data == null || !data.isObjectType()) | |||
throw new BadBytecode("bad AALOAD"); | |||
else | |||
stackTypes[s] = new TypeData.ArrayElement(data); | |||
@@ -278,7 +290,7 @@ public abstract class Tracer implements TypeTag { | |||
private int doXLOAD(int localVar, TypeData type) { | |||
stackTypes[stackTop++] = type; | |||
if (type == LONG || type == DOUBLE) | |||
if (type.is2WordType()) | |||
stackTypes[stackTop++] = TOP; | |||
readLocal(localVar); | |||
@@ -414,7 +426,7 @@ public abstract class Tracer implements TypeTag { | |||
stackTop--; | |||
writeLocal(index); | |||
localsTypes[index] = type; | |||
if (type == LONG || type == DOUBLE) { | |||
if (type.is2WordType()) { | |||
stackTop--; | |||
localsTypes[index + 1] = TOP; | |||
} | |||
@@ -433,7 +445,7 @@ public abstract class Tracer implements TypeTag { | |||
private void doDUP_XX(int delta, int len) { | |||
TypeData types[] = stackTypes; | |||
int sp = stackTop; | |||
int sp = stackTop - 1; | |||
int end = sp - len; | |||
while (sp > end) { | |||
types[sp + delta] = types[sp]; | |||
@@ -614,8 +626,13 @@ public abstract class Tracer implements TypeTag { | |||
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("[L" + type + ";"); | |||
= new TypeData.ClassName(type); | |||
return 3; } | |||
case Opcode.ARRAYLENGTH : | |||
stackTypes[stackTop - 1] = INTEGER; |
@@ -28,38 +28,9 @@ public abstract class TypeData { | |||
* array type is a subtype of Cloneable and Serializable | |||
*/ | |||
protected ArrayList equivalences; | |||
protected TypeData() {} | |||
protected TypeData() { | |||
equivalences = new ArrayList(); | |||
equivalences.add(this); | |||
} | |||
public void merge(TypeData neighbor) { | |||
if (this == neighbor) | |||
return; | |||
ArrayList list = equivalences; | |||
ArrayList list2 = neighbor.equivalences; | |||
if (list == list2) | |||
return; | |||
int n = list2.size(); | |||
for (int i = 0; i < n; i++) { | |||
TypeData td = (TypeData)list2.get(i); | |||
add(list, td); | |||
td.equivalences = list; | |||
} | |||
} | |||
private static void add(ArrayList list, TypeData td) { | |||
int n = list.size(); | |||
for (int i = 0; i < n; i++) | |||
if (list.get(i) == td) | |||
return; | |||
list.add(td); | |||
} | |||
public abstract void merge(TypeData neighbor); | |||
/** | |||
* Sets the type name of this object type. If the given type name is | |||
@@ -69,7 +40,7 @@ public abstract class TypeData { | |||
* @param className dot-separated name unless the type is an array type. | |||
*/ | |||
static void setType(TypeData td, String className, ClassPool cp) throws BadBytecode { | |||
if (td == null) | |||
if (td == TypeTag.TOP) | |||
throw new BadBytecode("unset variable"); | |||
else | |||
td.setType(className, cp); | |||
@@ -90,14 +61,13 @@ public abstract class TypeData { | |||
*/ | |||
public abstract TypeData copy(); | |||
public boolean isBasicType() { return false; } | |||
public boolean isObjectType() { return false; } | |||
public abstract boolean isObjectType(); | |||
public boolean is2WordType() { return false; } | |||
public boolean isNullType() { return false; } | |||
public abstract String getName() throws BadBytecode; | |||
protected abstract void setType(String s, ClassPool cp) throws BadBytecode; | |||
public abstract void evalExpectedType(ClassPool cp) throws BadBytecode; | |||
protected abstract boolean hasExpectedType(); // only TypeName can return true. | |||
public abstract String getExpected() throws BadBytecode; | |||
/** | |||
@@ -112,6 +82,8 @@ public abstract class TypeData { | |||
typeTag = tag; | |||
} | |||
public void merge(TypeData neighbor) {} | |||
public boolean equals(Object obj) { | |||
return this == obj; | |||
} | |||
@@ -119,7 +91,12 @@ public abstract class TypeData { | |||
public int getTypeTag() { return typeTag; } | |||
public int getTypeData(ConstPool cp) { return 0; } | |||
public boolean isBasicType() { return true; } | |||
public boolean isObjectType() { return false; } | |||
public boolean is2WordType() { | |||
return typeTag == StackMapTable.LONG | |||
|| typeTag == StackMapTable.DOUBLE; | |||
} | |||
public TypeData copy() { | |||
return this; | |||
@@ -135,10 +112,6 @@ public abstract class TypeData { | |||
return name; | |||
} | |||
protected boolean hasExpectedType() { | |||
return false; | |||
} | |||
protected void setType(String s, ClassPool cp) throws BadBytecode { | |||
throw new BadBytecode("conflict:" + name + " and " + s); | |||
} | |||
@@ -147,16 +120,50 @@ public abstract class TypeData { | |||
} | |||
protected static abstract class TypeName extends TypeData { | |||
protected ArrayList equivalences; | |||
private String expectedName; | |||
private CtClass cache; | |||
private boolean evalDone; | |||
protected TypeName() { | |||
equivalences = new ArrayList(); | |||
equivalences.add(this); | |||
expectedName = null; | |||
cache = null; | |||
evalDone = false; | |||
} | |||
public void merge(TypeData neighbor) { | |||
if (this == neighbor) | |||
return; | |||
if (!(neighbor instanceof TypeName)) | |||
return; // neighbor might be UninitData | |||
TypeName neighbor2 = (TypeName)neighbor; | |||
ArrayList list = equivalences; | |||
ArrayList list2 = neighbor2.equivalences; | |||
if (list == list2) | |||
return; | |||
int n = list2.size(); | |||
for (int i = 0; i < n; i++) { | |||
TypeName tn = (TypeName)list2.get(i); | |||
add(list, tn); | |||
tn.equivalences = list; | |||
} | |||
} | |||
private static void add(ArrayList list, TypeData td) { | |||
int n = list.size(); | |||
for (int i = 0; i < n; i++) | |||
if (list.get(i) == td) | |||
return; | |||
list.add(td); | |||
} | |||
/* NullType overrides this method. | |||
*/ | |||
public int getTypeTag() { return StackMapTable.OBJECT; } | |||
@@ -206,7 +213,7 @@ public abstract class TypeData { | |||
int n = equiv.size(); | |||
for (int i = 0; i < n; i++) { | |||
TypeData td = (TypeData)equiv.get(i); | |||
if (td.hasExpectedType()) { | |||
if (td instanceof TypeName) { | |||
TypeName tn = (TypeName)td; | |||
if (update(cp, name, tn.expectedName)) | |||
name = tn.expectedName; | |||
@@ -218,7 +225,7 @@ public abstract class TypeData { | |||
for (int i = 0; i < n; i++) { | |||
TypeData td = (TypeData)equiv.get(i); | |||
if (td.hasExpectedType()) { | |||
if (td instanceof TypeName) { | |||
TypeName tn = (TypeName)td; | |||
tn.expectedName = name; | |||
tn.cache = null; | |||
@@ -241,7 +248,7 @@ public abstract class TypeData { | |||
return origName; | |||
} | |||
protected boolean hasExpectedType() { return true; } | |||
protected boolean isTypeName() { return true; } | |||
private boolean update(ClassPool cp, String oldName, String typeName) throws BadBytecode { | |||
if (typeName == null) | |||
@@ -410,6 +417,8 @@ public abstract class TypeData { | |||
this.initialized = false; | |||
} | |||
public void merge(TypeData neighbor) {} | |||
public int getTypeTag() { return StackMapTable.UNINIT; } | |||
public int getTypeData(ConstPool cp) { return offset; } | |||
@@ -433,14 +442,14 @@ public abstract class TypeData { | |||
return new ClassName(className); | |||
} | |||
public boolean isObjectType() { return true; } | |||
protected void setType(String typeName, ClassPool cp) throws BadBytecode { | |||
initialized = true; | |||
} | |||
public void evalExpectedType(ClassPool cp) throws BadBytecode {} | |||
protected boolean hasExpectedType() { return false; } | |||
public String getName() { | |||
return className; | |||
} |