Optimize computation of member names in Java scopes

Effectively all custom logic was moved from MemberIndex to JavaScope classes,
and as member names are being cached there, so it prevents repeating supertypes
traversal (see getAllMemberNames extension)
This commit is contained in:
Denis Zharkov
2016-09-06 17:18:43 +03:00
parent cc0f2e87d4
commit 5a2851308f
5 changed files with 67 additions and 98 deletions
@@ -67,13 +67,15 @@ class LazyJavaClassMemberScope(
private val jClass: JavaClass
) : LazyJavaScope(c) {
override fun computeMemberIndex(): MemberIndex {
return object : ClassMemberIndex(jClass, { !it.isStatic }) {
// For SAM-constructors
override fun getMethodNames(nameFilter: (Name) -> Boolean): Set<Name>
= super.getMethodNames(nameFilter) + computeClassNames(DescriptorKindFilter.CLASSIFIERS, nameFilter)
override fun computeMemberIndex() = ClassMemberIndex(jClass, { !it.isStatic })
override fun computeFunctionNames(kindFilter: DescriptorKindFilter, nameFilter: ((Name) -> Boolean)?) =
ownerDescriptor.typeConstructor.supertypes.flatMapTo(HashSet()) {
it.memberScope.getFunctionNames()
}.apply {
addAll(super.computeFunctionNames(kindFilter, null))
addAll(computeClassNames(kindFilter, nameFilter))
}
}
internal val constructors = c.storageManager.createLazyValue {
val constructors = jClass.constructors
@@ -628,7 +630,7 @@ class LazyJavaClassMemberScope(
val field = enumEntryIndex()[name]
if (field != null) {
val enumMemberNames: NotNullLazyValue<Set<Name>> = c.storageManager.createLazyValue {
(memberIndex().getAllFieldNames() + memberIndex().getMethodNames({ true })).toSet()
getFunctionNames() + getVariableNames()
}
EnumEntrySyntheticClassDescriptor.create(
c.storageManager, ownerDescriptor, name, enumMemberNames, c.resolveAnnotations(field),
@@ -664,13 +666,10 @@ class LazyJavaClassMemberScope(
= nestedClassIndex().keys + enumEntryIndex().keys
override fun computePropertyNames(kindFilter: DescriptorKindFilter, nameFilter: ((Name) -> Boolean)?): Set<Name> {
if (jClass.isAnnotationType) return memberIndex().getMethodNames(nameFilter ?: alwaysTrue())
return memberIndex().getAllFieldNames() +
ownerDescriptor.getTypeConstructor().getSupertypes().flatMapTo(LinkedHashSet<Name>()) { supertype ->
supertype.memberScope.getContributedDescriptors(kindFilter, nameFilter ?: alwaysTrue()).map { variable ->
variable.getName()
}
if (jClass.isAnnotationType) return getFunctionNames()
val result = LinkedHashSet(memberIndex().getAllFieldNames())
return ownerDescriptor.typeConstructor.supertypes.flatMapTo(result) { supertype ->
supertype.memberScope.getVariableNames()
}
}
@@ -35,6 +35,7 @@ import org.jetbrains.kotlin.name.SpecialNames
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
import org.jetbrains.kotlin.storage.NullableLazyValue
import org.jetbrains.kotlin.utils.alwaysTrue
import java.util.*
class LazyJavaPackageScope(
c: LazyJavaResolverContext,
@@ -133,10 +134,7 @@ class LazyJavaPackageScope(
override fun getContributedVariables(name: Name, location: LookupLocation): Collection<PropertyDescriptor> = emptyList()
override fun computeMemberIndex(): MemberIndex = object : MemberIndex by EMPTY_MEMBER_INDEX {
// For SAM-constructors
override fun getMethodNames(nameFilter: (Name) -> Boolean): Set<Name> = computeClassNames(DescriptorKindFilter.CLASSIFIERS, nameFilter)
}
override fun computeMemberIndex(): MemberIndex = EMPTY_MEMBER_INDEX
override fun computeClassNames(kindFilter: DescriptorKindFilter, nameFilter: ((Name) -> Boolean)?): Set<Name> {
// neither objects nor enum members can be in java package
@@ -151,7 +149,8 @@ class LazyJavaPackageScope(
// optimization: only SAM-constructors may exist in java package
if (kindFilter.excludes.contains(SamConstructorDescriptorKindExclude)) return emptySet()
return super.computeFunctionNames(kindFilter, nameFilter)
// For SAM-constructors
return computeClassNames(DescriptorKindFilter.CLASSIFIERS, nameFilter)
}
override fun computeNonDeclaredFunctions(result: MutableCollection<SimpleFunctionDescriptor>, name: Name) {
@@ -46,7 +46,6 @@ import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.utils.Printer
import org.jetbrains.kotlin.utils.addIfNotNull
import org.jetbrains.kotlin.utils.alwaysTrue
import org.jetbrains.kotlin.utils.toReadOnlyList
import java.util.*
@@ -221,17 +220,19 @@ abstract class LazyJavaScope(protected val c: LazyJavaResolverContext) : MemberS
override fun getFunctionNames() = functionNamesLazy
override fun getVariableNames() = propertyNamesLazy
protected open fun computeFunctionNames(kindFilter: DescriptorKindFilter, nameFilter: ((Name) -> Boolean)?): Set<Name>
= memberIndex().getMethodNames(nameFilter ?: alwaysTrue())
override fun getContributedFunctions(name: Name, location: LookupLocation): Collection<SimpleFunctionDescriptor> {
if (name !in getFunctionNames()) return emptyList()
return functions(name)
}
protected open fun computeFunctionNames(kindFilter: DescriptorKindFilter, nameFilter: ((Name) -> Boolean)?): Set<Name> =
memberIndex().getMethodNames()
protected abstract fun computeNonDeclaredProperties(name: Name, result: MutableCollection<PropertyDescriptor>)
protected abstract fun computePropertyNames(kindFilter: DescriptorKindFilter, nameFilter: ((Name) -> Boolean)?): Set<Name>
protected open fun computePropertyNames(kindFilter: DescriptorKindFilter, nameFilter: ((Name) -> Boolean)?): Set<Name> =
memberIndex().getAllFieldNames()
private val properties = c.storageManager.createMemoizedFunction {
name: Name ->
@@ -31,7 +31,8 @@ import org.jetbrains.kotlin.resolve.DescriptorFactory.createEnumValueOfMethod
import org.jetbrains.kotlin.resolve.DescriptorFactory.createEnumValuesMethod
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.resolve.scopes.MemberScope
import org.jetbrains.kotlin.utils.DFS
class LazyJavaStaticClassScope(
c: LazyJavaResolverContext,
@@ -39,27 +40,21 @@ class LazyJavaStaticClassScope(
override val ownerDescriptor: LazyJavaClassDescriptor
) : LazyJavaStaticScope(c) {
override fun computeMemberIndex(): MemberIndex {
val delegate = ClassMemberIndex(jClass) { it.isStatic }
return object : MemberIndex by delegate {
override fun getMethodNames(nameFilter: (Name) -> Boolean): Set<Name> {
// Should be a super call, but KT-2860
return delegate.getMethodNames(nameFilter) +
// For SAM-constructors
jClass.innerClasses.map { it.name }
override fun computeMemberIndex() = ClassMemberIndex(jClass) { it.isStatic }
override fun computeFunctionNames(kindFilter: DescriptorKindFilter, nameFilter: ((Name) -> Boolean)?) =
super.computeFunctionNames(kindFilter, nameFilter).toMutableSet().apply {
addAll(ownerDescriptor.getParentJavaStaticClassScope()?.getFunctionNames().orEmpty())
if (jClass.isEnum) {
addAll(listOf(DescriptorUtils.ENUM_VALUE_OF, DescriptorUtils.ENUM_VALUES))
}
addAll(jClass.innerClasses.map(JavaClass::name))
}
}
override fun computeFunctionNames(kindFilter: DescriptorKindFilter, nameFilter: ((Name) -> Boolean)?): Set<Name> {
if (jClass.isEnum) {
return super.computeFunctionNames(kindFilter, nameFilter) + listOf(DescriptorUtils.ENUM_VALUE_OF, DescriptorUtils.ENUM_VALUES)
override fun computePropertyNames(kindFilter: DescriptorKindFilter, nameFilter: ((Name) -> Boolean)?) =
super.computePropertyNames(kindFilter, nameFilter).toMutableSet().apply {
flatMapJavaStaticSupertypesScopes(ownerDescriptor, this) { it.getVariableNames() }
}
return super.computeFunctionNames(kindFilter, nameFilter)
}
override fun computePropertyNames(kindFilter: DescriptorKindFilter, nameFilter: ((Name) -> Boolean)?): Set<Name> =
memberIndex().getAllFieldNames()
override fun computeClassNames(kindFilter: DescriptorKindFilter, nameFilter: ((Name) -> Boolean)?): Set<Name> = emptySet()
@@ -85,7 +80,9 @@ class LazyJavaStaticClassScope(
}
override fun computeNonDeclaredProperties(name: Name, result: MutableCollection<PropertyDescriptor>) {
val propertiesFromSupertypes = getStaticPropertiesFromJavaSupertypes(name, ownerDescriptor)
val propertiesFromSupertypes = flatMapJavaStaticSupertypesScopes(ownerDescriptor, mutableSetOf()) {
it.getContributedVariables(name, NoLookupLocation.WHEN_GET_SUPER_MEMBERS)
}
if (result.isNotEmpty()) {
result.addAll(resolveOverridesForStaticMembers(
@@ -106,19 +103,34 @@ class LazyJavaStaticClassScope(
return staticScope.getContributedFunctions(name, NoLookupLocation.WHEN_GET_SUPER_MEMBERS).toSet()
}
private fun getStaticPropertiesFromJavaSupertypes(name: Name, descriptor: ClassDescriptor): Set<PropertyDescriptor> {
private fun <R> flatMapJavaStaticSupertypesScopes(
root: ClassDescriptor,
result: MutableSet<R>,
onJavaStaticScope: (MemberScope) -> Collection<R>
): Set<R> {
DFS.dfs(listOf(root),
{
it.typeConstructor.supertypes.asSequence().mapNotNull {
supertype -> supertype.constructor.declarationDescriptor as? ClassDescriptor
}.asIterable()
},
object : DFS.AbstractNodeHandler<ClassDescriptor, Unit>() {
override fun beforeChildren(current: ClassDescriptor): Boolean {
if (current === root) return true
val staticScope = current.staticScope
fun getStaticProperties(supertype: KotlinType): Iterable<PropertyDescriptor> {
val superTypeDescriptor = supertype.constructor.declarationDescriptor as? ClassDescriptor ?: return emptyList()
if (staticScope is LazyJavaStaticScope) {
result.addAll(onJavaStaticScope(staticScope))
return false
}
return true
}
val staticScope = superTypeDescriptor.staticScope
override fun result() {}
}
)
if (staticScope !is LazyJavaStaticClassScope) return getStaticPropertiesFromJavaSupertypes(name, superTypeDescriptor)
return staticScope.getContributedVariables(name, NoLookupLocation.WHEN_GET_SUPER_MEMBERS)
}
return descriptor.typeConstructor.supertypes.flatMap(::getStaticProperties).toSet()
return result
}
private val PropertyDescriptor.realOriginal: PropertyDescriptor
@@ -21,13 +21,11 @@ import org.jetbrains.kotlin.load.java.structure.JavaClass
import org.jetbrains.kotlin.load.java.structure.JavaField
import org.jetbrains.kotlin.load.java.structure.JavaMember
import org.jetbrains.kotlin.load.java.structure.JavaMethod
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import java.util.*
interface MemberIndex {
fun findMethodsByName(name: Name): Collection<JavaMethod>
fun getMethodNames(nameFilter: (Name) -> Boolean): Set<Name>
fun getMethodNames(): Set<Name>
fun findFieldByName(name: Name): JavaField?
fun getAllFieldNames(): Set<Name>
@@ -35,22 +33,12 @@ interface MemberIndex {
object EMPTY_MEMBER_INDEX : MemberIndex {
override fun findMethodsByName(name: Name) = listOf<JavaMethod>()
override fun getMethodNames(nameFilter: (Name) -> Boolean) = emptySet<Name>()
override fun getMethodNames() = emptySet<Name>()
override fun findFieldByName(name: Name): JavaField? = null
override fun getAllFieldNames() = emptySet<Name>()
}
private val ADDITIONAL_MEMBER_NAMES_MAP = mapOf(
FqName("java.util.List") to listOf(Name.identifier("removeAt")),
FqName("java.lang.CharSequence") to listOf(Name.identifier("get")),
FqName("java.util.Map") to listOf(Name.identifier("keys"), Name.identifier("entries")),
FqName("java.lang.Number") to listOf(
Name.identifier("toByte"), Name.identifier("toShort"), Name.identifier("toInt"), Name.identifier("toLong"),
Name.identifier("toFloat"), Name.identifier("toDouble")
)
)
open class ClassMemberIndex(val jClass: JavaClass, val memberFilter: (JavaMember) -> Boolean) : MemberIndex {
private val methodFilter = {
m: JavaMethod ->
@@ -61,39 +49,9 @@ open class ClassMemberIndex(val jClass: JavaClass, val memberFilter: (JavaMember
private val fields = jClass.fields.asSequence().filter(memberFilter).associateBy { m -> m.name }
override fun findMethodsByName(name: Name): Collection<JavaMethod> = methods[name] ?: listOf()
override fun getMethodNames(nameFilter: (Name) -> Boolean): Set<Name> =
jClass.getAllMemberNames(methodFilter) { methods }
override fun getMethodNames(): Set<Name> = jClass.methods.asSequence().filter(methodFilter).mapTo(mutableSetOf(), JavaMethod::name)
override fun findFieldByName(name: Name): JavaField? = fields[name]
override fun getAllFieldNames(): Set<Name> = jClass.getAllMemberNames(memberFilter) { fields }
override fun getAllFieldNames(): Set<Name> = jClass.fields.asSequence().filter(memberFilter).mapTo(mutableSetOf(), JavaField::name)
}
private fun JavaClass.getNonDeclaredMethodNames(): List<Name> {
return ADDITIONAL_MEMBER_NAMES_MAP[this.fqName].orEmpty()
}
private fun <M : JavaMember> JavaClass.getAllMemberNames(filter: (M) -> Boolean, getMembers: JavaClass.() -> Collection<M>): Set<Name> {
val result = HashSet<Name>()
val visitedSuperClasses = HashSet<JavaClass>()
fun JavaClass.visit(): Unit {
if (!visitedSuperClasses.add(this)) return
for (member in getMembers()) {
if (filter(member)) {
result.add(member.name)
}
}
for (supertype in supertypes) {
val classifier = supertype.classifier
if (classifier is JavaClass) {
result.addAll(classifier.getNonDeclaredMethodNames())
classifier.visit()
}
}
}
this.visit()
return result
}