diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ExpressionCodegen.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ExpressionCodegen.kt index ca76c49e925..43cc074f9df 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ExpressionCodegen.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ExpressionCodegen.kt @@ -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) 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 a891e6a5b74..fe117dc643b 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 @@ -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 diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerialInfoImplJvmIrGenerator.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerialInfoImplJvmIrGenerator.kt new file mode 100644 index 00000000000..3fec3b27092 --- /dev/null +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerialInfoImplJvmIrGenerator.kt @@ -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 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>. +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() + 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() + } + } +} diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerIrGenerator.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerIrGenerator.kt index e90e2f6fbda..5428fd0c0ed 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerIrGenerator.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerIrGenerator.kt @@ -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) } } diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/extensions/SerializationLoweringExtension.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/extensions/SerializationLoweringExtension.kt index 7453383e170..89f3b8e3025 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/extensions/SerializationLoweringExtension.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/extensions/SerializationLoweringExtension.kt @@ -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) } -} \ No newline at end of file +} diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/KSerializerDescriptorResolver.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/KSerializerDescriptorResolver.kt index 353bc1875b8..47698a6facf 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/KSerializerDescriptorResolver.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/KSerializerDescriptorResolver.kt @@ -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()!! ) } }