import java.io.IOException;
import java.util.Map;
+import javassist.CannotCompileException;
+import javassist.bytecode.StackMapTable.InsertLocal;
+import javassist.bytecode.StackMapTable.NewRemover;
+import javassist.bytecode.StackMapTable.Shifter;
+
/**
* Another <code>stack_map</code> attribute defined in CLDC 1.1 for J2ME.
*
* is more than 64K. However, for the J2ME CLDC technology, they are always 16bit.
* The implementation of the StackMap class assumes they are 16bit.
*
+ * @see MethodInfo#doPreverify
* @see StackMapTable
* @since 3.12
*/
* Makes a copy.
*/
public AttributeInfo copy(ConstPool newCp, Map classnames) {
- ConstPool cp = getConstPool();
- byte[] srcInfo = info;
- byte[] newInfo = new byte[srcInfo.length];
- int num = numOfEntries();
- ByteArray.write16bit(num, newInfo, 0);
- int pos = 2;
- for (int i = 0; i < num; i++) {
- int offset = ByteArray.readU16bit(srcInfo, pos);
- ByteArray.write16bit(offset, newInfo, pos);
- pos += 2;
- int numLoc = ByteArray.readU16bit(srcInfo, pos);
- ByteArray.write16bit(numLoc, newInfo, pos);
- pos = copyFrames(srcInfo, newInfo, pos + 2, numLoc, cp, newCp,
- classnames);
- int numStack = ByteArray.readU16bit(srcInfo, pos);
- ByteArray.write16bit(numStack, newInfo, pos);
- pos = copyFrames(srcInfo, newInfo, pos + 2, numStack, cp, newCp,
- classnames);
- }
-
- return new StackMap(newCp, newInfo);
- }
-
- private static int copyFrames(byte[] srcInfo, byte[] newInfo, int pos,
- int numFrames, ConstPool cp, ConstPool newCp, Map classnames) {
- for (int k = 0; k < numFrames; k++) {
- byte tag = srcInfo[pos];
- newInfo[pos] = tag;
+ Copier copier = new Copier(this, newCp, classnames);
+ copier.visit();
+ return copier.getStackMap();
+ }
+
+ /**
+ * A code walker for a StackMap attribute.
+ */
+ public static class Walker {
+ byte[] info;
+
+ /**
+ * Constructs a walker.
+ */
+ public Walker(StackMap sm) {
+ info = sm.get();
+ }
+
+ /**
+ * Visits each entry of the stack map frames.
+ */
+ public void visit() {
+ int num = ByteArray.readU16bit(info, 0);
+ int pos = 2;
+ for (int i = 0; i < num; i++) {
+ int offset = ByteArray.readU16bit(info, pos);
+ int numLoc = ByteArray.readU16bit(info, pos + 2);
+ pos = locals(pos + 4, offset, numLoc);
+ int numStack = ByteArray.readU16bit(info, pos);
+ pos = stack(pos + 2, offset, numStack);
+ }
+ }
+
+ /**
+ * Invoked when <code>locals</code> of <code>stack_map_frame</code>
+ * is visited.
+ */
+ public int locals(int pos, int offset, int num) {
+ return typeInfoArray(pos, offset, num, true);
+ }
+
+ /**
+ * Invoked when <code>stack</code> of <code>stack_map_frame</code>
+ * is visited.
+ */
+ public int stack(int pos, int offset, int num) {
+ return typeInfoArray(pos, offset, num, false);
+ }
+
+ /**
+ * Invoked when an array of <code>verification_type_info</code> is
+ * visited.
+ *
+ * @param num the number of elements.
+ * @param isLocals true if this array is for <code>locals</code>.
+ * false if it is for <code>stack</code>.
+ * @return
+ */
+ public int typeInfoArray(int pos, int offset, int num, boolean isLocals) {
+ for (int k = 0; k < num; k++)
+ pos = typeInfoArray2(k, pos);
+
+ return pos;
+ }
+
+ int typeInfoArray2(int k, int pos) {
+ byte tag = info[pos];
if (tag == OBJECT) {
- int clazz = ByteArray.readU16bit(srcInfo, pos + 1);
- clazz = cp.copy(clazz, newCp, classnames);
- ByteArray.write16bit(clazz, newInfo, pos + 1);
+ int clazz = ByteArray.readU16bit(info, pos + 1);
+ objectVariable(pos, clazz);
pos += 3;
}
else if (tag == UNINIT) {
- ByteArray.write16bit(ByteArray.readU16bit(srcInfo, pos + 1),
- newInfo, pos + 1);
+ int offsetOfNew = ByteArray.readU16bit(info, pos + 1);
+ uninitialized(pos, offsetOfNew);
pos += 3;
}
- else
+ else {
+ typeInfo(pos, tag);
pos++;
+ }
+
+ return pos;
}
- return pos;
+ /**
+ * Invoked when an element of <code>verification_type_info</code>
+ * (except <code>Object_variable_info</code> and
+ * <code>Uninitialized_variable_info</code>) is visited.
+ */
+ public void typeInfo(int pos, byte tag) {}
+
+ /**
+ * Invoked when an element of type <code>Object_variable_info</code>
+ * is visited.
+ */
+ public void objectVariable(int pos, int clazz) {}
+
+ /**
+ * Invoked when an element of type <code>Uninitialized_variable_info</code>
+ * is visited.
+ */
+ public void uninitialized(int pos, int offset) {}
+ }
+
+ static class Copier extends Walker {
+ byte[] dest;
+ ConstPool srcCp, destCp;
+ Map classnames;
+
+ Copier(StackMap map, ConstPool newCp, Map classnames) {
+ super(map);
+ srcCp = map.getConstPool();
+ dest = new byte[info.length];
+ destCp = newCp;
+ this.classnames = classnames;
+ }
+
+ public void visit() {
+ int num = ByteArray.readU16bit(info, 0);
+ ByteArray.write16bit(num, dest, 0);
+ super.visit();
+ }
+
+ public int locals(int pos, int offset, int num) {
+ ByteArray.write16bit(offset, dest, pos - 4);
+ return super.locals(pos, offset, num);
+ }
+
+ public int typeInfoArray(int pos, int offset, int num, boolean isLocals) {
+ ByteArray.write16bit(num, dest, pos - 2);
+ return super.typeInfoArray(pos, offset, num, isLocals);
+ }
+
+ public void typeInfo(int pos, byte tag) {
+ dest[pos] = tag;
+ }
+
+ public void objectVariable(int pos, int clazz) {
+ dest[pos] = OBJECT;
+ int newClazz = srcCp.copy(clazz, destCp, classnames);
+ ByteArray.write16bit(newClazz, dest, pos + 1);
+ }
+
+ public void uninitialized(int pos, int offset) {
+ dest[pos] = UNINIT;
+ ByteArray.write16bit(offset, dest, pos + 1);
+ }
+
+ public StackMap getStackMap() {
+ return new StackMap(destCp, dest);
+ }
}
/**
- * Printer
+ * Updates this stack map table when a new local variable is inserted
+ * for a new parameter.
+ *
+ * @param index the index of the added local variable.
+ * @param tag the type tag of that local variable.
+ * It is available by <code>StackMapTable.typeTagOf(char)</code>.
+ * @param classInfo the index of the <code>CONSTANT_Class_info</code> structure
+ * in a constant pool table. This should be zero unless the tag
+ * is <code>ITEM_Object</code>.
+ *
+ * @see javassist.CtBehavior#addParameter(javassist.CtClass)
+ * @see StackMapTable#typeTagOf(char)
+ * @see ConstPool
*/
- public static class Printer {
- /**
- * Prints a <code>StackMap</code> attribute.
- *
- * @param sm the <code>StackMap</code> attribute.
- * @param cp the <code>ConstPool</code> of the class file
- * containing the <code>StackMap</code> attribute.
- */
- public static void print(StackMap sm, java.io.PrintWriter out) {
- int num = sm.numOfEntries();
- out.println(num + " entries");
- byte[] srcInfo = sm.get();
- int pos = 2;
- for (int i = 0; i < num; i++) {
- int offset = ByteArray.readU16bit(srcInfo, pos);
- out.println(" * offset " + offset);
- pos += 2;
- int numLoc = ByteArray.readU16bit(srcInfo, pos);
- pos = printFrames(srcInfo, pos + 2, numLoc);
- int numStack = ByteArray.readU16bit(srcInfo, pos);
- pos = printFrames(srcInfo, pos + 2, numStack);
+ public void insertLocal(int index, int tag, int classInfo)
+ throws BadBytecode
+ {
+ byte[] data = new InsertLocal(this, index, tag, classInfo).doit();
+ this.set(data);
+ }
+
+ static class SimpleCopy extends Walker {
+ Writer writer;
+
+ SimpleCopy(StackMap map) {
+ super(map);
+ writer = new Writer();
+ }
+
+ byte[] doit() {
+ visit();
+ return writer.toByteArray();
+ }
+
+ public void visit() {
+ int num = ByteArray.readU16bit(info, 0);
+ writer.write16bit(num);
+ super.visit();
+ }
+
+ public int locals(int pos, int offset, int num) {
+ writer.write16bit(offset);
+ return super.locals(pos, offset, num);
+ }
+
+ public int typeInfoArray(int pos, int offset, int num, boolean isLocals) {
+ writer.write16bit(num);
+ return super.typeInfoArray(pos, offset, num, isLocals);
+ }
+
+ public void typeInfo(int pos, byte tag) {
+ writer.writeVerifyTypeInfo(tag, 0);
+ }
+
+ public void objectVariable(int pos, int clazz) {
+ writer.writeVerifyTypeInfo(OBJECT, clazz);
+ }
+
+ public void uninitialized(int pos, int offset) {
+ writer.writeVerifyTypeInfo(UNINIT, offset);
+ }
+ }
+
+ static class InsertLocal extends SimpleCopy {
+ private int varIndex;
+ private int varTag, varData;
+
+ InsertLocal(StackMap map, int varIndex, int varTag, int varData) {
+ super(map);
+ this.varIndex = varIndex;
+ this.varTag = varTag;
+ this.varData = varData;
+ }
+
+ public int typeInfoArray(int pos, int offset, int num, boolean isLocals) {
+ if (!isLocals || num < varIndex)
+ return super.typeInfoArray(pos, offset, num, isLocals);
+
+ writer.write16bit(num + 1);
+ for (int k = 0; k < num; k++) {
+ if (k == varIndex)
+ writeVarTypeInfo();
+
+ pos = typeInfoArray2(k, pos);
}
+
+ if (num == varIndex)
+ writeVarTypeInfo();
+
+ return pos;
+ }
+
+ private void writeVarTypeInfo() {
+ if (varTag == OBJECT)
+ writer.writeVerifyTypeInfo(OBJECT, varData);
+ else if (varTag == UNINIT)
+ writer.writeVerifyTypeInfo(UNINIT, varData);
+ else
+ writer.writeVerifyTypeInfo(varTag, 0);
+ }
+ }
+
+ void shiftPc(int where, int gapSize, boolean exclusive)
+ throws BadBytecode
+ {
+ new Shifter(this, where, gapSize, exclusive).visit();
+ }
+
+ static class Shifter extends Walker {
+ private int where, gap;
+ private boolean exclusive;
+
+ public Shifter(StackMap smt, int where, int gap, boolean exclusive) {
+ super(smt);
+ this.where = where;
+ this.gap = gap;
+ this.exclusive = exclusive;
}
- private static int printFrames(byte[] srcInfo, int pos,
- int numFrames) {
- for (int k = 0; k < numFrames; k++) {
- byte tag = srcInfo[pos];
+ public int locals(int pos, int offset, int num) {
+ if (exclusive ? where <= offset : where < offset)
+ ByteArray.write16bit(offset + gap, info, pos - 4);
+
+ return super.locals(pos, offset, num);
+ }
+ }
+
+ /**
+ * Undocumented method. Do not use; internal-use only.
+ *
+ * <p>This method is for javassist.convert.TransformNew.
+ * It is called to update the stack map when
+ * the NEW opcode (and the following DUP) is removed.
+ *
+ * @param where the position of the removed NEW opcode.
+ */
+ public void removeNew(int where) throws CannotCompileException {
+ byte[] data = new NewRemover(this, where).doit();
+ this.set(data);
+ }
+
+ static class NewRemover extends SimpleCopy {
+ int posOfNew;
+
+ NewRemover(StackMap map, int where) {
+ super(map);
+ posOfNew = where;
+ }
+
+ public int stack(int pos, int offset, int num) {
+ return stackTypeInfoArray(pos, offset, num);
+ }
+
+ private int stackTypeInfoArray(int pos, int offset, int num) {
+ int p = pos;
+ int count = 0;
+ for (int k = 0; k < num; k++) {
+ byte tag = info[p];
+ if (tag == OBJECT)
+ p += 3;
+ else if (tag == UNINIT) {
+ int offsetOfNew = ByteArray.readU16bit(info, p + 1);
+ if (offsetOfNew == posOfNew)
+ count++;
+
+ p += 3;
+ }
+ else
+ p++;
+ }
+
+ writer.write16bit(num - count);
+ for (int k = 0; k < num; k++) {
+ byte tag = info[pos];
if (tag == OBJECT) {
- ByteArray.readU16bit(srcInfo, pos + 1);
+ int clazz = ByteArray.readU16bit(info, pos + 1);
+ objectVariable(pos, clazz);
pos += 3;
}
else if (tag == UNINIT) {
- ByteArray.readU16bit(srcInfo, pos + 1);
+ int offsetOfNew = ByteArray.readU16bit(info, pos + 1);
+ if (offsetOfNew != posOfNew)
+ uninitialized(pos, offsetOfNew);
+
pos += 3;
}
- else
+ else {
+ typeInfo(pos, tag);
pos++;
+ }
}
return pos;
}
}
+ /**
+ * Prints this stack map.
+ */
+ public void print(java.io.PrintWriter out) {
+ new Printer(this, out).print();
+ }
+
+ static class Printer extends Walker {
+ private java.io.PrintWriter writer;
+
+ public Printer(StackMap map, java.io.PrintWriter out) {
+ super(map);
+ writer = out;
+ }
+
+ public void print() {
+ int num = ByteArray.readU16bit(info, 0);
+ writer.println(num + " entries");
+ visit();
+ }
+
+ public int locals(int pos, int offset, int num) {
+ writer.println(" * offset " + offset);
+ return super.locals(pos, offset, num);
+ }
+ }
+
/**
* Internal use only.
*/
output = new ByteArrayOutputStream();
}
+ /**
+ * Converts the written data into a byte array.
+ */
+ public byte[] toByteArray() {
+ return output.toByteArray();
+ }
+
/**
* Converts to a <code>StackMap</code> attribute.
*/