From 4b523825be1a6f67fa6aef601c7d3c72b673e986 Mon Sep 17 00:00:00 2001 From: Ilya Kirillov Date: Mon, 17 Jul 2023 14:21:09 +0200 Subject: [PATCH] [Analysis API] forbid providing custom KtLifetimeToken for every `analyze` call ^KT-60488 fixed --- .../KtFe10CliAnalysisSessionProvider.kt | 6 +-- .../api/fir/KtFirAnalysisSessionProvider.kt | 10 ++--- .../AnalysisApiBaseTestServiceRegistrar.kt | 6 +-- .../StandaloneAnalysisAPISessionBuilder.kt | 6 +-- .../jetbrains/kotlin/analysis/api/analyze.kt | 22 +++------- ...Provider.kt => KtLifetimeTokenProvider.kt} | 12 +++--- .../api/session/KtAnalysisSessionProvider.kt | 42 +++++++------------ 7 files changed, 41 insertions(+), 63 deletions(-) rename analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/lifetime/{KtDefaultLifetimeTokenProvider.kt => KtLifetimeTokenProvider.kt} (54%) diff --git a/analysis/analysis-api-fe10/src/org/jetbrains/kotlin/analysis/api/descriptors/KtFe10CliAnalysisSessionProvider.kt b/analysis/analysis-api-fe10/src/org/jetbrains/kotlin/analysis/api/descriptors/KtFe10CliAnalysisSessionProvider.kt index eee9688a8b3..e3974efb984 100644 --- a/analysis/analysis-api-fe10/src/org/jetbrains/kotlin/analysis/api/descriptors/KtFe10CliAnalysisSessionProvider.kt +++ b/analysis/analysis-api-fe10/src/org/jetbrains/kotlin/analysis/api/descriptors/KtFe10CliAnalysisSessionProvider.kt @@ -17,11 +17,11 @@ import java.lang.UnsupportedOperationException @OptIn(KtAnalysisApiInternals::class) class KtFe10AnalysisSessionProvider(project: Project) : KtAnalysisSessionProvider(project) { - override fun getAnalysisSession(useSiteKtElement: KtElement, factory: KtLifetimeTokenFactory): KtAnalysisSession { - return KtFe10AnalysisSession(project, useSiteKtElement, factory.create(project)) + override fun getAnalysisSession(useSiteKtElement: KtElement): KtAnalysisSession { + return KtFe10AnalysisSession(project, useSiteKtElement, tokenFactory.create(project)) } - override fun getAnalysisSessionByUseSiteKtModule(useSiteKtModule: KtModule, factory: KtLifetimeTokenFactory): KtAnalysisSession { + override fun getAnalysisSessionByUseSiteKtModule(useSiteKtModule: KtModule): KtAnalysisSession { throw UnsupportedOperationException("getAnalysisSessionByModule() should not be used on KtFe10AnalysisSession") } diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/KtFirAnalysisSessionProvider.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/KtFirAnalysisSessionProvider.kt index f47b8ea88e0..d0486342b30 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/KtFirAnalysisSessionProvider.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/KtFirAnalysisSessionProvider.kt @@ -35,17 +35,17 @@ class KtFirAnalysisSessionProvider(project: Project) : KtAnalysisSessionProvider LowMemoryWatcher.register(::clearCaches, project) } - override fun getAnalysisSession(useSiteKtElement: KtElement, factory: KtLifetimeTokenFactory): KtAnalysisSession { + override fun getAnalysisSession(useSiteKtElement: KtElement): KtAnalysisSession { val module = ProjectStructureProvider.getModule(project, useSiteKtElement, contextualModule = null) - return getAnalysisSessionByUseSiteKtModule(module, factory) + return getAnalysisSessionByUseSiteKtModule(module) } - override fun getAnalysisSessionByUseSiteKtModule(useSiteKtModule: KtModule, factory: KtLifetimeTokenFactory): KtAnalysisSession { - val key = Pair(useSiteKtModule, factory.identifier) + override fun getAnalysisSessionByUseSiteKtModule(useSiteKtModule: KtModule): KtAnalysisSession { + val key = Pair(useSiteKtModule, tokenFactory.identifier) return cache.computeIfAbsent(key) { CachedValuesManager.getManager(project).createCachedValue { val firResolveSession = useSiteKtModule.getFirResolveSession(project) - val validityToken = factory.create(project) + val validityToken = tokenFactory.create(project) CachedValueProvider.Result( KtFirAnalysisSession.createAnalysisSessionByFirResolveSession(firResolveSession, validityToken), diff --git a/analysis/analysis-api-impl-base/tests/org/jetbrains/kotlin/analysis/api/impl/base/test/configurators/AnalysisApiBaseTestServiceRegistrar.kt b/analysis/analysis-api-impl-base/tests/org/jetbrains/kotlin/analysis/api/impl/base/test/configurators/AnalysisApiBaseTestServiceRegistrar.kt index 22d6f1843ef..58a554ef606 100644 --- a/analysis/analysis-api-impl-base/tests/org/jetbrains/kotlin/analysis/api/impl/base/test/configurators/AnalysisApiBaseTestServiceRegistrar.kt +++ b/analysis/analysis-api-impl-base/tests/org/jetbrains/kotlin/analysis/api/impl/base/test/configurators/AnalysisApiBaseTestServiceRegistrar.kt @@ -10,8 +10,8 @@ import com.intellij.mock.MockProject import com.intellij.openapi.project.Project import com.intellij.psi.PsiClass import org.jetbrains.kotlin.analysis.api.KtAnalysisApiInternals -import org.jetbrains.kotlin.analysis.api.lifetime.KtDefaultLifetimeTokenProvider -import org.jetbrains.kotlin.analysis.api.lifetime.KtReadActionConfinementDefaultLifetimeTokenProvider +import org.jetbrains.kotlin.analysis.api.lifetime.KtLifetimeTokenProvider +import org.jetbrains.kotlin.analysis.api.lifetime.KtReadActionConfinementLifetimeTokenProvider 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.decompiled.light.classes.DecompiledLightClassesFactory @@ -38,7 +38,7 @@ object AnalysisApiBaseTestServiceRegistrar: AnalysisApiTestServiceRegistrar() { override fun registerProjectServices(project: MockProject, testServices: TestServices) { project.apply { registerService(KotlinModificationTrackerFactory::class.java, KotlinStaticModificationTrackerFactory::class.java) - registerService(KtDefaultLifetimeTokenProvider::class.java, KtReadActionConfinementDefaultLifetimeTokenProvider::class.java) + registerService(KtLifetimeTokenProvider::class.java, KtReadActionConfinementLifetimeTokenProvider::class.java) //KotlinClassFileDecompiler is registered as application service so it's available for the tests run in parallel as well //when the decompiler is registered, for compiled class KtClsFile is created instead of ClsFileImpl 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 index dd4da8a6eed..08c11318baa 100644 --- 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 @@ -16,8 +16,8 @@ import kotlin.contracts.ExperimentalContracts import kotlin.contracts.InvocationKind import kotlin.contracts.contract import org.jetbrains.kotlin.analysis.api.KtAnalysisApiInternals -import org.jetbrains.kotlin.analysis.api.lifetime.KtDefaultLifetimeTokenProvider -import org.jetbrains.kotlin.analysis.api.lifetime.KtReadActionConfinementDefaultLifetimeTokenProvider +import org.jetbrains.kotlin.analysis.api.lifetime.KtLifetimeTokenProvider +import org.jetbrains.kotlin.analysis.api.lifetime.KtReadActionConfinementLifetimeTokenProvider import org.jetbrains.kotlin.analysis.api.standalone.base.project.structure.FirStandaloneServiceRegistrar import org.jetbrains.kotlin.analysis.api.standalone.base.project.structure.StandaloneProjectFactory import org.jetbrains.kotlin.analysis.low.level.api.fir.api.services.FirSealedClassInheritorsProcessorFactory @@ -121,7 +121,7 @@ public class StandaloneAnalysisAPISessionBuilder( FirStandaloneServiceRegistrar.registerProjectModelServices(project, kotlinCoreProjectEnvironment.parentDisposable) registerService(KotlinModificationTrackerFactory::class.java, KotlinStaticModificationTrackerFactory::class.java) - registerService(KtDefaultLifetimeTokenProvider::class.java, KtReadActionConfinementDefaultLifetimeTokenProvider::class.java) + registerService(KtLifetimeTokenProvider::class.java, KtReadActionConfinementLifetimeTokenProvider::class.java) registerService(KtModuleScopeProvider::class.java, KtModuleScopeProviderImpl()) registerService(KotlinAnnotationsResolverFactory::class.java, KotlinStaticAnnotationsResolverFactory(ktFiles)) diff --git a/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/analyze.kt b/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/analyze.kt index d3e3bdda740..d053233d41e 100644 --- a/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/analyze.kt +++ b/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/analyze.kt @@ -22,50 +22,40 @@ import org.jetbrains.kotlin.psi.KtFile * Uses [useSiteKtElement] as an [KtElement] which containing module is a use-site module, * i.e, the module from which perspective the project will be analyzed. * - * [nonDefaultLifetimeTokenFactory] represents lifetime and accessibility guaranties - * which will be applied to the [org.jetbrains.kotlin.analysis.api.lifetime.KtLifetimeOwner] instances created during code analysis. - * * @see KtAnalysisSession - * @see KtLifetimeTokenFactory * @see analyzeWithReadAction */ public inline fun analyze( useSiteKtElement: KtElement, - nonDefaultLifetimeTokenFactory: KtLifetimeTokenFactory? = null, action: KtAnalysisSession.() -> R ): R = KtAnalysisSessionProvider.getInstance(useSiteKtElement.project) - .analyse(useSiteKtElement, nonDefaultLifetimeTokenFactory, action) + .analyse(useSiteKtElement, action) /** * Execute given [action] in [KtAnalysisSession] context * Uses [useSiteKtModule] as use-site module, i.e, the module from which perspective the project will be analyzed. * - * [nonDefaultLifetimeTokenFactory] represents lifetime and accessibility guaranties - * which will be applied to the [org.jetbrains.kotlin.analysis.api.lifetime.KtLifetimeOwner] instances created during code analysis. - * * @see KtAnalysisSession * @see KtLifetimeTokenFactory */ public inline fun analyze( useSiteKtModule: KtModule, - nonDefaultLifetimeTokenFactory: KtLifetimeTokenFactory? = null, crossinline action: KtAnalysisSession.() -> R ): R { val sessionProvider = KtAnalysisSessionProvider.getInstance(useSiteKtModule.project) - return sessionProvider.analyze(useSiteKtModule, nonDefaultLifetimeTokenFactory, action) + return sessionProvider.analyze(useSiteKtModule, action) } public inline fun analyzeInDependedAnalysisSession( originalFile: KtFile, elementToReanalyze: KtElement, - nonDefaultLifetimeTokenFactory: KtLifetimeTokenFactory? = null, action: KtAnalysisSession.() -> R ): R = KtAnalysisSessionProvider.getInstance(originalFile.project) - .analyseInDependedAnalysisSession(originalFile, elementToReanalyze, nonDefaultLifetimeTokenFactory, action) + .analyseInDependedAnalysisSession(originalFile, elementToReanalyze, action) /** * Execute given [action] in [KtAnalysisSession] context like [analyze] does but execute it in read action @@ -81,10 +71,9 @@ public inline fun analyzeInDependedAnalysisSession( */ public inline fun analyzeWithReadAction( contextElement: KtElement, - nonDefaultLifetimeTokenFactory: KtLifetimeTokenFactory? = null, crossinline action: KtAnalysisSession.() -> R ): R = ApplicationManager.getApplication().runReadAction(Computable { - analyze(contextElement, nonDefaultLifetimeTokenFactory, action) + analyze(contextElement, action) }) /** @@ -97,13 +86,12 @@ public inline fun analyzeWithReadAction( public inline fun analyzeInModalWindow( contextElement: KtElement, windowTitle: String, - nonDefaultLifetimeTokenFactory: KtLifetimeTokenFactory? = null, crossinline action: KtAnalysisSession.() -> R ): R { ApplicationManager.getApplication().assertIsDispatchThread() val task = object : Task.WithResult(contextElement.project, windowTitle, /*canBeCancelled*/ true) { override fun compute(indicator: ProgressIndicator): R = - analyzeWithReadAction(contextElement, nonDefaultLifetimeTokenFactory) { action() } + analyzeWithReadAction(contextElement) { action() } } task.queue() return task.result diff --git a/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/lifetime/KtDefaultLifetimeTokenProvider.kt b/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/lifetime/KtLifetimeTokenProvider.kt similarity index 54% rename from analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/lifetime/KtDefaultLifetimeTokenProvider.kt rename to analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/lifetime/KtLifetimeTokenProvider.kt index 4051c7fe9e6..561b596f469 100644 --- a/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/lifetime/KtDefaultLifetimeTokenProvider.kt +++ b/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/lifetime/KtLifetimeTokenProvider.kt @@ -9,19 +9,19 @@ import com.intellij.openapi.project.Project import org.jetbrains.kotlin.analysis.api.KtAnalysisApiInternals @KtAnalysisApiInternals -public abstract class KtDefaultLifetimeTokenProvider { - public abstract fun getDefaultLifetimeTokenFactory(): KtLifetimeTokenFactory +public abstract class KtLifetimeTokenProvider { + public abstract fun getLifetimeTokenFactory(): KtLifetimeTokenFactory public companion object { @KtAnalysisApiInternals - public fun getService(project: Project): KtDefaultLifetimeTokenProvider = - project.getService(KtDefaultLifetimeTokenProvider::class.java) + public fun getService(project: Project): KtLifetimeTokenProvider = + project.getService(KtLifetimeTokenProvider::class.java) } } @KtAnalysisApiInternals -public class KtReadActionConfinementDefaultLifetimeTokenProvider: KtDefaultLifetimeTokenProvider() { - override fun getDefaultLifetimeTokenFactory(): KtLifetimeTokenFactory { +public class KtReadActionConfinementLifetimeTokenProvider : KtLifetimeTokenProvider() { + override fun getLifetimeTokenFactory(): KtLifetimeTokenFactory { return KtReadActionConfinementLifetimeTokenFactory } } \ No newline at end of file diff --git a/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/session/KtAnalysisSessionProvider.kt b/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/session/KtAnalysisSessionProvider.kt index bb88f556b28..b8487abe271 100644 --- a/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/session/KtAnalysisSessionProvider.kt +++ b/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/session/KtAnalysisSessionProvider.kt @@ -11,7 +11,7 @@ import org.jetbrains.annotations.TestOnly import org.jetbrains.kotlin.analysis.api.KtAnalysisSession import org.jetbrains.kotlin.analysis.api.KtAnalysisApiInternals import org.jetbrains.kotlin.analysis.api.lifetime.impl.NoWriteActionInAnalyseCallChecker -import org.jetbrains.kotlin.analysis.api.lifetime.KtDefaultLifetimeTokenProvider +import org.jetbrains.kotlin.analysis.api.lifetime.KtLifetimeTokenProvider import org.jetbrains.kotlin.analysis.api.lifetime.KtLifetimeTokenFactory import org.jetbrains.kotlin.analysis.project.structure.KtModule import org.jetbrains.kotlin.psi.KtElement @@ -21,63 +21,53 @@ import org.jetbrains.kotlin.psi.KtFile * Provides [KtAnalysisSession] by [contextElement] * Should not be used directly, consider using [analyse]/[analyzeWithReadAction]/[analyzeInModalWindow] instead */ -@KtAnalysisApiInternals +@OptIn(KtAnalysisApiInternals::class) public abstract class KtAnalysisSessionProvider(public val project: Project) : Disposable { + @KtAnalysisApiInternals + public val tokenFactory: KtLifetimeTokenFactory = KtLifetimeTokenProvider.getService(project).getLifetimeTokenFactory() @Suppress("LeakingThis") public val noWriteActionInAnalyseCallChecker: NoWriteActionInAnalyseCallChecker = NoWriteActionInAnalyseCallChecker(this) - public abstract fun getAnalysisSession(useSiteKtElement: KtElement, factory: KtLifetimeTokenFactory): KtAnalysisSession + public abstract fun getAnalysisSession(useSiteKtElement: KtElement): KtAnalysisSession - public abstract fun getAnalysisSessionByUseSiteKtModule(useSiteKtModule: KtModule, factory: KtLifetimeTokenFactory): KtAnalysisSession + public abstract fun getAnalysisSessionByUseSiteKtModule(useSiteKtModule: KtModule): KtAnalysisSession public inline fun analyseInDependedAnalysisSession( originalFile: KtFile, elementToReanalyze: KtElement, - nonDefaultLifetimeTokenFactory: KtLifetimeTokenFactory?, - action: KtAnalysisSession.() -> R + action: KtAnalysisSession.() -> R, ): R { - val factory = - nonDefaultLifetimeTokenFactory ?: KtDefaultLifetimeTokenProvider.getService(project).getDefaultLifetimeTokenFactory() - - val originalAnalysisSession = getAnalysisSession(originalFile, factory) + val originalAnalysisSession = getAnalysisSession(originalFile) val dependedAnalysisSession = originalAnalysisSession .createContextDependentCopy(originalFile, elementToReanalyze) - return analyse(dependedAnalysisSession, factory, action) + return analyse(dependedAnalysisSession, action) } public inline fun analyse( useSiteKtElement: KtElement, - nonDefaultLifetimeTokenFactory: KtLifetimeTokenFactory?, - action: KtAnalysisSession.() -> R + action: KtAnalysisSession.() -> R, ): R { - val factory = - nonDefaultLifetimeTokenFactory ?: KtDefaultLifetimeTokenProvider.getService(project).getDefaultLifetimeTokenFactory() - return analyse(getAnalysisSession(useSiteKtElement, factory), factory, action) + return analyse(getAnalysisSession(useSiteKtElement), action) } public inline fun analyze( useSiteKtModule: KtModule, - nonDefaultLifetimeTokenFactory: KtLifetimeTokenFactory?, - action: KtAnalysisSession.() -> R + action: KtAnalysisSession.() -> R, ): R { - val factory = - nonDefaultLifetimeTokenFactory ?: KtDefaultLifetimeTokenProvider.getService(project).getDefaultLifetimeTokenFactory() - - return analyse(getAnalysisSessionByUseSiteKtModule(useSiteKtModule, factory), factory, action) + return analyse(getAnalysisSessionByUseSiteKtModule(useSiteKtModule), action) } public inline fun analyse( analysisSession: KtAnalysisSession, - factory: KtLifetimeTokenFactory, - action: KtAnalysisSession.() -> R + action: KtAnalysisSession.() -> R, ): R { noWriteActionInAnalyseCallChecker.beforeEnteringAnalysisContext() - factory.beforeEnteringAnalysisContext(analysisSession.token) + tokenFactory.beforeEnteringAnalysisContext(analysisSession.token) return try { analysisSession.action() } finally { - factory.afterLeavingAnalysisContext(analysisSession.token) + tokenFactory.afterLeavingAnalysisContext(analysisSession.token) noWriteActionInAnalyseCallChecker.afterLeavingAnalysisContext() } }