diff --git a/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/IrBuilderWithPluginContext.kt b/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/IrBuilderWithPluginContext.kt index 152864802ed..9d56e20864b 100644 --- a/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/IrBuilderWithPluginContext.kt +++ b/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/IrBuilderWithPluginContext.kt @@ -367,20 +367,7 @@ interface IrBuilderWithPluginContext { val annotationClass = annotationCall.symbol.owner.parentAsClass if (!annotationClass.isSerialInfoAnnotation) return@mapNotNull null - if (compilerContext.platform.isJvm()) { - val implClass = compilerContext.serialInfoImplJvmIrGenerator.getImplClass(annotationClass) - val ctor = implClass.constructors.singleOrNull { it.valueParameters.size == annotationCall.valueArgumentsCount } - ?: error("No constructor args found for SerialInfo annotation Impl class: ${implClass.render()}") - irCall(ctor).apply { - for (i in 0 until annotationCall.valueArgumentsCount) { - val argument = annotationCall.getValueArgument(i) - ?: annotationClass.primaryConstructor!!.valueParameters[i].defaultValue?.expression - putValueArgument(i, argument!!.deepCopyWithVariables()) - } - } - } else { - annotationCall.deepCopyWithVariables() - } + annotationCall.deepCopyWithVariables() } @OptIn(ObsoleteDescriptorBasedAPI::class) @@ -400,4 +387,4 @@ interface IrBuilderWithPluginContext { } fun IrClass.getSuperClassOrAny(): IrClass = getSuperClassNotAny() ?: compilerContext.irBuiltIns.anyClass.owner -} \ No newline at end of file +} diff --git a/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/FirSerializationPredicates.kt b/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/FirSerializationPredicates.kt index 6e9f7b0775e..ca6c87ff8ac 100644 --- a/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/FirSerializationPredicates.kt +++ b/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/FirSerializationPredicates.kt @@ -5,9 +5,7 @@ package org.jetbrains.kotlinx.serialization.compiler.fir -import org.jetbrains.kotlin.fir.extensions.predicate.AnnotatedWith -import org.jetbrains.kotlin.fir.extensions.predicate.DeclarationPredicate -import org.jetbrains.kotlin.fir.extensions.predicate.ancestorAnnotated +import org.jetbrains.kotlin.fir.extensions.predicate.* import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationAnnotations object FirSerializationPredicates { @@ -15,5 +13,7 @@ object FirSerializationPredicates { AnnotatedWith(setOf(SerializationAnnotations.serializerAnnotationFqName)) // @Serializer(for=...) internal val generatedSerializer: DeclarationPredicate = ancestorAnnotated(SerializationAnnotations.serializableAnnotationFqName) // @Serializable X.$serializer - internal val annotatedWithSerializable = AnnotatedWith(setOf(SerializationAnnotations.serializableAnnotationFqName)) -} \ No newline at end of file + internal val hasMetaAnnotation = metaAnnotated(SerializationAnnotations.metaSerializableAnnotationFqName) + internal val annotatedWithSerializableOrMeta = + AnnotatedWith(setOf(SerializationAnnotations.serializableAnnotationFqName)) or metaAnnotated(SerializationAnnotations.metaSerializableAnnotationFqName) +} diff --git a/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/SerializationFirResolveExtension.kt b/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/SerializationFirResolveExtension.kt index c64f5ca4e7c..150f0e5d7cd 100644 --- a/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/SerializationFirResolveExtension.kt +++ b/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/SerializationFirResolveExtension.kt @@ -10,11 +10,13 @@ import org.jetbrains.kotlin.descriptors.EffectiveVisibility import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.descriptors.Visibilities import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.analysis.checkers.getContainingDeclarationSymbol import org.jetbrains.kotlin.fir.copy import org.jetbrains.kotlin.fir.declarations.FirDeclarationOrigin import org.jetbrains.kotlin.fir.declarations.builder.* import org.jetbrains.kotlin.fir.declarations.impl.FirResolvedDeclarationStatusImpl import org.jetbrains.kotlin.fir.declarations.origin +import org.jetbrains.kotlin.fir.declarations.utils.isCompanion import org.jetbrains.kotlin.fir.extensions.FirDeclarationGenerationExtension import org.jetbrains.kotlin.fir.extensions.FirDeclarationPredicateRegistrar import org.jetbrains.kotlin.fir.extensions.MemberGenerationContext @@ -55,15 +57,15 @@ class SerializationFirResolveExtension(session: FirSession) : FirDeclarationGene hasFactory && hasMarkedFactory } - internal val FirClassSymbol<*>.shouldHaveGeneratedSerializer: Boolean - get() = (isInternalSerializable && isFinalOrOpen()) || (classKind == ClassKind.ENUM_CLASS && hasSerializableAnnotationWithoutArgs && !runtimeHasEnumSerializerFactory) - override fun getNestedClassifiersNames(classSymbol: FirClassSymbol<*>): Set { val result = mutableSetOf() - if (classSymbol.shouldHaveGeneratedMethodsInCompanion && !classSymbol.isSerializableObject) - result += SpecialNames.DEFAULT_NAME_FOR_COMPANION_OBJECT - if (classSymbol.shouldHaveGeneratedSerializer /* TODO && !classSymbol.hasCompanionObjectAsSerializer*/) - result += SerialEntityNames.SERIALIZER_CLASS_NAME + with(session) { + if (classSymbol.shouldHaveGeneratedMethodsInCompanion && !classSymbol.isSerializableObject) + result += SpecialNames.DEFAULT_NAME_FOR_COMPANION_OBJECT + if (classSymbol.shouldHaveGeneratedSerializer(!runtimeHasEnumSerializerFactory) /* TODO && !classSymbol.hasCompanionObjectAsSerializer*/) + result += SerialEntityNames.SERIALIZER_CLASS_NAME + } + return result } @@ -115,7 +117,8 @@ class SerializationFirResolveExtension(session: FirSession) : FirDeclarationGene } } } - else -> if (classSymbol.isSerializableObject) result += SerialEntityNames.SERIALIZER_PROVIDER_NAME + + else -> if (with(session) { classSymbol.isSerializableObject }) result += SerialEntityNames.SERIALIZER_PROVIDER_NAME } return result } @@ -129,8 +132,7 @@ class SerializationFirResolveExtension(session: FirSession) : FirDeclarationGene useSiteSuperType.scopeForSupertype(session, scopeSession, owner.fir) } val targets = scopes.flatMap { extractor(it) } - val target = targets.singleOrNull() ?: error("Multiple overrides found for ${callableId.callableName}") - return target + return targets.singleOrNull() ?: error("Multiple overrides found for ${callableId.callableName}") } // TODO: support @Serializer(for) @@ -138,7 +140,14 @@ class SerializationFirResolveExtension(session: FirSession) : FirDeclarationGene override fun generateFunctions(callableId: CallableId, context: MemberGenerationContext?): List { val owner = context?.owner ?: return emptyList() if (callableId.callableName == SerialEntityNames.SERIALIZER_PROVIDER_NAME) { - val serializableClass = owner.getSerializableClassSymbolIfCompanion(session) ?: return emptyList() + val serializableClass = if (owner.isCompanion) { + val containingSymbol = owner.getContainingDeclarationSymbol(session) as? FirClassSymbol<*> ?: return emptyList() + if (with(session) { containingSymbol.shouldHaveGeneratedMethodsInCompanion }) containingSymbol else null + } else { + if (with(session) { owner.isSerializableObject }) owner else null + } + + serializableClass ?: return emptyList() return listOf(generateSerializerGetterInCompanion(owner, serializableClass, callableId)) } if (owner.name != SerialEntityNames.SERIALIZER_CLASS_NAME) return emptyList() @@ -228,7 +237,7 @@ class SerializationFirResolveExtension(session: FirSession) : FirDeclarationGene // FIXME: it seems that this list will always be used, why not provide it automatically? private val matchedClasses by lazy { - session.predicateBasedProvider.getSymbolsByPredicate(FirSerializationPredicates.annotatedWithSerializable) + session.predicateBasedProvider.getSymbolsByPredicate(FirSerializationPredicates.annotatedWithSerializableOrMeta) .filterIsInstance() } @@ -324,7 +333,7 @@ class SerializationFirResolveExtension(session: FirSession) : FirDeclarationGene name = SpecialNames.DEFAULT_NAME_FOR_COMPANION_OBJECT symbol = FirRegularClassSymbol(classId) superTypeRefs += session.builtinTypes.anyType - if (owner.companionNeedsSerializerFactory()) { + if (with(session) { owner.companionNeedsSerializerFactory }) { val serializerFactoryClassId = ClassId(SerializationPackages.internalPackageFqName, SERIALIZER_FACTORY_INTERFACE_NAME) superTypeRefs += serializerFactoryClassId.constructClassLikeType(emptyArray(), false).toFirResolvedTypeRef() } @@ -333,16 +342,20 @@ class SerializationFirResolveExtension(session: FirSession) : FirDeclarationGene } override fun FirDeclarationPredicateRegistrar.registerPredicates() { - register(FirSerializationPredicates.annotatedWithSerializable) + register(FirSerializationPredicates.annotatedWithSerializableOrMeta, FirSerializationPredicates.hasMetaAnnotation) } - private fun FirClassSymbol<*>.companionNeedsSerializerFactory(): Boolean { - if (!(moduleData.platform.isNative() || moduleData.platform.isJs())) return false - if (isSerializableObject) return true - if (isSerializableEnum) return true - if (isAbstractOrSealedSerializableClass) return true - if (isSealedSerializableInterface) return true - if (typeParameterSymbols.isEmpty()) return false - return true - } + context(FirSession) + @Suppress("IncorrectFormatting") // KTIJ-22227 + private val FirClassSymbol<*>.companionNeedsSerializerFactory: Boolean + get() { + if (!(moduleData.platform.isNative() || moduleData.platform.isJs())) return false + if (isSerializableObject) return true + if (isSerializableEnum) return true + if (isAbstractOrSealedSerializableClass) return true + if (isSealedSerializableInterface) return true + if (typeParameterSymbols.isEmpty()) return false + return true + } + } diff --git a/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/SerializationFirUtils.kt b/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/SerializationFirUtils.kt index 3591b7ff8e5..3da9b49f880 100644 --- a/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/SerializationFirUtils.kt +++ b/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/SerializationFirUtils.kt @@ -7,24 +7,18 @@ package org.jetbrains.kotlinx.serialization.compiler.fir import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.fir.FirSession -import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext -import org.jetbrains.kotlin.fir.analysis.checkers.getContainingDeclarationSymbol -import org.jetbrains.kotlin.fir.analysis.checkers.toRegularClassSymbol -import org.jetbrains.kotlin.fir.analysis.checkers.unsubstitutedScope import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.declarations.utils.* import org.jetbrains.kotlin.fir.expressions.FirAnnotation import org.jetbrains.kotlin.fir.expressions.FirAnnotationCall import org.jetbrains.kotlin.fir.expressions.arguments -import org.jetbrains.kotlin.fir.resolve.defaultType +import org.jetbrains.kotlin.fir.extensions.predicateBasedProvider import org.jetbrains.kotlin.fir.resolve.fullyExpandedType -import org.jetbrains.kotlin.fir.scopes.getSingleClassifier import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol import org.jetbrains.kotlin.fir.types.* import org.jetbrains.kotlin.name.Name -import org.jetbrains.kotlin.name.StandardClassIds import org.jetbrains.kotlinx.serialization.compiler.fir.services.dependencySerializationInfoProvider import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationAnnotations @@ -32,7 +26,6 @@ import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationAnnotat import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationAnnotations.metaSerializableAnnotationClassId import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationAnnotations.serialInfoClassId import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationAnnotations.serialNameAnnotationClassId -import org.jetbrains.kotlinx.serialization.compiler.resolve.SpecialBuiltins object AnnotationParameterNames { val VALUE = Name.identifier("value") @@ -65,10 +58,6 @@ val FirBasedSymbol<*>.hasSerialTransient: Boolean val FirBasedSymbol<*>.serialTransientAnnotation: FirAnnotation? get() = getAnnotationByClassId(SerializationAnnotations.serialTransientClassId) -context(CheckerContext) -val FirBasedSymbol<*>.hasAnySerialAnnotation: Boolean - get() = serialNameValue != null || resolvedAnnotationsWithClassIds.any { it.annotationClassSymbol?.isSerialInfoAnnotation == true } - val FirClassSymbol<*>.hasSerializableAnnotation: Boolean get() = serializableAnnotation(needArguments = false) != null @@ -90,81 +79,84 @@ val FirClassSymbol<*>.hasSerializableAnnotationWithoutArgs: Boolean } } ?: false -context(CheckerContext) -val FirClassSymbol<*>.hasSerializableOrMetaAnnotationWithoutArgs: Boolean - get() = hasSerializableAnnotationWithoutArgs || (!hasSerializableAnnotation && hasMetaSerializableAnnotation) - - -context(CheckerContext) -val FirClassSymbol<*>.hasSerializableOrMetaAnnotation - get() = hasSerializableAnnotation || hasMetaSerializableAnnotation - -context(CheckerContext) -val FirClassSymbol<*>.hasMetaSerializableAnnotation: Boolean - get() = metaSerializableAnnotation(needArguments = false) != null - -context(CheckerContext) -fun FirClassSymbol<*>.metaSerializableAnnotation(needArguments: Boolean): FirAnnotation? { - val annotations = if (needArguments) resolvedAnnotationsWithClassIds else resolvedAnnotationsWithArguments - return annotations.firstOrNull { it.isMetaSerializableAnnotation } -} - -context(CheckerContext) -val FirAnnotation.isMetaSerializableAnnotation: Boolean - get() = annotationClassSymbol?.hasAnnotation(metaSerializableAnnotationClassId) ?: false - -context(CheckerContext) -val ConeKotlinType.serializableWith: ConeKotlinType? - get() = customAnnotations.serializableWith ?: toRegularClassSymbol(session)?.serializableWith - internal val FirBasedSymbol<*>.serializableWith: ConeKotlinType? get() = serializableAnnotation(needArguments = true)?.getKClassArgument(AnnotationParameterNames.WITH) internal val List.serializableWith: ConeKotlinType? get() = serializableAnnotation()?.getKClassArgument(AnnotationParameterNames.WITH) +internal val FirClassSymbol<*>.serializerAnnotation: FirAnnotation? + get() = getAnnotationByClassId(SerializationAnnotations.serializerAnnotationClassId) + +// ---------------------- class utils ---------------------- internal val FirClassSymbol<*>.serializerForClass: ConeKotlinType? get() = resolvedAnnotationsWithArguments .getAnnotationByClassId(SerializationAnnotations.serializerAnnotationClassId) ?.getKClassArgument(AnnotationParameterNames.FOR_CLASS) -internal val FirClassSymbol<*>.serializerAnnotation: FirAnnotation? - get() = getAnnotationByClassId(SerializationAnnotations.serializerAnnotationClassId) - -context(CheckerContext) -private val FirAnnotation.annotationClassSymbol: FirRegularClassSymbol? - get() = annotationTypeRef.coneType - .fullyExpandedType(session) - .toRegularClassSymbol(session) - -// ---------------------- class utils ---------------------- - -internal val FirClassSymbol<*>.shouldHaveGeneratedMethodsInCompanion: Boolean - get() = this.isSerializableObject || this.isSerializableEnum || this.classKind == ClassKind.CLASS && hasSerializableAnnotation || this.isSealedSerializableInterface - -internal val FirClassSymbol<*>.isSerializableObject: Boolean - get() = classKind.isObject && hasSerializableAnnotation - +context(FirSession) +@Suppress("IncorrectFormatting") // KTIJ-22227 internal val FirClassSymbol<*>.isInternallySerializableObject: Boolean - get() = classKind.isObject && hasSerializableAnnotationWithoutArgs + get() = classKind.isObject && hasSerializableOrMetaAnnotationWithoutArgs +context(FirSession) +@Suppress("IncorrectFormatting") // KTIJ-22227 +internal val FirClassSymbol<*>.isSerializableObject: Boolean + get() = classKind.isObject && hasSerializableOrMetaAnnotation + +context(FirSession) +@Suppress("IncorrectFormatting") // KTIJ-22227 internal val FirClassSymbol<*>.isSealedSerializableInterface: Boolean - get() = classKind.isInterface && rawStatus.modality == Modality.SEALED && hasSerializableAnnotation + get() = classKind.isInterface && rawStatus.modality == Modality.SEALED && hasSerializableOrMetaAnnotation +context(FirSession) +val FirClassSymbol<*>.hasSerializableOrMetaAnnotation: Boolean + get() = hasSerializableAnnotation || hasMetaSerializableAnnotation + +context(FirSession) +val FirClassSymbol<*>.hasMetaSerializableAnnotation: Boolean + get() = predicateBasedProvider.matches(FirSerializationPredicates.hasMetaAnnotation, this) + +context(FirSession) +@Suppress("IncorrectFormatting") // KTIJ-22227 +internal val FirClassSymbol<*>.shouldHaveGeneratedMethodsInCompanion: Boolean + get() = isSerializableObject + || isSerializableEnum + || (classKind == ClassKind.CLASS && hasSerializableOrMetaAnnotation) + || isSealedSerializableInterface + +context(FirSession) +@Suppress("IncorrectFormatting") // KTIJ-22227 internal val FirClassSymbol<*>.isInternalSerializable: Boolean get() { if (!classKind.isClass) return false - return hasSerializableAnnotationWithoutArgs + return hasSerializableOrMetaAnnotationWithoutArgs } +context(FirSession) +val FirClassSymbol<*>.hasSerializableOrMetaAnnotationWithoutArgs: Boolean + get() = hasSerializableAnnotationWithoutArgs || (!hasSerializableAnnotation && hasMetaSerializableAnnotation) + +context(FirSession) +@Suppress("IncorrectFormatting") // KTIJ-22227 internal val FirClassSymbol<*>.isAbstractOrSealedSerializableClass: Boolean get() = isInternalSerializable && (rawStatus.modality == Modality.ABSTRACT || rawStatus.modality == Modality.SEALED) -internal val FirClassSymbol<*>.isInternallySerializableEnum: Boolean - get() = classKind.isEnumClass && hasSerializableAnnotationWithoutArgs - +/** + * Check that class is enum and marked by `Serializable` or meta-serializable annotation. + */ +context(FirSession) +@Suppress("IncorrectFormatting") // KTIJ-22227 internal val FirClassSymbol<*>.isSerializableEnum: Boolean - get() = classKind.isEnumClass && hasSerializableAnnotation + get() = classKind.isEnumClass && hasSerializableOrMetaAnnotation + +/** + * Check that class is enum and marked by `Serializable` annotation without serializer argument or meta-serializable annotation. + */ +context(FirSession) +@Suppress("IncorrectFormatting") // KTIJ-22227 +internal val FirClassSymbol<*>.isSerializableEnumWithoutArgs: Boolean + get() = classKind.isEnumClass && hasSerializableOrMetaAnnotationWithoutArgs internal fun FirClassSymbol<*>.isFinalOrOpen(): Boolean { val modality = rawStatus.modality @@ -172,75 +164,30 @@ internal fun FirClassSymbol<*>.isFinalOrOpen(): Boolean { return (modality == null || modality == Modality.FINAL || modality == Modality.OPEN) } -internal fun FirClassSymbol<*>.getSerializableClassSymbolIfCompanion(session: FirSession): FirClassSymbol<*>? { - if (isSerializableObject) return this - if (!isCompanion) return null - val classDescriptor = (getContainingDeclarationSymbol(session) as? FirClassSymbol<*>) ?: return null - if (!classDescriptor.shouldHaveGeneratedMethodsInCompanion) return null - return classDescriptor -} - +context(FirSession) +@Suppress("IncorrectFormatting") // KTIJ-22227 val FirClassSymbol<*>.hasCompanionObjectAsSerializer: Boolean - get() = isInternallySerializableObject || - (this as? FirRegularClassSymbol)?.companionObjectSymbol?.serializerForClass == this.defaultType() + get() = isInternallySerializableObject || hasCustomSerializerOnCompanion -fun FirClassSymbol<*>.getSuperClassNotAny(session: FirSession): FirRegularClassSymbol? { - return getSuperClassOrAny(session).takeUnless { it.classId == StandardClassIds.Any } -} +context(FirSession) +@Suppress("IncorrectFormatting") // KTIJ-22227 +val FirClassSymbol<*>.hasCustomSerializerOnCompanion: Boolean + get() = (this as? FirRegularClassSymbol)?.companionObjectSymbol?.serializerForClass != null -fun FirClassSymbol<*>.getSuperClassOrAny(session: FirSession): FirRegularClassSymbol { - return resolvedSuperTypes.firstNotNullOfOrNull { superType -> - superType.fullyExpandedType(session) - .toRegularClassSymbol(session) - ?.takeIf { it.classKind == ClassKind.CLASS } - } ?: session.builtinTypes.anyType.toRegularClassSymbol(session) ?: error("Symbol for kotlin/Any not found") -} - -context(CheckerContext) -val FirClassSymbol<*>?.classSerializer: FirClassSymbol<*>? - get() { - if (this == null) return null - // serializer annotation on class? - serializableWith?.let { return it.toRegularClassSymbol(session) } - // companion object serializer? - if (this is FirRegularClassSymbol && hasCompanionObjectAsSerializer) return companionObjectSymbol - // can infer @Poly? - polymorphicSerializerIfApplicableAutomatically?.let { return it } - // default serializable? - if (shouldHaveGeneratedSerializer) { - // $serializer nested class - return unsubstitutedScope(this@CheckerContext) - .getSingleClassifier(SerialEntityNames.SERIALIZER_CLASS_NAME) as? FirClassSymbol<*> - } - return null - } - -context(CheckerContext) -val FirClassSymbol<*>.polymorphicSerializerIfApplicableAutomatically: FirClassSymbol<*>? - get() { - val serializerName = when { - isInterface -> when (modality) { - Modality.SEALED -> SpecialBuiltins.sealedSerializer - else -> SpecialBuiltins.polymorphicSerializer - } - isInternalSerializable -> when (modality) { - Modality.SEALED -> SpecialBuiltins.sealedSerializer - Modality.ABSTRACT -> SpecialBuiltins.polymorphicSerializer - else -> null - } - else -> null - } - return serializerName?.let { session.dependencySerializationInfoProvider.getClassFromSerializationPackage(Name.identifier(it)) } - } - -context(CheckerContext) +context(FirSession) val FirClassSymbol<*>.isEnumWithLegacyGeneratedSerializer: Boolean - get() = isInternallySerializableEnum && session.dependencySerializationInfoProvider.useGeneratedEnumSerializer + get() = classKind.isEnumClass && dependencySerializationInfoProvider.useGeneratedEnumSerializer && hasSerializableOrMetaAnnotationWithoutArgs -context(CheckerContext) +context(FirSession) val FirClassSymbol<*>.shouldHaveGeneratedSerializer: Boolean get() = (isInternalSerializable && (modality == Modality.FINAL || modality == Modality.OPEN)) || isEnumWithLegacyGeneratedSerializer +context(FirSession) +@Suppress("IncorrectFormatting") // KTIJ-22227 +internal fun FirClassSymbol<*>.shouldHaveGeneratedSerializer(useGeneratedEnumSerializer: Boolean): Boolean { + return (isInternalSerializable && isFinalOrOpen()) || (useGeneratedEnumSerializer && isSerializableEnumWithoutArgs) +} + // ---------------------- type utils ---------------------- val ConeKotlinType.isKSerializer: Boolean @@ -259,15 +206,6 @@ fun ConeKotlinType.serializerForType(session: FirSession): ConeKotlinType? { val ConeKotlinType.isTypeParameter: Boolean get() = this is ConeTypeParameterType -context(CheckerContext) -val ConeKotlinType.overriddenSerializer: ConeKotlinType? - get() = toRegularClassSymbol(session)?.serializableWith - -context(CheckerContext) +context(FirSession) val ConeKotlinType.isGeneratedSerializableObject: Boolean - get() = toRegularClassSymbol(session)?.let { it.classKind.isObject && it.hasSerializableOrMetaAnnotationWithoutArgs } ?: false - -// ---------------------- other ---------------------- - -val CheckerContext.currentFile: FirFile - get() = containingDeclarations.first() as FirFile + get() = toRegularClassSymbol(this@FirSession)?.let { it.classKind.isObject && it.hasSerializableOrMetaAnnotationWithoutArgs } ?: false diff --git a/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/checkers/FirSerializationPluginClassChecker.kt b/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/checkers/FirSerializationPluginClassChecker.kt index d1bb7ce5c7e..d82294e1bf1 100644 --- a/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/checkers/FirSerializationPluginClassChecker.kt +++ b/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/checkers/FirSerializationPluginClassChecker.kt @@ -9,6 +9,7 @@ import org.jetbrains.kotlin.KtSourceElement import org.jetbrains.kotlin.config.KotlinCompilerVersion import org.jetbrains.kotlin.descriptors.isEnumClass import org.jetbrains.kotlin.diagnostics.* +import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirClassChecker import org.jetbrains.kotlin.fir.analysis.checkers.isInlineClass @@ -19,6 +20,7 @@ import org.jetbrains.kotlin.fir.expressions.* import org.jetbrains.kotlin.fir.moduleData import org.jetbrains.kotlin.fir.resolve.defaultType import org.jetbrains.kotlin.fir.resolve.fullyExpandedType +import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol import org.jetbrains.kotlin.fir.symbols.impl.* import org.jetbrains.kotlin.fir.types.* import org.jetbrains.kotlin.name.* @@ -31,6 +33,7 @@ import org.jetbrains.kotlinx.serialization.compiler.fir.services.dependencySeria import org.jetbrains.kotlinx.serialization.compiler.fir.services.findTypeSerializerOrContextUnchecked import org.jetbrains.kotlinx.serialization.compiler.fir.services.serializablePropertiesProvider import org.jetbrains.kotlinx.serialization.compiler.fir.services.versionReader +import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationAnnotations object FirSerializationPluginClassChecker : FirClassChecker() { private val JAVA_SERIALIZABLE_ID = ClassId.topLevel(FqName("java.io.Serializable")) @@ -204,9 +207,9 @@ object FirSerializationPluginClassChecker : FirClassChecker() { ) return false } - - if (!classSymbol.hasSerializableOrMetaAnnotation) return false - + + if (!with(session) { classSymbol.hasSerializableOrMetaAnnotation }) return false + if (classSymbol.isAnonymousObjectOrInsideIt) { reporter.reportOn(classSymbol.serializableOrMetaAnnotationSource, FirSerializationErrors.ANONYMOUS_OBJECTS_NOT_SUPPORTED) return false @@ -227,13 +230,13 @@ object FirSerializationPluginClassChecker : FirClassChecker() { return false } - if (!classSymbol.hasSerializableOrMetaAnnotationWithoutArgs) { + if (!with(session) { classSymbol.hasSerializableOrMetaAnnotationWithoutArgs }) { // defined custom serializer checkClassWithCustomSerializer(classSymbol, reporter) return false } - if (classSymbol.serializableAnnotationIsUseless) { + if (with(session) { classSymbol.serializableAnnotationIsUseless }) { reporter.reportOn(classSymbol.serializableOrMetaAnnotationSource, FirSerializationErrors.SERIALIZABLE_ANNOTATION_IGNORED) return false } @@ -241,7 +244,7 @@ object FirSerializationPluginClassChecker : FirClassChecker() { // check that we can instantiate supertype if (!classSymbol.isEnumClass) { // enums are inherited from java.lang.Enum and can't be inherited from other classes val superClassSymbol = classSymbol.getSuperClassOrAny(session) - if (!superClassSymbol.isInternalSerializable) { + if (with(session) { !superClassSymbol.isInternalSerializable }) { val noArgConstructorSymbol = superClassSymbol.declarationSymbols.firstOrNull { it is FirConstructorSymbol && it.valueParameterSymbols.isEmpty() } if (noArgConstructorSymbol == null) { reporter.reportOn(classSymbol.serializableOrMetaAnnotationSource, FirSerializationErrors.NON_SERIALIZABLE_PARENT_MUST_HAVE_NOARG_CTOR) @@ -296,9 +299,11 @@ object FirSerializationPluginClassChecker : FirClassChecker() { context(CheckerContext) @Suppress("IncorrectFormatting") // KTIJ-22227 private fun buildSerializableProperties(classSymbol: FirClassSymbol<*>, reporter: DiagnosticReporter): FirSerializableProperties? { - if (!classSymbol.hasSerializableOrMetaAnnotation) return null - if (!classSymbol.isInternalSerializable) return null - if (classSymbol.hasCompanionObjectAsSerializer) return null + with(session) { + if (!classSymbol.hasSerializableOrMetaAnnotation) return null + if (!classSymbol.isInternalSerializable) return null + if (classSymbol.hasCompanionObjectAsSerializer) return null + } val properties = session.serializablePropertiesProvider.getSerializablePropertiesForClass(classSymbol) if (!properties.isExternallySerializable) { @@ -487,34 +492,4 @@ object FirSerializationPluginClassChecker : FirClassChecker() { reporter.reportOn(source, FirSerializationErrors.SERIALIZER_NULLABILITY_INCOMPATIBLE, serializerType, classType) } } - - // -------------------------------------------------------------------------------------- - - context(CheckerContext) - @Suppress("IncorrectFormatting") // KTIJ-22227 - private val FirClassSymbol<*>.isSerializableEnumWithMissingSerializer: Boolean - get() { - if (!isEnumClass) return false - if (hasSerializableOrMetaAnnotation) return false - if (hasAnySerialAnnotation) return true - return collectEnumEntries().any { it.hasAnySerialAnnotation } - } - - context(CheckerContext) - @Suppress("IncorrectFormatting") // KTIJ-22227 - private val FirClassSymbol<*>.serializableOrMetaAnnotationSource: KtSourceElement? - get() { - serializableAnnotation(needArguments = false)?.source?.let { return it } - metaSerializableAnnotation(needArguments = false)?.source?.let { return it } - return null - } - - context(CheckerContext) - @Suppress("IncorrectFormatting") // KTIJ-22227 - private val FirClassSymbol<*>.serializableAnnotationIsUseless: Boolean - get() = !classKind.isEnumClass && - hasSerializableOrMetaAnnotationWithoutArgs && - !isInternalSerializable && - !hasCompanionObjectAsSerializer && - !isSealedSerializableInterface } diff --git a/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/checkers/SerializationFirCheckerUtils.kt b/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/checkers/SerializationFirCheckerUtils.kt new file mode 100644 index 00000000000..c8c16e4f5bf --- /dev/null +++ b/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/checkers/SerializationFirCheckerUtils.kt @@ -0,0 +1,169 @@ +/* + * 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.fir.checkers + +import org.jetbrains.kotlin.KtSourceElement +import org.jetbrains.kotlin.descriptors.ClassKind +import org.jetbrains.kotlin.descriptors.Modality +import org.jetbrains.kotlin.descriptors.isEnumClass +import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext +import org.jetbrains.kotlin.fir.analysis.checkers.toRegularClassSymbol +import org.jetbrains.kotlin.fir.analysis.checkers.unsubstitutedScope +import org.jetbrains.kotlin.fir.declarations.FirFile +import org.jetbrains.kotlin.fir.declarations.collectEnumEntries +import org.jetbrains.kotlin.fir.declarations.hasAnnotation +import org.jetbrains.kotlin.fir.declarations.utils.isEnumClass +import org.jetbrains.kotlin.fir.declarations.utils.isInterface +import org.jetbrains.kotlin.fir.declarations.utils.modality +import org.jetbrains.kotlin.fir.expressions.FirAnnotation +import org.jetbrains.kotlin.fir.resolve.fullyExpandedType +import org.jetbrains.kotlin.fir.scopes.getSingleClassifier +import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol +import org.jetbrains.kotlin.fir.types.ConeKotlinType +import org.jetbrains.kotlin.fir.types.coneType +import org.jetbrains.kotlin.fir.types.customAnnotations +import org.jetbrains.kotlin.fir.types.toRegularClassSymbol +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.name.StandardClassIds +import org.jetbrains.kotlinx.serialization.compiler.fir.* +import org.jetbrains.kotlinx.serialization.compiler.fir.serializableWith +import org.jetbrains.kotlinx.serialization.compiler.fir.services.dependencySerializationInfoProvider +import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames +import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationAnnotations +import org.jetbrains.kotlinx.serialization.compiler.resolve.SpecialBuiltins + +// ---------------------- search utils ---------------------- + +context(CheckerContext) +@Suppress("IncorrectFormatting") // KTIJ-22227 +internal val FirClassSymbol<*>?.classSerializer: FirClassSymbol<*>? + get() { + if (this == null) return null + // serializer annotation on class? + serializableWith?.let { return it.toRegularClassSymbol(session) } + // companion object serializer? + if (this is FirRegularClassSymbol && with(session) { hasCompanionObjectAsSerializer }) return companionObjectSymbol + // can infer @Poly? + polymorphicSerializerIfApplicableAutomatically?.let { return it } + // default serializable? + if (with(session) { shouldHaveGeneratedSerializer }) { + // $serializer nested class + return unsubstitutedScope(this@CheckerContext) + .getSingleClassifier(SerialEntityNames.SERIALIZER_CLASS_NAME) as? FirClassSymbol<*> + } + return null + } + +context(CheckerContext) +@Suppress("IncorrectFormatting") // KTIJ-22227 +val FirClassSymbol<*>.polymorphicSerializerIfApplicableAutomatically: FirClassSymbol<*>? + get() { + val serializerName = when { + isInterface -> when (modality) { + Modality.SEALED -> SpecialBuiltins.sealedSerializer + else -> SpecialBuiltins.polymorphicSerializer + } + + with(session) { isInternalSerializable } -> when (modality) { + Modality.SEALED -> SpecialBuiltins.sealedSerializer + Modality.ABSTRACT -> SpecialBuiltins.polymorphicSerializer + else -> null + } + + else -> null + } + return serializerName?.let { session.dependencySerializationInfoProvider.getClassFromSerializationPackage(Name.identifier(it)) } + } + +// ---------------------- annotation utils ---------------------- + +context(CheckerContext) +@Suppress("IncorrectFormatting") // KTIJ-22227 +internal val FirAnnotation.annotationClassSymbol: FirRegularClassSymbol? + get() = annotationTypeRef.coneType + .fullyExpandedType(session) + .toRegularClassSymbol(session) + +context(CheckerContext) +@Suppress("IncorrectFormatting") // KTIJ-22227 +internal val FirAnnotation.isMetaSerializableAnnotation: Boolean + get() = annotationClassSymbol?.hasAnnotation(SerializationAnnotations.metaSerializableAnnotationClassId) ?: false + + +context(CheckerContext) +@Suppress("IncorrectFormatting") // KTIJ-22227 +internal fun FirClassSymbol<*>.metaSerializableAnnotation(needArguments: Boolean): FirAnnotation? { + val annotations = if (needArguments) resolvedAnnotationsWithClassIds else resolvedAnnotationsWithArguments + return annotations.firstOrNull { it.isMetaSerializableAnnotation } +} + +context(CheckerContext) +@Suppress("IncorrectFormatting") // KTIJ-22227 +internal val FirClassSymbol<*>.serializableOrMetaAnnotationSource: KtSourceElement? + get() { + serializableAnnotation(needArguments = false)?.source?.let { return it } + metaSerializableAnnotation(needArguments = false)?.source?.let { return it } + return null + } + +context(CheckerContext) +@Suppress("IncorrectFormatting") // KTIJ-22227 +internal val FirBasedSymbol<*>.hasAnySerialAnnotation: Boolean + get() = serialNameValue != null || resolvedAnnotationsWithClassIds.any { it.annotationClassSymbol?.isSerialInfoAnnotation == true } + +// ---------------------- class utils ---------------------- + +fun FirClassSymbol<*>.getSuperClassNotAny(session: FirSession): FirRegularClassSymbol? { + return getSuperClassOrAny(session).takeUnless { it.classId == StandardClassIds.Any } +} + +fun FirClassSymbol<*>.getSuperClassOrAny(session: FirSession): FirRegularClassSymbol { + return resolvedSuperTypes.firstNotNullOfOrNull { superType -> + superType.fullyExpandedType(session) + .toRegularClassSymbol(session) + ?.takeIf { it.classKind == ClassKind.CLASS } + } ?: session.builtinTypes.anyType.toRegularClassSymbol(session) ?: error("Symbol for kotlin/Any not found") +} + +context(CheckerContext) +@Suppress("IncorrectFormatting") // KTIJ-22227 +internal val FirClassSymbol<*>.isSerializableEnumWithMissingSerializer: Boolean + get() { + if (!isEnumClass) return false + if (with(session) { hasSerializableOrMetaAnnotation }) return false + if (hasAnySerialAnnotation) return true + return collectEnumEntries().any { it.hasAnySerialAnnotation } + } + +context(FirSession) +@Suppress("IncorrectFormatting") // KTIJ-22227 +internal val FirClassSymbol<*>.serializableAnnotationIsUseless: Boolean + get() = !classKind.isEnumClass && + hasSerializableOrMetaAnnotationWithoutArgs && + !isInternalSerializable && + !hasCompanionObjectAsSerializer && + !isSealedSerializableInterface + +// ---------------------- type utils ---------------------- + +context(CheckerContext) +@Suppress("IncorrectFormatting") // KTIJ-22227 +internal val ConeKotlinType.serializableWith: ConeKotlinType? + get() = customAnnotations.serializableWith ?: toRegularClassSymbol(session)?.serializableWith + + +context(CheckerContext) +@Suppress("IncorrectFormatting") // KTIJ-22227 +internal val ConeKotlinType.overriddenSerializer: ConeKotlinType? + get() = toRegularClassSymbol(session)?.serializableWith + + +// ---------------------- others ---------------------- +internal val CheckerContext.currentFile: FirFile + get() = containingDeclarations.first() as FirFile diff --git a/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/services/ContextualSerializersProvider.kt b/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/services/ContextualSerializersProvider.kt index ba7d764f60c..a8f8d1c34e0 100644 --- a/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/services/ContextualSerializersProvider.kt +++ b/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/services/ContextualSerializersProvider.kt @@ -25,7 +25,9 @@ import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.utils.addToStdlib.runIf import org.jetbrains.kotlinx.serialization.compiler.fir.* -import org.jetbrains.kotlinx.serialization.compiler.fir.serializableWith +import org.jetbrains.kotlinx.serialization.compiler.fir.checkers.classSerializer +import org.jetbrains.kotlinx.serialization.compiler.fir.checkers.currentFile +import org.jetbrains.kotlinx.serialization.compiler.fir.checkers.overriddenSerializer import org.jetbrains.kotlinx.serialization.compiler.resolve.* @@ -116,20 +118,20 @@ fun findTypeSerializer(type: ConeKotlinType): FirClassSymbol<*>? { if (type.isArrayType) { return serializationProvider.getClassFromInternalSerializationPackage(SpecialBuiltins.Names.referenceArraySerializer) } - if (type.isGeneratedSerializableObject) { + if (with(session) { type.isGeneratedSerializableObject }) { return serializationProvider.getClassFromInternalSerializationPackage(SpecialBuiltins.Names.objectSerializer) } // see if there is a standard serializer - val standardSerializer = findStandardKotlinTypeSerializer(type) ?: findEnumTypeSerializer(type) + val standardSerializer = with(session) { findStandardKotlinTypeSerializer(type) ?: findEnumTypeSerializer(type) } if (standardSerializer != null) return standardSerializer val symbol = type.toRegularClassSymbol(session) ?: return null - if (symbol.isSealedSerializableInterface) { + if (with(session) { symbol.isSealedSerializableInterface }) { return serializationProvider.getClassFromSerializationPackage(SpecialBuiltins.Names.polymorphicSerializer) } return symbol.classSerializer // check for serializer defined on the type } -context(CheckerContext) +context(FirSession) fun findStandardKotlinTypeSerializer(type: ConeKotlinType): FirClassSymbol<*>? { val name = when { type.isBoolean -> PrimitiveBuiltins.booleanSerializer @@ -142,15 +144,15 @@ fun findStandardKotlinTypeSerializer(type: ConeKotlinType): FirClassSymbol<*>? { type.isChar -> PrimitiveBuiltins.charSerializer else -> findStandardKotlinTypeSerializerName(type.classId?.asFqNameString()) }?.let(Name::identifier) ?: return null - val symbolProvider = session.symbolProvider + val symbolProvider = symbolProvider return symbolProvider.getClassLikeSymbolByClassId(ClassId(SerializationPackages.internalPackageFqName, name)) as? FirClassSymbol<*> ?: symbolProvider.getClassLikeSymbolByClassId(ClassId(SerializationPackages.packageFqName, name)) as? FirClassSymbol<*> } -context(CheckerContext) +context(FirSession) fun findEnumTypeSerializer(type: ConeKotlinType): FirClassSymbol<*>? { - val symbol = type.toRegularClassSymbol(session) ?: return null + val symbol = type.toRegularClassSymbol(this@FirSession) ?: return null return runIf(symbol.isEnumClass && !symbol.isEnumWithLegacyGeneratedSerializer) { - session.symbolProvider.getClassLikeSymbolByClassId(SerializersClassIds.enumSerializerId) as? FirClassSymbol<*> + symbolProvider.getClassLikeSymbolByClassId(SerializersClassIds.enumSerializerId) as? FirClassSymbol<*> } } diff --git a/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/services/FirSerializablePropertiesProvider.kt b/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/services/FirSerializablePropertiesProvider.kt index 7f86d354c47..1c91d2394e0 100644 --- a/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/services/FirSerializablePropertiesProvider.kt +++ b/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/services/FirSerializablePropertiesProvider.kt @@ -13,17 +13,17 @@ import org.jetbrains.kotlin.fir.caches.createCache import org.jetbrains.kotlin.fir.caches.firCachesFactory import org.jetbrains.kotlin.fir.caches.getValue import org.jetbrains.kotlin.fir.declarations.utils.correspondingValueParameterFromPrimaryConstructor +import org.jetbrains.kotlin.fir.declarations.utils.isEnumClass import org.jetbrains.kotlin.fir.declarations.utils.visibility -import org.jetbrains.kotlin.fir.expressions.toResolvedCallableReference +import org.jetbrains.kotlin.fir.extensions.FirDeclarationPredicateRegistrar import org.jetbrains.kotlin.fir.extensions.FirExtensionSessionComponent -import org.jetbrains.kotlin.fir.references.impl.FirPropertyFromParameterResolvedNamedReference import org.jetbrains.kotlin.fir.scopes.impl.declaredMemberScope import org.jetbrains.kotlin.fir.scopes.processAllProperties import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol -import org.jetbrains.kotlin.fir.symbols.impl.FirValueParameterSymbol import org.jetbrains.kotlin.utils.addIfNotNull import org.jetbrains.kotlinx.serialization.compiler.fir.* +import org.jetbrains.kotlinx.serialization.compiler.fir.checkers.getSuperClassNotAny import org.jetbrains.kotlinx.serialization.compiler.fir.isInternalSerializable import org.jetbrains.kotlinx.serialization.compiler.resolve.ISerializableProperty @@ -35,6 +35,10 @@ class FirSerializablePropertiesProvider(session: FirSession) : FirExtensionSessi return cache.getValue(classSymbol) } + override fun FirDeclarationPredicateRegistrar.registerPredicates() { + register(FirSerializationPredicates.hasMetaAnnotation) + } + private fun createSerializableProperties(classSymbol: FirClassSymbol<*>): FirSerializableProperties { val allPropertySymbols = buildList { classSymbol @@ -49,7 +53,7 @@ class FirSerializablePropertiesProvider(session: FirSession) : FirExtensionSessi it to parameterSymbol.hasDefaultValue }.toMap().withDefault { false } - val isInternalSerializable = classSymbol.isInternalSerializable + val isInternalSerializable = with(session) { classSymbol.isInternalSerializable } fun isPropertySerializable(propertySymbol: FirPropertySymbol): Boolean { return when { @@ -75,7 +79,7 @@ class FirSerializablePropertiesProvider(session: FirSession) : FirExtensionSessi .let { (fromConstructor, standalone) -> val superClassSymbol = classSymbol.getSuperClassNotAny(session) buildList { - if (superClassSymbol != null && superClassSymbol.isInternalSerializable) { + if (superClassSymbol != null && with(session) { superClassSymbol.isInternalSerializable }) { addAll(getSerializablePropertiesForClass(superClassSymbol).serializableProperties) } addAll(fromConstructor) @@ -84,7 +88,7 @@ class FirSerializablePropertiesProvider(session: FirSession) : FirExtensionSessi } .let { restoreCorrectOrderFromClassProtoExtension(classSymbol, it) } - val isExternallySerializable = classSymbol.isInternallySerializableEnum || + val isExternallySerializable = classSymbol.isEnumClass || primaryConstructorProperties.size == classSymbol.primaryConstructorSymbol()?.valueParameterSymbols?.size val (serializableConstructorProperties, serializableStandaloneProperties) = serializableProperties.partition { it.propertySymbol in primaryConstructorProperties } diff --git a/plugins/kotlinx-serialization/testData/boxIr/metaSerializable.kt b/plugins/kotlinx-serialization/testData/boxIr/metaSerializable.kt index 62b41388563..1ea8c308517 100644 --- a/plugins/kotlinx-serialization/testData/boxIr/metaSerializable.kt +++ b/plugins/kotlinx-serialization/testData/boxIr/metaSerializable.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND_FIR: JVM_IR // TARGET_BACKEND: JVM_IR // WITH_STDLIB diff --git a/plugins/kotlinx-serialization/testData/firMembers/metaSerializable.fir.txt b/plugins/kotlinx-serialization/testData/firMembers/metaSerializable.fir.txt new file mode 100644 index 00000000000..6648d0e093d --- /dev/null +++ b/plugins/kotlinx-serialization/testData/firMembers/metaSerializable.fir.txt @@ -0,0 +1,468 @@ +FILE: metaSerializable.kt + @R|kotlinx/serialization/MetaSerializable|() @R|kotlin/annotation/Target|(allowedTargets = vararg(Q|kotlin/annotation/AnnotationTarget|.R|kotlin/annotation/AnnotationTarget.CLASS|, Q|kotlin/annotation/AnnotationTarget|.R|kotlin/annotation/AnnotationTarget.PROPERTY|)) public final annotation class MySerializable : R|kotlin/Annotation| { + public constructor(): R|MySerializable| { + super() + } + + } + @R|kotlinx/serialization/MetaSerializable|() @R|kotlin/annotation/Target|(allowedTargets = vararg(Q|kotlin/annotation/AnnotationTarget|.R|kotlin/annotation/AnnotationTarget.CLASS|, Q|kotlin/annotation/AnnotationTarget|.R|kotlin/annotation/AnnotationTarget.PROPERTY|)) public final annotation class MySerializableWithInfo : R|kotlin/Annotation| { + public constructor(value: R|kotlin/Int|, kclass: R|kotlin/reflect/KClass<*>|): R|MySerializableWithInfo| { + super() + } + + public final val value: R|kotlin/Int| = R|/value| + public get(): R|kotlin/Int| + + public final val kclass: R|kotlin/reflect/KClass<*>| = R|/kclass| + public get(): R|kotlin/reflect/KClass<*>| + + } + @R|MySerializable|() public final class Project1 : R|kotlin/Any| { + public constructor(name: R|kotlin/String|, language: R|kotlin/String|): R|Project1| { + super() + } + + public final val name: R|kotlin/String| = R|/name| + public get(): R|kotlin/String| + + public final val language: R|kotlin/String| = R|/language| + public get(): R|kotlin/String| + + public final companion object Companion : R|kotlin/Any| { + public final fun serializer(): R|kotlinx/serialization/KSerializer| + + private constructor(): R|Project1.Companion| + + } + + public final object $serializer : R|kotlinx/serialization/internal/GeneratedSerializer| { + public final fun serialize(encoder: R|kotlinx/serialization/encoding/Encoder|, value: R|Project1|): R|kotlin/Unit| + + public final fun deserialize(decoder: R|kotlinx/serialization/encoding/Decoder|): R|Project1| + + public final val descriptor: R|kotlinx/serialization/descriptors/SerialDescriptor| + public get(): R|kotlinx/serialization/descriptors/SerialDescriptor| + + public final fun childSerializers(): R|kotlin/Array>| + + private constructor(): R|Project1.$serializer| + + } + + } + @R|MySerializableWithInfo|(value = Int(123), kclass = (Q|kotlin/String|)) public final class Project2 : R|kotlin/Any| { + public constructor(name: R|kotlin/String|, language: R|kotlin/String|): R|Project2| { + super() + } + + public final val name: R|kotlin/String| = R|/name| + public get(): R|kotlin/String| + + public final val language: R|kotlin/String| = R|/language| + public get(): R|kotlin/String| + + public final companion object Companion : R|kotlin/Any| { + public final fun serializer(): R|kotlinx/serialization/KSerializer| + + private constructor(): R|Project2.Companion| + + } + + public final object $serializer : R|kotlinx/serialization/internal/GeneratedSerializer| { + public final fun serialize(encoder: R|kotlinx/serialization/encoding/Encoder|, value: R|Project2|): R|kotlin/Unit| + + public final fun deserialize(decoder: R|kotlinx/serialization/encoding/Decoder|): R|Project2| + + public final val descriptor: R|kotlinx/serialization/descriptors/SerialDescriptor| + public get(): R|kotlinx/serialization/descriptors/SerialDescriptor| + + public final fun childSerializers(): R|kotlin/Array>| + + private constructor(): R|Project2.$serializer| + + } + + } + @R|kotlinx/serialization/Serializable|() public final class Wrapper : R|kotlin/Any| { + public constructor(@R|MySerializableWithInfo|(value = Int(234), kclass = (Q|kotlin/Int|)) project: R|Project2|): R|Wrapper| { + super() + } + + @R|MySerializableWithInfo|(value = Int(234), kclass = (Q|kotlin/Int|)) public final val project: R|Project2| = R|/project| + public get(): R|Project2| + + public final companion object Companion : R|kotlin/Any| { + public final fun serializer(): R|kotlinx/serialization/KSerializer| + + private constructor(): R|Wrapper.Companion| + + } + + public final object $serializer : R|kotlinx/serialization/internal/GeneratedSerializer| { + public final fun serialize(encoder: R|kotlinx/serialization/encoding/Encoder|, value: R|Wrapper|): R|kotlin/Unit| + + public final fun deserialize(decoder: R|kotlinx/serialization/encoding/Decoder|): R|Wrapper| + + public final val descriptor: R|kotlinx/serialization/descriptors/SerialDescriptor| + public get(): R|kotlinx/serialization/descriptors/SerialDescriptor| + + public final fun childSerializers(): R|kotlin/Array>| + + private constructor(): R|Wrapper.$serializer| + + } + + } + @R|kotlinx/serialization/Serializable|() @R|MySerializableWithInfo|(value = Int(123), kclass = (Q|kotlin/String|)) public final class Project3 : R|kotlin/Any| { + public constructor(name: R|kotlin/String|, language: R|kotlin/String|): R|Project3| { + super() + } + + public final val name: R|kotlin/String| = R|/name| + public get(): R|kotlin/String| + + public final val language: R|kotlin/String| = R|/language| + public get(): R|kotlin/String| + + public final companion object Companion : R|kotlin/Any| { + public final fun serializer(): R|kotlinx/serialization/KSerializer| + + private constructor(): R|Project3.Companion| + + } + + public final object $serializer : R|kotlinx/serialization/internal/GeneratedSerializer| { + public final fun serialize(encoder: R|kotlinx/serialization/encoding/Encoder|, value: R|Project3|): R|kotlin/Unit| + + public final fun deserialize(decoder: R|kotlinx/serialization/encoding/Decoder|): R|Project3| + + public final val descriptor: R|kotlinx/serialization/descriptors/SerialDescriptor| + public get(): R|kotlinx/serialization/descriptors/SerialDescriptor| + + public final fun childSerializers(): R|kotlin/Array>| + + private constructor(): R|Project3.$serializer| + + } + + } + @R|kotlinx/serialization/Serializable|(with = (Q|MySerializer|)) @R|MySerializableWithInfo|(value = Int(123), kclass = (Q|kotlin/String|)) public final class Project4 : R|kotlin/Any| { + public constructor(name: R|kotlin/String|, language: R|kotlin/String|): R|Project4| { + super() + } + + public final val name: R|kotlin/String| = R|/name| + public get(): R|kotlin/String| + + public final val language: R|kotlin/String| = R|/language| + public get(): R|kotlin/String| + + public final companion object Companion : R|kotlin/Any| { + public final fun serializer(): R|kotlinx/serialization/KSerializer| + + private constructor(): R|Project4.Companion| + + } + + } + @R|MySerializableWithInfo|(value = Int(123), kclass = (Q|kotlin/String|)) public sealed class TestSealed : R|kotlin/Any| { + protected constructor(): R|TestSealed| { + super() + } + + @R|MySerializableWithInfo|(value = Int(123), kclass = (Q|kotlin/String|)) public final class A : R|TestSealed| { + public constructor(value1: R|kotlin/String|): R|TestSealed.A| { + super() + } + + public final val value1: R|kotlin/String| = R|/value1| + public get(): R|kotlin/String| + + public final companion object Companion : R|kotlin/Any| { + public final fun serializer(): R|kotlinx/serialization/KSerializer| + + private constructor(): R|TestSealed.A.Companion| + + } + + public final object $serializer : R|kotlinx/serialization/internal/GeneratedSerializer| { + public final fun serialize(encoder: R|kotlinx/serialization/encoding/Encoder|, value: R|TestSealed.A|): R|kotlin/Unit| + + public final fun deserialize(decoder: R|kotlinx/serialization/encoding/Decoder|): R|TestSealed.A| + + public final val descriptor: R|kotlinx/serialization/descriptors/SerialDescriptor| + public get(): R|kotlinx/serialization/descriptors/SerialDescriptor| + + public final fun childSerializers(): R|kotlin/Array>| + + private constructor(): R|TestSealed.A.$serializer| + + } + + } + + @R|MySerializableWithInfo|(value = Int(123), kclass = (Q|kotlin/String|)) public final class B : R|TestSealed| { + public constructor(value2: R|kotlin/String|): R|TestSealed.B| { + super() + } + + public final val value2: R|kotlin/String| = R|/value2| + public get(): R|kotlin/String| + + public final companion object Companion : R|kotlin/Any| { + public final fun serializer(): R|kotlinx/serialization/KSerializer| + + private constructor(): R|TestSealed.B.Companion| + + } + + public final object $serializer : R|kotlinx/serialization/internal/GeneratedSerializer| { + public final fun serialize(encoder: R|kotlinx/serialization/encoding/Encoder|, value: R|TestSealed.B|): R|kotlin/Unit| + + public final fun deserialize(decoder: R|kotlinx/serialization/encoding/Decoder|): R|TestSealed.B| + + public final val descriptor: R|kotlinx/serialization/descriptors/SerialDescriptor| + public get(): R|kotlinx/serialization/descriptors/SerialDescriptor| + + public final fun childSerializers(): R|kotlin/Array>| + + private constructor(): R|TestSealed.B.$serializer| + + } + + } + + public final companion object Companion : R|kotlin/Any| { + public final fun serializer(): R|kotlinx/serialization/KSerializer| + + private constructor(): R|TestSealed.Companion| + + } + + } + @R|MySerializable|() public abstract class TestAbstract : R|kotlin/Any| { + public constructor(): R|TestAbstract| { + super() + } + + @R|MySerializableWithInfo|(value = Int(123), kclass = (Q|kotlin/String|)) public final class A : R|TestSealed| { + public constructor(value1: R|kotlin/String|): R|TestAbstract.A| { + super() + } + + public final val value1: R|kotlin/String| = R|/value1| + public get(): R|kotlin/String| + + public final companion object Companion : R|kotlin/Any| { + public final fun serializer(): R|kotlinx/serialization/KSerializer| + + private constructor(): R|TestAbstract.A.Companion| + + } + + public final object $serializer : R|kotlinx/serialization/internal/GeneratedSerializer| { + public final fun serialize(encoder: R|kotlinx/serialization/encoding/Encoder|, value: R|TestAbstract.A|): R|kotlin/Unit| + + public final fun deserialize(decoder: R|kotlinx/serialization/encoding/Decoder|): R|TestAbstract.A| + + public final val descriptor: R|kotlinx/serialization/descriptors/SerialDescriptor| + public get(): R|kotlinx/serialization/descriptors/SerialDescriptor| + + public final fun childSerializers(): R|kotlin/Array>| + + private constructor(): R|TestAbstract.A.$serializer| + + } + + } + + @R|MySerializableWithInfo|(value = Int(123), kclass = (Q|kotlin/String|)) public final class B : R|TestSealed| { + public constructor(value2: R|kotlin/String|): R|TestAbstract.B| { + super() + } + + public final val value2: R|kotlin/String| = R|/value2| + public get(): R|kotlin/String| + + public final companion object Companion : R|kotlin/Any| { + public final fun serializer(): R|kotlinx/serialization/KSerializer| + + private constructor(): R|TestAbstract.B.Companion| + + } + + public final object $serializer : R|kotlinx/serialization/internal/GeneratedSerializer| { + public final fun serialize(encoder: R|kotlinx/serialization/encoding/Encoder|, value: R|TestAbstract.B|): R|kotlin/Unit| + + public final fun deserialize(decoder: R|kotlinx/serialization/encoding/Decoder|): R|TestAbstract.B| + + public final val descriptor: R|kotlinx/serialization/descriptors/SerialDescriptor| + public get(): R|kotlinx/serialization/descriptors/SerialDescriptor| + + public final fun childSerializers(): R|kotlin/Array>| + + private constructor(): R|TestAbstract.B.$serializer| + + } + + } + + public final companion object Companion : R|kotlin/Any| { + public final fun serializer(): R|kotlinx/serialization/KSerializer| + + private constructor(): R|TestAbstract.Companion| + + } + + } + @R|MySerializableWithInfo|(value = Int(123), kclass = (Q|kotlin/String|)) public final enum class TestEnum : R|kotlin/Enum| { + private constructor(): R|TestEnum| { + super|>() + } + + public final static enum entry Value1: R|TestEnum| + public final static enum entry Value2: R|TestEnum| + public final static fun values(): R|kotlin/Array| { + } + + public final static fun valueOf(value: R|kotlin/String|): R|TestEnum| { + } + + public final static val entries: R|kotlin/enums/EnumEntries| + public get(): R|kotlin/enums/EnumEntries| + + public final companion object Companion : R|kotlin/Any| { + public final fun serializer(): R|kotlinx/serialization/KSerializer| + + private constructor(): R|TestEnum.Companion| + + } + + public final object $serializer : R|kotlinx/serialization/internal/GeneratedSerializer| { + public final fun serialize(encoder: R|kotlinx/serialization/encoding/Encoder|, value: R|TestEnum|): R|kotlin/Unit| + + public final fun deserialize(decoder: R|kotlinx/serialization/encoding/Decoder|): R|TestEnum| + + public final val descriptor: R|kotlinx/serialization/descriptors/SerialDescriptor| + public get(): R|kotlinx/serialization/descriptors/SerialDescriptor| + + public final fun childSerializers(): R|kotlin/Array>| + + private constructor(): R|TestEnum.$serializer| + + } + + } + @R|MySerializableWithInfo|(value = Int(123), kclass = (Q|kotlin/String|)) public final object TestObject : R|kotlin/Any| { + private constructor(): R|TestObject| { + super() + } + + public final fun serializer(): R|kotlinx/serialization/KSerializer| + + } + public final object MySerializer : R|kotlinx/serialization/KSerializer| { + private constructor(): R|MySerializer| { + super() + } + + public final override val descriptor: R|kotlinx/serialization/descriptors/SerialDescriptor| = R|kotlinx/serialization/descriptors/PrimitiveSerialDescriptor|(String(Project4), Q|kotlinx/serialization/descriptors/PrimitiveKind.STRING|) + public get(): R|kotlinx/serialization/descriptors/SerialDescriptor| + + public final override fun serialize(encoder: R|kotlinx/serialization/encoding/Encoder|, value: R|Project4|): R|kotlin/Unit| { + ^serialize R|/encoder|.R|kotlinx/serialization/encoding/Encoder.encodeString|((R|/value|.R|/Project4.name|, String(:), R|/value|.R|/Project4.language|)) + } + + public final override fun deserialize(decoder: R|kotlinx/serialization/encoding/Decoder|): R|Project4| { + lval params: R|kotlin/collections/List| = R|/decoder|.R|kotlinx/serialization/encoding/Decoder.decodeString|().R|kotlin/text/split|(vararg(Char(:))) + ^deserialize R|/Project4.Project4|(R|/params|.R|SubstitutionOverride|(Int(0)), R|/params|.R|SubstitutionOverride|(Int(1))) + } + + } + public final fun testMetaSerializable(): R|kotlin/Unit| { + lval string: R|kotlin/String| = Q|kotlinx/serialization/json/Json|.R|kotlinx/serialization/json/Json.encodeToString|(Q|Project1|.R|/Project1.Companion.serializer|(), R|/Project1.Project1|(String(name), String(lang))) + R|kotlin/test/assertEquals|(String({"name":"name","language":"lang"}), R|/string|) + lval reconstructed: R|Project1| = Q|kotlinx/serialization/json/Json|.R|kotlinx/serialization/json/Json.decodeFromString|(Q|Project1|.R|/Project1.Companion.serializer|(), R|/string|) + R|kotlin/test/assertEquals|(String(name), R|/reconstructed|.R|/Project1.name|) + R|kotlin/test/assertEquals|(String(lang), R|/reconstructed|.R|/Project1.language|) + } + public final fun testMetaSerializableWithInfo(): R|kotlin/Unit| { + lval string: R|kotlin/String| = Q|kotlinx/serialization/json/Json|.R|kotlinx/serialization/json/Json.encodeToString|(Q|Project2|.R|/Project2.Companion.serializer|(), R|/Project2.Project2|(String(name), String(lang))) + R|kotlin/test/assertEquals|(String({"name":"name","language":"lang"}), R|/string|) + lval reconstructed: R|Project2| = Q|kotlinx/serialization/json/Json|.R|kotlinx/serialization/json/Json.decodeFromString|(Q|Project2|.R|/Project2.Companion.serializer|(), R|/string|) + R|kotlin/test/assertEquals|(String(name), R|/reconstructed|.R|/Project2.name|) + R|kotlin/test/assertEquals|(String(lang), R|/reconstructed|.R|/Project2.language|) + lval info: R|MySerializableWithInfo| = Q|Project2|.R|/Project2.Companion.serializer|().R|SubstitutionOverride|.R|kotlinx/serialization/descriptors/SerialDescriptor.annotations|.R|kotlin/collections/filterIsInstance|().R|kotlin/collections/first|() + R|kotlin/test/assertEquals|(Int(123), R|/info|.R|/MySerializableWithInfo.value|) + R|kotlin/test/assertEquals||>((Q|kotlin/String|), R|/info|.R|/MySerializableWithInfo.kclass|) + } + public final fun testMetaSerializableOnProperty(): R|kotlin/Unit| { + lval info: R|MySerializableWithInfo| = Q|Wrapper|.R|/Wrapper.Companion.serializer|().R|SubstitutionOverride|.R|kotlinx/serialization/descriptors/SerialDescriptor.getElementAnnotations|(Int(0)).R|kotlin/collections/filterIsInstance|().R|kotlin/collections/first|() + R|kotlin/test/assertEquals|(Int(234), R|/info|.R|/MySerializableWithInfo.value|) + R|kotlin/test/assertEquals||>((Q|kotlin/Int|), R|/info|.R|/MySerializableWithInfo.kclass|) + } + public final fun testSerializableAndMetaAnnotation(): R|kotlin/Unit| { + lval string: R|kotlin/String| = Q|kotlinx/serialization/json/Json|.R|kotlinx/serialization/json/Json.encodeToString|(Q|Project3|.R|/Project3.Companion.serializer|(), R|/Project3.Project3|(String(name), String(lang))) + R|kotlin/test/assertEquals|(String({"name":"name","language":"lang"}), R|/string|) + lval reconstructed: R|Project3| = Q|kotlinx/serialization/json/Json|.R|kotlinx/serialization/json/Json.decodeFromString|(Q|Project3|.R|/Project3.Companion.serializer|(), R|/string|) + R|kotlin/test/assertEquals|(String(name), R|/reconstructed|.R|/Project3.name|) + R|kotlin/test/assertEquals|(String(lang), R|/reconstructed|.R|/Project3.language|) + lval info: R|MySerializableWithInfo| = Q|Project3|.R|/Project3.Companion.serializer|().R|SubstitutionOverride|.R|kotlinx/serialization/descriptors/SerialDescriptor.annotations|.R|kotlin/collections/filterIsInstance|().R|kotlin/collections/first|() + R|kotlin/test/assertEquals|(Int(123), R|/info|.R|/MySerializableWithInfo.value|) + R|kotlin/test/assertEquals||>((Q|kotlin/String|), R|/info|.R|/MySerializableWithInfo.kclass|) + } + public final fun testCustomSerializerAndMetaAnnotation(): R|kotlin/Unit| { + lval string: R|kotlin/String| = Q|kotlinx/serialization/json/Json|.R|kotlinx/serialization/json/Json.encodeToString|(Q|Project4|.R|/Project4.Companion.serializer|(), R|/Project4.Project4|(String(name), String(lang))) + R|kotlin/test/assertEquals|(String(name:lang), R|/string|) + lval reconstructed: R|Project4| = Q|kotlinx/serialization/json/Json|.R|kotlinx/serialization/json/Json.decodeFromString|(Q|Project4|.R|/Project4.Companion.serializer|(), R|/string|) + R|kotlin/test/assertEquals|(String(name), R|/reconstructed|.R|/Project4.name|) + R|kotlin/test/assertEquals|(String(lang), R|/reconstructed|.R|/Project4.language|) + } + public final fun testSealed(): R|kotlin/Unit| { + lval serializerA: R|kotlinx/serialization/KSerializer| = Q|TestSealed.A|.R|/TestSealed.A.Companion.serializer|() + lval serializerB: R|kotlinx/serialization/KSerializer| = Q|TestSealed.B|.R|/TestSealed.B.Companion.serializer|() + R|kotlin/test/assertNotNull||>(R|/serializerA|) + R|kotlin/test/assertNotNull||>(R|/serializerB|) + lval infoA: R|MySerializableWithInfo| = R|/serializerA|.R|SubstitutionOverride|.R|kotlinx/serialization/descriptors/SerialDescriptor.annotations|.R|kotlin/collections/filterIsInstance|().R|kotlin/collections/first|() + lval infoB: R|MySerializableWithInfo| = R|/serializerB|.R|SubstitutionOverride|.R|kotlinx/serialization/descriptors/SerialDescriptor.annotations|.R|kotlin/collections/filterIsInstance|().R|kotlin/collections/first|() + R|kotlin/test/assertEquals|(Int(123), R|/infoA|.R|/MySerializableWithInfo.value|) + R|kotlin/test/assertEquals||>((Q|kotlin/String|), R|/infoA|.R|/MySerializableWithInfo.kclass|) + R|kotlin/test/assertEquals|(Int(123), R|/infoB|.R|/MySerializableWithInfo.value|) + R|kotlin/test/assertEquals||>((Q|kotlin/String|), R|/infoB|.R|/MySerializableWithInfo.kclass|) + } + public final fun testAbstract(): R|kotlin/Unit| { + lval serializerA: R|kotlinx/serialization/KSerializer| = Q|TestAbstract.A|.R|/TestAbstract.A.Companion.serializer|() + lval serializerB: R|kotlinx/serialization/KSerializer| = Q|TestAbstract.B|.R|/TestAbstract.B.Companion.serializer|() + R|kotlin/test/assertNotNull||>(R|/serializerA|) + R|kotlin/test/assertNotNull||>(R|/serializerB|) + lval infoA: R|MySerializableWithInfo| = R|/serializerA|.R|SubstitutionOverride|.R|kotlinx/serialization/descriptors/SerialDescriptor.annotations|.R|kotlin/collections/filterIsInstance|().R|kotlin/collections/first|() + lval infoB: R|MySerializableWithInfo| = R|/serializerB|.R|SubstitutionOverride|.R|kotlinx/serialization/descriptors/SerialDescriptor.annotations|.R|kotlin/collections/filterIsInstance|().R|kotlin/collections/first|() + R|kotlin/test/assertEquals|(Int(123), R|/infoA|.R|/MySerializableWithInfo.value|) + R|kotlin/test/assertEquals||>((Q|kotlin/String|), R|/infoA|.R|/MySerializableWithInfo.kclass|) + R|kotlin/test/assertEquals|(Int(123), R|/infoB|.R|/MySerializableWithInfo.value|) + R|kotlin/test/assertEquals||>((Q|kotlin/String|), R|/infoB|.R|/MySerializableWithInfo.kclass|) + } + public final fun testEnum(): R|kotlin/Unit| { + lval serializer: R|kotlinx/serialization/KSerializer| = Q|TestEnum|.R|/TestEnum.Companion.serializer|() + R|kotlin/test/assertNotNull||>(R|/serializer|) + lval info: R|MySerializableWithInfo| = R|/serializer|.R|SubstitutionOverride|.R|kotlinx/serialization/descriptors/SerialDescriptor.annotations|.R|kotlin/collections/filterIsInstance|().R|kotlin/collections/first|() + R|kotlin/test/assertEquals|(Int(123), R|/info|.R|/MySerializableWithInfo.value|) + R|kotlin/test/assertEquals||>((Q|kotlin/String|), R|/info|.R|/MySerializableWithInfo.kclass|) + } + public final fun testObject(): R|kotlin/Unit| { + lval serializer: R|kotlinx/serialization/KSerializer| = Q|TestObject|.R|/TestObject.serializer|() + R|kotlin/test/assertNotNull||>(R|/serializer|) + lval info: R|MySerializableWithInfo| = R|/serializer|.R|SubstitutionOverride|.R|kotlinx/serialization/descriptors/SerialDescriptor.annotations|.R|kotlin/collections/filterIsInstance|().R|kotlin/collections/first|() + R|kotlin/test/assertEquals|(Int(123), R|/info|.R|/MySerializableWithInfo.value|) + R|kotlin/test/assertEquals||>((Q|kotlin/String|), R|/info|.R|/MySerializableWithInfo.kclass|) + } + public final fun box(): R|kotlin/String| { + R|/testMetaSerializable|() + R|/testMetaSerializableWithInfo|() + R|/testMetaSerializableOnProperty|() + R|/testSealed|() + R|/testAbstract|() + R|/testEnum|() + R|/testObject|() + ^box String(OK) + } diff --git a/plugins/kotlinx-serialization/testData/firMembers/metaSerializable.kt b/plugins/kotlinx-serialization/testData/firMembers/metaSerializable.kt new file mode 100644 index 00000000000..e2e63a5d84d --- /dev/null +++ b/plugins/kotlinx-serialization/testData/firMembers/metaSerializable.kt @@ -0,0 +1,180 @@ +// WITH_STDLIB + +import kotlinx.serialization.* +import kotlinx.serialization.encoding.* +import kotlinx.serialization.descriptors.* +import kotlinx.serialization.json.* +import kotlin.reflect.KClass +import kotlin.test.* + +@MetaSerializable +@Target(AnnotationTarget.CLASS, AnnotationTarget.PROPERTY) +annotation class MySerializable + +@MetaSerializable +@Target(AnnotationTarget.CLASS, AnnotationTarget.PROPERTY) +annotation class MySerializableWithInfo( + val value: Int, + val kclass: KClass<*> +) + +@MySerializable +class Project1(val name: String, val language: String) + +@MySerializableWithInfo(123, String::class) +class Project2(val name: String, val language: String) + +@Serializable +class Wrapper( + @MySerializableWithInfo(234, Int::class) val project: Project2 +) + +@Serializable +@MySerializableWithInfo(123, String::class) +class Project3(val name: String, val language: String) + +@Serializable(with = MySerializer::class) +@MySerializableWithInfo(123, String::class) +class Project4(val name: String, val language: String) + +@MySerializableWithInfo(123, String::class) +sealed class TestSealed { + @MySerializableWithInfo(123, String::class) + class A(val value1: String) : TestSealed() + @MySerializableWithInfo(123, String::class) + class B(val value2: String) : TestSealed() +} + +@MySerializable +abstract class TestAbstract { + @MySerializableWithInfo(123, String::class) + class A(val value1: String) : TestSealed() + + @MySerializableWithInfo(123, String::class) + class B(val value2: String) : TestSealed() +} + +@MySerializableWithInfo(123, String::class) +enum class TestEnum { Value1, Value2 } + +@MySerializableWithInfo(123, String::class) +object TestObject + +object MySerializer : KSerializer { + + override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("Project4", PrimitiveKind.STRING) + + override fun serialize(encoder: Encoder, value: Project4) = encoder.encodeString("${value.name}:${value.language}") + + override fun deserialize(decoder: Decoder): Project4 { + val params = decoder.decodeString().split(':') + return Project4(params[0], params[1]) + } +} + +fun testMetaSerializable() { + val string = Json.encodeToString(Project1.serializer(), Project1("name", "lang")) + assertEquals("""{"name":"name","language":"lang"}""", string) + + val reconstructed = Json.decodeFromString(Project1.serializer(), string) + assertEquals("name", reconstructed.name) + assertEquals("lang", reconstructed.language) +} + +fun testMetaSerializableWithInfo() { + val string = Json.encodeToString(Project2.serializer(), Project2("name", "lang")) + assertEquals("""{"name":"name","language":"lang"}""", string) + + val reconstructed = Json.decodeFromString(Project2.serializer(), string) + assertEquals("name", reconstructed.name) + assertEquals("lang", reconstructed.language) + + val info = Project2.serializer().descriptor.annotations.filterIsInstance().first() + assertEquals(123, info.value) + assertEquals(String::class, info.kclass) +} + +fun testMetaSerializableOnProperty() { + val info = Wrapper.serializer().descriptor.getElementAnnotations(0).filterIsInstance().first() + assertEquals(234, info.value) + assertEquals(Int::class, info.kclass) +} + +fun testSerializableAndMetaAnnotation() { + val string = Json.encodeToString(Project3.serializer(), Project3("name", "lang")) + assertEquals("""{"name":"name","language":"lang"}""", string) + + val reconstructed = Json.decodeFromString(Project3.serializer(), string) + assertEquals("name", reconstructed.name) + assertEquals("lang", reconstructed.language) + + val info = Project3.serializer().descriptor.annotations.filterIsInstance().first() + assertEquals(123, info.value) + assertEquals(String::class, info.kclass) +} + +fun testCustomSerializerAndMetaAnnotation() { + val string = Json.encodeToString(Project4.serializer(), Project4("name", "lang")) + assertEquals("""name:lang""", string) + + val reconstructed = Json.decodeFromString(Project4.serializer(), string) + assertEquals("name", reconstructed.name) + assertEquals("lang", reconstructed.language) +} + +fun testSealed() { + val serializerA = TestSealed.A.serializer() + val serializerB = TestSealed.B.serializer() + assertNotNull(serializerA) + assertNotNull(serializerB) + + val infoA = serializerA.descriptor.annotations.filterIsInstance().first() + val infoB = serializerB.descriptor.annotations.filterIsInstance().first() + assertEquals(123, infoA.value) + assertEquals(String::class, infoA.kclass) + assertEquals(123, infoB.value) + assertEquals(String::class, infoB.kclass) +} + +fun testAbstract() { + val serializerA = TestAbstract.A.serializer() + val serializerB = TestAbstract.B.serializer() + assertNotNull(serializerA) + assertNotNull(serializerB) + + val infoA = serializerA.descriptor.annotations.filterIsInstance().first() + val infoB = serializerB.descriptor.annotations.filterIsInstance().first() + assertEquals(123, infoA.value) + assertEquals(String::class, infoA.kclass) + assertEquals(123, infoB.value) + assertEquals(String::class, infoB.kclass) +} + +fun testEnum() { + val serializer = TestEnum.serializer() + assertNotNull(serializer) + + val info = serializer.descriptor.annotations.filterIsInstance().first() + assertEquals(123, info.value) + assertEquals(String::class, info.kclass) +} + +fun testObject() { + val serializer = TestObject.serializer() + assertNotNull(serializer) + + val info = serializer.descriptor.annotations.filterIsInstance().first() + assertEquals(123, info.value) + assertEquals(String::class, info.kclass) +} + +fun box(): String { + testMetaSerializable() + testMetaSerializableWithInfo() + testMetaSerializableOnProperty() + testSealed() + testAbstract() + testEnum() + testObject() + return "OK" +} diff --git a/plugins/kotlinx-serialization/tests-gen/org/jetbrains/kotlinx/serialization/runners/SerializationFirBlackBoxTestGenerated.java b/plugins/kotlinx-serialization/tests-gen/org/jetbrains/kotlinx/serialization/runners/SerializationFirBlackBoxTestGenerated.java index e529d3d1abc..49d763afa61 100644 --- a/plugins/kotlinx-serialization/tests-gen/org/jetbrains/kotlinx/serialization/runners/SerializationFirBlackBoxTestGenerated.java +++ b/plugins/kotlinx-serialization/tests-gen/org/jetbrains/kotlinx/serialization/runners/SerializationFirBlackBoxTestGenerated.java @@ -157,6 +157,12 @@ public class SerializationFirBlackBoxTestGenerated extends AbstractSerialization runTest("plugins/kotlinx-serialization/testData/firMembers/inlineClasses.kt"); } + @Test + @TestMetadata("metaSerializable.kt") + public void testMetaSerializable() throws Exception { + runTest("plugins/kotlinx-serialization/testData/firMembers/metaSerializable.kt"); + } + @Test @TestMetadata("multipleProperties.kt") public void testMultipleProperties() throws Exception { diff --git a/plugins/kotlinx-serialization/tests-gen/org/jetbrains/kotlinx/serialization/runners/SerializationFirDiagnosticTestGenerated.java b/plugins/kotlinx-serialization/tests-gen/org/jetbrains/kotlinx/serialization/runners/SerializationFirDiagnosticTestGenerated.java index c6713fbc920..c93bf227f5e 100644 --- a/plugins/kotlinx-serialization/tests-gen/org/jetbrains/kotlinx/serialization/runners/SerializationFirDiagnosticTestGenerated.java +++ b/plugins/kotlinx-serialization/tests-gen/org/jetbrains/kotlinx/serialization/runners/SerializationFirDiagnosticTestGenerated.java @@ -180,6 +180,12 @@ public class SerializationFirDiagnosticTestGenerated extends AbstractSerializati runTest("plugins/kotlinx-serialization/testData/firMembers/inlineClasses.kt"); } + @Test + @TestMetadata("metaSerializable.kt") + public void testMetaSerializable() throws Exception { + runTest("plugins/kotlinx-serialization/testData/firMembers/metaSerializable.kt"); + } + @Test @TestMetadata("multipleProperties.kt") public void testMultipleProperties() throws Exception {