Directly search in IrClass for declarations to fill bodies with plugin

because some other compiler plugins (Compose) copy/rewrite IR declarations
completely, and in the end, functions that are present in the final IR tree
do not have bodies.

Correctly create IrProperty and IrField when they were absent from the
descriptors (in case for private serializer properties)

Do not use symbol table because 'declare' API is not available in plugins.

Fixes https://github.com/JetBrains/compose-jb/issues/46
This commit is contained in:
Leonid Startsev
2020-11-16 23:08:14 +03:00
parent 5efefabb93
commit b1c355e7eb
5 changed files with 56 additions and 70 deletions
@@ -1,17 +1,6 @@
/* /*
* Copyright 2010-2017 JetBrains s.r.o. * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/ */
package org.jetbrains.kotlin.ir.util package org.jetbrains.kotlin.ir.util
@@ -8,6 +8,7 @@ package org.jetbrains.kotlinx.serialization.compiler.backend.common
import org.jetbrains.kotlin.backend.common.CodegenUtil.getMemberToGenerate import org.jetbrains.kotlin.backend.common.CodegenUtil.getMemberToGenerate
import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.ir.declarations.IrProperty
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationDescriptorSerializerPlugin import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationDescriptorSerializerPlugin
@@ -69,12 +70,13 @@ abstract class SerializerCodegen(
serializerDescriptor::checkSerializableClassPropertyResult serializerDescriptor::checkSerializableClassPropertyResult
) { true } ) { true }
val localSerializersFieldsDescriptors: List<PropertyDescriptor> = findLocalSerializersFieldDescriptors() lateinit var localSerializersFieldsDescriptors: List<Pair<PropertyDescriptor, IrProperty>>
protected set
// Can be false if user specified inheritance from KSerializer explicitly // Can be false if user specified inheritance from KSerializer explicitly
protected val isGeneratedSerializer = serializerDescriptor.typeConstructor.supertypes.any(::isGeneratedKSerializer) protected val isGeneratedSerializer = serializerDescriptor.typeConstructor.supertypes.any(::isGeneratedKSerializer)
private fun findLocalSerializersFieldDescriptors(): List<PropertyDescriptor> { protected fun findLocalSerializersFieldDescriptors(): List<PropertyDescriptor> {
val count = serializableDescriptor.declaredTypeParameters.size val count = serializableDescriptor.declaredTypeParameters.size
if (count == 0) return emptyList() if (count == 0) return emptyList()
val propNames = (0 until count).map { "${SerialEntityNames.typeArgPrefix}$it" } val propNames = (0 until count).map { "${SerialEntityNames.typeArgPrefix}$it" }
@@ -15,8 +15,7 @@ import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.* import org.jetbrains.kotlin.ir.expressions.impl.*
import org.jetbrains.kotlin.ir.symbols.* import org.jetbrains.kotlin.ir.symbols.*
import org.jetbrains.kotlin.ir.symbols.impl.IrTypeParameterSymbolImpl import org.jetbrains.kotlin.ir.symbols.impl.*
import org.jetbrains.kotlin.ir.symbols.impl.IrValueParameterSymbolImpl
import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl
import org.jetbrains.kotlin.ir.types.impl.makeTypeProjection import org.jetbrains.kotlin.ir.types.impl.makeTypeProjection
@@ -42,10 +41,12 @@ import org.jetbrains.kotlinx.serialization.compiler.resolve.*
interface IrBuilderExtension { interface IrBuilderExtension {
val compilerContext: SerializationPluginContext val compilerContext: SerializationPluginContext
private inline fun <reified T : IrDeclaration> IrClass.searchForDeclaration(descriptor: DeclarationDescriptor): T? {
return declarations.singleOrNull { it.descriptor == descriptor } as? T
}
fun IrClass.contributeFunction(descriptor: FunctionDescriptor, bodyGen: IrBlockBodyBuilder.(IrFunction) -> Unit) { fun IrClass.contributeFunction(descriptor: FunctionDescriptor, bodyGen: IrBlockBodyBuilder.(IrFunction) -> Unit) {
val functionSymbol = compilerContext.symbolTable.referenceSimpleFunction(descriptor) val f: IrSimpleFunction = searchForDeclaration(descriptor) ?: compilerContext.symbolTable.referenceSimpleFunction(descriptor).owner
assert(functionSymbol.isBound)
val f: IrSimpleFunction = functionSymbol.owner
// TODO: default parameters // TODO: default parameters
f.body = DeclarationIrBuilder(compilerContext, f.symbol, this.startOffset, this.endOffset).irBlockBody( f.body = DeclarationIrBuilder(compilerContext, f.symbol, this.startOffset, this.endOffset).irBlockBody(
this.startOffset, this.startOffset,
@@ -59,11 +60,7 @@ interface IrBuilderExtension {
overwriteValueParameters: Boolean = false, overwriteValueParameters: Boolean = false,
bodyGen: IrBlockBodyBuilder.(IrConstructor) -> Unit bodyGen: IrBlockBodyBuilder.(IrConstructor) -> Unit
) { ) {
val ctorSymbol = compilerContext.symbolTable.referenceConstructor(descriptor) val c: IrConstructor = searchForDeclaration(descriptor) ?: compilerContext.symbolTable.referenceConstructor(descriptor).owner
assert(ctorSymbol.isBound)
val c = ctorSymbol.owner
c.body = DeclarationIrBuilder(compilerContext, c.symbol, this.startOffset, this.endOffset).irBlockBody( c.body = DeclarationIrBuilder(compilerContext, c.symbol, this.startOffset, this.endOffset).irBlockBody(
this.startOffset, this.startOffset,
this.endOffset this.endOffset
@@ -94,7 +91,7 @@ interface IrBuilderExtension {
irInvoke( irInvoke(
dispatchReceiver, dispatchReceiver,
callee, callee,
*valueArguments.toTypedArray(), args = valueArguments.toTypedArray(),
typeHint = returnTypeHint typeHint = returnTypeHint
).also { call -> typeArguments.forEachIndexed(call::putTypeArgument) } ).also { call -> typeArguments.forEachIndexed(call::putTypeArgument) }
@@ -226,16 +223,12 @@ interface IrBuilderExtension {
fun generateSimplePropertyWithBackingField( fun generateSimplePropertyWithBackingField(
propertyDescriptor: PropertyDescriptor, propertyDescriptor: PropertyDescriptor,
propertyParent: IrClass, propertyParent: IrClass,
declare: Boolean,
fieldName: Name = propertyDescriptor.name, fieldName: Name = propertyDescriptor.name,
): IrProperty { ): IrProperty {
val irPropertySymbol = compilerContext.symbolTable.referenceProperty(propertyDescriptor) val irProperty = propertyParent.searchForDeclaration<IrProperty>(propertyDescriptor) ?: run {
assert(irPropertySymbol.isBound || declare)
if (!irPropertySymbol.isBound) {
with(propertyDescriptor) { with(propertyDescriptor) {
propertyParent.factory.createProperty( propertyParent.factory.createProperty(
propertyParent.startOffset, propertyParent.endOffset, SERIALIZABLE_PLUGIN_ORIGIN, irPropertySymbol, propertyParent.startOffset, propertyParent.endOffset, SERIALIZABLE_PLUGIN_ORIGIN, IrPropertySymbolImpl(propertyDescriptor),
name, visibility, modality, isVar, isConst, isLateInit, isDelegated, isExternal name, visibility, modality, isVar, isConst, isLateInit, isDelegated, isExternal
).also { ).also {
it.parent = propertyParent it.parent = propertyParent
@@ -243,50 +236,54 @@ interface IrBuilderExtension {
} }
} }
} }
val irProperty = irPropertySymbol.owner
irProperty.backingField = generatePropertyBackingField(propertyDescriptor, irProperty, fieldName).apply { propertyParent.generatePropertyBackingFieldIfNeeded(propertyDescriptor, irProperty, fieldName)
parent = propertyParent
correspondingPropertySymbol = irPropertySymbol
}
val fieldSymbol = irProperty.backingField!!.symbol val fieldSymbol = irProperty.backingField!!.symbol
irProperty.getter = propertyDescriptor.getter?.let { generatePropertyAccessor(irProperty, it, fieldSymbol, declare) } irProperty.getter = propertyDescriptor.getter?.let {
?.apply { parent = propertyParent } propertyParent.generatePropertyAccessor(propertyDescriptor, irProperty, it, fieldSymbol, isGetter = true)
irProperty.setter = propertyDescriptor.setter?.let { generatePropertyAccessor(irProperty, it, fieldSymbol, declare) } }?.apply { parent = propertyParent }
?.apply { parent = propertyParent } irProperty.setter = propertyDescriptor.setter?.let {
propertyParent.generatePropertyAccessor(propertyDescriptor, irProperty, it, fieldSymbol, isGetter = false)
}?.apply { parent = propertyParent }
return irProperty return irProperty
} }
private fun generatePropertyBackingField( private fun IrClass.generatePropertyBackingFieldIfNeeded(
descriptor: PropertyDescriptor, propertyDescriptor: PropertyDescriptor,
originProperty: IrProperty, originProperty: IrProperty,
name: Name, name: Name,
): IrField { ) {
val fieldSymbol = compilerContext.symbolTable.referenceField(descriptor) if (originProperty.backingField != null) return
if (fieldSymbol.isBound) return fieldSymbol.owner
return with(descriptor) { val field = with(propertyDescriptor) {
// TODO: type parameters // TODO: type parameters
originProperty.factory.createField( originProperty.factory.createField(
originProperty.startOffset, originProperty.endOffset, SERIALIZABLE_PLUGIN_ORIGIN, fieldSymbol, name, type.toIrType(), originProperty.startOffset, originProperty.endOffset, SERIALIZABLE_PLUGIN_ORIGIN, IrFieldSymbolImpl(propertyDescriptor), name, type.toIrType(),
visibility, !isVar, isEffectivelyExternal(), dispatchReceiverParameter == null visibility, !isVar, isEffectivelyExternal(), dispatchReceiverParameter == null
) )
} }
field.apply {
parent = this@generatePropertyBackingFieldIfNeeded
correspondingPropertySymbol = originProperty.symbol
}
originProperty.backingField = field
} }
private fun generatePropertyAccessor( private fun IrClass.generatePropertyAccessor(
propertyDescriptor: PropertyDescriptor,
property: IrProperty, property: IrProperty,
descriptor: PropertyAccessorDescriptor, descriptor: PropertyAccessorDescriptor,
fieldSymbol: IrFieldSymbol, fieldSymbol: IrFieldSymbol,
declare: Boolean isGetter: Boolean,
): IrSimpleFunction { ): IrSimpleFunction {
val symbol = compilerContext.symbolTable.referenceSimpleFunction(descriptor) val irAccessor: IrSimpleFunction = when (isGetter) {
assert(symbol.isBound || declare) true -> searchForDeclaration<IrProperty>(propertyDescriptor)?.getter
false -> searchForDeclaration<IrProperty>(propertyDescriptor)?.setter
if (!symbol.isBound) { } ?: run {
with(descriptor) { with(descriptor) {
property.factory.createFunction( property.factory.createFunction(
fieldSymbol.owner.startOffset, fieldSymbol.owner.endOffset, SERIALIZABLE_PLUGIN_ORIGIN, symbol, fieldSymbol.owner.startOffset, fieldSymbol.owner.endOffset, SERIALIZABLE_PLUGIN_ORIGIN, IrSimpleFunctionSymbolImpl(descriptor),
name, visibility, modality, returnType!!.toIrType(), name, visibility, modality, returnType!!.toIrType(),
isInline, isExternal, isTailrec, isSuspend, isOperator, isInfix, isExpect isInline, isExternal, isTailrec, isSuspend, isOperator, isInfix, isExpect
) )
@@ -298,11 +295,9 @@ interface IrBuilderExtension {
} }
} }
val irAccessor = symbol.owner irAccessor.body = when (isGetter) {
irAccessor.body = when (descriptor) { true -> generateDefaultGetterBody(descriptor as PropertyGetterDescriptor, irAccessor)
is PropertyGetterDescriptor -> generateDefaultGetterBody(descriptor, irAccessor) false -> generateDefaultSetterBody(descriptor as PropertySetterDescriptor, irAccessor)
is PropertySetterDescriptor -> generateDefaultSetterBody(descriptor, irAccessor)
else -> throw AssertionError("Should be getter or setter: $descriptor")
} }
return irAccessor return irAccessor
@@ -557,8 +552,8 @@ interface IrBuilderExtension {
kType, kType,
genericIndex genericIndex
) { it, _ -> ) { it, _ ->
val prop = enclosingGenerator.localSerializersFieldsDescriptors[it] val (prop, ir) = enclosingGenerator.localSerializersFieldsDescriptors[it]
irGetField(irGet(dispatchReceiverParameter), compilerContext.symbolTable.referenceField(prop).owner) irGetField(irGet(dispatchReceiverParameter), ir.backingField!!)
} }
fun IrBuilderWithScope.serializerInstance( fun IrBuilderWithScope.serializerInstance(
@@ -65,7 +65,7 @@ class SerialInfoImplJvmIrGenerator(
ctor.body = ctorBody ctor.body = ctorBody
for (property in properties) { for (property in properties) {
generateSimplePropertyWithBackingField(property.descriptor, irClass, true, Name.identifier("_" + property.name.asString())) generateSimplePropertyWithBackingField(property.descriptor, irClass, Name.identifier("_" + property.name.asString()))
val getter = property.getter!! val getter = property.getter!!
getter.origin = SERIALIZABLE_SYNTHETIC_ORIGIN getter.origin = SERIALIZABLE_SYNTHETIC_ORIGIN
@@ -65,11 +65,11 @@ open class SerializerIrGenerator(
// how to (auto)create backing field and getter/setter? // how to (auto)create backing field and getter/setter?
compilerContext.symbolTable.withReferenceScope(irClass.descriptor) { compilerContext.symbolTable.withReferenceScope(irClass.descriptor) {
prop = generateSimplePropertyWithBackingField(desc, irClass, false) prop = generateSimplePropertyWithBackingField(desc, irClass)
// TODO: Do not use descriptors here // TODO: Do not use descriptors here
localSerializersFieldsDescriptors.forEach { localSerializersFieldsDescriptors = findLocalSerializersFieldDescriptors().map { it ->
generateSimplePropertyWithBackingField(it, irClass, true) it to generateSimplePropertyWithBackingField(it, irClass)
} }
} }
@@ -195,11 +195,11 @@ open class SerializerIrGenerator(
// store type arguments serializers in fields // store type arguments serializers in fields
val thisAsReceiverParameter = irClass.thisReceiver!! val thisAsReceiverParameter = irClass.thisReceiver!!
ctor.valueParameters.forEachIndexed { index, param -> ctor.valueParameters.forEachIndexed { index, param ->
val localSerial = compilerContext.symbolTable.referenceField(localSerializersFieldsDescriptors[index]) val localSerial = localSerializersFieldsDescriptors[index].second.backingField!!
+irSetField(generateReceiverExpressionForFieldAccess( +irSetField(generateReceiverExpressionForFieldAccess(
thisAsReceiverParameter.symbol, thisAsReceiverParameter.symbol,
localSerializersFieldsDescriptors[index] localSerializersFieldsDescriptors[index].first
), localSerial.owner, irGet(param)) ), localSerial, irGet(param))
} }
@@ -221,7 +221,7 @@ open class SerializerIrGenerator(
val typeParams = serializableDescriptor.declaredTypeParameters.mapIndexed { idx, _ -> val typeParams = serializableDescriptor.declaredTypeParameters.mapIndexed { idx, _ ->
irGetField( irGetField(
irGet(irFun.dispatchReceiverParameter!!), irGet(irFun.dispatchReceiverParameter!!),
compilerContext.symbolTable.referenceField(localSerializersFieldsDescriptors[idx]).owner localSerializersFieldsDescriptors[idx].second.backingField!!
) )
} }
val kSerType = ((irFun.returnType as IrSimpleType).arguments.first() as IrTypeProjection).type val kSerType = ((irFun.returnType as IrSimpleType).arguments.first() as IrTypeProjection).type