Call superclass deserialization constructor if superclass is also internal serializable or no-arg ctor of non-serializable class on Native and JS
This commit is contained in:
+4
-4
@@ -17,10 +17,7 @@ import org.jetbrains.kotlin.ir.declarations.impl.IrTypeParameterImpl
|
|||||||
import org.jetbrains.kotlin.ir.declarations.impl.IrValueParameterImpl
|
import org.jetbrains.kotlin.ir.declarations.impl.IrValueParameterImpl
|
||||||
import org.jetbrains.kotlin.ir.expressions.*
|
import org.jetbrains.kotlin.ir.expressions.*
|
||||||
import org.jetbrains.kotlin.ir.expressions.impl.*
|
import org.jetbrains.kotlin.ir.expressions.impl.*
|
||||||
import org.jetbrains.kotlin.ir.symbols.IrFieldSymbol
|
import org.jetbrains.kotlin.ir.symbols.*
|
||||||
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
|
|
||||||
import org.jetbrains.kotlin.ir.symbols.IrValueParameterSymbol
|
|
||||||
import org.jetbrains.kotlin.ir.symbols.IrValueSymbol
|
|
||||||
import org.jetbrains.kotlin.ir.types.IrType
|
import org.jetbrains.kotlin.ir.types.IrType
|
||||||
import org.jetbrains.kotlin.ir.types.toKotlinType
|
import org.jetbrains.kotlin.ir.types.toKotlinType
|
||||||
import org.jetbrains.kotlin.ir.types.typeWith
|
import org.jetbrains.kotlin.ir.types.typeWith
|
||||||
@@ -489,4 +486,7 @@ interface IrBuilderExtension {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun IrClass.serializableSyntheticConstructor(): IrConstructorSymbol =
|
||||||
|
this.constructors.single { it.origin == SERIALIZABLE_PLUGIN_ORIGIN }.symbol
|
||||||
}
|
}
|
||||||
|
|||||||
+57
-8
@@ -5,12 +5,15 @@ import org.jetbrains.kotlin.backend.common.lower.irThrow
|
|||||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||||
import org.jetbrains.kotlin.codegen.CompilationException
|
import org.jetbrains.kotlin.codegen.CompilationException
|
||||||
import org.jetbrains.kotlin.descriptors.ClassConstructorDescriptor
|
import org.jetbrains.kotlin.descriptors.ClassConstructorDescriptor
|
||||||
|
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||||
import org.jetbrains.kotlin.ir.builders.*
|
import org.jetbrains.kotlin.ir.builders.*
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrAnonymousInitializer
|
import org.jetbrains.kotlin.ir.declarations.IrAnonymousInitializer
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrProperty
|
import org.jetbrains.kotlin.ir.declarations.IrProperty
|
||||||
|
import org.jetbrains.kotlin.ir.declarations.IrValueParameter
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.impl.IrDelegatingConstructorCallImpl
|
||||||
import org.jetbrains.kotlin.ir.util.SymbolTable
|
import org.jetbrains.kotlin.ir.util.SymbolTable
|
||||||
import org.jetbrains.kotlin.ir.util.TypeTranslator
|
import org.jetbrains.kotlin.ir.util.TypeTranslator
|
||||||
import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi
|
import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi
|
||||||
@@ -43,15 +46,23 @@ class SerializableIrGenerator(
|
|||||||
val exceptionCtorRef = compilerContext.externalSymbols.referenceConstructor(exceptionCtor)
|
val exceptionCtorRef = compilerContext.externalSymbols.referenceConstructor(exceptionCtor)
|
||||||
val exceptionType = exceptionCtorRef.owner.returnType
|
val exceptionType = exceptionCtorRef.owner.returnType
|
||||||
|
|
||||||
val thiz = irClass.thisReceiver!!
|
val serializableProperties = properties.serializableProperties
|
||||||
if (KotlinBuiltIns.isAny(irClass.descriptor.getSuperClassOrAny()))
|
val seenVarsOffset = serializableProperties.bitMaskSlotCount()
|
||||||
generateAnySuperConstructorCall(toBuilder = this@contributeConstructor)
|
|
||||||
else
|
|
||||||
TODO("Serializable classes with inheritance")
|
|
||||||
val seenVarsOffset = properties.serializableProperties.bitMaskSlotCount()
|
|
||||||
val seenVars = (0 until seenVarsOffset).map { ctor.valueParameters[it] }
|
val seenVars = (0 until seenVarsOffset).map { ctor.valueParameters[it] }
|
||||||
|
|
||||||
for ((index, prop) in properties.serializableProperties.withIndex()) {
|
val thiz = irClass.thisReceiver!!
|
||||||
|
val superClass = irClass.descriptor.getSuperClassOrAny()
|
||||||
|
var startPropOffset: Int = 0
|
||||||
|
when {
|
||||||
|
KotlinBuiltIns.isAny(superClass) -> generateAnySuperConstructorCall(toBuilder = this@contributeConstructor)
|
||||||
|
superClass.isInternalSerializable -> {
|
||||||
|
startPropOffset = generateSuperSerializableCall(superClass, ctor.valueParameters, seenVarsOffset)
|
||||||
|
}
|
||||||
|
else -> generateSuperNonSerializableCall(superClass)
|
||||||
|
}
|
||||||
|
|
||||||
|
for (index in startPropOffset until serializableProperties.size) {
|
||||||
|
val prop = serializableProperties[index]
|
||||||
val paramRef = ctor.valueParameters[index + seenVarsOffset]
|
val paramRef = ctor.valueParameters[index + seenVarsOffset]
|
||||||
// assign this.a = a in else branch
|
// assign this.a = a in else branch
|
||||||
val assignParamExpr = irSetField(irGet(thiz), prop.irField, irGet(paramRef))
|
val assignParamExpr = irSetField(irGet(thiz), prop.irField, irGet(paramRef))
|
||||||
@@ -78,7 +89,7 @@ class SerializableIrGenerator(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// remaining initializers of variables
|
// remaining initializers of variables
|
||||||
val serialDescs = properties.serializableProperties.map { it.descriptor }.toSet()
|
val serialDescs = serializableProperties.map { it.descriptor }.toSet()
|
||||||
irClass.declarations.asSequence()
|
irClass.declarations.asSequence()
|
||||||
.filterIsInstance<IrProperty>()
|
.filterIsInstance<IrProperty>()
|
||||||
.filter { it.descriptor !in serialDescs }
|
.filter { it.descriptor !in serialDescs }
|
||||||
@@ -94,6 +105,44 @@ class SerializableIrGenerator(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun IrBlockBodyBuilder.generateSuperNonSerializableCall(superClass: ClassDescriptor) {
|
||||||
|
val suitableCtor = superClass.constructors.singleOrNull { it.valueParameters.size == 0 }
|
||||||
|
?: throw IllegalArgumentException("Non-serializable parent of serializable $serializableDescriptor must have no arg constructor")
|
||||||
|
val ctorRef = compilerContext.externalSymbols.referenceConstructor(suitableCtor)
|
||||||
|
+IrDelegatingConstructorCallImpl(
|
||||||
|
startOffset,
|
||||||
|
endOffset,
|
||||||
|
compilerContext.irBuiltIns.unitType,
|
||||||
|
ctorRef,
|
||||||
|
suitableCtor
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns offset in serializable properties array
|
||||||
|
private fun IrBlockBodyBuilder.generateSuperSerializableCall(
|
||||||
|
superClass: ClassDescriptor,
|
||||||
|
allValueParameters: List<IrValueParameter>,
|
||||||
|
propertiesStart: Int
|
||||||
|
): Int {
|
||||||
|
check(superClass.isInternalSerializable)
|
||||||
|
val superCtorRef = compilerContext.externalSymbols.referenceClass(superClass).owner.serializableSyntheticConstructor()
|
||||||
|
val superProperties = SerializableProperties(superClass, bindingContext).serializableProperties
|
||||||
|
val superSlots = superProperties.bitMaskSlotCount()
|
||||||
|
val arguments = allValueParameters.subList(0, superSlots) +
|
||||||
|
allValueParameters.subList(propertiesStart, propertiesStart + superProperties.size) +
|
||||||
|
allValueParameters.last() // SerializationConstructorMarker
|
||||||
|
val call = IrDelegatingConstructorCallImpl(
|
||||||
|
startOffset,
|
||||||
|
endOffset,
|
||||||
|
compilerContext.irBuiltIns.unitType,
|
||||||
|
superCtorRef,
|
||||||
|
superCtorRef.owner.descriptor
|
||||||
|
)
|
||||||
|
arguments.forEachIndexed { index, parameter -> call.putValueArgument(index, irGet(parameter)) }
|
||||||
|
+call
|
||||||
|
return superProperties.size
|
||||||
|
}
|
||||||
|
|
||||||
override fun generateWriteSelfMethod(methodDescriptor: FunctionDescriptor) {
|
override fun generateWriteSelfMethod(methodDescriptor: FunctionDescriptor) {
|
||||||
// no-op
|
// no-op
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-3
@@ -396,10 +396,8 @@ class SerializerIrGenerator(val irClass: IrClass, override val compilerContext:
|
|||||||
// todo: set properties in external deserialization
|
// todo: set properties in external deserialization
|
||||||
var args: List<IrExpression> = localProps.map { it.get() }
|
var args: List<IrExpression> = localProps.map { it.get() }
|
||||||
val ctor: IrConstructorSymbol = if (serializableDescriptor.isInternalSerializable) {
|
val ctor: IrConstructorSymbol = if (serializableDescriptor.isInternalSerializable) {
|
||||||
val ctorDesc = serializableIrClass
|
|
||||||
.constructors.single { it.origin == SERIALIZABLE_PLUGIN_ORIGIN }
|
|
||||||
args = bitMasks.map { irGet(it) } + args + irNull()
|
args = bitMasks.map { irGet(it) } + args + irNull()
|
||||||
ctorDesc.symbol
|
serializableIrClass.serializableSyntheticConstructor()
|
||||||
} else {
|
} else {
|
||||||
compilerContext.externalSymbols.referenceConstructor(serializableDescriptor.unsubstitutedPrimaryConstructor!!)
|
compilerContext.externalSymbols.referenceConstructor(serializableDescriptor.unsubstitutedPrimaryConstructor!!)
|
||||||
}
|
}
|
||||||
|
|||||||
+58
-5
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlinx.serialization.compiler.backend.js
|
package org.jetbrains.kotlinx.serialization.compiler.backend.js
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||||
import org.jetbrains.kotlin.codegen.CompilationException
|
import org.jetbrains.kotlin.codegen.CompilationException
|
||||||
import org.jetbrains.kotlin.descriptors.ClassConstructorDescriptor
|
import org.jetbrains.kotlin.descriptors.ClassConstructorDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||||
@@ -27,9 +28,11 @@ import org.jetbrains.kotlin.js.translate.context.Namer
|
|||||||
import org.jetbrains.kotlin.js.translate.context.TranslationContext
|
import org.jetbrains.kotlin.js.translate.context.TranslationContext
|
||||||
import org.jetbrains.kotlin.js.translate.declaration.DeclarationBodyVisitor
|
import org.jetbrains.kotlin.js.translate.declaration.DeclarationBodyVisitor
|
||||||
import org.jetbrains.kotlin.js.translate.general.Translation
|
import org.jetbrains.kotlin.js.translate.general.Translation
|
||||||
|
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils
|
||||||
import org.jetbrains.kotlin.js.translate.utils.TranslationUtils
|
import org.jetbrains.kotlin.js.translate.utils.TranslationUtils
|
||||||
import org.jetbrains.kotlin.psi.KtExpression
|
import org.jetbrains.kotlin.psi.KtExpression
|
||||||
import org.jetbrains.kotlin.psi.KtPureClassOrObject
|
import org.jetbrains.kotlin.psi.KtPureClassOrObject
|
||||||
|
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassOrAny
|
||||||
import org.jetbrains.kotlinx.serialization.compiler.backend.common.SerializableCodegen
|
import org.jetbrains.kotlinx.serialization.compiler.backend.common.SerializableCodegen
|
||||||
import org.jetbrains.kotlinx.serialization.compiler.backend.common.anonymousInitializers
|
import org.jetbrains.kotlinx.serialization.compiler.backend.common.anonymousInitializers
|
||||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.*
|
import org.jetbrains.kotlinx.serialization.compiler.resolve.*
|
||||||
@@ -53,15 +56,38 @@ class SerializableJsTranslator(
|
|||||||
@Suppress("NAME_SHADOWING")
|
@Suppress("NAME_SHADOWING")
|
||||||
val context = context.innerContextWithAliased(serializableDescriptor.thisAsReceiverParameter, thiz)
|
val context = context.innerContextWithAliased(serializableDescriptor.thisAsReceiverParameter, thiz)
|
||||||
|
|
||||||
|
// use serializationConstructorMarker for passing "this" from inheritors to base class
|
||||||
|
val markerAsThis = jsFun.parameters.last().name.makeRef()
|
||||||
|
|
||||||
+JsVars(
|
+JsVars(
|
||||||
JsVars.JsVar(
|
JsVars.JsVar(
|
||||||
thiz.name,
|
thiz.name,
|
||||||
Namer.createObjectWithPrototypeFrom(context.getInnerNameForDescriptor(serializableDescriptor).makeRef())
|
JsAstUtils.or(
|
||||||
|
markerAsThis,
|
||||||
|
Namer.createObjectWithPrototypeFrom(context.getInnerNameForDescriptor(serializableDescriptor).makeRef())
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
val seenVarsOffset = properties.serializableProperties.bitMaskSlotCount()
|
val serializableProperties = properties.serializableProperties
|
||||||
|
val seenVarsOffset = serializableProperties.bitMaskSlotCount()
|
||||||
val seenVars = (0 until seenVarsOffset).map { jsFun.parameters[it].name.makeRef() }
|
val seenVars = (0 until seenVarsOffset).map { jsFun.parameters[it].name.makeRef() }
|
||||||
for ((index, prop) in properties.serializableProperties.withIndex()) {
|
val superClass = serializableDescriptor.getSuperClassOrAny()
|
||||||
|
var startPropOffset: Int = 0
|
||||||
|
when {
|
||||||
|
KotlinBuiltIns.isAny(superClass) -> { /* no=op */ }
|
||||||
|
superClass.isInternalSerializable -> {
|
||||||
|
startPropOffset = generateSuperSerializableCall(
|
||||||
|
superClass,
|
||||||
|
jsFun.parameters.map { it.name.makeRef() },
|
||||||
|
thiz,
|
||||||
|
seenVarsOffset
|
||||||
|
)
|
||||||
|
}
|
||||||
|
else -> generateSuperNonSerializableCall(superClass, thiz)
|
||||||
|
}
|
||||||
|
|
||||||
|
for (index in startPropOffset until serializableProperties.size) {
|
||||||
|
val prop = serializableProperties[index]
|
||||||
val paramRef = jsFun.parameters[index + seenVarsOffset].name.makeRef()
|
val paramRef = jsFun.parameters[index + seenVarsOffset].name.makeRef()
|
||||||
// assign this.a = a in else branch
|
// assign this.a = a in else branch
|
||||||
val assignParamStmt = TranslationUtils.assignmentToBackingField(context, prop.descriptor, paramRef).makeStmt()
|
val assignParamStmt = TranslationUtils.assignmentToBackingField(context, prop.descriptor, paramRef).makeStmt()
|
||||||
@@ -79,7 +105,7 @@ class SerializableJsTranslator(
|
|||||||
}
|
}
|
||||||
|
|
||||||
//transient initializers and init blocks
|
//transient initializers and init blocks
|
||||||
val serialDescs = properties.serializableProperties.map { it.descriptor }
|
val serialDescs = serializableProperties.map { it.descriptor }
|
||||||
(initMap - serialDescs).forEach { (desc, expr) ->
|
(initMap - serialDescs).forEach { (desc, expr) ->
|
||||||
val e = requireNotNull(expr) { "transient without an initializer" }
|
val e = requireNotNull(expr) { "transient without an initializer" }
|
||||||
val initExpr = Translation.translateAsExpression(e, context)
|
val initExpr = Translation.translateAsExpression(e, context)
|
||||||
@@ -96,6 +122,33 @@ class SerializableJsTranslator(
|
|||||||
context.addDeclarationStatement(f.makeStmt())
|
context.addDeclarationStatement(f.makeStmt())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun JsBlockBuilder.generateSuperNonSerializableCall(superClass: ClassDescriptor, thisParameter: JsExpression) {
|
||||||
|
val suitableCtor = superClass.constructors.singleOrNull { it.valueParameters.size == 0 }
|
||||||
|
?: throw IllegalArgumentException("Non-serializable parent of serializable $serializableDescriptor must have no arg constructor")
|
||||||
|
if (suitableCtor.isPrimary) {
|
||||||
|
+JsInvocation(Namer.getFunctionCallRef(context.getInnerReference(superClass)), thisParameter).makeStmt()
|
||||||
|
} else {
|
||||||
|
+JsAstUtils.assignment(thisParameter, JsInvocation(context.getInnerReference(suitableCtor), thisParameter)).makeStmt()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun JsBlockBuilder.generateSuperSerializableCall(
|
||||||
|
superClass: ClassDescriptor,
|
||||||
|
parameters: List<JsExpression>,
|
||||||
|
thisParameter: JsExpression,
|
||||||
|
propertiesStart: Int
|
||||||
|
): Int {
|
||||||
|
val constrDesc = KSerializerDescriptorResolver.createLoadConstructorDescriptor(superClass, context.bindingContext())
|
||||||
|
val constrRef = context.getInnerNameForDescriptor(constrDesc).makeRef()
|
||||||
|
val superProperties = SerializableProperties(superClass, bindingContext).serializableProperties
|
||||||
|
val superSlots = superProperties.bitMaskSlotCount()
|
||||||
|
val arguments = parameters.subList(0, superSlots) +
|
||||||
|
parameters.subList(propertiesStart, propertiesStart + superProperties.size) +
|
||||||
|
thisParameter // SerializationConstructorMarker
|
||||||
|
+JsAstUtils.assignment(thisParameter, JsInvocation(constrRef, arguments)).makeStmt()
|
||||||
|
return superProperties.size
|
||||||
|
}
|
||||||
|
|
||||||
override fun generateWriteSelfMethod(methodDescriptor: FunctionDescriptor) {
|
override fun generateWriteSelfMethod(methodDescriptor: FunctionDescriptor) {
|
||||||
// no-op yet
|
// no-op yet
|
||||||
}
|
}
|
||||||
@@ -117,4 +170,4 @@ class SerializableJsTranslator(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user