[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
This commit is contained in:
Dmitrii Gridin
2023-01-30 14:13:46 +01:00
committed by Space Team
parent 3b9318bd3a
commit 347dc83773
12 changed files with 46 additions and 45 deletions
@@ -9,7 +9,7 @@ import com.intellij.psi.PsiAnnotation
import com.intellij.psi.PsiModifierList
internal sealed interface AnnotationsBox {
fun annotations(owner: PsiModifierList): Array<PsiAnnotation>
fun annotationsArray(owner: PsiModifierList): Array<PsiAnnotation>
fun findAnnotation(owner: PsiModifierList, qualifiedName: String): PsiAnnotation?
fun hasAnnotation(owner: PsiModifierList, qualifiedName: String): Boolean = findAnnotation(owner, qualifiedName) != null
}
@@ -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> = PsiAnnotation.EMPTY_ARRAY
override fun annotationsArray(owner: PsiModifierList): Array<PsiAnnotation> = PsiAnnotation.EMPTY_ARRAY
override fun findAnnotation(owner: PsiModifierList, qualifiedName: String): PsiAnnotation? = null
}
@@ -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<Array<PsiAnnotation>?> = AtomicReference()
private val cachedCollection: AtomicReference<Collection<PsiAnnotation>?> = AtomicReference()
override fun annotations(owner: PsiModifierList): Array<PsiAnnotation> {
annotationsArray.get()?.let { return it }
private fun getOrComputeCachedAnnotations(owner: PsiModifierList): Collection<PsiAnnotation> {
cachedCollection.get()?.let { return it }
val annotations = annotationsProvider.annotationInfos().mapNotNullTo(SmartList<PsiAnnotation>()) { 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<PsiAnnotation>() else PsiAnnotation.EMPTY_ARRAY
)
cachedCollection.compareAndSet(null, resultAnnotations)
return getOrComputeCachedAnnotations(owner)
}
private fun setAnnotationsArray(array: Array<PsiAnnotation>): Array<PsiAnnotation> =
if (annotationsArray.compareAndSet(null, array)) {
array
} else {
annotationsArray.get() ?: error("Unexpected state")
}
override fun annotationsArray(owner: PsiModifierList): Array<PsiAnnotation> {
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 }
}
}
@@ -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<PsiAnnotation>) : AnnotationsBox {
private val annotations: AtomicReference<Array<PsiAnnotation>?> = AtomicReference()
private val cachedCollection: AtomicReference<Collection<PsiAnnotation>?> = AtomicReference()
private fun getOrComputeAnnotations(owner: PsiModifierList): Array<PsiAnnotation> {
annotations.get()?.let { return it }
private fun getOrComputeAnnotations(owner: PsiModifierList): Collection<PsiAnnotation> {
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<PsiAnnotation> = getOrComputeAnnotations(owner)
override fun annotationsArray(
owner: PsiModifierList,
): Array<PsiAnnotation> = getOrComputeAnnotations(owner).toArrayIfNotEmptyOrDefault(PsiAnnotation.EMPTY_ARRAY)
override fun findAnnotation(
owner: PsiModifierList,
@@ -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<List<KtNamedAnnotationValue>>,
) : SymbolLightAbstractAnnotationParameterList(parent) {
private val _attributes: Array<PsiNameValuePair> by lazyPub {
private val _attributes: Collection<PsiNameValuePair> by lazyPub {
val attributes = lazyArguments.value.map {
SymbolNameValuePairForAnnotationArgument(it, this)
}
if (attributes.isEmpty()) PsiNameValuePair.EMPTY_ARRAY else attributes.toTypedArray()
attributes
}
override fun getAttributes(): Array<PsiNameValuePair> = _attributes
override fun getAttributes(): Array<PsiNameValuePair> = _attributes.toArrayIfNotEmptyOrDefault(PsiNameValuePair.EMPTY_ARRAY)
}
@@ -63,7 +63,7 @@ internal class SymbolLightClassForEnumEntry(
override fun getContainingClass(): PsiClass = enumClass
override fun getTypeParameters(): Array<PsiTypeParameter> = emptyArray()
override fun getTypeParameters(): Array<PsiTypeParameter> = PsiTypeParameter.EMPTY_ARRAY
override fun getTypeParameterList(): PsiTypeParameterList? = null
override fun getQualifiedName(): String = "${enumConstant.containingClass.qualifiedName}.${enumConstant.name}"
@@ -236,8 +236,7 @@ class SymbolLightClassForFacade(
return JavaPsiFacade.getInstance(project).findClass(CommonClassNames.JAVA_LANG_OBJECT, resolveScope)
}
override fun getSupers(): Array<PsiClass> =
superClass?.let { arrayOf(it) } ?: arrayOf()
override fun getSupers(): Array<PsiClass> = superClass?.let { arrayOf(it) } ?: PsiClass.EMPTY_ARRAY
override fun getSuperTypes(): Array<PsiClassType> =
arrayOf(PsiType.getJavaLangObject(manager, resolveScope))
@@ -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<PsiTypeParameter> = emptyArray()
override fun getTypeParameters(): Array<PsiTypeParameter> = PsiTypeParameter.EMPTY_ARRAY
override fun computeModifierList(): PsiModifierList = SymbolLightClassModifierList(
containingDeclaration = this,
@@ -12,12 +12,11 @@ internal abstract class SymbolLightPsiJavaCodeReferenceElementBase(private val k
PsiElement by ktElement,
PsiJavaCodeReferenceElement {
override fun multiResolve(incompleteCode: Boolean): Array<JavaResolveResult> = emptyArray()
override fun multiResolve(incompleteCode: Boolean): Array<JavaResolveResult> = 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<PsiType> = emptyArray()
override fun getTypeParameters(): Array<PsiType> = PsiType.EMPTY_ARRAY
override fun isQualified(): Boolean = false
@@ -46,7 +46,7 @@ internal sealed class SymbolLightModifierList<out T : KtLightElement<KtModifierL
override fun hasExplicitModifier(name: String) = hasModifierProperty(name)
override fun hasModifierProperty(name: String): Boolean = modifiersBox.hasModifier(name)
override fun getAnnotations(): Array<PsiAnnotation> = annotationsBox.annotations(this)
override fun getAnnotations(): Array<PsiAnnotation> = annotationsBox.annotationsArray(this)
override fun getApplicableAnnotations(): Array<PsiAnnotation> = annotations
override fun findAnnotation(qualifiedName: String): PsiAnnotation? = annotationsBox.findAnnotation(this, qualifiedName)
override fun hasAnnotation(qualifiedName: String): Boolean = annotationsBox.hasAnnotation(this, qualifiedName)
@@ -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<PsiTypeParameter> by lazyPub {
private val _typeParameters: Collection<PsiTypeParameter> 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<PsiTypeParameter> = _typeParameters
override fun getTypeParameters(): Array<PsiTypeParameter> = _typeParameters.toArrayIfNotEmptyOrDefault(PsiTypeParameter.EMPTY_ARRAY)
override fun getTypeParameterIndex(typeParameter: PsiTypeParameter?): Int = _typeParameters.indexOf(typeParameter)
@@ -271,3 +271,7 @@ internal inline fun <T : KtSymbol, R> KtSymbolPointer<T>.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 <reified T> Collection<T>.toArrayIfNotEmptyOrDefault(default: Array<T>): Array<T> {
return if (isNotEmpty()) toTypedArray() else default
}