diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/SerializerCodegen.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/SerializerCodegen.kt index 356768b5c13..c9316de3bac 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/SerializerCodegen.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/SerializerCodegen.kt @@ -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) { diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializableIrGenerator.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializableIrGenerator.kt index 2fa1456fd92..ffbb0b3fcba 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializableIrGenerator.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializableIrGenerator.kt @@ -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) diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerIrGenerator.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerIrGenerator.kt index 0fbcafeb58b..3542b9568b1 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerIrGenerator.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerIrGenerator.kt @@ -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> = orderedProperties.mapIndexed { index, property -> + val branchBodies: List> = 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!!) diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/js/SerializableJsTranslator.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/js/SerializableJsTranslator.kt index f070bbb8118..e032fb1186a 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/js/SerializableJsTranslator.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/js/SerializableJsTranslator.kt @@ -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. " + diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/js/SerializerJsTranslator.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/js/SerializerJsTranslator.kt index e8aab144722..4ec18401aff 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/js/SerializerJsTranslator.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/js/SerializerJsTranslator.kt @@ -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 = mutableListOf(bitMasks[0]) + val args: MutableList = bitMasks.toMutableList() args += localProps args += JsNullLiteral() +JsReturn(JsInvocation(constrRef, args)) diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/JVMCodegenUtil.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/JVMCodegenUtil.kt index d22fe021d9b..8616e7b410e 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/JVMCodegenUtil.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/JVMCodegenUtil.kt @@ -120,9 +120,17 @@ fun InstructionAdapter.genKOutputMethodCall( ) } -internal fun InstructionAdapter.buildInternalConstructorDesc(propsStartVar: Int, bitMaskBase: Int, codegen: ClassBodyCodegen, args: List): String { - val constructorDesc = StringBuilder("(I") - load(bitMaskBase, OPT_MASK_TYPE) +internal fun InstructionAdapter.buildInternalConstructorDesc( + propsStartVar: Int, + bitMaskBase: Int, + codegen: ClassBodyCodegen, + args: List +): 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) { diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializableCodegenImpl.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializableCodegenImpl.kt index 56ccf880b90..727b265ef33 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializableCodegenImpl.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializableCodegenImpl.kt @@ -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) diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializerCodegenImpl.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializerCodegenImpl.kt index 7f9e367e1b3..89d3115d4dc 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializerCodegenImpl.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializerCodegenImpl.kt @@ -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, "", "(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 := ... 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) diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/KSerializerDescriptorResolver.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/KSerializerDescriptorResolver.kt index 94af73f9b23..abd77ea2a90 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/KSerializerDescriptorResolver.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/KSerializerDescriptorResolver.kt @@ -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() - 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( diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/SerializableProperties.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/SerializableProperties.kt index 12364361f92..6ffe6c03a5e 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/SerializableProperties.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/SerializableProperties.kt @@ -73,4 +73,7 @@ class SerializableProperties(private val serializableClass: ClassDescriptor, val val primaryConstructorWithDefaults = serializableClass.unsubstitutedPrimaryConstructor ?.original?.valueParameters?.any { it.declaresDefaultValue() } ?: false -} \ No newline at end of file +} + +internal fun List.bitMaskSlotCount() = size / 32 + 1 +internal fun bitMaskSlotAt(propertyIndex: Int) = propertyIndex / 32 \ No newline at end of file