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 5c9cae1a483..1ed083947d0 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, AnnotationTarget.CONSTRUCTOR) +@Target(AnnotationTarget.PROPERTY, AnnotationTarget.FUNCTION, AnnotationTarget.CONSTRUCTOR, AnnotationTarget.CLASS) @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/providers/FirThreadSafeSymbolProviderWrapper.kt b/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/providers/FirThreadSafeSymbolProviderWrapper.kt new file mode 100644 index 00000000000..8a1302623e9 --- /dev/null +++ b/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/providers/FirThreadSafeSymbolProviderWrapper.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.providers + +import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProvider +import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProviderInternals +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.idea.fir.low.level.api.annotations.PrivateForInline +import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.Name +import java.util.concurrent.locks.ReadWriteLock +import java.util.concurrent.locks.ReentrantReadWriteLock +import kotlin.concurrent.withLock + +internal class FirThreadSafeSymbolProviderWrapper(private val provider: FirSymbolProvider) : FirSymbolProvider(provider.session) { + private val lock = ReentrantReadWriteLock() + private val classesCache = ThreadSafeCache>(lock) + private val topLevelCache = ThreadSafeCache>>(lock) + private val packages = ThreadSafeCache(lock) + + override fun getClassLikeSymbolByFqName(classId: ClassId): FirClassLikeSymbol<*>? = + classesCache.getOrCompute(classId) { + provider.getClassLikeSymbolByFqName(classId) + } + + override fun getTopLevelCallableSymbols(packageFqName: FqName, name: Name): List> = + topLevelCache.getOrCompute(CallableId(packageFqName, name)) { + provider.getTopLevelCallableSymbols(packageFqName, name) + } ?: emptyList() + + @FirSymbolProviderInternals + override fun getTopLevelCallableSymbolsTo(destination: MutableList>, packageFqName: FqName, name: Name) { + error("Should not be called for wrapper") + } + + override fun getPackage(fqName: FqName): FqName? = + packages.getOrCompute(fqName) { provider.getPackage(fqName) } +} + +private class ThreadSafeCache(private val lock: ReadWriteLock) { + private val map = HashMap() + + @OptIn(PrivateForInline::class) + inline fun getOrCompute(key: KEY, compute: () -> VALUE?): VALUE? { + var value = lock.readLock().withLock { map[key] } + if (value == null) { + lock.writeLock().withLock { + value = compute() ?: NULLABLE_VALUE + map[key] = value!! + } + } + @Suppress("UNCHECKED_CAST") + return when (value) { + NULLABLE_VALUE -> null + null -> error("We should not read null from map here") + else -> value as VALUE + } + } +} + +@Suppress("ClassName") +@PrivateForInline +internal object NULLABLE_VALUE \ 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/sessions/FirIdeSessionFactory.kt b/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/sessions/FirIdeSessionFactory.kt index 99394ee786c..84379f87c22 100644 --- a/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/sessions/FirIdeSessionFactory.kt +++ b/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/sessions/FirIdeSessionFactory.kt @@ -38,6 +38,7 @@ import org.jetbrains.kotlin.idea.fir.low.level.api.lazy.resolve.FirLazyDeclarati import org.jetbrains.kotlin.idea.fir.low.level.api.providers.FirModuleWithDependenciesSymbolProvider import org.jetbrains.kotlin.idea.fir.low.level.api.providers.FirIdeProvider import org.jetbrains.kotlin.idea.fir.low.level.api.sessions.FirIdeSessionFactory.registerIdeComponents +import org.jetbrains.kotlin.idea.fir.low.level.api.providers.FirThreadSafeSymbolProviderWrapper import org.jetbrains.kotlin.idea.fir.low.level.api.util.ModuleLibrariesSearchScope import org.jetbrains.kotlin.idea.fir.low.level.api.util.checkCanceled import org.jetbrains.kotlin.load.java.JavaClassFinderImpl @@ -160,14 +161,16 @@ internal object FirIdeSessionFactory { this, buildList { add( - KotlinDeserializedJvmSymbolsProvider( - this@apply, - project, - packagePartProvider, - javaSymbolProvider, - kotlinClassFinder, - javaClassFinder, - kotlinScopeProvider + FirThreadSafeSymbolProviderWrapper( + KotlinDeserializedJvmSymbolsProvider( + this@apply, + project, + packagePartProvider, + javaSymbolProvider, + kotlinClassFinder, + javaClassFinder, + kotlinScopeProvider + ) ) ) add(javaSymbolProvider)