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
|
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.ClassConstructorDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.secondaryConstructors
|
import org.jetbrains.kotlin.resolve.descriptorUtil.secondaryConstructors
|
||||||
import org.jetbrains.kotlinx.serialization.compiler.diagnostic.VersionReader
|
|
||||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.*
|
import org.jetbrains.kotlinx.serialization.compiler.resolve.*
|
||||||
|
|
||||||
abstract class SerializableCodegen(
|
abstract class SerializableCodegen(
|
||||||
@@ -22,9 +19,6 @@ abstract class SerializableCodegen(
|
|||||||
protected val properties = bindingContext.serializablePropertiesFor(serializableDescriptor)
|
protected val properties = bindingContext.serializablePropertiesFor(serializableDescriptor)
|
||||||
protected val staticDescriptor = serializableDescriptor.declaredTypeParameters.isEmpty()
|
protected val staticDescriptor = serializableDescriptor.declaredTypeParameters.isEmpty()
|
||||||
|
|
||||||
private val fieldMissingOptimizationVersion = ApiVersion.parse("1.1")!!
|
|
||||||
protected val useFieldMissingOptimization = canUseFieldMissingOptimization()
|
|
||||||
|
|
||||||
fun generate() {
|
fun generate() {
|
||||||
generateSyntheticInternalConstructor()
|
generateSyntheticInternalConstructor()
|
||||||
generateSyntheticMethods()
|
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 abstract fun generateInternalConstructor(constructorDescriptor: ClassConstructorDescriptor)
|
||||||
|
|
||||||
protected open fun generateWriteSelfMethod(methodDescriptor: FunctionDescriptor) {
|
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.
|
* 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.IrSimpleTypeImpl
|
||||||
import org.jetbrains.kotlin.ir.types.impl.makeTypeProjection
|
import org.jetbrains.kotlin.ir.types.impl.makeTypeProjection
|
||||||
import org.jetbrains.kotlin.ir.util.*
|
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.js.resolve.diagnostics.findPsi
|
||||||
import org.jetbrains.kotlin.name.ClassId
|
import org.jetbrains.kotlin.name.ClassId
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.platform.jvm.isJvm
|
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.classId
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.isEffectivelyExternal
|
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.isTypeParameter
|
||||||
import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
|
import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
|
||||||
import org.jetbrains.kotlin.types.typeUtil.representativeUpperBound
|
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.common.*
|
||||||
import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.*
|
import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.*
|
||||||
import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPluginContext
|
import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPluginContext
|
||||||
@@ -42,10 +45,20 @@ import org.jetbrains.kotlinx.serialization.compiler.resolve.*
|
|||||||
interface IrBuilderExtension {
|
interface IrBuilderExtension {
|
||||||
val compilerContext: SerializationPluginContext
|
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? {
|
private inline fun <reified T : IrDeclaration> IrClass.searchForDeclaration(descriptor: DeclarationDescriptor): T? {
|
||||||
return declarations.singleOrNull { it.descriptor == descriptor } as? 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) {
|
fun IrClass.contributeFunction(descriptor: FunctionDescriptor, bodyGen: IrBlockBodyBuilder.(IrFunction) -> Unit) {
|
||||||
val f: IrSimpleFunction = searchForDeclaration(descriptor) ?: compilerContext.symbolTable.referenceSimpleFunction(descriptor).owner
|
val f: IrSimpleFunction = searchForDeclaration(descriptor) ?: compilerContext.symbolTable.referenceSimpleFunction(descriptor).owner
|
||||||
// TODO: default parameters
|
// 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.
|
// 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
|
val SerializableProperty.irField: IrField get() = compilerContext.symbolTable.referenceField(this.descriptor).owner
|
||||||
|
|
||||||
fun SerializableProperty.getIrPropertyFrom(thisClass: IrClass): IrProperty {
|
fun IrClass.searchForProperty(descriptor: PropertyDescriptor): IrProperty {
|
||||||
val desc = this.descriptor
|
|
||||||
// this API is used to reference both current module descriptors and external ones (because serializable class can be in any of them),
|
// 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.
|
// 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) {
|
return searchForDeclaration(descriptor) ?: if (descriptor.module == compilerContext.moduleDescriptor) {
|
||||||
compilerContext.symbolTable.referenceProperty(desc).owner
|
compilerContext.symbolTable.referenceProperty(descriptor).owner
|
||||||
} else {
|
} 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(
|
fun generateSimplePropertyWithBackingField(
|
||||||
propertyDescriptor: PropertyDescriptor,
|
propertyDescriptor: PropertyDescriptor,
|
||||||
propertyParent: IrClass,
|
propertyParent: IrClass,
|
||||||
@@ -485,18 +618,45 @@ interface IrBuilderExtension {
|
|||||||
return defaultsMap + extractDefaultValuesFromConstructor(irClass.getSuperClassNotAny())
|
return defaultsMap + extractDefaultValuesFromConstructor(irClass.getSuperClassNotAny())
|
||||||
}
|
}
|
||||||
|
|
||||||
fun buildInitializersRemapping(irClass: IrClass): (IrField) -> IrExpression? {
|
/*
|
||||||
val defaultsMap = extractDefaultValuesFromConstructor(irClass)
|
Creates an initializer adapter function that can replace IR expressions of getting constructor parameter value by some other expression.
|
||||||
return fun(f: IrField): IrExpression? {
|
Also adapter may replace IR expression of getting `this` value by another expression.
|
||||||
val i = f.initializer?.expression ?: return null
|
*/
|
||||||
val irExpression =
|
fun createInitializerAdapter(
|
||||||
if (i is IrGetValueImpl && i.origin == IrStatementOrigin.INITIALIZE_PROPERTY_FROM_PARAMETER) {
|
irClass: IrClass,
|
||||||
// this is a primary constructor property, use corresponding default of value parameter
|
paramGetReplacer: (ValueParameterDescriptor) -> IrExpression?,
|
||||||
defaultsMap.getValue(i.symbol.descriptor as ParameterDescriptor)
|
thisGetReplacer: Pair<IrValueSymbol, () -> IrExpression>? = null
|
||||||
} else {
|
): (IrExpressionBody) -> IrExpression {
|
||||||
i
|
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.
|
* 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.backend.common.lower.irThrow
|
||||||
import org.jetbrains.kotlin.codegen.CompilationException
|
import org.jetbrains.kotlin.codegen.CompilationException
|
||||||
import org.jetbrains.kotlin.descriptors.*
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
|
import org.jetbrains.kotlin.ir.IrStatement
|
||||||
import org.jetbrains.kotlin.ir.builders.*
|
import org.jetbrains.kotlin.ir.builders.*
|
||||||
import org.jetbrains.kotlin.ir.builders.declarations.addField
|
import org.jetbrains.kotlin.ir.builders.declarations.addField
|
||||||
import org.jetbrains.kotlin.ir.declarations.*
|
import org.jetbrains.kotlin.ir.declarations.*
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
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.expressions.impl.IrDelegatingConstructorCallImpl
|
||||||
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
|
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
|
||||||
import org.jetbrains.kotlin.ir.types.*
|
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.fqNameSafe
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
||||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
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.SerializableCodegen
|
||||||
import org.jetbrains.kotlinx.serialization.compiler.backend.common.serialName
|
import org.jetbrains.kotlinx.serialization.compiler.backend.common.serialName
|
||||||
import org.jetbrains.kotlinx.serialization.compiler.diagnostic.serializableAnnotationIsUseless
|
import org.jetbrains.kotlinx.serialization.compiler.diagnostic.serializableAnnotationIsUseless
|
||||||
import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPluginContext
|
import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPluginContext
|
||||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.*
|
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.MISSING_FIELD_EXC
|
||||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.SERIAL_DESC_FIELD
|
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
|
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.initializedDescriptorFieldName
|
||||||
|
|
||||||
class SerializableIrGenerator(
|
class SerializableIrGenerator(
|
||||||
@@ -50,11 +51,6 @@ class SerializableIrGenerator(
|
|||||||
|
|
||||||
private val addElementFun = serialDescImplClass.findFunctionSymbol(CallingConventions.addElement)
|
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 {
|
private fun IrClass.hasSerializableAnnotationWithoutArgs(): Boolean {
|
||||||
val annot = getAnnotation(SerializationAnnotations.serializableAnnotationFqName) ?: return false
|
val annot = getAnnotation(SerializationAnnotations.serializableAnnotationFqName) ?: return false
|
||||||
|
|
||||||
@@ -69,7 +65,40 @@ class SerializableIrGenerator(
|
|||||||
|
|
||||||
override fun generateInternalConstructor(constructorDescriptor: ClassConstructorDescriptor) =
|
override fun generateInternalConstructor(constructorDescriptor: ClassConstructorDescriptor) =
|
||||||
irClass.contributeConstructor(constructorDescriptor) { ctor ->
|
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
|
// Missing field exception parts
|
||||||
val exceptionFqn = getSerializationPackageFqn(MISSING_FIELD_EXC)
|
val exceptionFqn = getSerializationPackageFqn(MISSING_FIELD_EXC)
|
||||||
@@ -77,16 +106,19 @@ class SerializableIrGenerator(
|
|||||||
.single { it.owner.valueParameters.singleOrNull()?.type?.isString() == true }
|
.single { it.owner.valueParameters.singleOrNull()?.type?.isString() == true }
|
||||||
val exceptionType = exceptionCtorRef.owner.returnType
|
val exceptionType = exceptionCtorRef.owner.returnType
|
||||||
|
|
||||||
val serializableProperties = properties.serializableProperties
|
|
||||||
val seenVarsOffset = serializableProperties.bitMaskSlotCount()
|
val seenVarsOffset = serializableProperties.bitMaskSlotCount()
|
||||||
val seenVars = (0 until seenVarsOffset).map { ctor.valueParameters[it] }
|
val seenVars = (0 until seenVarsOffset).map { ctor.valueParameters[it] }
|
||||||
|
|
||||||
val thiz = irClass.thisReceiver!!
|
|
||||||
val superClass = irClass.getSuperClassOrAny()
|
val superClass = irClass.getSuperClassOrAny()
|
||||||
var startPropOffset: Int = 0
|
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 {
|
when {
|
||||||
superClass.symbol == compilerContext.irBuiltIns.anyClass -> generateAnySuperConstructorCall(toBuilder = this@contributeConstructor)
|
superClass.symbol == compilerContext.irBuiltIns.anyClass -> generateAnySuperConstructorCall(toBuilder = this@contributeConstructor)
|
||||||
@@ -96,6 +128,7 @@ class SerializableIrGenerator(
|
|||||||
else -> generateSuperNonSerializableCall(superClass)
|
else -> generateSuperNonSerializableCall(superClass)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
statementsAfterSerializableProperty[null]?.forEach { +it }
|
||||||
for (index in startPropOffset until serializableProperties.size) {
|
for (index in startPropOffset until serializableProperties.size) {
|
||||||
val prop = serializableProperties[index]
|
val prop = serializableProperties[index]
|
||||||
val paramRef = ctor.valueParameters[index + seenVarsOffset]
|
val paramRef = ctor.valueParameters[index + seenVarsOffset]
|
||||||
@@ -106,13 +139,14 @@ class SerializableIrGenerator(
|
|||||||
|
|
||||||
val ifNotSeenExpr: IrExpression = if (prop.optional) {
|
val ifNotSeenExpr: IrExpression = if (prop.optional) {
|
||||||
val initializerBody =
|
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)
|
irSetField(irGet(thiz), backingFieldToAssign, initializerBody)
|
||||||
} else {
|
} else {
|
||||||
// property required
|
// property required
|
||||||
if (useFieldMissingOptimization) {
|
if (useFieldMissingOptimization()) {
|
||||||
// field definitely not empty as it's checked before - no need another IF, only assign property from param
|
// field definitely not empty as it's checked before - no need another IF, only assign property from param
|
||||||
+assignParamExpr
|
+assignParamExpr
|
||||||
|
statementsAfterSerializableProperty[prop.descriptor]?.forEach { +it }
|
||||||
continue
|
continue
|
||||||
} else {
|
} else {
|
||||||
irThrow(irInvoke(null, exceptionCtorRef, irString(prop.name), typeHint = exceptionType))
|
irThrow(irInvoke(null, exceptionCtorRef, irString(prop.name), typeHint = exceptionType))
|
||||||
@@ -130,97 +164,11 @@ class SerializableIrGenerator(
|
|||||||
)
|
)
|
||||||
|
|
||||||
+irIfThenElse(compilerContext.irBuiltIns.unitType, propNotSeenTest, ifNotSeenExpr, assignParamExpr)
|
+irIfThenElse(compilerContext.irBuiltIns.unitType, propNotSeenTest, ifNotSeenExpr, assignParamExpr)
|
||||||
}
|
|
||||||
|
|
||||||
// remaining initializers of variables
|
statementsAfterSerializableProperty[prop.descriptor]?.forEach { +it }
|
||||||
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
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
+irIfThen(compilerContext.irBuiltIns.unitType, fieldsMissedTest, throwErrorExpr)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun IrBlockBodyBuilder.getSerialDescriptorExpr(): IrExpression {
|
private fun IrBlockBodyBuilder.getSerialDescriptorExpr(): IrExpression {
|
||||||
return if (serializableDescriptor.shouldHaveGeneratedSerializer && staticDescriptor) {
|
return if (serializableDescriptor.shouldHaveGeneratedSerializer && staticDescriptor) {
|
||||||
val serializer = serializableDescriptor.classSerializer!!
|
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.
|
* 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.descriptors.*
|
||||||
import org.jetbrains.kotlin.ir.builders.*
|
import org.jetbrains.kotlin.ir.builders.*
|
||||||
import org.jetbrains.kotlin.ir.declarations.*
|
import org.jetbrains.kotlin.ir.declarations.*
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrConstructorCall
|
import org.jetbrains.kotlin.ir.expressions.*
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
|
||||||
import org.jetbrains.kotlin.ir.expressions.impl.*
|
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.IrConstructorSymbol
|
||||||
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
|
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
|
||||||
import org.jetbrains.kotlin.ir.symbols.impl.IrAnonymousInitializerSymbolImpl
|
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.ir.util.*
|
||||||
import org.jetbrains.kotlin.platform.jvm.isJvm
|
import org.jetbrains.kotlin.platform.jvm.isJvm
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
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.resolve.descriptorUtil.fqNameSafe
|
||||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||||
import org.jetbrains.kotlinx.serialization.compiler.backend.common.SerialTypeInfo
|
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.SerializerCodegen
|
||||||
import org.jetbrains.kotlinx.serialization.compiler.backend.common.getSerialTypeInfo
|
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.SerializationDescriptorSerializerPlugin
|
||||||
import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPluginContext
|
import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPluginContext
|
||||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.*
|
import org.jetbrains.kotlinx.serialization.compiler.resolve.*
|
||||||
@@ -243,9 +240,6 @@ open class SerializerIrGenerator(
|
|||||||
|
|
||||||
override fun generateSave(function: FunctionDescriptor) = irClass.contributeFunction(function) { saveFunc ->
|
override fun generateSave(function: FunctionDescriptor) = irClass.contributeFunction(function) { saveFunc ->
|
||||||
|
|
||||||
val fieldInitializer: (SerializableProperty) -> IrExpression? =
|
|
||||||
buildInitializersRemapping(serializableIrClass).run { { invoke(it.irField) } }
|
|
||||||
|
|
||||||
fun irThis(): IrExpression =
|
fun irThis(): IrExpression =
|
||||||
IrGetValueImpl(startOffset, endOffset, saveFunc.dispatchReceiverParameter!!.symbol)
|
IrGetValueImpl(startOffset, endOffset, saveFunc.dispatchReceiverParameter!!.symbol)
|
||||||
|
|
||||||
@@ -280,16 +274,22 @@ open class SerializerIrGenerator(
|
|||||||
// Ignore comparing to default values of properties from superclass,
|
// 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.
|
// 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.
|
// 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) {
|
val ignoreIndexTo = if (superClass.isInternalSerializable) {
|
||||||
bindingContext.serializablePropertiesFor(superClass).size
|
bindingContext.serializablePropertiesFor(superClass).size
|
||||||
} else {
|
} else {
|
||||||
-1
|
-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?
|
// 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)
|
// output.writeXxxElementValue(classDesc, index, value)
|
||||||
val elementCall = formEncodeDecodePropertyCall(irGet(localOutput), saveFunc.dispatchReceiverParameter!!, property, {innerSerial, sti ->
|
val elementCall = formEncodeDecodePropertyCall(irGet(localOutput), saveFunc.dispatchReceiverParameter!!, property, {innerSerial, sti ->
|
||||||
val f =
|
val f =
|
||||||
@@ -314,11 +314,12 @@ open class SerializerIrGenerator(
|
|||||||
+elementCall
|
+elementCall
|
||||||
} else {
|
} else {
|
||||||
// emit check:
|
// 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)
|
// output.encodeIntElement(this.descriptor, i, obj.prop)
|
||||||
|
// block {obj.prop != DEFAULT_VALUE} may contain several statements
|
||||||
val shouldEncodeFunc = kOutputClass.referenceMethod(CallingConventions.shouldEncodeDefault)
|
val shouldEncodeFunc = kOutputClass.referenceMethod(CallingConventions.shouldEncodeDefault)
|
||||||
val partA = irNotEquals(property.irGet(), fieldInitializer(property)!!)
|
val partA = irInvoke(irGet(localOutput), shouldEncodeFunc, irGet(localSerialDesc), irInt(index))
|
||||||
val partB = 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
|
// Ir infrastructure does not have dedicated symbol for ||, so
|
||||||
// `a || b == if (a) true else b`, see org.jetbrains.kotlin.ir.builders.PrimitivesKt.oror
|
// `a || b == if (a) true else b`, see org.jetbrains.kotlin.ir.builders.PrimitivesKt.oror
|
||||||
val condition = irIfThenElse(compilerContext.irBuiltIns.booleanType, partA, irTrue(), partB)
|
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
|
// returns null: Any? for boxed types and 0: <number type> for primitives
|
||||||
private fun IrBuilderWithScope.defaultValueAndType(prop: SerializableProperty): Pair<IrExpression, IrType> {
|
private fun IrBuilderWithScope.defaultValueAndType(descriptor: PropertyDescriptor): Pair<IrExpression, IrType> {
|
||||||
val kType = prop.descriptor.returnType!!
|
val kType = descriptor.returnType!!
|
||||||
val T = kType.toIrType()
|
val T = kType.toIrType()
|
||||||
val defaultPrimitive: IrExpression? = when {
|
val defaultPrimitive: IrExpression? = when {
|
||||||
T.isInt() -> IrConstImpl.int(startOffset, endOffset, T, 0)
|
T.isInt() -> IrConstImpl.int(startOffset, endOffset, T, 0)
|
||||||
@@ -397,12 +398,26 @@ open class SerializerIrGenerator(
|
|||||||
// calculating bit mask vars
|
// calculating bit mask vars
|
||||||
val blocksCnt = serializableProperties.bitMaskSlotCount()
|
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...
|
// var bitMask0 = 0, bitMask1 = 0...
|
||||||
val bitMasks = (0 until blocksCnt).map { irTemporary(irInt(0), "bitMask$it", isMutable = true) }
|
val bitMasks = (0 until blocksCnt).map { irTemporary(irInt(0), "bitMask$it", isMutable = true) }
|
||||||
// var local0 = null, local1 = null ...
|
// var local0 = null, local1 = null ...
|
||||||
val localProps = serializableProperties.mapIndexed { i, prop ->
|
val serialPropertiesMap = serializableProperties.mapIndexed { i, prop -> i to prop.descriptor }.associate { (i, descriptor) ->
|
||||||
val (expr, type) = defaultValueAndType(prop)
|
val (expr, type) = defaultValueAndType(descriptor)
|
||||||
irTemporary(expr, "local$i", type, isMutable = true)
|
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(...)
|
//input = input.beginStructure(...)
|
||||||
@@ -423,7 +438,7 @@ open class SerializerIrGenerator(
|
|||||||
inputClass.referenceMethod(
|
inputClass.referenceMethod(
|
||||||
"${CallingConventions.decode}${sti.elementMethodPrefix}Serializable${CallingConventions.elementPostfix}", {it.valueParameters.size == 4}
|
"${CallingConventions.decode}${sti.elementMethodPrefix}Serializable${CallingConventions.elementPostfix}", {it.valueParameters.size == 4}
|
||||||
) to listOf(
|
) to listOf(
|
||||||
localSerialDesc.get(), irInt(index), innerSerial, localProps[index].get()
|
localSerialDesc.get(), irInt(index), innerSerial, serialPropertiesMap.getValue(property.descriptor).get()
|
||||||
)
|
)
|
||||||
}, {
|
}, {
|
||||||
inputClass.referenceMethod(
|
inputClass.referenceMethod(
|
||||||
@@ -434,7 +449,7 @@ open class SerializerIrGenerator(
|
|||||||
}, returnTypeHint = property.type.toIrType())
|
}, returnTypeHint = property.type.toIrType())
|
||||||
// local$i = localInput.decode...(...)
|
// local$i = localInput.decode...(...)
|
||||||
+irSet(
|
+irSet(
|
||||||
localProps[index].symbol,
|
serialPropertiesMap.getValue(property.descriptor).symbol,
|
||||||
decodeFuncToCall
|
decodeFuncToCall
|
||||||
)
|
)
|
||||||
// bitMask[i] |= 1 << x
|
// bitMask[i] |= 1 << x
|
||||||
@@ -491,17 +506,103 @@ open class SerializerIrGenerator(
|
|||||||
irGet(localSerialDesc)
|
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 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()
|
args = bitMasks.map { irGet(it) } + args + irNull()
|
||||||
serializableSyntheticConstructor(serializableIrClass)
|
val ctor: IrConstructorSymbol = serializableSyntheticConstructor(serializableIrClass)
|
||||||
|
+irReturn(irInvoke(null, ctor, typeArgs, args))
|
||||||
} else {
|
} 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 {
|
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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with 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
|
package org.jetbrains.kotlinx.serialization.compiler.backend.jvm
|
||||||
|
|
||||||
import org.jetbrains.kotlin.codegen.*
|
import org.jetbrains.kotlin.codegen.*
|
||||||
|
import org.jetbrains.kotlin.config.ApiVersion
|
||||||
import org.jetbrains.kotlin.descriptors.ClassConstructorDescriptor
|
import org.jetbrains.kotlin.descriptors.ClassConstructorDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
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.descriptorUtil.module
|
||||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.OtherOrigin
|
import org.jetbrains.kotlin.resolve.jvm.diagnostics.OtherOrigin
|
||||||
import org.jetbrains.kotlinx.serialization.compiler.backend.common.*
|
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.diagnostic.serializableAnnotationIsUseless
|
||||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.*
|
import org.jetbrains.kotlinx.serialization.compiler.resolve.*
|
||||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.ARRAY_MASK_FIELD_MISSING_FUNC_NAME
|
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) {
|
) : SerializableCodegen(classCodegen.descriptor, classCodegen.bindingContext) {
|
||||||
|
|
||||||
private val thisAsmType = classCodegen.typeMapper.mapClass(serializableDescriptor)
|
private val thisAsmType = classCodegen.typeMapper.mapClass(serializableDescriptor)
|
||||||
|
private val fieldMissingOptimizationVersion = ApiVersion.parse("1.1")!!
|
||||||
|
private val useFieldMissingOptimization = canUseFieldMissingOptimization()
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun generateSerializableExtensions(codegen: ImplementationBodyCodegen) {
|
fun generateSerializableExtensions(codegen: ImplementationBodyCodegen) {
|
||||||
@@ -289,7 +293,7 @@ class SerializableCodegenImpl(
|
|||||||
val allPresentsLabel = Label()
|
val allPresentsLabel = Label()
|
||||||
val maskSlotCount = properties.serializableProperties.bitMaskSlotCount()
|
val maskSlotCount = properties.serializableProperties.bitMaskSlotCount()
|
||||||
if (maskSlotCount == 1) {
|
if (maskSlotCount == 1) {
|
||||||
val goldenMask = getGoldenMask()
|
val goldenMask = properties.goldenMask
|
||||||
|
|
||||||
iconst(goldenMask)
|
iconst(goldenMask)
|
||||||
dup()
|
dup()
|
||||||
@@ -310,7 +314,7 @@ class SerializableCodegenImpl(
|
|||||||
} else {
|
} else {
|
||||||
val fieldsMissingLabel = Label()
|
val fieldsMissingLabel = Label()
|
||||||
|
|
||||||
val goldenMaskList = getGoldenMaskList()
|
val goldenMaskList = properties.goldenMaskList
|
||||||
goldenMaskList.forEachIndexed { i, goldenMask ->
|
goldenMaskList.forEachIndexed { i, goldenMask ->
|
||||||
val maskIndex = maskVar + i
|
val maskIndex = maskVar + i
|
||||||
// if( (goldenMask & seen) != goldenMask )
|
// if( (goldenMask & seen) != goldenMask )
|
||||||
@@ -404,4 +408,12 @@ class SerializableCodegenImpl(
|
|||||||
this.gen(param.defaultValue, mapType)
|
this.gen(param.defaultValue, mapType)
|
||||||
this.v.putfield(thisAsmType.internalName, prop.name.asString(), mapType.descriptor)
|
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
|
?.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 List<SerializableProperty>.bitMaskSlotCount() = size / 32 + 1
|
||||||
internal fun bitMaskSlotAt(propertyIndex: Int) = propertyIndex / 32
|
internal fun bitMaskSlotAt(propertyIndex: Int) = propertyIndex / 32
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user