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

Jason Thomas Greene jgreene at jboss.com
Sat May 24 01:12:52 EDT 2008


  User: jgreene 
  Date: 08/05/24 01:12:52

  Added:       src/main/javassist/bytecode/analysis            
                        Analyzer.java Executor.java Frame.java
                        FramePrinter.java IntQueue.java MultiArrayType.java
                        MultiType.java Subroutine.java
                        SubroutineScanner.java Type.java Util.java
                        package.html
  Log:
  Fix subtypeOf in CtArray
  Introduce full data-flow analysis API
  Fix AALOAD by using data-flow analysis to determine the type
  Introduce a testsuite to the project
  Add a framedump toolp
  
  Revision  Changes    Path
  1.1      date: 2008/05/24 05:12:52;  author: jgreene;  state: Exp;javassist/src/main/javassist/bytecode/analysis/Analyzer.java
  
  Index: Analyzer.java
  ===================================================================
  /*
   * Javassist, a Java-bytecode translator toolkit.
   * Copyright (C) 1999-2007 Shigeru Chiba, and others. 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.analysis;
  
  import java.util.Iterator;
  
  import javassist.ClassPool;
  import javassist.CtClass;
  import javassist.CtMethod;
  import javassist.NotFoundException;
  import javassist.bytecode.AccessFlag;
  import javassist.bytecode.BadBytecode;
  import javassist.bytecode.CodeAttribute;
  import javassist.bytecode.CodeIterator;
  import javassist.bytecode.ConstPool;
  import javassist.bytecode.Descriptor;
  import javassist.bytecode.ExceptionTable;
  import javassist.bytecode.MethodInfo;
  import javassist.bytecode.Opcode;
  
  /**
   * A data-flow analyzer that determines the type state of the stack and local
   * variable table at every reachable instruction in a method. During analysis,
   * bytecode verification is performed in a similar manner to that described
   * in the JVM specification.
   *
   * <p>Example:</p>
   *
   * <pre>
   * // Method to analyze
   * public Object doSomething(int x) {
   *     Number n;
   *     if (x < 5) {
   *        n = new Double(0);
   *     } else {
   *        n = new Long(0);
   *     }
   *
   *     return n;
   * }
   *
   * // Which compiles to:
   * // 0:   iload_1
   * // 1:   iconst_5
   * // 2:   if_icmpge   17
   * // 5:   new #18; //class java/lang/Double
   * // 8:   dup
   * // 9:   dconst_0
   * // 10:  invokespecial   #44; //Method java/lang/Double."<init>":(D)V
   * // 13:  astore_2
   * // 14:  goto    26
   * // 17:  new #16; //class java/lang/Long
   * // 20:  dup
   * // 21:  lconst_1
   * // 22:  invokespecial   #47; //Method java/lang/Long."<init>":(J)V
   * // 25:  astore_2
   * // 26:  aload_2
   * // 27:  areturn
   *
   * public void analyzeIt(CtClass clazz, MethodInfo method) {
   *     Analyzer analyzer = new Analyzer();
   *     Frame[] frames = analyzer.analyze(clazz, method);
   *     frames[0].getLocal(0).getCtClass(); // returns clazz;
   *     frames[0].getLocal(1).getCtClass(); // returns java.lang.String
   *     frames[1].peek(); // returns Type.INTEGER
   *     frames[27].peek().getCtClass(); // returns java.lang.Number
   * }
   * </pre>
   *
   * @see FramePrinter
   * @author Jason T. Greene
   */
  public class Analyzer implements Opcode {
      private final SubroutineScanner scanner = new SubroutineScanner();
      private CtClass clazz;
      private ExceptionInfo[] exceptions;
      private Frame[] frames;
      private Subroutine[] subroutines;
  
      private static class ExceptionInfo {
          private int end;
          private int handler;
          private int start;
          private Type type;
  
          private ExceptionInfo(int start, int end, int handler, Type type) {
              this.start = start;
              this.end = end;
              this.handler = handler;
              this.type = type;
          }
      }
  
      /**
       * Performs data-flow analysis on a method and returns an array, indexed by
       * instruction position, containing the starting frame state of all reachable
       * instructions. Non-reachable code, and illegal code offsets are represented
       * as a null in the frame state array. This can be used to detect dead code.
       *
       * If the method does not contain code (it is either native or abstract), null
       * is returned.
       *
       * @param clazz the declaring class of the method
       * @param method the method to analyze
       * @return an array, indexed by instruction position, of the starting frame state,
       *         or null if this method doesn't have code
       * @throws BadBytecode if the bytecode does not comply with the JVM specification
       */
      public Frame[] analyze(CtClass clazz, MethodInfo method) throws BadBytecode {
          this.clazz = clazz;
          CodeAttribute codeAttribute = method.getCodeAttribute();
          // Native or Abstract
          if (codeAttribute == null)
              return null;
  
          int maxLocals = codeAttribute.getMaxLocals();
          int maxStack = codeAttribute.getMaxStack();
          int codeLength = codeAttribute.getCodeLength();
  
          CodeIterator iter = codeAttribute.iterator();
          IntQueue queue = new IntQueue();
  
          exceptions = buildExceptionInfo(method);
          subroutines = scanner.scan(method);
  
          Executor executor = new Executor(clazz.getClassPool(), method.getConstPool());
          frames = new Frame[codeLength];
          frames[iter.lookAhead()] = firstFrame(method, maxLocals, maxStack);
          queue.add(iter.next());
          while (!queue.isEmpty()) {
              analyzeNextEntry(method, iter, queue, executor);
          }
  
          return frames;
      }
  
      /**
       * Performs data-flow analysis on a method and returns an array, indexed by
       * instruction position, containing the starting frame state of all reachable
       * instructions. Non-reachable code, and illegal code offsets are represented
       * as a null in the frame state array. This can be used to detect dead code.
       *
       * If the method does not contain code (it is either native or abstract), null
       * is returned.
       *
       * @param method the method to analyze
       * @return an array, indexed by instruction position, of the starting frame state,
       *         or null if this method doesn't have code
       * @throws BadBytecode if the bytecode does not comply with the JVM specification
       */
      public Frame[] analyze(CtMethod method) throws BadBytecode {
          return analyze(method.getDeclaringClass(), method.getMethodInfo2());
      }
  
      private void analyzeNextEntry(MethodInfo method, CodeIterator iter,
              IntQueue queue, Executor executor) throws BadBytecode {
          int pos = queue.take();
          iter.move(pos);
          iter.next();
  
          Frame frame = frames[pos].copy();
          Subroutine subroutine = subroutines[pos];
  
          try {
              executor.execute(method, pos, iter, frame, subroutine);
          } catch (RuntimeException e) {
              throw new BadBytecode(e.getMessage() + "[pos = " + pos + "]", e);
          }
  
          int opcode = iter.byteAt(pos);
  
          if (opcode == TABLESWITCH) {
              mergeTableSwitch(queue, pos, iter, frame);
          } else if (opcode == LOOKUPSWITCH) {
              mergeLookupSwitch(queue, pos, iter, frame);
          } else if (opcode == RET) {
              mergeRet(queue, iter, pos, frame, subroutine);
          } else if (Util.isJumpInstruction(opcode)) {
              int target = Util.getJumpTarget(pos, iter);
  
              if (Util.isJsr(opcode)) {
                  // Merge the state before the jsr into the next instruction
                  mergeJsr(queue, frames[pos], subroutines[target], pos, lookAhead(iter, pos));
              } else if (! Util.isGoto(opcode)) {
                  merge(queue, frame, lookAhead(iter, pos));
              }
  
              merge(queue, frame, target);
          } else if (opcode != ATHROW && ! Util.isReturn(opcode)) {
              // Can advance to next instruction
              merge(queue, frame, lookAhead(iter, pos));
          }
  
          // Merge all exceptions that are reachable from this instruction.
          // The redundancy is intentional, since the state must be based
          // on the current instruction frame.
          mergeExceptionHandlers(queue, method, pos, frame);
      }
  
      private ExceptionInfo[] buildExceptionInfo(MethodInfo method) {
          ConstPool constPool = method.getConstPool();
          ClassPool classes = clazz.getClassPool();
  
          ExceptionTable table = method.getCodeAttribute().getExceptionTable();
          ExceptionInfo[] exceptions = new ExceptionInfo[table.size()];
          for (int i = 0; i < table.size(); i++) {
              int index = table.catchType(i);
              Type type;
              try {
                  type = index == 0 ? Type.THROWABLE : Type.get(classes.get(constPool.getClassInfo(index)));
              } catch (NotFoundException e) {
                  throw new IllegalStateException(e);
              }
  
              exceptions[i] = new ExceptionInfo(table.startPc(i), table.endPc(i), table.handlerPc(i), type);
          }
  
          return exceptions;
      }
  
      private Frame firstFrame(MethodInfo method, int maxLocals, int maxStack) {
          int pos = 0;
  
          Frame first = new Frame(maxLocals, maxStack);
          if ((method.getAccessFlags() & AccessFlag.STATIC) == 0) {
              first.setLocal(pos++, Type.get(clazz));
          }
  
          CtClass[] parameters;
          try {
              parameters = Descriptor.getParameterTypes(method.getDescriptor(), clazz.getClassPool());
          } catch (NotFoundException e) {
              throw new RuntimeException(e);
          }
  
          for (int i = 0; i < parameters.length; i++) {
              Type type = zeroExtend(Type.get(parameters[i]));
              first.setLocal(pos++, type);
              if (type.getSize() == 2)
                  first.setLocal(pos++, Type.TOP);
          }
  
          return first;
      }
  
      private int getNext(CodeIterator iter, int of, int restore) throws BadBytecode {
          iter.move(of);
          iter.next();
          int next = iter.lookAhead();
          iter.move(restore);
          iter.next();
  
          return next;
      }
  
      private int lookAhead(CodeIterator iter, int pos) throws BadBytecode {
          if (! iter.hasNext())
              throw new BadBytecode("Execution falls off end! [pos = " + pos + "]");
  
          return iter.lookAhead();
      }
  
  
      private void merge(IntQueue queue, Frame frame, int target) {
          Frame old = frames[target];
          boolean changed;
  
          if (old == null) {
              frames[target] = frame.copy();
              changed = true;
          } else {
              changed = old.merge(frame);
          }
  
          if (changed) {
              queue.add(target);
          }
      }
  
      private void mergeExceptionHandlers(IntQueue queue, MethodInfo method, int pos, Frame frame) {
          for (int i = 0; i < exceptions.length; i++) {
              ExceptionInfo exception = exceptions[i];
  
              // Start is inclusive, while end is exclusive!
              if (pos >= exception.start && pos < exception.end) {
                  Frame newFrame = frame.copy();
                  newFrame.clearStack();
                  newFrame.push(exception.type);
                  merge(queue, newFrame, exception.handler);
              }
          }
      }
  
      private void mergeJsr(IntQueue queue, Frame frame, Subroutine sub, int pos, int next) throws BadBytecode {
          if (sub == null)
              throw new BadBytecode("No subroutine at jsr target! [pos = " + pos + "]");
  
          Frame old = frames[next];
          boolean changed = false;
  
          if (old == null) {
              old = frames[next] = frame.copy();
              changed = true;
          } else {
              for (int i = 0; i < frame.localsLength(); i++) {
                  // Skip everything accessed by a subroutine, mergeRet must handle this
                  if (!sub.isAccessed(i)) {
                      Type oldType = old.getLocal(i);
                      Type newType = frame.getLocal(i);
                      if (oldType == null) {
                          old.setLocal(i, newType);
                          changed = true;
                          continue;
                      }
  
                      newType = oldType.merge(newType);
                      // Always set the type, in case a multi-type switched to a standard type.
                      old.setLocal(i, newType);
                      if (!newType.equals(oldType) || newType.popChanged())
                          changed = true;
                  }
              }
          }
  
          if (! old.isJsrMerged()) {
              old.setJsrMerged(true);
              changed = true;
          }
  
          if (changed && old.isRetMerged())
              queue.add(next);
  
      }
  
      private void mergeLookupSwitch(IntQueue queue, int pos, CodeIterator iter, Frame frame) throws BadBytecode {
          int index = (pos & ~3) + 4;
          // default
          merge(queue, frame, pos + iter.s32bitAt(index));
          int npairs = iter.s32bitAt(index += 4);
          int end = npairs * 8 + (index += 4);
  
          // skip "match"
          for (index += 4; index < end; index += 8) {
              int target = iter.s32bitAt(index) + pos;
              merge(queue, frame, target);
          }
      }
  
      private void mergeRet(IntQueue queue, CodeIterator iter, int pos, Frame frame, Subroutine subroutine) throws BadBytecode {
          if (subroutine == null)
              throw new BadBytecode("Ret on no subroutine! [pos = " + pos + "]");
  
          Iterator callerIter = subroutine.callers().iterator();
          while (callerIter.hasNext()) {
              int caller = ((Integer) callerIter.next()).intValue();
              int returnLoc = getNext(iter, caller, pos);
              boolean changed = false;
  
              Frame old = frames[returnLoc];
              if (old == null) {
                  old = frames[returnLoc] = frame.copyStack();
                  changed = true;
              } else {
                  changed = old.mergeStack(frame);
              }
  
              for (Iterator i = subroutine.accessed().iterator(); i.hasNext(); ) {
                  int index = ((Integer)i.next()).intValue();
                  Type oldType = old.getLocal(index);
                  Type newType = frame.getLocal(index);
                  if (oldType != newType) {
                      old.setLocal(index, newType);
                      changed = true;
                  }
              }
  
              if (! old.isRetMerged()) {
                  old.setRetMerged(true);
                  changed = true;
              }
  
              if (changed && old.isJsrMerged())
                  queue.add(returnLoc);
          }
      }
  
  
      private void mergeTableSwitch(IntQueue queue, int pos, CodeIterator iter, Frame frame) throws BadBytecode {
          // Skip 4 byte alignment padding
          int index = (pos & ~3) + 4;
          // default
          merge(queue, frame, pos + iter.s32bitAt(index));
          int low = iter.s32bitAt(index += 4);
          int high = iter.s32bitAt(index += 4);
          int end = (high - low + 1) * 4 + (index += 4);
  
          // Offset table
          for (; index < end; index += 4) {
              int target = iter.s32bitAt(index) + pos;
              merge(queue, frame, target);
          }
      }
  
      private Type zeroExtend(Type type) {
          if (type == Type.SHORT || type == Type.BYTE || type == Type.CHAR || type == Type.BOOLEAN)
              return  Type.INTEGER;
  
          return type;
      }
  }
  
  
  
  1.1      date: 2008/05/24 05:12:52;  author: jgreene;  state: Exp;javassist/src/main/javassist/bytecode/analysis/Executor.java
  
  Index: Executor.java
  ===================================================================
  /*
   * Javassist, a Java-bytecode translator toolkit.
   * Copyright (C) 1999-2007 Shigeru Chiba, and others. 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.analysis;
  
  import javassist.ClassPool;
  import javassist.CtClass;
  import javassist.NotFoundException;
  import javassist.bytecode.BadBytecode;
  import javassist.bytecode.CodeIterator;
  import javassist.bytecode.ConstPool;
  import javassist.bytecode.Descriptor;
  import javassist.bytecode.MethodInfo;
  import javassist.bytecode.Opcode;
  
  /**
   * Executor is responsible for modeling the effects of a JVM instruction on a frame.
   *
   * @author Jason T. Greene
   */
  public class Executor implements Opcode {
      private final ConstPool constPool;
      private final ClassPool classPool;
      private final Type STRING_TYPE;
      private final Type CLASS_TYPE;
      private final Type THROWABLE_TYPE;
      private int lastPos;
  
      public Executor(ClassPool classPool, ConstPool constPool) {
          this.constPool = constPool;
          this.classPool = classPool;
  
          try {
              STRING_TYPE = getType("java.lang.String");
              CLASS_TYPE = getType("java.lang.Class");
              THROWABLE_TYPE = getType("java.lang.Throwable");
          } catch (Exception e) {
              throw new RuntimeException(e);
          }
      }
  
  
      /**
       * Execute the instruction, modeling the effects on the specified frame and subroutine.
       * If a subroutine is passed, the access flags will be modified if this instruction accesses
       * the local variable table.
       *
       * @param method the method containing the instruction
       * @param pos the position of the instruction in the method
       * @param iter the code iterator used to find the instruction
       * @param frame the frame to modify to represent the result of the instruction
       * @param subroutine the optional subroutine this instruction belongs to.
       * @throws BadBytecode if the bytecode violates the jvm spec
       */
      public void execute(MethodInfo method, int pos, CodeIterator iter, Frame frame, Subroutine subroutine) throws BadBytecode {
          this.lastPos = pos;
          int opcode = iter.byteAt(pos);
  
  
          // Declared opcode in order
          switch (opcode) {
              case NOP:
                  break;
              case ACONST_NULL:
                  frame.push(Type.UNINIT);
                  break;
              case ICONST_M1:
              case ICONST_0:
              case ICONST_1:
              case ICONST_2:
              case ICONST_3:
              case ICONST_4:
              case ICONST_5:
                  frame.push(Type.INTEGER);
                  break;
              case LCONST_0:
              case LCONST_1:
                  frame.push(Type.LONG);
                  frame.push(Type.TOP);
                  break;
              case FCONST_0:
              case FCONST_1:
              case FCONST_2:
                  frame.push(Type.FLOAT);
                  break;
              case DCONST_0:
              case DCONST_1:
                  frame.push(Type.DOUBLE);
                  frame.push(Type.TOP);
                  break;
              case BIPUSH:
              case SIPUSH:
                  frame.push(Type.INTEGER);
                  break;
              case LDC:
                  evalLDC(iter.byteAt(pos + 1),  frame);
                  break;
              case LDC_W :
              case LDC2_W :
                  evalLDC(iter.u16bitAt(pos + 1), frame);
                  break;
              case ILOAD:
                  evalLoad(Type.INTEGER, iter.byteAt(pos + 1), frame, subroutine);
                  break;
              case LLOAD:
                  evalLoad(Type.LONG, iter.byteAt(pos + 1), frame, subroutine);
                  break;
              case FLOAD:
                  evalLoad(Type.FLOAT, iter.byteAt(pos + 1), frame, subroutine);
                  break;
              case DLOAD:
                  evalLoad(Type.DOUBLE, iter.byteAt(pos + 1), frame, subroutine);
                  break;
              case ALOAD:
                  evalLoad(Type.OBJECT, iter.byteAt(pos + 1), frame, subroutine);
                  break;
              case ILOAD_0:
              case ILOAD_1:
              case ILOAD_2:
              case ILOAD_3:
                  evalLoad(Type.INTEGER, opcode - ILOAD_0, frame, subroutine);
                  break;
              case LLOAD_0:
              case LLOAD_1:
              case LLOAD_2:
              case LLOAD_3:
                  evalLoad(Type.LONG, opcode - LLOAD_0, frame, subroutine);
                  break;
              case FLOAD_0:
              case FLOAD_1:
              case FLOAD_2:
              case FLOAD_3:
                  evalLoad(Type.FLOAT, opcode - FLOAD_0, frame, subroutine);
                  break;
              case DLOAD_0:
              case DLOAD_1:
              case DLOAD_2:
              case DLOAD_3:
                  evalLoad(Type.DOUBLE, opcode - DLOAD_0, frame, subroutine);
                  break;
              case ALOAD_0:
              case ALOAD_1:
              case ALOAD_2:
              case ALOAD_3:
                  evalLoad(Type.OBJECT, opcode - ALOAD_0, frame, subroutine);
                  break;
              case IALOAD:
                  evalArrayLoad(Type.INTEGER, frame);
                  break;
              case LALOAD:
                  evalArrayLoad(Type.LONG, frame);
                  break;
              case FALOAD:
                  evalArrayLoad(Type.FLOAT, frame);
                  break;
              case DALOAD:
                  evalArrayLoad(Type.DOUBLE, frame);
                  break;
              case AALOAD:
                  evalArrayLoad(Type.OBJECT, frame);
                  break;
              case BALOAD:
              case CALOAD:
              case SALOAD:
                  evalArrayLoad(Type.INTEGER, frame);
                  break;
              case ISTORE:
                  evalStore(Type.INTEGER, iter.byteAt(pos + 1), frame, subroutine);
                  break;
              case LSTORE:
                  evalStore(Type.LONG, iter.byteAt(pos + 1), frame, subroutine);
                  break;
              case FSTORE:
                  evalStore(Type.FLOAT, iter.byteAt(pos + 1), frame, subroutine);
                  break;
              case DSTORE:
                  evalStore(Type.DOUBLE, iter.byteAt(pos + 1), frame, subroutine);
                  break;
              case ASTORE:
                  evalStore(Type.OBJECT, iter.byteAt(pos + 1), frame, subroutine);
                  break;
              case ISTORE_0:
              case ISTORE_1:
              case ISTORE_2:
              case ISTORE_3:
                  evalStore(Type.INTEGER, opcode - ISTORE_0, frame, subroutine);
                  break;
              case LSTORE_0:
              case LSTORE_1:
              case LSTORE_2:
              case LSTORE_3:
                  evalStore(Type.LONG, opcode - LSTORE_0, frame, subroutine);
                  break;
              case FSTORE_0:
              case FSTORE_1:
              case FSTORE_2:
              case FSTORE_3:
                  evalStore(Type.FLOAT, opcode - FSTORE_0, frame, subroutine);
                  break;
              case DSTORE_0:
              case DSTORE_1:
              case DSTORE_2:
              case DSTORE_3:
                  evalStore(Type.DOUBLE, opcode - DSTORE_0, frame, subroutine);
                  break;
              case ASTORE_0:
              case ASTORE_1:
              case ASTORE_2:
              case ASTORE_3:
                  evalStore(Type.OBJECT, opcode - ASTORE_0, frame, subroutine);
                  break;
              case IASTORE:
                  evalArrayStore(Type.INTEGER, frame);
                  break;
              case LASTORE:
                  evalArrayStore(Type.LONG, frame);
                  break;
              case FASTORE:
                  evalArrayStore(Type.FLOAT, frame);
                  break;
              case DASTORE:
                  evalArrayStore(Type.DOUBLE, frame);
                  break;
              case AASTORE:
                  evalArrayStore(Type.OBJECT, frame);
                  break;
              case BASTORE:
              case CASTORE:
              case SASTORE:
                  evalArrayStore(Type.INTEGER, frame);
                  break;
              case POP:
                  if (frame.pop() == Type.TOP)
                      throw new BadBytecode("POP can not be used with a category 2 value, pos = " + pos);
                  break;
              case POP2:
                  frame.pop();
                  frame.pop();
                  break;
              case DUP: {
                  Type type = frame.peek();
                  if (type == Type.TOP)
                      throw new BadBytecode("DUP can not be used with a category 2 value, pos = " + pos);
  
                  frame.push(frame.peek());
                  break;
              }
              case DUP_X1:
              case DUP_X2: {
                  Type type = frame.peek();
                  if (type == Type.TOP)
                      throw new BadBytecode("DUP can not be used with a category 2 value, pos = " + pos);
                  int end = frame.getTopIndex();
                  int insert = end - (opcode - DUP_X1) - 1;
                  frame.push(type);
  
                  while (end > insert) {
                      frame.setStack(end, frame.getStack(end - 1));
                      end--;
                  }
                  frame.setStack(insert, type);
                  break;
              }
              case DUP2:
                  frame.push(frame.getStack(frame.getTopIndex() - 1));
                  frame.push(frame.getStack(frame.getTopIndex() - 1));
                  break;
              case DUP2_X1:
              case DUP2_X2: {
                  int end = frame.getTopIndex();
                  int insert = end - (opcode - DUP2_X1) - 1;
                  Type type1 = frame.getStack(frame.getTopIndex() - 1);
                  Type type2 = frame.peek();
                   frame.push(type1);
                   frame.push(type2);
                  while (end > insert) {
                      frame.setStack(end, frame.getStack(end - 2));
                      end--;
                  }
                  frame.setStack(insert, type2);
                  frame.setStack(insert - 1, type1);
                  break;
              }
              case SWAP: {
                  Type type1 = frame.pop();
                  Type type2 = frame.pop();
                  if (type1.getSize() == 2 || type2.getSize() == 2)
                      throw new BadBytecode("Swap can not be used with category 2 values, pos = " + pos);
                  frame.push(type1);
                  frame.push(type2);
                  break;
              }
  
              // Math
              case IADD:
                  evalBinaryMath(Type.INTEGER, frame);
                  break;
              case LADD:
                  evalBinaryMath(Type.LONG, frame);
                  break;
              case FADD:
                  evalBinaryMath(Type.FLOAT, frame);
                  break;
              case DADD:
                  evalBinaryMath(Type.DOUBLE, frame);
                  break;
              case ISUB:
                  evalBinaryMath(Type.INTEGER, frame);
                  break;
              case LSUB:
                  evalBinaryMath(Type.LONG, frame);
                  break;
              case FSUB:
                  evalBinaryMath(Type.FLOAT, frame);
                  break;
              case DSUB:
                  evalBinaryMath(Type.DOUBLE, frame);
                  break;
              case IMUL:
                  evalBinaryMath(Type.INTEGER, frame);
                  break;
              case LMUL:
                  evalBinaryMath(Type.LONG, frame);
                  break;
              case FMUL:
                  evalBinaryMath(Type.FLOAT, frame);
                  break;
              case DMUL:
                  evalBinaryMath(Type.DOUBLE, frame);
                  break;
              case IDIV:
                  evalBinaryMath(Type.INTEGER, frame);
                  break;
              case LDIV:
                  evalBinaryMath(Type.LONG, frame);
                  break;
              case FDIV:
                  evalBinaryMath(Type.FLOAT, frame);
                  break;
              case DDIV:
                  evalBinaryMath(Type.DOUBLE, frame);
                  break;
              case IREM:
                  evalBinaryMath(Type.INTEGER, frame);
                  break;
              case LREM:
                  evalBinaryMath(Type.LONG, frame);
                  break;
              case FREM:
                  evalBinaryMath(Type.FLOAT, frame);
                  break;
              case DREM:
                  evalBinaryMath(Type.DOUBLE, frame);
                  break;
  
              // Unary
              case INEG:
                  verifyAssignable(Type.INTEGER, simplePeek(frame));
                  break;
              case LNEG:
                  verifyAssignable(Type.LONG, simplePeek(frame));
                  break;
              case FNEG:
                  verifyAssignable(Type.FLOAT, simplePeek(frame));
                  break;
              case DNEG:
                  verifyAssignable(Type.DOUBLE, simplePeek(frame));
                  break;
  
              // Shifts
              case ISHL:
                  evalShift(Type.INTEGER, frame);
                  break;
              case LSHL:
                  evalShift(Type.LONG, frame);
                  break;
              case ISHR:
                  evalShift(Type.INTEGER, frame);
                  break;
              case LSHR:
                  evalShift(Type.LONG, frame);
                  break;
              case IUSHR:
                  evalShift(Type.INTEGER,frame);
                  break;
              case LUSHR:
                  evalShift(Type.LONG, frame);
                  break;
  
              // Bitwise Math
              case IAND:
                  evalBinaryMath(Type.INTEGER, frame);
                  break;
              case LAND:
                  evalBinaryMath(Type.LONG, frame);
                  break;
              case IOR:
                  evalBinaryMath(Type.INTEGER, frame);
                  break;
              case LOR:
                  evalBinaryMath(Type.LONG, frame);
                  break;
              case IXOR:
                  evalBinaryMath(Type.INTEGER, frame);
                  break;
              case LXOR:
                  evalBinaryMath(Type.LONG, frame);
                  break;
  
              case IINC: {
                  int index = iter.byteAt(pos + 1);
                  verifyAssignable(Type.INTEGER, frame.getLocal(index));
                  access(index, Type.INTEGER, subroutine);
                  break;
              }
  
              // Conversion
              case I2L:
                  verifyAssignable(Type.INTEGER, simplePop(frame));
                  simplePush(Type.LONG, frame);
                  break;
              case I2F:
                  verifyAssignable(Type.INTEGER, simplePop(frame));
                  simplePush(Type.FLOAT, frame);
                  break;
              case I2D:
                  verifyAssignable(Type.INTEGER, simplePop(frame));
                  simplePush(Type.DOUBLE, frame);
                  break;
              case L2I:
                  verifyAssignable(Type.LONG, simplePop(frame));
                  simplePush(Type.INTEGER, frame);
                  break;
              case L2F:
                  verifyAssignable(Type.LONG, simplePop(frame));
                  simplePush(Type.FLOAT, frame);
                  break;
              case L2D:
                  verifyAssignable(Type.LONG, simplePop(frame));
                  simplePush(Type.DOUBLE, frame);
                  break;
              case F2I:
                  verifyAssignable(Type.FLOAT, simplePop(frame));
                  simplePush(Type.INTEGER, frame);
                  break;
              case F2L:
                  verifyAssignable(Type.FLOAT, simplePop(frame));
                  simplePush(Type.LONG, frame);
                  break;
              case F2D:
                  verifyAssignable(Type.FLOAT, simplePop(frame));
                  simplePush(Type.DOUBLE, frame);
                  break;
              case D2I:
                  verifyAssignable(Type.DOUBLE, simplePop(frame));
                  simplePush(Type.INTEGER, frame);
                  break;
              case D2L:
                  verifyAssignable(Type.DOUBLE, simplePop(frame));
                  simplePush(Type.LONG, frame);
                  break;
              case D2F:
                  verifyAssignable(Type.DOUBLE, simplePop(frame));
                  simplePush(Type.FLOAT, frame);
                  break;
              case I2B:
              case I2C:
              case I2S:
                  verifyAssignable(Type.INTEGER, frame.peek());
                  break;
              case LCMP:
                  verifyAssignable(Type.LONG, simplePop(frame));
                  verifyAssignable(Type.LONG, simplePop(frame));
                  frame.push(Type.INTEGER);
                  break;
              case FCMPL:
              case FCMPG:
                  verifyAssignable(Type.FLOAT, simplePop(frame));
                  verifyAssignable(Type.FLOAT, simplePop(frame));
                  frame.push(Type.INTEGER);
                  break;
              case DCMPL:
              case DCMPG:
                  verifyAssignable(Type.DOUBLE, simplePop(frame));
                  verifyAssignable(Type.DOUBLE, simplePop(frame));
                  frame.push(Type.INTEGER);
                  break;
  
              // Control flow
              case IFEQ:
              case IFNE:
              case IFLT:
              case IFGE:
              case IFGT:
              case IFLE:
                  verifyAssignable(Type.INTEGER, simplePop(frame));
                  break;
              case IF_ICMPEQ:
              case IF_ICMPNE:
              case IF_ICMPLT:
              case IF_ICMPGE:
              case IF_ICMPGT:
              case IF_ICMPLE:
                  verifyAssignable(Type.INTEGER, simplePop(frame));
                  verifyAssignable(Type.INTEGER, simplePop(frame));
                  break;
              case IF_ACMPEQ:
              case IF_ACMPNE:
                  verifyAssignable(Type.OBJECT, simplePop(frame));
                  verifyAssignable(Type.OBJECT, simplePop(frame));
                  break;
              case GOTO:
                  break;
              case JSR:
                  frame.push(Type.RETURN_ADDRESS);
                  break;
              case RET:
                  verifyAssignable(Type.RETURN_ADDRESS, frame.getLocal(iter.byteAt(pos + 1)));
                  break;
              case TABLESWITCH:
              case LOOKUPSWITCH:
              case IRETURN:
                  verifyAssignable(Type.INTEGER, simplePop(frame));
                  break;
              case LRETURN:
                  verifyAssignable(Type.LONG, simplePop(frame));
                  break;
              case FRETURN:
                  verifyAssignable(Type.FLOAT, simplePop(frame));
                  break;
              case DRETURN:
                  verifyAssignable(Type.DOUBLE, simplePop(frame));
                  break;
              case ARETURN:
                  try {
                      CtClass returnType = Descriptor.getReturnType(method.getDescriptor(), classPool);
                      verifyAssignable(Type.get(returnType), simplePop(frame));
                  } catch (NotFoundException e) {
                     throw new RuntimeException(e);
                  }
                  break;
              case RETURN:
                  break;
              case GETSTATIC:
                  evalGetField(opcode, iter.u16bitAt(pos + 1), frame);
                  break;
              case PUTSTATIC:
                  evalPutField(opcode, iter.u16bitAt(pos + 1), frame);
                  break;
              case GETFIELD:
                  evalGetField(opcode, iter.u16bitAt(pos + 1), frame);
                  break;
              case PUTFIELD:
                  evalPutField(opcode, iter.u16bitAt(pos + 1), frame);
                  break;
              case INVOKEVIRTUAL:
              case INVOKESPECIAL:
              case INVOKESTATIC:
                  evalInvokeMethod(opcode, iter.u16bitAt(pos + 1), frame);
                  break;
              case INVOKEINTERFACE:
                  evalInvokeIntfMethod(opcode, iter.u16bitAt(pos + 1), frame);
                  break;
              case 186:
                  throw new RuntimeException("Bad opcode 186");
              case NEW:
                  frame.push(typeFromDesc(constPool.getClassInfo(iter.u16bitAt(pos + 1))));
                  break;
              case NEWARRAY:
                  evalNewArray(pos, iter, frame);
                  break;
              case ANEWARRAY:
                  evalNewObjectArray(pos, iter, frame);
                  break;
              case ARRAYLENGTH: {
                  Type array = simplePop(frame);
                  if (! array.isArray() && array != Type.UNINIT)
                      throw new BadBytecode("Array length passed a non-array [pos = " + pos + "]: " + array);
                  frame.push(Type.INTEGER);
                  break;
              }
              case ATHROW:
                  verifyAssignable(THROWABLE_TYPE, simplePop(frame));
                  break;
              case CHECKCAST:
                  verifyAssignable(Type.OBJECT, simplePop(frame));
                  frame.push(typeFromDesc(constPool.getClassInfo(iter.u16bitAt(pos + 1))));
                  break;
              case INSTANCEOF:
                  verifyAssignable(Type.OBJECT, simplePop(frame));
                  frame.push(Type.INTEGER);
                  break;
              case MONITORENTER:
              case MONITOREXIT:
                  verifyAssignable(Type.OBJECT, simplePop(frame));
                  break;
              case WIDE:
                  evalWide(pos, iter, frame, subroutine);
                  break;
              case MULTIANEWARRAY:
                  evalNewObjectArray(pos, iter, frame);
                  break;
              case IFNULL:
              case IFNONNULL:
                  verifyAssignable(Type.OBJECT, simplePop(frame));
                  break;
              case GOTO_W:
                  break;
              case JSR_W:
                  frame.push(Type.RETURN_ADDRESS);
                  break;
          }
      }
  
      private Type zeroExtend(Type type) {
          if (type == Type.SHORT || type == Type.BYTE || type == Type.CHAR || type == Type.BOOLEAN)
              return  Type.INTEGER;
  
          return type;
      }
  
      private void evalArrayLoad(Type expectedComponent, Frame frame) throws BadBytecode {
          Type index = frame.pop();
          Type array = frame.pop();
  
          // Special case, an array defined by aconst_null
          // TODO - we might need to be more inteligent about this
          if (array == Type.UNINIT) {
              verifyAssignable(Type.INTEGER, index);
              if (expectedComponent == Type.OBJECT) {
                  simplePush(Type.UNINIT, frame);
              } else {
                  simplePush(expectedComponent, frame);
              }
              return;
          }
  
          Type component = array.getComponent();
  
          if (component == null)
              throw new BadBytecode("Not an array! [pos = " + lastPos + "]: " + component);
  
          component = zeroExtend(component);
  
          verifyAssignable(expectedComponent, component);
          verifyAssignable(Type.INTEGER, index);
          simplePush(component, frame);
      }
  
      private void evalArrayStore(Type expectedComponent, Frame frame) throws BadBytecode {
          Type value = simplePop(frame);
          Type index = frame.pop();
          Type array = frame.pop();
  
          if (array == Type.UNINIT) {
              verifyAssignable(Type.INTEGER, index);
              return;
          }
  
          Type component = array.getComponent();
  
          if (component == null)
              throw new BadBytecode("Not an array! [pos = " + lastPos + "]: " + component);
  
          component = zeroExtend(component);
  
          verifyAssignable(expectedComponent, component);
          verifyAssignable(Type.INTEGER, index);
  
          // This intentionally only checks for Object on aastore
          // downconverting of an array (no casts)
          // e.g. Object[] blah = new String[];
          //      blah[2] = (Object) "test";
          //      blah[3] = new Integer(); // compiler doesnt catch it (has legal bytecode),
          //                               // but will throw arraystoreexception
          if (expectedComponent == Type.OBJECT) {
              verifyAssignable(expectedComponent, value);
          } else {
              verifyAssignable(component, value);
          }
      }
  
      private void evalBinaryMath(Type expected, Frame frame) throws BadBytecode {
          Type value2 = simplePop(frame);
          Type value1 = simplePop(frame);
  
          verifyAssignable(expected, value2);
          verifyAssignable(expected, value1);
          simplePush(value1, frame);
      }
  
      private void evalGetField(int opcode, int index, Frame frame) throws BadBytecode {
          String desc = constPool.getFieldrefType(index);
          Type type = zeroExtend(typeFromDesc(desc));
  
          if (opcode == GETFIELD) {
              Type objectType = typeFromDesc(constPool.getFieldrefClassName(index));
              verifyAssignable(objectType, simplePop(frame));
          }
  
          simplePush(type, frame);
      }
  
      private void evalInvokeIntfMethod(int opcode, int index, Frame frame) throws BadBytecode {
          String desc = constPool.getInterfaceMethodrefType(index);
          Type[] types = paramTypesFromDesc(desc);
          int i = types.length;
  
          while (i > 0)
              verifyAssignable(zeroExtend(types[--i]), simplePop(frame));
  
          String classDesc = constPool.getInterfaceMethodrefClassName(index);
          Type objectType = typeFromDesc(classDesc);
          verifyAssignable(objectType, simplePop(frame));
  
          Type returnType = returnTypeFromDesc(desc);
          if (returnType != Type.VOID)
              simplePush(zeroExtend(returnType), frame);
      }
  
      private void evalInvokeMethod(int opcode, int index, Frame frame) throws BadBytecode {
          String desc = constPool.getMethodrefType(index);
          Type[] types = paramTypesFromDesc(desc);
          int i = types.length;
  
          while (i > 0)
              verifyAssignable(zeroExtend(types[--i]), simplePop(frame));
  
          if (opcode != INVOKESTATIC) {
              Type objectType = typeFromDesc(constPool.getMethodrefClassName(index));
              verifyAssignable(objectType, simplePop(frame));
          }
  
          Type returnType = returnTypeFromDesc(desc);
          if (returnType != Type.VOID)
              simplePush(zeroExtend(returnType), frame);
      }
  
  
      private void evalLDC(int index, Frame frame) throws BadBytecode {
          int tag = constPool.getTag(index);
          Type type;
          switch (tag) {
          case ConstPool.CONST_String:
              type = STRING_TYPE;
              break;
          case ConstPool.CONST_Integer:
              type = Type.INTEGER;
              break;
          case ConstPool.CONST_Float:
              type = Type.FLOAT;
              break;
          case ConstPool.CONST_Long:
              type = Type.LONG;
              break;
          case ConstPool.CONST_Double:
              type = Type.DOUBLE;
              break;
          case ConstPool.CONST_Class:
              type = CLASS_TYPE;
              break;
          default:
              throw new BadBytecode("bad LDC [pos = " + lastPos + "]: " + tag);
          }
  
          simplePush(type, frame);
      }
  
      private void evalLoad(Type expected, int index, Frame frame, Subroutine subroutine) throws BadBytecode {
          Type type = frame.getLocal(index);
  
          verifyAssignable(expected, type);
  
          simplePush(type, frame);
          access(index, type, subroutine);
      }
  
      private void evalNewArray(int pos, CodeIterator iter, Frame frame) throws BadBytecode {
          verifyAssignable(Type.INTEGER, simplePop(frame));
          Type type = null;
          int typeInfo = iter.byteAt(pos + 1);
          switch (typeInfo) {
              case T_BOOLEAN:
                  type = getType("boolean[]");
                  break;
              case T_CHAR:
                  type = getType("char[]");
                  break;
              case T_BYTE:
                  type = getType("byte[]");
                  break;
              case T_SHORT:
                  type = getType("short[]");
                  break;
              case T_INT:
                  type = getType("int[]");
                  break;
              case T_LONG:
                  type = getType("long[]");
                  break;
              case T_FLOAT:
                  type = getType("float[]");
                  break;
              case T_DOUBLE:
                  type = getType("double[]");
                  break;
              default:
                  throw new BadBytecode("Invalid array type [pos = " + pos + "]: " + typeInfo);
  
          }
  
          frame.push(type);
      }
  
      private void evalNewObjectArray(int pos, CodeIterator iter, Frame frame) throws BadBytecode {
          // Convert to x[] format
          Type type = typeFromDesc(constPool.getClassInfo(iter.u16bitAt(pos + 1)));
          String name = type.getCtClass().getName();
          int opcode = iter.byteAt(pos);
          int dimensions;
  
          if (opcode == MULTIANEWARRAY) {
              dimensions = iter.byteAt(pos + 3);
          } else {
              name = name + "[]";
              dimensions = 1;
          }
  
          while (dimensions-- > 0) {
              verifyAssignable(Type.INTEGER, simplePop(frame));
          }
  
          simplePush(getType(name), frame);
      }
  
      private void evalPutField(int opcode, int index, Frame frame) throws BadBytecode {
          String desc = constPool.getFieldrefType(index);
          Type type = zeroExtend(typeFromDesc(desc));
  
          verifyAssignable(type, simplePop(frame));
  
          if (opcode == PUTFIELD) {
              Type objectType = typeFromDesc(constPool.getFieldrefClassName(index));
              verifyAssignable(objectType, simplePop(frame));
          }
      }
  
      private void evalShift(Type expected, Frame frame) throws BadBytecode {
          Type value2 = simplePop(frame);
          Type value1 = simplePop(frame);
  
          verifyAssignable(Type.INTEGER, value2);
          verifyAssignable(expected, value1);
          simplePush(value1, frame);
      }
  
      private void evalStore(Type expected, int index, Frame frame, Subroutine subroutine) throws BadBytecode {
          Type type = simplePop(frame);
  
          // RETURN_ADDRESS is allowed by ASTORE
          if (! (expected == Type.OBJECT && type == Type.RETURN_ADDRESS))
              verifyAssignable(expected, type);
          simpleSetLocal(index, type, frame);
          access(index, type, subroutine);
      }
  
      private void evalWide(int pos, CodeIterator iter, Frame frame, Subroutine subroutine) throws BadBytecode {
          int opcode = iter.byteAt(pos + 1);
          int index = iter.u16bitAt(pos + 2);
          switch (opcode) {
              case ILOAD:
                  evalLoad(Type.INTEGER, index, frame, subroutine);
                  break;
              case LLOAD:
                  evalLoad(Type.LONG, index, frame, subroutine);
                  break;
              case FLOAD:
                  evalLoad(Type.FLOAT, index, frame, subroutine);
                  break;
              case DLOAD:
                  evalLoad(Type.DOUBLE, index, frame, subroutine);
                  break;
              case ALOAD:
                  evalLoad(Type.OBJECT, index, frame, subroutine);
                  break;
              case ISTORE:
                  evalStore(Type.INTEGER, index, frame, subroutine);
                  break;
              case LSTORE:
                  evalStore(Type.LONG, index, frame, subroutine);
                  break;
              case FSTORE:
                  evalStore(Type.FLOAT, index, frame, subroutine);
                  break;
              case DSTORE:
                  evalStore(Type.DOUBLE, index, frame, subroutine);
                  break;
              case ASTORE:
                  evalStore(Type.OBJECT, index, frame, subroutine);
                  break;
              case IINC:
                  verifyAssignable(Type.INTEGER, frame.getLocal(index));
                  break;
              case RET:
                  verifyAssignable(Type.RETURN_ADDRESS, frame.getLocal(index));
                  break;
              default:
                  throw new BadBytecode("Invalid WIDE operand [pos = " + pos + "]: " + opcode);
          }
  
      }
  
      private Type getType(String name) throws BadBytecode {
          try {
              return Type.get(classPool.get(name));
          } catch (NotFoundException e) {
              throw new BadBytecode("Could not find class [pos = " + lastPos + "]: " + name);
          }
      }
  
      private Type[] paramTypesFromDesc(String desc) throws BadBytecode {
          CtClass classes[] = null;
          try {
              classes = Descriptor.getParameterTypes(desc, classPool);
          } catch (NotFoundException e) {
              throw new BadBytecode("Could not find class in descriptor [pos = " + lastPos + "]: " + e.getMessage());
          }
  
          if (classes == null)
              throw new BadBytecode("Could not obtain parameters for descriptor [pos = " + lastPos + "]: " + desc);
  
          Type[] types = new Type[classes.length];
          for (int i = 0; i < types.length; i++)
              types[i] = Type.get(classes[i]);
  
          return types;
      }
  
      private Type returnTypeFromDesc(String desc) throws BadBytecode {
          CtClass clazz = null;
          try {
              clazz = Descriptor.getReturnType(desc, classPool);
          } catch (NotFoundException e) {
              throw new BadBytecode("Could not find class in descriptor [pos = " + lastPos + "]: " + e.getMessage());
          }
  
          if (clazz == null)
              throw new BadBytecode("Could not obtain return type for descriptor [pos = " + lastPos + "]: " + desc);
  
          return Type.get(clazz);
      }
  
      private Type simplePeek(Frame frame) {
          Type type = frame.peek();
          return (type == Type.TOP) ? frame.getStack(frame.getTopIndex() - 1) : type;
      }
  
      private Type simplePop(Frame frame) {
          Type type = frame.pop();
          return (type == Type.TOP) ? frame.pop() : type;
      }
  
      private void simplePush(Type type, Frame frame) {
          frame.push(type);
          if (type.getSize() == 2)
              frame.push(Type.TOP);
      }
  
      private void access(int index, Type type, Subroutine subroutine) {
          if (subroutine == null)
              return;
          subroutine.access(index);
          if (type.getSize() == 2)
              subroutine.access(index + 1);
      }
  
      private void simpleSetLocal(int index, Type type, Frame frame) {
          frame.setLocal(index, type);
          if (type.getSize() == 2)
              frame.setLocal(index + 1, Type.TOP);
      }
  
      private Type typeFromDesc(String desc) throws BadBytecode {
          CtClass clazz = null;
          try {
              clazz = Descriptor.toCtClass(desc, classPool);
          } catch (NotFoundException e) {
              throw new BadBytecode("Could not find class in descriptor [pos = " + lastPos + "]: " + e.getMessage());
          }
  
          if (clazz == null)
              throw new BadBytecode("Could not obtain type for descriptor [pos = " + lastPos + "]: " + desc);
  
          return Type.get(clazz);
      }
  
      private void verifyAssignable(Type expected, Type type) throws BadBytecode {
          if (! expected.isAssignableFrom(type))
              throw new BadBytecode("Expected type: " + expected + " Got: " + type + " [pos = " + lastPos + "]");
      }
  }
  
  
  
  1.1      date: 2008/05/24 05:12:52;  author: jgreene;  state: Exp;javassist/src/main/javassist/bytecode/analysis/Frame.java
  
  Index: Frame.java
  ===================================================================
  /*
   * Javassist, a Java-bytecode translator toolkit.
   * Copyright (C) 1999-2007 Shigeru Chiba, and others. 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.analysis;
  
  
  /**
   * Represents the stack frame and local variable table at a particular point in time.
   *
   * @author Jason T. Greene
   */
  public class Frame {
      private Type[] locals;
      private Type[] stack;
      private int top;
      private boolean jsrMerged;
      private boolean retMerged;
  
      /**
       * Create a new frame with the specified local variable table size, and max stack size
       *
       * @param locals the number of local variable table entries
       * @param stack the maximum stack size
       */
      public Frame(int locals, int stack) {
          this.locals = new Type[locals];
          this.stack = new Type[stack];
      }
  
      /**
       * Returns the local varaible table entry at index.
       *
       * @param index the position in the table
       * @return the type if one exists, or null if the position is empty
       */
      public Type getLocal(int index) {
          return locals[index];
      }
  
      /**
       * Sets the local variable table entry at index to a type.
       *
       * @param index the position in the table
       * @param type the type to set at the position
       */
      public void setLocal(int index, Type type) {
          locals[index] = type;
      }
  
  
      /**
       * Returns the type on the stack at the specified index.
       *
       * @param index the position on the stack
       * @return the type of the stack position
       */
      public Type getStack(int index) {
          return stack[index];
      }
  
      /**
       * Sets the type of the stack position
       *
       * @param index the position on the stack
       * @param type the type to set
       */
      public void setStack(int index, Type type) {
          stack[index] = type;
      }
  
      /**
       * Empties the stack
       */
      public void clearStack() {
          top = 0;
      }
  
      /**
       * Gets the index of the type sitting at the top of the stack.
       * This is not to be confused with a length operation which
       * would return the number of elements, not the position of
       * the last element.
       *
       * @return the position of the element at the top of the stack
       */
      public int getTopIndex() {
          return top - 1;
      }
  
      /**
       * Returns the number of local variable table entries, specified
       * at construction.
       *
       * @return the number of local variable table entries
       */
      public int localsLength() {
          return locals.length;
      }
  
      /**
       * Gets the top of the stack without altering it
       *
       * @return the top of the stack
       */
      public Type peek() {
          if (top < 1)
              throw new IndexOutOfBoundsException("Stack is empty");
  
          return stack[top - 1];
      }
  
      /**
       * Alters the stack to contain one less element and return it.
       *
       * @return the element popped from the stack
       */
      public Type pop() {
          if (top < 1)
              throw new IndexOutOfBoundsException("Stack is empty");
          return stack[--top];
      }
  
      /**
       * Alters the stack by placing the passed type on the top
       *
       * @param type the type to add to the top
       */
      public void push(Type type) {
          stack[top++] = type;
      }
  
  
      /**
       * Makes a shallow copy of this frame, i.e. the type instances will
       * remain the same.
       *
       * @return the shallow copy
       */
      public Frame copy() {
          Frame frame = new Frame(locals.length, stack.length);
          System.arraycopy(locals, 0, frame.locals, 0, locals.length);
          System.arraycopy(stack, 0, frame.stack, 0, stack.length);
          frame.top = top;
          return frame;
      }
  
      /**
       * Makes a shallow copy of the stack portion of this frame. The local
       * variable table size will be copied, but its contents will be empty.
       *
       * @return the shallow copy of the stack
       */
      public Frame copyStack() {
          Frame frame = new Frame(locals.length, stack.length);
          System.arraycopy(stack, 0, frame.stack, 0, stack.length);
          frame.top = top;
          return frame;
      }
  
      /**
       * Merges all types on the stack of this frame instance with that of the specified frame.
       * The local variable table is left untouched.
       *
       * @param frame the frame to merge the stack from
       * @return true if any changes where made
       */
      public boolean mergeStack(Frame frame) {
          boolean changed = false;
          if (top != frame.top)
              throw new RuntimeException("Operand stacks could not be merged, they are different sizes!");
  
          for (int i = 0; i < top; i++) {
              if (stack[i] != null) {
                  Type prev = stack[i];
                  Type merged = prev.merge(frame.stack[i]);
                  if (merged == Type.BOGUS)
                      throw new RuntimeException("Operand stacks could not be merged due to differing primitive types: pos = " + i);
  
                  stack[i] = merged;
                  // always replace the instance in case a multi-interface type changes to a normal Type
                  if ((! merged.equals(prev)) || merged.popChanged()) {
                      changed = true;
                  }
              }
          }
  
          return changed;
      }
  
      /**
       * Merges all types on the stack and local variable table of this frame with that of the specified
       * type.
       *
       * @param frame the frame to merge with
       * @return true if any changes to this frame where made by this merge
       */
      public boolean merge(Frame frame) {
          boolean changed = false;
  
          // Local variable table
          for (int i = 0; i < locals.length; i++) {
              if (locals[i] != null) {
                  Type prev = locals[i];
                  Type merged = prev.merge(frame.locals[i]);
                  // always replace the instance in case a multi-interface type changes to a normal Type
                  locals[i] = merged;
                  if (! merged.equals(prev) || merged.popChanged()) {
                      changed = true;
                  }
              } else if (frame.locals[i] != null) {
                  locals[i] = frame.locals[i];
                  changed = true;
              }
          }
  
          changed |= mergeStack(frame);
          return changed;
      }
  
      public String toString() {
          StringBuffer buffer = new StringBuffer();
  
          buffer.append("locals = [");
          for (int i = 0; i < locals.length; i++) {
              buffer.append(locals[i] == null ? "empty" : locals[i].toString());
              if (i < locals.length - 1)
                  buffer.append(", ");
          }
          buffer.append("] stack = [");
          for (int i = 0; i < top; i++) {
              buffer.append(stack[i]);
              if (i < top - 1)
                  buffer.append(", ");
          }
          buffer.append("]");
  
          return buffer.toString();
      }
  
      /**
       * Whether or not state from the source JSR instruction has been merged
       *
       * @return true if JSR state has been merged
       */
      boolean isJsrMerged() {
          return jsrMerged;
      }
  
      /**
       * Sets whether of not the state from the source JSR instruction has been merged
       *
       * @param jsrMerged true if merged, otherwise false
       */
      void setJsrMerged(boolean jsrMerged) {
          this.jsrMerged = jsrMerged;
      }
  
      /**
       * Whether or not state from the RET instruction, of the subroutine that was jumped
       * to has been merged.
       *
       * @return true if RET state has been merged
       */
      boolean isRetMerged() {
          return retMerged;
      }
  
      /**
       * Sets whether or not state from the RET instruction, of the subroutine that was jumped
       * to has been merged.
       *
       * @param retMerged true if RET state has been merged
       */
      void setRetMerged(boolean retMerged) {
          this.retMerged = retMerged;
      }
  }
  
  
  
  1.1      date: 2008/05/24 05:12:52;  author: jgreene;  state: Exp;javassist/src/main/javassist/bytecode/analysis/FramePrinter.java
  
  Index: FramePrinter.java
  ===================================================================
  /*
   * Javassist, a Java-bytecode translator toolkit.
   * Copyright (C) 1999-2007 Shigeru Chiba, and others. 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.analysis;
  
  import java.io.PrintStream;
  
  import javassist.CtClass;
  import javassist.CtMethod;
  import javassist.Modifier;
  import javassist.NotFoundException;
  import javassist.bytecode.BadBytecode;
  import javassist.bytecode.CodeAttribute;
  import javassist.bytecode.CodeIterator;
  import javassist.bytecode.ConstPool;
  import javassist.bytecode.Descriptor;
  import javassist.bytecode.InstructionPrinter;
  import javassist.bytecode.MethodInfo;
  
  /**
   * A utility class for printing a merged view of the frame state and the
   * instructions of a method.
   *
   * @author Jason T. Greene
   */
  public final class FramePrinter {
      private final PrintStream stream;
  
      public FramePrinter(PrintStream stream) {
          this.stream = stream;
      }
  
      public static void print(CtClass clazz, PrintStream stream) {
          (new FramePrinter(stream)).print(clazz);
      }
  
      public void print(CtClass clazz) {
          CtMethod[] methods = clazz.getDeclaredMethods();
          for (int i = 0; i < methods.length; i++) {
              print(methods[i]);
          }
      }
  
      private String getMethodString(CtMethod method) {
          try {
              return Modifier.toString(method.getModifiers()) + " "
                      + method.getReturnType().getName() + " " + method.getName()
                      + Descriptor.toString(method.getSignature()) + ";";
          } catch (NotFoundException e) {
              throw new RuntimeException(e);
          }
      }
  
      public void print(CtMethod method) {
          stream.println("\n" + getMethodString(method));
          MethodInfo info = method.getMethodInfo2();
          ConstPool pool = info.getConstPool();
          CodeAttribute code = info.getCodeAttribute();
          if (code == null)
              return;
  
          Frame[] frames;
          try {
              frames = (new Analyzer()).analyze(method.getDeclaringClass(), info);
          } catch (BadBytecode e) {
              throw new RuntimeException(e);
          }
  
          int spacing = String.valueOf(code.getCodeLength()).length();
  
          CodeIterator iterator = code.iterator();
          while (iterator.hasNext()) {
              int pos;
              try {
                  pos = iterator.next();
              } catch (BadBytecode e) {
                  throw new RuntimeException(e);
              }
  
              stream.println(pos + ": " + InstructionPrinter.instructionString(iterator, pos, pool));
  
              addSpacing(spacing + 3);
              Frame frame = frames[pos];
              if (frame == null) {
                  stream.println("--DEAD CODE--");
                  continue;
              }
              printStack(frame);
  
              addSpacing(spacing + 3);
              printLocals(frame);
          }
  
      }
  
      private void printStack(Frame frame) {
          stream.print("stack [");
          int top = frame.getTopIndex();
          for (int i = 0; i <= top; i++) {
              if (i > 0)
                  stream.print(", ");
              Type type = frame.getStack(i);
              stream.print(type);
          }
          stream.println("]");
      }
  
      private void printLocals(Frame frame) {
          stream.print("locals [");
          int length = frame.localsLength();
          for (int i = 0; i < length; i++) {
              if (i > 0)
                  stream.print(", ");
              Type type = frame.getLocal(i);
              stream.print(type == null ? "empty" : type.toString());
          }
          stream.println("]");
      }
  
      private void addSpacing(int count) {
          while (count-- > 0)
              stream.print(' ');
      }
  
  }
  
  
  
  1.1      date: 2008/05/24 05:12:52;  author: jgreene;  state: Exp;javassist/src/main/javassist/bytecode/analysis/IntQueue.java
  
  Index: IntQueue.java
  ===================================================================
  /*
   * Javassist, a Java-bytecode translator toolkit.
   * Copyright (C) 1999-2007 Shigeru Chiba, and others. 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.analysis;
  
  import java.util.NoSuchElementException;
  
  class IntQueue {
      private static class Entry {
          private IntQueue.Entry next;
          private int value;
          private Entry(int value) {
              this.value = value;
          }
      }
      private IntQueue.Entry head;
  
      private IntQueue.Entry tail;
  
      void add(int value) {
          IntQueue.Entry entry = new Entry(value);
          if (tail != null)
              tail.next = entry;
          tail = entry;
  
          if (head == null)
              head = entry;
      }
  
      boolean isEmpty() {
          return head == null;
      }
  
      int take() {
          if (head == null)
              throw new NoSuchElementException();
  
          int value = head.value;
          head = head.next;
          if (head == null)
              tail = null;
  
          return value;
      }
  }
  
  
  1.1      date: 2008/05/24 05:12:52;  author: jgreene;  state: Exp;javassist/src/main/javassist/bytecode/analysis/MultiArrayType.java
  
  Index: MultiArrayType.java
  ===================================================================
  /*
   * Javassist, a Java-bytecode translator toolkit.
   * Copyright (C) 1999-2007 Shigeru Chiba, and others. 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.analysis;
  
  import javassist.ClassPool;
  import javassist.CtClass;
  import javassist.NotFoundException;
  
  /**
   * Represents an array of {@link MultiType} instances.
   *
   * @author Jason T. Greene
   */
  public class MultiArrayType extends Type {
      private MultiType component;
      private int dims;
  
      public MultiArrayType(MultiType component, int dims) {
          super(null);
          this.component = component;
          this.dims = dims;
      }
  
      public CtClass getCtClass() {
          CtClass clazz = component.getCtClass();
          if (clazz == null)
              return null;
  
          ClassPool pool = clazz.getClassPool();
          if (pool == null)
              pool = ClassPool.getDefault();
  
          String name = arrayName(clazz.getName(), dims);
  
          try {
              return pool.get(name);
          } catch (NotFoundException e) {
              throw new RuntimeException(e);
          }
      }
  
      boolean popChanged() {
          return component.popChanged();
      }
  
      public int getDimensions() {
          return dims;
      }
  
      public Type getComponent() {
         return dims == 1 ? (Type)component : new MultiArrayType(component, dims - 1);
      }
  
      public int getSize() {
          return 1;
      }
  
      public boolean isArray() {
          return true;
      }
  
      public boolean isAssignableFrom(Type type) {
          throw new UnsupportedOperationException("Not implemented");
      }
  
      public boolean isReference() {
         return true;
      }
  
      public boolean isAssignableTo(Type type) {
          if (eq(type.getCtClass(), Type.OBJECT.getCtClass()))
              return true;
  
          if (eq(type.getCtClass(), Type.CLONEABLE.getCtClass()))
              return true;
  
          if (eq(type.getCtClass(), Type.SERIALIZABLE.getCtClass()))
              return true;
  
          if (! type.isArray())
              return false;
  
          Type typeRoot = getRootComponent(type);
          int typeDims = type.getDimensions();
  
          if (typeDims > dims)
              return false;
  
          if (typeDims < dims) {
              if (eq(typeRoot.getCtClass(), Type.OBJECT.getCtClass()))
                  return true;
  
              if (eq(typeRoot.getCtClass(), Type.CLONEABLE.getCtClass()))
                  return true;
  
              if (eq(typeRoot.getCtClass(), Type.SERIALIZABLE.getCtClass()))
                  return true;
  
              return false;
          }
  
          return component.isAssignableTo(typeRoot);
      }
  
      public boolean equals(Object o) {
          if (! (o instanceof MultiArrayType))
              return false;
          MultiArrayType multi = (MultiArrayType)o;
  
          return component.equals(multi.component) && dims == multi.dims;
      }
  
      public String toString() {
          // follows the same detailed formating scheme as component
          return arrayName(component.toString(), dims);
      }
  }
  
  
  
  1.1      date: 2008/05/24 05:12:52;  author: jgreene;  state: Exp;javassist/src/main/javassist/bytecode/analysis/MultiType.java
  
  Index: MultiType.java
  ===================================================================
  /*
   * Javassist, a Java-bytecode translator toolkit.
   * Copyright (C) 1999-2007 Shigeru Chiba, and others. 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.analysis;
  
  import java.util.HashMap;
  import java.util.Iterator;
  import java.util.Map;
  
  import javassist.CtClass;
  
  /**
   * MultiType represents an unresolved type. Whenever two <literal>Type</literal>
   * instances are merged, if they share more than one super type (either an
   * interface or a superclass), then a <literal>MultiType</literal> is used to
   * represent the possible super types. The goal of a <literal>MultiType</literal>
   * is to reduce the set of possible types down to a single resolved type. This
   * is done by eliminating non-assignable types from the typeset when the
   * <literal>MultiType</literal> is passed as an argument to
   * {@link Type#isAssignableFrom(Type)}, as well as removing non-intersecting
   * types during a merge.
   *
   * Note: Currently the <litera>MultiType</literal> instance is reused as much
   * as possible so that updates are visible from all frames. In addition, all
   * <literal>MultiType</literal> merge paths are also updated. This is somewhat
   * hackish, but it appears to handle most scenarios.
   *
   * @author Jason T. Greene
   */
  
  /* TODO - A better, but more involved, approach would be to track the instruction
   * offset that resulted in the creation of this type, and
   * whenever the typeset changes, to force a merge on that position. This
   * would require creating a new MultiType instance every time the typeset
   * changes, and somehow communicating assignment changes to the Analyzer
   */
  public class MultiType extends Type {
      private Map interfaces;
      private Type resolved;
      private Type potentialClass;
      private MultiType mergeSource;
      private boolean changed = false;
  
      public MultiType(Map interfaces) {
          this(interfaces, null);
      }
  
      public MultiType(Map interfaces, Type potentialClass) {
          super(null);
          this.interfaces = interfaces;
          this.potentialClass = potentialClass;
      }
  
      /**
       * Gets the class that corresponds with this type. If this information
       * is not yet known, java.lang.Object will be returned.
       */
      public CtClass getCtClass() {
          if (resolved != null)
              return resolved.getCtClass();
  
          return Type.OBJECT.getCtClass();
      }
  
      /**
       * Always returns null since this type is never used for an array.
       */
      public Type getComponent() {
          return null;
      }
  
      /**
       * Always returns 1, since this type is a reference.
       */
      public int getSize() {
          return 1;
      }
  
      /**
       * Always reutnrs false since this type is never used for an array
       */
      public boolean isArray() {
          return false;
      }
  
      /**
       * Returns true if the internal state has changed.
       */
      boolean popChanged() {
          boolean changed = this.changed;
          this.changed = false;
          return changed;
      }
  
      public boolean isAssignableFrom(Type type) {
          throw new UnsupportedOperationException("Not implemented");
      }
  
      public boolean isAssignableTo(Type type) {
          if (resolved != null)
              return type.isAssignableFrom(resolved);
  
          if (Type.OBJECT.equals(type))
              return true;
  
          if (potentialClass != null && !type.isAssignableFrom(potentialClass))
              potentialClass = null;
  
          Map map = mergeMultiAndSingle(this, type);
  
          if (map.size() == 1 && potentialClass == null) {
              // Update previous merge paths to the same resolved type
              resolved = Type.get((CtClass)map.values().iterator().next());
              propogateResolved();
  
              return true;
          }
  
          // Keep all previous merge paths up to date
          if (map.size() >= 1) {
              interfaces = map;
              propogateState();
  
              return true;
          }
  
          if (potentialClass != null) {
              resolved = potentialClass;
              propogateResolved();
  
              return true;
          }
  
          return false;
      }
  
      private void propogateState() {
          MultiType source = mergeSource;
          while (source != null) {
              source.interfaces = interfaces;
              source.potentialClass = potentialClass;
              source = source.mergeSource;
          }
      }
  
      private void propogateResolved() {
          MultiType source = mergeSource;
          while (source != null) {
              source.resolved = resolved;
              source = source.mergeSource;
          }
      }
  
      /**
       * Always returns true, since this type is always a reference.
       *
       * @return true
       */
      public boolean isReference() {
         return true;
      }
  
      private Map getAllMultiInterfaces(MultiType type) {
          Map map = new HashMap();
  
          Iterator iter = type.interfaces.values().iterator();
          while (iter.hasNext()) {
              CtClass intf = (CtClass)iter.next();
              map.put(intf.getName(), intf);
              getAllInterfaces(intf, map);
          }
  
          return map;
      }
  
  
      private Map mergeMultiInterfaces(MultiType type1, MultiType type2) {
          Map map1 = getAllMultiInterfaces(type1);
          Map map2 = getAllMultiInterfaces(type2);
  
          return findCommonInterfaces(map1, map2);
      }
  
      private Map mergeMultiAndSingle(MultiType multi, Type single) {
          Map map1 = getAllMultiInterfaces(multi);
          Map map2 = getAllInterfaces(single.getCtClass(), null);
  
          return findCommonInterfaces(map1, map2);
      }
  
      private boolean inMergeSource(MultiType source) {
          while (source != null) {
              if (source == this) {
                  System.out.println("INMERGESOURCE!");
                  return true;
  
              }
              source = source.mergeSource;
          }
  
          return false;
      }
  
      public Type merge(Type type) {
          if (this == type)
              return this;
  
          if (type == UNINIT)
              return this;
  
          if (type == BOGUS)
              return BOGUS;
  
          if (type == null)
              return this;
  
          if (resolved != null)
              return resolved.merge(type);
  
          if (potentialClass != null) {
              Type mergePotential = potentialClass.merge(type);
              if (! mergePotential.equals(potentialClass) || mergePotential.popChanged()) {
                  potentialClass = Type.OBJECT.equals(mergePotential) ? null : mergePotential;
                  changed = true;
              }
          }
  
          Map merged;
  
          if (type instanceof MultiType) {
              MultiType multi = (MultiType)type;
  
              if (multi.resolved != null) {
                  merged = mergeMultiAndSingle(this, multi.resolved);
              } else {
                  merged = mergeMultiInterfaces(multi, this);
                  if (! inMergeSource(multi))
                      mergeSource = multi;
              }
          } else {
              merged = mergeMultiAndSingle(this, type);
          }
  
          // Keep all previous merge paths up to date
          if (merged.size() > 1 || (merged.size() == 1 && potentialClass != null)) {
              // Check for changes
              if (merged.size() != interfaces.size()) {
                  changed = true;
              } else if (changed == false){
                  Iterator iter = merged.keySet().iterator();
                  while (iter.hasNext())
                      if (! interfaces.containsKey(iter.next()))
                          changed = true;
              }
  
              interfaces = merged;
              propogateState();
  
              return this;
          }
  
          if (merged.size() == 1) {
              resolved = Type.get((CtClass) merged.values().iterator().next());
          } else if (potentialClass != null){
              resolved = potentialClass;
          } else {
              resolved = OBJECT;
          }
  
          propogateResolved();
  
          return resolved;
      }
  
      public boolean equals(Object o) {
          if (! (o instanceof MultiType))
              return false;
  
          MultiType multi = (MultiType) o;
          if (resolved != null)
              return resolved.equals(multi.resolved);
          else if (multi.resolved != null)
              return false;
  
          return interfaces.keySet().equals(multi.interfaces.keySet());
      }
  
      public String toString() {
          if (resolved != null)
              return resolved.toString();
  
          StringBuffer buffer = new StringBuffer("{");
          Iterator iter = interfaces.keySet().iterator();
          while (iter.hasNext()) {
              buffer.append(iter.next());
              buffer.append(", ");
          }
          buffer.setLength(buffer.length() - 2);
          if (potentialClass != null)
              buffer.append(", *").append(potentialClass.toString());
          buffer.append("}");
          return buffer.toString();
      }
  }
  
  
  
  1.1      date: 2008/05/24 05:12:52;  author: jgreene;  state: Exp;javassist/src/main/javassist/bytecode/analysis/Subroutine.java
  
  Index: Subroutine.java
  ===================================================================
  /*
   * Javassist, a Java-bytecode translator toolkit.
   * Copyright (C) 1999-2007 Shigeru Chiba, and others. 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.analysis;
  
  import java.util.ArrayList;
  import java.util.Collection;
  import java.util.HashSet;
  import java.util.List;
  import java.util.Set;
  
  /**
   * Represents a nested method subroutine (marked by JSR and RET).
   *
   * @author Jason T. Greene
   */
  public class Subroutine {
      //private Set callers = new HashSet();
      private List callers = new ArrayList();
      private Set access = new HashSet();
      private int start;
  
      public Subroutine(int start, int caller) {
          this.start = start;
          callers.add(Integer.valueOf(caller));
      }
  
      public void addCaller(int caller) {
          callers.add(Integer.valueOf(caller));
      }
  
      public int start() {
          return start;
      }
  
      public void access(int index) {
          access.add(Integer.valueOf(index));
      }
  
      public boolean isAccessed(int index) {
          return access.contains(Integer.valueOf(index));
      }
  
      public Collection accessed() {
          return access;
      }
  
      public Collection callers() {
          return callers;
      }
  
      public String toString() {
          return "start = " + start + " callers = " + callers.toString();
      }
  }
  
  
  1.1      date: 2008/05/24 05:12:52;  author: jgreene;  state: Exp;javassist/src/main/javassist/bytecode/analysis/SubroutineScanner.java
  
  Index: SubroutineScanner.java
  ===================================================================
  /*
   * Javassist, a Java-bytecode translator toolkit.
   * Copyright (C) 1999-2007 Shigeru Chiba, and others. 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.analysis;
  
  import java.util.HashMap;
  import java.util.HashSet;
  import java.util.Map;
  import java.util.Set;
  
  import javassist.bytecode.BadBytecode;
  import javassist.bytecode.CodeAttribute;
  import javassist.bytecode.CodeIterator;
  import javassist.bytecode.ExceptionTable;
  import javassist.bytecode.MethodInfo;
  import javassist.bytecode.Opcode;
  
  /**
   * Discovers the subroutines in a method, and tracks all callers.
   *
   * @author Jason T. Greene
   */
  public class SubroutineScanner implements Opcode {
  
      private Subroutine[] subroutines;
      Map subTable = new HashMap();
      Set done = new HashSet();
  
  
      public Subroutine[] scan(MethodInfo method) throws BadBytecode {
          CodeAttribute code = method.getCodeAttribute();
          CodeIterator iter = code.iterator();
  
          subroutines = new Subroutine[code.getCodeLength()];
          subTable.clear();
          done.clear();
  
          scan(0, iter, null);
  
          ExceptionTable exceptions = code.getExceptionTable();
          for (int i = 0; i < exceptions.size(); i++) {
              int handler = exceptions.handlerPc(i);
              // If an exception is thrown in subroutine, the handler
              // is part of the same subroutine.
              scan(handler, iter, subroutines[exceptions.startPc(i)]);
          }
  
          return subroutines;
      }
  
      private void scan(int pos, CodeIterator iter, Subroutine sub) throws BadBytecode {
          // Skip already processed blocks
          if (done.contains(Integer.valueOf(pos)))
              return;
  
          done.add(Integer.valueOf(pos));
  
          int old = iter.lookAhead();
          iter.move(pos);
  
          boolean next;
          do {
              pos = iter.next();
              next = scanOp(pos, iter, sub) && iter.hasNext();
          } while (next);
  
          iter.move(old);
      }
  
      private boolean scanOp(int pos, CodeIterator iter, Subroutine sub) throws BadBytecode {
          subroutines[pos] = sub;
  
          int opcode = iter.byteAt(pos);
  
          if (opcode == TABLESWITCH) {
              scanTableSwitch(pos, iter, sub);
  
              return false;
          }
  
          if (opcode == LOOKUPSWITCH) {
              scanLookupSwitch(pos, iter, sub);
  
              return false;
          }
  
          // All forms of return and throw end current code flow
          if (Util.isReturn(opcode) || opcode == RET || opcode == ATHROW)
              return false;
  
          if (Util.isJumpInstruction(opcode)) {
              int target = Util.getJumpTarget(pos, iter);
              if (opcode == JSR || opcode == JSR_W) {
                  Subroutine s = (Subroutine) subTable.get(Integer.valueOf(target));
                  if (s == null) {
                      s = new Subroutine(target, pos);
                      subTable.put(Integer.valueOf(target), s);
                      scan(target, iter, s);
                  } else {
                      s.addCaller(pos);
                  }
              } else {
                  scan(target, iter, sub);
  
                  // GOTO ends current code flow
                  if (Util.isGoto(opcode))
                      return false;
              }
          }
  
          return true;
      }
  
      private void scanLookupSwitch(int pos, CodeIterator iter, Subroutine sub) throws BadBytecode {
          int index = (pos & ~3) + 4;
          // default
          scan(pos + iter.s32bitAt(index), iter, sub);
          int npairs = iter.s32bitAt(index += 4);
          int end = npairs * 8 + (index += 4);
  
          // skip "match"
          for (index += 4; index < end; index += 8) {
              int target = iter.s32bitAt(index) + pos;
              scan(target, iter, sub);
          }
      }
  
      private void scanTableSwitch(int pos, CodeIterator iter, Subroutine sub) throws BadBytecode {
          // Skip 4 byte alignment padding
          int index = (pos & ~3) + 4;
          // default
          scan(pos + iter.s32bitAt(index), iter, sub);
          int low = iter.s32bitAt(index += 4);
          int high = iter.s32bitAt(index += 4);
          int end = (high - low + 1) * 4 + (index += 4);
  
          // Offset table
          for (; index < end; index += 4) {
              int target = iter.s32bitAt(index) + pos;
              scan(target, iter, sub);
          }
      }
  
  
  }
  
  
  
  1.1      date: 2008/05/24 05:12:52;  author: jgreene;  state: Exp;javassist/src/main/javassist/bytecode/analysis/Type.java
  
  Index: Type.java
  ===================================================================
  /*
   * Javassist, a Java-bytecode translator toolkit.
   * Copyright (C) 1999-2007 Shigeru Chiba, and others. 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.analysis;
  
  import java.util.ArrayList;
  import java.util.HashMap;
  import java.util.IdentityHashMap;
  import java.util.Iterator;
  import java.util.Map;
  
  import javassist.ClassPool;
  import javassist.CtClass;
  import javassist.NotFoundException;
  
  /**
   * Represents a JVM type in data-flow analysis. This abstraction is necessary since
   * a JVM type not only includes all normal Java types, but also a few special types
   * that are used by the JVM internally. See the static field types on this class for
   * more info on these special types.
   *
   * All primitive and special types reuse the same instance, so identity comparison can
   * be used when examining them. Normal java types must use {@link #equals(Object)} to
   * compare type instances.
   *
   * In most cases, applications which consume this API, only need to call {@link #getCtClass()}
   * to obtain the needed type information.
   *
   * @author Jason T. Greene
   */
  public class Type {
      private final CtClass clazz;
      private final boolean special;
  
      private static final Map prims = new IdentityHashMap();
      /** Represents the double primitive type */
      public static final Type DOUBLE = new Type(CtClass.doubleType);
      /** Represents the boolean primitive type */
      public static final Type BOOLEAN = new Type(CtClass.booleanType);
      /** Represents the long primitive type */
      public static final Type LONG = new Type(CtClass.longType);
      /** Represents the char primitive type */
      public static final Type CHAR = new Type(CtClass.charType);
      /** Represents the byte primitive type */
      public static final Type BYTE = new Type(CtClass.byteType);
      /** Represents the short primitive type */
      public static final Type SHORT = new Type(CtClass.shortType);
      /** Represents the integer primitive type */
      public static final Type INTEGER = new Type(CtClass.intType);
      /** Represents the float primitive type */
      public static final Type FLOAT = new Type(CtClass.floatType);
      /** Represents the void primitive type */
      public static final Type VOID = new Type(CtClass.voidType);
  
      /**
       * Represents an unknown, or null type. This occurs when aconst_null is used.
       * It is important not to treat this type as java.lang.Object, since a null can
       * be assigned to any reference type. The analyzer will replace these with
       * an actual known type if it can be determined by a merged path with known type
       * information. If this type is encountered on a frame then it is guaranteed to
       * be null, and the type information is simply not available. Any attempts to
       * infer the type, without further information from the compiler would be a guess.
       */
      public static final Type UNINIT = new Type(null);
  
      /**
       * Represents an internal JVM return address, which is used by the RET
       * instruction to return to a JSR that invoked the subroutine.
       */
      public static final Type RETURN_ADDRESS = new Type(null, true);
  
      /** A placeholder used by the analyzer for the second word position of a double-word type */
      public static final Type TOP = new Type(null, true);
  
      /**
       * Represents a non-accessible value. Code cannot access the value this type
       * represents. It occurs when bytecode reuses a local variable table
       * position with non-mergable types. An example would be compiled code which
       * uses the same position for a primitive type in one branch, and a reference type
       * in another branch.
       */
      public static final Type BOGUS = new Type(null, true);
  
      /** Represents the java.lang.Object reference type */
      public static final Type OBJECT = lookupType("java.lang.Object");
      /** Represents the java.io.Serializable reference type */
      public static final Type SERIALIZABLE = lookupType("java.io.Serializable");
      /** Represents the java.lang.Coneable reference type */
      public static final Type CLONEABLE = lookupType("java.lang.Cloneable");
      /** Represents the java.lang.Throwable reference type */
      public static final Type THROWABLE = lookupType("java.lang.Throwable");
  
      static {
          prims.put(CtClass.doubleType, DOUBLE);
          prims.put(CtClass.longType, LONG);
          prims.put(CtClass.charType, CHAR);
          prims.put(CtClass.shortType, SHORT);
          prims.put(CtClass.intType, INTEGER);
          prims.put(CtClass.floatType, FLOAT);
          prims.put(CtClass.byteType, BYTE);
          prims.put(CtClass.booleanType, BOOLEAN);
          prims.put(CtClass.voidType, VOID);
  
      }
  
      /**
       * Obtain the Type for a given class. If the class is a primitive,
       * the the unique type instance for the primitive will be returned.
       * Otherwise a new Type instance representing the class is returned.
       *
       * @param clazz The java class
       * @return a type instance for this class
       */
      public static Type get(CtClass clazz) {
          Type type = (Type)prims.get(clazz);
          return type != null ? type : new Type(clazz);
      }
  
      private static Type lookupType(String name) {
          try {
               return new Type(ClassPool.getDefault().get(name));
          } catch (NotFoundException e) {
              throw new RuntimeException(e);
          }
      }
  
      Type(CtClass clazz) {
          this(clazz, false);
      }
  
      private Type(CtClass clazz, boolean special) {
          this.clazz = clazz;
          this.special = special;
      }
  
      // Used to indicate a merge internally triggered a change
      boolean popChanged() {
          return false;
      }
  
      /**
       * Gets the word size of this type. Double-word types, such as long and double
       * will occupy two positions on the local variable table or stack.
       *
       * @return the number of words needed to hold this type
       */
      public int getSize() {
          return clazz == CtClass.doubleType || clazz == CtClass.longType || this == TOP ? 2 : 1;
      }
  
      /**
       * Returns the class this type represents. If the type is special, null will be returned.
       *
       * @return the class for this type, or null if special
       */
      public CtClass getCtClass() {
          return clazz;
      }
  
      /**
       * Returns whether or not this type is a normal java reference, i.e. it is or extends java.lang.Object.
       *
       * @return true if a java reference, false if a primitive or special
       */
      public boolean isReference() {
          return !special && (clazz == null || !clazz.isPrimitive());
      }
  
      /**
       * Returns whether or not the type is special. A special type is one that is either used
       * for internal tracking, or is only used internally by the JVM.
       *
       * @return true if special, false if not
       */
      public boolean isSpecial() {
          return special;
      }
  
      /**
       * Returns whether or not this type is an array.
       *
       * @return true if an array, false if not
       */
      public boolean isArray() {
          return clazz != null && clazz.isArray();
      }
  
      /**
       * Returns the number of dimensions of this array. If the type is not an
       * array zero is returned.
       *
       * @return zero if not an array, otherwise the number of array dimensions.
       */
      public int getDimensions() {
          if (!isArray()) return 0;
  
          String name = clazz.getName();
          int pos = name.length() - 2;
          int count = 0;
          while (name.charAt(pos) == '[' ) {
              pos -= 2;
              count++;
          }
  
          return count;
      }
  
      /**
       * Returns the array component if this type is an array. If the type
       * is not an array null is returned.
       *
       * @return the array component if an array, otherwise null
       */
      public Type getComponent() {
          if (this.clazz == null || !this.clazz.isArray())
              return null;
  
          CtClass component;
          try {
              component = this.clazz.getComponentType();
          } catch (NotFoundException e) {
              throw new RuntimeException(e);
          }
  
          Type type = (Type)prims.get(component);
          return (type != null) ? type : new Type(component);
      }
  
      /**
       * Determines whether this type is assignable, to the passed type.
       * A type is assignable to another if it is either the same type, or
       * a sub-type.
       *
       * @param type the type to test assignability to
       * @return true if this is assignable to type, otherwise false
       */
      public boolean isAssignableFrom(Type type) {
          if (this == type)
              return true;
  
          if ((type == UNINIT && isReference()) || this == UNINIT && type.isReference())
              return true;
  
          if (type instanceof MultiType)
              return ((MultiType)type).isAssignableTo(this);
  
          if (type instanceof MultiArrayType)
              return ((MultiArrayType)type).isAssignableTo(this);
  
  
          // Primitives and Special types must be identical
          if (clazz == null || clazz.isPrimitive())
              return false;
  
          try {
              return type.clazz.subtypeOf(clazz);
          } catch (Exception e) {
              throw new RuntimeException(e);
          }
      }
  
      /**
       * Finds the common base type, or interface which both this and the specified
       * type can be assigned. If there is more than one possible answer, then a {@link MultiType},
       * or a {@link MultiArrayType} is returned. Multi-types have special rules,
       * and successive merges and assignment tests on them will alter their internal state,
       * as well as other multi-types they have been merged with. This method is used by
       * the data-flow analyzer to merge the type state from multiple branches.
       *
       * @param type the type to merge with
       * @return the merged type
       */
      public Type merge(Type type) {
          if (type == this)
              return this;
          if (type == null)
              return this;
          if (type == Type.UNINIT)
              return this;
          if (this == Type.UNINIT)
              return type;
  
          // Unequal primitives and special types can not be merged
          if (! type.isReference() || ! this.isReference())
              return BOGUS;
  
          // Centralize merging of multi-interface types
          if (type instanceof MultiType)
              return type.merge(this);
  
          if (type.isArray() && this.isArray())
              return mergeArray(type);
  
          try {
              return mergeClasses(type);
          } catch (NotFoundException e) {
              throw new RuntimeException(e);
          }
      }
  
     Type getRootComponent(Type type) {
          while (type.isArray())
              type = type.getComponent();
  
          return type;
      }
  
      private Type createArray(Type rootComponent, int dims) {
          if (rootComponent instanceof MultiType)
              return new MultiArrayType((MultiType) rootComponent, dims);
  
          String name = arrayName(rootComponent.clazz.getName(), dims);
  
          Type type;
          try {
              type = Type.get(getClassPool(rootComponent).get(name));
          } catch (NotFoundException e) {
              throw new RuntimeException(e);
          }
  
          return type;
      }
  
      String arrayName(String component, int dims) {
       // Using char[] since we have no StringBuilder in JDK4, and StringBuffer is slow.
          // Although, this is more efficient even if we did have one.
          int i = component.length();
          int size = i + dims * 2;
          char[] string = new char[size];
          component.getChars(0, i, string, 0);
          while (i < size) {
              string[i++] = '[';
              string[i++] = ']';
          }
          component = new String(string);
          return component;
      }
  
      private ClassPool getClassPool(Type rootComponent) {
          ClassPool pool = rootComponent.clazz.getClassPool();
          return pool != null ? pool : ClassPool.getDefault();
      }
  
      private Type mergeArray(Type type) {
          Type typeRoot = getRootComponent(type);
          Type thisRoot = getRootComponent(this);
          int typeDims = type.getDimensions();
          int thisDims = this.getDimensions();
  
          // Array commponents can be merged when the dimensions are equal
          if (typeDims == thisDims) {
              Type mergedComponent = thisRoot.merge(typeRoot);
  
              // If the components can not be merged (a primitive component mixed with a different type)
              // then Object is the common type.
              if (mergedComponent == Type.BOGUS)
                  return Type.OBJECT;
  
              return createArray(mergedComponent, thisDims);
          }
  
          Type targetRoot;
          int targetDims;
  
          if (typeDims < thisDims) {
              targetRoot = typeRoot;
              targetDims = typeDims;
          } else {
              targetRoot = thisRoot;
              targetDims = thisDims;
          }
  
          // Special case, arrays are cloneable and serializable, so prefer them when dimensions differ
          if (eq(CLONEABLE.clazz, targetRoot.clazz) || eq(SERIALIZABLE.clazz, targetRoot.clazz))
              return createArray(targetRoot, targetDims);
  
          return createArray(OBJECT, targetDims);
      }
  
      private static CtClass findCommonSuperClass(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;
          }
  
          // Phase 2 - Move deepBackup up by (deep end - deep)
          for (;;) {
              deep = deep.getSuperclass();
              if (deep == null)
                  break;
  
              backupDeep = backupDeep.getSuperclass();
          }
  
          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;
      }
  
      private Type mergeClasses(Type type) throws NotFoundException {
          CtClass superClass = findCommonSuperClass(this.clazz, type.clazz);
  
          // If its Object, then try and find a common interface(s)
          if (superClass.getSuperclass() == null) {
              Map interfaces = findCommonInterfaces(type);
              if (interfaces.size() == 1)
                  return new Type((CtClass) interfaces.values().iterator().next());
              if (interfaces.size() > 1)
                  return new MultiType(interfaces);
  
              // Only Object is in common
              return new Type(superClass);
          }
  
          // Check for a common interface that is not on the found supertype
          Map commonDeclared = findExclusiveDeclaredInterfaces(type, superClass);
          if (commonDeclared.size() > 0) {
              return new MultiType(commonDeclared, new Type(superClass));
          }
  
          return new Type(superClass);
      }
  
      private Map findCommonInterfaces(Type type) {
          Map typeMap = getAllInterfaces(type.clazz, null);
          Map thisMap = getAllInterfaces(this.clazz, null);
  
          return findCommonInterfaces(typeMap, thisMap);
      }
  
      private Map findExclusiveDeclaredInterfaces(Type type, CtClass exclude) {
          Map typeMap = getDeclaredInterfaces(type.clazz, null);
          Map thisMap = getDeclaredInterfaces(this.clazz, null);
          Map excludeMap = getAllInterfaces(exclude, null);
  
          Iterator i = excludeMap.keySet().iterator();
          while (i.hasNext()) {
              Object intf = i.next();
              typeMap.remove(intf);
              thisMap.remove(intf);
          }
  
          return findCommonInterfaces(typeMap, thisMap);
      }
  
  
      Map findCommonInterfaces(Map typeMap, Map alterMap) {
          Iterator i = alterMap.keySet().iterator();
          while (i.hasNext()) {
              if (! typeMap.containsKey(i.next()))
                  i.remove();
          }
  
          // Reduce to subinterfaces
          // This does not need to be recursive since we make a copy,
          // and that copy contains all super types for the whole hierarchy
          i = new ArrayList(alterMap.values()).iterator();
          while (i.hasNext()) {
              CtClass intf = (CtClass) i.next();
              CtClass[] interfaces;
              try {
                  interfaces = intf.getInterfaces();
              } catch (NotFoundException e) {
                  throw new RuntimeException(e);
              }
  
              for (int c = 0; c < interfaces.length; c++)
                  alterMap.remove(interfaces[c].getName());
          }
  
          return alterMap;
      }
  
      Map getAllInterfaces(CtClass clazz, Map map) {
          if (map == null)
              map = new HashMap();
  
          if (clazz.isInterface())
              map.put(clazz.getName(), clazz);
          do {
              try {
                  CtClass[] interfaces = clazz.getInterfaces();
                  for (int i = 0; i < interfaces.length; i++) {
                      CtClass intf = interfaces[i];
                      map.put(intf.getName(), intf);
                      getAllInterfaces(intf, map);
                  }
  
                  clazz = clazz.getSuperclass();
              } catch (NotFoundException e) {
                  throw new RuntimeException(e);
              }
          } while (clazz != null);
  
          return map;
      }
  
      Map getDeclaredInterfaces(CtClass clazz, Map map) {
          if (map == null)
              map = new HashMap();
  
          if (clazz.isInterface())
              map.put(clazz.getName(), clazz);
  
          CtClass[] interfaces;
          try {
              interfaces = clazz.getInterfaces();
          } catch (NotFoundException e) {
              throw new RuntimeException(e);
          }
  
          for (int i = 0; i < interfaces.length; i++) {
              CtClass intf = interfaces[i];
              map.put(intf.getName(), intf);
              getDeclaredInterfaces(intf, map);
          }
  
          return map;
      }
  
      public boolean equals(Object o) {
          if (! (o instanceof Type))
              return false;
  
          return o.getClass() == getClass() && eq(clazz, ((Type)o).clazz);
      }
  
      static boolean eq(CtClass one, CtClass two) {
          return one == two || (one != null && two != null && one.getName().equals(two.getName()));
      }
  
      public String toString() {
          if (this == BOGUS)
              return "BOGUS";
          if (this == UNINIT)
              return "UNINIT";
          if (this == RETURN_ADDRESS)
              return "RETURN ADDRESS";
          if (this == TOP)
              return "TOP";
  
          return clazz == null ? "null" : clazz.getName();
      }
  }
  
  
  
  1.1      date: 2008/05/24 05:12:52;  author: jgreene;  state: Exp;javassist/src/main/javassist/bytecode/analysis/Util.java
  
  Index: Util.java
  ===================================================================
  /*
   * Javassist, a Java-bytecode translator toolkit.
   * Copyright (C) 1999-2007 Shigeru Chiba, and others. 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.analysis;
  
  import javassist.bytecode.CodeIterator;
  import javassist.bytecode.Opcode;
  
  /**
   * A set of common utility methods.
   *
   * @author Jason T. Greene
   */
  public class Util implements Opcode {
      public static int getJumpTarget(int pos, CodeIterator iter) {
          int opcode = iter.byteAt(pos);
          pos += (opcode == JSR_W || opcode == GOTO_W) ? iter.s32bitAt(pos + 1) : iter.s16bitAt(pos + 1);
          return pos;
      }
  
      public static boolean isJumpInstruction(int opcode) {
          return (opcode >= IFEQ && opcode <= JSR) || opcode == IFNULL || opcode == IFNONNULL || opcode == JSR_W || opcode == GOTO_W;
      }
  
      public static boolean isGoto(int opcode) {
          return opcode == GOTO || opcode == GOTO_W;
      }
  
      public static boolean isJsr(int opcode) {
          return opcode == JSR || opcode == JSR_W;
      }
  
      public static boolean isReturn(int opcode) {
          return (opcode >= IRETURN && opcode <= RETURN);
      }
  }
  
  
  
  1.1      date: 2008/05/24 05:12:52;  author: jgreene;  state: Exp;javassist/src/main/javassist/bytecode/analysis/package.html
  
  Index: package.html
  ===================================================================
  <html>
  <body>
  Bytecode Analysis API.
  
  <p>This package provides an API for performing data-flow analysis on a method's bytecode.
  This allows the user to determine the type state of the stack and local variable table 
  at the start of every instruction. In addition this API can be used to validate 
  bytecode, find dead bytecode, and identify unnecessary checkcasts.
  
  <p>The users of this package must know the specifications of
  class file and Java bytecode.  For more details, read this book:
  
  <ul>Tim Lindholm and Frank Yellin,
  "The Java Virtual Machine Specification 2nd Ed.",
  Addison-Wesley, 1999.
  </ul>
  
  </body>
  </html>
  
  
  



More information about the jboss-cvs-commits mailing list