diff --git a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrPluginContext.kt b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrPluginContext.kt index 0b7df171f49..489d07efec7 100644 --- a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrPluginContext.kt +++ b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrPluginContext.kt @@ -17,6 +17,7 @@ import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider import org.jetbrains.kotlin.fir.scopes.* import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol import org.jetbrains.kotlin.ir.IrBuiltIns import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI @@ -41,6 +42,7 @@ class Fir2IrPluginContext(private val components: Fir2IrComponents) : IrPluginCo override val moduleDescriptor: ModuleDescriptor get() = error(ERROR_MESSAGE) + @Suppress("OVERRIDE_DEPRECATION") @ObsoleteDescriptorBasedAPI override val bindingContext: BindingContext get() = error(ERROR_MESSAGE) @@ -67,7 +69,8 @@ class Fir2IrPluginContext(private val components: Fir2IrComponents) : IrPluginCo get() = components.session.symbolProvider override fun referenceClass(classId: ClassId): IrClassSymbol? { - return referenceClassLikeSymbol(classId, symbolProvider::getClassLikeSymbolByClassId, symbolTable::referenceClass) + val firSymbol = symbolProvider.getClassLikeSymbolByClassId(classId) as? FirClassSymbol<*> ?: return null + return components.classifierStorage.getIrClassSymbol(firSymbol) } override fun referenceTypeAlias(classId: ClassId): IrTypeAliasSymbol? { @@ -135,6 +138,8 @@ class Fir2IrPluginContext(private val components: Fir2IrComponents) : IrPluginCo error(ERROR_MESSAGE) } + + @Deprecated("Use classId overload instead") override fun referenceClass(fqName: FqName): IrClassSymbol? { error(ERROR_MESSAGE) } @@ -147,10 +152,12 @@ class Fir2IrPluginContext(private val components: Fir2IrComponents) : IrPluginCo error(ERROR_MESSAGE) } + @Deprecated("Use callableId overload instead") override fun referenceFunctions(fqName: FqName): Collection { error(ERROR_MESSAGE) } + @Deprecated("Use callableId overload instead") override fun referenceProperties(fqName: FqName): Collection { error(ERROR_MESSAGE) } diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/extensions/IrPluginContext.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/extensions/IrPluginContext.kt index a2fafe9ee4f..fdea8bf3492 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/extensions/IrPluginContext.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/extensions/IrPluginContext.kt @@ -29,11 +29,13 @@ interface IrPluginContext : IrGeneratorContext { val moduleDescriptor: ModuleDescriptor @ObsoleteDescriptorBasedAPI + @Deprecated("", level = DeprecationLevel.ERROR) val bindingContext: BindingContext val symbolTable: ReferenceSymbolTable @ObsoleteDescriptorBasedAPI +// @Deprecated("", level = DeprecationLevel.ERROR) val typeTranslator: TypeTranslator val symbols: BuiltinSymbolsBase @@ -49,10 +51,13 @@ interface IrPluginContext : IrGeneratorContext { fun createDiagnosticReporter(pluginId: String): IrMessageLogger // The following API is experimental + @Deprecated("Use classId overload instead") fun referenceClass(fqName: FqName): IrClassSymbol? fun referenceTypeAlias(fqName: FqName): IrTypeAliasSymbol? fun referenceConstructors(classFqn: FqName): Collection + @Deprecated("Use callableId overload instead") fun referenceFunctions(fqName: FqName): Collection + @Deprecated("Use callableId overload instead") fun referenceProperties(fqName: FqName): Collection // This one is experimental too diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/extensions/IrPluginContextImpl.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/extensions/IrPluginContextImpl.kt index 53c314c8b4e..a9c8759c485 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/extensions/IrPluginContextImpl.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/extensions/IrPluginContextImpl.kt @@ -29,6 +29,7 @@ import org.jetbrains.kotlin.resolve.scopes.MemberScope open class IrPluginContextImpl constructor( private val module: ModuleDescriptor, + @Deprecated("", level = DeprecationLevel.ERROR) @OptIn(ObsoleteDescriptorBasedAPI::class) override val bindingContext: BindingContext, override val languageVersionSettings: LanguageVersionSettings, @@ -97,6 +98,7 @@ open class IrPluginContextImpl constructor( return symbols } + @Deprecated("Use classId overload instead") @OptIn(ObsoleteDescriptorBasedAPI::class) override fun referenceClass(fqName: FqName): IrClassSymbol? { assert(!fqName.isRoot) @@ -120,10 +122,12 @@ open class IrPluginContextImpl constructor( } override fun referenceConstructors(classFqn: FqName): Collection { + @Suppress("DEPRECATION") val classSymbol = referenceClass(classFqn) ?: error("Cannot find class $classFqn") return classSymbol.owner.declarations.filterIsInstance().map { it.symbol } } + @Deprecated("Use callableId overload instead") @OptIn(ObsoleteDescriptorBasedAPI::class) override fun referenceFunctions(fqName: FqName): Collection { assert(!fqName.isRoot) @@ -133,6 +137,7 @@ open class IrPluginContextImpl constructor( } } + @Deprecated("Use callableId overload instead") @OptIn(ObsoleteDescriptorBasedAPI::class) override fun referenceProperties(fqName: FqName): Collection { assert(!fqName.isRoot) @@ -143,6 +148,7 @@ open class IrPluginContextImpl constructor( } override fun referenceClass(classId: ClassId): IrClassSymbol? { + @Suppress("DEPRECATION") return referenceClass(classId.asSingleFqName()) } @@ -155,10 +161,12 @@ open class IrPluginContextImpl constructor( } override fun referenceFunctions(callableId: CallableId): Collection { + @Suppress("DEPRECATION") return referenceFunctions(callableId.asSingleFqName()) } override fun referenceProperties(callableId: CallableId): Collection { + @Suppress("DEPRECATION") return referenceProperties(callableId.asSingleFqName()) } diff --git a/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/codegen/AndroidIrExtension.kt b/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/codegen/AndroidIrExtension.kt index 9d666560e1d..a4014bb3d4a 100644 --- a/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/codegen/AndroidIrExtension.kt +++ b/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/codegen/AndroidIrExtension.kt @@ -120,6 +120,7 @@ private class AndroidIrTransformer(val extension: AndroidIrExtension, val plugin } // NOTE: sparse array version intentionally not implemented; this plugin is deprecated + @Suppress("DEPRECATION") // TODO: check that it still works with FIR private val mapFactory = pluginContext.referenceFunctions(FqName("kotlin.collections.mutableMapOf")) .single { it.owner.valueParameters.isEmpty() } private val mapGet = pluginContext.irBuiltIns.mapClass.owner.functions diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/build.gradle.kts b/plugins/kotlin-serialization/kotlin-serialization-compiler/build.gradle.kts index a998e959db5..64eafb58b37 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/build.gradle.kts +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/build.gradle.kts @@ -14,6 +14,7 @@ dependencies { compileOnly(project(":compiler:frontend")) compileOnly(project(":compiler:backend")) compileOnly(project(":compiler:ir.backend.common")) + compileOnly(project(":compiler:backend.jvm")) compileOnly(project(":compiler:ir.tree")) compileOnly(project(":js:js.frontend")) compileOnly(project(":js:js.translator")) diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/AbstractSerialGenerator.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/AbstractSerialGenerator.kt index c0049fa9685..639bf6add03 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/AbstractSerialGenerator.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/AbstractSerialGenerator.kt @@ -22,9 +22,12 @@ import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationAnnotat import org.jetbrains.kotlinx.serialization.compiler.resolve.isKSerializer import org.jetbrains.kotlinx.serialization.compiler.resolve.toClassDescriptor -abstract class AbstractSerialGenerator(val bindingContext: BindingContext, val currentDeclaration: ClassDescriptor) { +const val K2_ERR_MESSAGE = "K2 not supported yet.\nBindingContext is null, meaning this function is used from the K2 compiler. Please report to devs so we can support this feature." + +abstract class AbstractSerialGenerator(val bindingContext: BindingContext?, val currentDeclaration: ClassDescriptor) { private fun getKClassListFromFileAnnotation(annotationFqName: FqName, declarationInFile: DeclarationDescriptor): List { + if (bindingContext == null) return emptyList()// TODO val annotation = AnnotationsUtils .getContainingFileAnnotations(bindingContext, declarationInFile) .find { it.fqName == annotationFqName } diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/SerializableCodegen.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/SerializableCodegen.kt index a6344f077e2..d8343017a8d 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/SerializableCodegen.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/SerializableCodegen.kt @@ -16,7 +16,7 @@ import org.jetbrains.kotlinx.serialization.compiler.resolve.* abstract class SerializableCodegen( protected val serializableDescriptor: ClassDescriptor, - bindingContext: BindingContext + bindingContext: BindingContext? ) : AbstractSerialGenerator(bindingContext, serializableDescriptor) { protected val properties = bindingContext.serializablePropertiesFor(serializableDescriptor) diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/SerializableCompanionCodegen.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/SerializableCompanionCodegen.kt index 728abb1e1bf..f0cf7c7d7e3 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/SerializableCompanionCodegen.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/SerializableCompanionCodegen.kt @@ -8,6 +8,7 @@ package org.jetbrains.kotlinx.serialization.compiler.backend.common import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor +import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlinx.serialization.compiler.resolve.* @@ -15,12 +16,12 @@ import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.SE abstract class SerializableCompanionCodegen( protected val companionDescriptor: ClassDescriptor, - bindingContext: BindingContext + bindingContext: BindingContext? ) : AbstractSerialGenerator(bindingContext, companionDescriptor) { protected val serializableDescriptor: ClassDescriptor = getSerializableClassDescriptorByCompanion(companionDescriptor)!! - fun generate() { - val serializerGetterDescriptor = companionDescriptor.unsubstitutedMemberScope.getContributedFunctions( + open fun getSerializerGetterDescriptor(): FunctionDescriptor { + return companionDescriptor.unsubstitutedMemberScope.getContributedFunctions( SERIALIZER_PROVIDER_NAME, NoLookupLocation.FROM_BACKEND ).firstOrNull { @@ -32,6 +33,10 @@ abstract class SerializableCompanionCodegen( "Can't find synthesized 'Companion.serializer()' function to generate, " + "probably clash with user-defined function has occurred" ) + } + + fun generate() { + val serializerGetterDescriptor = getSerializerGetterDescriptor() if (serializableDescriptor.isSerializableObject || serializableDescriptor.isAbstractOrSealedSerializableClass() diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/SerializerCodegen.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/SerializerCodegen.kt index bd9f278b32a..f4eb4ef3e77 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/SerializerCodegen.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/SerializerCodegen.kt @@ -16,7 +16,7 @@ import org.jetbrains.kotlinx.serialization.compiler.resolve.* abstract class SerializerCodegen( protected val serializerDescriptor: ClassDescriptor, - bindingContext: BindingContext, + bindingContext: BindingContext?, metadataPlugin: SerializationDescriptorSerializerPlugin? ) : AbstractSerialGenerator(bindingContext, serializerDescriptor) { val serializableDescriptor: ClassDescriptor = getSerializableClassDescriptorBySerializer(serializerDescriptor)!! diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/TypeUtil.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/TypeUtil.kt index f274e18f24a..c45f02ccb9b 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/TypeUtil.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/TypeUtil.kt @@ -6,10 +6,15 @@ package org.jetbrains.kotlinx.serialization.compiler.backend.common import com.intellij.psi.PsiElement +import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.codegen.CompilationException import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.annotations.Annotations +import org.jetbrains.kotlin.ir.declarations.IrProperty +import org.jetbrains.kotlin.ir.symbols.IrClassSymbol +import org.jetbrains.kotlin.ir.types.* +import org.jetbrains.kotlin.ir.util.isTypeParameter import org.jetbrains.kotlin.js.descriptorUtils.getJetTypeFqName import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi import org.jetbrains.kotlin.name.ClassId @@ -21,17 +26,32 @@ import org.jetbrains.kotlin.psi.KtPureClassOrObject import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.types.model.KotlinTypeMarker import org.jetbrains.kotlin.types.typeUtil.* import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.enumSerializerId import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.referenceArraySerializerId +import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPluginContext import org.jetbrains.kotlinx.serialization.compiler.resolve.* import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationPackages.internalPackageFqName +interface ISerialTypeInfo> { + val property: S + val elementMethodPrefix: String + val serializer: C? +} + open class SerialTypeInfo( - val property: SerializableProperty, - val elementMethodPrefix: String, - val serializer: ClassDescriptor? = null -) + override val property: SerializableProperty, + override val elementMethodPrefix: String, + override val serializer: ClassDescriptor? = null +) : ISerialTypeInfo + +class IrSerialTypeInfo( + override val property: IrSerializableProperty, + override val elementMethodPrefix: String, + override val serializer: IrClassSymbol? = null +) : ISerialTypeInfo + fun AbstractSerialGenerator.findAddOnSerializer(propertyType: KotlinType, module: ModuleDescriptor): ClassDescriptor? { additionalSerializersInScopeOfCurrentFile[propertyType.toClassDescriptor to propertyType.isMarkedNullable]?.let { return it } @@ -46,6 +66,33 @@ fun AbstractSerialGenerator.findAddOnSerializer(propertyType: KotlinType, module fun KotlinType.isGeneratedSerializableObject() = toClassDescriptor?.run { kind == ClassKind.OBJECT && hasSerializableOrMetaAnnotationWithoutArgs } == true +fun AbstractSerialGenerator.getIrSerialTypeInfo(property: IrSerializableProperty, ctx: SerializationPluginContext): IrSerialTypeInfo { + fun SerializableInfo(serializer: IrClassSymbol?) = + IrSerialTypeInfo(property, if (property.type.isNullable()) "Nullable" else "", serializer) + + val T = property.type + property.serializableWith?.let { return SerializableInfo(it.classOrNull!!) } +// TODO findAddOnSerializer(T, property.module)?.let { return SerializableInfo(it) } + T.overridenSerializer?.let { return SerializableInfo(it.classOrNull!!) } + return when { + T.isTypeParameter() -> IrSerialTypeInfo(property, if (property.type.isMarkedNullable()) "Nullable" else "", null) + T.isPrimitiveType() -> IrSerialTypeInfo( + property, + T.classFqName!!.asString().removePrefix("kotlin.") + ) + T.isString() -> IrSerialTypeInfo(property, "String") + T.isArray() -> { + val serializer = property.serializableWith?.classOrNull ?: ctx.getClassFromRuntime(SpecialBuiltins.referenceArraySerializer) + SerializableInfo(serializer) + } + else -> { + val serializer = + findTypeSerializerOrContext(ctx, property.type) + SerializableInfo(serializer) + } + } +} + @Suppress("FunctionName", "LocalVariableName") fun AbstractSerialGenerator.getSerialTypeInfo(property: SerializableProperty): SerialTypeInfo { fun SerializableInfo(serializer: ClassDescriptor?) = @@ -155,20 +202,32 @@ fun findTypeSerializer(module: ModuleDescriptor, kType: KotlinType): ClassDescri val stdSer = findStandardKotlinTypeSerializer(module, kType) // see if there is a standard serializer ?: findEnumTypeSerializer(module, kType) if (stdSer != null) return stdSer - if (kType.isInterface() && kType.toClassDescriptor?.isSealedSerializableInterface == false) return module.getClassFromSerializationPackage(SpecialBuiltins.polymorphicSerializer) + if (kType.isInterface() && kType.toClassDescriptor?.isSealedSerializableInterface == false) return module.getClassFromSerializationPackage( + SpecialBuiltins.polymorphicSerializer + ) return kType.toClassDescriptor?.classSerializer // check for serializer defined on the type } fun findStandardKotlinTypeSerializer(module: ModuleDescriptor, kType: KotlinType): ClassDescriptor? { - val name = when (kType.getJetTypeFqName(false)) { - "Z" -> if (kType.isBoolean()) "BooleanSerializer" else return null - "B" -> if (kType.isByte()) "ByteSerializer" else return null - "S" -> if (kType.isShort()) "ShortSerializer" else return null - "I" -> if (kType.isInt()) "IntSerializer" else return null - "J" -> if (kType.isLong()) "LongSerializer" else return null - "F" -> if (kType.isFloat()) "FloatSerializer" else return null - "D" -> if (kType.isDouble()) "DoubleSerializer" else return null - "C" -> if (kType.isChar()) "CharSerializer" else return null + val typeName = kType.getJetTypeFqName(false) + val name = when (typeName) { + "Z" -> if (kType.isBoolean()) "BooleanSerializer" else null + "B" -> if (kType.isByte()) "ByteSerializer" else null + "S" -> if (kType.isShort()) "ShortSerializer" else null + "I" -> if (kType.isInt()) "IntSerializer" else null + "J" -> if (kType.isLong()) "LongSerializer" else null + "F" -> if (kType.isFloat()) "FloatSerializer" else null + "D" -> if (kType.isDouble()) "DoubleSerializer" else null + "C" -> if (kType.isChar()) "CharSerializer" else null + else -> findStandardKotlinTypeSerializer(typeName) + } ?: return null + val identifier = Name.identifier(name) + return module.findClassAcrossModuleDependencies(ClassId(internalPackageFqName, identifier)) + ?: module.findClassAcrossModuleDependencies(ClassId(SerializationPackages.packageFqName, identifier)) +} + +fun findStandardKotlinTypeSerializer(typeName: String): String? { + return when (typeName) { "kotlin.Unit" -> "UnitSerializer" "kotlin.Nothing" -> "NothingSerializer" "kotlin.Boolean" -> "BooleanSerializer" @@ -223,9 +282,6 @@ fun findStandardKotlinTypeSerializer(module: ModuleDescriptor, kType: KotlinType "java.util.Map.Entry" -> "MapEntrySerializer" else -> return null } - val identifier = Name.identifier(name) - return module.findClassAcrossModuleDependencies(ClassId(internalPackageFqName, identifier)) - ?: module.findClassAcrossModuleDependencies(ClassId(SerializationPackages.packageFqName, identifier)) } fun findEnumTypeSerializer(module: ModuleDescriptor, kType: KotlinType): ClassDescriptor? { diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/TypeUtilIr.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/TypeUtilIr.kt new file mode 100644 index 00000000000..cfccde7d9d4 --- /dev/null +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/TypeUtilIr.kt @@ -0,0 +1,326 @@ +/* + * 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.common + +import com.intellij.psi.PsiElement +import org.jetbrains.kotlin.codegen.CompilationException +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.ir.declarations.* +import org.jetbrains.kotlin.ir.expressions.IrConstructorCall +import org.jetbrains.kotlin.ir.symbols.IrClassSymbol +import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol +import org.jetbrains.kotlin.ir.types.* +import org.jetbrains.kotlin.ir.util.* +import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.resolve.descriptorUtil.classId +import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe +import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.contextSerializerId +import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.enumSerializerId +import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.objectSerializerId +import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.polymorphicSerializerId +import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.referenceArraySerializerId +import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPluginContext +import org.jetbrains.kotlinx.serialization.compiler.fir.SerializationPluginKey +import org.jetbrains.kotlinx.serialization.compiler.resolve.* + +fun AbstractSerialGenerator.findTypeSerializerOrContextUnchecked( + context: SerializationPluginContext, kType: IrType +): IrClassSymbol? { + val annotations = kType.annotations + if (kType.isTypeParameter()) return null + annotations.serializableWith()?.let { return it.classOrNull } + additionalSerializersInScopeOfCurrentFile[kType.classOrNull?.descriptor to kType.isMarkedNullable()]?.let { + return context.referenceClass( + ClassId.topLevel(it.fqNameSafe) + ) + } + if (kType.isMarkedNullable()) return findTypeSerializerOrContextUnchecked(context, kType.makeNotNull()) + if (kType.toKotlinType() in contextualKClassListInCurrentFile) return context.referenceClass(contextSerializerId) + return analyzeSpecialSerializers(context, annotations) ?: findTypeSerializer(context, kType) +} + +fun analyzeSpecialSerializers( + context: SerializationPluginContext, + annotations: List +): IrClassSymbol? = when { + annotations.hasAnnotation(SerializationAnnotations.contextualFqName) || annotations.hasAnnotation(SerializationAnnotations.contextualOnPropertyFqName) -> + context.referenceClass(contextSerializerId) + // can be annotation on type usage, e.g. List<@Polymorphic Any> + annotations.hasAnnotation(SerializationAnnotations.polymorphicFqName) -> + context.referenceClass(polymorphicSerializerId) + else -> null +} + +fun AbstractSerialGenerator.findTypeSerializerOrContext( + context: SerializationPluginContext, kType: IrType, + sourceElement: PsiElement? = null +): IrClassSymbol? { + if (kType.isTypeParameter()) return null + return findTypeSerializerOrContextUnchecked(context, kType) ?: throw CompilationException( + "Serializer for element of type $kType has not been found.\n" + + "To use context serializer as fallback, explicitly annotate element with @Contextual", + null, + sourceElement + ) +} + +fun findTypeSerializer(context: SerializationPluginContext, type: IrType): IrClassSymbol? { + val userOverride = type.overridenSerializer + if (userOverride != null) return userOverride.classOrNull + if (type.isTypeParameter()) return null + if (type.isArray()) return context.referenceClass(referenceArraySerializerId) + if (type.isGeneratedSerializableObject()) return context.referenceClass(objectSerializerId) + val stdSer = findStandardKotlinTypeSerializer(context, type) // see if there is a standard serializer + ?: findEnumTypeSerializer(context, type) + if (stdSer != null) return stdSer + if (type.isInterface() && type.classOrNull?.owner?.isSealedSerializableInterface == false) return context.referenceClass( + polymorphicSerializerId + ) + return type.classOrNull?.owner.classSerializer(context) // check for serializer defined on the type +} + +internal fun IrClass?.classSerializer(context: SerializationPluginContext): IrClassSymbol? = this?.let { + // serializer annotation on class? + serializableWith?.let { return it.classOrNull } + // companion object serializer? + if (hasCompanionObjectAsSerializer) return companionObject()?.symbol + // can infer @Poly? + polymorphicSerializerIfApplicableAutomatically(context)?.let { return it } + // default serializable? + if (shouldHaveGeneratedSerializer) { + // $serializer nested class + return this.declarations + .filterIsInstance() + .singleOrNull { it.name == SerialEntityNames.SERIALIZER_CLASS_NAME }?.symbol + } + return null +} + +internal val IrClass.shouldHaveGeneratedSerializer: Boolean + get() = (isInternalSerializable && (modality == Modality.FINAL || modality == Modality.OPEN)) + || isEnumWithLegacyGeneratedSerializer() + +internal fun IrClass.polymorphicSerializerIfApplicableAutomatically(context: SerializationPluginContext): IrClassSymbol? { + val serializer = when { + kind == ClassKind.INTERFACE && modality == Modality.SEALED -> SpecialBuiltins.sealedSerializer + kind == ClassKind.INTERFACE -> SpecialBuiltins.polymorphicSerializer + isInternalSerializable && modality == Modality.ABSTRACT -> SpecialBuiltins.polymorphicSerializer + isInternalSerializable && modality == Modality.SEALED -> SpecialBuiltins.sealedSerializer + else -> null + } + return serializer?.let { context.getClassFromRuntimeOrNull(it, SerializationPackages.packageFqName, SerializationPackages.internalPackageFqName) } +} + +internal val IrClass.isInternalSerializable: Boolean + get() { + if (kind != ClassKind.CLASS) return false + return hasSerializableOrMetaAnnotationWithoutArgs() + } + +internal val IrClass.isAbstractOrSealedSerializableClass: Boolean get() = isInternalSerializable && (modality == Modality.ABSTRACT || modality == Modality.SEALED) + +internal val IrClass.isStaticSerializable: Boolean get() = this.typeParameters.isEmpty() + + +internal val IrClass.hasCompanionObjectAsSerializer: Boolean + get() = isInternallySerializableObject || companionObject()?.serializerForClass == this.defaultType + +internal val IrClass.isInternallySerializableObject: Boolean + get() = kind == ClassKind.OBJECT && hasSerializableOrMetaAnnotationWithoutArgs() + +internal val IrClass.serializerForClass: IrType? + get() = null // TODO("Serializer(forClass)") + +fun findEnumTypeSerializer(context: SerializationPluginContext, type: IrType): IrClassSymbol? { + val classSymbol = type.classOrNull?.owner ?: return null + return if (classSymbol.kind == ClassKind.ENUM_CLASS && !classSymbol.isEnumWithLegacyGeneratedSerializer()) + context.referenceClass(enumSerializerId) + else null +} + +internal fun IrClass.isEnumWithLegacyGeneratedSerializer(): Boolean = isInternallySerializableEnum() && useGeneratedEnumSerializer + +internal val IrClass.useGeneratedEnumSerializer: Boolean + get() = true // todo ???? + +internal val IrClass.isSealedSerializableInterface: Boolean + get() = kind == ClassKind.INTERFACE && modality == Modality.SEALED && hasSerializableOrMetaAnnotationWithoutArgs() // in previous version, it was just 'serializableOrMeta' + +internal fun IrClass.isInternallySerializableEnum(): Boolean = + kind == ClassKind.ENUM_CLASS && hasSerializableOrMetaAnnotationWithoutArgs() + +fun findStandardKotlinTypeSerializer(context: SerializationPluginContext, type: IrType): IrClassSymbol? { + val typeName = type.classFqName?.toString() + val name = when (typeName) { + "Z" -> if (type.isBoolean()) "BooleanSerializer" else null + "B" -> if (type.isByte()) "ByteSerializer" else null + "S" -> if (type.isShort()) "ShortSerializer" else null + "I" -> if (type.isInt()) "IntSerializer" else null + "J" -> if (type.isLong()) "LongSerializer" else null + "F" -> if (type.isFloat()) "FloatSerializer" else null + "D" -> if (type.isDouble()) "DoubleSerializer" else null + "C" -> if (type.isChar()) "CharSerializer" else null + null -> null + else -> findStandardKotlinTypeSerializer(typeName) + } ?: return null + return context.getClassFromRuntimeOrNull(name, SerializationPackages.internalPackageFqName, SerializationPackages.packageFqName) +} + +fun IrType.isGeneratedSerializableObject(): Boolean { + return classOrNull?.run { owner.kind == ClassKind.OBJECT && owner.hasSerializableOrMetaAnnotationWithoutArgs() } == true +} + +internal val IrClass.isSerializableObject: Boolean + get() = kind == ClassKind.OBJECT && hasSerializableOrMetaAnnotation() + +// todo: optimize & unify +internal fun IrClass.hasSerializableOrMetaAnnotationWithoutArgs(): Boolean { + val annot = getAnnotation(SerializationAnnotations.serializableAnnotationFqName) + if (annot != null) { + for (i in 0 until annot.valueArgumentsCount) { + if (annot.getValueArgument(i) != null) return false + } + return true + } + val metaAnnotation = annotations + .flatMap { it.symbol.owner.constructedClass.annotations } + .find { it.isAnnotation(SerializationAnnotations.metaSerializableAnnotationFqName) } + return metaAnnotation != null +} + +fun SerializationPluginContext.getClassFromRuntimeOrNull(className: String, vararg packages: FqName): IrClassSymbol? { + val listToSearch = if (packages.isEmpty()) SerializationPackages.allPublicPackages else packages.toList() + for (pkg in listToSearch) { + referenceClass(ClassId(pkg, Name.identifier(className)))?.let { return it } + } + return null +} + +fun SerializationPluginContext.getClassFromRuntime(className: String, vararg packages: FqName): IrClassSymbol { + return getClassFromRuntimeOrNull(className, *packages) ?: + error("Class $className wasn't found in ${packages.toList().ifEmpty { SerializationPackages.allPublicPackages }}. " + + "Check that you have correct version of serialization runtime in classpath.") +} + +fun SerializationPluginContext.getClassFromInternalSerializationPackage(className: String): IrClassSymbol = + getClassFromRuntimeOrNull(className, SerializationPackages.internalPackageFqName) + ?: error("Class $className wasn't found in ${SerializationPackages.internalPackageFqName}. Check that you have correct version of serialization runtime in classpath.") + + +internal val IrType.overridenSerializer: IrSimpleType? + get() { + val desc = this.classOrNull ?: return null + desc.owner.serializableWith?.let { return it } + return null + } + +internal val IrClass.serializableWith: IrSimpleType? + get() = annotations.serializableWith() + + +internal fun List.serializableWith(): IrSimpleType? { + // XXX::class +// val annotationArg = +// firstOrNull { it.type.classFqName == SerializationAnnotations.serializerAnnotationFqName }?.getValueArgument(0) ?: return null + // TODO("SerializableWith") + return null +} + +internal fun getSerializableClassByCompanion(companionClass: IrClass): IrClass? { + if (companionClass.isSerializableObject) return companionClass + if (!companionClass.isCompanion) return null + val classDescriptor = (companionClass.parent as? IrClass) ?: return null + if (!classDescriptor.shouldHaveGeneratedMethodsInCompanion) return null + return classDescriptor +} + +internal val IrClass.shouldHaveGeneratedMethodsInCompanion: Boolean + get() = this.isSerializableObject || this.isSerializableEnum() || (this.kind == ClassKind.CLASS && hasSerializableOrMetaAnnotation()) || this.isSealedSerializableInterface + +internal fun IrClass.isSerializableEnum(): Boolean = kind == ClassKind.ENUM_CLASS && hasSerializableOrMetaAnnotation() + +fun IrClass.hasSerializableOrMetaAnnotation() = descriptor.hasSerializableOrMetaAnnotation // TODO + +internal val IrType.genericIndex: Int? + get() = (this.classifierOrNull as? IrTypeParameterSymbol)?.owner?.index + +fun IrType.serialName(): String = this.classOrNull!!.owner.serialName() +fun IrClass.serialName(): String { + return descriptor.serialName() // TODO +} + +fun AbstractSerialGenerator.allSealedSerializableSubclassesFor( + irClass: IrClass, + context: SerializationPluginContext +): Pair, List> { + assert(irClass.modality == Modality.SEALED) + fun recursiveSealed(klass: IrClass): Collection { + return klass.sealedSubclasses.map { it.owner }.flatMap { if (it.modality == Modality.SEALED) recursiveSealed(it) else setOf(it) } + } + + val serializableSubtypes = recursiveSealed(irClass).map { it.defaultType } + return serializableSubtypes.mapNotNull { subtype -> + findTypeSerializerOrContextUnchecked(context, subtype)?.let { Pair(subtype, it) } + }.unzip() +} + +fun IrClass.findEnumValuesMethod() = this.functions.singleOrNull { f -> + f.name == Name.identifier("values") && f.valueParameters.isEmpty() && f.extensionReceiverParameter == null +} ?: throw AssertionError("Enum class does not have single .values() function") + +internal fun IrClass.enumEntries(): List { + check(this.kind == ClassKind.ENUM_CLASS) + return declarations.filterIsInstance() +// .filter { it.kind == ClassKind.ENUM_ENTRY } + .toList() +} + +internal fun IrClass.isEnumWithSerialInfoAnnotation(): Boolean { + if (kind != ClassKind.ENUM_CLASS) return false + if (annotations.hasAnySerialAnnotation) return true + return enumEntries().any { (it.annotations.hasAnySerialAnnotation) } +} + +internal val List.hasAnySerialAnnotation: Boolean + get() = false // TODO serialNameValue != null || any { it.annotationClass?.isSerialInfoAnnotation == true } + +internal val List.serialNameValue: String? + get() = null // todo("List.serialNameValue") @SerialName + +internal fun getSerializableClassDescriptorBySerializer(serializer: IrClass): IrClass? { + val serializerForClass = serializer.serializerForClass + if (serializerForClass != null) return serializerForClass.classOrNull?.owner + if (serializer.name !in setOf( + SerialEntityNames.SERIALIZER_CLASS_NAME, + SerialEntityNames.GENERATED_SERIALIZER_CLASS + ) + ) return null + val classDescriptor = (serializer.parent as? IrClass) ?: return null + if (!classDescriptor.shouldHaveGeneratedSerializer) return null + return classDescriptor +} + +internal fun isKSerializer(type: IrType): Boolean { + val simpleType = type as? IrSimpleType ?: return false + val classifier = simpleType.classifier as? IrClassSymbol ?: return false + val fqName = classifier.owner.fqNameWhenAvailable + return fqName == SerialEntityNames.KSERIALIZER_NAME_FQ || fqName == SerialEntityNames.GENERATED_SERIALIZER_FQ +} + +internal fun IrClass.findPluginGeneratedMethod(name: String): IrSimpleFunction? { + return this.functions.find { it.name.asString() == name && it.origin == IrDeclarationOrigin.GeneratedByPlugin(SerializationPluginKey) } +} + +internal fun IrConstructor.isSerializationCtor(): Boolean { + /*kind == CallableMemberDescriptor.Kind.SYNTHESIZED does not work because DeserializedClassConstructorDescriptor loses its kind*/ + return valueParameters.lastOrNull()?.run { + name == SerialEntityNames.dummyParamName && type.classFqName == SerializationPackages.internalPackageFqName.child( + SerialEntityNames.SERIAL_CTOR_MARKER_NAME + ) + } == true +} \ No newline at end of file diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/Fir2IrGeneratorUtils.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/Fir2IrGeneratorUtils.kt new file mode 100644 index 00000000000..ae9e69a0faa --- /dev/null +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/Fir2IrGeneratorUtils.kt @@ -0,0 +1,41 @@ +/* + * 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.backend.common.extensions.IrPluginContext +import org.jetbrains.kotlin.ir.declarations.IrClass +import org.jetbrains.kotlin.ir.declarations.IrConstructor +import org.jetbrains.kotlin.ir.expressions.IrBody +import org.jetbrains.kotlin.ir.expressions.impl.IrDelegatingConstructorCallImpl +import org.jetbrains.kotlin.ir.expressions.impl.IrInstanceInitializerCallImpl +import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol +import org.jetbrains.kotlin.ir.types.IrSimpleType +import org.jetbrains.kotlin.ir.util.primaryConstructor + +// TODO KT-53096 +fun IrPluginContext.generateBodyForDefaultConstructor(declaration: IrConstructor): IrBody? { + val type = declaration.returnType as? IrSimpleType ?: return null + + val delegatingAnyCall = IrDelegatingConstructorCallImpl( + -1, + -1, + irBuiltIns.anyType, + irBuiltIns.anyClass.owner.primaryConstructor?.symbol ?: return null, + typeArgumentsCount = 0, + valueArgumentsCount = 0 + ) + + val initializerCall = IrInstanceInitializerCallImpl( + -1, + -1, + (declaration.parent as? IrClass)?.symbol ?: return null, + type + ) + + return irFactory.createBlockBody(-1, -1, listOf(delegatingAnyCall, initializerCall)) +} + +val Sequence.primary get() = find { it.isPrimary } ?: error("Expected to have a primary constructor") \ No newline at end of file diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/GeneratorHelpers.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/GeneratorHelpers.kt index 09d48f0505b..8a6e37484b6 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/GeneratorHelpers.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/GeneratorHelpers.kt @@ -5,9 +5,11 @@ package org.jetbrains.kotlinx.serialization.compiler.backend.ir -import org.jetbrains.kotlin.ir.deepCopyWithVariables import org.jetbrains.kotlin.backend.common.lower.DeclarationIrBuilder +import org.jetbrains.kotlin.ir.deepCopyWithVariables import org.jetbrains.kotlin.backend.common.lower.irIfThen +import org.jetbrains.kotlin.backend.common.sourceElement +import org.jetbrains.kotlin.backend.jvm.functionByName import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.ir.builders.* @@ -23,14 +25,21 @@ import org.jetbrains.kotlin.ir.types.impl.makeTypeProjection import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi +import org.jetbrains.kotlin.backend.jvm.ir.representativeUpperBound +import org.jetbrains.kotlin.builtins.StandardNames +import org.jetbrains.kotlin.name.CallableId 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.psi import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.descriptorUtil.* import org.jetbrains.kotlin.resolve.jvm.JvmPrimitiveType +import org.jetbrains.kotlin.resolve.source.getPsi import org.jetbrains.kotlin.types.* +import org.jetbrains.kotlin.types.checker.SimpleClassicTypeSystemContext.typeConstructor +import org.jetbrains.kotlin.types.model.dependsOnTypeConstructor import org.jetbrains.kotlin.types.typeUtil.* import org.jetbrains.kotlin.util.OperatorNameConventions import org.jetbrains.kotlinx.serialization.compiler.backend.common.* @@ -47,16 +56,16 @@ interface IrBuilderExtension { val compilerContext: SerializationPluginContext private val throwMissedFieldExceptionFunc - get() = compilerContext.referenceFunctions(SerialEntityNames.SINGLE_MASK_FIELD_MISSING_FUNC_FQ).singleOrNull() + get() = compilerContext.referenceFunctions(CallableId(SerializationPackages.internalPackageFqName, SerialEntityNames.SINGLE_MASK_FIELD_MISSING_FUNC_NAME)).singleOrNull() private val throwMissedFieldExceptionArrayFunc - get() = compilerContext.referenceFunctions(SerialEntityNames.ARRAY_MASK_FIELD_MISSING_FUNC_FQ).singleOrNull() + get() = compilerContext.referenceFunctions(CallableId(SerializationPackages.internalPackageFqName, SerialEntityNames.ARRAY_MASK_FIELD_MISSING_FUNC_NAME)).singleOrNull() private val enumSerializerFactoryFunc - get() = compilerContext.referenceFunctions(SerialEntityNames.ENUM_SERIALIZER_FACTORY_FUNC_FQ).singleOrNull() + get() = compilerContext.referenceFunctions(CallableId(SerializationPackages.internalPackageFqName, SerialEntityNames.ENUM_SERIALIZER_FACTORY_FUNC_NAME)).singleOrNull() private val markedEnumSerializerFactoryFunc - get() = compilerContext.referenceFunctions(SerialEntityNames.MARKED_ENUM_SERIALIZER_FACTORY_FUNC_FQ).singleOrNull() + get() = compilerContext.referenceFunctions(CallableId(SerializationPackages.internalPackageFqName, SerialEntityNames.MARKED_ENUM_SERIALIZER_FACTORY_FUNC_NAME)).singleOrNull() private inline fun IrClass.searchForDeclaration(descriptor: DeclarationDescriptor): T? { return declarations.singleOrNull { it.descriptor == descriptor } as? T @@ -66,6 +75,13 @@ interface IrBuilderExtension { return throwMissedFieldExceptionFunc != null && throwMissedFieldExceptionArrayFunc != null } + fun addFunctionBody(function: F, bodyGen: IrBlockBodyBuilder.(F) -> Unit) { + function.body = DeclarationIrBuilder(compilerContext, function.symbol, function.startOffset, function.endOffset).irBlockBody( + function.startOffset, + function.endOffset + ) { bodyGen(function) } + } + fun IrClass.contributeFunction(descriptor: FunctionDescriptor, ignoreWhenMissing: Boolean = false, bodyGen: IrBlockBodyBuilder.(IrFunction) -> Unit) { val f: IrSimpleFunction = searchForDeclaration(descriptor) ?: (if (ignoreWhenMissing) return else compilerContext.symbolTable.referenceSimpleFunction(descriptor).owner) @@ -104,9 +120,9 @@ interface IrBuilderExtension { DeclarationIrBuilder(compilerContext, function.symbol, startOffset, endOffset).irBlockBody(startOffset, endOffset, bodyGen) function.parent = this - val f0Type = module.findClassAcrossModuleDependencies(ClassId.topLevel(FUNCTION0_FQ))!!.defaultType - val f0ParamSymbol = compilerContext.symbolTable.referenceTypeParameter(f0Type.constructor.parameters[0]) - val f0IrType = f0Type.toIrType().substitute(mapOf(f0ParamSymbol to type)) + val f0Type = compilerContext.irBuiltIns.functionN(0) + val f0ParamSymbol = f0Type.typeParameters[0].symbol + val f0IrType = f0Type.defaultType.substitute(mapOf(f0ParamSymbol to type)) return IrFunctionExpressionImpl( startOffset, @@ -123,13 +139,13 @@ interface IrBuilderExtension { name: Name, initializerBuilder: IrBlockBodyBuilder.() -> Unit ): IrProperty { - val lazySafeModeClassDescriptor = compilerContext.referenceClass(LAZY_MODE_FQ)!!.descriptor - val lazyFunctionSymbol = compilerContext.referenceFunctions(LAZY_FUNC_FQ).single { + val lazySafeModeClassDescriptor = compilerContext.referenceClass(ClassId.topLevel(LAZY_MODE_FQ))!!.owner + val lazyFunctionSymbol = compilerContext.referenceFunctions(CallableId(StandardNames.BUILT_INS_PACKAGE_FQ_NAME, Name.identifier("lazy"))).single { it.descriptor.valueParameters.size == 2 && it.descriptor.valueParameters[0].type == lazySafeModeClassDescriptor.defaultType } val publicationEntryDescriptor = lazySafeModeClassDescriptor.enumEntries().single { it.name == LAZY_PUBLICATION_MODE_NAME } - val lazyIrClass = compilerContext.referenceClass(LAZY_FQ)!!.owner + val lazyIrClass = compilerContext.referenceClass(ClassId.topLevel(LAZY_FQ))!!.owner val lazyIrType = lazyIrClass.defaultType.substitute(mapOf(lazyIrClass.typeParameters[0].symbol to targetIrType)) val propertyDescriptor = @@ -146,8 +162,8 @@ interface IrBuilderExtension { val enumElement = IrGetEnumValueImpl( startOffset, endOffset, - publicationEntryDescriptor.classValueType!!.toIrType(), - compilerContext.symbolTable.referenceEnumEntry(publicationEntryDescriptor) + lazySafeModeClassDescriptor.defaultType, + publicationEntryDescriptor.symbol ) val lambdaExpression = containingClass.createLambdaExpression(targetIrType, initializerBuilder) @@ -191,7 +207,7 @@ interface IrBuilderExtension { } fun IrBlockBodyBuilder.getLazyValueExpression(thisParam: IrValueParameter, property: IrProperty, type: IrType): IrExpression { - val lazyIrClass = compilerContext.referenceClass(LAZY_FQ)!!.owner + val lazyIrClass = compilerContext.referenceClass(ClassId.topLevel(LAZY_FQ))!!.owner val valueGetter = lazyIrClass.getPropertyGetter("value")!! val propertyGetter = property.getter!! @@ -245,16 +261,6 @@ interface IrBuilderExtension { } } - fun ClassDescriptor.referenceFunctionSymbol( - functionName: String, - predicate: (IrSimpleFunction) -> Boolean = { true } - ): IrFunctionSymbol { - val irClass = compilerContext.referenceClass(fqNameSafe)?.owner ?: error("Couldn't load class $this") - val simpleFunctions = irClass.declarations.filterIsInstance() - - return simpleFunctions.filter { it.name.asString() == functionName }.single { predicate(it) }.symbol - } - fun IrBuilderWithScope.createPrimitiveArrayOfExpression( elementPrimitiveType: IrType, arrayElements: List @@ -270,18 +276,10 @@ interface IrBuilderExtension { fun IrBuilderWithScope.irBinOp(name: Name, lhs: IrExpression, rhs: IrExpression): IrExpression { val classFqName = (lhs.type as IrSimpleType).classOrNull!!.owner.fqNameWhenAvailable!! - val symbol = compilerContext.referenceFunctions(classFqName.child(name)).single() + val symbol = compilerContext.referenceFunctions(CallableId(ClassId.topLevel(classFqName), name)).single() return irInvoke(lhs, symbol, rhs) } - fun IrBuilderWithScope.irGetObject(classDescriptor: ClassDescriptor) = - IrGetObjectValueImpl( - startOffset, - endOffset, - classDescriptor.defaultType.toIrType(), - compilerContext.symbolTable.referenceClass(classDescriptor) - ) - fun IrBuilderWithScope.irGetObject(irObject: IrClass) = IrGetObjectValueImpl( startOffset, @@ -297,14 +295,6 @@ interface IrBuilderExtension { } } - fun IrBuilderWithScope.irEmptyVararg(forValueParameter: ValueParameterDescriptor) = - IrVarargImpl( - startOffset, - endOffset, - forValueParameter.type.toIrType(), - forValueParameter.varargElementType!!.toIrType() - ) - class BranchBuilder( val irWhen: IrWhen, context: IrGeneratorContext, @@ -330,10 +320,12 @@ interface IrBuilderExtension { result ) + @Deprecated("", level = DeprecationLevel.ERROR) fun KotlinType.toIrType() = compilerContext.typeTranslator.translateType(this) // note: this method should be used only for properties from current module. Fields from other modules are private and inaccessible. - val SerializableProperty.irField: IrField? get() = compilerContext.symbolTable.referenceField(this.descriptor).let { if (it.isBound) it.owner else null } + val IrSerializableProperty.irField: IrField? + get() = this.descriptor.backingField fun IrClass.searchForProperty(descriptor: PropertyDescriptor): IrProperty { // this API is used to reference both current module descriptors and external ones (because serializable class can be in any of them), @@ -341,13 +333,11 @@ interface IrBuilderExtension { return searchForDeclaration(descriptor) ?: if (descriptor.module == compilerContext.moduleDescriptor) { compilerContext.symbolTable.referenceProperty(descriptor).owner } else { + @Suppress("DEPRECATION") compilerContext.referenceProperties(descriptor.fqNameSafe).single().owner } } - fun SerializableProperty.getIrPropertyFrom(thisClass: IrClass): IrProperty { - return thisClass.searchForProperty(descriptor) - } /* @@ -356,37 +346,40 @@ interface IrBuilderExtension { */ fun IrBuilderWithScope.createPropertyByParamReplacer( irClass: IrClass, - serialProperties: List, + serialProperties: List, instance: IrValueParameter, - bindingContext: BindingContext + bindingContext: BindingContext? ): (ValueParameterDescriptor) -> IrExpression? { - fun SerializableProperty.irGet(): IrExpression { + fun IrSerializableProperty.irGet(): IrExpression { val ownerType = instance.symbol.owner.type return getProperty( irGet( type = ownerType, variable = instance.symbol - ), getIrPropertyFrom(irClass) + ), descriptor ) } val serialPropertiesMap = serialProperties.associateBy { it.descriptor } - val transientPropertiesMap = + val transientPropertiesSet = irClass.declarations.asSequence() .filterIsInstance() - .filter { it.backingField != null }.filter { !serialPropertiesMap.containsKey(it.descriptor) } - .associateBy { it.symbol.descriptor } + .filter { it.backingField != null } + .filter { !serialPropertiesMap.containsKey(it) } + .toSet() - return { - val propertyDescriptor = bindingContext[BindingContext.VALUE_PARAMETER_AS_PROPERTY, it] + return { vpd -> + val propertyDescriptor = irClass.properties.find { it.name == vpd.name } if (propertyDescriptor != null) { val value = serialPropertiesMap[propertyDescriptor] - value?.irGet() ?: transientPropertiesMap[propertyDescriptor]?.let { prop -> - getProperty( - irGet(instance), - prop - ) + value?.irGet() ?: run { + if (propertyDescriptor in transientPropertiesSet) + getProperty( + irGet(instance), + propertyDescriptor + ) + else null } } else { null @@ -428,7 +421,7 @@ interface IrBuilderExtension { fun IrBlockBodyBuilder.generateGoldenMaskCheck( seenVars: List, - properties: SerializableProperties, + properties: IrSerializableProperties, serialDescriptor: IrExpression ) { val fieldsMissedTest: IrExpression @@ -539,6 +532,7 @@ interface IrBuilderExtension { val field = with(propertyDescriptor) { // TODO: type parameters + @Suppress("DEPRECATION_ERROR") // should be called only with old FE originProperty.factory.createField( originProperty.startOffset, originProperty.endOffset, SERIALIZABLE_PLUGIN_ORIGIN, IrFieldSymbolImpl(propertyDescriptor), name, type.toIrType(), visibility, !isVar, isEffectivelyExternal(), dispatchReceiverParameter == null @@ -564,6 +558,7 @@ interface IrBuilderExtension { false -> searchForDeclaration(propertyDescriptor)?.setter } ?: run { with(descriptor) { + @Suppress("DEPRECATION_ERROR") // should never be called after FIR frontend property.factory.createFunction( fieldSymbol.owner.startOffset, fieldSymbol.owner.endOffset, SERIALIZABLE_PLUGIN_ORIGIN, IrSimpleFunctionSymbolImpl(descriptor), name, visibility, modality, returnType!!.toIrType(), @@ -572,6 +567,7 @@ interface IrBuilderExtension { }.also { f -> generateOverriddenFunctionSymbols(f, compilerContext.symbolTable) f.createParameterDeclarations(descriptor) + @Suppress("DEPRECATION_ERROR") // should never be called after FIR frontend f.returnType = descriptor.returnType!!.toIrType() f.correspondingPropertySymbol = fieldSymbol.owner.correspondingPropertySymbol } @@ -601,7 +597,7 @@ interface IrBuilderExtension { val receiver = generateReceiverExpressionForFieldAccess(irAccessor.dispatchReceiverParameter!!.symbol, property) - val propertyIrType = property.type.toIrType() + val propertyIrType = irAccessor.returnType irBody.statements.add( IrReturnImpl( startOffset, endOffset, compilerContext.irBuiltIns.nothingType, @@ -644,7 +640,7 @@ interface IrBuilderExtension { return irBody } - fun generateReceiverExpressionForFieldAccess( + fun generateReceiverExpressionForFieldAccess( // todo: remove this ownerSymbol: IrValueSymbol, property: PropertyDescriptor ): IrExpression { @@ -666,6 +662,7 @@ interface IrBuilderExtension { ) { val function = this fun irValueParameter(descriptor: ParameterDescriptor): IrValueParameter = with(descriptor) { + @Suppress("DEPRECATION_ERROR") // should never be called after FIR frontend factory.createValueParameter( function.startOffset, function.endOffset, SERIALIZABLE_PLUGIN_ORIGIN, IrValueParameterSymbolImpl(this), name, indexOrMinusOne, type.toIrType(), varargElementType?.toIrType(), isCrossinline, isNoinline, @@ -700,7 +697,7 @@ interface IrBuilderExtension { typeParameter.parent = this } } - + @Suppress("DEPRECATION_ERROR") // should never be called after FIR frontend newTypeParameters.forEach { typeParameter -> typeParameter.superTypes = typeParameter.descriptor.upperBounds.map { it.toIrType() } } @@ -708,27 +705,13 @@ interface IrBuilderExtension { typeParameters = newTypeParameters } - fun createClassReference(classType: KotlinType, startOffset: Int, endOffset: Int): IrClassReference { - val clazz = classType.toClassDescriptor!! - val classSymbol = compilerContext.referenceClass(clazz.fqNameSafe) ?: error("Couldn't load class $clazz") + fun createClassReference(classType: IrType, startOffset: Int, endOffset: Int): IrClassReference { return IrClassReferenceImpl( startOffset, endOffset, compilerContext.irBuiltIns.kClassClass.starProjectedType, - classSymbol, - classType.approximateJvmErasure.toIrType() - ) - } - - fun createClassReference(irClass: IrClass, startOffset: Int, endOffset: Int): IrClassReference { - val classType = irClass.descriptor.toSimpleType(false) - val classSymbol = irClass.symbol - return IrClassReferenceImpl( - startOffset, - endOffset, - compilerContext.irBuiltIns.kClassClass.starProjectedType, - classSymbol, - classType.approximateJvmErasure.toIrType() + classType.classifierOrFail, + classType ) } @@ -755,7 +738,9 @@ interface IrBuilderExtension { else -> error("Unsupported classifier: $this") } - fun IrBuilderWithScope.classReference(classType: KotlinType): IrClassReference = createClassReference(classType, startOffset, endOffset) + + fun IrBuilderWithScope.classReference(classSymbol: IrType): IrClassReference = + createClassReference(classSymbol, startOffset, endOffset) private fun extractDefaultValuesFromConstructor(irClass: IrClass?): Map { if (irClass == null) return emptyMap() @@ -808,15 +793,6 @@ interface IrBuilderExtension { } } - fun findEnumValuesMethod(enumClass: ClassDescriptor): IrFunction { - assert(enumClass.kind == ClassKind.ENUM_CLASS) - return compilerContext.referenceClass(enumClass.fqNameSafe)?.let { - it.owner.functions.singleOrNull { f -> - f.name == Name.identifier("values") && f.valueParameters.isEmpty() && f.extensionReceiverParameter == null - } ?: throw AssertionError("Enum class does not have single .values() function") - } ?: error("Couldn't load class $enumClass") - } - private fun getEnumMembersNames(enumClass: ClassDescriptor): Sequence { assert(enumClass.kind == ClassKind.ENUM_CLASS) return enumClass.unsubstitutedMemberScope.getContributedDescriptors().asSequence() @@ -850,21 +826,20 @@ interface IrBuilderExtension { fun IrBuilderWithScope.serializerTower( generator: SerializerIrGenerator, dispatchReceiverParameter: IrValueParameter, - property: SerializableProperty + property: IrSerializableProperty ): IrExpression? { - val nullableSerClass = compilerContext.referenceProperties(SerialEntityNames.wrapIntoNullableExt).single() + val nullableSerClass = compilerContext.referenceProperties(SerialEntityNames.wrapIntoNullableCallableId).single() val serializer = - property.serializableWith?.toClassDescriptor + property.serializableWith?.classOrNull ?: if (!property.type.isTypeParameter()) generator.findTypeSerializerOrContext( - property.module, - property.type, - property.descriptor.findPsi() + compilerContext, + property.type ) else null return serializerInstance( generator, dispatchReceiverParameter, serializer, - property.module, + compilerContext, property.type, genericIndex = property.genericIndex ) @@ -872,12 +847,12 @@ interface IrBuilderExtension { } private fun IrBuilderWithScope.wrapWithNullableSerializerIfNeeded( - type: KotlinType, + type: IrType, expression: IrExpression, nullableProp: IrPropertySymbol - ): IrExpression = if (type.isMarkedNullable) { - val resultType = type.makeNotNullable() - val typeArguments = listOf(resultType.toIrType()) + ): IrExpression = if (type.isMarkedNullable()) { + val resultType = type.makeNotNull() + val typeArguments = listOf(resultType) val callee = nullableProp.owner.getter!! val returnType = callee.returnType.substitute(callee.typeParameters, typeArguments) @@ -892,10 +867,19 @@ interface IrBuilderExtension { expression } +// private fun IrBuilderWithScope.wrapWithNullableSerializerIfNeeded( +// type: KotlinType, +// expression: IrExpression, +// nullableProp: IrPropertySymbol +// ): IrExpression = wrapWithNullableSerializerIfNeeded(type.toIrType(), expression, nullableProp) - fun wrapIrTypeIntoKSerializerIrType(module: ModuleDescriptor, type: IrType, variance: Variance = Variance.INVARIANT): IrType { - val serializerFqn = getSerializationPackageFqn(SerialEntityNames.KSERIALIZER_CLASS) - val kSerClass = compilerContext.referenceClass(serializerFqn) ?: error("Couldn't find class $serializerFqn") + + fun wrapIrTypeIntoKSerializerIrType( + type: IrType, + variance: Variance = Variance.INVARIANT + ): IrType { + val kSerClass = compilerContext.referenceClass(ClassId(SerializationPackages.packageFqName, SerialEntityNames.KSERIALIZER_NAME)) + ?: error("Couldn't find class ${SerialEntityNames.KSERIALIZER_NAME}") return IrSimpleTypeImpl( kSerClass, hasQuestionMark = false, arguments = listOf( makeTypeProjection(type, variance) @@ -906,14 +890,14 @@ interface IrBuilderExtension { fun IrBuilderWithScope.serializerInstance( enclosingGenerator: SerializerIrGenerator, dispatchReceiverParameter: IrValueParameter, - serializerClassOriginal: ClassDescriptor?, - module: ModuleDescriptor, - kType: KotlinType, + serializerClassOriginal: IrClassSymbol?, + pluginContext: SerializationPluginContext, + kType: IrType, genericIndex: Int? = null ): IrExpression? = serializerInstance( enclosingGenerator, serializerClassOriginal, - module, + pluginContext, kType, genericIndex ) { it, _ -> @@ -925,23 +909,41 @@ interface IrBuilderExtension { enclosingGenerator: AbstractSerialGenerator, serializerClassOriginal: ClassDescriptor?, module: ModuleDescriptor, - kType: KotlinType, + kType: IrType, genericIndex: Int? = null, - genericGetter: ((Int, KotlinType) -> IrExpression)? = null + genericGetter: ((Int) -> IrExpression)? = null ): IrExpression? { - val nullableSerClass = compilerContext.referenceProperties(SerialEntityNames.wrapIntoNullableExt).single() + return serializerInstance( + enclosingGenerator, + serializerClassOriginal?.let { compilerContext.symbolTable.referenceClass(it) }, + compilerContext, + kType, + genericIndex, + if (genericGetter != null) { i, _ -> genericGetter(i) } else null + ) + } + + fun IrBuilderWithScope.serializerInstance( + enclosingGenerator: AbstractSerialGenerator, + serializerClassOriginal: IrClassSymbol?, + pluginContext: SerializationPluginContext, + kType: IrType, + genericIndex: Int? = null, + genericGetter: ((Int, IrType) -> IrExpression)? = null + ): IrExpression? { + val nullableSerClass = compilerContext.referenceProperties(SerialEntityNames.wrapIntoNullableCallableId).single() if (serializerClassOriginal == null) { if (genericIndex == null) return null return genericGetter?.invoke(genericIndex, kType) } - if (serializerClassOriginal.kind == ClassKind.OBJECT) { + if (serializerClassOriginal.owner.kind == ClassKind.OBJECT) { return irGetObject(serializerClassOriginal) } - fun instantiate(serializer: ClassDescriptor?, type: KotlinType): IrExpression? { + fun instantiate(serializer: IrClassSymbol?, type: IrType): IrExpression? { val expr = serializerInstance( enclosingGenerator, serializer, - module, + pluginContext, type, type.genericIndex, genericGetter @@ -951,11 +953,11 @@ interface IrBuilderExtension { var serializerClass = serializerClassOriginal var args: List - var typeArgs: List - val thisIrType = kType.toIrType() + var typeArgs: List + val thisIrType = (kType as? IrSimpleType) ?: error("Don't know how to work with type ${kType::class}") var needToCopyAnnotations = false - when (serializerClassOriginal.classId) { + when (serializerClassOriginal.owner.classId) { polymorphicSerializerId -> { needToCopyAnnotations = true args = listOf(classReference(kType)) @@ -965,29 +967,26 @@ interface IrBuilderExtension { args = listOf(classReference(kType)) typeArgs = listOf(thisIrType) - val hasNewCtxSerCtor = - serializerClassOriginal.classId == contextSerializerId && compilerContext.referenceConstructors(serializerClass.fqNameSafe) - .any { it.owner.valueParameters.size == 3 } + val hasNewCtxSerCtor = compilerContext.referenceConstructors(contextSerializerId).any { it.owner.valueParameters.size == 3 } if (hasNewCtxSerCtor) { // new signature of context serializer args = args + mutableListOf().apply { - val fallbackDefaultSerializer = findTypeSerializer(module, kType) + val fallbackDefaultSerializer = findTypeSerializer(pluginContext, kType) add(instantiate(fallbackDefaultSerializer, kType) ?: irNull()) add( createArrayOfExpression( wrapIrTypeIntoKSerializerIrType( - module, thisIrType, variance = Variance.OUT_VARIANCE ), - kType.arguments.map { + thisIrType.arguments.map { val argSer = enclosingGenerator.findTypeSerializerOrContext( - module, - it.type, - sourceElement = serializerClassOriginal.findPsi() + compilerContext, + it.typeOrNull!!, //todo: handle star projections here + sourceElement = serializerClassOriginal.owner.sourceElement()?.psi ) - instantiate(argSer, it.type)!! + instantiate(argSer, it.typeOrNull!!)!! }) ) } @@ -995,7 +994,7 @@ interface IrBuilderExtension { } objectSerializerId -> { needToCopyAnnotations = true - args = listOf(irString(kType.serialName()), irGetObject(kType.toClassDescriptor!!)) + args = listOf(irString(kType.serialName()), irGetObject(kType.classOrNull!!)) typeArgs = listOf(thisIrType) } sealedSerializerId -> { @@ -1004,8 +1003,8 @@ interface IrBuilderExtension { add(irString(kType.serialName())) add(classReference(kType)) val (subclasses, subSerializers) = enclosingGenerator.allSealedSerializableSubclassesFor( - kType.toClassDescriptor!!, - module + kType.classOrNull!!.owner, + pluginContext ) val projectedOutCurrentKClass = compilerContext.irBuiltIns.kClassClass.typeWithArguments( @@ -1019,23 +1018,21 @@ interface IrBuilderExtension { ) add( createArrayOfExpression( - wrapIrTypeIntoKSerializerIrType(module, thisIrType, variance = Variance.OUT_VARIANCE), + wrapIrTypeIntoKSerializerIrType(thisIrType, variance = Variance.OUT_VARIANCE), subSerializers.mapIndexed { i, serializer -> val type = subclasses[i] val expr = serializerInstance( enclosingGenerator, serializer, - module, + pluginContext, type, type.genericIndex ) { _, genericType -> serializerInstance( enclosingGenerator, - module.getClassFromSerializationPackage( - SpecialBuiltins.polymorphicSerializer - ), - module, - (genericType.constructor.declarationDescriptor as TypeParameterDescriptor).representativeUpperBound + pluginContext.referenceClass(polymorphicSerializerId), + pluginContext, + (genericType.classifierOrNull as IrTypeParameterSymbol).owner.representativeUpperBound )!! }!! wrapWithNullableSerializerIfNeeded(type, expr, nullableSerClass) @@ -1046,8 +1043,8 @@ interface IrBuilderExtension { typeArgs = listOf(thisIrType) } enumSerializerId -> { - serializerClass = module.getClassFromInternalSerializationPackage(SpecialBuiltins.enumSerializer) - val enumDescriptor = kType.toClassDescriptor!! + serializerClass = pluginContext.referenceClass(enumSerializerId) + val enumDescriptor = kType.classOrNull!! typeArgs = listOf(thisIrType) // instantiate serializer only inside enum Companion if (enclosingGenerator !is SerializableCompanionIrGenerator) { @@ -1056,21 +1053,22 @@ interface IrBuilderExtension { } val enumArgs = mutableListOf( - irString(enumDescriptor.serialName()), - irCall(findEnumValuesMethod(enumDescriptor)), + irString(thisIrType.serialName()), + irCall(enumDescriptor.owner.findEnumValuesMethod()), ) val enumSerializerFactoryFunc = enumSerializerFactoryFunc val markedEnumSerializerFactoryFunc = markedEnumSerializerFactoryFunc if (enumSerializerFactoryFunc != null && markedEnumSerializerFactoryFunc != null) { // runtime contains enum serializer factory functions - val factoryFunc: IrSimpleFunctionSymbol = if (enumDescriptor.isEnumWithSerialInfoAnnotation()) { + val factoryFunc: IrSimpleFunctionSymbol = if (enumDescriptor.owner.isEnumWithSerialInfoAnnotation()) { // need to store SerialInfo annotation in descriptor - val enumEntries = enumDescriptor.enumEntries() + val enumEntries = enumDescriptor.owner.enumEntries() val entriesNames = enumEntries.map { it.annotations.serialNameValue?.let { n -> irString(n) } ?: irNull() } val entriesAnnotations = enumEntries.map { - val annotationConstructors = it.annotations.mapNotNull { a -> - compilerContext.typeTranslator.constantValueGenerator.generateAnnotationConstructorCall(a) + val annotationConstructors = it.annotations.map { a -> +// compilerContext.typeTranslator.constantValueGenerator.generateAnnotationConstructorCall(a) + a.deepCopyWithVariables() } val annotationsConstructors = copyAnnotationsFrom(annotationConstructors) if (annotationsConstructors.isEmpty()) { @@ -1100,36 +1098,37 @@ interface IrBuilderExtension { else -> { args = kType.arguments.map { val argSer = enclosingGenerator.findTypeSerializerOrContext( - module, - it.type, - sourceElement = serializerClassOriginal.findPsi() + pluginContext, + it.typeOrNull!!, // todo: stars + sourceElement = serializerClassOriginal.owner.source.getPsi() ) - instantiate(argSer, it.type) ?: return null + instantiate(argSer, it.typeOrNull!!) ?: return null } - typeArgs = kType.arguments.map { it.type.toIrType() } + typeArgs = kType.arguments.map { it.typeOrNull!! } } } - if (serializerClassOriginal.classId == referenceArraySerializerId) { - args = listOf(wrapperClassReference(kType.arguments.single().type)) + args + if (serializerClassOriginal.owner.classId == referenceArraySerializerId) { + args = listOf(wrapperClassReference(kType.arguments.single().typeOrNull!!)) + args typeArgs = listOf(typeArgs[0].makeNotNull()) + typeArgs } // If KType is interface, .classSerializer always yields PolymorphicSerializer, which may be unavailable for interfaces from other modules - if (!kType.isInterface() && serializerClassOriginal == kType.toClassDescriptor?.classSerializer && enclosingGenerator !is SerializableCompanionIrGenerator) { + if (!kType.isInterface() && serializerClassOriginal == kType.classOrNull!!.owner.classSerializer(pluginContext) && enclosingGenerator !is SerializableCompanionIrGenerator) { // This is default type serializer, we can shortcut through Companion.serializer() // BUT not during generation of this method itself callSerializerFromCompanion(thisIrType, typeArgs, args)?.let { return it } } - val serializable = getSerializableClassDescriptorBySerializer(serializerClass) - val ctor = if (serializable?.declaredTypeParameters?.isNotEmpty() == true) { + val serializable = serializerClass?.owner?.let { getSerializableClassDescriptorBySerializer(it) } + requireNotNull(serializerClass) + val ctor = if (serializable?.typeParameters?.isNotEmpty() == true) { requireNotNull( - findSerializerConstructorForTypeArgumentsSerializers(serializerClass) + findSerializerConstructorForTypeArgumentsSerializers(serializerClass.owner) ) { "Generated serializer does not have constructor with required number of arguments" } } else { - val constructors = compilerContext.referenceConstructors(serializerClass.fqNameSafe) + val constructors = serializerClass.constructors // search for new signature of polymorphic/sealed/contextual serializer if (!needToCopyAnnotations) { constructors.single { it.owner.isPrimary } @@ -1163,7 +1162,8 @@ interface IrBuilderExtension { fun collectSerialInfoAnnotations(irClass: IrClass): List { if (!(irClass.isInterface || irClass.descriptor.hasSerializableOrMetaAnnotation)) return emptyList() - val annotationByFq: MutableMap = irClass.annotations.associateBy { it.symbol.owner.parentAsClass.descriptor.fqNameSafe }.toMutableMap() + val annotationByFq: MutableMap = + irClass.annotations.associateBy { it.symbol.owner.parentAsClass.descriptor.fqNameSafe }.toMutableMap() for (clazz in irClass.getAllSuperclasses()) { val annotations = clazz.annotations .mapNotNull { @@ -1195,7 +1195,7 @@ interface IrBuilderExtension { val adjustedArgs: List = // 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)) { - val serializer = findStandardKotlinTypeSerializer(baseClass.module, context.irBuiltIns.unitType.toKotlinType())!! + 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) } } else { @@ -1219,12 +1219,13 @@ interface IrBuilderExtension { return ((lastArgType as? IrSimpleType)?.arguments?.firstOrNull()?.typeOrNull?.classFqName?.toString() == "kotlin.Annotation") } - private fun IrBuilderWithScope.wrapperClassReference(classType: KotlinType): IrClassReference { + private fun IrBuilderWithScope.wrapperClassReference(classType: IrType): IrClassReference { if (compilerContext.platform.isJvm()) { // "Byte::class" -> "java.lang.Byte::class" - val wrapperFqName = KotlinBuiltIns.getPrimitiveType(classType)?.let(JvmPrimitiveType::get)?.wrapperFqName +// TODO: get rid of descriptor + val wrapperFqName = KotlinBuiltIns.getPrimitiveType(classType.classOrNull!!.descriptor)?.let(JvmPrimitiveType::get)?.wrapperFqName if (wrapperFqName != null) { - val wrapperClass = compilerContext.moduleDescriptor.findClassAcrossModuleDependencies(ClassId.topLevel(wrapperFqName)) + val wrapperClass = compilerContext.referenceClass(ClassId.topLevel(wrapperFqName)) ?: error("Primitive wrapper class for $classType not found: $wrapperFqName") return classReference(wrapperClass.defaultType) } @@ -1232,28 +1233,19 @@ interface IrBuilderExtension { return classReference(classType) } - private fun findSerializerConstructorForTypeArgumentsSerializers(serializer: ClassDescriptor): IrConstructorSymbol? { - val serializableImplementationTypeArguments = extractKSerializerArgumentFromImplementation(serializer)?.arguments - ?: throw AssertionError("Serializer does not implement KSerializer??") + fun findSerializerConstructorForTypeArgumentsSerializers(serializer: IrClass): IrConstructorSymbol? { - val typeParamsCount = serializableImplementationTypeArguments.size + val typeParamsCount = ((serializer.superTypes.find { isKSerializer(it) } as IrSimpleType).arguments.first().typeOrNull!! as IrSimpleType).arguments.size if (typeParamsCount == 0) return null //don't need it - val constructors = compilerContext.referenceConstructors(serializer.fqNameSafe) - fun isKSerializer(type: IrType): Boolean { - val simpleType = type as? IrSimpleType ?: return false - val classifier = simpleType.classifier as? IrClassSymbol ?: return false - return classifier.owner.fqNameWhenAvailable == SerialEntityNames.KSERIALIZER_NAME_FQ - } - - return constructors.singleOrNull { - it.owner.valueParameters.let { vps -> vps.size == typeParamsCount && vps.all { vp -> isKSerializer(vp.type) } } - } + return serializer.constructors.singleOrNull { + it.valueParameters.let { vps -> vps.size == typeParamsCount && vps.all { vp -> isKSerializer(vp.type) } } + }?.symbol } private fun IrConstructor.isSerializationCtor(): Boolean { val serialMarker = - compilerContext.referenceClass(SerializationPackages.internalPackageFqName.child(SerialEntityNames.SERIAL_CTOR_MARKER_NAME)) + compilerContext.referenceClass(ClassId.topLevel(SerializationPackages.internalPackageFqName.child(SerialEntityNames.SERIAL_CTOR_MARKER_NAME))) return valueParameters.lastOrNull()?.run { name == SerialEntityNames.dummyParamName && type.classifierOrNull == serialMarker @@ -1279,23 +1271,23 @@ interface IrBuilderExtension { fun IrBlockBodyBuilder.serializeAllProperties( generator: AbstractSerialGenerator, serializableIrClass: IrClass, - serializableProperties: List, + serializableProperties: List, objectToSerialize: IrValueDeclaration, localOutput: IrValueDeclaration, localSerialDesc: IrValueDeclaration, - kOutputClass: ClassDescriptor, + kOutputClass: IrClassSymbol, ignoreIndexTo: Int, initializerAdapter: (IrExpressionBody) -> IrExpression, - genericGetter: ((Int, KotlinType) -> IrExpression)? + genericGetter: ((Int, IrType) -> IrExpression)? ) { - fun SerializableProperty.irGet(): IrExpression { + fun IrSerializableProperty.irGet(): IrExpression { val ownerType = objectToSerialize.symbol.owner.type return getProperty( irGet( type = ownerType, variable = objectToSerialize.symbol - ), getIrPropertyFrom(serializableIrClass) + ), descriptor ) } @@ -1307,7 +1299,7 @@ interface IrBuilderExtension { irGet(localOutput), property, { innerSerial, sti -> val f = - kOutputClass.referenceFunctionSymbol("${CallingConventions.encode}${sti.elementMethodPrefix}Serializable${CallingConventions.elementPostfix}") + kOutputClass.functionByName("${CallingConventions.encode}${sti.elementMethodPrefix}Serializable${CallingConventions.elementPostfix}") f to listOf( irGet(localSerialDesc), irInt(index), @@ -1316,7 +1308,7 @@ interface IrBuilderExtension { ) }, { val f = - kOutputClass.referenceFunctionSymbol("${CallingConventions.encode}${it.elementMethodPrefix}${CallingConventions.elementPostfix}") + kOutputClass.functionByName("${CallingConventions.encode}${it.elementMethodPrefix}${CallingConventions.elementPostfix}") val args: MutableList = mutableListOf(irGet(localSerialDesc), irInt(index)) if (it.elementMethodPrefix != "Unit") args.add(property.irGet()) f to args @@ -1325,7 +1317,7 @@ interface IrBuilderExtension { ) // check for call to .shouldEncodeElementDefault - val encodeDefaults = property.getIrPropertyFrom(serializableIrClass).getEncodeDefaultAnnotationValue() + val encodeDefaults = property.descriptor.getEncodeDefaultAnnotationValue() val field = property.irField // Nullable when property from another module; can't compare it with default value on JS or Native if (!property.optional || encodeDefaults == true || field == null) { // emit call right away @@ -1340,7 +1332,7 @@ interface IrBuilderExtension { // emit check: // if (if (output.shouldEncodeElementDefault(this.descriptor, i)) true else {obj.prop != DEFAULT_VALUE} ) { // output.encodeIntElement(this.descriptor, i, obj.prop)// block {obj.prop != DEFAULT_VALUE} may contain several statements - val shouldEncodeFunc = kOutputClass.referenceFunctionSymbol(CallingConventions.shouldEncodeDefault) + val shouldEncodeFunc = kOutputClass.functionByName(CallingConventions.shouldEncodeDefault) val partA = irInvoke(irGet(localOutput), shouldEncodeFunc, irGet(localSerialDesc), irInt(index)) // Ir infrastructure does not have dedicated symbol for ||, so // `a || b == if (a) true else b`, see org.jetbrains.kotlin.ir.builders.PrimitivesKt.oror @@ -1359,7 +1351,8 @@ interface IrBuilderExtension { fun IrProperty.getEncodeDefaultAnnotationValue(): Boolean? { val call = annotations.findAnnotation(SerializationAnnotations.encodeDefaultFqName) ?: return null val arg = call.getValueArgument(0) ?: return true // ALWAYS by default - val argValue = (arg as? IrGetEnumValue ?: error("Argument of enum constructor expected to implement IrGetEnumValue, got $arg")).symbol.owner.name.toString() + val argValue = (arg as? IrGetEnumValue + ?: error("Argument of enum constructor expected to implement IrGetEnumValue, got $arg")).symbol.owner.name.toString() return when (argValue) { "ALWAYS" -> true "NEVER" -> false @@ -1371,23 +1364,23 @@ interface IrBuilderExtension { fun IrBlockBodyBuilder.formEncodeDecodePropertyCall( enclosingGenerator: AbstractSerialGenerator, encoder: IrExpression, - property: SerializableProperty, - whenHaveSerializer: (serializer: IrExpression, sti: SerialTypeInfo) -> FunctionWithArgs, - whenDoNot: (sti: SerialTypeInfo) -> FunctionWithArgs, - genericGetter: ((Int, KotlinType) -> IrExpression)? = null, + property: IrSerializableProperty, + whenHaveSerializer: (serializer: IrExpression, sti: IrSerialTypeInfo) -> FunctionWithArgs, + whenDoNot: (sti: IrSerialTypeInfo) -> FunctionWithArgs, + genericGetter: ((Int, IrType) -> IrExpression)? = null, returnTypeHint: IrType? = null ): IrExpression { - val sti = enclosingGenerator.getSerialTypeInfo(property) + val sti = enclosingGenerator.getIrSerialTypeInfo(property, compilerContext) val innerSerial = serializerInstance( enclosingGenerator, sti.serializer, - property.module, + compilerContext, property.type, property.genericIndex, genericGetter ) val (functionToCall, args: List) = if (innerSerial != null) whenHaveSerializer(innerSerial, sti) else whenDoNot(sti) - val typeArgs = if (functionToCall.descriptor.typeParameters.isNotEmpty()) listOf(property.type.toIrType()) else listOf() + val typeArgs = if (functionToCall.descriptor.typeParameters.isNotEmpty()) listOf(property.type) else listOf() return irInvoke(encoder, functionToCall, typeArguments = typeArgs, valueArguments = args, returnTypeHint = returnTypeHint) } diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerialInfoImplJvmIrGenerator.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerialInfoImplJvmIrGenerator.kt index 05f51710d41..fb713483d28 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerialInfoImplJvmIrGenerator.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerialInfoImplJvmIrGenerator.kt @@ -28,6 +28,7 @@ import org.jetbrains.kotlin.ir.util.constructors import org.jetbrains.kotlin.ir.util.createImplicitParameterDeclarationWithWrappedDescriptor import org.jetbrains.kotlin.ir.util.defaultType import org.jetbrains.kotlin.ir.util.kotlinFqName +import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.resolve.DescriptorUtils @@ -44,7 +45,7 @@ class SerialInfoImplJvmIrGenerator( override val compilerContext: SerializationPluginContext get() = context - private val jvmNameClass get() = context.referenceClass(DescriptorUtils.JVM_NAME)!!.owner + private val jvmNameClass get() = context.referenceClass(ClassId.topLevel(DescriptorUtils.JVM_NAME))!!.owner private val javaLangClass = createClass(createPackage("java.lang"), "Class", ClassKind.CLASS) private val javaLangType = javaLangClass.starProjectedType @@ -54,6 +55,7 @@ class SerialInfoImplJvmIrGenerator( fun getImplClass(serialInfoAnnotationClass: IrClass): IrClass = annotationToImpl.getOrPut(serialInfoAnnotationClass) { + @Suppress("DEPRECATION") // TODO val implClassSymbol = context.referenceClass(serialInfoAnnotationClass.kotlinFqName.child(SerialEntityNames.IMPL_NAME)) implClassSymbol!!.owner.apply(this::generate) } diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializableCompanionIrGenerator.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializableCompanionIrGenerator.kt index 3f147edc90f..311f7994e0a 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializableCompanionIrGenerator.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializableCompanionIrGenerator.kt @@ -7,58 +7,67 @@ package org.jetbrains.kotlinx.serialization.compiler.backend.ir import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.incremental.components.NoLookupLocation -import org.jetbrains.kotlin.ir.builders.IrBuilderWithScope -import org.jetbrains.kotlin.ir.builders.irGet -import org.jetbrains.kotlin.ir.builders.irInt -import org.jetbrains.kotlin.ir.builders.irReturn +import org.jetbrains.kotlin.ir.builders.* import org.jetbrains.kotlin.ir.declarations.IrClass import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.ir.expressions.impl.IrConstructorCallImpl import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl -import org.jetbrains.kotlin.ir.util.defaultType -import org.jetbrains.kotlin.ir.util.parentAsClass -import org.jetbrains.kotlin.ir.util.patchDeclarationParents -import org.jetbrains.kotlin.ir.util.substitute +import org.jetbrains.kotlin.ir.types.defaultType +import org.jetbrains.kotlin.ir.types.impl.IrErrorClassImpl.endOffset +import org.jetbrains.kotlin.ir.types.impl.IrErrorClassImpl.startOffset +import org.jetbrains.kotlin.ir.types.toKotlinType +import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.Name -import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.calls.components.isVararg import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe import org.jetbrains.kotlin.resolve.descriptorUtil.module -import org.jetbrains.kotlin.util.collectionUtils.filterIsInstanceAnd import org.jetbrains.kotlinx.serialization.compiler.backend.common.SerializableCompanionCodegen import org.jetbrains.kotlinx.serialization.compiler.backend.common.findTypeSerializer +import org.jetbrains.kotlinx.serialization.compiler.backend.common.getSerializableClassByCompanion +import org.jetbrains.kotlinx.serialization.compiler.backend.common.isKSerializer import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPluginContext import org.jetbrains.kotlinx.serialization.compiler.resolve.* class SerializableCompanionIrGenerator( val irClass: IrClass, + val serializableIrClass: IrClass, override val compilerContext: SerializationPluginContext, - bindingContext: BindingContext -) : SerializableCompanionCodegen(irClass.descriptor, bindingContext), IrBuilderExtension { +) : SerializableCompanionCodegen(irClass.descriptor, null), IrBuilderExtension { + + override fun getSerializerGetterDescriptor(): FunctionDescriptor { // todo: remove .toKotlinType() + return irClass.findDeclaration { + (it.valueParameters.size == serializableDescriptor.declaredTypeParameters.size + && it.valueParameters.all { p -> isKSerializer(p.type.toKotlinType()) }) && isKSerializer(it.returnType.toKotlinType()) + }?.descriptor ?: throw IllegalStateException( + "Can't find synthesized 'Companion.serializer()' function to generate, " + + "probably clash with user-defined function has occurred" + ) + } companion object { fun generate( irClass: IrClass, context: SerializationPluginContext, - bindingContext: BindingContext ) { val companionDescriptor = irClass.descriptor val serializableClass = getSerializableClassDescriptorByCompanion(companionDescriptor) ?: return if (serializableClass.shouldHaveGeneratedMethodsInCompanion) { - SerializableCompanionIrGenerator(irClass, context, bindingContext).generate() + SerializableCompanionIrGenerator(irClass, getSerializableClassByCompanion(irClass)!!, context).generate() + val declaration = irClass.constructors.primary // todo: move to appropriate place + if (declaration.body == null) declaration.body = context.generateBodyForDefaultConstructor(declaration) irClass.patchDeclarationParents(irClass.parent) } } } - private fun IrBuilderWithScope.patchSerializableClassWithMarkerAnnotation(serializer: ClassDescriptor) { + private fun IrBuilderWithScope.patchSerializableClassWithMarkerAnnotation(serializer: IrClass) { if (serializer.kind != ClassKind.OBJECT) { return } - val annotationMarkerClass = serializer.module.findClassAcrossModuleDependencies( + val annotationMarkerClass = compilerContext.referenceClass( ClassId( SerializationPackages.packageFqName, Name.identifier(SerialEntityNames.ANNOTATION_MARKER_CLASS) @@ -67,29 +76,18 @@ class SerializableCompanionIrGenerator( val irSerializableClass = if (irClass.isCompanion) irClass.parentAsClass else irClass val serializableWithAlreadyPresent = irSerializableClass.annotations.any { - it.symbol.descriptor.constructedClass.fqNameSafe == annotationMarkerClass.fqNameSafe + it.symbol.descriptor.constructedClass.fqNameSafe == annotationMarkerClass.owner.fqNameWhenAvailable } if (serializableWithAlreadyPresent) return - val annotationCtor = compilerContext.referenceConstructors(annotationMarkerClass.fqNameSafe).single { it.owner.isPrimary } - val annotationType = annotationMarkerClass.defaultType.toIrType() - - val serializerIrClass = if (serializableDescriptor.isInternalSerializable) { - // internally generated serializer always declared inside serializable class - irSerializableClass.declarations - .filterIsInstanceAnd { it.name == serializer.name } - .singleOrNull() ?: throw Exception("No class with name ${serializer.fqNameSafe}") - } else { - // FIXME referenceClass not supports local classes so it should be replaced in future - compilerContext.referenceClass(serializer.fqNameSafe)!!.owner - } - + val annotationCtor = annotationMarkerClass.constructors.single { it.owner.isPrimary } + val annotationType = annotationMarkerClass.defaultType val annotationCtorCall = IrConstructorCallImpl.fromSymbolDescriptor(startOffset, endOffset, annotationType, annotationCtor).apply { putValueArgument( 0, createClassReference( - serializerIrClass, + serializer.defaultType, startOffset, endOffset ) @@ -100,24 +98,23 @@ class SerializableCompanionIrGenerator( } override fun generateLazySerializerGetter(methodDescriptor: FunctionDescriptor) { - val serializerDescriptor = requireNotNull( + val serializer = requireNotNull( findTypeSerializer( - serializableDescriptor.module, - serializableDescriptor.toSimpleType() + compilerContext, + serializableIrClass.defaultType ) ) - val kSerializerIrClass = compilerContext.referenceClass(SerialEntityNames.KSERIALIZER_NAME_FQ)!!.owner + val kSerializerIrClass = serializer.owner val targetIrType = kSerializerIrClass.defaultType.substitute(mapOf(kSerializerIrClass.typeParameters[0].symbol to compilerContext.irBuiltIns.anyType)) val property = createLazyProperty(irClass, targetIrType, SerialEntityNames.CACHED_SERIALIZER_PROPERTY_NAME) { val expr = serializerInstance( this@SerializableCompanionIrGenerator, - serializerDescriptor, serializableDescriptor.module, - serializableDescriptor.defaultType + serializer, compilerContext, kSerializerIrClass.defaultType ) - patchSerializableClassWithMarkerAnnotation(serializerDescriptor) + patchSerializableClassWithMarkerAnnotation(kSerializerIrClass) +irReturn(requireNotNull(expr)) } @@ -131,17 +128,17 @@ class SerializableCompanionIrGenerator( irClass.contributeFunction(methodDescriptor) { getter -> val serializer = requireNotNull( findTypeSerializer( - serializableDescriptor.module, - serializableDescriptor.toSimpleType() + compilerContext, + serializableIrClass.defaultType ) ) val args: List = getter.valueParameters.map { irGet(it) } val expr = serializerInstance( this@SerializableCompanionIrGenerator, - serializer, serializableDescriptor.module, - serializableDescriptor.defaultType + serializer, compilerContext, + serializableIrClass.defaultType ) { it, _ -> args[it] } - patchSerializableClassWithMarkerAnnotation(serializer) + patchSerializableClassWithMarkerAnnotation(serializer.owner) +irReturn(requireNotNull(expr)) } generateSerializerFactoryIfNeeded(methodDescriptor) @@ -149,16 +146,13 @@ class SerializableCompanionIrGenerator( private fun generateSerializerFactoryIfNeeded(getterDescriptor: FunctionDescriptor) { if (!companionDescriptor.needSerializerFactory()) return - val serialFactoryDescriptor = companionDescriptor.unsubstitutedMemberScope.getContributedFunctions( - SerialEntityNames.SERIALIZER_PROVIDER_NAME, - NoLookupLocation.FROM_BACKEND - ).firstOrNull { + val serialFactoryDescriptor = irClass.findDeclaration { it.valueParameters.size == 1 && it.valueParameters.first().isVararg - && it.kind == CallableMemberDescriptor.Kind.SYNTHESIZED - && it.returnType != null && isKSerializer(it.returnType) +// && it.kind == CallableMemberDescriptor.Kind.SYNTHESIZED + && isKSerializer(it.returnType) } ?: return - irClass.contributeFunction(serialFactoryDescriptor) { factory -> + addFunctionBody(serialFactoryDescriptor) { factory -> val kSerializerStarType = factory.returnType val array = factory.valueParameters.first() val argsSize = serializableDescriptor.declaredTypeParameters.size @@ -177,8 +171,9 @@ class SerializableCompanionIrGenerator( returnTypeHint = kSerializerStarType ) +irReturn(call) - patchSerializableClassWithMarkerAnnotation(companionDescriptor) + patchSerializableClassWithMarkerAnnotation(irClass) } } } + diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializableIrGenerator.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializableIrGenerator.kt index 73f02c95ef6..66e7ed5269c 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializableIrGenerator.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializableIrGenerator.kt @@ -5,71 +5,70 @@ package org.jetbrains.kotlinx.serialization.compiler.backend.ir -import org.jetbrains.kotlin.ir.deepCopyWithVariables import org.jetbrains.kotlin.backend.common.lower.irThrow import org.jetbrains.kotlin.codegen.CompilationException import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.ir.IrStatement import org.jetbrains.kotlin.ir.builders.* import org.jetbrains.kotlin.ir.declarations.* -import org.jetbrains.kotlin.ir.expressions.* +import org.jetbrains.kotlin.ir.deepCopyWithVariables +import org.jetbrains.kotlin.ir.expressions.IrExpression +import org.jetbrains.kotlin.ir.expressions.IrExpressionBody +import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin import org.jetbrains.kotlin.ir.expressions.impl.IrDelegatingConstructorCallImpl -import org.jetbrains.kotlin.ir.types.IrSimpleType -import org.jetbrains.kotlin.ir.types.IrTypeProjection -import org.jetbrains.kotlin.ir.types.classOrNull -import org.jetbrains.kotlin.ir.types.isString +import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.util.* -import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi -import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe import org.jetbrains.kotlin.resolve.descriptorUtil.module +import org.jetbrains.kotlin.resolve.descriptorUtil.secondaryConstructors +import org.jetbrains.kotlin.resolve.isInlineClass +import org.jetbrains.kotlin.types.typeUtil.isUnit import org.jetbrains.kotlin.util.OperatorNameConventions import org.jetbrains.kotlin.util.collectionUtils.filterIsInstanceAnd import org.jetbrains.kotlin.utils.getOrPutNullable -import org.jetbrains.kotlinx.serialization.compiler.backend.common.SerializableCodegen -import org.jetbrains.kotlinx.serialization.compiler.backend.common.findTypeSerializerOrContext +import org.jetbrains.kotlinx.serialization.compiler.backend.common.* import org.jetbrains.kotlinx.serialization.compiler.backend.common.isStaticSerializable -import org.jetbrains.kotlinx.serialization.compiler.backend.common.serialName import org.jetbrains.kotlinx.serialization.compiler.diagnostic.serializableAnnotationIsUseless import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPluginContext import org.jetbrains.kotlinx.serialization.compiler.resolve.* import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.CACHED_DESCRIPTOR_FIELD_NAME +import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.LOAD import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.MISSING_FIELD_EXC +import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.SAVE import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.SERIAL_DESC_FIELD class SerializableIrGenerator( val irClass: IrClass, - override val compilerContext: SerializationPluginContext, - bindingContext: BindingContext -) : SerializableCodegen(irClass.descriptor, bindingContext), IrBuilderExtension { + override val compilerContext: SerializationPluginContext +) : AbstractSerialGenerator(null, irClass.descriptor), IrBuilderExtension { - private val serialDescClass: ClassDescriptor = serializableDescriptor.module - .getClassFromSerializationDescriptorsPackage(SerialEntityNames.SERIAL_DESCRIPTOR_CLASS) + protected val properties = bindingContext.serializablePropertiesForIrBackend(irClass) - private val serialDescImplClass: ClassDescriptor = serializableDescriptor - .getClassFromInternalSerializationPackage(SerialEntityNames.SERIAL_DESCRIPTOR_CLASS_IMPL) + private val serialDescriptorClass = compilerContext.referenceClass( + ClassId( + SerializationPackages.descriptorsPackageFqName, + Name.identifier(SerialEntityNames.SERIAL_DESCRIPTOR_CLASS) + ) + )!!.owner - private val addElementFun = serialDescImplClass.referenceFunctionSymbol(CallingConventions.addElement) + private val serialDescriptorImplClass = compilerContext.referenceClass( + ClassId( + SerializationPackages.internalPackageFqName, + Name.identifier(SerialEntityNames.SERIAL_DESCRIPTOR_CLASS_IMPL) + ) + )!!.owner - private fun IrClass.hasSerializableOrMetaAnnotationWithoutArgs(): Boolean { - val annot = getAnnotation(SerializationAnnotations.serializableAnnotationFqName) - if (annot != null) { - for (i in 0 until annot.valueArgumentsCount) { - if (annot.getValueArgument(i) != null) return false - } - return true - } - val metaAnnotation = annotations - .flatMap { it.symbol.owner.constructedClass.annotations } - .find { it.isAnnotation(SerializationAnnotations.metaSerializableAnnotationFqName) } - return metaAnnotation != null - } + private val addElementFun = + serialDescriptorImplClass.findDeclaration { it.name.toString() == CallingConventions.addElement }!!.symbol private val IrClass.isInternalSerializable: Boolean get() = kind == ClassKind.CLASS && hasSerializableOrMetaAnnotationWithoutArgs() - override fun generateInternalConstructor(constructorDescriptor: ClassConstructorDescriptor) = - irClass.contributeConstructor(constructorDescriptor) { ctor -> + fun generateInternalConstructor(constructorDescriptor: IrConstructor) = + addFunctionBody(constructorDescriptor) { ctor -> val thiz = irClass.thisReceiver!! val serializableProperties = properties.serializableProperties @@ -81,14 +80,14 @@ class SerializableIrGenerator( val initializerAdapter: (IrExpressionBody) -> IrExpression = createInitializerAdapter(irClass, propertyByParamReplacer) - var current: PropertyDescriptor? = null - val statementsAfterSerializableProperty: MutableMap> = mutableMapOf() + var current: IrProperty? = null + val statementsAfterSerializableProperty: MutableMap> = mutableMapOf() irClass.declarations.asSequence().forEach { when { // only properties with backing field it is IrProperty && it.backingField != null -> { - if (it.descriptor in serialDescs) { - current = it.descriptor + if (it in serialDescs) { + current = it } else if (it.backingField?.initializer != null) { // skip transient lateinit or deferred properties (with null initializer) val expression = initializerAdapter(it.backingField!!.initializer!!) @@ -121,9 +120,9 @@ class SerializableIrGenerator( if (useFieldMissingOptimization() && // for abstract classes fields MUST BE checked in child classes - !serializableDescriptor.isAbstractOrSealedSerializableClass() + !irClass.isAbstractOrSealedSerializableClass ) { - val getDescriptorExpr = if (serializableDescriptor.isStaticSerializable) { + val getDescriptorExpr = if (irClass.isStaticSerializable) { getStaticSerialDescriptorExpr() } else { // synthetic constructor is created only for internally serializable classes - so companion definitely exists @@ -133,7 +132,7 @@ class SerializableIrGenerator( generateGoldenMaskCheck(seenVars, properties, getDescriptorExpr) } when { - superClass.symbol == compilerContext.irBuiltIns.anyClass -> generateAnySuperConstructorCall(toBuilder = this@contributeConstructor) + superClass.symbol == compilerContext.irBuiltIns.anyClass -> generateAnySuperConstructorCall(toBuilder = this@addFunctionBody) superClass.isInternalSerializable -> { startPropOffset = generateSuperSerializableCall(superClass, ctor.valueParameters, seenVarsOffset) } @@ -146,7 +145,7 @@ class SerializableIrGenerator( val paramRef = ctor.valueParameters[index + seenVarsOffset] // Assign this.a = a in else branch // Set field directly w/o setter to match behavior of old backend plugin - val backingFieldToAssign = prop.getIrPropertyFrom(irClass).backingField!! + val backingFieldToAssign = prop.descriptor.backingField!! val assignParamExpr = irSetField(irGet(thiz), backingFieldToAssign, irGet(paramRef)) val ifNotSeenExpr: IrExpression = if (prop.optional) { @@ -196,17 +195,17 @@ class SerializableIrGenerator( } private fun IrBlockBodyBuilder.getStaticSerialDescriptorExpr(): IrExpression { - val serializer = serializableDescriptor.classSerializer!! + val serializerIrClass = irClass.classSerializer(compilerContext)!!.owner // internally generated serializer always declared inside serializable class - val serializerIrClass = irClass.declarations - .filterIsInstanceAnd { it.name == serializer.name } - .singleOrNull() ?: throw Exception("No class with name ${serializer.fqNameSafe}") +// val serializerIrClass = irClass.declarations +// .filterIsInstanceAnd { it.name == serializer.name } +// .singleOrNull() ?: throw Exception("No class with name ${serializer.fqNameSafe}") val serialDescriptorGetter = serializerIrClass.getPropertyGetter(SERIAL_DESC_FIELD)!! return irGet( serializerIrClass.defaultType, - irGetObject(serializer), + irGetObject(serializerIrClass), serialDescriptorGetter.owner.symbol ) } @@ -216,7 +215,7 @@ class SerializableIrGenerator( } private fun IrBlockBodyBuilder.createCachedDescriptorProperty(companionObject: IrClass): IrProperty { - val serialDescIrType = serialDescClass.defaultType.toIrType() + val serialDescIrType = serialDescriptorClass.defaultType return createCompanionValProperty(companionObject, serialDescIrType, CACHED_DESCRIPTOR_FIELD_NAME) { val serialDescVar = irTemporary( @@ -231,16 +230,16 @@ class SerializableIrGenerator( } private fun IrBlockBodyBuilder.getInstantiateDescriptorExpr(): IrExpression { - val classConstructors = compilerContext.referenceConstructors(serialDescImplClass.fqNameSafe) + val classConstructors = compilerContext.referenceConstructors(serialDescriptorImplClass.kotlinFqName) //todo: remove reference val serialClassDescImplCtor = classConstructors.single { it.owner.isPrimary } return irInvoke( null, serialClassDescImplCtor, - irString(serializableDescriptor.serialName()), irNull(), irInt(properties.serializableProperties.size) + irString(irClass.serialName()), irNull(), irInt(properties.serializableProperties.size) ) } private fun IrBlockBodyBuilder.getAddElementToDescriptorExpr( - property: SerializableProperty, + property: IrSerializableProperty, serialDescVar: IrVariable ): IrExpression { return irInvoke( @@ -254,7 +253,7 @@ class SerializableIrGenerator( private fun IrBlockBodyBuilder.generateSuperNonSerializableCall(superClass: IrClass) { val ctorRef = superClass.declarations.filterIsInstance().singleOrNull { it.valueParameters.isEmpty() } - ?: error("Non-serializable parent of serializable $serializableDescriptor must have no arg constructor") + ?: error("Non-serializable parent of serializable $irClass must have no arg constructor") val call = IrDelegatingConstructorCallImpl.fromSymbolDescriptor( @@ -301,13 +300,13 @@ class SerializableIrGenerator( return superProperties.size } - override fun generateWriteSelfMethod(methodDescriptor: FunctionDescriptor) { - irClass.contributeFunction(methodDescriptor, ignoreWhenMissing = true) { writeSelfFunction -> + fun generateWriteSelfMethod(methodDescriptor: IrSimpleFunction) { + addFunctionBody(methodDescriptor) { writeSelfFunction -> val objectToSerialize = writeSelfFunction.valueParameters[0] val localOutput = writeSelfFunction.valueParameters[1] val localSerialDesc = writeSelfFunction.valueParameters[2] val serializableProperties = properties.serializableProperties - val kOutputClass = serializableDescriptor.getClassFromSerializationPackage(SerialEntityNames.STRUCTURE_ENCODER_CLASS) + val kOutputClass = compilerContext.getClassFromRuntime(SerialEntityNames.STRUCTURE_ENCODER_CLASS) val propertyByParamReplacer: (ValueParameterDescriptor) -> IrExpression? = createPropertyByParamReplacer(irClass, serializableProperties, objectToSerialize, bindingContext) @@ -339,21 +338,21 @@ class SerializableIrGenerator( val args = mutableListOf(irGet(objectToSerialize), irGet(localOutput), irGet(localSerialDesc)) val typeArgsForParent = - serializableDescriptor.typeConstructor.supertypes.single { it.toClassDescriptor?.isInternalSerializable == true }.arguments + (irClass.superTypes.single { it.classOrNull?.owner?.isInternalSerializable == true } as? IrSimpleType)?.arguments.orEmpty() val parentWriteSelfSerializers = typeArgsForParent.map { arg -> - val genericIdx = serializableDescriptor.defaultType.arguments.indexOf(arg).let { if (it == -1) null else it } - val serial = findTypeSerializerOrContext(serializableDescriptor.module, arg.type) + val genericIdx = irClass.defaultType.arguments.indexOf(arg).let { if (it == -1) null else it } + val serial = findTypeSerializerOrContext(compilerContext, arg.typeOrNull!!) serializerInstance( this@SerializableIrGenerator, serial, - serializableDescriptor.module, - arg.type, + compilerContext, + arg.typeOrNull!!, genericIdx ) { it, _ -> irGet(writeSelfFunction.valueParameters[3 + it]) }!! } - +irInvoke(null, superWriteSelfF.symbol, typeArgsForParent.map { it.type.toIrType() }, args + parentWriteSelfSerializers) + +irInvoke(null, superWriteSelfF.symbol, typeArgsForParent.map { it.typeOrNull!! }, args + parentWriteSelfSerializers) } } @@ -367,16 +366,45 @@ class SerializableIrGenerator( } } + fun generate() { + generateSyntheticInternalConstructor() + generateSyntheticMethods() + } + + private inline fun IrClass.shouldHaveSpecificSyntheticMethods(functionPresenceChecker: () -> IrSimpleFunction?) = + !isValue && (isAbstractOrSealedSerializableClass || functionPresenceChecker() != null) + + private fun generateSyntheticInternalConstructor() { // TODO: this doesn't work with OLD FE + val serializerDescriptor = irClass.classSerializer(compilerContext)?.owner ?: return + if (irClass.shouldHaveSpecificSyntheticMethods { serializerDescriptor.findPluginGeneratedMethod(LOAD) }) { + val constrDesc = irClass.constructors.find(IrConstructor::isSerializationCtor) ?: return + generateInternalConstructor(constrDesc) + } + } + + private fun generateSyntheticMethods() { + val serializerDescriptor = irClass.classSerializer(compilerContext)?.owner ?: return + if (irClass.shouldHaveSpecificSyntheticMethods { serializerDescriptor.findPluginGeneratedMethod(SAVE) }) { + val func = + irClass.functions.singleOrNull { function -> + function.name == SerialEntityNames.WRITE_SELF_NAME && + function.modality == Modality.FINAL && + function.returnType.isUnit() + } ?: return + generateWriteSelfMethod(func) + } + } + + companion object { fun generate( irClass: IrClass, context: SerializationPluginContext, - bindingContext: BindingContext ) { val serializableClass = irClass.descriptor if (serializableClass.isInternalSerializable) { - SerializableIrGenerator(irClass, context, bindingContext).generate() + SerializableIrGenerator(irClass, context).generate() irClass.patchDeclarationParents(irClass.parent) } else if (serializableClass.serializableAnnotationIsUseless) { throw CompilationException( diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerForEnumsGenerator.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerForEnumsGenerator.kt index 9e431990a74..51103703eb3 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerForEnumsGenerator.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerForEnumsGenerator.kt @@ -5,6 +5,7 @@ package org.jetbrains.kotlinx.serialization.compiler.backend.ir +import org.jetbrains.kotlin.backend.jvm.functionByName import org.jetbrains.kotlin.builtins.StandardNames import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor @@ -12,31 +13,37 @@ import org.jetbrains.kotlin.ir.builders.* import org.jetbrains.kotlin.ir.declarations.IrClass import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction import org.jetbrains.kotlin.ir.declarations.IrVariable +import org.jetbrains.kotlin.ir.deepCopyWithVariables import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl +import org.jetbrains.kotlin.ir.symbols.IrClassSymbol import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol +import org.jetbrains.kotlin.ir.util.constructors import org.jetbrains.kotlin.ir.util.defaultType import org.jetbrains.kotlin.ir.util.functions import org.jetbrains.kotlin.ir.util.properties import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe +import org.jetbrains.kotlinx.serialization.compiler.backend.common.enumEntries +import org.jetbrains.kotlinx.serialization.compiler.backend.common.getClassFromInternalSerializationPackage +import org.jetbrains.kotlinx.serialization.compiler.backend.common.getClassFromRuntime +import org.jetbrains.kotlinx.serialization.compiler.backend.common.serialNameValue import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPluginContext import org.jetbrains.kotlinx.serialization.compiler.resolve.* class SerializerForEnumsGenerator( irClass: IrClass, compilerContext: SerializationPluginContext, - bindingContext: BindingContext, serialInfoJvmGenerator: SerialInfoImplJvmIrGenerator, -) : SerializerIrGenerator(irClass, compilerContext, bindingContext, null, serialInfoJvmGenerator) { - override fun generateSave(function: FunctionDescriptor) = irClass.contributeFunction(function) { saveFunc -> +) : SerializerIrGenerator(irClass, compilerContext, null, serialInfoJvmGenerator) { + override fun generateSave(function: IrSimpleFunction) = addFunctionBody(function) { saveFunc -> fun irThis(): IrExpression = IrGetValueImpl(startOffset, endOffset, saveFunc.dispatchReceiverParameter!!.symbol) - val encoderClass = serializerDescriptor.getClassFromSerializationPackage(SerialEntityNames.ENCODER_CLASS) - val descriptorGetterSymbol = irAnySerialDescProperty?.owner?.getter!!.symbol - val encodeEnum = encoderClass.referenceFunctionSymbol(CallingConventions.encodeEnum) + val encoderClass = compilerContext.getClassFromRuntime(SerialEntityNames.ENCODER_CLASS) + val descriptorGetterSymbol = irAnySerialDescProperty?.getter!!.symbol + val encodeEnum = encoderClass.functionByName(CallingConventions.encodeEnum) val serialDescGetter = irGet(descriptorGetterSymbol.owner.returnType, irThis(), descriptorGetterSymbol) val serializableIrClass = requireNotNull(serializableIrClass) { "Enums do not support external serialization" } @@ -46,13 +53,13 @@ class SerializerForEnumsGenerator( +call } - override fun generateLoad(function: FunctionDescriptor) = irClass.contributeFunction(function) { loadFunc -> + override fun generateLoad(function: IrSimpleFunction) = addFunctionBody(function) { loadFunc -> fun irThis(): IrExpression = IrGetValueImpl(startOffset, endOffset, loadFunc.dispatchReceiverParameter!!.symbol) - val decoderClass = serializerDescriptor.getClassFromSerializationPackage(SerialEntityNames.DECODER_CLASS) - val descriptorGetterSymbol = irAnySerialDescProperty?.owner?.getter!!.symbol - val decode = decoderClass.referenceFunctionSymbol(CallingConventions.decodeEnum) + val decoderClass = compilerContext.getClassFromRuntime(SerialEntityNames.DECODER_CLASS) + val descriptorGetterSymbol = irAnySerialDescProperty?.getter!!.symbol + val decode = decoderClass.functionByName(CallingConventions.decodeEnum) val serialDescGetter = irGet(descriptorGetterSymbol.owner.returnType, irThis(), descriptorGetterSymbol) val serializableIrClass = requireNotNull(serializableIrClass) { "Enums do not support external serialization" } @@ -73,27 +80,23 @@ class SerializerForEnumsGenerator( +irReturn(getValueByOrdinal) } - override val serialDescImplClass: ClassDescriptor = serializerDescriptor - .getClassFromInternalSerializationPackage(SerialEntityNames.SERIAL_DESCRIPTOR_FOR_ENUM) + override val serialDescImplClass: IrClassSymbol = compilerContext.getClassFromInternalSerializationPackage(SerialEntityNames.SERIAL_DESCRIPTOR_FOR_ENUM) - override fun IrBlockBodyBuilder.instantiateNewDescriptor( - serialDescImplClass: ClassDescriptor, - correctThis: IrExpression - ): IrExpression { - val ctor = compilerContext.referenceConstructors(serialDescImplClass.fqNameSafe).single { it.owner.isPrimary } + override fun IrBlockBodyBuilder.instantiateNewDescriptor(serialDescImplClass: IrClassSymbol, correctThis: IrExpression): IrExpression { + val ctor = serialDescImplClass.constructors.single { it.owner.isPrimary } return irInvoke( null, ctor, irString(serialName), - irInt(serializableDescriptor.enumEntries().size) + irInt(serializableIrClass.enumEntries().size) ) } override fun IrBlockBodyBuilder.addElementsContentToDescriptor( - serialDescImplClass: ClassDescriptor, + serialDescImplClass: IrClassSymbol, localDescriptor: IrVariable, addFunction: IrFunctionSymbol ) { - val enumEntries = serializableDescriptor.enumEntries() + val enumEntries = serializableIrClass.enumEntries() for (entry in enumEntries) { // regular .serialName() produces fqName here, which is kinda inconvenient for enum entry val serialName = entry.annotations.serialNameValue ?: entry.name.toString() @@ -107,9 +110,9 @@ class SerializerForEnumsGenerator( +call // serialDesc.pushAnnotation(...) copySerialInfoAnnotationsToDescriptor( - entry.annotations.mapNotNull(compilerContext.typeTranslator.constantValueGenerator::generateAnnotationConstructorCall), + entry.annotations.map {it.deepCopyWithVariables()}, localDescriptor, - serialDescImplClass.referenceFunctionSymbol(CallingConventions.addAnnotation) + serialDescImplClass.functionByName(CallingConventions.addAnnotation) ) } } diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerForInlineClassGenerator.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerForInlineClassGenerator.kt index 7903b575458..a94c3335383 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerForInlineClassGenerator.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerForInlineClassGenerator.kt @@ -5,34 +5,38 @@ package org.jetbrains.kotlinx.serialization.compiler.backend.ir +import org.jetbrains.kotlin.backend.jvm.functionByName import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.ir.builders.* import org.jetbrains.kotlin.ir.declarations.IrClass +import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl +import org.jetbrains.kotlin.ir.symbols.IrClassSymbol import org.jetbrains.kotlin.ir.types.IrSimpleType import org.jetbrains.kotlin.ir.types.IrType import org.jetbrains.kotlin.ir.types.typeOrNull import org.jetbrains.kotlin.ir.util.constructors import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe +import org.jetbrains.kotlinx.serialization.compiler.backend.common.getClassFromInternalSerializationPackage +import org.jetbrains.kotlinx.serialization.compiler.backend.common.getClassFromRuntime import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPluginContext import org.jetbrains.kotlinx.serialization.compiler.resolve.* class SerializerForInlineClassGenerator( irClass: IrClass, compilerContext: SerializationPluginContext, - bindingContext: BindingContext, serialInfoJvmGenerator: SerialInfoImplJvmIrGenerator, -) : SerializerIrGenerator(irClass, compilerContext, bindingContext, null, serialInfoJvmGenerator) { - override fun generateSave(function: FunctionDescriptor) = irClass.contributeFunction(function) { saveFunc -> +) : SerializerIrGenerator(irClass, compilerContext, null, serialInfoJvmGenerator) { + override fun generateSave(function: IrSimpleFunction) = addFunctionBody(function) { saveFunc -> fun irThis(): IrExpression = IrGetValueImpl(startOffset, endOffset, saveFunc.dispatchReceiverParameter!!.symbol) - val encoderClass = serializerDescriptor.getClassFromSerializationPackage(SerialEntityNames.ENCODER_CLASS) - val descriptorGetterSymbol = irAnySerialDescProperty?.owner?.getter!!.symbol - val encodeInline = encoderClass.referenceFunctionSymbol(CallingConventions.encodeInline) + val encoderClass = compilerContext.getClassFromRuntime(SerialEntityNames.ENCODER_CLASS) + val descriptorGetterSymbol = irAnySerialDescProperty?.getter!!.symbol + val encodeInline = encoderClass.functionByName(CallingConventions.encodeInline) val serialDescGetter = irGet(descriptorGetterSymbol.owner.returnType, irThis(), descriptorGetterSymbol) // val inlineEncoder = encoder.encodeInline() @@ -45,14 +49,14 @@ class SerializerForInlineClassGenerator( // inlineEncoder.encodeInt/String/SerializableValue val elementCall = formEncodeDecodePropertyCall(irGet(inlineEncoder), saveFunc.dispatchReceiverParameter!!, property, {innerSerial, sti -> val f = - encoderClass.referenceFunctionSymbol("${CallingConventions.encode}${sti.elementMethodPrefix}SerializableValue") + encoderClass.functionByName("${CallingConventions.encode}${sti.elementMethodPrefix}SerializableValue") f to listOf( innerSerial, value ) }, { val f = - encoderClass.referenceFunctionSymbol("${CallingConventions.encode}${it.elementMethodPrefix}") + encoderClass.functionByName("${CallingConventions.encode}${it.elementMethodPrefix}") val args = if (it.elementMethodPrefix != "Unit") listOf(value) else emptyList() f to args }) @@ -61,37 +65,33 @@ class SerializerForInlineClassGenerator( +actualEncodeCall } - override fun generateLoad(function: FunctionDescriptor) = irClass.contributeFunction(function) { loadFunc -> + override fun generateLoad(function: IrSimpleFunction) = addFunctionBody(function) { loadFunc -> fun irThis(): IrExpression = IrGetValueImpl(startOffset, endOffset, loadFunc.dispatchReceiverParameter!!.symbol) - val decoderClass = serializerDescriptor.getClassFromSerializationPackage(SerialEntityNames.DECODER_CLASS) - val descriptorGetterSymbol = irAnySerialDescProperty?.owner?.getter!!.symbol - val decodeInline = decoderClass.referenceFunctionSymbol(CallingConventions.decodeInline) + val decoderClass = compilerContext.getClassFromRuntime(SerialEntityNames.DECODER_CLASS) + val descriptorGetterSymbol = irAnySerialDescProperty?.getter!!.symbol + val decodeInline = decoderClass.functionByName(CallingConventions.decodeInline) val serialDescGetter = irGet(descriptorGetterSymbol.owner.returnType, irThis(), descriptorGetterSymbol) // val inlineDecoder = decoder.decodeInline() val inlineDecoder: IrExpression = irInvoke(irGet(loadFunc.valueParameters[0]), decodeInline, serialDescGetter) val property = serializableProperties.first() - val inlinedType = property.type.toIrType() + val inlinedType = property.type val actualCall = formEncodeDecodePropertyCall(inlineDecoder, loadFunc.dispatchReceiverParameter!!, property, { innerSerial, sti -> - decoderClass.referenceFunctionSymbol( "${CallingConventions.decode}${sti.elementMethodPrefix}SerializableValue") to listOf(innerSerial) + decoderClass.functionByName( "${CallingConventions.decode}${sti.elementMethodPrefix}SerializableValue") to listOf(innerSerial) }, { - decoderClass.referenceFunctionSymbol("${CallingConventions.decode}${it.elementMethodPrefix}") to listOf() + decoderClass.functionByName("${CallingConventions.decode}${it.elementMethodPrefix}") to listOf() }, returnTypeHint = inlinedType) val value = coerceToBox(actualCall, loadFunc.returnType) +irReturn(value) } - override val serialDescImplClass: ClassDescriptor = serializerDescriptor - .getClassFromInternalSerializationPackage(SerialEntityNames.SERIAL_DESCRIPTOR_FOR_INLINE) + override val serialDescImplClass: IrClassSymbol = compilerContext.getClassFromInternalSerializationPackage(SerialEntityNames.SERIAL_DESCRIPTOR_FOR_INLINE) - override fun IrBlockBodyBuilder.instantiateNewDescriptor( - serialDescImplClass: ClassDescriptor, - correctThis: IrExpression - ): IrExpression { - val ctor = compilerContext.referenceConstructors(serialDescImplClass.fqNameSafe).single { it.owner.isPrimary } + override fun IrBlockBodyBuilder.instantiateNewDescriptor(serialDescImplClass: IrClassSymbol, correctThis: IrExpression): IrExpression { + val ctor = serialDescImplClass.constructors.single { it.owner.isPrimary } return irInvoke( null, ctor, irString(serialName), @@ -109,6 +109,6 @@ class SerializerForInlineClassGenerator( listOf(expression) ) - private fun IrBlockBodyBuilder.getFromBox(expression: IrExpression, serializableProperty: SerializableProperty): IrExpression = - getProperty(expression, serializableProperty.getIrPropertyFrom(serializableIrClass)) + private fun IrBlockBodyBuilder.getFromBox(expression: IrExpression, serializableProperty: IrSerializableProperty): IrExpression = + getProperty(expression, serializableProperty.descriptor) } diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerIrGenerator.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerIrGenerator.kt index ec9e0ca4567..4f4161f5933 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerIrGenerator.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerIrGenerator.kt @@ -9,6 +9,7 @@ import org.jetbrains.kotlin.ir.deepCopyWithVariables import org.jetbrains.kotlin.backend.common.lower.DeclarationIrBuilder import org.jetbrains.kotlin.backend.common.lower.irIfThen import org.jetbrains.kotlin.backend.common.lower.irThrow +import org.jetbrains.kotlin.backend.jvm.functionByName import org.jetbrains.kotlin.builtins.PrimitiveType import org.jetbrains.kotlin.codegen.CompilationException import org.jetbrains.kotlin.descriptors.* @@ -16,29 +17,29 @@ import org.jetbrains.kotlin.ir.builders.* import org.jetbrains.kotlin.ir.declarations.* 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.IrConstructorSymbol import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol import org.jetbrains.kotlin.ir.symbols.impl.IrAnonymousInitializerSymbolImpl import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.util.* -import org.jetbrains.kotlin.resolve.BindingContext -import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.calls.components.hasDefaultValue -import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe import org.jetbrains.kotlin.resolve.isInlineClass import org.jetbrains.kotlin.util.OperatorNameConventions import org.jetbrains.kotlin.utils.addToStdlib.cast -import org.jetbrains.kotlinx.serialization.compiler.backend.common.SerialTypeInfo -import org.jetbrains.kotlinx.serialization.compiler.backend.common.SerializerCodegen +import org.jetbrains.kotlinx.serialization.compiler.backend.common.* import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationDescriptorSerializerPlugin import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPluginContext +import org.jetbrains.kotlinx.serialization.compiler.fir.SerializationPluginKey import org.jetbrains.kotlinx.serialization.compiler.resolve.* import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.DECODER_CLASS import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.ENCODER_CLASS -import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.SERIAL_DESCRIPTOR_CLASS_IMPL +import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.SAVE import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.STRUCTURE_DECODER_CLASS import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.STRUCTURE_ENCODER_CLASS import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.UNKNOWN_FIELD_EXC +import org.jetbrains.kotlinx.serialization.compiler.resolve.getSerializableClassDescriptorBySerializer +import org.jetbrains.kotlinx.serialization.compiler.resolve.isEnumWithLegacyGeneratedSerializer object SERIALIZABLE_PLUGIN_ORIGIN : IrDeclarationOriginImpl("SERIALIZER", true) @@ -47,31 +48,63 @@ internal typealias FunctionWithArgs = Pair> open class SerializerIrGenerator( val irClass: IrClass, final override val compilerContext: SerializationPluginContext, - bindingContext: BindingContext, metadataPlugin: SerializationDescriptorSerializerPlugin?, private val serialInfoJvmGenerator: SerialInfoImplJvmIrGenerator, -) : SerializerCodegen(irClass.descriptor, bindingContext, metadataPlugin), IrBuilderExtension { - protected val serializableIrClass = compilerContext.symbolTable.referenceClass(serializableDescriptor).owner - protected val irAnySerialDescProperty = anySerialDescProperty?.let { compilerContext.symbolTable.referenceProperty(it) } +) : AbstractSerialGenerator(null, irClass.descriptor), IrBuilderExtension { + protected val serializableIrClass = getSerializableClassDescriptorBySerializer(irClass)!! + + protected val serialName: String = serializableIrClass.serialName() + protected val properties = bindingContext.serializablePropertiesForIrBackend(serializableIrClass, metadataPlugin) + protected val serializableProperties = properties.serializableProperties + protected val isGeneratedSerializer = irClass.descriptor.typeConstructor.supertypes.any(::isGeneratedKSerializer) // TODO TODO + + protected val generatedSerialDescPropertyDescriptor = getProperty( + SerialEntityNames.SERIAL_DESC_FIELD, + { true } + )?.takeIf { it.origin == IrDeclarationOrigin.GeneratedByPlugin(SerializationPluginKey) } + + protected val anySerialDescProperty = getProperty( + SerialEntityNames.SERIAL_DESC_FIELD, + ) { true } // TODO REMOVE TRUE + + protected val irAnySerialDescProperty = anySerialDescProperty + + fun getProperty( + name: String, + isReturnTypeOk: (IrProperty) -> Boolean + ): IrProperty? { + return irClass.properties.singleOrNull { it.name.asString() == name && isReturnTypeOk(it) } + } + + var localSerializersFieldsDescriptors: List> = emptyList() + protected set + protected fun findLocalSerializersFieldDescriptors(): List { + val count = serializableIrClass.typeParameters.size + if (count == 0) return emptyList() + val propNames = (0 until count).map { "${SerialEntityNames.typeArgPrefix}$it" } + return propNames.mapNotNull { name -> + getProperty(name) { isKSerializer(it.getter!!.returnType) } + } + } - protected open val serialDescImplClass: ClassDescriptor = serializerDescriptor - .getClassFromInternalSerializationPackage(SERIAL_DESCRIPTOR_CLASS_IMPL) + protected open val serialDescImplClass: IrClassSymbol = + compilerContext.getClassFromInternalSerializationPackage(SerialEntityNames.SERIAL_DESCRIPTOR_CLASS_IMPL) - override fun generateSerialDesc() { - val desc: PropertyDescriptor = generatedSerialDescPropertyDescriptor ?: return - val addFuncS = serialDescImplClass.referenceFunctionSymbol(CallingConventions.addElement) + fun generateSerialDesc() { + val desc = generatedSerialDescPropertyDescriptor ?: return + val addFuncS = serialDescImplClass.functionByName(CallingConventions.addElement) val thisAsReceiverParameter = irClass.thisReceiver!! lateinit var prop: IrProperty // how to (auto)create backing field and getter/setter? compilerContext.symbolTable.withReferenceScope(irClass) { - prop = generateSimplePropertyWithBackingField(desc, irClass) + prop = generateSimplePropertyWithBackingField(desc.descriptor, irClass) // TODO check if this works correctly with old FE // TODO: Do not use descriptors here - localSerializersFieldsDescriptors = findLocalSerializersFieldDescriptors().map { descriptor -> - descriptor to generateSimplePropertyWithBackingField(descriptor, irClass) + localSerializersFieldsDescriptors = findLocalSerializersFieldDescriptors().map { prop -> + prop.descriptor to generateSimplePropertyWithBackingField(prop.descriptor, irClass) } } @@ -97,14 +130,14 @@ open class SerializerIrGenerator( copySerialInfoAnnotationsToDescriptor( collectSerialInfoAnnotations(serializableIrClass), localDesc, - serialDescImplClass.referenceFunctionSymbol(CallingConventions.addClassAnnotation) + serialDescImplClass.functionByName(CallingConventions.addClassAnnotation) ) // save local descriptor to field +irSetField( generateReceiverExpressionForFieldAccess( thisAsReceiverParameter.symbol, - generatedSerialDescPropertyDescriptor + generatedSerialDescPropertyDescriptor.descriptor ), prop.backingField!!, irGet(localDesc) @@ -115,11 +148,11 @@ open class SerializerIrGenerator( } protected open fun IrBlockBodyBuilder.instantiateNewDescriptor( - serialDescImplClass: ClassDescriptor, + serialDescImplClass: IrClassSymbol, correctThis: IrExpression ): IrExpression { - val classConstructors = compilerContext.referenceConstructors(serialDescImplClass.fqNameSafe) - val serialClassDescImplCtor = classConstructors.single { it.owner.isPrimary } +// val classConstructors = compilerContext.referenceConstructors(serialDescImplClass.fqNameSafe) + val serialClassDescImplCtor = serialDescImplClass.constructors.single { it.owner.isPrimary } return irInvoke( null, serialClassDescImplCtor, irString(serialName), if (isGeneratedSerializer) correctThis else irNull(), irInt(serializableProperties.size) @@ -127,11 +160,11 @@ open class SerializerIrGenerator( } protected open fun IrBlockBodyBuilder.addElementsContentToDescriptor( - serialDescImplClass: ClassDescriptor, + serialDescImplClass: IrClassSymbol, localDescriptor: IrVariable, addFunction: IrFunctionSymbol ) { - fun addFieldCall(prop: SerializableProperty) = irInvoke( + fun addFieldCall(prop: IrSerializableProperty) = irInvoke( irGet(localDescriptor), addFunction, irString(prop.name), @@ -143,11 +176,11 @@ open class SerializerIrGenerator( if (classProp.transient) continue +addFieldCall(classProp) // add property annotations - val property = classProp.getIrPropertyFrom(serializableIrClass) + val property = classProp.descriptor//.getIrPropertyFrom(serializableIrClass) copySerialInfoAnnotationsToDescriptor( property.annotations, localDescriptor, - serialDescImplClass.referenceFunctionSymbol(CallingConventions.addAnnotation) + serialDescImplClass.functionByName(CallingConventions.addAnnotation) ) } } @@ -162,11 +195,10 @@ open class SerializerIrGenerator( } } - override fun generateGenericFieldsAndConstructor(typedConstructorDescriptor: ClassConstructorDescriptor) = - irClass.contributeConstructor(typedConstructorDescriptor) { ctor -> + fun generateGenericFieldsAndConstructor(typedConstructorDescriptor: IrConstructor) = + addFunctionBody(typedConstructorDescriptor) { ctor -> // generate call to primary ctor to init serialClassDesc and super() - val primaryCtor = irClass.constructors.find { it.isPrimary } - ?: throw AssertionError("Serializer class must have primary constructor") + val primaryCtor = irClass.constructors.primary +IrDelegatingConstructorCallImpl.fromSymbolDescriptor( startOffset, endOffset, @@ -191,7 +223,7 @@ open class SerializerIrGenerator( } } - override fun generateChildSerializersGetter(function: FunctionDescriptor) = irClass.contributeFunction(function) { irFun -> + open fun generateChildSerializersGetter(function: IrSimpleFunction) = addFunctionBody(function) { irFun -> val allSerializers = serializableProperties.map { requireNotNull( serializerTower(this@SerializerIrGenerator, irFun.dispatchReceiverParameter!!, it) @@ -203,8 +235,8 @@ open class SerializerIrGenerator( +irReturn(array) } - override fun generateTypeParamsSerializersGetter(function: FunctionDescriptor) = irClass.contributeFunction(function) { irFun -> - val typeParams = serializableDescriptor.declaredTypeParameters.mapIndexed { idx, _ -> + open fun generateTypeParamsSerializersGetter(function: IrSimpleFunction) = addFunctionBody(function) { irFun -> + val typeParams = serializableIrClass.typeParameters.mapIndexed { idx, _ -> irGetField( irGet(irFun.dispatchReceiverParameter!!), localSerializersFieldsDescriptors[idx].second.backingField!! @@ -215,26 +247,26 @@ open class SerializerIrGenerator( +irReturn(array) } - override fun generateSerializableClassProperty(property: PropertyDescriptor) { + open fun generateSerializableClassProperty(property: IrProperty) { /* Already implemented in .generateSerialClassDesc ? */ } - override fun generateSave(function: FunctionDescriptor) = irClass.contributeFunction(function) { saveFunc -> + open fun generateSave(function: IrSimpleFunction) = addFunctionBody(function) { saveFunc -> fun irThis(): IrExpression = IrGetValueImpl(startOffset, endOffset, saveFunc.dispatchReceiverParameter!!.symbol) - val kOutputClass = serializerDescriptor.getClassFromSerializationPackage(STRUCTURE_ENCODER_CLASS) - val encoderClass = serializerDescriptor.getClassFromSerializationPackage(ENCODER_CLASS) + val kOutputClass = compilerContext.getClassFromRuntime(STRUCTURE_ENCODER_CLASS) + val encoderClass = compilerContext.getClassFromRuntime(ENCODER_CLASS) - val descriptorGetterSymbol = irAnySerialDescProperty?.owner?.getter!!.symbol + val descriptorGetterSymbol = irAnySerialDescProperty?.getter!!.symbol val localSerialDesc = irTemporary(irGet(descriptorGetterSymbol.owner.returnType, irThis(), descriptorGetterSymbol), "desc") // fun beginStructure(desc: SerialDescriptor, vararg typeParams: KSerializer<*>): StructureEncoder - val beginFunc = encoderClass.referenceFunctionSymbol(CallingConventions.begin) { it.valueParameters.size == 1 } + val beginFunc = encoderClass.functions.single { it.owner.name.asString() == CallingConventions.begin && it.owner.valueParameters.size == 1 } - val call = irCall(beginFunc, type = kOutputClass.defaultType.toIrType()).mapValueParametersIndexed { _, _ -> + val call = irCall(beginFunc, type = kOutputClass.defaultType).mapValueParametersIndexed { _, _ -> irGet(localSerialDesc) } // can it be done in more concise way? e.g. additional builder function? @@ -274,16 +306,16 @@ open class SerializerIrGenerator( } // output.writeEnd(serialClassDesc) - val wEndFunc = kOutputClass.referenceFunctionSymbol(CallingConventions.end) + val wEndFunc = kOutputClass.functionByName(CallingConventions.end) +irInvoke(irGet(localOutput), wEndFunc, irGet(localSerialDesc)) } protected fun IrBlockBodyBuilder.formEncodeDecodePropertyCall( encoder: IrExpression, dispatchReceiver: IrValueParameter, - property: SerializableProperty, - whenHaveSerializer: (serializer: IrExpression, sti: SerialTypeInfo) -> FunctionWithArgs, - whenDoNot: (sti: SerialTypeInfo) -> FunctionWithArgs, + property: IrSerializableProperty, + whenHaveSerializer: (serializer: IrExpression, sti: IrSerialTypeInfo) -> FunctionWithArgs, + whenDoNot: (sti: IrSerialTypeInfo) -> FunctionWithArgs, returnTypeHint: IrType? = null ): IrExpression = formEncodeDecodePropertyCall( this@SerializerIrGenerator, @@ -299,9 +331,8 @@ open class SerializerIrGenerator( ) // returns null: Any? for boxed types and 0: for primitives - private fun IrBuilderWithScope.defaultValueAndType(descriptor: PropertyDescriptor): Pair { - val kType = descriptor.returnType!! - val T = kType.toIrType() + private fun IrBuilderWithScope.defaultValueAndType(descriptor: IrProperty): Pair { + val T = descriptor.getter!!.returnType val defaultPrimitive: IrExpression? = if (T.isMarkedNullable()) null else when (T.getPrimitiveType()) { @@ -321,9 +352,9 @@ open class SerializerIrGenerator( defaultPrimitive to T } - override fun generateLoad(function: FunctionDescriptor) = irClass.contributeFunction(function) { loadFunc -> - if (serializableDescriptor.modality == Modality.ABSTRACT || serializableDescriptor.modality == Modality.SEALED) { - return@contributeFunction + open fun generateLoad(function: IrSimpleFunction) = addFunctionBody(function) { loadFunc -> + if (serializableIrClass.modality == Modality.ABSTRACT || serializableIrClass.modality == Modality.SEALED) { + return@addFunctionBody } fun irThis(): IrExpression = @@ -331,9 +362,9 @@ open class SerializerIrGenerator( fun IrVariable.get() = irGet(this) - val inputClass = serializerDescriptor.getClassFromSerializationPackage(STRUCTURE_DECODER_CLASS) - val decoderClass = serializerDescriptor.getClassFromSerializationPackage(DECODER_CLASS) - val descriptorGetterSymbol = irAnySerialDescProperty?.owner?.getter!!.symbol + val inputClass = compilerContext.getClassFromRuntime(STRUCTURE_DECODER_CLASS) + val decoderClass = compilerContext.getClassFromRuntime(DECODER_CLASS) + val descriptorGetterSymbol = irAnySerialDescProperty?.getter!!.symbol val localSerialDesc = irTemporary(irGet(descriptorGetterSymbol.owner.returnType, irThis(), descriptorGetterSymbol), "desc") // workaround due to unavailability of labels (KT-25386) @@ -350,7 +381,7 @@ open class SerializerIrGenerator( val transients = serializableIrClass.declarations.asSequence() .filterIsInstance() - .filter { !serialPropertiesIndexes.containsKey(it.descriptor) } + .filter { !serialPropertiesIndexes.contains(it) } .filter { it.backingField != null } // var bitMask0 = 0, bitMask1 = 0... @@ -361,18 +392,18 @@ open class SerializerIrGenerator( descriptor to irTemporary(expr, "local$i", type, isMutable = true) } // var transient0 = null, transient0 = null ... - val transientsPropertiesMap = transients.mapIndexed { i, prop -> i to prop.descriptor }.associate { (i, descriptor) -> + val transientsPropertiesMap = transients.mapIndexed { i, prop -> i to prop }.associate { (i, descriptor) -> val (expr, type) = defaultValueAndType(descriptor) descriptor to irTemporary(expr, "transient$i", type, isMutable = true) } //input = input.beginStructure(...) - val beginFunc = decoderClass.referenceFunctionSymbol(CallingConventions.begin) { it.valueParameters.size == 1 } + val beginFunc = decoderClass.functions.single { it.owner.name.asString() == CallingConventions.begin && it.owner.valueParameters.size == 1 } val call = irInvoke( irGet(loadFunc.valueParameters[0]), beginFunc, irGet(localSerialDesc), - typeHint = inputClass.defaultType.toIrType() + typeHint = inputClass.defaultType ) val localInput = irTemporary(call, "input") @@ -380,19 +411,20 @@ open class SerializerIrGenerator( val decoderCalls: List> = serializableProperties.mapIndexed { index, property -> val body = irBlock { - val decodeFuncToCall = formEncodeDecodePropertyCall(localInput.get(), loadFunc.dispatchReceiverParameter!!, property, {innerSerial, sti -> - inputClass.referenceFunctionSymbol( - "${CallingConventions.decode}${sti.elementMethodPrefix}Serializable${CallingConventions.elementPostfix}", {it.valueParameters.size == 4} - ) to listOf( - localSerialDesc.get(), irInt(index), innerSerial, serialPropertiesMap.getValue(property.descriptor).get() - ) - }, { - inputClass.referenceFunctionSymbol( - "${CallingConventions.decode}${it.elementMethodPrefix}${CallingConventions.elementPostfix}", {it.valueParameters.size == 2} - ) to listOf( - localSerialDesc.get(), irInt(index) - ) - }, returnTypeHint = property.type.toIrType()) + val decodeFuncToCall = + formEncodeDecodePropertyCall(localInput.get(), loadFunc.dispatchReceiverParameter!!, property, { innerSerial, sti -> + inputClass.functions.single { + it.owner.name.asString() == "${CallingConventions.decode}${sti.elementMethodPrefix}Serializable${CallingConventions.elementPostfix}" && + it.owner.valueParameters.size == 4 + } to listOf( + localSerialDesc.get(), irInt(index), innerSerial, serialPropertiesMap.getValue(property.descriptor).get() + ) + }, {sti -> + inputClass.functions.single { + it.owner.name.asString() == "${CallingConventions.decode}${sti.elementMethodPrefix}${CallingConventions.elementPostfix}" && + it.owner.valueParameters.size == 2 + } to listOf(localSerialDesc.get(), irInt(index)) + }, returnTypeHint = property.type) // local$i = localInput.decode...(...) +irSet( serialPropertiesMap.getValue(property.descriptor).symbol, @@ -407,7 +439,7 @@ open class SerializerIrGenerator( } // if (decoder.decodeSequentially()) - val decodeSequentiallyCall = irInvoke(localInput.get(), inputClass.referenceFunctionSymbol(CallingConventions.decodeSequentially)) + val decodeSequentiallyCall = irInvoke(localInput.get(), inputClass.functionByName(CallingConventions.decodeSequentially)) val sequentialPart = irBlock { decoderCalls.forEach { (_, expr) -> +expr.deepCopyWithVariables() } @@ -416,7 +448,7 @@ open class SerializerIrGenerator( val byIndexPart: IrExpression = irWhile().also { loop -> loop.condition = flagVar.get() loop.body = irBlock { - val readElementF = inputClass.referenceFunctionSymbol(CallingConventions.decodeElementIndex) + val readElementF = inputClass.functionByName(CallingConventions.decodeElementIndex) +irSet(indexVar.symbol, irInvoke(localInput.get(), readElementF, localSerialDesc.get())) +irWhen { // if index == -1 (READ_DONE) break loop @@ -445,7 +477,7 @@ open class SerializerIrGenerator( +irIfThenElse(compilerContext.irBuiltIns.unitType, decodeSequentiallyCall, sequentialPart, byIndexPart) //input.endStructure(...) - val endFunc = inputClass.referenceFunctionSymbol(CallingConventions.end) + val endFunc = inputClass.functionByName(CallingConventions.end) +irInvoke( localInput.get(), endFunc, @@ -453,16 +485,16 @@ open class SerializerIrGenerator( ) val typeArgs = (loadFunc.returnType as IrSimpleType).arguments.map { (it as IrTypeProjection).type } - if (serializableDescriptor.isInternalSerializable) { + if (serializableIrClass.isInternalSerializable) { var args: List = serializableProperties.map { serialPropertiesMap.getValue(it.descriptor).get() } args = bitMasks.map { irGet(it) } + args + irNull() val ctor: IrConstructorSymbol = serializableSyntheticConstructor(serializableIrClass) +irReturn(irInvoke(null, ctor, typeArgs, args)) } else { - if (DescriptorUtils.isLocal(serializerDescriptor)) { + if (irClass.isLocal) { // if the serializer is local, then the serializable class too, since they must be in the same scope throw CompilationException( - "External serializer class `${serializerDescriptor.fqNameSafe}` is local. Local external serializers are not supported yet.", + "External serializer class `${irClass.fqNameWhenAvailable}` is local. Local external serializers are not supported yet.", null, null ) @@ -470,13 +502,11 @@ open class SerializerIrGenerator( generateGoldenMaskCheck(bitMasks, properties, localSerialDesc.get()) - val ctor: IrConstructorSymbol = - compilerContext.referenceConstructors(serializableDescriptor.fqNameSafe).single { it.owner.isPrimary } + val ctor: IrConstructorSymbol = serializableIrClass.constructors.primary.symbol val params = ctor.owner.valueParameters - - val variableByParamReplacer: (ValueParameterDescriptor) -> IrExpression? = { - val propertyDescriptor = bindingContext[BindingContext.VALUE_PARAMETER_AS_PROPERTY, it] + val variableByParamReplacer: (ValueParameterDescriptor) -> IrExpression? = { vpd -> + val propertyDescriptor = serializableIrClass.properties.find { it.name == vpd.name } if (propertyDescriptor != null) { val serializable = serialPropertiesMap[propertyDescriptor] (serializable ?: transientsPropertiesMap[propertyDescriptor])?.get() @@ -489,8 +519,8 @@ open class SerializerIrGenerator( // constructor args: val ctorArgs = params.map { parameter -> - val parameterDescriptor = parameter.descriptor as ValueParameterDescriptor - val propertyDescriptor = bindingContext[BindingContext.VALUE_PARAMETER_AS_PROPERTY, parameterDescriptor]!! + val propertyDescriptor = serializableIrClass.properties.find { it.name == parameter.name }!! // todo: check with tests + val parameterDescriptor = parameter.descriptor as ValueParameterDescriptor // TODO: remove descriptor here val serialProperty = serialPropertiesMap[propertyDescriptor] // null if transient @@ -532,14 +562,14 @@ open class SerializerIrGenerator( private fun IrBlockBodyBuilder.generateSetStandaloneProperties( serializableVar: IrVariable, - propVars: (PropertyDescriptor) -> IrVariable, - propIndexes: (PropertyDescriptor) -> Int, + propVars: (IrProperty) -> IrVariable, + propIndexes: (IrProperty) -> Int, bitMasks: List ) { for (property in properties.serializableStandaloneProperties) { val localPropIndex = propIndexes(property.descriptor) // generate setter call - val setter = property.getIrPropertyFrom(serializableIrClass).setter!! + val setter = property.descriptor.setter!! val propSeenTest = irNotEquals( irInt(0), @@ -556,21 +586,46 @@ open class SerializerIrGenerator( } } + // !!! TODO: this doesn't work with OLD FE !!! + fun generate() { + val prop = generatedSerialDescPropertyDescriptor?.let { generateSerializableClassProperty(it); true } ?: false + if (prop) + generateSerialDesc() + val save = irClass.findPluginGeneratedMethod(SAVE)?.let { generateSave(it); true } ?: false + val load = irClass.findPluginGeneratedMethod(SAVE)?.let { generateSave(it); true } ?: false + irClass.findPluginGeneratedMethod(SerialEntityNames.CHILD_SERIALIZERS_GETTER.identifier)?.let { generateChildSerializersGetter(it) } + irClass.findPluginGeneratedMethod(SerialEntityNames.TYPE_PARAMS_SERIALIZERS_GETTER.identifier)?.let { generateTypeParamsSerializersGetter(it) } + if (!prop && (save || load)) + generateSerialDesc() + if (serializableIrClass.typeParameters.isNotEmpty()) { + findSerializerConstructorForTypeArgumentsSerializers(irClass)?.let { + generateGenericFieldsAndConstructor(it.owner) + } + } + } + + + companion object { fun generate( irClass: IrClass, context: SerializationPluginContext, - bindingContext: BindingContext, metadataPlugin: SerializationDescriptorSerializerPlugin?, serialInfoJvmGenerator: SerialInfoImplJvmIrGenerator, ) { val serializableDesc = getSerializableClassDescriptorBySerializer(irClass.symbol.descriptor) ?: return val generator = when { - serializableDesc.isEnumWithLegacyGeneratedSerializer() -> SerializerForEnumsGenerator(irClass, context, bindingContext, serialInfoJvmGenerator) - serializableDesc.isInlineClass() -> SerializerForInlineClassGenerator(irClass, context, bindingContext, serialInfoJvmGenerator) - else -> SerializerIrGenerator(irClass, context, bindingContext, metadataPlugin, serialInfoJvmGenerator) + serializableDesc.isEnumWithLegacyGeneratedSerializer() -> SerializerForEnumsGenerator( + irClass, + context, + serialInfoJvmGenerator + ) + serializableDesc.isInlineClass() -> SerializerForInlineClassGenerator(irClass, context, serialInfoJvmGenerator) + else -> SerializerIrGenerator(irClass, context, metadataPlugin, serialInfoJvmGenerator) } generator.generate() + val declaration = irClass.constructors.primary // todo: move to appropriate place + if (declaration.body == null) declaration.body = context.generateBodyForDefaultConstructor(declaration) irClass.patchDeclarationParents(irClass.parent) } } diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/diagnostic/VersionReader.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/diagnostic/VersionReader.kt index 529952317c3..6342d2c7f45 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/diagnostic/VersionReader.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/diagnostic/VersionReader.kt @@ -51,8 +51,8 @@ object VersionReader { return versions } - fun getVersionsForCurrentModuleFromContext(module: ModuleDescriptor, context: BindingContext): RuntimeVersions? { - context.get(VERSIONS_SLICE, module)?.let { return it } + fun getVersionsForCurrentModuleFromContext(module: ModuleDescriptor, context: BindingContext?): RuntimeVersions? { + context?.get(VERSIONS_SLICE, module)?.let { return it } return getVersionsForCurrentModule(module) } diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/extensions/SerializationLoweringExtension.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/extensions/SerializationLoweringExtension.kt index bf186de2983..acb58450ca6 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/extensions/SerializationLoweringExtension.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/extensions/SerializationLoweringExtension.kt @@ -56,9 +56,9 @@ private class SerializerClassLowering( private val serialInfoJvmGenerator = SerialInfoImplJvmIrGenerator(context, moduleFragment).also { context.serialInfoImplJvmIrGenerator = it } override fun lower(irClass: IrClass) { - SerializableIrGenerator.generate(irClass, context, context.bindingContext) - SerializerIrGenerator.generate(irClass, context, context.bindingContext, context.metadataPlugin, serialInfoJvmGenerator) - SerializableCompanionIrGenerator.generate(irClass, context, context.bindingContext) + SerializableIrGenerator.generate(irClass, context) + SerializerIrGenerator.generate(irClass, context, context.metadataPlugin, serialInfoJvmGenerator) + SerializableCompanionIrGenerator.generate(irClass, context) if (context.platform.isJvm() && KSerializerDescriptorResolver.isSerialInfoImpl(irClass.descriptor)) { serialInfoJvmGenerator.generate(irClass) diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/fir/FirBuildersUtils.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/fir/FirBuildersUtils.kt index b2cf5018a04..fd5c3c2fa65 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/fir/FirBuildersUtils.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/fir/FirBuildersUtils.kt @@ -34,7 +34,7 @@ import org.jetbrains.kotlin.name.CallableId import org.jetbrains.kotlin.name.ClassId -// FIXME: this has to be shared (copied from plugin example) +// FIXME KT-53096: this has to be shared (copied from plugin example) @OptIn(SymbolInternals::class) fun FirDeclarationGenerationExtension.buildConstructor(classId: ClassId, isInner: Boolean, key: GeneratedDeclarationKey): FirConstructor { val lookupTag = ConeClassLikeLookupTagImpl(classId) diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/fir/SerializationFirResolveExtension.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/fir/SerializationFirResolveExtension.kt index 4d7ab279a49..f8c8082ad61 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/fir/SerializationFirResolveExtension.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/fir/SerializationFirResolveExtension.kt @@ -212,7 +212,7 @@ class SerializationFirResolveExtension(session: FirSession) : FirDeclarationGene return f.symbol } - @OptIn(SymbolInternals::class) + @OptIn(SymbolInternals::class) // TODO: localSerializersFieldDescriptors override fun generateProperties(callableId: CallableId, context: MemberGenerationContext?): List { val owner = context?.owner ?: return emptyList() if (owner.name != SerialEntityNames.SERIALIZER_CLASS_NAME) return emptyList() diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/NamingConventions.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/NamingConventions.kt index abaa4246299..a23c64702c2 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/NamingConventions.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/NamingConventions.kt @@ -5,6 +5,8 @@ package org.jetbrains.kotlinx.serialization.compiler.resolve +import org.jetbrains.kotlin.name.CallableId +import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name @@ -110,6 +112,7 @@ object SerialEntityNames { internal const val typeArgPrefix = "typeSerial" internal val wrapIntoNullableExt = SerializationPackages.builtinsPackageFqName.child(Name.identifier("nullable")) + internal val wrapIntoNullableCallableId = CallableId(SerializationPackages.builtinsPackageFqName, Name.identifier("nullable")) } object SpecialBuiltins { diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/SerializableProperties.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/SerializableProperties.kt index c963820cdfd..5edff32aed9 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/SerializableProperties.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/SerializableProperties.kt @@ -6,6 +6,15 @@ package org.jetbrains.kotlinx.serialization.compiler.resolve import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.ir.declarations.IrClass +import org.jetbrains.kotlin.ir.declarations.IrProperty +import org.jetbrains.kotlin.ir.declarations.lazy.IrMaybeDeserializedClass +import org.jetbrains.kotlin.ir.symbols.IrClassSymbol +import org.jetbrains.kotlin.ir.types.IrSimpleType +import org.jetbrains.kotlin.ir.types.IrType +import org.jetbrains.kotlin.ir.types.classOrNull +import org.jetbrains.kotlin.ir.types.isAny +import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.psi.KtDeclarationWithInitializer import org.jetbrains.kotlin.psi.KtParameter @@ -17,25 +26,43 @@ import org.jetbrains.kotlin.resolve.source.getPsi import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedClassDescriptor import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedPropertyDescriptor import org.jetbrains.kotlin.serialization.deserialization.getName +import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.types.checker.SimpleClassicTypeSystemContext.isInterface +import org.jetbrains.kotlinx.serialization.compiler.backend.common.isInternalSerializable +import org.jetbrains.kotlinx.serialization.compiler.backend.common.isInternallySerializableEnum import org.jetbrains.kotlinx.serialization.compiler.diagnostic.SERIALIZABLE_PROPERTIES import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationDescriptorSerializerPlugin import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPluginMetadataExtensions -class SerializableProperties(private val serializableClass: ClassDescriptor, val bindingContext: BindingContext) { +interface ISerializableProperties> { + val serializableProperties: List + val isExternallySerializable: Boolean + val serializableConstructorProperties: List + val serializableStandaloneProperties: List +} + +class SerializableProperties(private val serializableClass: ClassDescriptor, val bindingContext: BindingContext?) : + ISerializableProperties { private val primaryConstructorParameters: List = serializableClass.unsubstitutedPrimaryConstructor?.valueParameters ?: emptyList() - val serializableProperties: List - val isExternallySerializable: Boolean + override val serializableProperties: List + override val isExternallySerializable: Boolean private val primaryConstructorProperties: Map init { val descriptorsSequence = serializableClass.unsubstitutedMemberScope.getContributedDescriptors(DescriptorKindFilter.VARIABLES) .asSequence() // call to any BindingContext.get should be only AFTER MemberScope.getContributedDescriptors + // TODO: fix binding context shit primaryConstructorProperties = primaryConstructorParameters.asSequence() - .map { parameter -> bindingContext[BindingContext.VALUE_PARAMETER_AS_PROPERTY, parameter] to parameter.declaresDefaultValue() } + .map { parameter -> + bindingContext?.get( + BindingContext.VALUE_PARAMETER_AS_PROPERTY, + parameter + ) to parameter.declaresDefaultValue() + } .mapNotNull { (a, b) -> if (a == null) null else a to b } .toMap() @@ -56,7 +83,8 @@ class SerializableProperties(private val serializableClass: ClassDescriptor, val prop.hasBackingField(bindingContext) || (prop is DeserializedPropertyDescriptor && prop.backingField != null) // workaround for TODO in .hasBackingField // workaround for overridden getter (val) and getter+setter (var) - in this case hasBackingField returning false // but initializer presents only for property with backing field - || declaresDefaultValue, + || declaresDefaultValue + || prop.backingField != null, // todo: find out what happens next declaresDefaultValue ) } @@ -76,12 +104,12 @@ class SerializableProperties(private val serializableClass: ClassDescriptor, val } - val serializableConstructorProperties: List = + override val serializableConstructorProperties: List = serializableProperties.asSequence() .filter { primaryConstructorProperties.contains(it.descriptor) } .toList() - val serializableStandaloneProperties: List = + override val serializableStandaloneProperties: List = serializableProperties.minus(serializableConstructorProperties) val size = serializableProperties.size @@ -92,7 +120,7 @@ class SerializableProperties(private val serializableClass: ClassDescriptor, val ?.original?.valueParameters?.any { it.declaresDefaultValue() } ?: false } -fun PropertyDescriptor.declaresDefaultValue(): Boolean{ +fun PropertyDescriptor.declaresDefaultValue(): Boolean { when (val declaration = this.source.getPsi()) { is KtDeclarationWithInitializer -> return declaration.initializer != null is KtParameter -> return declaration.defaultValue != null @@ -112,7 +140,7 @@ fun PropertyDescriptor.declaresDefaultValue(): Boolean{ } -internal val SerializableProperties.goldenMask: Int +internal val ISerializableProperties<*, *, *>.goldenMask: Int get() { var goldenMask = 0 var requiredBit = 1 @@ -125,7 +153,7 @@ internal val SerializableProperties.goldenMask: Int return goldenMask } -internal val SerializableProperties.goldenMaskList: List +internal val ISerializableProperties<*, *, *>.goldenMaskList: List get() { val maskSlotCount = serializableProperties.bitMaskSlotCount() val goldenMaskList = MutableList(maskSlotCount) { 0 } @@ -140,11 +168,14 @@ internal val SerializableProperties.goldenMaskList: List return goldenMaskList } -internal fun List.bitMaskSlotCount() = size / 32 + 1 +internal fun List>.bitMaskSlotCount() = size / 32 + 1 internal fun bitMaskSlotAt(propertyIndex: Int) = propertyIndex / 32 -internal fun BindingContext.serializablePropertiesFor(classDescriptor: ClassDescriptor, serializationDescriptorSerializer: SerializationDescriptorSerializerPlugin? = null): SerializableProperties { - val props = this.get(SERIALIZABLE_PROPERTIES, classDescriptor) ?: SerializableProperties(classDescriptor, this) +internal fun BindingContext?.serializablePropertiesFor( + classDescriptor: ClassDescriptor, + serializationDescriptorSerializer: SerializationDescriptorSerializerPlugin? = null +): SerializableProperties { + val props = this?.get(SERIALIZABLE_PROPERTIES, classDescriptor) ?: SerializableProperties(classDescriptor, this) serializationDescriptorSerializer?.putIfNeeded(classDescriptor, props) return props } @@ -156,3 +187,73 @@ private fun unsort(descriptor: ClassDescriptor, props: List, + override val isExternallySerializable: Boolean, + override val serializableConstructorProperties: List, + override val serializableStandaloneProperties: List +) : ISerializableProperties { +} + +internal fun BindingContext?.serializablePropertiesForIrBackend( + classDescriptor: IrClass, + serializationDescriptorSerializer: SerializationDescriptorSerializerPlugin? = null +): IrSerializableProperties { + if (this != null) { + // can work with old FE + TODO("Support this for old FE") + } else { + val properties = classDescriptor.properties.toList() + val primaryConstructorParams = classDescriptor.primaryConstructor?.valueParameters.orEmpty() + val primaryParamsAsProps = properties.associateBy { it.name }.let { namesMap -> + primaryConstructorParams.mapNotNull { + if (it.name !in namesMap) null else namesMap.getValue(it.name) to it.hasDefaultValue() + }.toMap() + } + + fun isPropSerializable(it: IrProperty) = + if (classDescriptor.isInternalSerializable) !it.annotations.hasAnnotation(SerializationAnnotations.serialTransientFqName) + else !DescriptorVisibilities.isPrivate(it.visibility) && ((it.isVar && !it.annotations.hasAnnotation(SerializationAnnotations.serialTransientFqName)) || primaryParamsAsProps.contains( + it + )) + + val (primaryCtorSerializableProps, bodySerializableProps) = properties + .filter { !it.isFakeOverride && !it.isDelegated } + .filter(::isPropSerializable) + .map { + val isConstructorParameterWithDefault = primaryParamsAsProps[it] ?: false + IrSerializableProperty( + it, + isConstructorParameterWithDefault, + it.backingField != null, + it.backingField?.initializer != null || isConstructorParameterWithDefault + ) + } + .partition { primaryParamsAsProps.contains(it.descriptor) } + + val serializableProps = run { + val supers = classDescriptor.getParentClassNotAny() + if (supers == null || !supers.isInternalSerializable) + primaryCtorSerializableProps + bodySerializableProps + else + serializablePropertiesForIrBackend( + supers, + serializationDescriptorSerializer + ).serializableProperties + primaryCtorSerializableProps + bodySerializableProps + } // todo: implement unsorting + + val isExternallySerializable = + classDescriptor.isInternallySerializableEnum() || primaryConstructorParams.size == primaryParamsAsProps.size + + return IrSerializableProperties(serializableProps, isExternallySerializable, primaryCtorSerializableProps, bodySerializableProps) + } +} + +fun IrClass.getParentClassNotAny(): IrClass? { + val parentClass = + superTypes + .mapNotNull { it.classOrNull?.owner } + .singleOrNull { it.kind == ClassKind.CLASS || it.kind == ClassKind.ENUM_CLASS } ?: return null + return if (parentClass.defaultType.isAny()) null else parentClass +} \ No newline at end of file diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/SerializableProperty.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/SerializableProperty.kt index c3e42649a48..3f53f4ebb3f 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/SerializableProperty.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/SerializableProperty.kt @@ -19,26 +19,63 @@ package org.jetbrains.kotlinx.serialization.compiler.resolve import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.PropertyDescriptor import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor +import org.jetbrains.kotlin.fir.scopes.impl.overrides +import org.jetbrains.kotlin.ir.declarations.IrProperty +import org.jetbrains.kotlin.ir.types.IrSimpleType +import org.jetbrains.kotlin.ir.types.IrType +import org.jetbrains.kotlin.ir.types.defaultType +import org.jetbrains.kotlin.ir.util.hasAnnotation +import org.jetbrains.kotlin.ir.util.module import org.jetbrains.kotlin.psi.KtDeclarationWithInitializer import org.jetbrains.kotlin.psi.KtParameter import org.jetbrains.kotlin.psi.ValueArgument import org.jetbrains.kotlin.resolve.descriptorUtil.module import org.jetbrains.kotlin.resolve.source.getPsi +import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.types.checker.SimpleClassicTypeSystemContext.asSimpleType import org.jetbrains.kotlinx.serialization.compiler.backend.common.analyzeSpecialSerializers +import org.jetbrains.kotlinx.serialization.compiler.backend.common.genericIndex +import org.jetbrains.kotlinx.serialization.compiler.backend.common.overridenSerializer +import org.jetbrains.kotlinx.serialization.compiler.backend.common.serialNameValue class SerializableProperty( - val descriptor: PropertyDescriptor, - val isConstructorParameterWithDefault: Boolean, + override val descriptor: PropertyDescriptor, + override val isConstructorParameterWithDefault: Boolean, hasBackingField: Boolean, declaresDefaultValue: Boolean -) { - val name = descriptor.annotations.serialNameValue ?: descriptor.name.asString() - val type = descriptor.type - val genericIndex = type.genericIndex +) : ISerializableProperty { + override val name = descriptor.annotations.serialNameValue ?: descriptor.name.asString() + override val type = descriptor.type + override val genericIndex = type.genericIndex val module = descriptor.module - val serializableWith = descriptor.serializableWith ?: analyzeSpecialSerializers(module, descriptor.annotations)?.defaultType - val optional = !descriptor.annotations.serialRequired && declaresDefaultValue - val transient = descriptor.annotations.serialTransient || !hasBackingField + override val serializableWith = descriptor.serializableWith ?: analyzeSpecialSerializers(module, descriptor.annotations)?.defaultType + override val optional = !descriptor.annotations.serialRequired && declaresDefaultValue + override val transient = descriptor.annotations.serialTransient || !hasBackingField val annotationsWithArguments: List, List>> = descriptor.annotationsWithArguments() } + +interface ISerializableProperty { + val descriptor: D + val isConstructorParameterWithDefault: Boolean + val name: String + val type: T + val genericIndex: Int? + val serializableWith: T? + val optional: Boolean + val transient: Boolean +} + +class IrSerializableProperty( + override val descriptor: IrProperty, + override val isConstructorParameterWithDefault: Boolean, + hasBackingField: Boolean, + declaresDefaultValue: Boolean +) : ISerializableProperty { + override val name = descriptor.annotations.serialNameValue ?: descriptor.name.asString() + override val type = descriptor.getter!!.returnType as IrSimpleType + override val genericIndex = type.genericIndex + override val serializableWith = type.overridenSerializer /* ?:analyzeSpecialSerializers(module, descriptor.annotations)?.defaultType */ // TODO + override val optional = !descriptor.annotations.hasAnnotation(SerializationAnnotations.requiredAnnotationFqName) && declaresDefaultValue + override val transient = descriptor.annotations.hasAnnotation(SerializationAnnotations.serialTransientFqName) || !hasBackingField +} \ No newline at end of file diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/test/org/jetbrains/kotlinx/serialization/AbstractSerializationFirMembersTest.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/test/org/jetbrains/kotlinx/serialization/AbstractSerializationFirMembersTest.kt index 5ba54386517..036c80310a3 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/test/org/jetbrains/kotlinx/serialization/AbstractSerializationFirMembersTest.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/test/org/jetbrains/kotlinx/serialization/AbstractSerializationFirMembersTest.kt @@ -8,17 +8,22 @@ package org.jetbrains.kotlinx.serialization import org.jetbrains.kotlin.fir.extensions.FirExtensionRegistrarAdapter import org.jetbrains.kotlin.test.TargetBackend import org.jetbrains.kotlin.test.backend.BlackBoxCodegenSuppressor -import org.jetbrains.kotlin.test.backend.handlers.* +import org.jetbrains.kotlin.test.backend.handlers.IrTextDumpHandler +import org.jetbrains.kotlin.test.backend.handlers.IrTreeVerifierHandler +import org.jetbrains.kotlin.test.backend.handlers.JvmBoxRunner import org.jetbrains.kotlin.test.backend.ir.JvmIrBackendFacade import org.jetbrains.kotlin.test.builders.TestConfigurationBuilder import org.jetbrains.kotlin.test.builders.fir2IrStep import org.jetbrains.kotlin.test.builders.irHandlersStep import org.jetbrains.kotlin.test.builders.jvmArtifactsHandlersStep import org.jetbrains.kotlin.test.directives.FirDiagnosticsDirectives +import org.jetbrains.kotlin.test.model.TestModule import org.jetbrains.kotlin.test.runners.AbstractKotlinCompilerTest import org.jetbrains.kotlin.test.runners.AbstractKotlinCompilerWithTargetBackendTest import org.jetbrains.kotlin.test.runners.baseFirDiagnosticTestConfiguration +import org.jetbrains.kotlin.test.services.RuntimeClasspathProvider import org.jetbrains.kotlinx.serialization.compiler.fir.FirSerializationExtensionRegistrar +import java.io.File abstract class AbstractSerializationFirMembersTest: AbstractKotlinCompilerTest() { override fun TestConfigurationBuilder.configuration() { @@ -38,6 +43,16 @@ abstract class AbstractSerializationFirMembersTest: AbstractKotlinCompilerTest() open class AbstractSerializationFirBlackBoxTest : AbstractKotlinCompilerWithTargetBackendTest(TargetBackend.JVM_IR) { override fun TestConfigurationBuilder.configuration() { baseFirDiagnosticTestConfiguration() + configureForKotlinxSerialization(listOf(getSerializationCoreLibraryJar()!!)) { + FirExtensionRegistrarAdapter.registerExtension(FirSerializationExtensionRegistrar()) + } + useCustomRuntimeClasspathProviders({ ts -> + object : RuntimeClasspathProvider(ts) { + override fun runtimeClassPaths(module: TestModule): List { + return listOf(getSerializationCoreLibraryJar()!!) + } + } + }) defaultDirectives { +FirDiagnosticsDirectives.ENABLE_PLUGIN_PHASES } @@ -55,9 +70,5 @@ open class AbstractSerializationFirBlackBoxTest : AbstractKotlinCompilerWithTarg } useAfterAnalysisCheckers(::BlackBoxCodegenSuppressor) - - configureForKotlinxSerialization(listOf(getSerializationCoreLibraryJar()!!)) { - FirExtensionRegistrarAdapter.registerExtension(FirSerializationExtensionRegistrar()) - } } }