<p>-version 3.12
<ul>
- <li>JIRA JASSIST-89
+ <li>JIRA JASSIST-89, 95
</ul>
<p>-version 3.11 on July 3, 2009
attributes.add(smt);
}
+ /**
+ * Adds a stack map table for J2ME (CLDC). 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.
+ * @since 3.12
+ */
+ public void setAttribute(StackMap sm) {
+ AttributeInfo.remove(attributes, StackMapTable.tag);
+ if (sm != null)
+ attributes.add(sm);
+ }
+
/**
* Copies code.
*/
int descriptor;
LinkedList attribute; // may be null
+ /**
+ * If this value is true, Javassist maintains a <code>StackMap</code> attribute
+ * generated by the <code>preverify</code> tool of J2ME (CLDC). The initial
+ * value of this field is <code>false</code>.
+ */
+ public static boolean doPreverify = false;
+
/**
* The name of constructors: <code><init></code>.
*/
/**
* Rebuilds a stack map table if the class file is for Java 6
* or later. Java 5 or older Java VMs do not recognize a stack
- * map table.
+ * map table. If <code>doPreverify</code> is true, this method
+ * also rebuilds a stack map for J2ME (CLDC).
*
* @param pool used for making type hierarchy.
* @param cf rebuild if this class file is for Java 6 or later.
* @see #rebuildStackMap(ClassPool)
+ * @see #rebuildStackMapForME(ClassPool)
* @since 3.6
*/
public void rebuildStackMapIf6(ClassPool pool, ClassFile cf)
{
if (cf.getMajorVersion() >= ClassFile.JAVA_6)
rebuildStackMap(pool);
+
+ if (doPreverify)
+ rebuildStackMapForME(pool);
}
/**
}
}
+ /**
+ * Rebuilds a stack map table for J2ME (CLDC). If no stack map table is included,
+ * a new one is created. If this <code>MethodInfo</code> does not
+ * include a code attribute, nothing happens.
+ *
+ * @param pool used for making type hierarchy.
+ * @see StackMapTable
+ * @since 3.12
+ */
+ public void rebuildStackMapForME(ClassPool pool) throws BadBytecode {
+ CodeAttribute ca = getCodeAttribute();
+ if (ca != null) {
+ StackMap sm = MapMaker.make2(pool, this);
+ ca.setAttribute(sm);
+ }
+ }
+
/**
* Returns the line number of the source line corresponding to the specified
* bytecode contained in this method.
package javassist.bytecode;
-import java.io.DataInputStream;
import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
import java.io.IOException;
import java.util.Map;
-import javassist.bytecode.stackmap.*;
/**
* Another <code>stack_map</code> attribute defined in CLDC 1.1 for J2ME.
return pos;
}
- int i;
-
- static class NullType extends TypeData.BasicType {
- NullType() {
- super("null", StackMapTable.NULL);
- }
- }
-
- static final NullType nullType = new NullType();
+ /**
+ * Internal use only.
+ */
+ public static class Writer {
+ // see javassist.bytecode.stackmap.MapMaker
- static class Conv extends StackMapTable.Walker {
private ByteArrayOutputStream output;
- private ConstPool pool;
- private int offset;
- private TypedBlock frame;
- // more than one entries must be included!
-
- public Conv(byte[] data, MethodInfo minfo, CodeAttribute ca)
- throws BadBytecode {
- super(data);
+ /**
+ * Constructs a writer.
+ */
+ public Writer() {
output = new ByteArrayOutputStream();
- pool = minfo.getConstPool();
- offset = -1;
- frame = TypedBlock.make(minfo, ca);
- }
-
- private void write16bit(int value) {
- output.write((byte) (value >>> 8));
- output.write((byte) value);
- }
-
- private void writeFrame() {
- int numLocals = frame.numLocals;
- int numStack = frame.stackTop;
- }
-
- private void writeTypes(TypeData[] types, int num) {
- for (int i = 0; i < num; i++) {
- TypeData t = types[i];
- int tag = t.getTypeTag();
- if (t.is2WordType())
- i++;
-
- output.write(tag);
- if (tag == UNINIT) {
- write16bit(t.getTypeData(pool));
- }
- }
- }
-
- public void sameFrame(int pos, int offsetDelta) {
- offset += offsetDelta + 1;
- }
-
- public void sameLocals(int pos, int offsetDelta, int stackTag,
- int stackData) {
- offset += offsetDelta + 1;
}
- public void chopFrame(int pos, int offsetDelta, int k) {
- offset += offsetDelta + 1;
+ /**
+ * Converts to a <code>StackMap</code> attribute.
+ */
+ public StackMap toStackMap(ConstPool cp) {
+ return new StackMap(cp, output.toByteArray());
}
- public void appendFrame(int pos, int offsetDelta, int[] tags, int[] data) {
- offset += offsetDelta + 1;
+ /**
+ * Writes a <code>union verification_type_info</code> value.
+ *
+ * @param data <code>cpool_index</code> or <code>offset</code>.
+ */
+ public void writeVerifyTypeInfo(int tag, int data) {
+ output.write(tag);
+ if (tag == StackMap.OBJECT || tag == StackMap.UNINIT)
+ write16bit(data);
}
- public void fullFrame(int pos, int offsetDelta, int[] localTags,
- int[] localData, int[] stackTags, int[] stackData) {
- offset += offsetDelta + 1;
+ /**
+ * Writes a 16bit value.
+ */
+ public void write16bit(int value) {
+ output.write((value >>> 8) & 0xff);
+ output.write(value & 0xff);
}
}
}
return mm.toStackMap(blocks);
}
+ /**
+ * Computes the stack map table for J2ME.
+ * It returns null if the given method does not have to have a
+ * stack map table.
+ */
+ public static StackMap make2(ClassPool classes, MethodInfo minfo)
+ throws BadBytecode
+ {
+ CodeAttribute ca = minfo.getCodeAttribute();
+ if (ca == null)
+ return null;
+
+ TypedBlock[] blocks = TypedBlock.makeBlocks(minfo, ca, true);
+ if (blocks == null)
+ return null;
+
+ MapMaker mm = new MapMaker(classes, minfo, ca);
+ mm.make(blocks, ca.getCode());
+ return mm.toStackMap2(minfo.getConstPool(), blocks);
+ }
+
public MapMaker(ClassPool classes, MethodInfo minfo, CodeAttribute ca) {
super(classes, minfo.getConstPool(),
ca.getMaxStack(), ca.getMaxLocals(),
return num;
}
+
+ // Phase 3 for J2ME
+
+ public StackMap toStackMap2(ConstPool cp, TypedBlock[] blocks) {
+ StackMap.Writer writer = new StackMap.Writer();
+ int n = blocks.length;
+ int i;
+ if (blocks[0].incoming > 0) // the first instruction is a branch target.
+ i = 1;
+ else
+ i = 0;
+
+ writer.write16bit(n - i);
+ for (; i < n; i++)
+ writeStackFrame(writer, cp, blocks[i].position, blocks[i]);
+
+ return writer.toStackMap(cp);
+ }
+
+ private void writeStackFrame(StackMap.Writer writer, ConstPool cp, int offset, TypedBlock tb) {
+ writer.write16bit(offset);
+ writeVerifyTypeInfo(writer, cp, tb.localsTypes, tb.numLocals);
+ writeVerifyTypeInfo(writer, cp, tb.stackTypes, tb.stackTop);
+ }
+
+ private void writeVerifyTypeInfo(StackMap.Writer writer, ConstPool cp, TypeData[] types, int num) {
+ writer.write16bit(num);
+ for (int i = 0; i < num; i++) {
+ TypeData td = types[i];
+ if (td == TOP)
+ writer.writeVerifyTypeInfo(StackMap.TOP, 0);
+ else {
+ writer.writeVerifyTypeInfo(td.getTypeTag(), td.getTypeData(cp));
+ if (td.is2WordType())
+ i++;
+ }
+ }
+ }
}
* a stack element has a more specific type, this method updates the
* type of it.
*
- * @pos the position of the instruction.
+ * @param pos the position of the instruction.
* @return the size of the instruction at POS.
*/
protected int doOpcode(int pos, byte[] code) throws BadBytecode {
doALOAD(index);
break; }
case Opcode.ISTORE :
- return doWIDE_STORE(pos, code, INTEGER);
+ doWIDE_STORE(pos, code, INTEGER);
+ break;
case Opcode.LSTORE :
- return doWIDE_STORE(pos, code, LONG);
+ doWIDE_STORE(pos, code, LONG);
+ break;
case Opcode.FSTORE :
- return doWIDE_STORE(pos, code, FLOAT);
+ doWIDE_STORE(pos, code, FLOAT);
+ break;
case Opcode.DSTORE :
- return doWIDE_STORE(pos, code, DOUBLE);
+ doWIDE_STORE(pos, code, DOUBLE);
+ break;
case Opcode.ASTORE : {
int index = ByteArray.readU16bit(code, pos + 2);
- return doASTORE(index); }
+ doASTORE(index);
+ break; }
case Opcode.IINC :
// this does not call writeLocal().
return 6;
return 4;
}
- private int doWIDE_XLOAD(int pos, byte[] code, TypeData type) {
+ private void doWIDE_XLOAD(int pos, byte[] code, TypeData type) {
int index = ByteArray.readU16bit(code, pos + 2);
- return doXLOAD(index, type);
+ doXLOAD(index, type);
}
- private int doWIDE_STORE(int pos, byte[] code, TypeData type) {
+ private void doWIDE_STORE(int pos, byte[] code, TypeData type) {
int index = ByteArray.readU16bit(code, pos + 2);
- return doXSTORE(index, type);
+ doXSTORE(index, type);
}
private int doPutField(int pos, byte[] code, boolean notStatic) throws BadBytecode {
<p>If you modify a class file for the J2ME execution environment,
you must perform <it>preverification</it>. Preverifying is basically
-producing a stack map table, which was introduced into J2SE at JDK 1.6.
-However, Javassist does not automatically produce a stack map table
-for class files compiled by the J2ME compiler. You have to manually
-produce stack map tables for the modified methods.
+producing stack maps, which is similar to stack map tables introduced
+into J2SE at JDK 1.6. Javassist maintains the stack maps for J2ME only if
+<code>javassist.bytecode.MethodInfo.doPreverify</code> is true.
-<p>For a given method represented by a <code>CtMethod</code> object <code>m</code>,
-you can produce a stack map table by calling the following methods:
+<p>You can also manually
+produce a stack map for a modified method.
+For a given method represented by a <code>CtMethod</code> object <code>m</code>,
+you can produce a stack map by calling the following methods:
<ul><pre>
-m.getMethodInfo().rebuildStackMap(cpool);
+m.getMethodInfo().rebuildStackMapForME(cpool);
</pre></ul>
<p>Here, <code>cpool</code> is a <code>ClassPool</code> object, which is
available by calling <code>getClassPool()</code> on a <code>CtClass</code>
object. A <code>ClassPool</code> object is responsible for finding
-class files from given class pathes.
+class files from given class pathes. To obtain all the <code>CtMethod</code>
+objects, call the <code>getDeclaredMethods</code> method on a <code>CtClass</code> object.
<p><br>