[jboss-cvs] javassist SVN: r655 - in trunk: src/main/javassist and 5 other directories.

jboss-cvs-commits at lists.jboss.org jboss-cvs-commits at lists.jboss.org
Fri Sep 14 11:22:16 EDT 2012


Author: chiba
Date: 2012-09-14 11:22:13 -0400 (Fri, 14 Sep 2012)
New Revision: 655

Added:
   trunk/src/test/javassist/bytecode/StackMapTest.java
Removed:
   trunk/src/main/javassist/bytecode/stackmap/Liveness.java
Modified:
   trunk/.classpath
   trunk/javassist.jar
   trunk/src/main/javassist/CodeConverter.java
   trunk/src/main/javassist/CtNewMethod.java
   trunk/src/main/javassist/CtNewWrappedConstructor.java
   trunk/src/main/javassist/CtNewWrappedMethod.java
   trunk/src/main/javassist/bytecode/ConstPool.java
   trunk/src/main/javassist/bytecode/StackMapTable.java
   trunk/src/main/javassist/bytecode/stackmap/BasicBlock.java
   trunk/src/main/javassist/bytecode/stackmap/MapMaker.java
   trunk/src/main/javassist/bytecode/stackmap/Tracer.java
   trunk/src/main/javassist/bytecode/stackmap/TypeData.java
   trunk/src/main/javassist/bytecode/stackmap/TypeTag.java
   trunk/src/main/javassist/bytecode/stackmap/TypedBlock.java
   trunk/src/test/javassist/JvstTest.java
   trunk/src/test/javassist/JvstTest4.java
   trunk/src/test/javassist/tools/reflect/LoaderTest.java
Log:
fixed JASSIST-160 by rewriting the whole javassist.bytecode.stackmap package.

Modified: trunk/.classpath
===================================================================
--- trunk/.classpath	2012-09-14 12:06:49 UTC (rev 654)
+++ trunk/.classpath	2012-09-14 15:22:13 UTC (rev 655)
@@ -1,8 +1,9 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <classpath>
-	<classpathentry excluding="javassist/util/HotSwapper.java" kind="src" path="src/main"/>
-	<classpathentry kind="src" path="src/test"/>
+	<classpathentry excluding=".svn" kind="src" path="src/main"/>
+	<classpathentry excluding=".svn" kind="src" path="src/test"/>
 	<classpathentry kind="lib" path="lib/junit.jar"/>
 	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+	<classpathentry kind="lib" path="/System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Classes/classes.jar"/>
 	<classpathentry kind="output" path="eclipse-output/classes"/>
 </classpath>

Modified: trunk/javassist.jar
===================================================================
(Binary files differ)

Modified: trunk/src/main/javassist/CodeConverter.java
===================================================================
--- trunk/src/main/javassist/CodeConverter.java	2012-09-14 12:06:49 UTC (rev 654)
+++ trunk/src/main/javassist/CodeConverter.java	2012-09-14 15:22:13 UTC (rev 655)
@@ -532,6 +532,14 @@
 
         if (stack > 0)
             codeAttr.setMaxStack(codeAttr.getMaxStack() + stack);
+
+        try {
+        	minfo.rebuildStackMapIf6(clazz.getClassPool(),
+                                     clazz.getClassFile2());
+        }
+        catch (BadBytecode b) {
+            throw new CannotCompileException(b.getMessage(), b);
+        }
     }
 
     /**

Modified: trunk/src/main/javassist/CtNewMethod.java
===================================================================
--- trunk/src/main/javassist/CtNewMethod.java	2012-09-14 12:06:49 UTC (rev 654)
+++ trunk/src/main/javassist/CtNewMethod.java	2012-09-14 15:22:13 UTC (rev 655)
@@ -246,7 +246,9 @@
         }
 
         minfo.setCodeAttribute(code.toCodeAttribute());
-        return new CtMethod(minfo, field.getDeclaringClass());
+        CtClass cc = field.getDeclaringClass();
+        // a stack map is not needed.
+        return new CtMethod(minfo, cc);
     }
 
     /**
@@ -290,7 +292,9 @@
         }
 
         minfo.setCodeAttribute(code.toCodeAttribute());
-        return new CtMethod(minfo, field.getDeclaringClass());
+        CtClass cc = field.getDeclaringClass();
+        // a stack map is not needed.
+        return new CtMethod(minfo, cc);
     }
 
     /**
@@ -359,6 +363,7 @@
         code.setMaxLocals(++s);
         code.setMaxStack(s < 2 ? 2 : s); // for a 2-word return value
         minfo.setCodeAttribute(code.toCodeAttribute());
+        // a stack map is not needed. 
         return new CtMethod(minfo, declaring);
     }
 

Modified: trunk/src/main/javassist/CtNewWrappedConstructor.java
===================================================================
--- trunk/src/main/javassist/CtNewWrappedConstructor.java	2012-09-14 12:06:49 UTC (rev 654)
+++ trunk/src/main/javassist/CtNewWrappedConstructor.java	2012-09-14 15:22:13 UTC (rev 655)
@@ -39,6 +39,7 @@
                                      howToCallSuper, body,
                                      parameterTypes, constParam);
             cons.getMethodInfo2().setCodeAttribute(code.toCodeAttribute());
+            // a stack map table is not needed.
             return cons;
         }
         catch (NotFoundException e) {

Modified: trunk/src/main/javassist/CtNewWrappedMethod.java
===================================================================
--- trunk/src/main/javassist/CtNewWrappedMethod.java	2012-09-14 12:06:49 UTC (rev 654)
+++ trunk/src/main/javassist/CtNewWrappedMethod.java	2012-09-14 15:22:13 UTC (rev 655)
@@ -44,7 +44,9 @@
 
         Bytecode code = makeBody(declaring, declaring.getClassFile2(), body,
                                  parameterTypes, returnType, constParam);
-        mt.getMethodInfo2().setCodeAttribute(code.toCodeAttribute());
+        MethodInfo minfo = mt.getMethodInfo2();
+        minfo.setCodeAttribute(code.toCodeAttribute());
+        // a stack map has been already created. 
         return mt;
     }
 

Modified: trunk/src/main/javassist/bytecode/ConstPool.java
===================================================================
--- trunk/src/main/javassist/bytecode/ConstPool.java	2012-09-14 12:06:49 UTC (rev 654)
+++ trunk/src/main/javassist/bytecode/ConstPool.java	2012-09-14 15:22:13 UTC (rev 655)
@@ -174,7 +174,7 @@
      * @return  a fully-qualified class or interface name specified
      *          by <code>name_index</code>.  If the type is an array
      *          type, this method returns an encoded name like
-     *          <code>[java.lang.Object;</code> (note that the separators
+     *          <code>[Ljava.lang.Object;</code> (note that the separators
      *          are not slashes but dots).
      * @see javassist.ClassPool#getCtClass(String)
      */

Modified: trunk/src/main/javassist/bytecode/StackMapTable.java
===================================================================
--- trunk/src/main/javassist/bytecode/StackMapTable.java	2012-09-14 12:06:49 UTC (rev 654)
+++ trunk/src/main/javassist/bytecode/StackMapTable.java	2012-09-14 15:22:13 UTC (rev 655)
@@ -67,7 +67,7 @@
     {
         try {
             return new StackMapTable(newCp,
-                            new Copier(this.constPool, info, newCp).doit());
+                            new Copier(this.constPool, info, newCp, classnames).doit());
         }
         catch (BadBytecode e) {
             throw new RuntimeCopyException("bad bytecode. fatal?"); 
@@ -400,16 +400,18 @@
 
     static class Copier extends SimpleCopy {
         private ConstPool srcPool, destPool;
+        private Map classnames;
 
-        public Copier(ConstPool src, byte[] data, ConstPool dest) {
+        public Copier(ConstPool src, byte[] data, ConstPool dest, Map names) {
             super(data);
             srcPool = src;
             destPool = dest;
+            classnames = names;
         }
 
         protected int copyData(int tag, int data) {
             if (tag == OBJECT)
-                return srcPool.copy(data, destPool, null); 
+                return srcPool.copy(data, destPool, classnames); 
             else
                 return data;
         }
@@ -418,7 +420,7 @@
             int[] newData = new int[data.length];
             for (int i = 0; i < data.length; i++)
                 if (tags[i] == OBJECT)
-                    newData[i] = srcPool.copy(data[i], destPool, null);
+                    newData[i] = srcPool.copy(data[i], destPool, classnames);
                 else
                     newData[i] = data[i];
 

Modified: trunk/src/main/javassist/bytecode/stackmap/BasicBlock.java
===================================================================
--- trunk/src/main/javassist/bytecode/stackmap/BasicBlock.java	2012-09-14 12:06:49 UTC (rev 654)
+++ trunk/src/main/javassist/bytecode/stackmap/BasicBlock.java	2012-09-14 15:22:13 UTC (rev 655)
@@ -274,7 +274,7 @@
                 else if (op == Opcode.JSR_W)
                     makeJsr(marks, index, index + ci.s32bitAt(index + 1), 5);
                 else if (op == Opcode.WIDE && ci.byteAt(index + 1) == Opcode.RET)
-                    makeMark(marks, index, null, 1, true);
+                    makeMark(marks, index, null, 4, true);
             }
 
             if (et != null) {

Deleted: trunk/src/main/javassist/bytecode/stackmap/Liveness.java
===================================================================
--- trunk/src/main/javassist/bytecode/stackmap/Liveness.java	2012-09-14 12:06:49 UTC (rev 654)
+++ trunk/src/main/javassist/bytecode/stackmap/Liveness.java	2012-09-14 15:22:13 UTC (rev 655)
@@ -1,366 +0,0 @@
-/*
- * Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999- 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,
- * or the Apache License Version 2.0.
- *
- * 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.*;
-
-public class Liveness {
-    protected static final byte UNKNOWN = 0;
-    protected static final byte READ = 1;
-    protected static final byte UPDATED = 2;
-    protected byte[] localsUsage;
-
-    /**
-     * If true, all the arguments become alive within the whole method body.
-     *
-     * To correctly compute a stack map table, all the arguments must
-     * be alive (localsUsage[?] must be READ) at least in the first block.
-     */
-    public static boolean useArgs = true;
-
-    public void compute(CodeIterator ci, TypedBlock[] blocks, int maxLocals,
-                        TypeData[] args)
-        throws BadBytecode
-    {
-        computeUsage(ci, blocks, maxLocals);
-        if (useArgs)
-            useAllArgs(blocks, args);
-
-        computeLiveness1(blocks[0]);
-        while (hasChanged(blocks))
-            computeLiveness2(blocks[0]);
-    }
-
-    private void useAllArgs(TypedBlock[] blocks, TypeData[] args) {
-        for (int k = 0; k < blocks.length; k++) {
-            byte[] usage = blocks[k].localsUsage;
-            for (int i = 0; i < args.length; i++)
-                if (args[i] != TypeTag.TOP)
-                    usage[i] = READ;
-        }
-    }
-
-    static final int NOT_YET = 0;
-    static final int CHANGED_LAST = 1;
-    static final int DONE = 2;
-    static final int CHANGED_NOW = 3;
-
-    private void computeLiveness1(TypedBlock tb) {
-        if (tb.updating) {
-            // a loop was detected.
-            computeLiveness1u(tb);
-            return;
-        }
-
-        if (tb.inputs != null)
-            return;
-
-        tb.updating = true;
-        byte[] usage = tb.localsUsage;
-        int n = usage.length;
-        boolean[] in = new boolean[n];
-        for (int i = 0; i < n; i++)
-            in[i] = usage[i] == READ;
-
-        BasicBlock.Catch handlers = tb.toCatch;
-        while (handlers != null) {
-            TypedBlock h = (TypedBlock)handlers.body;
-            computeLiveness1(h);
-            for (int k = 0; k < n; k++)
-                if (h.inputs[k])
-                    in[k] = true;
-
-            handlers = handlers.next;
-        }
-
-        if (tb.exit != null) {
-            for (int i = 0; i < tb.exit.length; i++) {
-                TypedBlock e = (TypedBlock)tb.exit[i];
-                computeLiveness1(e);
-                for (int k = 0; k < n; k++)
-                    if (!in[k])
-                        in[k] = usage[k] == UNKNOWN && e.inputs[k];
-            }
-        }
-
-        tb.updating = false;
-        if (tb.inputs == null) {
-            tb.inputs = in;
-            tb.status = DONE;
-        }
-        else {
-            for (int i = 0; i < n; i++)
-                if (in[i] && !tb.inputs[i]) {
-                    tb.inputs[i] = true; 
-                    tb.status = CHANGED_NOW;
-                }
-        }
-    }
-
-    private void computeLiveness1u(TypedBlock tb) {
-        if (tb.inputs == null) {
-            byte[] usage = tb.localsUsage;
-            int n = usage.length;
-            boolean[] in = new boolean[n];
-            for (int i = 0; i < n; i++)
-                in[i] = usage[i] == READ;
-
-            tb.inputs = in;
-            tb.status = DONE;
-        }
-    }
-
-    private void computeLiveness2(TypedBlock tb) {
-        if (tb.updating || tb.status >= DONE)
-            return;
-
-        tb.updating = true;
-        if (tb.exit == null)
-            tb.status = DONE;
-        else {
-            boolean changed = false;
-            for (int i = 0; i < tb.exit.length; i++) {
-                TypedBlock e = (TypedBlock)tb.exit[i];
-                computeLiveness2(e);
-                if (e.status != DONE)
-                    changed = true;
-            }
-
-            if (changed) {
-                changed = false;
-                byte[] usage = tb.localsUsage;
-                int n = usage.length;
-                for (int i = 0; i < tb.exit.length; i++) {
-                    TypedBlock e = (TypedBlock)tb.exit[i];
-                    if (e.status != DONE)
-                        for (int k = 0; k < n; k++)
-                            if (!tb.inputs[k]) {
-                                if (usage[k] == UNKNOWN && e.inputs[k]) {
-                                    tb.inputs[k] = true;
-                                    changed = true;
-                                }
-                            }
-                }
-
-                tb.status = changed ? CHANGED_NOW : DONE;
-            }
-            else
-                tb.status = DONE;
-        }
-
-        if (computeLiveness2except(tb))
-            tb.status = CHANGED_NOW;
-
-        tb.updating = false;
-    }
-
-    private boolean computeLiveness2except(TypedBlock tb) {
-        BasicBlock.Catch handlers = tb.toCatch;
-        boolean changed = false;
-        while (handlers != null) {
-            TypedBlock h = (TypedBlock)handlers.body;
-            computeLiveness2(h);
-            if (h.status != DONE) {
-                boolean[] in = tb.inputs;
-                int n = in.length;
-                for (int k = 0; k < n; k++)
-                    if (!in[k] && h.inputs[k]) {
-                        in[k] = true;
-                        changed = true;
-                    }
-            }
-
-            handlers = handlers.next;
-        }
-
-        return changed;
-    }
-
-    private boolean hasChanged(TypedBlock[] blocks) {
-        int n = blocks.length;
-        boolean changed = false;
-        for (int i = 0; i < n; i++) {
-            TypedBlock tb = blocks[i];
-            if (tb.status == CHANGED_NOW) {
-                tb.status = CHANGED_LAST;
-                changed = true;
-            }
-            else
-                tb.status = NOT_YET;
-        }
-
-        return changed;
-    }
-
-    private void computeUsage(CodeIterator ci, TypedBlock[] blocks, int maxLocals)
-        throws BadBytecode
-    {
-        int n = blocks.length;
-        for (int i = 0; i < n; i++) {
-            TypedBlock tb = blocks[i];
-            localsUsage = tb.localsUsage = new byte[maxLocals];
-            int pos = tb.position;
-            analyze(ci, pos, pos + tb.length);
-            localsUsage = null;
-        }
-    }
-
-    protected final void readLocal(int reg) {
-        if (localsUsage[reg] == UNKNOWN)
-            localsUsage[reg] = READ;
-    }
-
-    protected final void writeLocal(int reg) {
-        if (localsUsage[reg] == UNKNOWN)
-            localsUsage[reg] = UPDATED;
-    }
-
-    protected void analyze(CodeIterator ci, int begin, int end)
-        throws BadBytecode
-    {
-        ci.begin();
-        ci.move(begin);
-        while (ci.hasNext()) {
-            int index = ci.next();
-            if (index >= end)
-                break;
-
-            int op = ci.byteAt(index);
-            if (op < 96)
-                if (op < 54)
-                    doOpcode0_53(ci, index, op);
-                else
-                    doOpcode54_95(ci, index, op);
-            else
-                if (op == Opcode.IINC) {
-                    // this does not call writeLocal().
-                    readLocal(ci.byteAt(index + 1));
-                }
-                else if (op == Opcode.WIDE)
-                    doWIDE(ci, index);
-        }
-    }
-
-    private void doOpcode0_53(CodeIterator ci, int pos, int op) {
-        switch (op) {
-        case Opcode.ILOAD :
-        case Opcode.LLOAD :
-        case Opcode.FLOAD :
-        case Opcode.DLOAD :
-        case Opcode.ALOAD :
-            readLocal(ci.byteAt(pos + 1));
-            break;
-        case Opcode.ILOAD_0 :
-        case Opcode.ILOAD_1 :
-        case Opcode.ILOAD_2 :
-        case Opcode.ILOAD_3 :
-            readLocal(op - Opcode.ILOAD_0);
-            break;
-        case Opcode.LLOAD_0 :
-        case Opcode.LLOAD_1 :
-        case Opcode.LLOAD_2 :
-        case Opcode.LLOAD_3 :
-            readLocal(op - Opcode.LLOAD_0);
-            break;
-        case Opcode.FLOAD_0 :
-        case Opcode.FLOAD_1 :
-        case Opcode.FLOAD_2 :
-        case Opcode.FLOAD_3 :
-            readLocal(op - Opcode.FLOAD_0);
-            break;
-        case Opcode.DLOAD_0 :
-        case Opcode.DLOAD_1 :
-        case Opcode.DLOAD_2 :
-        case Opcode.DLOAD_3 :
-            readLocal(op - Opcode.DLOAD_0);
-            break;
-        case Opcode.ALOAD_0 :
-        case Opcode.ALOAD_1 :
-        case Opcode.ALOAD_2 :
-        case Opcode.ALOAD_3 :
-            readLocal(op - Opcode.ALOAD_0);
-            break;
-        }
-    }
-
-    private void doOpcode54_95(CodeIterator ci, int pos, int op) {
-        switch (op) {
-        case Opcode.ISTORE :
-        case Opcode.LSTORE :
-        case Opcode.FSTORE :
-        case Opcode.DSTORE :
-        case Opcode.ASTORE :
-            writeLocal(ci.byteAt(pos + 1));
-            break;
-        case Opcode.ISTORE_0 :
-        case Opcode.ISTORE_1 :
-        case Opcode.ISTORE_2 :
-        case Opcode.ISTORE_3 :
-            writeLocal(op - Opcode.ISTORE_0);
-            break;
-        case Opcode.LSTORE_0 :
-        case Opcode.LSTORE_1 :
-        case Opcode.LSTORE_2 :
-        case Opcode.LSTORE_3 :
-            writeLocal(op - Opcode.LSTORE_0);
-            break;
-        case Opcode.FSTORE_0 :
-        case Opcode.FSTORE_1 :
-        case Opcode.FSTORE_2 :
-        case Opcode.FSTORE_3 :
-            writeLocal(op - Opcode.FSTORE_0);
-            break;
-        case Opcode.DSTORE_0 :
-        case Opcode.DSTORE_1 :
-        case Opcode.DSTORE_2 :
-        case Opcode.DSTORE_3 :
-            writeLocal(op - Opcode.DSTORE_0);
-            break;
-        case Opcode.ASTORE_0 :
-        case Opcode.ASTORE_1 :
-        case Opcode.ASTORE_2 :
-        case Opcode.ASTORE_3 :
-            writeLocal(op - Opcode.ASTORE_0);
-            break;
-        }
-    }
-
-    private void doWIDE(CodeIterator ci, int pos) throws BadBytecode {
-        int op = ci.byteAt(pos + 1);
-        int var = ci.u16bitAt(pos + 2);
-        switch (op) {
-        case Opcode.ILOAD :
-        case Opcode.LLOAD :
-        case Opcode.FLOAD :
-        case Opcode.DLOAD :
-        case Opcode.ALOAD :
-            readLocal(var);
-            break;
-        case Opcode.ISTORE :
-        case Opcode.LSTORE :
-        case Opcode.FSTORE :
-        case Opcode.DSTORE :
-        case Opcode.ASTORE :
-            writeLocal(var);
-            break;
-        case Opcode.IINC :
-            readLocal(var);
-            // this does not call writeLocal().
-            break;
-        }
-    }
-}

Modified: trunk/src/main/javassist/bytecode/stackmap/MapMaker.java
===================================================================
--- trunk/src/main/javassist/bytecode/stackmap/MapMaker.java	2012-09-14 12:06:49 UTC (rev 654)
+++ trunk/src/main/javassist/bytecode/stackmap/MapMaker.java	2012-09-14 15:22:13 UTC (rev 655)
@@ -16,7 +16,9 @@
 
 package javassist.bytecode.stackmap;
 
+import java.util.ArrayList;
 import javassist.ClassPool;
+import javassist.NotFoundException;
 import javassist.bytecode.*;
 
 /**
@@ -125,9 +127,7 @@
               TypedBlock.getRetType(minfo.getDescriptor()));
     }
 
-    protected MapMaker(MapMaker old, boolean copyStack) {
-        super(old, copyStack);
-    }
+    protected MapMaker(MapMaker old) { super(old); }
 
     /**
      * Runs an analyzer (Phase 1 and 2).
@@ -135,34 +135,11 @@
     void make(TypedBlock[] blocks, byte[] code)
         throws BadBytecode
     {
-        TypedBlock first = blocks[0];
-        fixParamTypes(first);
-        TypeData[] srcTypes = first.localsTypes;
-        copyFrom(srcTypes.length, srcTypes, this.localsTypes);
-        make(code, first);
-
-        int n = blocks.length;
-        for (int i = 0; i < n; i++)
-            evalExpected(blocks[i]);
-    }
-
-    /*
-     * If a parameter type is String but it is used only as Object
-     * within the method body, this MapMaker class will report its type
-     * is Object.  To avoid this, fixParamTypes calls TypeData.setType()
-     * on each parameter type.
-     */
-    private void fixParamTypes(TypedBlock first) throws BadBytecode {
-        TypeData[] types = first.localsTypes;
-        int n = types.length;
-        for (int i = 0; i < n; i++) {
-            TypeData t = types[i];
-            if (t instanceof TypeData.ClassName) {
-                /* Skip the following statement if t.isNullType() is true
-                 * although a parameter type is never null type.
-                 */
-                TypeData.setType(t, t.getName(), classPool);
-            }
+        make(code, blocks[0]);
+        try {
+            fixTypes(blocks);
+        } catch (NotFoundException e) {
+            throw new BadBytecode("failed to resolve types", e);
         }
     }
 
@@ -171,17 +148,19 @@
     private void make(byte[] code, TypedBlock tb)
         throws BadBytecode
     {
-        BasicBlock.Catch handlers = tb.toCatch;
-        while (handlers != null) {
-            traceException(code, handlers);
-            handlers = handlers.next;
-        }
+        copyTypeData(tb.stackTop, tb.stackTypes, stackTypes);
+        stackTop = tb.stackTop;
+        copyTypeData(tb.localsTypes.length, tb.localsTypes, localsTypes);
 
+        traceException(code, tb.toCatch);
+
         int pos = tb.position;
         int end = pos + tb.length;
         while (pos < end)
             pos += doOpcode(pos, code);
 
+        traceException(code, tb.toCatch);
+
         if (tb.exit != null) {
             for (int i = 0; i < tb.exit.length; i++) {
                 TypedBlock e = (TypedBlock)tb.exit[i];
@@ -189,7 +168,7 @@
                     mergeMap(e, true);
                 else {
                     recordStackMap(e);
-                    MapMaker maker = new MapMaker(this, true);
+                    MapMaker maker = new MapMaker(this);
                     maker.make(code, e);
                 }
             }
@@ -199,71 +178,66 @@
     private void traceException(byte[] code, TypedBlock.Catch handler)
         throws BadBytecode
     {
-        TypedBlock tb = (TypedBlock)handler.body;
-        if (tb.alreadySet())
-            mergeMap(tb, false);
-        else {
-            recordStackMap(tb, handler.typeIndex);
-            MapMaker maker = new MapMaker(this, false);
+        while (handler != null) {
+            TypedBlock tb = (TypedBlock)handler.body;
+            if (tb.alreadySet())
+                mergeMap(tb, false);
+            else {
+                recordStackMap(tb, handler.typeIndex);
+                MapMaker maker = new MapMaker(this);
+                maker.make(code, tb);
+            }
 
-            /* the following code is equivalent to maker.copyFrom(this)
-             * except stackTypes are not copied.
-             */ 
-            maker.stackTypes[0] = tb.stackTypes[0].getSelf();
-            maker.stackTop = 1;
-            maker.make(code, tb);
+            handler = handler.next;
         }
     }
 
-    private void mergeMap(TypedBlock dest, boolean mergeStack) {
-        boolean[] inputs = dest.inputs;
-        int n = inputs.length;
+    private void mergeMap(TypedBlock dest, boolean mergeStack) throws BadBytecode {
+        int n = localsTypes.length;
         for (int i = 0; i < n; i++)
-            if (inputs[i])
-                merge(localsTypes[i], dest.localsTypes[i]); 
+            dest.localsTypes[i] = merge(localsTypes[i], dest.localsTypes[i]); 
 
         if (mergeStack) {
             n = stackTop;
             for (int i = 0; i < n; i++)
-                merge(stackTypes[i], dest.stackTypes[i]);
+                dest.stackTypes[i] = merge(stackTypes[i], dest.stackTypes[i]);
         }
     }
 
-    private void merge(TypeData td, TypeData target) {
-        boolean tdIsObj = false;
-        boolean targetIsObj = false;
-        // td or target is null if it is TOP. 
-        if (td != TOP && td.isObjectType())
-            tdIsObj = true;
-
-        if (target != TOP && target.isObjectType())
-            targetIsObj = true;
-
-        if (tdIsObj && targetIsObj)
-            target.merge(td);
+    private TypeData merge(TypeData src, TypeData target) throws BadBytecode {
+        if (src == target)
+            return target;
+        else if (target instanceof TypeData.ClassName
+                 || target instanceof TypeData.BasicType)  // a parameter
+            return target;
+        else if (target instanceof TypeData.AbsTypeVar) {
+            ((TypeData.AbsTypeVar)target).merge(src);
+            return target;
+        }
+        else
+            throw new RuntimeException("fatal: this should never happen");
     }
 
     private void recordStackMap(TypedBlock target)
         throws BadBytecode
     {
-        TypeData[] tStackTypes = new TypeData[stackTypes.length];
+        TypeData[] tStackTypes = TypeData.make(stackTypes.length);
         int st = stackTop;
-        copyFrom(st, stackTypes, tStackTypes);
+        recordTypeData(st, stackTypes, tStackTypes);
         recordStackMap0(target, st, tStackTypes);
     }
 
     private void recordStackMap(TypedBlock target, int exceptionType)
         throws BadBytecode
     {
+        TypeData[] tStackTypes = TypeData.make(stackTypes.length);
         String type;
-        if (exceptionType == 0)
+        if (exceptionType == 0)     // for finally clauses
             type = "java.lang.Throwable";
         else
             type = cpool.getClassInfo(exceptionType);
 
-        TypeData[] tStackTypes = new TypeData[stackTypes.length];
         tStackTypes[0] = new TypeData.ClassName(type);
-
         recordStackMap0(target, 1, tStackTypes);
     }
 
@@ -271,34 +245,50 @@
         throws BadBytecode
     {
         int n = localsTypes.length;
-        TypeData[] tLocalsTypes = new TypeData[n];
-        int k = copyFrom(n, localsTypes, tLocalsTypes);
+        TypeData[] tLocalsTypes = TypeData.make(n);
+        int k = recordTypeData(n, localsTypes, tLocalsTypes);
+        target.setStackMap(st, tStackTypes, k, tLocalsTypes);
+    }
 
-        boolean[] inputs = target.inputs;
-        for (int i = 0; i < n; i++)
-            if (!inputs[i])
-                tLocalsTypes[i] = TOP;
+    protected static int recordTypeData(int n, TypeData[] srcTypes, TypeData[] destTypes) {
+        int k = -1;
+        for (int i = 0; i < n; i++) {
+            TypeData t = srcTypes[i];
+            destTypes[i] = t.join();
+            if (t != TOP)
+            	k = i + 1;		// t might be long or double.
+        }
 
-        target.setStackMap(st, tStackTypes, k, tLocalsTypes);
+        return k + 1;
     }
 
+    protected static void copyTypeData(int n, TypeData[] srcTypes, TypeData[] destTypes) {
+        for (int i = 0; i < n; i++)
+            destTypes[i] = srcTypes[i];
+    }
+
     // Phase 2
 
-    void evalExpected(TypedBlock target) throws BadBytecode {
-        ClassPool cp = classPool;
-        evalExpected(cp, target.stackTop, target.stackTypes);
-        TypeData[] types = target.localsTypes;
-        if (types != null)  // unless this block is dead code
-            evalExpected(cp, types.length, types);
-    }
+    /*
+     * This method first finds strongly connected components (SCCs)
+     * on a graph made by TypeData by Tarjan's algorithm.
+     * SCCs are TypeData nodes sharing the same type.
+     * Since SCCs are found in the topologically sorted order,
+     * their types are also fixed when they are found. 
+     */
+    private void fixTypes(TypedBlock[] blocks) throws NotFoundException {
+        ArrayList preOrder = new ArrayList();
+        int len = blocks.length;
+        int index = 0;
+        for (int i = 0; i < len; i++) {
+            TypedBlock block = blocks[i];
+            int n = block.localsTypes.length;
+            for (int j = 0; j < n; j++)
+                index = block.localsTypes[j].dfs(preOrder, index, classPool);
 
-    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);
+            n = block.stackTop;
+            for (int j = 0; j < n; j++)
+                index = block.stackTypes[j].dfs(preOrder, index, classPool); 
         }
     }
 
@@ -370,19 +360,14 @@
         }
         else if (stackTop == 1 && diffL == 0) {
             TypeData td = bb.stackTypes[0];
-            if (td == TOP)
-                writer.sameLocals(offsetDelta, StackMapTable.TOP, 0);
-            else
-                writer.sameLocals(offsetDelta, td.getTypeTag(),
-                                  td.getTypeData(cpool));
+            writer.sameLocals(offsetDelta, td.getTypeTag(), td.getTypeData(cpool));
             return;
         }
         else if (stackTop == 2 && diffL == 0) {
             TypeData td = bb.stackTypes[0];
-            if (td != TOP && td.is2WordType()) {
+            if (td.is2WordType()) {
                 // bb.stackTypes[1] must be TOP.
-                writer.sameLocals(offsetDelta, td.getTypeTag(),
-                                  td.getTypeData(cpool));
+                writer.sameLocals(offsetDelta, td.getTypeTag(), td.getTypeData(cpool));
                 return;
             }
         }
@@ -401,16 +386,10 @@
         int j = 0;
         for (int i = 0; i < num; i++) {
             TypeData td = types[offset + i];
-            if (td == TOP) {
-                tags[j] = StackMapTable.TOP;
-                data[j] = 0;
-            }
-            else {
-                tags[j] = td.getTypeTag();
-                data[j] = td.getTypeData(cp);
-                if (td.is2WordType())
-                    i++;
-            }
+            tags[j] = td.getTypeTag();
+            data[j] = td.getTypeData(cp);
+            if (td.is2WordType())
+                i++;
 
             j++;
         }
@@ -439,14 +418,8 @@
 
     private static boolean stackMapEq(TypeData[] oldTd, TypeData[] newTd, int len) {
         for (int i = 0; i < len; i++) {
-            TypeData td = oldTd[i];
-            if (td == TOP) {        // the next element to LONG/DOUBLE is TOP.
-                if (newTd[i] != TOP)
-                    return false;
-            }
-            else
-                if (!oldTd[i].equals(newTd[i]))
-                    return false;
+            if (!oldTd[i].eq(newTd[i]))
+                return false;
         }
 
         return true;
@@ -457,7 +430,7 @@
         while (offset < len) {
             TypeData td = types[offset++];
             num++;
-            if (td != TOP && td.is2WordType())
+            if (td.is2WordType())
                 offset++;
         }
 
@@ -515,13 +488,9 @@
         writer.write16bit(num - numDWord);
         for (int i = 0; i < num; i++) {
             TypeData td = types[i];
-            if (td == TOP)
-                writer.writeVerifyTypeInfo(StackMap.TOP, 0);
-            else {
-                writer.writeVerifyTypeInfo(td.getTypeTag(), td.getTypeData(cp));
-                if (td.is2WordType())
-                    i++;
-            }
+            writer.writeVerifyTypeInfo(td.getTypeTag(), td.getTypeData(cp));
+            if (td.is2WordType())
+                i++;
         }
     }
 }

Modified: trunk/src/main/javassist/bytecode/stackmap/Tracer.java
===================================================================
--- trunk/src/main/javassist/bytecode/stackmap/Tracer.java	2012-09-14 12:06:49 UTC (rev 654)
+++ trunk/src/main/javassist/bytecode/stackmap/Tracer.java	2012-09-14 15:22:13 UTC (rev 655)
@@ -31,7 +31,7 @@
 public abstract class Tracer implements TypeTag {
     protected ClassPool classPool;
     protected ConstPool cpool;
-    protected String returnType;
+    protected String returnType;    // used as the type of ARETURN
 
     protected int stackTop;
     protected TypeData[] stackTypes;
@@ -43,41 +43,19 @@
         cpool = cp;
         returnType = retType;
         stackTop = 0;
-        stackTypes = new TypeData[maxStack];
-        localsTypes = new TypeData[maxLocals];
+        stackTypes = TypeData.make(maxStack);
+        localsTypes = TypeData.make(maxLocals);
     }
 
-    public Tracer(Tracer t, boolean copyStack) {
+    public Tracer(Tracer t) {
         classPool = t.classPool;
         cpool = t.cpool;
         returnType = t.returnType;
-
         stackTop = t.stackTop;
-        int size = t.stackTypes.length;
-        stackTypes = new TypeData[size];
-        if (copyStack)
-            copyFrom(t.stackTop, t.stackTypes, stackTypes);
-
-        int size2 = t.localsTypes.length;
-        localsTypes = new TypeData[size2];
-        copyFrom(size2, t.localsTypes, localsTypes);
+        stackTypes = TypeData.make(t.stackTypes.length);
+        localsTypes = TypeData.make(t.localsTypes.length);
     }
 
-    protected 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 == TOP ? TOP : t.getSelf();
-            if (t != TOP)
-                if (t.is2WordType())
-                    k = i + 1;
-                else
-                    k = i;
-        }
-
-        return k + 1;
-    }
-
     /**
      * Does abstract interpretation on the given bytecode instruction.
      * It records whether or not a local variable (i.e. register) is accessed.
@@ -255,11 +233,7 @@
         case Opcode.AALOAD : {
             int s = --stackTop - 1;
             TypeData data = stackTypes[s];
-            if (data == null || !data.isObjectType())
-                throw new BadBytecode("bad AALOAD");
-            else
-                stackTypes[s] = new TypeData.ArrayElement(data);
-
+            stackTypes[s] = TypeData.ArrayElement.make(data);
             break; }
         case Opcode.BALOAD :
         case Opcode.CALOAD :
@@ -309,14 +283,12 @@
         return 2;
     }
 
-    private int doALOAD(int localVar) { // int localVar, TypeData type) {
+    private int doALOAD(int localVar) {
         stackTypes[stackTop++] = localsTypes[localVar];
         return 2;
     }
 
     private int doOpcode54_95(int pos, byte[] code, int op) throws BadBytecode {
-        TypeData[] localsTypes = this.localsTypes;
-        TypeData[] stackTypes = this.stackTypes;
         switch (op) {
         case Opcode.ISTORE :
             return doXSTORE(pos, code, INTEGER);
@@ -376,9 +348,9 @@
             stackTop -= (op == Opcode.LASTORE || op == Opcode.DASTORE) ? 4 : 3;
             break;
         case Opcode.AASTORE :
-            TypeData.setType(stackTypes[stackTop - 1],
-                             TypeData.ArrayElement.getElementType(stackTypes[stackTop - 3].getName()),
-                             classPool);
+            TypeData.ArrayElement.aastore(stackTypes[stackTop - 3],
+                                          stackTypes[stackTop - 1],
+                                          classPool);
             stackTop -= 3;
             break;
         case Opcode.BASTORE :
@@ -450,7 +422,7 @@
     private int doASTORE(int index) {
         stackTop--;
         // implicit upcast might be done.
-        localsTypes[index] = stackTypes[stackTop].copy();
+        localsTypes[index] = stackTypes[stackTop];
         return 2;
     }
 
@@ -475,16 +447,16 @@
             // this does not call writeLocal().
             return 3;
         case Opcode.I2L :
-            stackTypes[stackTop] = LONG;
-            stackTypes[stackTop - 1] = TOP;
+            stackTypes[stackTop - 1] = LONG;
+            stackTypes[stackTop] = TOP;
             stackTop++;
             break;
         case Opcode.I2F :
             stackTypes[stackTop - 1] = FLOAT;
             break;
         case Opcode.I2D :
-            stackTypes[stackTop] = DOUBLE;
-            stackTypes[stackTop - 1] = TOP;
+            stackTypes[stackTop - 1] = DOUBLE;
+            stackTypes[stackTop] = TOP;
             stackTop++;
             break;
         case Opcode.L2I :
@@ -494,24 +466,26 @@
             stackTypes[--stackTop - 1] = FLOAT;
             break;
         case Opcode.L2D :
-            stackTypes[stackTop - 1] = DOUBLE;
+            stackTypes[stackTop - 2] = DOUBLE;
             break;
         case Opcode.F2I :
             stackTypes[stackTop - 1] = INTEGER;
             break;
         case Opcode.F2L :
-            stackTypes[stackTop - 1] = TOP;
-            stackTypes[stackTop++] = LONG;
+            stackTypes[stackTop - 1] = LONG;
+            stackTypes[stackTop] = TOP;
+            stackTop++;
             break;
         case Opcode.F2D :
-            stackTypes[stackTop - 1] = TOP;
-            stackTypes[stackTop++] = DOUBLE;
+            stackTypes[stackTop - 1] = DOUBLE;
+            stackTypes[stackTop] = TOP;
+            stackTop++;
             break;
         case Opcode.D2I :
             stackTypes[--stackTop - 1] = INTEGER;
             break;
         case Opcode.D2L :
-            stackTypes[stackTop - 1] = LONG;
+            stackTypes[stackTop - 2] = LONG;
             break;
         case Opcode.D2F :
             stackTypes[--stackTop - 1] = FLOAT;
@@ -602,7 +576,7 @@
             visitReturn(pos, code);
             break;
         case Opcode.ARETURN :
-            TypeData.setType(stackTypes[--stackTop], returnType, classPool);
+            stackTypes[--stackTop].setType(returnType, classPool);
             visitReturn(pos, code);
             break;
         case Opcode.RETURN :
@@ -644,17 +618,21 @@
                     = new TypeData.ClassName(type);
             return 3; }
         case Opcode.ARRAYLENGTH :
-            TypeData.setType(stackTypes[stackTop - 1], "[Ljava.lang.Object;", classPool);
+            stackTypes[stackTop - 1].setType("[Ljava.lang.Object;", classPool);
             stackTypes[stackTop - 1] = INTEGER;
             break;
         case Opcode.ATHROW :
-            TypeData.setType(stackTypes[--stackTop], "java.lang.Throwable", classPool);
+            stackTypes[--stackTop].setType("java.lang.Throwable", classPool);
             visitThrow(pos, code);
             break;
         case Opcode.CHECKCAST : {
             // TypeData.setType(stackTypes[stackTop - 1], "java.lang.Object", classPool);
             int i = ByteArray.readU16bit(code, pos + 1);
-            stackTypes[stackTop - 1] = new TypeData.ClassName(cpool.getClassInfo(i));
+            String type = cpool.getClassInfo(i);
+            if (type.charAt(0) == '[')
+                type = type.replace('.', '/');  // getClassInfo() may return "[java.lang.Object;".
+
+            stackTypes[stackTop - 1] = new TypeData.ClassName(type);
             return 3; }
         case Opcode.INSTANCEOF :
             // TypeData.setType(stackTypes[stackTop - 1], "java.lang.Object", classPool);
@@ -748,9 +726,9 @@
         stackTop -= Descriptor.dataSize(desc);
         char c = desc.charAt(0);
         if (c == 'L')
-            TypeData.setType(stackTypes[stackTop], getFieldClassName(desc, 0), classPool);
+            stackTypes[stackTop].setType(getFieldClassName(desc, 0), classPool);
         else if (c == '[')
-            TypeData.setType(stackTypes[stackTop], desc, classPool);
+            stackTypes[stackTop].setType(desc, classPool);
 
         setFieldTarget(notStatic, index);
         return 3;
@@ -767,7 +745,7 @@
     private void setFieldTarget(boolean notStatic, int index) throws BadBytecode {
         if (notStatic) {
             String className = cpool.getFieldrefClassName(index);
-            TypeData.setType(stackTypes[--stackTop], className, classPool);
+            stackTypes[--stackTop].setType(className, classPool);
         }
     }
 
@@ -823,7 +801,7 @@
         checkParamTypes(desc, 1);
         if (notStatic) {
             String className = cpool.getMethodrefClassName(i);
-            TypeData.setType(stackTypes[--stackTop], className, classPool);
+            stackTypes[--stackTop].setType(className, classPool);
         }
 
         pushMemberType(desc);
@@ -835,7 +813,7 @@
         String desc = cpool.getInterfaceMethodrefType(i);
         checkParamTypes(desc, 1);
         String className = cpool.getInterfaceMethodrefClassName(i);
-        TypeData.setType(stackTypes[--stackTop], className, classPool);
+        stackTypes[--stackTop].setType(className, classPool);
         pushMemberType(desc);
         return 5;
     }
@@ -912,10 +890,9 @@
             stackTop--;
 
         if (array)
-            TypeData.setType(stackTypes[stackTop],
-                             desc.substring(i, k), classPool);
+            stackTypes[stackTop].setType(desc.substring(i, k), classPool);
         else if (c == 'L')
-            TypeData.setType(stackTypes[stackTop],
-                             desc.substring(i + 1, k - 1).replace('/', '.'), classPool);
+            stackTypes[stackTop].setType(desc.substring(i + 1, k - 1).replace('/', '.'),
+                                         classPool);
     }
 }

Modified: trunk/src/main/javassist/bytecode/stackmap/TypeData.java
===================================================================
--- trunk/src/main/javassist/bytecode/stackmap/TypeData.java	2012-09-14 12:06:49 UTC (rev 654)
+++ trunk/src/main/javassist/bytecode/stackmap/TypeData.java	2012-09-14 15:22:13 UTC (rev 655)
@@ -20,8 +20,11 @@
 import javassist.CtClass;
 import javassist.NotFoundException;
 import javassist.bytecode.ConstPool;
+import javassist.bytecode.Descriptor;
 import javassist.bytecode.StackMapTable;
 import javassist.bytecode.BadBytecode;
+import java.util.HashSet;
+import java.util.Iterator;
 import java.util.ArrayList;
 
 public abstract class TypeData {
@@ -29,10 +32,16 @@
      * array type is a subtype of Cloneable and Serializable 
      */
 
+    public static TypeData[] make(int size) {
+        TypeData[] array = new TypeData[size];
+        for (int i = 0; i < size; i++)
+            array[i] = TypeTag.TOP;
+
+        return array;
+    }
+
     protected TypeData() {}
 
-    public abstract void merge(TypeData neighbor);
-
     /**
      * 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
@@ -40,38 +49,50 @@
      *
      * @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 == TypeTag.TOP)
-            throw new BadBytecode("unset variable");
-        else
-            td.setType(className, cp);
+    private static void setType(TypeData td, String className, ClassPool cp) throws BadBytecode {
+        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 join() { return new TypeVar(this); }
+
+    /**
+     * If the type is a basic type, this method normalizes the type
+     * and returns a BasicType object.  Otherwise, it returns null.
      */
-    public TypeData getSelf() { return this; } 
+    public abstract BasicType isBasicType();
 
-    /* An operand value is copied when it is stored in a
-     * local variable.
+    public abstract boolean is2WordType();
+
+    /**
+     * Returns false if getName() returns a valid type name.
      */
-    public abstract TypeData copy();
-
-    public abstract boolean isObjectType();
-    public boolean is2WordType() { 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;
-    public abstract String getExpected() throws BadBytecode;
+    public boolean isUninit() { return false; }
 
+    public abstract boolean eq(TypeData d);
+
+    public abstract String getName();
+    public abstract void setType(String s, ClassPool cp) throws BadBytecode;
+
+    // depth-first search
+    public int dfs(ArrayList order, int index, ClassPool cp)
+        throws NotFoundException
+    {
+        return index;
+    }
+
     /**
+     * Returns this if it is a TypeVar or a TypeVar that this
+     * type depends on.  Otherwise, this method returns null.
+     * It is used by dfs().
+     */
+    protected TypeVar toTypeVar() { return null; }
+
+    /**
      * Primitive types.
      */
     protected static class BasicType extends TypeData {
@@ -83,437 +104,607 @@
             typeTag = tag;
         }
 
-        public void merge(TypeData neighbor) {}
+        public int getTypeTag() { return typeTag; }
+        public int getTypeData(ConstPool cp) { return 0; }
 
-        public boolean equals(Object obj) {
-            return this == obj;
+        public TypeData join() {
+        	if (this == TypeTag.TOP)
+        		return this;
+        	else
+        		return super.join();
         }
 
-        public int getTypeTag() { return typeTag; }
-        public int getTypeData(ConstPool cp) { return 0; }
+        public BasicType isBasicType() { return this; }
 
-        public boolean isObjectType() { return false; }
-
         public boolean is2WordType() {
             return typeTag == StackMapTable.LONG
                     || typeTag == StackMapTable.DOUBLE;
         }
 
-        public TypeData copy() {
-            return this;
-        }
+        public boolean eq(TypeData d) { return this == d; }
 
-        public void evalExpectedType(ClassPool cp) throws BadBytecode {}
-
-        public String getExpected() throws BadBytecode {
-            return name;
-        }
-
         public String getName() {
             return name;
         }
 
-        protected void setType(String s, ClassPool cp) throws BadBytecode {
+        public 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 {
-        protected ArrayList equivalences;
+    // a type variable
+    public static abstract class AbsTypeVar extends TypeData {
+        public AbsTypeVar() {}
+        public abstract void merge(TypeData t);
+        public int getTypeTag() { return StackMapTable.OBJECT; }
 
-        protected String expectedName;
-        private CtClass cache;
-        private boolean evalDone;
-
-        protected TypeName() {
-            equivalences = new ArrayList();
-            equivalences.add(this);
-            expectedName = null;
-            cache = null;
-            evalDone = false;
+        public int getTypeData(ConstPool cp) {
+            return cp.addClassInfo(getName());
         }
 
-        public void merge(TypeData neighbor) {
-            if (this == neighbor)
-                return;
+        public boolean eq(TypeData d) { return getName().equals(d.getName()); }
+    }
 
-            if (!(neighbor instanceof TypeName))
-                return;     // neighbor might be UninitData
+    // a type variable representing a class type.
+    public static class TypeVar extends AbsTypeVar {
+        protected ArrayList lowers;     // lower bounds of this type. ArrayList<TypeData>
+        protected ArrayList usedBy;     // reverse relations of lowers
+        protected ArrayList uppers;     // upper bounds of this type.
+        protected String fixedType;
 
-            TypeName neighbor2 = (TypeName)neighbor;
-            ArrayList list = equivalences;
-            ArrayList list2 = neighbor2.equivalences;
-            if (list == list2)
-                return;
+        public TypeVar(TypeData t) {
+            uppers = null;
+            lowers = new ArrayList(2);
+            usedBy = new ArrayList(2);
+            merge(t);
+            fixedType = null;
+        }
 
-            int n = list2.size();
-            for (int i = 0; i < n; i++) {
-                TypeName tn = (TypeName)list2.get(i);
-                add(list, tn);
-                tn.equivalences = list;
-            }
+        public String getName() {
+            if (fixedType == null)
+                return ((TypeData)lowers.get(0)).getName();
+            else
+                return fixedType;
         }
 
-        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;
+        public BasicType isBasicType() {
+            if (fixedType == null)
+                return ((TypeData)lowers.get(0)).isBasicType();
+            else
+                return null;
+        }
 
-            list.add(td);
+        public boolean is2WordType() {
+            if (fixedType == null)
+                return ((TypeData)lowers.get(0)).is2WordType();
+            else
+                return false;
         }
 
-        /* NullType overrides this method.
-         */
-        public int getTypeTag() { return StackMapTable.OBJECT; }
+        public boolean isNullType() {
+            if (fixedType == null)
+                return ((TypeData)lowers.get(0)).isNullType();
+            else
+                return false;
+        }
 
-        public int getTypeData(ConstPool cp) {
-            String type;
-            try {
-                type = getExpected();
-            } catch (BadBytecode e) {
-                throw new RuntimeException("fatal error: ", e); 
-            }
+        public boolean isUninit() {
+            if (fixedType == null)
+                return ((TypeData)lowers.get(0)).isUninit();
+            else
+                return false;
+        }
 
-            return getTypeData2(cp, type);
+        public void merge(TypeData t) {
+        	lowers.add(t);
+        	if (t instanceof TypeVar)
+        	    ((TypeVar)t).usedBy.add(this);
         }
 
-        /* NullType overrides this method.
-         */
-        protected int getTypeData2(ConstPool cp, String type) {
-            return cp.addClassInfo(type);
+        public int getTypeTag() {
+            /* If fixedType is null after calling dfs(), then this
+               type is NULL, Uninit, or a basic type.  So call
+               getTypeTag() on the first element of lowers. */
+            if (fixedType == null)
+                return ((TypeData)lowers.get(0)).getTypeTag();
+            else
+                return super.getTypeTag();
         }
 
-        public boolean equals(Object obj) {
-            if (obj instanceof TypeName) {
-                try {
-                    TypeName tn = (TypeName)obj;
-                    return getExpected().equals(tn.getExpected());
-                }
-                catch (BadBytecode e) {}
-            }
+        public int getTypeData(ConstPool cp) {
+            if (fixedType == null)
+                return ((TypeData)lowers.get(0)).getTypeData(cp);
+            else
+                return super.getTypeData(cp);
+        }
 
-            return false;
+        public void setType(String typeName, ClassPool cp) throws BadBytecode {
+            if (uppers == null)
+                uppers = new ArrayList();
+
+            uppers.add(typeName);
         }
 
-        public boolean isObjectType() { return true; }
+        protected TypeVar toTypeVar() { return this; }
 
-        protected void setType(String typeName, ClassPool cp) throws BadBytecode {
-            if (update(cp, expectedName, typeName))
-                expectedName = typeName;
+        private int visited = 0;
+        private int smallest = 0;
+        private boolean inList = false;
+
+        // depth-first serach
+        public int dfs(ArrayList preOrder, int index, ClassPool cp) throws NotFoundException {
+        	if (visited > 0)
+        		return index;		// MapMaker.make() may call an already visited node.
+
+        	visited = smallest = ++index;
+        	preOrder.add(this);
+        	inList = true;
+        	int n = lowers.size();
+        	for (int i = 0; i < n; i++) {
+        		TypeVar child = ((TypeData)lowers.get(i)).toTypeVar();
+        		if (child != null)
+        			if (child.visited == 0) {
+        				index = child.dfs(preOrder, index, cp);
+        				if (child.smallest < smallest)
+        					smallest = child.smallest;
+        			}
+        			else if (child.inList)
+        				if (child.visited < smallest)
+        					smallest = child.visited;
+        	}
+
+        	if (visited == smallest) {
+                ArrayList scc = new ArrayList();    // strongly connected component
+        		TypeVar cv;
+        		do {
+        			cv = (TypeVar)preOrder.remove(preOrder.size() - 1);
+        			cv.inList = false;
+        			scc.add(cv);
+        		} while (cv != this);
+        		fixTypes(scc, cp);
+        	}
+
+        	return index;
         }
 
-        public void evalExpectedType(ClassPool cp) throws BadBytecode {
-            if (this.evalDone)
-                return;
+        private void fixTypes(ArrayList scc, ClassPool cp) throws NotFoundException {
+            HashSet lowersSet = new HashSet();
+            boolean isBasicType = false;
+            TypeData kind = null;
+            int size = scc.size();
+            for (int i = 0; i < size; i++) {
+                ArrayList tds = ((TypeVar)scc.get(i)).lowers;
+                int size2 = tds.size();
+                for (int j = 0; j < size2; j++) {
+                    TypeData d = (TypeData)tds.get(j);
+                    BasicType bt = d.isBasicType();
+                    if (kind == null) {
+                        if (bt == null) {
+                            isBasicType = false;
+                            kind = d;
+                            /* If scc has only an UninitData, fixedType is kept null.
+                               So lowerSet must be empty.  If scc has not only an UninitData
+                               but also another TypeData, an error must be thrown but this
+                               error detection has not been implemented. */
+                            if (d.isUninit())
+                                break;
+                        }
+                        else {
+                            isBasicType = true;
+                            kind = bt;
+                        }
+                    }
+                    else {
+                        if ((bt == null && isBasicType)
+                            || (bt != null && kind != bt)) {
+                            isBasicType = true;
+                            kind = TypeTag.TOP;
+                            break;
+                        }
+                    }
 
-            ArrayList equiv = this.equivalences;
-            int n = equiv.size();
-            String name = evalExpectedType2(equiv, n);
-            if (name == null) {
-                name = this.expectedName;
-                for (int i = 0; i < n; i++) {
-                    TypeData td = (TypeData)equiv.get(i);
-                    if (td instanceof TypeName) {
-                        TypeName tn = (TypeName)td;
-                        if (update(cp, name, tn.expectedName))
-                            name = tn.expectedName;
-                    }
+                    if (bt == null && !d.isNullType())
+                        lowersSet.add(d.getName());
                 }
             }
 
-            for (int i = 0; i < n; i++) {
-                TypeData td = (TypeData)equiv.get(i);
-                if (td instanceof TypeName) {
-                    TypeName tn = (TypeName)td;
-                    tn.expectedName = name;
-                    tn.cache = null;
-                    tn.evalDone = true;
+            if (isBasicType) {
+                for (int i = 0; i < size; i++) {
+                    TypeVar cv = (TypeVar)scc.get(i);
+                    cv.lowers.clear();
+                    cv.lowers.add(kind);
                 }
             }
+            else {
+                String typeName = fixTypes2(scc, lowersSet, cp);
+                for (int i = 0; i < size; i++) {
+                    TypeVar cv = (TypeVar)scc.get(i);
+                    cv.fixedType = typeName;
+                }
+            }
         }
 
-        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;
+        private String fixTypes2(ArrayList scc, HashSet lowersSet, ClassPool cp) throws NotFoundException {
+            Iterator it = lowersSet.iterator();
+            if (lowersSet.size() == 0)
+                return null;      // only NullType
+            else if (lowersSet.size() == 1)
+                return (String)it.next(); 
+            else {
+            	CtClass cc = cp.get((String)it.next());
+            	while (it.hasNext())
+            		cc = commonSuperClassEx(cc, cp.get((String)it.next()));
+
+            	if (cc.getSuperclass() == null || isObjectArray(cc))
+            	    cc = fixByUppers(scc, cp, new HashSet(), cc);
+
+            	if (cc.isArray())
+            	    return Descriptor.toJvmName(cc);
+            	else
+            	    return cc.getName();
             }
+        }
 
-            return origName;
+        private static boolean isObjectArray(CtClass cc) throws NotFoundException {
+            return cc.isArray() && cc.getComponentType().getSuperclass() == null;
         }
 
-        protected boolean isTypeName() { return true; }
+        private CtClass fixByUppers(ArrayList users, ClassPool cp, HashSet visited, CtClass type)
+            throws NotFoundException
+        {
+            if (users == null)
+                return type;
 
-        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;
-            else if (typeName.charAt(0) == '['
-                     && oldName.equals("[Ljava.lang.Object;")) {
-                /* this rule is not correct but Tracer class sets the type
-                   of the operand of arraylength to java.lang.Object[].
-                   Thus, int[] etc. must be a subtype of java.lang.Object[].
-                 */
-                return true;
-            }
+            int size = users.size();
+            for (int i = 0; i < size; i++) {
+                TypeVar t = (TypeVar)users.get(i);
+                if (!visited.add(t))
+                    return type;
 
-            try {
-                if (cache == null)
-                    cache = cp.get(oldName);
-    
-                CtClass cache2 = cp.get(typeName);
-                if (cache2.subtypeOf(cache)) {
-                    cache = cache2;
-                    return true;
+                if (t.uppers != null) {
+                    int s = t.uppers.size();
+                    for (int k = 0; k < s; k++) {
+                        CtClass cc = cp.get((String)t.uppers.get(k));
+                        if (cc.subtypeOf(type))
+                            type = cc;
+                    }
                 }
-                else
-                    return false;
+
+                type = fixByUppers(t.usedBy, cp, visited, type);
             }
-            catch (NotFoundException e) {
-                throw new BadBytecode("cannot find " + e.getMessage());
-            }
-        }
 
-        /* See also {NullType,ArrayElement}.getExpected().
-         */
-        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;
-            }
+            return type;
         }
+    }
 
-        public String toString() {
-            try {
-                String en = expectedName;
-                if (en != null)
-                    return en;
+    /**
+     * Finds the most specific common super class of the given classes
+     * by considering array types.
+     */
+    public static CtClass commonSuperClassEx(CtClass one, CtClass two) throws NotFoundException {
+        if (one.isArray() && two.isArray()) {
+            CtClass ele1 = one.getComponentType();
+            CtClass ele2 = two.getComponentType();
+            CtClass element = commonSuperClassEx(ele1, ele2); 
+            if (element == ele1)
+                return one;
+            else if (element == ele2)
+                return two;
 
-                String name = getName();
-                if (equivalences.size() == 1)
-                    return name;
-                else
-                    return name + "?";
-            }
-            catch (BadBytecode e) {
-                return "<" + e.getMessage() + ">";
-            }
+            return one.getClassPool().get(element.getName() + "[]");
         }
+        else
+            return commonSuperClass(one, two);
     }
 
     /**
-     * Type data for OBJECT.
+     * Finds the most specific common super class of the given classes.
+     * This method is a copy from javassist.bytecode.analysis.Type.
      */
-    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 static CtClass commonSuperClass(CtClass one, CtClass two) throws NotFoundException {
+        CtClass deep = one;
+        CtClass shallow = two;
+        CtClass backupShallow = shallow;
+        CtClass backupDeep = deep;
+
+        // Phase 1 - Find the deepest hierarchy, set deep and shallow correctly
+        for (;;) {
+            // In case we get lucky, and find a match early
+            if (eq(deep, shallow) && deep.getSuperclass() != null)
+                return deep;
+
+            CtClass deepSuper = deep.getSuperclass();
+            CtClass shallowSuper = shallow.getSuperclass();
+
+            if (shallowSuper == null) {
+                // right, now reset shallow
+                shallow = backupShallow;
+                break;
+            }
+
+            if (deepSuper == null) {
+                // wrong, swap them, since deep is now useless, its our tmp before we swap it
+                deep = backupDeep;
+                backupDeep = backupShallow;
+                backupShallow = deep;
+
+                deep = shallow;
+                shallow = backupShallow;
+                break;
+            }
+
+            deep = deepSuper;
+            shallow = shallowSuper;
         }
 
-        public TypeData copy() {
-            return new ClassName(name);
+        // Phase 2 - Move deepBackup up by (deep end - deep)
+        for (;;) {
+            deep = deep.getSuperclass();
+            if (deep == null)
+                break;
+
+            backupDeep = backupDeep.getSuperclass();
         }
 
-        public String getName() {   // never returns null.
-            return name;
+        deep = backupDeep;
+
+        // Phase 3 - The hierarchy positions are now aligned
+        // The common super class is easy to find now
+        while (!eq(deep, shallow)) {
+            deep = deep.getSuperclass();
+            shallow = shallow.getSuperclass();
         }
+
+        return deep;
     }
 
-    /**
-     * Type data for NULL or OBJECT.
-     * The types represented by the instances of this class are
-     * initially NULL but will be OBJECT.
+    static boolean eq(CtClass one, CtClass two) {
+        return one == two || (one != null && two != null && one.getName().equals(two.getName()));
+    }
+
+    public static void aastore(TypeData array, TypeData value, ClassPool cp) throws BadBytecode {
+        if (array instanceof AbsTypeVar)
+            if (!value.isNullType())
+                ((AbsTypeVar)array).merge(ArrayType.make(value));
+
+        if (value instanceof AbsTypeVar)
+            if (array instanceof AbsTypeVar)
+                ArrayElement.make(array);   // should call value.setType() later.
+            else if (array instanceof ClassName) {
+                if (!array.isNullType()) {
+                    String type = ArrayElement.typeName(array.getName());
+                    value.setType(type, cp);
+                }
+            }
+            else
+                throw new BadBytecode("bad AASTORE: " + array);
+    }
+
+    /* A type variable representing an array type.
+     * It is a decorator of another type variable.  
      */
-    public static class NullType extends ClassName {
-        public NullType() {
-            super("null");      // type name
+    public static class ArrayType extends AbsTypeVar {
+        private AbsTypeVar element;
+
+        private ArrayType(AbsTypeVar elementType) {
+            element = elementType;
         }
 
-        public TypeData copy() {
-            return new NullType();
+        static TypeData make(TypeData element) throws BadBytecode {
+            if (element instanceof ArrayElement)
+                return ((ArrayElement)element).arrayType();
+            else if (element instanceof AbsTypeVar)
+                return new ArrayType((AbsTypeVar)element);
+            else if (element instanceof ClassName)
+                if (!element.isNullType())
+                    return new ClassName(typeName(element.getName()));
+
+            throw new BadBytecode("bad AASTORE: " + element);
         }
 
-        public boolean isNullType() { return true; }
+        public void merge(TypeData t) {
+        	try {
+        	    if (!t.isNullType())
+        	        element.merge(ArrayElement.make(t));
+        	}
+        	catch (BadBytecode e) {
+        		// never happens
+        		throw new RuntimeException("fatal: " + e);
+        	}
+        }
 
-        public int getTypeTag() {
-            try {
-                if ("null".equals(getExpected()))
-                    return StackMapTable.NULL;
-                else
-                    return super.getTypeTag();
-            }
-            catch (BadBytecode e) {
-                throw new RuntimeException("fatal error: ", e); 
-            }
+        public String getName() {
+            return typeName(element.getName());
         }
 
-        protected int getTypeData2(ConstPool cp, String type) {
-            if ("null".equals(type))
-                return 0;
+        public AbsTypeVar elementType() { return element; }
+
+        public BasicType isBasicType() { return null; }
+        public boolean is2WordType() { return false; }
+
+        /* elementType must be a class name.  Basic type names
+         * are not allowed.
+         */
+        public static String typeName(String elementType) {
+            if (elementType.charAt(0) == '[')
+                return "[" + elementType;
             else
-                return super.getTypeData2(cp, type);
+                return "[L" + elementType.replace('.', '/') + ";";
         }
 
-        public String getExpected() throws BadBytecode {
-            String en = expectedName;
-            if (en == null) {
-              // ArrayList equiv = equivalences;
-              // if (equiv.size() == 1)
-              //    return getName();
-              // else
-                    return "java.lang.Object";
-            }
-            else
-                return en;
+        public void setType(String s, ClassPool cp) throws BadBytecode {
+            element.setType(ArrayElement.typeName(s), cp);
         }
+
+        protected TypeVar toTypeVar() { return element.toTypeVar(); }
+
+        public int dfs(ArrayList order, int index, ClassPool cp) throws NotFoundException {
+        	return element.dfs(order, index, cp);
+        }
     }
 
-    /**
-     * 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;
+    /* A type variable representing an array-element type.
+     * It is a decorator of another type variable.  
+     */ 
+    public static class ArrayElement extends AbsTypeVar {
+        private AbsTypeVar array;
     
-        public ArrayElement(TypeData a) {   // a is never null
+        private ArrayElement(AbsTypeVar a) {   // a is never null
             array = a;
         }
 
-        public TypeData copy() {
-            return new ArrayElement(array);
-        }
+        public static TypeData make(TypeData array) throws BadBytecode {
+            if (array instanceof ArrayType)
+                return ((ArrayType)array).elementType();
+            else if (array instanceof AbsTypeVar)
+                return new ArrayElement((AbsTypeVar)array);
+            else if (array instanceof ClassName)
+                if (!array.isNullType())
+                    return new ClassName(typeName(array.getName()));
 
-        public boolean isNullType() {
-            return array.isNullType();
+            throw new BadBytecode("bad AASTORE: " + array);
         }
 
-        protected void setType(String typeName, ClassPool cp) throws BadBytecode {
-            super.setType(typeName, cp);
-            array.setType(getArrayType(typeName), cp);
+        public void merge(TypeData t) {
+        	try {
+        	    if (!t.isNullType())
+        	        array.merge(ArrayType.make(t));
+        	}
+        	catch (BadBytecode e) {
+        		// never happens
+        		throw new RuntimeException("fatal: " + e);
+        	}
         }
 
-        public String getName() throws BadBytecode {
-            return getName2(array.getName());
+        public String getName() {
+            return typeName(array.getName());
         }
 
-        private String getName2(String name) throws BadBytecode {
-            if (name.length() > 1 && name.charAt(0) == '[') {
-                char c = name.charAt(1);
+        public AbsTypeVar arrayType() { return array; }
+
+        /* arrayType must be a class name.  Basic type names are
+         * not allowed.
+         */
+
+        public BasicType isBasicType() { return null; }
+
+        public boolean is2WordType() { return false; }
+
+        private static String typeName(String arrayType) {
+            if (arrayType.length() > 1 && arrayType.charAt(0) == '[') {
+                char c = arrayType.charAt(1);
                 if (c == 'L')
-                    return name.substring(2, name.length() - 1).replace('/', '.');
+                    return arrayType.substring(2, arrayType.length() - 1).replace('/', '.');
                 else if (c == '[')
-                    return name.substring(1);
+                    return arrayType.substring(1);
             }
 
-            if (array.isNullType())
-                return "java.lang.Object";
-            else
-                throw new BadBytecode("bad array type for AALOAD: "
-                                  + name);
+            return "java.lang.Object";      // the array type may be NullType
         }
 
-        public String getExpected() throws BadBytecode {
-            ArrayList equiv = equivalences;
-            if (equiv.size() == 1)
-                return getName2(array.getExpected());     // not getName();
-            else {
-                String en = expectedName;
-                if (en == null)
-                    return "java.lang.Object";
-                else
-                    return en;
-            }
+        public void setType(String s, ClassPool cp) throws BadBytecode {
+            array.setType(ArrayType.typeName(s), cp);
         }
 
-        public static String getArrayType(String elementType) {
-            if (elementType.charAt(0) == '[')
-                return "[" + elementType;
-            else
-                return "[L" + elementType.replace('.', '/') + ";";
+        protected TypeVar toTypeVar() { return array.toTypeVar(); }
+
+        public int dfs(ArrayList order, int index, ClassPool cp) throws NotFoundException {
+        	return array.dfs(order, index, cp);
         }
+    }
 
-        public static String getElementType(String arrayType) {
-            char c = arrayType.charAt(1);
-            if (c == 'L')
-                return arrayType.substring(2, arrayType.length() - 1).replace('/', '.');                    
-            else if (c == '[')
-                return arrayType.substring(1);
-            else
-                return arrayType;
+    /**
+     * Type data for OBJECT.
+     */
+    public static class ClassName extends TypeData {
+        private String name;    	// dot separated.
+
+        public ClassName(String n) {
+            name = n;
         }
+
+        public String getName() {
+            return name;
+        }
+
+        public BasicType isBasicType() { return null; }
+
+        public boolean is2WordType() { return false; }
+
+        public int getTypeTag() { return StackMapTable.OBJECT; }
+
+        public int getTypeData(ConstPool cp) {
+            return cp.addClassInfo(getName());
+        }
+
+        public boolean eq(TypeData d) { return name.equals(d.getName()); }
+
+        public void setType(String typeName, ClassPool cp) throws BadBytecode {}
     }
 
     /**
+     * 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");      // type name
+        }
+
+        public int getTypeTag() {
+            return StackMapTable.NULL;
+        }
+
+        public boolean isNullType() { return true; }
+        public int getTypeData(ConstPool cp) { return 0; }
+    }
+
+    /**
      * Type data for UNINIT.
      */
-    public static class UninitData extends TypeData {
-        String className;
+    public static class UninitData extends ClassName {
         int offset;
         boolean initialized;
 
         UninitData(int offset, String className) {
-            this.className = className;
+            super(className);
             this.offset = offset;
             this.initialized = false;
         }
 
-        public void merge(TypeData neighbor) {}
-
-        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 int getTypeTag() {
+            return StackMapTable.UNINIT;
         }
 
-        public TypeData getSelf() {
-            if (initialized)
-                return copy();
-            else
-                return this;
+        public int getTypeData(ConstPool cp) {
+            return offset;
         }
 
-        public TypeData copy() {
-            return new ClassName(className);
+        public TypeData join() {
+            TypeData td = initialized ? new ClassName(getName()) : this; 
+            return new TypeVar(td);
         }
 
-        public boolean isObjectType() { return true; }
+        public boolean isUninit() { return true; }
 
-        protected void setType(String typeName, ClassPool cp) throws BadBytecode {
-            initialized = true;
+        public boolean eq(TypeData d) {
+            if (d instanceof UninitData) {
+                UninitData ud = (UninitData)d;
+                return offset == ud.offset && getName().equals(ud.getName());
+            }
+            else
+                return false;
         }
 
-        public void evalExpectedType(ClassPool cp) throws BadBytecode {}
-
-        public String getName() {
-            return className;
+        public void setType(String typeName, ClassPool cp) throws BadBytecode {
+            super.setType(typeName, cp);
+            initialized = true;
         }
 
-        public String getExpected() { return className; }
-
-        public String toString() { return "uninit:" + className + "@" + offset; }
+        public String toString() { return "uninit:" + getName() + "@" + offset; }
     }
 
     public static class UninitThis extends UninitData {
@@ -521,11 +712,12 @@
             super(-1, className);
         }
 
-        public int getTypeTag() { return StackMapTable.THIS; }
-        public int getTypeData(ConstPool cp) { return 0; }
+        public int getTypeTag() {
+            return StackMapTable.THIS;
+        }
 
-        public boolean equals(Object obj) {
-            return obj instanceof UninitThis;
+        public int getTypeData(ConstPool cp) {
+            return 0;
         }
 
         public String toString() { return "uninit:this"; }

Modified: trunk/src/main/javassist/bytecode/stackmap/TypeTag.java
===================================================================
--- trunk/src/main/javassist/bytecode/stackmap/TypeTag.java	2012-09-14 12:06:49 UTC (rev 654)
+++ trunk/src/main/javassist/bytecode/stackmap/TypeTag.java	2012-09-14 15:22:13 UTC (rev 655)
@@ -19,7 +19,8 @@
 import javassist.bytecode.StackMapTable;
 
 public interface TypeTag {
-    TypeData TOP = null;
+    String TOP_TYPE = "*top*";
+    TypeData TOP = new TypeData.BasicType(TOP_TYPE, StackMapTable.TOP);
     TypeData INTEGER = new TypeData.BasicType("int", StackMapTable.INTEGER);
     TypeData FLOAT = new TypeData.BasicType("float", StackMapTable.FLOAT);
     TypeData DOUBLE = new TypeData.BasicType("double", StackMapTable.DOUBLE);

Modified: trunk/src/main/javassist/bytecode/stackmap/TypedBlock.java
===================================================================
--- trunk/src/main/javassist/bytecode/stackmap/TypedBlock.java	2012-09-14 12:06:49 UTC (rev 654)
+++ trunk/src/main/javassist/bytecode/stackmap/TypedBlock.java	2012-09-14 15:22:13 UTC (rev 655)
@@ -20,17 +20,11 @@
 
 public class TypedBlock extends BasicBlock {
     public int stackTop, numLocals;
-    public TypeData[] stackTypes, localsTypes;
+    // localsTypes is set to non-null when this block is first visited by a MapMaker.
+    // see alreadySet().
+    public TypeData[] localsTypes;
+    public TypeData[] stackTypes;
 
-    // set by a Liveness object.
-    // inputs[i] is true if the i-th variable is used within this block.  
-    public boolean[] inputs;
-
-    // working area for Liveness class. 
-    public boolean updating;
-    public int status;
-    public byte[] localsUsage;
-
     /**
      * Divides the method body into basic blocks.
      * The type information of the first block is initialized.
@@ -52,16 +46,12 @@
         blocks[0].initFirstBlock(ca.getMaxStack(), ca.getMaxLocals(),
                                  pool.getClassName(), minfo.getDescriptor(),
                                  isStatic, minfo.isConstructor());
-        new Liveness().compute(ca.iterator(), blocks, ca.getMaxLocals(),
-                               blocks[0].localsTypes);
         return blocks;
     }
 
     protected TypedBlock(int pos) {
         super(pos);
         localsTypes = null;
-        inputs = null;
-        updating = false;
     }
 
     protected void toString2(StringBuffer sbuf) {
@@ -70,11 +60,6 @@
         printTypes(sbuf, stackTop, stackTypes);
         sbuf.append("}, locals={");
         printTypes(sbuf, numLocals, localsTypes);
-        sbuf.append("}, inputs={");
-        if (inputs != null)
-            for (int i = 0; i < inputs.length; i++)
-                sbuf.append(inputs[i] ? "1, " : "0, ");
-
         sbuf.append('}');
     }
 
@@ -111,10 +96,9 @@
     public void resetNumLocals() {
         if (localsTypes != null) {
             int nl = localsTypes.length;
-            while (nl > 0 && localsTypes[nl - 1] == TypeTag.TOP) {
+            while (nl > 0 && localsTypes[nl - 1].isBasicType() == TypeTag.TOP) {
                 if (nl > 1) {
-                    TypeData td = localsTypes[nl - 2];
-                    if (td == TypeTag.LONG || td == TypeTag.DOUBLE)
+                    if (localsTypes[nl - 2].is2WordType())
                         break;
                 }
 
@@ -153,8 +137,8 @@
             throw new BadBytecode("no method descriptor: " + methodDesc);
 
         stackTop = 0;
-        stackTypes = new TypeData[maxStack];
-        TypeData[] locals = new TypeData[maxLocals];
+        stackTypes = TypeData.make(maxStack);
+        TypeData[] locals = TypeData.make(maxLocals);
         if (isConstructor)
             locals[0] = new TypeData.UninitThis(className);
         else if (!isStatic)

Modified: trunk/src/test/javassist/JvstTest.java
===================================================================
--- trunk/src/test/javassist/JvstTest.java	2012-09-14 12:06:49 UTC (rev 654)
+++ trunk/src/test/javassist/JvstTest.java	2012-09-14 15:22:13 UTC (rev 655)
@@ -1110,7 +1110,7 @@
         suite.addTestSuite(JvstTest4.class);
         suite.addTestSuite(LoaderTestByRandall.class);
         suite.addTestSuite(javassist.bytecode.BytecodeTest.class);
-        // suite.addTestSuite(javassist.bytecode.StackMapTest.class);
+        suite.addTestSuite(javassist.bytecode.StackMapTest.class);
         suite.addTestSuite(javassist.compiler.CompTest.class);
         suite.addTestSuite(javassist.SetterTest.class);
         suite.addTestSuite(javassist.bytecode.InsertGap0.class);

Modified: trunk/src/test/javassist/JvstTest4.java
===================================================================
--- trunk/src/test/javassist/JvstTest4.java	2012-09-14 12:06:49 UTC (rev 654)
+++ trunk/src/test/javassist/JvstTest4.java	2012-09-14 15:22:13 UTC (rev 655)
@@ -683,9 +683,10 @@
             }
         }
         System.gc();
+        System.gc();
         int size = javassist.compiler.MemberResolver.getInvalidMapSize();
         System.out.println("JIRA150b " + size);
-        assertTrue(size < N - 10);
+        assertTrue("JIRA150b size: " + size, size < N - 10);
     }
 
     public void testJIRA152() throws Exception {

Added: trunk/src/test/javassist/bytecode/StackMapTest.java
===================================================================
--- trunk/src/test/javassist/bytecode/StackMapTest.java	                        (rev 0)
+++ trunk/src/test/javassist/bytecode/StackMapTest.java	2012-09-14 15:22:13 UTC (rev 655)
@@ -0,0 +1,315 @@
+package javassist.bytecode;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.regex.Pattern;
+import java.util.regex.Matcher;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.lang.reflect.Method;
+
+import javassist.ClassPool;
+import javassist.CtClass;
+import javassist.CtMethod;
+import javassist.CtNewMethod;
+import javassist.JvstTest;
+import javassist.Loader;
+import javassist.bytecode.stackmap.MapMaker;
+import javassist.bytecode.stackmap.TypeData;
+import junit.framework.TestCase;
+
+public class StackMapTest extends TestCase {
+    public static final String PATH = JvstTest.PATH;
+    private ClassPool loader, dloader;
+    private Loader cloader;
+
+    public StackMapTest(String name) { super(name); }
+
+    protected void setUp() throws Exception {
+        loader = ClassPool.getDefault();
+        dloader = new ClassPool(null);
+        dloader.appendSystemPath();
+        dloader.insertClassPath(".");
+        cloader = new Loader(dloader);
+    }
+
+    protected Object make(String name) throws Exception {
+        return cloader.loadClass(name).newInstance();
+    }
+
+    protected int invoke(Object target, String method) throws Exception {
+        Method m = target.getClass().getMethod(method, new Class[0]);
+        Object res = m.invoke(target, new Object[0]);
+        return ((Integer)res).intValue();
+    }
+
+    protected static void rebuildStackMaps(CtClass cc) throws BadBytecode, IOException {
+        ClassPool cp = cc.getClassPool();
+        Iterator it = cc.getClassFile().getMethods().iterator();
+        while (it.hasNext()) {
+            MethodInfo minfo = (MethodInfo)it.next();
+            rebuildStackMap(cc, cp, minfo);
+        }
+    }
+
+    protected static void rebuildStackMap(CtClass cc, ClassPool cp, MethodInfo minfo) throws BadBytecode, IOException {
+        CodeAttribute ca = minfo.getCodeAttribute();
+        if (ca != null) {
+            StackMapTable smt = (StackMapTable)ca.getAttribute(StackMapTable.tag);
+            if (smt != null) {
+                String data = readSmt(smt);
+                StackMapTable smt2 = MapMaker.make(cp, minfo);
+                String data2 = readSmt(smt2);
+                try {
+                    assertEquals(cc.getName() + ":" + minfo.getName() + ":" + minfo.getDescriptor(),
+            	                     data, data2);
+            	}
+                catch (junit.framework.ComparisonFailure e) {
+                    System.out.println("*** " + cc.getName() + ":" + minfo.getName() + ":" + minfo.getDescriptor());
+                    smt.println(System.out);
+                    System.out.println("---");
+                    smt2.println(System.out);
+                }
+            }
+        }
+    }
+
+    protected static void rebuildStackMaps2(CtClass cc) throws BadBytecode {
+    	ClassPool cp = cc.getClassPool();
+        Iterator it = cc.getClassFile().getMethods().iterator();
+        while (it.hasNext()) {
+            MethodInfo minfo = (MethodInfo)it.next();
+            minfo.rebuildStackMap(cp);
+        }
+    }
+
+    protected static String readSmt(StackMapTable smt) throws BadBytecode, IOException {
+    	if (smt == null)
+    		return "";
+
+    	ByteArrayOutputStream out = new ByteArrayOutputStream();
+    	PrintStream pout = new PrintStream(out);
+    	smt.println(pout);
+    	pout.close();
+    	return out.toString();
+    }
+
+    public static void main(String[] args) throws Exception {
+        if (args.length == 2 && args[0].equals("-c")) { 
+            CtClass cc = ClassPool.getDefault().get(args[0].replace('/', '.'));
+            rebuildStackMaps(cc);   
+        }
+        else {
+            for (int i = 0; i < args.length; i++) {
+                CtClass cc = ClassPool.getDefault().get(getClassName(args[i]));
+                System.out.println(cc.getName());
+                rebuildStackMaps2(cc);
+                cc.writeFile("rebuild");
+            }
+        }
+    }
+
+    public static String getClassName(String fileName) {
+        Matcher m = Pattern.compile("(.*)\\.class").matcher(fileName);
+        if (m.matches())
+            return m.group(1).replace('/', '.');
+        else
+            return fileName;
+    }
+
+    public void testCommonSuper() throws Exception {
+        CtClass type = loader.get("javassist.CtClassType[]");
+        CtClass base = loader.get("javassist.CtClass[]");
+        CtClass array = loader.get("javassist.CtArray[]");
+        CtClass array2 = loader.get("javassist.CtArray[][]");
+        CtClass str = loader.get("java.lang.String[]");
+        CtClass strObj = loader.get("java.lang.String");
+        CtClass obj = loader.get("java.lang.Object[]");
+        CtClass objObj = loader.get("java.lang.Object");
+
+        assertEquals(base, TypeData.commonSuperClassEx(type, base));
+        assertEquals(base, TypeData.commonSuperClassEx(base, type));
+        assertEquals(base, TypeData.commonSuperClassEx(array, type));
+        assertEquals(obj, TypeData.commonSuperClassEx(base, str));
+        assertEquals(objObj, TypeData.commonSuperClassEx(strObj, str));
+        assertEquals(obj, TypeData.commonSuperClassEx(array, array2));
+        assertEquals(obj, TypeData.commonSuperClassEx(base, array2));
+    }
+
+    public void testRebuild() throws Exception {
+        CtClass cc = loader.get("javassist.bytecode.StackMapTest$T1");
+        rebuildStackMaps2(cc);
+        //Class c = cc.toClass();
+        //Object t1 = c.newInstance();
+        cc.writeFile();
+        Object t1 = make(cc.getName());
+        assertEquals(3, invoke(t1, "test"));
+    }
+
+    public static interface Intf {
+        int foo();
+    }
+
+    public static class C1 implements Intf {
+        public int foo() { return 0; }
+    }
+    
+    public static class C2 implements Intf {
+        public int foo() { return 3; }
+    }
+
+    public static class T1 {
+        public int test() {
+            return foo(-1);
+        }
+
+        public int foo(int i) {
+            Intf obj;
+            if (i > 0)
+                obj = new C1();
+            else
+                obj = new C2();
+
+            return obj.foo();
+        }
+    }
+
+    public void testRebuild2() throws Exception {
+        CtClass cc = loader.get("javassist.bytecode.StackMapTest$C3");
+        rebuildStackMaps2(cc);
+        cc.writeFile();
+        Object t1 = make(cc.getName());
+        assertEquals(7, invoke(t1, "test"));
+    }
+
+    public static class C3 {
+        int value;
+        public C3(int i) {
+            value = i;
+        }
+        public C3(boolean b) {
+            this(b ? 7 : 10);
+        }
+        public C3() { this(true); }
+        public int test() { return value; }
+    }
+
+    public void testRebuild3() throws Exception {
+        CtClass cc = loader.get("javassist.bytecode.StackMapTest$T3");
+        rebuildStackMaps2(cc);
+        cc.writeFile();
+        Object t1 = make(cc.getName());
+        assertEquals(1100, invoke(t1, "test"));
+    }
+
+    public static interface Intf2 {
+        int bar();
+    }
+
+    public static class D1 extends C1 implements Intf2 {
+        public int bar() { return 10; }
+    }
+
+    public static class D2 extends C1 implements Intf2 {
+        public int bar() { return 100; }
+    }
+
+    public static class T3 {
+        public int bar(Intf2 i) { return 1000; }
+
+        public int test() {
+            return foo(-1);
+        }
+
+        public int foo(int i) {
+            Intf2 obj;
+            if (i > 0)
+                obj = new D1();
+            else
+                obj = new D2();
+
+            System.out.println(obj.toString());
+            return obj.bar() + bar(obj);
+        }
+    }
+
+    public void testRebuildArray() throws Exception {
+        CtClass cc = loader.get("javassist.bytecode.StackMapTest$T4");
+        rebuildStackMaps2(cc);
+        cc.writeFile();
+        Object t1 = make(cc.getName());
+        assertEquals(30, invoke(t1, "test"));
+    }
+
+    public static class T4 {
+        public int test() {
+            return foo(3) + foo2(3) + foo3(3) + foo4(3);
+        }
+
+        public int foo(int i) {
+            C1[] a;
+            if (i > 0)
+                a = new D1[1];
+            else
+                a = new D2[1];
+
+            if (i > 0)
+                a[0] = new D1();
+            else
+                a[0] = new D2();
+
+            return a[0].foo();
+        }
+
+        public int foo2(int i) {
+            Intf2[] a;
+            if (i > 0)
+                a = new D1[1];
+            else
+                a = new D2[1];
+
+            if (i > 0)
+                a[0] = new D1();
+            else
+                a[0] = new D2();
+
+            return a[0].bar();
+        }
+
+        public int foo3(int i) {
+            Intf2[] a = null;
+            if (i > 0)
+                a = new D1[1];
+
+            a[0] = new D1();
+            return a[0].bar();
+        }
+
+        public int foo4(int i) {
+            Intf2[] a = new Intf2[1];
+            if (i > 0)
+            	a[0] = new D1();
+            return a[0].bar();
+        }
+    }
+
+    public void tstCtClassType() throws Exception {
+        ClassPool cp = ClassPool.getDefault();
+        CtClass cc = cp.get("javassist.CtClassType");
+        MethodInfo minfo = getMethodInfo(cc.getClassFile(), "getFields", "(Ljava/util/ArrayList;Ljavassist/CtClass;)V");
+        rebuildStackMap(cc, cp, minfo);
+    }
+
+    MethodInfo getMethodInfo(ClassFile cf, String name, String desc) {
+        List list = cf.getMethods();
+        Iterator it = list.iterator();
+        while (it.hasNext()) {
+            MethodInfo mi = (MethodInfo)it.next();
+            if (mi.getName().equals(name) && mi.getDescriptor().equals(desc))
+                return mi;
+        }
+
+        return null;
+    }
+}


Property changes on: trunk/src/test/javassist/bytecode/StackMapTest.java
___________________________________________________________________
Added: svn:mime-type
   + text/plain

Modified: trunk/src/test/javassist/tools/reflect/LoaderTest.java
===================================================================
--- trunk/src/test/javassist/tools/reflect/LoaderTest.java	2012-09-14 12:06:49 UTC (rev 654)
+++ trunk/src/test/javassist/tools/reflect/LoaderTest.java	2012-09-14 15:22:13 UTC (rev 655)
@@ -53,6 +53,10 @@
                               "javassist.tools.reflect.ClassMetaobject");
 
         ClassPool cp = ClassPool.getDefault();
+
+        CtClass cc2 = cp.get("javassist.tools.reflect.SuperClass");
+        cc2.debugWriteFile("reflected/");
+
         CtClass cc = cp.get("javassist.tools.reflect.SubClass");
 
         CtMethod[] ms = cc.getMethods();



More information about the jboss-cvs-commits mailing list