Remove auto-applying ContextSerializer

This commit is contained in:
Leonid Startsev
2018-09-19 21:43:56 +03:00
committed by Leonid Startsev
parent 0cfce8bd99
commit 82fa152514
7 changed files with 101 additions and 74 deletions
@@ -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? {
@@ -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)
)
}
@@ -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
}
@@ -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
}
@@ -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)
}
@@ -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"}
@@ -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 {