diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializableIrGenerator.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializableIrGenerator.kt index e12f1fbf61a..6a81ee170ee 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializableIrGenerator.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializableIrGenerator.kt @@ -12,11 +12,14 @@ import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.ir.IrStatement import org.jetbrains.kotlin.ir.builders.* import org.jetbrains.kotlin.ir.declarations.* -import org.jetbrains.kotlin.ir.expressions.IrExpression -import org.jetbrains.kotlin.ir.expressions.IrExpressionBody +import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.impl.IrDelegatingConstructorCallImpl -import org.jetbrains.kotlin.ir.types.* +import org.jetbrains.kotlin.ir.types.IrSimpleType +import org.jetbrains.kotlin.ir.types.IrTypeProjection +import org.jetbrains.kotlin.ir.types.classOrNull +import org.jetbrains.kotlin.ir.types.isString import org.jetbrains.kotlin.ir.util.* +import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe @@ -24,7 +27,10 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.module import org.jetbrains.kotlin.util.OperatorNameConventions import org.jetbrains.kotlin.util.collectionUtils.filterIsInstanceAnd import org.jetbrains.kotlin.utils.getOrPutNullable -import org.jetbrains.kotlinx.serialization.compiler.backend.common.* +import org.jetbrains.kotlinx.serialization.compiler.backend.common.SerializableCodegen +import org.jetbrains.kotlinx.serialization.compiler.backend.common.findTypeSerializerOrContext +import org.jetbrains.kotlinx.serialization.compiler.backend.common.isStaticSerializable +import org.jetbrains.kotlinx.serialization.compiler.backend.common.serialName import org.jetbrains.kotlinx.serialization.compiler.diagnostic.serializableAnnotationIsUseless import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPluginContext import org.jetbrains.kotlinx.serialization.compiler.resolve.* @@ -169,6 +175,20 @@ class SerializableIrGenerator( statementsAfterSerializableProperty[prop.descriptor]?.forEach { +it } } + + // Handle function-intialized interface delegates + irClass.declarations + .filterIsInstance() + .filter { it.origin == IrDeclarationOrigin.DELEGATE } + .forEach { + val receiver = if (!it.isStatic) irGet(thiz) else null + +irSetField( + receiver, + it, + initializerAdapter(it.initializer!!), + IrStatementOrigin.INITIALIZE_FIELD + ) + } } private fun IrBlockBodyBuilder.getStaticSerialDescriptorExpr(): IrExpression { @@ -263,8 +283,8 @@ class SerializableIrGenerator( val superProperties = bindingContext.serializablePropertiesFor(superClass.descriptor).serializableProperties val superSlots = superProperties.bitMaskSlotCount() val arguments = allValueParameters.subList(0, superSlots) + - allValueParameters.subList(propertiesStart, propertiesStart + superProperties.size) + - allValueParameters.last() // SerializationConstructorMarker + allValueParameters.subList(propertiesStart, propertiesStart + superProperties.size) + + allValueParameters.last() // SerializationConstructorMarker val call = IrDelegatingConstructorCallImpl.fromSymbolDescriptor( startOffset, endOffset, @@ -290,7 +310,8 @@ class SerializableIrGenerator( // Since writeSelf is a static method, we have to replace all references to this in property initializers val thisSymbol = irClass.thisReceiver!!.symbol - val initializerAdapter: (IrExpressionBody) -> IrExpression = createInitializerAdapter(irClass, propertyByParamReplacer, thisSymbol to { irGet(objectToSerialize) }) + val initializerAdapter: (IrExpressionBody) -> IrExpression = + createInitializerAdapter(irClass, propertyByParamReplacer, thisSymbol to { irGet(objectToSerialize) }) // Compute offset of properties in superclass var ignoreIndexTo = -1 diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializableCodegenImpl.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializableCodegenImpl.kt index f22f03bbe14..cde26effd94 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializableCodegenImpl.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializableCodegenImpl.kt @@ -12,6 +12,7 @@ import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.descriptors.PropertyDescriptor import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi import org.jetbrains.kotlin.lexer.KtTokens +import org.jetbrains.kotlin.psi.KtDelegatedSuperTypeEntry import org.jetbrains.kotlin.psi.KtExpression import org.jetbrains.kotlin.psi.KtParameter import org.jetbrains.kotlin.resolve.BindingContext @@ -42,9 +43,9 @@ class SerializableCodegenImpl( companion object { fun generateSerializableExtensions(codegen: ImplementationBodyCodegen) { val serializableClass = codegen.descriptor - if (serializableClass.isInternalSerializable) + if (serializableClass.isInternalSerializable) { SerializableCodegenImpl(codegen).generate() - else if (serializableClass.serializableAnnotationIsUseless) { + } else if (serializableClass.serializableAnnotationIsUseless) { throw CompilationException( "@Serializable annotation on $serializableClass would be ignored because it is impossible to serialize it automatically. " + "Provide serializer manually via e.g. companion object", null, serializableClass.findPsi() @@ -228,7 +229,7 @@ class SerializableCodegenImpl( } // these properties required to be manually invoked, because they are not in serializableProperties - val serializedProps = properties.serializableProperties.map { it.descriptor } + val serializedProps = properties.serializableProperties.map { it.descriptor }.toSet() (descToProps - serializedProps) .filter { classCodegen.shouldInitializeProperty(it.value) } @@ -236,6 +237,24 @@ class SerializableCodegenImpl( (paramsToProps - serializedProps) .forEach { (t, u) -> exprCodegen.genInitParam(t, u) } + // Initialize delegates + var delegate = 0 + for (specifier in classCodegen.myClass.superTypeListEntries) { + if (specifier is KtDelegatedSuperTypeEntry) { + val expr = specifier.delegateExpression!! + + load(0, thisAsmType) + val stackValue = exprCodegen.gen(expr) + stackValue.put(exprCodegen.v) + + putfield( + thisAsmType.internalName, + "\$\$delegate_${delegate++}", + stackValue.type.descriptor + ) + } + } + // init blocks // todo: proper order with other initializers? classCodegen.myClass.anonymousInitializers() diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/test/org/jetbrains/kotlinx/serialization/SerializationIrBytecodeListingTestGenerated.java b/plugins/kotlin-serialization/kotlin-serialization-compiler/test/org/jetbrains/kotlinx/serialization/SerializationIrBytecodeListingTestGenerated.java index 984902f504d..9d1ff8ab24d 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/test/org/jetbrains/kotlinx/serialization/SerializationIrBytecodeListingTestGenerated.java +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/test/org/jetbrains/kotlinx/serialization/SerializationIrBytecodeListingTestGenerated.java @@ -34,6 +34,11 @@ public class SerializationIrBytecodeListingTestGenerated extends AbstractSeriali runTest("plugins/kotlin-serialization/kotlin-serialization-compiler/testData/codegen/Basic.kt"); } + @TestMetadata("Delegated.kt") + public void testDelegated() throws Exception { + runTest("plugins/kotlin-serialization/kotlin-serialization-compiler/testData/codegen/Delegated.kt"); + } + @TestMetadata("Sealed.kt") public void testSealed() throws Exception { runTest("plugins/kotlin-serialization/kotlin-serialization-compiler/testData/codegen/Sealed.kt"); diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/test/org/jetbrains/kotlinx/serialization/SerializationPluginBytecodeListingTestGenerated.java b/plugins/kotlin-serialization/kotlin-serialization-compiler/test/org/jetbrains/kotlinx/serialization/SerializationPluginBytecodeListingTestGenerated.java index 1defccadf12..8be9db753a9 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/test/org/jetbrains/kotlinx/serialization/SerializationPluginBytecodeListingTestGenerated.java +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/test/org/jetbrains/kotlinx/serialization/SerializationPluginBytecodeListingTestGenerated.java @@ -34,6 +34,11 @@ public class SerializationPluginBytecodeListingTestGenerated extends AbstractSer runTest("plugins/kotlin-serialization/kotlin-serialization-compiler/testData/codegen/Basic.kt"); } + @TestMetadata("Delegated.kt") + public void testDelegated() throws Exception { + runTest("plugins/kotlin-serialization/kotlin-serialization-compiler/testData/codegen/Delegated.kt"); + } + @TestMetadata("Sealed.kt") public void testSealed() throws Exception { runTest("plugins/kotlin-serialization/kotlin-serialization-compiler/testData/codegen/Sealed.kt"); diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/codegen/Delegated.ir.txt b/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/codegen/Delegated.ir.txt new file mode 100644 index 00000000000..c29c2c1b47c --- /dev/null +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/codegen/Delegated.ir.txt @@ -0,0 +1,139 @@ +public abstract interface A : java/lang/Object { + public abstract java.lang.String getText() +} + +public final class DelegatedKt : java/lang/Object { + public final static A generateImpl() + + private final static java.lang.String generateImpl$lambda-0() +} + +public final class Test$$serializer : java/lang/Object, kotlinx/serialization/internal/GeneratedSerializer { + public final static Test$$serializer INSTANCE + + public final static kotlinx.serialization.descriptors.SerialDescriptor descriptor + + static void () { + NEW (Test$$serializer) + DUP + INVOKESPECIAL (Test$$serializer, , ()V) + PUTSTATIC (Test$$serializer, INSTANCE, LTest$$serializer;) + LABEL (L0) + LINENUMBER (12) + NEW (kotlinx/serialization/internal/PluginGeneratedSerialDescriptor) + DUP + LDC (Test) + GETSTATIC (Test$$serializer, INSTANCE, LTest$$serializer;) + CHECKCAST (kotlinx/serialization/internal/GeneratedSerializer) + ICONST_0 + INVOKESPECIAL (kotlinx/serialization/internal/PluginGeneratedSerialDescriptor, , (Ljava/lang/String;Lkotlinx/serialization/internal/GeneratedSerializer;I)V) + CHECKCAST (kotlinx/serialization/descriptors/SerialDescriptor) + PUTSTATIC (Test$$serializer, descriptor, Lkotlinx/serialization/descriptors/SerialDescriptor;) + LABEL (L1) + LINENUMBER (13) + RETURN + } + + private void () { + LABEL (L0) + LINENUMBER (12) + ALOAD (0) + INVOKESPECIAL (java/lang/Object, , ()V) + RETURN + LABEL (L1) + } + + public kotlinx.serialization.KSerializer[] childSerializers() + + public Test deserialize(kotlinx.serialization.encoding.Decoder decoder) + + public java.lang.Object deserialize(kotlinx.serialization.encoding.Decoder decoder) + + public kotlinx.serialization.descriptors.SerialDescriptor getDescriptor() + + public void serialize(kotlinx.serialization.encoding.Encoder encoder, Test value) + + public void serialize(kotlinx.serialization.encoding.Encoder encoder, java.lang.Object value) + + public kotlinx.serialization.KSerializer[] typeParametersSerializers() +} + +public final class Test$Companion : java/lang/Object { + private void () { + LABEL (L0) + LINENUMBER (12) + ALOAD (0) + INVOKESPECIAL (java/lang/Object, , ()V) + RETURN + LABEL (L1) + } + + public void (kotlin.jvm.internal.DefaultConstructorMarker $constructor_marker) { + LABEL (L0) + ALOAD (0) + INVOKESPECIAL (Test$Companion, , ()V) + RETURN + LABEL (L1) + } + + public final kotlinx.serialization.KSerializer serializer() +} + +public final class Test : java/lang/Object, A { + private final A $$delegate_0 + + public final static Test$Companion Companion + + static void () { + NEW (Test$Companion) + DUP + ACONST_NULL + INVOKESPECIAL (Test$Companion, , (Lkotlin/jvm/internal/DefaultConstructorMarker;)V) + PUTSTATIC (Test, Companion, LTest$Companion;) + RETURN + } + + public void () { + LABEL (L0) + LINENUMBER (12) + ALOAD (0) + INVOKESPECIAL (java/lang/Object, , ()V) + LABEL (L1) + LINENUMBER (13) + ALOAD (0) + INVOKESTATIC (DelegatedKt, generateImpl, ()LA;) + PUTFIELD (Test, $$delegate_0, LA;) + RETURN + LABEL (L2) + } + + public void (int seen1, kotlinx.serialization.internal.SerializationConstructorMarker serializationConstructorMarker) { + LABEL (L0) + LINENUMBER (12) + ICONST_0 + ILOAD (1) + IAND + IFEQ (L1) + ILOAD (1) + ICONST_0 + GETSTATIC (Test$$serializer, INSTANCE, LTest$$serializer;) + INVOKEVIRTUAL (Test$$serializer, getDescriptor, ()Lkotlinx/serialization/descriptors/SerialDescriptor;) + INVOKESTATIC (kotlinx/serialization/internal/PluginExceptionsKt, throwMissingFieldException, (IILkotlinx/serialization/descriptors/SerialDescriptor;)V) + LABEL (L1) + ALOAD (0) + INVOKESPECIAL (java/lang/Object, , ()V) + ALOAD (0) + LABEL (L2) + LINENUMBER (13) + INVOKESTATIC (DelegatedKt, generateImpl, ()LA;) + LABEL (L3) + LINENUMBER (12) + PUTFIELD (Test, $$delegate_0, LA;) + RETURN + LABEL (L4) + } + + public java.lang.String getText() + + public final static void write$Self(Test self, kotlinx.serialization.encoding.CompositeEncoder output, kotlinx.serialization.descriptors.SerialDescriptor serialDesc) +} diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/codegen/Delegated.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/codegen/Delegated.kt new file mode 100644 index 00000000000..59b92011fc4 --- /dev/null +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/codegen/Delegated.kt @@ -0,0 +1,13 @@ +// CURIOUS_ABOUT , +// WITH_STDLIB + +import kotlinx.serialization.* + +fun interface A { + fun getText(): String +} + +fun generateImpl() = A { "Hello, world!" } + +@Serializable +class Test : A by generateImpl() \ No newline at end of file diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/codegen/Delegated.txt b/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/codegen/Delegated.txt new file mode 100644 index 00000000000..63e483e2f00 --- /dev/null +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/codegen/Delegated.txt @@ -0,0 +1,158 @@ +public abstract interface A : java/lang/Object { + public abstract java.lang.String getText() +} + +final class DelegatedKt$generateImpl$1 : java/lang/Object, A { + public final static DelegatedKt$generateImpl$1 INSTANCE + + static void () { + NEW (DelegatedKt$generateImpl$1) + DUP + INVOKESPECIAL (DelegatedKt$generateImpl$1, , ()V) + PUTSTATIC (DelegatedKt$generateImpl$1, INSTANCE, LDelegatedKt$generateImpl$1;) + RETURN + } + + void () { + ALOAD (0) + INVOKESPECIAL (java/lang/Object, , ()V) + RETURN + } + + public final java.lang.String getText() +} + +public final class DelegatedKt : java/lang/Object { + public final static A generateImpl() +} + +public final class Test$$serializer : java/lang/Object, kotlinx/serialization/internal/GeneratedSerializer { + private final static kotlinx.serialization.descriptors.SerialDescriptor $$serialDesc + + public final static Test$$serializer INSTANCE + + static void () { + LABEL (L0) + LINENUMBER (13) + NEW (Test$$serializer) + DUP + INVOKESPECIAL (Test$$serializer, , ()V) + ASTORE (0) + ALOAD (0) + PUTSTATIC (Test$$serializer, INSTANCE, LTest$$serializer;) + NEW (kotlinx/serialization/internal/PluginGeneratedSerialDescriptor) + DUP + LDC (Test) + GETSTATIC (Test$$serializer, INSTANCE, LTest$$serializer;) + CHECKCAST (kotlinx/serialization/internal/GeneratedSerializer) + LDC (0) + INVOKESPECIAL (kotlinx/serialization/internal/PluginGeneratedSerialDescriptor, , (Ljava/lang/String;Lkotlinx/serialization/internal/GeneratedSerializer;I)V) + ASTORE (0) + ALOAD (0) + PUTSTATIC (Test$$serializer, $$serialDesc, Lkotlinx/serialization/descriptors/SerialDescriptor;) + RETURN + } + + private void () { + LABEL (L0) + LINENUMBER (13) + ALOAD (0) + INVOKESPECIAL (java/lang/Object, , ()V) + RETURN + LABEL (L1) + } + + public kotlinx.serialization.KSerializer[] childSerializers() + + public Test deserialize(kotlinx.serialization.encoding.Decoder decoder) + + public java.lang.Object deserialize(kotlinx.serialization.encoding.Decoder p0) + + public kotlinx.serialization.descriptors.SerialDescriptor getDescriptor() + + public void serialize(kotlinx.serialization.encoding.Encoder encoder, Test value) + + public void serialize(kotlinx.serialization.encoding.Encoder p0, java.lang.Object p1) + + public kotlinx.serialization.KSerializer[] typeParametersSerializers() +} + +public final class Test$Companion : java/lang/Object { + private void () { + LABEL (L0) + LINENUMBER (13) + ALOAD (0) + INVOKESPECIAL (java/lang/Object, , ()V) + RETURN + LABEL (L1) + } + + public void (kotlin.jvm.internal.DefaultConstructorMarker $constructor_marker) { + LABEL (L0) + LINENUMBER (13) + ALOAD (0) + INVOKESPECIAL (Test$Companion, , ()V) + RETURN + LABEL (L1) + } + + public final kotlinx.serialization.KSerializer serializer() +} + +public final class Test : java/lang/Object, A { + private final A $$delegate_0 + + public final static Test$Companion Companion + + static void () { + NEW (Test$Companion) + DUP + ACONST_NULL + INVOKESPECIAL (Test$Companion, , (Lkotlin/jvm/internal/DefaultConstructorMarker;)V) + PUTSTATIC (Test, Companion, LTest$Companion;) + RETURN + } + + public void () { + LABEL (L0) + LINENUMBER (13) + ALOAD (0) + INVOKESPECIAL (java/lang/Object, , ()V) + ALOAD (0) + LABEL (L1) + LINENUMBER (13) + INVOKESTATIC (DelegatedKt, generateImpl, ()LA;) + PUTFIELD (Test, $$delegate_0, LA;) + RETURN + LABEL (L2) + } + + public void (int seen1, kotlinx.serialization.internal.SerializationConstructorMarker serializationConstructorMarker) { + LABEL (L0) + ICONST_0 + DUP + ILOAD (1) + IAND + IF_ICMPEQ (L1) + ILOAD (1) + ICONST_0 + GETSTATIC (Test$$serializer, INSTANCE, LTest$$serializer;) + CHECKCAST (kotlinx/serialization/KSerializer) + INVOKEINTERFACE (kotlinx/serialization/KSerializer, getDescriptor, ()Lkotlinx/serialization/descriptors/SerialDescriptor;) + INVOKESTATIC (kotlinx/serialization/internal/PluginExceptionsKt, throwMissingFieldException, (IILkotlinx/serialization/descriptors/SerialDescriptor;)V) + LABEL (L1) + ALOAD (0) + INVOKESPECIAL (java/lang/Object, , ()V) + ALOAD (0) + LABEL (L2) + LINENUMBER (13) + INVOKESTATIC (DelegatedKt, generateImpl, ()LA;) + PUTFIELD (Test, $$delegate_0, LA;) + RETURN + LABEL (L3) + } + + public java.lang.String getText() + + public final static void write$Self(Test self, kotlinx.serialization.encoding.CompositeEncoder output, kotlinx.serialization.descriptors.SerialDescriptor serialDesc) +}