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.expressions.*
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.*
|
||||
import org.jetbrains.kotlin.ir.symbols.IrFieldSymbol
|
||||
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.symbols.*
|
||||
import org.jetbrains.kotlin.ir.types.IrType
|
||||
import org.jetbrains.kotlin.ir.types.toKotlinType
|
||||
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.codegen.CompilationException
|
||||
import org.jetbrains.kotlin.descriptors.ClassConstructorDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.ir.builders.*
|
||||
import org.jetbrains.kotlin.ir.declarations.IrAnonymousInitializer
|
||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||
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.impl.IrDelegatingConstructorCallImpl
|
||||
import org.jetbrains.kotlin.ir.util.SymbolTable
|
||||
import org.jetbrains.kotlin.ir.util.TypeTranslator
|
||||
import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi
|
||||
@@ -43,15 +46,23 @@ class SerializableIrGenerator(
|
||||
val exceptionCtorRef = compilerContext.externalSymbols.referenceConstructor(exceptionCtor)
|
||||
val exceptionType = exceptionCtorRef.owner.returnType
|
||||
|
||||
val thiz = irClass.thisReceiver!!
|
||||
if (KotlinBuiltIns.isAny(irClass.descriptor.getSuperClassOrAny()))
|
||||
generateAnySuperConstructorCall(toBuilder = this@contributeConstructor)
|
||||
else
|
||||
TODO("Serializable classes with inheritance")
|
||||
val seenVarsOffset = properties.serializableProperties.bitMaskSlotCount()
|
||||
val serializableProperties = properties.serializableProperties
|
||||
val seenVarsOffset = serializableProperties.bitMaskSlotCount()
|
||||
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]
|
||||
// assign this.a = a in else branch
|
||||
val assignParamExpr = irSetField(irGet(thiz), prop.irField, irGet(paramRef))
|
||||
@@ -78,7 +89,7 @@ class SerializableIrGenerator(
|
||||
}
|
||||
|
||||
// remaining initializers of variables
|
||||
val serialDescs = properties.serializableProperties.map { it.descriptor }.toSet()
|
||||
val serialDescs = serializableProperties.map { it.descriptor }.toSet()
|
||||
irClass.declarations.asSequence()
|
||||
.filterIsInstance<IrProperty>()
|
||||
.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) {
|
||||
// no-op
|
||||
}
|
||||
|
||||
+1
-3
@@ -396,10 +396,8 @@ class SerializerIrGenerator(val irClass: IrClass, override val compilerContext:
|
||||
// todo: set properties in external deserialization
|
||||
var args: List<IrExpression> = localProps.map { it.get() }
|
||||
val ctor: IrConstructorSymbol = if (serializableDescriptor.isInternalSerializable) {
|
||||
val ctorDesc = serializableIrClass
|
||||
.constructors.single { it.origin == SERIALIZABLE_PLUGIN_ORIGIN }
|
||||
args = bitMasks.map { irGet(it) } + args + irNull()
|
||||
ctorDesc.symbol
|
||||
serializableIrClass.serializableSyntheticConstructor()
|
||||
} else {
|
||||
compilerContext.externalSymbols.referenceConstructor(serializableDescriptor.unsubstitutedPrimaryConstructor!!)
|
||||
}
|
||||
|
||||
+58
-5
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.jetbrains.kotlinx.serialization.compiler.backend.js
|
||||
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.codegen.CompilationException
|
||||
import org.jetbrains.kotlin.descriptors.ClassConstructorDescriptor
|
||||
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.declaration.DeclarationBodyVisitor
|
||||
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.psi.KtExpression
|
||||
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.anonymousInitializers
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.*
|
||||
@@ -53,15 +56,38 @@ class SerializableJsTranslator(
|
||||
@Suppress("NAME_SHADOWING")
|
||||
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.JsVar(
|
||||
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() }
|
||||
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()
|
||||
// assign this.a = a in else branch
|
||||
val assignParamStmt = TranslationUtils.assignmentToBackingField(context, prop.descriptor, paramRef).makeStmt()
|
||||
@@ -79,7 +105,7 @@ class SerializableJsTranslator(
|
||||
}
|
||||
|
||||
//transient initializers and init blocks
|
||||
val serialDescs = properties.serializableProperties.map { it.descriptor }
|
||||
val serialDescs = serializableProperties.map { it.descriptor }
|
||||
(initMap - serialDescs).forEach { (desc, expr) ->
|
||||
val e = requireNotNull(expr) { "transient without an initializer" }
|
||||
val initExpr = Translation.translateAsExpression(e, context)
|
||||
@@ -96,6 +122,33 @@ class SerializableJsTranslator(
|
||||
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) {
|
||||
// no-op yet
|
||||
}
|
||||
@@ -117,4 +170,4 @@ class SerializableJsTranslator(
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user