/**
  * Creates a new local variable of the given type.
  *
  * @param type the type of the local variable to be created.
  * @return the identifier of the newly created local variable.
  */
 public int newLocal(final Type type) {
   Object t;
   switch (type.getSort()) {
     case Type.BOOLEAN:
     case Type.CHAR:
     case Type.BYTE:
     case Type.SHORT:
     case Type.INT:
       t = Opcodes.INTEGER;
       break;
     case Type.FLOAT:
       t = Opcodes.FLOAT;
       break;
     case Type.LONG:
       t = Opcodes.LONG;
       break;
     case Type.DOUBLE:
       t = Opcodes.DOUBLE;
       break;
     case Type.ARRAY:
       t = type.getDescriptor();
       break;
       // case Type.OBJECT:
     default:
       t = type.getInternalName();
       break;
   }
   int local = nextLocal;
   setLocalType(local, type);
   setFrameLocal(local, t);
   nextLocal += type.getSize();
   return local;
 }
  /**
   * Creates a new {@link AnalyzerAdapter}.
   *
   * @param api the ASM API version implemented by this visitor. Must be one of {@link Opcodes#ASM4}
   *     or {@link Opcodes#ASM5}.
   * @param owner the owner's class name.
   * @param access the method's access flags (see {@link Opcodes}).
   * @param name the method's name.
   * @param desc the method's descriptor (see {@link Type Type}).
   * @param mv the method visitor to which this adapter delegates calls. May be <tt>null</tt>.
   */
  protected AnalyzerAdapter(
      final int api,
      final String owner,
      final int access,
      final String name,
      final String desc,
      final MethodVisitor mv) {
    super(api, mv);
    this.owner = owner;
    locals = new ArrayList<Object>();
    stack = new ArrayList<Object>();
    uninitializedTypes = new HashMap<Object, Object>();

    if ((access & Opcodes.ACC_STATIC) == 0) {
      if ("<init>".equals(name)) {
        locals.add(Opcodes.UNINITIALIZED_THIS);
      } else {
        locals.add(owner);
      }
    }
    Type[] types = Type.getArgumentTypes(desc);
    for (int i = 0; i < types.length; ++i) {
      Type type = types[i];
      switch (type.getSort()) {
        case Type.BOOLEAN:
        case Type.CHAR:
        case Type.BYTE:
        case Type.SHORT:
        case Type.INT:
          locals.add(Opcodes.INTEGER);
          break;
        case Type.FLOAT:
          locals.add(Opcodes.FLOAT);
          break;
        case Type.LONG:
          locals.add(Opcodes.LONG);
          locals.add(Opcodes.TOP);
          break;
        case Type.DOUBLE:
          locals.add(Opcodes.DOUBLE);
          locals.add(Opcodes.TOP);
          break;
        case Type.ARRAY:
          locals.add(types[i].getDescriptor());
          break;
          // case Type.OBJECT:
        default:
          locals.add(types[i].getInternalName());
      }
    }
    maxLocals = locals.size();
  }
 /**
  * Creates a new {@link LocalVariablesSorter}.
  *
  * @param access access flags of the adapted method.
  * @param desc the method's descriptor (see {@link Type Type}).
  * @param mv the method visitor to which this adapter delegates calls.
  */
 public LocalVariablesSorter(final int access, final String desc, final MethodVisitor mv) {
   super(mv);
   Type[] args = Type.getArgumentTypes(desc);
   nextLocal = (Opcodes.ACC_STATIC & access) != 0 ? 0 : 1;
   for (int i = 0; i < args.length; i++) {
     nextLocal += args[i].getSize();
   }
   firstLocal = nextLocal;
 }
 private int remap(final int var, final Type type) {
   if (var < firstLocal) {
     return var;
   }
   int key = 2 * var + type.getSize() - 1;
   int size = mapping.length;
   if (key >= size) {
     int[] newMapping = new int[Math.max(2 * size, key + 1)];
     System.arraycopy(mapping, 0, newMapping, 0, size);
     mapping = newMapping;
   }
   int value = mapping[key];
   if (value == 0) {
     value = nextLocal + 1;
     mapping[key] = value;
     setLocalType(nextLocal, type);
     nextLocal += type.getSize();
   }
   if (value - 1 != var) {
     changed = true;
   }
   return value - 1;
 }
 private void pop(final String desc) {
   char c = desc.charAt(0);
   if (c == '(') {
     int n = 0;
     Type[] types = Type.getArgumentTypes(desc);
     for (int i = 0; i < types.length; ++i) {
       n += types[i].getSize();
     }
     pop(n);
   } else if (c == 'J' || c == 'D') {
     pop(2);
   } else {
     pop(1);
   }
 }
/**
 * A {@link MethodAdapter} that renumbers local variables in their order of appearance. This adapter
 * allows one to easily add new local variables to a method. It may be used by inheriting from this
 * class, but the preferred way of using it is via delegation: the next visitor in the chain can
 * indeed add new locals when needed by calling {@link #newLocal} on this adapter (this requires a
 * reference back to this {@link LocalVariablesSorter}).
 *
 * @author Chris Nokleberg
 * @author Eugene Kuleshov
 * @author Eric Bruneton
 */
public class LocalVariablesSorter extends MethodAdapter {

  private static final Type OBJECT_TYPE = Type.getObjectType("java/lang/Object");

  /**
   * Mapping from old to new local variable indexes. A local variable at index i of size 1 is
   * remapped to 'mapping[2*i]', while a local variable at index i of size 2 is remapped to
   * 'mapping[2*i+1]'.
   */
  private int[] mapping = new int[40];

  /** Array used to store stack map local variable types after remapping. */
  private Object[] newLocals = new Object[20];

  /** Index of the first local variable, after formal parameters. */
  protected final int firstLocal;

  /** Index of the next local variable to be created by {@link #newLocal}. */
  protected int nextLocal;

  /** Indicates if at least one local variable has moved due to remapping. */
  private boolean changed;

  /**
   * Creates a new {@link LocalVariablesSorter}.
   *
   * @param access access flags of the adapted method.
   * @param desc the method's descriptor (see {@link Type Type}).
   * @param mv the method visitor to which this adapter delegates calls.
   */
  public LocalVariablesSorter(final int access, final String desc, final MethodVisitor mv) {
    super(mv);
    Type[] args = Type.getArgumentTypes(desc);
    nextLocal = (Opcodes.ACC_STATIC & access) != 0 ? 0 : 1;
    for (int i = 0; i < args.length; i++) {
      nextLocal += args[i].getSize();
    }
    firstLocal = nextLocal;
  }

  public void visitVarInsn(final int opcode, final int var) {
    Type type;
    switch (opcode) {
      case Opcodes.LLOAD:
      case Opcodes.LSTORE:
        type = Type.LONG_TYPE;
        break;

      case Opcodes.DLOAD:
      case Opcodes.DSTORE:
        type = Type.DOUBLE_TYPE;
        break;

      case Opcodes.FLOAD:
      case Opcodes.FSTORE:
        type = Type.FLOAT_TYPE;
        break;

      case Opcodes.ILOAD:
      case Opcodes.ISTORE:
        type = Type.INT_TYPE;
        break;

      case Opcodes.ALOAD:
      case Opcodes.ASTORE:
        type = OBJECT_TYPE;
        break;

        // case RET:
      default:
        type = Type.VOID_TYPE;
    }
    mv.visitVarInsn(opcode, remap(var, type));
  }

  public void visitIincInsn(final int var, final int increment) {
    mv.visitIincInsn(remap(var, Type.INT_TYPE), increment);
  }

  public void visitMaxs(final int maxStack, final int maxLocals) {
    mv.visitMaxs(maxStack, nextLocal);
  }

  public void visitLocalVariable(
      final String name,
      final String desc,
      final String signature,
      final Label start,
      final Label end,
      final int index) {
    int size = "J".equals(desc) || "D".equals(desc) ? 2 : 1;
    int newIndex = remap(index, size);
    mv.visitLocalVariable(name, desc, signature, start, end, newIndex);
  }

  public void visitFrame(
      final int type,
      final int nLocal,
      final Object[] local,
      final int nStack,
      final Object[] stack) {
    if (type != Opcodes.F_NEW) { // uncompressed frame
      throw new IllegalStateException(
          "ClassReader.accept() should be called with EXPAND_FRAMES flag");
    }

    if (!changed) { // optimization for the case where mapping = identity
      mv.visitFrame(type, nLocal, local, nStack, stack);
      return;
    }

    // creates a copy of newLocals
    Object[] oldLocals = new Object[newLocals.length];
    System.arraycopy(newLocals, 0, oldLocals, 0, oldLocals.length);

    // copies types from 'local' to 'newLocals'
    // 'newLocals' already contains the variables added with 'newLocal'

    int index = 0; // old local variable index
    int number = 0; // old local variable number
    for (; number < nLocal; ++number) {
      Object t = local[number];
      int size = t == Opcodes.LONG || t == Opcodes.DOUBLE ? 2 : 1;
      if (t != Opcodes.TOP) {
        setFrameLocal(remap(index, size), t);
      }
      index += size;
    }

    // removes TOP after long and double types as well as trailing TOPs

    index = 0;
    number = 0;
    for (int i = 0; index < newLocals.length; ++i) {
      Object t = newLocals[index++];
      if (t != null && t != Opcodes.TOP) {
        newLocals[i] = t;
        number = i + 1;
        if (t == Opcodes.LONG || t == Opcodes.DOUBLE) {
          index += 1;
        }
      } else {
        newLocals[i] = Opcodes.TOP;
      }
    }

    // visits remapped frame
    mv.visitFrame(type, number, newLocals, nStack, stack);

    // restores original value of 'newLocals'
    newLocals = oldLocals;
  }

  // -------------

  /**
   * Creates a new local variable of the given type.
   *
   * @param type the type of the local variable to be created.
   * @return the identifier of the newly created local variable.
   */
  public int newLocal(final Type type) {
    Object t;
    switch (type.getSort()) {
      case Type.BOOLEAN:
      case Type.CHAR:
      case Type.BYTE:
      case Type.SHORT:
      case Type.INT:
        t = Opcodes.INTEGER;
        break;
      case Type.FLOAT:
        t = Opcodes.FLOAT;
        break;
      case Type.LONG:
        t = Opcodes.LONG;
        break;
      case Type.DOUBLE:
        t = Opcodes.DOUBLE;
        break;
      case Type.ARRAY:
        t = type.getDescriptor();
        break;
        // case Type.OBJECT:
      default:
        t = type.getInternalName();
        break;
    }
    int local = nextLocal;
    setLocalType(local, type);
    setFrameLocal(local, t);
    nextLocal += type.getSize();
    return local;
  }

  /**
   * Sets the current type of the given local variable. The default implementation of this method
   * does nothing.
   *
   * @param local a local variable identifier, as returned by {@link #newLocal newLocal()}.
   * @param type the type of the value being stored in the local variable
   */
  protected void setLocalType(final int local, final Type type) {}

  private void setFrameLocal(final int local, final Object type) {
    int l = newLocals.length;
    if (local >= l) {
      Object[] a = new Object[Math.max(2 * l, local + 1)];
      System.arraycopy(newLocals, 0, a, 0, l);
      newLocals = a;
    }
    newLocals[local] = type;
  }

  private int remap(final int var, final Type type) {
    if (var < firstLocal) {
      return var;
    }
    int key = 2 * var + type.getSize() - 1;
    int size = mapping.length;
    if (key >= size) {
      int[] newMapping = new int[Math.max(2 * size, key + 1)];
      System.arraycopy(mapping, 0, newMapping, 0, size);
      mapping = newMapping;
    }
    int value = mapping[key];
    if (value == 0) {
      value = nextLocal + 1;
      mapping[key] = value;
      setLocalType(nextLocal, type);
      nextLocal += type.getSize();
    }
    if (value - 1 != var) {
      changed = true;
    }
    return value - 1;
  }

  private int remap(final int var, final int size) {
    if (var < firstLocal || !changed) {
      return var;
    }
    int key = 2 * var + size - 1;
    int value = key < mapping.length ? mapping[key] : 0;
    if (value == 0) {
      throw new IllegalStateException("Unknown local variable " + var);
    }
    return value - 1;
  }
}
 private void execute(final int opcode, final int iarg, final String sarg) {
   if (this.locals == null) {
     labels = null;
     return;
   }
   Object t1, t2, t3, t4;
   switch (opcode) {
     case Opcodes.NOP:
     case Opcodes.INEG:
     case Opcodes.LNEG:
     case Opcodes.FNEG:
     case Opcodes.DNEG:
     case Opcodes.I2B:
     case Opcodes.I2C:
     case Opcodes.I2S:
     case Opcodes.GOTO:
     case Opcodes.RETURN:
       break;
     case Opcodes.ACONST_NULL:
       push(Opcodes.NULL);
       break;
     case Opcodes.ICONST_M1:
     case Opcodes.ICONST_0:
     case Opcodes.ICONST_1:
     case Opcodes.ICONST_2:
     case Opcodes.ICONST_3:
     case Opcodes.ICONST_4:
     case Opcodes.ICONST_5:
     case Opcodes.BIPUSH:
     case Opcodes.SIPUSH:
       push(Opcodes.INTEGER);
       break;
     case Opcodes.LCONST_0:
     case Opcodes.LCONST_1:
       push(Opcodes.LONG);
       push(Opcodes.TOP);
       break;
     case Opcodes.FCONST_0:
     case Opcodes.FCONST_1:
     case Opcodes.FCONST_2:
       push(Opcodes.FLOAT);
       break;
     case Opcodes.DCONST_0:
     case Opcodes.DCONST_1:
       push(Opcodes.DOUBLE);
       push(Opcodes.TOP);
       break;
     case Opcodes.ILOAD:
     case Opcodes.FLOAD:
     case Opcodes.ALOAD:
       push(get(iarg));
       break;
     case Opcodes.LLOAD:
     case Opcodes.DLOAD:
       push(get(iarg));
       push(Opcodes.TOP);
       break;
     case Opcodes.IALOAD:
     case Opcodes.BALOAD:
     case Opcodes.CALOAD:
     case Opcodes.SALOAD:
       pop(2);
       push(Opcodes.INTEGER);
       break;
     case Opcodes.LALOAD:
     case Opcodes.D2L:
       pop(2);
       push(Opcodes.LONG);
       push(Opcodes.TOP);
       break;
     case Opcodes.FALOAD:
       pop(2);
       push(Opcodes.FLOAT);
       break;
     case Opcodes.DALOAD:
     case Opcodes.L2D:
       pop(2);
       push(Opcodes.DOUBLE);
       push(Opcodes.TOP);
       break;
     case Opcodes.AALOAD:
       pop(1);
       t1 = pop();
       if (t1 instanceof String) {
         pushDesc(((String) t1).substring(1));
       } else {
         push("java/lang/Object");
       }
       break;
     case Opcodes.ISTORE:
     case Opcodes.FSTORE:
     case Opcodes.ASTORE:
       t1 = pop();
       set(iarg, t1);
       if (iarg > 0) {
         t2 = get(iarg - 1);
         if (t2 == Opcodes.LONG || t2 == Opcodes.DOUBLE) {
           set(iarg - 1, Opcodes.TOP);
         }
       }
       break;
     case Opcodes.LSTORE:
     case Opcodes.DSTORE:
       pop(1);
       t1 = pop();
       set(iarg, t1);
       set(iarg + 1, Opcodes.TOP);
       if (iarg > 0) {
         t2 = get(iarg - 1);
         if (t2 == Opcodes.LONG || t2 == Opcodes.DOUBLE) {
           set(iarg - 1, Opcodes.TOP);
         }
       }
       break;
     case Opcodes.IASTORE:
     case Opcodes.BASTORE:
     case Opcodes.CASTORE:
     case Opcodes.SASTORE:
     case Opcodes.FASTORE:
     case Opcodes.AASTORE:
       pop(3);
       break;
     case Opcodes.LASTORE:
     case Opcodes.DASTORE:
       pop(4);
       break;
     case Opcodes.POP:
     case Opcodes.IFEQ:
     case Opcodes.IFNE:
     case Opcodes.IFLT:
     case Opcodes.IFGE:
     case Opcodes.IFGT:
     case Opcodes.IFLE:
     case Opcodes.IRETURN:
     case Opcodes.FRETURN:
     case Opcodes.ARETURN:
     case Opcodes.TABLESWITCH:
     case Opcodes.LOOKUPSWITCH:
     case Opcodes.ATHROW:
     case Opcodes.MONITORENTER:
     case Opcodes.MONITOREXIT:
     case Opcodes.IFNULL:
     case Opcodes.IFNONNULL:
       pop(1);
       break;
     case Opcodes.POP2:
     case Opcodes.IF_ICMPEQ:
     case Opcodes.IF_ICMPNE:
     case Opcodes.IF_ICMPLT:
     case Opcodes.IF_ICMPGE:
     case Opcodes.IF_ICMPGT:
     case Opcodes.IF_ICMPLE:
     case Opcodes.IF_ACMPEQ:
     case Opcodes.IF_ACMPNE:
     case Opcodes.LRETURN:
     case Opcodes.DRETURN:
       pop(2);
       break;
     case Opcodes.DUP:
       t1 = pop();
       push(t1);
       push(t1);
       break;
     case Opcodes.DUP_X1:
       t1 = pop();
       t2 = pop();
       push(t1);
       push(t2);
       push(t1);
       break;
     case Opcodes.DUP_X2:
       t1 = pop();
       t2 = pop();
       t3 = pop();
       push(t1);
       push(t3);
       push(t2);
       push(t1);
       break;
     case Opcodes.DUP2:
       t1 = pop();
       t2 = pop();
       push(t2);
       push(t1);
       push(t2);
       push(t1);
       break;
     case Opcodes.DUP2_X1:
       t1 = pop();
       t2 = pop();
       t3 = pop();
       push(t2);
       push(t1);
       push(t3);
       push(t2);
       push(t1);
       break;
     case Opcodes.DUP2_X2:
       t1 = pop();
       t2 = pop();
       t3 = pop();
       t4 = pop();
       push(t2);
       push(t1);
       push(t4);
       push(t3);
       push(t2);
       push(t1);
       break;
     case Opcodes.SWAP:
       t1 = pop();
       t2 = pop();
       push(t1);
       push(t2);
       break;
     case Opcodes.IADD:
     case Opcodes.ISUB:
     case Opcodes.IMUL:
     case Opcodes.IDIV:
     case Opcodes.IREM:
     case Opcodes.IAND:
     case Opcodes.IOR:
     case Opcodes.IXOR:
     case Opcodes.ISHL:
     case Opcodes.ISHR:
     case Opcodes.IUSHR:
     case Opcodes.L2I:
     case Opcodes.D2I:
     case Opcodes.FCMPL:
     case Opcodes.FCMPG:
       pop(2);
       push(Opcodes.INTEGER);
       break;
     case Opcodes.LADD:
     case Opcodes.LSUB:
     case Opcodes.LMUL:
     case Opcodes.LDIV:
     case Opcodes.LREM:
     case Opcodes.LAND:
     case Opcodes.LOR:
     case Opcodes.LXOR:
       pop(4);
       push(Opcodes.LONG);
       push(Opcodes.TOP);
       break;
     case Opcodes.FADD:
     case Opcodes.FSUB:
     case Opcodes.FMUL:
     case Opcodes.FDIV:
     case Opcodes.FREM:
     case Opcodes.L2F:
     case Opcodes.D2F:
       pop(2);
       push(Opcodes.FLOAT);
       break;
     case Opcodes.DADD:
     case Opcodes.DSUB:
     case Opcodes.DMUL:
     case Opcodes.DDIV:
     case Opcodes.DREM:
       pop(4);
       push(Opcodes.DOUBLE);
       push(Opcodes.TOP);
       break;
     case Opcodes.LSHL:
     case Opcodes.LSHR:
     case Opcodes.LUSHR:
       pop(3);
       push(Opcodes.LONG);
       push(Opcodes.TOP);
       break;
     case Opcodes.IINC:
       set(iarg, Opcodes.INTEGER);
       break;
     case Opcodes.I2L:
     case Opcodes.F2L:
       pop(1);
       push(Opcodes.LONG);
       push(Opcodes.TOP);
       break;
     case Opcodes.I2F:
       pop(1);
       push(Opcodes.FLOAT);
       break;
     case Opcodes.I2D:
     case Opcodes.F2D:
       pop(1);
       push(Opcodes.DOUBLE);
       push(Opcodes.TOP);
       break;
     case Opcodes.F2I:
     case Opcodes.ARRAYLENGTH:
     case Opcodes.INSTANCEOF:
       pop(1);
       push(Opcodes.INTEGER);
       break;
     case Opcodes.LCMP:
     case Opcodes.DCMPL:
     case Opcodes.DCMPG:
       pop(4);
       push(Opcodes.INTEGER);
       break;
     case Opcodes.JSR:
     case Opcodes.RET:
       throw new RuntimeException("JSR/RET are not supported");
     case Opcodes.GETSTATIC:
       pushDesc(sarg);
       break;
     case Opcodes.PUTSTATIC:
       pop(sarg);
       break;
     case Opcodes.GETFIELD:
       pop(1);
       pushDesc(sarg);
       break;
     case Opcodes.PUTFIELD:
       pop(sarg);
       pop();
       break;
     case Opcodes.NEW:
       push(labels.get(0));
       break;
     case Opcodes.NEWARRAY:
       pop();
       switch (iarg) {
         case Opcodes.T_BOOLEAN:
           pushDesc("[Z");
           break;
         case Opcodes.T_CHAR:
           pushDesc("[C");
           break;
         case Opcodes.T_BYTE:
           pushDesc("[B");
           break;
         case Opcodes.T_SHORT:
           pushDesc("[S");
           break;
         case Opcodes.T_INT:
           pushDesc("[I");
           break;
         case Opcodes.T_FLOAT:
           pushDesc("[F");
           break;
         case Opcodes.T_DOUBLE:
           pushDesc("[D");
           break;
           // case Opcodes.T_LONG:
         default:
           pushDesc("[J");
           break;
       }
       break;
     case Opcodes.ANEWARRAY:
       pop();
       pushDesc("[" + Type.getObjectType(sarg));
       break;
     case Opcodes.CHECKCAST:
       pop();
       pushDesc(Type.getObjectType(sarg).getDescriptor());
       break;
       // case Opcodes.MULTIANEWARRAY:
     default:
       pop(iarg);
       pushDesc(sarg);
       break;
   }
   labels = null;
 }