From 347dc837730e1720d5fd464a47eaaf25f9eaeb89 Mon Sep 17 00:00:00 2001 From: Dmitrii Gridin Date: Mon, 30 Jan 2023 14:13:46 +0100 Subject: [PATCH] [SLC] avoid redundant empty array allocation and add new array creation we should instantiate a new array on each request to avoid unexpected mutation from a usage side ^KT-56046 --- .../symbol/annotations/AnnotationsBox.kt | 2 +- .../symbol/annotations/EmptyAnnotationsBox.kt | 2 +- .../symbol/annotations/LazyAnnotationsBox.kt | 34 +++++++++---------- .../annotations/SimpleAnnotationsBox.kt | 19 +++++------ .../SymbolLightLazyAnnotationParameterList.kt | 7 ++-- .../classes/SymbolLightClassForEnumEntry.kt | 2 +- .../classes/SymbolLightClassForFacade.kt | 3 +- ...ymbolLightClassForInterfaceDefaultImpls.kt | 2 +- ...bolLightPsiJavaCodeReferenceElementBase.kt | 7 ++-- .../modifierLists/SymbolLightModifierList.kt | 2 +- .../SymbolLightTypeParameterList.kt | 7 ++-- .../light/classes/symbol/symbolLightUtils.kt | 4 +++ 12 files changed, 46 insertions(+), 45 deletions(-) diff --git a/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/annotations/AnnotationsBox.kt b/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/annotations/AnnotationsBox.kt index c9a4f1a5411..87491234973 100644 --- a/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/annotations/AnnotationsBox.kt +++ b/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/annotations/AnnotationsBox.kt @@ -9,7 +9,7 @@ import com.intellij.psi.PsiAnnotation import com.intellij.psi.PsiModifierList internal sealed interface AnnotationsBox { - fun annotations(owner: PsiModifierList): Array + fun annotationsArray(owner: PsiModifierList): Array fun findAnnotation(owner: PsiModifierList, qualifiedName: String): PsiAnnotation? fun hasAnnotation(owner: PsiModifierList, qualifiedName: String): Boolean = findAnnotation(owner, qualifiedName) != null } diff --git a/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/annotations/EmptyAnnotationsBox.kt b/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/annotations/EmptyAnnotationsBox.kt index 79a03409879..b8709f6707a 100644 --- a/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/annotations/EmptyAnnotationsBox.kt +++ b/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/annotations/EmptyAnnotationsBox.kt @@ -9,6 +9,6 @@ import com.intellij.psi.PsiAnnotation import com.intellij.psi.PsiModifierList internal object EmptyAnnotationsBox : AnnotationsBox { - override fun annotations(owner: PsiModifierList): Array = PsiAnnotation.EMPTY_ARRAY + override fun annotationsArray(owner: PsiModifierList): Array = PsiAnnotation.EMPTY_ARRAY override fun findAnnotation(owner: PsiModifierList, qualifiedName: String): PsiAnnotation? = null } diff --git a/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/annotations/LazyAnnotationsBox.kt b/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/annotations/LazyAnnotationsBox.kt index fd88a27c5de..5626612d4b5 100644 --- a/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/annotations/LazyAnnotationsBox.kt +++ b/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/annotations/LazyAnnotationsBox.kt @@ -7,6 +7,7 @@ package org.jetbrains.kotlin.light.classes.symbol.annotations import com.intellij.psi.PsiAnnotation import com.intellij.psi.PsiModifierList +import org.jetbrains.kotlin.light.classes.symbol.toArrayIfNotEmptyOrDefault import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.StandardClassIds import org.jetbrains.kotlin.utils.SmartList @@ -17,10 +18,10 @@ internal class LazyAnnotationsBox( private val additionalAnnotationsProvider: AdditionalAnnotationsProvider = EmptyAdditionalAnnotationsProvider, private val annotationFilter: AnnotationFilter = AlwaysAllowedAnnotationFilter, ) : AnnotationsBox { - private val annotationsArray: AtomicReference?> = AtomicReference() + private val cachedCollection: AtomicReference?> = AtomicReference() - override fun annotations(owner: PsiModifierList): Array { - annotationsArray.get()?.let { return it } + private fun getOrComputeCachedAnnotations(owner: PsiModifierList): Collection { + cachedCollection.get()?.let { return it } val annotations = annotationsProvider.annotationInfos().mapNotNullTo(SmartList()) { applicationInfo -> applicationInfo.classId?.let { _ -> @@ -32,17 +33,14 @@ internal class LazyAnnotationsBox( additionalAnnotationsProvider.addAllAnnotations(annotations, foundQualifiers, owner) val resultAnnotations = annotationFilter.filtered(annotations) - return setAnnotationsArray( - if (resultAnnotations.isNotEmpty()) resultAnnotations.toTypedArray() else PsiAnnotation.EMPTY_ARRAY - ) + cachedCollection.compareAndSet(null, resultAnnotations) + + return getOrComputeCachedAnnotations(owner) } - private fun setAnnotationsArray(array: Array): Array = - if (annotationsArray.compareAndSet(null, array)) { - array - } else { - annotationsArray.get() ?: error("Unexpected state") - } + override fun annotationsArray(owner: PsiModifierList): Array { + return getOrComputeCachedAnnotations(owner).toArrayIfNotEmptyOrDefault(PsiAnnotation.EMPTY_ARRAY) + } override fun findAnnotation( owner: PsiModifierList, @@ -52,8 +50,8 @@ internal class LazyAnnotationsBox( fun findAnnotation(owner: PsiModifierList, qualifiedName: String, withAdditionalAnnotations: Boolean): PsiAnnotation? { if (!annotationFilter.isAllowed(qualifiedName)) return null - annotationsArray.get()?.let { array -> - return array.find { it.qualifiedName == qualifiedName } + cachedCollection.get()?.let { annotations -> + return annotations.find { it.qualifiedName == qualifiedName } } val specialAnnotationClassId = specialAnnotationsList[qualifiedName] @@ -66,21 +64,21 @@ internal class LazyAnnotationsBox( null } - return specialAnnotation ?: annotations(owner).find { it.qualifiedName == qualifiedName } + return specialAnnotation ?: getOrComputeCachedAnnotations(owner).find { it.qualifiedName == qualifiedName } } override fun hasAnnotation(owner: PsiModifierList, qualifiedName: String): Boolean { if (!annotationFilter.isAllowed(qualifiedName)) return false - annotationsArray.get()?.let { array -> - return array.any { it.qualifiedName == qualifiedName } + cachedCollection.get()?.let { annotations -> + return annotations.any { it.qualifiedName == qualifiedName } } val specialAnnotationClassId = specialAnnotationsList[qualifiedName] return if (specialAnnotationClassId != null) { specialAnnotationClassId in annotationsProvider } else { - annotations(owner).any { it.qualifiedName == qualifiedName } + getOrComputeCachedAnnotations(owner).any { it.qualifiedName == qualifiedName } } } diff --git a/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/annotations/SimpleAnnotationsBox.kt b/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/annotations/SimpleAnnotationsBox.kt index 300e95da8b4..33118b58334 100644 --- a/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/annotations/SimpleAnnotationsBox.kt +++ b/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/annotations/SimpleAnnotationsBox.kt @@ -7,24 +7,23 @@ package org.jetbrains.kotlin.light.classes.symbol.annotations import com.intellij.psi.PsiAnnotation import com.intellij.psi.PsiModifierList +import org.jetbrains.kotlin.light.classes.symbol.toArrayIfNotEmptyOrDefault import java.util.concurrent.atomic.AtomicReference internal class SimpleAnnotationsBox(private val annotationsComputer: (PsiModifierList) -> Collection) : AnnotationsBox { - private val annotations: AtomicReference?> = AtomicReference() + private val cachedCollection: AtomicReference?> = AtomicReference() - private fun getOrComputeAnnotations(owner: PsiModifierList): Array { - annotations.get()?.let { return it } + private fun getOrComputeAnnotations(owner: PsiModifierList): Collection { + cachedCollection.get()?.let { return it } val nonCachedAnnotations = annotationsComputer(owner) - val resultArray = if (nonCachedAnnotations.isEmpty()) PsiAnnotation.EMPTY_ARRAY else nonCachedAnnotations.toTypedArray() - return if (annotations.compareAndSet(null, resultArray)) { - resultArray - } else { - getOrComputeAnnotations(owner) - } + cachedCollection.compareAndSet(null, nonCachedAnnotations) + return getOrComputeAnnotations(owner) } - override fun annotations(owner: PsiModifierList): Array = getOrComputeAnnotations(owner) + override fun annotationsArray( + owner: PsiModifierList, + ): Array = getOrComputeAnnotations(owner).toArrayIfNotEmptyOrDefault(PsiAnnotation.EMPTY_ARRAY) override fun findAnnotation( owner: PsiModifierList, diff --git a/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/annotations/SymbolLightLazyAnnotationParameterList.kt b/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/annotations/SymbolLightLazyAnnotationParameterList.kt index 88d1197613e..ab5e1a612f7 100644 --- a/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/annotations/SymbolLightLazyAnnotationParameterList.kt +++ b/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/annotations/SymbolLightLazyAnnotationParameterList.kt @@ -8,18 +8,19 @@ package org.jetbrains.kotlin.light.classes.symbol.annotations import com.intellij.psi.PsiNameValuePair import org.jetbrains.kotlin.analysis.api.annotations.KtNamedAnnotationValue import org.jetbrains.kotlin.asJava.classes.lazyPub +import org.jetbrains.kotlin.light.classes.symbol.toArrayIfNotEmptyOrDefault internal class SymbolLightLazyAnnotationParameterList( parent: SymbolLightAbstractAnnotation, private val lazyArguments: Lazy>, ) : SymbolLightAbstractAnnotationParameterList(parent) { - private val _attributes: Array by lazyPub { + private val _attributes: Collection by lazyPub { val attributes = lazyArguments.value.map { SymbolNameValuePairForAnnotationArgument(it, this) } - if (attributes.isEmpty()) PsiNameValuePair.EMPTY_ARRAY else attributes.toTypedArray() + attributes } - override fun getAttributes(): Array = _attributes + override fun getAttributes(): Array = _attributes.toArrayIfNotEmptyOrDefault(PsiNameValuePair.EMPTY_ARRAY) } diff --git a/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/classes/SymbolLightClassForEnumEntry.kt b/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/classes/SymbolLightClassForEnumEntry.kt index 1132c448a6f..e0ac0aa2567 100644 --- a/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/classes/SymbolLightClassForEnumEntry.kt +++ b/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/classes/SymbolLightClassForEnumEntry.kt @@ -63,7 +63,7 @@ internal class SymbolLightClassForEnumEntry( override fun getContainingClass(): PsiClass = enumClass - override fun getTypeParameters(): Array = emptyArray() + override fun getTypeParameters(): Array = PsiTypeParameter.EMPTY_ARRAY override fun getTypeParameterList(): PsiTypeParameterList? = null override fun getQualifiedName(): String = "${enumConstant.containingClass.qualifiedName}.${enumConstant.name}" diff --git a/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/classes/SymbolLightClassForFacade.kt b/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/classes/SymbolLightClassForFacade.kt index 1c2cb92be27..6804950c0f2 100644 --- a/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/classes/SymbolLightClassForFacade.kt +++ b/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/classes/SymbolLightClassForFacade.kt @@ -236,8 +236,7 @@ class SymbolLightClassForFacade( return JavaPsiFacade.getInstance(project).findClass(CommonClassNames.JAVA_LANG_OBJECT, resolveScope) } - override fun getSupers(): Array = - superClass?.let { arrayOf(it) } ?: arrayOf() + override fun getSupers(): Array = superClass?.let { arrayOf(it) } ?: PsiClass.EMPTY_ARRAY override fun getSuperTypes(): Array = arrayOf(PsiType.getJavaLangObject(manager, resolveScope)) diff --git a/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/classes/SymbolLightClassForInterfaceDefaultImpls.kt b/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/classes/SymbolLightClassForInterfaceDefaultImpls.kt index 97b21458825..6d47fa35a28 100644 --- a/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/classes/SymbolLightClassForInterfaceDefaultImpls.kt +++ b/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/classes/SymbolLightClassForInterfaceDefaultImpls.kt @@ -36,7 +36,7 @@ internal class SymbolLightClassForInterfaceDefaultImpls(private val containingCl override fun hashCode(): Int = containingClass.hashCode() override fun getTypeParameterList(): PsiTypeParameterList? = null - override fun getTypeParameters(): Array = emptyArray() + override fun getTypeParameters(): Array = PsiTypeParameter.EMPTY_ARRAY override fun computeModifierList(): PsiModifierList = SymbolLightClassModifierList( containingDeclaration = this, diff --git a/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/codeReferences/SymbolLightPsiJavaCodeReferenceElementBase.kt b/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/codeReferences/SymbolLightPsiJavaCodeReferenceElementBase.kt index ba165e6c20e..1299ea8beef 100644 --- a/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/codeReferences/SymbolLightPsiJavaCodeReferenceElementBase.kt +++ b/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/codeReferences/SymbolLightPsiJavaCodeReferenceElementBase.kt @@ -12,12 +12,11 @@ internal abstract class SymbolLightPsiJavaCodeReferenceElementBase(private val k PsiElement by ktElement, PsiJavaCodeReferenceElement { - override fun multiResolve(incompleteCode: Boolean): Array = emptyArray() + override fun multiResolve(incompleteCode: Boolean): Array = JavaResolveResult.EMPTY_ARRAY override fun processVariants(processor: PsiScopeProcessor) {} - override fun advancedResolve(incompleteCode: Boolean): JavaResolveResult = - JavaResolveResult.EMPTY + override fun advancedResolve(incompleteCode: Boolean): JavaResolveResult = JavaResolveResult.EMPTY override fun getQualifier(): PsiElement? = null @@ -27,7 +26,7 @@ internal abstract class SymbolLightPsiJavaCodeReferenceElementBase(private val k override fun getParameterList(): PsiReferenceParameterList? = null - override fun getTypeParameters(): Array = emptyArray() + override fun getTypeParameters(): Array = PsiType.EMPTY_ARRAY override fun isQualified(): Boolean = false diff --git a/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/modifierLists/SymbolLightModifierList.kt b/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/modifierLists/SymbolLightModifierList.kt index 53092a7d30c..6738a1b4601 100644 --- a/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/modifierLists/SymbolLightModifierList.kt +++ b/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/modifierLists/SymbolLightModifierList.kt @@ -46,7 +46,7 @@ internal sealed class SymbolLightModifierList = annotationsBox.annotations(this) + override fun getAnnotations(): Array = annotationsBox.annotationsArray(this) override fun getApplicableAnnotations(): Array = annotations override fun findAnnotation(qualifiedName: String): PsiAnnotation? = annotationsBox.findAnnotation(this, qualifiedName) override fun hasAnnotation(qualifiedName: String): Boolean = annotationsBox.hasAnnotation(this, qualifiedName) diff --git a/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/parameters/SymbolLightTypeParameterList.kt b/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/parameters/SymbolLightTypeParameterList.kt index 48df34128f4..291897df1ca 100644 --- a/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/parameters/SymbolLightTypeParameterList.kt +++ b/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/parameters/SymbolLightTypeParameterList.kt @@ -15,6 +15,7 @@ import org.jetbrains.kotlin.asJava.classes.lazyPub import org.jetbrains.kotlin.idea.KotlinLanguage import org.jetbrains.kotlin.light.classes.symbol.basicIsEquivalentTo import org.jetbrains.kotlin.light.classes.symbol.compareSymbolPointers +import org.jetbrains.kotlin.light.classes.symbol.toArrayIfNotEmptyOrDefault import org.jetbrains.kotlin.light.classes.symbol.withSymbol import org.jetbrains.kotlin.psi.KtTypeParameterListOwner @@ -39,7 +40,7 @@ internal class SymbolLightTypeParameterList( place: PsiElement ): Boolean = typeParameters.all { processor.execute(it, state) } - private val _typeParameters: Array by lazyPub { + private val _typeParameters: Collection by lazyPub { symbolWithTypeParameterPointer.withSymbol(ktModule) { it.typeParameters.mapIndexed { index, parameter -> SymbolLightTypeParameter( @@ -48,11 +49,11 @@ internal class SymbolLightTypeParameterList( index = index, typeParameterSymbol = parameter, ) - }.toTypedArray() + } } } - override fun getTypeParameters(): Array = _typeParameters + override fun getTypeParameters(): Array = _typeParameters.toArrayIfNotEmptyOrDefault(PsiTypeParameter.EMPTY_ARRAY) override fun getTypeParameterIndex(typeParameter: PsiTypeParameter?): Int = _typeParameters.indexOf(typeParameter) diff --git a/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/symbolLightUtils.kt b/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/symbolLightUtils.kt index 29f54f6c65b..99a82b5aff0 100644 --- a/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/symbolLightUtils.kt +++ b/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/symbolLightUtils.kt @@ -271,3 +271,7 @@ internal inline fun KtSymbolPointer.withSymbol( internal val KtPropertySymbol.isConstOrJvmField: Boolean get() = isConst || hasJvmFieldAnnotation() internal val KtPropertySymbol.isConst: Boolean get() = (this as? KtKotlinPropertySymbol)?.isConst == true internal val KtPropertySymbol.isLateInit: Boolean get() = (this as? KtKotlinPropertySymbol)?.isLateInit == true + +internal inline fun Collection.toArrayIfNotEmptyOrDefault(default: Array): Array { + return if (isNotEmpty()) toTypedArray() else default +}