jodd-proxetta/src/main/java/jodd/asm7/ClassReader.java
private int[] readTypeAnnotations(
      final MethodVisitor methodVisitor,
      final Context context,
      final int runtimeTypeAnnotationsOffset,
      final boolean visible) {
    char[] charBuffer = context.charBuffer;
    int currentOffset = runtimeTypeAnnotationsOffset;
    // Read the num_annotations field and create an array to store the type_annotation offsets.
    int[] typeAnnotationsOffsets = new int[readUnsignedShort(currentOffset)];
    currentOffset += 2;
    // Parse the 'annotations' array field.
    for (int i = 0; i < typeAnnotationsOffsets.length; ++i) {
      typeAnnotationsOffsets[i] = currentOffset;
      // Parse the type_annotation's target_type and the target_info fields. The size of the
      // target_info field depends on the value of target_type.
      int targetType = readInt(currentOffset);
      switch (targetType >>> 24) {
        case TypeReference.LOCAL_VARIABLE:
        case TypeReference.RESOURCE_VARIABLE:
          // A localvar_target has a variable size, which depends on the value of their table_length
          // field. It also references bytecode offsets, for which we need labels.
          int tableLength = readUnsignedShort(currentOffset + 1);
          currentOffset += 3;
          while (tableLength-- > 0) {
            int startPc = readUnsignedShort(currentOffset);
            int length = readUnsignedShort(currentOffset + 2);
            // Skip the index field (2 bytes).
            currentOffset += 6;
            createLabel(startPc, context.currentMethodLabels);
            createLabel(startPc + length, context.currentMethodLabels);
          }
          break;
        case TypeReference.CAST:
        case TypeReference.CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT:
        case TypeReference.METHOD_INVOCATION_TYPE_ARGUMENT:
        case TypeReference.CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT:
        case TypeReference.METHOD_REFERENCE_TYPE_ARGUMENT:
          currentOffset += 4;
          break;
        case TypeReference.CLASS_EXTENDS:
        case TypeReference.CLASS_TYPE_PARAMETER_BOUND:
        case TypeReference.METHOD_TYPE_PARAMETER_BOUND:
        case TypeReference.THROWS:
        case TypeReference.EXCEPTION_PARAMETER:
        case TypeReference.INSTANCEOF:
        case TypeReference.NEW:
        case TypeReference.CONSTRUCTOR_REFERENCE:
        case TypeReference.METHOD_REFERENCE:
          currentOffset += 3;
          break;
        case TypeReference.CLASS_TYPE_PARAMETER:
        case TypeReference.METHOD_TYPE_PARAMETER:
        case TypeReference.METHOD_FORMAL_PARAMETER:
        case TypeReference.FIELD:
        case TypeReference.METHOD_RETURN:
        case TypeReference.METHOD_RECEIVER:
        default:
          // TypeReference type which can't be used in Code attribute, or which is unknown.
          throw new IllegalArgumentException();
      }
      // Parse the rest of the type_annotation structure, starting with the target_path structure
      // (whose size depends on its path_length field).
      int pathLength = readByte(currentOffset);
      if ((targetType >>> 24) == TypeReference.EXCEPTION_PARAMETER) {
        // Parse the target_path structure and create a corresponding TypePath.
        TypePath path = pathLength == 0 ? null : new TypePath(b, currentOffset);
        currentOffset += 1 + 2 * pathLength;
        // 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.
        currentOffset =
            readElementValues(
                methodVisitor.visitTryCatchAnnotation(
                    targetType & 0xFFFFFF00, path, annotationDescriptor, visible),
                currentOffset,
                /* named = */ true,
                charBuffer);
      } else {
        // We don't want to visit the other target_type annotations, so we just skip them (which
        // requires some parsing because the element_value_pairs array has a variable size). First,
        // skip the target_path structure:
        currentOffset += 3 + 2 * pathLength;
        // Then skip the num_element_value_pairs and element_value_pairs fields (by reading them
        // with a null AnnotationVisitor).
        currentOffset =
            readElementValues(
                /* annotationVisitor = */ null, currentOffset, /* named = */ true, charBuffer);
      }
    }
    return typeAnnotationsOffsets;
  }
private int[] readTypeAnnotations(
      final MethodVisitor methodVisitor,
      final Context context,
      final int runtimeTypeAnnotationsOffset,
      final boolean visible) {
    char[] charBuffer = context.charBuffer;
    int currentOffset = runtimeTypeAnnotationsOffset;
    // Read the num_annotations field and create an array to store the type_annotation offsets.
    int[] typeAnnotationsOffsets = new int[readUnsignedShort(currentOffset)];
    currentOffset += 2;
    // Parse the 'annotations' array field.
    for (int i = 0; i < typeAnnotationsOffsets.length; ++i) {
      typeAnnotationsOffsets[i] = currentOffset;
      // Parse the type_annotation's target_type and the target_info fields. The size of the
      // target_info field depends on the value of target_type.
      int targetType = readInt(currentOffset);
      switch (targetType >>> 24) {
        case TypeReference.LOCAL_VARIABLE:
        case TypeReference.RESOURCE_VARIABLE:
          // A localvar_target has a variable size, which depends on the value of their table_length
          // field. It also references bytecode offsets, for which we need labels.
          int tableLength = readUnsignedShort(currentOffset + 1);
          currentOffset += 3;
          while (tableLength-- > 0) {
            int startPc = readUnsignedShort(currentOffset);
            int length = readUnsignedShort(currentOffset + 2);
            // Skip the index field (2 bytes).
            currentOffset += 6;
            createLabel(startPc, context.currentMethodLabels);
            createLabel(startPc + length, context.currentMethodLabels);
          }
          break;
        case TypeReference.CAST:
        case TypeReference.CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT:
        case TypeReference.METHOD_INVOCATION_TYPE_ARGUMENT:
        case TypeReference.CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT:
        case TypeReference.METHOD_REFERENCE_TYPE_ARGUMENT:
          currentOffset += 4;
          break;
        case TypeReference.CLASS_EXTENDS:
        case TypeReference.CLASS_TYPE_PARAMETER_BOUND:
        case TypeReference.METHOD_TYPE_PARAMETER_BOUND:
        case TypeReference.THROWS:
        case TypeReference.EXCEPTION_PARAMETER:
        case TypeReference.INSTANCEOF:
        case TypeReference.NEW:
        case TypeReference.CONSTRUCTOR_REFERENCE:
        case TypeReference.METHOD_REFERENCE:
          currentOffset += 3;
          break;
        case TypeReference.CLASS_TYPE_PARAMETER:
        case TypeReference.METHOD_TYPE_PARAMETER:
        case TypeReference.METHOD_FORMAL_PARAMETER:
        case TypeReference.FIELD:
        case TypeReference.METHOD_RETURN:
        case TypeReference.METHOD_RECEIVER:
        default:
          // TypeReference type which can't be used in Code attribute, or which is unknown.
          throw new IllegalArgumentException();
      }
      // Parse the rest of the type_annotation structure, starting with the target_path structure
      // (whose size depends on its path_length field).
      int pathLength = readByte(currentOffset);
      if ((targetType >>> 24) == TypeReference.EXCEPTION_PARAMETER) {
        // Parse the target_path structure and create a corresponding TypePath.
        TypePath path = pathLength == 0 ? null : new TypePath(b, currentOffset);
        currentOffset += 1 + 2 * pathLength;
        // 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.
        currentOffset =
            readElementValues(
                methodVisitor.visitTryCatchAnnotation(
                    targetType & 0xFFFFFF00, path, annotationDescriptor, visible),
                currentOffset,
                /* named = */ true,
                charBuffer);
      } else {
        // We don't want to visit the other target_type annotations, so we just skip them (which
        // requires some parsing because the element_value_pairs array has a variable size). First,
        // skip the target_path structure:
        currentOffset += 3 + 2 * pathLength;
        // Then skip the num_element_value_pairs and element_value_pairs fields (by reading them
        // with a null AnnotationVisitor).
        currentOffset =
            readElementValues(
                /* annotationVisitor = */ null, currentOffset, /* named = */ true, charBuffer);
      }
    }
    return typeAnnotationsOffsets;
  }
Parses a Runtime[In]VisibleTypeAnnotations attribute to find the offset of each type_annotation
entry it contains, to find the corresponding labels, and to visit the try catch block
annotations.

@param methodVisitor the method visitor to be used to visit the try catch block annotations.
@param context information about the class being parsed.
@param runtimeTypeAnnotationsOffset the start offset of a Runtime[In]VisibleTypeAnnotations
attribute, excluding the attribute_info's attribute_name_index and attribute_length fields.
@param visible true if the attribute to parse is a RuntimeVisibleTypeAnnotations attribute,
false it is a RuntimeInvisibleTypeAnnotations attribute.
@return the start offset of each entry of the Runtime[In]VisibleTypeAnnotations_attribute's
'annotations' array field.