Kx-serialization: support SerialInfo for JVM IR
No tests added because this is going to be checked by the upcoming build of kotlinx-serialization runtime with JVM IR. #KT-42976 In Progress
This commit is contained in:
+3
-1
@@ -218,7 +218,9 @@ class ExpressionCodegen(
|
|||||||
mv.visitLineNumber(1, startLabel)
|
mv.visitLineNumber(1, startLabel)
|
||||||
}
|
}
|
||||||
val info = BlockInfo()
|
val info = BlockInfo()
|
||||||
val body = irFunction.body!!
|
val body = irFunction.body
|
||||||
|
?: error("Function has no body: ${irFunction.render()}")
|
||||||
|
|
||||||
generateNonNullAssertions()
|
generateNonNullAssertions()
|
||||||
generateFakeContinuationConstructorIfNeeded()
|
generateFakeContinuationConstructorIfNeeded()
|
||||||
val result = body.accept(this, info)
|
val result = body.accept(this, info)
|
||||||
|
|||||||
+5
-4
@@ -220,10 +220,10 @@ interface IrBuilderExtension {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun generateSimplePropertyWithBackingField(
|
fun generateSimplePropertyWithBackingField(
|
||||||
ownerSymbol: IrValueSymbol,
|
|
||||||
propertyDescriptor: PropertyDescriptor,
|
propertyDescriptor: PropertyDescriptor,
|
||||||
propertyParent: IrClass,
|
propertyParent: IrClass,
|
||||||
declare: Boolean
|
declare: Boolean,
|
||||||
|
fieldName: Name = propertyDescriptor.name,
|
||||||
): IrProperty {
|
): IrProperty {
|
||||||
val irPropertySymbol = compilerContext.symbolTable.referenceProperty(propertyDescriptor)
|
val irPropertySymbol = compilerContext.symbolTable.referenceProperty(propertyDescriptor)
|
||||||
assert(irPropertySymbol.isBound || declare)
|
assert(irPropertySymbol.isBound || declare)
|
||||||
@@ -241,7 +241,7 @@ interface IrBuilderExtension {
|
|||||||
}
|
}
|
||||||
val irProperty = irPropertySymbol.owner
|
val irProperty = irPropertySymbol.owner
|
||||||
|
|
||||||
irProperty.backingField = generatePropertyBackingField(propertyDescriptor, irProperty).apply {
|
irProperty.backingField = generatePropertyBackingField(propertyDescriptor, irProperty, fieldName).apply {
|
||||||
parent = propertyParent
|
parent = propertyParent
|
||||||
correspondingPropertySymbol = irPropertySymbol
|
correspondingPropertySymbol = irPropertySymbol
|
||||||
}
|
}
|
||||||
@@ -255,7 +255,8 @@ interface IrBuilderExtension {
|
|||||||
|
|
||||||
private fun generatePropertyBackingField(
|
private fun generatePropertyBackingField(
|
||||||
descriptor: PropertyDescriptor,
|
descriptor: PropertyDescriptor,
|
||||||
originProperty: IrProperty
|
originProperty: IrProperty,
|
||||||
|
name: Name,
|
||||||
): IrField {
|
): IrField {
|
||||||
val fieldSymbol = compilerContext.symbolTable.referenceField(descriptor)
|
val fieldSymbol = compilerContext.symbolTable.referenceField(descriptor)
|
||||||
if (fieldSymbol.isBound) return fieldSymbol.owner
|
if (fieldSymbol.isBound) return fieldSymbol.owner
|
||||||
|
|||||||
+94
@@ -0,0 +1,94 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2020 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.kotlinx.serialization.compiler.backend.ir
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
|
||||||
|
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
||||||
|
import org.jetbrains.kotlin.ir.builders.declarations.addConstructor
|
||||||
|
import org.jetbrains.kotlin.ir.builders.declarations.addValueParameter
|
||||||
|
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||||
|
import org.jetbrains.kotlin.ir.declarations.IrProperty
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.IrConstructorCall
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.impl.*
|
||||||
|
import org.jetbrains.kotlin.ir.util.constructors
|
||||||
|
import org.jetbrains.kotlin.ir.util.defaultType
|
||||||
|
import org.jetbrains.kotlin.name.Name
|
||||||
|
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||||
|
import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPluginContext
|
||||||
|
import org.jetbrains.kotlinx.serialization.compiler.resolve.KSerializerDescriptorResolver
|
||||||
|
|
||||||
|
// This doesn't support annotation arguments of type KClass and Array<KClass> because the codegen doesn't compute JVM signatures for
|
||||||
|
// such cases correctly (because inheriting from annotation classes is prohibited in Kotlin).
|
||||||
|
// Currently it results in an "accidental override" error where a method with return type KClass conflicts with the one with Class.
|
||||||
|
// TODO: support annotation properties of types KClass<...> and Array<KClass<...>>.
|
||||||
|
class SerialInfoImplJvmIrGenerator(
|
||||||
|
private val irClass: IrClass,
|
||||||
|
private val context: SerializationPluginContext,
|
||||||
|
) : IrBuilderExtension {
|
||||||
|
override val compilerContext: SerializationPluginContext
|
||||||
|
get() = context
|
||||||
|
|
||||||
|
private val jvmNameClass = context.referenceClass(DescriptorUtils.JVM_NAME)!!.owner
|
||||||
|
|
||||||
|
private fun generate() {
|
||||||
|
val properties = irClass.declarations.filterIsInstance<IrProperty>()
|
||||||
|
if (properties.isEmpty()) return
|
||||||
|
|
||||||
|
val startOffset = UNDEFINED_OFFSET
|
||||||
|
val endOffset = UNDEFINED_OFFSET
|
||||||
|
|
||||||
|
val ctor = irClass.addConstructor {
|
||||||
|
visibility = DescriptorVisibilities.PUBLIC
|
||||||
|
}
|
||||||
|
val ctorBody = context.irFactory.createBlockBody(
|
||||||
|
startOffset, endOffset, listOf(
|
||||||
|
IrDelegatingConstructorCallImpl(
|
||||||
|
startOffset, endOffset, context.irBuiltIns.unitType, context.irBuiltIns.anyClass.constructors.single(),
|
||||||
|
typeArgumentsCount = 0, valueArgumentsCount = 0
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
ctor.body = ctorBody
|
||||||
|
|
||||||
|
for (property in properties) {
|
||||||
|
generateSimplePropertyWithBackingField(property.descriptor, irClass, false, Name.identifier("_" + property.name.asString()))
|
||||||
|
|
||||||
|
val getter = property.getter!!
|
||||||
|
getter.origin = SERIALIZABLE_SYNTHETIC_ORIGIN
|
||||||
|
// Add JvmName annotation to property getters to force the resulting JVM method name for 'x' be 'x', instead of 'getX',
|
||||||
|
// and to avoid having useless bridges for it generated in BridgeLowering.
|
||||||
|
// Unfortunately, this results in an extra `@JvmName` annotation in the bytecode, but it shouldn't matter very much.
|
||||||
|
getter.annotations += jvmName(property.name.asString())
|
||||||
|
|
||||||
|
val field = property.backingField!!
|
||||||
|
field.visibility = DescriptorVisibilities.PRIVATE
|
||||||
|
field.origin = SERIALIZABLE_SYNTHETIC_ORIGIN
|
||||||
|
|
||||||
|
val parameter = ctor.addValueParameter(property.name.asString(), getter.returnType)
|
||||||
|
ctorBody.statements += IrSetFieldImpl(
|
||||||
|
startOffset, endOffset, field.symbol,
|
||||||
|
IrGetValueImpl(startOffset, endOffset, irClass.thisReceiver!!.symbol),
|
||||||
|
IrGetValueImpl(startOffset, endOffset, parameter.symbol),
|
||||||
|
context.irBuiltIns.unitType,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun jvmName(name: String): IrConstructorCall =
|
||||||
|
IrConstructorCallImpl(
|
||||||
|
UNDEFINED_OFFSET, UNDEFINED_OFFSET, jvmNameClass.defaultType, jvmNameClass.constructors.single().symbol,
|
||||||
|
typeArgumentsCount = 0, constructorTypeArgumentsCount = 0, valueArgumentsCount = 1,
|
||||||
|
).apply {
|
||||||
|
putValueArgument(0, IrConstImpl.string(UNDEFINED_OFFSET, UNDEFINED_OFFSET, context.irBuiltIns.stringType, name))
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun generate(irClass: IrClass, context: SerializationPluginContext) {
|
||||||
|
if (KSerializerDescriptorResolver.isSerialInfoImpl(irClass.descriptor))
|
||||||
|
SerialInfoImplJvmIrGenerator(irClass, context).generate()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+5
-5
@@ -28,7 +28,6 @@ import org.jetbrains.kotlin.ir.util.patchDeclarationParents
|
|||||||
import org.jetbrains.kotlin.ir.util.withReferenceScope
|
import org.jetbrains.kotlin.ir.util.withReferenceScope
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
||||||
import org.jetbrains.kotlin.types.KotlinType
|
|
||||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||||
import org.jetbrains.kotlinx.serialization.compiler.backend.common.SerializerCodegen
|
import org.jetbrains.kotlinx.serialization.compiler.backend.common.SerializerCodegen
|
||||||
import org.jetbrains.kotlinx.serialization.compiler.backend.common.getSerialTypeInfo
|
import org.jetbrains.kotlinx.serialization.compiler.backend.common.getSerialTypeInfo
|
||||||
@@ -42,9 +41,11 @@ import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.ST
|
|||||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.STRUCTURE_ENCODER_CLASS
|
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.STRUCTURE_ENCODER_CLASS
|
||||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.UNKNOWN_FIELD_EXC
|
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.UNKNOWN_FIELD_EXC
|
||||||
|
|
||||||
// Is creating synthetic origin is a good idea or not?
|
|
||||||
object SERIALIZABLE_PLUGIN_ORIGIN : IrDeclarationOriginImpl("SERIALIZER")
|
object SERIALIZABLE_PLUGIN_ORIGIN : IrDeclarationOriginImpl("SERIALIZER")
|
||||||
|
|
||||||
|
// TODO: use in places where elements need to have ACC_SYNTHETIC on JVM
|
||||||
|
object SERIALIZABLE_SYNTHETIC_ORIGIN : IrDeclarationOriginImpl("SERIALIZER")
|
||||||
|
|
||||||
open class SerializerIrGenerator(
|
open class SerializerIrGenerator(
|
||||||
val irClass: IrClass,
|
val irClass: IrClass,
|
||||||
final override val compilerContext: SerializationPluginContext,
|
final override val compilerContext: SerializationPluginContext,
|
||||||
@@ -69,12 +70,11 @@ open class SerializerIrGenerator(
|
|||||||
|
|
||||||
// how to (auto)create backing field and getter/setter?
|
// how to (auto)create backing field and getter/setter?
|
||||||
compilerContext.symbolTable.withReferenceScope(irClass.descriptor) {
|
compilerContext.symbolTable.withReferenceScope(irClass.descriptor) {
|
||||||
|
prop = generateSimplePropertyWithBackingField(desc, irClass, false)
|
||||||
prop = generateSimplePropertyWithBackingField(thisAsReceiverParameter.symbol, desc, irClass, false)
|
|
||||||
|
|
||||||
// TODO: Do not use descriptors here
|
// TODO: Do not use descriptors here
|
||||||
localSerializersFieldsDescriptors.forEach {
|
localSerializersFieldsDescriptors.forEach {
|
||||||
generateSimplePropertyWithBackingField(thisAsReceiverParameter.symbol, it, irClass, true)
|
generateSimplePropertyWithBackingField(it, irClass, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+11
-3
@@ -6,8 +6,8 @@
|
|||||||
package org.jetbrains.kotlinx.serialization.compiler.extensions
|
package org.jetbrains.kotlinx.serialization.compiler.extensions
|
||||||
|
|
||||||
import org.jetbrains.kotlin.backend.common.ClassLoweringPass
|
import org.jetbrains.kotlin.backend.common.ClassLoweringPass
|
||||||
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
|
|
||||||
import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension
|
import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension
|
||||||
|
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
|
||||||
import org.jetbrains.kotlin.backend.common.runOnFilePostfix
|
import org.jetbrains.kotlin.backend.common.runOnFilePostfix
|
||||||
import org.jetbrains.kotlin.ir.IrElement
|
import org.jetbrains.kotlin.ir.IrElement
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||||
@@ -17,6 +17,8 @@ import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
|||||||
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
|
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
|
||||||
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
|
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
|
||||||
import org.jetbrains.kotlin.ir.visitors.acceptVoid
|
import org.jetbrains.kotlin.ir.visitors.acceptVoid
|
||||||
|
import org.jetbrains.kotlin.platform.jvm.isJvm
|
||||||
|
import org.jetbrains.kotlinx.serialization.compiler.backend.ir.SerialInfoImplJvmIrGenerator
|
||||||
import org.jetbrains.kotlinx.serialization.compiler.backend.ir.SerializableCompanionIrGenerator
|
import org.jetbrains.kotlinx.serialization.compiler.backend.ir.SerializableCompanionIrGenerator
|
||||||
import org.jetbrains.kotlinx.serialization.compiler.backend.ir.SerializableIrGenerator
|
import org.jetbrains.kotlinx.serialization.compiler.backend.ir.SerializableIrGenerator
|
||||||
import org.jetbrains.kotlinx.serialization.compiler.backend.ir.SerializerIrGenerator
|
import org.jetbrains.kotlinx.serialization.compiler.backend.ir.SerializerIrGenerator
|
||||||
@@ -48,10 +50,16 @@ private class SerializerClassLowering(
|
|||||||
SerializableIrGenerator.generate(irClass, context, context.bindingContext)
|
SerializableIrGenerator.generate(irClass, context, context.bindingContext)
|
||||||
SerializerIrGenerator.generate(irClass, context, context.bindingContext, metadataPlugin)
|
SerializerIrGenerator.generate(irClass, context, context.bindingContext, metadataPlugin)
|
||||||
SerializableCompanionIrGenerator.generate(irClass, context, context.bindingContext)
|
SerializableCompanionIrGenerator.generate(irClass, context, context.bindingContext)
|
||||||
|
|
||||||
|
if (context.platform.isJvm()) {
|
||||||
|
SerialInfoImplJvmIrGenerator.generate(irClass, context)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
open class SerializationLoweringExtension @JvmOverloads constructor(val metadataPlugin: SerializationDescriptorSerializerPlugin? = null) : IrGenerationExtension {
|
open class SerializationLoweringExtension @JvmOverloads constructor(
|
||||||
|
val metadataPlugin: SerializationDescriptorSerializerPlugin? = null
|
||||||
|
) : IrGenerationExtension {
|
||||||
override fun generate(
|
override fun generate(
|
||||||
moduleFragment: IrModuleFragment,
|
moduleFragment: IrModuleFragment,
|
||||||
pluginContext: IrPluginContext
|
pluginContext: IrPluginContext
|
||||||
@@ -60,4 +68,4 @@ open class SerializationLoweringExtension @JvmOverloads constructor(val metadata
|
|||||||
for (file in moduleFragment.files)
|
for (file in moduleFragment.files)
|
||||||
serializerClassLowering.runOnFileInOrder(file)
|
serializerClassLowering.runOnFileInOrder(file)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-7
@@ -588,13 +588,12 @@ object KSerializerDescriptorResolver {
|
|||||||
) {
|
) {
|
||||||
if (isSerialInfoImpl(thisDescriptor)) {
|
if (isSerialInfoImpl(thisDescriptor)) {
|
||||||
result.add(
|
result.add(
|
||||||
fromSupertypes[0].copy(
|
fromSupertypes.first().newCopyBuilder().apply {
|
||||||
thisDescriptor,
|
setOwner(thisDescriptor)
|
||||||
Modality.FINAL,
|
setModality(Modality.FINAL)
|
||||||
DescriptorVisibilities.PUBLIC,
|
setKind(CallableMemberDescriptor.Kind.SYNTHESIZED)
|
||||||
CallableMemberDescriptor.Kind.SYNTHESIZED,
|
setDispatchReceiverParameter(thisDescriptor.thisAsReceiverParameter)
|
||||||
true
|
}.build()!!
|
||||||
) as PropertyDescriptor
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user