psi2ir: dynamic member simple assignment/augmented assignment/++/--
This commit is contained in:
+135
-52
@@ -16,16 +16,18 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.psi2ir.generators
|
package org.jetbrains.kotlin.psi2ir.generators
|
||||||
|
|
||||||
|
import com.intellij.psi.tree.IElementType
|
||||||
import org.jetbrains.kotlin.descriptors.*
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor
|
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.impl.SyntheticFieldDescriptor
|
import org.jetbrains.kotlin.descriptors.impl.SyntheticFieldDescriptor
|
||||||
import org.jetbrains.kotlin.ir.builders.irBlock
|
import org.jetbrains.kotlin.ir.builders.irBlock
|
||||||
import org.jetbrains.kotlin.ir.builders.irGet
|
import org.jetbrains.kotlin.ir.builders.irGet
|
||||||
import org.jetbrains.kotlin.ir.builders.irTemporary
|
import org.jetbrains.kotlin.ir.builders.irTemporary
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
import org.jetbrains.kotlin.ir.expressions.*
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin
|
import org.jetbrains.kotlin.ir.expressions.impl.IrDynamicOperatorExpressionImpl
|
||||||
import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl
|
import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl
|
||||||
import org.jetbrains.kotlin.ir.util.referenceFunction
|
import org.jetbrains.kotlin.ir.util.referenceFunction
|
||||||
|
import org.jetbrains.kotlin.lexer.KtTokens
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.endOffset
|
import org.jetbrains.kotlin.psi.psiUtil.endOffset
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.startOffsetSkippingComments
|
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.BindingContext
|
||||||
import org.jetbrains.kotlin.resolve.calls.callUtil.isSafeCall
|
import org.jetbrains.kotlin.resolve.calls.callUtil.isSafeCall
|
||||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
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.resolve.scopes.receivers.ThisClassReceiver
|
||||||
import org.jetbrains.kotlin.types.KotlinType
|
import org.jetbrains.kotlin.types.KotlinType
|
||||||
|
|
||||||
@@ -53,35 +56,74 @@ class AssignmentGenerator(statementGenerator: StatementGenerator) : StatementGen
|
|||||||
val ktLeft = ktExpression.left!!
|
val ktLeft = ktExpression.left!!
|
||||||
val ktRight = ktExpression.right!!
|
val ktRight = ktExpression.right!!
|
||||||
val irAssignmentReceiver = generateAssignmentReceiver(ktLeft, origin)
|
val irAssignmentReceiver = generateAssignmentReceiver(ktLeft, origin)
|
||||||
|
val isDynamicCall = opResolvedCall.resultingDescriptor.isDynamic()
|
||||||
|
|
||||||
return irAssignmentReceiver.assign { irLValue ->
|
return irAssignmentReceiver.assign { irLValue ->
|
||||||
val opCall = statementGenerator.pregenerateCallReceivers(opResolvedCall)
|
if (isDynamicCall) {
|
||||||
opCall.setExplicitReceiverValue(irLValue)
|
IrDynamicOperatorExpressionImpl(
|
||||||
opCall.irValueArgumentsByIndex[0] = ktRight.genExpr()
|
ktExpression.startOffsetSkippingComments, ktExpression.endOffset,
|
||||||
val irOpCall = CallGenerator(statementGenerator).generateCall(ktExpression, opCall, origin)
|
context.irBuiltIns.unitType,
|
||||||
|
getDynamicAugmentedAssignmentOperator(ktExpression.operationToken)
|
||||||
if (isSimpleAssignment) {
|
).apply {
|
||||||
// Set( Op( Get(), RHS ) )
|
left = irLValue.load()
|
||||||
irLValue.store(irOpCall)
|
right = ktRight.genExpr()
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// Op( Get(), RHS )
|
val opCall = statementGenerator.pregenerateCallReceivers(opResolvedCall)
|
||||||
irOpCall
|
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 {
|
fun generatePrefixIncrementDecrement(ktExpression: KtPrefixExpression, origin: IrStatementOrigin): IrExpression {
|
||||||
val opResolvedCall = getResolvedCall(ktExpression)!!
|
val opResolvedCall = getResolvedCall(ktExpression)!!
|
||||||
val ktBaseExpression = ktExpression.baseExpression!!
|
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 ->
|
return irAssignmentReceiver.assign { irLValue ->
|
||||||
irBlock(ktExpression.startOffsetSkippingComments, ktExpression.endOffset, origin, irLValue.type) {
|
val startOffset = ktExpression.startOffsetSkippingComments
|
||||||
val opCall = statementGenerator.pregenerateCall(opResolvedCall)
|
val endOffset = ktExpression.endOffset
|
||||||
opCall.setExplicitReceiverValue(irLValue)
|
|
||||||
val irOpCall = CallGenerator(statementGenerator).generateCall(ktExpression, opCall, origin)
|
if (isDynamicCall) {
|
||||||
+irLValue.store(irOpCall)
|
IrDynamicOperatorExpressionImpl(
|
||||||
+irLValue.load()
|
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 {
|
fun generatePostfixIncrementDecrement(ktExpression: KtPostfixExpression, origin: IrStatementOrigin): IrExpression {
|
||||||
val opResolvedCall = getResolvedCall(ktExpression)!!
|
val opResolvedCall = getResolvedCall(ktExpression)!!
|
||||||
val ktBaseExpression = ktExpression.baseExpression!!
|
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 ->
|
return irAssignmentReceiver.assign { irLValue ->
|
||||||
irBlock(ktExpression.startOffsetSkippingComments, ktExpression.endOffset, origin, irLValue.type) {
|
if (isDynamicCall) {
|
||||||
val temporary = irTemporary(irLValue.load())
|
IrDynamicOperatorExpressionImpl(
|
||||||
val opCall = statementGenerator.pregenerateCall(opResolvedCall)
|
startOffset, endOffset,
|
||||||
opCall.setExplicitReceiverValue(
|
irLValue.type,
|
||||||
VariableLValue(context, startOffset, endOffset, temporary.symbol, temporary.type)
|
if (ktExpression.operationToken == KtTokens.PLUSPLUS)
|
||||||
)
|
IrDynamicOperator.POSTFIX_INCREMENT
|
||||||
val irOpCall = CallGenerator(statementGenerator).generateCall(ktExpression, opCall, origin)
|
else
|
||||||
+irLValue.store(irOpCall)
|
IrDynamicOperator.POSTFIX_DECREMENT
|
||||||
+irGet(temporary.type, temporary.symbol)
|
).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) {
|
if (ktLeft is KtArrayAccessExpression) {
|
||||||
return generateArrayAccessAssignmentReceiver(ktLeft, origin)
|
return generateArrayAccessAssignmentReceiver(ktLeft, origin)
|
||||||
}
|
}
|
||||||
@@ -116,7 +178,12 @@ class AssignmentGenerator(statementGenerator: StatementGenerator) : StatementGen
|
|||||||
return when (descriptor) {
|
return when (descriptor) {
|
||||||
is SyntheticFieldDescriptor -> {
|
is SyntheticFieldDescriptor -> {
|
||||||
val receiverValue =
|
val receiverValue =
|
||||||
statementGenerator.generateBackingFieldReceiver(ktLeft.startOffsetSkippingComments, ktLeft.endOffset, resolvedCall, descriptor)
|
statementGenerator.generateBackingFieldReceiver(
|
||||||
|
ktLeft.startOffsetSkippingComments,
|
||||||
|
ktLeft.endOffset,
|
||||||
|
resolvedCall,
|
||||||
|
descriptor
|
||||||
|
)
|
||||||
createBackingFieldLValue(ktLeft, descriptor.propertyDescriptor, receiverValue, origin)
|
createBackingFieldLValue(ktLeft, descriptor.propertyDescriptor, receiverValue, origin)
|
||||||
}
|
}
|
||||||
is LocalVariableDescriptor ->
|
is LocalVariableDescriptor ->
|
||||||
@@ -133,7 +200,7 @@ class AssignmentGenerator(statementGenerator: StatementGenerator) : StatementGen
|
|||||||
else
|
else
|
||||||
createVariableValue(ktLeft, descriptor, origin)
|
createVariableValue(ktLeft, descriptor, origin)
|
||||||
is PropertyDescriptor ->
|
is PropertyDescriptor ->
|
||||||
generateAssignmentReceiverForProperty(descriptor, origin, ktLeft, resolvedCall)
|
generateAssignmentReceiverForProperty(descriptor, origin, ktLeft, resolvedCall, isAssignmentStatement)
|
||||||
is ValueDescriptor ->
|
is ValueDescriptor ->
|
||||||
createVariableValue(ktLeft, descriptor, origin)
|
createVariableValue(ktLeft, descriptor, origin)
|
||||||
else ->
|
else ->
|
||||||
@@ -172,29 +239,45 @@ class AssignmentGenerator(statementGenerator: StatementGenerator) : StatementGen
|
|||||||
descriptor: PropertyDescriptor,
|
descriptor: PropertyDescriptor,
|
||||||
origin: IrStatementOrigin,
|
origin: IrStatementOrigin,
|
||||||
ktLeft: KtExpression,
|
ktLeft: KtExpression,
|
||||||
resolvedCall: ResolvedCall<*>
|
resolvedCall: ResolvedCall<*>,
|
||||||
|
isAssignmentStatement: Boolean
|
||||||
): AssignmentReceiver =
|
): AssignmentReceiver =
|
||||||
if (isValInitializationInConstructor(descriptor, resolvedCall)) {
|
when {
|
||||||
val thisClass = getThisClass()
|
descriptor.isDynamic() ->
|
||||||
val thisAsReceiverParameter = thisClass.thisAsReceiverParameter
|
DynamicMemberLValue(
|
||||||
val thisType = thisAsReceiverParameter.type.toIrType()
|
context,
|
||||||
val irThis = IrGetValueImpl(
|
ktLeft.startOffsetSkippingComments, ktLeft.endOffset,
|
||||||
ktLeft.startOffsetSkippingComments, ktLeft.endOffset,
|
descriptor.type.toIrType(),
|
||||||
thisType,
|
descriptor.name.asString(),
|
||||||
context.symbolTable.referenceValueParameter(thisAsReceiverParameter)
|
statementGenerator.generateCallReceiver(
|
||||||
)
|
ktLeft, descriptor, resolvedCall.dispatchReceiver, resolvedCall.extensionReceiver,
|
||||||
createBackingFieldLValue(ktLeft, descriptor, RematerializableValue(irThis), null)
|
isSafe = resolvedCall.call.isSafeCall(),
|
||||||
} else {
|
isAssignmentReceiver = isAssignmentStatement
|
||||||
val propertyReceiver = statementGenerator.generateCallReceiver(
|
)
|
||||||
ktLeft, descriptor, resolvedCall.dispatchReceiver, resolvedCall.extensionReceiver,
|
)
|
||||||
isSafe = resolvedCall.call.isSafeCall(),
|
isValInitializationInConstructor(descriptor, resolvedCall) -> {
|
||||||
isAssignmentReceiver = true
|
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
|
// TODO property imported from an object
|
||||||
createPropertyLValue(ktLeft, descriptor, propertyReceiver, getTypeArguments(resolvedCall), origin, superQualifier)
|
createPropertyLValue(ktLeft, descriptor, propertyReceiver, getTypeArguments(resolvedCall), origin, superQualifier)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createPropertyLValue(
|
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
|
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.IrExpression
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin
|
import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin
|
||||||
import org.jetbrains.kotlin.ir.expressions.impl.IrBlockImpl
|
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.ir.types.makeNullable
|
||||||
import org.jetbrains.kotlin.psi2ir.generators.GeneratorWithScope
|
import org.jetbrains.kotlin.psi2ir.generators.GeneratorWithScope
|
||||||
import org.jetbrains.kotlin.types.typeUtil.builtIns
|
|
||||||
import org.jetbrains.kotlin.types.typeUtil.makeNullable
|
|
||||||
|
|
||||||
|
|
||||||
class SafeCallReceiver(
|
class SafeCallReceiver(
|
||||||
@@ -33,7 +32,7 @@ class SafeCallReceiver(
|
|||||||
val endOffset: Int,
|
val endOffset: Int,
|
||||||
val extensionReceiver: IntermediateValue?,
|
val extensionReceiver: IntermediateValue?,
|
||||||
val dispatchReceiver: IntermediateValue?,
|
val dispatchReceiver: IntermediateValue?,
|
||||||
val isAssignmentReceiver: Boolean
|
val isStatement: Boolean
|
||||||
) : CallReceiver {
|
) : CallReceiver {
|
||||||
|
|
||||||
override fun call(withDispatchAndExtensionReceivers: (IntermediateValue?, IntermediateValue?) -> IrExpression): IrExpression {
|
override fun call(withDispatchAndExtensionReceivers: (IntermediateValue?, IntermediateValue?) -> IrExpression): IrExpression {
|
||||||
@@ -52,7 +51,7 @@ class SafeCallReceiver(
|
|||||||
|
|
||||||
val irResult = withDispatchAndExtensionReceivers(dispatchReceiverValue, extensionReceiverValue)
|
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)
|
val irBlock = IrBlockImpl(startOffset, endOffset, resultType, IrStatementOrigin.SAFE_CALL)
|
||||||
|
|
||||||
@@ -79,7 +78,7 @@ fun IrExpression.safeCallOnDispatchReceiver(
|
|||||||
generator, startOffset, endOffset,
|
generator, startOffset, endOffset,
|
||||||
extensionReceiver = null,
|
extensionReceiver = null,
|
||||||
dispatchReceiver = OnceExpressionValue(this),
|
dispatchReceiver = OnceExpressionValue(this),
|
||||||
isAssignmentReceiver = false
|
isStatement = false
|
||||||
).call { dispatchReceiverValue, _ ->
|
).call { dispatchReceiverValue, _ ->
|
||||||
ifNotNull(dispatchReceiverValue!!.load())
|
ifNotNull(dispatchReceiverValue!!.load())
|
||||||
}
|
}
|
||||||
@@ -24,7 +24,10 @@ var IrDynamicOperatorExpression.left: IrExpression
|
|||||||
var IrDynamicOperatorExpression.right: IrExpression
|
var IrDynamicOperatorExpression.right: IrExpression
|
||||||
get() = arguments[0]
|
get() = arguments[0]
|
||||||
set(value) {
|
set(value) {
|
||||||
arguments[0] = value
|
if (arguments.isEmpty())
|
||||||
|
arguments.add(value)
|
||||||
|
else
|
||||||
|
arguments[0] = value
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IrDynamicMemberExpression : IrDynamicExpression {
|
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.types.IrType
|
||||||
import org.jetbrains.kotlin.ir.visitors.IrElementTransformer
|
import org.jetbrains.kotlin.ir.visitors.IrElementTransformer
|
||||||
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
|
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
|
||||||
|
import org.jetbrains.kotlin.utils.SmartList
|
||||||
|
|
||||||
class IrDynamicOperatorExpressionImpl(
|
class IrDynamicOperatorExpressionImpl(
|
||||||
startOffset: Int,
|
startOffset: Int,
|
||||||
@@ -23,7 +24,7 @@ class IrDynamicOperatorExpressionImpl(
|
|||||||
|
|
||||||
override lateinit var receiver: IrExpression
|
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 =
|
override fun <R, D> accept(visitor: IrElementVisitor<R, D>, data: D): R =
|
||||||
visitor.visitDynamicOperatorExpression(this, data)
|
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:
|
FUN name:testProperty visibility:public modality:FINAL <> (nc:test.C?) returnType:kotlin.Unit flags:
|
||||||
VALUE_PARAMETER name:nc index:0 type:test.C? flags:
|
VALUE_PARAMETER name:nc index:0 type:test.C? flags:
|
||||||
BLOCK_BODY
|
BLOCK_BODY
|
||||||
BLOCK type=kotlin.Unit origin=SAFE_CALL
|
TYPE_OP type=kotlin.Unit origin=IMPLICIT_COERCION_TO_UNIT typeOperand=kotlin.Unit
|
||||||
VAR IR_TEMPORARY_VARIABLE name:tmp0_safe_receiver type:test.C? flags:val
|
typeOperand: CLASS IR_EXTERNAL_DECLARATION_STUB OBJECT name:Unit modality:FINAL visibility:public flags: superTypes:[kotlin.Any]
|
||||||
GET_VAR 'value-parameter nc: C?' type=test.C? origin=null
|
BLOCK type=kotlin.Int? origin=SAFE_CALL
|
||||||
WHEN type=kotlin.Unit origin=null
|
VAR IR_TEMPORARY_VARIABLE name:tmp0_safe_receiver type:test.C? flags:val
|
||||||
BRANCH
|
GET_VAR 'value-parameter nc: C?' type=test.C? origin=null
|
||||||
if: CALL 'EQEQ(Any?, Any?): Boolean' type=kotlin.Boolean origin=EQEQ
|
WHEN type=kotlin.Int? origin=null
|
||||||
arg0: GET_VAR 'tmp0_safe_receiver: C?' type=test.C? origin=null
|
BRANCH
|
||||||
arg1: CONST Null type=kotlin.Nothing? value=null
|
if: CALL 'EQEQ(Any?, Any?): Boolean' type=kotlin.Boolean origin=EQEQ
|
||||||
then: TYPE_OP type=kotlin.Unit origin=IMPLICIT_COERCION_TO_UNIT typeOperand=kotlin.Unit
|
arg0: GET_VAR 'tmp0_safe_receiver: C?' type=test.C? origin=null
|
||||||
typeOperand: CLASS IR_EXTERNAL_DECLARATION_STUB OBJECT name:Unit modality:FINAL visibility:public flags: superTypes:[kotlin.Any]
|
arg1: CONST Null type=kotlin.Nothing? value=null
|
||||||
CONST Null type=kotlin.Nothing? value=null
|
then: CONST Null type=kotlin.Nothing? value=null
|
||||||
BRANCH
|
BRANCH
|
||||||
if: CONST Boolean type=kotlin.Boolean value=true
|
if: CONST Boolean type=kotlin.Boolean value=true
|
||||||
then: TYPE_OP type=kotlin.Unit origin=IMPLICIT_COERCION_TO_UNIT typeOperand=kotlin.Unit
|
then: BLOCK type=kotlin.Int origin=POSTFIX_INCR
|
||||||
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:tmp1_receiver type:test.C? flags:val
|
VAR IR_TEMPORARY_VARIABLE name:tmp1_receiver type:test.C? flags:val
|
||||||
GET_VAR 'tmp0_safe_receiver: C?' type=test.C? origin=null
|
GET_VAR 'tmp0_safe_receiver: C?' type=test.C? origin=null
|
||||||
BLOCK type=kotlin.Int origin=POSTFIX_INCR
|
BLOCK type=kotlin.Int origin=POSTFIX_INCR
|
||||||
|
|||||||
@@ -56,6 +56,21 @@ public class IrJsTextTestCaseGenerated extends AbstractIrJsTextTestCase {
|
|||||||
runTest("compiler/testData/ir/irJsText/dynamic/dynamicMemberAccess.kt");
|
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")
|
@TestMetadata("dynamicWithImplicitCast.kt")
|
||||||
public void testDynamicWithImplicitCast() throws Exception {
|
public void testDynamicWithImplicitCast() throws Exception {
|
||||||
runTest("compiler/testData/ir/irJsText/dynamic/dynamicWithImplicitCast.kt");
|
runTest("compiler/testData/ir/irJsText/dynamic/dynamicWithImplicitCast.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user