[Analysis API] forbid providing custom KtLifetimeToken for every analyze call

^KT-60488 fixed
This commit is contained in:
Ilya Kirillov
2023-07-17 14:21:09 +02:00
committed by Space Team
parent b617b24d48
commit 4b523825be
7 changed files with 41 additions and 63 deletions
@@ -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")
}
@@ -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),
@@ -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
@@ -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))
@@ -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 <R> 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 <R> 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 <R> 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 <R> analyzeInDependedAnalysisSession(
*/
public inline fun <R> 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 <R> analyzeWithReadAction(
public inline fun <R> analyzeInModalWindow(
contextElement: KtElement,
windowTitle: String,
nonDefaultLifetimeTokenFactory: KtLifetimeTokenFactory? = null,
crossinline action: KtAnalysisSession.() -> R
): R {
ApplicationManager.getApplication().assertIsDispatchThread()
val task = object : Task.WithResult<R, Exception>(contextElement.project, windowTitle, /*canBeCancelled*/ true) {
override fun compute(indicator: ProgressIndicator): R =
analyzeWithReadAction(contextElement, nonDefaultLifetimeTokenFactory) { action() }
analyzeWithReadAction(contextElement) { action() }
}
task.queue()
return task.result
@@ -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
}
}
@@ -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 <R> 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 <R> 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 <R> 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 <R> 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()
}
}