kotlinx.serialization: Fix interface delegation when delegate is initialized in a function (#4727)

Fixes https://github.com/Kotlin/kotlinx.serialization/issues/241
This commit is contained in:
770grappenmaker
2022-03-24 14:11:36 +01:00
committed by GitHub
parent 7e491edf95
commit 64f0240d9f
7 changed files with 370 additions and 10 deletions
@@ -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<IrField>()
.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
@@ -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()
@@ -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");
@@ -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");
@@ -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 <clinit>() {
NEW (Test$$serializer)
DUP
INVOKESPECIAL (Test$$serializer, <init>, ()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, <init>, (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 <init>() {
LABEL (L0)
LINENUMBER (12)
ALOAD (0)
INVOKESPECIAL (java/lang/Object, <init>, ()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 <init>() {
LABEL (L0)
LINENUMBER (12)
ALOAD (0)
INVOKESPECIAL (java/lang/Object, <init>, ()V)
RETURN
LABEL (L1)
}
public void <init>(kotlin.jvm.internal.DefaultConstructorMarker $constructor_marker) {
LABEL (L0)
ALOAD (0)
INVOKESPECIAL (Test$Companion, <init>, ()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 <clinit>() {
NEW (Test$Companion)
DUP
ACONST_NULL
INVOKESPECIAL (Test$Companion, <init>, (Lkotlin/jvm/internal/DefaultConstructorMarker;)V)
PUTSTATIC (Test, Companion, LTest$Companion;)
RETURN
}
public void <init>() {
LABEL (L0)
LINENUMBER (12)
ALOAD (0)
INVOKESPECIAL (java/lang/Object, <init>, ()V)
LABEL (L1)
LINENUMBER (13)
ALOAD (0)
INVOKESTATIC (DelegatedKt, generateImpl, ()LA;)
PUTFIELD (Test, $$delegate_0, LA;)
RETURN
LABEL (L2)
}
public void <init>(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, <init>, ()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)
}
@@ -0,0 +1,13 @@
// CURIOUS_ABOUT <init>, <clinit>
// WITH_STDLIB
import kotlinx.serialization.*
fun interface A {
fun getText(): String
}
fun generateImpl() = A { "Hello, world!" }
@Serializable
class Test : A by generateImpl()
@@ -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 <clinit>() {
NEW (DelegatedKt$generateImpl$1)
DUP
INVOKESPECIAL (DelegatedKt$generateImpl$1, <init>, ()V)
PUTSTATIC (DelegatedKt$generateImpl$1, INSTANCE, LDelegatedKt$generateImpl$1;)
RETURN
}
void <init>() {
ALOAD (0)
INVOKESPECIAL (java/lang/Object, <init>, ()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 <clinit>() {
LABEL (L0)
LINENUMBER (13)
NEW (Test$$serializer)
DUP
INVOKESPECIAL (Test$$serializer, <init>, ()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, <init>, (Ljava/lang/String;Lkotlinx/serialization/internal/GeneratedSerializer;I)V)
ASTORE (0)
ALOAD (0)
PUTSTATIC (Test$$serializer, $$serialDesc, Lkotlinx/serialization/descriptors/SerialDescriptor;)
RETURN
}
private void <init>() {
LABEL (L0)
LINENUMBER (13)
ALOAD (0)
INVOKESPECIAL (java/lang/Object, <init>, ()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 <init>() {
LABEL (L0)
LINENUMBER (13)
ALOAD (0)
INVOKESPECIAL (java/lang/Object, <init>, ()V)
RETURN
LABEL (L1)
}
public void <init>(kotlin.jvm.internal.DefaultConstructorMarker $constructor_marker) {
LABEL (L0)
LINENUMBER (13)
ALOAD (0)
INVOKESPECIAL (Test$Companion, <init>, ()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 <clinit>() {
NEW (Test$Companion)
DUP
ACONST_NULL
INVOKESPECIAL (Test$Companion, <init>, (Lkotlin/jvm/internal/DefaultConstructorMarker;)V)
PUTSTATIC (Test, Companion, LTest$Companion;)
RETURN
}
public void <init>() {
LABEL (L0)
LINENUMBER (13)
ALOAD (0)
INVOKESPECIAL (java/lang/Object, <init>, ()V)
ALOAD (0)
LABEL (L1)
LINENUMBER (13)
INVOKESTATIC (DelegatedKt, generateImpl, ()LA;)
PUTFIELD (Test, $$delegate_0, LA;)
RETURN
LABEL (L2)
}
public void <init>(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, <init>, ()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)
}