[jboss-cvs] javassist/src/main/javassist/bytecode/stackmap ...
Shigeru Chiba
chiba at is.titech.ac.jp
Tue Apr 10 12:32:32 EDT 2007
User: chiba
Date: 07/04/10 12:32:32
Modified: src/main/javassist/bytecode/stackmap BasicBlock.java
Added: src/main/javassist/bytecode/stackmap MapMaker.java
TypeTag.java TypeData.java Tracer.java
Removed: src/main/javassist/bytecode/stackmap
StackAnalyzerCore.java StackAnalyzer.java
Log:
stackmap support (cont.)
Revision Changes Path
1.3 +283 -73 javassist/src/main/javassist/bytecode/stackmap/BasicBlock.java
(In the diff below, changes in quantity of whitespace are not shown.)
Index: BasicBlock.java
===================================================================
RCS file: /cvsroot/jboss/javassist/src/main/javassist/bytecode/stackmap/BasicBlock.java,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -b -r1.2 -r1.3
--- BasicBlock.java 9 Feb 2007 18:05:21 -0000 1.2
+++ BasicBlock.java 10 Apr 2007 16:32:32 -0000 1.3
@@ -1,55 +1,113 @@
+/*
+ * Javassist, a Java-bytecode translator toolkit.
+ * Copyright (C) 1999-2006 Shigeru Chiba. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. Alternatively, the contents of this file may be used under
+ * the terms of the GNU Lesser General Public License Version 2.1 or later.
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ */
+
package javassist.bytecode.stackmap;
import javassist.bytecode.*;
+import java.util.ArrayList;
+
+public class BasicBlock implements TypeTag, Comparable {
-public class BasicBlock {
- public int position;
- public int stackTop;
- public int[] stackTypes, localsTypes;
- public Object[] stackData, localsData;
+ public int position, length;
+ public int stackTop, numLocals;
+ public TypeData[] stackTypes, localsTypes;
+
+ /* 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
+ * only from the preceding block. Such a basic block represents
+ * the boundary of a try block.
+ */
+ public int inbound;
+
+ /* 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"));
+ System.out.println(b);
+ }*/
private BasicBlock(int pos) {
position = pos;
+ length = 0;
+ stackTop = numLocals = 0;
+ stackTypes = localsTypes = null;
+ inbound = 1;
+ }
+
+ public boolean alreadySet() { return stackTypes != null; }
+
+ /*
+ * Computes the correct value of numLocals.
+ * It assumes that:
+ * correct numLocals <= current numLocals
+ */
+ public void resetNumLocals() {
+ if (localsTypes != null) {
+ int nl = numLocals;
+ while (nl > 0 && localsTypes[nl - 1] == TypeTag.TOP)
+ --nl;
+
+ numLocals = nl;
+ }
}
- public void set(int st, int[] stypes, Object[] sdata, int[] ltypes, Object[] ldata)
+ public void setStackMap(int st, TypeData[] stack,
+ int nl, TypeData[] locals)
throws BadBytecode
{
- if (stackTypes == null) {
stackTop = st;
- stackTypes = copy(stypes);
- stackData = copy(sdata);
- localsTypes = copy(ltypes);
- localsData = copy(ldata);
- }
- else {
- if (st != stackTop)
- throw new BadBytecode("verification failure");
+ stackTypes = stack;
+ numLocals = nl;
+ localsTypes = locals;
+ }
+
+ private void updateLength(int nextPos) {
+ length = nextPos - position;
+ }
+
+ public String toString() {
+ StringBuffer sbuf = new StringBuffer();
+ sbuf.append("Block at ");
+ sbuf.append(position);
+ sbuf.append(" stack={");
+ printTypes(sbuf, stackTop, stackTypes);
+ sbuf.append("} locals={");
+ printTypes(sbuf, numLocals, localsTypes);
+ sbuf.append('}');
+ return sbuf.toString();
+ }
+
+ private static void printTypes(StringBuffer sbuf, int size,
+ TypeData[] types) {
+ if (types == null)
+ return;
- int n = ltypes.length;
- for (int i = 0; i < n; i++)
- if (ltypes[i] != localsTypes[i]) {
- localsTypes[i] = StackAnalyzerCore.EMPTY;
- localsData[i] = null;
- }
- else if (ltypes[i] == StackAnalyzerCore.OBJECT
- && !ldata[i].equals(localsData[i]))
- ; // localsData[i] = ??;
- }
- }
+ for (int i = 0; i < size; i++) {
+ if (i > 0)
+ sbuf.append(", ");
- private static int[] copy(int[] a) {
- int[] b = new int[a.length];
- System.arraycopy(a, 0, b, 0, a.length);
- return b;
+ TypeData td = types[i];
+ sbuf.append(td == null ? "<>" : td.toString());
}
-
- private static Object[] copy(Object[] a) {
- Object[] b = new Object[a.length];
- System.arraycopy(a, 0, b, 0, a.length);
- return b;
}
+ /**
+ * Finds the basic block including the given position.
+ *
+ * @param pos the position.
+ */
public static BasicBlock find(BasicBlock[] blocks, int pos) throws BadBytecode {
int n = blocks.length;
for (int i = 0; i < n; i++)
@@ -59,102 +117,254 @@
throw new BadBytecode("no basic block: " + pos);
}
- public static BasicBlock[] makeBlocks(CodeIterator ci, ExceptionTable et)
+ /**
+ * Divides the given code fragment into basic blocks.
+ */
+ public static BasicBlock[] makeBlocks(MethodInfo minfo) throws BadBytecode {
+ CodeAttribute ca = minfo.getCodeAttribute();
+ CodeIterator ci = ca.iterator();
+ ConstPool pool = minfo.getConstPool();
+ BasicBlock[] blocks = makeBlocks(ci, 0, ci.getCodeLength(), ca.getExceptionTable(), 0, pool);
+ boolean isStatic = (minfo.getAccessFlags() & AccessFlag.STATIC) != 0;
+ blocks[0].initFirstBlock(ca.getMaxStack(), ca.getMaxLocals(),
+ pool.getClassName(), minfo.getDescriptor(),
+ isStatic, minfo.isConstructor());
+ return blocks;
+ }
+
+ /**
+ * Divides the given code fragment into basic blocks.
+ *
+ * @param begin the position where the basic block analysis starts.
+ * @param end exclusive.
+ * @param et the appended exception table entries.
+ * @param etOffset the offset added to the handlerPc entries in the exception table.
+ * @param pool the constant pool.
+ */
+ public static BasicBlock[] makeBlocks(CodeIterator ci, int begin, int end,
+ ExceptionTable et, int etOffset, ConstPool pool)
throws BadBytecode
{
ci.begin();
- int[] targets = new int[16];
- int size = 0;
+ ci.move(begin);
+ ArrayList targets = new ArrayList();
+ targets.add(new BasicBlock(begin));
while (ci.hasNext()) {
int index = ci.next();
+ if (index >= end)
+ break;
+
int op = ci.byteAt(index);
if ((Opcode.IFEQ <= op && op <= Opcode.IF_ACMPNE)
|| op == Opcode.IFNULL || op == Opcode.IFNONNULL)
- targets = add(targets, size++, index + ci.s16bitAt(index + 1));
+ targets.add(new BasicBlock(index + ci.s16bitAt(index + 1)));
else if (Opcode.GOTO <= op && op <= Opcode.LOOKUPSWITCH)
switch (op) {
case Opcode.GOTO :
- targets = add(targets, size++, index + ci.s16bitAt(index + 1));
- break;
case Opcode.JSR :
- case Opcode.RET :
- throw new BadBytecode("jsr/ret at " + index);
+ targets.add(new BasicBlock(index + ci.s16bitAt(index + 1)));
+ break;
+ // case Opcode.RET :
+ // throw new BadBytecode("ret at " + index);
case Opcode.TABLESWITCH : {
int pos = (index & ~3) + 4;
- targets = add(targets, size++, index + ci.s32bitAt(pos)); // default offset
+ targets.add(new BasicBlock(index + ci.s32bitAt(pos))); // default branch target
int low = ci.s32bitAt(pos + 4);
int high = ci.s32bitAt(pos + 8);
int p = pos + 12;
int n = p + (high - low + 1) * 4;
while (p < n) {
- targets = add(targets, size++, index + ci.s32bitAt(p));
+ targets.add(new BasicBlock(index + ci.s32bitAt(p)));
p += 4;
}
break; }
case Opcode.LOOKUPSWITCH : {
int pos = (index & ~3) + 4;
- targets = add(targets, size++, index + ci.s32bitAt(pos)); // default offset
+ targets.add(new BasicBlock(index + ci.s32bitAt(pos))); // default branch target
int p = pos + 8 + 4;
int n = p + ci.s32bitAt(pos + 4) * 8;
while (p < n) {
- targets = add(targets, size++, index + ci.s32bitAt(p));
+ targets.add(new BasicBlock(index + ci.s32bitAt(p)));
p += 8;
}
break; }
}
- else if (op == Opcode.GOTO_W)
- targets = add(targets, size++, index + ci.s32bitAt(index + 1));
- else if (op == Opcode.JSR_W)
- throw new BadBytecode("jsr_w at " + index);
+ else if (op == Opcode.GOTO_W || op == Opcode.JSR_W)
+ targets.add(new BasicBlock(index + ci.s32bitAt(index + 1)));
}
if (et != null) {
int i = et.size();
while (--i >= 0) {
- targets = add(targets, size++, et.startPc(i));
- targets = add(targets, size++, et.handlerPc(i));
+ BasicBlock bb = new BasicBlock(et.startPc(i) + etOffset);
+ bb.inbound = 0;
+ targets.add(bb);
+ targets.add(new BasicBlock(et.handlerPc(i) + etOffset));
}
}
- return trimArray(targets);
+ return trimArray(targets, end);
}
- private static int[] add(int[] targets, int size, int value) {
- if (targets.length >= size) {
- int[] a = new int[size << 1];
- System.arraycopy(targets, 0, a, 0, targets.length);
- targets = a;
+ public int compareTo(Object obj) {
+ if (obj instanceof BasicBlock) {
+ int pos = ((BasicBlock)obj).position;
+ return position - pos;
}
- targets[size++] = value;
- return targets;
+ return -1;
}
- private static BasicBlock[] trimArray(int[] targets) {
- int size = targets.length;
- java.util.Arrays.sort(targets);
+ /**
+ * @param endPos exclusive
+ */
+ private static BasicBlock[] trimArray(ArrayList targets, int endPos) {
+ Object[] targetArray = targets.toArray();
+ int size = targetArray.length;
+ java.util.Arrays.sort(targetArray);
int s = 0;
- int t0 = 0;
+ int t0 = -1;
for (int i = 0; i < size; i++) {
- int t = targets[i];
+ int t = ((BasicBlock)targetArray[i]).position;
if (t != t0) {
s++;
t0 = t;
}
}
- BasicBlock[] results = new BasicBlock[s + 1];
- results[0] = new BasicBlock(0);
- t0 = 0;
- for (int i = 0, j = 1; i < size; i++) {
- int t = targets[i];
- if (t != t0) {
- BasicBlock b = new BasicBlock(t);
- results[j++] = b;
+ BasicBlock[] results = new BasicBlock[s];
+ BasicBlock bb0 = (BasicBlock)targetArray[0];
+ results[0] = bb0;
+ t0 = bb0.position;
+ int j = 1;
+ for (int i = 1; i < size; i++) {
+ BasicBlock bb = (BasicBlock)targetArray[i];
+ int t = bb.position;
+ if (t == t0)
+ results[j - 1].inbound += bb.inbound;
+ else {
+ results[j - 1].updateLength(t);
+ results[j++] = bb;
t0 = t;
}
}
+ results[j - 1].updateLength(endPos);
return results;
}
+
+ /**
+ * Initializes the first block by the given method descriptor.
+ *
+ * @param block the first basic block that this method initializes.
+ * @param className a dot-separated fully qualified class name.
+ * For example, <code>javassist.bytecode.stackmap.BasicBlock</code>.
+ * @param methodDesc method descriptor.
+ * @param isStatic true if the method is a static method.
+ * @param isConstructor true if the method is a constructor.
+ */
+ void initFirstBlock(int maxStack, int maxLocals, String className,
+ String methodDesc, boolean isStatic, boolean isConstructor)
+ throws BadBytecode
+ {
+ if (methodDesc.charAt(0) != '(')
+ throw new BadBytecode("no method descriptor: " + methodDesc);
+
+ stackTop = 0;
+ stackTypes = new TypeData[maxStack];
+ TypeData[] locals = new TypeData[maxLocals];
+ if (isConstructor)
+ locals[0] = new TypeData.UninitThis(className);
+ else if (!isStatic)
+ locals[0] = new TypeData.ClassName(className);
+
+ 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);
+
+ numLocals = n;
+ localsTypes = locals;
+ position = 0;
+ inbound = 0;
+ }
+
+ private static int descToTag(String desc, int i,
+ int n, TypeData[] types)
+ throws BadBytecode
+ {
+ int i0 = i;
+ int arrayDim = 0;
+ char c = desc.charAt(i);
+ if (c == ')')
+ return 0;
+
+ while (c == '[') {
+ ++arrayDim;
+ c = desc.charAt(++i);
+ }
+
+ if (c == 'L') {
+ int i2 = desc.indexOf(';', ++i);
+ if (arrayDim > 0)
+ types[n] = new TypeData.ClassName(desc.substring(i0, ++i2));
+ else
+ types[n] = new TypeData.ClassName(desc.substring(i0 + 1, ++i2 - 1)
+ .replace('/', '.'));
+ return i2;
+ }
+ else if (arrayDim > 0) {
+ types[n] = new TypeData.ClassName(desc.substring(i0, ++i));
+ return i;
+ }
+ else {
+ TypeData t = toPrimitiveTag(c);
+ if (t == null)
+ throw new BadBytecode("bad method descriptor: " + desc);
+
+ types[n] = t;
+ return i + 1;
+ }
+ }
+
+ private static TypeData toPrimitiveTag(char c) {
+ switch (c) {
+ case 'Z' :
+ case 'C' :
+ case 'B' :
+ case 'S' :
+ case 'I' :
+ return INTEGER;
+ case 'J' :
+ return LONG;
+ case 'F' :
+ return FLOAT;
+ case 'D' :
+ return DOUBLE;
+ case 'V' :
+ default :
+ return null;
+ }
+ }
+
+ public static String getRetType(String desc) {
+ int i = desc.indexOf(')');
+ if (i < 0)
+ return "java.lang.Object";
+
+ char c = desc.charAt(i + 1);
+ if (c == '[')
+ return desc.substring(i + 1);
+ else if (c == 'L')
+ return desc.substring(i + 2, desc.length() - 1).replace('/', '.');
+ else
+ return "java.lang.Object";
+ }
}
1.1 date: 2007/04/10 16:32:32; author: chiba; state: Exp;javassist/src/main/javassist/bytecode/stackmap/MapMaker.java
Index: MapMaker.java
===================================================================
/*
* Javassist, a Java-bytecode translator toolkit.
* Copyright (C) 1999-2006 Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
* the terms of the GNU Lesser General Public License Version 2.1 or later.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*/
package javassist.bytecode.stackmap;
import javassist.ClassPool;
import javassist.bytecode.*;
/**
* Stack map maker.
*/
public class MapMaker extends Tracer {
private boolean moveon;
private BasicBlock[] blocks;
public static void main(String[] args) throws Exception {
if (args.length > 1) {
main2(args);
return;
}
ClassPool cp = ClassPool.getDefault();
javassist.CtClass cc = cp.get(args[0]);
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));
}
cc.writeFile("tmp");
}
public static void main2(String[] args) throws Exception {
ClassPool cp = ClassPool.getDefault();
javassist.CtClass cc = cp.get(args[0]);
MapMaker mm;
if (args[1].equals("_init_"))
mm = makeMapMaker(cp, cc.getDeclaredConstructors()[0].getMethodInfo());
else
mm = makeMapMaker(cp, cc.getDeclaredMethod(args[1]).getMethodInfo());
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]);
}
}
/**
* Computes the stack map table of the given method and returns it.
* It returns null if the given method does not have to have a
* stack map table.
*/
public static StackMapTable getMap(ClassPool classes, MethodInfo minfo)
throws BadBytecode
{
MapMaker mm = makeMapMaker(classes, minfo);
if (mm == null)
return null;
else
return mm.toStackMap();
}
/*
* Makes basic blocks with stack maps. If the number of the basic blocks
* is one, this method returns null.
*/
public static MapMaker makeMapMaker(ClassPool classes, MethodInfo minfo)
throws BadBytecode
{
CodeAttribute ca = minfo.getCodeAttribute();
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;
boolean isStatic = (minfo.getAccessFlags() & AccessFlag.STATIC) != 0;
int maxStack = ca.getMaxStack();
int maxLocals = ca.getMaxLocals();
BasicBlock top = blocks[0];
String desc = minfo.getDescriptor();
top.initFirstBlock(maxStack, maxLocals, pool.getClassName(), desc,
isStatic, minfo.isConstructor());
String retType = BasicBlock.getRetType(desc);
MapMaker mm = new MapMaker(classes, pool, maxStack, maxLocals,
blocks, retType, blocks[0]);
mm.make(ca.getCode(), et);
return mm;
}
/**
* 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);
TypeData[] srcTypes = init.localsTypes;
copyFrom(srcTypes.length, srcTypes, this.localsTypes);
}
private MapMaker(ClassPool classes, ConstPool cp,
int maxStack, int maxLocals, BasicBlock[] bb,
String retType)
{
super(classes, cp, maxStack, maxLocals, retType);
blocks = bb;
}
public BasicBlock[] getBlocks() { return blocks; }
/**
* Runs an analyzer.
*/
void make(byte[] code, ExceptionTable et) throws BadBytecode {
make(code, blocks[0]);
traceExceptions(code, et);
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)
throws BadBytecode
{
int pos = bb.position;
int end = pos + bb.length;
moveon = true;
while (moveon && pos < end)
pos += doOpcode(pos, code);
if (moveon && pos < code.length) {
this.copyFrom(this);
nextBlock(pos, code, 0);
}
}
private void nextBlock(int pos, byte[] code, int offset) throws BadBytecode {
BasicBlock bb = BasicBlock.find(blocks, pos + offset);
if (bb.alreadySet()) {
mergeMap(stackTypes, bb.stackTypes);
mergeMap(localsTypes, bb.localsTypes);
}
else {
recordStackMap(bb);
MapMaker maker = new MapMaker(classPool, cpool, stackTypes.length,
localsTypes.length, blocks, returnType);
maker.copyFrom(this);
maker.make(code, bb);
}
}
private static void mergeMap(TypeData[] srcTypes, TypeData[] destTypes) {
int n = srcTypes.length;
for (int i = 0; i < n; i++) {
TypeData s = srcTypes[i];
TypeData d = destTypes[i];
boolean sIsObj = false;
boolean dIsObj = false;
// s or b is null if it is TOP.
if (s != TOP && s.isObjectType())
sIsObj = true;
if (d != TOP && d.isObjectType())
dIsObj = true;
if (sIsObj && dIsObj)
d.merge(s);
else if (s != d)
destTypes[i] = TOP;
}
}
private void copyFrom(MapMaker src) {
int sp = src.stackTop;
this.stackTop = sp;
copyFrom(sp, src.stackTypes, this.stackTypes);
TypeData[] srcTypes = src.localsTypes;
copyFrom(srcTypes.length, srcTypes, this.localsTypes);
}
private static int copyFrom(int n, TypeData[] srcTypes, TypeData[] destTypes) {
int k = -1;
for (int i = 0; i < n; i++) {
TypeData t = srcTypes[i];
destTypes[i] = t == null ? null : t.getSelf();
if (t != TOP)
k = i;
}
return k + 1;
}
private void recordStackMap(BasicBlock target)
throws BadBytecode
{
int n = localsTypes.length;
TypeData[] tLocalsTypes = new TypeData[n];
int k = copyFrom(n, localsTypes, tLocalsTypes);
n = stackTypes.length;
TypeData[] tStackTypes = new TypeData[n];
int st = stackTop;
copyFrom(st, stackTypes, tStackTypes);
target.setStackMap(st, tStackTypes, k, tLocalsTypes);
}
// 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);
}
private static void evalExpected(ClassPool cp, int n, TypeData[] types)
throws BadBytecode
{
for (int i = 0; i < n; i++) {
TypeData td = types[i];
if (td != null)
td.evalExpectedType(cp);
}
}
// Phase 3
public StackMapTable toStackMap() {
BasicBlock[] blocks = this.blocks;
StackMapTable.Writer writer = new StackMapTable.Writer(32);
int n = blocks.length;
BasicBlock prev = blocks[0];
int offsetDelta = prev.length;
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);
offsetDelta = bb.length - 1;
prev = bb;
}
else
offsetDelta += bb.length;
}
return writer.toStackMapTable(cpool);
}
private void toStackMapBody(StackMapTable.Writer writer, BasicBlock bb,
int diffL, int offsetDelta) {
// if diffL is -100, two TypeData arrays do not share
// any elements.
int stackTop = bb.stackTop;
if (stackTop == 0) {
if (diffL == 0) {
writer.sameFrame(offsetDelta);
return;
}
else if (0 > diffL && diffL >= -3) {
writer.chopFrame(offsetDelta, -diffL);
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);
writer.appendFrame(offsetDelta, tags, data);
return;
}
}
else if (stackTop == 1 && diffL == 0) {
TypeData[] types = bb.stackTypes;
TypeData td = types[0];
if (td == TOP)
writer.sameLocals(offsetDelta, StackMapTable.TOP, 0);
else
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);
writer.fullFrame(offsetDelta, ltags, ldata, stags, sdata);
}
private void fillStackMap(int num, int offset, int[] tags, int[] data, TypeData[] types) {
ConstPool cp = cpool;
for (int i = 0; i < num; i++) {
TypeData td = types[offset + i];
if (td == TOP) {
tags[i] = StackMapTable.TOP;
data[i] = 0;
}
else {
tags[i] = td.getTypeTag();
data[i] = td.getTypeData(cp);
}
}
}
private static int stackMapDiff(int oldTdLen, TypeData[] oldTd,
int newTdLen, TypeData[] newTd)
{
int diff = newTdLen - oldTdLen;
int len;
if (diff > 0)
len = oldTdLen;
else
len = newTdLen;
if (stackMapEq(oldTd, newTd, len))
return diff;
else
return -100;
}
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 (newTd[i] != TOP)
return false;
}
else
if (!oldTd[i].equals(newTd[i]))
return false;
}
return true;
}
// Branch actions
protected void visitBranch(int pos, byte[] code, int offset) throws BadBytecode {
nextBlock(pos, code, offset);
}
protected void visitGoto(int pos, byte[] code, int offset) throws BadBytecode {
nextBlock(pos, code, offset);
moveon = false;
}
protected void visitTableSwitch(int pos, byte[] code, int n, int offsetPos, int defaultOffset)
throws BadBytecode
{
nextBlock(pos, code, defaultOffset);
for (int i = 0; i < n; i++) {
nextBlock(pos, code, ByteArray.read32bit(code, offsetPos));
offsetPos += 4;
}
moveon = false;
}
protected void visitLookupSwitch(int pos, byte[] code, int n, int pairsPos, int defaultOffset)
throws BadBytecode
{
nextBlock(pos, code, defaultOffset);
pairsPos += 4;
for (int i = 0; i < n; i++) {
nextBlock(pos, code, ByteArray.read32bit(code, pairsPos));
pairsPos += 8;
}
moveon = false;
}
protected void visitReturn(int pos, byte[] code) { moveon = false; }
protected void visitThrow(int pos, byte[] code) { moveon = false; }
}
1.1 date: 2007/04/10 16:32:32; author: chiba; state: Exp;javassist/src/main/javassist/bytecode/stackmap/TypeTag.java
Index: TypeTag.java
===================================================================
/*
* Javassist, a Java-bytecode translator toolkit.
* Copyright (C) 1999-2006 Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
* the terms of the GNU Lesser General Public License Version 2.1 or later.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*/
package javassist.bytecode.stackmap;
import javassist.bytecode.StackMapTable;
public interface TypeTag {
TypeData TOP = null;
TypeData INTEGER = new TypeData.BasicType("int", StackMapTable.INTEGER);
TypeData FLOAT = new TypeData.BasicType("float", StackMapTable.FLOAT);
TypeData DOUBLE = new TypeData.BasicType("double", StackMapTable.DOUBLE);
TypeData LONG = new TypeData.BasicType("long", StackMapTable.LONG);
// and NULL, THIS, OBJECT, UNINIT
}
1.1 date: 2007/04/10 16:32:32; author: chiba; state: Exp;javassist/src/main/javassist/bytecode/stackmap/TypeData.java
Index: TypeData.java
===================================================================
/*
* Javassist, a Java-bytecode translator toolkit.
* Copyright (C) 1999-2006 Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
* the terms of the GNU Lesser General Public License Version 2.1 or later.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*/
package javassist.bytecode.stackmap;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.NotFoundException;
import javassist.bytecode.ConstPool;
import javassist.bytecode.StackMapTable;
import javassist.bytecode.BadBytecode;
import java.util.ArrayList;
public abstract class TypeData {
/* Memo:
* array type is a subtype of Cloneable and Serializable
*/
protected ArrayList equivalences;
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);
}
/**
* Sets the type name of this object type. If the given type name is
* a subclass of the current type name, then the given name becomes
* the name of this object type.
*
* @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)
throw new BadBytecode("unset variable");
else
td.setType(className, cp);
}
public abstract boolean equals(Object obj);
public abstract int getTypeTag();
public abstract int getTypeData(ConstPool cp);
/*
* See UninitData.getSelf().
*/
public TypeData getSelf() { return this; }
/* An operand value is copied when it is stored in a
* local variable.
*/
public abstract TypeData copy();
public boolean isBasicType() { return false; }
public boolean isObjectType() { 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;
/**
* Primitive types.
*/
protected static class BasicType extends TypeData {
private String name;
private int typeTag;
public BasicType(String type, int tag) {
name = type;
typeTag = tag;
}
public boolean equals(Object obj) {
return this == obj;
}
public int getTypeTag() { return typeTag; }
public int getTypeData(ConstPool cp) { return 0; }
public boolean isBasicType() { return true; }
public TypeData copy() {
return this;
}
public void evalExpectedType(ClassPool cp) throws BadBytecode {}
public String getExpected() throws BadBytecode {
return name;
}
public String getName() {
return name;
}
protected boolean hasExpectedType() {
return false;
}
protected void setType(String s, ClassPool cp) throws BadBytecode {
throw new BadBytecode("conflict:" + name + " and " + s);
}
public String toString() { return name; }
}
protected static abstract class TypeName extends TypeData {
private String expectedName;
private CtClass cache;
private boolean evalDone;
protected TypeName() {
expectedName = null;
cache = null;
evalDone = false;
}
/* NullType overrides this method.
*/
public int getTypeTag() { return StackMapTable.OBJECT; }
public int getTypeData(ConstPool cp) {
String type;
try {
type = getExpected();
} catch (BadBytecode e) {
throw new RuntimeException("fatal error: ", e);
}
return getTypeData2(cp, type);
}
/* NullType overrides this method.
*/
protected int getTypeData2(ConstPool cp, String type) {
return cp.addClassInfo(type);
}
public boolean equals(Object obj) {
if (obj instanceof TypeName) {
try {
TypeName tn = (TypeName)obj;
return getExpected().equals(tn.getExpected());
}
catch (BadBytecode e) {}
}
return false;
}
public boolean isObjectType() { return true; }
protected void setType(String typeName, ClassPool cp) throws BadBytecode {
if (update(cp, expectedName, typeName))
expectedName = typeName;
}
public void evalExpectedType(ClassPool cp) throws BadBytecode {
if (this.evalDone)
return;
ArrayList equiv = this.equivalences;
String name = this.expectedName;
int n = equiv.size();
for (int i = 0; i < n; i++) {
TypeData td = (TypeData)equiv.get(i);
if (td.hasExpectedType()) {
TypeName tn = (TypeName)td;
if (update(cp, name, tn.expectedName))
name = tn.expectedName;
}
}
if (name == null)
name = evalExpectedType2(equivalences, n);
for (int i = 0; i < n; i++) {
TypeData td = (TypeData)equiv.get(i);
if (td.hasExpectedType()) {
TypeName tn = (TypeName)td;
tn.expectedName = name;
tn.cache = null;
tn.evalDone = true;
}
}
}
private String evalExpectedType2(ArrayList equiv, int n) throws BadBytecode {
String origName = null;
for (int i = 0; i < n; i++) {
TypeData td = (TypeData)equiv.get(i);
if (!td.isNullType())
if (origName == null)
origName = td.getName();
else if (!origName.equals(td.getName()))
return null;
}
return origName;
}
protected boolean hasExpectedType() { return true; }
private boolean update(ClassPool cp, String oldName, String typeName) throws BadBytecode {
if (typeName == null)
return false;
else if (oldName == null)
return true;
else if (oldName.equals(typeName))
return false;
try {
if (cache == null)
cache = cp.get(oldName);
CtClass cache2 = cp.get(typeName);
if (cache2.subclassOf(cache)) {
cache = cache2;
return true;
}
else
return false;
}
catch (NotFoundException e) {
throw new BadBytecode("cannot find " + e.getMessage());
}
}
public String getExpected() throws BadBytecode {
ArrayList equiv = equivalences;
if (equiv.size() == 1)
return getName();
else {
String en = expectedName;
if (en == null)
return "java.lang.Object";
else
return en;
}
}
public String toString() {
try {
String en = expectedName;
if (en != null)
return en;
String name = getName();
if (equivalences.size() == 1)
return name;
else
return name + "?";
}
catch (BadBytecode e) {
return "<" + e.getMessage() + ">";
}
}
}
/**
* Type data for OBJECT.
*/
public static class ClassName extends TypeName {
private String name; // dot separated. null if this object is a copy of another.
public ClassName(String n) {
name = n;
}
public TypeData copy() {
return new ClassName(name);
}
public String getName() { // never returns null.
return name;
}
}
/**
* Type data for NULL or OBJECT.
* The types represented by the instances of this class are
* initially NULL but will be OBJECT.
*/
public static class NullType extends ClassName {
public NullType() {
super("null"); // type name
}
public TypeData copy() {
return new NullType();
}
public boolean isNullType() { return true; }
public int getTypeTag() {
try {
if ("null".equals(getExpected()))
return StackMapTable.NULL;
else
return super.getTypeTag();
}
catch (BadBytecode e) {
throw new RuntimeException("fatal error: ", e);
}
}
protected int getTypeData2(ConstPool cp, String type) {
if ("null".equals(type))
return 0;
else
return super.getTypeData2(cp, type);
}
}
/**
* Type data for OBJECT if the type is an object type and is
* derived as an element type from an array type by AALOAD.
*/
public static class ArrayElement extends TypeName {
TypeData array;
public ArrayElement(TypeData a) { // a is never null
array = a;
}
public TypeData copy() {
return new ArrayElement(array);
}
protected void setType(String typeName, ClassPool cp) throws BadBytecode {
super.setType(typeName, cp);
array.setType(getArrayType(typeName), cp);
}
public String getName() throws BadBytecode {
String name = array.getName();
if (name.length() > 1 && name.charAt(0) == '[') {
char c = name.charAt(1);
if (c == 'L')
return name.substring(2, name.length() - 1).replace('/', '.');
else if (c == '[')
return name.substring(1);
}
throw new BadBytecode("bad array type for AALOAD: "
+ name);
}
public static String getArrayType(String elementType) {
if (elementType.charAt(0) == '[')
return "[" + elementType;
else
return "[L" + elementType.replace('.', '/') + ";";
}
}
/**
* Type data for UNINIT.
*/
public static class UninitData extends TypeData {
String className;
int offset;
boolean initialized;
UninitData(int offset, String className) {
this.className = className;
this.offset = offset;
this.initialized = false;
}
public int getTypeTag() { return StackMapTable.UNINIT; }
public int getTypeData(ConstPool cp) { return offset; }
public boolean equals(Object obj) {
if (obj instanceof UninitData) {
UninitData ud = (UninitData)obj;
return offset == ud.offset && className.equals(ud.className);
}
else
return false;
}
public TypeData getSelf() {
if (initialized)
return copy();
else
return this;
}
public TypeData copy() {
return new ClassName(className);
}
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;
}
public String getExpected() { return className; }
public String toString() { return "uninit:" + className + "@" + offset; }
}
public static class UninitThis extends UninitData {
UninitThis(String className) {
super(-1, className);
}
public int getTypeTag() { return StackMapTable.THIS; }
public int getTypeData(ConstPool cp) { return 0; }
public boolean equals(Object obj) {
return obj instanceof UninitThis;
}
public String toString() { return "uninit:this"; }
}
}
1.1 date: 2007/04/10 16:32:32; author: chiba; state: Exp;javassist/src/main/javassist/bytecode/stackmap/Tracer.java
Index: Tracer.java
===================================================================
/*
* Javassist, a Java-bytecode translator toolkit.
* Copyright (C) 1999-2006 Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
* the terms of the GNU Lesser General Public License Version 2.1 or later.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*/
package javassist.bytecode.stackmap;
import javassist.bytecode.ByteArray;
import javassist.bytecode.Opcode;
import javassist.bytecode.ConstPool;
import javassist.bytecode.Descriptor;
import javassist.bytecode.BadBytecode;
import javassist.ClassPool;
/*
* A class for performing abstract interpretation.
* See also MapMaker class.
*/
public abstract class Tracer implements TypeTag {
protected ClassPool classPool;
protected ConstPool cpool;
protected String returnType;
protected int stackTop;
protected TypeData[] stackTypes;
protected TypeData[] localsTypes;
public Tracer(ClassPool classes, ConstPool cp, int maxStack, int maxLocals,
String retType) {
classPool = classes;
cpool = cp;
returnType = retType;
stackTop = 0;
stackTypes = new TypeData[maxStack];
localsTypes = new TypeData[maxLocals];
}
/* 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) {}
private void writeLocal(int reg) {}
/**
* 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.
*
* @pos the position of the instruction.
* @return the size of the instruction at POS.
*/
protected int doOpcode(int pos, byte[] code) throws BadBytecode {
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);
}
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.
*/
protected void visitJSR(int pos, byte[] code) throws BadBytecode {
throwBadBytecode(pos, "jsr");
}
/**
* Invoked when the visited instruction is ret or wide ret.
*/
protected void visitRET(int pos, byte[] code) throws BadBytecode {
throwBadBytecode(pos, "ret");
}
private void throwBadBytecode(int pos, String name) throws BadBytecode {
throw new BadBytecode(name + " at " + pos);
}
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;
readLocal(op - Opcode.ILOAD_0);
break;
case Opcode.LLOAD_0 :
case Opcode.LLOAD_1 :
case Opcode.LLOAD_2 :
case Opcode.LLOAD_3 :
stackTypes[stackTop++] = LONG;
stackTypes[stackTop++] = TOP;
readLocal(op - Opcode.LLOAD_0);
break;
case Opcode.FLOAD_0 :
case Opcode.FLOAD_1 :
case Opcode.FLOAD_2 :
case Opcode.FLOAD_3 :
stackTypes[stackTop++] = FLOAT;
readLocal(op - Opcode.FLOAD_0);
break;
case Opcode.DLOAD_0 :
case Opcode.DLOAD_1 :
case Opcode.DLOAD_2 :
case Opcode.DLOAD_3 :
stackTypes[stackTop++] = DOUBLE;
stackTypes[stackTop++] = TOP;
readLocal(op - Opcode.DLOAD_0);
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];
readLocal(reg);
break;
case Opcode.IALOAD :
stackTypes[--stackTop - 1] = INTEGER;
break;
case Opcode.LALOAD :
stackTypes[stackTop - 2] = LONG;
stackTypes[stackTop - 1] = TOP;
break;
case Opcode.FALOAD :
stackTypes[--stackTop - 1] = FLOAT;
break;
case Opcode.DALOAD :
stackTypes[stackTop - 2] = DOUBLE;
stackTypes[stackTop - 1] = TOP;
break;
case Opcode.AALOAD : {
int s = --stackTop - 1;
TypeData data = stackTypes[s];
if (data == null || data.isBasicType())
throw new BadBytecode("bad AALOAD");
else
stackTypes[s] = new TypeData.ArrayElement(data);
break; }
case Opcode.BALOAD :
case Opcode.CALOAD :
case Opcode.SALOAD :
stackTypes[--stackTop - 1] = INTEGER;
break;
default :
throw new RuntimeException("fatal");
}
return 1;
}
private void doLDC(int index) {
TypeData[] stackTypes = this.stackTypes;
int tag = cpool.getTag(index);
if (tag == ConstPool.CONST_String)
stackTypes[stackTop++] = new TypeData.ClassName("java.lang.String");
else if (tag == ConstPool.CONST_Integer)
stackTypes[stackTop++] = INTEGER;
else if (tag == ConstPool.CONST_Float)
stackTypes[stackTop++] = FLOAT;
else if (tag == ConstPool.CONST_Long) {
stackTypes[stackTop++] = LONG;
stackTypes[stackTop++] = TOP;
}
else if (tag == ConstPool.CONST_Double) {
stackTypes[stackTop++] = DOUBLE;
stackTypes[stackTop++] = TOP;
}
else if (tag == ConstPool.CONST_Class)
stackTypes[stackTop++] = new TypeData.ClassName("java.lang.Class");
else
throw new RuntimeException("bad LDC: " + tag);
}
private int doXLOAD(TypeData type, byte[] code, int pos) {
int localVar = code[pos + 1] & 0xff;
return doXLOAD(localVar, type);
}
private int doXLOAD(int localVar, TypeData type) {
stackTypes[stackTop++] = type;
if (type == LONG || type == DOUBLE)
stackTypes[stackTop++] = TOP;
readLocal(localVar);
return 2;
}
private int doALOAD(int localVar) { // int localVar, TypeData type) {
stackTypes[stackTop++] = localsTypes[localVar];
readLocal(localVar);
return 2;
}
private int doOpcode54_95(int pos, byte[] code, int op) {
TypeData[] localsTypes = this.localsTypes;
TypeData[] stackTypes = this.stackTypes;
switch (op) {
case Opcode.ISTORE :
return doXSTORE(pos, code, INTEGER);
case Opcode.LSTORE :
return doXSTORE(pos, code, LONG);
case Opcode.FSTORE :
return doXSTORE(pos, code, FLOAT);
case Opcode.DSTORE :
return doXSTORE(pos, code, DOUBLE);
case Opcode.ASTORE :
return doASTORE(code[pos + 1] & 0xff);
case Opcode.ISTORE_0 :
case Opcode.ISTORE_1 :
case Opcode.ISTORE_2 :
case Opcode.ISTORE_3 :
{ int var = op - Opcode.ISTORE_0;
localsTypes[var] = INTEGER;
writeLocal(var);
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;
writeLocal(var);
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;
writeLocal(var);
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;
writeLocal(var);
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 :
case Opcode.AASTORE :
case Opcode.BASTORE :
case Opcode.CASTORE :
case Opcode.SASTORE :
stackTop -= (op == Opcode.LASTORE || op == Opcode.DASTORE) ? 4 : 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--;
writeLocal(index);
localsTypes[index] = type;
if (type == LONG || type == DOUBLE) {
stackTop--;
localsTypes[index + 1] = TOP;
}
return 2;
}
private int doASTORE(int index) {
stackTop--;
writeLocal(index);
// implicit upcast might be done.
localsTypes[index] = stackTypes[stackTop].copy();
return 2;
}
private void doDUP_XX(int delta, int len) {
TypeData types[] = stackTypes;
int sp = stackTop;
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 :
readLocal(code[pos + 1] & 0xff);
// this does not call writeLocal().
return 3;
case Opcode.I2L :
stackTypes[stackTop] = LONG;
stackTypes[stackTop - 1] = TOP;
stackTop++;
break;
case Opcode.I2F :
stackTypes[stackTop - 1] = FLOAT;
break;
case Opcode.I2D :
stackTypes[stackTop] = DOUBLE;
stackTypes[stackTop - 1] = TOP;
stackTop++;
break;
case Opcode.L2I :
stackTypes[--stackTop - 1] = INTEGER;
break;
case Opcode.L2F :
stackTypes[--stackTop - 1] = FLOAT;
break;
case Opcode.L2D :
stackTypes[stackTop - 1] = DOUBLE;
break;
case Opcode.F2I :
stackTypes[stackTop - 1] = INTEGER;
break;
case Opcode.F2L :
stackTypes[stackTop - 1] = TOP;
stackTypes[stackTop++] = LONG;
break;
case Opcode.F2D :
stackTypes[stackTop - 1] = TOP;
stackTypes[stackTop++] = DOUBLE;
break;
case Opcode.D2I :
stackTypes[--stackTop - 1] = INTEGER;
break;
case Opcode.D2L :
stackTypes[stackTop - 1] = LONG;
break;
case Opcode.D2F :
stackTypes[--stackTop - 1] = FLOAT;
break;
case Opcode.I2B :
case Opcode.I2C :
case Opcode.I2S :
break;
default :
throw new RuntimeException("fatal");
}
return 1;
}
private int doOpcode148_201(int pos, byte[] code, int op) throws BadBytecode {
switch (op) {
case Opcode.LCMP :
stackTypes[stackTop - 4] = INTEGER;
stackTop -= 3;
break;
case Opcode.FCMPL :
case Opcode.FCMPG :
stackTypes[--stackTop - 1] = INTEGER;
break;
case Opcode.DCMPL :
case Opcode.DCMPG :
stackTypes[stackTop - 4] = INTEGER;
stackTop -= 3;
break;
case Opcode.IFEQ :
case Opcode.IFNE :
case Opcode.IFLT :
case Opcode.IFGE :
case Opcode.IFGT :
case Opcode.IFLE :
stackTop--; // branch
visitBranch(pos, code, ByteArray.readS16bit(code, pos + 1));
return 3;
case Opcode.IF_ICMPEQ :
case Opcode.IF_ICMPNE :
case Opcode.IF_ICMPLT :
case Opcode.IF_ICMPGE :
case Opcode.IF_ICMPGT :
case Opcode.IF_ICMPLE :
case Opcode.IF_ACMPEQ :
case Opcode.IF_ACMPNE :
stackTop -= 2; // branch
visitBranch(pos, code, ByteArray.readS16bit(code, pos + 1));
return 3;
case Opcode.GOTO :
visitGoto(pos, code, ByteArray.readS16bit(code, pos + 1));
return 3; // branch
case Opcode.JSR :
stackTypes[stackTop++] = TOP; // not allowed?
visitJSR(pos, code);
return 3; // branch
case Opcode.RET :
visitRET(pos, code);
return 2; // not allowed?
case Opcode.TABLESWITCH : {
stackTop--; // branch
int pos2 = (pos & ~3) + 8;
int low = ByteArray.read32bit(code, pos2);
int high = ByteArray.read32bit(code, pos2 + 4);
int n = high - low + 1;
visitTableSwitch(pos, code, n, pos2 + 8, ByteArray.read32bit(code, pos2 - 4));
return n * 4 + 16 - (pos & 3); }
case Opcode.LOOKUPSWITCH : {
stackTop--; // branch
int pos2 = (pos & ~3) + 8;
int n = ByteArray.read32bit(code, pos2);
visitLookupSwitch(pos, code, n, pos2 + 4, ByteArray.read32bit(code, pos2 - 4));
return n * 8 + 12 - (pos & 3); }
case Opcode.IRETURN :
stackTop--;
visitReturn(pos, code);
break;
case Opcode.LRETURN :
stackTop -= 2;
visitReturn(pos, code);
break;
case Opcode.FRETURN :
stackTop--;
visitReturn(pos, code);
break;
case Opcode.DRETURN :
stackTop -= 2;
visitReturn(pos, code);
break;
case Opcode.ARETURN :
TypeData.setType(stackTypes[--stackTop], returnType, classPool);
visitReturn(pos, code);
break;
case Opcode.RETURN :
visitReturn(pos, code);
break;
case Opcode.GETSTATIC :
return doGetField(pos, code, false);
case Opcode.PUTSTATIC :
return doPutField(pos, code, false);
case Opcode.GETFIELD :
return doGetField(pos, code, true);
case Opcode.PUTFIELD :
return doPutField(pos, code, true);
case Opcode.INVOKEVIRTUAL :
case Opcode.INVOKESPECIAL :
return doInvokeMethod(pos, code, true);
case Opcode.INVOKESTATIC :
return doInvokeMethod(pos, code, false);
case Opcode.INVOKEINTERFACE :
return doInvokeIntfMethod(pos, code);
case 186 :
throw new RuntimeException("bad opcode 186");
case Opcode.NEW : {
int i = ByteArray.readU16bit(code, pos + 1);
stackTypes[stackTop++]
= new TypeData.UninitData(pos, cpool.getClassInfo(i));
return 3; }
case Opcode.NEWARRAY :
return doNEWARRAY(pos, code);
case Opcode.ANEWARRAY : {
int i = ByteArray.readU16bit(code, pos + 1);
String type = cpool.getClassInfo(i).replace('.', '/');
stackTypes[stackTop - 1]
= new TypeData.ClassName("[L" + type + ";");
return 3; }
case Opcode.ARRAYLENGTH :
stackTypes[stackTop - 1] = INTEGER;
break;
case Opcode.ATHROW :
TypeData.setType(stackTypes[--stackTop], "java.lang.Throwable", classPool);
visitThrow(pos, code);
break;
case Opcode.CHECKCAST : {
// TypeData.setType(stackData[stackTop - 1], "java.lang.Object", classPool);
int i = ByteArray.readU16bit(code, pos + 1);
stackTypes[stackTop - 1] = new TypeData.ClassName(cpool.getClassInfo(i));
return 3; }
case Opcode.INSTANCEOF :
// TypeData.setType(stackData[stackTop - 1], "java.lang.Object", classPool);
stackTypes[stackTop - 1] = INTEGER;
return 3;
case Opcode.MONITORENTER :
case Opcode.MONITOREXIT :
stackTop--;
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 :
stackTypes[stackTop++] = TOP; // not allowed?
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 :
return doWIDE_STORE(pos, code, INTEGER);
case Opcode.LSTORE :
return doWIDE_STORE(pos, code, LONG);
case Opcode.FSTORE :
return doWIDE_STORE(pos, code, FLOAT);
case Opcode.DSTORE :
return doWIDE_STORE(pos, code, DOUBLE);
case Opcode.ASTORE : {
int index = ByteArray.readU16bit(code, pos + 2);
return doASTORE(index); }
case Opcode.IINC :
readLocal(ByteArray.readU16bit(code, pos + 2));
// 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 int doWIDE_XLOAD(int pos, byte[] code, TypeData type) {
int index = ByteArray.readU16bit(code, pos + 2);
return doXLOAD(index, type);
}
private int doWIDE_STORE(int pos, byte[] code, TypeData type) {
int index = ByteArray.readU16bit(code, pos + 2);
return doXSTORE(index, type);
}
private int doPutField(int pos, byte[] code, boolean notStatic) throws BadBytecode {
int index = ByteArray.readU16bit(code, pos + 1);
String desc = cpool.getFieldrefType(index);
stackTop -= Descriptor.dataSize(desc);
char c = desc.charAt(0);
if (c == 'L')
TypeData.setType(stackTypes[stackTop], getFieldClassName(desc, 0), classPool);
else if (c == '[')
TypeData.setType(stackTypes[stackTop], desc, classPool);
setFieldTarget(notStatic, index);
return 3;
}
private int doGetField(int pos, byte[] code, boolean notStatic) throws BadBytecode {
int index = ByteArray.readU16bit(code, pos + 1);
setFieldTarget(notStatic, index);
String desc = cpool.getFieldrefType(index);
pushMemberType(desc);
return 3;
}
private void setFieldTarget(boolean notStatic, int index) throws BadBytecode {
if (notStatic) {
String className = cpool.getFieldrefClassName(index);
TypeData.setType(stackTypes[--stackTop], className, classPool);
}
}
private int doNEWARRAY(int pos, byte[] code) {
int s = stackTop - 1;
String type;
switch (code[pos + 1] & 0xff) {
case Opcode.T_BOOLEAN :
type = "[Z";
break;
case Opcode.T_CHAR :
type = "[C";
break;
case Opcode.T_FLOAT :
type = "[F";
break;
case Opcode.T_DOUBLE :
type = "[D";
break;
case Opcode.T_BYTE :
type = "[B";
break;
case Opcode.T_SHORT :
type = "[S";
break;
case Opcode.T_INT :
type = "[I";
break;
case Opcode.T_LONG :
type = "[J";
break;
default :
throw new RuntimeException("bad newarray");
}
stackTypes[s] = new TypeData.ClassName(type);
return 2;
}
private int doMultiANewArray(int pos, byte[] code) {
int i = ByteArray.readU16bit(code, pos + 1);
int dim = code[pos + 3] & 0xff;
stackTop -= dim - 1;
String type = cpool.getClassInfo(i).replace('.', '/');
stackTypes[stackTop - 1] = new TypeData.ClassName(type);
return 4;
}
private int doInvokeMethod(int pos, byte[] code, boolean notStatic) throws BadBytecode {
int i = ByteArray.readU16bit(code, pos + 1);
String desc = cpool.getMethodrefType(i);
checkParamTypes(desc, 1);
if (notStatic) {
String className = cpool.getMethodrefClassName(i);
TypeData.setType(stackTypes[--stackTop], className, classPool);
}
pushMemberType(desc);
return 3;
}
private int doInvokeIntfMethod(int pos, byte[] code) throws BadBytecode {
int i = ByteArray.readU16bit(code, pos + 1);
String desc = cpool.getInterfaceMethodrefType(i);
checkParamTypes(desc, 1);
String className = cpool.getInterfaceMethodrefClassName(i);
TypeData.setType(stackTypes[--stackTop], className, classPool);
pushMemberType(desc);
return 5;
}
private void pushMemberType(String descriptor) {
int top = 0;
if (descriptor.charAt(0) == '(') {
top = descriptor.indexOf(')') + 1;
if (top < 1)
throw new IndexOutOfBoundsException("bad descriptor: "
+ descriptor);
}
TypeData[] types = stackTypes;
int index = stackTop;
switch (descriptor.charAt(top)) {
case '[' :
types[index] = new TypeData.ClassName(descriptor.substring(top));
break;
case 'L' :
types[index] = new TypeData.ClassName(getFieldClassName(descriptor, top));
break;
case 'J' :
types[index] = LONG;
types[index + 1] = TOP;
stackTop += 2;
return;
case 'F' :
types[index] = FLOAT;
break;
case 'D' :
types[index] = DOUBLE;
types[index + 1] = TOP;
stackTop += 2;
return;
case 'V' :
return;
default : // C, B, S, I, Z
types[index] = INTEGER;
break;
}
stackTop++;
}
private static String getFieldClassName(String desc, int index) {
return desc.substring(index + 1, desc.length() - 1).replace('/', '.');
}
private void checkParamTypes(String desc, int i) throws BadBytecode {
char c = desc.charAt(i);
if (c == ')')
return;
int k = i;
boolean array = false;
while (c == '[') {
array = true;
c = desc.charAt(++k);
}
if (c == 'L') {
k = desc.indexOf(';', k) + 1;
if (k <= 0)
throw new IndexOutOfBoundsException("bad descriptor");
}
else
k++;
checkParamTypes(desc, k);
if (!array && (c == 'J' || c == 'D'))
stackTop -= 2;
else
stackTop--;
if (array)
TypeData.setType(stackTypes[stackTop],
desc.substring(i, k), classPool);
else if (c == 'L')
TypeData.setType(stackTypes[stackTop],
desc.substring(i + 1, k - 1).replace('/', '.'), classPool);
}
}
More information about the jboss-cvs-commits
mailing list