From 5a2851308f3828c1800eb3e0648b308b634f9fbe Mon Sep 17 00:00:00 2001 From: Denis Zharkov Date: Tue, 6 Sep 2016 17:18:43 +0300 Subject: [PATCH] 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) --- .../descriptors/LazyJavaClassMemberScope.kt | 27 ++++--- .../lazy/descriptors/LazyJavaPackageScope.kt | 9 ++- .../java/lazy/descriptors/LazyJavaScope.kt | 9 +-- .../descriptors/LazyJavaStaticClassScope.kt | 70 +++++++++++-------- .../load/java/lazy/descriptors/MemberIndex.kt | 50 ++----------- 5 files changed, 67 insertions(+), 98 deletions(-) diff --git a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/lazy/descriptors/LazyJavaClassMemberScope.kt b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/lazy/descriptors/LazyJavaClassMemberScope.kt index acdbabb9aff..16988e44e01 100644 --- a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/lazy/descriptors/LazyJavaClassMemberScope.kt +++ b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/lazy/descriptors/LazyJavaClassMemberScope.kt @@ -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 - = 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> = 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 { - if (jClass.isAnnotationType) return memberIndex().getMethodNames(nameFilter ?: alwaysTrue()) - - return memberIndex().getAllFieldNames() + - ownerDescriptor.getTypeConstructor().getSupertypes().flatMapTo(LinkedHashSet()) { 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() } } diff --git a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/lazy/descriptors/LazyJavaPackageScope.kt b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/lazy/descriptors/LazyJavaPackageScope.kt index 8015cb7e49c..8b348031290 100644 --- a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/lazy/descriptors/LazyJavaPackageScope.kt +++ b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/lazy/descriptors/LazyJavaPackageScope.kt @@ -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 = emptyList() - override fun computeMemberIndex(): MemberIndex = object : MemberIndex by EMPTY_MEMBER_INDEX { - // For SAM-constructors - override fun getMethodNames(nameFilter: (Name) -> Boolean): Set = computeClassNames(DescriptorKindFilter.CLASSIFIERS, nameFilter) - } + override fun computeMemberIndex(): MemberIndex = EMPTY_MEMBER_INDEX override fun computeClassNames(kindFilter: DescriptorKindFilter, nameFilter: ((Name) -> Boolean)?): Set { // 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, name: Name) { diff --git a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/lazy/descriptors/LazyJavaScope.kt b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/lazy/descriptors/LazyJavaScope.kt index e3af865b65f..046b0e34310 100644 --- a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/lazy/descriptors/LazyJavaScope.kt +++ b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/lazy/descriptors/LazyJavaScope.kt @@ -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 - = memberIndex().getMethodNames(nameFilter ?: alwaysTrue()) override fun getContributedFunctions(name: Name, location: LookupLocation): Collection { if (name !in getFunctionNames()) return emptyList() return functions(name) } + protected open fun computeFunctionNames(kindFilter: DescriptorKindFilter, nameFilter: ((Name) -> Boolean)?): Set = + memberIndex().getMethodNames() + protected abstract fun computeNonDeclaredProperties(name: Name, result: MutableCollection) - protected abstract fun computePropertyNames(kindFilter: DescriptorKindFilter, nameFilter: ((Name) -> Boolean)?): Set + protected open fun computePropertyNames(kindFilter: DescriptorKindFilter, nameFilter: ((Name) -> Boolean)?): Set = + memberIndex().getAllFieldNames() private val properties = c.storageManager.createMemoizedFunction { name: Name -> diff --git a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/lazy/descriptors/LazyJavaStaticClassScope.kt b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/lazy/descriptors/LazyJavaStaticClassScope.kt index ed561736b95..51d2eb268ea 100644 --- a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/lazy/descriptors/LazyJavaStaticClassScope.kt +++ b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/lazy/descriptors/LazyJavaStaticClassScope.kt @@ -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 { - // 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 { - 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 = - memberIndex().getAllFieldNames() override fun computeClassNames(kindFilter: DescriptorKindFilter, nameFilter: ((Name) -> Boolean)?): Set = emptySet() @@ -85,7 +80,9 @@ class LazyJavaStaticClassScope( } override fun computeNonDeclaredProperties(name: Name, result: MutableCollection) { - 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 { + private fun flatMapJavaStaticSupertypesScopes( + root: ClassDescriptor, + result: MutableSet, + onJavaStaticScope: (MemberScope) -> Collection + ): Set { + DFS.dfs(listOf(root), + { + it.typeConstructor.supertypes.asSequence().mapNotNull { + supertype -> supertype.constructor.declarationDescriptor as? ClassDescriptor + }.asIterable() + }, + object : DFS.AbstractNodeHandler() { + override fun beforeChildren(current: ClassDescriptor): Boolean { + if (current === root) return true + val staticScope = current.staticScope - fun getStaticProperties(supertype: KotlinType): Iterable { - 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 diff --git a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/lazy/descriptors/MemberIndex.kt b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/lazy/descriptors/MemberIndex.kt index ee87924e619..3f079632132 100644 --- a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/lazy/descriptors/MemberIndex.kt +++ b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/lazy/descriptors/MemberIndex.kt @@ -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 - fun getMethodNames(nameFilter: (Name) -> Boolean): Set + fun getMethodNames(): Set fun findFieldByName(name: Name): JavaField? fun getAllFieldNames(): Set @@ -35,22 +33,12 @@ interface MemberIndex { object EMPTY_MEMBER_INDEX : MemberIndex { override fun findMethodsByName(name: Name) = listOf() - override fun getMethodNames(nameFilter: (Name) -> Boolean) = emptySet() + override fun getMethodNames() = emptySet() override fun findFieldByName(name: Name): JavaField? = null override fun getAllFieldNames() = emptySet() } -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 = methods[name] ?: listOf() - override fun getMethodNames(nameFilter: (Name) -> Boolean): Set = - jClass.getAllMemberNames(methodFilter) { methods } + override fun getMethodNames(): Set = jClass.methods.asSequence().filter(methodFilter).mapTo(mutableSetOf(), JavaMethod::name) override fun findFieldByName(name: Name): JavaField? = fields[name] - override fun getAllFieldNames(): Set = jClass.getAllMemberNames(memberFilter) { fields } + override fun getAllFieldNames(): Set = jClass.fields.asSequence().filter(memberFilter).mapTo(mutableSetOf(), JavaField::name) } -private fun JavaClass.getNonDeclaredMethodNames(): List { - return ADDITIONAL_MEMBER_NAMES_MAP[this.fqName].orEmpty() -} - -private fun JavaClass.getAllMemberNames(filter: (M) -> Boolean, getMembers: JavaClass.() -> Collection): Set { - val result = HashSet() - val visitedSuperClasses = HashSet() - - 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 -}