[IR] Support reflection for MFVC

Signed-off-by: Evgeniy.Zhelenskiy <Evgeniy.Zhelenskiy@jetbrains.com>

#KT-1179
This commit is contained in:
Evgeniy.Zhelenskiy
2023-03-02 01:48:02 +01:00
committed by Space Team
parent 1ff4906880
commit 88f293d4a9
52 changed files with 2910 additions and 172 deletions
@@ -105,15 +105,14 @@ internal class JvmMultiFieldValueClassLowering(
return expressions.subList(0, expressions.size - repeatable.size) to repeatable
}
private fun IrBuilderWithScope.castedToNotNull(expression: IrExpression) =
if (expression.type.isNullable()) irImplicitCast(expression, expression.type.makeNotNull()) else expression
fun IrBlockBuilder.addReplacement(expression: IrSetValue, safe: Boolean): IrExpression? {
oldValueSymbol2NewValueSymbol[expression.symbol]?.let {
return irSet(it.owner, expression.value).also { irSet -> +irSet }
}
val instance = oldSymbol2MfvcNodeInstance[expression.symbol] ?: return null
val values: List<IrExpression> = makeFlattenedExpressionsWithGivenSafety(instance.node, safe, castedToNotNull(expression.value))
val values: List<IrExpression> = makeFlattenedExpressionsWithGivenSafety(
instance.node, safe, castExpressionToNotNullTypeIfNeeded(expression.value, instance.type)
)
val setterExpressions = instance.makeSetterExpressions(this, values)
expression2MfvcNodeInstanceAccessor[setterExpressions] = MfvcNodeInstanceAccessor.Setter(instance, values)
+setterExpressions
@@ -166,7 +165,9 @@ internal class JvmMultiFieldValueClassLowering(
accessType = AccessType.AlwaysPrivate,
saveVariable = ::variablesSaver
)
val values: List<IrExpression> = makeFlattenedExpressionsWithGivenSafety(node, safe, castedToNotNull(expression.value))
val values: List<IrExpression> = makeFlattenedExpressionsWithGivenSafety(
node, safe, castExpressionToNotNullTypeIfNeeded(expression.value, node.type)
)
val setterExpressions = instance.makeSetterExpressions(this, values)
expression2MfvcNodeInstanceAccessor[setterExpressions] = MfvcNodeInstanceAccessor.Setter(instance, values)
+setterExpressions
@@ -318,6 +319,12 @@ internal class JvmMultiFieldValueClassLowering(
if (replacingDeclaration in possibleExtraBoxUsageGenerated) removeAllExtraBoxes()
} as IrBlockBody
is IrField -> replacingDeclaration.initializer = replacingDeclaration.initializer?.makeBodyWithAddedVariables(
context, variablesToAdd[replacingDeclaration] ?: emptySet(), replacingDeclaration.symbol
)?.apply {
if (replacingDeclaration in possibleExtraBoxUsageGenerated) removeAllExtraBoxes()
} as IrExpressionBody?
else -> Unit
}
}
@@ -326,13 +333,15 @@ internal class JvmMultiFieldValueClassLowering(
irClass.primaryConstructor?.let {
replacements.getReplacementForRegularClassConstructor(it)?.let { replacement -> addBindingsFor(it, replacement) }
}
val propertiesOrFields = collectPropertiesOrFieldsAfterLowering(irClass)
val oldBackingFields = propertiesOrFields.mapNotNull { propertyOrField ->
val property = (propertyOrField as? IrPropertyOrIrField.Property)?.property ?: return@mapNotNull null
property.backingField?.let { property to it }
}.toMap()
val propertiesOrFieldsReplacement =
collectRegularClassMfvcPropertiesOrFieldsReplacement(propertiesOrFields) // resets backing fields
val propertiesOrFields = collectPropertiesOrFieldsAfterLowering(irClass, context)
val oldBackingFields = buildMap {
for (propertyOrField in propertiesOrFields) {
val property = (propertyOrField as? IrPropertyOrIrField.Property)?.property ?: continue
val field = property.backingField ?: continue
put(property, field)
}
}
val propertiesOrFieldsReplacement = collectRegularClassMfvcPropertiesOrFieldsReplacement(propertiesOrFields)
val fieldsToRemove = propertiesOrFieldsReplacement.keys.mapNotNull {
when (it) {
@@ -390,12 +399,15 @@ internal class JvmMultiFieldValueClassLowering(
context.irFactory.createAnonymousInitializer(
startOffset = UNDEFINED_OFFSET,
endOffset = UNDEFINED_OFFSET, origin = IrDeclarationOrigin.GENERATED_MULTI_FIELD_VALUE_CLASS_MEMBER,
symbol = IrAnonymousInitializerSymbolImpl()
symbol = IrAnonymousInitializerSymbolImpl(),
isStatic = element.isStatic,
).apply {
parent = irClass
body = context.createJvmIrBuilder(symbol).irBlockBody {
+irSetField(
irClass.thisReceiver!!.takeUnless { element.isStatic }?.let { irGet(it) }, element, initializer.expression,
receiver = irClass.thisReceiver!!.takeUnless { element.isStatic }?.let { irGet(it) },
field = element,
value = initializer.expression.patchDeclarationParents(irClass),
origin = UNSAFE_MFVC_SET_ORIGIN
)
}
@@ -800,18 +812,18 @@ internal class JvmMultiFieldValueClassLowering(
}
override fun visitFunctionReference(expression: IrFunctionReference): IrExpression {
val originalFunction = expression.symbol.owner
if (originalFunction.getReplacement() == null) return super.visitFunctionReference(expression)
return makeNewLambda(originalFunction, expression, makeBody = { wrapper ->
with(context.createJvmIrBuilder(wrapper.symbol)) {
irExprBody(irCall(originalFunction).apply {
passTypeArgumentsFrom(wrapper)
for ((newParam, originalParam) in wrapper.explicitParameters zip originalFunction.explicitParameters) {
putArgument(originalParam, irGet(newParam))
}
}).transform(this@JvmMultiFieldValueClassLowering, null)
val function = expression.symbol.owner
val replacement = function.getReplacement() ?: return super.visitFunctionReference(expression)
return context.createJvmIrBuilder(expression.symbol, expression).irBlock {
// Bridge call is added in BridgeLowering
buildReplacement(function, expression, replacement) {
IrFunctionReferenceImpl(
expression.startOffset, expression.endOffset,
expression.type, replacement.symbol, function.typeParameters.size, replacement.valueParameters.size,
expression.reflectionTarget, expression.origin
).copyAttributes(expression)
}
})
}.unwrapBlock()
}
private fun IrFunction.getReplacement(): IrFunction? =
@@ -995,7 +1007,7 @@ internal class JvmMultiFieldValueClassLowering(
require(parameter2expression.size == structure.size)
require(structure.sumOf { it.valueParameters.size } == replacement.explicitParametersCount)
val newArguments: List<IrExpression?> =
makeNewArguments(parameter2expression.map { (_, argument) -> argument }, structure.map { it.valueParameters })
makeNewArguments(parameter2expression.map { (_, argument) -> argument }, structure)
val resultExpression = makeMemberAccessExpression(replacement.symbol).apply {
passTypeArgumentsWithOffsets(replacement, originalFunction) { original.getTypeArgument(it)!! }
for ((parameter, argument) in replacement.explicitParameters zip newArguments) {
@@ -1023,21 +1035,15 @@ internal class JvmMultiFieldValueClassLowering(
return super.visitStringConcatenation(expression)
}
private fun IrBlockBuilder.makeNewArguments(
oldArguments: List<IrExpression?>,
structure: List<List<IrValueParameter>>
): List<IrExpression?> {
val argumentSizes: List<Int> = structure.map { argTemplate -> argTemplate.size }
val newArguments = (oldArguments zip argumentSizes).flatMap { (oldArgument, parametersCount) ->
private fun IrBlockBuilder.makeNewArguments(oldArguments: List<IrExpression?>, structure: List<RemappedParameter>): List<IrExpression?> {
val argumentSizes: List<Int> = structure.map { argTemplate -> argTemplate.valueParameters.size }
val newArguments = (oldArguments zip argumentSizes).flatMapIndexed { index, (oldArgument, parametersCount) ->
when {
oldArgument == null -> List(parametersCount) { null }
parametersCount == 1 -> listOf(oldArgument.transform(this@JvmMultiFieldValueClassLowering, null))
else -> {
val castedIfNeeded = when {
oldArgument.type.needsMfvcFlattening() -> oldArgument
oldArgument.type.makeNotNull().needsMfvcFlattening() -> irImplicitCast(oldArgument, oldArgument.type.makeNotNull())
else -> error("Unexpected type: ${oldArgument.type.render()}")
}
val expectedType = (structure[index] as MultiFieldValueClassMapping).boxedType
val castedIfNeeded = castExpressionToNotNullTypeIfNeeded(oldArgument, expectedType)
flattenExpression(castedIfNeeded).also {
require(it.size == parametersCount) { "Expected $parametersCount arguments but got ${it.size}" }
}
@@ -1047,6 +1053,12 @@ internal class JvmMultiFieldValueClassLowering(
return newArguments
}
private fun IrBuilderWithScope.castExpressionToNotNullTypeIfNeeded(expression: IrExpression, type: IrType) = when (type) {
expression.type -> expression
expression.type.makeNotNull() -> irImplicitCast(expression, type)
else -> irAs(expression, type)
}
/**
* Inlines initialization of variables when possible and returns their values
*
@@ -1212,7 +1224,7 @@ internal class JvmMultiFieldValueClassLowering(
)
val type = if (expression is IrConstructorCall) expression.symbol.owner.constructedClass.defaultType else expression.type
val lowering = this@JvmMultiFieldValueClassLowering
if (rootNode == null || !type.needsMfvcFlattening()) {
if (rootNode == null || !type.needsMfvcFlattening() || instance.size == 1) {
require(instance.size == 1) { "Required 1 variable/field to store regular value but got ${instance.size}" }
instance.addSetterStatements(this, listOf(expression.transform(lowering, null)))
return
@@ -1263,11 +1275,7 @@ internal class JvmMultiFieldValueClassLowering(
}
}
val nullableTransformedExpression = expression.transform(this@JvmMultiFieldValueClassLowering, null)
val transformedExpression =
if (nullableTransformedExpression.type.isNullable())
irImplicitCast(nullableTransformedExpression, nullableTransformedExpression.type.makeNotNull())
else
nullableTransformedExpression
val transformedExpression = castExpressionToNotNullTypeIfNeeded(nullableTransformedExpression, instance.type)
val addedSettersToFlattened = valueDeclarationsRemapper.handleFlattenedGetterExpressions(this, transformedExpression) {
require(it.size == instance.size) { "Incompatible assignment sizes: ${it.size}, ${instance.size}" }
instance.makeSetterExpressions(this, it)
@@ -32,7 +32,6 @@ import org.jetbrains.kotlin.ir.types.IrSimpleType
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.defaultType
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.util.OperatorNameConventions
@@ -61,7 +60,7 @@ fun createLeafMfvcNode(
this.name = fullFieldName
this.type = type
this.visibility = DescriptorVisibilities.PRIVATE
this.metadata = null
oldBackingField.metadata = null
}.apply {
this.parent = oldBackingField.parent
this.annotations = fieldAnnotations.map { it.deepCopyWithVariables() }
@@ -246,19 +245,27 @@ fun createIntermediateMfvcNode(
)
}
fun collectPropertiesAfterLowering(irClass: IrClass): LinkedHashSet<IrProperty> =
LinkedHashSet(collectPropertiesOrFieldsAfterLowering(irClass).map { (it as Property).property })
fun collectPropertiesAfterLowering(irClass: IrClass, context: JvmBackendContext): LinkedHashSet<IrProperty> =
LinkedHashSet(collectPropertiesOrFieldsAfterLowering(irClass, context).map { (it as Property).property })
sealed class IrPropertyOrIrField {
data class Property(val property: IrProperty) : IrPropertyOrIrField()
data class Field(val field: IrField) : IrPropertyOrIrField()
}
fun collectPropertiesOrFieldsAfterLowering(irClass: IrClass): LinkedHashSet<IrPropertyOrIrField> =
fun collectPropertiesOrFieldsAfterLowering(irClass: IrClass, context: JvmBackendContext): LinkedHashSet<IrPropertyOrIrField> =
LinkedHashSet<IrPropertyOrIrField>().apply {
for (element in irClass.declarations) {
if (element is IrField) {
element.correspondingPropertySymbol?.owner?.takeUnless { it.isDelegated }?.let { add(Property(it)) } ?: add(Field(element))
val property = element.correspondingPropertySymbol?.owner
if (
property != null && !property.isDelegated &&
!context.multiFieldValueClassReplacements.getFieldsToRemove(element.parentAsClass).contains(element)
) {
add(Property(property))
} else {
add(Field(element))
}
} else if (element is IrSimpleFunction && element.extensionReceiverParameter == null && element.contextReceiverParametersCount == 0) {
element.correspondingPropertySymbol?.owner?.let { add(Property(it)) }
}
@@ -275,7 +282,7 @@ fun getRootNode(context: JvmBackendContext, mfvc: IrClass): RootMfvcNode {
val oldPrimaryConstructor = mfvc.primaryConstructor!!
val oldFields = mfvc.fields.filter { !it.isStatic }.toList()
val representation = mfvc.multiFieldValueClassRepresentation!!
val properties = collectPropertiesAfterLowering(mfvc).associateBy { it.isStatic(mfvc) to it.name }
val properties = collectPropertiesAfterLowering(mfvc, context).associateBy { it.isStatic(mfvc) to it.name }
val subnodes = makeRootMfvcNodeSubnodes(representation, properties, context, mfvc)
@@ -394,6 +401,11 @@ private fun makePrimaryConstructorImpl(
}
}
annotations = oldPrimaryConstructor.annotations
if (oldPrimaryConstructor.metadata != null) {
metadata = oldPrimaryConstructor.metadata
oldPrimaryConstructor.metadata = null
}
copyAttributes(oldPrimaryConstructor as? IrAttributeContainer)
// body is added in the Lowering file as it needs to be lowered
}
@@ -448,20 +460,18 @@ private fun makeRootMfvcNodeSubnodes(
Modality.FINAL,
oldBackingField,
).also {
updateAnnotationsAndPropertyFromOldProperty(oldProperty)
updateAnnotationsAndPropertyFromOldProperty(oldProperty, context, it)
it.unboxMethod.overriddenSymbols = listOf() // the getter is saved so it overrides itself
}
}
private fun updateAnnotationsAndPropertyFromOldProperty(
oldProperty: IrProperty
oldProperty: IrProperty,
context: JvmBackendContext,
node: MfvcNode,
) {
oldProperty.setter?.apply {
name = Name.identifier(JvmAbi.setterName(oldProperty.name.asString()))
correspondingPropertySymbol = null
origin = IrDeclarationOrigin.DEFINED
}
oldProperty.setter = null
if (node is LeafMfvcNode) return
oldProperty.backingField?.let { context.multiFieldValueClassReplacements.addFieldToRemove(it.parentAsClass, it) }
oldProperty.backingField = null
}
@@ -482,7 +492,7 @@ fun createIntermediateNodeForMfvcPropertyOfRegularClass(
parent, context, type, makeTypeArgumentsFromType(type), MethodFullNameMode.Getter, listOf(oldProperty.name),
fieldAnnotations, static, overriddenNode, null, oldGetter, modality, oldField
).also {
updateAnnotationsAndPropertyFromOldProperty(oldProperty)
updateAnnotationsAndPropertyFromOldProperty(oldProperty, context, it)
}
}
@@ -243,9 +243,11 @@ fun IrSimpleFunction.copyCorrespondingPropertyFrom(source: IrSimpleFunction) {
fun IrProperty.needsAccessor(accessor: IrSimpleFunction): Boolean = when {
// Properties in annotation classes become abstract methods named after the property.
(parent as? IrClass)?.kind == ClassKind.ANNOTATION_CLASS -> true
// Multi-field value class getters must always be added. Getters for properties of MFVC itself follow general rules.
// Multi-field value class accessors must always be added.
accessor.isGetter && accessor.contextReceiverParametersCount == 0 && accessor.extensionReceiverParameter == null &&
!accessor.parent.let { it is IrClass && it.isMultiFieldValueClass } && accessor.returnType.needsMfvcFlattening() -> true
accessor.returnType.needsMfvcFlattening() -> true
accessor.isSetter && accessor.contextReceiverParametersCount == 0 && accessor.extensionReceiverParameter == null &&
accessor.valueParameters.single().type.needsMfvcFlattening() -> true
// @JvmField properties have no getters/setters
resolveFakeOverride()?.backingField?.hasAnnotation(JvmAbi.JVM_FIELD_ANNOTATION_FQ_NAME) == true -> false
// We do not produce default accessors for private fields