From 77f52a9ddb235fa4c904cf74f2c0a2fff399f259 Mon Sep 17 00:00:00 2001 From: Dmitriy Novozhilov Date: Thu, 13 Aug 2020 15:16:45 +0300 Subject: [PATCH] [FIR] Add special inline class for caches which are used in symbol providers --- .../kotlin/fir/java/JavaSymbolProvider.kt | 8 +- .../KotlinDeserializedJvmSymbolsProvider.kt | 25 +++--- .../providers/AbstractFirSymbolProvider.kt | 41 ---------- .../AbstractFirSymbolProviderWithCache.kt | 76 +++++++++++++++++++ .../impl/FirDependenciesSymbolProviderImpl.kt | 4 +- 5 files changed, 94 insertions(+), 60 deletions(-) delete mode 100644 compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/providers/AbstractFirSymbolProvider.kt create mode 100644 compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/providers/AbstractFirSymbolProviderWithCache.kt diff --git a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/JavaSymbolProvider.kt b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/JavaSymbolProvider.kt index efa8fb3b6c7..78413e7f437 100644 --- a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/JavaSymbolProvider.kt +++ b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/JavaSymbolProvider.kt @@ -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() { +) : AbstractFirSymbolProviderWithCache() { 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 = mutableMapOf() + private val parentClassTypeParameterStackCache: SymbolProviderCache = SymbolProviderCache() private fun findClass( classId: ClassId, diff --git a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/deserialization/KotlinDeserializedJvmSymbolsProvider.kt b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/deserialization/KotlinDeserializedJvmSymbolsProvider.kt index d0295752a3c..103c82048f9 100644 --- a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/deserialization/KotlinDeserializedJvmSymbolsProvider.kt +++ b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/deserialization/KotlinDeserializedJvmSymbolsProvider.kt @@ -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>() { - private val classesCache = HashMap() - private val typeAliasCache = HashMap() - private val packagePartsCache = HashMap>() +) : AbstractFirSymbolProviderWithCache() { + private val typeAliasCache = SymbolProviderCache() + private val packagePartsCache = SymbolProviderCache>() + // TODO: implement thread safety for this property private val handledByJava = HashSet() 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() kotlinJvmBinaryClass.loadClassAnnotations( object : KotlinJvmBinaryClass.AnnotationVisitor { @@ -364,7 +363,7 @@ class KotlinDeserializedJvmSymbolsProvider( (symbol.fir.annotations as MutableList) += annotations } - return classesCache[classId] + return classCache[classId] } private fun loadFunctionsByName(part: PackagePartsCacheData, name: Name): List> { @@ -399,13 +398,13 @@ class KotlinDeserializedJvmSymbolsProvider( } private fun getPackageParts(packageFqName: FqName): Collection { - return packagePartsCache.getOrPut(packageFqName) { + return packagePartsCache.lookupCacheOrCalculate(packageFqName) { try { computePackagePartsInfos(packageFqName) } catch (e: ProcessCanceledException) { emptyList() } - } + }!! } private fun findRegularClass(classId: ClassId): FirRegularClass? = diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/providers/AbstractFirSymbolProvider.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/providers/AbstractFirSymbolProvider.kt deleted file mode 100644 index b8f365074a6..00000000000 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/providers/AbstractFirSymbolProvider.kt +++ /dev/null @@ -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> : FirSymbolProvider() { - protected val classCache = HashMap() - protected val topLevelCallableCache = HashMap>>() - protected val packageCache = HashMap() - - protected inline fun MutableMap.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 MutableMap.lookupCacheOrCalculateWithPostCompute( - key: K, crossinline l: (K) -> Pair, 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 - } - } -} diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/providers/AbstractFirSymbolProviderWithCache.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/providers/AbstractFirSymbolProviderWithCache.kt new file mode 100644 index 00000000000..3ce12721824 --- /dev/null +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/providers/AbstractFirSymbolProviderWithCache.kt @@ -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> : FirSymbolProvider() { + protected val classCache = SymbolProviderCache() + protected val topLevelCallableCache = SymbolProviderCache>>() + protected val packageCache = SymbolProviderCache() +} + +@Suppress("EXPERIMENTAL_FEATURE_WARNING") +inline class SymbolProviderCache @PrivateForInline constructor(@PrivateForInline val cache: HashMap) { + @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 lookupCacheOrCalculateWithPostCompute( + key: K, crossinline l: (K) -> Pair, 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) + } +} diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/providers/impl/FirDependenciesSymbolProviderImpl.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/providers/impl/FirDependenciesSymbolProviderImpl.kt index 2da2e0caa68..49c472fbe4b 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/providers/impl/FirDependenciesSymbolProviderImpl.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/providers/impl/FirDependenciesSymbolProviderImpl.kt @@ -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>() { +open class FirDependenciesSymbolProviderImpl(val session: FirSession) : AbstractFirSymbolProviderWithCache>() { protected open val dependencyProviders by lazy { val moduleInfo = session.moduleInfo ?: return@lazy emptyList() moduleInfo.dependenciesWithoutSelf().mapNotNull {