]> source.dussan.org Git - javassist.git/commitdiff
stackmap support (cont.)
authorchiba <chiba@30ef5769-5b8d-40dd-aea6-55b5d6557bb3>
Tue, 10 Apr 2007 16:32:32 +0000 (16:32 +0000)
committerchiba <chiba@30ef5769-5b8d-40dd-aea6-55b5d6557bb3>
Tue, 10 Apr 2007 16:32:32 +0000 (16:32 +0000)
git-svn-id: http://anonsvn.jboss.org/repos/javassist/trunk@359 30ef5769-5b8d-40dd-aea6-55b5d6557bb3

src/main/javassist/bytecode/CodeAttribute.java
src/main/javassist/bytecode/StackMapTable.java
src/main/javassist/bytecode/stackmap/BasicBlock.java
src/main/javassist/bytecode/stackmap/MapMaker.java [new file with mode: 0644]
src/main/javassist/bytecode/stackmap/StackAnalyzer.java [deleted file]
src/main/javassist/bytecode/stackmap/StackAnalyzerCore.java [deleted file]
src/main/javassist/bytecode/stackmap/Tracer.java [new file with mode: 0644]
src/main/javassist/bytecode/stackmap/TypeData.java [new file with mode: 0644]
src/main/javassist/bytecode/stackmap/TypeTag.java [new file with mode: 0644]

index 83a0a4296af4fca4a18f0f4afdb0c842cc1b1102..91f634b3d75546bd89be4d5bf9e87ca2c7c63dbc 100644 (file)
@@ -292,6 +292,20 @@ public class CodeAttribute extends AttributeInfo implements Opcode {
         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.
      */
index cb59efa2861f5a63e4548c3ea01ece32345fa597..7b4b9bf32ba240453b36fe0426c42d1c900ad60c 100644 (file)
@@ -41,7 +41,7 @@ public class StackMapTable extends AttributeInfo {
     /**
      * Constructs a <code>stack_map</code> attribute.
      */
-    private StackMapTable(ConstPool cp, byte[] newInfo) {
+    StackMapTable(ConstPool cp, byte[] newInfo) {
         super(cp, tag, newInfo);
     }
 
@@ -143,6 +143,9 @@ public class StackMapTable extends AttributeInfo {
 
         /**
          * Constructs a walker.
+         *
+         * @param smt       the StackMapTable that this walker
+         *                  walks around.
          */
         public Walker(StackMapTable smt) {
             this(smt.get());
@@ -156,7 +159,7 @@ public class StackMapTable extends AttributeInfo {
          *                  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);
         }
@@ -447,6 +450,17 @@ public class StackMapTable extends AttributeInfo {
             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>.
          */
@@ -559,6 +573,22 @@ public class StackMapTable extends AttributeInfo {
         }
     }
 
+    /**
+     * 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;
 
index 4f0111bfb1787240f22f3261a02f2dd7ec3fa308..dbce4e6cd133d00d0c56cf59fb7982316b29e436 100644 (file)
+/*
+ * 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++)
@@ -59,102 +117,254 @@ public class BasicBlock {
         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";
+    }
 }
diff --git a/src/main/javassist/bytecode/stackmap/MapMaker.java b/src/main/javassist/bytecode/stackmap/MapMaker.java
new file mode 100644 (file)
index 0000000..c7e92c0
--- /dev/null
@@ -0,0 +1,433 @@
+/*
+ * 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; }
+}
diff --git a/src/main/javassist/bytecode/stackmap/StackAnalyzer.java b/src/main/javassist/bytecode/stackmap/StackAnalyzer.java
deleted file mode 100644 (file)
index 264ffe8..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-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);
-    }
-}
diff --git a/src/main/javassist/bytecode/stackmap/StackAnalyzerCore.java b/src/main/javassist/bytecode/stackmap/StackAnalyzerCore.java
deleted file mode 100644 (file)
index c3c79c8..0000000
+++ /dev/null
@@ -1,846 +0,0 @@
-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++;
-    }
-}
diff --git a/src/main/javassist/bytecode/stackmap/Tracer.java b/src/main/javassist/bytecode/stackmap/Tracer.java
new file mode 100644 (file)
index 0000000..16eabb8
--- /dev/null
@@ -0,0 +1,890 @@
+/*
+ * 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);
+    }
+}
diff --git a/src/main/javassist/bytecode/stackmap/TypeData.java b/src/main/javassist/bytecode/stackmap/TypeData.java
new file mode 100644 (file)
index 0000000..dbe95c9
--- /dev/null
@@ -0,0 +1,467 @@
+/*
+ * 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"; }
+    }
+}
diff --git a/src/main/javassist/bytecode/stackmap/TypeTag.java b/src/main/javassist/bytecode/stackmap/TypeTag.java
new file mode 100644 (file)
index 0000000..85dd2b1
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * 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
+}