docs: private void readMethods() throws IOException, ClassfileFormatException {
          // Methods
          final int methodCount = inputStreamOrByteBuffer.readUnsignedShort();
          for (int i = 0; i < methodCount; i++) {
              // Info on modifier flags: http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.6
              final int methodModifierFlags = inputStreamOrByteBuffer.readUnsignedShort();
              final boolean isPublicMethod = ((methodModifierFlags & 0x0001) == 0x0001);
              final boolean methodIsVisible = isPublicMethod || scanSpec.ignoreMethodVisibility;
  
              String methodName = null;
              String methodTypeDescriptor = null;
              String methodTypeSignature = null;
              // Always enable MethodInfo for annotations (this is how annotation constants are defined)
              final boolean enableMethodInfo = scanSpec.enableMethodInfo || isAnnotation;
              if (enableMethodInfo || isAnnotation) { // Annotations store defaults in method_info
                  final int methodNameCpIdx = inputStreamOrByteBuffer.readUnsignedShort();
                  methodName = getConstantPoolString(methodNameCpIdx);
                  final int methodTypeDescriptorCpIdx = inputStreamOrByteBuffer.readUnsignedShort();
                  methodTypeDescriptor = getConstantPoolString(methodTypeDescriptorCpIdx);
              } else {
                  inputStreamOrByteBuffer.skip(4); // name_index, descriptor_index
              }
              final int attributesCount = inputStreamOrByteBuffer.readUnsignedShort();
              String[] methodParameterNames = null;
              int[] methodParameterModifiers = null;
              AnnotationInfo[][] methodParameterAnnotations = null;
              AnnotationInfoList methodAnnotationInfo = null;
              boolean methodHasBody = false;
              if (!methodIsVisible || (!enableMethodInfo && !isAnnotation)) {
                  // Skip method attributes
                  for (int j = 0; j < attributesCount; j++) {
                      inputStreamOrByteBuffer.skip(2); // attribute_name_index
                      final int attributeLength = inputStreamOrByteBuffer.readInt();
                      inputStreamOrByteBuffer.skip(attributeLength);
                  }
              } else {
                  // Look for method annotations
                  for (int j = 0; j < attributesCount; j++) {
                      final int attributeNameCpIdx = inputStreamOrByteBuffer.readUnsignedShort();
                      final int attributeLength = inputStreamOrByteBuffer.readInt();
                      if (scanSpec.enableAnnotationInfo
                              && (constantPoolStringEquals(attributeNameCpIdx, "RuntimeVisibleAnnotations")
                                      || (!scanSpec.disableRuntimeInvisibleAnnotations && constantPoolStringEquals(
                                              attributeNameCpIdx, "RuntimeInvisibleAnnotations")))) {
                          final int methodAnnotationCount = inputStreamOrByteBuffer.readUnsignedShort();
                          if (methodAnnotationCount > 0) {
                              if (methodAnnotationInfo == null) {
                                  methodAnnotationInfo = new AnnotationInfoList(1);
                              }
                              for (int k = 0; k < methodAnnotationCount; k++) {
                                  final AnnotationInfo annotationInfo = readAnnotation();
                                  methodAnnotationInfo.add(annotationInfo);
                              }
                          }
                      } else if (scanSpec.enableAnnotationInfo
                              && (constantPoolStringEquals(attributeNameCpIdx, "RuntimeVisibleParameterAnnotations")
                                      || (!scanSpec.disableRuntimeInvisibleAnnotations && constantPoolStringEquals(
                                              attributeNameCpIdx, "RuntimeInvisibleParameterAnnotations")))) {
                          // Merge together runtime visible and runtime invisible annotations into a single array
                          // of annotations for each method parameter (runtime visible and runtime invisible
                          // annotations are given in separate attributes, so if both attributes are present,
                          // have to make the parameter annotation arrays larger when the second attribute is
                          // encountered).
                          final int numParams = inputStreamOrByteBuffer.readUnsignedByte();
                          if (methodParameterAnnotations == null) {
                              methodParameterAnnotations = new AnnotationInfo[numParams][];
                          } else if (methodParameterAnnotations.length != numParams) {
                              throw new ClassfileFormatException(
                                      "Mismatch in number of parameters between RuntimeVisibleParameterAnnotations "
                                              + "and RuntimeInvisibleParameterAnnotations");
                          }
                          for (int paramIdx = 0; paramIdx < numParams; paramIdx++) {
                              final int numAnnotations = inputStreamOrByteBuffer.readUnsignedShort();
                              if (numAnnotations > 0) {
                                  int annStartIdx = 0;
                                  if (methodParameterAnnotations[paramIdx] != null) {
                                      annStartIdx = methodParameterAnnotations[paramIdx].length;
                                      methodParameterAnnotations[paramIdx] = Arrays.copyOf(
                                              methodParameterAnnotations[paramIdx], annStartIdx + numAnnotations);
                                  } else {
                                      methodParameterAnnotations[paramIdx] = new AnnotationInfo[numAnnotations];
                                  }
                                  for (int annIdx = 0; annIdx < numAnnotations; annIdx++) {
                                      methodParameterAnnotations[paramIdx][annStartIdx + annIdx] = readAnnotation();
                                  }
                              } else if (methodParameterAnnotations[paramIdx] == null) {
                                  methodParameterAnnotations[paramIdx] = NO_ANNOTATIONS;
                              }
                          }
                      } else if (constantPoolStringEquals(attributeNameCpIdx, "MethodParameters")) {
                          // Read method parameters. For Java, these are only produced in JDK8+, and only if the
                          // commandline switch `-parameters` is provided at compiletime.
                          final int paramCount = inputStreamOrByteBuffer.readUnsignedByte();
                          methodParameterNames = new String[paramCount];
                          methodParameterModifiers = new int[paramCount];
                          for (int k = 0; k < paramCount; k++) {
                              final int cpIdx = inputStreamOrByteBuffer.readUnsignedShort();
                              // If the constant pool index is zero, then the parameter is unnamed => use null
                              methodParameterNames[k] = cpIdx == 0 ? null : getConstantPoolString(cpIdx);
                              methodParameterModifiers[k] = inputStreamOrByteBuffer.readUnsignedShort();
                          }
                      } else if (constantPoolStringEquals(attributeNameCpIdx, "Signature")) {
                          // Add type params to method type signature
                          methodTypeSignature = getConstantPoolString(inputStreamOrByteBuffer.readUnsignedShort());
                      } else if (constantPoolStringEquals(attributeNameCpIdx, "AnnotationDefault")) {
                          if (annotationParamDefaultValues == null) {
                              annotationParamDefaultValues = new AnnotationParameterValueList();
                          }
                          this.annotationParamDefaultValues.add(new AnnotationParameterValue(methodName,
                                  // Get annotation parameter default value
                                  readAnnotationElementValue()));
                      } else if (constantPoolStringEquals(attributeNameCpIdx, "Code")) {
                          methodHasBody = true;
                          inputStreamOrByteBuffer.skip(attributeLength);
                      } else {
                          inputStreamOrByteBuffer.skip(attributeLength);
                      }
                  }
                  // Create MethodInfo
                  if (enableMethodInfo) {
                      if (methodInfoList == null) {
                          methodInfoList = new MethodInfoList();
                      }
                      methodInfoList.add(new MethodInfo(className, methodName, methodAnnotationInfo,
                              methodModifierFlags, methodTypeDescriptor, methodTypeSignature, methodParameterNames,
                              methodParameterModifiers, methodParameterAnnotations, methodHasBody));
                  }
              }
          }
      }