[SLC] implement lazy annotations
^KT-56046
This commit is contained in:
committed by
Space Team
parent
f2a33bf4b0
commit
73707941bb
+2
-30
@@ -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()
|
||||
}
|
||||
|
||||
+35
@@ -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<List<KtNamedAnnotationValue>>,
|
||||
) : KtLightElementBase(parent),
|
||||
PsiAnnotationParameterList {
|
||||
override val kotlinOrigin: KtElement?
|
||||
get() = (parent as SymbolLightLazyAnnotation).kotlinOrigin?.valueArgumentList
|
||||
|
||||
private val _attributes: Array<PsiNameValuePair> by lazyPub {
|
||||
val attributes = lazyArguments.value.map {
|
||||
SymbolNameValuePairForAnnotationArgument(it, this)
|
||||
}
|
||||
|
||||
if (attributes.isEmpty()) PsiNameValuePair.EMPTY_ARRAY else attributes.toTypedArray()
|
||||
}
|
||||
|
||||
override fun getAttributes(): Array<PsiNameValuePair> = _attributes
|
||||
|
||||
override fun equals(other: Any?): Boolean = other === this || other is SymbolLazyAnnotationParameterList && other.parent == parent
|
||||
override fun hashCode(): Int = parent.hashCode()
|
||||
}
|
||||
+98
@@ -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<KtAnnotatedSymbol>,
|
||||
private val ktModule: KtModule,
|
||||
private val index: Int?,
|
||||
specialAnnotationApplication: KtAnnotationApplication?,
|
||||
owner: PsiModifierList,
|
||||
) : SymbolLightAbstractAnnotation(owner) {
|
||||
init {
|
||||
require(index != null || specialAnnotationApplication != null)
|
||||
}
|
||||
|
||||
private val annotationApplication: Lazy<KtAnnotationApplication> = specialAnnotationApplication?.let(::lazyOf) ?: lazyPub {
|
||||
withAnnotatedSymbol { ktAnnotatedSymbol ->
|
||||
ktAnnotatedSymbol.annotations[index!!]
|
||||
}
|
||||
}
|
||||
|
||||
constructor(
|
||||
classId: ClassId,
|
||||
annotatedSymbolPointer: KtSymbolPointer<KtAnnotatedSymbol>,
|
||||
ktModule: KtModule,
|
||||
index: Int,
|
||||
owner: PsiModifierList,
|
||||
) : this(
|
||||
classId = classId,
|
||||
annotatedSymbolPointer = annotatedSymbolPointer,
|
||||
ktModule = ktModule,
|
||||
index = index,
|
||||
specialAnnotationApplication = null,
|
||||
owner = owner,
|
||||
)
|
||||
|
||||
constructor(
|
||||
classId: ClassId,
|
||||
annotatedSymbolPointer: KtSymbolPointer<KtAnnotatedSymbol>,
|
||||
ktModule: KtModule,
|
||||
annotationApplication: KtAnnotationApplication,
|
||||
owner: PsiModifierList,
|
||||
) : this(
|
||||
classId = classId,
|
||||
annotatedSymbolPointer = annotatedSymbolPointer,
|
||||
ktModule = ktModule,
|
||||
index = null,
|
||||
specialAnnotationApplication = annotationApplication,
|
||||
owner = owner,
|
||||
)
|
||||
|
||||
private inline fun <T> 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()
|
||||
}
|
||||
+41
@@ -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()
|
||||
}
|
||||
+145
@@ -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<KtAnnotatedSymbol>,
|
||||
private val ktModule: KtModule,
|
||||
private val owner: PsiModifierList,
|
||||
) : PsiAnnotationOwner {
|
||||
private inline fun <T> withAnnotatedSymbol(crossinline action: context(KtAnalysisSession) (KtAnnotatedSymbol) -> T): T =
|
||||
annotatedSymbolPointer.withSymbol(ktModule, action)
|
||||
|
||||
private val annotationsArray: AtomicReference<Array<SymbolLightLazyAnnotation>?> = AtomicReference()
|
||||
private var specialAnnotations: SmartList<SymbolLightLazyAnnotation>? = null
|
||||
private val monitor = Any()
|
||||
|
||||
override fun getAnnotations(): Array<SymbolLightLazyAnnotation> {
|
||||
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<SymbolLightLazyAnnotation>): Array<SymbolLightLazyAnnotation> =
|
||||
if (annotationsArray.compareAndSet(null, array)) {
|
||||
array
|
||||
} else {
|
||||
annotationsArray.get() ?: error("Unexpected state")
|
||||
}
|
||||
|
||||
override fun getApplicableAnnotations(): Array<SymbolLightLazyAnnotation> = 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<String, ClassId> = 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<SymbolLightLazyAnnotation> = emptyArray<SymbolLightLazyAnnotation>()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user