diff --git a/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/annotations/SymbolAnnotationParameterList.kt b/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/annotations/SymbolAnnotationParameterList.kt index b5a8964da44..455b5d48d3f 100644 --- a/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/annotations/SymbolAnnotationParameterList.kt +++ b/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/annotations/SymbolAnnotationParameterList.kt @@ -5,13 +5,11 @@ package org.jetbrains.kotlin.light.classes.symbol.annotations -import com.intellij.psi.* -import com.intellij.psi.impl.light.LightIdentifier +import com.intellij.psi.PsiAnnotationParameterList +import com.intellij.psi.PsiNameValuePair import org.jetbrains.kotlin.analysis.api.annotations.KtNamedAnnotationValue -import org.jetbrains.kotlin.asJava.classes.cannotModify import org.jetbrains.kotlin.asJava.classes.lazyPub import org.jetbrains.kotlin.asJava.elements.KtLightElementBase -import org.jetbrains.kotlin.light.classes.symbol.toAnnotationMemberValue import org.jetbrains.kotlin.psi.KtElement internal class SymbolAnnotationParameterList( @@ -31,29 +29,3 @@ internal class SymbolAnnotationParameterList( //TODO: EQ GHC EQIV } - -private class SymbolNameValuePairForAnnotationArgument( - private val constantValue: KtNamedAnnotationValue, - parent: PsiElement -) : KtLightElementBase(parent), PsiNameValuePair { - - override val kotlinOrigin: KtElement? get() = null - - private val _value by lazyPub { - constantValue.expression.toAnnotationMemberValue(this) - } - - override fun setValue(p0: PsiAnnotationMemberValue) = cannotModify() - - private val _nameIdentifier: PsiIdentifier by lazyPub { - LightIdentifier(parent.manager, constantValue.name.asString()) - } - - override fun getNameIdentifier(): PsiIdentifier = _nameIdentifier - - override fun getValue(): PsiAnnotationMemberValue? = _value - - override fun getLiteralValue(): String? = (value as? PsiLiteralExpression)?.value?.toString() - - override fun getName(): String = constantValue.name.asString() -} diff --git a/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/annotations/SymbolLazyAnnotationParameterList.kt b/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/annotations/SymbolLazyAnnotationParameterList.kt new file mode 100644 index 00000000000..0724c1a5971 --- /dev/null +++ b/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/annotations/SymbolLazyAnnotationParameterList.kt @@ -0,0 +1,35 @@ +/* + * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.light.classes.symbol.annotations + +import com.intellij.psi.PsiAnnotationParameterList +import com.intellij.psi.PsiNameValuePair +import org.jetbrains.kotlin.analysis.api.annotations.KtNamedAnnotationValue +import org.jetbrains.kotlin.asJava.classes.lazyPub +import org.jetbrains.kotlin.asJava.elements.KtLightElementBase +import org.jetbrains.kotlin.psi.KtElement + +internal class SymbolLazyAnnotationParameterList( + parent: SymbolLightLazyAnnotation, + private val lazyArguments: Lazy>, +) : KtLightElementBase(parent), + PsiAnnotationParameterList { + override val kotlinOrigin: KtElement? + get() = (parent as SymbolLightLazyAnnotation).kotlinOrigin?.valueArgumentList + + private val _attributes: Array by lazyPub { + val attributes = lazyArguments.value.map { + SymbolNameValuePairForAnnotationArgument(it, this) + } + + if (attributes.isEmpty()) PsiNameValuePair.EMPTY_ARRAY else attributes.toTypedArray() + } + + override fun getAttributes(): Array = _attributes + + override fun equals(other: Any?): Boolean = other === this || other is SymbolLazyAnnotationParameterList && other.parent == parent + override fun hashCode(): Int = parent.hashCode() +} diff --git a/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/annotations/SymbolLightLazyAnnotation.kt b/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/annotations/SymbolLightLazyAnnotation.kt new file mode 100644 index 00000000000..cef15e627cc --- /dev/null +++ b/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/annotations/SymbolLightLazyAnnotation.kt @@ -0,0 +1,98 @@ +/* + * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.light.classes.symbol.annotations + +import com.intellij.psi.PsiAnnotationMemberValue +import com.intellij.psi.PsiAnnotationParameterList +import com.intellij.psi.PsiModifierList +import com.intellij.psi.impl.PsiImplUtil +import org.jetbrains.kotlin.analysis.api.KtAnalysisSession +import org.jetbrains.kotlin.analysis.api.annotations.KtAnnotationApplication +import org.jetbrains.kotlin.analysis.api.annotations.annotations +import org.jetbrains.kotlin.analysis.api.symbols.markers.KtAnnotatedSymbol +import org.jetbrains.kotlin.analysis.api.symbols.pointers.KtSymbolPointer +import org.jetbrains.kotlin.analysis.project.structure.KtModule +import org.jetbrains.kotlin.asJava.classes.lazyPub +import org.jetbrains.kotlin.light.classes.symbol.withSymbol +import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.psi.KtCallElement + +internal class SymbolLightLazyAnnotation private constructor( + val classId: ClassId, + private val annotatedSymbolPointer: KtSymbolPointer, + private val ktModule: KtModule, + private val index: Int?, + specialAnnotationApplication: KtAnnotationApplication?, + owner: PsiModifierList, +) : SymbolLightAbstractAnnotation(owner) { + init { + require(index != null || specialAnnotationApplication != null) + } + + private val annotationApplication: Lazy = specialAnnotationApplication?.let(::lazyOf) ?: lazyPub { + withAnnotatedSymbol { ktAnnotatedSymbol -> + ktAnnotatedSymbol.annotations[index!!] + } + } + + constructor( + classId: ClassId, + annotatedSymbolPointer: KtSymbolPointer, + ktModule: KtModule, + index: Int, + owner: PsiModifierList, + ) : this( + classId = classId, + annotatedSymbolPointer = annotatedSymbolPointer, + ktModule = ktModule, + index = index, + specialAnnotationApplication = null, + owner = owner, + ) + + constructor( + classId: ClassId, + annotatedSymbolPointer: KtSymbolPointer, + ktModule: KtModule, + annotationApplication: KtAnnotationApplication, + owner: PsiModifierList, + ) : this( + classId = classId, + annotatedSymbolPointer = annotatedSymbolPointer, + ktModule = ktModule, + index = null, + specialAnnotationApplication = annotationApplication, + owner = owner, + ) + + private inline fun withAnnotatedSymbol(crossinline action: context(KtAnalysisSession) (KtAnnotatedSymbol) -> T): T = + annotatedSymbolPointer.withSymbol(ktModule, action) + + override val kotlinOrigin: KtCallElement? get() = annotationApplication.value.psi + + override fun getQualifiedName(): String = classId.asFqNameString() + + private val _parameterList: PsiAnnotationParameterList by lazyPub { + SymbolLazyAnnotationParameterList(this, lazyPub { annotationApplication.value.arguments }) + } + + override fun getParameterList(): PsiAnnotationParameterList = _parameterList + + override fun findAttributeValue(attributeName: String?): PsiAnnotationMemberValue? = + PsiImplUtil.findAttributeValue(this, attributeName) + + override fun findDeclaredAttributeValue(attributeName: String?) = + PsiImplUtil.findDeclaredAttributeValue(this, attributeName) + + override fun equals(other: Any?): Boolean = this === other || + other is SymbolLightLazyAnnotation && + other.classId == classId && + other.index == index && + other.ktModule == ktModule && + other.parent == parent + + override fun hashCode(): Int = classId.hashCode() +} diff --git a/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/annotations/SymbolNameValuePairForAnnotationArgument.kt b/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/annotations/SymbolNameValuePairForAnnotationArgument.kt new file mode 100644 index 00000000000..598cc3ba307 --- /dev/null +++ b/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/annotations/SymbolNameValuePairForAnnotationArgument.kt @@ -0,0 +1,41 @@ +/* + * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.light.classes.symbol.annotations + +import com.intellij.psi.* +import com.intellij.psi.impl.light.LightIdentifier +import org.jetbrains.kotlin.analysis.api.annotations.KtNamedAnnotationValue +import org.jetbrains.kotlin.asJava.classes.cannotModify +import org.jetbrains.kotlin.asJava.classes.lazyPub +import org.jetbrains.kotlin.asJava.elements.KtLightElementBase +import org.jetbrains.kotlin.light.classes.symbol.toAnnotationMemberValue +import org.jetbrains.kotlin.psi.KtElement + +internal class SymbolNameValuePairForAnnotationArgument( + private val constantValue: KtNamedAnnotationValue, + parent: PsiAnnotationParameterList, +) : KtLightElementBase(parent), PsiNameValuePair { + + override val kotlinOrigin: KtElement? get() = constantValue.expression.sourcePsi + + private val _value by lazyPub { + constantValue.expression.toAnnotationMemberValue(this) + } + + override fun setValue(newValue: PsiAnnotationMemberValue) = cannotModify() + + private val _nameIdentifier: PsiIdentifier by lazyPub { + LightIdentifier(manager, constantValue.name.asString()) + } + + override fun getNameIdentifier(): PsiIdentifier = _nameIdentifier + + override fun getValue(): PsiAnnotationMemberValue? = _value + + override fun getLiteralValue(): String? = (value as? PsiLiteralExpression)?.value?.toString() + + override fun getName(): String = constantValue.name.asString() +} diff --git a/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/modifierLists/LazyAnnotationsBox.kt b/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/modifierLists/LazyAnnotationsBox.kt new file mode 100644 index 00000000000..3f37030696e --- /dev/null +++ b/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/modifierLists/LazyAnnotationsBox.kt @@ -0,0 +1,145 @@ +/* + * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.light.classes.symbol.modifierLists + +import com.intellij.psi.PsiAnnotation +import com.intellij.psi.PsiAnnotationOwner +import com.intellij.psi.PsiModifierList +import org.jetbrains.kotlin.analysis.api.KtAnalysisSession +import org.jetbrains.kotlin.analysis.api.annotations.annotationClassIds +import org.jetbrains.kotlin.analysis.api.annotations.annotationsByClassId +import org.jetbrains.kotlin.analysis.api.annotations.hasAnnotation +import org.jetbrains.kotlin.analysis.api.symbols.markers.KtAnnotatedSymbol +import org.jetbrains.kotlin.analysis.api.symbols.pointers.KtSymbolPointer +import org.jetbrains.kotlin.analysis.project.structure.KtModule +import org.jetbrains.kotlin.light.classes.symbol.annotations.SymbolLightLazyAnnotation +import org.jetbrains.kotlin.light.classes.symbol.withSymbol +import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.name.StandardClassIds +import org.jetbrains.kotlin.utils.SmartList +import java.util.concurrent.atomic.AtomicReference + +internal class LazyAnnotationsBox( + private val annotatedSymbolPointer: KtSymbolPointer, + private val ktModule: KtModule, + private val owner: PsiModifierList, +) : PsiAnnotationOwner { + private inline fun withAnnotatedSymbol(crossinline action: context(KtAnalysisSession) (KtAnnotatedSymbol) -> T): T = + annotatedSymbolPointer.withSymbol(ktModule, action) + + private val annotationsArray: AtomicReference?> = AtomicReference() + private var specialAnnotations: SmartList? = null + private val monitor = Any() + + override fun getAnnotations(): Array { + annotationsArray.get()?.let { return it } + val classIds = withAnnotatedSymbol { ktAnnotatedSymbol -> + ktAnnotatedSymbol.annotationClassIds + } + + val annotations = classIds.withIndex().map { (index, classId) -> + SymbolLightLazyAnnotation(classId, annotatedSymbolPointer, ktModule, index, owner) + } + + val array = if (annotations.isNotEmpty()) annotations.toTypedArray() else emptyArray + + val valueToReturn = if (array.isEmpty()) { + setAnnotationsArray(array) + } else { + synchronized(monitor) { + specialAnnotations?.forEach { lazyAnnotation -> + val index = array.indexOfFirst { it.classId == lazyAnnotation.classId } + array[index] = lazyAnnotation + } + + setAnnotationsArray(array) + } + } + + return valueToReturn + } + + private fun setAnnotationsArray(array: Array): Array = + if (annotationsArray.compareAndSet(null, array)) { + array + } else { + annotationsArray.get() ?: error("Unexpected state") + } + + override fun getApplicableAnnotations(): Array = annotations + + override fun findAnnotation(qualifiedName: String): PsiAnnotation? { + annotationsArray.get()?.let { array -> + return array.find { it.qualifiedName == qualifiedName } + } + + val specialAnnotationClassId = specialAnnotationsList[qualifiedName] + return if (specialAnnotationClassId != null) { + val annotationApplication = withAnnotatedSymbol { ktAnnotatedSymbol -> + ktAnnotatedSymbol.annotationsByClassId(specialAnnotationClassId).firstOrNull() + } ?: return null + + val lazyAnnotation = SymbolLightLazyAnnotation( + specialAnnotationClassId, + annotatedSymbolPointer, + ktModule, + annotationApplication, + owner, + ) + + synchronized(monitor) { + if (specialAnnotations != null) { + val specialAnnotations = specialAnnotations!! + val oldAnnotation = specialAnnotations.find { it.classId == lazyAnnotation.classId } + if (oldAnnotation != null) { + oldAnnotation + } else { + specialAnnotations += lazyAnnotation + lazyAnnotation + } + } else { + specialAnnotations = SmartList(lazyAnnotation) + lazyAnnotation + } + } + } else { + annotations.find { it.qualifiedName == qualifiedName } + } + } + + override fun hasAnnotation(qualifiedName: String): Boolean { + annotationsArray.get()?.let { array -> + return array.any { it.qualifiedName == qualifiedName } + } + + val specialAnnotationClassId = specialAnnotationsList[qualifiedName] + return if (specialAnnotationClassId != null) { + withAnnotatedSymbol { ktAnnotatedSymbol -> ktAnnotatedSymbol.hasAnnotation(specialAnnotationClassId) } + } else { + annotations.any { it.qualifiedName == qualifiedName } + } + } + + override fun addAnnotation(qualifiedName: String): PsiAnnotation = throw UnsupportedOperationException() + + companion object { + /** + * @see org.jetbrains.kotlin.fir.resolve.transformers.plugin.CompilerRequiredAnnotationsHelper + */ + private val specialAnnotationsList: Map = listOf( + StandardClassIds.Annotations.Deprecated, +// Annotations.Retention, +// Annotations.Target, + StandardClassIds.Annotations.DeprecatedSinceKotlin, + StandardClassIds.Annotations.WasExperimental, + StandardClassIds.Annotations.JvmRecord, +// Annotations.Repeatable, +// Annotations.Java.Repeatable, + ).associateBy { it.asFqNameString() } + + private val emptyArray: Array = emptyArray() + } +}