Object serialization for JVM, JS and Native

This commit is contained in:
Leonid Startsev
2019-03-20 19:50:27 +03:00
parent 449c406e24
commit 8a90b82e9b
11 changed files with 137 additions and 76 deletions
@@ -30,6 +30,7 @@ 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.fqNameUnsafe
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.typeUtil.*
import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.enumSerializerId
@@ -48,10 +49,15 @@ fun AbstractSerialGenerator.findAddOnSerializer(propertyType: KotlinType, module
additionalSerializersInScopeOfCurrentFile[propertyType]?.let { return it }
if (propertyType in contextualKClassListInCurrentFile)
return module.getClassFromSerializationPackage(SpecialBuiltins.contextSerializer)
if (propertyType.toClassDescriptor?.annotations?.hasAnnotation(SerializationAnnotations.polymorphicFqName) == true)
return module.getClassFromSerializationPackage(SpecialBuiltins.polymorphicSerializer)
if (propertyType.isMarkedNullable) return findAddOnSerializer(propertyType.makeNotNullable(), module)
return null
}
fun KotlinType.isSerializableObject() =
toClassDescriptor?.run { kind == ClassKind.OBJECT && hasSerializableAnnotationWithoutArgs } == true
@Suppress("FunctionName", "LocalVariableName")
fun AbstractSerialGenerator.getSerialTypeInfo(property: SerializableProperty): SerialTypeInfo {
fun SerializableInfo(serializer: ClassDescriptor?) =
@@ -76,7 +82,7 @@ fun AbstractSerialGenerator.getSerialTypeInfo(property: SerializableProperty): S
)
SerializableInfo(serializer)
}
T.toClassDescriptor?.kind == ClassKind.ENUM_CLASS -> {
T.isEnum() -> {
val serializer = property.module.findClassAcrossModuleDependencies(enumSerializerId)
SerializableInfo(serializer)
}
@@ -88,6 +94,11 @@ fun AbstractSerialGenerator.getSerialTypeInfo(property: SerializableProperty): S
}
}
fun KotlinType.serialName(): String {
val serializableDescriptor = this.toClassDescriptor!!
return serializableDescriptor.annotations.serialNameValue ?: serializableDescriptor.fqNameUnsafe.asString()
}
/**
* Returns class descriptor for ContextSerializer or PolymorphicSerializer
* if [annotations] contains @Contextual or @Polymorphic annotation
@@ -136,6 +147,7 @@ fun findTypeSerializer(module: ModuleDescriptor, kType: KotlinType): ClassDescri
if (userOverride != null) return userOverride.toClassDescriptor
if (kType.isTypeParameter()) return null
if (KotlinBuiltIns.isArray(kType)) return module.getClassFromInternalSerializationPackage(SpecialBuiltins.referenceArraySerializer)
if (kType.isSerializableObject()) return module.getClassFromInternalSerializationPackage(SpecialBuiltins.objectSerializer)
val stdSer = findStandardKotlinTypeSerializer(module, kType) // see if there is a standard serializer
?: findEnumTypeSerializer(module, kType)
if (stdSer != null) return stdSer
@@ -38,11 +38,10 @@ import org.jetbrains.kotlin.types.TypeProjectionImpl
import org.jetbrains.kotlin.types.Variance
import org.jetbrains.kotlin.types.typeUtil.isTypeParameter
import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
import org.jetbrains.kotlinx.serialization.compiler.backend.common.AbstractSerialGenerator
import org.jetbrains.kotlinx.serialization.compiler.backend.common.findTypeSerializerOrContext
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
import org.jetbrains.kotlinx.serialization.compiler.backend.common.serialName
import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.*
import org.jetbrains.kotlinx.serialization.compiler.resolve.*
val BackendContext.externalSymbols: ReferenceSymbolTable get() = ir.symbols.externalSymbolTable
@@ -513,14 +512,32 @@ interface IrBuilderExtension {
module: ModuleDescriptor,
kType: KotlinType,
genericIndex: Int? = null
): IrExpression? = serializerInstance(
enclosingGenerator,
serializableDescriptor,
serializerClassOriginal,
module,
kType,
genericIndex
) {
val prop = enclosingGenerator.localSerializersFieldsDescriptors[it]
irGetField(irGet(dispatchReceiverParameter), compilerContext.localSymbolTable.referenceField(prop).owner)
}
fun IrBuilderWithScope.serializerInstance(
enclosingGenerator: AbstractSerialGenerator,
serializableDescriptor: ClassDescriptor,
serializerClassOriginal: ClassDescriptor?,
module: ModuleDescriptor,
kType: KotlinType,
genericIndex: Int? = null,
genericGetter: (Int) -> IrExpression
): IrExpression? {
val nullableSerClass =
compilerContext.externalSymbols.referenceClass(module.getClassFromInternalSerializationPackage(SpecialBuiltins.nullableSerializer))
if (serializerClassOriginal == null) {
if (genericIndex == null) return null
val prop = enclosingGenerator.localSerializersFieldsDescriptors[genericIndex]
return irGetField(irGet(dispatchReceiverParameter), compilerContext.localSymbolTable.referenceField(prop).owner)
return genericGetter(genericIndex)
}
if (serializerClassOriginal.kind == ClassKind.OBJECT) {
return irGetObject(serializerClassOriginal)
@@ -533,6 +550,10 @@ interface IrBuilderExtension {
args = listOf(classReference(kType))
typeArgs = listOf(kType.toIrType())
}
objectSerializerId -> {
args = listOf(irString(kType.serialName()), irGetObject(kType.toClassDescriptor!!))
typeArgs = listOf(kType.toIrType())
}
enumSerializerId -> {
serializerClass = serializableDescriptor.getClassFromInternalSerializationPackage("CommonEnumSerializer")
args = kType.toClassDescriptor!!.let { enumDesc ->
@@ -556,12 +577,12 @@ interface IrBuilderExtension {
)
val expr = serializerInstance(
enclosingGenerator,
dispatchReceiverParameter,
serializableDescriptor,
argSer,
module,
it.type,
it.type.genericIndex
it.type.genericIndex,
genericGetter
)
?: return null
wrapWithNullableSerializerIfNeeded(module, it.type, expr, nullableSerClass)
@@ -21,7 +21,10 @@ import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.descriptorUtil.module
import org.jetbrains.kotlinx.serialization.compiler.backend.common.SerializableCompanionCodegen
import org.jetbrains.kotlinx.serialization.compiler.backend.common.findTypeSerializer
import org.jetbrains.kotlinx.serialization.compiler.resolve.*
import org.jetbrains.kotlinx.serialization.compiler.resolve.getSerializableClassDescriptorByCompanion
import org.jetbrains.kotlinx.serialization.compiler.resolve.shouldHaveGeneratedMethodsInCompanion
class SerializableCompanionIrGenerator(
val irClass: IrClass,
@@ -82,30 +85,21 @@ class SerializableCompanionIrGenerator(
override fun generateSerializerGetter(methodDescriptor: FunctionDescriptor) =
irClass.contributeFunction(methodDescriptor, fromStubs = true) { getter ->
val serializer = serializableDescriptor.classSerializer!!
val expr = when {
serializer.kind == ClassKind.OBJECT -> irGetObject(serializer)
serializer.isSerializerWhichRequiersKClass() -> {
val serializableType = serializableDescriptor.defaultType
irInvoke(
null,
compilerContext.externalSymbols.referenceConstructor(serializer.unsubstitutedPrimaryConstructor!!),
typeArguments = listOf(serializableType.toIrType()),
valueArguments = listOf(classReference(serializableType)),
returnTypeHint = getter.returnType
)
}
else -> {
val desc = requireNotNull(
findSerializerConstructorForTypeArgumentsSerializers(serializer)
) { "Generated serializer does not have constructor with required number of arguments" }
val ctor = compilerContext.externalSymbols.referenceConstructor(desc)
val typeArgs = getter.typeParameters.map { it.defaultType }
val args: List<IrExpression> = getter.valueParameters.map { irGet(it) }
irInvoke(null, ctor, typeArgs, args, returnTypeHint = getter.returnType)
}
}
val serializer = requireNotNull(
findTypeSerializer(
serializableDescriptor.module,
serializableDescriptor.toSimpleType()
)
)
val args: List<IrExpression> = getter.valueParameters.map { irGet(it) }
val expr = serializerInstance(
this@SerializableCompanionIrGenerator,
serializableDescriptor, serializer,
serializableDescriptor.module,
serializableDescriptor.defaultType
) { args[it] }
patchSerializableClassWithMarkerAnnotation(serializer)
+irReturn(expr)
+irReturn(requireNotNull(expr))
}
}
@@ -30,9 +30,8 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.classId
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.typeUtil.isTypeParameter
import org.jetbrains.kotlinx.serialization.compiler.backend.common.bodyPropertiesDescriptorsMap
import org.jetbrains.kotlinx.serialization.compiler.backend.common.findTypeSerializerOrContext
import org.jetbrains.kotlinx.serialization.compiler.backend.common.primaryConstructorPropertiesDescriptorsMap
import org.jetbrains.kotlinx.serialization.compiler.backend.common.*
import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.objectSerializerId
import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.referenceArraySerializerId
import org.jetbrains.kotlinx.serialization.compiler.resolve.*
@@ -126,31 +125,40 @@ internal fun SerializerJsTranslator.serializerTower(property: SerializableProper
property.type,
property.descriptor.findPsi()
) else null
return serializerInstance(serializer, property.module, property.type, property.genericIndex)
return serializerInstance(context, serializer, property.module, property.type, property.genericIndex)
?.let { expr -> if (property.type.isMarkedNullable) JsNew(nullableSerClass, listOf(expr)) else expr }
}
internal fun SerializerJsTranslator.serializerInstance(
internal fun AbstractSerialGenerator.serializerInstance(
context: TranslationContext,
serializerClass: ClassDescriptor?,
module: ModuleDescriptor,
kType: KotlinType,
genericIndex: Int? = null
genericIndex: Int? = null,
genericGetter: (Int) -> JsExpression = { JsNameRef(context.scope().declareName("${SerialEntityNames.typeArgPrefix}$it"), JsThisRef()) }
): JsExpression? {
val nullableSerClass =
context.translateQualifiedReference(module.getClassFromInternalSerializationPackage(SpecialBuiltins.nullableSerializer))
if (serializerClass == null) {
if (genericIndex == null) return null
return JsNameRef(context.scope().declareName("${SerialEntityNames.typeArgPrefix}$genericIndex"), JsThisRef())
return genericGetter(genericIndex)
}
if (serializerClass.kind == ClassKind.OBJECT) {
return context.serializerObjectGetter(serializerClass)
} else {
var args = if (serializerClass.isSerializerWhichRequiersKClass())
listOf(ExpressionVisitor.getObjectKClass(context, kType.toClassDescriptor!!))
else kType.arguments.map {
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
var args = when {
serializerClass.isSerializerWhichRequiersKClass() -> listOf(
ExpressionVisitor.getObjectKClass(context, kType.toClassDescriptor!!)
)
serializerClass.classId == objectSerializerId -> listOf(
JsStringLiteral(kType.serialName()),
context.serializerObjectGetter(kType.toClassDescriptor!!)
)
else -> kType.arguments.map {
val argSer = findTypeSerializerOrContext(module, it.type, sourceElement = serializerClass.findPsi())
val expr = serializerInstance(context, argSer, module, it.type, it.type.genericIndex, genericGetter) ?: return null
if (it.type.isMarkedNullable) JsNew(nullableSerClass, listOf(expr)) else expr
}
}
if (serializerClass.classId == referenceArraySerializerId)
args = listOf(ExpressionVisitor.getObjectKClass(context, kType.arguments[0].type.toClassDescriptor!!)) + args
@@ -17,40 +17,45 @@
package org.jetbrains.kotlinx.serialization.compiler.backend.js
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.js.backend.ast.*
import org.jetbrains.kotlin.js.backend.ast.JsNameRef
import org.jetbrains.kotlin.js.backend.ast.JsReturn
import org.jetbrains.kotlin.js.translate.context.TranslationContext
import org.jetbrains.kotlin.js.translate.declaration.DeclarationBodyVisitor
import org.jetbrains.kotlin.js.translate.expression.ExpressionVisitor
import org.jetbrains.kotlin.psi.KtPureClassOrObject
import org.jetbrains.kotlin.resolve.descriptorUtil.module
import org.jetbrains.kotlinx.serialization.compiler.backend.common.SerializableCompanionCodegen
import org.jetbrains.kotlinx.serialization.compiler.resolve.*
import org.jetbrains.kotlinx.serialization.compiler.backend.common.findTypeSerializer
import org.jetbrains.kotlinx.serialization.compiler.resolve.getSerializableClassDescriptorByCompanion
import org.jetbrains.kotlinx.serialization.compiler.resolve.shouldHaveGeneratedMethodsInCompanion
import org.jetbrains.kotlinx.serialization.compiler.resolve.toSimpleType
class SerializableCompanionJsTranslator(
declaration: ClassDescriptor,
val translator: DeclarationBodyVisitor,
val context: TranslationContext
): SerializableCompanionCodegen(declaration, context.bindingContext()) {
) : SerializableCompanionCodegen(declaration, context.bindingContext()) {
override fun generateSerializerGetter(methodDescriptor: FunctionDescriptor) {
val f = context.buildFunction(methodDescriptor) {jsFun, context ->
val serializer = serializableDescriptor.classSerializer!!
val stmt: JsExpression = when {
serializer.kind == ClassKind.OBJECT -> context.serializerObjectGetter(serializer)
serializer.isSerializerWhichRequiersKClass() -> JsNew(
context.translateQualifiedReference(serializer),
listOf(ExpressionVisitor.getObjectKClass(context, serializableDescriptor))
val f = context.buildFunction(methodDescriptor) { jsFun, context ->
val serializer = requireNotNull(
findTypeSerializer(
serializableDescriptor.module,
serializableDescriptor.toSimpleType()
)
)
val args = jsFun.parameters.map { JsNameRef(it.name) }
val stmt =
requireNotNull(
serializerInstance(
context,
serializer,
serializableDescriptor.module,
serializableDescriptor.defaultType,
genericGetter = {
args[it]
})
)
else -> {
val args = jsFun.parameters.map { JsNameRef(it.name) }
val ref = context.getInnerNameForDescriptor(
requireNotNull(
findSerializerConstructorForTypeArgumentsSerializers(serializer)
) { "Generated serializer does not have constructor with required number of arguments" })
JsInvocation(ref.makeRef(), args)
}
}
+JsReturn(stmt)
}
translator.addFunction(methodDescriptor, f, null)
@@ -170,7 +170,7 @@ class SerializerJsTranslator(descriptor: ClassDescriptor,
if (property.transient) continue
// output.writeXxxElementValue(classDesc, index, value)
val sti = getSerialTypeInfo(property)
val innerSerial = serializerInstance(sti.serializer, property.module, property.type, property.genericIndex)
val innerSerial = serializerInstance(context, sti.serializer, property.module, property.type, property.genericIndex)
val invocation = if (innerSerial == null) {
val writeFunc =
kOutputClass.getFuncDesc("${CallingConventions.encode}${sti.elementMethodPrefix}${CallingConventions.elementPostfix}").single()
@@ -275,7 +275,7 @@ class SerializerJsTranslator(descriptor: ClassDescriptor,
case(JsIntLiteral(i)) {
// input.readXxxElementValue
val sti = getSerialTypeInfo(property)
val innerSerial = serializerInstance(sti.serializer, property.module, property.type, property.genericIndex)
val innerSerial = serializerInstance(context, sti.serializer, property.module, property.type, property.genericIndex)
val call: JsExpression = if (innerSerial == null) {
val unknownSer = (sti.elementMethodPrefix.isEmpty())
val readFunc =
@@ -156,6 +156,7 @@ internal fun ImplementationBodyCodegen.generateMethod(
internal val enumSerializerId = ClassId(internalPackageFqName, Name.identifier(SpecialBuiltins.enumSerializer))
internal val polymorphicSerializerId = ClassId(packageFqName, Name.identifier(SpecialBuiltins.polymorphicSerializer))
internal val referenceArraySerializerId = ClassId(internalPackageFqName, Name.identifier(SpecialBuiltins.referenceArraySerializer))
internal val objectSerializerId = ClassId(internalPackageFqName, Name.identifier(SpecialBuiltins.objectSerializer))
internal val contextSerializerId = ClassId(packageFqName, Name.identifier(SpecialBuiltins.contextSerializer))
@@ -294,6 +295,13 @@ internal fun AbstractSerialGenerator.stackValueSerializerInstance(codegen: Class
// Reference array serializer still needs serializer for its argument type
instantiate(argSerializers[0])
}
objectSerializerId -> {
val serialName = kType.serialName()
aconst(serialName)
signature.append("Ljava/lang/String;")
StackValue.singleton(kType.toClassDescriptor!!, codegen.typeMapper).put(Type.getType("Ljava/lang/Object;"), iv)
signature.append("Ljava/lang/Object;")
}
// all serializers get arguments with serializers of their generic types
else -> argSerializers.forEach(::instantiate)
}
@@ -20,9 +20,10 @@ import org.jetbrains.kotlin.codegen.ImplementationBodyCodegen
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.resolve.descriptorUtil.module
import org.jetbrains.kotlinx.serialization.compiler.backend.common.SerializableCompanionCodegen
import org.jetbrains.kotlinx.serialization.compiler.resolve.classSerializer
import org.jetbrains.kotlinx.serialization.compiler.backend.common.findTypeSerializer
import org.jetbrains.kotlinx.serialization.compiler.resolve.getSerializableClassDescriptorByCompanion
import org.jetbrains.kotlinx.serialization.compiler.resolve.shouldHaveGeneratedMethodsInCompanion
import org.jetbrains.kotlinx.serialization.compiler.resolve.toSimpleType
class SerializableCompanionCodegenImpl(private val classCodegen: ImplementationBodyCodegen) :
SerializableCompanionCodegen(classCodegen.descriptor, classCodegen.bindingContext) {
@@ -36,7 +37,12 @@ class SerializableCompanionCodegenImpl(private val classCodegen: ImplementationB
}
override fun generateSerializerGetter(methodDescriptor: FunctionDescriptor) {
val serial = serializableDescriptor.classSerializer ?: return
val serial = requireNotNull(
findTypeSerializer(
serializableDescriptor.module,
serializableDescriptor.toSimpleType()
)
)
classCodegen.generateMethod(methodDescriptor) { _, _ ->
stackValueSerializerInstance(
classCodegen,
@@ -18,6 +18,7 @@ package org.jetbrains.kotlinx.serialization.compiler.extensions
import org.jetbrains.kotlin.descriptors.ClassConstructorDescriptor
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor
import org.jetbrains.kotlin.name.Name
@@ -43,7 +44,7 @@ open class SerializationResolveExtension : SyntheticResolveExtension {
}
override fun getSyntheticFunctionNames(thisDescriptor: ClassDescriptor): List<Name> = when {
thisDescriptor.isCompanionObject && getSerializableClassDescriptorByCompanion(thisDescriptor) != null ->
thisDescriptor.isSerializableObject || thisDescriptor.isCompanionObject && getSerializableClassDescriptorByCompanion(thisDescriptor) != null ->
listOf(SerialEntityNames.SERIALIZER_PROVIDER_NAME)
else -> emptyList()
}
@@ -65,7 +66,8 @@ open class SerializationResolveExtension : SyntheticResolveExtension {
}
override fun getSyntheticCompanionObjectNameIfNeeded(thisDescriptor: ClassDescriptor): Name? =
if (thisDescriptor.shouldHaveGeneratedMethodsInCompanion) SpecialNames.DEFAULT_NAME_FOR_COMPANION_OBJECT
if (thisDescriptor.kind == ClassKind.CLASS && thisDescriptor.annotations.hasAnnotation(SerializationAnnotations.serializableAnnotationFqName))
SpecialNames.DEFAULT_NAME_FOR_COMPANION_OBJECT
else null
override fun addSyntheticSupertypes(thisDescriptor: ClassDescriptor, supertypes: MutableList<KotlinType>) {
@@ -105,7 +105,10 @@ val KotlinType?.toClassDescriptor: ClassDescriptor?
}
internal val ClassDescriptor.shouldHaveGeneratedMethodsInCompanion: Boolean
get() = this.kind == ClassKind.CLASS && annotations.hasAnnotation(SerializationAnnotations.serializableAnnotationFqName)
get() = this.isSerializableObject || this.kind == ClassKind.CLASS && annotations.hasAnnotation(SerializationAnnotations.serializableAnnotationFqName)
internal val ClassDescriptor.isSerializableObject: Boolean
get() = kind == ClassKind.OBJECT && hasSerializableAnnotationWithoutArgs
internal val ClassDescriptor.isInternalSerializable: Boolean //todo normal checking
get() {
@@ -159,7 +162,7 @@ internal val ClassDescriptor?.classSerializer: ClassDescriptor?
}
internal val ClassDescriptor.hasCompanionObjectAsSerializer: Boolean
get() = companionObjectDescriptor?.serializerForClass == this.defaultType
get() = isSerializableObject || companionObjectDescriptor?.serializerForClass == this.defaultType
internal fun ClassDescriptor.isSerializerWhichRequiersKClass() = classId in setOf(enumSerializerId, contextSerializerId, polymorphicSerializerId)
@@ -177,6 +180,7 @@ internal val KotlinType.genericIndex: Int?
get() = (this.constructor.declarationDescriptor as? TypeParameterDescriptor)?.index
internal fun getSerializableClassDescriptorByCompanion(thisDescriptor: ClassDescriptor): ClassDescriptor? {
if (thisDescriptor.isSerializableObject) return thisDescriptor
if (!thisDescriptor.isCompanionObject) return null
val classDescriptor = (thisDescriptor.containingDeclaration as? ClassDescriptor) ?: return null
if (!classDescriptor.shouldHaveGeneratedMethodsInCompanion) return null
@@ -76,6 +76,7 @@ object SerialEntityNames {
object SpecialBuiltins {
const val referenceArraySerializer = "ReferenceArraySerializer"
const val objectSerializer = "ObjectSerializer"
const val enumSerializer = "EnumSerializer"
const val polymorphicSerializer = "PolymorphicSerializer"
const val contextSerializer = "ContextSerializer"