IDE Performance: skip resolver construction for non-relevant modules
Querying non-existent packages does not trigger module resolver computation
This commit is contained in:
@@ -41,13 +41,14 @@ import org.jetbrains.kotlin.resolve.CompilerEnvironment
|
|||||||
import org.jetbrains.kotlin.resolve.MultiTargetPlatform
|
import org.jetbrains.kotlin.resolve.MultiTargetPlatform
|
||||||
import org.jetbrains.kotlin.resolve.TargetEnvironment
|
import org.jetbrains.kotlin.resolve.TargetEnvironment
|
||||||
import org.jetbrains.kotlin.resolve.TargetPlatform
|
import org.jetbrains.kotlin.resolve.TargetPlatform
|
||||||
|
import org.jetbrains.kotlin.storage.NotNullLazyValue
|
||||||
import org.jetbrains.kotlin.utils.keysToMap
|
import org.jetbrains.kotlin.utils.keysToMap
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.coroutines.experimental.buildSequence
|
import kotlin.coroutines.experimental.buildSequence
|
||||||
|
|
||||||
class ResolverForModule(
|
class ResolverForModule(
|
||||||
val packageFragmentProvider: PackageFragmentProvider,
|
val packageFragmentProvider: PackageFragmentProvider,
|
||||||
val componentProvider: ComponentProvider
|
val componentProvider: ComponentProvider
|
||||||
)
|
)
|
||||||
|
|
||||||
abstract class ResolverForProject<M : ModuleInfo> {
|
abstract class ResolverForProject<M : ModuleInfo> {
|
||||||
@@ -84,7 +85,7 @@ class ResolverForProjectImpl<M : ModuleInfo>(
|
|||||||
return resolverForModuleDescriptor(doGetDescriptorForModule(moduleInfo))
|
return resolverForModuleDescriptor(doGetDescriptorForModule(moduleInfo))
|
||||||
}
|
}
|
||||||
|
|
||||||
internal val resolverByModuleDescriptor: MutableMap<ModuleDescriptor, () -> ResolverForModule> = HashMap()
|
internal val resolverByModuleDescriptor: MutableMap<ModuleDescriptor, NotNullLazyValue<ResolverForModule>> = HashMap()
|
||||||
|
|
||||||
override val allModules: Collection<M> by lazy {
|
override val allModules: Collection<M> by lazy {
|
||||||
(descriptorByModule.keys + delegateResolver.allModules).toSet()
|
(descriptorByModule.keys + delegateResolver.allModules).toSet()
|
||||||
@@ -168,7 +169,8 @@ abstract class AnalyzerFacade<in P : PlatformAnalysisParameters> {
|
|||||||
delegateResolver: ResolverForProject<M> = EmptyResolverForProject(),
|
delegateResolver: ResolverForProject<M> = EmptyResolverForProject(),
|
||||||
packagePartProviderFactory: (M, ModuleContent) -> PackagePartProvider = { _, _ -> PackagePartProvider.Empty },
|
packagePartProviderFactory: (M, ModuleContent) -> PackagePartProvider = { _, _ -> PackagePartProvider.Empty },
|
||||||
firstDependency: M? = null,
|
firstDependency: M? = null,
|
||||||
modulePlatforms: (M) -> MultiTargetPlatform?
|
modulePlatforms: (M) -> MultiTargetPlatform?,
|
||||||
|
packageOracleFactory: PackageOracleFactory = PackageOracleFactory.OptimisticFactory
|
||||||
): ResolverForProject<M> {
|
): ResolverForProject<M> {
|
||||||
val storageManager = projectContext.storageManager
|
val storageManager = projectContext.storageManager
|
||||||
|
|
||||||
@@ -212,10 +214,10 @@ abstract class AnalyzerFacade<in P : PlatformAnalysisParameters> {
|
|||||||
|
|
||||||
for (module in modules) {
|
for (module in modules) {
|
||||||
val descriptor = resolverForProject.descriptorForModule(module)
|
val descriptor = resolverForProject.descriptorForModule(module)
|
||||||
|
val content = modulesContent(module)
|
||||||
val computeResolverForModule = storageManager.createLazyValue {
|
val computeResolverForModule = storageManager.createLazyValue {
|
||||||
ResolverForModuleComputationTracker.getInstance(projectContext.project)?.onResolverComputed(module)
|
ResolverForModuleComputationTracker.getInstance(projectContext.project)?.onResolverComputed(module)
|
||||||
|
|
||||||
val content = modulesContent(module)
|
|
||||||
analyzerFacade(module).createResolverForModule(
|
analyzerFacade(module).createResolverForModule(
|
||||||
module, descriptor, projectContext.withModule(descriptor), modulesContent(module),
|
module, descriptor, projectContext.withModule(descriptor), modulesContent(module),
|
||||||
platformParameters, targetEnvironment, resolverForProject,
|
platformParameters, targetEnvironment, resolverForProject,
|
||||||
@@ -223,7 +225,9 @@ abstract class AnalyzerFacade<in P : PlatformAnalysisParameters> {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
descriptor.initialize(DelegatingPackageFragmentProvider { computeResolverForModule().packageFragmentProvider })
|
DelegatingPackageFragmentProvider(content, packageOracleFactory.createOracle(module), computeResolverForModule)
|
||||||
|
.let { descriptor.initialize(it) }
|
||||||
|
|
||||||
resolverForProject.resolverByModuleDescriptor[descriptor] = computeResolverForModule
|
resolverForProject.resolverByModuleDescriptor[descriptor] = computeResolverForModule
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -245,17 +249,45 @@ abstract class AnalyzerFacade<in P : PlatformAnalysisParameters> {
|
|||||||
abstract val targetPlatform: TargetPlatform
|
abstract val targetPlatform: TargetPlatform
|
||||||
}
|
}
|
||||||
|
|
||||||
//NOTE: relies on delegate to be lazily computed and cached
|
|
||||||
private class DelegatingPackageFragmentProvider(
|
private class DelegatingPackageFragmentProvider(
|
||||||
private val delegate: () -> PackageFragmentProvider
|
moduleContent: ModuleContent,
|
||||||
|
private val packageOracle: PackageOracle,
|
||||||
|
private val resolverForModule: NotNullLazyValue<ResolverForModule>
|
||||||
) : PackageFragmentProvider {
|
) : PackageFragmentProvider {
|
||||||
|
private val syntheticFilePackages = moduleContent.syntheticFiles.map { it.packageFqName }.toSet()
|
||||||
|
|
||||||
override fun getPackageFragments(fqName: FqName): List<PackageFragmentDescriptor> {
|
override fun getPackageFragments(fqName: FqName): List<PackageFragmentDescriptor> {
|
||||||
return delegate().getPackageFragments(fqName)
|
if (certainlyDoesNotExist(fqName)) return emptyList()
|
||||||
|
|
||||||
|
return resolverForModule().packageFragmentProvider.getPackageFragments(fqName)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getSubPackagesOf(fqName: FqName, nameFilter: (Name) -> Boolean): Collection<FqName> {
|
override fun getSubPackagesOf(fqName: FqName, nameFilter: (Name) -> Boolean): Collection<FqName> {
|
||||||
return delegate().getSubPackagesOf(fqName, nameFilter)
|
if (certainlyDoesNotExist(fqName)) return emptyList()
|
||||||
|
|
||||||
|
return resolverForModule().packageFragmentProvider.getSubPackagesOf(fqName, nameFilter)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun certainlyDoesNotExist(fqName: FqName): Boolean {
|
||||||
|
if (resolverForModule.isComputed()) return false // let this request get cached inside delegate
|
||||||
|
|
||||||
|
return !packageOracle.packageExists(fqName) && fqName !in syntheticFilePackages
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface PackageOracle {
|
||||||
|
fun packageExists(fqName: FqName): Boolean
|
||||||
|
|
||||||
|
object Optimistic : PackageOracle {
|
||||||
|
override fun packageExists(fqName: FqName): Boolean = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface PackageOracleFactory {
|
||||||
|
fun createOracle(moduleInfo: ModuleInfo): PackageOracle
|
||||||
|
|
||||||
|
object OptimisticFactory : PackageOracleFactory {
|
||||||
|
override fun createOracle(moduleInfo: ModuleInfo) = PackageOracle.Optimistic
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+3
-1
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.idea.caches.resolve
|
package org.jetbrains.kotlin.idea.caches.resolve
|
||||||
|
|
||||||
|
import com.intellij.openapi.components.service
|
||||||
import com.intellij.openapi.module.ModuleManager
|
import com.intellij.openapi.module.ModuleManager
|
||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
import com.intellij.openapi.projectRoots.ProjectJdkTable
|
import com.intellij.openapi.projectRoots.ProjectJdkTable
|
||||||
@@ -84,7 +85,8 @@ fun createModuleResolverProvider(
|
|||||||
modulesContent, jvmPlatformParameters, IdeaEnvironment, builtInsProvider,
|
modulesContent, jvmPlatformParameters, IdeaEnvironment, builtInsProvider,
|
||||||
delegateResolver, { _, c -> IDEPackagePartProvider(c.moduleContentScope) },
|
delegateResolver, { _, c -> IDEPackagePartProvider(c.moduleContentScope) },
|
||||||
sdk?.let { SdkInfo(project, it) },
|
sdk?.let { SdkInfo(project, it) },
|
||||||
modulePlatforms = { module -> module.platform?.multiTargetPlatform }
|
modulePlatforms = { module -> module.platform?.multiTargetPlatform },
|
||||||
|
packageOracleFactory = project.service<IdePackageOracleFactory>()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,73 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2017 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.idea.caches.resolve
|
||||||
|
|
||||||
|
import com.intellij.openapi.components.service
|
||||||
|
import com.intellij.openapi.project.Project
|
||||||
|
import org.jetbrains.kotlin.analyzer.ModuleInfo
|
||||||
|
import org.jetbrains.kotlin.analyzer.PackageOracle
|
||||||
|
import org.jetbrains.kotlin.analyzer.PackageOracleFactory
|
||||||
|
import org.jetbrains.kotlin.idea.caches.PerModulePackageCacheService
|
||||||
|
import org.jetbrains.kotlin.name.FqName
|
||||||
|
import org.jetbrains.kotlin.name.isSubpackageOf
|
||||||
|
import org.jetbrains.kotlin.resolve.jvm.KotlinJavaPsiFacade
|
||||||
|
import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatform
|
||||||
|
|
||||||
|
class IdePackageOracleFactory(val project: Project) : PackageOracleFactory {
|
||||||
|
override fun createOracle(moduleInfo: ModuleInfo): PackageOracle {
|
||||||
|
if (moduleInfo !is IdeaModuleInfo) return PackageOracle.Optimistic
|
||||||
|
|
||||||
|
when {
|
||||||
|
moduleInfo.platform == JvmPlatform -> when (moduleInfo.moduleOrigin) {
|
||||||
|
ModuleOrigin.LIBRARY -> return JavaPackagesOracle(moduleInfo, project)
|
||||||
|
ModuleOrigin.MODULE -> return JvmSourceOracle(moduleInfo as ModuleSourceInfo, project)
|
||||||
|
ModuleOrigin.OTHER -> return PackageOracle.Optimistic
|
||||||
|
}
|
||||||
|
else -> when (moduleInfo.moduleOrigin) {
|
||||||
|
ModuleOrigin.MODULE -> return KotlinSourceFilesOracle(moduleInfo as ModuleSourceInfo)
|
||||||
|
else -> return PackageOracle.Optimistic // binaries for non-jvm platform need some oracles based on their structure
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class JavaPackagesOracle(moduleInfo: IdeaModuleInfo, project: Project) : PackageOracle {
|
||||||
|
private val scope = moduleInfo.contentScope()
|
||||||
|
private val facade = project.service<KotlinJavaPsiFacade>()
|
||||||
|
|
||||||
|
override fun packageExists(fqName: FqName) = facade.findPackage(fqName.asString(), scope) != null
|
||||||
|
}
|
||||||
|
|
||||||
|
private class KotlinSourceFilesOracle(private val moduleInfo: ModuleSourceInfo) : PackageOracle {
|
||||||
|
private val cacheService = moduleInfo.module.project.service<PerModulePackageCacheService>()
|
||||||
|
|
||||||
|
override fun packageExists(fqName: FqName): Boolean {
|
||||||
|
return cacheService.packageExists(fqName, moduleInfo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class JvmSourceOracle(moduleInfo: ModuleSourceInfo, project: Project) : PackageOracle {
|
||||||
|
private val javaPackagesOracle = JavaPackagesOracle(moduleInfo, project)
|
||||||
|
private val kotlinSourceOracle = KotlinSourceFilesOracle(moduleInfo)
|
||||||
|
|
||||||
|
override fun packageExists(fqName: FqName) =
|
||||||
|
javaPackagesOracle.packageExists(fqName)
|
||||||
|
|| kotlinSourceOracle.packageExists(fqName)
|
||||||
|
|| fqName.isSubpackageOf(ANDROID_SYNTHETIC_PACKAGE_PREFIX)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val ANDROID_SYNTHETIC_PACKAGE_PREFIX = FqName("kotlinx.android.synthetic")
|
||||||
@@ -322,6 +322,9 @@
|
|||||||
<projectService serviceInterface="org.jetbrains.kotlin.idea.caches.resolve.LibraryDependenciesCache"
|
<projectService serviceInterface="org.jetbrains.kotlin.idea.caches.resolve.LibraryDependenciesCache"
|
||||||
serviceImplementation="org.jetbrains.kotlin.idea.caches.resolve.LibraryDependenciesCacheImpl"/>
|
serviceImplementation="org.jetbrains.kotlin.idea.caches.resolve.LibraryDependenciesCacheImpl"/>
|
||||||
|
|
||||||
|
<projectService serviceInterface="org.jetbrains.kotlin.idea.caches.resolve.IdePackageOracleFactory"
|
||||||
|
serviceImplementation="org.jetbrains.kotlin.idea.caches.resolve.IdePackageOracleFactory"/>
|
||||||
|
|
||||||
<errorHandler implementation="org.jetbrains.kotlin.idea.reporter.KotlinReportSubmitter"/>
|
<errorHandler implementation="org.jetbrains.kotlin.idea.reporter.KotlinReportSubmitter"/>
|
||||||
|
|
||||||
<internalFileTemplate name="Kotlin File"/>
|
<internalFileTemplate name="Kotlin File"/>
|
||||||
|
|||||||
Reference in New Issue
Block a user