FIR2IR: use @Target to put annotations on either property or backing field

This commit is contained in:
Jinseong Jeon
2020-09-24 15:30:37 -07:00
committed by Mikhail Glukhikh
parent b59832c5fd
commit f3db113a82
3 changed files with 61 additions and 20 deletions
@@ -10,7 +10,7 @@ import org.jetbrains.kotlin.fir.FirAnnotationContainer
import org.jetbrains.kotlin.fir.backend.Fir2IrComponents import org.jetbrains.kotlin.fir.backend.Fir2IrComponents
import org.jetbrains.kotlin.fir.declarations.FirProperty import org.jetbrains.kotlin.fir.declarations.FirProperty
import org.jetbrains.kotlin.fir.declarations.FirValueParameter import org.jetbrains.kotlin.fir.declarations.FirValueParameter
import org.jetbrains.kotlin.fir.declarations.isJvmFieldAnnotation import org.jetbrains.kotlin.fir.declarations.hasMetaAnnotationUseSiteTargets
import org.jetbrains.kotlin.fir.expressions.FirAnnotationCall import org.jetbrains.kotlin.fir.expressions.FirAnnotationCall
import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.IrConstructorCall import org.jetbrains.kotlin.ir.expressions.IrConstructorCall
@@ -40,17 +40,24 @@ internal class AnnotationGenerator(private val components: Fir2IrComponents) : F
irValueParameter.annotations += irValueParameter.annotations +=
firValueParameter.annotations firValueParameter.annotations
.filter { .filter {
// TODO: for `null` use-site, refer to targets on the meta annotation
it.useSiteTarget == null || !isInConstructor || it.useSiteTarget == AnnotationUseSiteTarget.CONSTRUCTOR_PARAMETER it.useSiteTarget == null || !isInConstructor || it.useSiteTarget == AnnotationUseSiteTarget.CONSTRUCTOR_PARAMETER
} }
.toIrAnnotations() .toIrAnnotations()
} }
private fun FirAnnotationCall.targetsField(): Boolean =
// Check if the annotation has a field-targeting meta annotation, e.g., @Target(FIELD)
hasMetaAnnotationUseSiteTargets(session, AnnotationUseSiteTarget.FIELD, AnnotationUseSiteTarget.PROPERTY_DELEGATE_FIELD)
fun generate(irProperty: IrProperty, property: FirProperty) { fun generate(irProperty: IrProperty, property: FirProperty) {
irProperty.annotations += irProperty.annotations +=
property.annotations property.annotations
.filter { .filter {
!it.isJvmFieldAnnotation && it.useSiteTarget == AnnotationUseSiteTarget.PROPERTY ||
(it.useSiteTarget == null || it.useSiteTarget == AnnotationUseSiteTarget.PROPERTY) // NB: annotation with null use-site should be landed on the property (ahead of field),
// unless it has FIELD target on the meta annotation, like @Target(FIELD)
(it.useSiteTarget == null && !it.targetsField())
} }
.toIrAnnotations() .toIrAnnotations()
} }
@@ -62,9 +69,9 @@ internal class AnnotationGenerator(private val components: Fir2IrComponents) : F
irField.annotations += irField.annotations +=
property.annotations property.annotations
.filter { .filter {
it.isJvmFieldAnnotation || it.useSiteTarget == AnnotationUseSiteTarget.FIELD ||
it.useSiteTarget == AnnotationUseSiteTarget.FIELD || it.useSiteTarget == AnnotationUseSiteTarget.PROPERTY_DELEGATE_FIELD ||
it.useSiteTarget == AnnotationUseSiteTarget.PROPERTY_DELEGATE_FIELD (it.useSiteTarget == null && it.targetsField())
} }
.toIrAnnotations() .toIrAnnotations()
} }
@@ -5,10 +5,10 @@
package org.jetbrains.kotlin.fir.declarations package org.jetbrains.kotlin.fir.declarations
import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget
import org.jetbrains.kotlin.fir.FirAnnotationContainer import org.jetbrains.kotlin.fir.FirAnnotationContainer
import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.expressions.FirAnnotationCall import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccessExpression
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
import org.jetbrains.kotlin.fir.resolve.toSymbol import org.jetbrains.kotlin.fir.resolve.toSymbol
import org.jetbrains.kotlin.fir.symbols.ConeClassLikeLookupTag import org.jetbrains.kotlin.fir.symbols.ConeClassLikeLookupTag
@@ -18,6 +18,10 @@ import org.jetbrains.kotlin.fir.types.coneType
import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
private val RETENTION_CLASS_ID = ClassId.fromString("kotlin/annotation/Retention")
private val TARGET_CLASS_ID = ClassId.fromString("kotlin/annotation/Target")
private val JVM_FIELD_CLASS_ID = ClassId.fromString("kotlin/jvm/JvmField")
private fun FirAnnotationCall.toAnnotationLookupTag(): ConeClassLikeLookupTag = private fun FirAnnotationCall.toAnnotationLookupTag(): ConeClassLikeLookupTag =
(annotationTypeRef.coneType as ConeClassLikeType).lookupTag (annotationTypeRef.coneType as ConeClassLikeType).lookupTag
@@ -27,29 +31,60 @@ fun FirAnnotationCall.toAnnotationClassId(): ClassId =
private fun FirAnnotationCall.toAnnotationClass(session: FirSession): FirRegularClass? = private fun FirAnnotationCall.toAnnotationClass(session: FirSession): FirRegularClass? =
toAnnotationLookupTag().toSymbol(session)?.fir as? FirRegularClass toAnnotationLookupTag().toSymbol(session)?.fir as? FirRegularClass
// TODO: this is temporary solution, we need something better
private val FirExpression.callableNameOfMetaAnnotationArgument: Name?
get() =
(this as? FirQualifiedAccessExpression)?.let {
val callableSymbol = (it.calleeReference as? FirResolvedNamedReference)?.resolvedSymbol as? FirCallableSymbol<*>
callableSymbol?.callableId?.callableName
}
fun FirAnnotationContainer.nonSourceAnnotations(session: FirSession): List<FirAnnotationCall> = fun FirAnnotationContainer.nonSourceAnnotations(session: FirSession): List<FirAnnotationCall> =
annotations.filter { annotation -> annotations.filter { annotation ->
val firAnnotationClass = annotation.toAnnotationClass(session) val firAnnotationClass = annotation.toAnnotationClass(session)
firAnnotationClass != null && firAnnotationClass.annotations.none { meta -> firAnnotationClass != null && firAnnotationClass.annotations.none { meta ->
val firMetaAnnotationClassId = meta.toAnnotationClassId() meta.toAnnotationClassId() == RETENTION_CLASS_ID &&
firMetaAnnotationClassId.asString() == "kotlin/annotation/Retention" && meta.argumentList.arguments.singleOrNull()?.callableNameOfMetaAnnotationArgument == Name.identifier("SOURCE")
// TODO: this is temporary solution, we need something better
(meta.argumentList.arguments.singleOrNull() as? FirQualifiedAccessExpression)?.let {
val callableSymbol = (it.calleeReference as? FirResolvedNamedReference)?.resolvedSymbol as? FirCallableSymbol<*>
callableSymbol?.callableId?.callableName
} == Name.identifier("SOURCE")
} }
} }
inline val FirProperty.hasJvmFieldAnnotation: Boolean inline val FirProperty.hasJvmFieldAnnotation: Boolean
get() = annotations.any { it.isJvmFieldAnnotation } get() = annotations.any { it.isJvmFieldAnnotation }
inline val FirAnnotationCall.isJvmFieldAnnotation: Boolean val FirAnnotationCall.isJvmFieldAnnotation: Boolean
get() { get() = toAnnotationClassId() == JVM_FIELD_CLASS_ID
val classId = toAnnotationClassId()
return classId.packageFqName.asString() == "kotlin.jvm" && classId.relativeClassName.asString() == "JvmField" private fun FirAnnotationCall.useSiteTargetsFromMetaAnnotation(session: FirSession): List<AnnotationUseSiteTarget> {
val metaAnnotationAboutTarget =
toAnnotationClass(session)?.annotations?.find { it.toAnnotationClassId() == TARGET_CLASS_ID }
?: return emptyList()
return metaAnnotationAboutTarget.argumentList.arguments.toAnnotationUseSiteTargets()
}
private fun List<FirExpression>.toAnnotationUseSiteTargets(): List<AnnotationUseSiteTarget> =
flatMap { arg ->
val unwrappedArg = if (arg is FirNamedArgumentExpression) arg.expression else arg
if (unwrappedArg is FirArrayOfCall) {
unwrappedArg.argumentList.arguments.toAnnotationUseSiteTargets()
} else {
unwrappedArg.toAnnotationUseSiteTarget()?.let { listOf(it) } ?: emptyList()
}
} }
private val USE_SITE_TARGET_NAME_MAP =
AnnotationUseSiteTarget.values().map { it.name to it }.toMap()
private fun FirExpression.toAnnotationUseSiteTarget(): AnnotationUseSiteTarget? =
// TODO: depending on the context, "PARAMETER" can be mapped to either CONSTRUCTOR_PARAMETER or SETTER_PARAMETER ?
callableNameOfMetaAnnotationArgument?.identifier?.let {
USE_SITE_TARGET_NAME_MAP[it]
}
fun FirAnnotationCall.hasMetaAnnotationUseSiteTargets(session: FirSession, vararg useSiteTargets: AnnotationUseSiteTarget): Boolean {
val meta = useSiteTargetsFromMetaAnnotation(session)
return useSiteTargets.any { meta.contains(it) }
}
fun FirAnnotatedDeclaration.hasAnnotation(classId: ClassId): Boolean { fun FirAnnotatedDeclaration.hasAnnotation(classId: ClassId): Boolean {
return annotations.any { it.toAnnotationClassId() == classId } return annotations.any { it.toAnnotationClassId() == classId }
} }
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
// TARGET_BACKEND: JVM // TARGET_BACKEND: JVM
// WITH_RUNTIME // WITH_RUNTIME