Skip to content

Commit

Permalink
Optimize PluginDeclarationProviderFactory.getStubBasedPackageMemberDe…
Browse files Browse the repository at this point in the history
…clarationProvider

Add cache for fast package exists check in specific module
 #KT-16850 fixed
  • Loading branch information
semoro committed Apr 18, 2017
1 parent 9ca65fc commit 2380e0b
Show file tree
Hide file tree
Showing 12 changed files with 222 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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 ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.*
Expand All @@ -31,18 +32,20 @@ abstract class DeclarationProviderFactoryService {
project: Project,
storageManager: StorageManager,
syntheticFiles: Collection<KtFile>,
filesScope: GlobalSearchScope
filesScope: GlobalSearchScope,
moduleInfo: ModuleInfo
): DeclarationProviderFactory

companion object {
@JvmStatic fun createDeclarationProviderFactory(
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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
@@ -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)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -48,6 +49,7 @@ object PackageIndexUtil {
searchScope: GlobalSearchScope,
project: Project
): Boolean {

val subpackagesIndex = SubpackagesIndexService.getInstance(project)
if (!subpackagesIndex.packageExists(packageFqName)) {
return false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)

Expand All @@ -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)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
}
Original file line number Diff line number Diff line change
@@ -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 changes: 7 additions & 0 deletions idea/src/META-INF/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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"/>
Expand Down Expand Up @@ -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"/>
Expand Down

0 comments on commit 2380e0b

Please sign in to comment.