[AA] Implement disposable KtResolveExtensions
- This commit adds the `Disposable` interface to `KtResolveExtension`. Resolve extensions are disposed after their associated session has been invalidated, or reclaimed by the GC. - Example use case: Opening a message bus connection with the lifetime of the resolve extension. ^KT-61222 fixed
This commit is contained in:
committed by
Space Team
parent
efb56ed30a
commit
7a5a1833e7
+28
-5
@@ -5,23 +5,43 @@
|
||||
|
||||
package org.jetbrains.kotlin.analysis.api.resolve.extensions
|
||||
|
||||
import com.intellij.openapi.Disposable
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
|
||||
/**
|
||||
* Provides a list of Kotlin files which provides additional, generated declarations for resolution.
|
||||
* Provides a list of Kotlin files which contain additional generated declarations for resolution.
|
||||
*
|
||||
* Provided by the [KtResolveExtensionProvider].
|
||||
* It is created by [KtResolveExtensionProvider].
|
||||
*
|
||||
* All member implementations should:
|
||||
* - consider caching the results for subsequent invocations.
|
||||
* - be lightweight and not build the whole file structure inside.
|
||||
* - not use the Kotlin resolve inside, as this function is called during session initialization, so Analysis API access is forbidden.
|
||||
* - not use Kotlin resolve inside, as these functions are called during session initialization, so Analysis API access is forbidden.
|
||||
*
|
||||
* #### Lifecycle Management
|
||||
*
|
||||
* A [KtResolveExtension] is tied to the lifetime of its module's analysis session. It is created by [KtResolveExtensionProvider] during
|
||||
* creation of an analysis session, and disposed after the analysis session has been invalidated.
|
||||
*
|
||||
* [KtResolveExtension] implements the [Disposable] interface. The resolve extension can then act as a parent disposable, e.g. for a message
|
||||
* bus connection.
|
||||
*
|
||||
* You *must not* implement [KtResolveExtension]s as module-level services, due to the following reasons:
|
||||
*
|
||||
* 1. The IntelliJ platform SDK [discourages](https://plugins.jetbrains.com/docs/intellij/plugin-services.html#types) the use of
|
||||
* module-level services due to memory consumption. In particular, resolve extensions implemented as module-level services live longer
|
||||
* than their corresponding analysis session, so the resolve extension would not be garbage collected after its corresponding analysis
|
||||
* session has been invalidated.
|
||||
* 2. The module-level service living longer than the analysis session increases the risk of caching invalidated entities in a resolve
|
||||
* extension.
|
||||
* 3. Because the [KtResolveExtension] is a [Disposable], if implemented as a module-level service, the service would be disposed too early
|
||||
* during invalidation of the corresponding analysis session.
|
||||
*
|
||||
* @see KtResolveExtensionFile
|
||||
* @see KtResolveExtensionProvider
|
||||
*/
|
||||
public abstract class KtResolveExtension {
|
||||
public abstract class KtResolveExtension : Disposable {
|
||||
/**
|
||||
* Get the list of files that should be generated for the module. Returned files should contain valid Kotlin code.
|
||||
*
|
||||
@@ -64,4 +84,7 @@ public abstract class KtResolveExtension {
|
||||
* (potentially stale) externally generated sources.
|
||||
*/
|
||||
public open fun getShadowedScope(): GlobalSearchScope = GlobalSearchScope.EMPTY_SCOPE
|
||||
}
|
||||
|
||||
override fun dispose() {
|
||||
}
|
||||
}
|
||||
|
||||
+12
-3
@@ -6,6 +6,7 @@
|
||||
package org.jetbrains.kotlin.analysis.low.level.api.fir.project.structure
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.Disposer
|
||||
import org.jetbrains.kotlin.analysis.api.resolve.extensions.KtResolveExtensionProvider
|
||||
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.services.createSealedInheritorsProvider
|
||||
import org.jetbrains.kotlin.analysis.low.level.api.fir.compile.CodeFragmentScopeProvider
|
||||
@@ -39,9 +40,17 @@ internal fun LLFirSession.registerIdeComponents(project: Project) {
|
||||
register(SealedClassInheritorsProvider::class, createSealedInheritorsProvider(project))
|
||||
register(FirExceptionHandler::class, LLFirExceptionHandler)
|
||||
register(CodeFragmentScopeProvider::class, CodeFragmentScopeProvider(this))
|
||||
createResolveExtensionTool()?.let {
|
||||
register(LLFirResolveExtensionTool::class, it)
|
||||
}
|
||||
registerResolveExtensionTool()
|
||||
}
|
||||
|
||||
@SessionConfiguration
|
||||
private fun LLFirSession.registerResolveExtensionTool() {
|
||||
val resolveExtensionTool = createResolveExtensionTool() ?: return
|
||||
|
||||
// `KtResolveExtension`s are disposables meant to be tied to the lifetime of the `LLFirSession`.
|
||||
resolveExtensionTool.extensions.forEach { Disposer.register(requestDisposable(), it) }
|
||||
|
||||
register(LLFirResolveExtensionTool::class, resolveExtensionTool)
|
||||
}
|
||||
|
||||
private fun LLFirSession.createResolveExtensionTool(): LLFirResolveExtensionTool? {
|
||||
|
||||
+3
-1
@@ -37,6 +37,8 @@ import java.util.concurrent.ConcurrentHashMap
|
||||
* for the [org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProvider].
|
||||
*/
|
||||
abstract class LLFirResolveExtensionTool : FirSessionComponent {
|
||||
internal abstract val extensions: List<KtResolveExtension>
|
||||
|
||||
abstract val declarationProvider: LLFirResolveExtensionToolDeclarationProvider
|
||||
abstract val packageProvider: KotlinPackageProvider
|
||||
abstract val packageFilter: LLFirResolveExtensionToolPackageFilter
|
||||
@@ -48,7 +50,7 @@ val FirSession.llResolveExtensionTool: LLFirResolveExtensionTool? by FirSession.
|
||||
|
||||
internal class LLFirNonEmptyResolveExtensionTool(
|
||||
session: LLFirSession,
|
||||
extensions: List<KtResolveExtension>,
|
||||
override val extensions: List<KtResolveExtension>,
|
||||
) : LLFirResolveExtensionTool() {
|
||||
init {
|
||||
require(extensions.isNotEmpty())
|
||||
|
||||
Reference in New Issue
Block a user