Optimize PluginDeclarationProviderFactory.getStubBasedPackageMemberDeclarationProvider

Add cache for fast package exists check in specific module
 #KT-16850 fixed
This commit is contained in:
Simon Ogorodnik
2017-03-30 20:40:54 +03:00
parent 9ca65fcf99
commit 2380e0bacc
12 changed files with 222 additions and 11 deletions
@@ -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()
@@ -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) {
@@ -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)
@@ -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
@@ -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)
}
@@ -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)
+7
View File
@@ -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"/>