[Resolve] Introduce CompositeResolver
This commit introduces CompositeResolverForModuleFactory, which should work under so-called "composite resolution mode", where sources of all all modules are analyzed in one global facade. This allows to: - avoid re-analyzation of common sources - avoid retaining memory for all platforms (which can be very bad as soon as we'll start distinguishing various flavours of platforms, especially "flavours" of common platform) - support running platform-specific checks in common modules (e.g., report JVM_PLATFORM_DECLARATION_CLASH if common sources are going to have it) - support analysis of shared platform modules, like commonNative This mode heavily depends on so-called "type refinement" support in the compiler, which is introduced in other series of commits. In this commit, CompositeResolver and related codepaths are left unused. Also, this commit misses several important pieces of logic in resolvers-setup code, which should be different for CompositeResolver - computation of 'firstDependency' - computation of built-ins - computation of modules owned by facade They will be covered in the following commits
This commit is contained in:
+1
-1
@@ -53,7 +53,7 @@ abstract class PlatformDependentAnalyzerServices {
|
||||
)
|
||||
)
|
||||
|
||||
protected abstract fun computePlatformSpecificDefaultImports(storageManager: StorageManager, result: MutableList<ImportPath>)
|
||||
abstract fun computePlatformSpecificDefaultImports(storageManager: StorageManager, result: MutableList<ImportPath>)
|
||||
|
||||
open val excludedImports: List<FqName> get() = emptyList()
|
||||
|
||||
|
||||
@@ -59,7 +59,12 @@ fun StorageComponentContainer.configureModule(
|
||||
|
||||
useInstance(platform)
|
||||
useInstance(analyzerServices)
|
||||
useInstance(platform.componentPlatforms.singleOrNull()?.targetPlatformVersion ?: TargetPlatformVersion.NoVersion)
|
||||
|
||||
val nonTrivialPlatformVersion = platform
|
||||
.mapNotNull { it.targetPlatformVersion.takeIf { it != TargetPlatformVersion.NoVersion } }
|
||||
.singleOrNull()
|
||||
|
||||
useInstance(nonTrivialPlatformVersion ?: TargetPlatformVersion.NoVersion)
|
||||
|
||||
analyzerServices.platformConfigurator.configureModuleComponents(this)
|
||||
analyzerServices.platformConfigurator.configureModuleDependentCheckers(this)
|
||||
|
||||
@@ -100,7 +100,7 @@ abstract class PlatformConfiguratorBase(
|
||||
container.useImpl<ExperimentalMarkerDeclarationAnnotationChecker>()
|
||||
}
|
||||
|
||||
private fun configureExtensionsAndCheckers(container: StorageComponentContainer) {
|
||||
fun configureExtensionsAndCheckers(container: StorageComponentContainer) {
|
||||
with(container) {
|
||||
useInstanceIfNotNull(dynamicTypesSettings)
|
||||
additionalDeclarationCheckers.forEach { useInstance(it) }
|
||||
|
||||
+257
@@ -0,0 +1,257 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
@file:Suppress("Duplicates")
|
||||
|
||||
package org.jetbrains.kotlin.caches.resolve
|
||||
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import org.jetbrains.kotlin.analyzer.*
|
||||
import org.jetbrains.kotlin.analyzer.common.CommonAnalysisParameters
|
||||
import org.jetbrains.kotlin.analyzer.common.CommonPlatformAnalyzerServices
|
||||
import org.jetbrains.kotlin.analyzer.common.configureCommonSpecificComponents
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
||||
import org.jetbrains.kotlin.container.*
|
||||
import org.jetbrains.kotlin.context.ModuleContext
|
||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.PackageFragmentProvider
|
||||
import org.jetbrains.kotlin.descriptors.impl.CompositePackageFragmentProvider
|
||||
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl
|
||||
import org.jetbrains.kotlin.frontend.di.configureModule
|
||||
import org.jetbrains.kotlin.frontend.di.configureStandardResolveComponents
|
||||
import org.jetbrains.kotlin.frontend.java.di.configureJavaSpecificComponents
|
||||
import org.jetbrains.kotlin.frontend.java.di.initializeJavaSpecificComponents
|
||||
import org.jetbrains.kotlin.idea.project.IdeaEnvironment
|
||||
import org.jetbrains.kotlin.incremental.components.LookupTracker
|
||||
import org.jetbrains.kotlin.load.java.lazy.ModuleClassResolver
|
||||
import org.jetbrains.kotlin.load.java.lazy.ModuleClassResolverImpl
|
||||
import org.jetbrains.kotlin.load.kotlin.PackagePartProvider
|
||||
import org.jetbrains.kotlin.load.kotlin.VirtualFileFinderFactory
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.platform.TargetPlatform
|
||||
import org.jetbrains.kotlin.platform.has
|
||||
import org.jetbrains.kotlin.platform.idePlatformKind
|
||||
import org.jetbrains.kotlin.platform.isCommon
|
||||
import org.jetbrains.kotlin.platform.js.isJs
|
||||
import org.jetbrains.kotlin.platform.jvm.JvmPlatform
|
||||
import org.jetbrains.kotlin.platform.konan.KonanPlatform
|
||||
import org.jetbrains.kotlin.platform.konan.KonanPlatforms
|
||||
import org.jetbrains.kotlin.resolve.*
|
||||
import org.jetbrains.kotlin.resolve.checkers.ExpectedActualDeclarationChecker
|
||||
import org.jetbrains.kotlin.resolve.checkers.ExperimentalMarkerDeclarationAnnotationChecker
|
||||
import org.jetbrains.kotlin.resolve.jvm.JavaDescriptorResolver
|
||||
import org.jetbrains.kotlin.resolve.jvm.JvmPlatformParameters
|
||||
import org.jetbrains.kotlin.resolve.lazy.ResolveSession
|
||||
import org.jetbrains.kotlin.resolve.lazy.declarations.DeclarationProviderFactory
|
||||
import org.jetbrains.kotlin.resolve.lazy.declarations.DeclarationProviderFactoryService
|
||||
import org.jetbrains.kotlin.serialization.deserialization.MetadataPackageFragmentProvider
|
||||
import org.jetbrains.kotlin.serialization.deserialization.MetadataPartProvider
|
||||
import org.jetbrains.kotlin.serialization.js.KotlinJavascriptSerializationUtil
|
||||
import org.jetbrains.kotlin.serialization.js.createKotlinJavascriptPackageFragmentProvider
|
||||
import org.jetbrains.kotlin.storage.StorageManager
|
||||
import org.jetbrains.kotlin.utils.KotlinJavascriptMetadataUtils
|
||||
|
||||
class CompositeResolverForModuleFactory(
|
||||
private val commonAnalysisParameters: CommonAnalysisParameters,
|
||||
private val jvmAnalysisParameters: JvmPlatformParameters,
|
||||
private val targetPlatform: TargetPlatform,
|
||||
private val compilerServices: CompositeAnalyzerServices
|
||||
) : ResolverForModuleFactory() {
|
||||
override fun <M : ModuleInfo> createResolverForModule(
|
||||
moduleDescriptor: ModuleDescriptorImpl,
|
||||
moduleContext: ModuleContext,
|
||||
moduleContent: ModuleContent<M>,
|
||||
resolverForProject: ResolverForProject<M>,
|
||||
languageVersionSettings: LanguageVersionSettings
|
||||
): ResolverForModule {
|
||||
val (moduleInfo, syntheticFiles, moduleContentScope) = moduleContent
|
||||
val project = moduleContext.project
|
||||
val declarationProviderFactory = DeclarationProviderFactoryService.createDeclarationProviderFactory(
|
||||
project, moduleContext.storageManager, syntheticFiles,
|
||||
moduleContentScope,
|
||||
moduleInfo
|
||||
)
|
||||
|
||||
val metadataPartProvider = commonAnalysisParameters.metadataPartProviderFactory(moduleContent)
|
||||
val trace = CodeAnalyzerInitializer.getInstance(project).createTrace()
|
||||
|
||||
|
||||
val moduleClassResolver = ModuleClassResolverImpl { javaClass ->
|
||||
val referencedClassModule = jvmAnalysisParameters.moduleByJavaClass(javaClass)
|
||||
// We don't have full control over idea resolve api so we allow for a situation which should not happen in Kotlin.
|
||||
// For example, type in a java library can reference a class declared in a source root (is valid but rare case)
|
||||
// Providing a fallback strategy in this case can hide future problems, so we should at least log to be able to diagnose those
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val resolverForReferencedModule = referencedClassModule?.let { resolverForProject.tryGetResolverForModule(it as M) }
|
||||
|
||||
val resolverForModule = resolverForReferencedModule?.takeIf {
|
||||
referencedClassModule.platform.has<JvmPlatform>() || referencedClassModule.platform == null
|
||||
} ?: run {
|
||||
// in case referenced class lies outside of our resolver, resolve the class as if it is inside our module
|
||||
// this leads to java class being resolved several times
|
||||
resolverForProject.resolverForModule(moduleInfo)
|
||||
}
|
||||
resolverForModule.componentProvider.get<JavaDescriptorResolver>()
|
||||
}
|
||||
|
||||
val packagePartProvider = jvmAnalysisParameters.packagePartProviderFactory(moduleContent)
|
||||
|
||||
val container = createContainerForCompositePlatform(
|
||||
moduleContext, moduleContentScope, languageVersionSettings, targetPlatform,
|
||||
compilerServices, trace, declarationProviderFactory, metadataPartProvider,
|
||||
moduleClassResolver, packagePartProvider
|
||||
)
|
||||
|
||||
val packageFragmentProviders = sequence {
|
||||
yield(container.get<ResolveSession>().packageFragmentProvider)
|
||||
|
||||
yieldAll(getCommonProvidersIfAny(container))
|
||||
yieldAll(getJsProvidersIfAny(moduleInfo, moduleContext, moduleDescriptor, container))
|
||||
yieldAll(getJvmProvidersIfAny(container))
|
||||
yieldAll(getKonanProvidersIfAny(moduleInfo, container))
|
||||
}.toList()
|
||||
|
||||
return ResolverForModule(CompositePackageFragmentProvider(packageFragmentProviders), container)
|
||||
}
|
||||
|
||||
private fun getCommonProvidersIfAny(container: StorageComponentContainer): List<PackageFragmentProvider> =
|
||||
if (targetPlatform.isCommon()) listOf(container.get<MetadataPackageFragmentProvider>()) else emptyList()
|
||||
|
||||
private fun getJvmProvidersIfAny(container: StorageComponentContainer): List<PackageFragmentProvider> =
|
||||
if (targetPlatform.has<JvmPlatform>()) listOf(container.get<JavaDescriptorResolver>().packageFragmentProvider) else emptyList()
|
||||
|
||||
private fun getKonanProvidersIfAny(moduleInfo: ModuleInfo, container: StorageComponentContainer): List<PackageFragmentProvider> {
|
||||
if (!targetPlatform.has<KonanPlatform>()) return emptyList()
|
||||
val resolution = KonanPlatforms.defaultKonanPlatform.idePlatformKind.resolution
|
||||
|
||||
val konanProvider = resolution.createPlatformSpecificPackageFragmentProvider(
|
||||
moduleInfo,
|
||||
container.get<StorageManager>(),
|
||||
container.get<LanguageVersionSettings>(),
|
||||
container.get<ModuleDescriptor>()
|
||||
) ?: return emptyList()
|
||||
|
||||
return listOf(konanProvider)
|
||||
}
|
||||
|
||||
private fun getJsProvidersIfAny(
|
||||
moduleInfo: ModuleInfo,
|
||||
moduleContext: ModuleContext,
|
||||
moduleDescriptor: ModuleDescriptorImpl,
|
||||
container: StorageComponentContainer
|
||||
): List<PackageFragmentProvider> {
|
||||
if (moduleInfo !is LibraryModuleInfo || !moduleInfo.platform.isJs()) return emptyList()
|
||||
|
||||
return moduleInfo.getLibraryRoots()
|
||||
.flatMap { KotlinJavascriptMetadataUtils.loadMetadata(it) }
|
||||
.filter { it.version.isCompatible() }
|
||||
.map { metadata ->
|
||||
val (header, packageFragmentProtos) =
|
||||
KotlinJavascriptSerializationUtil.readModuleAsProto(metadata.body, metadata.version)
|
||||
createKotlinJavascriptPackageFragmentProvider(
|
||||
moduleContext.storageManager, moduleDescriptor, header, packageFragmentProtos, metadata.version,
|
||||
container.get(), LookupTracker.DO_NOTHING
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun createContainerForCompositePlatform(
|
||||
moduleContext: ModuleContext,
|
||||
moduleContentScope: GlobalSearchScope,
|
||||
languageVersionSettings: LanguageVersionSettings,
|
||||
targetPlatform: TargetPlatform,
|
||||
analyzerServices: CompositeAnalyzerServices,
|
||||
trace: BindingTrace,
|
||||
declarationProviderFactory: DeclarationProviderFactory,
|
||||
metadataPartProvider: MetadataPartProvider,
|
||||
// Guaranteed to be non-null for modules with JVM
|
||||
moduleClassResolver: ModuleClassResolver?,
|
||||
packagePartProvider: PackagePartProvider?
|
||||
): StorageComponentContainer = composeContainer("CompositePlatform") {
|
||||
// Shared by all PlatformConfigurators
|
||||
configureDefaultCheckers()
|
||||
|
||||
// Specific for each PlatformConfigurator
|
||||
for (configurator in analyzerServices.services.map { it.platformConfigurator as PlatformConfiguratorBase }) {
|
||||
configurator.configureExtensionsAndCheckers(this)
|
||||
}
|
||||
|
||||
// Called by all normal containers set-ups
|
||||
configureModule(moduleContext, targetPlatform, analyzerServices, trace, languageVersionSettings)
|
||||
configureStandardResolveComponents()
|
||||
useInstance(moduleContentScope)
|
||||
useInstance(declarationProviderFactory)
|
||||
|
||||
// Probably, should be in StandardResolveComponents, but
|
||||
useInstance(VirtualFileFinderFactory.getInstance(moduleContext.project).create(moduleContentScope))
|
||||
useInstance(packagePartProvider!!)
|
||||
|
||||
// JVM-specific
|
||||
if (targetPlatform.has<JvmPlatform>()) {
|
||||
configureJavaSpecificComponents(
|
||||
moduleContext, moduleClassResolver!!, languageVersionSettings, configureJavaClassFinder = null,
|
||||
javaClassTracker = null,
|
||||
useBuiltInsProvider = false
|
||||
)
|
||||
}
|
||||
|
||||
// Common-specific
|
||||
if (targetPlatform.isCommon()) {
|
||||
configureCommonSpecificComponents()
|
||||
}
|
||||
|
||||
IdeaEnvironment.configure(this)
|
||||
}.apply {
|
||||
if (targetPlatform.has<JvmPlatform>()) {
|
||||
initializeJavaSpecificComponents(trace)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class CompositeAnalyzerServices(val services: List<PlatformDependentAnalyzerServices>) : PlatformDependentAnalyzerServices() {
|
||||
override val platformConfigurator: PlatformConfigurator = CompositePlatformConigurator(services.map { it.platformConfigurator })
|
||||
|
||||
override fun computePlatformSpecificDefaultImports(storageManager: StorageManager, result: MutableList<ImportPath>) {
|
||||
val intersectionOfDefaultImports = services.map { service ->
|
||||
mutableListOf<ImportPath>()
|
||||
.apply { service.computePlatformSpecificDefaultImports(storageManager, this) }
|
||||
.toSet()
|
||||
}.safeIntersect()
|
||||
|
||||
result.addAll(intersectionOfDefaultImports)
|
||||
}
|
||||
|
||||
override val defaultLowPriorityImports: List<ImportPath> = services.map { it.defaultLowPriorityImports.toSet() }.safeIntersect()
|
||||
|
||||
override val excludedImports: List<FqName> = services.map { it.excludedImports.toSet() }.safeUnion()
|
||||
|
||||
private fun <T> List<Set<T>>.safeUnion(): List<T> =
|
||||
if (isEmpty()) emptyList() else reduce { first, second -> first.union(second) }.toList()
|
||||
|
||||
private fun <T> List<Set<T>>.safeIntersect(): List<T> =
|
||||
if (isEmpty()) emptyList() else reduce { first, second -> first.intersect(second) }.toList()
|
||||
}
|
||||
|
||||
class CompositePlatformConigurator(private val componentConfigurators: List<PlatformConfigurator>) : PlatformConfigurator {
|
||||
// TODO(dsavvinov): this is actually a hack. Review callers of that method, think about how to refactor it
|
||||
// Unfortunately, clients of that container will inject additional services into them,
|
||||
// without knowing about composite platform (see LocalClassifierAnalyzer), so we can't just use [createContainerForCompositePlatform]
|
||||
// here. Hopefully, platformSpecific container won't at least make things worse.
|
||||
override val platformSpecificContainer: StorageComponentContainer
|
||||
get() = CommonPlatformAnalyzerServices.platformConfigurator.platformSpecificContainer
|
||||
|
||||
override fun configureModuleComponents(container: StorageComponentContainer) {
|
||||
componentConfigurators.forEach { it.configureModuleComponents(container) }
|
||||
}
|
||||
|
||||
override fun configureModuleDependentCheckers(container: StorageComponentContainer) {
|
||||
// We (ab)use the fact that currently, platforms don't use that method, so the only injected compnent will be
|
||||
// ExperimentalMarkerDeclarationAnnotationChecker.
|
||||
// Unfortunately, it is declared in base class, so repeating call to 'configureModuleDependentCheckers' will lead
|
||||
// to multiple registrrations.
|
||||
container.useImpl<ExperimentalMarkerDeclarationAnnotationChecker>()
|
||||
}
|
||||
}
|
||||
+14
-1
@@ -27,6 +27,8 @@ import org.jetbrains.kotlin.analyzer.*
|
||||
import org.jetbrains.kotlin.analyzer.common.CommonAnalysisParameters
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.builtins.jvm.JvmBuiltIns
|
||||
import org.jetbrains.kotlin.caches.resolve.CompositeAnalyzerServices
|
||||
import org.jetbrains.kotlin.caches.resolve.CompositeResolverForModuleFactory
|
||||
import org.jetbrains.kotlin.caches.resolve.resolution
|
||||
import org.jetbrains.kotlin.context.GlobalContextImpl
|
||||
import org.jetbrains.kotlin.context.ProjectContext
|
||||
@@ -37,6 +39,8 @@ import org.jetbrains.kotlin.idea.caches.project.IdeaModuleInfo
|
||||
import org.jetbrains.kotlin.idea.caches.project.getNullableModuleInfo
|
||||
import org.jetbrains.kotlin.idea.compiler.IDELanguageSettingsProvider
|
||||
import org.jetbrains.kotlin.idea.project.IdeaEnvironment
|
||||
import org.jetbrains.kotlin.idea.project.findAnalyzerServices
|
||||
import org.jetbrains.kotlin.idea.project.useCompositeAnalysis
|
||||
import org.jetbrains.kotlin.load.java.structure.JavaClass
|
||||
import org.jetbrains.kotlin.load.java.structure.impl.JavaClassImpl
|
||||
import org.jetbrains.kotlin.platform.idePlatformKind
|
||||
@@ -49,6 +53,7 @@ import org.jetbrains.kotlin.resolve.CompositeBindingContext
|
||||
import org.jetbrains.kotlin.resolve.jvm.JvmPlatformParameters
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult
|
||||
import org.jetbrains.kotlin.platform.DefaultIdeTargetPlatformKindProvider
|
||||
import org.jetbrains.kotlin.platform.toTargetPlatform
|
||||
|
||||
internal class ProjectResolutionFacade(
|
||||
private val debugString: String,
|
||||
@@ -149,7 +154,15 @@ internal class ProjectResolutionFacade(
|
||||
else -> PlatformAnalysisParameters.Empty
|
||||
}
|
||||
|
||||
platform.idePlatformKind.resolution.createResolverForModuleFactory(parameters, IdeaEnvironment, platform)
|
||||
if (!project.useCompositeAnalysis)
|
||||
platform.idePlatformKind.resolution.createResolverForModuleFactory(parameters, IdeaEnvironment, platform)
|
||||
else
|
||||
CompositeResolverForModuleFactory(
|
||||
commonPlatformParameters,
|
||||
jvmPlatformParameters,
|
||||
modulePlatform!!,
|
||||
CompositeAnalyzerServices(modulePlatform.componentPlatforms.map { it.toTargetPlatform().findAnalyzerServices })
|
||||
)
|
||||
},
|
||||
builtIns = builtIns,
|
||||
delegateResolver = delegateResolverForProject,
|
||||
|
||||
@@ -5,13 +5,14 @@
|
||||
|
||||
package org.jetbrains.kotlin.idea.project
|
||||
|
||||
import org.jetbrains.kotlin.analyzer.common.CommonPlatformAnalyzerServices
|
||||
import org.jetbrains.kotlin.caches.resolve.CompositeAnalyzerServices
|
||||
import org.jetbrains.kotlin.js.resolve.JsPlatformAnalyzerServices
|
||||
import org.jetbrains.kotlin.platform.SimplePlatform
|
||||
import org.jetbrains.kotlin.platform.TargetPlatform
|
||||
import org.jetbrains.kotlin.platform.isCommon
|
||||
import org.jetbrains.kotlin.platform.js.isJs
|
||||
import org.jetbrains.kotlin.platform.jvm.isJvm
|
||||
import org.jetbrains.kotlin.platform.konan.isNative
|
||||
import org.jetbrains.kotlin.platform.js.JsPlatform
|
||||
import org.jetbrains.kotlin.platform.jvm.JvmPlatform
|
||||
import org.jetbrains.kotlin.platform.konan.KonanPlatform
|
||||
import org.jetbrains.kotlin.resolve.*
|
||||
import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatformAnalyzerServices
|
||||
import org.jetbrains.kotlin.resolve.konan.platform.NativePlatformAnalyzerServices
|
||||
@@ -20,9 +21,14 @@ import java.lang.IllegalStateException
|
||||
val TargetPlatform.findAnalyzerServices: PlatformDependentAnalyzerServices
|
||||
get() =
|
||||
when {
|
||||
isJvm() -> JvmPlatformAnalyzerServices
|
||||
isJs() -> JsPlatformAnalyzerServices
|
||||
isNative() -> NativePlatformAnalyzerServices
|
||||
isCommon() -> CommonPlatformAnalyzerServices
|
||||
else -> throw IllegalStateException("Unknown platform $this")
|
||||
}
|
||||
isCommon() -> CompositeAnalyzerServices(this.componentPlatforms.map { it.findAnalyzerServices })
|
||||
else -> single().findAnalyzerServices
|
||||
}
|
||||
|
||||
val SimplePlatform.findAnalyzerServices: PlatformDependentAnalyzerServices
|
||||
get() = when (this) {
|
||||
is JvmPlatform -> JvmPlatformAnalyzerServices
|
||||
is JsPlatform -> JsPlatformAnalyzerServices
|
||||
is KonanPlatform -> NativePlatformAnalyzerServices
|
||||
else -> throw IllegalStateException("Unknown platform $this")
|
||||
}
|
||||
Reference in New Issue
Block a user