]> source.dussan.org Git - javassist.git/commitdiff
for implementing StackMap attributes (JIRA 95)
authorchiba <chiba@30ef5769-5b8d-40dd-aea6-55b5d6557bb3>
Thu, 15 Oct 2009 10:32:49 +0000 (10:32 +0000)
committerchiba <chiba@30ef5769-5b8d-40dd-aea6-55b5d6557bb3>
Thu, 15 Oct 2009 10:32:49 +0000 (10:32 +0000)
git-svn-id: http://anonsvn.jboss.org/repos/javassist/trunk@495 30ef5769-5b8d-40dd-aea6-55b5d6557bb3

Readme.html
src/main/javassist/bytecode/CodeAttribute.java
src/main/javassist/bytecode/MethodInfo.java
src/main/javassist/bytecode/StackMap.java
src/main/javassist/bytecode/stackmap/MapMaker.java
src/main/javassist/bytecode/stackmap/Tracer.java
tutorial/tutorial3.html

index 2bf04613def6a31c43d754d0e5b8990fbdb5299c..3b839644582c4dd01b0af45b4cd477056c27c828 100644 (file)
@@ -284,7 +284,7 @@ see javassist.Dump.
 <p>-version 3.12
 
 <ul>
-       <li>JIRA JASSIST-89
+       <li>JIRA JASSIST-89, 95
 </ul>
 
 <p>-version 3.11 on July 3, 2009
index 0f81ae59833988e775d315fe2732ba9382068bbf..32f6946778514e1ed9abcd2d6654844571627551 100644 (file)
@@ -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.
      */
index eae31e108964193a32f0ab55dbc0e216358c4bde..6d819371e3d56237d231250dc767832715514f7f 100644 (file)
@@ -38,6 +38,13 @@ public final class MethodInfo {
     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>&lt;init&gt</code>.
      */
@@ -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 <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)
@@ -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 <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.
index b5748d90f26ccb2763a725155c237bbdf45f55a8..caef3d80a3ed75babcb4d7ebb1fe2ede31bc96fe 100644 (file)
 
 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.
@@ -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 <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);
         }
     }
 }
index c96073f889f6bf18b6e53f0fb5c645756cb643e1..df3300cc93959e61a7c880219f120ae4f377bec9 100644 (file)
@@ -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++;
+            }
+        }
+    }
 }
index 993ab050b654ac9a0b66f2fbdf819fb14565e23f..77f56b64982078bae29b3cbf11ae8005a3431060 100644 (file)
@@ -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 {
index 4d7d2e279078b141e6e4f994f13c76735f36c2bb..d72a26c781e809cd63ac544b8735bbab637ce578 100644 (file)
@@ -291,22 +291,24 @@ public Object get() { return value; }
 
 <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>