[FIR] Fix a couple of issues in annotation serialization.

This commit is contained in:
Mads Ager
2021-11-26 15:11:01 +01:00
committed by TeamCityServer
parent 3dd27b9b1f
commit f220e4a5ed
8 changed files with 93 additions and 34 deletions
@@ -13,7 +13,7 @@ import org.jetbrains.kotlin.metadata.deserialization.Flags
internal object FirAnnotationArgumentVisitor : AnnotationArgumentVisitor<Unit, FirAnnotationArgumentVisitorData>() {
override fun visitAnnotationValue(value: AnnotationValue, data: FirAnnotationArgumentVisitorData) {
data.builder.type = ProtoBuf.Annotation.Argument.Value.Type.ANNOTATION
// TODO: annotation = serializeAnnotation(value.value)
data.builder.annotation = data.serializer.serializeAnnotation(value.value)
}
override fun visitArrayValue(value: ArrayValue, data: FirAnnotationArgumentVisitorData) {
@@ -28,7 +28,7 @@ class FirAnnotationSerializer(private val session: FirSession, internal val stri
fun addArgument(argumentExpression: FirExpression, parameterName: Name) {
val argument = ProtoBuf.Annotation.Argument.newBuilder()
argument.nameId = stringTable.getStringIndex(parameterName.asString())
argument.setValue(valueProto(argumentExpression.toConstantValue() ?: return))
argument.setValue(valueProto(argumentExpression.toConstantValue(session) ?: return))
addArgument(argument)
}
@@ -884,7 +884,7 @@ class FirElementSerializer private constructor(
private fun serializeVersionRequirementFromRequireKotlin(annotation: FirAnnotation): ProtoBuf.VersionRequirement.Builder? {
val argumentMapping = annotation.argumentMapping.mapping
val versionString = (argumentMapping[RequireKotlinConstants.VERSION]?.toConstantValue() as? StringValue)?.value ?: return null
val versionString = (argumentMapping[RequireKotlinConstants.VERSION]?.toConstantValue(session) as? StringValue)?.value ?: return null
val matchResult = RequireKotlinConstants.VERSION_REGEX.matchEntire(versionString) ?: return null
val major = matchResult.groupValues.getOrNull(1)?.toIntOrNull() ?: return null
@@ -897,12 +897,12 @@ class FirElementSerializer private constructor(
writeVersionFull = { proto.versionFull = it }
)
val message = (argumentMapping[RequireKotlinConstants.MESSAGE]?.toConstantValue() as? StringValue)?.value
val message = (argumentMapping[RequireKotlinConstants.MESSAGE]?.toConstantValue(session) as? StringValue)?.value
if (message != null) {
proto.message = stringTable.getStringIndex(message)
}
when ((argumentMapping[RequireKotlinConstants.LEVEL]?.toConstantValue() as? EnumValue)?.enumEntryName?.asString()) {
when ((argumentMapping[RequireKotlinConstants.LEVEL]?.toConstantValue(session) as? EnumValue)?.enumEntryName?.asString()) {
DeprecationLevel.ERROR.name -> {
// ERROR is the default level
}
@@ -910,7 +910,7 @@ class FirElementSerializer private constructor(
DeprecationLevel.HIDDEN.name -> proto.level = ProtoBuf.VersionRequirement.Level.HIDDEN
}
when ((argumentMapping[RequireKotlinConstants.VERSION_KIND]?.toConstantValue() as? EnumValue)?.enumEntryName?.asString()) {
when ((argumentMapping[RequireKotlinConstants.VERSION_KIND]?.toConstantValue(session) as? EnumValue)?.enumEntryName?.asString()) {
ProtoBuf.VersionRequirement.VersionKind.LANGUAGE_VERSION.name -> {
// LANGUAGE_VERSION is the default kind
}
@@ -920,7 +920,7 @@ class FirElementSerializer private constructor(
proto.versionKind = ProtoBuf.VersionRequirement.VersionKind.API_VERSION
}
val errorCode = (argumentMapping[RequireKotlinConstants.ERROR_CODE]?.toConstantValue() as? IntValue)?.value
val errorCode = (argumentMapping[RequireKotlinConstants.ERROR_CODE]?.toConstantValue(session) as? IntValue)?.value
if (errorCode != null && errorCode != -1) {
proto.errorCode = errorCode
}
@@ -157,6 +157,7 @@ internal class KClassValue(value: Value) : ConstantValue<KClassValue.Value>(valu
var type = argumentType
var arrayDimensions = 0
while (true) {
if (type.isPrimitiveArray) break
type = type.arrayElementType() ?: break
arrayDimensions++
}
@@ -5,10 +5,23 @@
package org.jetbrains.kotlin.fir.serialization.constant
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.containingClass
import org.jetbrains.kotlin.fir.declarations.FirEnumEntry
import org.jetbrains.kotlin.fir.declarations.FirFunction
import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.expressions.builder.FirAnnotationCallBuilder
import org.jetbrains.kotlin.fir.expressions.builder.buildAnnotationArgumentMapping
import org.jetbrains.kotlin.fir.expressions.builder.buildAnnotationCall
import org.jetbrains.kotlin.fir.expressions.builder.buildArgumentList
import org.jetbrains.kotlin.fir.references.builder.buildSimpleNamedReference
import org.jetbrains.kotlin.fir.render
import org.jetbrains.kotlin.fir.resolve.defaultType
import org.jetbrains.kotlin.fir.resolve.toFirRegularClass
import org.jetbrains.kotlin.fir.resolve.toFirRegularClassSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirConstructorSymbol
import org.jetbrains.kotlin.fir.types.ConeClassLikeType
import org.jetbrains.kotlin.fir.types.classId
import org.jetbrains.kotlin.fir.types.coneTypeSafe
@@ -16,12 +29,12 @@ import org.jetbrains.kotlin.fir.types.coneTypeUnsafe
import org.jetbrains.kotlin.fir.visitors.FirDefaultVisitor
import org.jetbrains.kotlin.types.ConstantValueKind
internal fun FirExpression.toConstantValue(): ConstantValue<*>? = accept(FirToConstantValueTransformer, null)
internal fun FirExpression.toConstantValue(session: FirSession): ConstantValue<*>? = accept(FirToConstantValueTransformer, session)
internal object FirToConstantValueTransformer : FirDefaultVisitor<ConstantValue<*>?, Nothing?>() {
internal object FirToConstantValueTransformer : FirDefaultVisitor<ConstantValue<*>?, FirSession>() {
override fun visitElement(
element: FirElement,
data: Nothing?
data: FirSession
): ConstantValue<*>? {
error("Illegal element as annotation argument: ${element::class.qualifiedName} -> ${element.render()}")
}
@@ -29,7 +42,7 @@ internal object FirToConstantValueTransformer : FirDefaultVisitor<ConstantValue<
@OptIn(ExperimentalUnsignedTypes::class)
override fun <T> visitConstExpression(
constExpression: FirConstExpression<T>,
data: Nothing?
data: FirSession
): ConstantValue<*>? {
val value = constExpression.value
return when (constExpression.kind) {
@@ -53,59 +66,110 @@ internal object FirToConstantValueTransformer : FirDefaultVisitor<ConstantValue<
override fun visitArrayOfCall(
arrayOfCall: FirArrayOfCall,
data: Nothing?
data: FirSession
): ConstantValue<*> {
return ArrayValue(arrayOfCall.argumentList.arguments.mapNotNull { it.accept(this, null) })
return ArrayValue(arrayOfCall.argumentList.arguments.mapNotNull { it.accept(this, data) })
}
override fun visitAnnotation(
annotation: FirAnnotation,
data: Nothing?
data: FirSession
): ConstantValue<*> {
return AnnotationValue(annotation)
}
override fun visitAnnotationCall(annotationCall: FirAnnotationCall, data: Nothing?): ConstantValue<*> {
override fun visitAnnotationCall(annotationCall: FirAnnotationCall, data: FirSession): ConstantValue<*> {
return visitAnnotation(annotationCall, data)
}
override fun visitGetClassCall(
getClassCall: FirGetClassCall,
data: Nothing?
data: FirSession
): ConstantValue<*>? {
return KClassValue.create(getClassCall.typeRef.coneTypeUnsafe())
return KClassValue.create(getClassCall.argument.typeRef.coneTypeUnsafe())
}
override fun visitQualifiedAccessExpression(
qualifiedAccessExpression: FirQualifiedAccessExpression,
data: Nothing?
data: FirSession
): ConstantValue<*>? {
val symbol = qualifiedAccessExpression.toResolvedCallableSymbol() ?: return null
val enumEntry = symbol.fir as? FirEnumEntry ?: return null
val classId = enumEntry.returnTypeRef.coneTypeSafe<ConeClassLikeType>()?.classId ?: return null
val outerClassId = classId.outerClassId ?: return null
return EnumValue(outerClassId, enumEntry.name)
return when {
symbol.fir is FirEnumEntry -> {
val classId = symbol.fir.returnTypeRef.coneTypeSafe<ConeClassLikeType>()?.classId ?: return null
EnumValue(classId, (symbol.fir as FirEnumEntry).name)
}
symbol is FirConstructorSymbol -> {
val constructorCall = qualifiedAccessExpression as FirFunctionCall
val constructedClassSymbol = symbol.containingClass()?.toFirRegularClassSymbol(data) ?: return null
return if (constructedClassSymbol.classKind == ClassKind.ANNOTATION_CLASS) {
AnnotationValue(
buildAnnotationCall {
argumentMapping = buildAnnotationArgumentMapping {
constructorCall.argumentMapping?.forEach { (firExpression, firValueParameter) ->
mapping[firValueParameter.name] = firExpression
}
}
annotationTypeRef = qualifiedAccessExpression.typeRef
calleeReference = buildSimpleNamedReference {
source = qualifiedAccessExpression.source
name = qualifiedAccessExpression.calleeReference.name
}
}
)
} else {
null
}
}
symbol.callableId.packageName.asString() == "kotlin" -> {
val dispatchReceiver = qualifiedAccessExpression.dispatchReceiver
when (symbol.callableId.callableName.asString()) {
"toByte" -> ByteValue((dispatchReceiver.accept(this, data)!!.value as Number).toByte())
"toLong" -> LongValue((dispatchReceiver.accept(this, data)!!.value as Number).toLong())
"toShort" -> ShortValue((dispatchReceiver.accept(this, data)!!.value as Number).toShort())
"toFloat" -> FloatValue((dispatchReceiver.accept(this, data)!!.value as Number).toFloat())
"toDouble" -> DoubleValue((dispatchReceiver.accept(this, data)!!.value as Number).toDouble())
"toChar" -> CharValue((dispatchReceiver.accept(this, data)!!.value as Number).toChar())
"unaryMinus" -> {
when (val receiverValue = dispatchReceiver.accept(this, data)) {
is ByteValue -> ByteValue((-receiverValue.value).toByte())
is LongValue -> LongValue(-receiverValue.value)
is ShortValue -> ShortValue((-receiverValue.value).toShort())
is FloatValue -> FloatValue(-receiverValue.value)
is DoubleValue -> DoubleValue(-receiverValue.value)
else -> null
}
}
else -> null
}
}
else -> null
}
}
override fun visitPropertyAccessExpression(propertyAccessExpression: FirPropertyAccessExpression, data: Nothing?): ConstantValue<*>? {
override fun visitPropertyAccessExpression(propertyAccessExpression: FirPropertyAccessExpression, data: FirSession): ConstantValue<*>? {
return visitQualifiedAccessExpression(propertyAccessExpression, data)
}
override fun visitFunctionCall(
functionCall: FirFunctionCall,
data: Nothing?
data: FirSession
): ConstantValue<*>? {
return visitQualifiedAccessExpression(functionCall, data)
}
override fun visitVarargArgumentsExpression(
varargArgumentsExpression: FirVarargArgumentsExpression,
data: Nothing?
data: FirSession
): ConstantValue<*> {
return ArrayValue(varargArgumentsExpression.arguments.mapNotNull { it.accept(this, null) })
return ArrayValue(varargArgumentsExpression.arguments.mapNotNull { it.accept(this, data) })
}
override fun visitNamedArgumentExpression(namedArgumentExpression: FirNamedArgumentExpression, data: Nothing?): ConstantValue<*>? {
return namedArgumentExpression.expression.accept(this, null)
override fun visitNamedArgumentExpression(namedArgumentExpression: FirNamedArgumentExpression, data: FirSession): ConstantValue<*>? {
return namedArgumentExpression.expression.accept(this, data)
}
}
@@ -1,5 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
// FIR status: Fail: class kotlin.reflect.KClass
// TARGET_BACKEND: JVM
// WITH_REFLECT
@@ -1,5 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
// FIR status: assertEquals fails
// TARGET_BACKEND: JVM
// WITH_REFLECT
@@ -1,5 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
// FIR status: assertEquals fails (something wrong with argument order/mapping)
// TARGET_BACKEND: JVM
// WITH_REFLECT
package test