[FIR] Add special inline class for caches which are used in symbol providers

This commit is contained in:
Dmitriy Novozhilov
2020-08-13 15:16:45 +03:00
parent 4b6193159c
commit 77f52a9ddb
5 changed files with 94 additions and 60 deletions
@@ -9,7 +9,6 @@ import com.intellij.openapi.progress.ProcessCanceledException
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElement
import com.intellij.psi.search.GlobalSearchScope
import org.jetbrains.annotations.TestOnly
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.Visibilities
@@ -21,8 +20,9 @@ import org.jetbrains.kotlin.fir.declarations.impl.FirResolvedDeclarationStatusIm
import org.jetbrains.kotlin.fir.expressions.builder.*
import org.jetbrains.kotlin.fir.java.declarations.*
import org.jetbrains.kotlin.fir.resolve.constructType
import org.jetbrains.kotlin.fir.resolve.providers.AbstractFirSymbolProvider
import org.jetbrains.kotlin.fir.resolve.providers.AbstractFirSymbolProviderWithCache
import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProviderInternals
import org.jetbrains.kotlin.fir.resolve.providers.SymbolProviderCache
import org.jetbrains.kotlin.fir.resolve.scopes.wrapScopeWithJvmMapped
import org.jetbrains.kotlin.fir.scopes.FirScope
import org.jetbrains.kotlin.fir.scopes.impl.lazyNestedClassifierScope
@@ -48,7 +48,7 @@ class JavaSymbolProvider(
val session: FirSession,
val project: Project,
private val searchScope: GlobalSearchScope,
) : AbstractFirSymbolProvider<FirRegularClassSymbol>() {
) : AbstractFirSymbolProviderWithCache<FirRegularClassSymbol>() {
companion object {
private val VALUE_METHOD_NAME = Name.identifier("value")
}
@@ -56,7 +56,7 @@ class JavaSymbolProvider(
private val scopeProvider = JavaScopeProvider(::wrapScopeWithJvmMapped, this)
private val facade: KotlinJavaPsiFacade get() = KotlinJavaPsiFacade.getInstance(project)
private val parentClassTypeParameterStackCache: MutableMap<FirRegularClassSymbol, JavaTypeParameterStack> = mutableMapOf()
private val parentClassTypeParameterStackCache: SymbolProviderCache<FirRegularClassSymbol, JavaTypeParameterStack> = SymbolProviderCache()
private fun findClass(
classId: ClassId,
@@ -25,8 +25,9 @@ import org.jetbrains.kotlin.fir.references.builder.buildErrorNamedReference
import org.jetbrains.kotlin.fir.references.builder.buildResolvedNamedReference
import org.jetbrains.kotlin.fir.references.impl.FirReferencePlaceholderForResolvedAnnotations
import org.jetbrains.kotlin.fir.resolve.firSymbolProvider
import org.jetbrains.kotlin.fir.resolve.providers.AbstractFirSymbolProvider
import org.jetbrains.kotlin.fir.resolve.providers.AbstractFirSymbolProviderWithCache
import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProviderInternals
import org.jetbrains.kotlin.fir.resolve.providers.SymbolProviderCache
import org.jetbrains.kotlin.fir.resolve.providers.getClassDeclaredCallableSymbols
import org.jetbrains.kotlin.fir.scopes.FirScope
import org.jetbrains.kotlin.fir.scopes.KotlinScopeProvider
@@ -37,7 +38,6 @@ import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef
import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef
import org.jetbrains.kotlin.fir.types.constructClassType
import org.jetbrains.kotlin.load.java.JavaClassFinder
import org.jetbrains.kotlin.load.java.structure.JavaClass
import org.jetbrains.kotlin.load.kotlin.*
import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinaryClass.AnnotationArrayArgumentVisitor
import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader
@@ -54,7 +54,6 @@ import org.jetbrains.kotlin.resolve.jvm.JvmClassName
import org.jetbrains.kotlin.serialization.deserialization.IncompatibleVersionErrorData
import org.jetbrains.kotlin.serialization.deserialization.getName
import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult
import org.jetbrains.kotlin.utils.getOrPutNullable
class KotlinDeserializedJvmSymbolsProvider(
val session: FirSession,
@@ -64,11 +63,11 @@ class KotlinDeserializedJvmSymbolsProvider(
private val kotlinClassFinder: KotlinClassFinder,
private val javaClassFinder: JavaClassFinder,
private val kotlinScopeProvider: KotlinScopeProvider,
) : AbstractFirSymbolProvider<FirClassLikeSymbol<*>>() {
private val classesCache = HashMap<ClassId, FirRegularClassSymbol>()
private val typeAliasCache = HashMap<ClassId, FirTypeAliasSymbol?>()
private val packagePartsCache = HashMap<FqName, Collection<PackagePartsCacheData>>()
) : AbstractFirSymbolProviderWithCache<FirRegularClassSymbol>() {
private val typeAliasCache = SymbolProviderCache<ClassId, FirTypeAliasSymbol>()
private val packagePartsCache = SymbolProviderCache<FqName, Collection<PackagePartsCacheData>>()
// TODO: implement thread safety for this property
private val handledByJava = HashSet<ClassId>()
private class PackagePartsCacheData(
@@ -154,7 +153,7 @@ class KotlinDeserializedJvmSymbolsProvider(
classId: ClassId,
): FirTypeAliasSymbol? {
if (!classId.relativeClassName.isOneSegmentFQN()) return null
return typeAliasCache.getOrPutNullable(classId) {
return typeAliasCache.lookupCacheOrCalculate(classId) {
getPackageParts(classId.packageFqName).firstNotNullResult { part ->
val ids = part.typeAliasNameIndex[classId.shortClassName]
if (ids == null || ids.isEmpty()) return@firstNotNullResult null
@@ -307,7 +306,7 @@ class KotlinDeserializedJvmSymbolsProvider(
parentContext: FirDeserializationContext? = null
): FirRegularClassSymbol? {
if (hasNoTopLevelClassOf(classId)) return null
if (classesCache.containsKey(classId)) return classesCache[classId]
if (classId in classCache) return classCache[classId]
if (classId in handledByJava) return null
@@ -346,7 +345,7 @@ class KotlinDeserializedJvmSymbolsProvider(
this::findAndDeserializeClass
)
classesCache[classId] = symbol
classCache[classId] = symbol
val annotations = mutableListOf<FirAnnotationCall>()
kotlinJvmBinaryClass.loadClassAnnotations(
object : KotlinJvmBinaryClass.AnnotationVisitor {
@@ -364,7 +363,7 @@ class KotlinDeserializedJvmSymbolsProvider(
(symbol.fir.annotations as MutableList<FirAnnotationCall>) += annotations
}
return classesCache[classId]
return classCache[classId]
}
private fun loadFunctionsByName(part: PackagePartsCacheData, name: Name): List<FirCallableSymbol<*>> {
@@ -399,13 +398,13 @@ class KotlinDeserializedJvmSymbolsProvider(
}
private fun getPackageParts(packageFqName: FqName): Collection<PackagePartsCacheData> {
return packagePartsCache.getOrPut(packageFqName) {
return packagePartsCache.lookupCacheOrCalculate(packageFqName) {
try {
computePackagePartsInfos(packageFqName)
} catch (e: ProcessCanceledException) {
emptyList()
}
}
}!!
}
private fun findRegularClass(classId: ClassId): FirRegularClass? =
@@ -1,41 +0,0 @@
/*
* 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.fir.resolve.providers
import org.jetbrains.kotlin.fir.symbols.CallableId
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
abstract class AbstractFirSymbolProvider<C : FirClassLikeSymbol<*>> : FirSymbolProvider() {
protected val classCache = HashMap<ClassId, C?>()
protected val topLevelCallableCache = HashMap<CallableId, List<FirCallableSymbol<*>>>()
protected val packageCache = HashMap<FqName, FqName?>()
protected inline fun <K, V : Any?> MutableMap<K, V>.lookupCacheOrCalculate(key: K, crossinline l: (K) -> V): V? {
return if (containsKey(key)) {
this[key]
} else {
val calculated = l(key)
this[key] = calculated
calculated
}
}
protected inline fun <K, V : Any, T> MutableMap<K, V?>.lookupCacheOrCalculateWithPostCompute(
key: K, crossinline l: (K) -> Pair<V?, T>, postCompute: (V, T) -> Unit
): V? {
return if (containsKey(key)) {
this[key]
} else {
val calculated = l(key)
this[key] = calculated.first
calculated.first?.let { first -> postCompute(first, calculated.second) }
calculated.first
}
}
}
@@ -0,0 +1,76 @@
/*
* 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.fir.resolve.providers
import org.jetbrains.kotlin.fir.PrivateForInline
import org.jetbrains.kotlin.fir.symbols.CallableId
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
abstract class AbstractFirSymbolProviderWithCache<C : FirClassLikeSymbol<*>> : FirSymbolProvider() {
protected val classCache = SymbolProviderCache<ClassId, C>()
protected val topLevelCallableCache = SymbolProviderCache<CallableId, List<FirCallableSymbol<*>>>()
protected val packageCache = SymbolProviderCache<FqName, FqName>()
}
@Suppress("EXPERIMENTAL_FEATURE_WARNING")
inline class SymbolProviderCache<K, V : Any> @PrivateForInline constructor(@PrivateForInline val cache: HashMap<K, Any>) {
@OptIn(PrivateForInline::class)
constructor() : this(HashMap())
@PrivateForInline
object NullValue
@OptIn(PrivateForInline::class)
inline fun lookupCacheOrCalculate(key: K, crossinline l: (K) -> V?): V? {
@Suppress("UNCHECKED_CAST")
return when (val value = cache[key]) {
null -> {
val calculated = l(key)
cache[key] = calculated ?: NullValue
calculated
}
NullValue -> null
else -> value as V
}
}
@OptIn(PrivateForInline::class)
inline fun <T> lookupCacheOrCalculateWithPostCompute(
key: K, crossinline l: (K) -> Pair<V?, T>, postCompute: (V, T) -> Unit
): V? {
@Suppress("UNCHECKED_CAST")
return when (val value = cache[key]) {
null -> {
val calculated = l(key)
cache[key] = calculated.first ?: NullValue
calculated.first?.let { postCompute(it, calculated.second) }
calculated.first
}
NullValue -> null
else -> value as V
}
}
@OptIn(PrivateForInline::class)
operator fun contains(key: K): Boolean = key in cache
@Suppress("UNCHECKED_CAST")
@OptIn(PrivateForInline::class)
operator fun get(key: K): V? = cache[key].takeIf { it !== NullValue } as V?
@OptIn(PrivateForInline::class)
operator fun set(key: K, value: V) {
cache[key] = value
}
@OptIn(PrivateForInline::class)
fun remove(key: K) {
cache.remove(key)
}
}
@@ -8,7 +8,7 @@ package org.jetbrains.kotlin.fir.resolve.providers.impl
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.dependenciesWithoutSelf
import org.jetbrains.kotlin.fir.resolve.firSymbolProvider
import org.jetbrains.kotlin.fir.resolve.providers.AbstractFirSymbolProvider
import org.jetbrains.kotlin.fir.resolve.providers.AbstractFirSymbolProviderWithCache
import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProviderInternals
import org.jetbrains.kotlin.fir.scopes.FirScope
import org.jetbrains.kotlin.fir.symbols.CallableId
@@ -19,7 +19,7 @@ import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult
open class FirDependenciesSymbolProviderImpl(val session: FirSession) : AbstractFirSymbolProvider<FirClassLikeSymbol<*>>() {
open class FirDependenciesSymbolProviderImpl(val session: FirSession) : AbstractFirSymbolProviderWithCache<FirClassLikeSymbol<*>>() {
protected open val dependencyProviders by lazy {
val moduleInfo = session.moduleInfo ?: return@lazy emptyList()
moduleInfo.dependenciesWithoutSelf().mapNotNull {