[AA] KT-56617 Build FirClassSymbols for Java classes lazily
- Some completion performance tests (e.g. IntelliJ: "empty place typing with library cache") spent a considerable amount of time in `KtFirSymbolProviderByJavaPsi.getNamedClassSymbol`. - This commit introduces `KtFirPsiJavaClassSymbol`, which implements many `KtNamedClassOrObjectSymbol` properties with the `PsiClass` instead of the FIR class. The `FirClassSymbol` is built only when necessary. This improves performance when no "slow" properties need to be computed for the symbol. ^KT-56617 fixed
This commit is contained in:
committed by
Space Team
parent
e70a3a899f
commit
1c46d37663
+2
@@ -39,6 +39,7 @@ import org.jetbrains.kotlin.fir.expressions.FirAnonymousObjectExpression
|
||||
import org.jetbrains.kotlin.fir.java.JavaScopeProvider
|
||||
import org.jetbrains.kotlin.fir.java.declarations.FirJavaClass
|
||||
import org.jetbrains.kotlin.analysis.api.fir.scopes.JavaClassDeclaredMembersEnhancementScope
|
||||
import org.jetbrains.kotlin.analysis.api.fir.symbols.KtFirPsiJavaClassSymbol
|
||||
import org.jetbrains.kotlin.fir.resolve.ScopeSession
|
||||
import org.jetbrains.kotlin.fir.resolve.calls.FirSyntheticPropertiesScope
|
||||
import org.jetbrains.kotlin.fir.resolve.scope
|
||||
@@ -68,6 +69,7 @@ internal class KtFirScopeProvider(
|
||||
private inline fun <T> KtSymbolWithMembers.withFirForScope(crossinline body: (FirClass) -> T): T? {
|
||||
return when (this) {
|
||||
is KtFirNamedClassOrObjectSymbol -> body(firSymbol.fir)
|
||||
is KtFirPsiJavaClassSymbol -> body(firSymbol.fir)
|
||||
is KtFirAnonymousObjectSymbol -> body(firSymbol.fir)
|
||||
is KtFirEnumEntrySymbol -> {
|
||||
firSymbol.lazyResolveToPhase(FirResolvePhase.BODY_RESOLVE)
|
||||
|
||||
+2
-6
@@ -9,11 +9,10 @@ import com.intellij.psi.*
|
||||
import com.intellij.psi.impl.compiled.ClsElementImpl
|
||||
import org.jetbrains.kotlin.analysis.api.KtAnalysisApiInternals
|
||||
import org.jetbrains.kotlin.analysis.api.fir.KtFirAnalysisSession
|
||||
import org.jetbrains.kotlin.analysis.api.fir.symbols.KtFirPsiJavaClassSymbol
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.KtCallableSymbol
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.KtNamedClassOrObjectSymbol
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.KtSymbolProviderByJavaPsi
|
||||
import org.jetbrains.kotlin.analysis.low.level.api.fir.providers.firClassByPsiClassProvider
|
||||
import org.jetbrains.kotlin.analysis.project.structure.getKtModule
|
||||
import org.jetbrains.kotlin.asJava.KtLightClassMarker
|
||||
import org.jetbrains.kotlin.load.java.JvmAnnotationNames
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
@@ -28,10 +27,7 @@ internal class KtFirSymbolProviderByJavaPsi(
|
||||
if (psiClass is KtLightClassMarker) return null
|
||||
if (psiClass.isKotlinCompiledClass()) return null
|
||||
|
||||
val module = psiClass.getKtModule(analysisSession.project)
|
||||
val provider = firResolveSession.getSessionFor(module).firClassByPsiClassProvider
|
||||
val firClass = provider.getFirClass(psiClass) ?: return null
|
||||
return firSymbolBuilder.classifierBuilder.buildNamedClassOrObjectSymbol(firClass)
|
||||
return KtFirPsiJavaClassSymbol(psiClass, analysisSession)
|
||||
}
|
||||
|
||||
private fun PsiClass.isKotlinCompiledClass() =
|
||||
|
||||
+3
-20
@@ -10,9 +10,9 @@ import org.jetbrains.kotlin.analysis.api.KtAnalysisApiInternals
|
||||
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
|
||||
import org.jetbrains.kotlin.analysis.api.base.KtContextReceiver
|
||||
import org.jetbrains.kotlin.analysis.api.fir.KtFirAnalysisSession
|
||||
import org.jetbrains.kotlin.analysis.api.fir.KtSymbolByFirBuilder
|
||||
import org.jetbrains.kotlin.analysis.api.fir.annotations.KtFirAnnotationListForDeclaration
|
||||
import org.jetbrains.kotlin.analysis.api.fir.findPsi
|
||||
import org.jetbrains.kotlin.analysis.api.fir.symbols.pointers.KtFirClassLikeSymbolPointer
|
||||
import org.jetbrains.kotlin.analysis.api.fir.utils.cached
|
||||
import org.jetbrains.kotlin.analysis.api.impl.base.symbols.toKtClassKind
|
||||
import org.jetbrains.kotlin.analysis.api.lifetime.KtLifetimeToken
|
||||
@@ -20,15 +20,11 @@ import org.jetbrains.kotlin.analysis.api.lifetime.withValidityAssertion
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.KtClassKind
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.KtNamedClassOrObjectSymbol
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.markers.KtSymbolKind
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.pointers.CanNotCreateSymbolPointerForLocalLibraryDeclarationException
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.pointers.KtPsiBasedSymbolPointer
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.pointers.KtSymbolPointer
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.pointers.UnsupportedSymbolKind
|
||||
import org.jetbrains.kotlin.analysis.api.types.KtType
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.descriptors.Visibilities
|
||||
import org.jetbrains.kotlin.descriptors.Visibility
|
||||
import org.jetbrains.kotlin.fir.declarations.FirTypeParameter
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.*
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
@@ -93,12 +89,9 @@ internal class KtFirNamedClassOrObjectSymbol(
|
||||
}
|
||||
|
||||
override val typeParameters = withValidityAssertion {
|
||||
firSymbol.fir.typeParameters.filterIsInstance<FirTypeParameter>().map { typeParameter ->
|
||||
builder.classifierBuilder.buildTypeParameterSymbol(typeParameter.symbol)
|
||||
}
|
||||
firSymbol.createRegularKtTypeParameters(builder)
|
||||
}
|
||||
|
||||
|
||||
@OptIn(KtAnalysisApiInternals::class)
|
||||
override val classKind: KtClassKind
|
||||
get() = withValidityAssertion {
|
||||
@@ -109,17 +102,7 @@ internal class KtFirNamedClassOrObjectSymbol(
|
||||
|
||||
context(KtAnalysisSession)
|
||||
override fun createPointer(): KtSymbolPointer<KtNamedClassOrObjectSymbol> = withValidityAssertion {
|
||||
KtPsiBasedSymbolPointer.createForSymbolFromSource<KtNamedClassOrObjectSymbol>(this)?.let { return it }
|
||||
|
||||
when (val symbolKind = symbolKind) {
|
||||
KtSymbolKind.LOCAL ->
|
||||
throw CanNotCreateSymbolPointerForLocalLibraryDeclarationException(classIdIfNonLocal?.asString() ?: name.asString())
|
||||
|
||||
KtSymbolKind.CLASS_MEMBER, KtSymbolKind.TOP_LEVEL ->
|
||||
KtFirClassLikeSymbolPointer(classIdIfNonLocal!!, KtNamedClassOrObjectSymbol::class)
|
||||
|
||||
else -> throw UnsupportedSymbolKind(this::class, symbolKind)
|
||||
}
|
||||
createNamedClassOrObjectSymbolPointer()
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean = symbolEquals(other)
|
||||
|
||||
+126
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* Copyright 2010-2023 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.fir.symbols
|
||||
|
||||
import com.intellij.psi.PsiClass
|
||||
import org.jetbrains.kotlin.analysis.api.KtAnalysisApiInternals
|
||||
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
|
||||
import org.jetbrains.kotlin.analysis.api.annotations.KtAnnotationsList
|
||||
import org.jetbrains.kotlin.analysis.api.base.KtContextReceiver
|
||||
import org.jetbrains.kotlin.analysis.api.fir.KtFirAnalysisSession
|
||||
import org.jetbrains.kotlin.analysis.api.fir.annotations.KtFirAnnotationListForDeclaration
|
||||
import org.jetbrains.kotlin.analysis.api.fir.utils.cached
|
||||
import org.jetbrains.kotlin.analysis.api.impl.base.symbols.toKtClassKind
|
||||
import org.jetbrains.kotlin.analysis.api.lifetime.KtLifetimeToken
|
||||
import org.jetbrains.kotlin.analysis.api.lifetime.withValidityAssertion
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.KtClassKind
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.KtNamedClassOrObjectSymbol
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.KtSymbolOrigin
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.KtTypeParameterSymbol
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.markers.KtSymbolKind
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.pointers.KtSymbolPointer
|
||||
import org.jetbrains.kotlin.analysis.api.types.KtType
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.descriptors.Visibility
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.analysis.project.structure.getKtModule
|
||||
import org.jetbrains.kotlin.analysis.low.level.api.fir.providers.firClassByPsiClassProvider
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
|
||||
import org.jetbrains.kotlin.analysis.utils.classIdIfNonLocal
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.fir.java.classKind
|
||||
import org.jetbrains.kotlin.fir.java.modality
|
||||
import org.jetbrains.kotlin.load.java.structure.JavaClass
|
||||
import org.jetbrains.kotlin.load.java.structure.impl.JavaClassImpl
|
||||
|
||||
/**
|
||||
* Implements [KtNamedClassOrObjectSymbol] for a Java class. The underlying [firSymbol] is built lazily and only when needed. Many simple
|
||||
* properties are computed from the given [PsiClass] instead of [firSymbol]. This improves performance when "slow" properties don't need to
|
||||
* be accessed.
|
||||
*/
|
||||
internal class KtFirPsiJavaClassSymbol(
|
||||
override val psi: PsiClass,
|
||||
override val analysisSession: KtFirAnalysisSession,
|
||||
) : KtNamedClassOrObjectSymbol(), KtFirPsiSymbol<PsiClass, FirRegularClassSymbol> {
|
||||
/**
|
||||
* [javaClass] is used to defer some properties to the compiler's view of a Java class.
|
||||
*/
|
||||
private val javaClass: JavaClass = JavaClassImpl(psi)
|
||||
|
||||
override val name: Name = withValidityAssertion { javaClass.name }
|
||||
|
||||
override val classIdIfNonLocal: ClassId? by cached { psi.classIdIfNonLocal }
|
||||
|
||||
override val origin: KtSymbolOrigin
|
||||
get() = withValidityAssertion { KtSymbolOrigin.JAVA }
|
||||
|
||||
override val symbolKind: KtSymbolKind
|
||||
get() = withValidityAssertion {
|
||||
val classId = classIdIfNonLocal
|
||||
when {
|
||||
classId == null -> KtSymbolKind.LOCAL
|
||||
classId.outerClassId != null -> KtSymbolKind.CLASS_MEMBER
|
||||
else -> KtSymbolKind.TOP_LEVEL
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(KtAnalysisApiInternals::class)
|
||||
override val classKind: KtClassKind
|
||||
get() = withValidityAssertion { javaClass.classKind.toKtClassKind(isCompanionObject = false) }
|
||||
|
||||
override val modality: Modality
|
||||
get() = withValidityAssertion { javaClass.modality }
|
||||
|
||||
override val visibility: Visibility
|
||||
get() = withValidityAssertion { javaClass.visibility }
|
||||
|
||||
override val isInner: Boolean
|
||||
get() = withValidityAssertion { classIdIfNonLocal?.outerClassId != null && !javaClass.isStatic }
|
||||
|
||||
override val isData: Boolean get() = withValidityAssertion { false }
|
||||
override val isInline: Boolean get() = withValidityAssertion { false }
|
||||
override val isFun: Boolean get() = withValidityAssertion { false }
|
||||
override val isExternal: Boolean get() = withValidityAssertion { false }
|
||||
override val companionObject: KtNamedClassOrObjectSymbol? get() = withValidityAssertion { null }
|
||||
|
||||
override val contextReceivers: List<KtContextReceiver> get() = withValidityAssertion { emptyList() }
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Slow Operations (requiring access to the underlying FIR class symbol)
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
override val firSymbol: FirRegularClassSymbol by cached {
|
||||
val module = psi.getKtModule(analysisSession.project)
|
||||
val provider = analysisSession.firResolveSession.getSessionFor(module).firClassByPsiClassProvider
|
||||
val firClassSymbol = provider.getFirClass(psi)
|
||||
|
||||
require(firClassSymbol != null) {
|
||||
"A FIR class symbol should be available for ${KtFirPsiJavaClassSymbol::class.simpleName} `$classIdIfNonLocal`."
|
||||
}
|
||||
firClassSymbol
|
||||
}
|
||||
|
||||
override val superTypes: List<KtType> by cached {
|
||||
firSymbol.superTypesAndAnnotationsListForRegularClass(builder)
|
||||
}
|
||||
|
||||
override val typeParameters: List<KtTypeParameterSymbol> by cached {
|
||||
firSymbol.createRegularKtTypeParameters(builder)
|
||||
}
|
||||
|
||||
override val annotationsList: KtAnnotationsList by cached {
|
||||
KtFirAnnotationListForDeclaration.create(firSymbol, analysisSession.useSiteSession, token)
|
||||
}
|
||||
|
||||
context(KtAnalysisSession)
|
||||
override fun createPointer(): KtSymbolPointer<KtNamedClassOrObjectSymbol> = withValidityAssertion {
|
||||
createNamedClassOrObjectSymbolPointer()
|
||||
}
|
||||
|
||||
// TODO (marco): Can we implement equality and hash code without falling back to the symbol?
|
||||
override fun equals(other: Any?): Boolean = symbolEquals(other)
|
||||
override fun hashCode(): Int = symbolHashCode()
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright 2010-2023 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.fir.symbols
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.KtSymbolOrigin
|
||||
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
|
||||
|
||||
/**
|
||||
* A [KtFirSymbol] that is backed by some [PsiElement] and builds [firSymbol] lazily (by convention), allowing some properties to be
|
||||
* calculated without the need to build a [firSymbol].
|
||||
*/
|
||||
internal interface KtFirPsiSymbol<P : PsiElement, out S : FirBasedSymbol<*>> : KtFirSymbol<S> {
|
||||
override val psi: P
|
||||
|
||||
/**
|
||||
* The origin should be provided without using [firSymbol], if possible.
|
||||
*/
|
||||
abstract override val origin: KtSymbolOrigin
|
||||
}
|
||||
+30
@@ -8,12 +8,20 @@
|
||||
package org.jetbrains.kotlin.analysis.api.fir.symbols
|
||||
|
||||
import org.jetbrains.kotlin.analysis.api.KtAnalysisApiInternals
|
||||
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
|
||||
import org.jetbrains.kotlin.analysis.api.KtInitializerValue
|
||||
import org.jetbrains.kotlin.analysis.api.base.KtContextReceiver
|
||||
import org.jetbrains.kotlin.analysis.api.fir.KtSymbolByFirBuilder
|
||||
import org.jetbrains.kotlin.analysis.api.fir.symbols.pointers.KtFirClassLikeSymbolPointer
|
||||
import org.jetbrains.kotlin.analysis.api.fir.utils.asKtInitializerValue
|
||||
import org.jetbrains.kotlin.analysis.api.impl.base.KtContextReceiverImpl
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.KtNamedClassOrObjectSymbol
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.KtValueParameterSymbol
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.markers.KtSymbolKind
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.pointers.CanNotCreateSymbolPointerForLocalLibraryDeclarationException
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.pointers.KtPsiBasedSymbolPointer
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.pointers.KtSymbolPointer
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.pointers.UnsupportedSymbolKind
|
||||
import org.jetbrains.kotlin.analysis.api.types.KtType
|
||||
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.LLFirResolveSession
|
||||
import org.jetbrains.kotlin.analysis.utils.printer.getElementTextInContext
|
||||
@@ -57,6 +65,13 @@ internal fun <D> FirBasedSymbol<D>.createKtTypeParameters(
|
||||
}
|
||||
}
|
||||
|
||||
internal fun <D> FirBasedSymbol<D>.createRegularKtTypeParameters(
|
||||
builder: KtSymbolByFirBuilder,
|
||||
): List<KtFirTypeParameterSymbol> where D : FirTypeParameterRefsOwner, D : FirDeclaration {
|
||||
return fir.typeParameters.filterIsInstance<FirTypeParameter>().map { typeParameter ->
|
||||
builder.classifierBuilder.buildTypeParameterSymbol(typeParameter.symbol)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun FirCallableSymbol<*>.createContextReceivers(
|
||||
builder: KtSymbolByFirBuilder
|
||||
@@ -109,3 +124,18 @@ internal fun FirVariableSymbol<*>.getKtConstantInitializer(resolveSession: LLFir
|
||||
?.classKind == ClassKind.ANNOTATION_CLASS
|
||||
return firInitializer.asKtInitializerValue(moduleData.session, parentIsAnnotation)
|
||||
}
|
||||
|
||||
context(KtAnalysisSession)
|
||||
internal fun KtNamedClassOrObjectSymbol.createNamedClassOrObjectSymbolPointer(): KtSymbolPointer<KtNamedClassOrObjectSymbol> {
|
||||
KtPsiBasedSymbolPointer.createForSymbolFromSource<KtNamedClassOrObjectSymbol>(this)?.let { return it }
|
||||
|
||||
return when (val symbolKind = symbolKind) {
|
||||
KtSymbolKind.LOCAL ->
|
||||
throw CanNotCreateSymbolPointerForLocalLibraryDeclarationException(classIdIfNonLocal?.asString() ?: name.asString())
|
||||
|
||||
KtSymbolKind.CLASS_MEMBER, KtSymbolKind.TOP_LEVEL ->
|
||||
KtFirClassLikeSymbolPointer(classIdIfNonLocal!!, KtNamedClassOrObjectSymbol::class)
|
||||
|
||||
else -> throw UnsupportedSymbolKind(this::class, symbolKind)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ internal val JavaModifierListOwner.modality: Modality
|
||||
else -> Modality.OPEN
|
||||
}
|
||||
|
||||
internal val JavaClass.modality: Modality
|
||||
val JavaClass.modality: Modality
|
||||
get() = when {
|
||||
isAnnotationType || isEnum -> Modality.FINAL
|
||||
isSealed -> Modality.SEALED
|
||||
@@ -43,7 +43,7 @@ internal val JavaClass.modality: Modality
|
||||
else -> Modality.OPEN
|
||||
}
|
||||
|
||||
internal val JavaClass.classKind: ClassKind
|
||||
val JavaClass.classKind: ClassKind
|
||||
get() = when {
|
||||
isAnnotationType -> ClassKind.ANNOTATION_CLASS
|
||||
isInterface -> ClassKind.INTERFACE
|
||||
|
||||
Reference in New Issue
Block a user