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 413ffda7ab9..9759da79f06 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 @@ -16,13 +16,19 @@ package org.jetbrains.kotlinx.serialization.compiler.backend.common +import com.intellij.psi.PsiElement import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.codegen.CompilationException import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.descriptors.annotations.Annotations import org.jetbrains.kotlin.js.descriptorUtils.getJetTypeFqName -import org.jetbrains.kotlin.js.descriptorUtils.nameIfStandardType +import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.Name -import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.KtAnonymousInitializer +import org.jetbrains.kotlin.psi.KtParameter +import org.jetbrains.kotlin.psi.KtProperty +import org.jetbrains.kotlin.psi.KtPureClassOrObject import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny import org.jetbrains.kotlin.types.KotlinType @@ -30,7 +36,6 @@ import org.jetbrains.kotlin.types.typeUtil.containsTypeProjectionsInTopLevelArgu import org.jetbrains.kotlin.types.typeUtil.isBoolean import org.jetbrains.kotlin.types.typeUtil.isPrimitiveNumberType import org.jetbrains.kotlin.types.typeUtil.isTypeParameter -import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.contextSerializerId import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.enumSerializerId import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.polymorphicSerializerId import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.referenceArraySerializerId @@ -69,15 +74,31 @@ fun getSerialTypeInfo(property: SerializableProperty): SerialTypeInfo { SerialTypeInfo(property, if (property.type.isMarkedNullable) "Nullable" else "", serializer) } else -> { - val serializer = findTypeSerializerOrContext(property.module, property.type) + val serializer = + findTypeSerializerOrContext(property.module, property.type, property.descriptor.annotations, property.descriptor.findPsi()) SerialTypeInfo(property, if (property.type.isMarkedNullable) "Nullable" else "", serializer) } } } -fun findTypeSerializerOrContext(module: ModuleDescriptor, kType: KotlinType): ClassDescriptor? { - return findTypeSerializer(module, kType) - ?: module.findClassAcrossModuleDependencies(contextSerializerId) +fun findTypeSerializerOrContext( + module: ModuleDescriptor, + kType: KotlinType, + annotations: Annotations = kType.annotations, + sourceElement: PsiElement? = null +): ClassDescriptor? { + if (kType.isTypeParameter()) return null + fun getContextualSerializer() = + if (annotations.hasAnnotation(SerializationAnnotations.contextualFqName)) + module.getClassFromSerializationPackage(SpecialBuiltins.contextSerializer) + else + throw CompilationException( + "Serializer for element of type $kType has not been found.\n" + + "To use context serializer as fallback, explicitly annotate element with @ContextualSerializer", + null, + sourceElement + ) + return findTypeSerializer(module, kType) ?: getContextualSerializer() } fun findTypeSerializer(module: ModuleDescriptor, kType: KotlinType): ClassDescriptor? { diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerIrGenerator.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerIrGenerator.kt index 6cbaf883be7..156ecc2ed61 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerIrGenerator.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerIrGenerator.kt @@ -22,6 +22,7 @@ import org.jetbrains.kotlin.ir.expressions.mapValueParametersIndexed import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.util.* +import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.descriptorUtil.classId import org.jetbrains.kotlin.resolve.descriptorUtil.module @@ -140,7 +141,7 @@ class SerializerIrGenerator(val irClass: IrClass, override val compilerContext: var args = if (serializerClass.classId == enumSerializerId || serializerClass.classId == contextSerializerId) TODO("enum and context serializer") else kType.arguments.map { - val argSer = findTypeSerializerOrContext(module, it.type) + val argSer = findTypeSerializerOrContext(module, it.type, sourceElement = serializerClass.findPsi()) val expr = serializerInstance(argSer, module, it.type, it.type.genericIndex) ?: return null // todo: smth better than constructors[0] ?? if (it.type.isMarkedNullable) irInvoke(null, nullableSerClass.constructors.toList()[0], expr) else expr @@ -208,8 +209,6 @@ class SerializerIrGenerator(val irClass: IrClass, override val compilerContext: writeFunc, irGet(localSerialDesc), irInt(index), - // todo: direct field access? -// irInvoke(irGet(serialObjectSymbol), compilerContext.symbolTable.referenceFunction(property.descriptor.getter!!)) irGetField(irGet(serialObjectSymbol), property.irField) ) } else { @@ -220,8 +219,6 @@ class SerializerIrGenerator(val irClass: IrClass, override val compilerContext: irGet(localSerialDesc), irInt(index), innerSerial, - // todo: direct field access? -// irInvoke(irGet(serialObjectSymbol), compilerContext.symbolTable.referenceFunction(property.descriptor.getter!!)) irGetField(irGet(serialObjectSymbol), property.irField) ) } diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/js/SerializerJsTranslator.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/js/SerializerJsTranslator.kt index 8ef83aa3a49..c018fa232ae 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/js/SerializerJsTranslator.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/js/SerializerJsTranslator.kt @@ -20,6 +20,7 @@ import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.annotations.Annotated import org.jetbrains.kotlin.js.backend.ast.* +import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi import org.jetbrains.kotlin.js.translate.context.Namer import org.jetbrains.kotlin.js.translate.context.TranslationContext import org.jetbrains.kotlin.js.translate.declaration.DeclarationBodyVisitor @@ -186,7 +187,7 @@ class SerializerJsTranslator(descriptor: ClassDescriptor, var args = if (serializerClass.classId == enumSerializerId || serializerClass.classId == contextSerializerId) listOf(createGetKClassExpression(kType.toClassDescriptor!!)) else kType.arguments.map { - val argSer = findTypeSerializerOrContext(module, it.type) + val argSer = findTypeSerializerOrContext(module, it.type, sourceElement = serializerClass.findPsi()) val expr = serializerInstance(argSer, module, it.type, it.type.genericIndex) ?: return null if (it.type.isMarkedNullable) JsNew(nullableSerClass, listOf(expr)) else expr } diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/JVMCodegenUtil.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/JVMCodegenUtil.kt index 866d5fb936c..dc5ecef6cde 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/JVMCodegenUtil.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/JVMCodegenUtil.kt @@ -19,6 +19,7 @@ package org.jetbrains.kotlinx.serialization.compiler.backend.jvm import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.codegen.* import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi import org.jetbrains.kotlin.load.kotlin.TypeMappingMode import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.Name @@ -27,8 +28,9 @@ import org.jetbrains.kotlin.resolve.jvm.AsmTypes import org.jetbrains.kotlin.resolve.jvm.diagnostics.OtherOrigin import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.types.typeUtil.isTypeParameter import org.jetbrains.kotlinx.serialization.compiler.backend.common.SerialTypeInfo -import org.jetbrains.kotlinx.serialization.compiler.backend.common.findTypeSerializer +import org.jetbrains.kotlinx.serialization.compiler.backend.common.findTypeSerializerOrContext import org.jetbrains.kotlinx.serialization.compiler.resolve.* import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.DECODER_CLASS import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.ENCODER_CLASS @@ -141,16 +143,29 @@ internal val referenceArraySerializerId = ClassId(internalPackageFqName, Name.id internal val contextSerializerId = ClassId(packageFqName, Name.identifier(SpecialBuiltins.contextSerializer)) -internal fun InstructionAdapter.stackValueSerializerInstanceFromClass(codegen: ClassBodyCodegen, sti: JVMSerialTypeInfo, varIndexStart: Int): Boolean { - val serializer = sti.serializer ?: return false - return stackValueSerializerInstance(codegen, sti.property.module, sti.property.type, serializer, this, sti.property.genericIndex) { idx -> +internal fun InstructionAdapter.stackValueSerializerInstanceFromClass( + codegen: ClassBodyCodegen, + sti: JVMSerialTypeInfo, + varIndexStart: Int +): Boolean { + val serializer = sti.serializer + return stackValueSerializerInstance( + codegen, + sti.property.module, + sti.property.type, + serializer, + this, + sti.property.genericIndex + ) { idx -> load(varIndexStart + idx, kSerializerType) } } internal fun InstructionAdapter.stackValueSerializerInstanceFromSerializer(codegen: ClassBodyCodegen, sti: JVMSerialTypeInfo): Boolean { - val serializer = sti.serializer ?: return false - return stackValueSerializerInstance(codegen, sti.property.module, sti.property.type, serializer, this, sti.property.genericIndex) { idx -> + return stackValueSerializerInstance( + codegen, sti.property.module, sti.property.type, + sti.serializer, this, sti.property.genericIndex + ) { idx -> load(0, kSerializerType) getfield(codegen.typeMapper.mapClass(codegen.descriptor).internalName, "$typeArgPrefix$idx", kSerializerType.descriptor) } @@ -165,7 +180,7 @@ internal fun stackValueSerializerInstance(codegen: ClassBodyCodegen, module: Mod iv?.run { genericSerializerFieldGetter?.invoke(this, genericIndex) } return true } - val serializer = requireNotNull(maybeSerializer) + val serializer = maybeSerializer ?: return false if (serializer.kind == ClassKind.OBJECT) { // singleton serializer -- just get it if (iv != null) @@ -176,9 +191,21 @@ internal fun stackValueSerializerInstance(codegen: ClassBodyCodegen, module: Mod val argSerializers = kType.arguments.map { projection -> // bail out from stackValueSerializerInstance if any type argument is not serializable val argType = projection.type - val argSerializer = findTypeSerializerOrContext(module, argType, codegen.typeMapper.mapType(argType)) ?: return false + val argSerializer = if (argType.isTypeParameter()) null else { + findTypeSerializerOrContext(module, argType, sourceElement = codegen.descriptor.findPsi()) + ?: return false + } // check if it can be properly serialized with its args recursively - if (!stackValueSerializerInstance(codegen, module, argType, argSerializer, null, argType.genericIndex, genericSerializerFieldGetter)) + if (!stackValueSerializerInstance( + codegen, + module, + argType, + argSerializer, + null, + argType.genericIndex, + genericSerializerFieldGetter + ) + ) return false Pair(argType, argSerializer) } @@ -228,14 +255,20 @@ internal fun stackValueSerializerInstance(codegen: ClassBodyCodegen, module: Mod class JVMSerialTypeInfo( - property: SerializableProperty, - val type: Type, - nn: String, - serializer: ClassDescriptor? = null, - unit: Boolean = false + property: SerializableProperty, + val type: Type, + nn: String, + serializer: ClassDescriptor? = null, + unit: Boolean = false ) : SerialTypeInfo(property, nn, serializer, unit) fun getSerialTypeInfo(property: SerializableProperty, type: Type): JVMSerialTypeInfo { + if (property.type.isTypeParameter()) return JVMSerialTypeInfo( + property, + Type.getType("Ljava/lang/Object;"), + if (property.type.isMarkedNullable) "Nullable" else "", + null + ) when (type.sort) { BOOLEAN, BYTE, SHORT, INT, LONG, FLOAT, DOUBLE, CHAR -> { val name = type.className @@ -266,43 +299,15 @@ fun getSerialTypeInfo(property: SerializableProperty, type: Type): JVMSerialType // todo: more efficient enum support here, but only for enums that don't define custom serializer // otherwise, it is a serializer for some other type val serializer = property.serializableWith?.toClassDescriptor - ?: findTypeSerializerOrContext(property.module, property.type, type) + ?: findTypeSerializerOrContext( + property.module, + property.type, + property.descriptor.annotations, + property.descriptor.findPsi() + ) return JVMSerialTypeInfo(property, Type.getType("Ljava/lang/Object;"), if (property.type.isMarkedNullable) "Nullable" else "", serializer) } else -> throw AssertionError("Unexpected sort for $type") // should not happen } } - -fun findTypeSerializerOrContext(module: ModuleDescriptor, kType: KotlinType, asmType: Type): ClassDescriptor? { - return findTypeSerializer(module, kType) - ?: findStandardAsmTypeSerializer(module, asmType) // otherwise see if there is a standard serializer - ?: module.findClassAcrossModuleDependencies(contextSerializerId) -} - -fun findStandardAsmTypeSerializer(module: ModuleDescriptor, asmType: Type): ClassDescriptor? { - val name = asmType.standardSerializer ?: return null - return module.findClassAcrossModuleDependencies(ClassId(internalPackageFqName, Name.identifier(name))) -} - -internal val org.jetbrains.org.objectweb.asm.Type.standardSerializer: String? get() = when (this.descriptor) { - "Lkotlin/Unit;" -> "UnitSerializer" - "Z", "Ljava/lang/Boolean;" -> "BooleanSerializer" - "B", "Ljava/lang/Byte;" -> "ByteSerializer" - "S", "Ljava/lang/Short;" -> "ShortSerializer" - "I", "Ljava/lang/Integer;" -> "IntSerializer" - "J", "Ljava/lang/Long;" -> "LongSerializer" - "F", "Ljava/lang/Float;" -> "FloatSerializer" - "D", "Ljava/lang/Double;" -> "DoubleSerializer" - "C", "Ljava/lang/Character;" -> "CharSerializer" - "Ljava/lang/String;" -> "StringSerializer" - "Ljava/util/Collection;", "Ljava/util/List;", "Ljava/util/ArrayList;" -> "ArrayListSerializer" - "Ljava/util/Set;", "Ljava/util/LinkedHashSet;" -> "LinkedHashSetSerializer" - "Ljava/util/HashSet;" -> "HashSetSerializer" - "Ljava/util/Map;", "Ljava/util/LinkedHashMap;" -> "LinkedHashMapSerializer" - "Ljava/util/HashMap;" -> "HashMapSerializer" - "Ljava/util/Map\$Entry;" -> "MapEntrySerializer" - "Lkotlin/Pair;" -> "PairSerializer" - "Lkotlin/Triple;" -> "TripleSerializer" - else -> null -} diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializableCodegenImpl.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializableCodegenImpl.kt index b08fbbb703c..f041d5ab986 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializableCodegenImpl.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializableCodegenImpl.kt @@ -28,10 +28,7 @@ import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassOrAny import org.jetbrains.kotlin.resolve.descriptorUtil.module -import org.jetbrains.kotlinx.serialization.compiler.backend.common.SerializableCodegen -import org.jetbrains.kotlinx.serialization.compiler.backend.common.anonymousInitializers -import org.jetbrains.kotlinx.serialization.compiler.backend.common.bodyPropertiesDescriptorsMap -import org.jetbrains.kotlinx.serialization.compiler.backend.common.primaryPropertiesDescriptorsMap +import org.jetbrains.kotlinx.serialization.compiler.backend.common.* import org.jetbrains.kotlinx.serialization.compiler.resolve.* import org.jetbrains.org.objectweb.asm.Label import org.jetbrains.org.objectweb.asm.Type @@ -85,7 +82,7 @@ class SerializableCodegenImpl( load(serialDescI, descType) superTypeArguments.forEach { val genericIdx = serializableDescriptor.defaultType.arguments.indexOf(it).let { if (it == -1) null else it } - val serial = findTypeSerializerOrContext(serializableDescriptor.module, it.type, classCodegen.typeMapper.mapType(it.type)) + val serial = findTypeSerializerOrContext(serializableDescriptor.module, it.type) stackValueSerializerInstance(classCodegen, serializableDescriptor.module, it.type, serial, this, genericIdx) { load(offsetI + it, kSerializerType) } 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 a6186fae555..bbe14f0bdf5 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 @@ -32,7 +32,6 @@ import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf import org.jetbrains.kotlin.types.typeUtil.supertypes import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.KSERIALIZER_CLASS import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationPackages.packageFqName -import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationPackages.internalPackageFqName fun isKSerializer(type: KotlinType?): Boolean = @@ -176,14 +175,19 @@ fun ClassDescriptor.getKSerializerConstructorMarker(): ClassDescriptor = module.findClassAcrossModuleDependencies(ClassId(packageFqName, SerialEntityNames.SERIAL_CTOR_MARKER_NAME))!! fun ModuleDescriptor.getClassFromInternalSerializationPackage(classSimpleName: String) = - requireNotNull( - findClassAcrossModuleDependencies( - ClassId( - internalPackageFqName, - Name.identifier(classSimpleName) - ) + getFromPackage(SerializationPackages.internalPackageFqName, classSimpleName) + +fun ModuleDescriptor.getClassFromSerializationPackage(classSimpleName: String) = + getFromPackage(SerializationPackages.packageFqName, classSimpleName) + +private fun ModuleDescriptor.getFromPackage(packageFqName: FqName, classSimpleName: String) = requireNotNull( + findClassAcrossModuleDependencies( + ClassId( + packageFqName, + Name.identifier(classSimpleName) ) - ) { "Can't locate class $classSimpleName" } + ) +) { "Can't locate class $classSimpleName" } fun ClassDescriptor.getClassFromSerializationPackage(classSimpleName: String) = requireNotNull(module.findClassAcrossModuleDependencies(ClassId(packageFqName, Name.identifier(classSimpleName)))) {"Can't locate class $classSimpleName"} 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 750747adae4..61717b797cf 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 @@ -20,6 +20,8 @@ object SerializationAnnotations { internal val serialOptionalFqName = FqName("kotlinx.serialization.Optional") internal val serialTransientFqName = FqName("kotlinx.serialization.Transient") internal val serialInfoFqName = FqName("kotlinx.serialization.SerialInfo") + + internal val contextualFqName = FqName("kotlinx.serialization.ContextualSerialization") } object SerialEntityNames {