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 index 0e49644af92..cc72c12681c 100644 --- 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 @@ -14,6 +14,7 @@ import org.jetbrains.kotlin.descriptors.annotations.Annotations import org.jetbrains.kotlin.ir.builders.* import org.jetbrains.kotlin.ir.builders.declarations.buildFun import org.jetbrains.kotlin.ir.declarations.* +import org.jetbrains.kotlin.ir.descriptors.IrPropertyDelegateDescriptorImpl import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.impl.* import org.jetbrains.kotlin.ir.symbols.* @@ -28,10 +29,7 @@ import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.platform.jvm.isJvm import org.jetbrains.kotlin.resolve.BindingContext -import org.jetbrains.kotlin.resolve.descriptorUtil.classId -import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe -import org.jetbrains.kotlin.resolve.descriptorUtil.isEffectivelyExternal -import org.jetbrains.kotlin.resolve.descriptorUtil.module +import org.jetbrains.kotlin.resolve.descriptorUtil.* import org.jetbrains.kotlin.resolve.jvm.JvmPrimitiveType import org.jetbrains.kotlin.types.* import org.jetbrains.kotlin.types.typeUtil.isTypeParameter @@ -42,6 +40,12 @@ import org.jetbrains.kotlinx.serialization.compiler.backend.common.* import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.* import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPluginContext import org.jetbrains.kotlinx.serialization.compiler.resolve.* +import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationDependencies.FUNCTION0_FQ +import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationDependencies.KPROPERTY1_FQ +import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationDependencies.LAZY_FQ +import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationDependencies.LAZY_FUNC_FQ +import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationDependencies.LAZY_MODE_FQ +import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationDependencies.LAZY_PUBLICATION_MODE_NAME interface IrBuilderExtension { val compilerContext: SerializationPluginContext @@ -82,48 +86,125 @@ interface IrBuilderExtension { ) { bodyGen(c) } } - // function will not be created in the real class - fun IrClass.createInlinedFunction( - name: Name, - visibility: DescriptorVisibility, - origin: IrDeclarationOrigin, - returnType: IrType, - bodyGen: IrBlockBodyBuilder.(IrFunction) -> Unit - ): IrSimpleFunction { - val function = factory.buildFun { - this.name = name - this.visibility = visibility - this.origin = origin - this.isInline = true - this.returnType = returnType - } - val functionSymbol = function.symbol - function.parent = this - function.body = DeclarationIrBuilder(compilerContext, functionSymbol, startOffset, endOffset).irBlockBody( - startOffset, - endOffset - ) { bodyGen(function) } - return function - } - - fun IrClass.createSingletonLambda( - returnType: IrType, - startOffset: Int, - endOffset: Int, + fun IrClass.createLambdaExpression( + type: IrType, bodyGen: IrBlockBodyBuilder.() -> Unit - ): IrSimpleFunction { + ): IrFunctionExpression { val function = compilerContext.irFactory.buildFun { - this.startOffset = startOffset - this.endOffset = endOffset - this.returnType = returnType + this.startOffset = this@createLambdaExpression.startOffset + this.endOffset = this@createLambdaExpression.endOffset + this.returnType = type name = Name.identifier("") visibility = DescriptorVisibilities.LOCAL origin = SERIALIZABLE_PLUGIN_ORIGIN } function.body = DeclarationIrBuilder(compilerContext, function.symbol, startOffset, endOffset).irBlockBody(startOffset, endOffset, bodyGen) + function.parent = this - return function + val f0Type = module.findClassAcrossModuleDependencies(ClassId.topLevel(FUNCTION0_FQ))!!.defaultType + val f0ParamSymbol = compilerContext.symbolTable.referenceTypeParameter(f0Type.constructor.parameters[0]) + val f0IrType = f0Type.toIrType().substitute(mapOf(f0ParamSymbol to type)) + + return IrFunctionExpressionImpl( + startOffset, + endOffset, + f0IrType, + function, + IrStatementOrigin.LAMBDA + ) + } + + fun createLazyProperty( + containingClass: IrClass, + targetIrType: IrType, + name: Name, + initializerBuilder: IrBlockBodyBuilder.() -> Unit + ): IrProperty { + val lazySafeModeClassDescriptor = compilerContext.referenceClass(LAZY_MODE_FQ)!!.descriptor + val lazyFunctionSymbol = compilerContext.referenceFunctions(LAZY_FUNC_FQ).single { + it.descriptor.valueParameters.size == 2 && it.descriptor.valueParameters[0].type == lazySafeModeClassDescriptor.defaultType + } + val publicationEntryDescriptor = lazySafeModeClassDescriptor.enumEntries().single { it.name == LAZY_PUBLICATION_MODE_NAME } + + val lazyIrClass = compilerContext.referenceClass(LAZY_FQ)!!.owner + val lazyKotlinType = lazyIrClass.defaultType.substitute(mapOf(lazyIrClass.typeParameters[0].symbol to targetIrType)).toKotlinType() + + val kPropertyIrClass = compilerContext.referenceClass(KPROPERTY1_FQ)!!.owner + val kPropertyKotlinType = kPropertyIrClass.defaultType.substitute( + mapOf( + kPropertyIrClass.typeParameters[0].symbol to targetIrType, + kPropertyIrClass.typeParameters[1].symbol to containingClass.defaultType, + ) + ).toKotlinType() + + val targetKotlinType = targetIrType.toKotlinType() + + val propertyDescriptor = + KSerializerDescriptorResolver.createValPropertyDescriptor(name, containingClass.descriptor, targetKotlinType) + + val delegate = IrPropertyDelegateDescriptorImpl(propertyDescriptor, lazyKotlinType, kPropertyKotlinType) + + return generateSimplePropertyWithBackingField(delegate, containingClass, delegate.name).apply { + val builder = DeclarationIrBuilder(compilerContext, containingClass.symbol, startOffset, endOffset) + val initializerBody = builder.run { + val enumElement = IrGetEnumValueImpl( + startOffset, + endOffset, + publicationEntryDescriptor.classValueType!!.toIrType(), + compilerContext.symbolTable.referenceEnumEntry(publicationEntryDescriptor) + ) + + val lambdaExpression = containingClass.createLambdaExpression(targetIrType, initializerBuilder) + + irExprBody( + irInvoke(null, lazyFunctionSymbol, listOf(targetIrType), listOf(enumElement, lambdaExpression), targetIrType) + ) + } + backingField!!.initializer = initializerBody + } + } + + fun createCompanionValProperty( + companionClass: IrClass, + type: IrType, + name: Name, + initializerBuilder: IrBlockBodyBuilder.() -> Unit + ): IrProperty { + val targetKotlinType = type.toKotlinType() + val propertyDescriptor = + KSerializerDescriptorResolver.createValPropertyDescriptor(name, companionClass.descriptor, targetKotlinType) + + return generateSimplePropertyWithBackingField(propertyDescriptor, companionClass, name).apply { + companionClass.contributeAnonymousInitializer { + val irBlockBody = irBlockBody(startOffset, endOffset, initializerBuilder) + irBlockBody.statements.dropLast(1).forEach { +it } + val expression = irBlockBody.statements.last() as? IrExpression + ?: throw AssertionError("Last statement in property initializer builder is not an a expression") + +irSetField(irGetObject(companionClass), backingField!!, expression) + } + } + } + + fun IrClass.contributeAnonymousInitializer(bodyGen: IrBlockBodyBuilder.() -> Unit) { + val symbol = IrAnonymousInitializerSymbolImpl(descriptor) + factory.createAnonymousInitializer(startOffset, endOffset, SERIALIZABLE_PLUGIN_ORIGIN, symbol).also { + it.parent = this + declarations.add(it) + it.body = DeclarationIrBuilder(compilerContext, symbol, startOffset, endOffset).irBlockBody(startOffset, endOffset, bodyGen) + } + } + + fun IrBlockBodyBuilder.getLazyValueExpression(companionClass: IrClass, property: IrProperty): IrExpression { + val lazyIrClass = compilerContext.referenceClass(LAZY_FQ)!!.owner + val valueGetter = lazyIrClass.getPropertyGetter("value")!! + + val backingField = property.backingField!! + return irGet( + backingField.type, + irGetField(irGetObject(companionClass), backingField), + valueGetter + ) } fun IrBuilderWithScope.irInvoke( 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 index b4b044b2dfa..11c9fc5f333 100644 --- 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 @@ -12,17 +12,12 @@ import org.jetbrains.kotlin.ir.builders.declarations.* import org.jetbrains.kotlin.ir.declarations.IrClass import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction import org.jetbrains.kotlin.ir.expressions.IrExpression -import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin import org.jetbrains.kotlin.ir.expressions.impl.* -import org.jetbrains.kotlin.ir.util.getPropertyGetter -import org.jetbrains.kotlin.ir.util.module -import org.jetbrains.kotlin.ir.util.patchDeclarationParents +import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.name.ClassId -import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.calls.components.isVararg -import org.jetbrains.kotlin.resolve.descriptorUtil.classValueType import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe import org.jetbrains.kotlin.resolve.descriptorUtil.module import org.jetbrains.kotlinx.serialization.compiler.backend.common.SerializableCompanionCodegen @@ -36,25 +31,6 @@ class SerializableCompanionIrGenerator( bindingContext: BindingContext ) : SerializableCompanionCodegen(irClass.descriptor, bindingContext), IrBuilderExtension { - private val lazyDescriptor = irClass.module.findClassAcrossModuleDependencies( - ClassId(FqName("kotlin"), Name.identifier("Lazy")) - )!! - - private val lazySafeModeClassDescriptor = irClass.module.findClassAcrossModuleDependencies( - ClassId(FqName("kotlin"), Name.identifier("LazyThreadSafetyMode")) - )!! - - private val lazyFunctionSymbol = compilerContext.referenceFunctions(FqName("kotlin").child(Name.identifier("lazy"))).single { - it.descriptor.valueParameters.size == 2 && it.descriptor.valueParameters[0].type == lazySafeModeClassDescriptor.defaultType - } - - private val publicationEntryDescriptor = lazySafeModeClassDescriptor.enumEntries().single { it.name == Name.identifier("PUBLICATION") } - - private val function0Descriptor = irClass.module.findClassAcrossModuleDependencies( - ClassId(FqName("kotlin"), Name.identifier("Function0")) - )!! - - companion object { fun generate( irClass: IrClass, @@ -113,58 +89,22 @@ class SerializableCompanionIrGenerator( ) ) - val field = irClass.addField { - name = Name.identifier(SerialEntityNames.SERIALIZER_LAZY_DELEGATE_FIELD_NAME) - visibility = DescriptorVisibilities.PRIVATE - origin = SERIALIZABLE_PLUGIN_ORIGIN - isFinal = true - isStatic = true - type = lazyDescriptor.defaultType.toIrType() - }.apply { - val lambda = irClass.createSingletonLambda(serializerDescriptor.defaultType.toIrType(), startOffset, endOffset) { - val expr = serializerInstance( - this@SerializableCompanionIrGenerator, - serializerDescriptor, serializableDescriptor.module, - serializableDescriptor.defaultType - ) - patchSerializableClassWithMarkerAnnotation(serializerDescriptor) - +irReturn(requireNotNull(expr)) - } + val kSerializerIrClass = compilerContext.referenceClass(SerialEntityNames.KSERIALIZER_NAME_FQ)!!.owner + val targetIrType = + kSerializerIrClass.defaultType.substitute(mapOf(kSerializerIrClass.typeParameters[0].symbol to compilerContext.builtIns.anyType.toIrType())) - val call = IrCallImpl(startOffset, endOffset, type, lazyFunctionSymbol, 0, 2) - call.putValueArgument( - 0, - IrGetEnumValueImpl( - startOffset, - endOffset, - publicationEntryDescriptor.classValueType!!.toIrType(), - compilerContext.symbolTable.referenceEnumEntry(publicationEntryDescriptor) - ), + val property = createLazyProperty(irClass, targetIrType, SerialEntityNames.CACHED_SERIALIZER_PROPERTY_NAME) { + val expr = serializerInstance( + this@SerializableCompanionIrGenerator, + serializerDescriptor, serializableDescriptor.module, + serializableDescriptor.defaultType ) - - call.putValueArgument( - 1, - IrFunctionExpressionImpl( - startOffset, - endOffset, - function0Descriptor.defaultType.toIrType(), - lambda, - IrStatementOrigin.LAMBDA - ), - ) - - initializer = irClass.factory.createExpressionBody(startOffset, endOffset, call) + patchSerializableClassWithMarkerAnnotation(serializerDescriptor) + +irReturn(requireNotNull(expr)) } - val valueGetter = compilerContext.referenceClass(lazyDescriptor.fqNameSafe)!!.getPropertyGetter("value")!! - irClass.contributeFunction(methodDescriptor) { - +irReturn( - irGet( - serializerDescriptor.defaultType.toIrType(), - irGetField(null, field), valueGetter - ) - ) + +irReturn(getLazyValueExpression(irClass, property)) } generateSerializerFactoryIfNeeded(methodDescriptor) } 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 index 8cc43c95c2f..123d26a04c3 100644 --- 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 @@ -11,7 +11,6 @@ import org.jetbrains.kotlin.codegen.CompilationException import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.ir.IrStatement import org.jetbrains.kotlin.ir.builders.* -import org.jetbrains.kotlin.ir.builders.declarations.addField import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.ir.expressions.IrExpressionBody @@ -19,7 +18,6 @@ import org.jetbrains.kotlin.ir.expressions.impl.IrDelegatingConstructorCallImpl import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi -import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe import org.jetbrains.kotlin.resolve.descriptorUtil.module @@ -29,7 +27,7 @@ import org.jetbrains.kotlinx.serialization.compiler.backend.common.* import org.jetbrains.kotlinx.serialization.compiler.diagnostic.serializableAnnotationIsUseless import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPluginContext import org.jetbrains.kotlinx.serialization.compiler.resolve.* -import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.INITIALIZED_DESCRIPTOR_FIELD_NAME +import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.CACHED_DESCRIPTOR_FIELD_NAME import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.MISSING_FIELD_EXC import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.SERIAL_DESC_FIELD @@ -39,8 +37,6 @@ class SerializableIrGenerator( bindingContext: BindingContext ) : SerializableCodegen(irClass.descriptor, bindingContext), IrBuilderExtension { - private val descriptorGenerationFunctionName = Name.identifier("createInitializedDescriptor") - private val serialDescClass: ClassDescriptor = serializableDescriptor.module .getClassFromSerializationDescriptorsPackage(SerialEntityNames.SERIAL_DESCRIPTOR_CLASS) @@ -116,7 +112,14 @@ class SerializableIrGenerator( // for abstract classes fields MUST BE checked in child classes !serializableDescriptor.isAbstractSerializableClass() && !serializableDescriptor.isSealedSerializableClass() ) { - generateGoldenMaskCheck(seenVars, properties, getSerialDescriptorExpr()) + val getDescriptorExpr = if (serializableDescriptor.isStaticSerializable) { + getStaticSerialDescriptorExpr() + } else { + // synthetic constructor is created only for internally serializable classes - so companion definitely exists + val companionObject = irClass.companionObject()!! + getParametrizedSerialDescriptorExpr(companionObject, createCachedDescriptorProperty(companionObject)) + } + generateGoldenMaskCheck(seenVars, properties, getDescriptorExpr) } when { superClass.symbol == compilerContext.irBuiltIns.anyClass -> generateAnySuperConstructorCall(toBuilder = this@contributeConstructor) @@ -167,29 +170,25 @@ class SerializableIrGenerator( } } - private fun IrBlockBodyBuilder.getSerialDescriptorExpr(): IrExpression { - return if (serializableDescriptor.isStaticSerializable) { - val serializer = serializableDescriptor.classSerializer!! - val serialDescriptorGetter = compilerContext.referenceClass(serializer.fqNameSafe)!!.getPropertyGetter(SERIAL_DESC_FIELD)!! - irGet( - serialDescriptorGetter.owner.returnType, - irGetObject(serializer), - serialDescriptorGetter.owner.symbol - ) - } else { - irGetField(null, generateStaticDescriptorField()) - } + private fun IrBlockBodyBuilder.getStaticSerialDescriptorExpr(): IrExpression { + val serializer = serializableDescriptor.classSerializer!! + val serialDescriptorGetter = compilerContext.referenceClass(serializer.fqNameSafe)?.getPropertyGetter(SERIAL_DESC_FIELD) + ?: throw Exception("No class with name ${serializer.fqNameSafe}") + return irGet( + serialDescriptorGetter.owner.returnType, + irGetObject(serializer), + serialDescriptorGetter.owner.symbol + ) } - private fun IrBlockBodyBuilder.generateStaticDescriptorField(): IrField { - val serialDescItType = serialDescClass.defaultType.toIrType() + private fun IrBlockBodyBuilder.getParametrizedSerialDescriptorExpr(companionObject: IrClass, property: IrProperty): IrExpression { + return irGetField(irGetObject(companionObject), property.backingField!!) + } - val function = irClass.createInlinedFunction( - descriptorGenerationFunctionName, - DescriptorVisibilities.PRIVATE, - SERIALIZABLE_PLUGIN_ORIGIN, - serialDescItType - ) { + private fun IrBlockBodyBuilder.createCachedDescriptorProperty(companionObject: IrClass): IrProperty { + val serialDescIrType = serialDescClass.defaultType.toIrType() + + return createCompanionValProperty(companionObject, serialDescIrType, CACHED_DESCRIPTOR_FIELD_NAME) { val serialDescVar = irTemporary( getInstantiateDescriptorExpr(), nameHint = "serialDesc" @@ -197,17 +196,8 @@ class SerializableIrGenerator( for (property in properties.serializableProperties) { +getAddElementToDescriptorExpr(property, serialDescVar) } - +irReturn(irGet(serialDescVar)) + +irGet(serialDescVar) } - - return irClass.addField { - name = Name.identifier(INITIALIZED_DESCRIPTOR_FIELD_NAME) - visibility = DescriptorVisibilities.PRIVATE - origin = SERIALIZABLE_PLUGIN_ORIGIN - isFinal = true - isStatic = true - type = serialDescItType - }.apply { initializer = irClass.factory.createExpressionBody(irCall(function)) } } private fun IrBlockBodyBuilder.getInstantiateDescriptorExpr(): IrExpression { 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 5cddc57e5d4..4d7baeb9723 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 @@ -35,7 +35,7 @@ import org.jetbrains.kotlinx.serialization.compiler.diagnostic.VersionReader import org.jetbrains.kotlinx.serialization.compiler.diagnostic.serializableAnnotationIsUseless import org.jetbrains.kotlinx.serialization.compiler.resolve.* import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.ARRAY_MASK_FIELD_MISSING_FUNC_NAME -import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.INITIALIZED_DESCRIPTOR_FIELD_NAME +import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.CACHED_DESCRIPTOR_FIELD import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.SINGLE_MASK_FIELD_MISSING_FUNC_NAME import org.jetbrains.org.objectweb.asm.Label import org.jetbrains.org.objectweb.asm.Opcodes @@ -354,7 +354,7 @@ class SerializableCodegenImpl( } else { generateStaticDescriptorField() - getstatic(thisAsmType.internalName, INITIALIZED_DESCRIPTOR_FIELD_NAME, descType.descriptor) + getstatic(thisAsmType.internalName, CACHED_DESCRIPTOR_FIELD, descType.descriptor) } } @@ -362,7 +362,7 @@ class SerializableCodegenImpl( val flags = Opcodes.ACC_PRIVATE or Opcodes.ACC_FINAL or Opcodes.ACC_SYNTHETIC or Opcodes.ACC_STATIC classCodegen.v.newField( OtherOrigin(classCodegen.myClass.psiOrParent), flags, - INITIALIZED_DESCRIPTOR_FIELD_NAME, descType.descriptor, null, null + CACHED_DESCRIPTOR_FIELD, descType.descriptor, null, null ) val clInit = classCodegen.createOrGetClInitCodegen() @@ -380,7 +380,7 @@ class SerializableCodegenImpl( invokevirtual(descImplType.internalName, CallingConventions.addElement, "(Ljava/lang/String;Z)V", false) } - putstatic(thisAsmType.internalName, INITIALIZED_DESCRIPTOR_FIELD_NAME, descType.descriptor) + putstatic(thisAsmType.internalName, CACHED_DESCRIPTOR_FIELD, descType.descriptor) } } 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 f10ae20aa79..02990ee8e76 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,11 +23,12 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.module import org.jetbrains.kotlin.resolve.jvm.diagnostics.OtherOrigin import org.jetbrains.kotlinx.serialization.compiler.backend.common.SerializableCompanionCodegen import org.jetbrains.kotlinx.serialization.compiler.backend.common.findTypeSerializer -import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.SERIALIZER_LAZY_DELEGATE_FIELD_NAME +import org.jetbrains.kotlinx.serialization.compiler.resolve.* +import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.CACHED_SERIALIZER_PROPERTY +import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationDependencies.LAZY_PUBLICATION_MODE_NAME import org.jetbrains.kotlinx.serialization.compiler.resolve.getKSerializer import org.jetbrains.kotlinx.serialization.compiler.resolve.getSerializableClassDescriptorByCompanion import org.jetbrains.kotlinx.serialization.compiler.resolve.shouldHaveGeneratedMethodsInCompanion -import org.jetbrains.kotlinx.serialization.compiler.resolve.toSimpleType import org.jetbrains.org.objectweb.asm.Opcodes class SerializableCompanionCodegenImpl(private val classCodegen: ImplementationBodyCodegen) : @@ -42,11 +43,13 @@ class SerializableCompanionCodegenImpl(private val classCodegen: ImplementationB } override fun generateLazySerializerGetter(methodDescriptor: FunctionDescriptor) { + val fieldName = "$CACHED_SERIALIZER_PROPERTY\$delegate" + // Create field for lazy delegate classCodegen.v.newField( OtherOrigin(classCodegen.myClass.psiOrParent), Opcodes.ACC_PRIVATE or Opcodes.ACC_FINAL or Opcodes.ACC_SYNTHETIC or Opcodes.ACC_STATIC, - SERIALIZER_LAZY_DELEGATE_FIELD_NAME, + fieldName, kotlinLazyType.descriptor, "L${kotlinLazyType.internalName};>;", null @@ -75,7 +78,7 @@ class SerializableCompanionCodegenImpl(private val classCodegen: ImplementationB // initialize lazy delegate val clInit = classCodegen.createOrGetClInitCodegen() with(clInit.v) { - getstatic(threadSafeModeType.internalName, "PUBLICATION", threadSafeModeType.descriptor) + getstatic(threadSafeModeType.internalName, LAZY_PUBLICATION_MODE_NAME.identifier, threadSafeModeType.descriptor) getstatic(lambdaType.internalName, JvmAbi.INSTANCE_FIELD, lambdaType.descriptor) checkcast(function0Type) invokestatic( @@ -84,12 +87,12 @@ class SerializableCompanionCodegenImpl(private val classCodegen: ImplementationB "(${threadSafeModeType.descriptor}${function0Type.descriptor})${kotlinLazyType.descriptor}", false ) - putstatic(classCodegen.className, SERIALIZER_LAZY_DELEGATE_FIELD_NAME, kotlinLazyType.descriptor) + putstatic(classCodegen.className, fieldName, kotlinLazyType.descriptor) } // create serializer getter classCodegen.generateMethod(methodDescriptor) { _, _ -> - getstatic(classCodegen.className, SERIALIZER_LAZY_DELEGATE_FIELD_NAME, kotlinLazyType.descriptor) + getstatic(classCodegen.className, fieldName, kotlinLazyType.descriptor) invokeinterface(kotlinLazyType.internalName, getLazyValueName, "()Ljava/lang/Object;") checkcast(kSerializerType) areturn(kSerializerType) 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 798481c08f9..c8954fa21ba 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 @@ -290,6 +290,40 @@ object KSerializerDescriptorResolver { return functionDescriptor } + fun createValPropertyDescriptor( + name: Name, + containingClassDescriptor: ClassDescriptor, + type: KotlinType, + visibility: DescriptorVisibility = DescriptorVisibilities.PRIVATE, + createGetter: Boolean = false + ): PropertyDescriptor { + val propertyDescriptor = PropertyDescriptorImpl.create( + containingClassDescriptor, + Annotations.EMPTY, Modality.FINAL, visibility, false, name, + CallableMemberDescriptor.Kind.SYNTHESIZED, containingClassDescriptor.source, false, false, false, false, false, false + ) + val extensionReceiverParameter: ReceiverParameterDescriptor? = null // kludge to disambiguate call + propertyDescriptor.setType( + type, + emptyList(), // no need type parameters? + containingClassDescriptor.thisAsReceiverParameter, + extensionReceiverParameter + ) + + val propertyGetter: PropertyGetterDescriptorImpl? = if (createGetter) { + PropertyGetterDescriptorImpl( + propertyDescriptor, Annotations.EMPTY, Modality.FINAL, visibility, false, false, false, + CallableMemberDescriptor.Kind.SYNTHESIZED, null, containingClassDescriptor.source + ).apply { initialize(type) } + } else { + null + } + + propertyDescriptor.initialize(propertyGetter, null) + + return propertyDescriptor + } + fun createLoadConstructorDescriptor( classDescriptor: ClassDescriptor, bindingContext: BindingContext, diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/NamingConventions.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/NamingConventions.kt index b6466b202a3..165e4a1abe5 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/NamingConventions.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/NamingConventions.kt @@ -44,8 +44,8 @@ object SerialEntityNames { const val LOAD = "deserialize" const val SERIALIZER_CLASS = "\$serializer" - const val INITIALIZED_DESCRIPTOR_FIELD_NAME = "\$initializedDescriptor" - const val SERIALIZER_LAZY_DELEGATE_FIELD_NAME = "\$serializer\$delegate" + const val CACHED_DESCRIPTOR_FIELD = "\$cachedDescriptor" + const val CACHED_SERIALIZER_PROPERTY = "\$cachedSerializer" // classes val KSERIALIZER_NAME = Name.identifier(KSERIALIZER_CLASS) @@ -91,6 +91,8 @@ object SerialEntityNames { val ARRAY_MASK_FIELD_MISSING_FUNC_NAME = Name.identifier("throwArrayMissingFieldException") val SINGLE_MASK_FIELD_MISSING_FUNC_FQ = SerializationPackages.internalPackageFqName.child(SINGLE_MASK_FIELD_MISSING_FUNC_NAME) val ARRAY_MASK_FIELD_MISSING_FUNC_FQ = SerializationPackages.internalPackageFqName.child(ARRAY_MASK_FIELD_MISSING_FUNC_NAME) + val CACHED_SERIALIZER_PROPERTY_NAME = Name.identifier(CACHED_SERIALIZER_PROPERTY) + val CACHED_DESCRIPTOR_FIELD_NAME = Name.identifier(CACHED_DESCRIPTOR_FIELD) // parameters val dummyParamName = Name.identifier("serializationConstructorMarker") @@ -129,3 +131,12 @@ object CallingConventions { const val addAnnotation = "pushAnnotation" const val addClassAnnotation = "pushClassAnnotation" } + +internal object SerializationDependencies { + val LAZY_FQ = FqName("kotlin.Lazy") + val LAZY_FUNC_FQ = FqName("kotlin.lazy") + val LAZY_MODE_FQ = FqName("kotlin.LazyThreadSafetyMode") + val KPROPERTY1_FQ = FqName("kotlin.reflect.KProperty1") + val FUNCTION0_FQ = FqName("kotlin.Function0") + val LAZY_PUBLICATION_MODE_NAME = Name.identifier("PUBLICATION") +}