Support classes with more than 32 serializable properties
Via increasing count of int bit masks in synthetic constructor
This commit is contained in:
+1
-1
@@ -33,7 +33,7 @@ abstract class SerializerCodegen(
|
||||
val serializableDescriptor: ClassDescriptor = getSerializableClassDescriptorBySerializer(serializerDescriptor)!!
|
||||
protected val serialName: String = serializableDescriptor.annotations.serialNameValue ?: serializableDescriptor.fqNameUnsafe.asString()
|
||||
protected val properties = SerializableProperties(serializableDescriptor, bindingContext)
|
||||
protected val orderedProperties = properties.serializableProperties
|
||||
protected val serializableProperties = properties.serializableProperties
|
||||
|
||||
private fun checkSerializability() {
|
||||
check(properties.isExternallySerializable) {
|
||||
|
||||
+9
-7
@@ -19,11 +19,8 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassOrAny
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
import org.jetbrains.kotlinx.serialization.compiler.backend.common.SerializableCodegen
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.*
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.MISSING_FIELD_EXC
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.getClassFromSerializationPackage
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.hasCompanionObjectAsSerializer
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.hasSerializableAnnotationWithoutArgs
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.isInternalSerializable
|
||||
|
||||
class SerializableIrGenerator(
|
||||
val irClass: IrClass,
|
||||
@@ -51,10 +48,11 @@ class SerializableIrGenerator(
|
||||
generateAnySuperConstructorCall(toBuilder = this@contributeConstructor)
|
||||
else
|
||||
TODO("Serializable classes with inheritance")
|
||||
val seenVar = ctor.valueParameters[0]
|
||||
val seenVarsOffset = properties.serializableProperties.bitMaskSlotCount()
|
||||
val seenVars = (0 until seenVarsOffset).map { ctor.valueParameters[it] }
|
||||
|
||||
for ((index, prop) in properties.serializableProperties.withIndex()) {
|
||||
val paramRef = ctor.valueParameters[index + 1]
|
||||
val paramRef = ctor.valueParameters[index + seenVarsOffset]
|
||||
// assign this.a = a in else branch
|
||||
val assignParamExpr = irSetField(irGet(thiz), prop.irField, irGet(paramRef))
|
||||
|
||||
@@ -69,7 +67,11 @@ class SerializableIrGenerator(
|
||||
val propNotSeenTest =
|
||||
irEquals(
|
||||
irInt(0),
|
||||
irBinOp(OperatorNameConventions.AND, irGet(seenVar), irInt(1 shl (index % 32)))
|
||||
irBinOp(
|
||||
OperatorNameConventions.AND,
|
||||
irGet(seenVars[bitMaskSlotAt(index)]),
|
||||
irInt(1 shl (index % 32))
|
||||
)
|
||||
)
|
||||
|
||||
+irIfThenElse(compilerContext.irBuiltIns.unitType, propNotSeenTest, ifNotSeenExpr, assignParamExpr)
|
||||
|
||||
+8
-8
@@ -95,7 +95,7 @@ class SerializerIrGenerator(val irClass: IrClass, override val compilerContext:
|
||||
dispatchReceiver = irGet(localDesc)
|
||||
}.mapValueParameters { irString(fieldName) }
|
||||
|
||||
for (classProp in orderedProperties) {
|
||||
for (classProp in serializableProperties) {
|
||||
if (classProp.transient) continue
|
||||
+addFieldCall(classProp.name)
|
||||
// add property annotations
|
||||
@@ -163,7 +163,7 @@ class SerializerIrGenerator(val irClass: IrClass, override val compilerContext:
|
||||
}
|
||||
|
||||
override fun generateChildSerializersGetter(function: FunctionDescriptor) = irClass.contributeFunction(function) { irFun ->
|
||||
val allSerializers = orderedProperties.map { requireNotNull(
|
||||
val allSerializers = serializableProperties.map { requireNotNull(
|
||||
serializerTower(this@SerializerIrGenerator, it)) { "Property ${it.name} must have a serializer" }
|
||||
}
|
||||
|
||||
@@ -214,7 +214,7 @@ class SerializerIrGenerator(val irClass: IrClass, override val compilerContext:
|
||||
fun SerializableProperty.irGet(): IrGetField = irGetField(irGet(serialObjectSymbol), irField)
|
||||
|
||||
// internal serialization via virtual calls?
|
||||
for ((index, property) in orderedProperties.filter { !it.transient }.withIndex()) {
|
||||
for ((index, property) in serializableProperties.filter { !it.transient }.withIndex()) {
|
||||
// output.writeXxxElementValue(classDesc, index, value)
|
||||
val sti = getSerialTypeInfo(property)
|
||||
val innerSerial = serializerInstance(
|
||||
@@ -252,7 +252,7 @@ class SerializerIrGenerator(val irClass: IrClass, override val compilerContext:
|
||||
// if (obj.prop != DEFAULT_VALUE || output.shouldEncodeElementDefault(this.descriptor, i))
|
||||
// output.encodeIntElement(this.descriptor, i, obj.prop)
|
||||
val shouldEncodeFunc = kOutputClass.referenceMethod(CallingConventions.shouldEncodeDefault)
|
||||
val partA = irNotEquals(property.irGet(), fieldInitializer(property)!!) // todo: props w/o backing fields
|
||||
val partA = irNotEquals(property.irGet(), fieldInitializer(property)!!)
|
||||
val partB = irInvoke(irGet(localOutput), shouldEncodeFunc, irGet(localSerialDesc), irInt(index))
|
||||
// Ir infrastructure does not have dedicated symbol for ||, so
|
||||
// `a || b == if (a) true else b`, see org.jetbrains.kotlin.ir.builders.PrimitivesKt.oror
|
||||
@@ -305,12 +305,12 @@ class SerializerIrGenerator(val irClass: IrClass, override val compilerContext:
|
||||
// val readAll = irTemporaryVar(irBoolean(false), "readAll", parent = loadFunc)
|
||||
//
|
||||
// calculating bit mask vars
|
||||
val blocksCnt = orderedProperties.size / 32 + 1
|
||||
val blocksCnt = serializableProperties.bitMaskSlotCount()
|
||||
|
||||
// var bitMask0 = 0, bitMask1 = 0...
|
||||
val bitMasks = (0 until blocksCnt).map { irTemporaryVar(irInt(0), "bitMask$it") }
|
||||
// var local0 = null, local1 = null ...
|
||||
val localProps = orderedProperties.mapIndexed { i, prop ->
|
||||
val localProps = serializableProperties.mapIndexed { i, prop ->
|
||||
val (expr, type) = defaultValueAndType(prop)
|
||||
irTemporaryVar(
|
||||
expr,
|
||||
@@ -341,7 +341,7 @@ class SerializerIrGenerator(val irClass: IrClass, override val compilerContext:
|
||||
// if index == -1 (READ_DONE) break loop
|
||||
+IrBranchImpl(irEquals(indexVar.get(), irInt(-1)), irSetVar(flagVar.symbol, irBoolean(false)))
|
||||
|
||||
val branchBodies: List<Pair<Int, IrExpression>> = orderedProperties.mapIndexed { index, property ->
|
||||
val branchBodies: List<Pair<Int, IrExpression>> = serializableProperties.mapIndexed { index, property ->
|
||||
val body = irBlock {
|
||||
val sti = getSerialTypeInfo(property)
|
||||
val innerSerial = serializerInstance(this@SerializerIrGenerator, serializableDescriptor, sti.serializer, property.module, property.type, property.genericIndex)
|
||||
@@ -398,7 +398,7 @@ class SerializerIrGenerator(val irClass: IrClass, override val compilerContext:
|
||||
val ctor: IrConstructorSymbol = if (serializableDescriptor.isInternalSerializable) {
|
||||
val ctorDesc = serializableIrClass
|
||||
.constructors.single { it.origin == SERIALIZABLE_PLUGIN_ORIGIN }
|
||||
args = listOf(irGet(bitMasks[0])) + args + irNull()
|
||||
args = bitMasks.map { irGet(it) } + args + irNull()
|
||||
ctorDesc.symbol
|
||||
} else {
|
||||
compilerContext.externalSymbols.referenceConstructor(serializableDescriptor.unsubstitutedPrimaryConstructor!!)
|
||||
|
||||
+6
-9
@@ -32,16 +32,12 @@ import org.jetbrains.kotlin.psi.KtExpression
|
||||
import org.jetbrains.kotlin.psi.KtPureClassOrObject
|
||||
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.*
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.MISSING_FIELD_EXC
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.getClassFromSerializationPackage
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.hasCompanionObjectAsSerializer
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.hasSerializableAnnotationWithoutArgs
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.isInternalSerializable
|
||||
|
||||
class SerializableJsTranslator(
|
||||
val declaration: KtPureClassOrObject,
|
||||
val descriptor: ClassDescriptor,
|
||||
val translator: DeclarationBodyVisitor,
|
||||
val context: TranslationContext
|
||||
) : SerializableCodegen(descriptor, context.bindingContext()) {
|
||||
|
||||
@@ -63,9 +59,10 @@ class SerializableJsTranslator(
|
||||
Namer.createObjectWithPrototypeFrom(context.getInnerNameForDescriptor(serializableDescriptor).makeRef())
|
||||
)
|
||||
)
|
||||
val seenVar = jsFun.parameters[0].name.makeRef()
|
||||
val seenVarsOffset = properties.serializableProperties.bitMaskSlotCount()
|
||||
val seenVars = (0 until seenVarsOffset).map { jsFun.parameters[it].name.makeRef() }
|
||||
for ((index, prop) in properties.serializableProperties.withIndex()) {
|
||||
val paramRef = jsFun.parameters[index + 1].name.makeRef()
|
||||
val paramRef = jsFun.parameters[index + seenVarsOffset].name.makeRef()
|
||||
// assign this.a = a in else branch
|
||||
val assignParamStmt = TranslationUtils.assignmentToBackingField(context, prop.descriptor, paramRef).makeStmt()
|
||||
|
||||
@@ -77,7 +74,7 @@ class SerializableJsTranslator(
|
||||
JsThrow(JsNew(missingExceptionClassRef, listOf(JsStringLiteral(prop.name))))
|
||||
}
|
||||
// (seen & 1 << i == 0) -- not seen
|
||||
val notSeenTest = propNotSeenTest(seenVar, index)
|
||||
val notSeenTest = propNotSeenTest(seenVars[bitMaskSlotAt(index)], index)
|
||||
+JsIf(notSeenTest, ifNotSeenStmt, assignParamStmt)
|
||||
}
|
||||
|
||||
@@ -111,7 +108,7 @@ class SerializableJsTranslator(
|
||||
context: TranslationContext
|
||||
) {
|
||||
if (serializableClass.isInternalSerializable)
|
||||
SerializableJsTranslator(declaration, serializableClass, translator, context).generate()
|
||||
SerializableJsTranslator(declaration, serializableClass, context).generate()
|
||||
else if (serializableClass.hasSerializableAnnotationWithoutArgs && !serializableClass.hasCompanionObjectAsSerializer) {
|
||||
throw CompilationException(
|
||||
"@Serializable annotation on $serializableClass would be ignored because it is impossible to serialize it automatically. " +
|
||||
|
||||
+63
-53
@@ -72,7 +72,7 @@ class SerializerJsTranslator(descriptor: ClassDescriptor,
|
||||
val pushClassFunc = serialDescImplClass.getFuncDesc(CallingConventions.addClassAnnotation).single()
|
||||
val serialClassDescRef = JsNameRef(context.getNameForDescriptor(generatedSerialDescPropertyDescriptor), JsThisRef())
|
||||
|
||||
for (prop in orderedProperties) {
|
||||
for (prop in serializableProperties) {
|
||||
if (prop.transient) continue
|
||||
val call = JsInvocation(
|
||||
JsNameRef(context.getNameForDescriptor(addFunc), serialClassDescRef),
|
||||
@@ -100,7 +100,7 @@ class SerializerJsTranslator(descriptor: ClassDescriptor,
|
||||
}
|
||||
|
||||
override fun generateChildSerializersGetter(function: FunctionDescriptor) = generateFunction(function) { _, _ ->
|
||||
val allSerializers = orderedProperties.map { requireNotNull(serializerTower(it)) { "Property ${it.name} must have a serializer" } }
|
||||
val allSerializers = serializableProperties.map { requireNotNull(serializerTower(it)) { "Property ${it.name} must have a serializer" } }
|
||||
+JsReturn(JsArrayLiteral(allSerializers))
|
||||
}
|
||||
|
||||
@@ -160,7 +160,7 @@ class SerializerJsTranslator(descriptor: ClassDescriptor,
|
||||
fun SerializableProperty.jsNameRef() = JsNameRef(ctx.getNameForDescriptor(descriptor), objRef)
|
||||
|
||||
// todo: internal serialization via virtual calls
|
||||
val labeledProperties = orderedProperties.filter { !it.transient }
|
||||
val labeledProperties = serializableProperties.filter { !it.transient }
|
||||
for (index in labeledProperties.indices) {
|
||||
val property = labeledProperties[index]
|
||||
if (property.transient) continue
|
||||
@@ -224,15 +224,15 @@ class SerializerJsTranslator(descriptor: ClassDescriptor,
|
||||
+JsVars(JsVars.JsVar(indexVar.name), JsVars.JsVar(readAllVar.name, JsBooleanLiteral(false)))
|
||||
|
||||
// calculating bit mask vars
|
||||
val blocksCnt = orderedProperties.size / 32 + 1
|
||||
fun bitMaskOff(i: Int) = i / 32
|
||||
val blocksCnt = serializableProperties.bitMaskSlotCount()
|
||||
fun bitMaskOff(i: Int) = bitMaskSlotAt(i)
|
||||
|
||||
// var bitMask0 = 0, bitMask1 = 0...
|
||||
val bitMasks = (0 until blocksCnt).map { JsNameRef(jsFun.scope.declareFreshName("bitMask$it")) }
|
||||
+JsVars(bitMasks.map { JsVars.JsVar(it.name, JsIntLiteral(0)) }, false)
|
||||
|
||||
// var localProp0, localProp1, ...
|
||||
val localProps = orderedProperties.mapIndexed {i, _ -> JsNameRef(jsFun.scope.declareFreshName("local$i")) }
|
||||
val localProps = serializableProperties.mapIndexed { i, _ -> JsNameRef(jsFun.scope.declareFreshName("local$i")) }
|
||||
+JsVars(localProps.map { JsVars.JsVar(it.name) }, true)
|
||||
|
||||
//input = input.readBegin(...)
|
||||
@@ -241,8 +241,10 @@ class SerializerJsTranslator(descriptor: ClassDescriptor,
|
||||
}
|
||||
val inputVar = JsNameRef(jsFun.scope.declareFreshName("input"))
|
||||
val readBeginF = decoderClass.getFuncDesc(CallingConventions.begin).single()
|
||||
val readBeginCall = JsInvocation(JsNameRef(context.getNameForDescriptor(readBeginF), JsNameRef(jsFun.parameters[0].name)),
|
||||
serialClassDescRef, JsArrayLiteral(typeParams))
|
||||
val readBeginCall = JsInvocation(
|
||||
JsNameRef(context.getNameForDescriptor(readBeginF), JsNameRef(jsFun.parameters[0].name)),
|
||||
serialClassDescRef, JsArrayLiteral(typeParams)
|
||||
)
|
||||
+JsVars(JsVars.JsVar(inputVar.name, readBeginCall))
|
||||
|
||||
// while(true) {
|
||||
@@ -252,20 +254,20 @@ class SerializerJsTranslator(descriptor: ClassDescriptor,
|
||||
// index = input.readElement(classDesc)
|
||||
val readElementF = context.getNameForDescriptor(inputClass.getFuncDesc(CallingConventions.decodeElementIndex).single())
|
||||
+JsAstUtils.assignment(
|
||||
indexVar,
|
||||
JsInvocation(JsNameRef(readElementF, inputVar), serialClassDescRef)
|
||||
indexVar,
|
||||
JsInvocation(JsNameRef(readElementF, inputVar), serialClassDescRef)
|
||||
).makeStmt()
|
||||
// switch(index)
|
||||
jsSwitch(indexVar) {
|
||||
// -2: readAll = true
|
||||
case(JsIntLiteral(-2)) {
|
||||
+JsAstUtils.assignment(
|
||||
readAllVar,
|
||||
JsBooleanLiteral(true)
|
||||
readAllVar,
|
||||
JsBooleanLiteral(true)
|
||||
).makeStmt()
|
||||
}
|
||||
// all properties
|
||||
for ((i, property) in orderedProperties.withIndex()) {
|
||||
for ((i, property) in serializableProperties.withIndex()) {
|
||||
case(JsIntLiteral(i)) {
|
||||
// input.readXxxElementValue
|
||||
val sti = getSerialTypeInfo(property)
|
||||
@@ -273,64 +275,72 @@ class SerializerJsTranslator(descriptor: ClassDescriptor,
|
||||
val call: JsExpression = if (innerSerial == null) {
|
||||
val unknownSer = (sti.elementMethodPrefix.isEmpty())
|
||||
val readFunc =
|
||||
inputClass.getFuncDesc("${CallingConventions.decode}${sti.elementMethodPrefix}${CallingConventions.elementPostfix}")
|
||||
// if readElementValue, must have 3 parameters, if readXXXElementValue - 2
|
||||
.single { !unknownSer || (it.valueParameters.size == 3) }
|
||||
.let { context.getNameForDescriptor(it) }
|
||||
inputClass.getFuncDesc("${CallingConventions.decode}${sti.elementMethodPrefix}${CallingConventions.elementPostfix}")
|
||||
// if readElementValue, must have 3 parameters, if readXXXElementValue - 2
|
||||
.single { !unknownSer || (it.valueParameters.size == 3) }
|
||||
.let { context.getNameForDescriptor(it) }
|
||||
val readArgs = mutableListOf(serialClassDescRef, JsIntLiteral(i))
|
||||
if (unknownSer) readArgs.add(ExpressionVisitor.getObjectKClass(
|
||||
this@SerializerJsTranslator.context,
|
||||
property.type.toClassDescriptor!!
|
||||
))
|
||||
if (unknownSer) readArgs.add(
|
||||
ExpressionVisitor.getObjectKClass(
|
||||
this@SerializerJsTranslator.context,
|
||||
property.type.toClassDescriptor!!
|
||||
)
|
||||
)
|
||||
JsInvocation(JsNameRef(readFunc, inputVar), readArgs)
|
||||
}
|
||||
else {
|
||||
val notSeenTest = propNotSeenTest(bitMasks[i / 32], i)
|
||||
} else {
|
||||
val notSeenTest = propNotSeenTest(bitMasks[bitMaskOff(i)], i)
|
||||
val readFunc =
|
||||
inputClass.getFuncDesc("${CallingConventions.decode}${sti.elementMethodPrefix}Serializable${CallingConventions.elementPostfix}").single()
|
||||
.let { context.getNameForDescriptor(it) }
|
||||
inputClass.getFuncDesc("${CallingConventions.decode}${sti.elementMethodPrefix}Serializable${CallingConventions.elementPostfix}")
|
||||
.single()
|
||||
.let { context.getNameForDescriptor(it) }
|
||||
val updateFunc =
|
||||
inputClass.getFuncDesc("${CallingConventions.update}${sti.elementMethodPrefix}Serializable${CallingConventions.elementPostfix}").single()
|
||||
.let { context.getNameForDescriptor(it) }
|
||||
JsConditional(notSeenTest,
|
||||
JsInvocation(JsNameRef(readFunc, inputVar),
|
||||
serialClassDescRef,
|
||||
JsIntLiteral(i),
|
||||
innerSerial),
|
||||
JsInvocation(JsNameRef(updateFunc, inputVar),
|
||||
serialClassDescRef,
|
||||
JsIntLiteral(i),
|
||||
innerSerial,
|
||||
localProps[i]
|
||||
)
|
||||
inputClass.getFuncDesc("${CallingConventions.update}${sti.elementMethodPrefix}Serializable${CallingConventions.elementPostfix}")
|
||||
.single()
|
||||
.let { context.getNameForDescriptor(it) }
|
||||
JsConditional(
|
||||
notSeenTest,
|
||||
JsInvocation(
|
||||
JsNameRef(readFunc, inputVar),
|
||||
serialClassDescRef,
|
||||
JsIntLiteral(i),
|
||||
innerSerial
|
||||
),
|
||||
JsInvocation(
|
||||
JsNameRef(updateFunc, inputVar),
|
||||
serialClassDescRef,
|
||||
JsIntLiteral(i),
|
||||
innerSerial,
|
||||
localProps[i]
|
||||
)
|
||||
)
|
||||
}
|
||||
// localPropI = ...
|
||||
+JsAstUtils.assignment(
|
||||
localProps[i],
|
||||
call
|
||||
localProps[i],
|
||||
call
|
||||
).makeStmt()
|
||||
// need explicit unit instance
|
||||
if (sti.unit) {
|
||||
+JsAstUtils.assignment(
|
||||
localProps[i],
|
||||
context.getQualifiedReference(property.type.builtIns.unit)
|
||||
localProps[i],
|
||||
context.getQualifiedReference(property.type.builtIns.unit)
|
||||
).makeStmt()
|
||||
}
|
||||
// char unboxing crutch
|
||||
if (KotlinBuiltIns.isCharOrNullableChar(property.type)) {
|
||||
val coerceTo = TranslationUtils.getReturnTypeForCoercion(property.descriptor)
|
||||
+JsAstUtils.assignment(
|
||||
localProps[i],
|
||||
TranslationUtils.coerce(context, localProps[i], coerceTo)
|
||||
localProps[i],
|
||||
TranslationUtils.coerce(context, localProps[i], coerceTo)
|
||||
).makeStmt()
|
||||
}
|
||||
|
||||
// bitMask[i] |= 1 << x
|
||||
val bitPos = 1 shl (i % 32)
|
||||
+JsBinaryOperation(JsBinaryOperator.ASG_BIT_OR,
|
||||
bitMasks[i / 32],
|
||||
JsIntLiteral(bitPos)
|
||||
+JsBinaryOperation(
|
||||
JsBinaryOperator.ASG_BIT_OR,
|
||||
bitMasks[bitMaskOff(i)],
|
||||
JsIntLiteral(bitPos)
|
||||
).makeStmt()
|
||||
// if (!readAll) break
|
||||
+JsIf(JsAstUtils.not(readAllVar), JsBreak())
|
||||
@@ -343,7 +353,7 @@ class SerializerJsTranslator(descriptor: ClassDescriptor,
|
||||
// default: throw
|
||||
default {
|
||||
val excClassRef = serializableDescriptor.getClassFromSerializationPackage(SerialEntityNames.UNKNOWN_FIELD_EXC)
|
||||
.let { context.translateQualifiedReference(it) }
|
||||
.let { context.translateQualifiedReference(it) }
|
||||
+JsThrow(JsNew(excClassRef, listOf(indexVar)))
|
||||
}
|
||||
}
|
||||
@@ -351,17 +361,17 @@ class SerializerJsTranslator(descriptor: ClassDescriptor,
|
||||
|
||||
// input.readEnd(desc)
|
||||
val readEndF = inputClass.getFuncDesc(CallingConventions.end).single()
|
||||
.let { context.getNameForDescriptor(it) }
|
||||
.let { context.getNameForDescriptor(it) }
|
||||
+JsInvocation(
|
||||
JsNameRef(readEndF, inputVar),
|
||||
serialClassDescRef
|
||||
JsNameRef(readEndF, inputVar),
|
||||
serialClassDescRef
|
||||
).makeStmt()
|
||||
|
||||
// deserialization constructor call
|
||||
// todo: external deserialization with primary constructor and setters calls after resolution of KT-11586
|
||||
val constrDesc = KSerializerDescriptorResolver.createLoadConstructorDescriptor(serializableDescriptor, context.bindingContext())
|
||||
val constrRef = context.getInnerNameForDescriptor(constrDesc).makeRef()
|
||||
val args: MutableList<JsExpression> = mutableListOf(bitMasks[0])
|
||||
val args: MutableList<JsExpression> = bitMasks.toMutableList()
|
||||
args += localProps
|
||||
args += JsNullLiteral()
|
||||
+JsReturn(JsInvocation(constrRef, args))
|
||||
|
||||
+15
-5
@@ -120,9 +120,17 @@ fun InstructionAdapter.genKOutputMethodCall(
|
||||
)
|
||||
}
|
||||
|
||||
internal fun InstructionAdapter.buildInternalConstructorDesc(propsStartVar: Int, bitMaskBase: Int, codegen: ClassBodyCodegen, args: List<SerializableProperty>): String {
|
||||
val constructorDesc = StringBuilder("(I")
|
||||
load(bitMaskBase, OPT_MASK_TYPE)
|
||||
internal fun InstructionAdapter.buildInternalConstructorDesc(
|
||||
propsStartVar: Int,
|
||||
bitMaskBase: Int,
|
||||
codegen: ClassBodyCodegen,
|
||||
args: List<SerializableProperty>
|
||||
): String {
|
||||
val constructorDesc = StringBuilder("(")
|
||||
repeat(args.bitMaskSlotCount()) {
|
||||
constructorDesc.append("I")
|
||||
load(bitMaskBase + it, Type.INT_TYPE)
|
||||
}
|
||||
var propVar = propsStartVar
|
||||
for (property in args) {
|
||||
val propertyType = codegen.typeMapper.mapType(property.type)
|
||||
@@ -135,8 +143,10 @@ internal fun InstructionAdapter.buildInternalConstructorDesc(propsStartVar: Int,
|
||||
return constructorDesc.toString()
|
||||
}
|
||||
|
||||
internal fun ImplementationBodyCodegen.generateMethod(function: FunctionDescriptor,
|
||||
block: InstructionAdapter.(JvmMethodSignature, ExpressionCodegen) -> Unit) {
|
||||
internal fun ImplementationBodyCodegen.generateMethod(
|
||||
function: FunctionDescriptor,
|
||||
block: InstructionAdapter.(JvmMethodSignature, ExpressionCodegen) -> Unit
|
||||
) {
|
||||
this.functionCodegen.generateMethod(OtherOrigin(this.myClass.psiOrParent, function), function,
|
||||
object : FunctionGenerationStrategy.CodegenBased(this.state) {
|
||||
override fun doGenerateBody(codegen: ExpressionCodegen, signature: JvmMethodSignature) {
|
||||
|
||||
+6
-5
@@ -178,13 +178,15 @@ class SerializableCodegenImpl(
|
||||
|
||||
private fun InstructionAdapter.doGenerateConstructorImpl(exprCodegen: ExpressionCodegen) {
|
||||
val seenMask = 1
|
||||
var (propIndex, propOffset) = generateSuperSerializableCall(2)
|
||||
val bitMaskOff = fun(it: Int): Int { return seenMask + bitMaskSlotAt(it) }
|
||||
val bitMaskEnd = seenMask + properties.serializableProperties.bitMaskSlotCount()
|
||||
var (propIndex, propOffset) = generateSuperSerializableCall(bitMaskEnd)
|
||||
for (i in propIndex until properties.serializableProperties.size) {
|
||||
val prop = properties[i]
|
||||
val propType = prop.asmType
|
||||
if (!prop.optional) {
|
||||
// primary were validated before constructor call
|
||||
genValidateProperty(i) { seenMask }
|
||||
genValidateProperty(i, bitMaskOff)
|
||||
val nonThrowLabel = Label()
|
||||
ificmpne(nonThrowLabel)
|
||||
genExceptionThrow(serializationExceptionMissingFieldName, prop.name)
|
||||
@@ -194,12 +196,11 @@ class SerializableCodegenImpl(
|
||||
load(propOffset, propType)
|
||||
putfield(thisAsmType.internalName, prop.descriptor.name.asString(), propType.descriptor)
|
||||
} else {
|
||||
genValidateProperty(i) { seenMask }
|
||||
genValidateProperty(i, bitMaskOff)
|
||||
val setLbl = Label()
|
||||
val nextLabel = Label()
|
||||
ificmpeq(setLbl)
|
||||
// setting field
|
||||
// todo: validate nullability
|
||||
load(0, thisAsmType)
|
||||
load(propOffset, propType)
|
||||
putfield(thisAsmType.internalName, prop.descriptor.name.asString(), propType.descriptor)
|
||||
@@ -228,7 +229,7 @@ class SerializableCodegenImpl(
|
||||
.forEach { (t, u) -> exprCodegen.genInitParam(t, u) }
|
||||
|
||||
// init blocks
|
||||
// todo: proper order with other initializers
|
||||
// todo: proper order with other initializers?
|
||||
classCodegen.myClass.anonymousInitializers()
|
||||
.forEach { exprCodegen.gen(it, Type.VOID_TYPE) }
|
||||
areturn(Type.VOID_TYPE)
|
||||
|
||||
+52
-43
@@ -30,8 +30,8 @@ import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||
|
||||
class SerializerCodegenImpl(
|
||||
private val codegen: ImplementationBodyCodegen,
|
||||
serializableClass: ClassDescriptor
|
||||
private val codegen: ImplementationBodyCodegen,
|
||||
serializableClass: ClassDescriptor
|
||||
) : SerializerCodegen(codegen.descriptor, codegen.bindingContext) {
|
||||
|
||||
|
||||
@@ -89,7 +89,7 @@ class SerializerCodegenImpl(
|
||||
}
|
||||
invokespecial(descImplType.internalName, "<init>", "(Ljava/lang/String;${generatedSerializerType.descriptor})V", false)
|
||||
store(descriptorVar, descImplType)
|
||||
for (property in orderedProperties) {
|
||||
for (property in serializableProperties) {
|
||||
if (property.transient) continue
|
||||
load(descriptorVar, descImplType)
|
||||
aconst(property.name)
|
||||
@@ -187,13 +187,13 @@ class SerializerCodegenImpl(
|
||||
|
||||
override fun generateChildSerializersGetter(function: FunctionDescriptor) {
|
||||
codegen.generateMethod(function) { _, _ ->
|
||||
val size = orderedProperties.size
|
||||
val size = serializableProperties.size
|
||||
iconst(size)
|
||||
newarray(kSerializerType)
|
||||
for (i in 0 until size) {
|
||||
dup() // array
|
||||
iconst(i) // index
|
||||
val prop = orderedProperties[i]
|
||||
val prop = serializableProperties[i]
|
||||
assert(
|
||||
stackValueSerializerInstanceFromSerializerWithoutSti(
|
||||
codegen,
|
||||
@@ -208,7 +208,7 @@ class SerializerCodegenImpl(
|
||||
}
|
||||
|
||||
override fun generateSave(
|
||||
function: FunctionDescriptor
|
||||
function: FunctionDescriptor
|
||||
) {
|
||||
codegen.generateMethod(function) { signature, expressionCodegen ->
|
||||
// fun save(output: KOutput, obj : T)
|
||||
@@ -223,8 +223,9 @@ class SerializerCodegenImpl(
|
||||
genArrayOfTypeParametersSerializers()
|
||||
invokeinterface(
|
||||
encoderType.internalName, CallingConventions.begin,
|
||||
"(" + descType.descriptor + kSerializerArrayType.descriptor +
|
||||
")" + kOutputType.descriptor)
|
||||
"(" + descType.descriptor + kSerializerArrayType.descriptor +
|
||||
")" + kOutputType.descriptor
|
||||
)
|
||||
store(outputVar, kOutputType)
|
||||
if (serializableDescriptor.isInternalSerializable) {
|
||||
val sig = StringBuilder("(${kOutputType.descriptor}${descType.descriptor}")
|
||||
@@ -232,7 +233,7 @@ class SerializerCodegenImpl(
|
||||
load(objVar, objType)
|
||||
load(outputVar, kOutputType)
|
||||
load(descVar, descType)
|
||||
serializableDescriptor.declaredTypeParameters.forEachIndexed {i, _ ->
|
||||
serializableDescriptor.declaredTypeParameters.forEachIndexed { i, _ ->
|
||||
load(0, kSerializerType)
|
||||
getfield(codegen.typeMapper.mapClass(codegen.descriptor).internalName, "$typeArgPrefix$i", kSerializerType.descriptor)
|
||||
sig.append(kSerializerType.descriptor)
|
||||
@@ -240,11 +241,11 @@ class SerializerCodegenImpl(
|
||||
sig.append(")V")
|
||||
invokevirtual(
|
||||
objType.internalName, SerialEntityNames.WRITE_SELF_NAME.asString(),
|
||||
sig.toString(), false)
|
||||
}
|
||||
else {
|
||||
sig.toString(), false
|
||||
)
|
||||
} else {
|
||||
// loop for all properties
|
||||
val labeledProperties = orderedProperties.filter { !it.transient }
|
||||
val labeledProperties = serializableProperties.filter { !it.transient }
|
||||
for (index in labeledProperties.indices) {
|
||||
val property = labeledProperties[index]
|
||||
if (property.transient) continue
|
||||
@@ -260,7 +261,8 @@ class SerializerCodegenImpl(
|
||||
load(descVar, descType)
|
||||
invokeinterface(
|
||||
kOutputType.internalName, CallingConventions.end,
|
||||
"(" + descType.descriptor + ")V")
|
||||
"(" + descType.descriptor + ")V"
|
||||
)
|
||||
// return
|
||||
areturn(Type.VOID_TYPE)
|
||||
}
|
||||
@@ -280,7 +282,7 @@ class SerializerCodegenImpl(
|
||||
}
|
||||
|
||||
override fun generateLoad(
|
||||
function: FunctionDescriptor
|
||||
function: FunctionDescriptor
|
||||
) {
|
||||
codegen.generateMethod(function) { _, expressionCodegen ->
|
||||
// fun load(input: KInput): T
|
||||
@@ -289,9 +291,9 @@ class SerializerCodegenImpl(
|
||||
val indexVar = 3
|
||||
val readAllVar = 4
|
||||
val bitMaskBase = 5
|
||||
val blocksCnt = orderedProperties.size / OPT_MASK_BITS + 1
|
||||
fun bitMaskOff(i: Int) = bitMaskBase + (i / OPT_MASK_BITS) * OPT_MASK_TYPE.size
|
||||
val propsStartVar = bitMaskBase + OPT_MASK_TYPE.size * blocksCnt
|
||||
val blocksCnt = serializableProperties.bitMaskSlotCount()
|
||||
val bitMaskOff = fun(it: Int): Int { return bitMaskBase + bitMaskSlotAt(it) }
|
||||
val propsStartVar = bitMaskBase + blocksCnt
|
||||
stackSerialClassDesc(descVar)
|
||||
// boolean readAll = false
|
||||
iconst(0)
|
||||
@@ -304,7 +306,7 @@ class SerializerCodegenImpl(
|
||||
}
|
||||
// initialize all prop vars
|
||||
var propVar = propsStartVar
|
||||
for (property in orderedProperties) {
|
||||
for (property in serializableProperties) {
|
||||
val propertyType = codegen.typeMapper.mapType(property.type)
|
||||
stackValueDefault(propertyType)
|
||||
store(propVar, propertyType)
|
||||
@@ -316,8 +318,9 @@ class SerializerCodegenImpl(
|
||||
genArrayOfTypeParametersSerializers()
|
||||
invokeinterface(
|
||||
decoderType.internalName, CallingConventions.begin,
|
||||
"(" + descType.descriptor + kSerializerArrayType.descriptor +
|
||||
")" + kInputType.descriptor)
|
||||
"(" + descType.descriptor + kSerializerArrayType.descriptor +
|
||||
")" + kInputType.descriptor
|
||||
)
|
||||
store(inputVar, kInputType)
|
||||
// readElement: int index = input.readElement(classDesc)
|
||||
val readElementLabel = Label()
|
||||
@@ -326,10 +329,11 @@ class SerializerCodegenImpl(
|
||||
load(descVar, descType)
|
||||
invokeinterface(
|
||||
kInputType.internalName, CallingConventions.decodeElementIndex,
|
||||
"(" + descType.descriptor + ")I")
|
||||
"(" + descType.descriptor + ")I"
|
||||
)
|
||||
store(indexVar, Type.INT_TYPE)
|
||||
// switch(index)
|
||||
val labeledProperties = orderedProperties.filter { !it.transient }
|
||||
val labeledProperties = serializableProperties.filter { !it.transient }
|
||||
val readAllLabel = Label()
|
||||
val readEndLabel = Label()
|
||||
val incorrectIndLabel = Label()
|
||||
@@ -348,7 +352,7 @@ class SerializerCodegenImpl(
|
||||
// loop for all properties
|
||||
propVar = propsStartVar
|
||||
var labelNum = 0
|
||||
for ((index, property) in orderedProperties.withIndex()) {
|
||||
for ((index, property) in serializableProperties.withIndex()) {
|
||||
val propertyType = codegen.typeMapper.mapType(property.type)
|
||||
if (!property.transient) {
|
||||
// labelI:
|
||||
@@ -382,7 +386,7 @@ class SerializerCodegenImpl(
|
||||
// we can choose either it is read or update
|
||||
val readLabel = Label()
|
||||
val endL = Label()
|
||||
genValidateProperty(index, ::bitMaskOff)
|
||||
genValidateProperty(index, bitMaskOff)
|
||||
ificmpeq(readLabel)
|
||||
load(propVar, propertyType)
|
||||
StackValue.coerce(propertyType, sti.type, this)
|
||||
@@ -398,8 +402,7 @@ class SerializerCodegenImpl(
|
||||
|
||||
if (sti.unit) {
|
||||
StackValue.putUnitInstance(this)
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
StackValue.coerce(sti.type, propertyType, this)
|
||||
}
|
||||
store(propVar, propertyType)
|
||||
@@ -425,8 +428,10 @@ class SerializerCodegenImpl(
|
||||
visitLabel(readEndLabel)
|
||||
load(inputVar, kInputType)
|
||||
load(descVar, descType)
|
||||
invokeinterface(kInputType.internalName, CallingConventions.end,
|
||||
"(" + descType.descriptor + ")V")
|
||||
invokeinterface(
|
||||
kInputType.internalName, CallingConventions.end,
|
||||
"(" + descType.descriptor + ")V"
|
||||
)
|
||||
if (!serializableDescriptor.isInternalSerializable) {
|
||||
//validate all required (constructor) fields
|
||||
val nonThrowLabel = Label()
|
||||
@@ -435,10 +440,13 @@ class SerializerCodegenImpl(
|
||||
if (property.optional || property.transient) {
|
||||
// todo: Normal reporting of error
|
||||
if (!property.isConstructorParameterWithDefault)
|
||||
throw CompilationException("Property ${property.name} was declared as optional/transient but has no default value", null, null)
|
||||
}
|
||||
else {
|
||||
genValidateProperty(i, ::bitMaskOff)
|
||||
throw CompilationException(
|
||||
"Property ${property.name} was declared as optional/transient but has no default value",
|
||||
null,
|
||||
null
|
||||
)
|
||||
} else {
|
||||
genValidateProperty(i, bitMaskOff)
|
||||
// todo: print name of each variable?
|
||||
ificmpeq(throwLabel)
|
||||
}
|
||||
@@ -460,8 +468,9 @@ class SerializerCodegenImpl(
|
||||
// result := ... <created object>
|
||||
store(resultVar, serializableAsmType)
|
||||
// set other properties
|
||||
propVar = propsStartVar + properties.serializableConstructorProperties.map { codegen.typeMapper.mapType(it.type).size }.sum()
|
||||
genSetSerializableStandaloneProperties(expressionCodegen, propVar, resultVar, ::bitMaskOff)
|
||||
propVar = propsStartVar +
|
||||
properties.serializableConstructorProperties.map { codegen.typeMapper.mapType(it.type).size }.sum()
|
||||
genSetSerializableStandaloneProperties(expressionCodegen, propVar, resultVar, bitMaskOff)
|
||||
// load result
|
||||
load(resultVar, serializableAsmType)
|
||||
// will return result
|
||||
@@ -491,8 +500,7 @@ class SerializerCodegenImpl(
|
||||
}
|
||||
if (!properties.primaryConstructorWithDefaults) {
|
||||
constructorDesc.append(")V")
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
val cnt = properties.serializableConstructorProperties.size.coerceAtMost(32) //only 32 default values are supported
|
||||
val mask = if (cnt == 32) -1 else ((1 shl cnt) - 1)
|
||||
load(bitMaskBase, OPT_MASK_TYPE)
|
||||
@@ -505,7 +513,8 @@ class SerializerCodegenImpl(
|
||||
}
|
||||
|
||||
private fun InstructionAdapter.genSetSerializableStandaloneProperties(
|
||||
expressionCodegen: ExpressionCodegen, propVarStart: Int, resultVar: Int, bitMaskPos: (Int) -> Int) {
|
||||
expressionCodegen: ExpressionCodegen, propVarStart: Int, resultVar: Int, bitMaskPos: (Int) -> Int
|
||||
) {
|
||||
var propVar = propVarStart
|
||||
val offset = properties.serializableConstructorProperties.size
|
||||
for ((index, property) in properties.serializableStandaloneProperties.withIndex()) {
|
||||
@@ -518,8 +527,7 @@ class SerializerCodegenImpl(
|
||||
// if (seen)
|
||||
// set
|
||||
ificmpeq(nextLabel)
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
// if (!seen)
|
||||
// throw
|
||||
// set
|
||||
@@ -530,9 +538,10 @@ class SerializerCodegenImpl(
|
||||
|
||||
// generate setter call
|
||||
val propertyType = codegen.typeMapper.mapType(property.type)
|
||||
expressionCodegen.intermediateValueForProperty(property.descriptor, false, null,
|
||||
StackValue.local(resultVar, serializableAsmType)).
|
||||
store(StackValue.local(propVar, propertyType), this)
|
||||
expressionCodegen.intermediateValueForProperty(
|
||||
property.descriptor, false, null,
|
||||
StackValue.local(resultVar, serializableAsmType)
|
||||
).store(StackValue.local(propVar, propertyType), this)
|
||||
propVar += propertyType.size
|
||||
if (property.optional)
|
||||
visitLabel(nextLabel)
|
||||
|
||||
+10
-6
@@ -280,15 +280,19 @@ object KSerializerDescriptorResolver {
|
||||
val markerDesc = classDescriptor.getKSerializerConstructorMarker()
|
||||
val markerType = markerDesc.toSimpleType()
|
||||
|
||||
val parameterDescsAsProps = SerializableProperties(classDescriptor, bindingContext).serializableProperties.map { it.descriptor }
|
||||
val serializableProperties = SerializableProperties(classDescriptor, bindingContext).serializableProperties
|
||||
val parameterDescsAsProps = serializableProperties.map { it.descriptor }
|
||||
val bitMaskSlotsCount = serializableProperties.bitMaskSlotCount()
|
||||
var i = 0
|
||||
val consParams = mutableListOf<ValueParameterDescriptor>()
|
||||
consParams.add(
|
||||
ValueParameterDescriptorImpl(
|
||||
functionDescriptor, null, i++, Annotations.EMPTY, Name.identifier("seen"), functionDescriptor.builtIns.intType, false,
|
||||
false, false, null, functionDescriptor.source
|
||||
repeat(bitMaskSlotsCount) {
|
||||
consParams.add(
|
||||
ValueParameterDescriptorImpl(
|
||||
functionDescriptor, null, i++, Annotations.EMPTY, Name.identifier("seen$i"), functionDescriptor.builtIns.intType, false,
|
||||
false, false, null, functionDescriptor.source
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
for (prop in parameterDescsAsProps) {
|
||||
consParams.add(
|
||||
ValueParameterDescriptorImpl(
|
||||
|
||||
+4
-1
@@ -73,4 +73,7 @@ class SerializableProperties(private val serializableClass: ClassDescriptor, val
|
||||
|
||||
val primaryConstructorWithDefaults = serializableClass.unsubstitutedPrimaryConstructor
|
||||
?.original?.valueParameters?.any { it.declaresDefaultValue() } ?: false
|
||||
}
|
||||
}
|
||||
|
||||
internal fun List<SerializableProperty>.bitMaskSlotCount() = size / 32 + 1
|
||||
internal fun bitMaskSlotAt(propertyIndex: Int) = propertyIndex / 32
|
||||
Reference in New Issue
Block a user