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<*>> { tasks.withType<org.jetbrains.kotlin.gradle.dsl.KotlinCompile<*>> {
kotlinOptions { kotlinOptions {
freeCompilerArgs += "-opt-in=org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI"
freeCompilerArgs += "-opt-in=org.jetbrains.kotlin.compiler.plugin.ExperimentalCompilerApi" 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.backend.jvm.ir.representativeUpperBound
import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.isSealed
import org.jetbrains.kotlin.ir.builders.* import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.declarations.IrClass import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrFunction 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.extensions.SerializationPluginContext
import org.jetbrains.kotlinx.serialization.compiler.resolve.* 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 private val throwMissedFieldExceptionFunc = compilerContext.referenceFunctions(
= compilerContext.referenceFunctions(CallableId(SerializationPackages.internalPackageFqName, SerialEntityNames.SINGLE_MASK_FIELD_MISSING_FUNC_NAME)).singleOrNull() CallableId(
SerializationPackages.internalPackageFqName,
SerialEntityNames.SINGLE_MASK_FIELD_MISSING_FUNC_NAME
)
).singleOrNull()
private val throwMissedFieldExceptionArrayFunc private val throwMissedFieldExceptionArrayFunc = compilerContext.referenceFunctions(
= compilerContext.referenceFunctions(CallableId(SerializationPackages.internalPackageFqName, SerialEntityNames.ARRAY_MASK_FIELD_MISSING_FUNC_NAME)).singleOrNull() CallableId(
SerializationPackages.internalPackageFqName,
SerialEntityNames.ARRAY_MASK_FIELD_MISSING_FUNC_NAME
)
).singleOrNull()
private val enumSerializerFactoryFunc private val enumSerializerFactoryFunc = compilerContext.enumSerializerFactoryFunc
= compilerContext.enumSerializerFactoryFunc
private val markedEnumSerializerFactoryFunc private val markedEnumSerializerFactoryFunc = compilerContext.markedEnumSerializerFactoryFunc
= compilerContext.markedEnumSerializerFactoryFunc
fun useFieldMissingOptimization(): Boolean { fun useFieldMissingOptimization(): Boolean {
return throwMissedFieldExceptionFunc != null && throwMissedFieldExceptionArrayFunc != null return throwMissedFieldExceptionFunc != null && throwMissedFieldExceptionArrayFunc != null
@@ -250,7 +256,7 @@ abstract class BaseIrGenerator(private val currentClass: IrClass, final override
genericGetter genericGetter
) )
val (functionToCall, args: List<IrExpression>) = if (innerSerial != null) whenHaveSerializer(innerSerial, sti) else whenDoNot(sti) 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) 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> = 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 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)!! 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 // 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) } List(baseClass.typeParameters.size) { irGetObject(serializer) }
@@ -6,6 +6,7 @@
package org.jetbrains.kotlinx.serialization.compiler.backend.ir package org.jetbrains.kotlinx.serialization.compiler.backend.ir
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor 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.IrBuilderWithScope
import org.jetbrains.kotlin.ir.builders.irGet import org.jetbrains.kotlin.ir.builders.irGet
import org.jetbrains.kotlin.ir.builders.irGetField 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. 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. Also adapter may replace IR expression of getting `this` value by another expression.
*/ */
@OptIn(ObsoleteDescriptorBasedAPI::class)
fun createInitializerAdapter( fun createInitializerAdapter(
irClass: IrClass, irClass: IrClass,
paramGetReplacer: (ValueParameterDescriptor) -> IrExpression?, 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.KotlinBuiltIns
import org.jetbrains.kotlin.builtins.StandardNames import org.jetbrains.kotlin.builtins.StandardNames
import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI
import org.jetbrains.kotlin.ir.builders.* import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.builders.declarations.* import org.jetbrains.kotlin.ir.builders.declarations.*
import org.jetbrains.kotlin.ir.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.FqName
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.descriptorUtil.fqNameSafe
import org.jetbrains.kotlin.resolve.jvm.JvmPrimitiveType import org.jetbrains.kotlin.resolve.jvm.JvmPrimitiveType
import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPluginContext 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_FQ
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationDependencies.LAZY_MODE_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.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 { interface IrBuilderWithPluginContext {
@@ -134,7 +131,7 @@ interface IrBuilderWithPluginContext {
} }
fun IrClass.contributeAnonymousInitializer(bodyGen: IrBlockBodyBuilder.() -> Unit) { fun IrClass.contributeAnonymousInitializer(bodyGen: IrBlockBodyBuilder.() -> Unit) {
val symbol = IrAnonymousInitializerSymbolImpl(descriptor) val symbol = IrAnonymousInitializerSymbolImpl(symbol)
factory.createAnonymousInitializer(startOffset, endOffset, SERIALIZATION_PLUGIN_ORIGIN, symbol).also { factory.createAnonymousInitializer(startOffset, endOffset, SERIALIZATION_PLUGIN_ORIGIN, symbol).also {
it.parent = this it.parent = this
declarations.add(it) declarations.add(it)
@@ -256,9 +253,6 @@ interface IrBuilderWithPluginContext {
result result
) )
@FirIncompatiblePluginAPI
fun KotlinType.toIrType() = compilerContext.typeTranslator.translateType(this)
fun IrBuilderWithScope.setProperty(receiver: IrExpression, property: IrProperty, value: IrExpression): IrExpression { fun IrBuilderWithScope.setProperty(receiver: IrExpression, property: IrProperty, value: IrExpression): IrExpression {
return if (property.setter != null) return if (property.setter != null)
irSet(property.setter!!.returnType, receiver, property.setter!!.symbol, value) irSet(property.setter!!.returnType, receiver, property.setter!!.symbol, value)
@@ -269,7 +263,7 @@ interface IrBuilderWithPluginContext {
fun IrBuilderWithScope.generateAnySuperConstructorCall(toBuilder: IrBlockBodyBuilder) { fun IrBuilderWithScope.generateAnySuperConstructorCall(toBuilder: IrBlockBodyBuilder) {
val anyConstructor = compilerContext.irBuiltIns.anyClass.owner.declarations.single { it is IrConstructor } as IrConstructor val anyConstructor = compilerContext.irBuiltIns.anyClass.owner.declarations.single { it is IrConstructor } as IrConstructor
with(toBuilder) { with(toBuilder) {
+IrDelegatingConstructorCallImpl.fromSymbolDescriptor( +IrDelegatingConstructorCallImpl.fromSymbolOwner(
startOffset, endOffset, startOffset, endOffset,
compilerContext.irBuiltIns.unitType, compilerContext.irBuiltIns.unitType,
anyConstructor.symbol anyConstructor.symbol
@@ -348,14 +342,14 @@ interface IrBuilderWithPluginContext {
createClassReference(classSymbol.starProjectedType, startOffset, endOffset) createClassReference(classSymbol.starProjectedType, startOffset, endOffset)
fun collectSerialInfoAnnotations(irClass: IrClass): List<IrConstructorCall> { 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> = 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()) { for (clazz in irClass.getAllSuperclasses()) {
val annotations = clazz.annotations val annotations = clazz.annotations
.mapNotNull { .mapNotNull {
val descriptor = it.symbol.owner.parentAsClass.descriptor val parent = it.symbol.owner.parentAsClass
if (descriptor.isInheritableSerialInfoAnnotation) descriptor.fqNameSafe to it else null if (parent.isInheritableSerialInfoAnnotation) parent.fqNameWhenAvailable!! to it else null
} }
annotations.forEach { (fqname, call) -> annotations.forEach { (fqname, call) ->
if (fqname !in annotationByFq) { if (fqname !in annotationByFq) {
@@ -371,7 +365,7 @@ interface IrBuilderWithPluginContext {
fun IrBuilderWithScope.copyAnnotationsFrom(annotations: List<IrConstructorCall>): List<IrExpression> = fun IrBuilderWithScope.copyAnnotationsFrom(annotations: List<IrConstructorCall>): List<IrExpression> =
annotations.mapNotNull { annotationCall -> annotations.mapNotNull { annotationCall ->
val annotationClass = annotationCall.symbol.owner.parentAsClass val annotationClass = annotationCall.symbol.owner.parentAsClass
if (!annotationClass.descriptor.isSerialInfoAnnotation) return@mapNotNull null if (!annotationClass.isSerialInfoAnnotation) return@mapNotNull null
if (compilerContext.platform.isJvm()) { if (compilerContext.platform.isJvm()) {
val implClass = compilerContext.serialInfoImplJvmIrGenerator.getImplClass(annotationClass) val implClass = compilerContext.serialInfoImplJvmIrGenerator.getImplClass(annotationClass)
@@ -389,6 +383,7 @@ interface IrBuilderWithPluginContext {
} }
} }
@OptIn(ObsoleteDescriptorBasedAPI::class)
fun IrBuilderWithScope.wrapperClassReference(classType: IrType): IrClassReference { fun IrBuilderWithScope.wrapperClassReference(classType: IrType): IrClassReference {
if (compilerContext.platform.isJvm()) { if (compilerContext.platform.isJvm()) {
// "Byte::class" -> "java.lang.Byte::class" // "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.CallableMemberDescriptor
import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI
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.IrConstructorCall
import org.jetbrains.kotlin.ir.expressions.IrExpression 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.types.*
import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.name.Name 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.extensions.SerializationPluginContext
import org.jetbrains.kotlinx.serialization.compiler.fir.SerializationPluginKey import org.jetbrains.kotlinx.serialization.compiler.fir.SerializationPluginKey
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames import org.jetbrains.kotlinx.serialization.compiler.resolve.*
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationAnnotations
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationPackages
internal fun IrType.isKSerializer(): Boolean { internal fun IrType.isKSerializer(): Boolean {
val simpleType = this as? IrSimpleType ?: return false val simpleType = this as? IrSimpleType ?: return false
@@ -89,7 +90,7 @@ private fun IrClass.checkSerializableOrMetaAnnotationArgs(mustDoNotHaveArgs: Boo
return true return true
} }
return annotations return annotations
.map { it.annotationClass.annotations } .map { it.constructedClass.annotations }
.any { it.hasAnnotation(SerializationAnnotations.metaSerializableAnnotationFqName) } .any { it.hasAnnotation(SerializationAnnotations.metaSerializableAnnotationFqName) }
} }
@@ -98,6 +99,9 @@ internal val IrClass.isSerialInfoAnnotation: Boolean
|| annotations.hasAnnotation(SerializationAnnotations.inheritableSerialInfoFqName) || annotations.hasAnnotation(SerializationAnnotations.inheritableSerialInfoFqName)
|| annotations.hasAnnotation(SerializationAnnotations.metaSerializableAnnotationFqName) || annotations.hasAnnotation(SerializationAnnotations.metaSerializableAnnotationFqName)
internal val IrClass.isInheritableSerialInfoAnnotation: Boolean
get() = annotations.hasAnnotation(SerializationAnnotations.inheritableSerialInfoFqName)
internal fun IrClass.shouldHaveGeneratedSerializer(context: SerializationPluginContext): Boolean internal fun IrClass.shouldHaveGeneratedSerializer(context: SerializationPluginContext): Boolean
= (isInternalSerializable && (modality == Modality.FINAL || modality == Modality.OPEN)) = (isInternalSerializable && (modality == Modality.FINAL || modality == Modality.OPEN))
|| isEnumWithLegacyGeneratedSerializer(context) || isEnumWithLegacyGeneratedSerializer(context)
@@ -142,6 +146,7 @@ fun IrClass.getSuperClassNotAny(): IrClass? {
return if (parentClass.defaultType.isAny()) null else parentClass return if (parentClass.defaultType.isAny()) null else parentClass
} }
@OptIn(ObsoleteDescriptorBasedAPI::class)
internal fun IrDeclaration.isFromPlugin(): Boolean = internal fun IrDeclaration.isFromPlugin(): Boolean =
this.origin == IrDeclarationOrigin.GeneratedByPlugin(SerializationPluginKey) || (this.descriptor as? CallableMemberDescriptor)?.kind == CallableMemberDescriptor.Kind.SYNTHESIZED // old FE doesn't specify origin 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 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 = internal fun IrExpression.isInitializePropertyFromParameter(): Boolean =
this is IrGetValueImpl && this.origin == IrStatementOrigin.INITIALIZE_PROPERTY_FROM_PARAMETER this is IrGetValueImpl && this.origin == IrStatementOrigin.INITIALIZE_PROPERTY_FROM_PARAMETER
internal val IrConstructorCall.annotationClass internal val IrConstructorCall.constructedClass
get() = this.symbol.owner.constructedClass get() = this.symbol.owner.constructedClass
internal val List<IrConstructorCall>.hasAnySerialAnnotation: Boolean 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? internal val List<IrConstructorCall>.serialNameValue: String?
get() = findAnnotation(SerializationAnnotations.serialNameAnnotationFqName)?.getStringConstArgument(0) // @SerialName("foo") get() = findAnnotation(SerializationAnnotations.serialNameAnnotationFqName)?.getStringConstArgument(0) // @SerialName("foo")
@@ -6,6 +6,7 @@
package org.jetbrains.kotlinx.serialization.compiler.backend.ir package org.jetbrains.kotlinx.serialization.compiler.backend.ir
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities 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.IrClass
import org.jetbrains.kotlin.ir.declarations.IrProperty import org.jetbrains.kotlin.ir.declarations.IrProperty
import org.jetbrains.kotlin.ir.types.IrSimpleType import org.jetbrains.kotlin.ir.types.IrSimpleType
@@ -41,6 +42,7 @@ class IrSerializableProperties(
override val serializableStandaloneProperties: List<IrSerializableProperty> override val serializableStandaloneProperties: List<IrSerializableProperty>
) : ISerializableProperties<IrSerializableProperty> ) : ISerializableProperties<IrSerializableProperty>
@OptIn(ObsoleteDescriptorBasedAPI::class)
internal fun serializablePropertiesForIrBackend( internal fun serializablePropertiesForIrBackend(
irClass: IrClass, irClass: IrClass,
serializationDescriptorSerializer: SerializationDescriptorSerializerPlugin? = null 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.extensions.FirIncompatiblePluginAPI
import org.jetbrains.kotlin.backend.common.ir.addExtensionReceiver import org.jetbrains.kotlin.backend.common.ir.addExtensionReceiver
import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.builders.declarations.* import org.jetbrains.kotlin.ir.builders.declarations.*
import org.jetbrains.kotlin.ir.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.name.Name
import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.descriptorUtil.isEffectivelyExternal 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.extensions.SerializationPluginContext
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames 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 // 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). // 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. // 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( class SerialInfoImplJvmIrGenerator(
private val context: SerializationPluginContext, private val context: SerializationPluginContext,
private val moduleFragment: IrModuleFragment, private val moduleFragment: IrModuleFragment,
@@ -53,7 +56,7 @@ class SerialInfoImplJvmIrGenerator(
fun getImplClass(serialInfoAnnotationClass: IrClass): IrClass = fun getImplClass(serialInfoAnnotationClass: IrClass): IrClass =
annotationToImpl.getOrPut(serialInfoAnnotationClass) { annotationToImpl.getOrPut(serialInfoAnnotationClass) {
@OptIn(FirIncompatiblePluginAPI::class) // TODO @OptIn(FirIncompatiblePluginAPI::class)
val implClassSymbol = context.referenceClass(serialInfoAnnotationClass.kotlinFqName.child(SerialEntityNames.IMPL_NAME)) val implClassSymbol = context.referenceClass(serialInfoAnnotationClass.kotlinFqName.child(SerialEntityNames.IMPL_NAME))
implClassSymbol!!.owner.apply(this::generate) implClassSymbol!!.owner.apply(this::generate)
} }
@@ -112,6 +115,9 @@ class SerialInfoImplJvmIrGenerator(
putValueArgument(0, IrConstImpl.string(UNDEFINED_OFFSET, UNDEFINED_OFFSET, context.irBuiltIns.stringType, name)) 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 { private fun IrType.kClassToJClassIfNeeded(): IrType = when {
this.isKClass() -> javaLangType this.isKClass() -> javaLangType
this.isKClassArray() -> compilerContext.irBuiltIns.arrayClass.typeWith(javaLangType) this.isKClassArray() -> compilerContext.irBuiltIns.arrayClass.typeWith(javaLangType)
@@ -83,14 +83,14 @@ class SerializableCompanionIrGenerator(
val irSerializableClass = if (irClass.isCompanion) irClass.parentAsClass else irClass val irSerializableClass = if (irClass.isCompanion) irClass.parentAsClass else irClass
val serializableWithAlreadyPresent = irSerializableClass.annotations.any { val serializableWithAlreadyPresent = irSerializableClass.annotations.any {
it.symbol.descriptor.constructedClass.fqNameSafe == annotationMarkerClass.owner.fqNameWhenAvailable it.constructedClass.fqNameWhenAvailable == annotationMarkerClass.owner.fqNameWhenAvailable
} }
if (serializableWithAlreadyPresent) return if (serializableWithAlreadyPresent) return
val annotationCtor = annotationMarkerClass.constructors.single { it.owner.isPrimary } val annotationCtor = annotationMarkerClass.constructors.single { it.owner.isPrimary }
val annotationType = annotationMarkerClass.defaultType val annotationType = annotationMarkerClass.defaultType
val annotationCtorCall = IrConstructorCallImpl.fromSymbolDescriptor(startOffset, endOffset, annotationType, annotationCtor).apply { val annotationCtorCall = IrConstructorCallImpl.fromSymbolOwner(startOffset, endOffset, annotationType, annotationCtor).apply {
putValueArgument( putValueArgument(
0, 0,
createClassReference( createClassReference(
@@ -150,7 +150,7 @@ class SerializableCompanionIrGenerator(
} }
private fun generateSerializerFactoryIfNeeded(getterDescriptor: IrSimpleFunction) { private fun generateSerializerFactoryIfNeeded(getterDescriptor: IrSimpleFunction) {
if (!irClass.descriptor.needSerializerFactory()) return if (!irClass.needSerializerFactory(compilerContext)) return
val serialFactoryDescriptor = irClass.findDeclaration<IrSimpleFunction> { val serialFactoryDescriptor = irClass.findDeclaration<IrSimpleFunction> {
it.valueParameters.size == 1 it.valueParameters.size == 1
&& it.valueParameters.first().isVararg && it.valueParameters.first().isVararg
@@ -6,7 +6,6 @@
package org.jetbrains.kotlinx.serialization.compiler.backend.ir package org.jetbrains.kotlinx.serialization.compiler.backend.ir
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.descriptors.ClassKind import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
import org.jetbrains.kotlin.ir.IrStatement 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.expressions.impl.IrDelegatingConstructorCallImpl
import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.* 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.ClassId
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.util.OperatorNameConventions import org.jetbrains.kotlin.util.OperatorNameConventions
import org.jetbrains.kotlin.utils.getOrPutNullable 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.extensions.SerializationPluginContext
import org.jetbrains.kotlinx.serialization.compiler.resolve.* import org.jetbrains.kotlinx.serialization.compiler.resolve.*
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.CACHED_DESCRIPTOR_FIELD_NAME import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.CACHED_DESCRIPTOR_FIELD_NAME
@@ -97,8 +94,9 @@ class SerializableIrGenerator(
} }
// Missing field exception parts // Missing field exception parts
val exceptionCtorRef = compilerContext.referenceConstructors(ClassId(SerializationPackages.packageFqName, Name.identifier(MISSING_FIELD_EXC))) val exceptionCtorRef =
.single { it.owner.valueParameters.singleOrNull()?.type?.isString() == true } compilerContext.referenceConstructors(ClassId(SerializationPackages.packageFqName, Name.identifier(MISSING_FIELD_EXC)))
.single { it.owner.valueParameters.singleOrNull()?.type?.isString() == true }
val exceptionType = exceptionCtorRef.owner.returnType val exceptionType = exceptionCtorRef.owner.returnType
val seenVarsOffset = serializableProperties.bitMaskSlotCount() val seenVarsOffset = serializableProperties.bitMaskSlotCount()
@@ -244,7 +242,7 @@ class SerializableIrGenerator(
?: error("Non-serializable parent of serializable $irClass must have no arg constructor") ?: error("Non-serializable parent of serializable $irClass must have no arg constructor")
val call = IrDelegatingConstructorCallImpl.fromSymbolDescriptor( val call = IrDelegatingConstructorCallImpl.fromSymbolOwner(
startOffset, startOffset,
endOffset, endOffset,
compilerContext.irBuiltIns.unitType, compilerContext.irBuiltIns.unitType,
@@ -270,13 +268,14 @@ class SerializableIrGenerator(
propertiesStart: Int propertiesStart: Int
): Int { ): Int {
check(superClass.isInternalSerializable) 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 superProperties = serializablePropertiesForIrBackend(superClass).serializableProperties
val superSlots = superProperties.bitMaskSlotCount() val superSlots = superProperties.bitMaskSlotCount()
val arguments = allValueParameters.subList(0, superSlots) + val arguments = allValueParameters.subList(0, superSlots) +
allValueParameters.subList(propertiesStart, propertiesStart + superProperties.size) + allValueParameters.subList(propertiesStart, propertiesStart + superProperties.size) +
allValueParameters.last() // SerializationConstructorMarker allValueParameters.last() // SerializationConstructorMarker
val call = IrDelegatingConstructorCallImpl.fromSymbolDescriptor( val call = IrDelegatingConstructorCallImpl.fromSymbolOwner(
startOffset, startOffset,
endOffset, endOffset,
compilerContext.irBuiltIns.unitType, compilerContext.irBuiltIns.unitType,
@@ -307,7 +306,7 @@ class SerializableIrGenerator(
// Compute offset of properties in superclass // Compute offset of properties in superclass
var ignoreIndexTo = -1 var ignoreIndexTo = -1
val superClass = irClass.getSuperClassOrAny() val superClass = irClass.getSuperClassOrAny()
if (superClass.descriptor.isInternalSerializable) { if (superClass.isInternalSerializable) {
ignoreIndexTo = serializablePropertiesForIrBackend(superClass).serializableProperties.size ignoreIndexTo = serializablePropertiesForIrBackend(superClass).serializableProperties.size
// call super.writeSelf // call super.writeSelf
@@ -380,16 +379,18 @@ class SerializableIrGenerator(
irClass: IrClass, irClass: IrClass,
context: SerializationPluginContext, context: SerializationPluginContext,
) { ) {
val serializableClass = irClass.descriptor if (irClass.isInternalSerializable) {
if (serializableClass.isInternalSerializable) {
SerializableIrGenerator(irClass, context).generate() SerializableIrGenerator(irClass, context).generate()
irClass.patchDeclarationParents(irClass.parent) irClass.patchDeclarationParents(irClass.parent)
} else if (serializableClass.serializableAnnotationIsUseless) { } else {
throw CompilationException( val serializableAnnotationIsUseless = with(irClass) {
"@Serializable annotation on $serializableClass would be ignored because it is impossible to serialize it automatically. " + hasSerializableOrMetaAnnotationWithoutArgs() && !isInternalSerializable && !hasCompanionObjectAsSerializer && kind != ClassKind.ENUM_CLASS && !isSealedSerializableInterface
"Provide serializer manually via e.g. companion object", null, serializableClass.findPsi() }
) 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 decode = decoderClass.functionByName(CallingConventions.decodeEnum)
val serialDescGetter = irGet(descriptorGetterSymbol.owner.returnType, irThis(), descriptorGetterSymbol) val serialDescGetter = irGet(descriptorGetterSymbol.owner.returnType, irThis(), descriptorGetterSymbol)
val serializableIrClass = requireNotNull(serializableIrClass) { "Enums do not support external serialization" } val valuesF = this@SerializerForEnumsGenerator.serializableIrClass.functions.single { it.name == StandardNames.ENUM_VALUES }
val valuesF = serializableIrClass.functions.single { it.name == StandardNames.ENUM_VALUES }
val getValues = irInvoke(dispatchReceiver = null, callee = valuesF.symbol) val getValues = irInvoke(dispatchReceiver = null, callee = valuesF.symbol)
@@ -67,7 +66,7 @@ class SerializerForEnumsGenerator(
getValues, getValues,
arrayGet.symbol, arrayGet.symbol,
irInvoke(irGet(loadFunc.valueParameters[0]), decode, serialDescGetter), irInvoke(irGet(loadFunc.valueParameters[0]), decode, serialDescGetter),
typeHint = serializableIrClass.defaultType typeHint = this@SerializerForEnumsGenerator.serializableIrClass.defaultType
) )
+irReturn(getValueByOrdinal) +irReturn(getValueByOrdinal)
} }
@@ -16,14 +16,11 @@ import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
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.deepCopyWithVariables import org.jetbrains.kotlin.ir.deepCopyWithVariables
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.IrExpressionBody
import org.jetbrains.kotlin.ir.expressions.impl.IrBranchImpl import org.jetbrains.kotlin.ir.expressions.impl.IrBranchImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrDelegatingConstructorCallImpl import org.jetbrains.kotlin.ir.expressions.impl.IrDelegatingConstructorCallImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl 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.IrClassSymbol
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
@@ -117,7 +114,7 @@ open class SerializerIrGenerator(
} }
val anonymousInit = irClass.run { val anonymousInit = irClass.run {
val symbol = IrAnonymousInitializerSymbolImpl(descriptor) val symbol = IrAnonymousInitializerSymbolImpl(symbol)
irClass.factory.createAnonymousInitializer(startOffset, endOffset, SERIALIZATION_PLUGIN_ORIGIN, symbol).also { irClass.factory.createAnonymousInitializer(startOffset, endOffset, SERIALIZATION_PLUGIN_ORIGIN, symbol).also {
it.parent = this it.parent = this
declarations.add(it) declarations.add(it)
@@ -207,7 +204,7 @@ open class SerializerIrGenerator(
addFunctionBody(typedConstructorDescriptor) { ctor -> addFunctionBody(typedConstructorDescriptor) { ctor ->
// generate call to primary ctor to init serialClassDesc and super() // generate call to primary ctor to init serialClassDesc and super()
val primaryCtor = irClass.constructors.primary val primaryCtor = irClass.constructors.primary
+IrDelegatingConstructorCallImpl.fromSymbolDescriptor( +IrDelegatingConstructorCallImpl.fromSymbolOwner(
startOffset, startOffset,
endOffset, endOffset,
compilerContext.irBuiltIns.unitType, compilerContext.irBuiltIns.unitType,
@@ -268,15 +265,11 @@ open class SerializerIrGenerator(
val localSerialDesc = irTemporary(irGet(descriptorGetterSymbol.owner.returnType, irThis(), descriptorGetterSymbol), "desc") 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 = val beginFunc =
encoderClass.functions.single { it.owner.name.asString() == CallingConventions.begin && it.owner.valueParameters.size == 1 } encoderClass.functions.single { it.owner.name.asString() == CallingConventions.begin && it.owner.valueParameters.size == 1 }
val call = irCall(beginFunc, type = kOutputClass.defaultType).mapValueParametersIndexed { _, _ -> val call = irInvoke(irGet(saveFunc.valueParameters[0]), beginFunc, irGet(localSerialDesc), typeHint = kOutputClass.defaultType)
irGet(localSerialDesc)
}
// can it be done in more concise way? e.g. additional builder function?
call.dispatchReceiver = irGet(saveFunc.valueParameters[0])
val objectToSerialize = saveFunc.valueParameters[1] val objectToSerialize = saveFunc.valueParameters[1]
val localOutput = irTemporary(call, "output") 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.common.runOnFilePostfix
import org.jetbrains.kotlin.backend.jvm.ir.fileParent import org.jetbrains.kotlin.backend.jvm.ir.fileParent
import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI
import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
@@ -93,6 +94,7 @@ private class SerializerClassLowering(
SerializerIrGenerator.generate(irClass, context, context.metadataPlugin) SerializerIrGenerator.generate(irClass, context, context.metadataPlugin)
SerializableCompanionIrGenerator.generate(irClass, context) SerializableCompanionIrGenerator.generate(irClass, context)
@OptIn(ObsoleteDescriptorBasedAPI::class)
if (context.platform.isJvm() && KSerializerDescriptorResolver.isSerialInfoImpl(irClass.descriptor)) { if (context.platform.isJvm() && KSerializerDescriptorResolver.isSerialInfoImpl(irClass.descriptor)) {
serialInfoJvmGenerator.generate(irClass) serialInfoJvmGenerator.generate(irClass)
} }
@@ -20,7 +20,7 @@ enum class Plain {
class Holder(val p: Plain, val w: WithNames) class Holder(val p: Plain, val w: WithNames)
@OptIn(InternalSerializationApi::class) @OptIn(InternalSerializationApi::class)
fun box(): String { fun testSerializers(): String {
val cs = (Holder.serializer() as GeneratedSerializer<*>).childSerializers() val cs = (Holder.serializer() as GeneratedSerializer<*>).childSerializers()
val str1 = cs[0].toString() val str1 = cs[0].toString()
if (!str1.contains("kotlinx.serialization.internal.EnumSerializer")) return str1 if (!str1.contains("kotlinx.serialization.internal.EnumSerializer")) return str1
@@ -33,3 +33,16 @@ fun box(): String {
// if (!str2.contains("kotlinx.serialization.internal.EnumSerializer")) return str2 // if (!str2.contains("kotlinx.serialization.internal.EnumSerializer")) return str2
return "OK" 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())
}