[IR] Support reflection for MFVC
Signed-off-by: Evgeniy.Zhelenskiy <Evgeniy.Zhelenskiy@jetbrains.com> #KT-1179
This commit is contained in:
committed by
Space Team
parent
1ff4906880
commit
88f293d4a9
+51
-43
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user