private static MethodData collectMethodData()

in sources/java-incremental-compilation/jvm-inc-builder/src/com/intellij/tools/build/bazel/jvmIncBuilder/notNullVerification/NotNullVerifyingInstrumenter.java [89:228]


  private static MethodData collectMethodData(ClassReader reader, final Set<String> notNullAnnotations) {
    final MethodData result = new MethodData();
    reader.accept(new ClassVisitor(API_VERSION) {
      private boolean myEnum, myInner;

      @Override
      public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
        if (KOTLIN_METADATA_ANNOTATION_CLASS_DESCRIPTOR.equals(desc)) {
          result.myIsKotlinBytecode = true;
        }
        return super.visitAnnotation(desc, visible);
      }

      @Override
      public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
        super.visit(version, access, name, signature, superName, interfaces);
        result.myClassName = name;
        myEnum = (access & ACC_ENUM) != 0;
      }

      @Override
      public void visitInnerClass(String name, String outerName, String innerName, int access) {
        super.visitInnerClass(name, outerName, innerName, access);
        if (result.myClassName.equals(name)) {
          myInner = (access & ACC_STATIC) == 0;
        }
      }

      @Override
      public MethodVisitor visitMethod(int access, final String name, final String desc, String signature, String[] exceptions) {
        final Type[] args = Type.getArgumentTypes(desc);
        final boolean methodCanHaveNullability = isReferenceType(Type.getReturnType(desc));

        final Map<Integer, Integer> paramSlots = new LinkedHashMap<>(); // map: localVariableSlot -> methodParameterIndex
        int slotIndex = isStatic(access) ? 0 : 1;
        for (int paramIndex = 0; paramIndex < args.length; paramIndex++) {
          Type arg = args[paramIndex];
          paramSlots.put(slotIndex, paramIndex);
          slotIndex += arg.getSize();
        }

        final MethodInfo methodInfo = new MethodInfo();
        methodInfo.isStable = (access & (ACC_FINAL | ACC_STATIC | ACC_PRIVATE)) != 0;
        methodInfo.paramAnnotationOffset = !"<init>".equals(name) ? 0 : myEnum ? 2 : myInner ? 1 : 0;
        result.myMethodInfos.put(MethodData.key(name, desc), methodInfo);

        return new MethodVisitor(api) {
          private int myParamAnnotationOffset = methodInfo.paramAnnotationOffset;

          @Override
          public void visitAnnotableParameterCount(int parameterCount, boolean visible) {
            if (myParamAnnotationOffset != 0 && parameterCount == args.length) {
              myParamAnnotationOffset = 0;
            }
            super.visitAnnotableParameterCount(parameterCount, visible);
          }

          @Override
          public AnnotationVisitor visitParameterAnnotation(int parameter, String anno, boolean visible) {
            AnnotationVisitor base = super.visitParameterAnnotation(parameter, anno, visible);
            return checkParameterNullability(parameter + myParamAnnotationOffset, anno, base, false);
          }

          @Override
          public AnnotationVisitor visitAnnotation(String anno, boolean isRuntime) {
            AnnotationVisitor base = super.visitAnnotation(anno, isRuntime);
            if (methodCanHaveNullability && notNullAnnotations.contains(anno)) {
              return collectNotNullArgs(base, methodInfo.nullability.withNotNull(anno, ISE_CLASS_NAME));
            }
            return base;
          }

          @Override
          public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String anno, boolean visible) {
            AnnotationVisitor base = super.visitTypeAnnotation(typeRef, typePath, anno, visible);
            if (typePath != null) return base;

            TypeReference ref = new TypeReference(typeRef);
            if (methodCanHaveNullability && ref.getSort() == TypeReference.METHOD_RETURN) {
              if (notNullAnnotations.contains(anno)) {
                return collectNotNullArgs(base, methodInfo.nullability.withNotNull(anno, ISE_CLASS_NAME));
              }
              else if (seemsNullable(anno)) {
                methodInfo.nullability.hasTypeUseNullable = true;
              }
            }
            else if (ref.getSort() == TypeReference.METHOD_FORMAL_PARAMETER) {
              return checkParameterNullability(ref.getFormalParameterIndex() + methodInfo.paramAnnotationOffset, anno, base, true);
            }

            return base;
          }

          private boolean seemsNullable(String anno) {
            String shortName = getAnnoShortName(anno);
            // use hardcoded short names until it causes trouble
            // this is to avoid cumbersome passing of configured nullable names from the IDE
            return shortName.contains("Nullable") || shortName.equals("CheckForNull");
          }

          private AnnotationVisitor collectNotNullArgs(AnnotationVisitor base, final NotNullState state) {
            return new AnnotationVisitor(API_VERSION, base) {
              @Override
              public void visit(String methodName, Object o) {
                if (ANNOTATION_DEFAULT_METHOD.equals(methodName) && !((String) o).isEmpty()) {
                  state.message = (String) o;
                }
                else if ("exception".equals(methodName) && o instanceof Type && !((Type)o).getClassName().equals(Exception.class.getName())) {
                  state.exceptionType = ((Type)o).getInternalName();
                }
                super.visit(methodName, o);
              }
            };
          }

          private AnnotationVisitor checkParameterNullability(int parameter, String anno, AnnotationVisitor av, boolean typeUse) {
            if (parameter >= 0 && parameter < args.length && isReferenceType(args[parameter])) {
              if (notNullAnnotations.contains(anno)) {
                return collectNotNullArgs(av, methodInfo.obtainParameterNullability(parameter).withNotNull(anno, IAE_CLASS_NAME));
              }
              else if (typeUse && seemsNullable(anno)) {
                methodInfo.obtainParameterNullability(parameter).hasTypeUseNullable = true;
              }
            }

            return av;
          }

          @Override
          public void visitLocalVariable(String name2, String desc, String signature, Label start, Label end, int slotIndex) {
            Integer paramIndex = paramSlots.get(slotIndex);
            if (paramIndex != null) {
              methodInfo.paramNames.put(paramIndex, name2);
            }
          }
        };
      }
    }, ClassReader.SKIP_FRAMES);
    return result;
  }