Added external serializers in serialization plugin for IR backend
- added fallback support of external serializers in IR - implemented calculations of properties default values in IR - swapped check of shouldEncodeElementDefault and comparing the property with default value in IR. Now default value calculated only of shouldEncodeElementDefault returns false
This commit is contained in:
-40
@@ -5,14 +5,11 @@
|
||||
|
||||
package org.jetbrains.kotlinx.serialization.compiler.backend.common
|
||||
|
||||
import org.jetbrains.kotlin.config.ApiVersion
|
||||
import org.jetbrains.kotlin.descriptors.ClassConstructorDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.secondaryConstructors
|
||||
import org.jetbrains.kotlinx.serialization.compiler.diagnostic.VersionReader
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.*
|
||||
|
||||
abstract class SerializableCodegen(
|
||||
@@ -22,9 +19,6 @@ abstract class SerializableCodegen(
|
||||
protected val properties = bindingContext.serializablePropertiesFor(serializableDescriptor)
|
||||
protected val staticDescriptor = serializableDescriptor.declaredTypeParameters.isEmpty()
|
||||
|
||||
private val fieldMissingOptimizationVersion = ApiVersion.parse("1.1")!!
|
||||
protected val useFieldMissingOptimization = canUseFieldMissingOptimization()
|
||||
|
||||
fun generate() {
|
||||
generateSyntheticInternalConstructor()
|
||||
generateSyntheticMethods()
|
||||
@@ -49,40 +43,6 @@ abstract class SerializableCodegen(
|
||||
}
|
||||
}
|
||||
|
||||
protected fun getGoldenMask(): Int {
|
||||
var goldenMask = 0
|
||||
var requiredBit = 1
|
||||
for (property in properties.serializableProperties) {
|
||||
if (!property.optional) {
|
||||
goldenMask = goldenMask or requiredBit
|
||||
}
|
||||
requiredBit = requiredBit shl 1
|
||||
}
|
||||
return goldenMask
|
||||
}
|
||||
|
||||
protected fun getGoldenMaskList(): List<Int> {
|
||||
val maskSlotCount = properties.serializableProperties.bitMaskSlotCount()
|
||||
val goldenMaskList = MutableList(maskSlotCount) { 0 }
|
||||
|
||||
for (i in properties.serializableProperties.indices) {
|
||||
if (!properties.serializableProperties[i].optional) {
|
||||
val slotNumber = i / 32
|
||||
val bitInSlot = i % 32
|
||||
goldenMaskList[slotNumber] = goldenMaskList[slotNumber] or (1 shl bitInSlot)
|
||||
}
|
||||
}
|
||||
return goldenMaskList
|
||||
}
|
||||
|
||||
private fun canUseFieldMissingOptimization(): Boolean {
|
||||
val implementationVersion = VersionReader.getVersionsForCurrentModuleFromContext(
|
||||
serializableDescriptor.module,
|
||||
bindingContext
|
||||
)?.implementationVersion
|
||||
return if (implementationVersion != null) implementationVersion >= fieldMissingOptimizationVersion else false
|
||||
}
|
||||
|
||||
protected abstract fun generateInternalConstructor(constructorDescriptor: ClassConstructorDescriptor)
|
||||
|
||||
protected open fun generateWriteSelfMethod(methodDescriptor: FunctionDescriptor) {
|
||||
|
||||
+177
-17
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
@@ -21,10 +21,12 @@ import org.jetbrains.kotlin.ir.types.*
|
||||
import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl
|
||||
import org.jetbrains.kotlin.ir.types.impl.makeTypeProjection
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
||||
import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.platform.jvm.isJvm
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.classId
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.isEffectivelyExternal
|
||||
@@ -34,6 +36,7 @@ import org.jetbrains.kotlin.types.*
|
||||
import org.jetbrains.kotlin.types.typeUtil.isTypeParameter
|
||||
import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
|
||||
import org.jetbrains.kotlin.types.typeUtil.representativeUpperBound
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
import org.jetbrains.kotlinx.serialization.compiler.backend.common.*
|
||||
import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.*
|
||||
import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPluginContext
|
||||
@@ -42,10 +45,20 @@ import org.jetbrains.kotlinx.serialization.compiler.resolve.*
|
||||
interface IrBuilderExtension {
|
||||
val compilerContext: SerializationPluginContext
|
||||
|
||||
private val throwMissedFieldExceptionFunc
|
||||
get() = compilerContext.referenceFunctions(SerialEntityNames.SINGLE_MASK_FIELD_MISSING_FUNC_FQ).singleOrNull()
|
||||
|
||||
private val throwMissedFieldExceptionArrayFunc
|
||||
get() = compilerContext.referenceFunctions(SerialEntityNames.ARRAY_MASK_FIELD_MISSING_FUNC_FQ).singleOrNull()
|
||||
|
||||
private inline fun <reified T : IrDeclaration> IrClass.searchForDeclaration(descriptor: DeclarationDescriptor): T? {
|
||||
return declarations.singleOrNull { it.descriptor == descriptor } as? T
|
||||
}
|
||||
|
||||
fun useFieldMissingOptimization(): Boolean {
|
||||
return throwMissedFieldExceptionFunc != null && throwMissedFieldExceptionArrayFunc != null
|
||||
}
|
||||
|
||||
fun IrClass.contributeFunction(descriptor: FunctionDescriptor, bodyGen: IrBlockBodyBuilder.(IrFunction) -> Unit) {
|
||||
val f: IrSimpleFunction = searchForDeclaration(descriptor) ?: compilerContext.symbolTable.referenceSimpleFunction(descriptor).owner
|
||||
// TODO: default parameters
|
||||
@@ -214,14 +227,62 @@ interface IrBuilderExtension {
|
||||
// note: this method should be used only for properties from current module. Fields from other modules are private and inaccessible.
|
||||
val SerializableProperty.irField: IrField get() = compilerContext.symbolTable.referenceField(this.descriptor).owner
|
||||
|
||||
fun SerializableProperty.getIrPropertyFrom(thisClass: IrClass): IrProperty {
|
||||
val desc = this.descriptor
|
||||
fun IrClass.searchForProperty(descriptor: PropertyDescriptor): IrProperty {
|
||||
// this API is used to reference both current module descriptors and external ones (because serializable class can be in any of them),
|
||||
// so we use descriptor api for current module because it is not possible to obtain FQname for e.g. local classes.
|
||||
return thisClass.searchForDeclaration(desc) ?: if (desc.module == compilerContext.moduleDescriptor) {
|
||||
compilerContext.symbolTable.referenceProperty(desc).owner
|
||||
return searchForDeclaration(descriptor) ?: if (descriptor.module == compilerContext.moduleDescriptor) {
|
||||
compilerContext.symbolTable.referenceProperty(descriptor).owner
|
||||
} else {
|
||||
compilerContext.referenceProperties(desc.fqNameSafe).single().owner
|
||||
compilerContext.referenceProperties(descriptor.fqNameSafe).single().owner
|
||||
}
|
||||
}
|
||||
|
||||
fun SerializableProperty.getIrPropertyFrom(thisClass: IrClass): IrProperty {
|
||||
return thisClass.searchForProperty(descriptor)
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Create a function that creates `get property value expressions` for given corresponded constructor's param
|
||||
(constructor_params) -> get_property_value_expression
|
||||
*/
|
||||
fun IrBuilderWithScope.createPropertyByParamReplacer(
|
||||
irClass: IrClass,
|
||||
serialProperties: List<SerializableProperty>,
|
||||
instance: IrValueParameter,
|
||||
bindingContext: BindingContext
|
||||
): (ValueParameterDescriptor) -> IrExpression? {
|
||||
fun SerializableProperty.irGet(): IrExpression {
|
||||
val ownerType = instance.symbol.owner.type
|
||||
return getProperty(
|
||||
irGet(
|
||||
type = ownerType,
|
||||
variable = instance.symbol
|
||||
), getIrPropertyFrom(irClass)
|
||||
)
|
||||
}
|
||||
|
||||
val serialPropertiesMap = serialProperties.associateBy { it.descriptor }
|
||||
|
||||
val transientPropertiesMap =
|
||||
irClass.declarations.asSequence()
|
||||
.filterIsInstance<IrProperty>()
|
||||
.filter { it.backingField != null }.filter { !serialPropertiesMap.containsKey(it.descriptor) }
|
||||
.associateBy { it.symbol.descriptor }
|
||||
|
||||
return {
|
||||
val propertyDescriptor = bindingContext[BindingContext.VALUE_PARAMETER_AS_PROPERTY, it]
|
||||
if (propertyDescriptor != null) {
|
||||
val value = serialPropertiesMap[propertyDescriptor]
|
||||
value?.irGet() ?: transientPropertiesMap[propertyDescriptor]?.let { prop ->
|
||||
getProperty(
|
||||
irGet(instance),
|
||||
prop
|
||||
)
|
||||
}
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -257,6 +318,78 @@ interface IrBuilderExtension {
|
||||
}
|
||||
}
|
||||
|
||||
fun IrBlockBodyBuilder.generateGoldenMaskCheck(
|
||||
seenVars: List<IrValueDeclaration>,
|
||||
properties: SerializableProperties,
|
||||
serialDescriptor: IrExpression
|
||||
) {
|
||||
val fieldsMissedTest: IrExpression
|
||||
val throwErrorExpr: IrExpression
|
||||
|
||||
val maskSlotCount = seenVars.size
|
||||
if (maskSlotCount == 1) {
|
||||
val goldenMask = properties.goldenMask
|
||||
|
||||
|
||||
throwErrorExpr = irInvoke(
|
||||
null,
|
||||
throwMissedFieldExceptionFunc!!,
|
||||
irGet(seenVars[0]),
|
||||
irInt(goldenMask),
|
||||
serialDescriptor,
|
||||
typeHint = compilerContext.irBuiltIns.unitType
|
||||
)
|
||||
|
||||
fieldsMissedTest = irNotEquals(
|
||||
irInt(goldenMask),
|
||||
irBinOp(
|
||||
OperatorNameConventions.AND,
|
||||
irInt(goldenMask),
|
||||
irGet(seenVars[0])
|
||||
)
|
||||
)
|
||||
} else {
|
||||
val goldenMaskList = properties.goldenMaskList
|
||||
|
||||
var compositeExpression: IrExpression? = null
|
||||
for (i in goldenMaskList.indices) {
|
||||
val singleCheckExpr = irNotEquals(
|
||||
irInt(goldenMaskList[i]),
|
||||
irBinOp(
|
||||
OperatorNameConventions.AND,
|
||||
irInt(goldenMaskList[i]),
|
||||
irGet(seenVars[i])
|
||||
)
|
||||
)
|
||||
|
||||
compositeExpression = if (compositeExpression == null) {
|
||||
singleCheckExpr
|
||||
} else {
|
||||
irBinOp(
|
||||
OperatorNameConventions.OR,
|
||||
compositeExpression,
|
||||
singleCheckExpr
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fieldsMissedTest = compositeExpression!!
|
||||
|
||||
throwErrorExpr = irBlock {
|
||||
+irInvoke(
|
||||
null,
|
||||
throwMissedFieldExceptionArrayFunc!!,
|
||||
createPrimitiveArrayOfExpression(compilerContext.irBuiltIns.intType, goldenMaskList.indices.map { irGet(seenVars[it]) }),
|
||||
createPrimitiveArrayOfExpression(compilerContext.irBuiltIns.intType, goldenMaskList.map { irInt(it) }),
|
||||
serialDescriptor,
|
||||
typeHint = compilerContext.irBuiltIns.unitType
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+irIfThen(compilerContext.irBuiltIns.unitType, fieldsMissedTest, throwErrorExpr)
|
||||
}
|
||||
|
||||
fun generateSimplePropertyWithBackingField(
|
||||
propertyDescriptor: PropertyDescriptor,
|
||||
propertyParent: IrClass,
|
||||
@@ -485,18 +618,45 @@ interface IrBuilderExtension {
|
||||
return defaultsMap + extractDefaultValuesFromConstructor(irClass.getSuperClassNotAny())
|
||||
}
|
||||
|
||||
fun buildInitializersRemapping(irClass: IrClass): (IrField) -> IrExpression? {
|
||||
val defaultsMap = extractDefaultValuesFromConstructor(irClass)
|
||||
return fun(f: IrField): IrExpression? {
|
||||
val i = f.initializer?.expression ?: return null
|
||||
val irExpression =
|
||||
if (i is IrGetValueImpl && i.origin == IrStatementOrigin.INITIALIZE_PROPERTY_FROM_PARAMETER) {
|
||||
// this is a primary constructor property, use corresponding default of value parameter
|
||||
defaultsMap.getValue(i.symbol.descriptor as ParameterDescriptor)
|
||||
} else {
|
||||
i
|
||||
/*
|
||||
Creates an initializer adapter function that can replace IR expressions of getting constructor parameter value by some other expression.
|
||||
Also adapter may replace IR expression of getting `this` value by another expression.
|
||||
*/
|
||||
fun createInitializerAdapter(
|
||||
irClass: IrClass,
|
||||
paramGetReplacer: (ValueParameterDescriptor) -> IrExpression?,
|
||||
thisGetReplacer: Pair<IrValueSymbol, () -> IrExpression>? = null
|
||||
): (IrExpressionBody) -> IrExpression {
|
||||
val initializerTransformer = object : IrElementTransformerVoid() {
|
||||
// try to replace `get some value` expression
|
||||
override fun visitGetValue(expression: IrGetValue): IrExpression {
|
||||
val symbol = expression.symbol
|
||||
if (thisGetReplacer != null && thisGetReplacer.first == symbol) {
|
||||
// replace `get this value` expression
|
||||
return thisGetReplacer.second()
|
||||
}
|
||||
return irExpression?.deepCopyWithVariables()
|
||||
|
||||
val descriptor = symbol.descriptor
|
||||
if (descriptor is ValueParameterDescriptor) {
|
||||
// replace `get parameter value` expression
|
||||
paramGetReplacer(descriptor)?.let { return it }
|
||||
}
|
||||
|
||||
// otherwise leave expression as it is
|
||||
return super.visitGetValue(expression)
|
||||
}
|
||||
}
|
||||
val defaultsMap = extractDefaultValuesFromConstructor(irClass)
|
||||
return fun(initializer: IrExpressionBody): IrExpression {
|
||||
val rawExpression = initializer.expression
|
||||
val expression =
|
||||
if (rawExpression is IrGetValueImpl && rawExpression.origin == IrStatementOrigin.INITIALIZE_PROPERTY_FROM_PARAMETER) {
|
||||
// this is a primary constructor property, use corresponding default of value parameter
|
||||
defaultsMap.getValue(rawExpression.symbol.descriptor as ParameterDescriptor)!!
|
||||
} else {
|
||||
rawExpression
|
||||
}
|
||||
return expression.deepCopyWithVariables().transform(initializerTransformer, null)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+50
-102
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
@@ -9,10 +9,12 @@ import org.jetbrains.kotlin.backend.common.deepCopyWithVariables
|
||||
import org.jetbrains.kotlin.backend.common.lower.irThrow
|
||||
import org.jetbrains.kotlin.codegen.CompilationException
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.ir.IrStatement
|
||||
import org.jetbrains.kotlin.ir.builders.*
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.addField
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpressionBody
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrDelegatingConstructorCallImpl
|
||||
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
@@ -23,15 +25,14 @@ import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
import org.jetbrains.kotlin.utils.getOrPutNullable
|
||||
import org.jetbrains.kotlinx.serialization.compiler.backend.common.SerializableCodegen
|
||||
import org.jetbrains.kotlinx.serialization.compiler.backend.common.serialName
|
||||
import org.jetbrains.kotlinx.serialization.compiler.diagnostic.serializableAnnotationIsUseless
|
||||
import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPluginContext
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.*
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.ARRAY_MASK_FIELD_MISSING_FUNC_FQ
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.MISSING_FIELD_EXC
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.SERIAL_DESC_FIELD
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.SINGLE_MASK_FIELD_MISSING_FUNC_FQ
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.initializedDescriptorFieldName
|
||||
|
||||
class SerializableIrGenerator(
|
||||
@@ -50,11 +51,6 @@ class SerializableIrGenerator(
|
||||
|
||||
private val addElementFun = serialDescImplClass.findFunctionSymbol(CallingConventions.addElement)
|
||||
|
||||
val throwMissedFieldExceptionFunc =
|
||||
if (useFieldMissingOptimization) compilerContext.referenceFunctions(SINGLE_MASK_FIELD_MISSING_FUNC_FQ).single() else null
|
||||
val throwMissedFieldExceptionArrayFunc =
|
||||
if (useFieldMissingOptimization) compilerContext.referenceFunctions(ARRAY_MASK_FIELD_MISSING_FUNC_FQ).single() else null
|
||||
|
||||
private fun IrClass.hasSerializableAnnotationWithoutArgs(): Boolean {
|
||||
val annot = getAnnotation(SerializationAnnotations.serializableAnnotationFqName) ?: return false
|
||||
|
||||
@@ -69,7 +65,40 @@ class SerializableIrGenerator(
|
||||
|
||||
override fun generateInternalConstructor(constructorDescriptor: ClassConstructorDescriptor) =
|
||||
irClass.contributeConstructor(constructorDescriptor) { ctor ->
|
||||
val transformFieldInitializer = buildInitializersRemapping(irClass)
|
||||
val thiz = irClass.thisReceiver!!
|
||||
val serializableProperties = properties.serializableProperties
|
||||
|
||||
val serialDescs = serializableProperties.map { it.descriptor }.toSet()
|
||||
|
||||
val propertyByParamReplacer: (ValueParameterDescriptor) -> IrExpression? =
|
||||
createPropertyByParamReplacer(irClass, serializableProperties, thiz, bindingContext)
|
||||
|
||||
val initializerAdapter: (IrExpressionBody) -> IrExpression = createInitializerAdapter(irClass, propertyByParamReplacer)
|
||||
|
||||
|
||||
var current: PropertyDescriptor? = null
|
||||
val statementsAfterSerializableProperty: MutableMap<PropertyDescriptor?, MutableList<IrStatement>> = mutableMapOf()
|
||||
irClass.declarations.asSequence().forEach {
|
||||
when {
|
||||
// only properties with backing field
|
||||
it is IrProperty && it.backingField != null -> {
|
||||
if (it.descriptor in serialDescs) {
|
||||
current = it.descriptor
|
||||
} else if (it.backingField?.initializer != null) {
|
||||
// skip transient lateinit or deferred properties (with null initializer)
|
||||
val expression = initializerAdapter(it.backingField!!.initializer!!)
|
||||
|
||||
statementsAfterSerializableProperty.getOrPutNullable(current, { mutableListOf() })
|
||||
.add(irSetField(irGet(thiz), it.backingField!!, expression))
|
||||
}
|
||||
}
|
||||
it is IrAnonymousInitializer -> {
|
||||
val statements = it.body.deepCopyWithVariables().statements
|
||||
statementsAfterSerializableProperty.getOrPutNullable(current, { mutableListOf() })
|
||||
.addAll(statements)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Missing field exception parts
|
||||
val exceptionFqn = getSerializationPackageFqn(MISSING_FIELD_EXC)
|
||||
@@ -77,16 +106,19 @@ class SerializableIrGenerator(
|
||||
.single { it.owner.valueParameters.singleOrNull()?.type?.isString() == true }
|
||||
val exceptionType = exceptionCtorRef.owner.returnType
|
||||
|
||||
val serializableProperties = properties.serializableProperties
|
||||
val seenVarsOffset = serializableProperties.bitMaskSlotCount()
|
||||
val seenVars = (0 until seenVarsOffset).map { ctor.valueParameters[it] }
|
||||
|
||||
val thiz = irClass.thisReceiver!!
|
||||
|
||||
val superClass = irClass.getSuperClassOrAny()
|
||||
var startPropOffset: Int = 0
|
||||
|
||||
if (useFieldMissingOptimization) {
|
||||
generateOptimizedGoldenMaskCheck(seenVars)
|
||||
|
||||
if (useFieldMissingOptimization() &&
|
||||
// for abstract classes fields MUST BE checked in child classes
|
||||
!serializableDescriptor.isAbstractSerializableClass() && !serializableDescriptor.isSealedSerializableClass()
|
||||
) {
|
||||
generateGoldenMaskCheck(seenVars, properties, getSerialDescriptorExpr())
|
||||
}
|
||||
when {
|
||||
superClass.symbol == compilerContext.irBuiltIns.anyClass -> generateAnySuperConstructorCall(toBuilder = this@contributeConstructor)
|
||||
@@ -96,6 +128,7 @@ class SerializableIrGenerator(
|
||||
else -> generateSuperNonSerializableCall(superClass)
|
||||
}
|
||||
|
||||
statementsAfterSerializableProperty[null]?.forEach { +it }
|
||||
for (index in startPropOffset until serializableProperties.size) {
|
||||
val prop = serializableProperties[index]
|
||||
val paramRef = ctor.valueParameters[index + seenVarsOffset]
|
||||
@@ -106,13 +139,14 @@ class SerializableIrGenerator(
|
||||
|
||||
val ifNotSeenExpr: IrExpression = if (prop.optional) {
|
||||
val initializerBody =
|
||||
requireNotNull(transformFieldInitializer(prop.irField)) { "Optional value without an initializer" } // todo: filter abstract here
|
||||
requireNotNull(initializerAdapter(prop.irField.initializer!!)) { "Optional value without an initializer" } // todo: filter abstract here
|
||||
irSetField(irGet(thiz), backingFieldToAssign, initializerBody)
|
||||
} else {
|
||||
// property required
|
||||
if (useFieldMissingOptimization) {
|
||||
if (useFieldMissingOptimization()) {
|
||||
// field definitely not empty as it's checked before - no need another IF, only assign property from param
|
||||
+assignParamExpr
|
||||
statementsAfterSerializableProperty[prop.descriptor]?.forEach { +it }
|
||||
continue
|
||||
} else {
|
||||
irThrow(irInvoke(null, exceptionCtorRef, irString(prop.name), typeHint = exceptionType))
|
||||
@@ -130,97 +164,11 @@ class SerializableIrGenerator(
|
||||
)
|
||||
|
||||
+irIfThenElse(compilerContext.irBuiltIns.unitType, propNotSeenTest, ifNotSeenExpr, assignParamExpr)
|
||||
}
|
||||
|
||||
// remaining initializers of variables
|
||||
val serialDescs = serializableProperties.map { it.descriptor }.toSet()
|
||||
irClass.declarations.asSequence()
|
||||
.filterIsInstance<IrProperty>()
|
||||
.filter { it.descriptor !in serialDescs }
|
||||
.filter { it.backingField != null }
|
||||
.mapNotNull { prop -> transformFieldInitializer(prop.backingField!!)?.let { prop to it } }
|
||||
.forEach { (prop, expr) -> +irSetField(irGet(thiz), prop.backingField!!, expr) }
|
||||
|
||||
// init blocks
|
||||
irClass.declarations.asSequence()
|
||||
.filterIsInstance<IrAnonymousInitializer>()
|
||||
.forEach { initializer ->
|
||||
initializer.body.deepCopyWithVariables().statements.forEach { +it }
|
||||
}
|
||||
}
|
||||
|
||||
private fun IrBlockBodyBuilder.generateOptimizedGoldenMaskCheck(seenVars: List<IrValueParameter>) {
|
||||
if (serializableDescriptor.isAbstractSerializableClass() || serializableDescriptor.isSealedSerializableClass()) {
|
||||
// for abstract classes fields MUST BE checked in child classes
|
||||
return
|
||||
}
|
||||
|
||||
val fieldsMissedTest: IrExpression
|
||||
val throwErrorExpr: IrExpression
|
||||
|
||||
val maskSlotCount = seenVars.size
|
||||
if (maskSlotCount == 1) {
|
||||
val goldenMask = getGoldenMask()
|
||||
|
||||
throwErrorExpr = irInvoke(
|
||||
null,
|
||||
throwMissedFieldExceptionFunc!!,
|
||||
irGet(seenVars[0]),
|
||||
irInt(goldenMask),
|
||||
getSerialDescriptorExpr(),
|
||||
typeHint = compilerContext.irBuiltIns.unitType
|
||||
)
|
||||
|
||||
fieldsMissedTest = irNotEquals(
|
||||
irInt(goldenMask),
|
||||
irBinOp(
|
||||
OperatorNameConventions.AND,
|
||||
irInt(goldenMask),
|
||||
irGet(seenVars[0])
|
||||
)
|
||||
)
|
||||
} else {
|
||||
val goldenMaskList = getGoldenMaskList()
|
||||
|
||||
var compositeExpression: IrExpression? = null
|
||||
for (i in goldenMaskList.indices) {
|
||||
val singleCheckExpr = irNotEquals(
|
||||
irInt(goldenMaskList[i]),
|
||||
irBinOp(
|
||||
OperatorNameConventions.AND,
|
||||
irInt(goldenMaskList[i]),
|
||||
irGet(seenVars[i])
|
||||
)
|
||||
)
|
||||
|
||||
compositeExpression = if (compositeExpression == null) {
|
||||
singleCheckExpr
|
||||
} else {
|
||||
irBinOp(
|
||||
OperatorNameConventions.OR,
|
||||
compositeExpression,
|
||||
singleCheckExpr
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fieldsMissedTest = compositeExpression!!
|
||||
|
||||
throwErrorExpr = irBlock {
|
||||
+irInvoke(
|
||||
null,
|
||||
throwMissedFieldExceptionArrayFunc!!,
|
||||
createPrimitiveArrayOfExpression(compilerContext.irBuiltIns.intType, goldenMaskList.indices.map { irGet(seenVars[it]) }),
|
||||
createPrimitiveArrayOfExpression(compilerContext.irBuiltIns.intType, goldenMaskList.map { irInt(it) }),
|
||||
getSerialDescriptorExpr(),
|
||||
typeHint = compilerContext.irBuiltIns.unitType
|
||||
)
|
||||
statementsAfterSerializableProperty[prop.descriptor]?.forEach { +it }
|
||||
}
|
||||
}
|
||||
|
||||
+irIfThen(compilerContext.irBuiltIns.unitType, fieldsMissedTest, throwErrorExpr)
|
||||
}
|
||||
|
||||
private fun IrBlockBodyBuilder.getSerialDescriptorExpr(): IrExpression {
|
||||
return if (serializableDescriptor.shouldHaveGeneratedSerializer && staticDescriptor) {
|
||||
val serializer = serializableDescriptor.classSerializer!!
|
||||
|
||||
+129
-28
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
@@ -12,10 +12,8 @@ import org.jetbrains.kotlin.backend.common.lower.irThrow
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.ir.builders.*
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.expressions.IrConstructorCall
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.*
|
||||
import org.jetbrains.kotlin.ir.expressions.mapValueParametersIndexed
|
||||
import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.impl.IrAnonymousInitializerSymbolImpl
|
||||
@@ -23,13 +21,12 @@ import org.jetbrains.kotlin.ir.types.*
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.platform.jvm.isJvm
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.calls.components.hasDefaultValue
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
import org.jetbrains.kotlinx.serialization.compiler.backend.common.SerialTypeInfo
|
||||
import org.jetbrains.kotlinx.serialization.compiler.backend.common.SerializerCodegen
|
||||
import org.jetbrains.kotlinx.serialization.compiler.backend.common.getSerialTypeInfo
|
||||
import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.SerializerCodegenImpl
|
||||
import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.SerializerForEnumsCodegen
|
||||
import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationDescriptorSerializerPlugin
|
||||
import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPluginContext
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.*
|
||||
@@ -243,9 +240,6 @@ open class SerializerIrGenerator(
|
||||
|
||||
override fun generateSave(function: FunctionDescriptor) = irClass.contributeFunction(function) { saveFunc ->
|
||||
|
||||
val fieldInitializer: (SerializableProperty) -> IrExpression? =
|
||||
buildInitializersRemapping(serializableIrClass).run { { invoke(it.irField) } }
|
||||
|
||||
fun irThis(): IrExpression =
|
||||
IrGetValueImpl(startOffset, endOffset, saveFunc.dispatchReceiverParameter!!.symbol)
|
||||
|
||||
@@ -280,16 +274,22 @@ open class SerializerIrGenerator(
|
||||
// Ignore comparing to default values of properties from superclass,
|
||||
// because we do not have access to their fields (and initializers), if superclass is in another module.
|
||||
// In future, IR analogue of JVM's write$Self should be implemented.
|
||||
val superClass = irClass.getSuperClassOrAny().descriptor
|
||||
val superClass = serializableIrClass.getSuperClassOrAny().descriptor
|
||||
val ignoreIndexTo = if (superClass.isInternalSerializable) {
|
||||
bindingContext.serializablePropertiesFor(superClass).size
|
||||
} else {
|
||||
-1
|
||||
}
|
||||
|
||||
val propertyByParamReplacer: (ValueParameterDescriptor) -> IrExpression? =
|
||||
createPropertyByParamReplacer(serializableIrClass, serializableProperties, objectToSerialize, bindingContext)
|
||||
|
||||
val thisSymbol = serializableIrClass.thisReceiver!!.symbol
|
||||
val initializerAdapter: (IrExpressionBody) -> IrExpression =
|
||||
createInitializerAdapter(serializableIrClass, propertyByParamReplacer, thisSymbol to { irGet(objectToSerialize) })
|
||||
|
||||
// internal serialization via virtual calls?
|
||||
for ((index, property) in serializableProperties.filter { !it.transient }.withIndex()) {
|
||||
for ((index, property) in serializableProperties.withIndex()) {
|
||||
// output.writeXxxElementValue(classDesc, index, value)
|
||||
val elementCall = formEncodeDecodePropertyCall(irGet(localOutput), saveFunc.dispatchReceiverParameter!!, property, {innerSerial, sti ->
|
||||
val f =
|
||||
@@ -314,11 +314,12 @@ open class SerializerIrGenerator(
|
||||
+elementCall
|
||||
} else {
|
||||
// emit check:
|
||||
// if (obj.prop != DEFAULT_VALUE || output.shouldEncodeElementDefault(this.descriptor, i))
|
||||
// if ( if (output.shouldEncodeElementDefault(this.descriptor, i)) true else {obj.prop != DEFAULT_VALUE} ) {
|
||||
// output.encodeIntElement(this.descriptor, i, obj.prop)
|
||||
// block {obj.prop != DEFAULT_VALUE} may contain several statements
|
||||
val shouldEncodeFunc = kOutputClass.referenceMethod(CallingConventions.shouldEncodeDefault)
|
||||
val partA = irNotEquals(property.irGet(), fieldInitializer(property)!!)
|
||||
val partB = irInvoke(irGet(localOutput), shouldEncodeFunc, irGet(localSerialDesc), irInt(index))
|
||||
val partA = irInvoke(irGet(localOutput), shouldEncodeFunc, irGet(localSerialDesc), irInt(index))
|
||||
val partB = irNotEquals(property.irGet(), initializerAdapter(property.irField.initializer!!))
|
||||
// Ir infrastructure does not have dedicated symbol for ||, so
|
||||
// `a || b == if (a) true else b`, see org.jetbrains.kotlin.ir.builders.PrimitivesKt.oror
|
||||
val condition = irIfThenElse(compilerContext.irBuiltIns.booleanType, partA, irTrue(), partB)
|
||||
@@ -354,8 +355,8 @@ open class SerializerIrGenerator(
|
||||
}
|
||||
|
||||
// returns null: Any? for boxed types and 0: <number type> for primitives
|
||||
private fun IrBuilderWithScope.defaultValueAndType(prop: SerializableProperty): Pair<IrExpression, IrType> {
|
||||
val kType = prop.descriptor.returnType!!
|
||||
private fun IrBuilderWithScope.defaultValueAndType(descriptor: PropertyDescriptor): Pair<IrExpression, IrType> {
|
||||
val kType = descriptor.returnType!!
|
||||
val T = kType.toIrType()
|
||||
val defaultPrimitive: IrExpression? = when {
|
||||
T.isInt() -> IrConstImpl.int(startOffset, endOffset, T, 0)
|
||||
@@ -397,12 +398,26 @@ open class SerializerIrGenerator(
|
||||
// calculating bit mask vars
|
||||
val blocksCnt = serializableProperties.bitMaskSlotCount()
|
||||
|
||||
val serialPropertiesIndexes = serializableProperties
|
||||
.mapIndexed { i, property -> property to i }
|
||||
.associate { (p, i) -> p.descriptor to i }
|
||||
|
||||
val transients = serializableIrClass.declarations.asSequence()
|
||||
.filterIsInstance<IrProperty>()
|
||||
.filter { !serialPropertiesIndexes.containsKey(it.descriptor) }
|
||||
.filter { it.backingField != null }
|
||||
|
||||
// var bitMask0 = 0, bitMask1 = 0...
|
||||
val bitMasks = (0 until blocksCnt).map { irTemporary(irInt(0), "bitMask$it", isMutable = true) }
|
||||
// var local0 = null, local1 = null ...
|
||||
val localProps = serializableProperties.mapIndexed { i, prop ->
|
||||
val (expr, type) = defaultValueAndType(prop)
|
||||
irTemporary(expr, "local$i", type, isMutable = true)
|
||||
val serialPropertiesMap = serializableProperties.mapIndexed { i, prop -> i to prop.descriptor }.associate { (i, descriptor) ->
|
||||
val (expr, type) = defaultValueAndType(descriptor)
|
||||
descriptor to irTemporary(expr, "local$i", type, isMutable = true)
|
||||
}
|
||||
// var transient0 = null, transient0 = null ...
|
||||
val transientsPropertiesMap = transients.mapIndexed { i, prop -> i to prop.descriptor }.associate { (i, descriptor) ->
|
||||
val (expr, type) = defaultValueAndType(descriptor)
|
||||
descriptor to irTemporary(expr, "transient$i", type, isMutable = true)
|
||||
}
|
||||
|
||||
//input = input.beginStructure(...)
|
||||
@@ -423,7 +438,7 @@ open class SerializerIrGenerator(
|
||||
inputClass.referenceMethod(
|
||||
"${CallingConventions.decode}${sti.elementMethodPrefix}Serializable${CallingConventions.elementPostfix}", {it.valueParameters.size == 4}
|
||||
) to listOf(
|
||||
localSerialDesc.get(), irInt(index), innerSerial, localProps[index].get()
|
||||
localSerialDesc.get(), irInt(index), innerSerial, serialPropertiesMap.getValue(property.descriptor).get()
|
||||
)
|
||||
}, {
|
||||
inputClass.referenceMethod(
|
||||
@@ -434,7 +449,7 @@ open class SerializerIrGenerator(
|
||||
}, returnTypeHint = property.type.toIrType())
|
||||
// local$i = localInput.decode...(...)
|
||||
+irSet(
|
||||
localProps[index].symbol,
|
||||
serialPropertiesMap.getValue(property.descriptor).symbol,
|
||||
decodeFuncToCall
|
||||
)
|
||||
// bitMask[i] |= 1 << x
|
||||
@@ -491,17 +506,103 @@ open class SerializerIrGenerator(
|
||||
irGet(localSerialDesc)
|
||||
)
|
||||
|
||||
// todo: set properties in external deserialization
|
||||
var args: List<IrExpression> = localProps.map { it.get() }
|
||||
val typeArgs = (loadFunc.returnType as IrSimpleType).arguments.map { (it as IrTypeProjection).type }
|
||||
val ctor: IrConstructorSymbol = if (serializableDescriptor.isInternalSerializable) {
|
||||
if (serializableDescriptor.isInternalSerializable) {
|
||||
var args: List<IrExpression> = serializableProperties.map { serialPropertiesMap.getValue(it.descriptor).get() }
|
||||
args = bitMasks.map { irGet(it) } + args + irNull()
|
||||
serializableSyntheticConstructor(serializableIrClass)
|
||||
val ctor: IrConstructorSymbol = serializableSyntheticConstructor(serializableIrClass)
|
||||
+irReturn(irInvoke(null, ctor, typeArgs, args))
|
||||
} else {
|
||||
compilerContext.referenceConstructors(serializableDescriptor.fqNameSafe).single { it.owner.isPrimary }
|
||||
}
|
||||
// check properties presence
|
||||
val serialDescIrProperty = irClass.searchForProperty(generatedSerialDescPropertyDescriptor!!)
|
||||
val serialDescriptorExpr = irGetField(null, serialDescIrProperty.backingField!!)
|
||||
generateGoldenMaskCheck(bitMasks, properties, serialDescriptorExpr)
|
||||
|
||||
+irReturn(irInvoke(null, ctor, typeArgs, args))
|
||||
|
||||
val ctor: IrConstructorSymbol =
|
||||
compilerContext.referenceConstructors(serializableDescriptor.fqNameSafe).single { it.owner.isPrimary }
|
||||
val params = ctor.owner.valueParameters
|
||||
|
||||
|
||||
val variableByParamReplacer: (ValueParameterDescriptor) -> IrExpression? = {
|
||||
val propertyDescriptor = bindingContext[BindingContext.VALUE_PARAMETER_AS_PROPERTY, it]
|
||||
if (propertyDescriptor != null) {
|
||||
val serializable = serialPropertiesMap[propertyDescriptor]
|
||||
(serializable ?: transientsPropertiesMap[propertyDescriptor])?.get()
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
val initializerAdapter: (IrExpressionBody) -> IrExpression =
|
||||
createInitializerAdapter(serializableIrClass, variableByParamReplacer)
|
||||
|
||||
// constructor args:
|
||||
val ctorArgs = params.map { parameter ->
|
||||
val parameterDescriptor = parameter.descriptor as ValueParameterDescriptor
|
||||
val propertyDescriptor = bindingContext[BindingContext.VALUE_PARAMETER_AS_PROPERTY, parameterDescriptor]!!
|
||||
val serialProperty = serialPropertiesMap[propertyDescriptor]
|
||||
|
||||
// null if transient
|
||||
if (serialProperty != null) {
|
||||
val index = serialPropertiesIndexes.getValue(propertyDescriptor)
|
||||
if (parameterDescriptor.hasDefaultValue()) {
|
||||
val propNotSeenTest =
|
||||
irEquals(
|
||||
irInt(0),
|
||||
irBinOp(
|
||||
OperatorNameConventions.AND,
|
||||
bitMasks[index / 32].get(),
|
||||
irInt(1 shl (index % 32))
|
||||
)
|
||||
)
|
||||
|
||||
// if(mask$j && propertyMask == 0) local$i = <initializer>
|
||||
val defaultValueExp = parameter.defaultValue!!
|
||||
val expr = initializerAdapter(defaultValueExp)
|
||||
+irIfThen(propNotSeenTest, irSet(serialProperty.symbol, expr))
|
||||
}
|
||||
serialProperty.get()
|
||||
} else {
|
||||
val transientVar = transientsPropertiesMap.getValue(propertyDescriptor)
|
||||
if (parameterDescriptor.hasDefaultValue()) {
|
||||
val defaultValueExp = parameter.defaultValue!!
|
||||
val expr = initializerAdapter(defaultValueExp)
|
||||
+irSet(transientVar.symbol, expr)
|
||||
}
|
||||
transientVar.get()
|
||||
}
|
||||
}
|
||||
|
||||
val serializerVar = irTemporary(irInvoke(null, ctor, typeArgs, ctorArgs), "serializable")
|
||||
generateSetStandaloneProperties(serializerVar, serialPropertiesMap::getValue, serialPropertiesIndexes::getValue, bitMasks)
|
||||
+irReturn(irGet(serializerVar))
|
||||
}
|
||||
}
|
||||
|
||||
private fun IrBlockBodyBuilder.generateSetStandaloneProperties(
|
||||
serializableVar: IrVariable,
|
||||
propVars: (PropertyDescriptor) -> IrVariable,
|
||||
propIndexes: (PropertyDescriptor) -> Int,
|
||||
bitMasks: List<IrVariable>
|
||||
) {
|
||||
for (property in properties.serializableStandaloneProperties) {
|
||||
val localPropIndex = propIndexes(property.descriptor)
|
||||
// generate setter call
|
||||
val setter = property.getIrPropertyFrom(serializableIrClass).setter!!
|
||||
val propSeenTest =
|
||||
irNotEquals(
|
||||
irInt(0),
|
||||
irBinOp(
|
||||
OperatorNameConventions.AND,
|
||||
irGet(bitMasks[localPropIndex / 32]),
|
||||
irInt(1 shl (localPropIndex % 32))
|
||||
)
|
||||
)
|
||||
|
||||
val setterInvokeExpr = irSet(setter.returnType, irGet(serializableVar), setter.symbol, irGet(propVars(property.descriptor)))
|
||||
|
||||
+irIfThen(propSeenTest, setterInvokeExpr)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
+15
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
* Copyright 2010-2021 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,6 +17,7 @@
|
||||
package org.jetbrains.kotlinx.serialization.compiler.backend.jvm
|
||||
|
||||
import org.jetbrains.kotlin.codegen.*
|
||||
import org.jetbrains.kotlin.config.ApiVersion
|
||||
import org.jetbrains.kotlin.descriptors.ClassConstructorDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||
@@ -30,6 +31,7 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassOrAny
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.OtherOrigin
|
||||
import org.jetbrains.kotlinx.serialization.compiler.backend.common.*
|
||||
import org.jetbrains.kotlinx.serialization.compiler.diagnostic.VersionReader
|
||||
import org.jetbrains.kotlinx.serialization.compiler.diagnostic.serializableAnnotationIsUseless
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.*
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.ARRAY_MASK_FIELD_MISSING_FUNC_NAME
|
||||
@@ -45,6 +47,8 @@ class SerializableCodegenImpl(
|
||||
) : SerializableCodegen(classCodegen.descriptor, classCodegen.bindingContext) {
|
||||
|
||||
private val thisAsmType = classCodegen.typeMapper.mapClass(serializableDescriptor)
|
||||
private val fieldMissingOptimizationVersion = ApiVersion.parse("1.1")!!
|
||||
private val useFieldMissingOptimization = canUseFieldMissingOptimization()
|
||||
|
||||
companion object {
|
||||
fun generateSerializableExtensions(codegen: ImplementationBodyCodegen) {
|
||||
@@ -289,7 +293,7 @@ class SerializableCodegenImpl(
|
||||
val allPresentsLabel = Label()
|
||||
val maskSlotCount = properties.serializableProperties.bitMaskSlotCount()
|
||||
if (maskSlotCount == 1) {
|
||||
val goldenMask = getGoldenMask()
|
||||
val goldenMask = properties.goldenMask
|
||||
|
||||
iconst(goldenMask)
|
||||
dup()
|
||||
@@ -310,7 +314,7 @@ class SerializableCodegenImpl(
|
||||
} else {
|
||||
val fieldsMissingLabel = Label()
|
||||
|
||||
val goldenMaskList = getGoldenMaskList()
|
||||
val goldenMaskList = properties.goldenMaskList
|
||||
goldenMaskList.forEachIndexed { i, goldenMask ->
|
||||
val maskIndex = maskVar + i
|
||||
// if( (goldenMask & seen) != goldenMask )
|
||||
@@ -404,4 +408,12 @@ class SerializableCodegenImpl(
|
||||
this.gen(param.defaultValue, mapType)
|
||||
this.v.putfield(thisAsmType.internalName, prop.name.asString(), mapType.descriptor)
|
||||
}
|
||||
|
||||
private fun canUseFieldMissingOptimization(): Boolean {
|
||||
val implementationVersion = VersionReader.getVersionsForCurrentModuleFromContext(
|
||||
currentDeclaration.module,
|
||||
bindingContext
|
||||
)?.implementationVersion
|
||||
return if (implementationVersion != null) implementationVersion >= fieldMissingOptimizationVersion else false
|
||||
}
|
||||
}
|
||||
|
||||
+28
@@ -88,6 +88,34 @@ class SerializableProperties(private val serializableClass: ClassDescriptor, val
|
||||
?.original?.valueParameters?.any { it.declaresDefaultValue() } ?: false
|
||||
}
|
||||
|
||||
internal val SerializableProperties.goldenMask: Int
|
||||
get() {
|
||||
var goldenMask = 0
|
||||
var requiredBit = 1
|
||||
for (property in serializableProperties) {
|
||||
if (!property.optional) {
|
||||
goldenMask = goldenMask or requiredBit
|
||||
}
|
||||
requiredBit = requiredBit shl 1
|
||||
}
|
||||
return goldenMask
|
||||
}
|
||||
|
||||
internal val SerializableProperties.goldenMaskList: List<Int>
|
||||
get() {
|
||||
val maskSlotCount = serializableProperties.bitMaskSlotCount()
|
||||
val goldenMaskList = MutableList(maskSlotCount) { 0 }
|
||||
|
||||
for (i in serializableProperties.indices) {
|
||||
if (!serializableProperties[i].optional) {
|
||||
val slotNumber = i / 32
|
||||
val bitInSlot = i % 32
|
||||
goldenMaskList[slotNumber] = goldenMaskList[slotNumber] or (1 shl bitInSlot)
|
||||
}
|
||||
}
|
||||
return goldenMaskList
|
||||
}
|
||||
|
||||
internal fun List<SerializableProperty>.bitMaskSlotCount() = size / 32 + 1
|
||||
internal fun bitMaskSlotAt(propertyIndex: Int) = propertyIndex / 32
|
||||
|
||||
|
||||
Reference in New Issue
Block a user