Minor: extract ResolverForProjectImpl into separate file

This commit is contained in:
Dmitry Savvinov
2019-08-27 12:43:55 +03:00
parent 1e288c03c3
commit 48719c4050
2 changed files with 275 additions and 260 deletions
@@ -26,8 +26,6 @@ import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.config.LanguageVersionSettingsImpl
import org.jetbrains.kotlin.container.ComponentProvider
import org.jetbrains.kotlin.context.ModuleContext
import org.jetbrains.kotlin.context.ProjectContext
import org.jetbrains.kotlin.context.withModule
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor
import org.jetbrains.kotlin.descriptors.PackageFragmentProvider
@@ -38,8 +36,6 @@ import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.platform.TargetPlatform
import org.jetbrains.kotlin.platform.TargetPlatformVersion
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.resolve.CompilerEnvironment
import org.jetbrains.kotlin.resolve.TargetEnvironment
import org.jetbrains.kotlin.storage.StorageManager
import org.jetbrains.kotlin.storage.getValue
import java.util.*
@@ -86,187 +82,6 @@ class EmptyResolverForProject<M : ModuleInfo> : ResolverForProject<M>() {
override val builtInsProvider: (M) -> KotlinBuiltIns = { DefaultBuiltIns.Instance }
}
class ResolverForProjectImpl<M : ModuleInfo>(
private val debugName: String,
private val projectContext: ProjectContext,
modules: Collection<M>,
private val modulesContent: (M) -> ModuleContent<M>,
private val moduleLanguageSettingsProvider: LanguageSettingsProvider,
private val resolverForModuleFactoryByPlatform: (TargetPlatform?) -> ResolverForModuleFactory,
private val invalidateOnOOCB: Boolean,
override val builtInsProvider: (M) -> KotlinBuiltIns,
private val delegateResolver: ResolverForProject<M> = EmptyResolverForProject(),
private val sdkDependency: (M) -> M?,
private val packageOracleFactory: PackageOracleFactory = PackageOracleFactory.OptimisticFactory,
private val isReleaseCoroutines: Boolean? = null
) : ResolverForProject<M>() {
private 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
}
}
// Protected by ("projectContext.storageManager.lock")
private val descriptorByModule = mutableMapOf<M, ModuleData>()
// Protected by ("projectContext.storageManager.lock")
private val moduleInfoByDescriptor = mutableMapOf<ModuleDescriptorImpl, M>()
@Suppress("UNCHECKED_CAST")
private val moduleInfoToResolvableInfo: Map<M, M> =
modules.flatMap { module -> module.flatten().map { modulePart -> modulePart to module } }.toMap() as Map<M, M>
init {
assert(moduleInfoToResolvableInfo.values.toSet() == modules.toSet())
}
override fun tryGetResolverForModule(moduleInfo: M): ResolverForModule? {
if (!isCorrectModuleInfo(moduleInfo)) {
return null
}
return resolverForModuleDescriptor(doGetDescriptorForModule(moduleInfo))
}
private fun setupModuleDescriptor(module: M, moduleDescriptor: ModuleDescriptorImpl) {
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 = mutableMapOf<ModuleDescriptor, ResolverForModule>()
override val allModules: Collection<M> by lazy {
this.moduleInfoToResolvableInfo.keys + delegateResolver.allModules
}
override val name: String
get() = "Resolver for '$debugName'"
private fun isCorrectModuleInfo(moduleInfo: M) = moduleInfo in allModules
override fun resolverForModuleDescriptor(descriptor: ModuleDescriptor): ResolverForModule {
return projectContext.storageManager.compute {
val module = moduleInfoByDescriptor[descriptor]
if (module == null) {
if (delegateResolver is EmptyResolverForProject<*>) {
throw IllegalStateException("$descriptor is not contained in resolver $name")
}
return@compute delegateResolver.resolverForModuleDescriptor(descriptor)
}
resolverByModuleDescriptor.getOrPut(descriptor) {
checkModuleIsCorrect(module)
ResolverForModuleComputationTracker.getInstance(projectContext.project)?.onResolverComputed(module)
val moduleContent = modulesContent(module)
val languageVersionSettings =
moduleLanguageSettingsProvider.getLanguageVersionSettings(module, projectContext.project, isReleaseCoroutines)
// FIXME(dsavvinov): make sure that module.platform returns platform with the same JvmTarget as this one
val targetPlatformVersion = moduleLanguageSettingsProvider.getTargetPlatform(module, projectContext.project)
val resolverForModuleFactory = resolverForModuleFactoryByPlatform(module.platform)
resolverForModuleFactory.createResolverForModule(
descriptor as ModuleDescriptorImpl,
projectContext.withModule(descriptor),
moduleContent,
this@ResolverForProjectImpl,
languageVersionSettings
)
}
}
}
internal fun isResolverForModuleDescriptorComputed(descriptor: ModuleDescriptor) =
projectContext.storageManager.compute {
descriptor in resolverByModuleDescriptor
}
override fun descriptorForModule(moduleInfo: M): ModuleDescriptorImpl {
checkModuleIsCorrect(moduleInfo)
return doGetDescriptorForModule(moduleInfo)
}
override fun diagnoseUnknownModuleInfo(infos: List<ModuleInfo>): Nothing {
DiagnoseUnknownModuleInfoReporter.report(name, infos)
}
private fun checkModuleIsCorrect(moduleInfo: M) {
if (!isCorrectModuleInfo(moduleInfo)) {
diagnoseUnknownModuleInfo(listOf(moduleInfo))
}
}
private fun doGetDescriptorForModule(module: M): ModuleDescriptorImpl {
val moduleFromThisResolver = 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)
}
val moduleData = createModuleDescriptor(module)
descriptorByModule[module] = moduleData
return moduleData
}
private fun createModuleDescriptor(module: M): ModuleData {
val moduleDescriptor = ModuleDescriptorImpl(
module.name,
projectContext.storageManager,
builtInsProvider(module),
module.platform,
module.capabilities,
module.stableName
)
moduleInfoByDescriptor[moduleDescriptor] = module
setupModuleDescriptor(module, moduleDescriptor)
val modificationTracker = (module as? TrackableModuleInfo)?.createModificationTracker() ?: if (invalidateOnOOCB) {
KotlinModificationTrackerService.getInstance(projectContext.project).outOfBlockModificationTracker
} else {
null
}
return ModuleData(moduleDescriptor, modificationTracker)
}
}
data class ModuleContent<out M : ModuleInfo>(
val moduleInfo: M,
val syntheticFiles: Collection<KtFile>,
@@ -352,34 +167,6 @@ class LazyModuleDependencies<M : ModuleInfo>(
}
private class DelegatingPackageFragmentProvider<M : ModuleInfo>(
private val resolverForProject: ResolverForProjectImpl<M>,
private val module: ModuleDescriptor,
moduleContent: ModuleContent<M>,
private val packageOracle: PackageOracle
) : PackageFragmentProvider {
private val syntheticFilePackages = moduleContent.syntheticFiles.map { it.packageFqName }.toSet()
override fun getPackageFragments(fqName: FqName): List<PackageFragmentDescriptor> {
if (certainlyDoesNotExist(fqName)) return emptyList()
return resolverForProject.resolverForModuleDescriptor(module).packageFragmentProvider.getPackageFragments(fqName)
}
override fun getSubPackagesOf(fqName: FqName, nameFilter: (Name) -> Boolean): Collection<FqName> {
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
}
}
interface PackageOracle {
fun packageExists(fqName: FqName): Boolean
@@ -426,53 +213,6 @@ interface ResolverForModuleComputationTracker {
}
}
private object DiagnoseUnknownModuleInfoReporter {
fun report(name: String, infos: List<ModuleInfo>): Nothing {
val message = "$name does not know how to resolve $infos"
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 -> throw errorInModulesResolver(message)
}
}
name.contains(ResolverForProject.resolverForScriptDependenciesName) -> errorInScriptDependenciesInfoResolver(message)
name.contains(ResolverForProject.resolverForSpecialInfoName) -> {
when {
name.contains("ScriptModuleInfo") -> errorInScriptModuleInfoResolver(message)
else -> errorInSpecialModuleInfoResolver(message)
}
}
else -> otherError(message)
}
}
// Do not inline 'error*'-methods, they are needed to avoid Exception Analyzer merging those AssertionErrors
private fun errorInSdkResolver(message: String): Nothing = throw AssertionError(message)
private fun errorInLibrariesResolver(message: String): Nothing = throw AssertionError(message)
private fun errorInModulesResolver(message: String): Nothing = throw AssertionError(message)
private fun errorInModulesResolverWithEmptyInfos(message: String): Nothing = throw AssertionError(message)
private fun errorInModulesResolverWithScriptDependencies(message: String): Nothing = throw AssertionError(message)
private fun errorInModulesResolverWithLibraryInfo(message: String): Nothing = throw AssertionError(message)
private fun errorInScriptDependenciesInfoResolver(message: String): Nothing = throw AssertionError(message)
private fun errorInScriptModuleInfoResolver(message: String): Nothing = throw AssertionError(message)
private fun errorInSpecialModuleInfoResolver(message: String): Nothing = throw AssertionError(message)
private fun otherError(message: String): Nothing = throw AssertionError(message)
}
@Suppress("UNCHECKED_CAST")
fun <T> ModuleInfo.getCapability(capability: ModuleDescriptor.Capability<T>) = capabilities[capability] as? T
@@ -0,0 +1,275 @@
/*
* Copyright 2010-2019 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.util.ModificationTracker
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.context.ProjectContext
import org.jetbrains.kotlin.context.withModule
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor
import org.jetbrains.kotlin.descriptors.PackageFragmentProvider
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.platform.TargetPlatform
class ResolverForProjectImpl<M : ModuleInfo>(
private val debugName: String,
private val projectContext: ProjectContext,
modules: Collection<M>,
private val modulesContent: (M) -> ModuleContent<M>,
private val moduleLanguageSettingsProvider: LanguageSettingsProvider,
private val resolverForModuleFactoryByPlatform: (TargetPlatform?) -> ResolverForModuleFactory,
private val invalidateOnOOCB: Boolean,
override val builtInsProvider: (M) -> KotlinBuiltIns,
private val delegateResolver: ResolverForProject<M> = EmptyResolverForProject(),
private val sdkDependency: (M) -> M?,
private val packageOracleFactory: PackageOracleFactory = PackageOracleFactory.OptimisticFactory,
private val isReleaseCoroutines: Boolean? = null
) : ResolverForProject<M>() {
private 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
}
}
// Protected by ("projectContext.storageManager.lock")
private val descriptorByModule = mutableMapOf<M, ModuleData>()
// Protected by ("projectContext.storageManager.lock")
private val moduleInfoByDescriptor = mutableMapOf<ModuleDescriptorImpl, M>()
@Suppress("UNCHECKED_CAST")
private val moduleInfoToResolvableInfo: Map<M, M> =
modules.flatMap { module -> module.flatten().map { modulePart -> modulePart to module } }.toMap() as Map<M, M>
init {
assert(moduleInfoToResolvableInfo.values.toSet() == modules.toSet())
}
override fun tryGetResolverForModule(moduleInfo: M): ResolverForModule? {
if (!isCorrectModuleInfo(moduleInfo)) {
return null
}
return resolverForModuleDescriptor(doGetDescriptorForModule(moduleInfo))
}
private fun setupModuleDescriptor(module: M, moduleDescriptor: ModuleDescriptorImpl) {
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 = mutableMapOf<ModuleDescriptor, ResolverForModule>()
override val allModules: Collection<M> by lazy {
this.moduleInfoToResolvableInfo.keys + delegateResolver.allModules
}
override val name: String
get() = "Resolver for '$debugName'"
private fun isCorrectModuleInfo(moduleInfo: M) = moduleInfo in allModules
override fun resolverForModuleDescriptor(descriptor: ModuleDescriptor): ResolverForModule {
return projectContext.storageManager.compute {
val module = moduleInfoByDescriptor[descriptor]
if (module == null) {
if (delegateResolver is EmptyResolverForProject<*>) {
throw IllegalStateException("$descriptor is not contained in resolver $name")
}
return@compute delegateResolver.resolverForModuleDescriptor(descriptor)
}
resolverByModuleDescriptor.getOrPut(descriptor) {
checkModuleIsCorrect(module)
ResolverForModuleComputationTracker.getInstance(projectContext.project)
?.onResolverComputed(module)
val moduleContent = modulesContent(module)
val languageVersionSettings =
moduleLanguageSettingsProvider.getLanguageVersionSettings(module, projectContext.project, isReleaseCoroutines)
// FIXME(dsavvinov): make sure that module.platform returns platform with the same JvmTarget as this one
val targetPlatformVersion = moduleLanguageSettingsProvider.getTargetPlatform(module, projectContext.project)
val resolverForModuleFactory = resolverForModuleFactoryByPlatform(module.platform)
resolverForModuleFactory.createResolverForModule(
descriptor as ModuleDescriptorImpl,
projectContext.withModule(descriptor),
moduleContent,
this@ResolverForProjectImpl,
languageVersionSettings
)
}
}
}
internal fun isResolverForModuleDescriptorComputed(descriptor: ModuleDescriptor) =
projectContext.storageManager.compute {
descriptor in resolverByModuleDescriptor
}
override fun descriptorForModule(moduleInfo: M): ModuleDescriptorImpl {
checkModuleIsCorrect(moduleInfo)
return doGetDescriptorForModule(moduleInfo)
}
override fun diagnoseUnknownModuleInfo(infos: List<ModuleInfo>): Nothing {
DiagnoseUnknownModuleInfoReporter.report(name, infos)
}
private fun checkModuleIsCorrect(moduleInfo: M) {
if (!isCorrectModuleInfo(moduleInfo)) {
diagnoseUnknownModuleInfo(listOf(moduleInfo))
}
}
private fun doGetDescriptorForModule(module: M): ModuleDescriptorImpl {
val moduleFromThisResolver = 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)
}
val moduleData = createModuleDescriptor(module)
descriptorByModule[module] = moduleData
return moduleData
}
private fun createModuleDescriptor(module: M): ModuleData {
val moduleDescriptor = ModuleDescriptorImpl(
module.name,
projectContext.storageManager,
builtInsProvider(module),
module.platform,
module.capabilities,
module.stableName
)
moduleInfoByDescriptor[moduleDescriptor] = module
setupModuleDescriptor(module, moduleDescriptor)
val modificationTracker = (module as? TrackableModuleInfo)?.createModificationTracker() ?: if (invalidateOnOOCB) {
KotlinModificationTrackerService.getInstance(projectContext.project).outOfBlockModificationTracker
} else {
null
}
return ModuleData(moduleDescriptor, modificationTracker)
}
}
private class DelegatingPackageFragmentProvider<M : ModuleInfo>(
private val resolverForProject: ResolverForProjectImpl<M>,
private val module: ModuleDescriptor,
moduleContent: ModuleContent<M>,
private val packageOracle: PackageOracle
) : PackageFragmentProvider {
private val syntheticFilePackages = moduleContent.syntheticFiles.map { it.packageFqName }.toSet()
override fun getPackageFragments(fqName: FqName): List<PackageFragmentDescriptor> {
if (certainlyDoesNotExist(fqName)) return emptyList()
return resolverForProject.resolverForModuleDescriptor(module).packageFragmentProvider.getPackageFragments(fqName)
}
override fun getSubPackagesOf(fqName: FqName, nameFilter: (Name) -> Boolean): Collection<FqName> {
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
}
}
private object DiagnoseUnknownModuleInfoReporter {
fun report(name: String, infos: List<ModuleInfo>): Nothing {
val message = "$name does not know how to resolve $infos"
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 -> throw errorInModulesResolver(message)
}
}
name.contains(ResolverForProject.resolverForScriptDependenciesName) -> errorInScriptDependenciesInfoResolver(message)
name.contains(ResolverForProject.resolverForSpecialInfoName) -> {
when {
name.contains("ScriptModuleInfo") -> errorInScriptModuleInfoResolver(message)
else -> errorInSpecialModuleInfoResolver(message)
}
}
else -> otherError(message)
}
}
// Do not inline 'error*'-methods, they are needed to avoid Exception Analyzer merging those AssertionErrors
private fun errorInSdkResolver(message: String): Nothing = throw AssertionError(message)
private fun errorInLibrariesResolver(message: String): Nothing = throw AssertionError(message)
private fun errorInModulesResolver(message: String): Nothing = throw AssertionError(message)
private fun errorInModulesResolverWithEmptyInfos(message: String): Nothing = throw AssertionError(message)
private fun errorInModulesResolverWithScriptDependencies(message: String): Nothing = throw AssertionError(message)
private fun errorInModulesResolverWithLibraryInfo(message: String): Nothing = throw AssertionError(message)
private fun errorInScriptDependenciesInfoResolver(message: String): Nothing = throw AssertionError(message)
private fun errorInScriptModuleInfoResolver(message: String): Nothing = throw AssertionError(message)
private fun errorInSpecialModuleInfoResolver(message: String): Nothing = throw AssertionError(message)
private fun otherError(message: String): Nothing = throw AssertionError(message)
}