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.
This commit is contained in:
Dmitry Petrov
2018-07-19 15:37:57 +03:00
parent 73acc86785
commit 779cae7db5
7 changed files with 67 additions and 18 deletions
@@ -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,
@@ -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)
}
}
@@ -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(),
@@ -1,5 +1,8 @@
// FILE: javaSyntheticPropertyAccess.kt
fun test(j: J) {
j.foo
j.foo = 1
j.foo++
j.foo += 1
}
@@ -2,12 +2,33 @@ FILE fqName:<root> 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
@@ -3,7 +3,7 @@ FILE fqName:<root> 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:<get-test> visibility:public modality:FINAL <> () returnType:kotlin.String? flags:
correspondingProperty: PROPERTY name:test visibility:public modality:FINAL flags:val
BLOCK_BODY
@@ -7,4 +7,4 @@ FILE fqName:<root> fileName:/jdkClassSyntheticProperty.kt
RETURN type=kotlin.Nothing from='<get-test>() on Class<*>: Array<(out) Field!>!'
CALL 'getDeclaredFields(): Array<(out) Field!>!' type=kotlin.Array<out java.lang.reflect.Field!>? 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