Optimize PluginDeclarationProviderFactory.getStubBasedPackageMemberDeclarationProvider
Add cache for fast package exists check in specific module #KT-16850 fixed
This commit is contained in:
@@ -58,7 +58,8 @@ object JvmAnalyzerFacade : AnalyzerFacade<JvmPlatformParameters>() {
|
||||
val project = moduleContext.project
|
||||
val declarationProviderFactory = DeclarationProviderFactoryService.createDeclarationProviderFactory(
|
||||
project, moduleContext.storageManager, syntheticFiles,
|
||||
if (moduleInfo.isLibrary) GlobalSearchScope.EMPTY_SCOPE else moduleContentScope
|
||||
if (moduleInfo.isLibrary) GlobalSearchScope.EMPTY_SCOPE else moduleContentScope,
|
||||
moduleInfo
|
||||
)
|
||||
|
||||
val moduleClassResolver = ModuleClassResolverImpl { javaClass ->
|
||||
|
||||
@@ -101,7 +101,8 @@ object DefaultAnalyzerFacade : AnalyzerFacade<PlatformAnalysisParameters>() {
|
||||
val project = moduleContext.project
|
||||
val declarationProviderFactory = DeclarationProviderFactoryService.createDeclarationProviderFactory(
|
||||
project, moduleContext.storageManager, syntheticFiles,
|
||||
if (moduleInfo.isLibrary) GlobalSearchScope.EMPTY_SCOPE else moduleContentScope
|
||||
if (moduleInfo.isLibrary) GlobalSearchScope.EMPTY_SCOPE else moduleContentScope,
|
||||
moduleInfo
|
||||
)
|
||||
|
||||
val trace = CodeAnalyzerInitializer.getInstance(project).createTrace()
|
||||
|
||||
+3
-1
@@ -18,6 +18,7 @@ package org.jetbrains.kotlin.resolve.lazy.declarations
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import org.jetbrains.kotlin.analyzer.ModuleInfo
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.storage.StorageManager
|
||||
import org.jetbrains.kotlin.utils.sure
|
||||
@@ -29,7 +30,8 @@ class CliDeclarationProviderFactoryService(private val sourceFiles: Collection<K
|
||||
project: Project,
|
||||
storageManager: StorageManager,
|
||||
syntheticFiles: Collection<KtFile>,
|
||||
filesScope: GlobalSearchScope
|
||||
filesScope: GlobalSearchScope,
|
||||
moduleInfo: ModuleInfo
|
||||
): DeclarationProviderFactory {
|
||||
val allFiles = ArrayList<KtFile>()
|
||||
sourceFiles.filterTo(allFiles) {
|
||||
|
||||
+6
-3
@@ -21,6 +21,7 @@ import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.psi.search.DelegatingGlobalSearchScope
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import org.jetbrains.kotlin.analyzer.ModuleInfo
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.storage.StorageManager
|
||||
import java.util.*
|
||||
@@ -31,7 +32,8 @@ abstract class DeclarationProviderFactoryService {
|
||||
project: Project,
|
||||
storageManager: StorageManager,
|
||||
syntheticFiles: Collection<KtFile>,
|
||||
filesScope: GlobalSearchScope
|
||||
filesScope: GlobalSearchScope,
|
||||
moduleInfo: ModuleInfo
|
||||
): DeclarationProviderFactory
|
||||
|
||||
companion object {
|
||||
@@ -39,10 +41,11 @@ abstract class DeclarationProviderFactoryService {
|
||||
project: Project,
|
||||
storageManager: StorageManager,
|
||||
syntheticFiles: Collection<KtFile>,
|
||||
filesScope: GlobalSearchScope
|
||||
filesScope: GlobalSearchScope,
|
||||
moduleInfo: ModuleInfo
|
||||
): DeclarationProviderFactory {
|
||||
return ServiceManager.getService(project, DeclarationProviderFactoryService::class.java)!!
|
||||
.create(project, storageManager, syntheticFiles, filteringScope(syntheticFiles, filesScope))
|
||||
.create(project, storageManager, syntheticFiles, filteringScope(syntheticFiles, filesScope), moduleInfo)
|
||||
}
|
||||
|
||||
private fun filteringScope(syntheticFiles: Collection<KtFile>, baseScope: GlobalSearchScope): GlobalSearchScope {
|
||||
|
||||
@@ -51,7 +51,11 @@ object JsAnalyzerFacade : AnalyzerFacade<PlatformAnalysisParameters>() {
|
||||
val (syntheticFiles, moduleContentScope) = moduleContent
|
||||
val project = moduleContext.project
|
||||
val declarationProviderFactory = DeclarationProviderFactoryService.createDeclarationProviderFactory(
|
||||
project, moduleContext.storageManager, syntheticFiles, if (moduleInfo.isLibrary) GlobalSearchScope.EMPTY_SCOPE else moduleContentScope
|
||||
project,
|
||||
moduleContext.storageManager,
|
||||
syntheticFiles,
|
||||
if (moduleInfo.isLibrary) GlobalSearchScope.EMPTY_SCOPE else moduleContentScope,
|
||||
moduleInfo
|
||||
)
|
||||
|
||||
val languageVersionSettings = LanguageSettingsProvider.getInstance(project).getLanguageVersionSettings(moduleInfo, project)
|
||||
|
||||
+124
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
import com.intellij.openapi.components.ServiceManager
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.Key
|
||||
import com.intellij.openapi.util.Ref
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.openapi.vfs.VirtualFileManager
|
||||
import com.intellij.openapi.vfs.newvfs.BulkFileListener
|
||||
import com.intellij.openapi.vfs.newvfs.events.*
|
||||
import com.intellij.psi.impl.PsiTreeChangeEventImpl
|
||||
import com.intellij.psi.impl.PsiTreeChangePreprocessor
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import com.intellij.util.containers.ContainerUtil
|
||||
import org.jetbrains.kotlin.analyzer.ModuleInfo
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.ModuleSourceInfo
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.getModuleInfoByVirtualFile
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.getNullableModuleInfo
|
||||
import org.jetbrains.kotlin.idea.stubindex.PackageIndexUtil
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.psi.KtPackageDirective
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
|
||||
import java.util.concurrent.ConcurrentMap
|
||||
|
||||
class KotlinPackageContentModificationListener(
|
||||
private val project: Project
|
||||
) {
|
||||
init {
|
||||
val connection = project.messageBus.connect()
|
||||
|
||||
val scope = GlobalSearchScope.projectScope(project)
|
||||
|
||||
connection.subscribe(VirtualFileManager.VFS_CHANGES, object : BulkFileListener.Adapter() {
|
||||
override fun before(events: MutableList<out VFileEvent>) = onEvents(events) { it is VFileDeleteEvent || it is VFileMoveEvent }
|
||||
override fun after(events: List<VFileEvent>) = onEvents(events) { it is VFileMoveEvent || it is VFileCreateEvent || it is VFileCopyEvent }
|
||||
|
||||
fun onEvents(events: List<VFileEvent>, filter: (VFileEvent) -> Boolean) = events.asSequence()
|
||||
.filter(filter)
|
||||
.map { it.file }
|
||||
.filterNotNull()
|
||||
.filter { it in scope }
|
||||
.forEach { file ->
|
||||
PerModulePackageCacheService.notifyPackageChange(file, project)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
class KotlinPackageStatementPsiTreeChangePreprocessor : PsiTreeChangePreprocessor {
|
||||
override fun treeChanged(event: PsiTreeChangeEventImpl) {
|
||||
val file = event.file as? KtFile ?: return
|
||||
|
||||
when (event.code) {
|
||||
PsiTreeChangeEventImpl.PsiEventType.CHILD_ADDED, PsiTreeChangeEventImpl.PsiEventType.CHILD_MOVED, PsiTreeChangeEventImpl.PsiEventType.CHILD_REPLACED, PsiTreeChangeEventImpl.PsiEventType.CHILD_REMOVED -> {
|
||||
val child = event.child ?: return
|
||||
if (child.getParentOfType<KtPackageDirective>(false) != null)
|
||||
PerModulePackageCacheService.notifyPackageChange(file)
|
||||
}
|
||||
else -> {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class PerModulePackageCacheService {
|
||||
|
||||
val cache = ContainerUtil.createConcurrentWeakMap<ModuleInfo, Ref<ConcurrentMap<FqName, Boolean>>>()
|
||||
|
||||
companion object {
|
||||
|
||||
val PER_MODULE_PACKAGE_CACHE = Key.create<ConcurrentMap<ModuleInfo, Ref<ConcurrentMap<FqName, Boolean>>>>("per_module_package_cache")
|
||||
|
||||
internal fun notifyPackageChange(file: KtFile): Unit {
|
||||
(file.getNullableModuleInfo() as? ModuleSourceInfo)?.let { onChangeInModuleSource(it, file.project) }
|
||||
}
|
||||
|
||||
internal fun onChangeInModuleSource(moduleSourceInfo: ModuleSourceInfo, project: Project) = with(getInstance(project)) {
|
||||
cache[moduleSourceInfo]?.set(null)
|
||||
}
|
||||
|
||||
internal fun notifyPackageChange(file: VirtualFile, project: Project): Unit {
|
||||
(getModuleInfoByVirtualFile(project, file) as? ModuleSourceInfo)?.let { onChangeInModuleSource(it, project) }
|
||||
}
|
||||
|
||||
fun getInstance(project: Project): PerModulePackageCacheService = ServiceManager.getService(project, PerModulePackageCacheService::class.java)
|
||||
|
||||
fun packageExists(packageFqName: FqName, moduleInfo: ModuleSourceInfo, project: Project): Boolean = with(getInstance(project)) {
|
||||
val module = moduleInfo.module
|
||||
|
||||
// Module own cache is a view on global cache. Since global cache based on WeakReferences when module
|
||||
// gets disposed this soft map will be disposed too, leading to drop soft refs on ModuleInfo's, and then to
|
||||
// disposing global cache entry
|
||||
val moduleOwnCache = module.getUserData(PerModulePackageCacheService.PER_MODULE_PACKAGE_CACHE) ?: run {
|
||||
ContainerUtil.createConcurrentSoftMap<ModuleInfo, Ref<ConcurrentMap<FqName, Boolean>>>()
|
||||
.apply { module.putUserData(PerModulePackageCacheService.PER_MODULE_PACKAGE_CACHE, this) }
|
||||
}
|
||||
val cached = moduleOwnCache.getOrPut(moduleInfo) { cache.getOrPut(moduleInfo) { Ref() } }
|
||||
.apply { if (isNull) set(ContainerUtil.createConcurrentSoftMap<FqName, Boolean>()) }
|
||||
.get()
|
||||
|
||||
|
||||
return cached.getOrPut(packageFqName) {
|
||||
PackageIndexUtil.packageExists(packageFqName, moduleInfo.contentScope(), project)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -53,6 +53,32 @@ class KotlinSourceFilterScope private constructor(
|
||||
"cls=$includeClassFiles script=$includeScriptDependencies)"
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other?.javaClass != javaClass) return false
|
||||
if (!super.equals(other)) return false
|
||||
|
||||
other as KotlinSourceFilterScope
|
||||
|
||||
if (includeProjectSourceFiles != other.includeProjectSourceFiles) return false
|
||||
if (includeLibrarySourceFiles != other.includeLibrarySourceFiles) return false
|
||||
if (includeClassFiles != other.includeClassFiles) return false
|
||||
if (includeScriptDependencies != other.includeScriptDependencies) return false
|
||||
if (project != other.project) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = super.hashCode()
|
||||
result = 31 * result + includeProjectSourceFiles.hashCode()
|
||||
result = 31 * result + includeLibrarySourceFiles.hashCode()
|
||||
result = 31 * result + includeClassFiles.hashCode()
|
||||
result = 31 * result + includeScriptDependencies.hashCode()
|
||||
result = 31 * result + project.hashCode()
|
||||
return result
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun sourcesAndLibraries(delegate: GlobalSearchScope, project: Project) = create(delegate, true, true, true, true, project)
|
||||
|
||||
@@ -25,6 +25,7 @@ import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
|
||||
|
||||
object PackageIndexUtil {
|
||||
@JvmStatic fun getSubPackageFqNames(
|
||||
packageFqName: FqName,
|
||||
@@ -48,6 +49,7 @@ object PackageIndexUtil {
|
||||
searchScope: GlobalSearchScope,
|
||||
project: Project
|
||||
): Boolean {
|
||||
|
||||
val subpackagesIndex = SubpackagesIndexService.getInstance(project)
|
||||
if (!subpackagesIndex.packageExists(packageFqName)) {
|
||||
return false
|
||||
|
||||
+13
-2
@@ -18,6 +18,9 @@ package org.jetbrains.kotlin.idea.stubindex.resolve
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import org.jetbrains.kotlin.analyzer.ModuleInfo
|
||||
import org.jetbrains.kotlin.idea.caches.PerModulePackageCacheService
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.ModuleSourceInfo
|
||||
import org.jetbrains.kotlin.idea.stubindex.PackageIndexUtil
|
||||
import org.jetbrains.kotlin.idea.stubindex.SubpackagesIndexService
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
@@ -30,7 +33,8 @@ class PluginDeclarationProviderFactory(
|
||||
private val project: Project,
|
||||
private val indexedFilesScope: GlobalSearchScope,
|
||||
private val storageManager: StorageManager,
|
||||
private val nonIndexedFiles: Collection<KtFile>
|
||||
private val nonIndexedFiles: Collection<KtFile>,
|
||||
private val moduleInfo: ModuleInfo
|
||||
) : AbstractDeclarationProviderFactory(storageManager) {
|
||||
private val fileBasedDeclarationProviderFactory = FileBasedDeclarationProviderFactory(storageManager, nonIndexedFiles)
|
||||
|
||||
@@ -49,8 +53,15 @@ class PluginDeclarationProviderFactory(
|
||||
}
|
||||
}
|
||||
|
||||
private fun packageExists(name: FqName): Boolean {
|
||||
return if (moduleInfo is ModuleSourceInfo)
|
||||
PerModulePackageCacheService.packageExists(name, moduleInfo, project)
|
||||
else
|
||||
PackageIndexUtil.packageExists(name, indexedFilesScope, project)
|
||||
}
|
||||
|
||||
private fun getStubBasedPackageMemberDeclarationProvider(name: FqName): PackageMemberDeclarationProvider? {
|
||||
if (!PackageIndexUtil.packageExists(name, indexedFilesScope, project)) return null
|
||||
if (!packageExists(name)) return null
|
||||
|
||||
return StubBasedPackageMemberDeclarationProvider(name, project, indexedFilesScope)
|
||||
}
|
||||
|
||||
+4
-2
@@ -18,6 +18,7 @@ package org.jetbrains.kotlin.idea.stubindex.resolve
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import org.jetbrains.kotlin.analyzer.ModuleInfo
|
||||
import org.jetbrains.kotlin.idea.stubindex.KotlinSourceFilterScope
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.resolve.lazy.declarations.DeclarationProviderFactory
|
||||
@@ -30,7 +31,8 @@ class PluginDeclarationProviderFactoryService : DeclarationProviderFactoryServic
|
||||
project: Project,
|
||||
storageManager: StorageManager,
|
||||
syntheticFiles: Collection<KtFile>,
|
||||
filesScope: GlobalSearchScope
|
||||
filesScope: GlobalSearchScope,
|
||||
moduleInfo: ModuleInfo
|
||||
): DeclarationProviderFactory =
|
||||
PluginDeclarationProviderFactory(project, KotlinSourceFilterScope.sources(filesScope, project), storageManager, syntheticFiles)
|
||||
PluginDeclarationProviderFactory(project, KotlinSourceFilterScope.sources(filesScope, project), storageManager, syntheticFiles, moduleInfo)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* 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.core.util
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.util.CachedValue
|
||||
import com.intellij.psi.util.CachedValueProvider
|
||||
import com.intellij.psi.util.CachedValuesManager
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
operator fun <T> CachedValue<T>.getValue(o: Any, property: KProperty<*>): T = value
|
||||
|
||||
fun <T> CachedValue(project: Project, trackValue: Boolean = false, provider: () -> CachedValueProvider.Result<T>) =
|
||||
CachedValuesManager.getManager(project).createCachedValue(provider, trackValue)
|
||||
@@ -40,6 +40,9 @@
|
||||
<component>
|
||||
<implementation-class>org.jetbrains.kotlin.idea.project.KotlinCodeBlockModificationListener</implementation-class>
|
||||
</component>
|
||||
<component>
|
||||
<implementation-class>org.jetbrains.kotlin.idea.caches.KotlinPackageContentModificationListener</implementation-class>
|
||||
</component>
|
||||
</project-components>
|
||||
|
||||
<application-components>
|
||||
@@ -311,6 +314,8 @@
|
||||
<projectService serviceInterface="org.jetbrains.kotlin.idea.kdoc.SampleResolutionService"
|
||||
serviceImplementation="org.jetbrains.kotlin.idea.kdoc.IdeSampleResolutionService"/>
|
||||
|
||||
<projectService serviceImplementation="org.jetbrains.kotlin.idea.caches.PerModulePackageCacheService"/>
|
||||
|
||||
<errorHandler implementation="org.jetbrains.kotlin.idea.reporter.KotlinReportSubmitter"/>
|
||||
|
||||
<internalFileTemplate name="Kotlin File"/>
|
||||
@@ -454,6 +459,8 @@
|
||||
language="kotlin"
|
||||
implementation="org.jetbrains.kotlin.idea.references.KotlinDefaultAnnotationMethodImplicitReferenceContributor"/>
|
||||
|
||||
<psi.treeChangePreprocessor implementation="org.jetbrains.kotlin.idea.caches.KotlinPackageStatementPsiTreeChangePreprocessor"/>
|
||||
|
||||
<renamePsiElementProcessor id="KotlinClass"
|
||||
implementation="org.jetbrains.kotlin.idea.refactoring.rename.RenameKotlinClassProcessor"
|
||||
order="first"/>
|
||||
|
||||
Reference in New Issue
Block a user