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;
}