From 779cae7db549dff78460ce962d92b0ac457dd4a4 Mon Sep 17 00:00:00 2001 From: Dmitry Petrov Date: Thu, 19 Jul 2018 15:37:57 +0300 Subject: [PATCH] Adjust receivers for possibly synthetic properties access Synthetic property accessors are treated as extension functions on Java classes by the front-end. However, underlying Java accessor methods are proper members. --- .../kotlin/psi2ir/generators/CallGenerator.kt | 18 ++++++------- .../kotlin/psi2ir/intermediate/IrUtils.kt | 25 +++++++++++++++++ .../psi2ir/intermediate/PropertyLValue.kt | 8 +++--- .../javaSyntheticPropertyAccess.kt | 3 +++ .../javaSyntheticPropertyAccess.txt | 27 ++++++++++++++++--- .../ir/irText/stubs/javaSyntheticProperty.txt | 2 +- .../stubs/jdkClassSyntheticProperty.txt | 2 +- 7 files changed, 67 insertions(+), 18 deletions(-) diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/CallGenerator.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/CallGenerator.kt index 9bd30248df0..cc312a99e20 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/CallGenerator.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/CallGenerator.kt @@ -140,18 +140,17 @@ class CallGenerator(statementGenerator: StatementGenerator) : StatementGenerator endOffset: Int, call: CallBuilder ): IrExpression { - return call.callReceiver.call { dispatchReceiverValue, extensionReceiverValue -> - val superQualifierSymbol = call.superQualifier?.let { context.symbolTable.referenceClass(it) } + val getMethodDescriptor = descriptor.unwrappedGetMethod + val superQualifierSymbol = call.superQualifier?.let { context.symbolTable.referenceClass(it) } - val getterDescriptor = descriptor.unwrappedGetMethod - - if (getterDescriptor != null) { - val getterSymbol = context.symbolTable.referenceFunction(getterDescriptor.original) + return if (getMethodDescriptor != null) { + call.callReceiver.adjustForCallee(getMethodDescriptor).call { dispatchReceiverValue, extensionReceiverValue -> + val getterSymbol = context.symbolTable.referenceFunction(getMethodDescriptor.original) IrGetterCallImpl( startOffset, endOffset, descriptor.type.toIrType(), getterSymbol, - getterDescriptor, + getMethodDescriptor, descriptor.typeParametersCount, dispatchReceiverValue?.load(), extensionReceiverValue?.load(), @@ -160,8 +159,9 @@ class CallGenerator(statementGenerator: StatementGenerator) : StatementGenerator ).apply { putTypeArguments(call.typeArguments) { it.toIrType() } } - - } else { + } + } else { + call.callReceiver.call { dispatchReceiverValue, extensionReceiverValue -> val fieldSymbol = context.symbolTable.referenceField(descriptor.original) IrGetFieldImpl( startOffset, endOffset, diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/intermediate/IrUtils.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/intermediate/IrUtils.kt index e0860dd6f34..307f3c1e3fa 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/intermediate/IrUtils.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/intermediate/IrUtils.kt @@ -16,6 +16,7 @@ package org.jetbrains.kotlin.psi2ir.intermediate +import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor import org.jetbrains.kotlin.ir.declarations.IrVariable import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl @@ -23,3 +24,27 @@ import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl fun IrVariable.defaultLoad(): IrExpression = IrGetValueImpl(startOffset, endOffset, type, symbol) +fun CallReceiver.adjustForCallee(callee: CallableMemberDescriptor): CallReceiver = + object : CallReceiver { + override fun call(withDispatchAndExtensionReceivers: (IntermediateValue?, IntermediateValue?) -> IrExpression): IrExpression = + this@adjustForCallee.call { dispatchReceiverValue, extensionReceiverValue -> + val numReceiversPresent = listOfNotNull(dispatchReceiverValue, extensionReceiverValue).size + val numReceiversExpected = listOfNotNull(callee.dispatchReceiverParameter, callee.extensionReceiverParameter).size + if (numReceiversPresent != numReceiversExpected) + throw AssertionError("Mismatching receivers for $callee: $numReceiversPresent, expected: $numReceiversExpected") + + val newDispatchReceiverValue = + when { + callee.dispatchReceiverParameter == null -> null + dispatchReceiverValue != null -> dispatchReceiverValue + else -> extensionReceiverValue + } + val newExtensionReceiverValue = + when { + callee.extensionReceiverParameter == null -> null + dispatchReceiverValue != null && callee.dispatchReceiverParameter == null -> dispatchReceiverValue + else -> extensionReceiverValue + } + withDispatchAndExtensionReceivers(newDispatchReceiverValue, newExtensionReceiverValue) + } + } \ No newline at end of file diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/intermediate/PropertyLValue.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/intermediate/PropertyLValue.kt index 9f6aac3c79c..f53781acab4 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/intermediate/PropertyLValue.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/intermediate/PropertyLValue.kt @@ -141,11 +141,11 @@ class AccessorPropertyLValue( } override fun load(): IrExpression = - callReceiver.call { dispatchReceiverValue, extensionReceiverValue -> + callReceiver.adjustForCallee(getterDescriptor!!).call { dispatchReceiverValue, extensionReceiverValue -> IrGetterCallImpl( startOffset, endOffset, type, - getter!!, getterDescriptor!!, + getter!!, getterDescriptor, typeArgumentsCount, dispatchReceiverValue?.load(), extensionReceiverValue?.load(), @@ -157,11 +157,11 @@ class AccessorPropertyLValue( } override fun store(irExpression: IrExpression) = - callReceiver.call { dispatchReceiverValue, extensionReceiverValue -> + callReceiver.adjustForCallee(setterDescriptor!!).call { dispatchReceiverValue, extensionReceiverValue -> IrSetterCallImpl( startOffset, endOffset, context.irBuiltIns.unitType, - setter!!, setterDescriptor!!, + setter!!, setterDescriptor, typeArgumentsCount, dispatchReceiverValue?.load(), extensionReceiverValue?.load(), diff --git a/compiler/testData/ir/irText/expressions/javaSyntheticPropertyAccess.kt b/compiler/testData/ir/irText/expressions/javaSyntheticPropertyAccess.kt index 6a384159108..fb127559576 100644 --- a/compiler/testData/ir/irText/expressions/javaSyntheticPropertyAccess.kt +++ b/compiler/testData/ir/irText/expressions/javaSyntheticPropertyAccess.kt @@ -1,5 +1,8 @@ // FILE: javaSyntheticPropertyAccess.kt fun test(j: J) { + j.foo + j.foo = 1 + j.foo++ j.foo += 1 } diff --git a/compiler/testData/ir/irText/expressions/javaSyntheticPropertyAccess.txt b/compiler/testData/ir/irText/expressions/javaSyntheticPropertyAccess.txt index 17cbc83d4b3..4a9854770c1 100644 --- a/compiler/testData/ir/irText/expressions/javaSyntheticPropertyAccess.txt +++ b/compiler/testData/ir/irText/expressions/javaSyntheticPropertyAccess.txt @@ -2,12 +2,33 @@ FILE fqName: fileName:/javaSyntheticPropertyAccess.kt FUN name:test visibility:public modality:FINAL <> (j:J) returnType:kotlin.Unit flags: VALUE_PARAMETER name:j index:0 type:J flags: BLOCK_BODY + TYPE_OP type=kotlin.Unit origin=IMPLICIT_COERCION_TO_UNIT typeOperand=kotlin.Unit + typeOperand: CLASS IR_EXTERNAL_DECLARATION_STUB OBJECT name:Unit modality:FINAL visibility:public flags: superTypes:[kotlin.Any] + CALL 'getFoo(): Int' type=kotlin.Int origin=GET_PROPERTY + $this: GET_VAR 'value-parameter j: J' type=J origin=null + CALL 'setFoo(Int): Unit' type=kotlin.Unit origin=EQ + $this: GET_VAR 'value-parameter j: J' type=J origin=null + x: CONST Int type=kotlin.Int value=1 + TYPE_OP type=kotlin.Unit origin=IMPLICIT_COERCION_TO_UNIT typeOperand=kotlin.Unit + typeOperand: CLASS IR_EXTERNAL_DECLARATION_STUB OBJECT name:Unit modality:FINAL visibility:public flags: superTypes:[kotlin.Any] + BLOCK type=kotlin.Int origin=POSTFIX_INCR + VAR IR_TEMPORARY_VARIABLE name:tmp0_receiver type:J flags:val + GET_VAR 'value-parameter j: J' type=J origin=null + BLOCK type=kotlin.Int origin=POSTFIX_INCR + VAR IR_TEMPORARY_VARIABLE name:tmp1 type:kotlin.Int flags:val + CALL 'getFoo(): Int' type=kotlin.Int origin=POSTFIX_INCR + $this: GET_VAR 'tmp0_receiver: J' type=J origin=null + CALL 'setFoo(Int): Unit' type=kotlin.Unit origin=POSTFIX_INCR + $this: GET_VAR 'tmp0_receiver: J' type=J origin=null + x: CALL 'inc(): Int' type=kotlin.Int origin=POSTFIX_INCR + $this: GET_VAR 'tmp1: Int' type=kotlin.Int origin=null + GET_VAR 'tmp1: Int' type=kotlin.Int origin=null BLOCK type=kotlin.Unit origin=PLUSEQ - VAR IR_TEMPORARY_VARIABLE name:tmp0_receiver type:J flags:val + VAR IR_TEMPORARY_VARIABLE name:tmp2_receiver type:J flags:val GET_VAR 'value-parameter j: J' type=J origin=null CALL 'setFoo(Int): Unit' type=kotlin.Unit origin=PLUSEQ - $receiver: GET_VAR 'tmp0_receiver: J' type=J origin=null + $this: GET_VAR 'tmp2_receiver: J' type=J origin=null x: CALL 'plus(Int): Int' type=kotlin.Int origin=PLUSEQ $this: CALL 'getFoo(): Int' type=kotlin.Int origin=PLUSEQ - $receiver: GET_VAR 'tmp0_receiver: J' type=J origin=null + $this: GET_VAR 'tmp2_receiver: J' type=J origin=null other: CONST Int type=kotlin.Int value=1 diff --git a/compiler/testData/ir/irText/stubs/javaSyntheticProperty.txt b/compiler/testData/ir/irText/stubs/javaSyntheticProperty.txt index da3ca80b303..4336926a857 100644 --- a/compiler/testData/ir/irText/stubs/javaSyntheticProperty.txt +++ b/compiler/testData/ir/irText/stubs/javaSyntheticProperty.txt @@ -3,7 +3,7 @@ FILE fqName: fileName:/javaSyntheticProperty.kt FIELD PROPERTY_BACKING_FIELD name:test type:kotlin.String? visibility:public flags:final EXPRESSION_BODY CALL 'getFoo(): String!' type=kotlin.String? origin=GET_PROPERTY - $receiver: CALL 'constructor J()' type=J origin=null + $this: CALL 'constructor J()' type=J origin=null FUN DEFAULT_PROPERTY_ACCESSOR name: visibility:public modality:FINAL <> () returnType:kotlin.String? flags: correspondingProperty: PROPERTY name:test visibility:public modality:FINAL flags:val BLOCK_BODY diff --git a/compiler/testData/ir/irText/stubs/jdkClassSyntheticProperty.txt b/compiler/testData/ir/irText/stubs/jdkClassSyntheticProperty.txt index ea0ca002b3e..ae60acc8f8e 100644 --- a/compiler/testData/ir/irText/stubs/jdkClassSyntheticProperty.txt +++ b/compiler/testData/ir/irText/stubs/jdkClassSyntheticProperty.txt @@ -7,4 +7,4 @@ FILE fqName: fileName:/jdkClassSyntheticProperty.kt RETURN type=kotlin.Nothing from='() on Class<*>: Array<(out) Field!>!' CALL 'getDeclaredFields(): Array<(out) Field!>!' type=kotlin.Array? origin=GET_PROPERTY <`0>: kotlin.Any? - $receiver: GET_VAR 'this@test: Class<*>' type=java.lang.Class<*> origin=null + $this: GET_VAR 'this@test: Class<*>' type=java.lang.Class<*> origin=null