Remove module-wide ObsoleteDescriptorApi opt-in

and remove access to .descriptor whenever possible
This commit is contained in:
Leonid Startsev
2022-08-17 12:41:23 +02:00
committed by Space
parent 783087a551
commit dc7ba5d22b
13 changed files with 114 additions and 70 deletions
@@ -60,7 +60,6 @@ sourceSets {
tasks.withType<org.jetbrains.kotlin.gradle.dsl.KotlinCompile<*>> {
kotlinOptions {
freeCompilerArgs += "-opt-in=org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI"
freeCompilerArgs += "-opt-in=org.jetbrains.kotlin.compiler.plugin.ExperimentalCompilerApi"
}
}
@@ -11,7 +11,6 @@ import org.jetbrains.kotlin.backend.jvm.ir.fileParent
import org.jetbrains.kotlin.backend.jvm.ir.representativeUpperBound
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.isSealed
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrFunction
@@ -39,19 +38,26 @@ import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.*
import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPluginContext
import org.jetbrains.kotlinx.serialization.compiler.resolve.*
abstract class BaseIrGenerator(private val currentClass: IrClass, final override val compilerContext: SerializationPluginContext): IrBuilderWithPluginContext {
abstract class BaseIrGenerator(private val currentClass: IrClass, final override val compilerContext: SerializationPluginContext) :
IrBuilderWithPluginContext {
private val throwMissedFieldExceptionFunc
= compilerContext.referenceFunctions(CallableId(SerializationPackages.internalPackageFqName, SerialEntityNames.SINGLE_MASK_FIELD_MISSING_FUNC_NAME)).singleOrNull()
private val throwMissedFieldExceptionFunc = compilerContext.referenceFunctions(
CallableId(
SerializationPackages.internalPackageFqName,
SerialEntityNames.SINGLE_MASK_FIELD_MISSING_FUNC_NAME
)
).singleOrNull()
private val throwMissedFieldExceptionArrayFunc
= compilerContext.referenceFunctions(CallableId(SerializationPackages.internalPackageFqName, SerialEntityNames.ARRAY_MASK_FIELD_MISSING_FUNC_NAME)).singleOrNull()
private val throwMissedFieldExceptionArrayFunc = compilerContext.referenceFunctions(
CallableId(
SerializationPackages.internalPackageFqName,
SerialEntityNames.ARRAY_MASK_FIELD_MISSING_FUNC_NAME
)
).singleOrNull()
private val enumSerializerFactoryFunc
= compilerContext.enumSerializerFactoryFunc
private val enumSerializerFactoryFunc = compilerContext.enumSerializerFactoryFunc
private val markedEnumSerializerFactoryFunc
= compilerContext.markedEnumSerializerFactoryFunc
private val markedEnumSerializerFactoryFunc = compilerContext.markedEnumSerializerFactoryFunc
fun useFieldMissingOptimization(): Boolean {
return throwMissedFieldExceptionFunc != null && throwMissedFieldExceptionArrayFunc != null
@@ -250,7 +256,7 @@ abstract class BaseIrGenerator(private val currentClass: IrClass, final override
genericGetter
)
val (functionToCall, args: List<IrExpression>) = if (innerSerial != null) whenHaveSerializer(innerSerial, sti) else whenDoNot(sti)
val typeArgs = if (functionToCall.descriptor.typeParameters.isNotEmpty()) listOf(property.type) else listOf()
val typeArgs = if (functionToCall.owner.typeParameters.isNotEmpty()) listOf(property.type) else listOf()
return irInvoke(encoder, functionToCall, typeArguments = typeArgs, valueArguments = args, returnTypeHint = returnTypeHint)
}
@@ -267,7 +273,7 @@ abstract class BaseIrGenerator(private val currentClass: IrClass, final override
val adjustedArgs: List<IrExpression> =
// if typeArgs.size == args.size then the serializer is custom - we need to use the actual serializers from the arguments
if ((typeArgs.size != args.size) && (baseClass.descriptor.isSealed() || baseClass.descriptor.modality == Modality.ABSTRACT)) {
if ((typeArgs.size != args.size) && (baseClass.modality == Modality.SEALED || baseClass.modality == Modality.ABSTRACT)) {
val serializer = findStandardKotlinTypeSerializer(compilerContext, context.irBuiltIns.unitType)!!
// workaround for sealed and abstract classes - the `serializer` function expects non-null serializers, but does not use them, so serializers of any type can be passed
List(baseClass.typeParameters.size) { irGetObject(serializer) }
@@ -6,6 +6,7 @@
package org.jetbrains.kotlinx.serialization.compiler.backend.ir
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI
import org.jetbrains.kotlin.ir.builders.IrBuilderWithScope
import org.jetbrains.kotlin.ir.builders.irGet
import org.jetbrains.kotlin.ir.builders.irGetField
@@ -78,6 +79,7 @@ fun IrBuilderWithScope.createPropertyByParamReplacer(
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.
*/
@OptIn(ObsoleteDescriptorBasedAPI::class)
fun createInitializerAdapter(
irClass: IrClass,
paramGetReplacer: (ValueParameterDescriptor) -> IrExpression?,
@@ -11,6 +11,7 @@ import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.builtins.StandardNames
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.builders.declarations.*
import org.jetbrains.kotlin.ir.declarations.*
@@ -27,16 +28,12 @@ import org.jetbrains.kotlin.name.ClassId
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.jvm.JvmPrimitiveType
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPluginContext
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationDependencies.LAZY_FQ
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationDependencies.LAZY_MODE_FQ
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationDependencies.LAZY_PUBLICATION_MODE_NAME
import org.jetbrains.kotlinx.serialization.compiler.resolve.hasSerializableOrMetaAnnotation
import org.jetbrains.kotlinx.serialization.compiler.resolve.isInheritableSerialInfoAnnotation
import org.jetbrains.kotlinx.serialization.compiler.resolve.isSerialInfoAnnotation
interface IrBuilderWithPluginContext {
@@ -134,7 +131,7 @@ interface IrBuilderWithPluginContext {
}
fun IrClass.contributeAnonymousInitializer(bodyGen: IrBlockBodyBuilder.() -> Unit) {
val symbol = IrAnonymousInitializerSymbolImpl(descriptor)
val symbol = IrAnonymousInitializerSymbolImpl(symbol)
factory.createAnonymousInitializer(startOffset, endOffset, SERIALIZATION_PLUGIN_ORIGIN, symbol).also {
it.parent = this
declarations.add(it)
@@ -256,9 +253,6 @@ interface IrBuilderWithPluginContext {
result
)
@FirIncompatiblePluginAPI
fun KotlinType.toIrType() = compilerContext.typeTranslator.translateType(this)
fun IrBuilderWithScope.setProperty(receiver: IrExpression, property: IrProperty, value: IrExpression): IrExpression {
return if (property.setter != null)
irSet(property.setter!!.returnType, receiver, property.setter!!.symbol, value)
@@ -269,7 +263,7 @@ interface IrBuilderWithPluginContext {
fun IrBuilderWithScope.generateAnySuperConstructorCall(toBuilder: IrBlockBodyBuilder) {
val anyConstructor = compilerContext.irBuiltIns.anyClass.owner.declarations.single { it is IrConstructor } as IrConstructor
with(toBuilder) {
+IrDelegatingConstructorCallImpl.fromSymbolDescriptor(
+IrDelegatingConstructorCallImpl.fromSymbolOwner(
startOffset, endOffset,
compilerContext.irBuiltIns.unitType,
anyConstructor.symbol
@@ -348,14 +342,14 @@ interface IrBuilderWithPluginContext {
createClassReference(classSymbol.starProjectedType, startOffset, endOffset)
fun collectSerialInfoAnnotations(irClass: IrClass): List<IrConstructorCall> {
if (!(irClass.isInterface || irClass.descriptor.hasSerializableOrMetaAnnotation)) return emptyList()
if (!(irClass.isInterface || irClass.hasSerializableOrMetaAnnotation())) return emptyList()
val annotationByFq: MutableMap<FqName, IrConstructorCall> =
irClass.annotations.associateBy { it.symbol.owner.parentAsClass.descriptor.fqNameSafe }.toMutableMap()
irClass.annotations.associateBy { it.symbol.owner.parentAsClass.fqNameWhenAvailable!! }.toMutableMap()
for (clazz in irClass.getAllSuperclasses()) {
val annotations = clazz.annotations
.mapNotNull {
val descriptor = it.symbol.owner.parentAsClass.descriptor
if (descriptor.isInheritableSerialInfoAnnotation) descriptor.fqNameSafe to it else null
val parent = it.symbol.owner.parentAsClass
if (parent.isInheritableSerialInfoAnnotation) parent.fqNameWhenAvailable!! to it else null
}
annotations.forEach { (fqname, call) ->
if (fqname !in annotationByFq) {
@@ -371,7 +365,7 @@ interface IrBuilderWithPluginContext {
fun IrBuilderWithScope.copyAnnotationsFrom(annotations: List<IrConstructorCall>): List<IrExpression> =
annotations.mapNotNull { annotationCall ->
val annotationClass = annotationCall.symbol.owner.parentAsClass
if (!annotationClass.descriptor.isSerialInfoAnnotation) return@mapNotNull null
if (!annotationClass.isSerialInfoAnnotation) return@mapNotNull null
if (compilerContext.platform.isJvm()) {
val implClass = compilerContext.serialInfoImplJvmIrGenerator.getImplClass(annotationClass)
@@ -389,6 +383,7 @@ interface IrBuilderWithPluginContext {
}
}
@OptIn(ObsoleteDescriptorBasedAPI::class)
fun IrBuilderWithScope.wrapperClassReference(classType: IrType): IrClassReference {
if (compilerContext.platform.isJvm()) {
// "Byte::class" -> "java.lang.Byte::class"
@@ -10,6 +10,7 @@ import org.jetbrains.kotlin.backend.jvm.ir.representativeUpperBound
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.IrConstructorCall
import org.jetbrains.kotlin.ir.expressions.IrExpression
@@ -23,11 +24,11 @@ import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.platform.js.isJs
import org.jetbrains.kotlin.platform.konan.isNative
import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPluginContext
import org.jetbrains.kotlinx.serialization.compiler.fir.SerializationPluginKey
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationAnnotations
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationPackages
import org.jetbrains.kotlinx.serialization.compiler.resolve.*
internal fun IrType.isKSerializer(): Boolean {
val simpleType = this as? IrSimpleType ?: return false
@@ -89,7 +90,7 @@ private fun IrClass.checkSerializableOrMetaAnnotationArgs(mustDoNotHaveArgs: Boo
return true
}
return annotations
.map { it.annotationClass.annotations }
.map { it.constructedClass.annotations }
.any { it.hasAnnotation(SerializationAnnotations.metaSerializableAnnotationFqName) }
}
@@ -98,6 +99,9 @@ internal val IrClass.isSerialInfoAnnotation: Boolean
|| annotations.hasAnnotation(SerializationAnnotations.inheritableSerialInfoFqName)
|| annotations.hasAnnotation(SerializationAnnotations.metaSerializableAnnotationFqName)
internal val IrClass.isInheritableSerialInfoAnnotation: Boolean
get() = annotations.hasAnnotation(SerializationAnnotations.inheritableSerialInfoFqName)
internal fun IrClass.shouldHaveGeneratedSerializer(context: SerializationPluginContext): Boolean
= (isInternalSerializable && (modality == Modality.FINAL || modality == Modality.OPEN))
|| isEnumWithLegacyGeneratedSerializer(context)
@@ -142,6 +146,7 @@ fun IrClass.getSuperClassNotAny(): IrClass? {
return if (parentClass.defaultType.isAny()) null else parentClass
}
@OptIn(ObsoleteDescriptorBasedAPI::class)
internal fun IrDeclaration.isFromPlugin(): Boolean =
this.origin == IrDeclarationOrigin.GeneratedByPlugin(SerializationPluginKey) || (this.descriptor as? CallableMemberDescriptor)?.kind == CallableMemberDescriptor.Kind.SYNTHESIZED // old FE doesn't specify origin
@@ -165,14 +170,35 @@ fun IrClass.findSerializableSyntheticConstructor(): IrConstructorSymbol? {
return declarations.filterIsInstance<IrConstructor>().singleOrNull { it.isSerializationCtor() }?.symbol
}
internal fun IrClass.needSerializerFactory(compilerContext: SerializationPluginContext): Boolean {
if (!(compilerContext.platform?.isNative() == true || compilerContext.platform.isJs())) return false
val serializableClass = getSerializableClassDescriptorByCompanion(this) ?: return false
if (serializableClass.isSerializableObject) return true
if (serializableClass.isSerializableEnum()) return true
if (serializableClass.isAbstractOrSealedSerializableClass) return true
if (serializableClass.isSealedSerializableInterface) return true
if (serializableClass.typeParameters.isEmpty()) return false
return true
}
internal fun getSerializableClassDescriptorByCompanion(companion: IrClass): IrClass? {
if (companion.isSerializableObject) return companion
if (!companion.isCompanion) return null
val classDescriptor = (companion.parent as? IrClass) ?: return null
if (!classDescriptor.shouldHaveGeneratedMethodsInCompanion) return null
return classDescriptor
}
internal fun IrExpression.isInitializePropertyFromParameter(): Boolean =
this is IrGetValueImpl && this.origin == IrStatementOrigin.INITIALIZE_PROPERTY_FROM_PARAMETER
internal val IrConstructorCall.annotationClass
internal val IrConstructorCall.constructedClass
get() = this.symbol.owner.constructedClass
internal val List<IrConstructorCall>.hasAnySerialAnnotation: Boolean
get() = serialNameValue != null || any { it.annotationClass.isSerialInfoAnnotation == true }
get() = serialNameValue != null || any { it.constructedClass.isSerialInfoAnnotation }
internal val List<IrConstructorCall>.serialNameValue: String?
get() = findAnnotation(SerializationAnnotations.serialNameAnnotationFqName)?.getStringConstArgument(0) // @SerialName("foo")
@@ -6,6 +6,7 @@
package org.jetbrains.kotlinx.serialization.compiler.backend.ir
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrProperty
import org.jetbrains.kotlin.ir.types.IrSimpleType
@@ -41,6 +42,7 @@ class IrSerializableProperties(
override val serializableStandaloneProperties: List<IrSerializableProperty>
) : ISerializableProperties<IrSerializableProperty>
@OptIn(ObsoleteDescriptorBasedAPI::class)
internal fun serializablePropertiesForIrBackend(
irClass: IrClass,
serializationDescriptorSerializer: SerializationDescriptorSerializerPlugin? = null
@@ -8,6 +8,7 @@ package org.jetbrains.kotlinx.serialization.compiler.backend.ir
import org.jetbrains.kotlin.backend.common.extensions.FirIncompatiblePluginAPI
import org.jetbrains.kotlin.backend.common.ir.addExtensionReceiver
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.builders.declarations.*
import org.jetbrains.kotlin.ir.declarations.*
@@ -30,12 +31,14 @@ import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.descriptorUtil.isEffectivelyExternal
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPluginContext
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames
// This doesn't support annotation arguments of type KClass and Array<KClass> because the codegen doesn't compute JVM signatures for
// such cases correctly (because inheriting from annotation classes is prohibited in Kotlin).
// Currently it results in an "accidental override" error where a method with return type KClass conflicts with the one with Class.
@OptIn(ObsoleteDescriptorBasedAPI::class)
class SerialInfoImplJvmIrGenerator(
private val context: SerializationPluginContext,
private val moduleFragment: IrModuleFragment,
@@ -53,7 +56,7 @@ class SerialInfoImplJvmIrGenerator(
fun getImplClass(serialInfoAnnotationClass: IrClass): IrClass =
annotationToImpl.getOrPut(serialInfoAnnotationClass) {
@OptIn(FirIncompatiblePluginAPI::class) // TODO
@OptIn(FirIncompatiblePluginAPI::class)
val implClassSymbol = context.referenceClass(serialInfoAnnotationClass.kotlinFqName.child(SerialEntityNames.IMPL_NAME))
implClassSymbol!!.owner.apply(this::generate)
}
@@ -112,6 +115,9 @@ class SerialInfoImplJvmIrGenerator(
putValueArgument(0, IrConstImpl.string(UNDEFINED_OFFSET, UNDEFINED_OFFSET, context.irBuiltIns.stringType, name))
}
@FirIncompatiblePluginAPI
fun KotlinType.toIrType() = compilerContext.typeTranslator.translateType(this)
private fun IrType.kClassToJClassIfNeeded(): IrType = when {
this.isKClass() -> javaLangType
this.isKClassArray() -> compilerContext.irBuiltIns.arrayClass.typeWith(javaLangType)
@@ -83,14 +83,14 @@ class SerializableCompanionIrGenerator(
val irSerializableClass = if (irClass.isCompanion) irClass.parentAsClass else irClass
val serializableWithAlreadyPresent = irSerializableClass.annotations.any {
it.symbol.descriptor.constructedClass.fqNameSafe == annotationMarkerClass.owner.fqNameWhenAvailable
it.constructedClass.fqNameWhenAvailable == annotationMarkerClass.owner.fqNameWhenAvailable
}
if (serializableWithAlreadyPresent) return
val annotationCtor = annotationMarkerClass.constructors.single { it.owner.isPrimary }
val annotationType = annotationMarkerClass.defaultType
val annotationCtorCall = IrConstructorCallImpl.fromSymbolDescriptor(startOffset, endOffset, annotationType, annotationCtor).apply {
val annotationCtorCall = IrConstructorCallImpl.fromSymbolOwner(startOffset, endOffset, annotationType, annotationCtor).apply {
putValueArgument(
0,
createClassReference(
@@ -150,7 +150,7 @@ class SerializableCompanionIrGenerator(
}
private fun generateSerializerFactoryIfNeeded(getterDescriptor: IrSimpleFunction) {
if (!irClass.descriptor.needSerializerFactory()) return
if (!irClass.needSerializerFactory(compilerContext)) return
val serialFactoryDescriptor = irClass.findDeclaration<IrSimpleFunction> {
it.valueParameters.size == 1
&& it.valueParameters.first().isVararg
@@ -6,7 +6,6 @@
package org.jetbrains.kotlinx.serialization.compiler.backend.ir
import org.jetbrains.kotlin.backend.common.lower.irThrow
import org.jetbrains.kotlin.codegen.CompilationException
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
import org.jetbrains.kotlin.ir.IrStatement
@@ -19,12 +18,10 @@ import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin
import org.jetbrains.kotlin.ir.expressions.impl.IrDelegatingConstructorCallImpl
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.util.OperatorNameConventions
import org.jetbrains.kotlin.utils.getOrPutNullable
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.CACHED_DESCRIPTOR_FIELD_NAME
@@ -97,8 +94,9 @@ class SerializableIrGenerator(
}
// Missing field exception parts
val exceptionCtorRef = compilerContext.referenceConstructors(ClassId(SerializationPackages.packageFqName, Name.identifier(MISSING_FIELD_EXC)))
.single { it.owner.valueParameters.singleOrNull()?.type?.isString() == true }
val exceptionCtorRef =
compilerContext.referenceConstructors(ClassId(SerializationPackages.packageFqName, Name.identifier(MISSING_FIELD_EXC)))
.single { it.owner.valueParameters.singleOrNull()?.type?.isString() == true }
val exceptionType = exceptionCtorRef.owner.returnType
val seenVarsOffset = serializableProperties.bitMaskSlotCount()
@@ -244,7 +242,7 @@ class SerializableIrGenerator(
?: error("Non-serializable parent of serializable $irClass must have no arg constructor")
val call = IrDelegatingConstructorCallImpl.fromSymbolDescriptor(
val call = IrDelegatingConstructorCallImpl.fromSymbolOwner(
startOffset,
endOffset,
compilerContext.irBuiltIns.unitType,
@@ -270,13 +268,14 @@ class SerializableIrGenerator(
propertiesStart: Int
): Int {
check(superClass.isInternalSerializable)
val superCtorRef = superClass.findSerializableSyntheticConstructor() ?: error("Class serializable internally should have special constructor with marker")
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) +
allValueParameters.subList(propertiesStart, propertiesStart + superProperties.size) +
allValueParameters.last() // SerializationConstructorMarker
val call = IrDelegatingConstructorCallImpl.fromSymbolDescriptor(
val call = IrDelegatingConstructorCallImpl.fromSymbolOwner(
startOffset,
endOffset,
compilerContext.irBuiltIns.unitType,
@@ -307,7 +306,7 @@ class SerializableIrGenerator(
// Compute offset of properties in superclass
var ignoreIndexTo = -1
val superClass = irClass.getSuperClassOrAny()
if (superClass.descriptor.isInternalSerializable) {
if (superClass.isInternalSerializable) {
ignoreIndexTo = serializablePropertiesForIrBackend(superClass).serializableProperties.size
// call super.writeSelf
@@ -380,16 +379,18 @@ class SerializableIrGenerator(
irClass: IrClass,
context: SerializationPluginContext,
) {
val serializableClass = irClass.descriptor
if (serializableClass.isInternalSerializable) {
if (irClass.isInternalSerializable) {
SerializableIrGenerator(irClass, context).generate()
irClass.patchDeclarationParents(irClass.parent)
} else if (serializableClass.serializableAnnotationIsUseless) {
throw CompilationException(
"@Serializable annotation on $serializableClass would be ignored because it is impossible to serialize it automatically. " +
"Provide serializer manually via e.g. companion object", null, serializableClass.findPsi()
)
} else {
val serializableAnnotationIsUseless = with(irClass) {
hasSerializableOrMetaAnnotationWithoutArgs() && !isInternalSerializable && !hasCompanionObjectAsSerializer && kind != ClassKind.ENUM_CLASS && !isSealedSerializableInterface
}
if (serializableAnnotationIsUseless)
throw AssertionError(
"@Serializable annotation on $irClass would be ignored because it is impossible to serialize it automatically. " +
"Provide serializer manually via e.g. companion object"
)
}
}
}
@@ -54,8 +54,7 @@ class SerializerForEnumsGenerator(
val decode = decoderClass.functionByName(CallingConventions.decodeEnum)
val serialDescGetter = irGet(descriptorGetterSymbol.owner.returnType, irThis(), descriptorGetterSymbol)
val serializableIrClass = requireNotNull(serializableIrClass) { "Enums do not support external serialization" }
val valuesF = serializableIrClass.functions.single { it.name == StandardNames.ENUM_VALUES }
val valuesF = this@SerializerForEnumsGenerator.serializableIrClass.functions.single { it.name == StandardNames.ENUM_VALUES }
val getValues = irInvoke(dispatchReceiver = null, callee = valuesF.symbol)
@@ -67,7 +66,7 @@ class SerializerForEnumsGenerator(
getValues,
arrayGet.symbol,
irInvoke(irGet(loadFunc.valueParameters[0]), decode, serialDescGetter),
typeHint = serializableIrClass.defaultType
typeHint = this@SerializerForEnumsGenerator.serializableIrClass.defaultType
)
+irReturn(getValueByOrdinal)
}
@@ -16,14 +16,11 @@ import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.deepCopyWithVariables
import org.jetbrains.kotlin.ir.expressions.IrConstructorCall
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrExpressionBody
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrBranchImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrDelegatingConstructorCallImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl
import org.jetbrains.kotlin.ir.expressions.mapValueParametersIndexed
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
@@ -117,7 +114,7 @@ open class SerializerIrGenerator(
}
val anonymousInit = irClass.run {
val symbol = IrAnonymousInitializerSymbolImpl(descriptor)
val symbol = IrAnonymousInitializerSymbolImpl(symbol)
irClass.factory.createAnonymousInitializer(startOffset, endOffset, SERIALIZATION_PLUGIN_ORIGIN, symbol).also {
it.parent = this
declarations.add(it)
@@ -207,7 +204,7 @@ open class SerializerIrGenerator(
addFunctionBody(typedConstructorDescriptor) { ctor ->
// generate call to primary ctor to init serialClassDesc and super()
val primaryCtor = irClass.constructors.primary
+IrDelegatingConstructorCallImpl.fromSymbolDescriptor(
+IrDelegatingConstructorCallImpl.fromSymbolOwner(
startOffset,
endOffset,
compilerContext.irBuiltIns.unitType,
@@ -268,15 +265,11 @@ open class SerializerIrGenerator(
val localSerialDesc = irTemporary(irGet(descriptorGetterSymbol.owner.returnType, irThis(), descriptorGetterSymbol), "desc")
// fun beginStructure(desc: SerialDescriptor, vararg typeParams: KSerializer<*>): StructureEncoder
// public fun beginStructure(descriptor: SerialDescriptor): CompositeDecoder
val beginFunc =
encoderClass.functions.single { it.owner.name.asString() == CallingConventions.begin && it.owner.valueParameters.size == 1 }
val call = irCall(beginFunc, type = kOutputClass.defaultType).mapValueParametersIndexed { _, _ ->
irGet(localSerialDesc)
}
// can it be done in more concise way? e.g. additional builder function?
call.dispatchReceiver = irGet(saveFunc.valueParameters[0])
val call = irInvoke(irGet(saveFunc.valueParameters[0]), beginFunc, irGet(localSerialDesc), typeHint = kOutputClass.defaultType)
val objectToSerialize = saveFunc.valueParameters[1]
val localOutput = irTemporary(call, "output")
@@ -12,6 +12,7 @@ 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.ObsoleteDescriptorBasedAPI
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
@@ -93,6 +94,7 @@ private class SerializerClassLowering(
SerializerIrGenerator.generate(irClass, context, context.metadataPlugin)
SerializableCompanionIrGenerator.generate(irClass, context)
@OptIn(ObsoleteDescriptorBasedAPI::class)
if (context.platform.isJvm() && KSerializerDescriptorResolver.isSerialInfoImpl(irClass.descriptor)) {
serialInfoJvmGenerator.generate(irClass)
}
@@ -20,7 +20,7 @@ enum class Plain {
class Holder(val p: Plain, val w: WithNames)
@OptIn(InternalSerializationApi::class)
fun box(): String {
fun testSerializers(): String {
val cs = (Holder.serializer() as GeneratedSerializer<*>).childSerializers()
val str1 = cs[0].toString()
if (!str1.contains("kotlinx.serialization.internal.EnumSerializer")) return str1
@@ -32,4 +32,17 @@ fun box(): String {
// val str2 = cs[1].toString()
// if (!str2.contains("kotlinx.serialization.internal.EnumSerializer")) return str2
return "OK"
}
fun testSerialization(previous: String): String {
if (previous != "OK") return previous
val h = Holder(Plain.B, WithNames.ENTRY1)
val s = Json.encodeToString(Holder.serializer(), h)
if (s != """{"p":"B","w":"A"}""") return s
if (Json.decodeFromString(Holder.serializer(), s).w != WithNames.ENTRY1) return "Deserialization failure"
return "OK"
}
fun box(): String {
return testSerialization(testSerializers())
}