From d528d24f83ea63cee976be318790c2922a761496 Mon Sep 17 00:00:00 2001 From: Ilya Chernikov Date: Mon, 8 Jun 2020 18:13:36 +0200 Subject: [PATCH] Optimize AllUnderImportScope --- .../kotlin/resolve/AllUnderImportScope.kt | 95 ++++++++++++------- .../resolve/QualifiedExpressionResolver.kt | 2 +- .../org/jetbrains/kotlin/util/scopeUtils.kt | 20 +++- 3 files changed, 76 insertions(+), 41 deletions(-) diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/AllUnderImportScope.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/AllUnderImportScope.kt index a6df8edfa6d..a0831200b74 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/AllUnderImportScope.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/AllUnderImportScope.kt @@ -20,28 +20,19 @@ import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.incremental.components.LookupLocation import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name -import org.jetbrains.kotlin.resolve.scopes.BaseImportingScope -import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter -import org.jetbrains.kotlin.resolve.scopes.MemberScope -import org.jetbrains.kotlin.resolve.scopes.computeAllNames +import org.jetbrains.kotlin.resolve.scopes.* import org.jetbrains.kotlin.util.collectionUtils.flatMapScopes -import org.jetbrains.kotlin.util.collectionUtils.listOfNonEmptyScopes +import org.jetbrains.kotlin.util.collectionUtils.forEachScope import org.jetbrains.kotlin.utils.Printer -import org.jetbrains.kotlin.utils.addToStdlib.flatMapToNullable +import org.jetbrains.kotlin.utils.SmartList -class AllUnderImportScope( +class AllUnderImportScope private constructor( descriptor: DeclarationDescriptor, - excludedImportNames: Collection + excludedImportNames: Collection, + private val scope1: MemberScope, + private val scope2: MemberScope? ) : BaseImportingScope(null) { - private val scopes: Array = if (descriptor is ClassDescriptor) { - listOfNonEmptyScopes(descriptor.staticScope, descriptor.unsubstitutedInnerClassesScope).toTypedArray() - } else { - assert(descriptor is PackageViewDescriptor) { - "Must be class or package view descriptor: $descriptor" - } - listOfNonEmptyScopes((descriptor as PackageViewDescriptor).memberScope).toTypedArray() - } private val excludedNames: Set = if (excludedImportNames.isEmpty()) { // optimization emptySet() @@ -51,10 +42,22 @@ class AllUnderImportScope( excludedImportNames.mapNotNull { if (it.parent() == fqName) it.shortName() else null }.toSet() } - override fun computeImportedNames(): Set? = when (scopes.size) { - 0 -> null - 1 -> scopes[0].computeAllNames() - else -> scopes.asIterable().flatMapToNullable(hashSetOf(), MemberScope::computeAllNames) + override fun computeImportedNames(): Set? { + val names1 = scope1.computeAllNames() + return when { + scope2 == null -> names1 + names1 == null -> null + else -> { + val names2 = scope2.computeAllNames() + when { + names2 == null -> null + names1.isEmpty() -> names2 + else -> names1.toMutableSet().also { + it.addAll(names2) + } + } + } + } } override fun getContributedDescriptors( @@ -69,39 +72,61 @@ class AllUnderImportScope( } val noPackagesKindFilter = kindFilter.withoutKinds(DescriptorKindFilter.PACKAGES_MASK) - return scopes - .flatMapScopes { it.getContributedDescriptors(noPackagesKindFilter, nameFilterToUse) } - .filter { it !is PackageViewDescriptor } // subpackages are not imported + val result = SmartList() + forEachScope(scope1, scope2) { scope -> + scope.getContributedDescriptors(noPackagesKindFilter, nameFilterToUse) + .filterTo(result) { it !is PackageViewDescriptor } + } + return result } override fun getContributedClassifier(name: Name, location: LookupLocation): ClassifierDescriptor? { if (name in excludedNames) return null - var single: ClassifierDescriptor? = null - for (scope in scopes) { - val res = scope.getContributedClassifier(name, location) ?: continue - if (single == null) single = res - else return null - } - return single + val classifier1 = scope1.getContributedClassifier(name, location) + val classifier2 = scope2?.getContributedClassifier(name, location) + return if (classifier1 != null && classifier2 != null) null else (classifier1 ?: classifier2) } override fun getContributedVariables(name: Name, location: LookupLocation): Collection { if (name in excludedNames) return emptyList() - return scopes.flatMapScopes { it.getContributedVariables(name, location) } + return flatMapScopes(scope1, scope2) { it.getContributedVariables(name, location) } } override fun getContributedFunctions(name: Name, location: LookupLocation): Collection { if (name in excludedNames) return emptyList() - return scopes.flatMapScopes { it.getContributedFunctions(name, location) } + return flatMapScopes(scope1, scope2) { it.getContributedFunctions(name, location) } } override fun recordLookup(name: Name, location: LookupLocation) { - scopes.forEach { it.recordLookup(name, location) } + scope1.recordLookup(name, location) + scope2?.recordLookup(name, location) } override fun printStructure(p: Printer) { p.println(this::class.java.simpleName) } + + companion object { + fun create(descriptor: DeclarationDescriptor, excludedImportNames: Collection): ImportingScope { + val scope1 = + if (descriptor is ClassDescriptor) { + descriptor.staticScope + } else { + assert(descriptor is PackageViewDescriptor) { + "Must be class or package view descriptor: $descriptor" + } + (descriptor as PackageViewDescriptor).memberScope + } + + val scope2 = + if (descriptor is ClassDescriptor) { + descriptor.unsubstitutedInnerClassesScope.takeIf { it !== MemberScope.Empty } + } else null + + return if (scope1 === MemberScope.Empty) { + if (scope2 == null || scope2 === MemberScope.Empty) ImportingScope.Empty + else AllUnderImportScope(descriptor, excludedImportNames, scope2, null) + } else AllUnderImportScope(descriptor, excludedImportNames, scope1, scope2) + } + } } - - diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/QualifiedExpressionResolver.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/QualifiedExpressionResolver.kt index bc9ff086ede..2fbb40c5f3c 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/QualifiedExpressionResolver.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/QualifiedExpressionResolver.kt @@ -249,7 +249,7 @@ class QualifiedExpressionResolver(val languageVersionSettings: LanguageVersionSe return null } - return AllUnderImportScope(packageOrClassDescriptor, excludedImportNames) + return AllUnderImportScope.create(packageOrClassDescriptor, excludedImportNames) } else { return processSingleImport(moduleDescriptor, trace, importDirective, path, lastPart, packageFragmentForCheck) } diff --git a/core/descriptors/src/org/jetbrains/kotlin/util/scopeUtils.kt b/core/descriptors/src/org/jetbrains/kotlin/util/scopeUtils.kt index e559b6142c7..c987e56023e 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/util/scopeUtils.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/util/scopeUtils.kt @@ -64,12 +64,22 @@ inline fun getFromAllScopes(firstScope: Scope, restScopes: Array Array.flatMapScopes(transform: (Scope) -> Collection): Collection = - when (size) { - 0 -> emptyList() - 1 -> transform(get(0)) - else -> flatMapTo(ArrayList(), transform) +inline fun flatMapScopes(scope1: Scope?, scope2: Scope?, transform: (Scope) -> Collection): Collection { + val results1 = if (scope1 != null) transform(scope1) else emptyList() + if (scope2 == null) return results1 + else { + val results2 = transform(scope2) + if (results1.isEmpty()) return results2 + else return results1.toMutableList().also { + it.addAll(results2) + } } +} + +inline fun forEachScope(scope1: Scope?, scope2: Scope?, action: (Scope) -> Unit) { + if (scope1 != null) action(scope1) + if (scope2 != null) action(scope2) +} fun listOfNonEmptyScopes(vararg scopes: MemberScope?): SmartList = scopes.filterTo(SmartList()) { it != null && it !== MemberScope.Empty }