Skip to content

Commit 21b32b9

Browse files
committed
IDE Performance: skip resolver construction for non-relevant modules
Querying non-existent packages does not trigger module resolver computation
1 parent 55721e4 commit 21b32b9

File tree

4 files changed

+121
-11
lines changed

4 files changed

+121
-11
lines changed

compiler/frontend/src/org/jetbrains/kotlin/analyzer/AnalyzerFacade.kt

Lines changed: 42 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -41,13 +41,14 @@ import org.jetbrains.kotlin.resolve.CompilerEnvironment
4141
import org.jetbrains.kotlin.resolve.MultiTargetPlatform
4242
import org.jetbrains.kotlin.resolve.TargetEnvironment
4343
import org.jetbrains.kotlin.resolve.TargetPlatform
44+
import org.jetbrains.kotlin.storage.NotNullLazyValue
4445
import org.jetbrains.kotlin.utils.keysToMap
4546
import java.util.*
4647
import kotlin.coroutines.experimental.buildSequence
4748

4849
class ResolverForModule(
49-
val packageFragmentProvider: PackageFragmentProvider,
50-
val componentProvider: ComponentProvider
50+
val packageFragmentProvider: PackageFragmentProvider,
51+
val componentProvider: ComponentProvider
5152
)
5253

5354
abstract class ResolverForProject<M : ModuleInfo> {
@@ -84,7 +85,7 @@ class ResolverForProjectImpl<M : ModuleInfo>(
8485
return resolverForModuleDescriptor(doGetDescriptorForModule(moduleInfo))
8586
}
8687

87-
internal val resolverByModuleDescriptor: MutableMap<ModuleDescriptor, () -> ResolverForModule> = HashMap()
88+
internal val resolverByModuleDescriptor: MutableMap<ModuleDescriptor, NotNullLazyValue<ResolverForModule>> = HashMap()
8889

8990
override val allModules: Collection<M> by lazy {
9091
(descriptorByModule.keys + delegateResolver.allModules).toSet()
@@ -168,7 +169,8 @@ abstract class AnalyzerFacade<in P : PlatformAnalysisParameters> {
168169
delegateResolver: ResolverForProject<M> = EmptyResolverForProject(),
169170
packagePartProviderFactory: (M, ModuleContent) -> PackagePartProvider = { _, _ -> PackagePartProvider.Empty },
170171
firstDependency: M? = null,
171-
modulePlatforms: (M) -> MultiTargetPlatform?
172+
modulePlatforms: (M) -> MultiTargetPlatform?,
173+
packageOracleFactory: PackageOracleFactory = PackageOracleFactory.OptimisticFactory
172174
): ResolverForProject<M> {
173175
val storageManager = projectContext.storageManager
174176

@@ -212,18 +214,20 @@ abstract class AnalyzerFacade<in P : PlatformAnalysisParameters> {
212214

213215
for (module in modules) {
214216
val descriptor = resolverForProject.descriptorForModule(module)
217+
val content = modulesContent(module)
215218
val computeResolverForModule = storageManager.createLazyValue {
216219
ResolverForModuleComputationTracker.getInstance(projectContext.project)?.onResolverComputed(module)
217220

218-
val content = modulesContent(module)
219221
analyzerFacade(module).createResolverForModule(
220222
module, descriptor, projectContext.withModule(descriptor), modulesContent(module),
221223
platformParameters, targetEnvironment, resolverForProject,
222224
packagePartProviderFactory(module, content)
223225
)
224226
}
225227

226-
descriptor.initialize(DelegatingPackageFragmentProvider { computeResolverForModule().packageFragmentProvider })
228+
DelegatingPackageFragmentProvider(content, packageOracleFactory.createOracle(module), computeResolverForModule)
229+
.let { descriptor.initialize(it) }
230+
227231
resolverForProject.resolverByModuleDescriptor[descriptor] = computeResolverForModule
228232
}
229233

@@ -245,17 +249,45 @@ abstract class AnalyzerFacade<in P : PlatformAnalysisParameters> {
245249
abstract val targetPlatform: TargetPlatform
246250
}
247251

248-
//NOTE: relies on delegate to be lazily computed and cached
249252
private class DelegatingPackageFragmentProvider(
250-
private val delegate: () -> PackageFragmentProvider
253+
moduleContent: ModuleContent,
254+
private val packageOracle: PackageOracle,
255+
private val resolverForModule: NotNullLazyValue<ResolverForModule>
251256
) : PackageFragmentProvider {
257+
private val syntheticFilePackages = moduleContent.syntheticFiles.map { it.packageFqName }.toSet()
252258

253259
override fun getPackageFragments(fqName: FqName): List<PackageFragmentDescriptor> {
254-
return delegate().getPackageFragments(fqName)
260+
if (certainlyDoesNotExist(fqName)) return emptyList()
261+
262+
return resolverForModule().packageFragmentProvider.getPackageFragments(fqName)
255263
}
256264

257265
override fun getSubPackagesOf(fqName: FqName, nameFilter: (Name) -> Boolean): Collection<FqName> {
258-
return delegate().getSubPackagesOf(fqName, nameFilter)
266+
if (certainlyDoesNotExist(fqName)) return emptyList()
267+
268+
return resolverForModule().packageFragmentProvider.getSubPackagesOf(fqName, nameFilter)
269+
}
270+
271+
private fun certainlyDoesNotExist(fqName: FqName): Boolean {
272+
if (resolverForModule.isComputed()) return false // let this request get cached inside delegate
273+
274+
return !packageOracle.packageExists(fqName) && fqName !in syntheticFilePackages
275+
}
276+
}
277+
278+
interface PackageOracle {
279+
fun packageExists(fqName: FqName): Boolean
280+
281+
object Optimistic : PackageOracle {
282+
override fun packageExists(fqName: FqName): Boolean = true
283+
}
284+
}
285+
286+
interface PackageOracleFactory {
287+
fun createOracle(moduleInfo: ModuleInfo): PackageOracle
288+
289+
object OptimisticFactory : PackageOracleFactory {
290+
override fun createOracle(moduleInfo: ModuleInfo) = PackageOracle.Optimistic
259291
}
260292
}
261293

idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/ModuleDependencyMapper.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package org.jetbrains.kotlin.idea.caches.resolve
1818

19+
import com.intellij.openapi.components.service
1920
import com.intellij.openapi.module.ModuleManager
2021
import com.intellij.openapi.project.Project
2122
import com.intellij.openapi.projectRoots.ProjectJdkTable
@@ -84,7 +85,8 @@ fun createModuleResolverProvider(
8485
modulesContent, jvmPlatformParameters, IdeaEnvironment, builtInsProvider,
8586
delegateResolver, { _, c -> IDEPackagePartProvider(c.moduleContentScope) },
8687
sdk?.let { SdkInfo(project, it) },
87-
modulePlatforms = { module -> module.platform?.multiTargetPlatform }
88+
modulePlatforms = { module -> module.platform?.multiTargetPlatform },
89+
packageOracleFactory = project.service<IdePackageOracleFactory>()
8890
)
8991
}
9092

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/*
2+
* Copyright 2010-2017 JetBrains s.r.o.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.jetbrains.kotlin.idea.caches.resolve
18+
19+
import com.intellij.openapi.components.service
20+
import com.intellij.openapi.project.Project
21+
import org.jetbrains.kotlin.analyzer.ModuleInfo
22+
import org.jetbrains.kotlin.analyzer.PackageOracle
23+
import org.jetbrains.kotlin.analyzer.PackageOracleFactory
24+
import org.jetbrains.kotlin.idea.caches.PerModulePackageCacheService
25+
import org.jetbrains.kotlin.name.FqName
26+
import org.jetbrains.kotlin.name.isSubpackageOf
27+
import org.jetbrains.kotlin.resolve.jvm.KotlinJavaPsiFacade
28+
import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatform
29+
30+
class IdePackageOracleFactory(val project: Project) : PackageOracleFactory {
31+
override fun createOracle(moduleInfo: ModuleInfo): PackageOracle {
32+
if (moduleInfo !is IdeaModuleInfo) return PackageOracle.Optimistic
33+
34+
when {
35+
moduleInfo.platform == JvmPlatform -> when (moduleInfo.moduleOrigin) {
36+
ModuleOrigin.LIBRARY -> return JavaPackagesOracle(moduleInfo, project)
37+
ModuleOrigin.MODULE -> return JvmSourceOracle(moduleInfo as ModuleSourceInfo, project)
38+
ModuleOrigin.OTHER -> return PackageOracle.Optimistic
39+
}
40+
else -> when (moduleInfo.moduleOrigin) {
41+
ModuleOrigin.MODULE -> return KotlinSourceFilesOracle(moduleInfo as ModuleSourceInfo)
42+
else -> return PackageOracle.Optimistic // binaries for non-jvm platform need some oracles based on their structure
43+
}
44+
}
45+
}
46+
47+
private class JavaPackagesOracle(moduleInfo: IdeaModuleInfo, project: Project) : PackageOracle {
48+
private val scope = moduleInfo.contentScope()
49+
private val facade = project.service<KotlinJavaPsiFacade>()
50+
51+
override fun packageExists(fqName: FqName) = facade.findPackage(fqName.asString(), scope) != null
52+
}
53+
54+
private class KotlinSourceFilesOracle(private val moduleInfo: ModuleSourceInfo) : PackageOracle {
55+
private val cacheService = moduleInfo.module.project.service<PerModulePackageCacheService>()
56+
57+
override fun packageExists(fqName: FqName): Boolean {
58+
return cacheService.packageExists(fqName, moduleInfo)
59+
}
60+
}
61+
62+
private class JvmSourceOracle(moduleInfo: ModuleSourceInfo, project: Project) : PackageOracle {
63+
private val javaPackagesOracle = JavaPackagesOracle(moduleInfo, project)
64+
private val kotlinSourceOracle = KotlinSourceFilesOracle(moduleInfo)
65+
66+
override fun packageExists(fqName: FqName) =
67+
javaPackagesOracle.packageExists(fqName)
68+
|| kotlinSourceOracle.packageExists(fqName)
69+
|| fqName.isSubpackageOf(ANDROID_SYNTHETIC_PACKAGE_PREFIX)
70+
}
71+
}
72+
73+
private val ANDROID_SYNTHETIC_PACKAGE_PREFIX = FqName("kotlinx.android.synthetic")

idea/src/META-INF/plugin.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,9 @@
322322
<projectService serviceInterface="org.jetbrains.kotlin.idea.caches.resolve.LibraryDependenciesCache"
323323
serviceImplementation="org.jetbrains.kotlin.idea.caches.resolve.LibraryDependenciesCacheImpl"/>
324324

325+
<projectService serviceInterface="org.jetbrains.kotlin.idea.caches.resolve.IdePackageOracleFactory"
326+
serviceImplementation="org.jetbrains.kotlin.idea.caches.resolve.IdePackageOracleFactory"/>
327+
325328
<errorHandler implementation="org.jetbrains.kotlin.idea.reporter.KotlinReportSubmitter"/>
326329

327330
<internalFileTemplate name="Kotlin File"/>

0 commit comments

Comments
 (0)