psi2ir: dynamic member simple assignment/augmented assignment/++/--

This commit is contained in:
Dmitry Petrov
2019-02-07 14:46:24 +03:00
parent fc76d0970b
commit 9a2bd5f4e6
13 changed files with 501 additions and 77 deletions
@@ -16,16 +16,18 @@
package org.jetbrains.kotlin.psi2ir.generators
import com.intellij.psi.tree.IElementType
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor
import org.jetbrains.kotlin.descriptors.impl.SyntheticFieldDescriptor
import org.jetbrains.kotlin.ir.builders.irBlock
import org.jetbrains.kotlin.ir.builders.irGet
import org.jetbrains.kotlin.ir.builders.irTemporary
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrDynamicOperatorExpressionImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl
import org.jetbrains.kotlin.ir.util.referenceFunction
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.endOffset
import org.jetbrains.kotlin.psi.psiUtil.startOffsetSkippingComments
@@ -35,6 +37,7 @@ import org.jetbrains.kotlin.psi2ir.unwrappedSetMethod
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.calls.callUtil.isSafeCall
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.calls.tasks.isDynamic
import org.jetbrains.kotlin.resolve.scopes.receivers.ThisClassReceiver
import org.jetbrains.kotlin.types.KotlinType
@@ -53,35 +56,74 @@ class AssignmentGenerator(statementGenerator: StatementGenerator) : StatementGen
val ktLeft = ktExpression.left!!
val ktRight = ktExpression.right!!
val irAssignmentReceiver = generateAssignmentReceiver(ktLeft, origin)
val isDynamicCall = opResolvedCall.resultingDescriptor.isDynamic()
return irAssignmentReceiver.assign { irLValue ->
val opCall = statementGenerator.pregenerateCallReceivers(opResolvedCall)
opCall.setExplicitReceiverValue(irLValue)
opCall.irValueArgumentsByIndex[0] = ktRight.genExpr()
val irOpCall = CallGenerator(statementGenerator).generateCall(ktExpression, opCall, origin)
if (isSimpleAssignment) {
// Set( Op( Get(), RHS ) )
irLValue.store(irOpCall)
if (isDynamicCall) {
IrDynamicOperatorExpressionImpl(
ktExpression.startOffsetSkippingComments, ktExpression.endOffset,
context.irBuiltIns.unitType,
getDynamicAugmentedAssignmentOperator(ktExpression.operationToken)
).apply {
left = irLValue.load()
right = ktRight.genExpr()
}
} else {
// Op( Get(), RHS )
irOpCall
val opCall = statementGenerator.pregenerateCallReceivers(opResolvedCall)
opCall.setExplicitReceiverValue(irLValue)
opCall.irValueArgumentsByIndex[0] = ktRight.genExpr()
val irOpCall = CallGenerator(statementGenerator).generateCall(ktExpression, opCall, origin)
if (isSimpleAssignment) {
// Set( Op( Get(), RHS ) )
irLValue.store(irOpCall)
} else {
// Op( Get(), RHS )
irOpCall
}
}
}
}
private fun getDynamicAugmentedAssignmentOperator(operatorToken: IElementType): IrDynamicOperator =
when (operatorToken) {
KtTokens.PLUSEQ -> IrDynamicOperator.PLUSEQ
KtTokens.MINUSEQ -> IrDynamicOperator.MINUSEQ
KtTokens.MULTEQ -> IrDynamicOperator.MULEQ
KtTokens.DIVEQ -> IrDynamicOperator.DIVEQ
KtTokens.PERCEQ -> IrDynamicOperator.MODEQ
else -> throw AssertionError("Unexpected operator token: $operatorToken")
}
fun generatePrefixIncrementDecrement(ktExpression: KtPrefixExpression, origin: IrStatementOrigin): IrExpression {
val opResolvedCall = getResolvedCall(ktExpression)!!
val ktBaseExpression = ktExpression.baseExpression!!
val irAssignmentReceiver = generateAssignmentReceiver(ktBaseExpression, origin)
val irAssignmentReceiver = generateAssignmentReceiver(ktBaseExpression, origin, isAssignmentStatement = false)
val isDynamicCall = opResolvedCall.resultingDescriptor.isDynamic()
return irAssignmentReceiver.assign { irLValue ->
irBlock(ktExpression.startOffsetSkippingComments, ktExpression.endOffset, origin, irLValue.type) {
val opCall = statementGenerator.pregenerateCall(opResolvedCall)
opCall.setExplicitReceiverValue(irLValue)
val irOpCall = CallGenerator(statementGenerator).generateCall(ktExpression, opCall, origin)
+irLValue.store(irOpCall)
+irLValue.load()
val startOffset = ktExpression.startOffsetSkippingComments
val endOffset = ktExpression.endOffset
if (isDynamicCall) {
IrDynamicOperatorExpressionImpl(
startOffset, endOffset,
irLValue.type,
if (ktExpression.operationToken == KtTokens.PLUSPLUS)
IrDynamicOperator.PREFIX_INCREMENT
else
IrDynamicOperator.PREFIX_DECREMENT
).apply {
receiver = irLValue.load()
}
} else {
irBlock(startOffset, endOffset, origin, irLValue.type) {
val opCall = statementGenerator.pregenerateCall(opResolvedCall)
opCall.setExplicitReceiverValue(irLValue)
val irOpCall = CallGenerator(statementGenerator).generateCall(ktExpression, opCall, origin)
+irLValue.store(irOpCall)
+irLValue.load()
}
}
}
}
@@ -89,23 +131,43 @@ class AssignmentGenerator(statementGenerator: StatementGenerator) : StatementGen
fun generatePostfixIncrementDecrement(ktExpression: KtPostfixExpression, origin: IrStatementOrigin): IrExpression {
val opResolvedCall = getResolvedCall(ktExpression)!!
val ktBaseExpression = ktExpression.baseExpression!!
val irAssignmentReceiver = generateAssignmentReceiver(ktBaseExpression, origin)
val irAssignmentReceiver = generateAssignmentReceiver(ktBaseExpression, origin, isAssignmentStatement = false)
val isDynamicCall = opResolvedCall.resultingDescriptor.isDynamic()
val startOffset = ktExpression.startOffsetSkippingComments
val endOffset = ktExpression.endOffset
return irAssignmentReceiver.assign { irLValue ->
irBlock(ktExpression.startOffsetSkippingComments, ktExpression.endOffset, origin, irLValue.type) {
val temporary = irTemporary(irLValue.load())
val opCall = statementGenerator.pregenerateCall(opResolvedCall)
opCall.setExplicitReceiverValue(
VariableLValue(context, startOffset, endOffset, temporary.symbol, temporary.type)
)
val irOpCall = CallGenerator(statementGenerator).generateCall(ktExpression, opCall, origin)
+irLValue.store(irOpCall)
+irGet(temporary.type, temporary.symbol)
if (isDynamicCall) {
IrDynamicOperatorExpressionImpl(
startOffset, endOffset,
irLValue.type,
if (ktExpression.operationToken == KtTokens.PLUSPLUS)
IrDynamicOperator.POSTFIX_INCREMENT
else
IrDynamicOperator.POSTFIX_DECREMENT
).apply {
receiver = irLValue.load()
}
} else {
irBlock(startOffset, endOffset, origin, irLValue.type) {
val temporary = irTemporary(irLValue.load())
val opCall = statementGenerator.pregenerateCall(opResolvedCall)
opCall.setExplicitReceiverValue(
VariableLValue(context, startOffset, endOffset, temporary.symbol, temporary.type)
)
val irOpCall = CallGenerator(statementGenerator).generateCall(ktExpression, opCall, origin)
+irLValue.store(irOpCall)
+irGet(temporary.type, temporary.symbol)
}
}
}
}
private fun generateAssignmentReceiver(ktLeft: KtExpression, origin: IrStatementOrigin): AssignmentReceiver {
private fun generateAssignmentReceiver(
ktLeft: KtExpression,
origin: IrStatementOrigin,
isAssignmentStatement: Boolean = true
): AssignmentReceiver {
if (ktLeft is KtArrayAccessExpression) {
return generateArrayAccessAssignmentReceiver(ktLeft, origin)
}
@@ -116,7 +178,12 @@ class AssignmentGenerator(statementGenerator: StatementGenerator) : StatementGen
return when (descriptor) {
is SyntheticFieldDescriptor -> {
val receiverValue =
statementGenerator.generateBackingFieldReceiver(ktLeft.startOffsetSkippingComments, ktLeft.endOffset, resolvedCall, descriptor)
statementGenerator.generateBackingFieldReceiver(
ktLeft.startOffsetSkippingComments,
ktLeft.endOffset,
resolvedCall,
descriptor
)
createBackingFieldLValue(ktLeft, descriptor.propertyDescriptor, receiverValue, origin)
}
is LocalVariableDescriptor ->
@@ -133,7 +200,7 @@ class AssignmentGenerator(statementGenerator: StatementGenerator) : StatementGen
else
createVariableValue(ktLeft, descriptor, origin)
is PropertyDescriptor ->
generateAssignmentReceiverForProperty(descriptor, origin, ktLeft, resolvedCall)
generateAssignmentReceiverForProperty(descriptor, origin, ktLeft, resolvedCall, isAssignmentStatement)
is ValueDescriptor ->
createVariableValue(ktLeft, descriptor, origin)
else ->
@@ -172,29 +239,45 @@ class AssignmentGenerator(statementGenerator: StatementGenerator) : StatementGen
descriptor: PropertyDescriptor,
origin: IrStatementOrigin,
ktLeft: KtExpression,
resolvedCall: ResolvedCall<*>
resolvedCall: ResolvedCall<*>,
isAssignmentStatement: Boolean
): AssignmentReceiver =
if (isValInitializationInConstructor(descriptor, resolvedCall)) {
val thisClass = getThisClass()
val thisAsReceiverParameter = thisClass.thisAsReceiverParameter
val thisType = thisAsReceiverParameter.type.toIrType()
val irThis = IrGetValueImpl(
ktLeft.startOffsetSkippingComments, ktLeft.endOffset,
thisType,
context.symbolTable.referenceValueParameter(thisAsReceiverParameter)
)
createBackingFieldLValue(ktLeft, descriptor, RematerializableValue(irThis), null)
} else {
val propertyReceiver = statementGenerator.generateCallReceiver(
ktLeft, descriptor, resolvedCall.dispatchReceiver, resolvedCall.extensionReceiver,
isSafe = resolvedCall.call.isSafeCall(),
isAssignmentReceiver = true
)
when {
descriptor.isDynamic() ->
DynamicMemberLValue(
context,
ktLeft.startOffsetSkippingComments, ktLeft.endOffset,
descriptor.type.toIrType(),
descriptor.name.asString(),
statementGenerator.generateCallReceiver(
ktLeft, descriptor, resolvedCall.dispatchReceiver, resolvedCall.extensionReceiver,
isSafe = resolvedCall.call.isSafeCall(),
isAssignmentReceiver = isAssignmentStatement
)
)
isValInitializationInConstructor(descriptor, resolvedCall) -> {
val thisClass = getThisClass()
val thisAsReceiverParameter = thisClass.thisAsReceiverParameter
val thisType = thisAsReceiverParameter.type.toIrType()
val irThis = IrGetValueImpl(
ktLeft.startOffsetSkippingComments, ktLeft.endOffset,
thisType,
context.symbolTable.referenceValueParameter(thisAsReceiverParameter)
)
createBackingFieldLValue(ktLeft, descriptor, RematerializableValue(irThis), null)
}
else -> {
val propertyReceiver = statementGenerator.generateCallReceiver(
ktLeft, descriptor, resolvedCall.dispatchReceiver, resolvedCall.extensionReceiver,
isSafe = resolvedCall.call.isSafeCall(),
isAssignmentReceiver = isAssignmentStatement
)
val superQualifier = getSuperQualifier(resolvedCall)
val superQualifier = getSuperQualifier(resolvedCall)
// TODO property imported from an object
createPropertyLValue(ktLeft, descriptor, propertyReceiver, getTypeArguments(resolvedCall), origin, superQualifier)
// TODO property imported from an object
createPropertyLValue(ktLeft, descriptor, propertyReceiver, getTypeArguments(resolvedCall), origin, superQualifier)
}
}
private fun createPropertyLValue(
@@ -0,0 +1,67 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.psi2ir.intermediate
import org.jetbrains.kotlin.ir.builders.IrGeneratorContext
import org.jetbrains.kotlin.ir.expressions.IrDynamicOperator
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.impl.IrDynamicMemberExpressionImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrDynamicOperatorExpressionImpl
import org.jetbrains.kotlin.ir.expressions.left
import org.jetbrains.kotlin.ir.expressions.right
import org.jetbrains.kotlin.ir.types.IrType
class DynamicMemberLValue(
private val context: IrGeneratorContext,
private val startOffset: Int,
private val endOffset: Int,
override val type: IrType,
private val memberName: String,
private val receiver: CallReceiver
) : LValue, AssignmentReceiver {
override fun load(): IrExpression =
receiver.call { dispatchReceiverValue, extensionReceiverValue ->
val dynamicReceiver = getDynamicReceiver(dispatchReceiverValue, extensionReceiverValue)
IrDynamicMemberExpressionImpl(
startOffset, endOffset,
type,
memberName,
dynamicReceiver
)
}
override fun store(irExpression: IrExpression): IrExpression =
receiver.call { dispatchReceiverValue, extensionReceiverValue ->
val dynamicReceiver = getDynamicReceiver(dispatchReceiverValue, extensionReceiverValue)
IrDynamicOperatorExpressionImpl(
startOffset, endOffset,
context.irBuiltIns.unitType,
IrDynamicOperator.EQ
).apply {
left = dynamicReceiver
right = irExpression
}
}
override fun assign(withLValue: (LValue) -> IrExpression): IrExpression =
receiver.call { dispatchReceiverValue, extensionReceiverValue ->
withLValue(
DynamicMemberLValue(
context, startOffset, endOffset, type, memberName,
SimpleCallReceiver(dispatchReceiverValue, extensionReceiverValue)
)
)
}
private fun getDynamicReceiver(dispatchReceiverValue: IntermediateValue?, extensionReceiverValue: IntermediateValue?): IrExpression {
if (dispatchReceiverValue == null) throw AssertionError("Dynamic call $memberName should have a dispatch receiver")
if (extensionReceiverValue != null) throw AssertionError("Dynamic call $memberName should have no extension receiver")
return dispatchReceiverValue.load()
}
}
@@ -16,15 +16,14 @@
package org.jetbrains.kotlin.psi2ir.intermediate
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.builders.buildStatement
import org.jetbrains.kotlin.ir.builders.irIfNull
import org.jetbrains.kotlin.ir.builders.irNull
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin
import org.jetbrains.kotlin.ir.expressions.impl.IrBlockImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrIfThenElseImpl
import org.jetbrains.kotlin.ir.types.makeNullable
import org.jetbrains.kotlin.psi2ir.generators.GeneratorWithScope
import org.jetbrains.kotlin.types.typeUtil.builtIns
import org.jetbrains.kotlin.types.typeUtil.makeNullable
class SafeCallReceiver(
@@ -33,7 +32,7 @@ class SafeCallReceiver(
val endOffset: Int,
val extensionReceiver: IntermediateValue?,
val dispatchReceiver: IntermediateValue?,
val isAssignmentReceiver: Boolean
val isStatement: Boolean
) : CallReceiver {
override fun call(withDispatchAndExtensionReceivers: (IntermediateValue?, IntermediateValue?) -> IrExpression): IrExpression {
@@ -52,7 +51,7 @@ class SafeCallReceiver(
val irResult = withDispatchAndExtensionReceivers(dispatchReceiverValue, extensionReceiverValue)
val resultType = if (isAssignmentReceiver) generator.context.irBuiltIns.unitType else irResult.type.makeNullable()
val resultType = if (isStatement) generator.context.irBuiltIns.unitType else irResult.type.makeNullable()
val irBlock = IrBlockImpl(startOffset, endOffset, resultType, IrStatementOrigin.SAFE_CALL)
@@ -79,7 +78,7 @@ fun IrExpression.safeCallOnDispatchReceiver(
generator, startOffset, endOffset,
extensionReceiver = null,
dispatchReceiver = OnceExpressionValue(this),
isAssignmentReceiver = false
isStatement = false
).call { dispatchReceiverValue, _ ->
ifNotNull(dispatchReceiverValue!!.load())
}
@@ -24,7 +24,10 @@ var IrDynamicOperatorExpression.left: IrExpression
var IrDynamicOperatorExpression.right: IrExpression
get() = arguments[0]
set(value) {
arguments[0] = value
if (arguments.isEmpty())
arguments.add(value)
else
arguments[0] = value
}
interface IrDynamicMemberExpression : IrDynamicExpression {
@@ -11,6 +11,7 @@ import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.visitors.IrElementTransformer
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
import org.jetbrains.kotlin.utils.SmartList
class IrDynamicOperatorExpressionImpl(
startOffset: Int,
@@ -23,7 +24,7 @@ class IrDynamicOperatorExpressionImpl(
override lateinit var receiver: IrExpression
override val arguments: MutableList<IrExpression> = ArrayList()
override val arguments: MutableList<IrExpression> = SmartList()
override fun <R, D> accept(visitor: IrElementVisitor<R, D>, data: D): R =
visitor.visitDynamicOperatorExpression(this, data)
@@ -0,0 +1,7 @@
fun testMemberAssignment(d: dynamic) {
d.m = 1
}
fun testSafeMemberAssignment(d: dynamic) {
d?.m = 1
}
@@ -0,0 +1,26 @@
FILE fqName:<root> fileName:/dynamicMemberAssignment.kt
FUN name:testMemberAssignment visibility:public modality:FINAL <> (d:dynamic) returnType:kotlin.Unit flags:
VALUE_PARAMETER name:d index:0 type:dynamic flags:
BLOCK_BODY
DYN_OP operator=EQ type=kotlin.Unit
receiver: GET_VAR 'value-parameter d: dynamic' type=dynamic origin=null
0: CONST Int type=kotlin.Int value=1
FUN name:testSafeMemberAssignment visibility:public modality:FINAL <> (d:dynamic) returnType:kotlin.Unit flags:
VALUE_PARAMETER name:d index:0 type:dynamic flags:
BLOCK_BODY
BLOCK type=kotlin.Unit origin=SAFE_CALL
VAR IR_TEMPORARY_VARIABLE name:tmp0_safe_receiver type:dynamic flags:val
GET_VAR 'value-parameter d: dynamic' type=dynamic origin=null
WHEN type=kotlin.Unit origin=null
BRANCH
if: CALL 'EQEQ(Any?, Any?): Boolean' type=kotlin.Boolean origin=EQEQ
arg0: GET_VAR 'tmp0_safe_receiver: dynamic' type=dynamic origin=null
arg1: CONST Null type=kotlin.Nothing? value=null
then: 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]
CONST Null type=kotlin.Nothing? value=null
BRANCH
if: CONST Boolean type=kotlin.Boolean value=true
then: DYN_OP operator=EQ type=kotlin.Unit
receiver: GET_VAR 'tmp0_safe_receiver: dynamic' type=dynamic origin=null
0: CONST Int type=kotlin.Int value=1
@@ -0,0 +1,16 @@
fun testAugmentedMemberAssignment(d: dynamic) {
d.m += "+="
d.m -= "-="
d.m *= "*="
d.m /= "/="
d.m %= "%="
}
// see KT-29768
fun testSafeAugmentedMemberAssignment(d: dynamic) {
d?.m += "+="
d?.m -= "-="
d?.m *= "*="
d?.m /= "/="
d?.m %= "%="
}
@@ -0,0 +1,112 @@
FILE fqName:<root> fileName:/dynamicMemberAugmentedAssignment.kt
FUN name:testAugmentedMemberAssignment visibility:public modality:FINAL <> (d:dynamic) returnType:kotlin.Unit flags:
VALUE_PARAMETER name:d index:0 type:dynamic flags:
BLOCK_BODY
DYN_OP operator=PLUSEQ type=kotlin.Unit
receiver: DYN_MEMBER memberName='m' type=dynamic
GET_VAR 'value-parameter d: dynamic' type=dynamic origin=null
0: CONST String type=kotlin.String value="+="
DYN_OP operator=MINUSEQ type=kotlin.Unit
receiver: DYN_MEMBER memberName='m' type=dynamic
GET_VAR 'value-parameter d: dynamic' type=dynamic origin=null
0: CONST String type=kotlin.String value="-="
DYN_OP operator=MULEQ type=kotlin.Unit
receiver: DYN_MEMBER memberName='m' type=dynamic
GET_VAR 'value-parameter d: dynamic' type=dynamic origin=null
0: CONST String type=kotlin.String value="*="
DYN_OP operator=DIVEQ type=kotlin.Unit
receiver: DYN_MEMBER memberName='m' type=dynamic
GET_VAR 'value-parameter d: dynamic' type=dynamic origin=null
0: CONST String type=kotlin.String value="/="
DYN_OP operator=MODEQ type=kotlin.Unit
receiver: DYN_MEMBER memberName='m' type=dynamic
GET_VAR 'value-parameter d: dynamic' type=dynamic origin=null
0: CONST String type=kotlin.String value="%="
FUN name:testSafeAugmentedMemberAssignment visibility:public modality:FINAL <> (d:dynamic) returnType:kotlin.Unit flags:
VALUE_PARAMETER name:d index:0 type:dynamic flags:
BLOCK_BODY
BLOCK type=kotlin.Unit origin=SAFE_CALL
VAR IR_TEMPORARY_VARIABLE name:tmp0_safe_receiver type:dynamic flags:val
GET_VAR 'value-parameter d: dynamic' type=dynamic origin=null
WHEN type=kotlin.Unit origin=null
BRANCH
if: CALL 'EQEQ(Any?, Any?): Boolean' type=kotlin.Boolean origin=EQEQ
arg0: GET_VAR 'tmp0_safe_receiver: dynamic' type=dynamic origin=null
arg1: CONST Null type=kotlin.Nothing? value=null
then: 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]
CONST Null type=kotlin.Nothing? value=null
BRANCH
if: CONST Boolean type=kotlin.Boolean value=true
then: DYN_OP operator=PLUSEQ type=kotlin.Unit
receiver: DYN_MEMBER memberName='m' type=dynamic
GET_VAR 'tmp0_safe_receiver: dynamic' type=dynamic origin=null
0: CONST String type=kotlin.String value="+="
BLOCK type=kotlin.Unit origin=SAFE_CALL
VAR IR_TEMPORARY_VARIABLE name:tmp1_safe_receiver type:dynamic flags:val
GET_VAR 'value-parameter d: dynamic' type=dynamic origin=null
WHEN type=kotlin.Unit origin=null
BRANCH
if: CALL 'EQEQ(Any?, Any?): Boolean' type=kotlin.Boolean origin=EQEQ
arg0: GET_VAR 'tmp1_safe_receiver: dynamic' type=dynamic origin=null
arg1: CONST Null type=kotlin.Nothing? value=null
then: 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]
CONST Null type=kotlin.Nothing? value=null
BRANCH
if: CONST Boolean type=kotlin.Boolean value=true
then: DYN_OP operator=MINUSEQ type=kotlin.Unit
receiver: DYN_MEMBER memberName='m' type=dynamic
GET_VAR 'tmp1_safe_receiver: dynamic' type=dynamic origin=null
0: CONST String type=kotlin.String value="-="
BLOCK type=kotlin.Unit origin=SAFE_CALL
VAR IR_TEMPORARY_VARIABLE name:tmp2_safe_receiver type:dynamic flags:val
GET_VAR 'value-parameter d: dynamic' type=dynamic origin=null
WHEN type=kotlin.Unit origin=null
BRANCH
if: CALL 'EQEQ(Any?, Any?): Boolean' type=kotlin.Boolean origin=EQEQ
arg0: GET_VAR 'tmp2_safe_receiver: dynamic' type=dynamic origin=null
arg1: CONST Null type=kotlin.Nothing? value=null
then: 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]
CONST Null type=kotlin.Nothing? value=null
BRANCH
if: CONST Boolean type=kotlin.Boolean value=true
then: DYN_OP operator=MULEQ type=kotlin.Unit
receiver: DYN_MEMBER memberName='m' type=dynamic
GET_VAR 'tmp2_safe_receiver: dynamic' type=dynamic origin=null
0: CONST String type=kotlin.String value="*="
BLOCK type=kotlin.Unit origin=SAFE_CALL
VAR IR_TEMPORARY_VARIABLE name:tmp3_safe_receiver type:dynamic flags:val
GET_VAR 'value-parameter d: dynamic' type=dynamic origin=null
WHEN type=kotlin.Unit origin=null
BRANCH
if: CALL 'EQEQ(Any?, Any?): Boolean' type=kotlin.Boolean origin=EQEQ
arg0: GET_VAR 'tmp3_safe_receiver: dynamic' type=dynamic origin=null
arg1: CONST Null type=kotlin.Nothing? value=null
then: 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]
CONST Null type=kotlin.Nothing? value=null
BRANCH
if: CONST Boolean type=kotlin.Boolean value=true
then: DYN_OP operator=DIVEQ type=kotlin.Unit
receiver: DYN_MEMBER memberName='m' type=dynamic
GET_VAR 'tmp3_safe_receiver: dynamic' type=dynamic origin=null
0: CONST String type=kotlin.String value="/="
BLOCK type=kotlin.Unit origin=SAFE_CALL
VAR IR_TEMPORARY_VARIABLE name:tmp4_safe_receiver type:dynamic flags:val
GET_VAR 'value-parameter d: dynamic' type=dynamic origin=null
WHEN type=kotlin.Unit origin=null
BRANCH
if: CALL 'EQEQ(Any?, Any?): Boolean' type=kotlin.Boolean origin=EQEQ
arg0: GET_VAR 'tmp4_safe_receiver: dynamic' type=dynamic origin=null
arg1: CONST Null type=kotlin.Nothing? value=null
then: 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]
CONST Null type=kotlin.Nothing? value=null
BRANCH
if: CONST Boolean type=kotlin.Boolean value=true
then: DYN_OP operator=MODEQ type=kotlin.Unit
receiver: DYN_MEMBER memberName='m' type=dynamic
GET_VAR 'tmp4_safe_receiver: dynamic' type=dynamic origin=null
0: CONST String type=kotlin.String value="%="
@@ -0,0 +1,14 @@
fun testMemberIncrementDecrement(d: dynamic) {
val t1 = ++d.prefixIncr
val t2 = --d.prefixDecr
val t3 = d.postfixIncr++
val t4 = d.postfixDecr--
}
fun testSafeMemberIncrementDecrement(d: dynamic) {
val t1 = ++d?.prefixIncr
val t2 = --d?.prefixDecr
val t3 = d?.postfixIncr++
val t4 = d?.postfixDecr--
}
@@ -0,0 +1,83 @@
FILE fqName:<root> fileName:/dynamicMemberIncrementDecrement.kt
FUN name:testMemberIncrementDecrement visibility:public modality:FINAL <> (d:dynamic) returnType:kotlin.Unit flags:
VALUE_PARAMETER name:d index:0 type:dynamic flags:
BLOCK_BODY
VAR name:t1 type:dynamic flags:val
DYN_OP operator=PREFIX_INCREMENT type=dynamic
receiver: DYN_MEMBER memberName='prefixIncr' type=dynamic
GET_VAR 'value-parameter d: dynamic' type=dynamic origin=null
VAR name:t2 type:dynamic flags:val
DYN_OP operator=PREFIX_DECREMENT type=dynamic
receiver: DYN_MEMBER memberName='prefixDecr' type=dynamic
GET_VAR 'value-parameter d: dynamic' type=dynamic origin=null
VAR name:t3 type:dynamic flags:val
DYN_OP operator=POSTFIX_INCREMENT type=dynamic
receiver: DYN_MEMBER memberName='postfixIncr' type=dynamic
GET_VAR 'value-parameter d: dynamic' type=dynamic origin=null
VAR name:t4 type:dynamic flags:val
DYN_OP operator=POSTFIX_DECREMENT type=dynamic
receiver: DYN_MEMBER memberName='postfixDecr' type=dynamic
GET_VAR 'value-parameter d: dynamic' type=dynamic origin=null
FUN name:testSafeMemberIncrementDecrement visibility:public modality:FINAL <> (d:dynamic) returnType:kotlin.Unit flags:
VALUE_PARAMETER name:d index:0 type:dynamic flags:
BLOCK_BODY
VAR name:t1 type:dynamic flags:val
BLOCK type=dynamic origin=SAFE_CALL
VAR IR_TEMPORARY_VARIABLE name:tmp0_safe_receiver type:dynamic flags:val
GET_VAR 'value-parameter d: dynamic' type=dynamic origin=null
WHEN type=dynamic origin=null
BRANCH
if: CALL 'EQEQ(Any?, Any?): Boolean' type=kotlin.Boolean origin=EQEQ
arg0: GET_VAR 'tmp0_safe_receiver: dynamic' type=dynamic origin=null
arg1: CONST Null type=kotlin.Nothing? value=null
then: CONST Null type=kotlin.Nothing? value=null
BRANCH
if: CONST Boolean type=kotlin.Boolean value=true
then: DYN_OP operator=PREFIX_INCREMENT type=dynamic
receiver: DYN_MEMBER memberName='prefixIncr' type=dynamic
GET_VAR 'tmp0_safe_receiver: dynamic' type=dynamic origin=null
VAR name:t2 type:dynamic flags:val
BLOCK type=dynamic origin=SAFE_CALL
VAR IR_TEMPORARY_VARIABLE name:tmp1_safe_receiver type:dynamic flags:val
GET_VAR 'value-parameter d: dynamic' type=dynamic origin=null
WHEN type=dynamic origin=null
BRANCH
if: CALL 'EQEQ(Any?, Any?): Boolean' type=kotlin.Boolean origin=EQEQ
arg0: GET_VAR 'tmp1_safe_receiver: dynamic' type=dynamic origin=null
arg1: CONST Null type=kotlin.Nothing? value=null
then: CONST Null type=kotlin.Nothing? value=null
BRANCH
if: CONST Boolean type=kotlin.Boolean value=true
then: DYN_OP operator=PREFIX_DECREMENT type=dynamic
receiver: DYN_MEMBER memberName='prefixDecr' type=dynamic
GET_VAR 'tmp1_safe_receiver: dynamic' type=dynamic origin=null
VAR name:t3 type:dynamic flags:val
BLOCK type=dynamic origin=SAFE_CALL
VAR IR_TEMPORARY_VARIABLE name:tmp2_safe_receiver type:dynamic flags:val
GET_VAR 'value-parameter d: dynamic' type=dynamic origin=null
WHEN type=dynamic origin=null
BRANCH
if: CALL 'EQEQ(Any?, Any?): Boolean' type=kotlin.Boolean origin=EQEQ
arg0: GET_VAR 'tmp2_safe_receiver: dynamic' type=dynamic origin=null
arg1: CONST Null type=kotlin.Nothing? value=null
then: CONST Null type=kotlin.Nothing? value=null
BRANCH
if: CONST Boolean type=kotlin.Boolean value=true
then: DYN_OP operator=POSTFIX_INCREMENT type=dynamic
receiver: DYN_MEMBER memberName='postfixIncr' type=dynamic
GET_VAR 'tmp2_safe_receiver: dynamic' type=dynamic origin=null
VAR name:t4 type:dynamic flags:val
BLOCK type=dynamic origin=SAFE_CALL
VAR IR_TEMPORARY_VARIABLE name:tmp3_safe_receiver type:dynamic flags:val
GET_VAR 'value-parameter d: dynamic' type=dynamic origin=null
WHEN type=dynamic origin=null
BRANCH
if: CALL 'EQEQ(Any?, Any?): Boolean' type=kotlin.Boolean origin=EQEQ
arg0: GET_VAR 'tmp3_safe_receiver: dynamic' type=dynamic origin=null
arg1: CONST Null type=kotlin.Nothing? value=null
then: CONST Null type=kotlin.Nothing? value=null
BRANCH
if: CONST Boolean type=kotlin.Boolean value=true
then: DYN_OP operator=POSTFIX_DECREMENT type=dynamic
receiver: DYN_MEMBER memberName='postfixDecr' type=dynamic
GET_VAR 'tmp3_safe_receiver: dynamic' type=dynamic origin=null
@@ -61,22 +61,20 @@ FILE fqName:test fileName:/safeCallWithIncrementDecrement.kt
FUN name:testProperty visibility:public modality:FINAL <> (nc:test.C?) returnType:kotlin.Unit flags:
VALUE_PARAMETER name:nc index:0 type:test.C? flags:
BLOCK_BODY
BLOCK type=kotlin.Unit origin=SAFE_CALL
VAR IR_TEMPORARY_VARIABLE name:tmp0_safe_receiver type:test.C? flags:val
GET_VAR 'value-parameter nc: C?' type=test.C? origin=null
WHEN type=kotlin.Unit origin=null
BRANCH
if: CALL 'EQEQ(Any?, Any?): Boolean' type=kotlin.Boolean origin=EQEQ
arg0: GET_VAR 'tmp0_safe_receiver: C?' type=test.C? origin=null
arg1: CONST Null type=kotlin.Nothing? value=null
then: 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]
CONST Null type=kotlin.Nothing? value=null
BRANCH
if: CONST Boolean type=kotlin.Boolean value=true
then: 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
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=SAFE_CALL
VAR IR_TEMPORARY_VARIABLE name:tmp0_safe_receiver type:test.C? flags:val
GET_VAR 'value-parameter nc: C?' type=test.C? origin=null
WHEN type=kotlin.Int? origin=null
BRANCH
if: CALL 'EQEQ(Any?, Any?): Boolean' type=kotlin.Boolean origin=EQEQ
arg0: GET_VAR 'tmp0_safe_receiver: C?' type=test.C? origin=null
arg1: CONST Null type=kotlin.Nothing? value=null
then: CONST Null type=kotlin.Nothing? value=null
BRANCH
if: CONST Boolean type=kotlin.Boolean value=true
then: BLOCK type=kotlin.Int origin=POSTFIX_INCR
VAR IR_TEMPORARY_VARIABLE name:tmp1_receiver type:test.C? flags:val
GET_VAR 'tmp0_safe_receiver: C?' type=test.C? origin=null
BLOCK type=kotlin.Int origin=POSTFIX_INCR
@@ -56,6 +56,21 @@ public class IrJsTextTestCaseGenerated extends AbstractIrJsTextTestCase {
runTest("compiler/testData/ir/irJsText/dynamic/dynamicMemberAccess.kt");
}
@TestMetadata("dynamicMemberAssignment.kt")
public void testDynamicMemberAssignment() throws Exception {
runTest("compiler/testData/ir/irJsText/dynamic/dynamicMemberAssignment.kt");
}
@TestMetadata("dynamicMemberAugmentedAssignment.kt")
public void testDynamicMemberAugmentedAssignment() throws Exception {
runTest("compiler/testData/ir/irJsText/dynamic/dynamicMemberAugmentedAssignment.kt");
}
@TestMetadata("dynamicMemberIncrementDecrement.kt")
public void testDynamicMemberIncrementDecrement() throws Exception {
runTest("compiler/testData/ir/irJsText/dynamic/dynamicMemberIncrementDecrement.kt");
}
@TestMetadata("dynamicWithImplicitCast.kt")
public void testDynamicWithImplicitCast() throws Exception {
runTest("compiler/testData/ir/irJsText/dynamic/dynamicWithImplicitCast.kt");