org.jetbrains.kotlin.analyzer.AbstractResolverForProject.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of kotlin-compiler-embeddable Show documentation
Show all versions of kotlin-compiler-embeddable Show documentation
the Kotlin compiler embeddable
/*
* Copyright 2010-2022 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.analyzer
import com.intellij.openapi.Disposable
import com.intellij.openapi.util.ModificationTracker
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.context.ProjectContext
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.utils.KotlinExceptionWithAttachments
import org.jetbrains.kotlin.utils.checkWithAttachment
abstract class AbstractResolverForProject(
private val debugName: String,
protected val projectContext: ProjectContext,
modules: Collection,
protected val fallbackModificationTracker: ModificationTracker? = null,
private val delegateResolver: ResolverForProject = EmptyResolverForProject(),
private val packageOracleFactory: PackageOracleFactory = PackageOracleFactory.OptimisticFactory
) : ResolverForProject(), Disposable {
protected class ModuleData(
val moduleDescriptor: ModuleDescriptorImpl,
val modificationTracker: ModificationTracker?
) {
val modificationCount: Long = modificationTracker?.modificationCount ?: Long.MIN_VALUE
fun isOutOfDate(): Boolean {
val currentModCount = modificationTracker?.modificationCount
return currentModCount != null && currentModCount > modificationCount
}
}
@Volatile
protected var disposed = false
// Protected by ("projectContext.storageManager.lock")
protected val descriptorByModule = hashMapOf()
// Protected by ("projectContext.storageManager.lock")
private val moduleInfoByDescriptor = hashMapOf()
@Suppress("UNCHECKED_CAST")
private val moduleInfoToResolvableInfo: Map =
modules.flatMap { module -> module.flatten().map { modulePart -> modulePart to module } }.toMap() as Map
init {
assert(moduleInfoToResolvableInfo.values.toSet() == modules.toSet())
}
abstract fun sdkDependency(module: M): M?
abstract fun modulesContent(module: M): ModuleContent
abstract fun builtInsForModule(module: M): KotlinBuiltIns
abstract fun createResolverForModule(descriptor: ModuleDescriptor, moduleInfo: M): ResolverForModule
override fun tryGetResolverForModule(moduleInfo: M): ResolverForModule? {
checkValid()
if (!isCorrectModuleInfo(moduleInfo)) {
return null
}
return resolverForModuleDescriptor(doGetDescriptorForModule(moduleInfo))
}
private fun setupModuleDescriptor(module: M, moduleDescriptor: ModuleDescriptorImpl) {
checkValid()
moduleDescriptor.setDependencies(
LazyModuleDependencies(
projectContext.storageManager,
module,
sdkDependency(module),
this
)
)
val content = modulesContent(module)
moduleDescriptor.initialize(
DelegatingPackageFragmentProvider(
this, moduleDescriptor, content,
packageOracleFactory.createOracle(module)
)
)
}
// Protected by ("projectContext.storageManager.lock")
private val resolverByModuleDescriptor = hashMapOf()
override val allModules: Collection by lazy {
this.moduleInfoToResolvableInfo.keys + delegateResolver.allModules
}
override val name: String
get() = "Resolver for '$debugName'"
private fun isCorrectModuleInfo(moduleInfo: M): Boolean =
((moduleInfo as? DerivedModuleInfo)?.originalModule ?: moduleInfo) in allModules
final override fun resolverForModuleDescriptor(descriptor: ModuleDescriptor): ResolverForModule {
val moduleResolver = resolverForModuleDescriptorImpl(descriptor)
// Please, attach exceptions from here to EA-214260 (see `resolverForModuleDescriptorImpl` comment)
checkWithAttachment(
moduleResolver != null,
lazyMessage = { "$descriptor is not contained in resolver $name" },
attachments = {
it.withAttachment(
"resolverContents.txt",
"Expected module descriptor: $descriptor\n\n${renderResolversChainContents()}"
)
}
)
return moduleResolver
}
/**
* We have a problem investigating EA-214260 (KT-40301), that is why we separated searching the
* [ResolverForModule] and reporting the problem in [resolverForModuleDescriptor] (so we can tweak the reported information more
* accurately).
*
* We use the fact that [ResolverForProject] have only two inheritors: [EmptyResolverForProject] and [AbstractResolverForProject].
* So if the [delegateResolver] is not an [EmptyResolverForProject], it has to be [AbstractResolverForProject].
*
* Knowing that, we can safely use [resolverForModuleDescriptorImpl] recursively, and get the same result
* as with [resolverForModuleDescriptor].
*/
private fun resolverForModuleDescriptorImpl(descriptor: ModuleDescriptor): ResolverForModule? {
return projectContext.storageManager.compute {
checkValid()
descriptor.assertValid()
val module = moduleInfoByDescriptor[descriptor]
if (module == null) {
if (delegateResolver is EmptyResolverForProject<*>) {
return@compute null
}
return@compute (delegateResolver as AbstractResolverForProject).resolverForModuleDescriptorImpl(descriptor)
}
resolverByModuleDescriptor.getOrPut(descriptor) {
checkModuleIsCorrect(module)
ResolverForModuleComputationTracker.getInstance(projectContext.project)?.onResolverComputed(module)
createResolverForModule(descriptor, module)
}
}
}
internal fun isResolverForModuleDescriptorComputed(descriptor: ModuleDescriptor) =
projectContext.storageManager.compute {
descriptor in resolverByModuleDescriptor
}
override fun descriptorForModule(moduleInfo: M): ModuleDescriptorImpl {
checkValid()
checkModuleIsCorrect(moduleInfo)
return doGetDescriptorForModule(moduleInfo)
}
override fun moduleInfoForModuleDescriptor(moduleDescriptor: ModuleDescriptor): M {
checkValid()
return moduleInfoByDescriptor[moduleDescriptor] ?: delegateResolver.moduleInfoForModuleDescriptor(moduleDescriptor)
}
override fun diagnoseUnknownModuleInfo(infos: List): Nothing {
DiagnoseUnknownModuleInfoReporter.report(name, infos, allModules)
}
private fun checkModuleIsCorrect(moduleInfo: M) {
if (!isCorrectModuleInfo(moduleInfo)) {
diagnoseUnknownModuleInfo(listOf(moduleInfo))
}
}
private fun doGetDescriptorForModule(module: M): ModuleDescriptorImpl {
val moduleFromThisResolver =
module.takeIf { it is DerivedModuleInfo && it.originalModule in moduleInfoToResolvableInfo }
?: moduleInfoToResolvableInfo[module]
?: return delegateResolver.descriptorForModule(module) as ModuleDescriptorImpl
return projectContext.storageManager.compute {
var moduleData = descriptorByModule.getOrPut(moduleFromThisResolver) {
createModuleDescriptor(moduleFromThisResolver)
}
if (moduleData.isOutOfDate()) {
moduleData = recreateModuleDescriptor(moduleFromThisResolver)
}
moduleData.moduleDescriptor
}
}
private fun recreateModuleDescriptor(module: M): ModuleData {
val oldDescriptor = descriptorByModule[module]?.moduleDescriptor
if (oldDescriptor != null) {
oldDescriptor.isValid = false
moduleInfoByDescriptor.remove(oldDescriptor)
resolverByModuleDescriptor.remove(oldDescriptor)
projectContext.project.messageBus.syncPublisher(ModuleDescriptorListener.TOPIC).moduleDescriptorInvalidated(oldDescriptor)
}
val moduleData = createModuleDescriptor(module)
descriptorByModule[module] = moduleData
return moduleData
}
protected open fun getAdditionalCapabilities(): Map, Any?> = emptyMap()
private fun createModuleDescriptor(module: M): ModuleData {
val moduleDescriptor = ModuleDescriptorImpl(
module.name,
projectContext.storageManager,
builtInsForModule(module),
module.platform,
module.capabilities + getAdditionalCapabilities(),
module.stableName,
)
moduleInfoByDescriptor[moduleDescriptor] = module
setupModuleDescriptor(module, moduleDescriptor)
val modificationTracker = (module as? TrackableModuleInfo)?.createModificationTracker() ?: fallbackModificationTracker
return ModuleData(moduleDescriptor, modificationTracker)
}
private fun checkValid() {
if (disposed) {
reportInvalidResolver()
}
}
protected open fun reportInvalidResolver() {
throw InvalidResolverException("$name is invalidated")
}
override fun dispose() {
projectContext.storageManager.compute {
disposed = true
descriptorByModule.values.forEach {
moduleInfoByDescriptor.remove(it.moduleDescriptor)
it.moduleDescriptor.isValid = false
}
descriptorByModule.clear()
moduleInfoByDescriptor.keys.forEach { it.isValid = false }
moduleInfoByDescriptor.clear()
}
}
private fun renderResolversChainContents(): String {
val resolversChain = generateSequence(this) { it.delegateResolver as? AbstractResolverForProject }
return resolversChain.joinToString("\n\n") { resolver ->
"Resolver: ${resolver.name}\n'moduleInfoByDescriptor' content:\n[${resolver.renderResolverModuleInfos()}]"
}
}
private fun renderResolverModuleInfos(): String = projectContext.storageManager.compute {
moduleInfoByDescriptor.entries.joinToString(",\n") { (descriptor, moduleInfo) ->
"""
{
moduleDescriptor: $descriptor
moduleInfo: $moduleInfo
}
""".trimIndent()
}
}
}
private class DelegatingPackageFragmentProvider(
private val resolverForProject: AbstractResolverForProject,
private val module: ModuleDescriptor,
moduleContent: ModuleContent,
private val packageOracle: PackageOracle
) : PackageFragmentProviderOptimized {
private val syntheticFilePackages = moduleContent.syntheticFiles.map { it.packageFqName }.toSet()
@Suppress("OverridingDeprecatedMember", "OVERRIDE_DEPRECATION")
override fun getPackageFragments(fqName: FqName): List {
if (certainlyDoesNotExist(fqName)) return emptyList()
@Suppress("DEPRECATION")
return resolverForProject.resolverForModuleDescriptor(module).packageFragmentProvider.getPackageFragments(fqName)
}
override fun collectPackageFragments(fqName: FqName, packageFragments: MutableCollection) {
if (certainlyDoesNotExist(fqName)) return
resolverForProject.resolverForModuleDescriptor(module)
.packageFragmentProvider
.collectPackageFragmentsOptimizedIfPossible(fqName, packageFragments)
}
override fun isEmpty(fqName: FqName): Boolean {
if (certainlyDoesNotExist(fqName)) return true
return resolverForProject.resolverForModuleDescriptor(module).packageFragmentProvider.isEmpty(fqName)
}
override fun getSubPackagesOf(fqName: FqName, nameFilter: (Name) -> Boolean): Collection {
if (certainlyDoesNotExist(fqName)) return emptyList()
return resolverForProject.resolverForModuleDescriptor(module).packageFragmentProvider.getSubPackagesOf(fqName, nameFilter)
}
private fun certainlyDoesNotExist(fqName: FqName): Boolean {
if (resolverForProject.isResolverForModuleDescriptorComputed(module)) return false // let this request get cached inside delegate
return !packageOracle.packageExists(fqName) && fqName !in syntheticFilePackages
}
override fun toString(): String {
return "DelegatingProvider for $module in ${resolverForProject.name}"
}
}
private object DiagnoseUnknownModuleInfoReporter {
fun report(name: String, infos: List, allModules: Collection): Nothing {
val message = "$name does not know how to resolve"
val error = when {
name.contains(ResolverForProject.resolverForSdkName) -> errorInSdkResolver(message)
name.contains(ResolverForProject.resolverForLibrariesName) -> errorInLibrariesResolver(message)
name.contains(ResolverForProject.resolverForModulesName) -> {
when {
infos.isEmpty() -> errorInModulesResolverWithEmptyInfos(message)
infos.size == 1 -> {
val infoAsString = infos.single().toString()
when {
infoAsString.contains("ScriptDependencies") -> errorInModulesResolverWithScriptDependencies(message)
infoAsString.contains("Library") -> errorInModulesResolverWithLibraryInfo(message)
else -> errorInModulesResolver(message)
}
}
else -> errorInModulesResolver(message)
}
}
name.contains(ResolverForProject.resolverForScriptDependenciesName) -> errorInScriptDependenciesInfoResolver(message)
name.contains(ResolverForProject.resolverForSpecialInfoName) -> {
when {
name.contains("ScriptModuleInfo") -> errorInScriptModuleInfoResolver(message)
else -> errorInSpecialModuleInfoResolver(message)
}
}
else -> otherError(message)
}
throw error.withAttachment("infos.txt", infos).withAttachment("allModules.txt", allModules)
}
// Do not inline 'error*'-methods, they are needed to avoid Exception Analyzer merging those AssertionErrors
private fun errorInSdkResolver(message: String) = KotlinExceptionWithAttachments(message)
private fun errorInLibrariesResolver(message: String) = KotlinExceptionWithAttachments(message)
private fun errorInModulesResolver(message: String) = KotlinExceptionWithAttachments(message)
private fun errorInModulesResolverWithEmptyInfos(message: String) = KotlinExceptionWithAttachments(message)
private fun errorInModulesResolverWithScriptDependencies(message: String) = KotlinExceptionWithAttachments(message)
private fun errorInModulesResolverWithLibraryInfo(message: String) = KotlinExceptionWithAttachments(message)
private fun errorInScriptDependenciesInfoResolver(message: String) = KotlinExceptionWithAttachments(message)
private fun errorInScriptModuleInfoResolver(message: String) = KotlinExceptionWithAttachments(message)
private fun errorInSpecialModuleInfoResolver(message: String) = KotlinExceptionWithAttachments(message)
private fun otherError(message: String) = KotlinExceptionWithAttachments(message)
}
class InvalidResolverException(message: String) : IllegalStateException(message)