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:
Leonid Startsev
2019-01-21 17:55:11 +03:00
parent e02cfcb8ea
commit a6651fd6f9
4 changed files with 120 additions and 20 deletions
@@ -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
}
@@ -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
}
@@ -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!!)
}
@@ -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(
}
}
}
}
}