From 7cf3a5fce3acfd198867eb0f0aced97d40d83ecb Mon Sep 17 00:00:00 2001 From: chiba Date: Thu, 15 Oct 2009 10:32:49 +0000 Subject: [PATCH] for implementing StackMap attributes (JIRA 95) git-svn-id: http://anonsvn.jboss.org/repos/javassist/trunk@495 30ef5769-5b8d-40dd-aea6-55b5d6557bb3 --- Readme.html | 2 +- .../javassist/bytecode/CodeAttribute.java | 15 +++ src/main/javassist/bytecode/MethodInfo.java | 31 ++++++- src/main/javassist/bytecode/StackMap.java | 93 ++++++------------- .../javassist/bytecode/stackmap/MapMaker.java | 59 ++++++++++++ .../javassist/bytecode/stackmap/Tracer.java | 25 +++-- tutorial/tutorial3.html | 18 ++-- 7 files changed, 160 insertions(+), 83 deletions(-) diff --git a/Readme.html b/Readme.html index 2bf04613..3b839644 100644 --- a/Readme.html +++ b/Readme.html @@ -284,7 +284,7 @@ see javassist.Dump.

-version 3.12

-version 3.11 on July 3, 2009 diff --git a/src/main/javassist/bytecode/CodeAttribute.java b/src/main/javassist/bytecode/CodeAttribute.java index 0f81ae59..32f69467 100644 --- a/src/main/javassist/bytecode/CodeAttribute.java +++ b/src/main/javassist/bytecode/CodeAttribute.java @@ -315,6 +315,21 @@ public class CodeAttribute extends AttributeInfo implements Opcode { 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. */ diff --git a/src/main/javassist/bytecode/MethodInfo.java b/src/main/javassist/bytecode/MethodInfo.java index eae31e10..6d819371 100644 --- a/src/main/javassist/bytecode/MethodInfo.java +++ b/src/main/javassist/bytecode/MethodInfo.java @@ -38,6 +38,13 @@ public final class MethodInfo { int descriptor; LinkedList attribute; // may be null + /** + * If this value is true, Javassist maintains a StackMap attribute + * generated by the preverify tool of J2ME (CLDC). The initial + * value of this field is false. + */ + public static boolean doPreverify = false; + /** * The name of constructors: <init>. */ @@ -375,11 +382,13 @@ public final class MethodInfo { /** * 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 doPreverify 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) @@ -387,6 +396,9 @@ public final class MethodInfo { { if (cf.getMajorVersion() >= ClassFile.JAVA_6) rebuildStackMap(pool); + + if (doPreverify) + rebuildStackMapForME(pool); } /** @@ -406,6 +418,23 @@ public final class MethodInfo { } } + /** + * Rebuilds a stack map table for J2ME (CLDC). If no stack map table is included, + * a new one is created. If this MethodInfo 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. diff --git a/src/main/javassist/bytecode/StackMap.java b/src/main/javassist/bytecode/StackMap.java index b5748d90..caef3d80 100644 --- a/src/main/javassist/bytecode/StackMap.java +++ b/src/main/javassist/bytecode/StackMap.java @@ -15,11 +15,10 @@ 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 stack_map attribute defined in CLDC 1.1 for J2ME. @@ -157,77 +156,45 @@ public class StackMap extends AttributeInfo { 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 StackMap 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 union verification_type_info value. + * + * @param data cpool_index or offset. + */ + 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); } } } diff --git a/src/main/javassist/bytecode/stackmap/MapMaker.java b/src/main/javassist/bytecode/stackmap/MapMaker.java index c96073f8..df3300cc 100644 --- a/src/main/javassist/bytecode/stackmap/MapMaker.java +++ b/src/main/javassist/bytecode/stackmap/MapMaker.java @@ -97,6 +97,27 @@ public class MapMaker extends Tracer { 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(), @@ -441,4 +462,42 @@ public class MapMaker extends Tracer { 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++; + } + } + } } diff --git a/src/main/javassist/bytecode/stackmap/Tracer.java b/src/main/javassist/bytecode/stackmap/Tracer.java index 993ab050..77f56b64 100644 --- a/src/main/javassist/bytecode/stackmap/Tracer.java +++ b/src/main/javassist/bytecode/stackmap/Tracer.java @@ -84,7 +84,7 @@ public abstract class Tracer implements TypeTag { * 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 { @@ -699,16 +699,21 @@ public abstract class Tracer implements TypeTag { 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; @@ -722,14 +727,14 @@ public abstract class Tracer implements TypeTag { 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 { diff --git a/tutorial/tutorial3.html b/tutorial/tutorial3.html index 4d7d2e27..d72a26c7 100644 --- a/tutorial/tutorial3.html +++ b/tutorial/tutorial3.html @@ -291,22 +291,24 @@ public Object get() { return value; }

If you modify a class file for the J2ME execution environment, you must perform preverification. 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 +javassist.bytecode.MethodInfo.doPreverify is true. -

For a given method represented by a CtMethod object m, -you can produce a stack map table by calling the following methods: +

You can also manually +produce a stack map for a modified method. +For a given method represented by a CtMethod object m, +you can produce a stack map by calling the following methods:

Here, cpool is a ClassPool object, which is available by calling getClassPool() on a CtClass object. A ClassPool object is responsible for finding -class files from given class pathes. +class files from given class pathes. To obtain all the CtMethod +objects, call the getDeclaredMethods method on a CtClass object.


-- 2.39.5