psi2ir: dynamic member simple assignment/augmented assignment/++/--
This commit is contained in:
+135
-52
@@ -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(
|
||||
|
||||
+67
@@ -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()
|
||||
}
|
||||
}
|
||||
+6
-7
@@ -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 {
|
||||
|
||||
+2
-1
@@ -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
|
||||
+14
-16
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user