jodd-proxetta/src/main/java/jodd/asm7/ClassReader.java
private void readCode(
      final MethodVisitor methodVisitor, final Context context, final int codeOffset) {
    int currentOffset = codeOffset;

    // Read the max_stack, max_locals and code_length fields.
    final byte[] classFileBuffer = b;
    final char[] charBuffer = context.charBuffer;
    final int maxStack = readUnsignedShort(currentOffset);
    final int maxLocals = readUnsignedShort(currentOffset + 2);
    final int codeLength = readInt(currentOffset + 4);
    currentOffset += 8;

    // Read the bytecode 'code' array to create a label for each referenced instruction.
    final int bytecodeStartOffset = currentOffset;
    final int bytecodeEndOffset = currentOffset + codeLength;
    final Label[] labels = context.currentMethodLabels = new Label[codeLength + 1];
    while (currentOffset < bytecodeEndOffset) {
      final int bytecodeOffset = currentOffset - bytecodeStartOffset;
      final int opcode = classFileBuffer[currentOffset] & 0xFF;
      switch (opcode) {
        case Constants.NOP:
        case Constants.ACONST_NULL:
        case Constants.ICONST_M1:
        case Constants.ICONST_0:
        case Constants.ICONST_1:
        case Constants.ICONST_2:
        case Constants.ICONST_3:
        case Constants.ICONST_4:
        case Constants.ICONST_5:
        case Constants.LCONST_0:
        case Constants.LCONST_1:
        case Constants.FCONST_0:
        case Constants.FCONST_1:
        case Constants.FCONST_2:
        case Constants.DCONST_0:
        case Constants.DCONST_1:
        case Constants.IALOAD:
        case Constants.LALOAD:
        case Constants.FALOAD:
        case Constants.DALOAD:
        case Constants.AALOAD:
        case Constants.BALOAD:
        case Constants.CALOAD:
        case Constants.SALOAD:
        case Constants.IASTORE:
        case Constants.LASTORE:
        case Constants.FASTORE:
        case Constants.DASTORE:
        case Constants.AASTORE:
        case Constants.BASTORE:
        case Constants.CASTORE:
        case Constants.SASTORE:
        case Constants.POP:
        case Constants.POP2:
        case Constants.DUP:
        case Constants.DUP_X1:
        case Constants.DUP_X2:
        case Constants.DUP2:
        case Constants.DUP2_X1:
        case Constants.DUP2_X2:
        case Constants.SWAP:
        case Constants.IADD:
        case Constants.LADD:
        case Constants.FADD:
        case Constants.DADD:
        case Constants.ISUB:
        case Constants.LSUB:
        case Constants.FSUB:
        case Constants.DSUB:
        case Constants.IMUL:
        case Constants.LMUL:
        case Constants.FMUL:
        case Constants.DMUL:
        case Constants.IDIV:
        case Constants.LDIV:
        case Constants.FDIV:
        case Constants.DDIV:
        case Constants.IREM:
        case Constants.LREM:
        case Constants.FREM:
        case Constants.DREM:
        case Constants.INEG:
        case Constants.LNEG:
        case Constants.FNEG:
        case Constants.DNEG:
        case Constants.ISHL:
        case Constants.LSHL:
        case Constants.ISHR:
        case Constants.LSHR:
        case Constants.IUSHR:
        case Constants.LUSHR:
        case Constants.IAND:
        case Constants.LAND:
        case Constants.IOR:
        case Constants.LOR:
        case Constants.IXOR:
        case Constants.LXOR:
        case Constants.I2L:
        case Constants.I2F:
        case Constants.I2D:
        case Constants.L2I:
        case Constants.L2F:
        case Constants.L2D:
        case Constants.F2I:
        case Constants.F2L:
        case Constants.F2D:
        case Constants.D2I:
        case Constants.D2L:
        case Constants.D2F:
        case Constants.I2B:
        case Constants.I2C:
        case Constants.I2S:
        case Constants.LCMP:
        case Constants.FCMPL:
        case Constants.FCMPG:
        case Constants.DCMPL:
        case Constants.DCMPG:
        case Constants.IRETURN:
        case Constants.LRETURN:
        case Constants.FRETURN:
        case Constants.DRETURN:
        case Constants.ARETURN:
        case Constants.RETURN:
        case Constants.ARRAYLENGTH:
        case Constants.ATHROW:
        case Constants.MONITORENTER:
        case Constants.MONITOREXIT:
        case Constants.ILOAD_0:
        case Constants.ILOAD_1:
        case Constants.ILOAD_2:
        case Constants.ILOAD_3:
        case Constants.LLOAD_0:
        case Constants.LLOAD_1:
        case Constants.LLOAD_2:
        case Constants.LLOAD_3:
        case Constants.FLOAD_0:
        case Constants.FLOAD_1:
        case Constants.FLOAD_2:
        case Constants.FLOAD_3:
        case Constants.DLOAD_0:
        case Constants.DLOAD_1:
        case Constants.DLOAD_2:
        case Constants.DLOAD_3:
        case Constants.ALOAD_0:
        case Constants.ALOAD_1:
        case Constants.ALOAD_2:
        case Constants.ALOAD_3:
        case Constants.ISTORE_0:
        case Constants.ISTORE_1:
        case Constants.ISTORE_2:
        case Constants.ISTORE_3:
        case Constants.LSTORE_0:
        case Constants.LSTORE_1:
        case Constants.LSTORE_2:
        case Constants.LSTORE_3:
        case Constants.FSTORE_0:
        case Constants.FSTORE_1:
        case Constants.FSTORE_2:
        case Constants.FSTORE_3:
        case Constants.DSTORE_0:
        case Constants.DSTORE_1:
        case Constants.DSTORE_2:
        case Constants.DSTORE_3:
        case Constants.ASTORE_0:
        case Constants.ASTORE_1:
        case Constants.ASTORE_2:
        case Constants.ASTORE_3:
          currentOffset += 1;
          break;
        case Constants.IFEQ:
        case Constants.IFNE:
        case Constants.IFLT:
        case Constants.IFGE:
        case Constants.IFGT:
        case Constants.IFLE:
        case Constants.IF_ICMPEQ:
        case Constants.IF_ICMPNE:
        case Constants.IF_ICMPLT:
        case Constants.IF_ICMPGE:
        case Constants.IF_ICMPGT:
        case Constants.IF_ICMPLE:
        case Constants.IF_ACMPEQ:
        case Constants.IF_ACMPNE:
        case Constants.GOTO:
        case Constants.JSR:
        case Constants.IFNULL:
        case Constants.IFNONNULL:
          createLabel(bytecodeOffset + readShort(currentOffset + 1), labels);
          currentOffset += 3;
          break;
        case Constants.ASM_IFEQ:
        case Constants.ASM_IFNE:
        case Constants.ASM_IFLT:
        case Constants.ASM_IFGE:
        case Constants.ASM_IFGT:
        case Constants.ASM_IFLE:
        case Constants.ASM_IF_ICMPEQ:
        case Constants.ASM_IF_ICMPNE:
        case Constants.ASM_IF_ICMPLT:
        case Constants.ASM_IF_ICMPGE:
        case Constants.ASM_IF_ICMPGT:
        case Constants.ASM_IF_ICMPLE:
        case Constants.ASM_IF_ACMPEQ:
        case Constants.ASM_IF_ACMPNE:
        case Constants.ASM_GOTO:
        case Constants.ASM_JSR:
        case Constants.ASM_IFNULL:
        case Constants.ASM_IFNONNULL:
          createLabel(bytecodeOffset + readUnsignedShort(currentOffset + 1), labels);
          currentOffset += 3;
          break;
        case Constants.GOTO_W:
        case Constants.JSR_W:
        case Constants.ASM_GOTO_W:
          createLabel(bytecodeOffset + readInt(currentOffset + 1), labels);
          currentOffset += 5;
          break;
        case Constants.WIDE:
          switch (classFileBuffer[currentOffset + 1] & 0xFF) {
            case Constants.ILOAD:
            case Constants.FLOAD:
            case Constants.ALOAD:
            case Constants.LLOAD:
            case Constants.DLOAD:
            case Constants.ISTORE:
            case Constants.FSTORE:
            case Constants.ASTORE:
            case Constants.LSTORE:
            case Constants.DSTORE:
            case Constants.RET:
              currentOffset += 4;
              break;
            case Constants.IINC:
              currentOffset += 6;
              break;
            default:
              throw new IllegalArgumentException();
          }
          break;
        case Constants.TABLESWITCH:
          // Skip 0 to 3 padding bytes.
          currentOffset += 4 - (bytecodeOffset & 3);
          // Read the default label and the number of table entries.
          createLabel(bytecodeOffset + readInt(currentOffset), labels);
          int numTableEntries = readInt(currentOffset + 8) - readInt(currentOffset + 4) + 1;
          currentOffset += 12;
          // Read the table labels.
          while (numTableEntries-- > 0) {
            createLabel(bytecodeOffset + readInt(currentOffset), labels);
            currentOffset += 4;
          }
          break;
        case Constants.LOOKUPSWITCH:
          // Skip 0 to 3 padding bytes.
          currentOffset += 4 - (bytecodeOffset & 3);
          // Read the default label and the number of switch cases.
          createLabel(bytecodeOffset + readInt(currentOffset), labels);
          int numSwitchCases = readInt(currentOffset + 4);
          currentOffset += 8;
          // Read the switch labels.
          while (numSwitchCases-- > 0) {
            createLabel(bytecodeOffset + readInt(currentOffset + 4), labels);
            currentOffset += 8;
          }
          break;
        case Constants.ILOAD:
        case Constants.LLOAD:
        case Constants.FLOAD:
        case Constants.DLOAD:
        case Constants.ALOAD:
        case Constants.ISTORE:
        case Constants.LSTORE:
        case Constants.FSTORE:
        case Constants.DSTORE:
        case Constants.ASTORE:
        case Constants.RET:
        case Constants.BIPUSH:
        case Constants.NEWARRAY:
        case Constants.LDC:
          currentOffset += 2;
          break;
        case Constants.SIPUSH:
        case Constants.LDC_W:
        case Constants.LDC2_W:
        case Constants.GETSTATIC:
        case Constants.PUTSTATIC:
        case Constants.GETFIELD:
        case Constants.PUTFIELD:
        case Constants.INVOKEVIRTUAL:
        case Constants.INVOKESPECIAL:
        case Constants.INVOKESTATIC:
        case Constants.NEW:
        case Constants.ANEWARRAY:
        case Constants.CHECKCAST:
        case Constants.INSTANCEOF:
        case Constants.IINC:
          currentOffset += 3;
          break;
        case Constants.INVOKEINTERFACE:
        case Constants.INVOKEDYNAMIC:
          currentOffset += 5;
          break;
        case Constants.MULTIANEWARRAY:
          currentOffset += 4;
          break;
        default:
          throw new IllegalArgumentException();
      }
    }

    // Read the 'exception_table_length' and 'exception_table' field to create a label for each
    // referenced instruction, and to make methodVisitor visit the corresponding try catch blocks.
    int exceptionTableLength = readUnsignedShort(currentOffset);
    currentOffset += 2;
    while (exceptionTableLength-- > 0) {
      Label start = createLabel(readUnsignedShort(currentOffset), labels);
      Label end = createLabel(readUnsignedShort(currentOffset + 2), labels);
      Label handler = createLabel(readUnsignedShort(currentOffset + 4), labels);
      String catchType = readUTF8(cpInfoOffsets[readUnsignedShort(currentOffset + 6)], charBuffer);
      currentOffset += 8;
      methodVisitor.visitTryCatchBlock(start, end, handler, catchType);
    }

    // Read the Code attributes to create a label for each referenced instruction (the variables
    // are ordered as in Section 4.7 of the JVMS). Attribute offsets exclude the
    // attribute_name_index and attribute_length fields.
    // - The offset of the current 'stack_map_frame' in the StackMap[Table] attribute, or 0.
    // Initially, this is the offset of the first 'stack_map_frame' entry. Then this offset is
    // updated after each stack_map_frame is read.
    int stackMapFrameOffset = 0;
    // - The end offset of the StackMap[Table] attribute, or 0.
    int stackMapTableEndOffset = 0;
    // - Whether the stack map frames are compressed (i.e. in a StackMapTable) or not.
    boolean compressedFrames = true;
    // - The offset of the LocalVariableTable attribute, or 0.
    int localVariableTableOffset = 0;
    // - The offset of the LocalVariableTypeTable attribute, or 0.
    int localVariableTypeTableOffset = 0;
    // - The offset of each 'type_annotation' entry in the RuntimeVisibleTypeAnnotations
    // attribute, or null.
    int[] visibleTypeAnnotationOffsets = null;
    // - The offset of each 'type_annotation' entry in the RuntimeInvisibleTypeAnnotations
    // attribute, or null.
    int[] invisibleTypeAnnotationOffsets = null;
    // - The non standard attributes (linked with their {@link Attribute#nextAttribute} field).
    //   This list in the <i>reverse order</i> or their order in the ClassFile structure.
    Attribute attributes = null;

    int attributesCount = readUnsignedShort(currentOffset);
    currentOffset += 2;
    while (attributesCount-- > 0) {
      // Read the attribute_info's attribute_name and attribute_length fields.
      String attributeName = readUTF8(currentOffset, charBuffer);
      int attributeLength = readInt(currentOffset + 2);
      currentOffset += 6;
      if (Constants.LOCAL_VARIABLE_TABLE.equals(attributeName)) {
        if ((context.parsingOptions & SKIP_DEBUG) == 0) {
          localVariableTableOffset = currentOffset;
          // Parse the attribute to find the corresponding (debug only) labels.
          int currentLocalVariableTableOffset = currentOffset;
          int localVariableTableLength = readUnsignedShort(currentLocalVariableTableOffset);
          currentLocalVariableTableOffset += 2;
          while (localVariableTableLength-- > 0) {
            int startPc = readUnsignedShort(currentLocalVariableTableOffset);
            createDebugLabel(startPc, labels);
            int length = readUnsignedShort(currentLocalVariableTableOffset + 2);
            createDebugLabel(startPc + length, labels);
            // Skip the name_index, descriptor_index and index fields (2 bytes each).
            currentLocalVariableTableOffset += 10;
          }
        }
      } else if (Constants.LOCAL_VARIABLE_TYPE_TABLE.equals(attributeName)) {
        localVariableTypeTableOffset = currentOffset;
        // Here we do not extract the labels corresponding to the attribute content. We assume they
        // are the same or a subset of those of the LocalVariableTable attribute.
      } else if (Constants.LINE_NUMBER_TABLE.equals(attributeName)) {
        if ((context.parsingOptions & SKIP_DEBUG) == 0) {
          // Parse the attribute to find the corresponding (debug only) labels.
          int currentLineNumberTableOffset = currentOffset;
          int lineNumberTableLength = readUnsignedShort(currentLineNumberTableOffset);
          currentLineNumberTableOffset += 2;
          while (lineNumberTableLength-- > 0) {
            int startPc = readUnsignedShort(currentLineNumberTableOffset);
            int lineNumber = readUnsignedShort(currentLineNumberTableOffset + 2);
            currentLineNumberTableOffset += 4;
            createDebugLabel(startPc, labels);
            labels[startPc].addLineNumber(lineNumber);
          }
        }
      } else if (Constants.RUNTIME_VISIBLE_TYPE_ANNOTATIONS.equals(attributeName)) {
        visibleTypeAnnotationOffsets =
            readTypeAnnotations(methodVisitor, context, currentOffset, /* visible = */ true);
        // Here we do not extract the labels corresponding to the attribute content. This would
        // require a full parsing of the attribute, which would need to be repeated when parsing
        // the bytecode instructions (see below). Instead, the content of the attribute is read one
        // type annotation at a time (i.e. after a type annotation has been visited, the next type
        // annotation is read), and the labels it contains are also extracted one annotation at a
        // time. This assumes that type annotations are ordered by increasing bytecode offset.
      } else if (Constants.RUNTIME_INVISIBLE_TYPE_ANNOTATIONS.equals(attributeName)) {
        invisibleTypeAnnotationOffsets =
            readTypeAnnotations(methodVisitor, context, currentOffset, /* visible = */ false);
        // Same comment as above for the RuntimeVisibleTypeAnnotations attribute.
      } else if (Constants.STACK_MAP_TABLE.equals(attributeName)) {
        if ((context.parsingOptions & SKIP_FRAMES) == 0) {
          stackMapFrameOffset = currentOffset + 2;
          stackMapTableEndOffset = currentOffset + attributeLength;
        }
        // Here we do not extract the labels corresponding to the attribute content. This would
        // require a full parsing of the attribute, which would need to be repeated when parsing
        // the bytecode instructions (see below). Instead, the content of the attribute is read one
        // frame at a time (i.e. after a frame has been visited, the next frame is read), and the
        // labels it contains are also extracted one frame at a time. Thanks to the ordering of
        // frames, having only a "one frame lookahead" is not a problem, i.e. it is not possible to
        // see an offset smaller than the offset of the current instruction and for which no Label
        // exist. Except for UNINITIALIZED type offsets. We solve this by parsing the stack map
        // table without a full decoding (see below).
      } else if ("StackMap".equals(attributeName)) {
        if ((context.parsingOptions & SKIP_FRAMES) == 0) {
          stackMapFrameOffset = currentOffset + 2;
          stackMapTableEndOffset = currentOffset + attributeLength;
          compressedFrames = false;
        }
        // IMPORTANT! Here we assume that the frames are ordered, as in the StackMapTable attribute,
        // although this is not guaranteed by the attribute format. This allows an incremental
        // extraction of the labels corresponding to this attribute (see the comment above for the
        // StackMapTable attribute).
      } else {
        Attribute attribute =
            readAttribute(
                context.attributePrototypes,
                attributeName,
                currentOffset,
                attributeLength,
                charBuffer,
                codeOffset,
                labels);
        attribute.nextAttribute = attributes;
        attributes = attribute;
      }
      currentOffset += attributeLength;
    }

    // Initialize the context fields related to stack map frames, and generate the first
    // (implicit) stack map frame, if needed.
    final boolean expandFrames = (context.parsingOptions & EXPAND_FRAMES) != 0;
    if (stackMapFrameOffset != 0) {
      // The bytecode offset of the first explicit frame is not offset_delta + 1 but only
      // offset_delta. Setting the implicit frame offset to -1 allows us to use of the
      // "offset_delta + 1" rule in all cases.
      context.currentFrameOffset = -1;
      context.currentFrameType = 0;
      context.currentFrameLocalCount = 0;
      context.currentFrameLocalCountDelta = 0;
      context.currentFrameLocalTypes = new Object[maxLocals];
      context.currentFrameStackCount = 0;
      context.currentFrameStackTypes = new Object[maxStack];
      if (expandFrames) {
        computeImplicitFrame(context);
      }
      // Find the labels for UNINITIALIZED frame types. Instead of decoding each element of the
      // stack map table, we look for 3 consecutive bytes that "look like" an UNINITIALIZED type
      // (tag ITEM_Uninitialized, offset within bytecode bounds, NEW instruction at this offset).
      // We may find false positives (i.e. not real UNINITIALIZED types), but this should be rare,
      // and the only consequence will be the creation of an unneeded label. This is better than
      // creating a label for each NEW instruction, and faster than fully decoding the whole stack
      // map table.
      for (int offset = stackMapFrameOffset; offset < stackMapTableEndOffset - 2; ++offset) {
        if (classFileBuffer[offset] == Frame.ITEM_UNINITIALIZED) {
          int potentialBytecodeOffset = readUnsignedShort(offset + 1);
          if (potentialBytecodeOffset >= 0
              && potentialBytecodeOffset < codeLength
              && (classFileBuffer[bytecodeStartOffset + potentialBytecodeOffset] & 0xFF)
                  == Opcodes.NEW) {
            createLabel(potentialBytecodeOffset, labels);
          }
        }
      }
    }
    if (expandFrames && (context.parsingOptions & EXPAND_ASM_INSNS) != 0) {
      // Expanding the ASM specific instructions can introduce F_INSERT frames, even if the method
      // does not currently have any frame. These inserted frames must be computed by simulating the
      // effect of the bytecode instructions, one by one, starting from the implicit first frame.
      // For this, MethodWriter needs to know maxLocals before the first instruction is visited. To
      // ensure this, we visit the implicit first frame here (passing only maxLocals - the rest is
      // computed in MethodWriter).
      methodVisitor.visitFrame(Opcodes.F_NEW, maxLocals, null, 0, null);
    }

    // Visit the bytecode instructions. First, introduce state variables for the incremental parsing
    // of the type annotations.

    // Index of the next runtime visible type annotation to read (in the
    // visibleTypeAnnotationOffsets array).
    int currentVisibleTypeAnnotationIndex = 0;
    // The bytecode offset of the next runtime visible type annotation to read, or -1.
    int currentVisibleTypeAnnotationBytecodeOffset =
        getTypeAnnotationBytecodeOffset(visibleTypeAnnotationOffsets, 0);
    // Index of the next runtime invisible type annotation to read (in the
    // invisibleTypeAnnotationOffsets array).
    int currentInvisibleTypeAnnotationIndex = 0;
    // The bytecode offset of the next runtime invisible type annotation to read, or -1.
    int currentInvisibleTypeAnnotationBytecodeOffset =
        getTypeAnnotationBytecodeOffset(invisibleTypeAnnotationOffsets, 0);

    // Whether a F_INSERT stack map frame must be inserted before the current instruction.
    boolean insertFrame = false;

    // The delta to subtract from a goto_w or jsr_w opcode to get the corresponding goto or jsr
    // opcode, or 0 if goto_w and jsr_w must be left unchanged (i.e. when expanding ASM specific
    // instructions).
    final int wideJumpOpcodeDelta =
        (context.parsingOptions & EXPAND_ASM_INSNS) == 0 ? Constants.WIDE_JUMP_OPCODE_DELTA : 0;

    currentOffset = bytecodeStartOffset;
    while (currentOffset < bytecodeEndOffset) {
      final int currentBytecodeOffset = currentOffset - bytecodeStartOffset;

      // Visit the label and the line number(s) for this bytecode offset, if any.
      Label currentLabel = labels[currentBytecodeOffset];
      if (currentLabel != null) {
        currentLabel.accept(methodVisitor, (context.parsingOptions & SKIP_DEBUG) == 0);
      }

      // Visit the stack map frame for this bytecode offset, if any.
      while (stackMapFrameOffset != 0
          && (context.currentFrameOffset == currentBytecodeOffset
              || context.currentFrameOffset == -1)) {
        // If there is a stack map frame for this offset, make methodVisitor visit it, and read the
        // next stack map frame if there is one.
        if (context.currentFrameOffset != -1) {
          if (!compressedFrames || expandFrames) {
            methodVisitor.visitFrame(
                Opcodes.F_NEW,
                context.currentFrameLocalCount,
                context.currentFrameLocalTypes,
                context.currentFrameStackCount,
                context.currentFrameStackTypes);
          } else {
            methodVisitor.visitFrame(
                context.currentFrameType,
                context.currentFrameLocalCountDelta,
                context.currentFrameLocalTypes,
                context.currentFrameStackCount,
                context.currentFrameStackTypes);
          }
          // Since there is already a stack map frame for this bytecode offset, there is no need to
          // insert a new one.
          insertFrame = false;
        }
        if (stackMapFrameOffset < stackMapTableEndOffset) {
          stackMapFrameOffset =
              readStackMapFrame(stackMapFrameOffset, compressedFrames, expandFrames, context);
        } else {
          stackMapFrameOffset = 0;
        }
      }

      // Insert a stack map frame for this bytecode offset, if requested by setting insertFrame to
      // true during the previous iteration. The actual frame content is computed in MethodWriter.
      if (insertFrame) {
        if ((context.parsingOptions & EXPAND_FRAMES) != 0) {
          methodVisitor.visitFrame(Constants.F_INSERT, 0, null, 0, null);
        }
        insertFrame = false;
      }

      // Visit the instruction at this bytecode offset.
      int opcode = classFileBuffer[currentOffset] & 0xFF;
      switch (opcode) {
        case Constants.NOP:
        case Constants.ACONST_NULL:
        case Constants.ICONST_M1:
        case Constants.ICONST_0:
        case Constants.ICONST_1:
        case Constants.ICONST_2:
        case Constants.ICONST_3:
        case Constants.ICONST_4:
        case Constants.ICONST_5:
        case Constants.LCONST_0:
        case Constants.LCONST_1:
        case Constants.FCONST_0:
        case Constants.FCONST_1:
        case Constants.FCONST_2:
        case Constants.DCONST_0:
        case Constants.DCONST_1:
        case Constants.IALOAD:
        case Constants.LALOAD:
        case Constants.FALOAD:
        case Constants.DALOAD:
        case Constants.AALOAD:
        case Constants.BALOAD:
        case Constants.CALOAD:
        case Constants.SALOAD:
        case Constants.IASTORE:
        case Constants.LASTORE:
        case Constants.FASTORE:
        case Constants.DASTORE:
        case Constants.AASTORE:
        case Constants.BASTORE:
        case Constants.CASTORE:
        case Constants.SASTORE:
        case Constants.POP:
        case Constants.POP2:
        case Constants.DUP:
        case Constants.DUP_X1:
        case Constants.DUP_X2:
        case Constants.DUP2:
        case Constants.DUP2_X1:
        case Constants.DUP2_X2:
        case Constants.SWAP:
        case Constants.IADD:
        case Constants.LADD:
        case Constants.FADD:
        case Constants.DADD:
        case Constants.ISUB:
        case Constants.LSUB:
        case Constants.FSUB:
        case Constants.DSUB:
        case Constants.IMUL:
        case Constants.LMUL:
        case Constants.FMUL:
        case Constants.DMUL:
        case Constants.IDIV:
        case Constants.LDIV:
        case Constants.FDIV:
        case Constants.DDIV:
        case Constants.IREM:
        case Constants.LREM:
        case Constants.FREM:
        case Constants.DREM:
        case Constants.INEG:
        case Constants.LNEG:
        case Constants.FNEG:
        case Constants.DNEG:
        case Constants.ISHL:
        case Constants.LSHL:
        case Constants.ISHR:
        case Constants.LSHR:
        case Constants.IUSHR:
        case Constants.LUSHR:
        case Constants.IAND:
        case Constants.LAND:
        case Constants.IOR:
        case Constants.LOR:
        case Constants.IXOR:
        case Constants.LXOR:
        case Constants.I2L:
        case Constants.I2F:
        case Constants.I2D:
        case Constants.L2I:
        case Constants.L2F:
        case Constants.L2D:
        case Constants.F2I:
        case Constants.F2L:
        case Constants.F2D:
        case Constants.D2I:
        case Constants.D2L:
        case Constants.D2F:
        case Constants.I2B:
        case Constants.I2C:
        case Constants.I2S:
        case Constants.LCMP:
        case Constants.FCMPL:
        case Constants.FCMPG:
        case Constants.DCMPL:
        case Constants.DCMPG:
        case Constants.IRETURN:
        case Constants.LRETURN:
        case Constants.FRETURN:
        case Constants.DRETURN:
        case Constants.ARETURN:
        case Constants.RETURN:
        case Constants.ARRAYLENGTH:
        case Constants.ATHROW:
        case Constants.MONITORENTER:
        case Constants.MONITOREXIT:
          methodVisitor.visitInsn(opcode);
          currentOffset += 1;
          break;
        case Constants.ILOAD_0:
        case Constants.ILOAD_1:
        case Constants.ILOAD_2:
        case Constants.ILOAD_3:
        case Constants.LLOAD_0:
        case Constants.LLOAD_1:
        case Constants.LLOAD_2:
        case Constants.LLOAD_3:
        case Constants.FLOAD_0:
        case Constants.FLOAD_1:
        case Constants.FLOAD_2:
        case Constants.FLOAD_3:
        case Constants.DLOAD_0:
        case Constants.DLOAD_1:
        case Constants.DLOAD_2:
        case Constants.DLOAD_3:
        case Constants.ALOAD_0:
        case Constants.ALOAD_1:
        case Constants.ALOAD_2:
        case Constants.ALOAD_3:
          opcode -= Constants.ILOAD_0;
          methodVisitor.visitVarInsn(Opcodes.ILOAD + (opcode >> 2), opcode & 0x3);
          currentOffset += 1;
          break;
        case Constants.ISTORE_0:
        case Constants.ISTORE_1:
        case Constants.ISTORE_2:
        case Constants.ISTORE_3:
        case Constants.LSTORE_0:
        case Constants.LSTORE_1:
        case Constants.LSTORE_2:
        case Constants.LSTORE_3:
        case Constants.FSTORE_0:
        case Constants.FSTORE_1:
        case Constants.FSTORE_2:
        case Constants.FSTORE_3:
        case Constants.DSTORE_0:
        case Constants.DSTORE_1:
        case Constants.DSTORE_2:
        case Constants.DSTORE_3:
        case Constants.ASTORE_0:
        case Constants.ASTORE_1:
        case Constants.ASTORE_2:
        case Constants.ASTORE_3:
          opcode -= Constants.ISTORE_0;
          methodVisitor.visitVarInsn(Opcodes.ISTORE + (opcode >> 2), opcode & 0x3);
          currentOffset += 1;
          break;
        case Constants.IFEQ:
        case Constants.IFNE:
        case Constants.IFLT:
        case Constants.IFGE:
        case Constants.IFGT:
        case Constants.IFLE:
        case Constants.IF_ICMPEQ:
        case Constants.IF_ICMPNE:
        case Constants.IF_ICMPLT:
        case Constants.IF_ICMPGE:
        case Constants.IF_ICMPGT:
        case Constants.IF_ICMPLE:
        case Constants.IF_ACMPEQ:
        case Constants.IF_ACMPNE:
        case Constants.GOTO:
        case Constants.JSR:
        case Constants.IFNULL:
        case Constants.IFNONNULL:
          methodVisitor.visitJumpInsn(
              opcode, labels[currentBytecodeOffset + readShort(currentOffset + 1)]);
          currentOffset += 3;
          break;
        case Constants.GOTO_W:
        case Constants.JSR_W:
          methodVisitor.visitJumpInsn(
              opcode - wideJumpOpcodeDelta,
              labels[currentBytecodeOffset + readInt(currentOffset + 1)]);
          currentOffset += 5;
          break;
        case Constants.ASM_IFEQ:
        case Constants.ASM_IFNE:
        case Constants.ASM_IFLT:
        case Constants.ASM_IFGE:
        case Constants.ASM_IFGT:
        case Constants.ASM_IFLE:
        case Constants.ASM_IF_ICMPEQ:
        case Constants.ASM_IF_ICMPNE:
        case Constants.ASM_IF_ICMPLT:
        case Constants.ASM_IF_ICMPGE:
        case Constants.ASM_IF_ICMPGT:
        case Constants.ASM_IF_ICMPLE:
        case Constants.ASM_IF_ACMPEQ:
        case Constants.ASM_IF_ACMPNE:
        case Constants.ASM_GOTO:
        case Constants.ASM_JSR:
        case Constants.ASM_IFNULL:
        case Constants.ASM_IFNONNULL:
          {
            // A forward jump with an offset > 32767. In this case we automatically replace ASM_GOTO
            // with GOTO_W, ASM_JSR with JSR_W and ASM_IFxxx <l> with IFNOTxxx <L> GOTO_W <l> L:...,
            // where IFNOTxxx is the "opposite" opcode of ASMS_IFxxx (e.g. IFNE for ASM_IFEQ) and
            // where <L> designates the instruction just after the GOTO_W.
            // First, change the ASM specific opcodes ASM_IFEQ ... ASM_JSR, ASM_IFNULL and
            // ASM_IFNONNULL to IFEQ ... JSR, IFNULL and IFNONNULL.
            opcode =
                opcode < Constants.ASM_IFNULL
                    ? opcode - Constants.ASM_OPCODE_DELTA
                    : opcode - Constants.ASM_IFNULL_OPCODE_DELTA;
            Label target = labels[currentBytecodeOffset + readUnsignedShort(currentOffset + 1)];
            if (opcode == Opcodes.GOTO || opcode == Opcodes.JSR) {
              // Replace GOTO with GOTO_W and JSR with JSR_W.
              methodVisitor.visitJumpInsn(opcode + Constants.WIDE_JUMP_OPCODE_DELTA, target);
            } else {
              // Compute the "opposite" of opcode. This can be done by flipping the least
              // significant bit for IFNULL and IFNONNULL, and similarly for IFEQ ... IF_ACMPEQ
              // (with a pre and post offset by 1).
              opcode = opcode < Opcodes.GOTO ? ((opcode + 1) ^ 1) - 1 : opcode ^ 1;
              Label endif = createLabel(currentBytecodeOffset + 3, labels);
              methodVisitor.visitJumpInsn(opcode, endif);
              methodVisitor.visitJumpInsn(Constants.GOTO_W, target);
              // endif designates the instruction just after GOTO_W, and is visited as part of the
              // next instruction. Since it is a jump target, we need to insert a frame here.
              insertFrame = true;
            }
            currentOffset += 3;
            break;
          }
        case Constants.ASM_GOTO_W:
          {
            // Replace ASM_GOTO_W with GOTO_W.
            methodVisitor.visitJumpInsn(
                Constants.GOTO_W, labels[currentBytecodeOffset + readInt(currentOffset + 1)]);
            // The instruction just after is a jump target (because ASM_GOTO_W is used in patterns
            // IFNOTxxx <L> ASM_GOTO_W <l> L:..., see MethodWriter), so we need to insert a frame
            // here.
            insertFrame = true;
            currentOffset += 5;
            break;
          }
        case Constants.WIDE:
          opcode = classFileBuffer[currentOffset + 1] & 0xFF;
          if (opcode == Opcodes.IINC) {
            methodVisitor.visitIincInsn(
                readUnsignedShort(currentOffset + 2), readShort(currentOffset + 4));
            currentOffset += 6;
          } else {
            methodVisitor.visitVarInsn(opcode, readUnsignedShort(currentOffset + 2));
            currentOffset += 4;
          }
          break;
        case Constants.TABLESWITCH:
          {
            // Skip 0 to 3 padding bytes.
            currentOffset += 4 - (currentBytecodeOffset & 3);
            // Read the instruction.
            Label defaultLabel = labels[currentBytecodeOffset + readInt(currentOffset)];
            int low = readInt(currentOffset + 4);
            int high = readInt(currentOffset + 8);
            currentOffset += 12;
            Label[] table = new Label[high - low + 1];
            for (int i = 0; i < table.length; ++i) {
              table[i] = labels[currentBytecodeOffset + readInt(currentOffset)];
              currentOffset += 4;
            }
            methodVisitor.visitTableSwitchInsn(low, high, defaultLabel, table);
            break;
          }
        case Constants.LOOKUPSWITCH:
          {
            // Skip 0 to 3 padding bytes.
            currentOffset += 4 - (currentBytecodeOffset & 3);
            // Read the instruction.
            Label defaultLabel = labels[currentBytecodeOffset + readInt(currentOffset)];
            int numPairs = readInt(currentOffset + 4);
            currentOffset += 8;
            int[] keys = new int[numPairs];
            Label[] values = new Label[numPairs];
            for (int i = 0; i < numPairs; ++i) {
              keys[i] = readInt(currentOffset);
              values[i] = labels[currentBytecodeOffset + readInt(currentOffset + 4)];
              currentOffset += 8;
            }
            methodVisitor.visitLookupSwitchInsn(defaultLabel, keys, values);
            break;
          }
        case Constants.ILOAD:
        case Constants.LLOAD:
        case Constants.FLOAD:
        case Constants.DLOAD:
        case Constants.ALOAD:
        case Constants.ISTORE:
        case Constants.LSTORE:
        case Constants.FSTORE:
        case Constants.DSTORE:
        case Constants.ASTORE:
        case Constants.RET:
          methodVisitor.visitVarInsn(opcode, classFileBuffer[currentOffset + 1] & 0xFF);
          currentOffset += 2;
          break;
        case Constants.BIPUSH:
        case Constants.NEWARRAY:
          methodVisitor.visitIntInsn(opcode, classFileBuffer[currentOffset + 1]);
          currentOffset += 2;
          break;
        case Constants.SIPUSH:
          methodVisitor.visitIntInsn(opcode, readShort(currentOffset + 1));
          currentOffset += 3;
          break;
        case Constants.LDC:
          methodVisitor.visitLdcInsn(
              readConst(classFileBuffer[currentOffset + 1] & 0xFF, charBuffer));
          currentOffset += 2;
          break;
        case Constants.LDC_W:
        case Constants.LDC2_W:
          methodVisitor.visitLdcInsn(readConst(readUnsignedShort(currentOffset + 1), charBuffer));
          currentOffset += 3;
          break;
        case Constants.GETSTATIC:
        case Constants.PUTSTATIC:
        case Constants.GETFIELD:
        case Constants.PUTFIELD:
        case Constants.INVOKEVIRTUAL:
        case Constants.INVOKESPECIAL:
        case Constants.INVOKESTATIC:
        case Constants.INVOKEINTERFACE:
          {
            int cpInfoOffset = cpInfoOffsets[readUnsignedShort(currentOffset + 1)];
            int nameAndTypeCpInfoOffset = cpInfoOffsets[readUnsignedShort(cpInfoOffset + 2)];
            String owner = readClass(cpInfoOffset, charBuffer);
            String name = readUTF8(nameAndTypeCpInfoOffset, charBuffer);
            String descriptor = readUTF8(nameAndTypeCpInfoOffset + 2, charBuffer);
            if (opcode < Opcodes.INVOKEVIRTUAL) {
              methodVisitor.visitFieldInsn(opcode, owner, name, descriptor);
            } else {
              boolean isInterface =
                  classFileBuffer[cpInfoOffset - 1] == Symbol.CONSTANT_INTERFACE_METHODREF_TAG;
              methodVisitor.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
            }
            if (opcode == Opcodes.INVOKEINTERFACE) {
              currentOffset += 5;
            } else {
              currentOffset += 3;
            }
            break;
          }
        case Constants.INVOKEDYNAMIC:
          {
            int cpInfoOffset = cpInfoOffsets[readUnsignedShort(currentOffset + 1)];
            int nameAndTypeCpInfoOffset = cpInfoOffsets[readUnsignedShort(cpInfoOffset + 2)];
            String name = readUTF8(nameAndTypeCpInfoOffset, charBuffer);
            String descriptor = readUTF8(nameAndTypeCpInfoOffset + 2, charBuffer);
            int bootstrapMethodOffset = bootstrapMethodOffsets[readUnsignedShort(cpInfoOffset)];
            Handle handle =
                (Handle) readConst(readUnsignedShort(bootstrapMethodOffset), charBuffer);
            Object[] bootstrapMethodArguments =
                new Object[readUnsignedShort(bootstrapMethodOffset + 2)];
            bootstrapMethodOffset += 4;
            for (int i = 0; i < bootstrapMethodArguments.length; i++) {
              bootstrapMethodArguments[i] =
                  readConst(readUnsignedShort(bootstrapMethodOffset), charBuffer);
              bootstrapMethodOffset += 2;
            }
            methodVisitor.visitInvokeDynamicInsn(
                name, descriptor, handle, bootstrapMethodArguments);
            currentOffset += 5;
            break;
          }
        case Constants.NEW:
        case Constants.ANEWARRAY:
        case Constants.CHECKCAST:
        case Constants.INSTANCEOF:
          methodVisitor.visitTypeInsn(opcode, readClass(currentOffset + 1, charBuffer));
          currentOffset += 3;
          break;
        case Constants.IINC:
          methodVisitor.visitIincInsn(
              classFileBuffer[currentOffset + 1] & 0xFF, classFileBuffer[currentOffset + 2]);
          currentOffset += 3;
          break;
        case Constants.MULTIANEWARRAY:
          methodVisitor.visitMultiANewArrayInsn(
              readClass(currentOffset + 1, charBuffer), classFileBuffer[currentOffset + 3] & 0xFF);
          currentOffset += 4;
          break;
        default:
          throw new AssertionError();
      }

      // Visit the runtime visible instruction annotations, if any.
      while (visibleTypeAnnotationOffsets != null
          && currentVisibleTypeAnnotationIndex < visibleTypeAnnotationOffsets.length
          && currentVisibleTypeAnnotationBytecodeOffset <= currentBytecodeOffset) {
        if (currentVisibleTypeAnnotationBytecodeOffset == currentBytecodeOffset) {
          // Parse the target_type, target_info and target_path fields.
          int currentAnnotationOffset =
              readTypeAnnotationTarget(
                  context, visibleTypeAnnotationOffsets[currentVisibleTypeAnnotationIndex]);
          // Parse the type_index field.
          String annotationDescriptor = readUTF8(currentAnnotationOffset, charBuffer);
          currentAnnotationOffset += 2;
          // Parse num_element_value_pairs and element_value_pairs and visit these values.
          readElementValues(
              methodVisitor.visitInsnAnnotation(
                  context.currentTypeAnnotationTarget,
                  context.currentTypeAnnotationTargetPath,
                  annotationDescriptor,
                  /* visible = */ true),
              currentAnnotationOffset,
              /* named = */ true,
              charBuffer);
        }
        currentVisibleTypeAnnotationBytecodeOffset =
            getTypeAnnotationBytecodeOffset(
                visibleTypeAnnotationOffsets, ++currentVisibleTypeAnnotationIndex);
      }

      // Visit the runtime invisible instruction annotations, if any.
      while (invisibleTypeAnnotationOffsets != null
          && currentInvisibleTypeAnnotationIndex < invisibleTypeAnnotationOffsets.length
          && currentInvisibleTypeAnnotationBytecodeOffset <= currentBytecodeOffset) {
        if (currentInvisibleTypeAnnotationBytecodeOffset == currentBytecodeOffset) {
          // Parse the target_type, target_info and target_path fields.
          int currentAnnotationOffset =
              readTypeAnnotationTarget(
                  context, invisibleTypeAnnotationOffsets[currentInvisibleTypeAnnotationIndex]);
          // Parse the type_index field.
          String annotationDescriptor = readUTF8(currentAnnotationOffset, charBuffer);
          currentAnnotationOffset += 2;
          // Parse num_element_value_pairs and element_value_pairs and visit these values.
          readElementValues(
              methodVisitor.visitInsnAnnotation(
                  context.currentTypeAnnotationTarget,
                  context.currentTypeAnnotationTargetPath,
                  annotationDescriptor,
                  /* visible = */ false),
              currentAnnotationOffset,
              /* named = */ true,
              charBuffer);
        }
        currentInvisibleTypeAnnotationBytecodeOffset =
            getTypeAnnotationBytecodeOffset(
                invisibleTypeAnnotationOffsets, ++currentInvisibleTypeAnnotationIndex);
      }
    }
    if (labels[codeLength] != null) {
      methodVisitor.visitLabel(labels[codeLength]);
    }

    // Visit LocalVariableTable and LocalVariableTypeTable attributes.
    if (localVariableTableOffset != 0 && (context.parsingOptions & SKIP_DEBUG) == 0) {
      // The (start_pc, index, signature_index) fields of each entry of the LocalVariableTypeTable.
      int[] typeTable = null;
      if (localVariableTypeTableOffset != 0) {
        typeTable = new int[readUnsignedShort(localVariableTypeTableOffset) * 3];
        currentOffset = localVariableTypeTableOffset + 2;
        int typeTableIndex = typeTable.length;
        while (typeTableIndex > 0) {
          // Store the offset of 'signature_index', and the value of 'index' and 'start_pc'.
          typeTable[--typeTableIndex] = currentOffset + 6;
          typeTable[--typeTableIndex] = readUnsignedShort(currentOffset + 8);
          typeTable[--typeTableIndex] = readUnsignedShort(currentOffset);
          currentOffset += 10;
        }
      }
      int localVariableTableLength = readUnsignedShort(localVariableTableOffset);
      currentOffset = localVariableTableOffset + 2;
      while (localVariableTableLength-- > 0) {
        int startPc = readUnsignedShort(currentOffset);
        int length = readUnsignedShort(currentOffset + 2);
        String name = readUTF8(currentOffset + 4, charBuffer);
        String descriptor = readUTF8(currentOffset + 6, charBuffer);
        int index = readUnsignedShort(currentOffset + 8);
        currentOffset += 10;
        String signature = null;
        if (typeTable != null) {
          for (int i = 0; i < typeTable.length; i += 3) {
            if (typeTable[i] == startPc && typeTable[i + 1] == index) {
              signature = readUTF8(typeTable[i + 2], charBuffer);
              break;
            }
          }
        }
        methodVisitor.visitLocalVariable(
            name, descriptor, signature, labels[startPc], labels[startPc + length], index);
      }
    }

    // Visit the local variable type annotations of the RuntimeVisibleTypeAnnotations attribute.
    if (visibleTypeAnnotationOffsets != null) {
      for (int typeAnnotationOffset : visibleTypeAnnotationOffsets) {
        int targetType = readByte(typeAnnotationOffset);
        if (targetType == TypeReference.LOCAL_VARIABLE
            || targetType == TypeReference.RESOURCE_VARIABLE) {
          // Parse the target_type, target_info and target_path fields.
          currentOffset = readTypeAnnotationTarget(context, typeAnnotationOffset);
          // Parse the type_index field.
          String annotationDescriptor = readUTF8(currentOffset, charBuffer);
          currentOffset += 2;
          // Parse num_element_value_pairs and element_value_pairs and visit these values.
          readElementValues(
              methodVisitor.visitLocalVariableAnnotation(
                  context.currentTypeAnnotationTarget,
                  context.currentTypeAnnotationTargetPath,
                  context.currentLocalVariableAnnotationRangeStarts,
                  context.currentLocalVariableAnnotationRangeEnds,
                  context.currentLocalVariableAnnotationRangeIndices,
                  annotationDescriptor,
                  /* visible = */ true),
              currentOffset,
              /* named = */ true,
              charBuffer);
        }
      }
    }

    // Visit the local variable type annotations of the RuntimeInvisibleTypeAnnotations attribute.
    if (invisibleTypeAnnotationOffsets != null) {
      for (int typeAnnotationOffset : invisibleTypeAnnotationOffsets) {
        int targetType = readByte(typeAnnotationOffset);
        if (targetType == TypeReference.LOCAL_VARIABLE
            || targetType == TypeReference.RESOURCE_VARIABLE) {
          // Parse the target_type, target_info and target_path fields.
          currentOffset = readTypeAnnotationTarget(context, typeAnnotationOffset);
          // Parse the type_index field.
          String annotationDescriptor = readUTF8(currentOffset, charBuffer);
          currentOffset += 2;
          // Parse num_element_value_pairs and element_value_pairs and visit these values.
          readElementValues(
              methodVisitor.visitLocalVariableAnnotation(
                  context.currentTypeAnnotationTarget,
                  context.currentTypeAnnotationTargetPath,
                  context.currentLocalVariableAnnotationRangeStarts,
                  context.currentLocalVariableAnnotationRangeEnds,
                  context.currentLocalVariableAnnotationRangeIndices,
                  annotationDescriptor,
                  /* visible = */ false),
              currentOffset,
              /* named = */ true,
              charBuffer);
        }
      }
    }

    // Visit the non standard attributes.
    while (attributes != null) {
      // Copy and reset the nextAttribute field so that it can also be used in MethodWriter.
      Attribute nextAttribute = attributes.nextAttribute;
      attributes.nextAttribute = null;
      methodVisitor.visitAttribute(attributes);
      attributes = nextAttribute;
    }

    // Visit the max stack and max locals values.
    methodVisitor.visitMaxs(maxStack, maxLocals);
  }
private void readCode(
      final MethodVisitor methodVisitor, final Context context, final int codeOffset) {
    int currentOffset = codeOffset;

    // Read the max_stack, max_locals and code_length fields.
    final byte[] classFileBuffer = b;
    final char[] charBuffer = context.charBuffer;
    final int maxStack = readUnsignedShort(currentOffset);
    final int maxLocals = readUnsignedShort(currentOffset + 2);
    final int codeLength = readInt(currentOffset + 4);
    currentOffset += 8;

    // Read the bytecode 'code' array to create a label for each referenced instruction.
    final int bytecodeStartOffset = currentOffset;
    final int bytecodeEndOffset = currentOffset + codeLength;
    final Label[] labels = context.currentMethodLabels = new Label[codeLength + 1];
    while (currentOffset < bytecodeEndOffset) {
      final int bytecodeOffset = currentOffset - bytecodeStartOffset;
      final int opcode = classFileBuffer[currentOffset] & 0xFF;
      switch (opcode) {
        case Constants.NOP:
        case Constants.ACONST_NULL:
        case Constants.ICONST_M1:
        case Constants.ICONST_0:
        case Constants.ICONST_1:
        case Constants.ICONST_2:
        case Constants.ICONST_3:
        case Constants.ICONST_4:
        case Constants.ICONST_5:
        case Constants.LCONST_0:
        case Constants.LCONST_1:
        case Constants.FCONST_0:
        case Constants.FCONST_1:
        case Constants.FCONST_2:
        case Constants.DCONST_0:
        case Constants.DCONST_1:
        case Constants.IALOAD:
        case Constants.LALOAD:
        case Constants.FALOAD:
        case Constants.DALOAD:
        case Constants.AALOAD:
        case Constants.BALOAD:
        case Constants.CALOAD:
        case Constants.SALOAD:
        case Constants.IASTORE:
        case Constants.LASTORE:
        case Constants.FASTORE:
        case Constants.DASTORE:
        case Constants.AASTORE:
        case Constants.BASTORE:
        case Constants.CASTORE:
        case Constants.SASTORE:
        case Constants.POP:
        case Constants.POP2:
        case Constants.DUP:
        case Constants.DUP_X1:
        case Constants.DUP_X2:
        case Constants.DUP2:
        case Constants.DUP2_X1:
        case Constants.DUP2_X2:
        case Constants.SWAP:
        case Constants.IADD:
        case Constants.LADD:
        case Constants.FADD:
        case Constants.DADD:
        case Constants.ISUB:
        case Constants.LSUB:
        case Constants.FSUB:
        case Constants.DSUB:
        case Constants.IMUL:
        case Constants.LMUL:
        case Constants.FMUL:
        case Constants.DMUL:
        case Constants.IDIV:
        case Constants.LDIV:
        case Constants.FDIV:
        case Constants.DDIV:
        case Constants.IREM:
        case Constants.LREM:
        case Constants.FREM:
        case Constants.DREM:
        case Constants.INEG:
        case Constants.LNEG:
        case Constants.FNEG:
        case Constants.DNEG:
        case Constants.ISHL:
        case Constants.LSHL:
        case Constants.ISHR:
        case Constants.LSHR:
        case Constants.IUSHR:
        case Constants.LUSHR:
        case Constants.IAND:
        case Constants.LAND:
        case Constants.IOR:
        case Constants.LOR:
        case Constants.IXOR:
        case Constants.LXOR:
        case Constants.I2L:
        case Constants.I2F:
        case Constants.I2D:
        case Constants.L2I:
        case Constants.L2F:
        case Constants.L2D:
        case Constants.F2I:
        case Constants.F2L:
        case Constants.F2D:
        case Constants.D2I:
        case Constants.D2L:
        case Constants.D2F:
        case Constants.I2B:
        case Constants.I2C:
        case Constants.I2S:
        case Constants.LCMP:
        case Constants.FCMPL:
        case Constants.FCMPG:
        case Constants.DCMPL:
        case Constants.DCMPG:
        case Constants.IRETURN:
        case Constants.LRETURN:
        case Constants.FRETURN:
        case Constants.DRETURN:
        case Constants.ARETURN:
        case Constants.RETURN:
        case Constants.ARRAYLENGTH:
        case Constants.ATHROW:
        case Constants.MONITORENTER:
        case Constants.MONITOREXIT:
        case Constants.ILOAD_0:
        case Constants.ILOAD_1:
        case Constants.ILOAD_2:
        case Constants.ILOAD_3:
        case Constants.LLOAD_0:
        case Constants.LLOAD_1:
        case Constants.LLOAD_2:
        case Constants.LLOAD_3:
        case Constants.FLOAD_0:
        case Constants.FLOAD_1:
        case Constants.FLOAD_2:
        case Constants.FLOAD_3:
        case Constants.DLOAD_0:
        case Constants.DLOAD_1:
        case Constants.DLOAD_2:
        case Constants.DLOAD_3:
        case Constants.ALOAD_0:
        case Constants.ALOAD_1:
        case Constants.ALOAD_2:
        case Constants.ALOAD_3:
        case Constants.ISTORE_0:
        case Constants.ISTORE_1:
        case Constants.ISTORE_2:
        case Constants.ISTORE_3:
        case Constants.LSTORE_0:
        case Constants.LSTORE_1:
        case Constants.LSTORE_2:
        case Constants.LSTORE_3:
        case Constants.FSTORE_0:
        case Constants.FSTORE_1:
        case Constants.FSTORE_2:
        case Constants.FSTORE_3:
        case Constants.DSTORE_0:
        case Constants.DSTORE_1:
        case Constants.DSTORE_2:
        case Constants.DSTORE_3:
        case Constants.ASTORE_0:
        case Constants.ASTORE_1:
        case Constants.ASTORE_2:
        case Constants.ASTORE_3:
          currentOffset += 1;
          break;
        case Constants.IFEQ:
        case Constants.IFNE:
        case Constants.IFLT:
        case Constants.IFGE:
        case Constants.IFGT:
        case Constants.IFLE:
        case Constants.IF_ICMPEQ:
        case Constants.IF_ICMPNE:
        case Constants.IF_ICMPLT:
        case Constants.IF_ICMPGE:
        case Constants.IF_ICMPGT:
        case Constants.IF_ICMPLE:
        case Constants.IF_ACMPEQ:
        case Constants.IF_ACMPNE:
        case Constants.GOTO:
        case Constants.JSR:
        case Constants.IFNULL:
        case Constants.IFNONNULL:
          createLabel(bytecodeOffset + readShort(currentOffset + 1), labels);
          currentOffset += 3;
          break;
        case Constants.ASM_IFEQ:
        case Constants.ASM_IFNE:
        case Constants.ASM_IFLT:
        case Constants.ASM_IFGE:
        case Constants.ASM_IFGT:
        case Constants.ASM_IFLE:
        case Constants.ASM_IF_ICMPEQ:
        case Constants.ASM_IF_ICMPNE:
        case Constants.ASM_IF_ICMPLT:
        case Constants.ASM_IF_ICMPGE:
        case Constants.ASM_IF_ICMPGT:
        case Constants.ASM_IF_ICMPLE:
        case Constants.ASM_IF_ACMPEQ:
        case Constants.ASM_IF_ACMPNE:
        case Constants.ASM_GOTO:
        case Constants.ASM_JSR:
        case Constants.ASM_IFNULL:
        case Constants.ASM_IFNONNULL:
          createLabel(bytecodeOffset + readUnsignedShort(currentOffset + 1), labels);
          currentOffset += 3;
          break;
        case Constants.GOTO_W:
        case Constants.JSR_W:
        case Constants.ASM_GOTO_W:
          createLabel(bytecodeOffset + readInt(currentOffset + 1), labels);
          currentOffset += 5;
          break;
        case Constants.WIDE:
          switch (classFileBuffer[currentOffset + 1] & 0xFF) {
            case Constants.ILOAD:
            case Constants.FLOAD:
            case Constants.ALOAD:
            case Constants.LLOAD:
            case Constants.DLOAD:
            case Constants.ISTORE:
            case Constants.FSTORE:
            case Constants.ASTORE:
            case Constants.LSTORE:
            case Constants.DSTORE:
            case Constants.RET:
              currentOffset += 4;
              break;
            case Constants.IINC:
              currentOffset += 6;
              break;
            default:
              throw new IllegalArgumentException();
          }
          break;
        case Constants.TABLESWITCH:
          // Skip 0 to 3 padding bytes.
          currentOffset += 4 - (bytecodeOffset & 3);
          // Read the default label and the number of table entries.
          createLabel(bytecodeOffset + readInt(currentOffset), labels);
          int numTableEntries = readInt(currentOffset + 8) - readInt(currentOffset + 4) + 1;
          currentOffset += 12;
          // Read the table labels.
          while (numTableEntries-- > 0) {
            createLabel(bytecodeOffset + readInt(currentOffset), labels);
            currentOffset += 4;
          }
          break;
        case Constants.LOOKUPSWITCH:
          // Skip 0 to 3 padding bytes.
          currentOffset += 4 - (bytecodeOffset & 3);
          // Read the default label and the number of switch cases.
          createLabel(bytecodeOffset + readInt(currentOffset), labels);
          int numSwitchCases = readInt(currentOffset + 4);
          currentOffset += 8;
          // Read the switch labels.
          while (numSwitchCases-- > 0) {
            createLabel(bytecodeOffset + readInt(currentOffset + 4), labels);
            currentOffset += 8;
          }
          break;
        case Constants.ILOAD:
        case Constants.LLOAD:
        case Constants.FLOAD:
        case Constants.DLOAD:
        case Constants.ALOAD:
        case Constants.ISTORE:
        case Constants.LSTORE:
        case Constants.FSTORE:
        case Constants.DSTORE:
        case Constants.ASTORE:
        case Constants.RET:
        case Constants.BIPUSH:
        case Constants.NEWARRAY:
        case Constants.LDC:
          currentOffset += 2;
          break;
        case Constants.SIPUSH:
        case Constants.LDC_W:
        case Constants.LDC2_W:
        case Constants.GETSTATIC:
        case Constants.PUTSTATIC:
        case Constants.GETFIELD:
        case Constants.PUTFIELD:
        case Constants.INVOKEVIRTUAL:
        case Constants.INVOKESPECIAL:
        case Constants.INVOKESTATIC:
        case Constants.NEW:
        case Constants.ANEWARRAY:
        case Constants.CHECKCAST:
        case Constants.INSTANCEOF:
        case Constants.IINC:
          currentOffset += 3;
          break;
        case Constants.INVOKEINTERFACE:
        case Constants.INVOKEDYNAMIC:
          currentOffset += 5;
          break;
        case Constants.MULTIANEWARRAY:
          currentOffset += 4;
          break;
        default:
          throw new IllegalArgumentException();
      }
    }

    // Read the 'exception_table_length' and 'exception_table' field to create a label for each
    // referenced instruction, and to make methodVisitor visit the corresponding try catch blocks.
    int exceptionTableLength = readUnsignedShort(currentOffset);
    currentOffset += 2;
    while (exceptionTableLength-- > 0) {
      Label start = createLabel(readUnsignedShort(currentOffset), labels);
      Label end = createLabel(readUnsignedShort(currentOffset + 2), labels);
      Label handler = createLabel(readUnsignedShort(currentOffset + 4), labels);
      String catchType = readUTF8(cpInfoOffsets[readUnsignedShort(currentOffset + 6)], charBuffer);
      currentOffset += 8;
      methodVisitor.visitTryCatchBlock(start, end, handler, catchType);
    }

    // Read the Code attributes to create a label for each referenced instruction (the variables
    // are ordered as in Section 4.7 of the JVMS). Attribute offsets exclude the
    // attribute_name_index and attribute_length fields.
    // - The offset of the current 'stack_map_frame' in the StackMap[Table] attribute, or 0.
    // Initially, this is the offset of the first 'stack_map_frame' entry. Then this offset is
    // updated after each stack_map_frame is read.
    int stackMapFrameOffset = 0;
    // - The end offset of the StackMap[Table] attribute, or 0.
    int stackMapTableEndOffset = 0;
    // - Whether the stack map frames are compressed (i.e. in a StackMapTable) or not.
    boolean compressedFrames = true;
    // - The offset of the LocalVariableTable attribute, or 0.
    int localVariableTableOffset = 0;
    // - The offset of the LocalVariableTypeTable attribute, or 0.
    int localVariableTypeTableOffset = 0;
    // - The offset of each 'type_annotation' entry in the RuntimeVisibleTypeAnnotations
    // attribute, or null.
    int[] visibleTypeAnnotationOffsets = null;
    // - The offset of each 'type_annotation' entry in the RuntimeInvisibleTypeAnnotations
    // attribute, or null.
    int[] invisibleTypeAnnotationOffsets = null;
    // - The non standard attributes (linked with their {@link Attribute#nextAttribute} field).
    //   This list in the <i>reverse order</i> or their order in the ClassFile structure.
    Attribute attributes = null;

    int attributesCount = readUnsignedShort(currentOffset);
    currentOffset += 2;
    while (attributesCount-- > 0) {
      // Read the attribute_info's attribute_name and attribute_length fields.
      String attributeName = readUTF8(currentOffset, charBuffer);
      int attributeLength = readInt(currentOffset + 2);
      currentOffset += 6;
      if (Constants.LOCAL_VARIABLE_TABLE.equals(attributeName)) {
        if ((context.parsingOptions & SKIP_DEBUG) == 0) {
          localVariableTableOffset = currentOffset;
          // Parse the attribute to find the corresponding (debug only) labels.
          int currentLocalVariableTableOffset = currentOffset;
          int localVariableTableLength = readUnsignedShort(currentLocalVariableTableOffset);
          currentLocalVariableTableOffset += 2;
          while (localVariableTableLength-- > 0) {
            int startPc = readUnsignedShort(currentLocalVariableTableOffset);
            createDebugLabel(startPc, labels);
            int length = readUnsignedShort(currentLocalVariableTableOffset + 2);
            createDebugLabel(startPc + length, labels);
            // Skip the name_index, descriptor_index and index fields (2 bytes each).
            currentLocalVariableTableOffset += 10;
          }
        }
      } else if (Constants.LOCAL_VARIABLE_TYPE_TABLE.equals(attributeName)) {
        localVariableTypeTableOffset = currentOffset;
        // Here we do not extract the labels corresponding to the attribute content. We assume they
        // are the same or a subset of those of the LocalVariableTable attribute.
      } else if (Constants.LINE_NUMBER_TABLE.equals(attributeName)) {
        if ((context.parsingOptions & SKIP_DEBUG) == 0) {
          // Parse the attribute to find the corresponding (debug only) labels.
          int currentLineNumberTableOffset = currentOffset;
          int lineNumberTableLength = readUnsignedShort(currentLineNumberTableOffset);
          currentLineNumberTableOffset += 2;
          while (lineNumberTableLength-- > 0) {
            int startPc = readUnsignedShort(currentLineNumberTableOffset);
            int lineNumber = readUnsignedShort(currentLineNumberTableOffset + 2);
            currentLineNumberTableOffset += 4;
            createDebugLabel(startPc, labels);
            labels[startPc].addLineNumber(lineNumber);
          }
        }
      } else if (Constants.RUNTIME_VISIBLE_TYPE_ANNOTATIONS.equals(attributeName)) {
        visibleTypeAnnotationOffsets =
            readTypeAnnotations(methodVisitor, context, currentOffset, /* visible = */ true);
        // Here we do not extract the labels corresponding to the attribute content. This would
        // require a full parsing of the attribute, which would need to be repeated when parsing
        // the bytecode instructions (see below). Instead, the content of the attribute is read one
        // type annotation at a time (i.e. after a type annotation has been visited, the next type
        // annotation is read), and the labels it contains are also extracted one annotation at a
        // time. This assumes that type annotations are ordered by increasing bytecode offset.
      } else if (Constants.RUNTIME_INVISIBLE_TYPE_ANNOTATIONS.equals(attributeName)) {
        invisibleTypeAnnotationOffsets =
            readTypeAnnotations(methodVisitor, context, currentOffset, /* visible = */ false);
        // Same comment as above for the RuntimeVisibleTypeAnnotations attribute.
      } else if (Constants.STACK_MAP_TABLE.equals(attributeName)) {
        if ((context.parsingOptions & SKIP_FRAMES) == 0) {
          stackMapFrameOffset = currentOffset + 2;
          stackMapTableEndOffset = currentOffset + attributeLength;
        }
        // Here we do not extract the labels corresponding to the attribute content. This would
        // require a full parsing of the attribute, which would need to be repeated when parsing
        // the bytecode instructions (see below). Instead, the content of the attribute is read one
        // frame at a time (i.e. after a frame has been visited, the next frame is read), and the
        // labels it contains are also extracted one frame at a time. Thanks to the ordering of
        // frames, having only a "one frame lookahead" is not a problem, i.e. it is not possible to
        // see an offset smaller than the offset of the current instruction and for which no Label
        // exist. Except for UNINITIALIZED type offsets. We solve this by parsing the stack map
        // table without a full decoding (see below).
      } else if ("StackMap".equals(attributeName)) {
        if ((context.parsingOptions & SKIP_FRAMES) == 0) {
          stackMapFrameOffset = currentOffset + 2;
          stackMapTableEndOffset = currentOffset + attributeLength;
          compressedFrames = false;
        }
        // IMPORTANT! Here we assume that the frames are ordered, as in the StackMapTable attribute,
        // although this is not guaranteed by the attribute format. This allows an incremental
        // extraction of the labels corresponding to this attribute (see the comment above for the
        // StackMapTable attribute).
      } else {
        Attribute attribute =
            readAttribute(
                context.attributePrototypes,
                attributeName,
                currentOffset,
                attributeLength,
                charBuffer,
                codeOffset,
                labels);
        attribute.nextAttribute = attributes;
        attributes = attribute;
      }
      currentOffset += attributeLength;
    }

    // Initialize the context fields related to stack map frames, and generate the first
    // (implicit) stack map frame, if needed.
    final boolean expandFrames = (context.parsingOptions & EXPAND_FRAMES) != 0;
    if (stackMapFrameOffset != 0) {
      // The bytecode offset of the first explicit frame is not offset_delta + 1 but only
      // offset_delta. Setting the implicit frame offset to -1 allows us to use of the
      // "offset_delta + 1" rule in all cases.
      context.currentFrameOffset = -1;
      context.currentFrameType = 0;
      context.currentFrameLocalCount = 0;
      context.currentFrameLocalCountDelta = 0;
      context.currentFrameLocalTypes = new Object[maxLocals];
      context.currentFrameStackCount = 0;
      context.currentFrameStackTypes = new Object[maxStack];
      if (expandFrames) {
        computeImplicitFrame(context);
      }
      // Find the labels for UNINITIALIZED frame types. Instead of decoding each element of the
      // stack map table, we look for 3 consecutive bytes that "look like" an UNINITIALIZED type
      // (tag ITEM_Uninitialized, offset within bytecode bounds, NEW instruction at this offset).
      // We may find false positives (i.e. not real UNINITIALIZED types), but this should be rare,
      // and the only consequence will be the creation of an unneeded label. This is better than
      // creating a label for each NEW instruction, and faster than fully decoding the whole stack
      // map table.
      for (int offset = stackMapFrameOffset; offset < stackMapTableEndOffset - 2; ++offset) {
        if (classFileBuffer[offset] == Frame.ITEM_UNINITIALIZED) {
          int potentialBytecodeOffset = readUnsignedShort(offset + 1);
          if (potentialBytecodeOffset >= 0
              && potentialBytecodeOffset < codeLength
              && (classFileBuffer[bytecodeStartOffset + potentialBytecodeOffset] & 0xFF)
                  == Opcodes.NEW) {
            createLabel(potentialBytecodeOffset, labels);
          }
        }
      }
    }
    if (expandFrames && (context.parsingOptions & EXPAND_ASM_INSNS) != 0) {
      // Expanding the ASM specific instructions can introduce F_INSERT frames, even if the method
      // does not currently have any frame. These inserted frames must be computed by simulating the
      // effect of the bytecode instructions, one by one, starting from the implicit first frame.
      // For this, MethodWriter needs to know maxLocals before the first instruction is visited. To
      // ensure this, we visit the implicit first frame here (passing only maxLocals - the rest is
      // computed in MethodWriter).
      methodVisitor.visitFrame(Opcodes.F_NEW, maxLocals, null, 0, null);
    }

    // Visit the bytecode instructions. First, introduce state variables for the incremental parsing
    // of the type annotations.

    // Index of the next runtime visible type annotation to read (in the
    // visibleTypeAnnotationOffsets array).
    int currentVisibleTypeAnnotationIndex = 0;
    // The bytecode offset of the next runtime visible type annotation to read, or -1.
    int currentVisibleTypeAnnotationBytecodeOffset =
        getTypeAnnotationBytecodeOffset(visibleTypeAnnotationOffsets, 0);
    // Index of the next runtime invisible type annotation to read (in the
    // invisibleTypeAnnotationOffsets array).
    int currentInvisibleTypeAnnotationIndex = 0;
    // The bytecode offset of the next runtime invisible type annotation to read, or -1.
    int currentInvisibleTypeAnnotationBytecodeOffset =
        getTypeAnnotationBytecodeOffset(invisibleTypeAnnotationOffsets, 0);

    // Whether a F_INSERT stack map frame must be inserted before the current instruction.
    boolean insertFrame = false;

    // The delta to subtract from a goto_w or jsr_w opcode to get the corresponding goto or jsr
    // opcode, or 0 if goto_w and jsr_w must be left unchanged (i.e. when expanding ASM specific
    // instructions).
    final int wideJumpOpcodeDelta =
        (context.parsingOptions & EXPAND_ASM_INSNS) == 0 ? Constants.WIDE_JUMP_OPCODE_DELTA : 0;

    currentOffset = bytecodeStartOffset;
    while (currentOffset < bytecodeEndOffset) {
      final int currentBytecodeOffset = currentOffset - bytecodeStartOffset;

      // Visit the label and the line number(s) for this bytecode offset, if any.
      Label currentLabel = labels[currentBytecodeOffset];
      if (currentLabel != null) {
        currentLabel.accept(methodVisitor, (context.parsingOptions & SKIP_DEBUG) == 0);
      }

      // Visit the stack map frame for this bytecode offset, if any.
      while (stackMapFrameOffset != 0
          && (context.currentFrameOffset == currentBytecodeOffset
              || context.currentFrameOffset == -1)) {
        // If there is a stack map frame for this offset, make methodVisitor visit it, and read the
        // next stack map frame if there is one.
        if (context.currentFrameOffset != -1) {
          if (!compressedFrames || expandFrames) {
            methodVisitor.visitFrame(
                Opcodes.F_NEW,
                context.currentFrameLocalCount,
                context.currentFrameLocalTypes,
                context.currentFrameStackCount,
                context.currentFrameStackTypes);
          } else {
            methodVisitor.visitFrame(
                context.currentFrameType,
                context.currentFrameLocalCountDelta,
                context.currentFrameLocalTypes,
                context.currentFrameStackCount,
                context.currentFrameStackTypes);
          }
          // Since there is already a stack map frame for this bytecode offset, there is no need to
          // insert a new one.
          insertFrame = false;
        }
        if (stackMapFrameOffset < stackMapTableEndOffset) {
          stackMapFrameOffset =
              readStackMapFrame(stackMapFrameOffset, compressedFrames, expandFrames, context);
        } else {
          stackMapFrameOffset = 0;
        }
      }

      // Insert a stack map frame for this bytecode offset, if requested by setting insertFrame to
      // true during the previous iteration. The actual frame content is computed in MethodWriter.
      if (insertFrame) {
        if ((context.parsingOptions & EXPAND_FRAMES) != 0) {
          methodVisitor.visitFrame(Constants.F_INSERT, 0, null, 0, null);
        }
        insertFrame = false;
      }

      // Visit the instruction at this bytecode offset.
      int opcode = classFileBuffer[currentOffset] & 0xFF;
      switch (opcode) {
        case Constants.NOP:
        case Constants.ACONST_NULL:
        case Constants.ICONST_M1:
        case Constants.ICONST_0:
        case Constants.ICONST_1:
        case Constants.ICONST_2:
        case Constants.ICONST_3:
        case Constants.ICONST_4:
        case Constants.ICONST_5:
        case Constants.LCONST_0:
        case Constants.LCONST_1:
        case Constants.FCONST_0:
        case Constants.FCONST_1:
        case Constants.FCONST_2:
        case Constants.DCONST_0:
        case Constants.DCONST_1:
        case Constants.IALOAD:
        case Constants.LALOAD:
        case Constants.FALOAD:
        case Constants.DALOAD:
        case Constants.AALOAD:
        case Constants.BALOAD:
        case Constants.CALOAD:
        case Constants.SALOAD:
        case Constants.IASTORE:
        case Constants.LASTORE:
        case Constants.FASTORE:
        case Constants.DASTORE:
        case Constants.AASTORE:
        case Constants.BASTORE:
        case Constants.CASTORE:
        case Constants.SASTORE:
        case Constants.POP:
        case Constants.POP2:
        case Constants.DUP:
        case Constants.DUP_X1:
        case Constants.DUP_X2:
        case Constants.DUP2:
        case Constants.DUP2_X1:
        case Constants.DUP2_X2:
        case Constants.SWAP:
        case Constants.IADD:
        case Constants.LADD:
        case Constants.FADD:
        case Constants.DADD:
        case Constants.ISUB:
        case Constants.LSUB:
        case Constants.FSUB:
        case Constants.DSUB:
        case Constants.IMUL:
        case Constants.LMUL:
        case Constants.FMUL:
        case Constants.DMUL:
        case Constants.IDIV:
        case Constants.LDIV:
        case Constants.FDIV:
        case Constants.DDIV:
        case Constants.IREM:
        case Constants.LREM:
        case Constants.FREM:
        case Constants.DREM:
        case Constants.INEG:
        case Constants.LNEG:
        case Constants.FNEG:
        case Constants.DNEG:
        case Constants.ISHL:
        case Constants.LSHL:
        case Constants.ISHR:
        case Constants.LSHR:
        case Constants.IUSHR:
        case Constants.LUSHR:
        case Constants.IAND:
        case Constants.LAND:
        case Constants.IOR:
        case Constants.LOR:
        case Constants.IXOR:
        case Constants.LXOR:
        case Constants.I2L:
        case Constants.I2F:
        case Constants.I2D:
        case Constants.L2I:
        case Constants.L2F:
        case Constants.L2D:
        case Constants.F2I:
        case Constants.F2L:
        case Constants.F2D:
        case Constants.D2I:
        case Constants.D2L:
        case Constants.D2F:
        case Constants.I2B:
        case Constants.I2C:
        case Constants.I2S:
        case Constants.LCMP:
        case Constants.FCMPL:
        case Constants.FCMPG:
        case Constants.DCMPL:
        case Constants.DCMPG:
        case Constants.IRETURN:
        case Constants.LRETURN:
        case Constants.FRETURN:
        case Constants.DRETURN:
        case Constants.ARETURN:
        case Constants.RETURN:
        case Constants.ARRAYLENGTH:
        case Constants.ATHROW:
        case Constants.MONITORENTER:
        case Constants.MONITOREXIT:
          methodVisitor.visitInsn(opcode);
          currentOffset += 1;
          break;
        case Constants.ILOAD_0:
        case Constants.ILOAD_1:
        case Constants.ILOAD_2:
        case Constants.ILOAD_3:
        case Constants.LLOAD_0:
        case Constants.LLOAD_1:
        case Constants.LLOAD_2:
        case Constants.LLOAD_3:
        case Constants.FLOAD_0:
        case Constants.FLOAD_1:
        case Constants.FLOAD_2:
        case Constants.FLOAD_3:
        case Constants.DLOAD_0:
        case Constants.DLOAD_1:
        case Constants.DLOAD_2:
        case Constants.DLOAD_3:
        case Constants.ALOAD_0:
        case Constants.ALOAD_1:
        case Constants.ALOAD_2:
        case Constants.ALOAD_3:
          opcode -= Constants.ILOAD_0;
          methodVisitor.visitVarInsn(Opcodes.ILOAD + (opcode >> 2), opcode & 0x3);
          currentOffset += 1;
          break;
        case Constants.ISTORE_0:
        case Constants.ISTORE_1:
        case Constants.ISTORE_2:
        case Constants.ISTORE_3:
        case Constants.LSTORE_0:
        case Constants.LSTORE_1:
        case Constants.LSTORE_2:
        case Constants.LSTORE_3:
        case Constants.FSTORE_0:
        case Constants.FSTORE_1:
        case Constants.FSTORE_2:
        case Constants.FSTORE_3:
        case Constants.DSTORE_0:
        case Constants.DSTORE_1:
        case Constants.DSTORE_2:
        case Constants.DSTORE_3:
        case Constants.ASTORE_0:
        case Constants.ASTORE_1:
        case Constants.ASTORE_2:
        case Constants.ASTORE_3:
          opcode -= Constants.ISTORE_0;
          methodVisitor.visitVarInsn(Opcodes.ISTORE + (opcode >> 2), opcode & 0x3);
          currentOffset += 1;
          break;
        case Constants.IFEQ:
        case Constants.IFNE:
        case Constants.IFLT:
        case Constants.IFGE:
        case Constants.IFGT:
        case Constants.IFLE:
        case Constants.IF_ICMPEQ:
        case Constants.IF_ICMPNE:
        case Constants.IF_ICMPLT:
        case Constants.IF_ICMPGE:
        case Constants.IF_ICMPGT:
        case Constants.IF_ICMPLE:
        case Constants.IF_ACMPEQ:
        case Constants.IF_ACMPNE:
        case Constants.GOTO:
        case Constants.JSR:
        case Constants.IFNULL:
        case Constants.IFNONNULL:
          methodVisitor.visitJumpInsn(
              opcode, labels[currentBytecodeOffset + readShort(currentOffset + 1)]);
          currentOffset += 3;
          break;
        case Constants.GOTO_W:
        case Constants.JSR_W:
          methodVisitor.visitJumpInsn(
              opcode - wideJumpOpcodeDelta,
              labels[currentBytecodeOffset + readInt(currentOffset + 1)]);
          currentOffset += 5;
          break;
        case Constants.ASM_IFEQ:
        case Constants.ASM_IFNE:
        case Constants.ASM_IFLT:
        case Constants.ASM_IFGE:
        case Constants.ASM_IFGT:
        case Constants.ASM_IFLE:
        case Constants.ASM_IF_ICMPEQ:
        case Constants.ASM_IF_ICMPNE:
        case Constants.ASM_IF_ICMPLT:
        case Constants.ASM_IF_ICMPGE:
        case Constants.ASM_IF_ICMPGT:
        case Constants.ASM_IF_ICMPLE:
        case Constants.ASM_IF_ACMPEQ:
        case Constants.ASM_IF_ACMPNE:
        case Constants.ASM_GOTO:
        case Constants.ASM_JSR:
        case Constants.ASM_IFNULL:
        case Constants.ASM_IFNONNULL:
          {
            // A forward jump with an offset > 32767. In this case we automatically replace ASM_GOTO
            // with GOTO_W, ASM_JSR with JSR_W and ASM_IFxxx <l> with IFNOTxxx <L> GOTO_W <l> L:...,
            // where IFNOTxxx is the "opposite" opcode of ASMS_IFxxx (e.g. IFNE for ASM_IFEQ) and
            // where <L> designates the instruction just after the GOTO_W.
            // First, change the ASM specific opcodes ASM_IFEQ ... ASM_JSR, ASM_IFNULL and
            // ASM_IFNONNULL to IFEQ ... JSR, IFNULL and IFNONNULL.
            opcode =
                opcode < Constants.ASM_IFNULL
                    ? opcode - Constants.ASM_OPCODE_DELTA
                    : opcode - Constants.ASM_IFNULL_OPCODE_DELTA;
            Label target = labels[currentBytecodeOffset + readUnsignedShort(currentOffset + 1)];
            if (opcode == Opcodes.GOTO || opcode == Opcodes.JSR) {
              // Replace GOTO with GOTO_W and JSR with JSR_W.
              methodVisitor.visitJumpInsn(opcode + Constants.WIDE_JUMP_OPCODE_DELTA, target);
            } else {
              // Compute the "opposite" of opcode. This can be done by flipping the least
              // significant bit for IFNULL and IFNONNULL, and similarly for IFEQ ... IF_ACMPEQ
              // (with a pre and post offset by 1).
              opcode = opcode < Opcodes.GOTO ? ((opcode + 1) ^ 1) - 1 : opcode ^ 1;
              Label endif = createLabel(currentBytecodeOffset + 3, labels);
              methodVisitor.visitJumpInsn(opcode, endif);
              methodVisitor.visitJumpInsn(Constants.GOTO_W, target);
              // endif designates the instruction just after GOTO_W, and is visited as part of the
              // next instruction. Since it is a jump target, we need to insert a frame here.
              insertFrame = true;
            }
            currentOffset += 3;
            break;
          }
        case Constants.ASM_GOTO_W:
          {
            // Replace ASM_GOTO_W with GOTO_W.
            methodVisitor.visitJumpInsn(
                Constants.GOTO_W, labels[currentBytecodeOffset + readInt(currentOffset + 1)]);
            // The instruction just after is a jump target (because ASM_GOTO_W is used in patterns
            // IFNOTxxx <L> ASM_GOTO_W <l> L:..., see MethodWriter), so we need to insert a frame
            // here.
            insertFrame = true;
            currentOffset += 5;
            break;
          }
        case Constants.WIDE:
          opcode = classFileBuffer[currentOffset + 1] & 0xFF;
          if (opcode == Opcodes.IINC) {
            methodVisitor.visitIincInsn(
                readUnsignedShort(currentOffset + 2), readShort(currentOffset + 4));
            currentOffset += 6;
          } else {
            methodVisitor.visitVarInsn(opcode, readUnsignedShort(currentOffset + 2));
            currentOffset += 4;
          }
          break;
        case Constants.TABLESWITCH:
          {
            // Skip 0 to 3 padding bytes.
            currentOffset += 4 - (currentBytecodeOffset & 3);
            // Read the instruction.
            Label defaultLabel = labels[currentBytecodeOffset + readInt(currentOffset)];
            int low = readInt(currentOffset + 4);
            int high = readInt(currentOffset + 8);
            currentOffset += 12;
            Label[] table = new Label[high - low + 1];
            for (int i = 0; i < table.length; ++i) {
              table[i] = labels[currentBytecodeOffset + readInt(currentOffset)];
              currentOffset += 4;
            }
            methodVisitor.visitTableSwitchInsn(low, high, defaultLabel, table);
            break;
          }
        case Constants.LOOKUPSWITCH:
          {
            // Skip 0 to 3 padding bytes.
            currentOffset += 4 - (currentBytecodeOffset & 3);
            // Read the instruction.
            Label defaultLabel = labels[currentBytecodeOffset + readInt(currentOffset)];
            int numPairs = readInt(currentOffset + 4);
            currentOffset += 8;
            int[] keys = new int[numPairs];
            Label[] values = new Label[numPairs];
            for (int i = 0; i < numPairs; ++i) {
              keys[i] = readInt(currentOffset);
              values[i] = labels[currentBytecodeOffset + readInt(currentOffset + 4)];
              currentOffset += 8;
            }
            methodVisitor.visitLookupSwitchInsn(defaultLabel, keys, values);
            break;
          }
        case Constants.ILOAD:
        case Constants.LLOAD:
        case Constants.FLOAD:
        case Constants.DLOAD:
        case Constants.ALOAD:
        case Constants.ISTORE:
        case Constants.LSTORE:
        case Constants.FSTORE:
        case Constants.DSTORE:
        case Constants.ASTORE:
        case Constants.RET:
          methodVisitor.visitVarInsn(opcode, classFileBuffer[currentOffset + 1] & 0xFF);
          currentOffset += 2;
          break;
        case Constants.BIPUSH:
        case Constants.NEWARRAY:
          methodVisitor.visitIntInsn(opcode, classFileBuffer[currentOffset + 1]);
          currentOffset += 2;
          break;
        case Constants.SIPUSH:
          methodVisitor.visitIntInsn(opcode, readShort(currentOffset + 1));
          currentOffset += 3;
          break;
        case Constants.LDC:
          methodVisitor.visitLdcInsn(
              readConst(classFileBuffer[currentOffset + 1] & 0xFF, charBuffer));
          currentOffset += 2;
          break;
        case Constants.LDC_W:
        case Constants.LDC2_W:
          methodVisitor.visitLdcInsn(readConst(readUnsignedShort(currentOffset + 1), charBuffer));
          currentOffset += 3;
          break;
        case Constants.GETSTATIC:
        case Constants.PUTSTATIC:
        case Constants.GETFIELD:
        case Constants.PUTFIELD:
        case Constants.INVOKEVIRTUAL:
        case Constants.INVOKESPECIAL:
        case Constants.INVOKESTATIC:
        case Constants.INVOKEINTERFACE:
          {
            int cpInfoOffset = cpInfoOffsets[readUnsignedShort(currentOffset + 1)];
            int nameAndTypeCpInfoOffset = cpInfoOffsets[readUnsignedShort(cpInfoOffset + 2)];
            String owner = readClass(cpInfoOffset, charBuffer);
            String name = readUTF8(nameAndTypeCpInfoOffset, charBuffer);
            String descriptor = readUTF8(nameAndTypeCpInfoOffset + 2, charBuffer);
            if (opcode < Opcodes.INVOKEVIRTUAL) {
              methodVisitor.visitFieldInsn(opcode, owner, name, descriptor);
            } else {
              boolean isInterface =
                  classFileBuffer[cpInfoOffset - 1] == Symbol.CONSTANT_INTERFACE_METHODREF_TAG;
              methodVisitor.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
            }
            if (opcode == Opcodes.INVOKEINTERFACE) {
              currentOffset += 5;
            } else {
              currentOffset += 3;
            }
            break;
          }
        case Constants.INVOKEDYNAMIC:
          {
            int cpInfoOffset = cpInfoOffsets[readUnsignedShort(currentOffset + 1)];
            int nameAndTypeCpInfoOffset = cpInfoOffsets[readUnsignedShort(cpInfoOffset + 2)];
            String name = readUTF8(nameAndTypeCpInfoOffset, charBuffer);
            String descriptor = readUTF8(nameAndTypeCpInfoOffset + 2, charBuffer);
            int bootstrapMethodOffset = bootstrapMethodOffsets[readUnsignedShort(cpInfoOffset)];
            Handle handle =
                (Handle) readConst(readUnsignedShort(bootstrapMethodOffset), charBuffer);
            Object[] bootstrapMethodArguments =
                new Object[readUnsignedShort(bootstrapMethodOffset + 2)];
            bootstrapMethodOffset += 4;
            for (int i = 0; i < bootstrapMethodArguments.length; i++) {
              bootstrapMethodArguments[i] =
                  readConst(readUnsignedShort(bootstrapMethodOffset), charBuffer);
              bootstrapMethodOffset += 2;
            }
            methodVisitor.visitInvokeDynamicInsn(
                name, descriptor, handle, bootstrapMethodArguments);
            currentOffset += 5;
            break;
          }
        case Constants.NEW:
        case Constants.ANEWARRAY:
        case Constants.CHECKCAST:
        case Constants.INSTANCEOF:
          methodVisitor.visitTypeInsn(opcode, readClass(currentOffset + 1, charBuffer));
          currentOffset += 3;
          break;
        case Constants.IINC:
          methodVisitor.visitIincInsn(
              classFileBuffer[currentOffset + 1] & 0xFF, classFileBuffer[currentOffset + 2]);
          currentOffset += 3;
          break;
        case Constants.MULTIANEWARRAY:
          methodVisitor.visitMultiANewArrayInsn(
              readClass(currentOffset + 1, charBuffer), classFileBuffer[currentOffset + 3] & 0xFF);
          currentOffset += 4;
          break;
        default:
          throw new AssertionError();
      }

      // Visit the runtime visible instruction annotations, if any.
      while (visibleTypeAnnotationOffsets != null
          && currentVisibleTypeAnnotationIndex < visibleTypeAnnotationOffsets.length
          && currentVisibleTypeAnnotationBytecodeOffset <= currentBytecodeOffset) {
        if (currentVisibleTypeAnnotationBytecodeOffset == currentBytecodeOffset) {
          // Parse the target_type, target_info and target_path fields.
          int currentAnnotationOffset =
              readTypeAnnotationTarget(
                  context, visibleTypeAnnotationOffsets[currentVisibleTypeAnnotationIndex]);
          // Parse the type_index field.
          String annotationDescriptor = readUTF8(currentAnnotationOffset, charBuffer);
          currentAnnotationOffset += 2;
          // Parse num_element_value_pairs and element_value_pairs and visit these values.
          readElementValues(
              methodVisitor.visitInsnAnnotation(
                  context.currentTypeAnnotationTarget,
                  context.currentTypeAnnotationTargetPath,
                  annotationDescriptor,
                  /* visible = */ true),
              currentAnnotationOffset,
              /* named = */ true,
              charBuffer);
        }
        currentVisibleTypeAnnotationBytecodeOffset =
            getTypeAnnotationBytecodeOffset(
                visibleTypeAnnotationOffsets, ++currentVisibleTypeAnnotationIndex);
      }

      // Visit the runtime invisible instruction annotations, if any.
      while (invisibleTypeAnnotationOffsets != null
          && currentInvisibleTypeAnnotationIndex < invisibleTypeAnnotationOffsets.length
          && currentInvisibleTypeAnnotationBytecodeOffset <= currentBytecodeOffset) {
        if (currentInvisibleTypeAnnotationBytecodeOffset == currentBytecodeOffset) {
          // Parse the target_type, target_info and target_path fields.
          int currentAnnotationOffset =
              readTypeAnnotationTarget(
                  context, invisibleTypeAnnotationOffsets[currentInvisibleTypeAnnotationIndex]);
          // Parse the type_index field.
          String annotationDescriptor = readUTF8(currentAnnotationOffset, charBuffer);
          currentAnnotationOffset += 2;
          // Parse num_element_value_pairs and element_value_pairs and visit these values.
          readElementValues(
              methodVisitor.visitInsnAnnotation(
                  context.currentTypeAnnotationTarget,
                  context.currentTypeAnnotationTargetPath,
                  annotationDescriptor,
                  /* visible = */ false),
              currentAnnotationOffset,
              /* named = */ true,
              charBuffer);
        }
        currentInvisibleTypeAnnotationBytecodeOffset =
            getTypeAnnotationBytecodeOffset(
                invisibleTypeAnnotationOffsets, ++currentInvisibleTypeAnnotationIndex);
      }
    }
    if (labels[codeLength] != null) {
      methodVisitor.visitLabel(labels[codeLength]);
    }

    // Visit LocalVariableTable and LocalVariableTypeTable attributes.
    if (localVariableTableOffset != 0 && (context.parsingOptions & SKIP_DEBUG) == 0) {
      // The (start_pc, index, signature_index) fields of each entry of the LocalVariableTypeTable.
      int[] typeTable = null;
      if (localVariableTypeTableOffset != 0) {
        typeTable = new int[readUnsignedShort(localVariableTypeTableOffset) * 3];
        currentOffset = localVariableTypeTableOffset + 2;
        int typeTableIndex = typeTable.length;
        while (typeTableIndex > 0) {
          // Store the offset of 'signature_index', and the value of 'index' and 'start_pc'.
          typeTable[--typeTableIndex] = currentOffset + 6;
          typeTable[--typeTableIndex] = readUnsignedShort(currentOffset + 8);
          typeTable[--typeTableIndex] = readUnsignedShort(currentOffset);
          currentOffset += 10;
        }
      }
      int localVariableTableLength = readUnsignedShort(localVariableTableOffset);
      currentOffset = localVariableTableOffset + 2;
      while (localVariableTableLength-- > 0) {
        int startPc = readUnsignedShort(currentOffset);
        int length = readUnsignedShort(currentOffset + 2);
        String name = readUTF8(currentOffset + 4, charBuffer);
        String descriptor = readUTF8(currentOffset + 6, charBuffer);
        int index = readUnsignedShort(currentOffset + 8);
        currentOffset += 10;
        String signature = null;
        if (typeTable != null) {
          for (int i = 0; i < typeTable.length; i += 3) {
            if (typeTable[i] == startPc && typeTable[i + 1] == index) {
              signature = readUTF8(typeTable[i + 2], charBuffer);
              break;
            }
          }
        }
        methodVisitor.visitLocalVariable(
            name, descriptor, signature, labels[startPc], labels[startPc + length], index);
      }
    }

    // Visit the local variable type annotations of the RuntimeVisibleTypeAnnotations attribute.
    if (visibleTypeAnnotationOffsets != null) {
      for (int typeAnnotationOffset : visibleTypeAnnotationOffsets) {
        int targetType = readByte(typeAnnotationOffset);
        if (targetType == TypeReference.LOCAL_VARIABLE
            || targetType == TypeReference.RESOURCE_VARIABLE) {
          // Parse the target_type, target_info and target_path fields.
          currentOffset = readTypeAnnotationTarget(context, typeAnnotationOffset);
          // Parse the type_index field.
          String annotationDescriptor = readUTF8(currentOffset, charBuffer);
          currentOffset += 2;
          // Parse num_element_value_pairs and element_value_pairs and visit these values.
          readElementValues(
              methodVisitor.visitLocalVariableAnnotation(
                  context.currentTypeAnnotationTarget,
                  context.currentTypeAnnotationTargetPath,
                  context.currentLocalVariableAnnotationRangeStarts,
                  context.currentLocalVariableAnnotationRangeEnds,
                  context.currentLocalVariableAnnotationRangeIndices,
                  annotationDescriptor,
                  /* visible = */ true),
              currentOffset,
              /* named = */ true,
              charBuffer);
        }
      }
    }

    // Visit the local variable type annotations of the RuntimeInvisibleTypeAnnotations attribute.
    if (invisibleTypeAnnotationOffsets != null) {
      for (int typeAnnotationOffset : invisibleTypeAnnotationOffsets) {
        int targetType = readByte(typeAnnotationOffset);
        if (targetType == TypeReference.LOCAL_VARIABLE
            || targetType == TypeReference.RESOURCE_VARIABLE) {
          // Parse the target_type, target_info and target_path fields.
          currentOffset = readTypeAnnotationTarget(context, typeAnnotationOffset);
          // Parse the type_index field.
          String annotationDescriptor = readUTF8(currentOffset, charBuffer);
          currentOffset += 2;
          // Parse num_element_value_pairs and element_value_pairs and visit these values.
          readElementValues(
              methodVisitor.visitLocalVariableAnnotation(
                  context.currentTypeAnnotationTarget,
                  context.currentTypeAnnotationTargetPath,
                  context.currentLocalVariableAnnotationRangeStarts,
                  context.currentLocalVariableAnnotationRangeEnds,
                  context.currentLocalVariableAnnotationRangeIndices,
                  annotationDescriptor,
                  /* visible = */ false),
              currentOffset,
              /* named = */ true,
              charBuffer);
        }
      }
    }

    // Visit the non standard attributes.
    while (attributes != null) {
      // Copy and reset the nextAttribute field so that it can also be used in MethodWriter.
      Attribute nextAttribute = attributes.nextAttribute;
      attributes.nextAttribute = null;
      methodVisitor.visitAttribute(attributes);
      attributes = nextAttribute;
    }

    // Visit the max stack and max locals values.
    methodVisitor.visitMaxs(maxStack, maxLocals);
  }
private void readCode ( final MethodVisitor methodVisitor , final Context context , final int codeOffset ) { int currentOffset = codeOffset ; // Read the max_stack, max_locals and code_length fields. final byte [ ] classFileBuffer = b ; final char [ ] charBuffer = context . charBuffer ; final int maxStack = readUnsignedShort ( currentOffset ) ; final int maxLocals = readUnsignedShort ( currentOffset + 2 ) ; final int codeLength = readInt ( currentOffset + 4 ) ; currentOffset += 8 ; // Read the bytecode 'code' array to create a label for each referenced instruction. final int bytecodeStartOffset = currentOffset ; final int bytecodeEndOffset = currentOffset + codeLength ; final Label [ ] labels = context . currentMethodLabels = new Label [ codeLength + 1 ] ; while ( currentOffset < bytecodeEndOffset ) { final int bytecodeOffset = currentOffset - bytecodeStartOffset ; final int opcode = classFileBuffer [ currentOffset ] & 0xFF ; switch ( opcode ) { case Constants . NOP : case Constants . ACONST_NULL : case Constants . ICONST_M1 : case Constants . ICONST_0 : case Constants . ICONST_1 : case Constants . ICONST_2 : case Constants . ICONST_3 : case Constants . ICONST_4 : case Constants . ICONST_5 : case Constants . LCONST_0 : case Constants . LCONST_1 : case Constants . FCONST_0 : case Constants . FCONST_1 : case Constants . FCONST_2 : case Constants . DCONST_0 : case Constants . DCONST_1 : case Constants . IALOAD : case Constants . LALOAD : case Constants . FALOAD : case Constants . DALOAD : case Constants . AALOAD : case Constants . BALOAD : case Constants . CALOAD : case Constants . SALOAD : case Constants . IASTORE : case Constants . LASTORE : case Constants . FASTORE : case Constants . DASTORE : case Constants . AASTORE : case Constants . BASTORE : case Constants . CASTORE : case Constants . SASTORE : case Constants . POP : case Constants . POP2 : case Constants . DUP : case Constants . DUP_X1 : case Constants . DUP_X2 : case Constants . DUP2 : case Constants . DUP2_X1 : case Constants . DUP2_X2 : case Constants . SWAP : case Constants . IADD : case Constants . LADD : case Constants . FADD : case Constants . DADD : case Constants . ISUB : case Constants . LSUB : case Constants . FSUB : case Constants . DSUB : case Constants . IMUL : case Constants . LMUL : case Constants . FMUL : case Constants . DMUL : case Constants . IDIV : case Constants . LDIV : case Constants . FDIV : case Constants . DDIV : case Constants . IREM : case Constants . LREM : case Constants . FREM : case Constants . DREM : case Constants . INEG : case Constants . LNEG : case Constants . FNEG : case Constants . DNEG : case Constants . ISHL : case Constants . LSHL : case Constants . ISHR : case Constants . LSHR : case Constants . IUSHR : case Constants . LUSHR : case Constants . IAND : case Constants . LAND : case Constants . IOR : case Constants . LOR : case Constants . IXOR : case Constants . LXOR : case Constants . I2L : case Constants . I2F : case Constants . I2D : case Constants . L2I : case Constants . L2F : case Constants . L2D : case Constants . F2I : case Constants . F2L : case Constants . F2D : case Constants . D2I : case Constants . D2L : case Constants . D2F : case Constants . I2B : case Constants . I2C : case Constants . I2S : case Constants . LCMP : case Constants . FCMPL : case Constants . FCMPG : case Constants . DCMPL : case Constants . DCMPG : case Constants . IRETURN : case Constants . LRETURN : case Constants . FRETURN : case Constants . DRETURN : case Constants . ARETURN : case Constants . RETURN : case Constants . ARRAYLENGTH : case Constants . ATHROW : case Constants . MONITORENTER : case Constants . MONITOREXIT : case Constants . ILOAD_0 : case Constants . ILOAD_1 : case Constants . ILOAD_2 : case Constants . ILOAD_3 : case Constants . LLOAD_0 : case Constants . LLOAD_1 : case Constants . LLOAD_2 : case Constants . LLOAD_3 : case Constants . FLOAD_0 : case Constants . FLOAD_1 : case Constants . FLOAD_2 : case Constants . FLOAD_3 : case Constants . DLOAD_0 : case Constants . DLOAD_1 : case Constants . DLOAD_2 : case Constants . DLOAD_3 : case Constants . ALOAD_0 : case Constants . ALOAD_1 : case Constants . ALOAD_2 : case Constants . ALOAD_3 : case Constants . ISTORE_0 : case Constants . ISTORE_1 : case Constants . ISTORE_2 : case Constants . ISTORE_3 : case Constants . LSTORE_0 : case Constants . LSTORE_1 : case Constants . LSTORE_2 : case Constants . LSTORE_3 : case Constants . FSTORE_0 : case Constants . FSTORE_1 : case Constants . FSTORE_2 : case Constants . FSTORE_3 : case Constants . DSTORE_0 : case Constants . DSTORE_1 : case Constants . DSTORE_2 : case Constants . DSTORE_3 : case Constants . ASTORE_0 : case Constants . ASTORE_1 : case Constants . ASTORE_2 : case Constants . ASTORE_3 : currentOffset += 1 ; break ; case Constants . IFEQ : case Constants . IFNE : case Constants . IFLT : case Constants . IFGE : case Constants . IFGT : case Constants . IFLE : case Constants . IF_ICMPEQ : case Constants . IF_ICMPNE : case Constants . IF_ICMPLT : case Constants . IF_ICMPGE : case Constants . IF_ICMPGT : case Constants . IF_ICMPLE : case Constants . IF_ACMPEQ : case Constants . IF_ACMPNE : case Constants . GOTO : case Constants . JSR : case Constants . IFNULL : case Constants . IFNONNULL : createLabel ( bytecodeOffset + readShort ( currentOffset + 1 ) , labels ) ; currentOffset += 3 ; break ; case Constants . ASM_IFEQ : case Constants . ASM_IFNE : case Constants . ASM_IFLT : case Constants . ASM_IFGE : case Constants . ASM_IFGT : case Constants . ASM_IFLE : case Constants . ASM_IF_ICMPEQ : case Constants . ASM_IF_ICMPNE : case Constants . ASM_IF_ICMPLT : case Constants . ASM_IF_ICMPGE : case Constants . ASM_IF_ICMPGT : case Constants . ASM_IF_ICMPLE : case Constants . ASM_IF_ACMPEQ : case Constants . ASM_IF_ACMPNE : case Constants . ASM_GOTO : case Constants . ASM_JSR : case Constants . ASM_IFNULL : case Constants . ASM_IFNONNULL : createLabel ( bytecodeOffset + readUnsignedShort ( currentOffset + 1 ) , labels ) ; currentOffset += 3 ; break ; case Constants . GOTO_W : case Constants . JSR_W : case Constants . ASM_GOTO_W : createLabel ( bytecodeOffset + readInt ( currentOffset + 1 ) , labels ) ; currentOffset += 5 ; break ; case Constants . WIDE : switch ( classFileBuffer [ currentOffset + 1 ] & 0xFF ) { case Constants . ILOAD : case Constants . FLOAD : case Constants . ALOAD : case Constants . LLOAD : case Constants . DLOAD : case Constants . ISTORE : case Constants . FSTORE : case Constants . ASTORE : case Constants . LSTORE : case Constants . DSTORE : case Constants . RET : currentOffset += 4 ; break ; case Constants . IINC : currentOffset += 6 ; break ; default : throw new IllegalArgumentException ( ) ; } break ; case Constants . TABLESWITCH : // Skip 0 to 3 padding bytes. currentOffset += 4 - ( bytecodeOffset & 3 ) ; // Read the default label and the number of table entries. createLabel ( bytecodeOffset + readInt ( currentOffset ) , labels ) ; int numTableEntries = readInt ( currentOffset + 8 ) - readInt ( currentOffset + 4 ) + 1 ; currentOffset += 12 ; // Read the table labels. while ( numTableEntries -- > 0 ) { createLabel ( bytecodeOffset + readInt ( currentOffset ) , labels ) ; currentOffset += 4 ; } break ; case Constants . LOOKUPSWITCH : // Skip 0 to 3 padding bytes. currentOffset += 4 - ( bytecodeOffset & 3 ) ; // Read the default label and the number of switch cases. createLabel ( bytecodeOffset + readInt ( currentOffset ) , labels ) ; int numSwitchCases = readInt ( currentOffset + 4 ) ; currentOffset += 8 ; // Read the switch labels. while ( numSwitchCases -- > 0 ) { createLabel ( bytecodeOffset + readInt ( currentOffset + 4 ) , labels ) ; currentOffset += 8 ; } break ; case Constants . ILOAD : case Constants . LLOAD : case Constants . FLOAD : case Constants . DLOAD : case Constants . ALOAD : case Constants . ISTORE : case Constants . LSTORE : case Constants . FSTORE : case Constants . DSTORE : case Constants . ASTORE : case Constants . RET : case Constants . BIPUSH : case Constants . NEWARRAY : case Constants . LDC : currentOffset += 2 ; break ; case Constants . SIPUSH : case Constants . LDC_W : case Constants . LDC2_W : case Constants . GETSTATIC : case Constants . PUTSTATIC : case Constants . GETFIELD : case Constants . PUTFIELD : case Constants . INVOKEVIRTUAL : case Constants . INVOKESPECIAL : case Constants . INVOKESTATIC : case Constants . NEW : case Constants . ANEWARRAY : case Constants . CHECKCAST : case Constants . INSTANCEOF : case Constants . IINC : currentOffset += 3 ; break ; case Constants . INVOKEINTERFACE : case Constants . INVOKEDYNAMIC : currentOffset += 5 ; break ; case Constants . MULTIANEWARRAY : currentOffset += 4 ; break ; default : throw new IllegalArgumentException ( ) ; } } // Read the 'exception_table_length' and 'exception_table' field to create a label for each // referenced instruction, and to make methodVisitor visit the corresponding try catch blocks. int exceptionTableLength = readUnsignedShort ( currentOffset ) ; currentOffset += 2 ; while ( exceptionTableLength -- > 0 ) { Label start = createLabel ( readUnsignedShort ( currentOffset ) , labels ) ; Label end = createLabel ( readUnsignedShort ( currentOffset + 2 ) , labels ) ; Label handler = createLabel ( readUnsignedShort ( currentOffset + 4 ) , labels ) ; String catchType = readUTF8 ( cpInfoOffsets [ readUnsignedShort ( currentOffset + 6 ) ] , charBuffer ) ; currentOffset += 8 ; methodVisitor . visitTryCatchBlock ( start , end , handler , catchType ) ; } // Read the Code attributes to create a label for each referenced instruction (the variables // are ordered as in Section 4.7 of the JVMS). Attribute offsets exclude the // attribute_name_index and attribute_length fields. // - The offset of the current 'stack_map_frame' in the StackMap[Table] attribute, or 0. // Initially, this is the offset of the first 'stack_map_frame' entry. Then this offset is // updated after each stack_map_frame is read. int stackMapFrameOffset = 0 ; // - The end offset of the StackMap[Table] attribute, or 0. int stackMapTableEndOffset = 0 ; // - Whether the stack map frames are compressed (i.e. in a StackMapTable) or not. boolean compressedFrames = true ; // - The offset of the LocalVariableTable attribute, or 0. int localVariableTableOffset = 0 ; // - The offset of the LocalVariableTypeTable attribute, or 0. int localVariableTypeTableOffset = 0 ; // - The offset of each 'type_annotation' entry in the RuntimeVisibleTypeAnnotations // attribute, or null. int [ ] visibleTypeAnnotationOffsets = null ; // - The offset of each 'type_annotation' entry in the RuntimeInvisibleTypeAnnotations // attribute, or null. int [ ] invisibleTypeAnnotationOffsets = null ; // - The non standard attributes (linked with their {@link Attribute#nextAttribute} field). //   This list in the <i>reverse order</i> or their order in the ClassFile structure. Attribute attributes = null ; int attributesCount = readUnsignedShort ( currentOffset ) ; currentOffset += 2 ; while ( attributesCount -- > 0 ) { // Read the attribute_info's attribute_name and attribute_length fields. String attributeName = readUTF8 ( currentOffset , charBuffer ) ; int attributeLength = readInt ( currentOffset + 2 ) ; currentOffset += 6 ; if ( Constants . LOCAL_VARIABLE_TABLE . equals ( attributeName ) ) { if ( ( context . parsingOptions & SKIP_DEBUG ) == 0 ) { localVariableTableOffset = currentOffset ; // Parse the attribute to find the corresponding (debug only) labels. int currentLocalVariableTableOffset = currentOffset ; int localVariableTableLength = readUnsignedShort ( currentLocalVariableTableOffset ) ; currentLocalVariableTableOffset += 2 ; while ( localVariableTableLength -- > 0 ) { int startPc = readUnsignedShort ( currentLocalVariableTableOffset ) ; createDebugLabel ( startPc , labels ) ; int length = readUnsignedShort ( currentLocalVariableTableOffset + 2 ) ; createDebugLabel ( startPc + length , labels ) ; // Skip the name_index, descriptor_index and index fields (2 bytes each). currentLocalVariableTableOffset += 10 ; } } } else if ( Constants . LOCAL_VARIABLE_TYPE_TABLE . equals ( attributeName ) ) { localVariableTypeTableOffset = currentOffset ; // Here we do not extract the labels corresponding to the attribute content. We assume they // are the same or a subset of those of the LocalVariableTable attribute. } else if ( Constants . LINE_NUMBER_TABLE . equals ( attributeName ) ) { if ( ( context . parsingOptions & SKIP_DEBUG ) == 0 ) { // Parse the attribute to find the corresponding (debug only) labels. int currentLineNumberTableOffset = currentOffset ; int lineNumberTableLength = readUnsignedShort ( currentLineNumberTableOffset ) ; currentLineNumberTableOffset += 2 ; while ( lineNumberTableLength -- > 0 ) { int startPc = readUnsignedShort ( currentLineNumberTableOffset ) ; int lineNumber = readUnsignedShort ( currentLineNumberTableOffset + 2 ) ; currentLineNumberTableOffset += 4 ; createDebugLabel ( startPc , labels ) ; labels [ startPc ] . addLineNumber ( lineNumber ) ; } } } else if ( Constants . RUNTIME_VISIBLE_TYPE_ANNOTATIONS . equals ( attributeName ) ) { visibleTypeAnnotationOffsets = readTypeAnnotations ( methodVisitor , context , currentOffset , /* visible = */ true ) ; // Here we do not extract the labels corresponding to the attribute content. This would // require a full parsing of the attribute, which would need to be repeated when parsing // the bytecode instructions (see below). Instead, the content of the attribute is read one // type annotation at a time (i.e. after a type annotation has been visited, the next type // annotation is read), and the labels it contains are also extracted one annotation at a // time. This assumes that type annotations are ordered by increasing bytecode offset. } else if ( Constants . RUNTIME_INVISIBLE_TYPE_ANNOTATIONS . equals ( attributeName ) ) { invisibleTypeAnnotationOffsets = readTypeAnnotations ( methodVisitor , context , currentOffset , /* visible = */ false ) ; // Same comment as above for the RuntimeVisibleTypeAnnotations attribute. } else if ( Constants . STACK_MAP_TABLE . equals ( attributeName ) ) { if ( ( context . parsingOptions & SKIP_FRAMES ) == 0 ) { stackMapFrameOffset = currentOffset + 2 ; stackMapTableEndOffset = currentOffset + attributeLength ; } // Here we do not extract the labels corresponding to the attribute content. This would // require a full parsing of the attribute, which would need to be repeated when parsing // the bytecode instructions (see below). Instead, the content of the attribute is read one // frame at a time (i.e. after a frame has been visited, the next frame is read), and the // labels it contains are also extracted one frame at a time. Thanks to the ordering of // frames, having only a "one frame lookahead" is not a problem, i.e. it is not possible to // see an offset smaller than the offset of the current instruction and for which no Label // exist. Except for UNINITIALIZED type offsets. We solve this by parsing the stack map // table without a full decoding (see below). } else if ( "StackMap" . equals ( attributeName ) ) { if ( ( context . parsingOptions & SKIP_FRAMES ) == 0 ) { stackMapFrameOffset = currentOffset + 2 ; stackMapTableEndOffset = currentOffset + attributeLength ; compressedFrames = false ; } // IMPORTANT! Here we assume that the frames are ordered, as in the StackMapTable attribute, // although this is not guaranteed by the attribute format. This allows an incremental // extraction of the labels corresponding to this attribute (see the comment above for the // StackMapTable attribute). } else { Attribute attribute = readAttribute ( context . attributePrototypes , attributeName , currentOffset , attributeLength , charBuffer , codeOffset , labels ) ; attribute . nextAttribute = attributes ; attributes = attribute ; } currentOffset += attributeLength ; } // Initialize the context fields related to stack map frames, and generate the first // (implicit) stack map frame, if needed. final boolean expandFrames = ( context . parsingOptions & EXPAND_FRAMES ) != 0 ; if ( stackMapFrameOffset != 0 ) { // The bytecode offset of the first explicit frame is not offset_delta + 1 but only // offset_delta. Setting the implicit frame offset to -1 allows us to use of the // "offset_delta + 1" rule in all cases. context . currentFrameOffset = - 1 ; context . currentFrameType = 0 ; context . currentFrameLocalCount = 0 ; context . currentFrameLocalCountDelta = 0 ; context . currentFrameLocalTypes = new Object [ maxLocals ] ; context . currentFrameStackCount = 0 ; context . currentFrameStackTypes = new Object [ maxStack ] ; if ( expandFrames ) { computeImplicitFrame ( context ) ; } // Find the labels for UNINITIALIZED frame types. Instead of decoding each element of the // stack map table, we look for 3 consecutive bytes that "look like" an UNINITIALIZED type // (tag ITEM_Uninitialized, offset within bytecode bounds, NEW instruction at this offset). // We may find false positives (i.e. not real UNINITIALIZED types), but this should be rare, // and the only consequence will be the creation of an unneeded label. This is better than // creating a label for each NEW instruction, and faster than fully decoding the whole stack // map table. for ( int offset = stackMapFrameOffset ; offset < stackMapTableEndOffset - 2 ; ++ offset ) { if ( classFileBuffer [ offset ] == Frame . ITEM_UNINITIALIZED ) { int potentialBytecodeOffset = readUnsignedShort ( offset + 1 ) ; if ( potentialBytecodeOffset >= 0 && potentialBytecodeOffset < codeLength && ( classFileBuffer [ bytecodeStartOffset + potentialBytecodeOffset ] & 0xFF ) == Opcodes . NEW ) { createLabel ( potentialBytecodeOffset , labels ) ; } } } } if ( expandFrames && ( context . parsingOptions & EXPAND_ASM_INSNS ) != 0 ) { // Expanding the ASM specific instructions can introduce F_INSERT frames, even if the method // does not currently have any frame. These inserted frames must be computed by simulating the // effect of the bytecode instructions, one by one, starting from the implicit first frame. // For this, MethodWriter needs to know maxLocals before the first instruction is visited. To // ensure this, we visit the implicit first frame here (passing only maxLocals - the rest is // computed in MethodWriter). methodVisitor . visitFrame ( Opcodes . F_NEW , maxLocals , null , 0 , null ) ; } // Visit the bytecode instructions. First, introduce state variables for the incremental parsing // of the type annotations. // Index of the next runtime visible type annotation to read (in the // visibleTypeAnnotationOffsets array). int currentVisibleTypeAnnotationIndex = 0 ; // The bytecode offset of the next runtime visible type annotation to read, or -1. int currentVisibleTypeAnnotationBytecodeOffset = getTypeAnnotationBytecodeOffset ( visibleTypeAnnotationOffsets , 0 ) ; // Index of the next runtime invisible type annotation to read (in the // invisibleTypeAnnotationOffsets array). int currentInvisibleTypeAnnotationIndex = 0 ; // The bytecode offset of the next runtime invisible type annotation to read, or -1. int currentInvisibleTypeAnnotationBytecodeOffset = getTypeAnnotationBytecodeOffset ( invisibleTypeAnnotationOffsets , 0 ) ; // Whether a F_INSERT stack map frame must be inserted before the current instruction. boolean insertFrame = false ; // The delta to subtract from a goto_w or jsr_w opcode to get the corresponding goto or jsr // opcode, or 0 if goto_w and jsr_w must be left unchanged (i.e. when expanding ASM specific // instructions). final int wideJumpOpcodeDelta = ( context . parsingOptions & EXPAND_ASM_INSNS ) == 0 ? Constants . WIDE_JUMP_OPCODE_DELTA : 0 ; currentOffset = bytecodeStartOffset ; while ( currentOffset < bytecodeEndOffset ) { final int currentBytecodeOffset = currentOffset - bytecodeStartOffset ; // Visit the label and the line number(s) for this bytecode offset, if any. Label currentLabel = labels [ currentBytecodeOffset ] ; if ( currentLabel != null ) { currentLabel . accept ( methodVisitor , ( context . parsingOptions & SKIP_DEBUG ) == 0 ) ; } // Visit the stack map frame for this bytecode offset, if any. while ( stackMapFrameOffset != 0 && ( context . currentFrameOffset == currentBytecodeOffset || context . currentFrameOffset == - 1 ) ) { // If there is a stack map frame for this offset, make methodVisitor visit it, and read the // next stack map frame if there is one. if ( context . currentFrameOffset != - 1 ) { if ( ! compressedFrames || expandFrames ) { methodVisitor . visitFrame ( Opcodes . F_NEW , context . currentFrameLocalCount , context . currentFrameLocalTypes , context . currentFrameStackCount , context . currentFrameStackTypes ) ; } else { methodVisitor . visitFrame ( context . currentFrameType , context . currentFrameLocalCountDelta , context . currentFrameLocalTypes , context . currentFrameStackCount , context . currentFrameStackTypes ) ; } // Since there is already a stack map frame for this bytecode offset, there is no need to // insert a new one. insertFrame = false ; } if ( stackMapFrameOffset < stackMapTableEndOffset ) { stackMapFrameOffset = readStackMapFrame ( stackMapFrameOffset , compressedFrames , expandFrames , context ) ; } else { stackMapFrameOffset = 0 ; } } // Insert a stack map frame for this bytecode offset, if requested by setting insertFrame to // true during the previous iteration. The actual frame content is computed in MethodWriter. if ( insertFrame ) { if ( ( context . parsingOptions & EXPAND_FRAMES ) != 0 ) { methodVisitor . visitFrame ( Constants . F_INSERT , 0 , null , 0 , null ) ; } insertFrame = false ; } // Visit the instruction at this bytecode offset. int opcode = classFileBuffer [ currentOffset ] & 0xFF ; switch ( opcode ) { case Constants . NOP : case Constants . ACONST_NULL : case Constants . ICONST_M1 : case Constants . ICONST_0 : case Constants . ICONST_1 : case Constants . ICONST_2 : case Constants . ICONST_3 : case Constants . ICONST_4 : case Constants . ICONST_5 : case Constants . LCONST_0 : case Constants . LCONST_1 : case Constants . FCONST_0 : case Constants . FCONST_1 : case Constants . FCONST_2 : case Constants . DCONST_0 : case Constants . DCONST_1 : case Constants . IALOAD : case Constants . LALOAD : case Constants . FALOAD : case Constants . DALOAD : case Constants . AALOAD : case Constants . BALOAD : case Constants . CALOAD : case Constants . SALOAD : case Constants . IASTORE : case Constants . LASTORE : case Constants . FASTORE : case Constants . DASTORE : case Constants . AASTORE : case Constants . BASTORE : case Constants . CASTORE : case Constants . SASTORE : case Constants . POP : case Constants . POP2 : case Constants . DUP : case Constants . DUP_X1 : case Constants . DUP_X2 : case Constants . DUP2 : case Constants . DUP2_X1 : case Constants . DUP2_X2 : case Constants . SWAP : case Constants . IADD : case Constants . LADD : case Constants . FADD : case Constants . DADD : case Constants . ISUB : case Constants . LSUB : case Constants . FSUB : case Constants . DSUB : case Constants . IMUL : case Constants . LMUL : case Constants . FMUL : case Constants . DMUL : case Constants . IDIV : case Constants . LDIV : case Constants . FDIV : case Constants . DDIV : case Constants . IREM : case Constants . LREM : case Constants . FREM : case Constants . DREM : case Constants . INEG : case Constants . LNEG : case Constants . FNEG : case Constants . DNEG : case Constants . ISHL : case Constants . LSHL : case Constants . ISHR : case Constants . LSHR : case Constants . IUSHR : case Constants . LUSHR : case Constants . IAND : case Constants . LAND : case Constants . IOR : case Constants . LOR : case Constants . IXOR : case Constants . LXOR : case Constants . I2L : case Constants . I2F : case Constants . I2D : case Constants . L2I : case Constants . L2F : case Constants . L2D : case Constants . F2I : case Constants . F2L : case Constants . F2D : case Constants . D2I : case Constants . D2L : case Constants . D2F : case Constants . I2B : case Constants . I2C : case Constants . I2S : case Constants . LCMP : case Constants . FCMPL : case Constants . FCMPG : case Constants . DCMPL : case Constants . DCMPG : case Constants . IRETURN : case Constants . LRETURN : case Constants . FRETURN : case Constants . DRETURN : case Constants . ARETURN : case Constants . RETURN : case Constants . ARRAYLENGTH : case Constants . ATHROW : case Constants . MONITORENTER : case Constants . MONITOREXIT : methodVisitor . visitInsn ( opcode ) ; currentOffset += 1 ; break ; case Constants . ILOAD_0 : case Constants . ILOAD_1 : case Constants . ILOAD_2 : case Constants . ILOAD_3 : case Constants . LLOAD_0 : case Constants . LLOAD_1 : case Constants . LLOAD_2 : case Constants . LLOAD_3 : case Constants . FLOAD_0 : case Constants . FLOAD_1 : case Constants . FLOAD_2 : case Constants . FLOAD_3 : case Constants . DLOAD_0 : case Constants . DLOAD_1 : case Constants . DLOAD_2 : case Constants . DLOAD_3 : case Constants . ALOAD_0 : case Constants . ALOAD_1 : case Constants . ALOAD_2 : case Constants . ALOAD_3 : opcode -= Constants . ILOAD_0 ; methodVisitor . visitVarInsn ( Opcodes . ILOAD + ( opcode >> 2 ) , opcode & 0x3 ) ; currentOffset += 1 ; break ; case Constants . ISTORE_0 : case Constants . ISTORE_1 : case Constants . ISTORE_2 : case Constants . ISTORE_3 : case Constants . LSTORE_0 : case Constants . LSTORE_1 : case Constants . LSTORE_2 : case Constants . LSTORE_3 : case Constants . FSTORE_0 : case Constants . FSTORE_1 : case Constants . FSTORE_2 : case Constants . FSTORE_3 : case Constants . DSTORE_0 : case Constants . DSTORE_1 : case Constants . DSTORE_2 : case Constants . DSTORE_3 : case Constants . ASTORE_0 : case Constants . ASTORE_1 : case Constants . ASTORE_2 : case Constants . ASTORE_3 : opcode -= Constants . ISTORE_0 ; methodVisitor . visitVarInsn ( Opcodes . ISTORE + ( opcode >> 2 ) , opcode & 0x3 ) ; currentOffset += 1 ; break ; case Constants . IFEQ : case Constants . IFNE : case Constants . IFLT : case Constants . IFGE : case Constants . IFGT : case Constants . IFLE : case Constants . IF_ICMPEQ : case Constants . IF_ICMPNE : case Constants . IF_ICMPLT : case Constants . IF_ICMPGE : case Constants . IF_ICMPGT : case Constants . IF_ICMPLE : case Constants . IF_ACMPEQ : case Constants . IF_ACMPNE : case Constants . GOTO : case Constants . JSR : case Constants . IFNULL : case Constants . IFNONNULL : methodVisitor . visitJumpInsn ( opcode , labels [ currentBytecodeOffset + readShort ( currentOffset + 1 ) ] ) ; currentOffset += 3 ; break ; case Constants . GOTO_W : case Constants . JSR_W : methodVisitor . visitJumpInsn ( opcode - wideJumpOpcodeDelta , labels [ currentBytecodeOffset + readInt ( currentOffset + 1 ) ] ) ; currentOffset += 5 ; break ; case Constants . ASM_IFEQ : case Constants . ASM_IFNE : case Constants . ASM_IFLT : case Constants . ASM_IFGE : case Constants . ASM_IFGT : case Constants . ASM_IFLE : case Constants . ASM_IF_ICMPEQ : case Constants . ASM_IF_ICMPNE : case Constants . ASM_IF_ICMPLT : case Constants . ASM_IF_ICMPGE : case Constants . ASM_IF_ICMPGT : case Constants . ASM_IF_ICMPLE : case Constants . ASM_IF_ACMPEQ : case Constants . ASM_IF_ACMPNE : case Constants . ASM_GOTO : case Constants . ASM_JSR : case Constants . ASM_IFNULL : case Constants . ASM_IFNONNULL : { // A forward jump with an offset > 32767. In this case we automatically replace ASM_GOTO // with GOTO_W, ASM_JSR with JSR_W and ASM_IFxxx <l> with IFNOTxxx <L> GOTO_W <l> L:..., // where IFNOTxxx is the "opposite" opcode of ASMS_IFxxx (e.g. IFNE for ASM_IFEQ) and // where <L> designates the instruction just after the GOTO_W. // First, change the ASM specific opcodes ASM_IFEQ ... ASM_JSR, ASM_IFNULL and // ASM_IFNONNULL to IFEQ ... JSR, IFNULL and IFNONNULL. opcode = opcode < Constants . ASM_IFNULL ? opcode - Constants . ASM_OPCODE_DELTA : opcode - Constants . ASM_IFNULL_OPCODE_DELTA ; Label target = labels [ currentBytecodeOffset + readUnsignedShort ( currentOffset + 1 ) ] ; if ( opcode == Opcodes . GOTO || opcode == Opcodes . JSR ) { // Replace GOTO with GOTO_W and JSR with JSR_W. methodVisitor . visitJumpInsn ( opcode + Constants . WIDE_JUMP_OPCODE_DELTA , target ) ; } else { // Compute the "opposite" of opcode. This can be done by flipping the least // significant bit for IFNULL and IFNONNULL, and similarly for IFEQ ... IF_ACMPEQ // (with a pre and post offset by 1). opcode = opcode < Opcodes . GOTO ? ( ( opcode + 1 ) ^ 1 ) - 1 : opcode ^ 1 ; Label endif = createLabel ( currentBytecodeOffset + 3 , labels ) ; methodVisitor . visitJumpInsn ( opcode , endif ) ; methodVisitor . visitJumpInsn ( Constants . GOTO_W , target ) ; // endif designates the instruction just after GOTO_W, and is visited as part of the // next instruction. Since it is a jump target, we need to insert a frame here. insertFrame = true ; } currentOffset += 3 ; break ; } case Constants . ASM_GOTO_W : { // Replace ASM_GOTO_W with GOTO_W. methodVisitor . visitJumpInsn ( Constants . GOTO_W , labels [ currentBytecodeOffset + readInt ( currentOffset + 1 ) ] ) ; // The instruction just after is a jump target (because ASM_GOTO_W is used in patterns // IFNOTxxx <L> ASM_GOTO_W <l> L:..., see MethodWriter), so we need to insert a frame // here. insertFrame = true ; currentOffset += 5 ; break ; } case Constants . WIDE : opcode = classFileBuffer [ currentOffset + 1 ] & 0xFF ; if ( opcode == Opcodes . IINC ) { methodVisitor . visitIincInsn ( readUnsignedShort ( currentOffset + 2 ) , readShort ( currentOffset + 4 ) ) ; currentOffset += 6 ; } else { methodVisitor . visitVarInsn ( opcode , readUnsignedShort ( currentOffset + 2 ) ) ; currentOffset += 4 ; } break ; case Constants . TABLESWITCH : { // Skip 0 to 3 padding bytes. currentOffset += 4 - ( currentBytecodeOffset & 3 ) ; // Read the instruction. Label defaultLabel = labels [ currentBytecodeOffset + readInt ( currentOffset ) ] ; int low = readInt ( currentOffset + 4 ) ; int high = readInt ( currentOffset + 8 ) ; currentOffset += 12 ; Label [ ] table = new Label [ high - low + 1 ] ; for ( int i = 0 ; i < table . length ; ++ i ) { table [ i ] = labels [ currentBytecodeOffset + readInt ( currentOffset ) ] ; currentOffset += 4 ; } methodVisitor . visitTableSwitchInsn ( low , high , defaultLabel , table ) ; break ; } case Constants . LOOKUPSWITCH : { // Skip 0 to 3 padding bytes. currentOffset += 4 - ( currentBytecodeOffset & 3 ) ; // Read the instruction. Label defaultLabel = labels [ currentBytecodeOffset + readInt ( currentOffset ) ] ; int numPairs = readInt ( currentOffset + 4 ) ; currentOffset += 8 ; int [ ] keys = new int [ numPairs ] ; Label [ ] values = new Label [ numPairs ] ; for ( int i = 0 ; i < numPairs ; ++ i ) { keys [ i ] = readInt ( currentOffset ) ; values [ i ] = labels [ currentBytecodeOffset + readInt ( currentOffset + 4 ) ] ; currentOffset += 8 ; } methodVisitor . visitLookupSwitchInsn ( defaultLabel , keys , values ) ; break ; } case Constants . ILOAD : case Constants . LLOAD : case Constants . FLOAD : case Constants . DLOAD : case Constants . ALOAD : case Constants . ISTORE : case Constants . LSTORE : case Constants . FSTORE : case Constants . DSTORE : case Constants . ASTORE : case Constants . RET : methodVisitor . visitVarInsn ( opcode , classFileBuffer [ currentOffset + 1 ] & 0xFF ) ; currentOffset += 2 ; break ; case Constants . BIPUSH : case Constants . NEWARRAY : methodVisitor . visitIntInsn ( opcode , classFileBuffer [ currentOffset + 1 ] ) ; currentOffset += 2 ; break ; case Constants . SIPUSH : methodVisitor . visitIntInsn ( opcode , readShort ( currentOffset + 1 ) ) ; currentOffset += 3 ; break ; case Constants . LDC : methodVisitor . visitLdcInsn ( readConst ( classFileBuffer [ currentOffset + 1 ] & 0xFF , charBuffer ) ) ; currentOffset += 2 ; break ; case Constants . LDC_W : case Constants . LDC2_W : methodVisitor . visitLdcInsn ( readConst ( readUnsignedShort ( currentOffset + 1 ) , charBuffer ) ) ; currentOffset += 3 ; break ; case Constants . GETSTATIC : case Constants . PUTSTATIC : case Constants . GETFIELD : case Constants . PUTFIELD : case Constants . INVOKEVIRTUAL : case Constants . INVOKESPECIAL : case Constants . INVOKESTATIC : case Constants . INVOKEINTERFACE : { int cpInfoOffset = cpInfoOffsets [ readUnsignedShort ( currentOffset + 1 ) ] ; int nameAndTypeCpInfoOffset = cpInfoOffsets [ readUnsignedShort ( cpInfoOffset + 2 ) ] ; String owner = readClass ( cpInfoOffset , charBuffer ) ; String name = readUTF8 ( nameAndTypeCpInfoOffset , charBuffer ) ; String descriptor = readUTF8 ( nameAndTypeCpInfoOffset + 2 , charBuffer ) ; if ( opcode < Opcodes . INVOKEVIRTUAL ) { methodVisitor . visitFieldInsn ( opcode , owner , name , descriptor ) ; } else { boolean isInterface = classFileBuffer [ cpInfoOffset - 1 ] == Symbol . CONSTANT_INTERFACE_METHODREF_TAG ; methodVisitor . visitMethodInsn ( opcode , owner , name , descriptor , isInterface ) ; } if ( opcode == Opcodes . INVOKEINTERFACE ) { currentOffset += 5 ; } else { currentOffset += 3 ; } break ; } case Constants . INVOKEDYNAMIC : { int cpInfoOffset = cpInfoOffsets [ readUnsignedShort ( currentOffset + 1 ) ] ; int nameAndTypeCpInfoOffset = cpInfoOffsets [ readUnsignedShort ( cpInfoOffset + 2 ) ] ; String name = readUTF8 ( nameAndTypeCpInfoOffset , charBuffer ) ; String descriptor = readUTF8 ( nameAndTypeCpInfoOffset + 2 , charBuffer ) ; int bootstrapMethodOffset = bootstrapMethodOffsets [ readUnsignedShort ( cpInfoOffset ) ] ; Handle handle = ( Handle ) readConst ( readUnsignedShort ( bootstrapMethodOffset ) , charBuffer ) ; Object [ ] bootstrapMethodArguments = new Object [ readUnsignedShort ( bootstrapMethodOffset + 2 ) ] ; bootstrapMethodOffset += 4 ; for ( int i = 0 ; i < bootstrapMethodArguments . length ; i ++ ) { bootstrapMethodArguments [ i ] = readConst ( readUnsignedShort ( bootstrapMethodOffset ) , charBuffer ) ; bootstrapMethodOffset += 2 ; } methodVisitor . visitInvokeDynamicInsn ( name , descriptor , handle , bootstrapMethodArguments ) ; currentOffset += 5 ; break ; } case Constants . NEW : case Constants . ANEWARRAY : case Constants . CHECKCAST : case Constants . INSTANCEOF : methodVisitor . visitTypeInsn ( opcode , readClass ( currentOffset + 1 , charBuffer ) ) ; currentOffset += 3 ; break ; case Constants . IINC : methodVisitor . visitIincInsn ( classFileBuffer [ currentOffset + 1 ] & 0xFF , classFileBuffer [ currentOffset + 2 ] ) ; currentOffset += 3 ; break ; case Constants . MULTIANEWARRAY : methodVisitor . visitMultiANewArrayInsn ( readClass ( currentOffset + 1 , charBuffer ) , classFileBuffer [ currentOffset + 3 ] & 0xFF ) ; currentOffset += 4 ; break ; default : throw new AssertionError ( ) ; } // Visit the runtime visible instruction annotations, if any. while ( visibleTypeAnnotationOffsets != null && currentVisibleTypeAnnotationIndex < visibleTypeAnnotationOffsets . length && currentVisibleTypeAnnotationBytecodeOffset <= currentBytecodeOffset ) { if ( currentVisibleTypeAnnotationBytecodeOffset == currentBytecodeOffset ) { // Parse the target_type, target_info and target_path fields. int currentAnnotationOffset = readTypeAnnotationTarget ( context , visibleTypeAnnotationOffsets [ currentVisibleTypeAnnotationIndex ] ) ; // Parse the type_index field. String annotationDescriptor = readUTF8 ( currentAnnotationOffset , charBuffer ) ; currentAnnotationOffset += 2 ; // Parse num_element_value_pairs and element_value_pairs and visit these values. readElementValues ( methodVisitor . visitInsnAnnotation ( context . currentTypeAnnotationTarget , context . currentTypeAnnotationTargetPath , annotationDescriptor , /* visible = */ true ) , currentAnnotationOffset , /* named = */ true , charBuffer ) ; } currentVisibleTypeAnnotationBytecodeOffset = getTypeAnnotationBytecodeOffset ( visibleTypeAnnotationOffsets , ++ currentVisibleTypeAnnotationIndex ) ; } // Visit the runtime invisible instruction annotations, if any. while ( invisibleTypeAnnotationOffsets != null && currentInvisibleTypeAnnotationIndex < invisibleTypeAnnotationOffsets . length && currentInvisibleTypeAnnotationBytecodeOffset <= currentBytecodeOffset ) { if ( currentInvisibleTypeAnnotationBytecodeOffset == currentBytecodeOffset ) { // Parse the target_type, target_info and target_path fields. int currentAnnotationOffset = readTypeAnnotationTarget ( context , invisibleTypeAnnotationOffsets [ currentInvisibleTypeAnnotationIndex ] ) ; // Parse the type_index field. String annotationDescriptor = readUTF8 ( currentAnnotationOffset , charBuffer ) ; currentAnnotationOffset += 2 ; // Parse num_element_value_pairs and element_value_pairs and visit these values. readElementValues ( methodVisitor . visitInsnAnnotation ( context . currentTypeAnnotationTarget , context . currentTypeAnnotationTargetPath , annotationDescriptor , /* visible = */ false ) , currentAnnotationOffset , /* named = */ true , charBuffer ) ; } currentInvisibleTypeAnnotationBytecodeOffset = getTypeAnnotationBytecodeOffset ( invisibleTypeAnnotationOffsets , ++ currentInvisibleTypeAnnotationIndex ) ; } } if ( labels [ codeLength ] != null ) { methodVisitor . visitLabel ( labels [ codeLength ] ) ; } // Visit LocalVariableTable and LocalVariableTypeTable attributes. if ( localVariableTableOffset != 0 && ( context . parsingOptions & SKIP_DEBUG ) == 0 ) { // The (start_pc, index, signature_index) fields of each entry of the LocalVariableTypeTable. int [ ] typeTable = null ; if ( localVariableTypeTableOffset != 0 ) { typeTable = new int [ readUnsignedShort ( localVariableTypeTableOffset ) * 3 ] ; currentOffset = localVariableTypeTableOffset + 2 ; int typeTableIndex = typeTable . length ; while ( typeTableIndex > 0 ) { // Store the offset of 'signature_index', and the value of 'index' and 'start_pc'. typeTable [ -- typeTableIndex ] = currentOffset + 6 ; typeTable [ -- typeTableIndex ] = readUnsignedShort ( currentOffset + 8 ) ; typeTable [ -- typeTableIndex ] = readUnsignedShort ( currentOffset ) ; currentOffset += 10 ; } } int localVariableTableLength = readUnsignedShort ( localVariableTableOffset ) ; currentOffset = localVariableTableOffset + 2 ; while ( localVariableTableLength -- > 0 ) { int startPc = readUnsignedShort ( currentOffset ) ; int length = readUnsignedShort ( currentOffset + 2 ) ; String name = readUTF8 ( currentOffset + 4 , charBuffer ) ; String descriptor = readUTF8 ( currentOffset + 6 , charBuffer ) ; int index = readUnsignedShort ( currentOffset + 8 ) ; currentOffset += 10 ; String signature = null ; if ( typeTable != null ) { for ( int i = 0 ; i < typeTable . length ; i += 3 ) { if ( typeTable [ i ] == startPc && typeTable [ i + 1 ] == index ) { signature = readUTF8 ( typeTable [ i + 2 ] , charBuffer ) ; break ; } } } methodVisitor . visitLocalVariable ( name , descriptor , signature , labels [ startPc ] , labels [ startPc + length ] , index ) ; } } // Visit the local variable type annotations of the RuntimeVisibleTypeAnnotations attribute. if ( visibleTypeAnnotationOffsets != null ) { for ( int typeAnnotationOffset : visibleTypeAnnotationOffsets ) { int targetType = readByte ( typeAnnotationOffset ) ; if ( targetType == TypeReference . LOCAL_VARIABLE || targetType == TypeReference . RESOURCE_VARIABLE ) { // Parse the target_type, target_info and target_path fields. currentOffset = readTypeAnnotationTarget ( context , typeAnnotationOffset ) ; // Parse the type_index field. String annotationDescriptor = readUTF8 ( currentOffset , charBuffer ) ; currentOffset += 2 ; // Parse num_element_value_pairs and element_value_pairs and visit these values. readElementValues ( methodVisitor . visitLocalVariableAnnotation ( context . currentTypeAnnotationTarget , context . currentTypeAnnotationTargetPath , context . currentLocalVariableAnnotationRangeStarts , context . currentLocalVariableAnnotationRangeEnds , context . currentLocalVariableAnnotationRangeIndices , annotationDescriptor , /* visible = */ true ) , currentOffset , /* named = */ true , charBuffer ) ; } } } // Visit the local variable type annotations of the RuntimeInvisibleTypeAnnotations attribute. if ( invisibleTypeAnnotationOffsets != null ) { for ( int typeAnnotationOffset : invisibleTypeAnnotationOffsets ) { int targetType = readByte ( typeAnnotationOffset ) ; if ( targetType == TypeReference . LOCAL_VARIABLE || targetType == TypeReference . RESOURCE_VARIABLE ) { // Parse the target_type, target_info and target_path fields. currentOffset = readTypeAnnotationTarget ( context , typeAnnotationOffset ) ; // Parse the type_index field. String annotationDescriptor = readUTF8 ( currentOffset , charBuffer ) ; currentOffset += 2 ; // Parse num_element_value_pairs and element_value_pairs and visit these values. readElementValues ( methodVisitor . visitLocalVariableAnnotation ( context . currentTypeAnnotationTarget , context . currentTypeAnnotationTargetPath , context . currentLocalVariableAnnotationRangeStarts , context . currentLocalVariableAnnotationRangeEnds , context . currentLocalVariableAnnotationRangeIndices , annotationDescriptor , /* visible = */ false ) , currentOffset , /* named = */ true , charBuffer ) ; } } } // Visit the non standard attributes. while ( attributes != null ) { // Copy and reset the nextAttribute field so that it can also be used in MethodWriter. Attribute nextAttribute = attributes . nextAttribute ; attributes . nextAttribute = null ; methodVisitor . visitAttribute ( attributes ) ; attributes = nextAttribute ; } // Visit the max stack and max locals values. methodVisitor . visitMaxs ( maxStack , maxLocals ) ; }
Reads a JVMS 'Code' attribute and makes the given visitor visit it.

@param methodVisitor the visitor that must visit the Code attribute.
@param context information about the class being parsed.
@param codeOffset the start offset in {@link #b} of the Code attribute, excluding its
attribute_name_index and attribute_length fields.
Reads a JVMS Code attribute and makes the given visitor visit it .