From 027fd5600f090fc62ca474c37ba4b2748a907b2d Mon Sep 17 00:00:00 2001 From: Marco Pennekamp Date: Thu, 4 May 2023 21:37:51 +0200 Subject: [PATCH] [AA] KT-58257 Implement `KtStaticModuleDependentsProvider` (standalone) --- .../KtStaticModuleDependentsProvider.kt | 37 +++++++++++++++++++ .../structure/StandaloneProjectFactory.kt | 5 ++- .../api/standalone/StandaloneUtils.kt | 15 +++++--- .../KotlinModuleDependentsProviderBase.kt | 26 +++++++++++++ 4 files changed, 77 insertions(+), 6 deletions(-) create mode 100644 analysis/analysis-api-standalone/analysis-api-standalone-base/src/org/jetbrains/kotlin/analysis/api/standalone/base/project/structure/KtStaticModuleDependentsProvider.kt create mode 100644 analysis/project-structure/src/org/jetbrains/kotlin/analysis/project/structure/impl/KotlinModuleDependentsProviderBase.kt diff --git a/analysis/analysis-api-standalone/analysis-api-standalone-base/src/org/jetbrains/kotlin/analysis/api/standalone/base/project/structure/KtStaticModuleDependentsProvider.kt b/analysis/analysis-api-standalone/analysis-api-standalone-base/src/org/jetbrains/kotlin/analysis/api/standalone/base/project/structure/KtStaticModuleDependentsProvider.kt new file mode 100644 index 00000000000..f2cf864772b --- /dev/null +++ b/analysis/analysis-api-standalone/analysis-api-standalone-base/src/org/jetbrains/kotlin/analysis/api/standalone/base/project/structure/KtStaticModuleDependentsProvider.kt @@ -0,0 +1,37 @@ +/* + * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.analysis.api.standalone.base.project.structure + +import com.intellij.util.containers.ContainerUtil.createConcurrentSoftMap +import org.jetbrains.kotlin.analysis.project.structure.KtModule +import org.jetbrains.kotlin.analysis.project.structure.allDirectDependencies +import org.jetbrains.kotlin.analysis.project.structure.impl.KotlinModuleDependentsProviderBase + +class KtStaticModuleDependentsProvider(private val modules: List) : KotlinModuleDependentsProviderBase() { + private val directDependentsByKtModule: Map> by lazy { + // Direct dependencies should be computed lazily, because the built-ins module will be reachable via module dependencies. Getting + // the built-ins module relies on the built-ins session, which may depend on services that are registered after + // `KtStaticModuleDependentsProvider`. + buildMap> { + for (module in modules) { + for (dependency in module.allDirectDependencies()) { + // `module` should not be part of its own dependents, per the contract of `KotlinModuleDependentsProvider`. + if (dependency == module) continue + + val dependents = computeIfAbsent(dependency) { mutableSetOf() } + dependents.add(module) + } + } + } + } + + private val transitiveDependentsByKtModule = createConcurrentSoftMap>() + + override fun getDirectDependents(module: KtModule): Set = directDependentsByKtModule[module] ?: emptySet() + + override fun getTransitiveDependents(module: KtModule): Set = + transitiveDependentsByKtModule.computeIfAbsent(module) { computeTransitiveDependents(it) } +} diff --git a/analysis/analysis-api-standalone/analysis-api-standalone-base/src/org/jetbrains/kotlin/analysis/api/standalone/base/project/structure/StandaloneProjectFactory.kt b/analysis/analysis-api-standalone/analysis-api-standalone-base/src/org/jetbrains/kotlin/analysis/api/standalone/base/project/structure/StandaloneProjectFactory.kt index 2c2560b3acd..cfa5ad693a8 100644 --- a/analysis/analysis-api-standalone/analysis-api-standalone-base/src/org/jetbrains/kotlin/analysis/api/standalone/base/project/structure/StandaloneProjectFactory.kt +++ b/analysis/analysis-api-standalone/analysis-api-standalone-base/src/org/jetbrains/kotlin/analysis/api/standalone/base/project/structure/StandaloneProjectFactory.kt @@ -178,10 +178,13 @@ object StandaloneProjectFactory { registerService(ModuleAnnotationsResolver::class.java, CliModuleAnnotationsResolver()) } + val modules = projectStructureProvider.allKtModules project.registerService(ProjectStructureProvider::class.java, projectStructureProvider) + project.registerService(KotlinModuleDependentsProvider::class.java, KtStaticModuleDependentsProvider(modules)) + initialiseVirtualFileFinderServices( environment, - projectStructureProvider.allKtModules, + modules, projectStructureProvider.allSourceFiles, languageVersionSettings, jdkHome, diff --git a/analysis/analysis-api-standalone/src/org/jetbrains/kotlin/analysis/api/standalone/StandaloneUtils.kt b/analysis/analysis-api-standalone/src/org/jetbrains/kotlin/analysis/api/standalone/StandaloneUtils.kt index f164ab3cd83..a7589a0b46b 100644 --- a/analysis/analysis-api-standalone/src/org/jetbrains/kotlin/analysis/api/standalone/StandaloneUtils.kt +++ b/analysis/analysis-api-standalone/src/org/jetbrains/kotlin/analysis/api/standalone/StandaloneUtils.kt @@ -14,8 +14,10 @@ import org.jetbrains.kotlin.analysis.api.KtAnalysisApiInternals import org.jetbrains.kotlin.analysis.api.fir.KtFirAnalysisSessionProvider import org.jetbrains.kotlin.analysis.api.impl.base.references.HLApiReferenceProviderService import org.jetbrains.kotlin.analysis.api.session.KtAnalysisSessionProvider +import org.jetbrains.kotlin.analysis.api.standalone.base.project.structure.KtStaticModuleDependentsProvider import org.jetbrains.kotlin.analysis.decompiled.light.classes.ClsJavaStubByVirtualFileCache import org.jetbrains.kotlin.analysis.low.level.api.fir.api.services.FirSealedClassInheritorsProcessorFactory +import org.jetbrains.kotlin.analysis.project.structure.KotlinModuleDependentsProvider import org.jetbrains.kotlin.analysis.project.structure.KtModuleScopeProvider import org.jetbrains.kotlin.analysis.project.structure.KtModuleScopeProviderImpl import org.jetbrains.kotlin.analysis.project.structure.ProjectStructureProvider @@ -77,6 +79,7 @@ public fun configureApplicationEnvironment(app: MockApplication) { * * [FirSealedClassInheritorsProcessorFactory] * * [KtModuleScopeProvider] * * [ProjectStructureProvider] + * * [KotlinModuleDependentsProvider] * * [KotlinDeclarationProviderFactory] * * [KotlinPackageProviderFactory] * * [PackagePartProviderFactory] @@ -158,14 +161,16 @@ internal fun configureProjectEnvironment( KtModuleScopeProviderImpl() ) + val projectStructureProvider = buildKtModuleProviderByCompilerConfiguration(compilerConfig, project, ktFiles) project.registerService( ProjectStructureProvider::class.java, - buildKtModuleProviderByCompilerConfiguration( - compilerConfig, - project, - ktFiles, - ) + projectStructureProvider, ) + project.registerService( + KotlinModuleDependentsProvider::class.java, + KtStaticModuleDependentsProvider(projectStructureProvider.allKtModules), + ) + project.registerService( KotlinDeclarationProviderFactory::class.java, KotlinStaticDeclarationProviderFactory(project, ktFiles) diff --git a/analysis/project-structure/src/org/jetbrains/kotlin/analysis/project/structure/impl/KotlinModuleDependentsProviderBase.kt b/analysis/project-structure/src/org/jetbrains/kotlin/analysis/project/structure/impl/KotlinModuleDependentsProviderBase.kt new file mode 100644 index 00000000000..c2da97d118c --- /dev/null +++ b/analysis/project-structure/src/org/jetbrains/kotlin/analysis/project/structure/impl/KotlinModuleDependentsProviderBase.kt @@ -0,0 +1,26 @@ +/* + * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.analysis.project.structure.impl + +import org.jetbrains.kotlin.analysis.project.structure.KotlinModuleDependentsProvider +import org.jetbrains.kotlin.analysis.project.structure.KtModule + +public abstract class KotlinModuleDependentsProviderBase : KotlinModuleDependentsProvider() { + override fun getTransitiveDependents(module: KtModule): Set = computeTransitiveDependents(module) + + protected fun computeTransitiveDependents(module: KtModule): Set = buildSet { + // We could use `DFS` from utils, but this implementation has no handler overhead and is simple enough. + fun visit(module: KtModule) { + if (module in this) return + add(module) + getDirectDependents(module).forEach(::visit) + } + visit(module) + + // `module` should not be part of its own dependents, per the contract of `KotlinModuleDependentsProvider`. + remove(module) + } +}