Introduce ModuleInfo -> SdkInfo cache
This fixes KT-35050 properly by caching this mapping and preventing inspecting dependencies multiple times.
This commit is contained in:
-1
@@ -16,7 +16,6 @@ import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.builtins.jvm.JvmBuiltIns
|
||||
import org.jetbrains.kotlin.context.ProjectContext
|
||||
import org.jetbrains.kotlin.idea.caches.project.SdkInfo
|
||||
import org.jetbrains.kotlin.idea.caches.project.findSdkAcrossDependencies
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.BuiltInsCacheKey
|
||||
import org.jetbrains.kotlin.platform.TargetPlatform
|
||||
import org.jetbrains.kotlin.platform.impl.JvmIdePlatformKind
|
||||
|
||||
@@ -65,6 +65,8 @@ interface IdeaModuleInfo : org.jetbrains.kotlin.idea.caches.resolve.IdeaModuleIn
|
||||
|
||||
val moduleOrigin: ModuleOrigin
|
||||
|
||||
val project: Project?
|
||||
|
||||
override val capabilities: Map<ModuleDescriptor.Capability<*>, Any?>
|
||||
get() = super.capabilities + mapOf(OriginCapability to moduleOrigin)
|
||||
|
||||
@@ -156,6 +158,9 @@ interface ModuleSourceInfo : IdeaModuleInfo, TrackableModuleInfo {
|
||||
override val moduleOrigin: ModuleOrigin
|
||||
get() = ModuleOrigin.MODULE
|
||||
|
||||
override val project: Project
|
||||
get() = module.project
|
||||
|
||||
override val platform: TargetPlatform
|
||||
get() = TargetPlatformDetector.getPlatform(module)
|
||||
|
||||
@@ -284,7 +289,7 @@ private class ModuleTestSourceScope(module: Module) : ModuleSourceScope(module)
|
||||
override fun toString() = "ModuleTestSourceScope($module)"
|
||||
}
|
||||
|
||||
open class LibraryInfo(val project: Project, val library: Library) : IdeaModuleInfo, LibraryModuleInfo, BinaryModuleInfo {
|
||||
open class LibraryInfo(override val project: Project, val library: Library) : IdeaModuleInfo, LibraryModuleInfo, BinaryModuleInfo {
|
||||
override val moduleOrigin: ModuleOrigin
|
||||
get() = ModuleOrigin.LIBRARY
|
||||
|
||||
@@ -329,7 +334,7 @@ open class LibraryInfo(val project: Project, val library: Library) : IdeaModuleI
|
||||
|
||||
}
|
||||
|
||||
data class LibrarySourceInfo(val project: Project, val library: Library, override val binariesModuleInfo: BinaryModuleInfo) :
|
||||
data class LibrarySourceInfo(override val project: Project, val library: Library, override val binariesModuleInfo: BinaryModuleInfo) :
|
||||
IdeaModuleInfo, SourceForBinaryModuleInfo {
|
||||
|
||||
override val name: Name = Name.special(KotlinIdeaAnalysisBundle.message("sources.for.library.0", library.name.toString()))
|
||||
@@ -355,7 +360,7 @@ data class LibrarySourceInfo(val project: Project, val library: Library, overrid
|
||||
}
|
||||
|
||||
//TODO: (module refactoring) there should be separate SdkSourceInfo but there are no kotlin source in existing sdks for now :)
|
||||
data class SdkInfo(val project: Project, val sdk: Sdk) : IdeaModuleInfo {
|
||||
data class SdkInfo(override val project: Project, val sdk: Sdk) : IdeaModuleInfo {
|
||||
override val moduleOrigin: ModuleOrigin
|
||||
get() = ModuleOrigin.LIBRARY
|
||||
|
||||
@@ -378,6 +383,9 @@ object NotUnderContentRootModuleInfo : IdeaModuleInfo {
|
||||
|
||||
override val name: Name = Name.special(KotlinIdeaAnalysisBundle.message("special.module.for.files.not.under.source.root"))
|
||||
|
||||
override val project: Project?
|
||||
get() = null
|
||||
|
||||
override fun contentScope() = GlobalSearchScope.EMPTY_SCOPE
|
||||
|
||||
//TODO: (module refactoring) dependency on runtime can be of use here
|
||||
@@ -481,6 +489,9 @@ data class PlatformModuleInfo(
|
||||
|
||||
override val containedModules: List<ModuleSourceInfo> = listOf(platformModule) + commonModules
|
||||
|
||||
override val project: Project
|
||||
get() = platformModule.module.project
|
||||
|
||||
override val platform: TargetPlatform
|
||||
get() = platformModule.platform
|
||||
|
||||
|
||||
+3
-3
@@ -22,7 +22,7 @@ import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatformAnalyzerServices
|
||||
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition
|
||||
|
||||
data class ScriptModuleInfo(
|
||||
val project: Project,
|
||||
override val project: Project,
|
||||
val scriptFile: VirtualFile,
|
||||
val scriptDefinition: ScriptDefinition
|
||||
) : IdeaModuleInfo {
|
||||
@@ -59,7 +59,7 @@ data class ScriptModuleInfo(
|
||||
get() = JvmPlatformAnalyzerServices
|
||||
}
|
||||
|
||||
sealed class ScriptDependenciesInfo(val project: Project) : IdeaModuleInfo, BinaryModuleInfo {
|
||||
sealed class ScriptDependenciesInfo(override val project: Project) : IdeaModuleInfo, BinaryModuleInfo {
|
||||
abstract val sdk: Sdk?
|
||||
|
||||
override val name = Name.special(KotlinIdeaAnalysisBundle.message("script.dependencies"))
|
||||
@@ -118,7 +118,7 @@ sealed class ScriptDependenciesInfo(val project: Project) : IdeaModuleInfo, Bina
|
||||
}
|
||||
}
|
||||
|
||||
sealed class ScriptDependenciesSourceInfo(val project: Project) : IdeaModuleInfo, SourceForBinaryModuleInfo {
|
||||
sealed class ScriptDependenciesSourceInfo(override val project: Project) : IdeaModuleInfo, SourceForBinaryModuleInfo {
|
||||
override val name = Name.special(KotlinIdeaAnalysisBundle.message("source.for.script.dependencies"))
|
||||
|
||||
override val binariesModuleInfo: ScriptDependenciesInfo
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* 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.caches.project
|
||||
|
||||
import com.intellij.openapi.components.ServiceManager
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.util.containers.ConcurrentWeakKeySoftValueHashMap
|
||||
import com.intellij.util.containers.ContainerUtil
|
||||
import org.jetbrains.kotlin.analyzer.ModuleInfo
|
||||
import org.jetbrains.kotlin.caches.project.cacheInvalidatingOnRootModifications
|
||||
|
||||
/**
|
||||
* Maintains and caches mapping ModuleInfo -> SdkInfo *form its dependencies*
|
||||
* (note that this SDK might be different from Project SDK)
|
||||
*
|
||||
* Cache is needed because one and the same library might (and usually does)
|
||||
* participate as dependency in several modules, so if someone queries a lot
|
||||
* of modules for their SDKs (ex.: determine built-ins for each module in a
|
||||
* project), we end up inspecting one and the same dependency multiple times.
|
||||
*
|
||||
* With projects with abnormally high amount of dependencies this might lead
|
||||
* to performance issues.
|
||||
*/
|
||||
interface SdkInfoCache {
|
||||
fun findOrGetCachedSdk(moduleInfo: ModuleInfo): SdkInfo?
|
||||
|
||||
companion object {
|
||||
fun getInstance(project: Project): SdkInfoCache =
|
||||
ServiceManager.getService(project, SdkInfoCache::class.java)
|
||||
}
|
||||
}
|
||||
|
||||
class SdkInfoCacheImpl(private val project: Project) : SdkInfoCache {
|
||||
private val cache: MutableMap<ModuleInfo, SdkInfo?>
|
||||
// Note: it's ok to use thread-unsafe map, because 'doFindSdk' is pure
|
||||
get() = project.cacheInvalidatingOnRootModifications { mutableMapOf() }
|
||||
|
||||
override fun findOrGetCachedSdk(moduleInfo: ModuleInfo): SdkInfo? {
|
||||
if (!cache.containsKey(moduleInfo)) {
|
||||
cache[moduleInfo] = doFindSdk(moduleInfo)
|
||||
}
|
||||
|
||||
return cache[moduleInfo]
|
||||
}
|
||||
|
||||
private fun doFindSdk(moduleInfo: ModuleInfo): SdkInfo? =
|
||||
moduleInfo.dependencies().lazyClosure { it.dependencies() }.firstOrNull { it is SdkInfo } as SdkInfo?
|
||||
}
|
||||
@@ -17,6 +17,7 @@ import com.intellij.psi.PsiFile
|
||||
import org.jetbrains.kotlin.analyzer.ModuleInfo
|
||||
import org.jetbrains.kotlin.asJava.classes.KtLightClassForFacade
|
||||
import org.jetbrains.kotlin.asJava.elements.KtLightElement
|
||||
import org.jetbrains.kotlin.caches.project.cacheByClassInvalidatingOnRootModifications
|
||||
import org.jetbrains.kotlin.caches.project.cacheInvalidatingOnRootModifications
|
||||
import org.jetbrains.kotlin.idea.caches.lightClasses.KtLightClassForDecompiledDeclaration
|
||||
import org.jetbrains.kotlin.idea.core.isInTestSourceContentKotlinAware
|
||||
@@ -43,16 +44,10 @@ fun PsiElement.getNullableModuleInfo(): IdeaModuleInfo? = this.collectInfos(Modu
|
||||
|
||||
fun PsiElement.getModuleInfos(): Sequence<IdeaModuleInfo> = this.collectInfos(ModuleInfoCollector.ToSequence)
|
||||
|
||||
private fun ModuleInfo.doFindSdk(): SdkInfo? = dependencies().lazyClosure { it.dependencies() }.firstIsInstanceOrNull<SdkInfo>()
|
||||
|
||||
fun ModuleInfo.findSdkAcrossDependencies(): SdkInfo? {
|
||||
return when (this) {
|
||||
// It is important to check for cases of test/production explicitly, because we're using lambda's class
|
||||
// of 'cacheInvalidatingOnRootModifications' as key for cache, and it should be different for Production/Source
|
||||
is ModuleProductionSourceInfo -> module.cacheInvalidatingOnRootModifications { doFindSdk() }
|
||||
is ModuleTestSourceInfo -> module.cacheInvalidatingOnRootModifications { doFindSdk() }
|
||||
else -> doFindSdk()
|
||||
}
|
||||
val project = (this as? IdeaModuleInfo)?.project ?: return null
|
||||
|
||||
return SdkInfoCache.getInstance(project).findOrGetCachedSdk(this)
|
||||
}
|
||||
|
||||
fun getModuleInfoByVirtualFile(project: Project, virtualFile: VirtualFile): IdeaModuleInfo? =
|
||||
|
||||
@@ -362,6 +362,9 @@
|
||||
<projectService serviceInterface="org.jetbrains.kotlin.idea.caches.project.LibraryDependenciesCache"
|
||||
serviceImplementation="org.jetbrains.kotlin.idea.caches.project.LibraryDependenciesCacheImpl"/>
|
||||
|
||||
<projectService serviceInterface="org.jetbrains.kotlin.idea.caches.project.SdkInfoCache"
|
||||
serviceImplementation="org.jetbrains.kotlin.idea.caches.project.SdkInfoCacheImpl"/>
|
||||
|
||||
<projectService serviceInterface="org.jetbrains.kotlin.idea.caches.resolve.IdePackageOracleFactory"
|
||||
serviceImplementation="org.jetbrains.kotlin.idea.caches.resolve.IdePackageOracleFactory"/>
|
||||
|
||||
|
||||
@@ -371,6 +371,9 @@
|
||||
<projectService serviceInterface="org.jetbrains.kotlin.idea.caches.project.LibraryDependenciesCache"
|
||||
serviceImplementation="org.jetbrains.kotlin.idea.caches.project.LibraryDependenciesCacheImpl"/>
|
||||
|
||||
<projectService serviceInterface="org.jetbrains.kotlin.idea.caches.project.SdkInfoCache"
|
||||
serviceImplementation="org.jetbrains.kotlin.idea.caches.project.SdkInfoCacheImpl"/>
|
||||
|
||||
<projectService serviceInterface="org.jetbrains.kotlin.idea.caches.resolve.IdePackageOracleFactory"
|
||||
serviceImplementation="org.jetbrains.kotlin.idea.caches.resolve.IdePackageOracleFactory"/>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user