Generate writeSelf and load constructor even if they were not created by frontend
which is true in case for FIR which discourages purely synthetic declarations.
This commit is contained in:
+5
-8
@@ -18,9 +18,7 @@ import org.jetbrains.kotlin.ir.deepCopyWithVariables
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.*
|
||||
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrFieldSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrValueSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.impl.*
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
@@ -30,7 +28,6 @@ import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.platform.jvm.isJvm
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.isEffectivelyExternal
|
||||
import org.jetbrains.kotlin.resolve.jvm.JvmPrimitiveType
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPluginContext
|
||||
@@ -65,7 +62,7 @@ interface IrBuilderWithPluginContext {
|
||||
this.returnType = type
|
||||
name = Name.identifier("<anonymous>")
|
||||
visibility = DescriptorVisibilities.LOCAL
|
||||
origin = SERIALIZABLE_PLUGIN_ORIGIN
|
||||
origin = SERIALIZATION_PLUGIN_ORIGIN
|
||||
}
|
||||
function.body =
|
||||
DeclarationIrBuilder(compilerContext, function.symbol, startOffset, endOffset).irBlockBody(startOffset, endOffset, bodyGen)
|
||||
@@ -138,7 +135,7 @@ interface IrBuilderWithPluginContext {
|
||||
|
||||
fun IrClass.contributeAnonymousInitializer(bodyGen: IrBlockBodyBuilder.() -> Unit) {
|
||||
val symbol = IrAnonymousInitializerSymbolImpl(descriptor)
|
||||
factory.createAnonymousInitializer(startOffset, endOffset, SERIALIZABLE_PLUGIN_ORIGIN, symbol).also {
|
||||
factory.createAnonymousInitializer(startOffset, endOffset, SERIALIZATION_PLUGIN_ORIGIN, symbol).also {
|
||||
it.parent = this
|
||||
declarations.add(it)
|
||||
it.body = DeclarationIrBuilder(compilerContext, symbol, startOffset, endOffset).irBlockBody(startOffset, endOffset, bodyGen)
|
||||
@@ -299,7 +296,7 @@ interface IrBuilderWithPluginContext {
|
||||
endOffset = propertyParent.endOffset
|
||||
name = propertyName
|
||||
type = propertyType
|
||||
origin = SERIALIZABLE_PLUGIN_ORIGIN
|
||||
origin = SERIALIZATION_PLUGIN_ORIGIN
|
||||
isFinal = true
|
||||
this.visibility = DescriptorVisibilities.PRIVATE
|
||||
}.also { it.parent = propertyParent }
|
||||
@@ -309,7 +306,7 @@ interface IrBuilderWithPluginContext {
|
||||
endOffset = propertyParent.endOffset
|
||||
name = propertyName
|
||||
this.isVar = false
|
||||
origin = SERIALIZABLE_PLUGIN_ORIGIN
|
||||
origin = SERIALIZATION_PLUGIN_ORIGIN
|
||||
}
|
||||
|
||||
prop.apply {
|
||||
@@ -321,7 +318,7 @@ interface IrBuilderWithPluginContext {
|
||||
startOffset = propertyParent.startOffset
|
||||
endOffset = propertyParent.endOffset
|
||||
returnType = propertyType
|
||||
origin = SERIALIZABLE_PLUGIN_ORIGIN
|
||||
origin = SERIALIZATION_PLUGIN_ORIGIN
|
||||
this.visibility = visibility
|
||||
modality = Modality.FINAL
|
||||
}
|
||||
|
||||
+123
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Copyright 2010-2022 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.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlinx.serialization.compiler.backend.ir
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.addConstructor
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.addFunction
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.addTypeParameter
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.addValueParameter
|
||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
import org.jetbrains.kotlin.ir.util.companionObject
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.isInlineClass
|
||||
import org.jetbrains.kotlinx.serialization.compiler.backend.common.SerializerCodegen
|
||||
import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPluginContext
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.bitMaskSlotCount
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.hasCompanionObjectAsSerializer
|
||||
|
||||
/**
|
||||
* Generates only specific declarations, but NOT their bodies.
|
||||
* This pass is needed to be able to reference these declarations from other generated bodies
|
||||
* (e.g. to if we want to reference write$Self() from serialize(), we need to make sure that at least declaration of write$Self is already created.
|
||||
*
|
||||
* These functions were usually stubbed from descriptors, but since FIR discourages purely synthetic functions,
|
||||
* we manually add them here.
|
||||
*/
|
||||
class IrPreGenerator(
|
||||
val irClass: IrClass,
|
||||
compilerContext: SerializationPluginContext,
|
||||
) : BaseIrGenerator(irClass, compilerContext) {
|
||||
private val serialDescriptorSymbol = compilerContext.getClassFromRuntime(SerialEntityNames.SERIAL_DESCRIPTOR_CLASS)
|
||||
fun generate() {
|
||||
preGenerateWriteSelfMethodIfNeeded()
|
||||
preGenerateDeserializationConstructorIfNeeded()
|
||||
}
|
||||
|
||||
private fun preGenerateWriteSelfMethodIfNeeded() {
|
||||
if (!irClass.isInternalSerializable) return
|
||||
val serializerDescriptor = irClass.classSerializer(compilerContext)?.owner ?: return
|
||||
if (!irClass.shouldHaveSpecificSyntheticMethods { serializerDescriptor.findPluginGeneratedMethod(SerialEntityNames.SAVE) }) return
|
||||
if (irClass.findWriteSelfMethod() != null) return
|
||||
val method = irClass.addFunction {
|
||||
name = SerialEntityNames.WRITE_SELF_NAME
|
||||
returnType = compilerContext.irBuiltIns.unitType
|
||||
visibility = DescriptorVisibilities.PUBLIC
|
||||
modality = Modality.FINAL
|
||||
origin = SERIALIZATION_PLUGIN_ORIGIN
|
||||
}
|
||||
method.apply {
|
||||
dispatchReceiverParameter = null // function is static
|
||||
}
|
||||
|
||||
val typeParams = serializerDescriptor.typeParameters.map {
|
||||
method.addTypeParameter(
|
||||
it.name.asString(), compilerContext.irBuiltIns.anyNType
|
||||
)
|
||||
}
|
||||
val typeParamsAsArguments = typeParams.map { it.defaultType }
|
||||
|
||||
// object
|
||||
method.addValueParameter(
|
||||
Name.identifier("self"), irClass.typeWith(typeParamsAsArguments),
|
||||
SERIALIZATION_PLUGIN_ORIGIN
|
||||
)
|
||||
// encoder
|
||||
method.addValueParameter(
|
||||
Name.identifier("output"),
|
||||
compilerContext.getClassFromRuntime(SerialEntityNames.STRUCTURE_ENCODER_CLASS).defaultType,
|
||||
SERIALIZATION_PLUGIN_ORIGIN
|
||||
)
|
||||
// descriptor
|
||||
method.addValueParameter(
|
||||
Name.identifier("serialDesc"), serialDescriptorSymbol.defaultType,
|
||||
SERIALIZATION_PLUGIN_ORIGIN
|
||||
)
|
||||
// KSerializer<Tn>
|
||||
val kSerializerSymbol = compilerContext.getClassFromRuntime(SerialEntityNames.KSERIALIZER_CLASS)
|
||||
typeParamsAsArguments.forEachIndexed { i, it ->
|
||||
method.addValueParameter(
|
||||
Name.identifier("${SerialEntityNames.typeArgPrefix}$i"),
|
||||
kSerializerSymbol.typeWith(it),
|
||||
SERIALIZATION_PLUGIN_ORIGIN
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun preGenerateDeserializationConstructorIfNeeded() {
|
||||
if (!irClass.isInternalSerializable) return
|
||||
// do not add synthetic deserialization constructor if .deserialize method is customized
|
||||
if (irClass.hasCompanionObjectAsSerializer && irClass.companionObject()?.findPluginGeneratedMethod(SerialEntityNames.LOAD) == null) return
|
||||
if (irClass.isValue) return
|
||||
if (irClass.findSerializableSyntheticConstructor() != null) return
|
||||
val ctor = irClass.addConstructor {
|
||||
origin = SERIALIZATION_PLUGIN_ORIGIN
|
||||
visibility = DescriptorVisibilities.PUBLIC
|
||||
}
|
||||
val markerClassSymbol =
|
||||
compilerContext.getClassFromInternalSerializationPackage(SerialEntityNames.SERIAL_CTOR_MARKER_NAME.asString())
|
||||
val serializableProperties = serializablePropertiesForIrBackend(irClass).serializableProperties
|
||||
val bitMaskSlotsCount = serializableProperties.bitMaskSlotCount()
|
||||
|
||||
repeat(bitMaskSlotsCount) {
|
||||
ctor.addValueParameter(Name.identifier("seen$it"), compilerContext.irBuiltIns.intType, SERIALIZATION_PLUGIN_ORIGIN)
|
||||
}
|
||||
|
||||
for (prop in serializableProperties) {
|
||||
ctor.addValueParameter(prop.name, prop.type.makeNullableIfNotPrimitive(), SERIALIZATION_PLUGIN_ORIGIN)
|
||||
}
|
||||
|
||||
ctor.addValueParameter(SerialEntityNames.dummyParamName, markerClassSymbol.defaultType, SERIALIZATION_PLUGIN_ORIGIN)
|
||||
}
|
||||
|
||||
private fun IrType.makeNullableIfNotPrimitive() =
|
||||
if (this.isPrimitiveType(false)) this
|
||||
else this.makeNullable()
|
||||
|
||||
}
|
||||
+5
-2
@@ -163,7 +163,7 @@ internal fun IrConstructor.lastArgumentIsAnnotationArray(): Boolean {
|
||||
return ((lastArgType as? IrSimpleType)?.arguments?.firstOrNull()?.typeOrNull?.classFqName?.toString() == "kotlin.Annotation")
|
||||
}
|
||||
|
||||
fun IrClass.serializableSyntheticConstructor(): IrConstructorSymbol? { // FIXME: remove nullability W/A when FIR plugin will add proper deserialization ctor
|
||||
fun IrClass.findSerializableSyntheticConstructor(): IrConstructorSymbol? {
|
||||
return declarations.filterIsInstance<IrConstructor>().singleOrNull { it.isSerializationCtor() }?.symbol
|
||||
}
|
||||
|
||||
@@ -210,4 +210,7 @@ fun IrType.classOrUpperBound(): IrClassSymbol? = when(val cls = classifierOrNull
|
||||
is IrScriptSymbol -> cls.owner.targetClass
|
||||
is IrTypeParameterSymbol -> cls.owner.representativeUpperBound.classOrUpperBound()
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
internal inline fun IrClass.shouldHaveSpecificSyntheticMethods(functionPresenceChecker: () -> IrSimpleFunction?) =
|
||||
!isValue && (isAbstractOrSealedSerializableClass || functionPresenceChecker() != null)
|
||||
+7
-7
@@ -84,7 +84,7 @@ class SerialInfoImplJvmIrGenerator(
|
||||
generateSimplePropertyWithBackingField(property.descriptor, irClass, Name.identifier("_" + property.name.asString()))
|
||||
|
||||
val getter = property.getter!!
|
||||
getter.origin = SERIALIZABLE_PLUGIN_ORIGIN
|
||||
getter.origin = SERIALIZATION_PLUGIN_ORIGIN
|
||||
// Add JvmName annotation to property getters to force the resulting JVM method name for 'x' be 'x', instead of 'getX',
|
||||
// and to avoid having useless bridges for it generated in BridgeLowering.
|
||||
// Unfortunately, this results in an extra `@JvmName` annotation in the bytecode, but it shouldn't matter very much.
|
||||
@@ -92,7 +92,7 @@ class SerialInfoImplJvmIrGenerator(
|
||||
|
||||
val field = property.backingField!!
|
||||
field.visibility = DescriptorVisibilities.PRIVATE
|
||||
field.origin = SERIALIZABLE_PLUGIN_ORIGIN
|
||||
field.origin = SERIALIZATION_PLUGIN_ORIGIN
|
||||
|
||||
val parameter = ctor.addValueParameter(property.name.asString(), field.type)
|
||||
ctorBody.statements += IrSetFieldImpl(
|
||||
@@ -200,7 +200,7 @@ class SerialInfoImplJvmIrGenerator(
|
||||
propertyParent.factory.createProperty(
|
||||
propertyParent.startOffset,
|
||||
propertyParent.endOffset,
|
||||
SERIALIZABLE_PLUGIN_ORIGIN,
|
||||
SERIALIZATION_PLUGIN_ORIGIN,
|
||||
IrPropertySymbolImpl(propertyDescriptor),
|
||||
name,
|
||||
visibility,
|
||||
@@ -240,7 +240,7 @@ class SerialInfoImplJvmIrGenerator(
|
||||
originProperty.factory.createField(
|
||||
originProperty.startOffset,
|
||||
originProperty.endOffset,
|
||||
SERIALIZABLE_PLUGIN_ORIGIN,
|
||||
SERIALIZATION_PLUGIN_ORIGIN,
|
||||
IrFieldSymbolImpl(propertyDescriptor),
|
||||
name,
|
||||
type.toIrType(),
|
||||
@@ -274,7 +274,7 @@ class SerialInfoImplJvmIrGenerator(
|
||||
property.factory.createFunction(
|
||||
fieldSymbol.owner.startOffset,
|
||||
fieldSymbol.owner.endOffset,
|
||||
SERIALIZABLE_PLUGIN_ORIGIN, IrSimpleFunctionSymbolImpl(descriptor),
|
||||
SERIALIZATION_PLUGIN_ORIGIN, IrSimpleFunctionSymbolImpl(descriptor),
|
||||
name, visibility, modality, returnType!!.toIrType(),
|
||||
isInline, isEffectivelyExternal(), isTailrec, isSuspend, isOperator, isInfix, isExpect
|
||||
)
|
||||
@@ -368,7 +368,7 @@ class SerialInfoImplJvmIrGenerator(
|
||||
fun irValueParameter(descriptor: ParameterDescriptor): IrValueParameter = with(descriptor) {
|
||||
@OptIn(FirIncompatiblePluginAPI::class) // should never be called after FIR frontend
|
||||
factory.createValueParameter(
|
||||
function.startOffset, function.endOffset, SERIALIZABLE_PLUGIN_ORIGIN, IrValueParameterSymbolImpl(this),
|
||||
function.startOffset, function.endOffset, SERIALIZATION_PLUGIN_ORIGIN, IrValueParameterSymbolImpl(this),
|
||||
name, indexOrMinusOne, type.toIrType(), varargElementType?.toIrType(), isCrossinline, isNoinline,
|
||||
isHidden = false, isAssignable = false
|
||||
).also {
|
||||
@@ -394,7 +394,7 @@ class SerialInfoImplJvmIrGenerator(
|
||||
val newTypeParameters = descriptor.typeParameters.map {
|
||||
factory.createTypeParameter(
|
||||
startOffset, endOffset,
|
||||
SERIALIZABLE_PLUGIN_ORIGIN,
|
||||
SERIALIZATION_PLUGIN_ORIGIN,
|
||||
IrTypeParameterSymbolImpl(it),
|
||||
it.name, it.index, it.isReified, it.variance
|
||||
).also { typeParameter ->
|
||||
|
||||
+2
-5
@@ -270,7 +270,7 @@ class SerializableIrGenerator(
|
||||
propertiesStart: Int
|
||||
): Int {
|
||||
check(superClass.isInternalSerializable)
|
||||
val superCtorRef = superClass.serializableSyntheticConstructor()!!
|
||||
val superCtorRef = superClass.findSerializableSyntheticConstructor() ?: error("Class serializable internally should have special constructor with marker")
|
||||
val superProperties = serializablePropertiesForIrBackend(superClass).serializableProperties
|
||||
val superSlots = superProperties.bitMaskSlotCount()
|
||||
val arguments = allValueParameters.subList(0, superSlots) +
|
||||
@@ -358,10 +358,7 @@ class SerializableIrGenerator(
|
||||
generateSyntheticMethods()
|
||||
}
|
||||
|
||||
private inline fun IrClass.shouldHaveSpecificSyntheticMethods(functionPresenceChecker: () -> IrSimpleFunction?) =
|
||||
!isValue && (isAbstractOrSealedSerializableClass || functionPresenceChecker() != null)
|
||||
|
||||
private fun generateSyntheticInternalConstructor() { // TODO: this doesn't work with OLD FE
|
||||
private fun generateSyntheticInternalConstructor() {
|
||||
val serializerDescriptor = irClass.classSerializer(compilerContext)?.owner ?: return
|
||||
if (irClass.shouldHaveSpecificSyntheticMethods { serializerDescriptor.findPluginGeneratedMethod(LOAD) }) {
|
||||
val constrDesc = irClass.constructors.find(IrConstructor::isSerializationCtor) ?: return
|
||||
|
||||
-1
@@ -28,7 +28,6 @@ import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames
|
||||
class SerializerForEnumsGenerator(
|
||||
irClass: IrClass,
|
||||
compilerContext: SerializationPluginContext,
|
||||
serialInfoJvmGenerator: SerialInfoImplJvmIrGenerator,
|
||||
) : SerializerIrGenerator(irClass, compilerContext, null) {
|
||||
override fun generateSave(function: IrSimpleFunction) = addFunctionBody(function) { saveFunc ->
|
||||
fun irThis(): IrExpression =
|
||||
|
||||
+4
-6
@@ -47,7 +47,7 @@ import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.ST
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.STRUCTURE_ENCODER_CLASS
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.UNKNOWN_FIELD_EXC
|
||||
|
||||
object SERIALIZABLE_PLUGIN_ORIGIN : IrDeclarationOriginImpl("SERIALIZER", true)
|
||||
object SERIALIZATION_PLUGIN_ORIGIN : IrDeclarationOriginImpl("KOTLINX_SERIALIZATION", true)
|
||||
|
||||
internal typealias FunctionWithArgs = Pair<IrFunctionSymbol, List<IrExpression>>
|
||||
|
||||
@@ -118,7 +118,7 @@ open class SerializerIrGenerator(
|
||||
|
||||
val anonymousInit = irClass.run {
|
||||
val symbol = IrAnonymousInitializerSymbolImpl(descriptor)
|
||||
irClass.factory.createAnonymousInitializer(startOffset, endOffset, SERIALIZABLE_PLUGIN_ORIGIN, symbol).also {
|
||||
irClass.factory.createAnonymousInitializer(startOffset, endOffset, SERIALIZATION_PLUGIN_ORIGIN, symbol).also {
|
||||
it.parent = this
|
||||
declarations.add(it)
|
||||
}
|
||||
@@ -495,7 +495,7 @@ open class SerializerIrGenerator(
|
||||
)
|
||||
|
||||
val typeArgs = (loadFunc.returnType as IrSimpleType).arguments.map { (it as IrTypeProjection).type }
|
||||
val deserCtor: IrConstructorSymbol? = serializableIrClass.serializableSyntheticConstructor()
|
||||
val deserCtor: IrConstructorSymbol? = serializableIrClass.findSerializableSyntheticConstructor()
|
||||
if (serializableIrClass.isInternalSerializable && deserCtor != null) {
|
||||
var args: List<IrExpression> = serializableProperties.map { serialPropertiesMap.getValue(it.ir).get() }
|
||||
args = bitMasks.map { irGet(it) } + args + irNull()
|
||||
@@ -619,14 +619,12 @@ open class SerializerIrGenerator(
|
||||
irClass: IrClass,
|
||||
context: SerializationPluginContext,
|
||||
metadataPlugin: SerializationDescriptorSerializerPlugin?,
|
||||
serialInfoJvmGenerator: SerialInfoImplJvmIrGenerator,
|
||||
) {
|
||||
val serializableDesc = getSerializableClassDescriptorBySerializer(irClass.symbol.descriptor) ?: return
|
||||
val generator = when {
|
||||
serializableDesc.isEnumWithLegacyGeneratedSerializer() -> SerializerForEnumsGenerator(
|
||||
irClass,
|
||||
context,
|
||||
serialInfoJvmGenerator
|
||||
context
|
||||
)
|
||||
serializableDesc.isInlineClass() -> SerializerForInlineClassGenerator(irClass, context)
|
||||
else -> SerializerIrGenerator(irClass, context, metadataPlugin)
|
||||
|
||||
+35
-16
@@ -12,19 +12,13 @@ import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
|
||||
import org.jetbrains.kotlin.backend.common.runOnFilePostfix
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.fileParent
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFile
|
||||
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
|
||||
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.acceptVoid
|
||||
import org.jetbrains.kotlin.platform.jvm.isJvm
|
||||
import org.jetbrains.kotlinx.serialization.compiler.backend.ir.SerialInfoImplJvmIrGenerator
|
||||
import org.jetbrains.kotlinx.serialization.compiler.backend.ir.SerializableCompanionIrGenerator
|
||||
import org.jetbrains.kotlinx.serialization.compiler.backend.ir.SerializableIrGenerator
|
||||
import org.jetbrains.kotlinx.serialization.compiler.backend.ir.SerializerIrGenerator
|
||||
import org.jetbrains.kotlinx.serialization.compiler.backend.ir.*
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.KSerializerDescriptorResolver
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
@@ -52,25 +46,49 @@ class SerializationPluginContext(baseContext: IrPluginContext, val metadataPlugi
|
||||
internal val copiedStaticWriteSelf: MutableMap<IrSimpleFunction, IrSimpleFunction> = ConcurrentHashMap()
|
||||
}
|
||||
|
||||
private inline fun IrClass.runPluginSafe(block: () -> Unit) {
|
||||
try {
|
||||
block()
|
||||
} catch (e: Exception) {
|
||||
throw CompilationException(
|
||||
"kotlinx.serialization compiler plugin internal error: unable to transform declaration, see cause",
|
||||
this.fileParent,
|
||||
this,
|
||||
e
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class SerializerClassLowering(
|
||||
baseContext: IrPluginContext,
|
||||
metadataPlugin: SerializationDescriptorSerializerPlugin?,
|
||||
moduleFragment: IrModuleFragment
|
||||
) : IrElementTransformerVoid(), ClassLoweringPass {
|
||||
val context: SerializationPluginContext = SerializationPluginContext(baseContext, metadataPlugin)
|
||||
private val serialInfoJvmGenerator = SerialInfoImplJvmIrGenerator(context, moduleFragment).also { context.serialInfoImplJvmIrGenerator = it }
|
||||
private val serialInfoJvmGenerator =
|
||||
SerialInfoImplJvmIrGenerator(context, moduleFragment).also { context.serialInfoImplJvmIrGenerator = it }
|
||||
|
||||
override fun lower(irClass: IrClass) {
|
||||
try {
|
||||
irClass.runPluginSafe {
|
||||
SerializableIrGenerator.generate(irClass, context)
|
||||
SerializerIrGenerator.generate(irClass, context, context.metadataPlugin, serialInfoJvmGenerator)
|
||||
SerializerIrGenerator.generate(irClass, context, context.metadataPlugin)
|
||||
SerializableCompanionIrGenerator.generate(irClass, context)
|
||||
|
||||
if (context.platform.isJvm() && KSerializerDescriptorResolver.isSerialInfoImpl(irClass.descriptor)) {
|
||||
serialInfoJvmGenerator.generate(irClass)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
throw CompilationException("kotlinx.serialization compiler plugin internal error: unable to transform declaration, see cause", irClass.fileParent, irClass, e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class SerializerClassPreLowering(
|
||||
baseContext: IrPluginContext
|
||||
) : IrElementTransformerVoid(), ClassLoweringPass {
|
||||
val context: SerializationPluginContext = SerializationPluginContext(baseContext, null)
|
||||
|
||||
override fun lower(irClass: IrClass) {
|
||||
irClass.runPluginSafe {
|
||||
IrPreGenerator(irClass, context).generate()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -82,8 +100,9 @@ open class SerializationLoweringExtension @JvmOverloads constructor(
|
||||
moduleFragment: IrModuleFragment,
|
||||
pluginContext: IrPluginContext
|
||||
) {
|
||||
val serializerClassLowering = SerializerClassLowering(pluginContext, metadataPlugin, moduleFragment)
|
||||
for (file in moduleFragment.files)
|
||||
serializerClassLowering.runOnFileInOrder(file)
|
||||
val pass1 = SerializerClassPreLowering(pluginContext)
|
||||
val pass2 = SerializerClassLowering(pluginContext, metadataPlugin, moduleFragment)
|
||||
moduleFragment.files.forEach(pass1::runOnFileInOrder)
|
||||
moduleFragment.files.forEach(pass2::runOnFileInOrder)
|
||||
}
|
||||
}
|
||||
|
||||
+6
@@ -48,4 +48,10 @@ public class SerializationFirBlackBoxTestGenerated extends AbstractSerialization
|
||||
public void testMultipleProperties() throws Exception {
|
||||
runTest("plugins/kotlin-serialization/kotlin-serialization-compiler/testData/firMembers/multipleProperties.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("privatePropertiesSerialization.kt")
|
||||
public void testPrivatePropertiesSerialization() throws Exception {
|
||||
runTest("plugins/kotlin-serialization/kotlin-serialization-compiler/testData/firMembers/privatePropertiesSerialization.kt");
|
||||
}
|
||||
}
|
||||
|
||||
+6
@@ -47,4 +47,10 @@ public class SerializationFirMembersTestGenerated extends AbstractSerializationF
|
||||
public void testMultipleProperties() throws Exception {
|
||||
runTest("plugins/kotlin-serialization/kotlin-serialization-compiler/testData/firMembers/multipleProperties.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("privatePropertiesSerialization.kt")
|
||||
public void testPrivatePropertiesSerialization() throws Exception {
|
||||
runTest("plugins/kotlin-serialization/kotlin-serialization-compiler/testData/firMembers/privatePropertiesSerialization.kt");
|
||||
}
|
||||
}
|
||||
|
||||
+98
@@ -0,0 +1,98 @@
|
||||
FILE: privatePropertiesSerialization.kt
|
||||
@R|kotlinx/serialization/Serializable|() public open class Parent : R|kotlin/Any| {
|
||||
public constructor(ctor: R|kotlin/Int|): R|Parent| {
|
||||
super<R|kotlin/Any|>()
|
||||
}
|
||||
|
||||
private final val ctor: R|kotlin/Int| = R|<local>/ctor|
|
||||
private get(): R|kotlin/Int|
|
||||
|
||||
private final var body: R|kotlin/String| = String(42)
|
||||
private get(): R|kotlin/String|
|
||||
private set(value: R|kotlin/String|): R|kotlin/Unit|
|
||||
|
||||
public final fun checkDeser(c: R|kotlin/Int|, b: R|kotlin/String|): R|kotlin/String| {
|
||||
when () {
|
||||
!=(this@R|/Parent|.R|/Parent.ctor|, R|<local>/c|) -> {
|
||||
^checkDeser <strcat>(String(Ctor : ), this@R|/Parent|.R|/Parent.ctor|)
|
||||
}
|
||||
}
|
||||
|
||||
when () {
|
||||
!=(this@R|/Parent|.R|/Parent.body|, R|<local>/b|) -> {
|
||||
^checkDeser <strcat>(String(Body : ), this@R|/Parent|.R|/Parent.body|)
|
||||
}
|
||||
}
|
||||
|
||||
^checkDeser String(OK)
|
||||
}
|
||||
|
||||
public final companion object Companion : R|kotlin/Any| {
|
||||
public final fun serializer(): R|kotlinx/serialization/KSerializer<Parent>|
|
||||
|
||||
private constructor(): R|Parent.Companion|
|
||||
|
||||
}
|
||||
|
||||
public final object $serializer : R|kotlinx/serialization/internal/GeneratedSerializer<Parent>| {
|
||||
public final fun serialize(encoder: R|kotlinx/serialization/encoding/Encoder|, value: R|Parent|): R|kotlin/Unit|
|
||||
|
||||
public final fun deserialize(decoder: R|kotlinx/serialization/encoding/Decoder|): R|Parent|
|
||||
|
||||
public final val descriptor: R|kotlinx/serialization/descriptors/SerialDescriptor|
|
||||
public get(): R|kotlinx/serialization/descriptors/SerialDescriptor|
|
||||
|
||||
public final fun childSerializers(): R|kotlin/Array<kotlinx/serialization/KSerializer<*>>|
|
||||
|
||||
private constructor(): R|Parent.$serializer|
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@R|kotlinx/serialization/Serializable|() public final class Derived : R|Parent| {
|
||||
public constructor(): R|Derived| {
|
||||
super<R|Parent|>(Int(42))
|
||||
}
|
||||
|
||||
public final companion object Companion : R|kotlin/Any| {
|
||||
public final fun serializer(): R|kotlinx/serialization/KSerializer<Derived>|
|
||||
|
||||
private constructor(): R|Derived.Companion|
|
||||
|
||||
}
|
||||
|
||||
public final object $serializer : R|kotlinx/serialization/internal/GeneratedSerializer<Derived>| {
|
||||
public final fun serialize(encoder: R|kotlinx/serialization/encoding/Encoder|, value: R|Derived|): R|kotlin/Unit|
|
||||
|
||||
public final fun deserialize(decoder: R|kotlinx/serialization/encoding/Decoder|): R|Derived|
|
||||
|
||||
public final val descriptor: R|kotlinx/serialization/descriptors/SerialDescriptor|
|
||||
public get(): R|kotlinx/serialization/descriptors/SerialDescriptor|
|
||||
|
||||
public final fun childSerializers(): R|kotlin/Array<kotlinx/serialization/KSerializer<*>>|
|
||||
|
||||
private constructor(): R|Derived.$serializer|
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
public final fun test(targetString: R|kotlin/String|): R|kotlin/String| {
|
||||
lval c: R|Derived| = R|/Derived.Derived|()
|
||||
lval j: R|kotlinx/serialization/json/Json| = R|kotlinx/serialization/json/Json|(<L> = Json@fun R|kotlinx/serialization/json/JsonBuilder|.<anonymous>(): R|kotlin/Unit| <inline=NoInline> {
|
||||
this@R|special/anonymous|.R|kotlinx/serialization/json/JsonBuilder.encodeDefaults| = Boolean(true)
|
||||
}
|
||||
)
|
||||
lval s: R|kotlin/String| = R|<local>/j|.R|kotlinx/serialization/json/Json.encodeToString|<R|Derived|>(Q|Derived|.R|/Derived.Companion.serializer|(), R|<local>/c|)
|
||||
when () {
|
||||
!=(R|<local>/s|, R|<local>/targetString|) -> {
|
||||
^test R|<local>/s|
|
||||
}
|
||||
}
|
||||
|
||||
lval d: R|Derived| = R|<local>/j|.R|kotlinx/serialization/json/Json.decodeFromString|<R|Derived|>(Q|Derived|.R|/Derived.Companion.serializer|(), String({"ctor":43,"body":"43"}))
|
||||
^test R|<local>/d|.R|/Parent.checkDeser|(Int(43), String(43))
|
||||
^test String(OK)
|
||||
}
|
||||
public final fun box(): R|kotlin/String| {
|
||||
^box R|/test|(String({"ctor":42,"body":"42"}))
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
// WITH_STDLIB
|
||||
|
||||
/**
|
||||
* write$Self and synthetic constructor are required for correct serialization and deserialization of private fields,
|
||||
* otherwise inaccessible from e.g. Derived.$serializer.serialize().
|
||||
* This test verifies that.
|
||||
*/
|
||||
import kotlinx.serialization.*
|
||||
import kotlinx.serialization.json.*
|
||||
|
||||
@Serializable
|
||||
open class Parent(private val ctor: Int) {
|
||||
private var body: String = "42"
|
||||
|
||||
fun checkDeser(c: Int, b: String): String {
|
||||
if (this.ctor != c) return "Ctor : ${this.ctor}"
|
||||
if (this.body != b) return "Body : ${this.body}"
|
||||
return "OK"
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable
|
||||
class Derived: Parent(42)
|
||||
|
||||
fun test(targetString: String): String {
|
||||
val c = Derived()
|
||||
val j = Json { encodeDefaults = true }
|
||||
val s = j.encodeToString(Derived.serializer(), c)
|
||||
if (s != targetString) return s
|
||||
val d = j.decodeFromString(Derived.serializer(), """{"ctor":43,"body":"43"}""")
|
||||
return d.checkDeser(43, "43")
|
||||
return "OK"
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
return test("""{"ctor":42,"body":"42"}""")
|
||||
}
|
||||
Reference in New Issue
Block a user