From 8cd5966c4ad3b268c165d9157cc816ccb1c9f8f6 Mon Sep 17 00:00:00 2001 From: Leonid Startsev Date: Fri, 6 Jul 2018 15:40:55 +0300 Subject: [PATCH] IR-plugin [IR-plugin] Synthetic property declaration in $serializer [IR-plugin] Saving most of simple cases [IP-plugin] Some refactoring and start of work on load func [IP-plugin] Loading simple case without validation [IP-plugin] Rebase to IR types and Native-enabled [IP-plugin] Generating .serializer() accessor in companion [IP-plugin] Basic synthetic deserializing constructor, fixed some todos [IR-plugin] Remove reference to SerialDescriptor.init() since K/N now has freeze-aware lazy --- .../backend/common/SerializableCodegen.kt | 10 +- .../common/SerializableCompanionCodegen.kt | 15 +- .../backend/common/SerializerCodegen.kt | 9 +- .../compiler/backend/common/TypeUtil.kt | 3 +- .../compiler/backend/ir/GeneratorHelpers.kt | 311 ++++++++++++++ .../ir/SerializableCompanionIrGenerator.kt | 46 +++ .../backend/ir/SerializableIrGenerator.kt | 111 +++++ .../backend/ir/SerializerIrGenerator.kt | 378 ++++++++++++++++++ .../js/SerializableCompanionJsTranslator.kt | 10 +- .../backend/js/SerializableJsTranslator.kt | 5 +- .../backend/js/SerializerJsTranslator.kt | 5 +- .../backend/jvm/SerializableCodegenImpl.kt | 2 +- .../jvm/SerializableCompanionCodegenImpl.kt | 2 +- .../SerializationLoweringExtension.kt | 60 +++ .../resolve/KSerializerDescriptorResolver.kt | 2 +- 15 files changed, 940 insertions(+), 29 deletions(-) create mode 100644 plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/GeneratorHelpers.kt create mode 100644 plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializableCompanionIrGenerator.kt create mode 100644 plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializableIrGenerator.kt create mode 100644 plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerIrGenerator.kt create mode 100644 plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/extensions/SerializationLoweringExtension.kt diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/SerializableCodegen.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/SerializableCodegen.kt index d202dfdb6ab..6ffc98dc15c 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/SerializableCodegen.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/SerializableCodegen.kt @@ -19,14 +19,14 @@ package org.jetbrains.kotlinx.serialization.compiler.backend.common import org.jetbrains.kotlin.descriptors.ClassConstructorDescriptor import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor +import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlinx.serialization.compiler.resolve.KSerializerDescriptorResolver import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializableProperties -import org.jetbrains.kotlin.psi.KtPureClassOrObject -import org.jetbrains.kotlin.psi.synthetics.findClassDescriptor -import org.jetbrains.kotlin.resolve.BindingContext -abstract class SerializableCodegen(declaration: KtPureClassOrObject, private val bindingContext: BindingContext) { - protected val serializableDescriptor: ClassDescriptor = declaration.findClassDescriptor(bindingContext) +abstract class SerializableCodegen( + protected val serializableDescriptor: ClassDescriptor, + private val bindingContext: BindingContext +) { protected val properties = SerializableProperties(serializableDescriptor, bindingContext) fun generate() { diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/SerializableCompanionCodegen.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/SerializableCompanionCodegen.kt index b69efb52a8b..31b7df76edc 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/SerializableCompanionCodegen.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/SerializableCompanionCodegen.kt @@ -18,19 +18,18 @@ package org.jetbrains.kotlinx.serialization.compiler.backend.common import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor -import org.jetbrains.kotlin.psi.KtPureClassOrObject -import org.jetbrains.kotlin.psi.synthetics.findClassDescriptor -import org.jetbrains.kotlin.resolve.BindingContext -import org.jetbrains.kotlinx.serialization.compiler.resolve.KSerializerDescriptorResolver +import org.jetbrains.kotlin.psi2ir.findSingleFunction +import org.jetbrains.kotlinx.serialization.compiler.resolve.KSerializerDescriptorResolver.SERIALIZER_PROVIDER_NAME import org.jetbrains.kotlinx.serialization.compiler.resolve.getSerializableClassDescriptorByCompanion -abstract class SerializableCompanionCodegen(declaration: KtPureClassOrObject, bindingContext: BindingContext) { - protected val companionDescriptor: ClassDescriptor = declaration.findClassDescriptor(bindingContext) +abstract class SerializableCompanionCodegen( + protected val companionDescriptor: ClassDescriptor /*declaration: KtPureClassOrObject*/ +) { protected val serializableDescriptor: ClassDescriptor = getSerializableClassDescriptorByCompanion(companionDescriptor)!! fun generate() { - val fser = KSerializerDescriptorResolver.createSerializerGetterDescriptor(companionDescriptor, serializableDescriptor) - generateSerializerGetter(fser) + val serializerGetterDescriptor = companionDescriptor.unsubstitutedMemberScope.findSingleFunction(SERIALIZER_PROVIDER_NAME) + generateSerializerGetter(serializerGetterDescriptor) } protected abstract fun generateSerializerGetter(methodDescriptor: FunctionDescriptor) diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/SerializerCodegen.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/SerializerCodegen.kt index fcdabed30c3..7478e8a1280 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/SerializerCodegen.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/SerializerCodegen.kt @@ -24,6 +24,7 @@ import org.jetbrains.kotlin.psi.KtPureClassOrObject import org.jetbrains.kotlin.psi.synthetics.findClassDescriptor import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe +import org.jetbrains.kotlin.resolve.scopes.getDescriptorsFiltered import org.jetbrains.kotlinx.serialization.compiler.resolve.* import org.jetbrains.kotlinx.serialization.compiler.resolve.KSerializerDescriptorResolver.createTypedSerializerConstructorDescriptor @@ -36,11 +37,12 @@ abstract class SerializerCodegen(declaration: KtPureClassOrObject, bindingContex fun generate() { check(properties.isExternallySerializable) { "Class ${serializableDescriptor.name} is not externally serializable" } + generateSerialDesc() val prop = generateSerializableClassPropertyIfNeeded() val save = generateSaveIfNeeded() val load = generateLoadIfNeeded() - if (save || load || prop) - generateSerialDesc() +// if (save || load || prop) +// generateSerialDesc() if (serializableDescriptor.declaredTypeParameters.isNotEmpty() && typedSerializerConstructorNotDeclared()) { generateGenericFieldsAndConstructor(createTypedSerializerConstructorDescriptor(serializerDescriptor, serializableDescriptor)) } @@ -111,4 +113,7 @@ abstract class SerializerCodegen(declaration: KtPureClassOrObject, bindingContex property.returnType != null && isReturnTypeOk(property) } + + protected fun ClassDescriptor.getFuncDesc(funcName: String): Sequence = + unsubstitutedMemberScope.getDescriptorsFiltered { it == Name.identifier(funcName) }.asSequence().filterIsInstance() } 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 e1f2289def7..afdf503e150 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 @@ -51,7 +51,8 @@ fun getSerialTypeInfo(property: SerializableProperty): SerialTypeInfo { T.isTypeParameter() -> SerialTypeInfo(property, if (property.type.isMarkedNullable) "Nullable" else "", null) T.isPrimitiveNumberType() or T.isBoolean() -> SerialTypeInfo( property, - T.nameIfStandardType.toString().capitalize() + T.getJetTypeFqName(false).removePrefix("kotlin.") // i don't feel so good about it... +// alternative: KotlinBuiltIns.getPrimitiveType(T)!!.typeName.identifier ) KotlinBuiltIns.isString(T) -> SerialTypeInfo(property, "String") KotlinBuiltIns.isUnit(T) -> SerialTypeInfo(property, "Unit", unit = true) diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/GeneratorHelpers.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/GeneratorHelpers.kt new file mode 100644 index 00000000000..52980ba8e52 --- /dev/null +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/GeneratorHelpers.kt @@ -0,0 +1,311 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlinx.serialization.compiler.backend.ir + +import org.jetbrains.kotlin.backend.common.BackendContext +import org.jetbrains.kotlin.backend.common.lower.createIrBuilder +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET +import org.jetbrains.kotlin.ir.builders.* +import org.jetbrains.kotlin.ir.declarations.* +import org.jetbrains.kotlin.ir.declarations.impl.IrPropertyImpl +import org.jetbrains.kotlin.ir.declarations.impl.IrTypeParameterImpl +import org.jetbrains.kotlin.ir.declarations.impl.IrValueParameterImpl +import org.jetbrains.kotlin.ir.expressions.* +import org.jetbrains.kotlin.ir.expressions.impl.* +import org.jetbrains.kotlin.ir.symbols.IrFieldSymbol +import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol +import org.jetbrains.kotlin.ir.symbols.IrValueParameterSymbol +import org.jetbrains.kotlin.ir.symbols.IrValueSymbol +import org.jetbrains.kotlin.ir.types.IrType +import org.jetbrains.kotlin.ir.types.toKotlinType +import org.jetbrains.kotlin.ir.util.TypeTranslator +import org.jetbrains.kotlin.ir.util.declareSimpleFunctionWithOverrides +import org.jetbrains.kotlin.ir.util.withScope +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializableProperty + +interface IrBuilderExtension { + val compilerContext: BackendContext + val translator: TypeTranslator + + fun IrClass.contributeFunction(descriptor: FunctionDescriptor, bodyGen: IrBlockBodyBuilder.(IrFunction) -> Unit) { + val f = compilerContext.symbolTable.declareSimpleFunctionWithOverrides( + this.startOffset, + this.endOffset, + SERIALIZABLE_PLUGIN_ORIGIN, + descriptor + ) + f.parent = this + f.returnType = descriptor.returnType!!.toIrType() + f.createParameterDeclarations() + f.body = compilerContext.createIrBuilder(f.symbol).irBlockBody { bodyGen(f) } + this.addMember(f) + } + + fun IrClass.contributeConstructor( + descriptor: ClassConstructorDescriptor, + bodyGen: IrBlockBodyBuilder.(IrConstructor) -> Unit + ) { + val c = compilerContext.symbolTable.declareConstructor( + this.startOffset, + this.endOffset, + SERIALIZABLE_PLUGIN_ORIGIN, + descriptor + ) + c.parent = this + c.returnType = descriptor.returnType.toIrType() + c.createParameterDeclarations() + c.body = compilerContext.createIrBuilder(c.symbol).irBlockBody { bodyGen(c) } + this.addMember(c) + } + + fun IrBuilderWithScope.irInvoke( + dispatchReceiver: IrExpression? = null, + callee: IrFunctionSymbol, + vararg args: IrExpression, + typeHint: IrType? = null + ): IrCall { + val call = typeHint?.let { irCall(callee, type = it) } ?: irCall(callee) + call.dispatchReceiver = dispatchReceiver + args.forEachIndexed(call::putValueArgument) + return call + } + + fun IrBuilderWithScope.irBinOp(name: Name, lhs: IrExpression, rhs: IrExpression): IrExpression { + val symbol = compilerContext.ir.symbols.getBinaryOperator( + name, + lhs.type.toKotlinType(), + rhs.type.toKotlinType() + ) + return irInvoke(lhs, symbol, rhs) + } + + fun IrBuilderWithScope.irGetObject(classDescriptor: ClassDescriptor) = + IrGetObjectValueImpl( + startOffset, + endOffset, + classDescriptor.defaultType.toIrType(), + compilerContext.symbolTable.referenceClass(classDescriptor) + ) + + fun T.buildWithScope(builder: (T) -> Unit): T = + also { irDeclaration -> + compilerContext.symbolTable.withScope(irDeclaration.descriptor) { + builder(irDeclaration) + } + } + + fun IrBuilderWithScope.irEmptyVararg(forValueParameter: ValueParameterDescriptor) = + IrVarargImpl( + startOffset, + endOffset, + forValueParameter.type.toIrType(), + forValueParameter.varargElementType!!.toIrType() + ) + + class BranchBuilder( + val irWhen: IrWhen, + context: IrGeneratorContext, + scope: Scope, + startOffset: Int, + endOffset: Int + ) : IrBuilderWithScope(context, scope, startOffset, endOffset) { + operator fun IrBranch.unaryPlus() { + irWhen.branches.add(this) + } + } + + fun IrBuilderWithScope.irWhen(typeHint: IrType? = null, block: BranchBuilder.() -> Unit): IrWhen { + val whenExpr = IrWhenImpl(startOffset, endOffset, typeHint ?: compilerContext.irBuiltIns.unitType) + val builder = BranchBuilder(whenExpr, context, scope, startOffset, endOffset) + builder.block() + return whenExpr + } + + fun BranchBuilder.elseBranch(result: IrExpression): IrElseBranch = + IrElseBranchImpl( + IrConstImpl.boolean(result.startOffset, result.endOffset, compilerContext.irBuiltIns.booleanType, true), + result + ) + + fun translateType(ktType: KotlinType): IrType = + translator.translateType(ktType) + + fun KotlinType.toIrType() = translateType(this) + + + val SerializableProperty.irField: IrField + get () = compilerContext.symbolTable.referenceField(this.descriptor).owner + + /* + The rest of the file is mainly copied from FunctionGenerator. + However, I can't use it's directly because all generateSomething methods require KtProperty (psi element) + Also, FunctionGenerator itself has DeclarationGenerator as ctor param, which is a part of psi2ir + (it can be instantiated here, but I don't know how good is that idea) + */ + + fun IrBuilderWithScope.generateAnySuperConstructorCall(toBuilder: IrBlockBodyBuilder) { + val anyConstructor = compilerContext.builtIns.any.constructors.single() + with(toBuilder) { + +IrDelegatingConstructorCallImpl( + startOffset, endOffset, + compilerContext.irBuiltIns.unitType, + compilerContext.symbolTable.referenceConstructor(anyConstructor), + anyConstructor + ) + } + } + + fun generateSimplePropertyWithBackingField( + ownerSymbol: IrValueSymbol, + propertyDescriptor: PropertyDescriptor, + propertyParent: IrClass + ): IrProperty { + val irProperty = IrPropertyImpl( + UNDEFINED_OFFSET, UNDEFINED_OFFSET, + SERIALIZABLE_PLUGIN_ORIGIN, false, + propertyDescriptor + ) + irProperty.parent = propertyParent + irProperty.backingField = generatePropertyBackingField(propertyDescriptor).apply { parent = propertyParent } + val fieldSymbol = irProperty.backingField!!.symbol + irProperty.getter = propertyDescriptor.getter?.let { generatePropertyAccessor(it, fieldSymbol, ownerSymbol) } + ?.apply { parent = propertyParent } + irProperty.setter = propertyDescriptor.setter?.let { generatePropertyAccessor(it, fieldSymbol, ownerSymbol) } + ?.apply { parent = propertyParent } + return irProperty + } + + fun generatePropertyBackingField(propertyDescriptor: PropertyDescriptor): IrField { + return compilerContext.symbolTable.declareField( + UNDEFINED_OFFSET, + UNDEFINED_OFFSET, + SERIALIZABLE_PLUGIN_ORIGIN, + propertyDescriptor, + propertyDescriptor.type.toIrType() + ) + } + + fun generatePropertyAccessor( + descriptor: PropertyAccessorDescriptor, + fieldSymbol: IrFieldSymbol, + ownerSymbol: IrValueSymbol + ): IrSimpleFunction { + return compilerContext.symbolTable.declareSimpleFunctionWithOverrides( + UNDEFINED_OFFSET, UNDEFINED_OFFSET, + SERIALIZABLE_PLUGIN_ORIGIN, descriptor + ).buildWithScope { irAccessor -> + irAccessor.createParameterDeclarations((ownerSymbol as IrValueParameterSymbol).owner) // todo: neat this + irAccessor.returnType = irAccessor.descriptor.returnType!!.toIrType() + irAccessor.body = when (descriptor) { + is PropertyGetterDescriptor -> generateDefaultGetterBody(descriptor, irAccessor, ownerSymbol) + is PropertySetterDescriptor -> generateDefaultSetterBody(descriptor, irAccessor, ownerSymbol) + else -> throw AssertionError("Should be getter or setter: $descriptor") + } + } + + } + + private fun generateDefaultGetterBody( + getter: PropertyGetterDescriptor, + irAccessor: IrSimpleFunction, + ownerSymbol: IrValueSymbol + ): IrBlockBody { + val property = getter.correspondingProperty + + val irBody = IrBlockBodyImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET) + + val receiver = generateReceiverExpressionForFieldAccess(ownerSymbol, property) + + irBody.statements.add( + IrReturnImpl( + UNDEFINED_OFFSET, UNDEFINED_OFFSET, compilerContext.irBuiltIns.nothingType, + irAccessor.symbol, + IrGetFieldImpl( + UNDEFINED_OFFSET, UNDEFINED_OFFSET, + compilerContext.symbolTable.referenceField(property), + property.type.toIrType(), + receiver + ) + ) + ) + return irBody + } + + private fun generateDefaultSetterBody( + setter: PropertySetterDescriptor, + irAccessor: IrSimpleFunction, + ownerSymbol: IrValueSymbol + ): IrBlockBody { + val property = setter.correspondingProperty + + val startOffset = UNDEFINED_OFFSET + val endOffset = UNDEFINED_OFFSET + val irBody = IrBlockBodyImpl(startOffset, endOffset) + + val receiver = generateReceiverExpressionForFieldAccess(ownerSymbol, property) + + val irValueParameter = irAccessor.valueParameters.single() + irBody.statements.add( + IrSetFieldImpl( + UNDEFINED_OFFSET, UNDEFINED_OFFSET, + compilerContext.symbolTable.referenceField(property), + receiver, + IrGetValueImpl(startOffset, endOffset, irValueParameter.type, irValueParameter.symbol), + compilerContext.irBuiltIns.unitType + ) + ) + return irBody + } + + fun generateReceiverExpressionForFieldAccess( + ownerSymbol: IrValueSymbol, + property: PropertyDescriptor + ): IrExpression { + val containingDeclaration = property.containingDeclaration + return when (containingDeclaration) { + is ClassDescriptor -> + IrGetValueImpl( + UNDEFINED_OFFSET, UNDEFINED_OFFSET, +// symbolTable.referenceValue(containingDeclaration.thisAsReceiverParameter) + ownerSymbol + ) + else -> throw AssertionError("Property must be in class") + } + } + + // todo: delet zis + fun IrFunction.createParameterDeclarations(receiver: IrValueParameter? = null) { + fun ParameterDescriptor.irValueParameter() = IrValueParameterImpl( + UNDEFINED_OFFSET, UNDEFINED_OFFSET, + SERIALIZABLE_PLUGIN_ORIGIN, + this, + type.toIrType(), + (this as? ValueParameterDescriptor)?.varargElementType?.toIrType() + ).also { + it.parent = this@createParameterDeclarations + } + + dispatchReceiverParameter = receiver ?: (descriptor.dispatchReceiverParameter?.irValueParameter()) + extensionReceiverParameter = descriptor.extensionReceiverParameter?.irValueParameter() + + assert(valueParameters.isEmpty()) + descriptor.valueParameters.mapTo(valueParameters) { it.irValueParameter() } + + assert(typeParameters.isEmpty()) + descriptor.typeParameters.mapTo(typeParameters) { + IrTypeParameterImpl( + UNDEFINED_OFFSET, UNDEFINED_OFFSET, + SERIALIZABLE_PLUGIN_ORIGIN, + it + ).also { typeParameter -> + typeParameter.parent = this + } + } + } +} diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializableCompanionIrGenerator.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializableCompanionIrGenerator.kt new file mode 100644 index 00000000000..8cf12c622ba --- /dev/null +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializableCompanionIrGenerator.kt @@ -0,0 +1,46 @@ +package org.jetbrains.kotlinx.serialization.compiler.backend.ir + +import org.jetbrains.kotlin.backend.common.BackendContext +import org.jetbrains.kotlin.descriptors.ClassKind +import org.jetbrains.kotlin.descriptors.FunctionDescriptor +import org.jetbrains.kotlin.ir.builders.irReturn +import org.jetbrains.kotlin.ir.declarations.IrClass +import org.jetbrains.kotlin.ir.expressions.IrExpression +import org.jetbrains.kotlin.ir.util.TypeTranslator +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlinx.serialization.compiler.backend.common.SerializableCompanionCodegen +import org.jetbrains.kotlinx.serialization.compiler.resolve.classSerializer +import org.jetbrains.kotlinx.serialization.compiler.resolve.getSerializableClassDescriptorByCompanion +import org.jetbrains.kotlinx.serialization.compiler.resolve.isInternalSerializable +import org.jetbrains.kotlinx.serialization.compiler.resolve.toClassDescriptor + +class SerializableCompanionIrGenerator( + val irClass: IrClass, + override val compilerContext: BackendContext, + bindingContext: BindingContext +) : SerializableCompanionCodegen(irClass.descriptor), IrBuilderExtension { + override val translator: TypeTranslator = TypeTranslator(compilerContext.symbolTable) + + companion object { + fun generate(irClass: IrClass, + context: BackendContext, + bindingContext: BindingContext) { + val companionDescriptor = irClass.descriptor + val serializableClass = getSerializableClassDescriptorByCompanion(companionDescriptor) ?: return + if (serializableClass.isInternalSerializable) + SerializableCompanionIrGenerator(irClass, context, bindingContext).generate() + } + } + + override fun generateSerializerGetter(methodDescriptor: FunctionDescriptor) = irClass.contributeFunction(methodDescriptor) { getter -> + val serializer = serializableDescriptor.classSerializer?.toClassDescriptor!! + val expr = if (serializer.kind == ClassKind.OBJECT) { + irGetObject(serializer) + } else { + val ctor = compilerContext.symbolTable.referenceConstructor(serializer.unsubstitutedPrimaryConstructor!!) + val args: List = emptyList() // todo + irInvoke(null, ctor, *args.toTypedArray()) + } + +irReturn(expr) + } +} \ No newline at end of file diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializableIrGenerator.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializableIrGenerator.kt new file mode 100644 index 00000000000..24d48a31a20 --- /dev/null +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializableIrGenerator.kt @@ -0,0 +1,111 @@ +package org.jetbrains.kotlinx.serialization.compiler.backend.ir + +import org.jetbrains.kotlin.backend.common.BackendContext +import org.jetbrains.kotlin.backend.common.lower.irThrow +import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.descriptors.ClassConstructorDescriptor +import org.jetbrains.kotlin.descriptors.FunctionDescriptor +import org.jetbrains.kotlin.descriptors.ParameterDescriptor +import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor +import org.jetbrains.kotlin.ir.builders.* +import org.jetbrains.kotlin.ir.declarations.IrClass +import org.jetbrains.kotlin.ir.declarations.IrField +import org.jetbrains.kotlin.ir.expressions.IrExpression +import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin +import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl +import org.jetbrains.kotlin.ir.util.TypeTranslator +import org.jetbrains.kotlin.ir.util.constructors +import org.jetbrains.kotlin.ir.util.deepCopyWithSymbols +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassOrAny +import org.jetbrains.kotlin.util.OperatorNameConventions +import org.jetbrains.kotlinx.serialization.compiler.backend.common.SerializableCodegen +import org.jetbrains.kotlinx.serialization.compiler.resolve.getClassFromSerializationPackage +import org.jetbrains.kotlinx.serialization.compiler.resolve.isInternalSerializable + +class SerializableIrGenerator( + val irClass: IrClass, + override val compilerContext: BackendContext, + bindingContext: BindingContext +) : SerializableCodegen(irClass.descriptor, bindingContext), IrBuilderExtension { + override val translator: TypeTranslator = TypeTranslator(compilerContext.symbolTable) + + + override fun generateInternalConstructor(constructorDescriptor: ClassConstructorDescriptor) = + irClass.contributeConstructor(constructorDescriptor) { ctor -> + val original = irClass.constructors.singleOrNull { it.isPrimary } ?: throw IllegalStateException("Serializable class must have single primary constructor") + // default arguments of original constructor + val defaultsMap: Map = original.valueParameters.associate { it.descriptor to it.defaultValue?.expression } + + fun transformFieldInitializer(f: IrField): IrExpression? { + val i = f.initializer?.expression ?: return null + return if (i is IrGetValueImpl && i.origin == IrStatementOrigin.INITIALIZE_PROPERTY_FROM_PARAMETER) { + // this is a primary constructor property, use corresponding default of value parameter + defaultsMap.getValue(i.descriptor as ParameterDescriptor) + } else { + i + } + } + + // Missing field exception parts + val exceptionCtor = + serializableDescriptor.getClassFromSerializationPackage("MissingFieldException") + .unsubstitutedPrimaryConstructor!! + val exceptionCtorRef = compilerContext.symbolTable.referenceConstructor(exceptionCtor) + val exceptionType = exceptionCtor.returnType.toIrType() + + val thiz = irClass.thisReceiver!! + if (KotlinBuiltIns.isAny(irClass.descriptor.getSuperClassOrAny())) + generateAnySuperConstructorCall(toBuilder = this@contributeConstructor) + else + TODO("Serializable classes with inheritance") + val seenVar = ctor.valueParameters[0] + + for ((index, prop) in properties.serializableProperties.withIndex()) { + val paramRef = ctor.valueParameters[index + 1] + // assign this.a = a in else branch + val assignParamExpr = irSetField(irGet(thiz), prop.irField, irGet(paramRef)) + + val ifNotSeenExpr: IrExpression = if (prop.optional) { + val initializerBody = + requireNotNull(transformFieldInitializer(prop.irField)) { "Optional value without an initializer" } // todo: filter abstract here + irSetField(irGet(thiz), prop.irField, initializerBody) + } else { + irThrow(irInvoke(null, exceptionCtorRef, irString(prop.name), typeHint = exceptionType)) + } + + val propNotSeenTest = + irEquals( + irInt(0), + irBinOp(OperatorNameConventions.AND, irGet(seenVar), irInt(1 shl (index % 32))) + ) + + +irIfThenElse(compilerContext.irBuiltIns.unitType, propNotSeenTest, ifNotSeenExpr, assignParamExpr) + } + + // todo: transient initializers and init blocks + // remaining initalizers of variables +// val serialDescs = properties.serializableProperties.map { it.descriptor }.toSet() +// irClass.declarations.asSequence() +// .filterIsInstance() +// .filter { it.descriptor !in serialDescs } +// .filter { it.backingField != null } +// .mapNotNull { prop -> prop.backingField!!.initializer?.let { prop to it.expression } } +// .forEach { (prop, expr) -> +irSetField(irGet(thiz), prop.backingField!!, expr) } + } + + override fun generateWriteSelfMethod(methodDescriptor: FunctionDescriptor) { + // no-op + } + + companion object { + fun generate( + irClass: IrClass, + context: BackendContext, + bindingContext: BindingContext + ) { + if (irClass.descriptor.isInternalSerializable) + SerializableIrGenerator(irClass, context, bindingContext).generate() + } + } +} \ No newline at end of file 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 new file mode 100644 index 00000000000..4859b712dcc --- /dev/null +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerIrGenerator.kt @@ -0,0 +1,378 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlinx.serialization.compiler.backend.ir + +import org.jetbrains.kotlin.backend.common.BackendContext +import org.jetbrains.kotlin.backend.common.lower.createIrBuilder +import org.jetbrains.kotlin.backend.common.lower.irThrow +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.ir.builders.* +import org.jetbrains.kotlin.ir.declarations.* +import org.jetbrains.kotlin.ir.expressions.IrBlockBody +import org.jetbrains.kotlin.ir.expressions.IrExpression +import org.jetbrains.kotlin.ir.expressions.impl.IrBranchImpl +import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl +import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl +import org.jetbrains.kotlin.ir.expressions.impl.IrVarargImpl +import org.jetbrains.kotlin.ir.expressions.mapValueParameters +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.TypeTranslator +import org.jetbrains.kotlin.ir.util.constructors +import org.jetbrains.kotlin.ir.util.withScope +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.descriptorUtil.classId +import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.util.OperatorNameConventions +import org.jetbrains.kotlinx.serialization.compiler.backend.common.SerializerCodegen +import org.jetbrains.kotlinx.serialization.compiler.backend.common.findTypeSerializerOrContext +import org.jetbrains.kotlinx.serialization.compiler.backend.common.getSerialTypeInfo +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.referenceArraySerializerId +import org.jetbrains.kotlinx.serialization.compiler.resolve.* + +// Is creating synthetic origin is a good idea or not? +object SERIALIZABLE_PLUGIN_ORIGIN : IrDeclarationOriginImpl("SERIALIZER") + +class SerializerIrGenerator(val irClass: IrClass, override val compilerContext: BackendContext, bindingContext: BindingContext) : + SerializerCodegen(irClass.descriptor, bindingContext), IrBuilderExtension { + + override val translator: TypeTranslator = TypeTranslator(compilerContext.symbolTable) + + override fun generateSerialDesc() { + val desc: PropertyDescriptor = serialDescPropertyDescriptor ?: return + val serialDescImplClass = serializerDescriptor + .getClassFromInternalSerializationPackage("SerialClassDescImpl") + val serialDescImplConstructor = serialDescImplClass + .unsubstitutedPrimaryConstructor!! + + val addFuncS = serialDescImplClass.referenceMethod("addElement") + + val thisAsReceiverParameter = irClass.thisReceiver!! + lateinit var prop: IrProperty + + // how to (auto)create backing field and getter/setter? + compilerContext.symbolTable.withScope(irClass.descriptor) { + + introduceValueParameter(thisAsReceiverParameter) + prop = generateSimplePropertyWithBackingField(thisAsReceiverParameter.symbol, desc, irClass) + irClass.addMember(prop) + } + + compilerContext.symbolTable.declareAnonymousInitializer( + irClass.startOffset, irClass.endOffset, SERIALIZABLE_PLUGIN_ORIGIN, irClass.descriptor + ).buildWithScope { initIrBody -> + val ctor = irClass.declarations.filterIsInstance().singleOrNull() + val serialClassDescImplCtor = compilerContext.symbolTable.referenceConstructor(serialDescImplConstructor) + compilerContext.symbolTable.withScope(initIrBody.descriptor) { + initIrBody.body = compilerContext.createIrBuilder(initIrBody.symbol).irBlockBody { + val localDesc = irTemporary( + irCall( + serialClassDescImplCtor, + type = serialDescImplConstructor.returnType.toIrType() + ).mapValueParameters { irString(serialName) }, + nameHint = "serialDesc" + ) + + fun addFieldCall(fieldName: String) = + irCall(addFuncS, type = compilerContext.irBuiltIns.unitType).apply { + dispatchReceiver = irGet(localDesc) + }.mapValueParameters { irString(fieldName) } + + for (classProp in orderedProperties) { + if (classProp.transient) continue + +addFieldCall(classProp.name) + // serialDesc.pushAnnotation(...) todo + } + + +irSetField( + generateReceiverExpressionForFieldAccess( + thisAsReceiverParameter.symbol, + serialDescPropertyDescriptor + ), + prop.backingField!!, + irGet(localDesc) + ) + } + // workaround for KT-25353 +// irClass.addMember(initIrBody) + (ctor?.body as? IrBlockBody)?.statements?.addAll(initIrBody.body.statements) + } + } + } + + override fun generateGenericFieldsAndConstructor(typedConstructorDescriptor: ConstructorDescriptor) { +// TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + + override fun generateSerializableClassProperty(property: PropertyDescriptor) { +// TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + + private fun IrBuilderWithScope.serializerInstance( + serializerClass: ClassDescriptor?, + module: ModuleDescriptor, + kType: KotlinType, + genericIndex: Int? = null + ): IrExpression? { + val nullableSerClass = + compilerContext.symbolTable.referenceClass(module.getClassFromInternalSerializationPackage("NullableSerializer")) + if (serializerClass == null) { + if (genericIndex == null) return null + return TODO("Saved serializer for generic argument") + } + if (serializerClass.kind == ClassKind.OBJECT) { + return irGetObject(serializerClass) + } else { + 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 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 + } + if (serializerClass.classId == referenceArraySerializerId) + args = TODO("reference array serializer") + val serializable = getSerializableClassDescriptorBySerializer(serializerClass) + val ctor = if (serializable?.declaredTypeParameters?.isNotEmpty() == true) { + KSerializerDescriptorResolver.createTypedSerializerConstructorDescriptor(serializerClass, serializableDescriptor) + .let { compilerContext.symbolTable.referenceConstructor(it) } + } else { + compilerContext.symbolTable.referenceConstructor(serializerClass.unsubstitutedPrimaryConstructor!!) + } + return irInvoke( + null, + ctor, + *args.toTypedArray() + ) + } + } + + fun ClassDescriptor.referenceMethod(methodName: String) = + getFuncDesc(methodName).single().let { compilerContext.symbolTable.referenceFunction(it) } + + override fun generateSave(function: FunctionDescriptor) = irClass.contributeFunction(function) { saveFunc -> + + fun irThis(): IrExpression = + IrGetValueImpl(startOffset, endOffset, saveFunc.dispatchReceiverParameter!!.symbol) + + val kOutputClass = serializerDescriptor.getClassFromSerializationPackage("StructureEncoder") + + val descriptorGetterSymbol = compilerContext.symbolTable.referenceFunction(serialDescPropertyDescriptor?.getter!!) + + val localSerialDesc = irTemporary(irGet(descriptorGetterSymbol.owner.returnType, irThis(), descriptorGetterSymbol), "desc") + + // fun beginStructure(desc: SerialDescriptor, vararg typeParams: KSerializer<*>): StructureEncoder + val beginFunc = kOutputClass.referenceMethod("beginStructure") // todo: retrieve from actual encoder instead + + val call = irCall(beginFunc).mapValueParametersIndexed { i, parameterDescriptor -> + if (i == 0) irGet(localSerialDesc) else IrVarargImpl( + startOffset, + endOffset, + parameterDescriptor.type.toIrType(), + parameterDescriptor.varargElementType!!.toIrType() + ) + } + // can it be done in more concise way? e.g. additional builder function? + call.dispatchReceiver = irGet(saveFunc.valueParameters[0]) + val serialObjectSymbol = saveFunc.valueParameters[1] + val localOutput = irTemporary(call, "output") + + // internal serialization via virtual calls? + val labeledProperties = orderedProperties.filter { !it.transient } + for (index in labeledProperties.indices) { + val property = labeledProperties[index] + if (property.transient) continue + // output.writeXxxElementValue(classDesc, index, value) + val sti = getSerialTypeInfo(property) + val innerSerial = serializerInstance(sti.serializer, property.module, property.type, property.genericIndex) + if (innerSerial == null) { + val writeFunc = + kOutputClass.referenceMethod("encode${sti.elementMethodPrefix}ElementValue") + +irInvoke( + irGet(localOutput), + writeFunc, + irGet(localSerialDesc), + irInt(index), + // todo: direct field access? +// irInvoke(irGet(serialObjectSymbol), compilerContext.symbolTable.referenceFunction(property.descriptor.getter!!)) + irGetField(irGet(serialObjectSymbol), compilerContext.symbolTable.referenceField(property.descriptor).owner) + ) + } else { + val writeFunc = kOutputClass.referenceMethod("encode${sti.elementMethodPrefix}SerializableElementValue") + +irInvoke( + irGet(localOutput), + writeFunc, + irGet(localSerialDesc), + irInt(index), + innerSerial, + // todo: direct field access? +// irInvoke(irGet(serialObjectSymbol), compilerContext.symbolTable.referenceFunction(property.descriptor.getter!!)) + irGetField(irGet(serialObjectSymbol), compilerContext.symbolTable.referenceField(property.descriptor).owner) + ) + } + } + + // output.writeEnd(serialClassDesc) + val wEndFunc = kOutputClass.referenceMethod("endStructure") + +irInvoke(irGet(localOutput), wEndFunc, irGet(localSerialDesc)) + } + + // returns null: Any? for boxed types and 0: for primitives + fun IrBuilderWithScope.defaultValueAndType(prop: SerializableProperty): Pair { + val kType = prop.descriptor.returnType!! + val T = kType.toIrType() + val defaultPrimitive: IrExpression? = when { + T.isInt() -> IrConstImpl.int(startOffset, endOffset, T, 0) + T.isBoolean() -> IrConstImpl.boolean(startOffset, endOffset, T, false) + T.isLong() -> IrConstImpl.long(startOffset, endOffset, T, 0) + T.isDouble() -> IrConstImpl.double(startOffset, endOffset, T, 0.0) + T.isFloat() -> IrConstImpl.float(startOffset, endOffset, T, 0.0f) + T.isChar() -> IrConstImpl.char(startOffset, endOffset, T, 0.toChar()) + T.isByte() -> IrConstImpl.byte(startOffset, endOffset, T, 0) + T.isShort() -> IrConstImpl.short(startOffset, endOffset, T, 0) + else -> null + } + return if (defaultPrimitive == null) + irNull() to (compilerContext.builtIns.nullableAnyType) + else + defaultPrimitive to kType + } + + override fun generateLoad(function: FunctionDescriptor) = irClass.contributeFunction(function) { loadFunc -> + fun irThis(): IrExpression = + IrGetValueImpl(startOffset, endOffset, loadFunc.dispatchReceiverParameter!!.symbol) + + fun IrVariable.get() = irGet(this) + + val inputClass = serializerDescriptor.getClassFromSerializationPackage("StructureDecoder") + val descriptorGetterSymbol = compilerContext.symbolTable.referenceFunction(serialDescPropertyDescriptor?.getter!!) + val localSerialDesc = irTemporary(irGet(descriptorGetterSymbol.owner.returnType, irThis(), descriptorGetterSymbol), "desc") + + // workaround due to unavailability of labels (KT-25386) + val flagVar = irTemporaryVar(irBoolean(true), "flag", parent = loadFunc) + + val indexVar = irTemporaryVar(irInt(0), "index", parent = loadFunc) +// val readAll = irTemporaryVar(irBoolean(false), "readAll", parent = loadFunc) +// + // calculating bit mask vars + val blocksCnt = orderedProperties.size / 32 + 1 + + // var bitMask0 = 0, bitMask1 = 0... + val bitMasks = (0 until blocksCnt).map { irTemporaryVar(irInt(0), "bitMask$it", parent = loadFunc) } + // var local0 = null, local1 = null ... + val localProps = orderedProperties.mapIndexed { i, prop -> + val (expr, type) = defaultValueAndType(prop) + irTemporaryVar( + expr, + "local$i", + typeHint = type, + parent = loadFunc + ) + } + + //input = input.beginStructure(...) + val beginFunc = inputClass.referenceMethod("beginStructure") + val call = irInvoke( + irGet(loadFunc.valueParameters[0]), + beginFunc, + irGet(localSerialDesc), + irEmptyVararg(beginFunc.descriptor.valueParameters[1]) + ) + val localInput = irTemporary(call, "input") + + +irWhile().also { loop -> + loop.condition = flagVar.get() + loop.body = irBlock { + val readElementF = inputClass.referenceMethod("decodeElement") + +irSetVar(indexVar.symbol, irInvoke(localInput.get(), readElementF, localSerialDesc.get())) + +irWhen { + // if index == -2 (READ_ALL) todo... + +IrBranchImpl(irEquals(indexVar.get(), irInt(-2)), irThrowNpe()) + + // if index == -1 (READ_DONE) break loop + +IrBranchImpl(irEquals(indexVar.get(), irInt(-1)), irSetVar(flagVar.symbol, irBoolean(false))) + + val branchBodies: List> = orderedProperties.mapIndexed { index, property -> + val body = irBlock { + val sti = getSerialTypeInfo(property) + val innerSerial = serializerInstance(sti.serializer, property.module, property.type, property.genericIndex) + // todo: update + val decodeFuncToCall = + (if (innerSerial != null) "decode${sti.elementMethodPrefix}SerializableElementValue" + else "decode${sti.elementMethodPrefix}ElementValue") + .let { + inputClass.referenceMethod(it) + } + val args = mutableListOf(localSerialDesc.get(), irInt(index)) + if (innerSerial != null) + args.add(innerSerial) + // local$i = localInput.decode...(...) + +irSetVar(localProps[index].symbol, irInvoke(localInput.get(), decodeFuncToCall, *args.toTypedArray())) + // bitMask[i] |= 1 << x + val bitPos = 1 shl (index % 32) + val or = irBinOp(OperatorNameConventions.OR, bitMasks[index / 32].get(), irInt(bitPos)) + +irSetVar(bitMasks[index / 32].symbol, or) + } + index to body + } + branchBodies.forEach { (i, e) -> +IrBranchImpl(irEquals(indexVar.get(), irInt(i)), e) } + + // throw exception on unknown field + val exceptionCtor = + serializableDescriptor.getClassFromSerializationPackage("UnknownFieldException") + .unsubstitutedPrimaryConstructor!! + val excClassRef = compilerContext.symbolTable.referenceConstructor(exceptionCtor) + +elseBranch( + irThrow( + irInvoke( + null, + excClassRef, + indexVar.get(), + typeHint = exceptionCtor.returnType.toIrType() + ) + ) + ) + } + } + } + + //input.endStructure(...) + val endFunc = inputClass.referenceMethod("endStructure") + +irInvoke( + localInput.get(), + endFunc, + irGet(localSerialDesc) + ) + + // todo: set properties in external deserialization + var args: List = localProps.map { it.get() } + val ctor: IrConstructorSymbol = if (serializableDescriptor.isInternalSerializable) { + val ctorDesc = compilerContext.symbolTable.referenceClass(serializableDescriptor) + .owner.constructors.single { it.origin == SERIALIZABLE_PLUGIN_ORIGIN } + args = listOf(irGet(bitMasks[0])) + args + irNull() + ctorDesc.symbol + } else { + compilerContext.symbolTable.referenceConstructor(serializableDescriptor.unsubstitutedPrimaryConstructor!!) + } + + +irReturn(irInvoke(null, ctor, *args.toTypedArray())) + } + + companion object { + fun generate( + irClass: IrClass, + context: BackendContext, + bindingContext: BindingContext + ) { + if (getSerializableClassDescriptorBySerializer(irClass.descriptor) != null) + SerializerIrGenerator(irClass, context, bindingContext).generate() + } + } +} \ No newline at end of file diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/js/SerializableCompanionJsTranslator.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/js/SerializableCompanionJsTranslator.kt index 7e2d9adf1b4..466c29ef8a2 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/js/SerializableCompanionJsTranslator.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/js/SerializableCompanionJsTranslator.kt @@ -29,9 +29,11 @@ import org.jetbrains.kotlin.psi.KtPureClassOrObject import org.jetbrains.kotlinx.serialization.compiler.backend.common.SerializableCompanionCodegen import org.jetbrains.kotlinx.serialization.compiler.resolve.* -class SerializableCompanionJsTranslator(declaration: KtPureClassOrObject, - val translator: DeclarationBodyVisitor, - val context: TranslationContext): SerializableCompanionCodegen(declaration, context.bindingContext()) { +class SerializableCompanionJsTranslator( + declaration: ClassDescriptor, + val translator: DeclarationBodyVisitor, + val context: TranslationContext): SerializableCompanionCodegen(declaration) { + override fun generateSerializerGetter(methodDescriptor: FunctionDescriptor) { val f = context.buildFunction(methodDescriptor) {jsFun, context -> val serializer = serializableDescriptor.classSerializer?.toClassDescriptor!! @@ -52,7 +54,7 @@ class SerializableCompanionJsTranslator(declaration: KtPureClassOrObject, fun translate(declaration: KtPureClassOrObject, descriptor: ClassDescriptor, translator: DeclarationBodyVisitor, context: TranslationContext) { val serializableClass = getSerializableClassDescriptorByCompanion(descriptor) ?: return if (serializableClass.isInternalSerializable) - SerializableCompanionJsTranslator(declaration, translator, context).generate() + SerializableCompanionJsTranslator(descriptor, translator, context).generate() } } } \ No newline at end of file diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/js/SerializableJsTranslator.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/js/SerializableJsTranslator.kt index 89ad68d77d3..801466d796a 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/js/SerializableJsTranslator.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/js/SerializableJsTranslator.kt @@ -37,9 +37,10 @@ import org.jetbrains.kotlinx.serialization.compiler.resolve.isInternalSerializab class SerializableJsTranslator( val declaration: KtPureClassOrObject, + val descriptor: ClassDescriptor, val translator: DeclarationBodyVisitor, val context: TranslationContext -) : SerializableCodegen(declaration, context.bindingContext()) { +) : SerializableCodegen(descriptor, context.bindingContext()) { private val initMap: Map = declaration.run { (bodyPropertiesDescriptorsMap(context.bindingContext()).mapValues { it.value.delegateExpressionOrInitializer } + @@ -109,7 +110,7 @@ class SerializableJsTranslator( context: TranslationContext ) { if (descriptor.isInternalSerializable) - SerializableJsTranslator(declaration, translator, context).generate() + SerializableJsTranslator(declaration, descriptor, translator, context).generate() } } } \ No newline at end of file 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 448acea4386..e53a9dfca91 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 @@ -119,13 +119,10 @@ class SerializerJsTranslator(declaration: KtPureClassOrObject, context.addDeclarationStatement(f.makeStmt()) } - private fun ClassDescriptor.getFuncDesc(funcName: String) = - unsubstitutedMemberScope.getDescriptorsFiltered { it == Name.identifier(funcName) } - override fun generateSave(function: FunctionDescriptor) = generateFunction(function) { jsFun, ctx -> val kOutputClass = serializerDescriptor.getClassFromSerializationPackage("StructureEncoder") val wBeginFunc = ctx.getNameForDescriptor( - kOutputClass.getFuncDesc("beginStructure").single { (it as FunctionDescriptor).valueParameters.size == 2 }) + kOutputClass.getFuncDesc("beginStructure").single { it.valueParameters.size == 2 }) val serialClassDescRef = JsNameRef(context.getNameForDescriptor(serialDescPropertyDescriptor!!), JsThisRef()) // output.writeBegin(desc, []) 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 3080fff6840..f2d45405026 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 @@ -41,7 +41,7 @@ import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter class SerializableCodegenImpl( private val classCodegen: ImplementationBodyCodegen, serializableClass: ClassDescriptor -) : SerializableCodegen(classCodegen.myClass, classCodegen.bindingContext) { +) : SerializableCodegen(classCodegen.descriptor, classCodegen.bindingContext) { private val thisAsmType = classCodegen.typeMapper.mapClass(serializableDescriptor) diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializableCompanionCodegenImpl.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializableCompanionCodegenImpl.kt index 5677a36199e..1f935036618 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializableCompanionCodegenImpl.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializableCompanionCodegenImpl.kt @@ -23,7 +23,7 @@ import org.jetbrains.kotlinx.serialization.compiler.backend.common.SerializableC import org.jetbrains.kotlinx.serialization.compiler.resolve.* class SerializableCompanionCodegenImpl(private val codegen: ImplementationBodyCodegen) : - SerializableCompanionCodegen(codegen.myClass, codegen.bindingContext) { + SerializableCompanionCodegen(codegen.descriptor) { companion object { fun generateSerializableExtensions(codegen: ImplementationBodyCodegen) { diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/extensions/SerializationLoweringExtension.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/extensions/SerializationLoweringExtension.kt new file mode 100644 index 00000000000..4076d78dd21 --- /dev/null +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/extensions/SerializationLoweringExtension.kt @@ -0,0 +1,60 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlinx.serialization.compiler.extensions + +import org.jetbrains.kotlin.backend.common.BackendContext +import org.jetbrains.kotlin.backend.common.ClassLoweringPass +import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension +import org.jetbrains.kotlin.backend.common.runOnFilePostfix +import org.jetbrains.kotlin.ir.IrElement +import org.jetbrains.kotlin.ir.declarations.IrClass +import org.jetbrains.kotlin.ir.declarations.IrFile +import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid +import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid +import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid +import org.jetbrains.kotlin.ir.visitors.acceptVoid +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlinx.serialization.compiler.backend.ir.SerializableCompanionIrGenerator +import org.jetbrains.kotlinx.serialization.compiler.backend.ir.SerializableIrGenerator +import org.jetbrains.kotlinx.serialization.compiler.backend.ir.SerializerIrGenerator + +/** + * Copy of [runOnFilePostfix], but this implementation first lowers declaration, then its children. + */ +fun ClassLoweringPass.runOnFileInOrder(irFile: IrFile) { + irFile.acceptVoid(object : IrElementVisitorVoid { + override fun visitElement(element: IrElement) { + element.acceptChildrenVoid(this) + } + + override fun visitClass(declaration: IrClass) { + lower(declaration) + declaration.acceptChildrenVoid(this) + } + }) +} + +private class SerializerClassLowering( + val context: BackendContext, + val bindingContext: BindingContext +) : + IrElementTransformerVoid(), ClassLoweringPass { + override fun lower(irClass: IrClass) { + SerializableIrGenerator.generate(irClass, context, bindingContext) + SerializerIrGenerator.generate(irClass, context, bindingContext) + SerializableCompanionIrGenerator.generate(irClass, context, bindingContext) + } +} + +class SerializationLoweringExtension : IrGenerationExtension { + override fun generate( + file: IrFile, + backendContext: BackendContext, + bindingContext: BindingContext + ) { + SerializerClassLowering(backendContext, bindingContext).runOnFileInOrder(file) + } +} \ No newline at end of file diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/KSerializerDescriptorResolver.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/KSerializerDescriptorResolver.kt index 2e99c136b27..9d2c59121f7 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/KSerializerDescriptorResolver.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/KSerializerDescriptorResolver.kt @@ -280,7 +280,7 @@ object KSerializerDescriptorResolver { fun createTypedSerializerConstructorDescriptor( classDescriptor: ClassDescriptor, serializableDescriptor: ClassDescriptor - ): ConstructorDescriptor { + ): ClassConstructorDescriptor { val constrDesc = ClassConstructorDescriptorImpl.createSynthesized( classDescriptor, Annotations.EMPTY,