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)
|
||||
}
|
||||
val info = BlockInfo()
|
||||
val body = irFunction.body!!
|
||||
val body = irFunction.body
|
||||
?: error("Function has no body: ${irFunction.render()}")
|
||||
|
||||
generateNonNullAssertions()
|
||||
generateFakeContinuationConstructorIfNeeded()
|
||||
val result = body.accept(this, info)
|
||||
|
||||
+5
-4
@@ -220,10 +220,10 @@ interface IrBuilderExtension {
|
||||
}
|
||||
|
||||
fun generateSimplePropertyWithBackingField(
|
||||
ownerSymbol: IrValueSymbol,
|
||||
propertyDescriptor: PropertyDescriptor,
|
||||
propertyParent: IrClass,
|
||||
declare: Boolean
|
||||
declare: Boolean,
|
||||
fieldName: Name = propertyDescriptor.name,
|
||||
): IrProperty {
|
||||
val irPropertySymbol = compilerContext.symbolTable.referenceProperty(propertyDescriptor)
|
||||
assert(irPropertySymbol.isBound || declare)
|
||||
@@ -241,7 +241,7 @@ interface IrBuilderExtension {
|
||||
}
|
||||
val irProperty = irPropertySymbol.owner
|
||||
|
||||
irProperty.backingField = generatePropertyBackingField(propertyDescriptor, irProperty).apply {
|
||||
irProperty.backingField = generatePropertyBackingField(propertyDescriptor, irProperty, fieldName).apply {
|
||||
parent = propertyParent
|
||||
correspondingPropertySymbol = irPropertySymbol
|
||||
}
|
||||
@@ -255,7 +255,8 @@ interface IrBuilderExtension {
|
||||
|
||||
private fun generatePropertyBackingField(
|
||||
descriptor: PropertyDescriptor,
|
||||
originProperty: IrProperty
|
||||
originProperty: IrProperty,
|
||||
name: Name,
|
||||
): IrField {
|
||||
val fieldSymbol = compilerContext.symbolTable.referenceField(descriptor)
|
||||
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.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
import org.jetbrains.kotlinx.serialization.compiler.backend.common.SerializerCodegen
|
||||
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.UNKNOWN_FIELD_EXC
|
||||
|
||||
// Is creating synthetic origin is a good idea or not?
|
||||
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(
|
||||
val irClass: IrClass,
|
||||
final override val compilerContext: SerializationPluginContext,
|
||||
@@ -69,12 +70,11 @@ open class SerializerIrGenerator(
|
||||
|
||||
// how to (auto)create backing field and getter/setter?
|
||||
compilerContext.symbolTable.withReferenceScope(irClass.descriptor) {
|
||||
|
||||
prop = generateSimplePropertyWithBackingField(thisAsReceiverParameter.symbol, desc, irClass, false)
|
||||
prop = generateSimplePropertyWithBackingField(desc, irClass, false)
|
||||
|
||||
// TODO: Do not use descriptors here
|
||||
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
|
||||
|
||||
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.IrPluginContext
|
||||
import org.jetbrains.kotlin.backend.common.runOnFilePostfix
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
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.acceptChildrenVoid
|
||||
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.SerializableIrGenerator
|
||||
import org.jetbrains.kotlinx.serialization.compiler.backend.ir.SerializerIrGenerator
|
||||
@@ -48,10 +50,16 @@ private class SerializerClassLowering(
|
||||
SerializableIrGenerator.generate(irClass, context, context.bindingContext)
|
||||
SerializerIrGenerator.generate(irClass, context, context.bindingContext, metadataPlugin)
|
||||
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(
|
||||
moduleFragment: IrModuleFragment,
|
||||
pluginContext: IrPluginContext
|
||||
@@ -60,4 +68,4 @@ open class SerializationLoweringExtension @JvmOverloads constructor(val metadata
|
||||
for (file in moduleFragment.files)
|
||||
serializerClassLowering.runOnFileInOrder(file)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+6
-7
@@ -588,13 +588,12 @@ object KSerializerDescriptorResolver {
|
||||
) {
|
||||
if (isSerialInfoImpl(thisDescriptor)) {
|
||||
result.add(
|
||||
fromSupertypes[0].copy(
|
||||
thisDescriptor,
|
||||
Modality.FINAL,
|
||||
DescriptorVisibilities.PUBLIC,
|
||||
CallableMemberDescriptor.Kind.SYNTHESIZED,
|
||||
true
|
||||
) as PropertyDescriptor
|
||||
fromSupertypes.first().newCopyBuilder().apply {
|
||||
setOwner(thisDescriptor)
|
||||
setModality(Modality.FINAL)
|
||||
setKind(CallableMemberDescriptor.Kind.SYNTHESIZED)
|
||||
setDispatchReceiverParameter(thisDescriptor.thisAsReceiverParameter)
|
||||
}.build()!!
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user