From 1f837134bbb66d3bc1ca10182fc17512d9be9d28 Mon Sep 17 00:00:00 2001 From: Leonid Startsev Date: Tue, 1 Jun 2021 16:57:53 +0300 Subject: [PATCH] Support for @EncodeDefault annotation --- .../compiler/backend/ir/GeneratorHelpers.kt | 42 +++++++++++++++---- .../compiler/resolve/NamingConventions.kt | 1 + 2 files changed, 34 insertions(+), 9 deletions(-) diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/GeneratorHelpers.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/GeneratorHelpers.kt index bee6b3a280e..ec96f62b261 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/GeneratorHelpers.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/GeneratorHelpers.kt @@ -1227,24 +1227,48 @@ interface IrBuilderExtension { ) // check for call to .shouldEncodeElementDefault - if (!property.optional) { + val encodeDefaults = property.getIrPropertyFrom(serializableIrClass).getEncodeDefaultAnnotationValue() + if (!property.optional || encodeDefaults == true) { // emit call right away +elementCall } else { - // emit check: - // if (if (output.shouldEncodeElementDefault(this.descriptor, i)) true else {obj.prop != DEFAULT_VALUE} ) { - // output.encodeIntElement(this.descriptor, i, obj.prop)// block {obj.prop != DEFAULT_VALUE} may contain several statements - val shouldEncodeFunc = kOutputClass.referenceFunctionSymbol(CallingConventions.shouldEncodeDefault) - val partA = irInvoke(irGet(localOutput), shouldEncodeFunc, irGet(localSerialDesc), irInt(index)) val partB = irNotEquals(property.irGet(), initializerAdapter(property.irField.initializer!!)) - // Ir infrastructure does not have dedicated symbol for ||, so - // `a || b == if (a) true else b`, see org.jetbrains.kotlin.ir.builders.PrimitivesKt.oror - val condition = irIfThenElse(compilerContext.irBuiltIns.booleanType, partA, irTrue(), partB) + + val condition = if (encodeDefaults == false) { + // drop default without call to .shouldEncodeElementDefault + partB + } else { + // emit check: + // if (if (output.shouldEncodeElementDefault(this.descriptor, i)) true else {obj.prop != DEFAULT_VALUE} ) { + // output.encodeIntElement(this.descriptor, i, obj.prop)// block {obj.prop != DEFAULT_VALUE} may contain several statements + val shouldEncodeFunc = kOutputClass.referenceFunctionSymbol(CallingConventions.shouldEncodeDefault) + val partA = irInvoke(irGet(localOutput), shouldEncodeFunc, irGet(localSerialDesc), irInt(index)) + // Ir infrastructure does not have dedicated symbol for ||, so + // `a || b == if (a) true else b`, see org.jetbrains.kotlin.ir.builders.PrimitivesKt.oror + irIfThenElse(compilerContext.irBuiltIns.booleanType, partA, irTrue(), partB) + } +irIfThen(condition, elementCall) } } } + /** + * True — ALWAYS + * False — NEVER + * null — not specified + */ + fun IrProperty.getEncodeDefaultAnnotationValue(): Boolean? { + val call = annotations.findAnnotation(SerializationAnnotations.encodeDefaultFqName) ?: return null + val arg = call.getValueArgument(0) ?: return true // ALWAYS by default + val argValue = (arg as? IrGetEnumValue ?: error("Argument of enum constructor expected to implement IrGetEnumValue, got $arg")).symbol.owner.name.toString() + return when (argValue) { + "ALWAYS" -> true + "NEVER" -> false + else -> error("Unknown EncodeDefaultMode enum value: $argValue") + } + } + + fun IrBlockBodyBuilder.formEncodeDecodePropertyCall( enclosingGenerator: AbstractSerialGenerator, encoder: IrExpression, diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/NamingConventions.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/NamingConventions.kt index 165e4a1abe5..0cc4918ef1e 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/NamingConventions.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/NamingConventions.kt @@ -29,6 +29,7 @@ object SerializationAnnotations { internal val requiredAnnotationFqName = FqName("kotlinx.serialization.Required") val serialTransientFqName = FqName("kotlinx.serialization.Transient") internal val serialInfoFqName = FqName("kotlinx.serialization.SerialInfo") + internal val encodeDefaultFqName = FqName("kotlinx.serialization.EncodeDefault") internal val contextualFqName = FqName("kotlinx.serialization.ContextualSerialization") // this one is deprecated internal val contextualOnFileFqName = FqName("kotlinx.serialization.UseContextualSerialization")