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:
+28
-7
@@ -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
|
||||
|
||||
+22
-3
@@ -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()
|
||||
|
||||
+5
@@ -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");
|
||||
|
||||
+5
@@ -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");
|
||||
|
||||
Vendored
+139
@@ -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)
|
||||
}
|
||||
+13
@@ -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()
|
||||
+158
@@ -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)
|
||||
}
|
||||
Reference in New Issue
Block a user