Support annotations in constructor signatures of special serializers

This commit is contained in:
Leonid Startsev
2021-05-24 18:27:02 +03:00
committed by Space
parent 229085f3d1
commit b61277df97
3 changed files with 64 additions and 27 deletions
@@ -810,6 +810,27 @@ interface IrBuilderExtension {
.map { it.name.toString() }
}
fun IrBuilderWithScope.copyAnnotationsFrom(annotations: List<IrConstructorCall>): List<IrExpression> =
annotations.mapNotNull { annotationCall ->
val annotationClass = annotationCall.symbol.owner.parentAsClass
if (!annotationClass.descriptor.isSerialInfoAnnotation) return@mapNotNull null
if (compilerContext.platform.isJvm()) {
val implClass = compilerContext.serialInfoImplJvmIrGenerator.getImplClass(annotationClass)
val ctor = implClass.constructors.singleOrNull { it.valueParameters.size == annotationCall.valueArgumentsCount }
?: error("No constructor args found for SerialInfo annotation Impl class: ${implClass.render()}")
irCall(ctor).apply {
for (i in 0 until annotationCall.valueArgumentsCount) {
val argument = annotationCall.getValueArgument(i)
?: annotationClass.primaryConstructor!!.valueParameters[i].defaultValue?.expression
putValueArgument(i, argument!!.deepCopyWithVariables())
}
}
} else {
annotationCall.deepCopyWithVariables()
}
}
// Does not use sti and therefore does not perform encoder calls optimization
fun IrBuilderWithScope.serializerTower(
generator: SerializerIrGenerator,
@@ -920,8 +941,14 @@ interface IrBuilderExtension {
val hasNewCtxSerCtor =
serializerClassOriginal.classId == contextSerializerId && compilerContext.referenceConstructors(serializerClass.fqNameSafe)
.any { it.owner.valueParameters.size == 3 }
var needToCopyAnnotations = false
when (serializerClassOriginal.classId) {
contextSerializerId, polymorphicSerializerId -> {
polymorphicSerializerId -> {
needToCopyAnnotations = true
args = listOf(classReference(kType))
typeArgs = listOf(thisIrType)
}
contextSerializerId -> {
args = listOf(classReference(kType))
typeArgs = listOf(thisIrType)
@@ -950,10 +977,12 @@ interface IrBuilderExtension {
}
}
objectSerializerId -> {
needToCopyAnnotations = true
args = listOf(irString(kType.serialName()), irGetObject(kType.toClassDescriptor!!))
typeArgs = listOf(thisIrType)
}
sealedSerializerId -> {
needToCopyAnnotations = true
args = mutableListOf<IrExpression>().apply {
add(irString(kType.serialName()))
add(classReference(kType))
@@ -1031,16 +1060,36 @@ interface IrBuilderExtension {
findSerializerConstructorForTypeArgumentsSerializers(serializerClass)
) { "Generated serializer does not have constructor with required number of arguments" }
} else {
compilerContext.referenceConstructors(serializerClass.fqNameSafe).single { it.owner.isPrimary }
val constructors = compilerContext.referenceConstructors(serializerClass.fqNameSafe)
// search for new signature of polymorphic/sealed/contextual serializer
if (!needToCopyAnnotations) {
constructors.single { it.owner.isPrimary }
} else {
constructors.find { it.owner.lastArgumentIsAnnotationArray() } ?: run {
// not found - we are using old serialization runtime without this feature
needToCopyAnnotations = false
constructors.single { it.owner.isPrimary }
}
}
}
// Return type should be correctly substituted
assert(ctor.isBound)
val ctorDecl = ctor.owner
if (needToCopyAnnotations) {
val classAnnotations = copyAnnotationsFrom(thisIrType.getClass()?.annotations.orEmpty())
args = args + createArrayOfExpression(compilerContext.builtIns.annotationType.toIrType(), classAnnotations)
}
val typeParameters = ctorDecl.parentAsClass.typeParameters
val substitutedReturnType = ctorDecl.returnType.substitute(typeParameters, typeArgs)
return irInvoke(null, ctor, typeArguments = typeArgs, valueArguments = args, returnTypeHint = substitutedReturnType)
}
private fun IrConstructor.lastArgumentIsAnnotationArray(): Boolean {
val lastArgType = valueParameters.lastOrNull()?.type
return (lastArgType != null && (lastArgType as? IrSimpleType)?.arguments?.firstOrNull()?.typeOrNull?.classFqName?.toString() == "kotlin.Annotation")
}
private fun IrBuilderWithScope.wrapperClassReference(classType: KotlinType): IrClassReference {
if (compilerContext.platform.isJvm()) {
// "Byte::class" -> "java.lang.Byte::class"
@@ -156,25 +156,8 @@ open class SerializerIrGenerator(
receiver: IrVariable,
method: IrFunctionSymbol
) {
for (annotationCall in annotations) {
val annotationClass = annotationCall.symbol.owner.parentAsClass
if (!annotationClass.descriptor.isSerialInfoAnnotation) continue
val createAnnotation = if (compilerContext.platform.isJvm()) {
val implClass = serialInfoJvmGenerator.getImplClass(annotationClass)
val ctor = implClass.constructors.singleOrNull { it.valueParameters.size == annotationCall.valueArgumentsCount }
?: error("No constructor args found for SerialInfo annotation Impl class: ${implClass.render()}")
irCall(ctor).apply {
for (i in 0 until annotationCall.valueArgumentsCount) {
val argument = annotationCall.getValueArgument(i) ?: annotationClass.primaryConstructor!!.valueParameters[i].defaultValue?.expression
putValueArgument(i, argument!!.deepCopyWithVariables())
}
}
} else {
annotationCall.deepCopyWithVariables()
}
+irInvoke(irGet(receiver), method, createAnnotation)
copyAnnotationsFrom(annotations).forEach {
+irInvoke(irGet(receiver), method, it)
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Copyright 2010-2021 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.
*/
@@ -40,17 +40,22 @@ fun ClassLoweringPass.runOnFileInOrder(irFile: IrFile) {
})
}
typealias SerializationPluginContext = IrPluginContext
class SerializationPluginContext(baseContext: IrPluginContext, val metadataPlugin: SerializationDescriptorSerializerPlugin?) :
IrPluginContext by baseContext {
lateinit var serialInfoImplJvmIrGenerator: SerialInfoImplJvmIrGenerator
}
private class SerializerClassLowering(
val context: SerializationPluginContext,
val metadataPlugin: SerializationDescriptorSerializerPlugin?
baseContext: IrPluginContext,
metadataPlugin: SerializationDescriptorSerializerPlugin?
) : IrElementTransformerVoid(), ClassLoweringPass {
private val serialInfoJvmGenerator = SerialInfoImplJvmIrGenerator(context)
val context: SerializationPluginContext = SerializationPluginContext(baseContext, metadataPlugin)
private val serialInfoJvmGenerator = SerialInfoImplJvmIrGenerator(context).also { context.serialInfoImplJvmIrGenerator = it }
override fun lower(irClass: IrClass) {
SerializableIrGenerator.generate(irClass, context, context.bindingContext)
SerializerIrGenerator.generate(irClass, context, context.bindingContext, metadataPlugin, serialInfoJvmGenerator)
SerializerIrGenerator.generate(irClass, context, context.bindingContext, context.metadataPlugin, serialInfoJvmGenerator)
SerializableCompanionIrGenerator.generate(irClass, context, context.bindingContext)
if (context.platform.isJvm() && KSerializerDescriptorResolver.isSerialInfoImpl(irClass.descriptor)) {