diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/PropertyReferenceLowering.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/PropertyReferenceLowering.kt index dc9faff77fb..3a6039f9e55 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/PropertyReferenceLowering.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/PropertyReferenceLowering.kt @@ -7,9 +7,9 @@ package org.jetbrains.kotlin.backend.jvm.lower import org.jetbrains.kotlin.backend.common.ClassLoweringPass import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext +import org.jetbrains.kotlin.backend.common.ir.addSimpleDelegatingConstructor import org.jetbrains.kotlin.backend.common.ir.copyTo import org.jetbrains.kotlin.backend.common.ir.createImplicitParameterDeclarationWithWrappedDescriptor -import org.jetbrains.kotlin.backend.common.ir.passTypeArgumentsFrom import org.jetbrains.kotlin.backend.common.lower.createIrBuilder import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase import org.jetbrains.kotlin.backend.jvm.JvmBackendContext @@ -20,12 +20,8 @@ import org.jetbrains.kotlin.ir.builders.declarations.* import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.impl.IrClassReferenceImpl -import org.jetbrains.kotlin.ir.expressions.impl.IrInstanceInitializerCallImpl import org.jetbrains.kotlin.ir.expressions.impl.IrVarargImpl -import org.jetbrains.kotlin.ir.symbols.IrClassSymbol -import org.jetbrains.kotlin.ir.symbols.IrFieldSymbol -import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol -import org.jetbrains.kotlin.ir.symbols.IrSymbol +import org.jetbrains.kotlin.ir.symbols.* import org.jetbrains.kotlin.ir.types.createType import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl import org.jetbrains.kotlin.ir.types.impl.makeTypeProjection @@ -48,10 +44,10 @@ internal class PropertyReferenceLowering(val context: JvmBackendContext) : Class private val localPropertyIndices = mutableMapOf() // TODO: join IrLocalDelegatedPropertyReference and IrPropertyReference via the class hierarchy? - private val IrMemberAccessExpression.getter: IrFunctionSymbol? + private val IrMemberAccessExpression.getter: IrSimpleFunctionSymbol? get() = (this as? IrPropertyReference)?.getter ?: (this as? IrLocalDelegatedPropertyReference)?.getter - private val IrMemberAccessExpression.setter: IrFunctionSymbol? + private val IrMemberAccessExpression.setter: IrSimpleFunctionSymbol? get() = (this as? IrPropertyReference)?.setter ?: (this as? IrLocalDelegatedPropertyReference)?.setter private val IrMemberAccessExpression.field: IrFieldSymbol? @@ -60,12 +56,13 @@ internal class PropertyReferenceLowering(val context: JvmBackendContext) : Class private val IrSimpleFunction.signature: String get() = context.state.typeMapper.mapSignatureSkipGeneric(collectRealOverrides().first().descriptor).toString() + // Plain Java fields do not have a getter, but can be referenced nonetheless. The signature should be the one + // that a getter would have, if it existed. + private val IrField.signature: String + get() = "${JvmAbi.getterName(name.asString())}()${context.state.typeMapper.mapReturnType(descriptor)}" + private val IrMemberAccessExpression.signature: String - get() = localPropertyIndices[getter]?.let { "" } - ?: (getter?.owner as? IrSimpleFunction)?.signature - // Plain Java fields do not have a getter, but can be referenced nonetheless. The signature should be - // the one that a getter would have, if it existed. - ?: TODO("plain Java field signature") + get() = localPropertyIndices[getter]?.let { "" } ?: getter?.owner?.signature ?: field!!.owner.signature private val IrMemberAccessExpression.symbol: IrSymbol get() = getter?.owner?.symbol ?: field!!.owner.symbol @@ -85,8 +82,8 @@ internal class PropertyReferenceLowering(val context: JvmBackendContext) : Class // Return some declaration the parent of which is the containing class/package for the referenced property. This is // a bit of a hack, as the reason for not returning the container itself is the `mapImplementationOwner` call below. - // TODO: move after LocalDeclarationsLowering and always use the getter/field? (not possible right now: - // need Java field support to move below PropertiesToFieldsLowering) + // TODO: move after LocalDeclarationsLowering and always use the getter/field? Can't move below ConstLowering + // right now -- get() would not be optimized. private val IrMemberAccessExpression.propertyContainerChild: IrDeclaration? get() { var current: IrDeclaration? = getter?.owner ?: field?.owner @@ -102,7 +99,7 @@ internal class PropertyReferenceLowering(val context: JvmBackendContext) : Class context.ir.symbols.javaLangClass.typeWith(), context.ir.symbols.javaLangClass, // TODO: when the parent is an interface, this should map to DefaultImpls. However, that requires - // moving this lowering below InterfaceLowering; see another TODO above. + // moving this lowering below InterfaceLowering; see comment about the ordering above, though. CrIrType(context.state.typeMapper.mapImplementationOwner(propertyContainerChild!!.descriptor)) ) @@ -126,6 +123,23 @@ internal class PropertyReferenceLowering(val context: JvmBackendContext) : Class } } + private fun IrClass.addOverride(method: IrSimpleFunction, buildBody: IrBuilderWithScope.(List) -> IrExpression) = + addFunction { + setSourceRange(this@addOverride) + name = method.name + returnType = method.returnType + visibility = method.visibility + origin = this@addOverride.origin + }.apply { + overriddenSymbols.add(method.symbol) + dispatchReceiverParameter = thisReceiver!!.copyTo(this) + for (parameter in method.valueParameters) + valueParameters.add(parameter.copyTo(this)) + body = context.createIrBuilder(symbol, startOffset, endOffset).run { + irExprBody(buildBody(listOf(dispatchReceiverParameter!!) + valueParameters)) + } + } + private class PropertyReferenceKind( val interfaceSymbol: IrClassSymbol, val reflectedSymbol: IrClassSymbol, @@ -258,119 +272,75 @@ internal class PropertyReferenceLowering(val context: JvmBackendContext) : Class superTypes += IrSimpleTypeImpl(superClass.symbol, false, listOf(), listOf()) createImplicitParameterDeclarationWithWrappedDescriptor() } - val referenceThis = referenceClass.thisReceiver!! - - buildConstructor { - setSourceRange(expression) - returnType = referenceClass.defaultType - origin = referenceClass.origin - isPrimary = true - }.apply { - parent = referenceClass - referenceClass.declarations.add(this) - - val constructor = this - // See propertyReferenceKindFor -- only one of them could ever be present. - val parameter = (expression.dispatchReceiver ?: expression.extensionReceiver)?.let { - buildValueParameter { - setSourceRange(expression) - name = Name.identifier("receiver") - type = it.type - index = valueParameters.size - origin = referenceClass.origin - }.apply { - parent = constructor - valueParameters.add(this) - } - } - body = context.createIrBuilder(symbol).irBlockBody(startOffset, endOffset) { - val superArgs = if (parameter == null) 0 else 1 - +irDelegatingConstructorCall(superClass.constructors.single { it.valueParameters.size == superArgs }).apply { - parameter?.let { putValueArgument(0, irGet(parameter)) } - } - +IrInstanceInitializerCallImpl(startOffset, endOffset, referenceClass.symbol, context.irBuiltIns.unitType) - } - } - - fun buildOverride( - method: IrSimpleFunction, - build: IrBlockBodyBuilder.(IrValueParameter, List) -> IrExpression - ) = referenceClass.addFunction { - setSourceRange(expression) - name = method.name - returnType = method.returnType - visibility = method.visibility - origin = referenceClass.origin - }.apply { - overriddenSymbols.add(method.symbol) - val thisParameter = referenceThis.copyTo(this) - dispatchReceiverParameter = thisParameter - for (parameter in method.valueParameters) - valueParameters.add(parameter.copyTo(this)) - body = context.createIrBuilder(symbol).irBlockBody(startOffset, endOffset) { - +irReturn(build(thisParameter, valueParameters)) - } - } + // See propertyReferenceKindFor -- only one of them could ever be present. + val numOfSuperArgs = if (expression.dispatchReceiver != null || expression.extensionReceiver != null) 1 else 0 + val superConstructor = superClass.constructors.single { it.valueParameters.size == numOfSuperArgs } val receiverField = superClass.properties.single { it.name.asString() == "receiver" }.backingField!! - fun IrBuilderWithScope.setReceiversOn( - call: IrCall, - dispatchReceiverParameter: IrValueParameter, - valueParameters: List - ) { - var index = 0 - call.dispatchReceiver = call.symbol.owner.dispatchReceiverParameter?.let { - if (expression.dispatchReceiver != null) - irGetField(irGet(dispatchReceiverParameter), receiverField) - else - irImplicitCast(irGet(valueParameters[index++]), it.type) - } - call.extensionReceiver = call.symbol.owner.extensionReceiverParameter?.let { - if (expression.extensionReceiver != null) - irGetField(irGet(dispatchReceiverParameter), receiverField) - else - irImplicitCast(irGet(valueParameters[index++]), it.type) - } - } + val getName = superClass.functions.single { it.name.asString() == "getName" } + val getOwner = superClass.functions.single { it.name.asString() == "getOwner" } + val getSignature = superClass.functions.single { it.name.asString() == "getSignature" } + val get = superClass.functions.find { it.name.asString() == "get" } + val set = superClass.functions.find { it.name.asString() == "set" } - buildOverride(superClass.functions.single { it.name.asString() == "getName" }) { _, _ -> - irString(expression.descriptor.name.asString()) - } + referenceClass.addSimpleDelegatingConstructor(superConstructor, context.irBuiltIns, isPrimary = true) + referenceClass.addOverride(getName) { irString(expression.descriptor.name.asString()) } + referenceClass.addOverride(getOwner) { buildReflectedContainerReference(expression) } + referenceClass.addOverride(getSignature) { irString(expression.signature) } - buildOverride(superClass.functions.single { it.name.asString() == "getOwner" }) { _, _ -> - buildReflectedContainerReference(expression) - } - - buildOverride(superClass.functions.single { it.name.asString() == "getSignature" }) { _, _ -> - irString(expression.signature) - } - - expression.getter?.owner?.let { getter -> - buildOverride(superClass.functions.single { it.name.asString() == "get" }) { dispatchReceiverParameter, valueParameters -> - irGet(getter.returnType, null, getter.symbol).apply { - copyTypeArgumentsFrom(expression) - setReceiversOn( - this, - dispatchReceiverParameter, - valueParameters - ) + val field = expression.field?.owner + if (field == null) { + fun IrBuilderWithScope.setCallArguments(call: IrCall, arguments: List) { + var index = 1 + call.copyTypeArgumentsFrom(expression) + call.dispatchReceiver = call.symbol.owner.dispatchReceiverParameter?.let { + if (expression.dispatchReceiver != null) + irImplicitCast(irGetField(irGet(arguments[0]), receiverField), it.type) + else + irImplicitCast(irGet(arguments[index++]), it.type) + } + call.extensionReceiver = call.symbol.owner.extensionReceiverParameter?.let { + if (expression.extensionReceiver != null) + irImplicitCast(irGetField(irGet(arguments[0]), receiverField), it.type) + else + irImplicitCast(irGet(arguments[index++]), it.type) } } - } - expression.setter?.owner?.let { setter -> - buildOverride(superClass.functions.single { it.name.asString() == "set" }) { dispatchReceiverParameter, valueParameters -> - val type = setter.valueParameters.last().type - val value = irImplicitCast(irGet(valueParameters.last()), type) - irSet(setter.returnType, null, setter.symbol, value).apply { - copyTypeArgumentsFrom(expression) - setReceiversOn(this, dispatchReceiverParameter, valueParameters) + expression.getter?.owner?.let { getter -> + referenceClass.addOverride(get!!) { arguments -> + irGet(getter.returnType, null, getter.symbol).apply { + setCallArguments(this, arguments) + } } } - } - expression.field?.owner?.let { - TODO("plain Java property reference") // no accessors, should generate direct field read/write instead + expression.setter?.owner?.let { setter -> + referenceClass.addOverride(set!!) { arguments -> + irSet(setter.returnType, null, setter.symbol, irGet(arguments.last())).apply { + setCallArguments(this, arguments) + } + } + } + } else { + fun IrBuilderWithScope.fieldReceiver(arguments: List) = when { + field.isStatic -> + null + expression.dispatchReceiver != null -> + irImplicitCast(irGetField(irGet(arguments[0]), receiverField), field.parentAsClass.defaultType) + else -> + irImplicitCast(irGet(arguments[1]), field.parentAsClass.defaultType) + } + + referenceClass.addOverride(get!!) { arguments -> + irGetField(fieldReceiver(arguments), field) + } + + if (!field.isFinal) { + referenceClass.addOverride(set!!) { arguments -> + irSetField(fieldReceiver(arguments), field, irGet(arguments.last())) + } + } } return referenceClass } diff --git a/compiler/testData/codegen/box/reflection/annotations/annotationsOnJavaMembers.kt b/compiler/testData/codegen/box/reflection/annotations/annotationsOnJavaMembers.kt index 912c22b98c3..11bdb520da2 100644 --- a/compiler/testData/codegen/box/reflection/annotations/annotationsOnJavaMembers.kt +++ b/compiler/testData/codegen/box/reflection/annotations/annotationsOnJavaMembers.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR // TARGET_BACKEND: JVM // WITH_REFLECT diff --git a/compiler/testData/codegen/box/reflection/call/bound/javaInstanceField.kt b/compiler/testData/codegen/box/reflection/call/bound/javaInstanceField.kt index 5da9b671d22..654391f6875 100644 --- a/compiler/testData/codegen/box/reflection/call/bound/javaInstanceField.kt +++ b/compiler/testData/codegen/box/reflection/call/bound/javaInstanceField.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR // TARGET_BACKEND: JVM // WITH_REFLECT diff --git a/compiler/testData/codegen/box/reflection/properties/javaPropertyInheritedInKotlin.kt b/compiler/testData/codegen/box/reflection/properties/javaPropertyInheritedInKotlin.kt index cec4f2c997c..64a7a3acf06 100644 --- a/compiler/testData/codegen/box/reflection/properties/javaPropertyInheritedInKotlin.kt +++ b/compiler/testData/codegen/box/reflection/properties/javaPropertyInheritedInKotlin.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR // TARGET_BACKEND: JVM // FILE: J.java diff --git a/compiler/testData/codegen/box/reflection/properties/javaStaticField.kt b/compiler/testData/codegen/box/reflection/properties/javaStaticField.kt index 67abac2c66f..22de15590f5 100644 --- a/compiler/testData/codegen/box/reflection/properties/javaStaticField.kt +++ b/compiler/testData/codegen/box/reflection/properties/javaStaticField.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR // TARGET_BACKEND: JVM // WITH_REFLECT diff --git a/compiler/testData/codegen/box/reflection/properties/noConflictOnKotlinGetterAndJavaField.kt b/compiler/testData/codegen/box/reflection/properties/noConflictOnKotlinGetterAndJavaField.kt index 8d2808bc772..e0a994e63c4 100644 --- a/compiler/testData/codegen/box/reflection/properties/noConflictOnKotlinGetterAndJavaField.kt +++ b/compiler/testData/codegen/box/reflection/properties/noConflictOnKotlinGetterAndJavaField.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR // TARGET_BACKEND: JVM // WITH_REFLECT diff --git a/compiler/testData/codegen/box/reflection/properties/referenceToJavaFieldOfKotlinSubclass.kt b/compiler/testData/codegen/box/reflection/properties/referenceToJavaFieldOfKotlinSubclass.kt index 230c7b40412..691c0e8cc69 100644 --- a/compiler/testData/codegen/box/reflection/properties/referenceToJavaFieldOfKotlinSubclass.kt +++ b/compiler/testData/codegen/box/reflection/properties/referenceToJavaFieldOfKotlinSubclass.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR // TARGET_BACKEND: JVM // FILE: J.java diff --git a/compiler/testData/codegen/boxAgainstJava/callableReference/publicFinalField.kt b/compiler/testData/codegen/boxAgainstJava/callableReference/publicFinalField.kt index fc7599102e9..e6eb43158b5 100644 --- a/compiler/testData/codegen/boxAgainstJava/callableReference/publicFinalField.kt +++ b/compiler/testData/codegen/boxAgainstJava/callableReference/publicFinalField.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR // FILE: A.java public class A { diff --git a/compiler/testData/codegen/boxAgainstJava/callableReference/publicMutableField.kt b/compiler/testData/codegen/boxAgainstJava/callableReference/publicMutableField.kt index 64e703795df..84cbe7f31ec 100644 --- a/compiler/testData/codegen/boxAgainstJava/callableReference/publicMutableField.kt +++ b/compiler/testData/codegen/boxAgainstJava/callableReference/publicMutableField.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR // FILE: A.java public class A { diff --git a/compiler/testData/codegen/boxAgainstJava/reflection/mapping/javaFields.kt b/compiler/testData/codegen/boxAgainstJava/reflection/mapping/javaFields.kt index 5ccb4b7de60..2d85dc1ec3e 100644 --- a/compiler/testData/codegen/boxAgainstJava/reflection/mapping/javaFields.kt +++ b/compiler/testData/codegen/boxAgainstJava/reflection/mapping/javaFields.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR // WITH_REFLECT // FULL_JDK // FILE: J.java diff --git a/compiler/testData/codegen/boxAgainstJava/reflection/properties/equalsHashCodeToString.kt b/compiler/testData/codegen/boxAgainstJava/reflection/properties/equalsHashCodeToString.kt index a741b3347cd..d3edbc773f3 100644 --- a/compiler/testData/codegen/boxAgainstJava/reflection/properties/equalsHashCodeToString.kt +++ b/compiler/testData/codegen/boxAgainstJava/reflection/properties/equalsHashCodeToString.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR // WITH_REFLECT // FILE: test/J.java