fun lookupSpecialMethod()

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
  }