class LocalVariablesSorter extends MethodVisitor {

  @Override
  public void visitFrame(
      final int type,
      final int numLocal,
      final Object[] local,
      final int numStack,
      final Object[] stack) {
    if (type != Opcodes.F_NEW) { // Uncompressed frame.
      throw new IllegalArgumentException(
          "LocalVariablesSorter only accepts expanded frames (see ClassReader.EXPAND_FRAMES)");
    }

    // Create a copy of remappedLocals.
    Object[] oldRemappedLocals = new Object[remappedLocalTypes.length];
    System.arraycopy(remappedLocalTypes, 0, oldRemappedLocals, 0, oldRemappedLocals.length);

    updateNewLocals(remappedLocalTypes);

    // Copy the types from 'local' to 'remappedLocals'. 'remappedLocals' already contains the
    // variables added with 'newLocal'.
    int oldVar = 0; // Old local variable index.
    for (int i = 0; i < numLocal; ++i) {
      Object localType = local[i];
      if (localType != Opcodes.TOP) {
        Type varType = OBJECT_TYPE;
        if (localType == Opcodes.INTEGER) {
          varType = Type.INT_TYPE;
        } else if (localType == Opcodes.FLOAT) {
          varType = Type.FLOAT_TYPE;
        } else if (localType == Opcodes.LONG) {
          varType = Type.LONG_TYPE;
        } else if (localType == Opcodes.DOUBLE) {
          varType = Type.DOUBLE_TYPE;
        } else if (localType instanceof String) {
          varType = Type.getObjectType((String) localType);
        }
        setFrameLocal(remap(oldVar, varType), localType);
      }
      oldVar += localType == Opcodes.LONG || localType == Opcodes.DOUBLE ? 2 : 1;
    }

    // Remove TOP after long and double types as well as trailing TOPs.
    oldVar = 0;
    int newVar = 0;
    int remappedNumLocal = 0;
    while (oldVar < remappedLocalTypes.length) {
      Object localType = remappedLocalTypes[oldVar];
      oldVar += localType == Opcodes.LONG || localType == Opcodes.DOUBLE ? 2 : 1;
      if (localType != null && localType != Opcodes.TOP) {
        remappedLocalTypes[newVar++] = localType;
        remappedNumLocal = newVar;
      } else {
        remappedLocalTypes[newVar++] = Opcodes.TOP;
      }
    }

    // Visit the remapped frame.
    super.visitFrame(type, remappedNumLocal, remappedLocalTypes, numStack, stack);

    // Restore the original value of 'remappedLocals'.
    remappedLocalTypes = oldRemappedLocals;
  }

  public  LocalVariablesSorter(
      final int access, final String descriptor, final MethodVisitor methodVisitor);
  protected  LocalVariablesSorter(
      final int api, final int access, final String descriptor, final MethodVisitor methodVisitor);

}