in intellij-plugin-verifier/verifier-core/src/main/java/com/jetbrains/pluginverifier/verifiers/resolution/MethodResolver.kt [406:486]
fun lookupSpecialMethod(classRef: ClassFile, resolvedMethod: Method): Pair<Int, Method>? {
/*
1) If C contains a declaration for an instance method with the same name and descriptor as the resolved method,
then it is the method to be invoked .
*/
val matching = classRef.methods.find { it.name == resolvedMethod.name && it.descriptor == resolvedMethod.descriptor }
if (matching != null) {
return 1 to matching
}
/*
2) Otherwise, if C is a class and has a superclass, a search for a declaration of an instance method with the same name
and descriptor as the resolved method is performed, starting with the direct superclass of C and continuing with the
direct superclass of that class, and so forth, until a match is found or no further superclasses exist.
If a match is found, then it is the method to be invoked.
*/
if (!classRef.isInterface && classRef.superName != null) {
var current: ClassFile = context.classResolver.resolveClassChecked(classRef.superName!!, classRef, context)
?: return null
while (true) {
val match = current.methods.find { it.name == resolvedMethod.name && it.descriptor == resolvedMethod.descriptor }
if (match != null) {
return 2 to match
}
val superName = current.superName
superName ?: break
current = context.classResolver.resolveClassChecked(superName, current, context)
?: return null
}
}
/*
3) Otherwise, if C is an interface and the class Object contains a declaration of a public instance method with
the same name and descriptor as the resolved method, then it is the method to be invoked.
*/
if (classRef.isInterface) {
val objectClass = context.classResolver.resolveClassChecked("java/lang/Object", classRef, context)
?: return null
val match = objectClass.methods.find { it.name == resolvedMethod.name && it.descriptor == resolvedMethod.descriptor && it.isPublic }
if (match != null) {
return 3 to match
}
}
/*
4) Otherwise, if there is exactly one maximally-specific method (§5.4.3.3) in the superinterfaces of C that
matches the resolved method's name and descriptor and is not abstract, then it is the method to be invoked.
*/
val interfaceMethods = getMaximallySpecificSuperInterfaceMethods(classRef) ?: return null
val filtered = interfaceMethods.filter { it.name == resolvedMethod.name && it.descriptor == resolvedMethod.descriptor && !it.isAbstract }
if (filtered.size == 1) {
return 4 to filtered.single()
}
/*
Otherwise, if step 4 of the lookup procedure determines there are multiple maximally-specific methods in
the superinterfaces of C that match the resolved method's name and descriptor and are
not abstract, invokespecial throws an IncompatibleClassChangeError
*/
if (filtered.size > 1) {
var implementation1 = filtered[0].location
var implementation2 = filtered[1].location
if (implementation1.toString() > implementation2.toString()) {
val tmp = implementation1
implementation1 = implementation2
implementation2 = tmp
}
context.problemRegistrar.registerProblem(MultipleDefaultImplementationsProblem(callerMethod.location, methodReference, instruction, implementation1, implementation2))
return null
}
/*
Otherwise, if step 4 of the lookup procedure determines there are zero maximally-specific methods in the
superinterfaces of C that match the resolved method's name and descriptor and are not abstract,
invokespecial throws an AbstractMethodError.
*/
context.problemRegistrar.registerProblem(AbstractMethodInvocationProblem(methodReference, resolvedMethod.location, callerMethod.location, instruction))
return null
}