From 864800902bd361480b9b7ef1545a3eb574242d72 Mon Sep 17 00:00:00 2001 From: Ilya Kirillov Date: Sun, 4 Oct 2020 19:03:43 +0300 Subject: [PATCH] FIR IDE: introduce WeakFirByPsiRef --- .../api/FirModuleResolveStateForCompletion.kt | 4 ++ .../level/api/FirModuleResolveStateImpl.kt | 6 ++ .../low/level/api/annotations/annotations.kt | 2 +- .../level/api/api/FirModuleResolveState.kt | 2 + .../fir/low/level/api/api/WeakFirByPsiRef.kt | 69 +++++++++++++++++++ .../level/api/file/builder/FirFileBuilder.kt | 5 ++ 6 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/api/WeakFirByPsiRef.kt diff --git a/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/FirModuleResolveStateForCompletion.kt b/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/FirModuleResolveStateForCompletion.kt index 71901ceec52..96026b9bbd2 100644 --- a/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/FirModuleResolveStateForCompletion.kt +++ b/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/FirModuleResolveStateForCompletion.kt @@ -46,6 +46,10 @@ internal class FirModuleResolveStateForCompletion( override fun getFirFile(ktFile: KtFile): FirFile = originalState.getFirFile(ktFile) + override fun isFirFileBuilt(ktFile: KtFile): Boolean { + error("Should not be called in in completion") + } + override fun recordPsiToFirMappingsForCompletionFrom(fir: FirDeclaration, firFile: FirFile, ktFile: KtFile) { fir.accept(FirElementsRecorder(), completionMapping) } diff --git a/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/FirModuleResolveStateImpl.kt b/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/FirModuleResolveStateImpl.kt index 0487f2c92cf..67c6bcea7a7 100644 --- a/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/FirModuleResolveStateImpl.kt +++ b/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/FirModuleResolveStateImpl.kt @@ -69,6 +69,12 @@ internal class FirModuleResolveStateImpl( sessionProvider.getModuleCache(ktDeclaration.getModuleInfo() as ModuleSourceInfo) ) + override fun isFirFileBuilt(ktFile: KtFile): Boolean { + val moduleSourceInfo = ktFile.getModuleInfo() as? ModuleSourceInfo ?: return true + val cache = sessionProvider.getModuleCache(moduleSourceInfo) + return firFileBuilder.isFirFileBuilt(ktFile, cache) + } + override fun resolvedFirToPhase(declaration: D, toPhase: FirResolvePhase): D { val fileCache = when (val session = declaration.session) { is FirIdeSourcesSession -> session.cache diff --git a/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/annotations/annotations.kt b/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/annotations/annotations.kt index ec2049e2fbc..5c9cae1a483 100644 --- a/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/annotations/annotations.kt +++ b/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/annotations/annotations.kt @@ -5,6 +5,6 @@ package org.jetbrains.kotlin.idea.fir.low.level.api.annotations -@Target(AnnotationTarget.PROPERTY, AnnotationTarget.FUNCTION) +@Target(AnnotationTarget.PROPERTY, AnnotationTarget.FUNCTION, AnnotationTarget.CONSTRUCTOR) @RequiresOptIn annotation class PrivateForInline \ No newline at end of file diff --git a/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/api/FirModuleResolveState.kt b/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/api/FirModuleResolveState.kt index d174a485c8c..e392f286a35 100644 --- a/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/api/FirModuleResolveState.kt +++ b/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/api/FirModuleResolveState.kt @@ -37,6 +37,8 @@ abstract class FirModuleResolveState { internal abstract fun getFirFile(ktFile: KtFile): FirFile + internal abstract fun isFirFileBuilt(ktFile: KtFile): Boolean + internal abstract fun getDiagnostics(element: KtElement): List internal abstract fun collectDiagnosticsForFile(ktFile: KtFile): Collection diff --git a/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/api/WeakFirByPsiRef.kt b/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/api/WeakFirByPsiRef.kt new file mode 100644 index 00000000000..7a2a43be8e4 --- /dev/null +++ b/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/api/WeakFirByPsiRef.kt @@ -0,0 +1,69 @@ +/* + * Copyright 2010-2020 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.idea.fir.low.level.api.api + +import org.jetbrains.kotlin.fir.declarations.FirDeclaration +import org.jetbrains.kotlin.fir.declarations.FirResolvePhase +import org.jetbrains.kotlin.idea.fir.low.level.api.annotations.PrivateForInline +import org.jetbrains.kotlin.psi.KtDeclaration +import java.lang.ref.WeakReference +import kotlin.reflect.KClass + + +/** + * Lazy [FirDeclaration] reference which allows to find corresponding non-local [FirDeclaration] by [KtDeclaration] + * Builds declaration only when [withFir] is called or some other entity already built it + * Allows approximately to check if we already have corresponding [FirDeclaration] built by [isFirDeclarationAlreadyBuilt] + * + * To create one consider using [weakByPsiRef] + */ +class WeakFirByPsiRef @PrivateForInline constructor( + val ktDeclaration: KT, + @PrivateForInline val firClass: KClass, + resolveState: FirModuleResolveState, +) { + @PrivateForInline + val resolveStateWeakRef = WeakReference(resolveState) + + /** + * Checks if corresponding [FirDeclaration] is available now + * If return true consequent call to [withFir] will not build new RAW_FIR as it is already built + * If return false consequent call to [withFir] will build new [FirDeclaration] + * if it was not build between [isFirDeclarationAlreadyBuilt] & [withFir] calls + * + * Should be used with caution as this is only approximation and may return inaccurate results + */ + @OptIn(PrivateForInline::class) + fun isFirDeclarationAlreadyBuilt(): Boolean { + val resolveState = resolveStateWeakRef.get() + ?: error("FirModuleResolveState was garbage collected") + return resolveState.isFirFileBuilt(ktDeclaration.containingKtFile) + } + + /** + * Creates [FirDeclaration] by [KtDeclaration] if it is not created previously and runs an [action] with it + * [FirDeclaration] passed to [action] should not be leaked outside [action] lambda + * Otherwise, some threading problems may arise.` + * + * [FirDeclaration] passed to [action] will be resolved at least to [phase] + **/ + @OptIn(PrivateForInline::class) + inline fun withFir(phase: FirResolvePhase = FirResolvePhase.RAW_FIR, action: (fir: FIR) -> R): R { + val resolveState = resolveStateWeakRef.get() + ?: error("FirModuleResolveState was garbage collected") + return LowLevelFirApiFacade.withFirDeclaration(ktDeclaration, resolveState, phase) { fir -> + if (!firClass.isInstance(fir)) throw InvalidFirElementTypeException(ktDeclaration, firClass, fir::class) + @Suppress("UNCHECKED_CAST") + action(fir as FIR) + } + } +} + +@OptIn(PrivateForInline::class) +inline fun weakByPsiRef( + ktDeclaration: KT, + resolveState: FirModuleResolveState, +) = WeakFirByPsiRef(ktDeclaration, FIR::class, resolveState) \ No newline at end of file diff --git a/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/file/builder/FirFileBuilder.kt b/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/file/builder/FirFileBuilder.kt index ec2a58d4fe0..423dc2878ac 100644 --- a/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/file/builder/FirFileBuilder.kt +++ b/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/file/builder/FirFileBuilder.kt @@ -36,6 +36,11 @@ internal class FirFileBuilder( RawFirBuilder(cache.session, scopeProvider, stubMode = false).buildFirFile(ktFile) } + fun isFirFileBuilt( + ktFile: KtFile, + cache: ModuleFileCache + ): Boolean = cache.getCachedFirFile(ktFile) != null + fun getFirFileResolvedToPhaseWithCaching( ktFile: KtFile, cache: ModuleFileCache,