return AttributeInfo.lookup(attributes, name);
}
+ /**
+ * Adds a stack map table. If another copy of stack map table
+ * is already contained, the old one is removed.
+ *
+ * @param smt the stack map table added to this code attribute.
+ * If it is null, a new stack map is not added.
+ * Only the old stack map is removed.
+ */
+ public void setAttribute(StackMapTable smt) {
+ AttributeInfo.remove(attributes, StackMapTable.tag);
+ if (smt != null)
+ attributes.add(smt);
+ }
+
/**
* Copies code.
*/
/**
* Constructs a <code>stack_map</code> attribute.
*/
- private StackMapTable(ConstPool cp, byte[] newInfo) {
+ StackMapTable(ConstPool cp, byte[] newInfo) {
super(cp, tag, newInfo);
}
/**
* Constructs a walker.
+ *
+ * @param smt the StackMapTable that this walker
+ * walks around.
*/
public Walker(StackMapTable smt) {
this(smt.get());
* It can be obtained by <code>get()</code>
* in the <code>AttributeInfo</code> class.
*/
- Walker(byte[] data) {
+ public Walker(byte[] data) {
info = data;
numOfEntries = ByteArray.readU16bit(data, 0);
}
return b;
}
+ /**
+ * Constructs and a return a stack map table containing
+ * the written stack map entries.
+ *
+ * @param cp the constant pool used to write
+ * the stack map entries.
+ */
+ public StackMapTable toStackMapTable(ConstPool cp) {
+ return new StackMapTable(cp, toByteArray());
+ }
+
/**
* Writes a <code>same_frame</code> or a <code>same_frame_extended</code>.
*/
}
}
+ /**
+ * Prints the stack table map.
+ */
+ public void println(PrintWriter w) {
+ Printer.print(this, w);
+ }
+
+ /**
+ * Prints the stack table map.
+ *
+ * @param ps a print stream such as <code>System.out</code>.
+ */
+ public void println(java.io.PrintStream ps) {
+ Printer.print(this, new java.io.PrintWriter(ps, true));
+ }
+
static class Printer extends Walker {
private PrintWriter writer;
+/*
+ * 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 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 class BasicBlock {
- public int position;
- public int stackTop;
- public int[] stackTypes, localsTypes;
- public Object[] stackData, localsData;
+ /* 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 void set(int st, int[] stypes, Object[] sdata, int[] ltypes, Object[] ldata)
+ 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 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");
-
- 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] = ??;
- }
+ stackTop = st;
+ stackTypes = stack;
+ numLocals = nl;
+ localsTypes = locals;
}
- private static int[] copy(int[] a) {
- int[] b = new int[a.length];
- System.arraycopy(a, 0, b, 0, a.length);
- return b;
+ private void updateLength(int nextPos) {
+ length = nextPos - position;
}
- private static Object[] copy(Object[] a) {
- Object[] b = new Object[a.length];
- System.arraycopy(a, 0, b, 0, a.length);
- return b;
+ 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;
+
+ for (int i = 0; i < size; i++) {
+ if (i > 0)
+ sbuf.append(", ");
+
+ TypeData td = types[i];
+ sbuf.append(td == null ? "<>" : td.toString());
+ }
+ }
+
+ /**
+ * 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++)
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";
+ }
}
--- /dev/null
+/*
+ * 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; }
+}
+++ /dev/null
-package javassist.bytecode.stackmap;
-
-import javassist.bytecode.ConstPool;
-
-public class StackAnalyzer extends StackAnalyzerCore {
-
- public StackAnalyzer(ConstPool cp, int maxStack, int maxLocals) {
- super(cp, maxStack, maxLocals);
- }
-}
+++ /dev/null
-package javassist.bytecode.stackmap;
-
-import javassist.bytecode.StackMapTable;
-import javassist.bytecode.ByteArray;
-import javassist.bytecode.Opcode;
-import javassist.bytecode.ConstPool;
-import javassist.bytecode.Descriptor;
-import javassist.bytecode.BadBytecode;
-
-public class StackAnalyzerCore {
- private ConstPool cpool;
- private int stackTop;
- private int[] stackTypes;
- private Object[] stackData; // String or Integer
-
- private int[] localsTypes;
- private boolean[] localsUpdated;
- private Object[] localsData; // String or Integer
-
- static final int EMPTY = -1;
- static final int TOP = StackMapTable.TOP;
- static final int INTEGER = StackMapTable.INTEGER;
- static final int FLOAT = StackMapTable.FLOAT;
- static final int DOUBLE = StackMapTable.DOUBLE;
- static final int LONG = StackMapTable.LONG;
- static final int NULL = StackMapTable.NULL;
- static final int THIS = StackMapTable.THIS;
- static final int OBJECT = StackMapTable.OBJECT;
- static final int UNINIT = StackMapTable.UNINIT;
-
- public StackAnalyzerCore(ConstPool cp, int maxStack, int maxLocals) {
- cpool = cp;
- stackTop = 0;
- stackTypes = null;
- localsTypes = null;
- growStack(maxStack);
- growLocals(maxLocals);
- }
-
- public void clearUpdatedFlags() {
- boolean[] updated = localsUpdated;
- for (int i = 0; i < updated.length; i++)
- updated[i] = false;
- }
-
- public void growStack(int size) {
- int oldSize;
- int[] types = new int[size];
- Object[] data = new Object[size];
- if (stackTypes == null)
- oldSize = 0;
- else {
- oldSize = stackTypes.length;
- System.arraycopy(stackTypes, 0, types, 0, oldSize);
- System.arraycopy(stackData, 0, data, 0, oldSize);
- }
-
- stackTypes = types;
- stackData = data;
- for (int i = oldSize; i < size; i++)
- stackTypes[i] = EMPTY;
- }
-
- public void growLocals(int size) {
- int oldSize;
- int[] types = new int[size];
- boolean[] updated = new boolean[size];
- Object[] data = new Object[size];
- if (localsTypes == null)
- oldSize = 0;
- else {
- oldSize = localsTypes.length;
- System.arraycopy(localsTypes, 0, types, 0, oldSize);
- System.arraycopy(localsUpdated, 0, updated, 0, oldSize);
- System.arraycopy(localsData, 0, data, 0, oldSize);
- }
-
- localsTypes = types;
- localsUpdated = updated;
- localsData = data;
- for (int i = oldSize; i < size; i++)
- localsTypes[i] = EMPTY;
- }
-
- public void pushType(int type, Object data) {
- stackTypes[stackTop] = type;
- stackData[stackTop++] = data;
- }
-
- /**
- * @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) {}
- protected void visitGoto(int pos, byte[] code, int offset) {}
- protected void visitTableSwitch(int pos, byte[] code, int n, int offsetPos, int defaultByte) {}
- protected void visitLookupSwitch(int pos, byte[] code, int n, int pairsPos, int defaultByte) {}
- protected void visitReturn(int pos, byte[] code) {}
- protected void visitThrow(int pos, byte[] code) {}
-
- /**
- * 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.
- */
- 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[] stackTypes = this.stackTypes;
- switch (op) {
- case Opcode.NOP :
- break;
- case Opcode.ACONST_NULL :
- stackTypes[stackTop++] = NULL;
- 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);
- case Opcode.LLOAD :
- return doXLOAD(LONG);
- case Opcode.FLOAD :
- return doXLOAD(FLOAT);
- case Opcode.DLOAD :
- return doXLOAD(DOUBLE);
- case Opcode.ALOAD :
- stackTypes[stackTop] = OBJECT;
- stackData[stackTop++] = localsData[code[pos + 1] & 0xff];
- return 2;
- 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 :
- stackTypes[stackTop] = OBJECT;
- stackData[stackTop++] = localsData[op - Opcode.ALOAD_0];
- 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;
- stackTypes[s] = OBJECT;
- Object data = stackData[s];
- if (data != null && data instanceof String)
- stackData[s] = getDerefType((String)data);
- else
- throw new BadBytecode("bad AALOAD");
-
- 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) {
- int[] stackTypes = this.stackTypes;
- int tag = cpool.getTag(index);
- if (tag == ConstPool.CONST_String) {
- stackTypes[stackTop] = OBJECT;
- stackData[stackTop++] = "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] = OBJECT;
- stackData[stackTop++] = "java/lang/Class";
- }
- else
- throw new RuntimeException("bad LDC: " + tag);
- }
-
- private int doXLOAD(int type) {
- int[] stackTypes = this.stackTypes;
- stackTypes[stackTop++] = type;
- if (type == LONG || type == DOUBLE)
- stackTypes[stackTop++] = TOP;
-
- return 2;
- }
-
- private String getDerefType(String type) throws BadBytecode {
- if (type.charAt(0) == '[') {
- String type2 = type.substring(1);
- if (type2.length() > 0) {
- char c = type2.charAt(0);
- if (c == '[')
- return type2;
- else if (c == 'L')
- return type2.substring(1, type.length() - 2);
- else
- return type2;
- }
- }
-
- throw new BadBytecode("bad array type for AALOAD: " + type);
- }
-
- private int doOpcode54_95(int pos, byte[] code, int op) {
- int[] localsTypes = this.localsTypes;
- int[] 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 doXSTORE(pos, code, OBJECT);
- 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;
- localsUpdated[var] = true; }
- 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;
- localsUpdated[var] = true; }
- 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;
- localsUpdated[var] = true; }
- 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;
- localsUpdated[var] = true; }
- 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;
- localsTypes[var] = OBJECT;
- localsUpdated[var] = true;
- localsData[var] = stackData[--stackTop]; }
- 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];
- stackData[sp] = stackData[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];
- stackData[sp - len] = stackData[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);
- Object[] stackData = this.stackData;
- int sp = stackTop;
- stackTypes[sp - len] = stackTypes[sp];
- stackData[sp - len] = stackData[sp];
- stackTypes[sp - len + 1] = stackTypes[sp + 1];
- stackData[sp - len + 1] = stackData[sp + 1];
- stackTop = sp + 2;
- break; }
- case Opcode.SWAP : {
- Object[] stackData = this.stackData;
- int sp = stackTop - 1;
- int t = stackTypes[sp];
- Object d = stackData[sp];
- stackTypes[sp] = stackTypes[sp - 1];
- stackData[sp] = stackData[sp - 1];
- stackTypes[sp - 1] = t;
- stackData[sp - 1] = d;
- break; }
- default :
- throw new RuntimeException("fatal");
- }
-
- return 1;
- }
-
- private int doXSTORE(int pos, byte[] code, int type) {
- int index = code[pos + 1] & 0xff;
- return doXSTORE(index, type);
- }
-
- private int doXSTORE(int index, int type) {
- stackTop--;
- localsTypes[index] = type;
- localsUpdated[index] = true;
- if (type == LONG || type == DOUBLE) {
- stackTop--;
- localsTypes[index + 1] = TOP;
- }
- else if (type == OBJECT)
- localsData[index] = stackData[stackTop];
-
- return 2;
- }
-
- private void doDUP_XX(int delta, int len) {
- int types[] = stackTypes;
- Object data[] = stackData;
- int sp = stackTop;
- int end = sp - len;
- while (sp > end) {
- types[sp + delta] = types[sp];
- data[sp + delta] = data[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 :
- 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 :
- stackTop--;
- visitReturn(pos, code);
- break;
- case Opcode.RETURN :
- visitReturn(pos, code);
- break;
- case Opcode.GETSTATIC :
- return doFieldAccess(pos, code, true);
- case Opcode.PUTSTATIC :
- return doFieldAccess(pos, code, false);
- case Opcode.GETFIELD :
- stackTop--;
- return doFieldAccess(pos, code, true);
- case Opcode.PUTFIELD :
- stackTop--;
- return doFieldAccess(pos, code, false);
- case Opcode.INVOKEVIRTUAL :
- case Opcode.INVOKESPECIAL :
- return doInvokeMethod(pos, code, 1);
- case Opcode.INVOKESTATIC :
- return doInvokeMethod(pos, code, 0);
- case Opcode.INVOKEINTERFACE :
- return doInvokeIntfMethod(pos, code, 1);
- case 186 :
- throw new RuntimeException("bad opcode 186");
- case Opcode.NEW : {
- int i = ByteArray.readU16bit(code, pos + 1);
- stackTypes[stackTop - 1] = UNINIT;
- stackData[stackTop - 1] = new Integer(pos);
- return 3; }
- case Opcode.NEWARRAY :
- return doNEWARRAY(pos, code);
- case Opcode.ANEWARRAY : {
- int i = ByteArray.readU16bit(code, pos + 1);
- stackTypes[stackTop - 1] = OBJECT;
- stackData[stackTop - 1]
- = "[L" + cpool.getClassInfo(i).replace('.', '/') + ";";
- return 3; }
- case Opcode.ARRAYLENGTH :
- stackTypes[stackTop - 1] = INTEGER;
- break;
- case Opcode.ATHROW :
- stackTop--; // branch?
- visitThrow(pos, code);
- break;
- case Opcode.CHECKCAST : {
- int i = ByteArray.readU16bit(code, pos + 1);
- stackData[stackTop - 1] = cpool.getClassInfo(i);
- return 3; }
- case Opcode.INSTANCEOF :
- 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) {
- int op = code[pos + 1] & 0xff;
- switch (op) {
- case Opcode.ILOAD :
- doXLOAD(INTEGER);
- break;
- case Opcode.LLOAD :
- doXLOAD(LONG);
- break;
- case Opcode.FLOAD :
- doXLOAD(FLOAT);
- break;
- case Opcode.DLOAD :
- doXLOAD(DOUBLE);
- break;
- case Opcode.ALOAD :
- stackTypes[stackTop] = OBJECT;
- stackData[stackTop++] = localsData[ByteArray.readU16bit(code, pos)];
- 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 :
- return doWIDE_STORE(pos, code, OBJECT);
- case Opcode.IINC :
- return 6;
- case Opcode.RET :
- break;
- default :
- throw new RuntimeException("bad WIDE instruction: " + op);
- }
-
- return 4;
- }
-
- private int doWIDE_STORE(int pos, byte[] code, int type) {
- int index = ByteArray.readU16bit(code, pos);
- return doXSTORE(index, type);
- }
-
- private int doFieldAccess(int pos, byte[] code, boolean isGet) {
- int index = ByteArray.readU16bit(code, pos + 1);
- String desc = cpool.getFieldrefType(index);
- if (isGet)
- pushMemberType(desc);
- else
- stackTop -= Descriptor.dataSize(desc);
-
- return 3;
- }
-
- private int doNEWARRAY(int pos, byte[] code) {
- int s = stackTop - 1;
- stackTypes[s] = OBJECT;
- 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");
- }
-
- stackData[s] = 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);
- StringBuffer sbuf = new StringBuffer();
- while (dim-- > 0)
- sbuf.append('[');
-
- sbuf.append('L').append(type.replace('.', '/')).append(';');
- stackTypes[stackTop - 1] = OBJECT;
- stackData[stackTop - 1] = sbuf.toString();
- return 4;
- }
-
- private int doInvokeMethod(int pos, byte[] code, int targetSize) {
- int i = ByteArray.readU16bit(code, pos + 1);
- String desc = cpool.getMethodrefType(i);
- stackTop -= Descriptor.paramSize(desc) + targetSize;
- pushMemberType(desc);
- return 3;
- }
-
- private int doInvokeIntfMethod(int pos, byte[] code, int targetSize) {
- int i = ByteArray.readU16bit(code, pos + 1);
- String desc = cpool.getInterfaceMethodrefType(i);
- stackTop -= Descriptor.paramSize(desc) + targetSize;
- 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);
- }
-
- int[] types = stackTypes;
- int index = stackTop;
- switch (descriptor.charAt(top)) {
- case '[' :
- types[index] = OBJECT;
- stackData[index] = descriptor.substring(top);
- break;
- case 'L' :
- types[index] = OBJECT;
- stackData[index] = descriptor.substring(top + 1,
- descriptor.indexOf(';', 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++;
- }
-}
--- /dev/null
+/*
+ * 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);
+ }
+}
--- /dev/null
+/*
+ * 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"; }
+ }
+}
--- /dev/null
+/*
+ * 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
+}