From aef3df0336ce1b4670f2e087954157e11eef984b Mon Sep 17 00:00:00 2001 From: Jinseong Jeon Date: Wed, 1 Jun 2022 14:56:18 -0700 Subject: [PATCH] AA: introduce AA session and builder --- .../structure/StandaloneProjectFactory.kt | 16 +- .../analysis-api-standalone/build.gradle.kts | 1 + .../StandaloneAnalysisAPISession.kt | 29 +++ .../StandaloneAnalysisAPISessionBuilder.kt | 219 ++++++++++++++++++ .../builder/KtSourceModuleBuilder.kt | 11 + .../structure/impl/KtModuleProviderImpl.kt | 11 +- .../project/structure/impl/KtModuleUtils.kt | 1 + .../structure/impl/KtSourceModuleImpl.kt | 2 + .../build.gradle.kts | 7 +- 9 files changed, 288 insertions(+), 9 deletions(-) create mode 100644 analysis/analysis-api-standalone/src/org/jetbrains/kotlin/analysis/api/standalone/StandaloneAnalysisAPISession.kt create mode 100644 analysis/analysis-api-standalone/src/org/jetbrains/kotlin/analysis/api/standalone/StandaloneAnalysisAPISessionBuilder.kt 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 f1e0f663a9d..9f6e5a0e8e3 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 @@ -25,8 +25,7 @@ import org.jetbrains.kotlin.cli.jvm.index.SingleJavaFileRootsIndex import org.jetbrains.kotlin.cli.jvm.modules.CliJavaModuleFinder import org.jetbrains.kotlin.cli.jvm.modules.CliJavaModuleResolver import org.jetbrains.kotlin.cli.jvm.modules.JavaModuleGraph -import org.jetbrains.kotlin.config.CompilerConfiguration -import org.jetbrains.kotlin.config.LanguageVersionSettings +import org.jetbrains.kotlin.config.* import org.jetbrains.kotlin.load.kotlin.MetadataFinderFactory import org.jetbrains.kotlin.load.kotlin.VirtualFileFinderFactory import org.jetbrains.kotlin.resolve.ModuleAnnotationsResolver @@ -52,8 +51,8 @@ object StandaloneProjectFactory { projectStructureProvider: ProjectStructureProvider, modules: List, sourceFiles: List, - languageVersionSettings: LanguageVersionSettings, - jdkHome: Path?, + languageVersionSettings: LanguageVersionSettings = latestLanguageVersionSettings, + jdkHome: Path? = null, ) { val project = environment.project @@ -96,7 +95,7 @@ object StandaloneProjectFactory { javaFileManager.initialize( JvmDependenciesIndexImpl(sourceAndLibraryRoots), listOf( - createPackagePartsProvider(languageVersionSettings, project, libraryRoots) + createPackagePartsProvider(project, libraryRoots, languageVersionSettings) .invoke(ProjectScope.getLibrariesScope(project)) ), SingleJavaFileRootsIndex(emptyList()), @@ -174,9 +173,9 @@ object StandaloneProjectFactory { } fun createPackagePartsProvider( - languageVersionSettings: LanguageVersionSettings, project: MockProject, - libraryRoots: List + libraryRoots: List, + languageVersionSettings: LanguageVersionSettings = latestLanguageVersionSettings, ): (GlobalSearchScope) -> JvmPackagePartProvider = { scope -> JvmPackagePartProvider(languageVersionSettings, scope).apply { addRoots(libraryRoots, MessageCollector.NONE) @@ -185,4 +184,7 @@ object StandaloneProjectFactory { .addPackagePartProvider(this) } } + + private val latestLanguageVersionSettings: LanguageVersionSettings = + LanguageVersionSettingsImpl(LanguageVersion.LATEST_STABLE, ApiVersion.LATEST) } \ No newline at end of file diff --git a/analysis/analysis-api-standalone/build.gradle.kts b/analysis/analysis-api-standalone/build.gradle.kts index fc6b4ed5db9..12e5ead9b26 100644 --- a/analysis/analysis-api-standalone/build.gradle.kts +++ b/analysis/analysis-api-standalone/build.gradle.kts @@ -18,6 +18,7 @@ dependencies { api(project(":analysis:low-level-api-fir")) api(project(":analysis:symbol-light-classes")) api(project(":analysis:decompiled:light-classes-for-decompiled")) + api(project(":analysis:analysis-api-standalone:analysis-api-standalone-base")) testApi(projectTests(":analysis:analysis-api-impl-base")) testApi(projectTests(":analysis:analysis-api-fir")) diff --git a/analysis/analysis-api-standalone/src/org/jetbrains/kotlin/analysis/api/standalone/StandaloneAnalysisAPISession.kt b/analysis/analysis-api-standalone/src/org/jetbrains/kotlin/analysis/api/standalone/StandaloneAnalysisAPISession.kt new file mode 100644 index 00000000000..ef868400e17 --- /dev/null +++ b/analysis/analysis-api-standalone/src/org/jetbrains/kotlin/analysis/api/standalone/StandaloneAnalysisAPISession.kt @@ -0,0 +1,29 @@ +/* + * Copyright 2010-2022 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 + +import com.intellij.core.CoreApplicationEnvironment +import com.intellij.mock.MockProject +import com.intellij.openapi.application.Application +import com.intellij.openapi.project.Project +import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreProjectEnvironment + +public class StandaloneAnalysisAPISession internal constructor( + kotlinCoreProjectEnvironment: KotlinCoreProjectEnvironment, +) { + // TODO: better to limit exposure? Current usages are: addExtension, jarFileSystem + public val coreApplicationEnvironment: CoreApplicationEnvironment = kotlinCoreProjectEnvironment.environment + + public val application: Application = kotlinCoreProjectEnvironment.environment.application + + public val project: Project = kotlinCoreProjectEnvironment.project + + @Deprecated( + "Use session builder's service registration.", + ReplaceWith("project") + ) + public val mockProject: MockProject = kotlinCoreProjectEnvironment.project +} \ No newline at end of file diff --git a/analysis/analysis-api-standalone/src/org/jetbrains/kotlin/analysis/api/standalone/StandaloneAnalysisAPISessionBuilder.kt b/analysis/analysis-api-standalone/src/org/jetbrains/kotlin/analysis/api/standalone/StandaloneAnalysisAPISessionBuilder.kt new file mode 100644 index 00000000000..3bf8c53c6bd --- /dev/null +++ b/analysis/analysis-api-standalone/src/org/jetbrains/kotlin/analysis/api/standalone/StandaloneAnalysisAPISessionBuilder.kt @@ -0,0 +1,219 @@ +/* + * Copyright 2010-2022 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 + +import com.intellij.openapi.Disposable +import com.intellij.openapi.application.Application +import com.intellij.openapi.project.Project +import com.intellij.openapi.util.Disposer +import com.intellij.psi.PsiElementFinder +import com.intellij.psi.impl.PsiElementFinderImpl +import com.intellij.psi.search.GlobalSearchScope +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.lifetime.KtDefaultLifetimeTokenProvider +import org.jetbrains.kotlin.analysis.api.lifetime.KtReadActionConfinementDefaultLifetimeTokenProvider +import org.jetbrains.kotlin.analysis.api.session.KtAnalysisSessionProvider +import org.jetbrains.kotlin.analysis.api.standalone.base.project.structure.StandaloneProjectFactory +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.low.level.api.fir.api.services.PackagePartProviderFactory +import org.jetbrains.kotlin.analysis.project.structure.KtModuleScopeProvider +import org.jetbrains.kotlin.analysis.project.structure.KtModuleScopeProviderImpl +import org.jetbrains.kotlin.analysis.project.structure.ProjectStructureProvider +import org.jetbrains.kotlin.analysis.project.structure.builder.KtModuleProviderBuilder +import org.jetbrains.kotlin.analysis.project.structure.builder.buildProjectStructureProvider +import org.jetbrains.kotlin.analysis.project.structure.impl.KtModuleProviderImpl +import org.jetbrains.kotlin.analysis.project.structure.impl.buildKtModuleProviderByCompilerConfiguration +import org.jetbrains.kotlin.analysis.project.structure.impl.getPsiFilesFromPaths +import org.jetbrains.kotlin.analysis.project.structure.impl.getSourceFilePaths +import org.jetbrains.kotlin.analysis.providers.KotlinAnnotationsResolverFactory +import org.jetbrains.kotlin.analysis.providers.KotlinDeclarationProviderFactory +import org.jetbrains.kotlin.analysis.providers.KotlinModificationTrackerFactory +import org.jetbrains.kotlin.analysis.providers.KotlinPackageProviderFactory +import org.jetbrains.kotlin.analysis.providers.impl.KotlinStaticAnnotationsResolverFactory +import org.jetbrains.kotlin.analysis.providers.impl.KotlinStaticDeclarationProviderFactory +import org.jetbrains.kotlin.analysis.providers.impl.KotlinStaticModificationTrackerFactory +import org.jetbrains.kotlin.analysis.providers.impl.KotlinStaticPackageProviderFactory +import org.jetbrains.kotlin.asJava.KotlinAsJavaSupport +import org.jetbrains.kotlin.asJava.finder.JavaElementFinder +import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreProjectEnvironment +import org.jetbrains.kotlin.cli.jvm.compiler.setupIdeaStandaloneExecution +import org.jetbrains.kotlin.config.* +import org.jetbrains.kotlin.fir.declarations.SealedClassInheritorsProvider +import org.jetbrains.kotlin.fir.declarations.SealedClassInheritorsProviderImpl +import org.jetbrains.kotlin.idea.references.KotlinFirReferenceContributor +import org.jetbrains.kotlin.idea.references.KotlinReferenceProviderContributor +import org.jetbrains.kotlin.light.classes.symbol.KotlinAsJavaFirSupport +import org.jetbrains.kotlin.light.classes.symbol.caches.SymbolLightClassFacadeCache +import org.jetbrains.kotlin.load.kotlin.PackagePartProvider +import org.jetbrains.kotlin.psi.KotlinReferenceProvidersService +import org.jetbrains.kotlin.psi.KtFile +import kotlin.contracts.ExperimentalContracts +import kotlin.contracts.InvocationKind +import kotlin.contracts.contract + +public class StandaloneAnalysisAPISessionBuilder( + applicationDisposable: Disposable, + projectDisposable: Disposable, +) { + init { + setupIdeaStandaloneExecution() + } + + private val kotlinCoreProjectEnvironment: KotlinCoreProjectEnvironment = + StandaloneProjectFactory.createProjectEnvironment( + projectDisposable, + applicationDisposable, + ) + + public val application: Application = kotlinCoreProjectEnvironment.environment.application + + public val project: Project = kotlinCoreProjectEnvironment.project + + private lateinit var projectStructureProvider: ProjectStructureProvider + + public fun buildKtModuleProvider(init: KtModuleProviderBuilder.() -> Unit) { + projectStructureProvider = buildProjectStructureProvider(init) + } + + @Deprecated( + "Compiler configuration is not a good fit for specifying multi-module project.", + ReplaceWith("buildKtModuleProvider { }") + ) + public fun buildKtModuleProviderByCompilerConfiguration( + compilerConfiguration: CompilerConfiguration, + ) { + val project = kotlinCoreProjectEnvironment.project + projectStructureProvider = buildKtModuleProviderByCompilerConfiguration( + compilerConfiguration, + project, + getPsiFilesFromPaths(project, getSourceFilePaths(compilerConfiguration)), + ) + } + + private fun registerApplicationServices() { + kotlinCoreProjectEnvironment.environment.application.apply { + registerService(KotlinReferenceProvidersService::class.java, HLApiReferenceProviderService::class.java) + registerService(KotlinReferenceProviderContributor::class.java, KotlinFirReferenceContributor::class.java) + } + } + + public fun registerApplicationService(serviceInterface: Class, serviceImplementation: T) { + kotlinCoreProjectEnvironment.environment.application.apply { + registerService(serviceInterface, serviceImplementation) + } + } + + public fun registerApplicationService(serviceImplementation: Class) { + kotlinCoreProjectEnvironment.environment.application.apply { + registerService(serviceImplementation) + } + } + + @OptIn(KtAnalysisApiInternals::class) + private fun registerProjectServices( + ktFiles: List, + packagePartProvider: (GlobalSearchScope) -> PackagePartProvider, + ) { + val project = kotlinCoreProjectEnvironment.project + project.apply { + registerService(KotlinModificationTrackerFactory::class.java, KotlinStaticModificationTrackerFactory::class.java) + registerService(KtDefaultLifetimeTokenProvider::class.java, KtReadActionConfinementDefaultLifetimeTokenProvider::class.java) + + registerService(KtModuleScopeProvider::class.java, KtModuleScopeProviderImpl()) + registerService(KotlinModificationTrackerFactory::class.java, KotlinStaticModificationTrackerFactory::class.java) + registerService(KotlinAnnotationsResolverFactory::class.java, KotlinStaticAnnotationsResolverFactory(ktFiles)) + registerService(KotlinDeclarationProviderFactory::class.java, KotlinStaticDeclarationProviderFactory(ktFiles)) + registerService(KotlinPackageProviderFactory::class.java, KotlinStaticPackageProviderFactory(ktFiles)) + + registerService(KtAnalysisSessionProvider::class.java, KtFirAnalysisSessionProvider(this)) + registerService( + FirSealedClassInheritorsProcessorFactory::class.java, + object : FirSealedClassInheritorsProcessorFactory() { + override fun createSealedClassInheritorsProvider(): SealedClassInheritorsProvider { + return SealedClassInheritorsProviderImpl + } + } + ) + RegisterComponentService.registerLLFirResolveSessionService(this) + registerService( + PackagePartProviderFactory::class.java, + object : PackagePartProviderFactory() { + override fun createPackagePartProviderForLibrary(scope: GlobalSearchScope): PackagePartProvider { + return packagePartProvider(scope) + } + } + ) + + registerService(SymbolLightClassFacadeCache::class.java) + registerService(ClsJavaStubByVirtualFileCache::class.java) + registerService(KotlinAsJavaSupport::class.java, KotlinAsJavaFirSupport(this)) + } + + @Suppress("DEPRECATION") + PsiElementFinder.EP.getPoint(project).registerExtension(JavaElementFinder(project)) + @Suppress("DEPRECATION") + PsiElementFinder.EP.getPoint(project).registerExtension(PsiElementFinderImpl(project)) + } + + public fun registerProjectService(serviceInterface: Class, serviceImplementation: T) { + kotlinCoreProjectEnvironment.project.apply { + registerService(serviceInterface, serviceImplementation) + } + } + + public fun registerProjectService(serviceImplementation: Class) { + kotlinCoreProjectEnvironment.project.apply { + registerService(serviceImplementation) + } + } + + public fun build(): StandaloneAnalysisAPISession { + val ktModuleProviderImpl = projectStructureProvider as KtModuleProviderImpl + val modules = ktModuleProviderImpl.mainModules + val allSourceFiles = ktModuleProviderImpl.allSourceFiles() + StandaloneProjectFactory.registerServicesForProjectEnvironment( + kotlinCoreProjectEnvironment, + projectStructureProvider, + modules, + allSourceFiles, + ) + + registerApplicationServices() + + val project = kotlinCoreProjectEnvironment.project + val ktFiles = allSourceFiles.filterIsInstance() + val libraryRoots = StandaloneProjectFactory.getAllBinaryRoots(modules, kotlinCoreProjectEnvironment) + registerProjectServices( + ktFiles, + StandaloneProjectFactory.createPackagePartsProvider( + project, + libraryRoots, + ) + ) + + return StandaloneAnalysisAPISession( + kotlinCoreProjectEnvironment, + ) + } +} + +@OptIn(ExperimentalContracts::class) +public inline fun buildStandaloneAnalysisAPISession( + applicationDisposable: Disposable = Disposer.newDisposable("StandaloneAnalysisAPISession.application"), + projectDisposable: Disposable = Disposer.newDisposable("StandaloneAnalysisAPISession.project"), + init: StandaloneAnalysisAPISessionBuilder.() -> Unit +): StandaloneAnalysisAPISession { + contract { + callsInPlace(init, InvocationKind.EXACTLY_ONCE) + } + return StandaloneAnalysisAPISessionBuilder( + applicationDisposable, + projectDisposable, + ).apply(init).build() +} diff --git a/analysis/analysis-api-standalone/src/org/jetbrains/kotlin/analysis/project/structure/builder/KtSourceModuleBuilder.kt b/analysis/analysis-api-standalone/src/org/jetbrains/kotlin/analysis/project/structure/builder/KtSourceModuleBuilder.kt index 91c48610391..454cd098e63 100644 --- a/analysis/analysis-api-standalone/src/org/jetbrains/kotlin/analysis/project/structure/builder/KtSourceModuleBuilder.kt +++ b/analysis/analysis-api-standalone/src/org/jetbrains/kotlin/analysis/project/structure/builder/KtSourceModuleBuilder.kt @@ -5,6 +5,7 @@ package org.jetbrains.kotlin.analysis.project.structure.builder +import com.intellij.psi.PsiFile import org.jetbrains.kotlin.analysis.project.structure.KtSourceModule import org.jetbrains.kotlin.analysis.project.structure.impl.KtSourceModuleImpl import org.jetbrains.kotlin.config.ApiVersion @@ -20,6 +21,15 @@ public class KtSourceModuleBuilder : KtModuleBuilder() { public lateinit var moduleName: String public var languageVersionSettings: LanguageVersionSettings = LanguageVersionSettingsImpl(LanguageVersion.LATEST_STABLE, ApiVersion.LATEST) + private val sourceRoots: MutableList = mutableListOf() + + public fun addSourceRoot(file: PsiFile) { + sourceRoots.add(file) + } + + public fun addSourceRoots(files: Collection) { + sourceRoots.addAll(files) + } override fun build(): KtSourceModule { return KtSourceModuleImpl( @@ -31,6 +41,7 @@ public class KtSourceModuleBuilder : KtModuleBuilder() { project, moduleName, languageVersionSettings, + sourceRoots, ) } } diff --git a/analysis/analysis-api-standalone/src/org/jetbrains/kotlin/analysis/project/structure/impl/KtModuleProviderImpl.kt b/analysis/analysis-api-standalone/src/org/jetbrains/kotlin/analysis/project/structure/impl/KtModuleProviderImpl.kt index 3a30971c43c..4c11abc8804 100644 --- a/analysis/analysis-api-standalone/src/org/jetbrains/kotlin/analysis/project/structure/impl/KtModuleProviderImpl.kt +++ b/analysis/analysis-api-standalone/src/org/jetbrains/kotlin/analysis/project/structure/impl/KtModuleProviderImpl.kt @@ -6,10 +6,13 @@ package org.jetbrains.kotlin.analysis.project.structure.impl import com.intellij.psi.PsiElement +import com.intellij.psi.PsiFileSystemItem +import com.intellij.psi.PsiJavaFile +import org.jetbrains.kotlin.analysis.api.standalone.base.project.structure.StandaloneProjectFactory.findJvmRootsForJavaFiles import org.jetbrains.kotlin.analysis.project.structure.* internal class KtModuleProviderImpl( - private val mainModules: List, + internal val mainModules: List, ) : ProjectStructureProvider() { override fun getKtModuleForKtElement(element: PsiElement): KtModule { val containingFile = element.containingFile.virtualFile @@ -33,4 +36,10 @@ internal class KtModuleProviderImpl( .filterIsInstance() .firstOrNull { it.isBuiltinsContainingStdlib } } + + internal fun allSourceFiles(): List = buildList { + val files = mainModules.mapNotNull { (it as? KtSourceModuleImpl)?.sourceRoots }.flatten() + addAll(files) + addAll(findJvmRootsForJavaFiles(files.filterIsInstance())) + } } \ No newline at end of file diff --git a/analysis/analysis-api-standalone/src/org/jetbrains/kotlin/analysis/project/structure/impl/KtModuleUtils.kt b/analysis/analysis-api-standalone/src/org/jetbrains/kotlin/analysis/project/structure/impl/KtModuleUtils.kt index 4d382fb0fb6..c5295c9a3a6 100644 --- a/analysis/analysis-api-standalone/src/org/jetbrains/kotlin/analysis/project/structure/impl/KtModuleUtils.kt +++ b/analysis/analysis-api-standalone/src/org/jetbrains/kotlin/analysis/project/structure/impl/KtModuleUtils.kt @@ -154,6 +154,7 @@ internal fun buildKtModuleProviderByCompilerConfiguration( this.platform = platform this.project = project this.moduleName = moduleName + addSourceRoots(getPsiFilesFromPaths(project, getSourceFilePaths(compilerConfig))) } ) } diff --git a/analysis/analysis-api-standalone/src/org/jetbrains/kotlin/analysis/project/structure/impl/KtSourceModuleImpl.kt b/analysis/analysis-api-standalone/src/org/jetbrains/kotlin/analysis/project/structure/impl/KtSourceModuleImpl.kt index f54921cad8a..34a9bd108e5 100644 --- a/analysis/analysis-api-standalone/src/org/jetbrains/kotlin/analysis/project/structure/impl/KtSourceModuleImpl.kt +++ b/analysis/analysis-api-standalone/src/org/jetbrains/kotlin/analysis/project/structure/impl/KtSourceModuleImpl.kt @@ -6,6 +6,7 @@ package org.jetbrains.kotlin.analysis.project.structure.impl import com.intellij.openapi.project.Project +import com.intellij.psi.PsiFile import com.intellij.psi.search.GlobalSearchScope import org.jetbrains.kotlin.analysis.project.structure.KtModule import org.jetbrains.kotlin.analysis.project.structure.KtSourceModule @@ -22,6 +23,7 @@ internal class KtSourceModuleImpl( override val project: Project, override val moduleName: String, override val languageVersionSettings: LanguageVersionSettings, + internal val sourceRoots: List, ) : KtSourceModule, KtModuleWithPlatform { override val analyzerServices: PlatformDependentAnalyzerServices = super.analyzerServices } \ No newline at end of file diff --git a/prepare/ide-plugin-dependencies/analysis-api-standalone-for-ide/build.gradle.kts b/prepare/ide-plugin-dependencies/analysis-api-standalone-for-ide/build.gradle.kts index ccc04c9b0e9..d4189c5b663 100644 --- a/prepare/ide-plugin-dependencies/analysis-api-standalone-for-ide/build.gradle.kts +++ b/prepare/ide-plugin-dependencies/analysis-api-standalone-for-ide/build.gradle.kts @@ -2,4 +2,9 @@ plugins { kotlin("jvm") } -publishJarsForIde(listOf(":analysis:analysis-api-standalone")) +publishJarsForIde( + listOf( + ":analysis:analysis-api-standalone:analysis-api-standalone-base", + ":analysis:analysis-api-standalone", + ) +)