Add a cache for PsiBasedClassResolver instances

The PsiBasedClassResolver constructor isn't very cheap to call
because it runs computation of conflicting names each time

Again, this is helpful for Spring-interop because PsiBasedClassResolver
is used very frequently there

 #KT-23380 Fixed
This commit is contained in:
Denis Zharkov
2018-03-22 13:07:59 +03:00
parent f060604d98
commit e162749366
3 changed files with 28 additions and 4 deletions
@@ -52,7 +52,7 @@ class IdeLightClassInheritanceHelper : LightClassInheritanceHelper {
}
private fun isAmongEntries(baseClass: PsiClass, entries: List<KtSuperTypeListEntry>): ImpreciseResolveResult {
val psiBasedResolver = PsiBasedClassResolver(baseClass)
val psiBasedResolver = PsiBasedClassResolver.getInstance(baseClass)
entries@ for (entry in entries) {
val reference: KtSimpleNameExpression = entry.typeAsUserType?.referenceExpression ?: continue@entries
when (psiBasedResolver.canBeTargetReference(reference)) {
@@ -16,9 +16,14 @@
package org.jetbrains.kotlin.idea.search
import com.intellij.openapi.util.Key
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiModifier
import com.intellij.psi.search.PsiShortNamesCache
import com.intellij.psi.util.CachedValue
import com.intellij.psi.util.CachedValueProvider
import com.intellij.psi.util.CachedValuesManager
import com.intellij.psi.util.PsiModificationTracker
import org.jetbrains.annotations.TestOnly
import org.jetbrains.kotlin.asJava.ImpreciseResolveResult
import org.jetbrains.kotlin.asJava.ImpreciseResolveResult.*
@@ -61,9 +66,28 @@ class PsiBasedClassResolver @TestOnly constructor(private val targetClassFqName:
@TestOnly val attempts = AtomicInteger()
@TestOnly val trueHits = AtomicInteger()
@TestOnly val falseHits = AtomicInteger()
private val PSI_BASED_CLASS_RESOLVER_KEY = Key<CachedValue<PsiBasedClassResolver>>("PsiBasedClassResolver")
fun getInstance(target: PsiClass): PsiBasedClassResolver {
target.getUserData(PSI_BASED_CLASS_RESOLVER_KEY)?.let { return it.value }
val cachedValue = CachedValuesManager.getManager(target.project).createCachedValue(
{
CachedValueProvider.Result(
PsiBasedClassResolver(target),
PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT
)
}, false
)
target.putUserData(PSI_BASED_CLASS_RESOLVER_KEY, cachedValue)
return cachedValue.value
}
}
constructor(target: PsiClass): this(target.qualifiedName ?: "") {
private constructor(target: PsiClass): this(target.qualifiedName ?: "") {
if (target.qualifiedName == null || target.containingClass != null || targetPackage.isEmpty()) {
forceAmbiguity = true
return
@@ -239,4 +263,4 @@ private fun KtFile.getDefaultImports(): List<ImportPath> {
return TargetPlatformDetector.getPlatform(this).getDefaultImports(
versionSettings.supportsFeature(LanguageFeature.DefaultImportOfPackageKotlinComparisons)
)
}
}
@@ -78,7 +78,7 @@ class KotlinAnnotatedElementsSearcher : QueryExecutor<PsiModifierListOwner, Anno
consumer: (KtDeclaration) -> Boolean): Boolean {
assert(annClass.isAnnotationType) { "Annotation type should be passed to annotated members search" }
val psiBasedClassResolver = PsiBasedClassResolver(annClass)
val psiBasedClassResolver = PsiBasedClassResolver.getInstance(annClass)
val annotationFQN = annClass.qualifiedName!!
val candidates = getKotlinAnnotationCandidates(annClass, useScope)