diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/build.gradle.kts b/plugins/kotlin-serialization/kotlin-serialization-compiler/build.gradle.kts index 67ba652cd20..5f6270bed98 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:ir.tree.impl")) compileOnly(project(":js:js.frontend")) compileOnly(project(":js:js.translator")) compileOnly(project(":kotlin-util-klib-metadata")) 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 bc294272e7c..33ec08cc5a3 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 @@ -44,7 +44,7 @@ fun AbstractSerialGenerator.findAddOnSerializer(propertyType: KotlinType, module } fun KotlinType.isGeneratedSerializableObject() = - toClassDescriptor?.run { kind == ClassKind.OBJECT && hasSerializableAnnotationWithoutArgs } == true + toClassDescriptor?.run { kind == ClassKind.OBJECT && hasSerializableOrMetaAnnotationWithoutArgs } == true @Suppress("FunctionName", "LocalVariableName") fun AbstractSerialGenerator.getSerialTypeInfo(property: SerializableProperty): SerialTypeInfo { 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 97c4ad2ef53..f6078c5a408 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 @@ -497,7 +497,7 @@ interface IrBuilderExtension { propertyParent: IrClass, fieldName: Name = propertyDescriptor.name, ): IrProperty { - val irProperty = propertyParent.searchForDeclaration(propertyDescriptor) ?: run { + val irProperty = propertyParent.searchForDeclaration(propertyDescriptor) ?: run { with(propertyDescriptor) { propertyParent.factory.createProperty( propertyParent.startOffset, propertyParent.endOffset, SERIALIZABLE_PLUGIN_ORIGIN, IrPropertySymbolImpl(propertyDescriptor), @@ -520,6 +520,10 @@ interface IrBuilderExtension { return irProperty } + fun IrType.kClassToJClassIfNeeded(): IrType = this + + fun kClassExprToJClassIfNeeded(startOffset: Int, endOffset: Int, irExpression: IrExpression): IrExpression = irExpression + private fun IrClass.generatePropertyBackingFieldIfNeeded( propertyDescriptor: PropertyDescriptor, originProperty: IrProperty, @@ -568,7 +572,10 @@ interface IrBuilderExtension { } irAccessor.body = when (isGetter) { - true -> generateDefaultGetterBody(descriptor as PropertyGetterDescriptor, irAccessor) + true -> { + irAccessor.returnType = irAccessor.returnType.kClassToJClassIfNeeded() + generateDefaultGetterBody(descriptor as PropertyGetterDescriptor, irAccessor) + } false -> generateDefaultSetterBody(descriptor as PropertySetterDescriptor, irAccessor) } @@ -588,6 +595,7 @@ interface IrBuilderExtension { val receiver = generateReceiverExpressionForFieldAccess(irAccessor.dispatchReceiverParameter!!.symbol, property) + val propertyIrType = property.type.toIrType() irBody.statements.add( IrReturnImpl( startOffset, endOffset, compilerContext.irBuiltIns.nothingType, @@ -595,9 +603,11 @@ interface IrBuilderExtension { IrGetFieldImpl( startOffset, endOffset, irProperty.backingField?.symbol ?: error("Property expected to have backing field"), - property.type.toIrType(), + propertyIrType, receiver - ) + ).let { + if (propertyIrType.isKClass()) kClassExprToJClassIfNeeded(startOffset, endOffset, it) else it + } ) ) return irBody @@ -1104,7 +1114,7 @@ interface IrBuilderExtension { } fun collectSerialInfoAnnotations(irClass: IrClass): List { - if (!(irClass.isInterface || irClass.descriptor.hasSerializableAnnotation)) return emptyList() + if (!(irClass.isInterface || irClass.descriptor.hasSerializableOrMetaAnnotation)) return emptyList() val annotationByFq: MutableMap = irClass.annotations.associateBy { it.symbol.owner.parentAsClass.descriptor.fqNameSafe }.toMutableMap() for (clazz in irClass.getAllSuperclasses()) { val annotations = clazz.annotations 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 e9de46f84f7..55be156159f 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 @@ -5,17 +5,29 @@ package org.jetbrains.kotlinx.serialization.compiler.backend.ir +import org.jetbrains.kotlin.backend.common.ir.createImplicitParameterDeclarationWithWrappedDescriptor +import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.descriptors.DescriptorVisibilities +import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET -import org.jetbrains.kotlin.ir.builders.declarations.addConstructor -import org.jetbrains.kotlin.ir.builders.declarations.addValueParameter +import org.jetbrains.kotlin.ir.builders.declarations.* import org.jetbrains.kotlin.ir.declarations.IrClass +import org.jetbrains.kotlin.ir.declarations.IrModuleFragment +import org.jetbrains.kotlin.ir.declarations.IrPackageFragment import org.jetbrains.kotlin.ir.declarations.IrProperty +import org.jetbrains.kotlin.ir.declarations.impl.IrExternalPackageFragmentImpl +import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImpl import org.jetbrains.kotlin.ir.expressions.IrConstructorCall +import org.jetbrains.kotlin.ir.expressions.IrExpression +import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin import org.jetbrains.kotlin.ir.expressions.impl.* +import org.jetbrains.kotlin.ir.symbols.IrClassSymbol +import org.jetbrains.kotlin.ir.symbols.IrPropertySymbol +import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.util.constructors import org.jetbrains.kotlin.ir.util.defaultType import org.jetbrains.kotlin.ir.util.kotlinFqName +import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPluginContext @@ -24,15 +36,18 @@ import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames // This doesn't support annotation arguments of type KClass and Array because the codegen doesn't compute JVM signatures for // such cases correctly (because inheriting from annotation classes is prohibited in Kotlin). // Currently it results in an "accidental override" error where a method with return type KClass conflicts with the one with Class. -// TODO: support annotation properties of types KClass<...> and Array>. class SerialInfoImplJvmIrGenerator( private val context: SerializationPluginContext, + private val moduleFragment: IrModuleFragment, ) : IrBuilderExtension { override val compilerContext: SerializationPluginContext get() = context private val jvmNameClass get() = context.referenceClass(DescriptorUtils.JVM_NAME)!!.owner + private val javaLangClass = createClass(createPackage("java.lang"), "Class", ClassKind.CLASS) + private val javaLangType = javaLangClass.starProjectedType + private val implGenerated = mutableSetOf() private val annotationToImpl = mutableMapOf() @@ -78,7 +93,7 @@ class SerialInfoImplJvmIrGenerator( field.visibility = DescriptorVisibilities.PRIVATE field.origin = SERIALIZABLE_PLUGIN_ORIGIN - val parameter = ctor.addValueParameter(property.name.asString(), getter.returnType) + val parameter = ctor.addValueParameter(property.name.asString(), field.type) ctorBody.statements += IrSetFieldImpl( startOffset, endOffset, field.symbol, IrGetValueImpl(startOffset, endOffset, irClass.thisReceiver!!.symbol), @@ -95,4 +110,70 @@ class SerialInfoImplJvmIrGenerator( ).apply { putValueArgument(0, IrConstImpl.string(UNDEFINED_OFFSET, UNDEFINED_OFFSET, context.irBuiltIns.stringType, name)) } + + override fun IrType.kClassToJClassIfNeeded(): IrType = when { + this.isKClass() -> javaLangType + this.isKClassArray() -> compilerContext.irBuiltIns.arrayClass.typeWith(javaLangType) + else -> this + } + + override fun kClassExprToJClassIfNeeded(startOffset: Int, endOffset: Int, irExpression: IrExpression): IrExpression { + val getterSymbol = kClassJava.owner.getter!!.symbol + return IrCallImpl( + startOffset, endOffset, + javaLangClass.starProjectedType, + getterSymbol, + typeArgumentsCount = getterSymbol.owner.typeParameters.size, + valueArgumentsCount = 0, + origin = IrStatementOrigin.GET_PROPERTY + ).apply { + this.extensionReceiver = irExpression + } + } + + private val jvmName: IrClassSymbol = createClass(createPackage("kotlin.jvm"), "JvmName", ClassKind.ANNOTATION_CLASS) { klass -> + klass.addConstructor().apply { + addValueParameter("name", context.irBuiltIns.stringType) + } + } + + private val kClassJava: IrPropertySymbol = + IrFactoryImpl.buildProperty { + name = Name.identifier("java") + }.apply { + parent = createClass(createPackage("kotlin.jvm"), "JvmClassMappingKt", ClassKind.CLASS).owner + addGetter().apply { + annotations = listOf( + IrConstructorCallImpl.fromSymbolOwner(jvmName.typeWith(), jvmName.constructors.single()).apply { + putValueArgument(0, IrConstImpl.string(UNDEFINED_OFFSET, UNDEFINED_OFFSET, context.irBuiltIns.stringType, "getJavaClass")) + } + ) + addExtensionReceiver(context.irBuiltIns.kClassClass.starProjectedType) + returnType = javaLangClass.starProjectedType + } + }.symbol + + private fun IrType.isKClassArray() = + this is IrSimpleType && isArray() && arguments.single().typeOrNull?.isKClass() == true + + private fun createPackage(packageName: String): IrPackageFragment = + IrExternalPackageFragmentImpl.createEmptyExternalPackageFragment( + moduleFragment.descriptor, + FqName(packageName) + ) + + private fun createClass( + irPackage: IrPackageFragment, + shortName: String, + classKind: ClassKind, + block: (IrClass) -> Unit = {} + ): IrClassSymbol = IrFactoryImpl.buildClass { + name = Name.identifier(shortName) + kind = classKind + modality = Modality.FINAL + }.apply { + parent = irPackage + createImplicitParameterDeclarationWithWrappedDescriptor() + block(this) + }.symbol } 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 6a81ee170ee..53a0094682c 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 @@ -52,17 +52,21 @@ class SerializableIrGenerator( private val addElementFun = serialDescImplClass.referenceFunctionSymbol(CallingConventions.addElement) - private fun IrClass.hasSerializableAnnotationWithoutArgs(): Boolean { - val annot = getAnnotation(SerializationAnnotations.serializableAnnotationFqName) ?: return false - - for (i in 0 until annot.valueArgumentsCount) { - if (annot.getValueArgument(i) != null) return false + 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 } - - return true + val metaAnnotation = annotations + .flatMap { it.symbol.owner.constructedClass.annotations } + .find { it.isAnnotation(SerializationAnnotations.metaSerializableAnnotationFqName) } + return metaAnnotation != null } - private val IrClass.isInternalSerializable: Boolean get() = kind == ClassKind.CLASS && hasSerializableAnnotationWithoutArgs() + private val IrClass.isInternalSerializable: Boolean get() = kind == ClassKind.CLASS && hasSerializableOrMetaAnnotationWithoutArgs() override fun generateInternalConstructor(constructorDescriptor: ClassConstructorDescriptor) = irClass.contributeConstructor(constructorDescriptor) { ctor -> diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializerCodegenImpl.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializerCodegenImpl.kt index 785bece6875..0ed7dcdd9a9 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializerCodegenImpl.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializerCodegenImpl.kt @@ -26,7 +26,6 @@ open class SerializerCodegenImpl( metadataPlugin: SerializationDescriptorSerializerPlugin? ) : SerializerCodegen(codegen.descriptor, codegen.bindingContext, metadataPlugin) { - private val serialDescField = "\$\$serialDesc" protected val serializerAsmType = codegen.typeMapper.mapClass(codegen.descriptor) diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/diagnostic/SerializationPluginDeclarationChecker.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/diagnostic/SerializationPluginDeclarationChecker.kt index cb11e5c4272..77f1b8f4a9d 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/diagnostic/SerializationPluginDeclarationChecker.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/diagnostic/SerializationPluginDeclarationChecker.kt @@ -32,7 +32,6 @@ import org.jetbrains.kotlinx.serialization.compiler.backend.common.bodyPropertie import org.jetbrains.kotlinx.serialization.compiler.backend.common.findTypeSerializerOrContextUnchecked import org.jetbrains.kotlinx.serialization.compiler.backend.common.primaryConstructorPropertiesDescriptorsMap import org.jetbrains.kotlinx.serialization.compiler.resolve.* -import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationAnnotations.serializableAnnotationFqName internal val SERIALIZABLE_PROPERTIES: WritableSlice = Slices.createSimpleSlice() @@ -75,7 +74,13 @@ open class SerializationPluginDeclarationChecker : DeclarationChecker { if (existing.allValueArguments != call.allValueArguments) { if (reported.add(fqname)) { val entry = (existing as? LazyAnnotationDescriptor)?.annotationEntry ?: declaration - trace.report(SerializationErrors.INCONSISTENT_INHERITABLE_SERIALINFO.on(entry, existing.type, clazz.defaultType)) + trace.report( + SerializationErrors.INCONSISTENT_INHERITABLE_SERIALINFO.on( + entry, + existing.type, + clazz.defaultType + ) + ) } } } @@ -89,7 +94,7 @@ open class SerializationPluginDeclarationChecker : DeclarationChecker { // For non-JAR distributions (klib, js) this method is not invoked, since getVersionsForCurrentModule // unable to read from them if (!versions.implementationVersionMatchSupported()) { - descriptor.onSerializableAnnotation { + descriptor.onSerializableOrMetaAnnotation { trace.report( SerializationErrors.PROVIDED_RUNTIME_TOO_LOW.on( it, @@ -104,7 +109,7 @@ open class SerializationPluginDeclarationChecker : DeclarationChecker { private fun checkMinKotlin(versions: VersionReader.RuntimeVersions, descriptor: ClassDescriptor, trace: BindingTrace) { if (versions.currentCompilerMatchRequired()) return - descriptor.onSerializableAnnotation { + descriptor.onSerializableOrMetaAnnotation { trace.report( SerializationErrors.REQUIRED_KOTLIN_TOO_HIGH.on( it, @@ -141,26 +146,25 @@ open class SerializationPluginDeclarationChecker : DeclarationChecker { return false } - if (!descriptor.annotations.hasAnnotation(serializableAnnotationFqName)) return false + if (!descriptor.hasSerializableOrMetaAnnotation) return false if (!serializationPluginEnabledOn(descriptor)) { - trace.reportOnSerializableAnnotation(descriptor, SerializationErrors.PLUGIN_IS_NOT_ENABLED) + trace.reportOnSerializableOrMetaAnnotation(descriptor, SerializationErrors.PLUGIN_IS_NOT_ENABLED) return false } - if (descriptor.isAnonymousObjectOrContained) { - trace.reportOnSerializableAnnotation(descriptor, SerializationErrors.ANONYMOUS_OBJECTS_NOT_SUPPORTED) + trace.reportOnSerializableOrMetaAnnotation(descriptor, SerializationErrors.ANONYMOUS_OBJECTS_NOT_SUPPORTED) return false } if (descriptor.isInner) { - trace.reportOnSerializableAnnotation(descriptor, SerializationErrors.INNER_CLASSES_NOT_SUPPORTED) + trace.reportOnSerializableOrMetaAnnotation(descriptor, SerializationErrors.INNER_CLASSES_NOT_SUPPORTED) return false } if (descriptor.isInlineClass() && !canSupportInlineClasses(descriptor.module, trace)) { - descriptor.onSerializableAnnotation { + descriptor.onSerializableOrMetaAnnotation { trace.report( SerializationErrors.INLINE_CLASSES_NOT_SUPPORTED.on( it, @@ -171,14 +175,14 @@ open class SerializationPluginDeclarationChecker : DeclarationChecker { } return false } - if (!descriptor.hasSerializableAnnotationWithoutArgs) { + if (!descriptor.hasSerializableOrMetaAnnotationWithoutArgs) { // defined custom serializer checkClassWithCustomSerializer(descriptor, declaration, trace) return false } if (descriptor.serializableAnnotationIsUseless) { - trace.reportOnSerializableAnnotation(descriptor, SerializationErrors.SERIALIZABLE_ANNOTATION_IGNORED) + trace.reportOnSerializableOrMetaAnnotation(descriptor, SerializationErrors.SERIALIZABLE_ANNOTATION_IGNORED) return false } @@ -186,7 +190,7 @@ open class SerializationPluginDeclarationChecker : DeclarationChecker { if (!descriptor.isSerializableEnum()) { // enums are inherited from java.lang.Enum and can't be inherited from other classes val superClass = descriptor.getSuperClassOrAny() if (!superClass.isInternalSerializable && superClass.constructors.singleOrNull { it.valueParameters.size == 0 } == null) { - trace.reportOnSerializableAnnotation(descriptor, SerializationErrors.NON_SERIALIZABLE_PARENT_MUST_HAVE_NOARG_CTOR) + trace.reportOnSerializableOrMetaAnnotation(descriptor, SerializationErrors.NON_SERIALIZABLE_PARENT_MUST_HAVE_NOARG_CTOR) return false } } @@ -194,7 +198,7 @@ open class SerializationPluginDeclarationChecker : DeclarationChecker { } private fun checkClassWithCustomSerializer(descriptor: ClassDescriptor, declaration: KtDeclaration, trace: BindingTrace) { - val annotationPsi = descriptor.findSerializableAnnotationDeclaration() + val annotationPsi = descriptor.findSerializableOrMetaAnnotationDeclaration() checkCustomSerializerMatch(descriptor.module, descriptor.defaultType, descriptor, annotationPsi, trace, declaration) checkCustomSerializerIsNotLocal(descriptor.module, descriptor, trace, declaration) } @@ -213,7 +217,7 @@ open class SerializationPluginDeclarationChecker : DeclarationChecker { private fun ClassDescriptor.isSerializableEnumWithMissingSerializer(): Boolean { if (kind != ClassKind.ENUM_CLASS) return false - if (annotations.hasAnnotation(serializableAnnotationFqName)) return false + if (hasSerializableOrMetaAnnotation) return false if (annotations.hasAnySerialAnnotation) return true return enumEntries().any { (it.annotations.hasAnySerialAnnotation) } } @@ -226,12 +230,12 @@ open class SerializationPluginDeclarationChecker : DeclarationChecker { } private fun buildSerializableProperties(descriptor: ClassDescriptor, trace: BindingTrace): SerializableProperties? { - if (!descriptor.annotations.hasAnnotation(serializableAnnotationFqName)) return null + if (!descriptor.hasSerializableOrMetaAnnotation) return null if (!descriptor.isInternalSerializable) return null if (descriptor.hasCompanionObjectAsSerializer) return null // customized by user val props = SerializableProperties(descriptor, trace.bindingContext) - if (!props.isExternallySerializable) trace.reportOnSerializableAnnotation( + if (!props.isExternallySerializable) trace.reportOnSerializableOrMetaAnnotation( descriptor, SerializationErrors.PRIMARY_CONSTRUCTOR_PARAMETER_IS_NOT_A_PROPERTY ) @@ -240,7 +244,7 @@ open class SerializationPluginDeclarationChecker : DeclarationChecker { val namesSet = mutableSetOf() props.serializableProperties.forEach { if (!namesSet.add(it.name)) { - descriptor.onSerializableAnnotation { a -> + descriptor.onSerializableOrMetaAnnotation { a -> trace.report(SerializationErrors.DUPLICATE_SERIAL_NAME.on(a, it.name)) } } @@ -331,11 +335,13 @@ open class SerializationPluginDeclarationChecker : DeclarationChecker { if (type.genericIndex != null) return // type arguments always have serializer stored in class' field val element = ktType?.typeElement if (type.isUnsupportedInlineType() && !canSupportInlineClasses(module, trace)) { - trace.report(SerializationErrors.INLINE_CLASSES_NOT_SUPPORTED.on( - element ?: fallbackElement, - VersionReader.minVersionForInlineClasses.toString(), - VersionReader.getVersionsForCurrentModuleFromTrace(module, trace)?.implementationVersion.toString() - )) + trace.report( + SerializationErrors.INLINE_CLASSES_NOT_SUPPORTED.on( + element ?: fallbackElement, + VersionReader.minVersionForInlineClasses.toString(), + VersionReader.getVersionsForCurrentModuleFromTrace(module, trace)?.implementationVersion.toString() + ) + ) } val serializer = findTypeSerializerOrContextUnchecked(module, type) if (serializer != null) { @@ -380,7 +386,7 @@ open class SerializationPluginDeclarationChecker : DeclarationChecker { val serializerDescriptor = serializerType.toClassDescriptor ?: return if (DescriptorUtils.isLocal(serializerDescriptor)) { - val element = declaration.findSerializableAnnotationDeclaration() ?: declarationElement + val element = declaration.findSerializableOrMetaAnnotationDeclaration() ?: declarationElement trace.report( SerializationErrors.LOCAL_SERIALIZER_USAGE.on( @@ -408,16 +414,19 @@ open class SerializationPluginDeclarationChecker : DeclarationChecker { ) } - private inline fun ClassDescriptor.onSerializableAnnotation(report: (KtAnnotationEntry) -> Unit) { - findSerializableAnnotationDeclaration()?.let(report) + private inline fun ClassDescriptor.onSerializableOrMetaAnnotation(report: (KtAnnotationEntry) -> Unit) { + findSerializableOrMetaAnnotationDeclaration()?.let(report) } - private fun BindingTrace.reportOnSerializableAnnotation(descriptor: ClassDescriptor, error: DiagnosticFactory0) { - descriptor.onSerializableAnnotation { e -> + private fun BindingTrace.reportOnSerializableOrMetaAnnotation( + descriptor: ClassDescriptor, + error: DiagnosticFactory0 + ) { + descriptor.onSerializableOrMetaAnnotation { e -> report(error.on(e)) } } } internal val ClassDescriptor.serializableAnnotationIsUseless: Boolean - get() = hasSerializableAnnotationWithoutArgs && !isInternalSerializable && !hasCompanionObjectAsSerializer && !isSerializableEnum() && !isSealedSerializableInterface + get() = hasSerializableOrMetaAnnotationWithoutArgs && !isInternalSerializable && !hasCompanionObjectAsSerializer && !isSerializableEnum() && !isSealedSerializableInterface diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/extensions/SerializationComponentRegistrar.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/extensions/SerializationComponentRegistrar.kt index d5949df18f7..76b4f75e2e8 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/extensions/SerializationComponentRegistrar.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/extensions/SerializationComponentRegistrar.kt @@ -48,13 +48,11 @@ class SerializationComponentRegistrar : ComponentRegistrar { StorageComponentContainerContributor.registerExtension(project, SerializationPluginComponentContainerContributor()) } - private fun registerProtoExtensions() { SerializationPluginMetadataExtensions.registerAllExtensions(JvmProtoBufUtil.EXTENSION_REGISTRY) SerializationPluginMetadataExtensions.registerAllExtensions(JsSerializerProtocol.extensionRegistry) SerializationPluginMetadataExtensions.registerAllExtensions(KlibMetadataSerializerProtocol.extensionRegistry) } - } } 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 ac3aa742cb1..bf186de2983 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 @@ -49,10 +49,11 @@ class SerializationPluginContext(baseContext: IrPluginContext, val metadataPlugi private class SerializerClassLowering( baseContext: IrPluginContext, - metadataPlugin: SerializationDescriptorSerializerPlugin? + metadataPlugin: SerializationDescriptorSerializerPlugin?, + moduleFragment: IrModuleFragment ) : IrElementTransformerVoid(), ClassLoweringPass { val context: SerializationPluginContext = SerializationPluginContext(baseContext, metadataPlugin) - private val serialInfoJvmGenerator = SerialInfoImplJvmIrGenerator(context).also { context.serialInfoImplJvmIrGenerator = it } + private val serialInfoJvmGenerator = SerialInfoImplJvmIrGenerator(context, moduleFragment).also { context.serialInfoImplJvmIrGenerator = it } override fun lower(irClass: IrClass) { SerializableIrGenerator.generate(irClass, context, context.bindingContext) @@ -72,7 +73,7 @@ open class SerializationLoweringExtension @JvmOverloads constructor( moduleFragment: IrModuleFragment, pluginContext: IrPluginContext ) { - val serializerClassLowering = SerializerClassLowering(pluginContext, metadataPlugin) + val serializerClassLowering = SerializerClassLowering(pluginContext, metadataPlugin, moduleFragment) for (file in moduleFragment.files) serializerClassLowering.runOnFileInOrder(file) } diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/KSerializationUtil.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/KSerializationUtil.kt index 53706f557e1..6ae8b4bbeb7 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/KSerializationUtil.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/KSerializationUtil.kt @@ -8,6 +8,7 @@ package org.jetbrains.kotlinx.serialization.compiler.resolve import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.annotations.Annotated +import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor import org.jetbrains.kotlin.descriptors.annotations.Annotations import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.FqName @@ -23,7 +24,9 @@ import org.jetbrains.kotlin.types.* import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf import org.jetbrains.kotlin.types.typeUtil.representativeUpperBound import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationAnnotations.inheritableSerialInfoFqName +import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationAnnotations.metaSerializableAnnotationFqName import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationAnnotations.serialInfoFqName +import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationAnnotations.serializableAnnotationFqName internal fun isAllowedToHaveAutoGeneratedSerializerMethods( classDescriptor: ClassDescriptor, @@ -47,7 +50,6 @@ internal fun isGeneratedKSerializer(type: KotlinType?): Boolean = internal fun ClassDescriptor.getGeneratedSerializerDescriptor(): ClassDescriptor = module.getClassFromInternalSerializationPackage(SerialEntityNames.GENERATED_SERIALIZER_CLASS.identifier) - internal fun ClassDescriptor.createSerializerTypeFor(argument: SimpleType, baseSerializerInterface: FqName): SimpleType { val projectionType = Variance.INVARIANT val types = listOf(TypeProjectionImpl(projectionType, argument)) @@ -68,13 +70,15 @@ internal val DeclarationDescriptor.serializableWith: KotlinType? get() = annotations.serializableWith(module) internal fun Annotations.serializableWith(module: ModuleDescriptor): KotlinType? = - this.findAnnotationKotlinTypeValue(SerializationAnnotations.serializableAnnotationFqName, module,"with") + this.findAnnotationKotlinTypeValue(serializableAnnotationFqName, module, "with") internal val DeclarationDescriptor.serializerForClass: KotlinType? get() = annotations.findAnnotationKotlinTypeValue(SerializationAnnotations.serializerAnnotationFqName, module, "forClass") internal val ClassDescriptor.isSerialInfoAnnotation: Boolean - get() = annotations.hasAnnotation(serialInfoFqName) || annotations.hasAnnotation(inheritableSerialInfoFqName) + get() = annotations.hasAnnotation(serialInfoFqName) + || annotations.hasAnnotation(inheritableSerialInfoFqName) + || annotations.hasAnnotation(metaSerializableAnnotationFqName) internal val ClassDescriptor.isInheritableSerialInfoAnnotation: Boolean get() = annotations.hasAnnotation(inheritableSerialInfoFqName) @@ -93,7 +97,7 @@ internal val Annotations.serialTransient: Boolean val KotlinType?.toClassDescriptor: ClassDescriptor? @JvmName("toClassDescriptor") get() = this?.constructor?.declarationDescriptor?.let { descriptor -> - when(descriptor) { + when (descriptor) { is ClassDescriptor -> descriptor is TypeParameterDescriptor -> descriptor.representativeUpperBound.toClassDescriptor else -> null @@ -101,26 +105,27 @@ val KotlinType?.toClassDescriptor: ClassDescriptor? } internal val ClassDescriptor.shouldHaveGeneratedMethodsInCompanion: Boolean - get() = this.isSerializableObject || this.isSerializableEnum() || this.kind == ClassKind.CLASS && hasSerializableAnnotation || this.isSealedSerializableInterface + get() = this.isSerializableObject || this.isSerializableEnum() || (this.kind == ClassKind.CLASS && hasSerializableOrMetaAnnotation) || this.isSealedSerializableInterface internal val ClassDescriptor.isSerializableObject: Boolean - get() = kind == ClassKind.OBJECT && hasSerializableAnnotation + get() = kind == ClassKind.OBJECT && hasSerializableOrMetaAnnotation internal val ClassDescriptor.isInternallySerializableObject: Boolean - get() = kind == ClassKind.OBJECT && hasSerializableAnnotationWithoutArgs + get() = kind == ClassKind.OBJECT && hasSerializableOrMetaAnnotationWithoutArgs internal val ClassDescriptor.isSealedSerializableInterface: Boolean - get() = kind == ClassKind.INTERFACE && modality == Modality.SEALED && hasSerializableAnnotation + get() = kind == ClassKind.INTERFACE && modality == Modality.SEALED && hasSerializableOrMetaAnnotation internal val ClassDescriptor.isInternalSerializable: Boolean //todo normal checking get() { if (kind != ClassKind.CLASS) return false - return hasSerializableAnnotationWithoutArgs + return hasSerializableOrMetaAnnotationWithoutArgs } -internal fun ClassDescriptor.isSerializableEnum(): Boolean = kind == ClassKind.ENUM_CLASS && hasSerializableAnnotation +internal fun ClassDescriptor.isSerializableEnum(): Boolean = kind == ClassKind.ENUM_CLASS && hasSerializableOrMetaAnnotation -internal fun ClassDescriptor.isInternallySerializableEnum(): Boolean = kind == ClassKind.ENUM_CLASS && hasSerializableAnnotationWithoutArgs +internal fun ClassDescriptor.isInternallySerializableEnum(): Boolean = + kind == ClassKind.ENUM_CLASS && hasSerializableOrMetaAnnotationWithoutArgs internal val ClassDescriptor.shouldHaveGeneratedSerializer: Boolean get() = (isInternalSerializable && (modality == Modality.FINAL || modality == Modality.OPEN)) || isInternallySerializableEnum() @@ -136,25 +141,47 @@ internal fun ClassDescriptor.enumEntries(): List { internal val Annotations.hasAnySerialAnnotation: Boolean get() = serialNameValue != null || any { it.annotationClass?.isSerialInfoAnnotation == true } -internal val ClassDescriptor.hasSerializableAnnotation - get() = annotations.hasAnnotation(SerializationAnnotations.serializableAnnotationFqName) +internal val ClassDescriptor.hasSerializableOrMetaAnnotation + get() = hasSerializableAnnotation || hasMetaSerializableAnnotation -internal val ClassDescriptor.hasSerializableAnnotationWithoutArgs: Boolean +private val ClassDescriptor.hasSerializableAnnotation + get() = annotations.hasSerializableAnnotation + +private val Annotations.hasSerializableAnnotation + get() = hasAnnotation(serializableAnnotationFqName) + +internal val ClassDescriptor.hasMetaSerializableAnnotation: Boolean + get() = annotations.any { it.isMetaSerializableAnnotation } + +internal val AnnotationDescriptor.isMetaSerializableAnnotation: Boolean + get() = annotationClass?.annotations?.hasAnnotation(metaSerializableAnnotationFqName) ?: false + +internal val ClassDescriptor.hasSerializableOrMetaAnnotationWithoutArgs: Boolean + get() = hasSerializableAnnotationWithoutArgs + || (!annotations.hasSerializableAnnotation && hasMetaSerializableAnnotation) + +private val ClassDescriptor.hasSerializableAnnotationWithoutArgs: Boolean get() { if (!hasSerializableAnnotation) return false // If provided descriptor is lazy, carefully look at psi in order not to trigger full resolve which may be recursive. - // Otherwise, this descriptor is deserialized from another module and it is OK to check value right away. + // Otherwise, this descriptor is deserialized from another module, and it is OK to check value right away. val psi = findSerializableAnnotationDeclaration() ?: return (serializableWith == null) return psi.valueArguments.isEmpty() } -internal fun Annotated.findSerializableAnnotationDeclaration(): KtAnnotationEntry? { - val lazyDesc = annotations.findAnnotation(SerializationAnnotations.serializableAnnotationFqName) as? LazyAnnotationDescriptor +private fun Annotated.findSerializableAnnotationDeclaration(): KtAnnotationEntry? { + val lazyDesc = annotations.findAnnotation(serializableAnnotationFqName) as? LazyAnnotationDescriptor + return lazyDesc?.annotationEntry +} + +internal fun Annotated.findSerializableOrMetaAnnotationDeclaration(): KtAnnotationEntry? { + val lazyDesc = (annotations.findAnnotation(serializableAnnotationFqName) + ?: annotations.firstOrNull { it.isMetaSerializableAnnotation }) as? LazyAnnotationDescriptor return lazyDesc?.annotationEntry } // For abstract classes marked with @Serializable, -// methods are generated anyway although they shouldn't have +// methods are generated anyway, although they shouldn't have // generated $serializer and use Polymorphic one. internal fun ClassDescriptor.isAbstractOrSealedSerializableClass(): Boolean = isInternalSerializable && (modality == Modality.ABSTRACT || modality == Modality.SEALED) @@ -235,18 +262,18 @@ internal fun getSerializableClassDescriptorBySerializer(serializerDescriptor: Cl } internal fun ClassDescriptor.checkSerializableClassPropertyResult(prop: PropertyDescriptor): Boolean = - prop.returnType!!.isSubtypeOf(getClassFromSerializationPackage(SerialEntityNames.SERIAL_DESCRIPTOR_CLASS).toSimpleType(false)) // todo: cache lookup + prop.returnType!!.isSubtypeOf(getClassFromSerializationPackage(SerialEntityNames.SERIAL_DESCRIPTOR_CLASS).toSimpleType(false)) // todo: cache lookup // todo: serialization: do an actual check better that just number of parameters internal fun ClassDescriptor.checkSaveMethodParameters(parameters: List): Boolean = - parameters.size == 2 + parameters.size == 2 internal fun ClassDescriptor.checkSaveMethodResult(type: KotlinType): Boolean = - KotlinBuiltIns.isUnit(type) + KotlinBuiltIns.isUnit(type) // todo: serialization: do an actual check better that just number of parameters internal fun ClassDescriptor.checkLoadMethodParameters(parameters: List): Boolean = - parameters.size == 1 + parameters.size == 1 internal fun ClassDescriptor.checkLoadMethodResult(type: KotlinType): Boolean = getSerializableClassDescriptorBySerializer(this)?.defaultType == type 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 e7ecd500118..14f14010ab2 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 @@ -31,6 +31,7 @@ object SerializationAnnotations { // Also implicitly used in kotlin-native.compiler.backend.native/CodeGenerationInfo.kt internal val serialInfoFqName = FqName("kotlinx.serialization.SerialInfo") internal val inheritableSerialInfoFqName = FqName("kotlinx.serialization.InheritableSerialInfo") + internal val metaSerializableAnnotationFqName = FqName("kotlinx.serialization.MetaSerializable") internal val encodeDefaultFqName = FqName("kotlinx.serialization.EncodeDefault") internal val contextualFqName = FqName("kotlinx.serialization.ContextualSerialization") // this one is deprecated @@ -51,6 +52,7 @@ object SerialEntityNames { const val CACHED_SERIALIZER_PROPERTY = "\$cachedSerializer" // classes + val KCLASS_NAME_FQ = FqName("kotlin.reflect.KClass") val KSERIALIZER_NAME = Name.identifier(KSERIALIZER_CLASS) val SERIAL_CTOR_MARKER_NAME = Name.identifier("SerializationConstructorMarker") val KSERIALIZER_NAME_FQ = SerializationPackages.packageFqName.child(KSERIALIZER_NAME) 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 be561974183..c963820cdfd 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 @@ -29,7 +29,6 @@ class SerializableProperties(private val serializableClass: ClassDescriptor, val val isExternallySerializable: Boolean private val primaryConstructorProperties: Map - init { val descriptorsSequence = serializableClass.unsubstitutedMemberScope.getContributedDescriptors(DescriptorKindFilter.VARIABLES) .asSequence() diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/test/org/jetbrains/kotlinx/serialization/SerializationIrBoxTestGenerated.java b/plugins/kotlin-serialization/kotlin-serialization-compiler/test/org/jetbrains/kotlinx/serialization/SerializationIrBoxTestGenerated.java index 1719df71b49..5f832533dc3 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/test/org/jetbrains/kotlinx/serialization/SerializationIrBoxTestGenerated.java +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/test/org/jetbrains/kotlinx/serialization/SerializationIrBoxTestGenerated.java @@ -31,6 +31,12 @@ public class SerializationIrBoxTestGenerated extends AbstractSerializationIrBoxT runTest("plugins/kotlin-serialization/kotlin-serialization-compiler/testData/boxIr/classSerializerAsObject.kt"); } + @Test + @TestMetadata("metaSerializable.kt") + public void testMetaSerializable() throws Exception { + runTest("plugins/kotlin-serialization/kotlin-serialization-compiler/testData/boxIr/metaSerializable.kt"); + } + @Test @TestMetadata("multimoduleInheritance.kt") public void testMultimoduleInheritance() throws Exception { diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/test/org/jetbrains/kotlinx/serialization/SerializationPluginDiagnosticTestGenerated.java b/plugins/kotlin-serialization/kotlin-serialization-compiler/test/org/jetbrains/kotlinx/serialization/SerializationPluginDiagnosticTestGenerated.java index 7d63ac83601..832869336f3 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/test/org/jetbrains/kotlinx/serialization/SerializationPluginDiagnosticTestGenerated.java +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/test/org/jetbrains/kotlinx/serialization/SerializationPluginDiagnosticTestGenerated.java @@ -59,6 +59,11 @@ public class SerializationPluginDiagnosticTestGenerated extends AbstractSerializ runTest("plugins/kotlin-serialization/kotlin-serialization-compiler/testData/diagnostics/LocalAndAnonymous.kt"); } + @TestMetadata("MetaSerializable.kt") + public void testMetaSerializable() throws Exception { + runTest("plugins/kotlin-serialization/kotlin-serialization-compiler/testData/diagnostics/MetaSerializable.kt"); + } + @TestMetadata("NoSuitableCtorInParent.kt") public void testNoSuitableCtorInParent() throws Exception { runTest("plugins/kotlin-serialization/kotlin-serialization-compiler/testData/diagnostics/NoSuitableCtorInParent.kt"); diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/boxIr/metaSerializable.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/boxIr/metaSerializable.kt new file mode 100644 index 00000000000..39fb7f04329 --- /dev/null +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/boxIr/metaSerializable.kt @@ -0,0 +1,186 @@ +// IGNORE_BACKEND_FIR: JVM_IR +// TARGET_BACKEND: JVM_IR + +// WITH_STDLIB + +import kotlinx.serialization.* +import kotlinx.serialization.* +import kotlinx.serialization.encoding.* +import kotlinx.serialization.descriptors.* +import kotlinx.serialization.json.* +import kotlin.reflect.KClass +import kotlin.test.* + +// TODO: for this test to work, runtime dependency should be updated to (yet unreleased) serialization with @MetaSerializable annotation + +//@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" +}