kotlinx.serialization: Support @MetaSerializable (#4583)
This commit is contained in:
@@ -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"))
|
||||
|
||||
+1
-1
@@ -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 {
|
||||
|
||||
+15
-5
@@ -497,7 +497,7 @@ interface IrBuilderExtension {
|
||||
propertyParent: IrClass,
|
||||
fieldName: Name = propertyDescriptor.name,
|
||||
): IrProperty {
|
||||
val irProperty = propertyParent.searchForDeclaration<IrProperty>(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<IrConstructorCall> {
|
||||
if (!(irClass.isInterface || irClass.descriptor.hasSerializableAnnotation)) return emptyList()
|
||||
if (!(irClass.isInterface || irClass.descriptor.hasSerializableOrMetaAnnotation)) return emptyList()
|
||||
val annotationByFq: MutableMap<FqName, IrConstructorCall> = irClass.annotations.associateBy { it.symbol.owner.parentAsClass.descriptor.fqNameSafe }.toMutableMap()
|
||||
for (clazz in irClass.getAllSuperclasses()) {
|
||||
val annotations = clazz.annotations
|
||||
|
||||
+85
-4
@@ -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<KClass> because the codegen doesn't compute JVM signatures for
|
||||
// such cases correctly (because inheriting from annotation classes is prohibited in Kotlin).
|
||||
// Currently it results in an "accidental override" error where a method with return type KClass conflicts with the one with Class.
|
||||
// TODO: support annotation properties of types KClass<...> and Array<KClass<...>>.
|
||||
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<IrClass>()
|
||||
private val annotationToImpl = mutableMapOf<IrClass, IrClass>()
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
+12
-8
@@ -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 ->
|
||||
|
||||
-1
@@ -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)
|
||||
|
||||
+38
-29
@@ -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<ClassDescriptor, SerializableProperties> = 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<String>()
|
||||
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<in KtAnnotationEntry>) {
|
||||
descriptor.onSerializableAnnotation { e ->
|
||||
private fun BindingTrace.reportOnSerializableOrMetaAnnotation(
|
||||
descriptor: ClassDescriptor,
|
||||
error: DiagnosticFactory0<in KtAnnotationEntry>
|
||||
) {
|
||||
descriptor.onSerializableOrMetaAnnotation { e ->
|
||||
report(error.on(e))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal val ClassDescriptor.serializableAnnotationIsUseless: Boolean
|
||||
get() = hasSerializableAnnotationWithoutArgs && !isInternalSerializable && !hasCompanionObjectAsSerializer && !isSerializableEnum() && !isSealedSerializableInterface
|
||||
get() = hasSerializableOrMetaAnnotationWithoutArgs && !isInternalSerializable && !hasCompanionObjectAsSerializer && !isSerializableEnum() && !isSealedSerializableInterface
|
||||
|
||||
-2
@@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+4
-3
@@ -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)
|
||||
}
|
||||
|
||||
+49
-22
@@ -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<ClassDescriptor> {
|
||||
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<ValueParameterDescriptor>): 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<ValueParameterDescriptor>): Boolean =
|
||||
parameters.size == 1
|
||||
parameters.size == 1
|
||||
|
||||
internal fun ClassDescriptor.checkLoadMethodResult(type: KotlinType): Boolean =
|
||||
getSerializableClassDescriptorBySerializer(this)?.defaultType == type
|
||||
|
||||
+2
@@ -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)
|
||||
|
||||
-1
@@ -29,7 +29,6 @@ class SerializableProperties(private val serializableClass: ClassDescriptor, val
|
||||
val isExternallySerializable: Boolean
|
||||
private val primaryConstructorProperties: Map<PropertyDescriptor, Boolean>
|
||||
|
||||
|
||||
init {
|
||||
val descriptorsSequence = serializableClass.unsubstitutedMemberScope.getContributedDescriptors(DescriptorKindFilter.VARIABLES)
|
||||
.asSequence()
|
||||
|
||||
+6
@@ -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 {
|
||||
|
||||
+5
@@ -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");
|
||||
|
||||
Vendored
+186
@@ -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<Project4> {
|
||||
|
||||
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<MySerializableWithInfo>().first()
|
||||
// assertEquals(123, info.value)
|
||||
// assertEquals(String::class, info.kclass)
|
||||
}
|
||||
|
||||
fun testMetaSerializableOnProperty() {
|
||||
// val info = Wrapper.serializer().descriptor.getElementAnnotations(0).filterIsInstance<MySerializableWithInfo>().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<MySerializableWithInfo>().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<MySerializableWithInfo>().first()
|
||||
// val infoB = serializerB.descriptor.annotations.filterIsInstance<MySerializableWithInfo>().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<MySerializableWithInfo>().first()
|
||||
// val infoB = serializerB.descriptor.annotations.filterIsInstance<MySerializableWithInfo>().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<MySerializableWithInfo>().first()
|
||||
// assertEquals(123, info.value)
|
||||
// assertEquals(String::class, info.kclass)
|
||||
}
|
||||
|
||||
fun testObject() {
|
||||
// val serializer = TestObject.serializer()
|
||||
// assertNotNull(serializer)
|
||||
//
|
||||
// val info = serializer.descriptor.annotations.filterIsInstance<MySerializableWithInfo>().first()
|
||||
// assertEquals(123, info.value)
|
||||
// assertEquals(String::class, info.kclass)
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
testMetaSerializable()
|
||||
testMetaSerializableWithInfo()
|
||||
testMetaSerializableOnProperty()
|
||||
testSealed()
|
||||
testAbstract()
|
||||
testEnum()
|
||||
testObject()
|
||||
return "OK"
|
||||
}
|
||||
Reference in New Issue
Block a user