Optimize getSingleAbstractMethodOrNull

Searching for the single abstract method leads to computing
whole member scopes of given intrefaces, especially when
they contain a lot of methods (i.e. they're definitely not SAMs)

On the other side this method is very hot because it's called
for each Java interface used as a type for some value parameter
(for building SAM adapters)

The idea is to apply some heuristics to understand using only
JavaMethod and supertypes that this interface is definitely not a SAM
This commit is contained in:
Denis Zharkov
2017-03-24 12:44:04 +03:00
parent db3f70cfe9
commit e4cb5496b8
4 changed files with 35 additions and 5 deletions
@@ -23,4 +23,10 @@ import org.jetbrains.kotlin.types.SimpleType;
public interface JavaClassDescriptor extends ClassDescriptor {
@Nullable
SimpleType getFunctionTypeForSamInterface();
/**
* May return false even in case when the class is not SAM interface, but returns true only if it's definitely not a SAM.
* But it should work much faster than the exact check.
*/
boolean isDefinitelyNotSamInterface();
}
@@ -48,6 +48,7 @@ import org.jetbrains.kotlin.serialization.deserialization.NotFoundClasses
import org.jetbrains.kotlin.storage.getValue
import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.utils.addIfNotNull
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
import java.util.*
class LazyJavaClassDescriptor(
@@ -59,6 +60,11 @@ class LazyJavaClassDescriptor(
outerContext.components.sourceElementFactory.source(jClass),
/* isExternal = */ false), JavaClassDescriptor {
companion object {
@JvmStatic
private val PUBLIC_METHOD_NAMES_IN_OBJECT = setOf("equals", "hashCode", "getClass", "wait", "notify", "notifyAll", "toString")
}
private val c: LazyJavaResolverContext = outerContext.child(this, jClass)
init {
@@ -133,6 +139,25 @@ class LazyJavaClassDescriptor(
override fun getFunctionTypeForSamInterface(): SimpleType? = c.components.samConversionResolver.resolveFunctionTypeIfSamInterface(this)
override fun isDefinitelyNotSamInterface(): Boolean {
if (kind != ClassKind.INTERFACE) return true
val candidates = jClass.methods.filter { it.isAbstract && it.typeParameters.isEmpty() }
// From the definition of function interfaces in the Java specification (pt. 9.8):
// "methods that are members of I that do not have the same signature as any public instance method of the class Object"
// It means that if an interface declares `int hashCode()` then the method won't be taken into account when
// checking if the interface is SAM.
// We make here a conservative check just filtering out methods by name.
// If we ignore a method with wrong signature (different from one in Object) it's not very bad,
// we'll just say that the interface MAY BE a SAM when it's not and then more detailed check will be applied.
if (candidates.count { it.name.identifier !in PUBLIC_METHOD_NAMES_IN_OBJECT } > 1) return true
// Check if any of the super-interfaces contain too many methods to be a SAM
return typeConstructor().supertypes.any {
it.constructor.declarationDescriptor.safeAs<LazyJavaClassDescriptor>()?.isDefinitelyNotSamInterface == true
}
}
override fun getSealedSubclasses(): Collection<ClassDescriptor> = emptyList()
override fun toString() = "Lazy Java class ${this.fqNameUnsafe}"