Introduce GeneratedSerializer and childSerializers

Descriptors passing on JVM
This commit is contained in:
Leonid Startsev
2018-09-21 20:32:42 +03:00
committed by Leonid Startsev
parent f0e81c6eb8
commit e40383b1ce
6 changed files with 193 additions and 79 deletions
@@ -42,6 +42,7 @@ abstract class SerializerCodegen(
val prop = generateSerializableClassPropertyIfNeeded()
val save = generateSaveIfNeeded()
val load = generateLoadIfNeeded()
generateDescriptorGetterIfNeeded()
// if (save || load || prop)
// generateSerialDesc()
if (serializableDescriptor.declaredTypeParameters.isNotEmpty() && typedSerializerConstructorNotDeclared()) {
@@ -49,6 +50,19 @@ abstract class SerializerCodegen(
}
}
private fun generateDescriptorGetterIfNeeded(): Boolean {
val function = getMemberToGenerate(
serializerDescriptor, SerialEntityNames.GENERATED_DESCRIPTOR_GETTER.identifier,
{ true }, { true }
) ?: return false
generateChildSerializersGetter(function)
return true
}
protected open fun generateChildSerializersGetter(function: FunctionDescriptor) {
// TODO()
}
// checks if user didn't declared constructor (KSerializer<T0>, KSerializer<T1>...) on a KSerializer<T<T0, T1...>>
private fun typedSerializerConstructorNotDeclared(): Boolean {
val serializableImplementationTypeArguments = extractKSerializerArgumentFromImplementation(serializerDescriptor)?.arguments
@@ -55,6 +55,7 @@ import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
// todo: extract packages constants too?
internal val descType = Type.getObjectType("kotlinx/serialization/$SERIAL_DESCRIPTOR_CLASS")
internal val descImplType = Type.getObjectType("kotlinx/serialization/internal/$SERIAL_DESCRIPTOR_CLASS_IMPL")
internal val generatedSerializerType = Type.getObjectType("kotlinx/serialization/internal/${SerialEntityNames.GENERATED_SERIALIZER_CLASS}")
internal val kOutputType = Type.getObjectType("kotlinx/serialization/$STRUCTURE_ENCODER_CLASS")
internal val encoderType = Type.getObjectType("kotlinx/serialization/$ENCODER_CLASS")
internal val decoderType = Type.getObjectType("kotlinx/serialization/$DECODER_CLASS")
@@ -168,6 +169,32 @@ internal fun InstructionAdapter.stackValueSerializerInstanceFromClass(
}
}
internal fun InstructionAdapter.stackValueSerializerInstanceFromSerializerWithoutSti(
codegen: ClassBodyCodegen,
property: SerializableProperty,
serializerCodegen: AbstractSerialGenerator
): Boolean {
val serializer =
property.serializableWith?.toClassDescriptor
?: if (!property.type.isTypeParameter()) serializerCodegen.findTypeSerializerOrContext(
property.module,
property.type,
property.descriptor.annotations,
property.descriptor.findPsi()
) else null
return serializerCodegen.stackValueSerializerInstance(
codegen,
property.module,
property.type,
serializer,
this,
property.genericIndex
) { idx ->
load(0, kSerializerType)
getfield(codegen.typeMapper.mapClass(codegen.descriptor).internalName, "$typeArgPrefix$idx", kSerializerType.descriptor)
}.also { if (it && property.type.isMarkedNullable) wrapStackValueIntoNullableSerializer() }
}
internal fun InstructionAdapter.stackValueSerializerInstanceFromSerializer(codegen: ClassBodyCodegen, sti: JVMSerialTypeInfo, serializerCodegen: AbstractSerialGenerator): Boolean {
return serializerCodegen.stackValueSerializerInstance(
codegen, sti.property.module, sti.property.type,
@@ -240,13 +267,19 @@ internal fun AbstractSerialGenerator.stackValueSerializerInstance(codegen: Class
}
// all serializers get arguments with serializers of their generic types
argSerializers.forEach { (argType, argSerializer) ->
assert(stackValueSerializerInstance(codegen, module, argType, argSerializer, this, argType.genericIndex, genericSerializerFieldGetter))
assert(
stackValueSerializerInstance(
codegen,
module,
argType,
argSerializer,
this,
argType.genericIndex,
genericSerializerFieldGetter
)
)
// wrap into nullable serializer if argType is nullable
if (argType.isMarkedNullable) {
invokestatic("kotlinx/serialization/internal/NullableSerializerKt", "makeNullable", // todo: extract?
"(" + kSerializerType.descriptor + ")" + kSerializerType.descriptor, false)
}
if (argType.isMarkedNullable) wrapStackValueIntoNullableSerializer()
signature.append(kSerializerType.descriptor)
}
signature.append(")V")
@@ -256,6 +289,12 @@ internal fun AbstractSerialGenerator.stackValueSerializerInstance(codegen: Class
return true
}
fun InstructionAdapter.wrapStackValueIntoNullableSerializer() =
invokestatic(
"kotlinx/serialization/internal/NullableSerializerKt", "makeNullable",
"(" + kSerializerType.descriptor + ")" + kSerializerType.descriptor, false
)
//
// ======= Serializers Resolving =======
//
@@ -291,11 +330,13 @@ fun AbstractSerialGenerator.getSerialTypeInfo(property: SerializableProperty, ty
// reference elements
serializer = property.module.findClassAcrossModuleDependencies(referenceArraySerializerId)
}
// primitive elements are not supported yet
// primitive elements are not supported yet
}
}
return JVMSerialTypeInfo(property, Type.getType("Ljava/lang/Object;"),
if (property.type.isMarkedNullable) "Nullable" else "", serializer)
return JVMSerialTypeInfo(
property, Type.getType("Ljava/lang/Object;"),
if (property.type.isMarkedNullable) "Nullable" else "", serializer
)
}
OBJECT -> {
// no explicit serializer for this property. Check other built in types
@@ -312,8 +353,10 @@ fun AbstractSerialGenerator.getSerialTypeInfo(property: SerializableProperty, ty
property.descriptor.annotations,
property.descriptor.findPsi()
)
return JVMSerialTypeInfo(property, Type.getType("Ljava/lang/Object;"),
if (property.type.isMarkedNullable) "Nullable" else "", serializer)
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
}
@@ -40,6 +40,9 @@ class SerializerCodegenImpl(
private val serializerAsmType = codegen.typeMapper.mapClass(codegen.descriptor)
private val serializableAsmType = codegen.typeMapper.mapClass(serializableClass)
// if we have type parameters, descriptor initializing must be performed in constructor
private val staticDescriptor = serializableDescriptor.declaredTypeParameters.isEmpty()
companion object {
fun generateSerializerExtensions(codegen: ImplementationBodyCodegen) {
val serializableClass = getSerializableClassDescriptorBySerializer(codegen.descriptor) ?: return
@@ -55,64 +58,86 @@ class SerializerCodegenImpl(
)
}
codegen.generateMethod(typedConstructorDescriptor) { _, _ ->
var locals: Int = 0
codegen.generateMethod(typedConstructorDescriptor) { _, exprGen ->
load(0, serializerAsmType)
invokespecial("java/lang/Object", "<init>", "()V", false)
serializableDescriptor.declaredTypeParameters.forEachIndexed { i, _ ->
load(0, serializerAsmType)
load(i+1, kSerializerType)
load(++locals, kSerializerType)
putfield(serializerAsmType.internalName, "$typeArgPrefix$i", kSerializerType.descriptor)
}
if (!staticDescriptor) exprGen.generateSerialDescriptor(++locals, false)
areturn(Type.VOID_TYPE)
}
}
override fun generateSerialDesc() {
codegen.v.newField(OtherOrigin(codegen.myClass.psiOrParent), ACC_PRIVATE or ACC_STATIC or ACC_FINAL or ACC_SYNTHETIC,
serialDescField, descType.descriptor, null, null)
// todo: lazy initialization of $$serialDesc that is performed only when save/load is invoked first time
val expr = codegen.createOrGetClInitCodegen()
with(expr.v) {
val classDescVar = 0
anew(descImplType)
dup()
aconst(serialName)
invokespecial(descImplType.internalName, "<init>", "(Ljava/lang/String;)V", false)
store(classDescVar, descImplType)
for (property in orderedProperties) {
if (property.transient) continue
load(classDescVar, descImplType)
aconst(property.name)
invokevirtual(descImplType.internalName, CallingConventions.addElement, "(Ljava/lang/String;)V", false)
// pushing annotations
for ((annotationClass, args, consParams) in property.annotationsWithArguments) {
if (args.size != consParams.size) throw IllegalArgumentException("Can't use arguments with defaults for serializable annotations yet")
load(classDescVar, descImplType)
expr.generateSyntheticAnnotationOnStack(annotationClass, args, consParams)
invokevirtual(
descImplType.internalName,
CallingConventions.addAnnotation,
"(Ljava/lang/annotation/Annotation;)V",
false
)
}
}
// add annotations on class itself
for ((annotationClass, args, consParams) in serializableDescriptor.annotationsWithArguments()) {
private fun ExpressionCodegen.generateSerialDescriptor(descriptorVar: Int, isStatic: Boolean) = with(v) {
anew(descImplType)
dup()
aconst(serialName)
if (isStatic) {
assert(serializerDescriptor.kind == ClassKind.OBJECT) { "Serializer for type without type parameters must be an object" }
// static descriptor means serializer is an object. it is safer to get it from correct field
StackValue.singleton(serializerDescriptor, codegen.typeMapper).put(generatedSerializerType, this)
} else {
load(0, serializerAsmType)
}
invokespecial(descImplType.internalName, "<init>", "(Ljava/lang/String;${generatedSerializerType.descriptor})V", false)
store(descriptorVar, descImplType)
for (property in orderedProperties) {
if (property.transient) continue
load(descriptorVar, descImplType)
aconst(property.name)
iconst(if (property.optional) 1 else 0)
invokevirtual(descImplType.internalName, CallingConventions.addElement, "(Ljava/lang/String;Z)V", false)
// pushing annotations
for ((annotationClass, args, consParams) in property.annotationsWithArguments) {
if (args.size != consParams.size) throw IllegalArgumentException("Can't use arguments with defaults for serializable annotations yet")
load(classDescVar, descImplType)
expr.generateSyntheticAnnotationOnStack(annotationClass, args, consParams)
load(descriptorVar, descImplType)
generateSyntheticAnnotationOnStack(annotationClass, args, consParams)
invokevirtual(
descImplType.internalName,
CallingConventions.addClassAnnotation,
CallingConventions.addAnnotation,
"(Ljava/lang/annotation/Annotation;)V",
false
)
}
load(classDescVar, descImplType)
putstatic(serializerAsmType.internalName, serialDescField, descType.descriptor)
}
// add annotations on class itself
for ((annotationClass, args, consParams) in serializableDescriptor.annotationsWithArguments()) {
if (args.size != consParams.size) throw IllegalArgumentException("Can't use arguments with defaults for serializable annotations yet")
load(descriptorVar, descImplType)
generateSyntheticAnnotationOnStack(annotationClass, args, consParams)
invokevirtual(
descImplType.internalName,
CallingConventions.addClassAnnotation,
"(Ljava/lang/annotation/Annotation;)V",
false
)
}
if (isStatic) {
load(descriptorVar, descImplType)
putstatic(serializerAsmType.internalName, serialDescField, descType.descriptor)
} else {
load(0, serializerAsmType)
load(descriptorVar, descImplType)
putfield(serializerAsmType.internalName, serialDescField, descType.descriptor)
}
}
override fun generateSerialDesc() {
var flags = ACC_PRIVATE or ACC_FINAL or ACC_SYNTHETIC
if (staticDescriptor) flags = flags or ACC_STATIC
codegen.v.newField(
OtherOrigin(codegen.myClass.psiOrParent), flags,
serialDescField, descType.descriptor, null, null
)
// todo: lazy initialization of $$serialDesc ?
if (!staticDescriptor) return
val expr = codegen.createOrGetClInitCodegen()
expr.generateSerialDescriptor(0, true)
}
private fun ExpressionCodegen.generateSyntheticAnnotationOnStack(
@@ -139,18 +164,44 @@ class SerializerCodegenImpl(
}
}
private fun InstructionAdapter.serialCLassDescToLocalVar(classDescVar: Int) {
getstatic(serializerAsmType.internalName, serialDescField, descType.descriptor)
store(classDescVar, descType)
// use null to put value on stack, use number to store it to var
private fun InstructionAdapter.stackSerialClassDesc(classDescVar: Int?) {
if (staticDescriptor)
getstatic(serializerAsmType.internalName, serialDescField, descType.descriptor)
else {
load(0, serializerAsmType)
getfield(serializerAsmType.internalName, serialDescField, descType.descriptor)
}
classDescVar?.let { store(it, descType) }
}
override fun generateSerializableClassProperty(property: PropertyDescriptor) {
codegen.generateMethod(property.getter!!) { _, _ ->
getstatic(serializerAsmType.internalName, serialDescField, descType.descriptor)
stackSerialClassDesc(null)
areturn(descType)
}
}
override fun generateChildSerializersGetter(function: FunctionDescriptor) {
codegen.generateMethod(function) { _, _ ->
val size = orderedProperties.size
iconst(size)
newarray(kSerializerType)
for (i in 0 until size) {
dup() // array
iconst(i) // index
val prop = orderedProperties[i]
stackValueSerializerInstanceFromSerializerWithoutSti(
codegen,
prop,
this@SerializerCodegenImpl
)
astore(kSerializerType)
}
areturn(kSerializerArrayType)
}
}
override fun generateSave(
function: FunctionDescriptor
) {
@@ -159,7 +210,7 @@ class SerializerCodegenImpl(
val outputVar = 1
val objVar = 2
val descVar = 3
serialCLassDescToLocalVar(descVar)
stackSerialClassDesc(descVar)
val objType = signature.valueParameters[1].asmType
// output = output.writeBegin(classDesc, new KSerializer[0])
load(outputVar, encoderType)
@@ -236,7 +287,7 @@ class SerializerCodegenImpl(
val blocksCnt = orderedProperties.size / OPT_MASK_BITS + 1
fun bitMaskOff(i: Int) = bitMaskBase + (i / OPT_MASK_BITS) * OPT_MASK_TYPE.size
val propsStartVar = bitMaskBase + OPT_MASK_TYPE.size * blocksCnt
serialCLassDescToLocalVar(descVar)
stackSerialClassDesc(descVar)
// boolean readAll = false
iconst(0)
store(readAllVar, Type.BOOLEAN_TYPE)
@@ -35,21 +35,26 @@ import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationPackage
fun isKSerializer(type: KotlinType?): Boolean =
type != null && KotlinBuiltIns.isConstructedFromGivenClass(type, SerialEntityNames.KSERIALIZER_NAME_FQ)
type != null && KotlinBuiltIns.isConstructedFromGivenClass(type, SerialEntityNames.KSERIALIZER_NAME_FQ)
fun isGeneratedKSerializer(type: KotlinType?): Boolean =
type != null && KotlinBuiltIns.isConstructedFromGivenClass(type, SerialEntityNames.GENERATED_SERIALIZER_FQ)
fun ClassDescriptor.getKSerializerDescriptor(): ClassDescriptor =
module.findClassAcrossModuleDependencies(ClassId(packageFqName, SerialEntityNames.KSERIALIZER_NAME))!!
module.getClassFromInternalSerializationPackage(SerialEntityNames.GENERATED_SERIALIZER_CLASS.identifier)
fun ClassDescriptor.getKSerializerType(argument: SimpleType): SimpleType {
fun ClassDescriptor.createGeneratedSerializerTypeFor(argument: SimpleType): SimpleType {
val projectionType = Variance.INVARIANT
val types = listOf(TypeProjectionImpl(projectionType, argument))
return KotlinTypeFactory.simpleNotNullType(Annotations.EMPTY, getKSerializerDescriptor(), types)
}
internal fun extractKSerializerArgumentFromImplementation(implementationClass: ClassDescriptor): KotlinType? {
val kSerializerSupertype = implementationClass.typeConstructor.supertypes
.find { isKSerializer(it) } ?: return null
val supertypes = implementationClass.typeConstructor.supertypes
val kSerializerSupertype = supertypes.find { isGeneratedKSerializer(it) }
?: supertypes.find { isKSerializer(it) }
?: return null
return kSerializerSupertype.arguments.first().type
}
@@ -140,7 +145,11 @@ fun getSerializableClassDescriptorByCompanion(thisDescriptor: ClassDescriptor):
fun getSerializableClassDescriptorBySerializer(serializerDescriptor: ClassDescriptor): ClassDescriptor? {
val serializerForClass = serializerDescriptor.annotations.serializerForClass
if (serializerForClass != null) return serializerForClass.toClassDescriptor
if (serializerDescriptor.name != SerialEntityNames.SERIALIZER_CLASS_NAME) return null
if (serializerDescriptor.name !in setOf(
SerialEntityNames.SERIALIZER_CLASS_NAME,
SerialEntityNames.GENERATED_SERIALIZER_CLASS
)
) return null
val classDescriptor = (serializerDescriptor.containingDeclaration as? ClassDescriptor) ?: return null
if (!classDescriptor.isInternalSerializable) return null
return classDescriptor
@@ -187,7 +196,7 @@ private fun ModuleDescriptor.getFromPackage(packageFqName: FqName, classSimpleNa
Name.identifier(classSimpleName)
)
)
) { "Can't locate class $classSimpleName" }
) { "Can't locate class $classSimpleName from package $packageFqName" }
fun ClassDescriptor.getClassFromSerializationPackage(classSimpleName: String) =
requireNotNull(module.findClassAcrossModuleDependencies(ClassId(packageFqName, Name.identifier(classSimpleName)))) {"Can't locate class $classSimpleName"}
@@ -57,7 +57,7 @@ object KSerializerDescriptorResolver {
fun addSerializerSupertypes(classDescriptor: ClassDescriptor, supertypes: MutableList<KotlinType>) {
val serializableClassDescriptor = getSerializableClassDescriptorBySerializer(classDescriptor) ?: return
if (supertypes.none(::isKSerializer)) {
supertypes.add(classDescriptor.getKSerializerType(serializableClassDescriptor.defaultType))
supertypes.add(classDescriptor.createGeneratedSerializerTypeFor(serializableClassDescriptor.defaultType))
}
}
@@ -157,16 +157,15 @@ object KSerializerDescriptorResolver {
fromSupertypes.none { checkParameters(it) && it.modality == Modality.FINAL }
}
if (name == SerialEntityNames.SAVE_NAME &&
shouldAddSerializerFunction { classDescriptor.checkSaveMethodParameters(it.valueParameters) }
) {
result.add(createSaveFunctionDescriptor(thisDescriptor))
}
val isSave = name == SerialEntityNames.SAVE_NAME &&
shouldAddSerializerFunction { classDescriptor.checkSaveMethodParameters(it.valueParameters) }
val isLoad = name == SerialEntityNames.LOAD_NAME &&
shouldAddSerializerFunction { classDescriptor.checkLoadMethodParameters(it.valueParameters) }
val isDescriptorGetter = name == SerialEntityNames.GENERATED_DESCRIPTOR_GETTER &&
shouldAddSerializerFunction { true /* TODO? */ }
if (name == SerialEntityNames.LOAD_NAME &&
shouldAddSerializerFunction { classDescriptor.checkLoadMethodParameters(it.valueParameters) }
) {
result.add(createLoadFunctionDescriptor(thisDescriptor))
if (isSave || isLoad || isDescriptorGetter) {
result.add(doCreateSerializerFunction(thisDescriptor, name))
}
}
@@ -176,12 +175,6 @@ object KSerializerDescriptorResolver {
): PropertyDescriptor =
doCreateSerializerProperty(companionDescriptor, classDescriptor, SerialEntityNames.SERIAL_DESC_FIELD_NAME)
fun createSaveFunctionDescriptor(companionDescriptor: ClassDescriptor): SimpleFunctionDescriptor =
doCreateSerializerFunction(companionDescriptor, SerialEntityNames.SAVE_NAME)
fun createLoadFunctionDescriptor(companionDescriptor: ClassDescriptor): SimpleFunctionDescriptor =
doCreateSerializerFunction(companionDescriptor, SerialEntityNames.LOAD_NAME)
private fun doCreateSerializerProperty(
companionDescriptor: ClassDescriptor,
classDescriptor: ClassDescriptor,
@@ -38,6 +38,9 @@ object SerialEntityNames {
val SERIALIZER_CLASS_NAME = Name.identifier(SERIALIZER_CLASS)
val IMPL_NAME = Name.identifier("Impl")
val GENERATED_SERIALIZER_CLASS = Name.identifier("GeneratedSerializer")
val GENERATED_SERIALIZER_FQ = SerializationPackages.internalPackageFqName.child(GENERATED_SERIALIZER_CLASS)
const val ENCODER_CLASS = "Encoder"
const val STRUCTURE_ENCODER_CLASS = "CompositeEncoder"
const val DECODER_CLASS = "Decoder"
@@ -58,6 +61,7 @@ object SerialEntityNames {
val SERIAL_DESC_FIELD_NAME = Name.identifier(SERIAL_DESC_FIELD)
val SAVE_NAME = Name.identifier(SAVE)
val LOAD_NAME = Name.identifier(LOAD)
val GENERATED_DESCRIPTOR_GETTER = Name.identifier("childSerializers")
val WRITE_SELF_NAME = Name.identifier("write\$Self")
val SERIALIZER_PROVIDER_NAME = Name.identifier("serializer")