[jboss-cvs] javassist/src/main/javassist/bytecode/stackmap ...

Shigeru Chiba chiba at is.titech.ac.jp
Tue Apr 10 12:32:32 EDT 2007


  User: chiba   
  Date: 07/04/10 12:32:32

  Modified:    src/main/javassist/bytecode/stackmap        BasicBlock.java
  Added:       src/main/javassist/bytecode/stackmap        MapMaker.java
                        TypeTag.java TypeData.java Tracer.java
  Removed:     src/main/javassist/bytecode/stackmap       
                        StackAnalyzerCore.java StackAnalyzer.java
  Log:
  stackmap support (cont.)
  
  Revision  Changes    Path
  1.3       +283 -73   javassist/src/main/javassist/bytecode/stackmap/BasicBlock.java
  
  (In the diff below, changes in quantity of whitespace are not shown.)
  
  Index: BasicBlock.java
  ===================================================================
  RCS file: /cvsroot/jboss/javassist/src/main/javassist/bytecode/stackmap/BasicBlock.java,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -b -r1.2 -r1.3
  --- BasicBlock.java	9 Feb 2007 18:05:21 -0000	1.2
  +++ BasicBlock.java	10 Apr 2007 16:32:32 -0000	1.3
  @@ -1,55 +1,113 @@
  +/*
  + * Javassist, a Java-bytecode translator toolkit.
  + * Copyright (C) 1999-2006 Shigeru Chiba. All Rights Reserved.
  + *
  + * The contents of this file are subject to the Mozilla Public License Version
  + * 1.1 (the "License"); you may not use this file except in compliance with
  + * the License.  Alternatively, the contents of this file may be used under
  + * the terms of the GNU Lesser General Public License Version 2.1 or later.
  + *
  + * Software distributed under the License is distributed on an "AS IS" basis,
  + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  + * for the specific language governing rights and limitations under the
  + * License.
  + */
  +
   package javassist.bytecode.stackmap;
   
   import javassist.bytecode.*;
  +import java.util.ArrayList;
  +
  +public class BasicBlock implements TypeTag, Comparable {
   
  -public class BasicBlock {
  -    public int position;
  -    public int stackTop;
  -    public int[] stackTypes, localsTypes;
  -    public Object[] stackData, localsData;
  +    public int position, length;
  +    public int stackTop, numLocals;
  +    public TypeData[] stackTypes, localsTypes;
  +
  +    /* The number of the basic blocks from which a thread of control
  +     * may reach this basic block.  The number excludes the preceding
  +     * block.  Thus, if it is zero, a thread of control reaches
  +     * only from the preceding block.  Such a basic block represents
  +     * the boundary of a try block.
  +     */
  +    public int inbound;
  +
  +    /* public static void main(String[] args) throws Exception {
  +        BasicBlock b = new BasicBlock(0);
  +        b.initFirstBlock(8, 1, args[0], args[1], args[2].equals("static"), args[2].equals("const"));
  +        System.out.println(b);
  +    }*/
   
       private BasicBlock(int pos) {
           position = pos;
  +        length = 0;
  +        stackTop = numLocals = 0;
  +        stackTypes = localsTypes = null;
  +        inbound = 1;
  +    }
  +
  +    public boolean alreadySet() { return stackTypes != null; }
  +
  +    /*
  +     * Computes the correct value of numLocals.
  +     * It assumes that:
  +     *     correct numLocals <= current numLocals 
  +     */
  +    public void resetNumLocals() {
  +        if (localsTypes != null) {
  +            int nl = numLocals;
  +            while (nl > 0 && localsTypes[nl - 1] == TypeTag.TOP)
  +                --nl;
  +
  +            numLocals = nl;
  +        }
       }
   
  -    public void set(int st, int[] stypes, Object[] sdata, int[] ltypes, Object[] ldata)
  +    public void setStackMap(int st, TypeData[] stack,
  +                            int nl, TypeData[] locals)
           throws BadBytecode
       {
  -        if (stackTypes == null) {
               stackTop = st;
  -            stackTypes = copy(stypes);
  -            stackData = copy(sdata);
  -            localsTypes = copy(ltypes);
  -            localsData = copy(ldata);
  -        }
  -        else {
  -            if (st != stackTop)
  -                throw new BadBytecode("verification failure");
  +        stackTypes = stack;
  +        numLocals = nl;
  +        localsTypes = locals;
  +    }
  +
  +    private void updateLength(int nextPos) {
  +        length = nextPos - position;
  +    }
  +
  +    public String toString() {
  +        StringBuffer sbuf = new StringBuffer();
  +        sbuf.append("Block at ");
  +        sbuf.append(position);
  +        sbuf.append(" stack={");
  +        printTypes(sbuf, stackTop, stackTypes);
  +        sbuf.append("} locals={");
  +        printTypes(sbuf, numLocals, localsTypes);
  +        sbuf.append('}');
  +        return sbuf.toString();
  +    }
  +
  +    private static void printTypes(StringBuffer sbuf, int size,
  +                                   TypeData[] types) {
  +        if (types == null)
  +            return;
   
  -            int n = ltypes.length;
  -            for (int i = 0; i < n; i++)
  -                if (ltypes[i] != localsTypes[i]) {
  -                    localsTypes[i] = StackAnalyzerCore.EMPTY;
  -                    localsData[i] = null;
  -                }
  -                else if (ltypes[i] == StackAnalyzerCore.OBJECT
  -                         && !ldata[i].equals(localsData[i]))
  -                    ; // localsData[i] = ??;
  -        }
  -    }
  +        for (int i = 0; i < size; i++) {
  +            if (i > 0)
  +                sbuf.append(", ");
   
  -    private static int[] copy(int[] a) {
  -        int[] b = new int[a.length];
  -        System.arraycopy(a, 0, b, 0, a.length);
  -        return b;
  +            TypeData td = types[i];
  +            sbuf.append(td == null ? "<>" : td.toString());
       }
  -
  -    private static Object[] copy(Object[] a) {
  -        Object[] b = new Object[a.length];
  -        System.arraycopy(a, 0, b, 0, a.length);
  -        return b;
       }
   
  +    /**
  +     * Finds the basic block including the given position.
  +     *
  +     * @param pos       the position.
  +     */
       public static BasicBlock find(BasicBlock[] blocks, int pos) throws BadBytecode {
           int n = blocks.length;
           for (int i = 0; i < n; i++)
  @@ -59,102 +117,254 @@
           throw new BadBytecode("no basic block: " + pos);
       }
   
  -    public static BasicBlock[] makeBlocks(CodeIterator ci, ExceptionTable et)
  +    /**
  +     * Divides the given code fragment into basic blocks.
  +     */
  +    public static BasicBlock[] makeBlocks(MethodInfo minfo) throws BadBytecode {
  +        CodeAttribute ca = minfo.getCodeAttribute();
  +        CodeIterator ci = ca.iterator();
  +        ConstPool pool = minfo.getConstPool();
  +        BasicBlock[] blocks = makeBlocks(ci, 0, ci.getCodeLength(), ca.getExceptionTable(), 0, pool);
  +        boolean isStatic = (minfo.getAccessFlags() & AccessFlag.STATIC) != 0;
  +        blocks[0].initFirstBlock(ca.getMaxStack(), ca.getMaxLocals(),
  +                                 pool.getClassName(), minfo.getDescriptor(),
  +                                 isStatic, minfo.isConstructor());
  +        return blocks;
  +    }
  +
  +    /**
  +     * Divides the given code fragment into basic blocks.
  +     *
  +     * @param begin         the position where the basic block analysis starts. 
  +     * @param end           exclusive.
  +     * @param et            the appended exception table entries.
  +     * @param etOffset      the offset added to the handlerPc entries in the exception table.
  +     * @param pool          the constant pool.
  +     */
  +    public static BasicBlock[] makeBlocks(CodeIterator ci, int begin, int end,
  +                                           ExceptionTable et, int etOffset, ConstPool pool)
           throws BadBytecode
       {
           ci.begin();
  -        int[] targets = new int[16];
  -        int size = 0;
  +        ci.move(begin);
  +        ArrayList targets = new ArrayList();
  +        targets.add(new BasicBlock(begin));
           while (ci.hasNext()) {
               int index = ci.next();
  +            if (index >= end)
  +                break;
  +
               int op = ci.byteAt(index);
               if ((Opcode.IFEQ <= op && op <= Opcode.IF_ACMPNE)
                   || op == Opcode.IFNULL || op == Opcode.IFNONNULL)
  -                targets = add(targets, size++, index + ci.s16bitAt(index + 1));
  +                targets.add(new BasicBlock(index + ci.s16bitAt(index + 1)));
               else if (Opcode.GOTO <= op && op <= Opcode.LOOKUPSWITCH)
                   switch (op) {
                   case Opcode.GOTO :
  -                    targets = add(targets, size++, index + ci.s16bitAt(index + 1));
  -                    break;
                   case Opcode.JSR :
  -                case Opcode.RET :
  -                    throw new BadBytecode("jsr/ret at " + index);
  +                    targets.add(new BasicBlock(index + ci.s16bitAt(index + 1)));
  +                    break;
  +                // case Opcode.RET :
  +                //    throw new BadBytecode("ret at " + index);
                   case Opcode.TABLESWITCH : {
                       int pos = (index & ~3) + 4;
  -                    targets = add(targets, size++, index + ci.s32bitAt(pos));   // default offset
  +                    targets.add(new BasicBlock(index + ci.s32bitAt(pos)));   // default branch target
                       int low = ci.s32bitAt(pos + 4);
                       int high = ci.s32bitAt(pos + 8);
                       int p = pos + 12;
                       int n = p + (high - low + 1) * 4;
                       while (p < n) {
  -                        targets = add(targets, size++, index + ci.s32bitAt(p));
  +                        targets.add(new BasicBlock(index + ci.s32bitAt(p)));
                           p += 4;
                       }
                       break; }
                   case Opcode.LOOKUPSWITCH : {
                       int pos = (index & ~3) + 4;
  -                    targets = add(targets, size++, index + ci.s32bitAt(pos));   // default offset
  +                    targets.add(new BasicBlock(index + ci.s32bitAt(pos)));   // default branch target
                       int p = pos + 8 + 4;
                       int n = p + ci.s32bitAt(pos + 4) * 8;
                       while (p < n) {
  -                        targets = add(targets, size++, index + ci.s32bitAt(p));
  +                        targets.add(new BasicBlock(index + ci.s32bitAt(p)));
                           p += 8;
                       }
                       break; }
                   }
  -            else if (op == Opcode.GOTO_W)
  -                targets = add(targets, size++, index + ci.s32bitAt(index + 1));
  -            else if (op == Opcode.JSR_W)
  -                throw new BadBytecode("jsr_w at " + index);
  +            else if (op == Opcode.GOTO_W || op == Opcode.JSR_W)
  +                targets.add(new BasicBlock(index + ci.s32bitAt(index + 1)));
           }
   
           if (et != null) {
               int i = et.size();
               while (--i >= 0) {
  -                targets = add(targets, size++, et.startPc(i));
  -                targets = add(targets, size++, et.handlerPc(i));
  +                BasicBlock bb = new BasicBlock(et.startPc(i) + etOffset);
  +                bb.inbound = 0;
  +                targets.add(bb);
  +                targets.add(new BasicBlock(et.handlerPc(i) + etOffset));
               }
           }
   
  -        return trimArray(targets);
  +        return trimArray(targets, end);
       }
   
  -    private static int[] add(int[] targets, int size, int value) {
  -        if (targets.length >= size) {
  -            int[] a = new int[size << 1];
  -            System.arraycopy(targets, 0, a, 0, targets.length);
  -            targets = a;
  +    public int compareTo(Object obj) {
  +        if (obj instanceof BasicBlock) {
  +            int pos = ((BasicBlock)obj).position;
  +            return position - pos;
           }
   
  -        targets[size++] = value;
  -        return targets;
  +        return -1;
       }
   
  -    private static BasicBlock[] trimArray(int[] targets) {
  -        int size = targets.length;
  -        java.util.Arrays.sort(targets);
  +    /**
  +     * @param endPos        exclusive
  +     */
  +    private static BasicBlock[] trimArray(ArrayList targets, int endPos) {
  +        Object[] targetArray = targets.toArray();
  +        int size = targetArray.length;
  +        java.util.Arrays.sort(targetArray);
           int s = 0;
  -        int t0 = 0;
  +        int t0 = -1;
           for (int i = 0; i < size; i++) {
  -            int t = targets[i];
  +            int t = ((BasicBlock)targetArray[i]).position;
               if (t != t0) {
                   s++;
                   t0 = t;
               }
           }
   
  -        BasicBlock[] results = new BasicBlock[s + 1];
  -        results[0] = new BasicBlock(0);
  -        t0 = 0;
  -        for (int i = 0, j = 1; i < size; i++) { 
  -            int t = targets[i];
  -            if (t != t0) {
  -                BasicBlock b = new BasicBlock(t);
  -                results[j++] = b;
  +        BasicBlock[] results = new BasicBlock[s];
  +        BasicBlock bb0 = (BasicBlock)targetArray[0];
  +        results[0] = bb0;
  +        t0 = bb0.position;
  +        int j = 1;
  +        for (int i = 1; i < size; i++) {
  +            BasicBlock bb = (BasicBlock)targetArray[i];
  +            int t = bb.position;
  +            if (t == t0)
  +                results[j - 1].inbound += bb.inbound;
  +            else {
  +                results[j - 1].updateLength(t);
  +                results[j++] = bb;
                   t0 = t;
               }
           }
   
  +        results[j - 1].updateLength(endPos);
           return results;
       }
  +
  +    /**
  +     * Initializes the first block by the given method descriptor.
  +     *
  +     * @param block             the first basic block that this method initializes.
  +     * @param className         a dot-separated fully qualified class name.
  +     *                          For example, <code>javassist.bytecode.stackmap.BasicBlock</code>.
  +     * @param methodDesc        method descriptor.
  +     * @param isStatic          true if the method is a static method.
  +     * @param isConstructor     true if the method is a constructor.
  +     */
  +    void initFirstBlock(int maxStack, int maxLocals, String className,
  +                        String methodDesc, boolean isStatic, boolean isConstructor)
  +        throws BadBytecode
  +    {
  +        if (methodDesc.charAt(0) != '(')
  +            throw new BadBytecode("no method descriptor: " + methodDesc);
  +
  +        stackTop = 0;
  +        stackTypes = new TypeData[maxStack];
  +        TypeData[] locals = new TypeData[maxLocals];
  +        if (isConstructor)
  +            locals[0] = new TypeData.UninitThis(className);
  +        else if (!isStatic)
  +            locals[0] = new TypeData.ClassName(className);
  +
  +        int n = isStatic ? -1 : 0;
  +        int i = 1;
  +        do {
  +            try {
  +                i = descToTag(methodDesc, i, ++n, locals);
  +            }
  +            catch (StringIndexOutOfBoundsException e) {
  +                throw new BadBytecode("bad method descriptor: "
  +                                      + methodDesc);
  +            }
  +        } while (i > 0);
  +
  +        numLocals = n;
  +        localsTypes = locals;
  +        position = 0;
  +        inbound = 0;
  +    }
  +
  +    private static int descToTag(String desc, int i,
  +                                 int n, TypeData[] types)
  +        throws BadBytecode
  +    {
  +        int i0 = i;
  +        int arrayDim = 0;
  +        char c = desc.charAt(i);
  +        if (c == ')')
  +            return 0;
  +
  +        while (c == '[') {
  +            ++arrayDim;
  +            c = desc.charAt(++i);
  +        }
  +
  +        if (c == 'L') {
  +            int i2 = desc.indexOf(';', ++i);
  +            if (arrayDim > 0)
  +                types[n] = new TypeData.ClassName(desc.substring(i0, ++i2));
  +            else
  +                types[n] = new TypeData.ClassName(desc.substring(i0 + 1, ++i2 - 1)
  +                                                      .replace('/', '.'));
  +            return i2;
  +        }
  +        else if (arrayDim > 0) {
  +            types[n] = new TypeData.ClassName(desc.substring(i0, ++i));
  +            return i;
  +        }
  +        else {
  +            TypeData t = toPrimitiveTag(c);
  +            if (t == null)
  +                throw new BadBytecode("bad method descriptor: " + desc);
  +
  +            types[n] = t;
  +            return i + 1;
  +        }
  +    }
  +
  +    private static TypeData toPrimitiveTag(char c) {
  +        switch (c) {
  +        case 'Z' :
  +        case 'C' :
  +        case 'B' :
  +        case 'S' :
  +        case 'I' :
  +            return INTEGER;
  +        case 'J' :
  +            return LONG;
  +        case 'F' :
  +            return FLOAT;
  +        case 'D' :
  +            return DOUBLE;
  +        case 'V' :
  +        default :
  +            return null;
  +        }
  +    }
  +
  +    public static String getRetType(String desc) {
  +        int i = desc.indexOf(')');
  +        if (i < 0)
  +            return "java.lang.Object";
  +
  +        char c = desc.charAt(i + 1);
  +        if (c == '[')
  +            return desc.substring(i + 1);
  +        else if (c == 'L')
  +            return desc.substring(i + 2, desc.length() - 1).replace('/', '.');
  +        else
  +            return "java.lang.Object";
  +    }
   }
  
  
  
  1.1      date: 2007/04/10 16:32:32;  author: chiba;  state: Exp;javassist/src/main/javassist/bytecode/stackmap/MapMaker.java
  
  Index: MapMaker.java
  ===================================================================
  /*
   * Javassist, a Java-bytecode translator toolkit.
   * Copyright (C) 1999-2006 Shigeru Chiba. All Rights Reserved.
   *
   * The contents of this file are subject to the Mozilla Public License Version
   * 1.1 (the "License"); you may not use this file except in compliance with
   * the License.  Alternatively, the contents of this file may be used under
   * the terms of the GNU Lesser General Public License Version 2.1 or later.
   *
   * Software distributed under the License is distributed on an "AS IS" basis,
   * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
   * for the specific language governing rights and limitations under the
   * License.
   */
  
  package javassist.bytecode.stackmap;
  
  import javassist.ClassPool;
  import javassist.bytecode.*;
  
  /**
   * Stack map maker.
   */
  public class MapMaker extends Tracer {
      private boolean moveon;
      private BasicBlock[] blocks;
  
      public static void main(String[] args) throws Exception {
          if (args.length > 1) {
              main2(args);
              return;
          }
  
          ClassPool cp = ClassPool.getDefault();
          javassist.CtClass cc = cp.get(args[0]);
          ClassFile cf = cc.getClassFile();
          java.util.List minfos = cf.getMethods();
          for (int i = 0; i < minfos.size(); i++) {
              MethodInfo minfo = (MethodInfo)minfos.get(i);
              CodeAttribute ca = minfo.getCodeAttribute();
              ca.setAttribute(MapMaker.getMap(cp, minfo)); 
          }
  
          cc.writeFile("tmp");
      }
  
      public static void main2(String[] args) throws Exception {
          ClassPool cp = ClassPool.getDefault();
          javassist.CtClass cc = cp.get(args[0]);
          MapMaker mm;
          if (args[1].equals("_init_"))
              mm = makeMapMaker(cp, cc.getDeclaredConstructors()[0].getMethodInfo());
          else
              mm = makeMapMaker(cp, cc.getDeclaredMethod(args[1]).getMethodInfo());
  
          if (mm == null)
              System.out.println("single basic block");
          else {
              BasicBlock[] blocks = mm.getBlocks();
              for (int i = 0; i < blocks.length; i++)
                  System.out.println(blocks[i]);
          }
      }
  
      /**
       * Computes the stack map table of the given method and returns it.
       * It returns null if the given method does not have to have a
       * stack map table.
       */
      public static StackMapTable getMap(ClassPool classes, MethodInfo minfo)
          throws BadBytecode
      {
          MapMaker mm = makeMapMaker(classes, minfo);
          if (mm == null)
              return null;
          else
              return mm.toStackMap();
      }
  
      /*
       * Makes basic blocks with stack maps.  If the number of the basic blocks
       * is one, this method returns null.
       */
      public static MapMaker makeMapMaker(ClassPool classes, MethodInfo minfo)
          throws BadBytecode
      {
          CodeAttribute ca = minfo.getCodeAttribute();
          CodeIterator ci = ca.iterator();
          ConstPool pool = minfo.getConstPool();
          ExceptionTable et = ca.getExceptionTable();
          BasicBlock[] blocks = BasicBlock.makeBlocks(ci, 0, ci.getCodeLength(),
                                                      et, 0, pool);
          if (blocks.length < 2)
              return null;
  
          boolean isStatic = (minfo.getAccessFlags() & AccessFlag.STATIC) != 0;
          int maxStack = ca.getMaxStack();
          int maxLocals = ca.getMaxLocals();
          BasicBlock top = blocks[0];
          String desc = minfo.getDescriptor();
          top.initFirstBlock(maxStack, maxLocals, pool.getClassName(), desc,
                             isStatic, minfo.isConstructor());
          String retType = BasicBlock.getRetType(desc);
          MapMaker mm = new MapMaker(classes, pool, maxStack, maxLocals,
                                     blocks, retType, blocks[0]);
          mm.make(ca.getCode(), et);
          return mm;
      }
  
      /**
       * Constructs a tracer.
       */
      MapMaker(ClassPool classes, ConstPool cp,
                      int maxStack, int maxLocals, BasicBlock[] bb,
                      String retType, BasicBlock init) {
          this(classes, cp, maxStack, maxLocals, bb, retType);
          TypeData[] srcTypes = init.localsTypes;
          copyFrom(srcTypes.length, srcTypes, this.localsTypes);
      }
  
      private MapMaker(ClassPool classes, ConstPool cp,
              int maxStack, int maxLocals, BasicBlock[] bb,
              String retType)
      {
          super(classes, cp, maxStack, maxLocals, retType);
          blocks = bb;
      }
  
      public BasicBlock[] getBlocks() { return blocks; }
  
      /**
       * Runs an analyzer.
       */
      void make(byte[] code, ExceptionTable et) throws BadBytecode {
          make(code, blocks[0]);
          traceExceptions(code, et);
          int n = blocks.length;
          for (int i = 0; i < n; i++)
              evalExpected(blocks[i]);
      }
  
      private void traceExceptions(byte[] code, ExceptionTable et)
          throws BadBytecode
      {
          int n = et.size();
          for (int i = 0; i < n; i++) {
              int startPc = et.startPc(i);
              int handlerPc = et.handlerPc(i);
              BasicBlock handler = BasicBlock.find(blocks, handlerPc);
              if (handler.alreadySet())
                  continue;
  
              BasicBlock thrower = BasicBlock.find(blocks, startPc);
              TypeData[] srcTypes = thrower.localsTypes;
              copyFrom(srcTypes.length, srcTypes, this.localsTypes);
              int typeIndex = et.catchType(i);
              String type;
              if (typeIndex == 0)
                  type = "java.lang.Throwable";
              else
                  type = cpool.getClassInfo(typeIndex);
  
              stackTop = 1;
              stackTypes[0] = new TypeData.ClassName(type);
              recordStackMap(handler);
              make(code, handler);
          }
      }
  
      // Phase 1: Code Tracing
  
      private void make(byte[] code, BasicBlock bb)
          throws BadBytecode
      {
          int pos = bb.position;
          int end = pos + bb.length;
          moveon = true;
          while (moveon && pos < end)
              pos += doOpcode(pos, code);
  
          if (moveon && pos < code.length) {
              this.copyFrom(this);
              nextBlock(pos, code, 0);
          }
      }
  
      private void nextBlock(int pos, byte[] code, int offset) throws BadBytecode {
          BasicBlock bb = BasicBlock.find(blocks, pos + offset);
          if (bb.alreadySet()) {
              mergeMap(stackTypes, bb.stackTypes);
              mergeMap(localsTypes, bb.localsTypes);
          }
          else {
              recordStackMap(bb);
              MapMaker maker = new MapMaker(classPool, cpool, stackTypes.length,
                                            localsTypes.length, blocks, returnType);
              maker.copyFrom(this);
              maker.make(code, bb);
          }
      }
  
      private static void mergeMap(TypeData[] srcTypes, TypeData[] destTypes) {
          int n = srcTypes.length;
          for (int i = 0; i < n; i++) {
              TypeData s = srcTypes[i];
              TypeData d = destTypes[i];
              boolean sIsObj = false;
              boolean dIsObj = false;
              // s or b is null if it is TOP. 
              if (s != TOP && s.isObjectType())
                  sIsObj = true;
  
              if (d != TOP && d.isObjectType())
                  dIsObj = true;
  
              if (sIsObj && dIsObj)
                  d.merge(s);
              else if (s != d)
                  destTypes[i] = TOP;
          }
      }
  
      private void copyFrom(MapMaker src) {
          int sp = src.stackTop;
          this.stackTop = sp;
          copyFrom(sp, src.stackTypes, this.stackTypes);
          TypeData[] srcTypes = src.localsTypes;
          copyFrom(srcTypes.length, srcTypes, this.localsTypes);
      }
  
      private static int copyFrom(int n, TypeData[] srcTypes, TypeData[] destTypes) {
          int k = -1;
          for (int i = 0; i < n; i++) {
              TypeData t = srcTypes[i];
              destTypes[i] = t == null ? null : t.getSelf();
              if (t != TOP)
                  k = i;
          }
  
          return k + 1;
      }
  
      private void recordStackMap(BasicBlock target)
          throws BadBytecode
      {
          int n = localsTypes.length;
          TypeData[] tLocalsTypes = new TypeData[n];
          int k = copyFrom(n, localsTypes, tLocalsTypes);
  
          n = stackTypes.length;
          TypeData[] tStackTypes = new TypeData[n];
          int st = stackTop;
          copyFrom(st, stackTypes, tStackTypes);
  
          target.setStackMap(st, tStackTypes, k, tLocalsTypes);
      }
  
      // Phase 2
  
      void evalExpected(BasicBlock target) throws BadBytecode {
          ClassPool cp = classPool;
          evalExpected(cp, target.stackTop, target.stackTypes);
          TypeData[] types = target.localsTypes;
          evalExpected(cp, types.length, types);
      }
  
      private static void evalExpected(ClassPool cp, int n, TypeData[] types)
          throws BadBytecode
      {
          for (int i = 0; i < n; i++) {
              TypeData td = types[i];
              if (td != null)
                  td.evalExpectedType(cp);
          }
      }
  
      // Phase 3
  
      public StackMapTable toStackMap() {
          BasicBlock[] blocks = this.blocks;
          StackMapTable.Writer writer = new StackMapTable.Writer(32);
          int n = blocks.length;
          BasicBlock prev = blocks[0];
          int offsetDelta = prev.length;
          for (int i = 1; i < n; i++) {
              BasicBlock bb = blocks[i];
              if (bb.inbound > 0) {
                  bb.resetNumLocals();
                  int diffL = stackMapDiff(prev.numLocals, prev.localsTypes,
                                           bb.numLocals, bb.localsTypes);
                  toStackMapBody(writer, bb, diffL, offsetDelta);
                  offsetDelta = bb.length - 1;
                  prev = bb;
              }
              else
                  offsetDelta += bb.length;
  
          }
  
          return writer.toStackMapTable(cpool);
      }
  
      private void toStackMapBody(StackMapTable.Writer writer, BasicBlock bb,
                                  int diffL, int offsetDelta) {
          // if diffL is -100, two TypeData arrays do not share
          // any elements.
  
          int stackTop = bb.stackTop;
          if (stackTop == 0) {
              if (diffL == 0) {
                  writer.sameFrame(offsetDelta);
                  return;
              }
              else if (0 > diffL && diffL >= -3) {
                  writer.chopFrame(offsetDelta, -diffL);
                  return;
              }
              else if (0 < diffL && diffL <= 3) {
                  int[] tags = new int[diffL];
                  int[] data = new int[diffL];
                  fillStackMap(diffL, bb.numLocals - diffL, tags, data,
                               bb.localsTypes);
                  writer.appendFrame(offsetDelta, tags, data);
                  return;
              }
          }
          else if (stackTop == 1 && diffL == 0) {
              TypeData[] types = bb.stackTypes;
              TypeData td = types[0];
              if (td == TOP)
                  writer.sameLocals(offsetDelta, StackMapTable.TOP, 0);
              else
                  writer.sameLocals(offsetDelta, td.getTypeTag(),
                                    td.getTypeData(cpool));
              return;
          }
  
          int[] stags = new int[stackTop];
          int[] sdata = new int[stackTop];
          int nl = bb.numLocals;
          int[] ltags = new int[nl];
          int[] ldata = new int[nl];
          fillStackMap(stackTop, 0, stags, sdata, bb.stackTypes);
          fillStackMap(nl, 0, ltags, ldata, bb.localsTypes);
          writer.fullFrame(offsetDelta, ltags, ldata, stags, sdata);
      }
  
      private void fillStackMap(int num, int offset, int[] tags, int[] data, TypeData[] types) {
          ConstPool cp = cpool;
          for (int i = 0; i < num; i++) {
              TypeData td = types[offset + i];
              if (td == TOP) {
                  tags[i] = StackMapTable.TOP;
                  data[i] = 0;
              }
              else {
                  tags[i] = td.getTypeTag();
                  data[i] = td.getTypeData(cp);
              }
          }
      }
  
      private static int stackMapDiff(int oldTdLen, TypeData[] oldTd,
                                      int newTdLen, TypeData[] newTd)
      {
          int diff = newTdLen - oldTdLen;
          int len;
          if (diff > 0)
              len = oldTdLen;
          else
              len = newTdLen;
  
          if (stackMapEq(oldTd, newTd, len))
              return diff;
          else
              return -100;
      }
  
      private static boolean stackMapEq(TypeData[] oldTd, TypeData[] newTd, int len) {
          for (int i = 0; i < len; i++) {
              TypeData td = oldTd[i];
              if (td == TOP) {
                  if (newTd[i] != TOP)
                      return false;
              }
              else
                  if (!oldTd[i].equals(newTd[i]))
                      return false;
          }
  
          return true;
      }
  
      // Branch actions
  
      protected void visitBranch(int pos, byte[] code, int offset) throws BadBytecode {
          nextBlock(pos, code, offset);
      }
  
      protected void visitGoto(int pos, byte[] code, int offset) throws BadBytecode {
          nextBlock(pos, code, offset);
          moveon = false;
      }
  
      protected void visitTableSwitch(int pos, byte[] code, int n, int offsetPos, int defaultOffset)
          throws BadBytecode
      {
          nextBlock(pos, code, defaultOffset);
          for (int i = 0; i < n; i++) {
              nextBlock(pos, code, ByteArray.read32bit(code, offsetPos));
              offsetPos += 4;
          }
  
          moveon = false;
      }
  
      protected void visitLookupSwitch(int pos, byte[] code, int n, int pairsPos, int defaultOffset)
          throws BadBytecode
      {
          nextBlock(pos, code, defaultOffset);
          pairsPos += 4;
          for (int i = 0; i < n; i++) {
              nextBlock(pos, code, ByteArray.read32bit(code, pairsPos));
              pairsPos += 8;
          }
  
          moveon = false;
      }
  
      protected void visitReturn(int pos, byte[] code) { moveon = false; }
  
      protected void visitThrow(int pos, byte[] code) { moveon = false; }
  }
  
  
  
  1.1      date: 2007/04/10 16:32:32;  author: chiba;  state: Exp;javassist/src/main/javassist/bytecode/stackmap/TypeTag.java
  
  Index: TypeTag.java
  ===================================================================
  /*
   * Javassist, a Java-bytecode translator toolkit.
   * Copyright (C) 1999-2006 Shigeru Chiba. All Rights Reserved.
   *
   * The contents of this file are subject to the Mozilla Public License Version
   * 1.1 (the "License"); you may not use this file except in compliance with
   * the License.  Alternatively, the contents of this file may be used under
   * the terms of the GNU Lesser General Public License Version 2.1 or later.
   *
   * Software distributed under the License is distributed on an "AS IS" basis,
   * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
   * for the specific language governing rights and limitations under the
   * License.
   */
  
  package javassist.bytecode.stackmap;
  
  import javassist.bytecode.StackMapTable;
  
  public interface TypeTag {
      TypeData TOP = null;
      TypeData INTEGER = new TypeData.BasicType("int", StackMapTable.INTEGER);
      TypeData FLOAT = new TypeData.BasicType("float", StackMapTable.FLOAT);
      TypeData DOUBLE = new TypeData.BasicType("double", StackMapTable.DOUBLE);
      TypeData LONG = new TypeData.BasicType("long", StackMapTable.LONG);
  
      // and NULL, THIS, OBJECT, UNINIT
  }
  
  
  
  1.1      date: 2007/04/10 16:32:32;  author: chiba;  state: Exp;javassist/src/main/javassist/bytecode/stackmap/TypeData.java
  
  Index: TypeData.java
  ===================================================================
  /*
   * Javassist, a Java-bytecode translator toolkit.
   * Copyright (C) 1999-2006 Shigeru Chiba. All Rights Reserved.
   *
   * The contents of this file are subject to the Mozilla Public License Version
   * 1.1 (the "License"); you may not use this file except in compliance with
   * the License.  Alternatively, the contents of this file may be used under
   * the terms of the GNU Lesser General Public License Version 2.1 or later.
   *
   * Software distributed under the License is distributed on an "AS IS" basis,
   * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
   * for the specific language governing rights and limitations under the
   * License.
   */
  
  package javassist.bytecode.stackmap;
  
  import javassist.ClassPool;
  import javassist.CtClass;
  import javassist.NotFoundException;
  import javassist.bytecode.ConstPool;
  import javassist.bytecode.StackMapTable;
  import javassist.bytecode.BadBytecode;
  import java.util.ArrayList;
  
  public abstract class TypeData {
      /* Memo:
       * array type is a subtype of Cloneable and Serializable 
       */
  
      protected ArrayList equivalences;
  
      protected TypeData() {
          equivalences = new ArrayList();
          equivalences.add(this);
      }
  
      public void merge(TypeData neighbor) {
          if (this == neighbor)
              return;
  
          ArrayList list = equivalences;
          ArrayList list2 = neighbor.equivalences;
          if (list == list2)
              return;
  
          int n = list2.size();
          for (int i = 0; i < n; i++) {
              TypeData td = (TypeData)list2.get(i);
              add(list, td);
              td.equivalences = list;
          }
      }
  
      private static void add(ArrayList list, TypeData td) {
          int n = list.size();
          for (int i = 0; i < n; i++)
              if (list.get(i) == td)
                  return;
  
          list.add(td);
      }
  
      /**
       * Sets the type name of this object type.  If the given type name is
       * a subclass of the current type name, then the given name becomes
       * the name of this object type.
       *
       * @param className     dot-separated name unless the type is an array type. 
       */
      static void setType(TypeData td, String className, ClassPool cp) throws BadBytecode {
          if (td == null)
              throw new BadBytecode("unset variable");
          else
              td.setType(className, cp);
      }
  
      public abstract boolean equals(Object obj);
  
      public abstract int getTypeTag();
      public abstract int getTypeData(ConstPool cp);
  
      /*
       * See UninitData.getSelf().
       */
      public TypeData getSelf() { return this; } 
  
      /* An operand value is copied when it is stored in a
       * local variable.
       */
      public abstract TypeData copy();
  
      public boolean isBasicType() { return false; }
      public boolean isObjectType() { return false; }
      public boolean isNullType() { return false; }
  
      public abstract String getName() throws BadBytecode;
      protected abstract void setType(String s, ClassPool cp) throws BadBytecode;
      public abstract void evalExpectedType(ClassPool cp) throws BadBytecode;
      protected abstract boolean hasExpectedType();   // only TypeName can return true.
      public abstract String getExpected() throws BadBytecode;
  
      /**
       * Primitive types.
       */
      protected static class BasicType extends TypeData {
          private String name;
          private int typeTag;
  
          public BasicType(String type, int tag) {
              name = type;
              typeTag = tag;
          }
  
          public boolean equals(Object obj) {
              return this == obj;
          }
  
          public int getTypeTag() { return typeTag; }
          public int getTypeData(ConstPool cp) { return 0; }
  
          public boolean isBasicType() { return true; }
  
          public TypeData copy() {
              return this;
          }
  
          public void evalExpectedType(ClassPool cp) throws BadBytecode {}
  
          public String getExpected() throws BadBytecode {
              return name;
          }
  
          public String getName() {
              return name;
          }
  
          protected boolean hasExpectedType() {
              return false;
          }
  
          protected void setType(String s, ClassPool cp) throws BadBytecode {
              throw new BadBytecode("conflict:" + name + " and " + s);
          }
  
          public String toString() { return name; }
      }
  
      protected static abstract class TypeName extends TypeData {
          private String expectedName;
          private CtClass cache;
          private boolean evalDone;
  
          protected TypeName() {
              expectedName = null;
              cache = null;
              evalDone = false;
          }
  
          /* NullType overrides this method.
           */
          public int getTypeTag() { return StackMapTable.OBJECT; }
  
          public int getTypeData(ConstPool cp) {
              String type;
              try {
                  type = getExpected();
              } catch (BadBytecode e) {
                  throw new RuntimeException("fatal error: ", e); 
              }
  
              return getTypeData2(cp, type);
          }
  
          /* NullType overrides this method.
           */
          protected int getTypeData2(ConstPool cp, String type) {
              return cp.addClassInfo(type);
          }
  
          public boolean equals(Object obj) {
              if (obj instanceof TypeName) {
                  try {
                      TypeName tn = (TypeName)obj;
                      return getExpected().equals(tn.getExpected());
                  }
                  catch (BadBytecode e) {}
              }
  
              return false;
          }
  
          public boolean isObjectType() { return true; }
  
          protected void setType(String typeName, ClassPool cp) throws BadBytecode {
              if (update(cp, expectedName, typeName))
                  expectedName = typeName;
          }
  
          public void evalExpectedType(ClassPool cp) throws BadBytecode {
              if (this.evalDone)
                  return;
  
              ArrayList equiv = this.equivalences;
              String name = this.expectedName;
              int n = equiv.size();
              for (int i = 0; i < n; i++) {
                  TypeData td = (TypeData)equiv.get(i);
                  if (td.hasExpectedType()) {
                      TypeName tn = (TypeName)td;
                      if (update(cp, name, tn.expectedName))
                          name = tn.expectedName;
                  }
              }
  
              if (name == null)
                  name = evalExpectedType2(equivalences, n);
  
              for (int i = 0; i < n; i++) {
                  TypeData td = (TypeData)equiv.get(i);
                  if (td.hasExpectedType()) {
                      TypeName tn = (TypeName)td;
                      tn.expectedName = name;
                      tn.cache = null;
                      tn.evalDone = true;
                  }
              }
          }
  
          private String evalExpectedType2(ArrayList equiv, int n) throws BadBytecode {
              String origName = null;
              for (int i = 0; i < n; i++) {
                  TypeData td = (TypeData)equiv.get(i);
                  if (!td.isNullType())
                      if (origName == null)
                          origName = td.getName();
                      else if (!origName.equals(td.getName()))
                          return null;
              }
  
              return origName;
          }
  
          protected boolean hasExpectedType() { return true; }
  
          private boolean update(ClassPool cp, String oldName, String typeName) throws BadBytecode {
              if (typeName == null)
                  return false;
              else if (oldName == null)
                  return true;
              else if (oldName.equals(typeName))
                  return false;
  
              try {
                  if (cache == null)
                      cache = cp.get(oldName);
      
                  CtClass cache2 = cp.get(typeName);
                  if (cache2.subclassOf(cache)) {
                      cache = cache2;
                      return true;
                  }
                  else
                      return false;
              }
              catch (NotFoundException e) {
                  throw new BadBytecode("cannot find " + e.getMessage());
              }
          }
  
          public String getExpected() throws BadBytecode {
              ArrayList equiv = equivalences;
              if (equiv.size() == 1)
                  return getName();
              else {
                  String en = expectedName;
                  if (en == null)
                      return "java.lang.Object";
                  else
                      return en;
              }
          }
  
          public String toString() {
              try {
                  String en = expectedName;
                  if (en != null)
                      return en;
  
                  String name = getName();
                  if (equivalences.size() == 1)
                      return name;
                  else
                      return name + "?";
              }
              catch (BadBytecode e) {
                  return "<" + e.getMessage() + ">";
              }
          }
      }
  
      /**
       * Type data for OBJECT.
       */
      public static class ClassName extends TypeName {
          private String name;    // dot separated.  null if this object is a copy of another.
      
          public ClassName(String n) {
              name = n;
          }
  
          public TypeData copy() {
              return new ClassName(name);
          }
  
          public String getName() {   // never returns null.
              return name;
          }
      }
  
      /**
       * Type data for NULL or OBJECT.
       * The types represented by the instances of this class are
       * initially NULL but will be OBJECT.
       */
      public static class NullType extends ClassName {
          public NullType() {
              super("null");      // type name
          }
  
          public TypeData copy() {
              return new NullType();
          }
  
          public boolean isNullType() { return true; }
  
          public int getTypeTag() {
              try {
                  if ("null".equals(getExpected()))
                      return StackMapTable.NULL;
                  else
                      return super.getTypeTag();
              }
              catch (BadBytecode e) {
                  throw new RuntimeException("fatal error: ", e); 
              }
          }
  
          protected int getTypeData2(ConstPool cp, String type) {
              if ("null".equals(type))
                  return 0;
              else
                  return super.getTypeData2(cp, type);
          }
      }
  
      /**
       * Type data for OBJECT if the type is an object type and is
       * derived as an element type from an array type by AALOAD.
       */
      public static class ArrayElement extends TypeName {
          TypeData array;
      
          public ArrayElement(TypeData a) {   // a is never null
              array = a;
          }
  
          public TypeData copy() {
              return new ArrayElement(array);
          }
  
          protected void setType(String typeName, ClassPool cp) throws BadBytecode {
              super.setType(typeName, cp);
              array.setType(getArrayType(typeName), cp);
          }
  
          public String getName() throws BadBytecode {
              String name = array.getName();
              if (name.length() > 1 && name.charAt(0) == '[') {
                  char c = name.charAt(1);
                  if (c == 'L')
                      return name.substring(2, name.length() - 1).replace('/', '.');                    
                  else if (c == '[')
                      return name.substring(1);
              }
      
              throw new BadBytecode("bad array type for AALOAD: "
                                    + name);
          }
  
          public static String getArrayType(String elementType) {
              if (elementType.charAt(0) == '[')
                  return "[" + elementType;
              else
                  return "[L" + elementType.replace('.', '/') + ";";
          }
      }
  
      /**
       * Type data for UNINIT.
       */
      public static class UninitData extends TypeData {
          String className;
          int offset;
          boolean initialized;
  
          UninitData(int offset, String className) {
              this.className = className;
              this.offset = offset;
              this.initialized = false;
          }
  
          public int getTypeTag() { return StackMapTable.UNINIT; }
          public int getTypeData(ConstPool cp) { return offset; }
  
          public boolean equals(Object obj) {
              if (obj instanceof UninitData) {
                  UninitData ud = (UninitData)obj;
                  return offset == ud.offset && className.equals(ud.className);
              }
              else
                  return false;
          }
  
          public TypeData getSelf() {
              if (initialized)
                  return copy();
              else
                  return this;
          }
  
          public TypeData copy() {
              return new ClassName(className);
          }
  
          protected void setType(String typeName, ClassPool cp) throws BadBytecode {
              initialized = true;
          }
  
          public void evalExpectedType(ClassPool cp) throws BadBytecode {}
  
          protected boolean hasExpectedType() { return false; }
  
          public String getName() {
              return className;
          }
  
          public String getExpected() { return className; }
  
          public String toString() { return "uninit:" + className + "@" + offset; }
      }
  
      public static class UninitThis extends UninitData {
          UninitThis(String className) {
              super(-1, className);
          }
  
          public int getTypeTag() { return StackMapTable.THIS; }
          public int getTypeData(ConstPool cp) { return 0; }
  
          public boolean equals(Object obj) {
              return obj instanceof UninitThis;
          }
  
          public String toString() { return "uninit:this"; }
      }
  }
  
  
  
  1.1      date: 2007/04/10 16:32:32;  author: chiba;  state: Exp;javassist/src/main/javassist/bytecode/stackmap/Tracer.java
  
  Index: Tracer.java
  ===================================================================
  /*
   * Javassist, a Java-bytecode translator toolkit.
   * Copyright (C) 1999-2006 Shigeru Chiba. All Rights Reserved.
   *
   * The contents of this file are subject to the Mozilla Public License Version
   * 1.1 (the "License"); you may not use this file except in compliance with
   * the License.  Alternatively, the contents of this file may be used under
   * the terms of the GNU Lesser General Public License Version 2.1 or later.
   *
   * Software distributed under the License is distributed on an "AS IS" basis,
   * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
   * for the specific language governing rights and limitations under the
   * License.
   */
  
  package javassist.bytecode.stackmap;
  
  import javassist.bytecode.ByteArray;
  import javassist.bytecode.Opcode;
  import javassist.bytecode.ConstPool;
  import javassist.bytecode.Descriptor;
  import javassist.bytecode.BadBytecode;
  import javassist.ClassPool;
  
  /*
   * A class for performing abstract interpretation.
   * See also MapMaker class. 
   */
  
  public abstract class Tracer implements TypeTag {
      protected ClassPool classPool;
      protected ConstPool cpool;
      protected String returnType;
  
      protected int stackTop;
      protected TypeData[] stackTypes;
      protected TypeData[] localsTypes;
  
      public Tracer(ClassPool classes, ConstPool cp, int maxStack, int maxLocals,
                    String retType) {
          classPool = classes;
          cpool = cp;
          returnType = retType;
          stackTop = 0;
          stackTypes = new TypeData[maxStack];
          localsTypes = new TypeData[maxLocals];
      }
  
      /* If the type is LONG or DOUBLE,
       * the next local variable is also read.
       * IINC (or WIDE IINC) calls only readLocal() but it does not call writeLocal().
       */
      private void readLocal(int reg) {}
  
      private void writeLocal(int reg) {}
  
      /**
       * Does abstract interpretation on the given bytecode instruction.
       * It records whether or not a local variable (i.e. register) is accessed.
       * If the instruction requires that a local variable or
       * a stack element has a more specific type, this method updates the
       * type of it.
       *
       * @pos         the position of the instruction.
       * @return      the size of the instruction at POS.
       */
      protected int doOpcode(int pos, byte[] code) throws BadBytecode {
          int op = code[pos] & 0xff;
          if (op < 96)
              if (op < 54)
                  return doOpcode0_53(pos, code, op);
              else
                  return doOpcode54_95(pos, code, op);
          else
              if (op < 148)
                  return doOpcode96_147(pos, code, op);
              else
                  return doOpcode148_201(pos, code, op);
      }
  
      protected void visitBranch(int pos, byte[] code, int offset) throws BadBytecode {}
      protected void visitGoto(int pos, byte[] code, int offset) throws BadBytecode {}
      protected void visitReturn(int pos, byte[] code) throws BadBytecode {}
      protected void visitThrow(int pos, byte[] code) throws BadBytecode {}
  
      /**
       * @param pos           the position of TABLESWITCH
       * @param code          bytecode
       * @param n             the number of case labels
       * @param offsetPos     the position of the branch-target table.
       * @param defaultOffset     the offset to the default branch target.
       */
      protected void visitTableSwitch(int pos, byte[] code, int n,
                  int offsetPos, int defaultOffset) throws BadBytecode {}
  
      /**
       * @param pos           the position of LOOKUPSWITCH
       * @param code          bytecode
       * @param n             the number of case labels
       * @param offsetPos     the position of the table of pairs of a value and a branch target.
       * @param defaultOffset     the offset to the default branch target.
       */
      protected void visitLookupSwitch(int pos, byte[] code, int n,
                  int pairsPos, int defaultOffset) throws BadBytecode {}
  
      /**
       * Invoked when the visited instruction is jsr.
       */
      protected void visitJSR(int pos, byte[] code) throws BadBytecode {
          throwBadBytecode(pos, "jsr");
      }
  
      /**
       * Invoked when the visited instruction is ret or wide ret.
       */
      protected void visitRET(int pos, byte[] code) throws BadBytecode {
          throwBadBytecode(pos, "ret");
      }
  
      private void throwBadBytecode(int pos, String name) throws BadBytecode {
          throw new BadBytecode(name + " at " + pos);
      }
  
      private int doOpcode0_53(int pos, byte[] code, int op) throws BadBytecode {
          int reg;
          TypeData[] stackTypes = this.stackTypes;
          switch (op) {
          case Opcode.NOP :
              break;
          case Opcode.ACONST_NULL :
              stackTypes[stackTop++] = new TypeData.NullType();
              break;
          case Opcode.ICONST_M1 :
          case Opcode.ICONST_0 :
          case Opcode.ICONST_1 :
          case Opcode.ICONST_2 :
          case Opcode.ICONST_3 :
          case Opcode.ICONST_4 :
          case Opcode.ICONST_5 :
              stackTypes[stackTop++] = INTEGER;
              break;
          case Opcode.LCONST_0 :
          case Opcode.LCONST_1 :
              stackTypes[stackTop++] = LONG;
              stackTypes[stackTop++] = TOP;
              break;
          case Opcode.FCONST_0 :
          case Opcode.FCONST_1 :
          case Opcode.FCONST_2 :
              stackTypes[stackTop++] = FLOAT;
              break;
          case Opcode.DCONST_0 :
          case Opcode.DCONST_1 :
              stackTypes[stackTop++] = DOUBLE;
              stackTypes[stackTop++] = TOP;
              break;
          case Opcode.BIPUSH :
          case Opcode.SIPUSH :
              stackTypes[stackTop++] = INTEGER;
              return op == Opcode.SIPUSH ? 3 : 2;
          case Opcode.LDC :
              doLDC(code[pos + 1] & 0xff);
              return 2;
          case Opcode.LDC_W :
          case Opcode.LDC2_W :
              doLDC(ByteArray.readU16bit(code, pos + 1));
              return 3;
          case Opcode.ILOAD :
              return doXLOAD(INTEGER, code, pos);
          case Opcode.LLOAD :
              return doXLOAD(LONG, code, pos);
          case Opcode.FLOAD :
              return doXLOAD(FLOAT, code, pos);
          case Opcode.DLOAD :
              return doXLOAD(DOUBLE, code, pos);
          case Opcode.ALOAD :
              return doALOAD(code[pos + 1] & 0xff);
          case Opcode.ILOAD_0 :
          case Opcode.ILOAD_1 :
          case Opcode.ILOAD_2 :
          case Opcode.ILOAD_3 :
              stackTypes[stackTop++] = INTEGER;
              readLocal(op - Opcode.ILOAD_0);
              break;
          case Opcode.LLOAD_0 :
          case Opcode.LLOAD_1 :
          case Opcode.LLOAD_2 :
          case Opcode.LLOAD_3 :
              stackTypes[stackTop++] = LONG;
              stackTypes[stackTop++] = TOP;
              readLocal(op - Opcode.LLOAD_0);
              break;
          case Opcode.FLOAD_0 :
          case Opcode.FLOAD_1 :
          case Opcode.FLOAD_2 :
          case Opcode.FLOAD_3 :
              stackTypes[stackTop++] = FLOAT;
              readLocal(op - Opcode.FLOAD_0);
              break;
          case Opcode.DLOAD_0 :
          case Opcode.DLOAD_1 :
          case Opcode.DLOAD_2 :
          case Opcode.DLOAD_3 :
              stackTypes[stackTop++] = DOUBLE;
              stackTypes[stackTop++] = TOP;
              readLocal(op - Opcode.DLOAD_0);
              break;
          case Opcode.ALOAD_0 :
          case Opcode.ALOAD_1 :
          case Opcode.ALOAD_2 :
          case Opcode.ALOAD_3 :
              reg = op - Opcode.ALOAD_0;
              stackTypes[stackTop++] = localsTypes[reg];
              readLocal(reg);
              break;
          case Opcode.IALOAD :
              stackTypes[--stackTop - 1] = INTEGER;
              break;
          case Opcode.LALOAD :
              stackTypes[stackTop - 2] = LONG;
              stackTypes[stackTop - 1] = TOP;
              break;
          case Opcode.FALOAD :
              stackTypes[--stackTop - 1] = FLOAT;
              break;
          case Opcode.DALOAD :
              stackTypes[stackTop - 2] = DOUBLE;
              stackTypes[stackTop - 1] = TOP;
              break;
          case Opcode.AALOAD : {
              int s = --stackTop - 1;
              TypeData data = stackTypes[s];
              if (data == null || data.isBasicType())
                  throw new BadBytecode("bad AALOAD");
              else
                  stackTypes[s] = new TypeData.ArrayElement(data);
  
              break; }
          case Opcode.BALOAD :
          case Opcode.CALOAD :
          case Opcode.SALOAD :
              stackTypes[--stackTop - 1] = INTEGER;
              break;
          default :
              throw new RuntimeException("fatal");
          }
  
          return 1;
      }
  
      private void doLDC(int index) {
          TypeData[] stackTypes = this.stackTypes;
          int tag = cpool.getTag(index);
          if (tag == ConstPool.CONST_String)
              stackTypes[stackTop++] = new TypeData.ClassName("java.lang.String");
          else if (tag == ConstPool.CONST_Integer)
              stackTypes[stackTop++] = INTEGER;
          else if (tag == ConstPool.CONST_Float)
              stackTypes[stackTop++] = FLOAT;
          else if (tag == ConstPool.CONST_Long) {
              stackTypes[stackTop++] = LONG;
              stackTypes[stackTop++] = TOP;
          }
          else if (tag == ConstPool.CONST_Double) {
              stackTypes[stackTop++] = DOUBLE;
              stackTypes[stackTop++] = TOP;
          }
          else if (tag == ConstPool.CONST_Class)
              stackTypes[stackTop++] = new TypeData.ClassName("java.lang.Class");
          else
              throw new RuntimeException("bad LDC: " + tag);
      }
  
      private int doXLOAD(TypeData type, byte[] code, int pos) {
          int localVar = code[pos + 1] & 0xff;
          return doXLOAD(localVar, type);
      }
  
      private int doXLOAD(int localVar, TypeData type) {
          stackTypes[stackTop++] = type;
          if (type == LONG || type == DOUBLE)
              stackTypes[stackTop++] = TOP;
  
          readLocal(localVar);
          return 2;
      }
  
      private int doALOAD(int localVar) { // int localVar, TypeData type) {
          stackTypes[stackTop++] = localsTypes[localVar];
          readLocal(localVar);
          return 2;
      }
  
      private int doOpcode54_95(int pos, byte[] code, int op) {
          TypeData[] localsTypes = this.localsTypes;
          TypeData[] stackTypes = this.stackTypes;
          switch (op) {
          case Opcode.ISTORE :
              return doXSTORE(pos, code, INTEGER);
          case Opcode.LSTORE :
              return doXSTORE(pos, code, LONG);
          case Opcode.FSTORE :
              return doXSTORE(pos, code, FLOAT);
          case Opcode.DSTORE :
              return doXSTORE(pos, code, DOUBLE);
          case Opcode.ASTORE :
              return doASTORE(code[pos + 1] & 0xff);
          case Opcode.ISTORE_0 :
          case Opcode.ISTORE_1 :
          case Opcode.ISTORE_2 :
          case Opcode.ISTORE_3 :
            { int var = op - Opcode.ISTORE_0;
              localsTypes[var] = INTEGER;
              writeLocal(var);
              stackTop--; }
              break;
          case Opcode.LSTORE_0 :
          case Opcode.LSTORE_1 :
          case Opcode.LSTORE_2 :
          case Opcode.LSTORE_3 :
            { int var = op - Opcode.LSTORE_0;
              localsTypes[var] = LONG;
              localsTypes[var + 1] = TOP;
              writeLocal(var);
              stackTop -= 2; }
              break;
          case Opcode.FSTORE_0 :
          case Opcode.FSTORE_1 :
          case Opcode.FSTORE_2 :
          case Opcode.FSTORE_3 :
            { int var = op - Opcode.FSTORE_0;
              localsTypes[var] = FLOAT;
              writeLocal(var);
              stackTop--; }
              break;
          case Opcode.DSTORE_0 :
          case Opcode.DSTORE_1 :
          case Opcode.DSTORE_2 :
          case Opcode.DSTORE_3 :
            { int var = op - Opcode.DSTORE_0;
              localsTypes[var] = DOUBLE;
              localsTypes[var + 1] = TOP;
              writeLocal(var);
              stackTop -= 2; }
              break;
          case Opcode.ASTORE_0 :
          case Opcode.ASTORE_1 :
          case Opcode.ASTORE_2 :
          case Opcode.ASTORE_3 :
            { int var = op - Opcode.ASTORE_0;
              doASTORE(var);
              break; }
          case Opcode.IASTORE :
          case Opcode.LASTORE :
          case Opcode.FASTORE :
          case Opcode.DASTORE :
          case Opcode.AASTORE :
          case Opcode.BASTORE :
          case Opcode.CASTORE :
          case Opcode.SASTORE :
              stackTop -= (op == Opcode.LASTORE || op == Opcode.DASTORE) ? 4 : 3;
              break;
          case Opcode.POP :
              stackTop--;
              break;
          case Opcode.POP2 :
              stackTop -= 2;
              break;
          case Opcode.DUP : {
              int sp = stackTop;
              stackTypes[sp] = stackTypes[sp - 1];
              stackTop = sp + 1;
              break; }
          case Opcode.DUP_X1 :
          case Opcode.DUP_X2 : {
              int len = op - Opcode.DUP_X1 + 2;
              doDUP_XX(1, len);
              int sp = stackTop;
              stackTypes[sp - len] = stackTypes[sp];
              stackTop = sp + 1;
              break; }
          case Opcode.DUP2 :
              doDUP_XX(2, 2);
              stackTop += 2;
              break;
          case Opcode.DUP2_X1 :
          case Opcode.DUP2_X2 : {
              int len = op - Opcode.DUP2_X1 + 3;
              doDUP_XX(2, len);
              int sp = stackTop;
              stackTypes[sp - len] = stackTypes[sp];
              stackTypes[sp - len + 1] = stackTypes[sp + 1];
              stackTop = sp + 2; 
              break; }
          case Opcode.SWAP : {
              int sp = stackTop - 1;
              TypeData t = stackTypes[sp];
              stackTypes[sp] = stackTypes[sp - 1];
              stackTypes[sp - 1] = t;
              break; }
          default :
              throw new RuntimeException("fatal");
          }
  
          return 1;
      }
  
      private int doXSTORE(int pos, byte[] code, TypeData type) {
          int index = code[pos + 1] & 0xff;
          return doXSTORE(index, type);
      }
  
      private int doXSTORE(int index, TypeData type) {
          stackTop--;
          writeLocal(index);
          localsTypes[index] = type;
          if (type == LONG || type == DOUBLE) {
              stackTop--;
              localsTypes[index + 1] = TOP;
          }
  
          return 2;
      }
  
      private int doASTORE(int index) {
          stackTop--;
          writeLocal(index);
          // implicit upcast might be done.
          localsTypes[index] = stackTypes[stackTop].copy();
  
          return 2;
      }
  
      private void doDUP_XX(int delta, int len) {
          TypeData types[] = stackTypes;
          int sp = stackTop;
          int end = sp - len;
          while (sp > end) {
              types[sp + delta] = types[sp];
              sp--;
          }
      }
  
      private int doOpcode96_147(int pos, byte[] code, int op) {
          if (op <= Opcode.LXOR) {    // IADD...LXOR
              stackTop += Opcode.STACK_GROW[op];
              return 1;
          }
  
          switch (op) {
          case Opcode.IINC :
              readLocal(code[pos + 1] & 0xff);
              // this does not call writeLocal().
              return 3;
          case Opcode.I2L :
              stackTypes[stackTop] = LONG;
              stackTypes[stackTop - 1] = TOP;
              stackTop++;
              break;
          case Opcode.I2F :
              stackTypes[stackTop - 1] = FLOAT;
              break;
          case Opcode.I2D :
              stackTypes[stackTop] = DOUBLE;
              stackTypes[stackTop - 1] = TOP;
              stackTop++;
              break;
          case Opcode.L2I :
              stackTypes[--stackTop - 1] = INTEGER;
              break;
          case Opcode.L2F :
              stackTypes[--stackTop - 1] = FLOAT;
              break;
          case Opcode.L2D :
              stackTypes[stackTop - 1] = DOUBLE;
              break;
          case Opcode.F2I :
              stackTypes[stackTop - 1] = INTEGER;
              break;
          case Opcode.F2L :
              stackTypes[stackTop - 1] = TOP;
              stackTypes[stackTop++] = LONG;
              break;
          case Opcode.F2D :
              stackTypes[stackTop - 1] = TOP;
              stackTypes[stackTop++] = DOUBLE;
              break;
          case Opcode.D2I :
              stackTypes[--stackTop - 1] = INTEGER;
              break;
          case Opcode.D2L :
              stackTypes[stackTop - 1] = LONG;
              break;
          case Opcode.D2F :
              stackTypes[--stackTop - 1] = FLOAT;
              break;
          case Opcode.I2B :
          case Opcode.I2C :
          case Opcode.I2S :
              break;
          default :
              throw new RuntimeException("fatal");
          }
  
          return 1;
      }
  
      private int doOpcode148_201(int pos, byte[] code, int op) throws BadBytecode {
          switch (op) {
          case Opcode.LCMP :
              stackTypes[stackTop - 4] = INTEGER;
              stackTop -= 3;
              break;
          case Opcode.FCMPL :
          case Opcode.FCMPG :
              stackTypes[--stackTop - 1] = INTEGER;
              break;
          case Opcode.DCMPL :
          case Opcode.DCMPG :
              stackTypes[stackTop - 4] = INTEGER;
              stackTop -= 3;
              break;
          case Opcode.IFEQ :
          case Opcode.IFNE :
          case Opcode.IFLT :
          case Opcode.IFGE :
          case Opcode.IFGT :
          case Opcode.IFLE :
              stackTop--;     // branch
              visitBranch(pos, code, ByteArray.readS16bit(code, pos + 1));
              return 3;
          case Opcode.IF_ICMPEQ :
          case Opcode.IF_ICMPNE :
          case Opcode.IF_ICMPLT :
          case Opcode.IF_ICMPGE :
          case Opcode.IF_ICMPGT :
          case Opcode.IF_ICMPLE :
          case Opcode.IF_ACMPEQ :
          case Opcode.IF_ACMPNE :
              stackTop -= 2;  // branch
              visitBranch(pos, code, ByteArray.readS16bit(code, pos + 1));
              return 3;
          case Opcode.GOTO :
              visitGoto(pos, code, ByteArray.readS16bit(code, pos + 1));
              return 3;       // branch
          case Opcode.JSR :
              stackTypes[stackTop++] = TOP;       // not allowed?
              visitJSR(pos, code);
              return 3;       // branch
          case Opcode.RET :
              visitRET(pos, code);
              return 2;                           // not allowed?
          case Opcode.TABLESWITCH : {
              stackTop--;     // branch
              int pos2 = (pos & ~3) + 8;
              int low = ByteArray.read32bit(code, pos2);
              int high = ByteArray.read32bit(code, pos2 + 4);
              int n = high - low + 1;
              visitTableSwitch(pos, code, n, pos2 + 8, ByteArray.read32bit(code, pos2 - 4));
              return n * 4 + 16 - (pos & 3); }
          case Opcode.LOOKUPSWITCH : {
              stackTop--;     // branch
              int pos2 = (pos & ~3) + 8;
              int n = ByteArray.read32bit(code, pos2);
              visitLookupSwitch(pos, code, n, pos2 + 4, ByteArray.read32bit(code, pos2 - 4));
              return n * 8 + 12 - (pos & 3); }
          case Opcode.IRETURN :
              stackTop--;
              visitReturn(pos, code);
              break;
          case Opcode.LRETURN :
              stackTop -= 2;
              visitReturn(pos, code);
              break;
          case Opcode.FRETURN :
              stackTop--;
              visitReturn(pos, code);
              break;
          case Opcode.DRETURN :
              stackTop -= 2;
              visitReturn(pos, code);
              break;
          case Opcode.ARETURN :
              TypeData.setType(stackTypes[--stackTop], returnType, classPool);
              visitReturn(pos, code);
              break;
          case Opcode.RETURN :
              visitReturn(pos, code);
              break;
          case Opcode.GETSTATIC :
              return doGetField(pos, code, false);
          case Opcode.PUTSTATIC :
              return doPutField(pos, code, false);
          case Opcode.GETFIELD :
              return doGetField(pos, code, true);
          case Opcode.PUTFIELD :
              return doPutField(pos, code, true);
          case Opcode.INVOKEVIRTUAL :
          case Opcode.INVOKESPECIAL :
              return doInvokeMethod(pos, code, true);
          case Opcode.INVOKESTATIC :
              return doInvokeMethod(pos, code, false);
          case Opcode.INVOKEINTERFACE :
              return doInvokeIntfMethod(pos, code);
          case 186 :
              throw new RuntimeException("bad opcode 186");
          case Opcode.NEW : {
              int i = ByteArray.readU16bit(code, pos + 1);
              stackTypes[stackTop++]
                        = new TypeData.UninitData(pos, cpool.getClassInfo(i));
              return 3; }
          case Opcode.NEWARRAY :
              return doNEWARRAY(pos, code);
          case Opcode.ANEWARRAY : {
              int i = ByteArray.readU16bit(code, pos + 1);
              String type = cpool.getClassInfo(i).replace('.', '/');
              stackTypes[stackTop - 1]
                      = new TypeData.ClassName("[L" + type + ";");
              return 3; }
          case Opcode.ARRAYLENGTH :
              stackTypes[stackTop - 1] = INTEGER;
              break;
          case Opcode.ATHROW :
              TypeData.setType(stackTypes[--stackTop], "java.lang.Throwable", classPool);
              visitThrow(pos, code);
              break;
          case Opcode.CHECKCAST : {
              // TypeData.setType(stackData[stackTop - 1], "java.lang.Object", classPool);
              int i = ByteArray.readU16bit(code, pos + 1);
              stackTypes[stackTop - 1] = new TypeData.ClassName(cpool.getClassInfo(i));
              return 3; }
          case Opcode.INSTANCEOF :
              // TypeData.setType(stackData[stackTop - 1], "java.lang.Object", classPool);
              stackTypes[stackTop - 1] = INTEGER;
              return 3;
          case Opcode.MONITORENTER :
          case Opcode.MONITOREXIT :
              stackTop--;
              break;
          case Opcode.WIDE :
              return doWIDE(pos, code);
          case Opcode.MULTIANEWARRAY :
              return doMultiANewArray(pos, code);
          case Opcode.IFNULL :
          case Opcode.IFNONNULL :
              stackTop--;         // branch
              visitBranch(pos, code, ByteArray.readS16bit(code, pos + 1));
              return 3;
          case Opcode.GOTO_W :
              visitGoto(pos, code, ByteArray.read32bit(code, pos + 1));
              return 5;           // branch
          case Opcode.JSR_W :
              stackTypes[stackTop++] = TOP;       // not allowed?
              visitJSR(pos, code);
              return 5;
          }
          return 1;
      }
  
      private int doWIDE(int pos, byte[] code) throws BadBytecode {
          int op = code[pos + 1] & 0xff;
          switch (op) {
          case Opcode.ILOAD :
              doWIDE_XLOAD(pos, code, INTEGER);
              break;
          case Opcode.LLOAD :
              doWIDE_XLOAD(pos, code, LONG);
              break;
          case Opcode.FLOAD :
              doWIDE_XLOAD(pos, code, FLOAT);
              break;
          case Opcode.DLOAD :
              doWIDE_XLOAD(pos, code, DOUBLE);
              break;
          case Opcode.ALOAD : {
              int index = ByteArray.readU16bit(code, pos + 2);
              doALOAD(index);
              break; }
          case Opcode.ISTORE :
              return doWIDE_STORE(pos, code, INTEGER);
          case Opcode.LSTORE :
              return doWIDE_STORE(pos, code, LONG);
          case Opcode.FSTORE :
              return doWIDE_STORE(pos, code, FLOAT);
          case Opcode.DSTORE :
              return doWIDE_STORE(pos, code, DOUBLE);
          case Opcode.ASTORE : {
              int index = ByteArray.readU16bit(code, pos + 2);
              return doASTORE(index); }
          case Opcode.IINC :
              readLocal(ByteArray.readU16bit(code, pos + 2));
              // this does not call writeLocal().
              return 6;
          case Opcode.RET :
              visitRET(pos, code);
              break;
          default :
              throw new RuntimeException("bad WIDE instruction: " + op);
          }
  
          return 4;
      }
  
      private int doWIDE_XLOAD(int pos, byte[] code, TypeData type) {
          int index = ByteArray.readU16bit(code, pos + 2);
          return doXLOAD(index, type);
      }
  
      private int doWIDE_STORE(int pos, byte[] code, TypeData type) {
          int index = ByteArray.readU16bit(code, pos + 2);
          return doXSTORE(index, type);
      }
  
      private int doPutField(int pos, byte[] code, boolean notStatic) throws BadBytecode {
          int index = ByteArray.readU16bit(code, pos + 1);
          String desc = cpool.getFieldrefType(index);
          stackTop -= Descriptor.dataSize(desc);
          char c = desc.charAt(0);
          if (c == 'L')
              TypeData.setType(stackTypes[stackTop], getFieldClassName(desc, 0), classPool);
          else if (c == '[')
              TypeData.setType(stackTypes[stackTop], desc, classPool);
  
          setFieldTarget(notStatic, index);
          return 3;
      }
  
      private int doGetField(int pos, byte[] code, boolean notStatic) throws BadBytecode {
          int index = ByteArray.readU16bit(code, pos + 1);
          setFieldTarget(notStatic, index);
          String desc = cpool.getFieldrefType(index);
          pushMemberType(desc);
          return 3;
      }
  
      private void setFieldTarget(boolean notStatic, int index) throws BadBytecode {
          if (notStatic) {
              String className = cpool.getFieldrefClassName(index);
              TypeData.setType(stackTypes[--stackTop], className, classPool);
          }
      }
  
      private int doNEWARRAY(int pos, byte[] code) {
          int s = stackTop - 1;
          String type;
          switch (code[pos + 1] & 0xff) {
          case Opcode.T_BOOLEAN :
              type = "[Z";
              break;
          case Opcode.T_CHAR :
              type = "[C";
              break;
          case Opcode.T_FLOAT :
              type = "[F";
              break;
          case Opcode.T_DOUBLE :
              type = "[D";
              break;
          case Opcode.T_BYTE :
              type = "[B";
              break;
          case Opcode.T_SHORT :
              type = "[S";
              break;
          case Opcode.T_INT :
              type = "[I";
              break;
          case Opcode.T_LONG :
              type = "[J";
              break;
          default :
              throw new RuntimeException("bad newarray");
          }
  
          stackTypes[s] = new TypeData.ClassName(type);
          return 2;
      }
  
      private int doMultiANewArray(int pos, byte[] code) {
          int i = ByteArray.readU16bit(code, pos + 1);
          int dim = code[pos + 3] & 0xff;
          stackTop -= dim - 1;
  
          String type = cpool.getClassInfo(i).replace('.', '/');
          stackTypes[stackTop - 1] = new TypeData.ClassName(type);
          return 4;
      }
  
      private int doInvokeMethod(int pos, byte[] code, boolean notStatic) throws BadBytecode {
          int i = ByteArray.readU16bit(code, pos + 1);
          String desc = cpool.getMethodrefType(i);
          checkParamTypes(desc, 1);
          if (notStatic) {
              String className = cpool.getMethodrefClassName(i);
              TypeData.setType(stackTypes[--stackTop], className, classPool);
          }
  
          pushMemberType(desc);
          return 3;
      }
  
      private int doInvokeIntfMethod(int pos, byte[] code) throws BadBytecode {
          int i = ByteArray.readU16bit(code, pos + 1);
          String desc = cpool.getInterfaceMethodrefType(i);
          checkParamTypes(desc, 1);
          String className = cpool.getInterfaceMethodrefClassName(i);
          TypeData.setType(stackTypes[--stackTop], className, classPool);
          pushMemberType(desc);
          return 5;
      }
  
      private void pushMemberType(String descriptor) {
          int top = 0;
          if (descriptor.charAt(0) == '(') {
              top = descriptor.indexOf(')') + 1;
              if (top < 1)
                  throw new IndexOutOfBoundsException("bad descriptor: "
                                                      + descriptor);
          }
  
          TypeData[] types = stackTypes;
          int index = stackTop;
          switch (descriptor.charAt(top)) {
          case '[' :
              types[index] = new TypeData.ClassName(descriptor.substring(top));
              break;
          case 'L' :
              types[index] = new TypeData.ClassName(getFieldClassName(descriptor, top));
              break;
          case 'J' :
              types[index] = LONG;
              types[index + 1] = TOP;
              stackTop += 2;
              return;
          case 'F' :
              types[index] = FLOAT;
              break;
          case 'D' :
              types[index] = DOUBLE;
              types[index + 1] = TOP;
              stackTop += 2;
              return;
          case 'V' :
              return;
          default : // C, B, S, I, Z
              types[index] = INTEGER;
              break;
          }
  
          stackTop++;
      }
  
      private static String getFieldClassName(String desc, int index) {
          return desc.substring(index + 1, desc.length() - 1).replace('/', '.');
      }
  
      private void checkParamTypes(String desc, int i) throws BadBytecode {
          char c = desc.charAt(i);
          if (c == ')')
              return;
  
          int k = i;
          boolean array = false;
          while (c == '[') {
              array = true;
              c = desc.charAt(++k);
          }
  
          if (c == 'L') {
              k = desc.indexOf(';', k) + 1;
              if (k <= 0)
                  throw new IndexOutOfBoundsException("bad descriptor");
          }
          else
              k++;
  
          checkParamTypes(desc, k);
          if (!array && (c == 'J' || c == 'D'))
              stackTop -= 2;
          else
              stackTop--;
  
          if (array)
              TypeData.setType(stackTypes[stackTop],
                               desc.substring(i, k), classPool);
          else if (c == 'L')
              TypeData.setType(stackTypes[stackTop],
                               desc.substring(i + 1, k - 1).replace('/', '.'), classPool);
      }
  }
  
  
  



More information about the jboss-cvs-commits mailing list