IR: augmented assignment for array access expressions

This commit is contained in:
Dmitry Petrov
2016-08-16 18:18:26 +03:00
committed by Dmitry Petrov
parent 0a57eb8ea4
commit d7412c449e
19 changed files with 425 additions and 61 deletions
@@ -38,5 +38,5 @@ fun IrExpression.toExpectedType(expectedType: KotlinType?): IrExpression {
) )
} }
fun IrVariable.implicitGetExpression(): IrExpression = fun IrVariable.createDefaultGetExpression(): IrExpression =
IrGetVariableExpressionImpl(startOffset, endOffset, descriptor) IrGetVariableExpressionImpl(startOffset, endOffset, descriptor)
@@ -17,6 +17,7 @@
package org.jetbrains.kotlin.psi2ir.generators package org.jetbrains.kotlin.psi2ir.generators
import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.ir.declarations.IrVariable
import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.psi.KtExpression import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.psi.psiUtil.endOffset import org.jetbrains.kotlin.psi.psiUtil.endOffset
@@ -35,7 +36,13 @@ class IrCallGenerator(
private val expressionValues = HashMap<KtExpression, IrValue>() private val expressionValues = HashMap<KtExpression, IrValue>()
private val receiverValues = HashMap<ReceiverValue, IrValue>() private val receiverValues = HashMap<ReceiverValue, IrValue>()
private val valueArgumentValues = HashMap<ResolvedValueArgument, IrValue>() private val valueArgumentValues = HashMap<ValueParameterDescriptor, IrValue>()
fun createTemporary(ktExpression: KtExpression, irExpression: IrExpression): IrVariable {
val irTmpVar = irStatementGenerator.declarationFactory.createTemporaryVariable(irExpression)
putValue(ktExpression, IrTemporaryVariableValue(irTmpVar))
return irTmpVar
}
fun putValue(ktExpression: KtExpression, irValue: IrValue) { fun putValue(ktExpression: KtExpression, irValue: IrValue) {
expressionValues[ktExpression] = irValue expressionValues[ktExpression] = irValue
@@ -45,7 +52,7 @@ class IrCallGenerator(
receiverValues[receiver] = irValue receiverValues[receiver] = irValue
} }
fun putValue(valueArgument: ResolvedValueArgument, irValue: IrValue) { fun putValue(valueArgument: ValueParameterDescriptor, irValue: IrValue) {
valueArgumentValues[valueArgument] = irValue valueArgumentValues[valueArgument] = irValue
} }
@@ -134,9 +141,15 @@ class IrCallGenerator(
hasResult = isUsedAsExpression(ktExpression), hasResult = isUsedAsExpression(ktExpression),
isDesugared = true) isDesugared = true)
val valueArgumentsToValueParameters = HashMap<ResolvedValueArgument, ValueParameterDescriptor>()
for ((index, valueArgument) in resolvedCall.valueArgumentsByIndex!!.withIndex()) {
val valueParameter = resolvedCall.resultingDescriptor.valueParameters[index]
valueArgumentsToValueParameters[valueArgument] = valueParameter
}
val temporariesForValueArguments = HashMap<ResolvedValueArgument, Pair<VariableDescriptor, IrExpression>>() val temporariesForValueArguments = HashMap<ResolvedValueArgument, Pair<VariableDescriptor, IrExpression>>()
for (valueArgument in valueArgumentsInEvaluationOrder) { for (valueArgument in valueArgumentsInEvaluationOrder) {
val irArgument = generateValueArgument(valueArgument) ?: continue val irArgument = generateValueArgument(valueArgument, valueArgumentsToValueParameters[valueArgument]!!) ?: continue
val irTemporary = irStatementGenerator.declarationFactory.createTemporaryVariable(irArgument) val irTemporary = irStatementGenerator.declarationFactory.createTemporaryVariable(irArgument)
temporariesForValueArguments[valueArgument] = Pair(irTemporary.descriptor, irArgument) temporariesForValueArguments[valueArgument] = Pair(irTemporary.descriptor, irArgument)
irBlock.addStatement(irTemporary) irBlock.addStatement(irTemporary)
@@ -187,17 +200,17 @@ class IrCallGenerator(
fun generateValueArgument(valueArgument: ResolvedValueArgument, valueParameterDescriptor: ValueParameterDescriptor): IrExpression? = fun generateValueArgument(valueArgument: ResolvedValueArgument, valueParameterDescriptor: ValueParameterDescriptor): IrExpression? =
if (valueParameterDescriptor.varargElementType != null) { if (valueParameterDescriptor.varargElementType != null) {
generateValueArgument(valueArgument) doGenerateValueArgument(valueArgument, valueParameterDescriptor)
} }
else { else {
generateValueArgument(valueArgument)?.toExpectedType(valueParameterDescriptor.type) doGenerateValueArgument(valueArgument, valueParameterDescriptor)?.toExpectedType(valueParameterDescriptor.type)
} }
fun generateValueArgument(valueArgument: ResolvedValueArgument): IrExpression? = private fun doGenerateValueArgument(valueArgument: ResolvedValueArgument, valueParameterDescriptor: ValueParameterDescriptor): IrExpression? =
if (valueArgument is DefaultValueArgument) if (valueArgument is DefaultValueArgument)
null null
else else
valueArgumentValues[valueArgument]?.load() ?: doGenerateValueArgument(valueArgument) valueArgumentValues[valueParameterDescriptor]?.load() ?: doGenerateValueArgument(valueArgument)
private fun doGenerateValueArgument(valueArgument: ResolvedValueArgument): IrExpression? = private fun doGenerateValueArgument(valueArgument: ResolvedValueArgument): IrExpression? =
@@ -205,7 +218,7 @@ class IrCallGenerator(
is ExpressionValueArgument -> is ExpressionValueArgument ->
generateExpression(valueArgument.valueArgument!!.getArgumentExpression()!!) generateExpression(valueArgument.valueArgument!!.getArgumentExpression()!!)
is VarargValueArgument -> is VarargValueArgument ->
TODO("vararg") createDummyExpression(valueArgument.arguments[0].getArgumentExpression()!!, "vararg")
else -> else ->
TODO("Unexpected valueArgument: ${valueArgument.javaClass.simpleName}") TODO("Unexpected valueArgument: ${valueArgument.javaClass.simpleName}")
} }
@@ -22,12 +22,11 @@ import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor
import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrOperator import org.jetbrains.kotlin.ir.expressions.IrOperator
import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.KtArrayAccessExpression
import org.jetbrains.kotlin.psi.KtBinaryExpression import org.jetbrains.kotlin.psi.KtBinaryExpression
import org.jetbrains.kotlin.psi.KtExpression import org.jetbrains.kotlin.psi.KtExpression
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.types.KotlinType
val KT_OPERATOR_TO_IR_OPERATOR = hashMapOf( val KT_OPERATOR_TO_IR_OPERATOR = hashMapOf(
KtTokens.PLUSEQ to IrOperator.PLUSEQ, KtTokens.PLUSEQ to IrOperator.PLUSEQ,
@@ -54,18 +53,22 @@ class IrOperatorExpressionGenerator(val irStatementGenerator: IrStatementGenerat
val ktLeft = expression.left!! val ktLeft = expression.left!!
val irOperator = getIrOperator(ktOperator) val irOperator = getIrOperator(ktOperator)
val lhs = generateReferenceForAssignmentLhs(ktLeft, irOperator) val irLhs = generateLValue(ktLeft, irOperator)
val isSimpleAssignment = get(BindingContext.VARIABLE_REASSIGNMENT, expression) ?: false val isSimpleAssignment = get(BindingContext.VARIABLE_REASSIGNMENT, expression) ?: false
val operatorCall = getResolvedCall(expression)!! val operatorCall = getResolvedCall(expression)!!
val opCallGenerator = IrCallGenerator(irStatementGenerator).apply { putValue(ktLeft, lhs) } if (isSimpleAssignment && irLhs is IrLValueWithAugmentedStore) {
return irLhs.augmentedStore(operatorCall, irStatementGenerator.generateExpression(expression.right!!))
}
val opCallGenerator = IrCallGenerator(irStatementGenerator).apply { putValue(ktLeft, irLhs) }
val irOpCall = opCallGenerator.generateCall(expression, operatorCall, irOperator) val irOpCall = opCallGenerator.generateCall(expression, operatorCall, irOperator)
return if (isSimpleAssignment) { return if (isSimpleAssignment) {
// Set( Op( Get(), RHS ) ) // Set( Op( Get(), RHS ) )
lhs.store(irOpCall) irLhs.store(irOpCall)
} }
else { else {
// Op( Get(), RHS ) // Op( Get(), RHS )
@@ -80,11 +83,22 @@ class IrOperatorExpressionGenerator(val irStatementGenerator: IrStatementGenerat
private fun generateAssignment(expression: KtBinaryExpression): IrExpression { private fun generateAssignment(expression: KtBinaryExpression): IrExpression {
val ktLeft = expression.left!! val ktLeft = expression.left!!
val ktRight = expression.right!! val ktRight = expression.right!!
val lhsReference = generateReferenceForAssignmentLhs(ktLeft, IrOperator.EQ) val lhsReference = generateLValue(ktLeft, IrOperator.EQ)
return lhsReference.store(irStatementGenerator.generateExpression(ktRight)) return lhsReference.store(irStatementGenerator.generateExpression(ktRight))
} }
private fun generateReferenceForAssignmentLhs(ktLeft: KtExpression, irOperator: IrOperator?): IrReference { private fun generateLValue(ktLeft: KtExpression, irOperator: IrOperator?): IrLValue {
if (ktLeft is KtArrayAccessExpression) {
val irArrayValue = IrGenerateExpressionValue(irStatementGenerator, ktLeft.arrayExpression!!)
val indexExpressions = ktLeft.indexExpressions.map {
it to IrGenerateExpressionValue(irStatementGenerator, it)
}
val indexedGetCall = get(BindingContext.INDEXED_LVALUE_GET, ktLeft)
val indexedSetCall = get(BindingContext.INDEXED_LVALUE_SET, ktLeft)
return IrIndexedLValue(irStatementGenerator, ktLeft, irOperator,
irArrayValue, indexExpressions, indexedGetCall, indexedSetCall)
}
val resolvedCall = getResolvedCall(ktLeft) ?: TODO("no resolved call for LHS") val resolvedCall = getResolvedCall(ktLeft) ?: TODO("no resolved call for LHS")
val descriptor = resolvedCall.candidateDescriptor val descriptor = resolvedCall.candidateDescriptor
@@ -93,10 +107,10 @@ class IrOperatorExpressionGenerator(val irStatementGenerator: IrStatementGenerat
if (descriptor.isDelegated) if (descriptor.isDelegated)
TODO("Delegated local variable") TODO("Delegated local variable")
else else
IrVariableReferenceValue(ktLeft, irOperator, descriptor) IrVariableLValueValue(ktLeft, irOperator, descriptor)
is PropertyDescriptor -> is PropertyDescriptor ->
IrCallGenerator(irStatementGenerator).run { IrCallGenerator(irStatementGenerator).run {
IrPropertyReferenceValue( IrPropertyLValueValue(
ktLeft, irOperator, descriptor, ktLeft, irOperator, descriptor,
generateReceiver(ktLeft, resolvedCall.dispatchReceiver, descriptor.dispatchReceiverParameter), generateReceiver(ktLeft, resolvedCall.dispatchReceiver, descriptor.dispatchReceiverParameter),
generateReceiver(ktLeft, resolvedCall.extensionReceiver, descriptor.extensionReceiverParameter), generateReceiver(ktLeft, resolvedCall.extensionReceiver, descriptor.extensionReceiverParameter),
@@ -107,5 +121,4 @@ class IrOperatorExpressionGenerator(val irStatementGenerator: IrStatementGenerat
TODO("Other cases of LHS") TODO("Other cases of LHS")
} }
} }
} }
@@ -24,7 +24,6 @@ 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.startOffset import org.jetbrains.kotlin.psi.psiUtil.startOffset
import org.jetbrains.kotlin.psi2ir.deparenthesize import org.jetbrains.kotlin.psi2ir.deparenthesize
import org.jetbrains.kotlin.psi2ir.implicitGetExpression
import org.jetbrains.kotlin.psi2ir.toExpectedType import org.jetbrains.kotlin.psi2ir.toExpectedType
import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.BindingContextUtils import org.jetbrains.kotlin.resolve.BindingContextUtils
@@ -73,11 +72,11 @@ class IrStatementGenerator(
val irBlock = IrBlockExpressionImpl(multiDeclaration.startOffset, multiDeclaration.endOffset, null, val irBlock = IrBlockExpressionImpl(multiDeclaration.startOffset, multiDeclaration.endOffset, null,
hasResult = false, isDesugared = true) hasResult = false, isDesugared = true)
val ktInitializer = multiDeclaration.initializer!! val ktInitializer = multiDeclaration.initializer!!
val irInitializerVal = declarationFactory.createTemporaryVariable(ktInitializer.genExpr()) val irTmpInitializer = declarationFactory.createTemporaryVariable(ktInitializer.genExpr())
irBlock.addStatement(irInitializerVal) irBlock.addStatement(irTmpInitializer)
val irCallGenerator = IrCallGenerator(this) val irCallGenerator = IrCallGenerator(this)
irCallGenerator.putValue(ktInitializer, justExpressionValue { irInitializerVal.implicitGetExpression() }) irCallGenerator.putValue(ktInitializer, IrTemporaryVariableValue(irTmpInitializer))
for ((index, ktEntry) in multiDeclaration.entries.withIndex()) { for ((index, ktEntry) in multiDeclaration.entries.withIndex()) {
val componentResolvedCall = getOrFail(BindingContext.COMPONENT_RESOLVED_CALL, ktEntry) val componentResolvedCall = getOrFail(BindingContext.COMPONENT_RESOLVED_CALL, ktEntry)
@@ -18,55 +18,74 @@ package org.jetbrains.kotlin.psi2ir.generators
import org.jetbrains.kotlin.descriptors.PropertyDescriptor import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.descriptors.VariableDescriptor import org.jetbrains.kotlin.descriptors.VariableDescriptor
import org.jetbrains.kotlin.ir.declarations.IrVariable
import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.psi.KtArrayAccessExpression
import org.jetbrains.kotlin.psi.KtElement import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.psi.psiUtil.endOffset import org.jetbrains.kotlin.psi.psiUtil.endOffset
import org.jetbrains.kotlin.psi.psiUtil.startOffset import org.jetbrains.kotlin.psi.psiUtil.startOffset
import org.jetbrains.kotlin.psi2ir.createDefaultGetExpression
import org.jetbrains.kotlin.psi2ir.toExpectedType import org.jetbrains.kotlin.psi2ir.toExpectedType
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
interface IrValue { interface IrValue {
fun load(): IrExpression fun load(): IrExpression
} }
inline fun justExpressionValue(crossinline makeExpression: () -> IrExpression) = inline fun onDemandExpressionValue(crossinline makeExpression: () -> IrExpression) =
object : IrValue { object : IrValue {
override fun load() = makeExpression() override fun load() = makeExpression()
} }
interface IrReference : IrValue { class IrTemporaryVariableValue(val irVariable: IrVariable) : IrValue {
fun store(value: IrExpression): IrExpression override fun load(): IrExpression =
irVariable.createDefaultGetExpression()
} }
class IrVariableReferenceValue( class IrGenerateExpressionValue(val irStatementGenerator: IrStatementGenerator, val ktExpression: KtExpression) : IrValue {
override fun load(): IrExpression =
irStatementGenerator.generateExpression(ktExpression)
}
interface IrLValue : IrValue {
fun store(irExpression: IrExpression): IrExpression
}
interface IrLValueWithAugmentedStore : IrLValue {
fun augmentedStore(operatorCall: ResolvedCall<*>, irRhs: IrExpression): IrExpression
}
class IrVariableLValueValue(
val ktElement: KtElement, val ktElement: KtElement,
val irOperator: IrOperator?, val irOperator: IrOperator?,
val descriptor: VariableDescriptor val descriptor: VariableDescriptor
) : IrReference { ) : IrLValue {
override fun load(): IrExpression = override fun load(): IrExpression =
IrGetVariableExpressionImpl( IrGetVariableExpressionImpl(
ktElement.startOffset, ktElement.endOffset, ktElement.startOffset, ktElement.endOffset,
descriptor, irOperator descriptor, irOperator
) )
override fun store(value: IrExpression): IrExpression = override fun store(irExpression: IrExpression): IrExpression =
IrSetVariableExpressionImpl( IrSetVariableExpressionImpl(
ktElement.startOffset, ktElement.endOffset, ktElement.startOffset, ktElement.endOffset,
descriptor, value.toExpectedType(descriptor.type), irOperator descriptor, irExpression.toExpectedType(descriptor.type), irOperator
) )
} }
class IrPropertyReferenceValue( class IrPropertyLValueValue(
val ktElement: KtElement, val ktElement: KtElement,
val irOperator: IrOperator?, val irOperator: IrOperator?,
val descriptor: PropertyDescriptor, val descriptor: PropertyDescriptor,
val dispatchReceiver: IrExpression?, val dispatchReceiver: IrExpression?,
val extensionReceiver: IrExpression?, val extensionReceiver: IrExpression?,
val isSafe: Boolean val isSafe: Boolean
) : IrReference { ) : IrLValue {
private fun IrPropertyAccessExpression.setReceivers() = private fun IrPropertyAccessExpression.setReceivers() =
apply { apply {
dispatchReceiver = this@IrPropertyReferenceValue.dispatchReceiver dispatchReceiver = this@IrPropertyLValueValue.dispatchReceiver
extensionReceiver = this@IrPropertyReferenceValue.extensionReceiver extensionReceiver = this@IrPropertyLValueValue.extensionReceiver
} }
override fun load(): IrExpression = override fun load(): IrExpression =
@@ -75,9 +94,88 @@ class IrPropertyReferenceValue(
descriptor.type, isSafe, descriptor, irOperator descriptor.type, isSafe, descriptor, irOperator
).setReceivers() ).setReceivers()
override fun store(value: IrExpression): IrExpression = override fun store(irExpression: IrExpression): IrExpression =
IrSetPropertyExpressionImpl( IrSetPropertyExpressionImpl(
ktElement.startOffset, ktElement.endOffset, ktElement.startOffset, ktElement.endOffset,
isSafe, descriptor, value.toExpectedType(descriptor.type), irOperator isSafe, descriptor, irExpression.toExpectedType(descriptor.type), irOperator
).setReceivers() ).setReceivers()
}
class IrIndexedLValue(
var irStatementGenerator: IrStatementGenerator,
val ktArrayAccessExpression: KtArrayAccessExpression,
val irOperator: IrOperator?,
val arrayValue: IrValue,
val indexValues: List<Pair<KtExpression, IrValue>>,
val indexedGetCall: ResolvedCall<*>?,
val indexedSetCall: ResolvedCall<*>?
) : IrLValueWithAugmentedStore {
override fun load(): IrExpression {
if (indexedGetCall == null) throw AssertionError("Indexed LValue has no 'get' call: ${ktArrayAccessExpression.text}")
val irBlock = IrBlockExpressionImpl(ktArrayAccessExpression.startOffset, ktArrayAccessExpression.endOffset,
indexedGetCall.resultingDescriptor.returnType,
hasResult = true, isDesugared = true)
val callGenerator = IrCallGenerator(irStatementGenerator)
defineContextVariables(irBlock, callGenerator)
irBlock.addStatement(callGenerator.generateCall(ktArrayAccessExpression, indexedGetCall, irOperator))
return irBlock
}
override fun store(irExpression: IrExpression): IrExpression {
if (indexedSetCall == null) throw AssertionError("Indexed LValue has no 'set' call: ${ktArrayAccessExpression.text}")
val irBlock = IrBlockExpressionImpl(ktArrayAccessExpression.startOffset, ktArrayAccessExpression.endOffset,
indexedSetCall.resultingDescriptor.returnType,
hasResult = true, isDesugared = true)
val callGenerator = IrCallGenerator(irStatementGenerator)
defineContextVariables(irBlock, callGenerator)
callGenerator.putValue(indexedSetCall.resultingDescriptor.valueParameters.last(),
onDemandExpressionValue { irExpression })
irBlock.addStatement(callGenerator.generateCall(ktArrayAccessExpression, indexedSetCall, irOperator))
return irBlock
}
override fun augmentedStore(operatorCall: ResolvedCall<*>, irRhs: IrExpression): IrExpression {
if (indexedGetCall == null) throw AssertionError("Indexed LValue has no 'get' call: ${ktArrayAccessExpression.text}")
if (indexedSetCall == null) throw AssertionError("Indexed LValue has no 'set' call: ${ktArrayAccessExpression.text}")
val irBlock = IrBlockExpressionImpl(ktArrayAccessExpression.startOffset, ktArrayAccessExpression.endOffset,
indexedSetCall.resultingDescriptor.returnType,
hasResult = true, isDesugared = true)
val callGenerator = IrCallGenerator(irStatementGenerator)
defineContextVariables(irBlock, callGenerator)
callGenerator.putValue(operatorCall.resultingDescriptor.valueParameters[0], onDemandExpressionValue { irRhs })
val operatorCallReceiver = operatorCall.extensionReceiver ?: operatorCall.dispatchReceiver
callGenerator.putValue(operatorCallReceiver!!,
onDemandExpressionValue { callGenerator.generateCall(ktArrayAccessExpression, indexedGetCall, irOperator) })
callGenerator.putValue(indexedSetCall.resultingDescriptor.valueParameters.last(),
onDemandExpressionValue { callGenerator.generateCall(ktArrayAccessExpression, operatorCall, irOperator) })
irBlock.addStatement(callGenerator.generateCall(ktArrayAccessExpression, indexedSetCall, irOperator))
return irBlock
}
private fun defineContextVariables(irBlock: IrBlockExpression, callGenerator: IrCallGenerator) {
irBlock.addStatement(callGenerator.createTemporary(ktArrayAccessExpression.arrayExpression!!, arrayValue.load()))
for ((ktIndexExpression, irIndexValue) in indexValues) {
irBlock.addStatement(callGenerator.createTemporary(ktIndexExpression, irIndexValue.load()))
}
}
} }
@@ -18,11 +18,7 @@ package org.jetbrains.kotlin.ir
import org.jetbrains.kotlin.codegen.CodegenTestCase import org.jetbrains.kotlin.codegen.CodegenTestCase
import org.jetbrains.kotlin.ir.declarations.IrFile import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.declarations.IrModuleImpl
import org.jetbrains.kotlin.psi2ir.Psi2IrTranslator import org.jetbrains.kotlin.psi2ir.Psi2IrTranslator
import org.jetbrains.kotlin.psi2ir.generators.IrGeneratorContext
import org.jetbrains.kotlin.psi2ir.generators.IrModuleGenerator
import org.jetbrains.kotlin.psi2ir.transformations.collapseDesugaredBlocks
import org.jetbrains.kotlin.resolve.AnalyzingUtils import org.jetbrains.kotlin.resolve.AnalyzingUtils
import org.jetbrains.kotlin.resolve.lazy.JvmResolveUtil import org.jetbrains.kotlin.resolve.lazy.JvmResolveUtil
import org.jetbrains.kotlin.test.ConfigurationKind import org.jetbrains.kotlin.test.ConfigurationKind
@@ -50,7 +50,8 @@ fun <T : IrElement?> T.detach(): T {
} }
fun IrElement.replaceWith(otherElement: IrElement) { fun IrElement.replaceWith(otherElement: IrElement) {
parent?.run { replaceChild(slot, otherElement.detach()) } ?: throw AssertionError("Can't replace a non-root element $this") val parent = this.parent ?: throw AssertionError("Can't replace a non-root element $this")
parent.replaceChild(slot, otherElement.detach())
} }
fun IrElement.assertChild(child: IrElement) { fun IrElement.assertChild(child: IrElement) {
@@ -48,7 +48,9 @@ class IrCallExpressionImpl(
argumentsByParameterIndex[index] argumentsByParameterIndex[index]
override fun putArgument(index: Int, valueArgument: IrExpression?) { override fun putArgument(index: Int, valueArgument: IrExpression?) {
if (index >= argumentsByParameterIndex.size) throw AssertionError("$this: No such argument slot: $index") if (index >= argumentsByParameterIndex.size) {
throw AssertionError("$this: No such argument slot: $index")
}
valueArgument?.assertDetached() valueArgument?.assertDetached()
argumentsByParameterIndex[index]?.detach() argumentsByParameterIndex[index]?.detach()
argumentsByParameterIndex[index] = valueArgument argumentsByParameterIndex[index] = valueArgument
@@ -27,6 +27,8 @@ import org.jetbrains.kotlin.renderer.DescriptorRendererModifier
import org.jetbrains.kotlin.renderer.OverrideRenderingPolicy import org.jetbrains.kotlin.renderer.OverrideRenderingPolicy
import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.KotlinType
fun IrElement.render() = accept(RenderIrElementVisitor(), null)
class RenderIrElementVisitor : IrElementVisitor<String, Nothing?> { class RenderIrElementVisitor : IrElementVisitor<String, Nothing?> {
override fun visitElement(element: IrElement, data: Nothing?): String = override fun visitElement(element: IrElement, data: Nothing?): String =
"? ${element.javaClass.simpleName}" "? ${element.javaClass.simpleName}"
@@ -0,0 +1,40 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.ir.util
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
inline fun IrElement.validateTree(crossinline onFailBody: (IrElement, IrElement) -> Unit) {
accept(object : IrTreeValidationVisitor() {
override fun onFail(irElement: IrElement, expectedParent: IrElement) {
onFailBody(irElement, expectedParent)
}
}, null)
}
abstract class IrTreeValidationVisitor : IrElementVisitor<Unit, IrElement?> {
override fun visitElement(element: IrElement, data: IrElement?) {
if (data != null && element.parent != data) {
onFail(element, data)
}
element.acceptChildren(this, element)
}
protected abstract fun onFail(irElement: IrElement, expectedParent: IrElement)
}
+5
View File
@@ -0,0 +1,5 @@
// <<< arrayAssignment.txt
fun test() {
val x = intArrayOf(1, 2, 3)
x[1] = 0
}
+15
View File
@@ -0,0 +1,15 @@
IrFile /arrayAssignment.kt
IrFunction public fun test(): kotlin.Unit
IrExpressionBody
BLOCK type=<no-type> hasResult=false isDesugared=false
VAR val x: kotlin.IntArray
CALL .intArrayOf type=kotlin.IntArray operator=
elements: DUMMY vararg type=kotlin.Int
VAR val tmp0: kotlin.IntArray
GET_VAR x type=kotlin.IntArray
VAR val tmp1: kotlin.Int
LITERAL Int type=kotlin.Int value='1'
CALL .set type=kotlin.Unit operator=EQ
$this: GET_VAR tmp0 type=kotlin.IntArray
index: GET_VAR tmp1 type=kotlin.Int
value: LITERAL Int type=kotlin.Int value='0'
+17 -4
View File
@@ -1,6 +1,19 @@
// <<< augmentedAssignment1.txt // <<< augmentedAssignment1.txt
fun test(): Int { var p = 0
fun testVariable() {
var x = 0 var x = 0
x += 10 x += 1
return x x -= 2
} x *= 3
x /= 4
x %= 5
}
fun testProperty() {
p += 1
p -= 2
p *= 3
p /= 4
p %= 5
}
+45 -5
View File
@@ -1,12 +1,52 @@
IrFile /augmentedAssignment1.kt IrFile /augmentedAssignment1.kt
IrFunction public fun test(): kotlin.Int IrProperty public var p: kotlin.Int getter=null setter=null
IrExpressionBody IrExpressionBody
BLOCK type=<no-type> hasResult=false isDesugared=false LITERAL Int type=kotlin.Int value='0'
IrFunction public fun testVariable(): kotlin.Unit
IrExpressionBody
BLOCK type=kotlin.Int hasResult=true isDesugared=false
VAR var x: kotlin.Int VAR var x: kotlin.Int
LITERAL Int type=kotlin.Int value='0' LITERAL Int type=kotlin.Int value='0'
SET_VAR x type=<no-type> SET_VAR x type=<no-type>
CALL .plus type=kotlin.Int operator=PLUSEQ CALL .plus type=kotlin.Int operator=PLUSEQ
$this: GET_VAR x type=kotlin.Int $this: GET_VAR x type=kotlin.Int
other: LITERAL Int type=kotlin.Int value='10' other: LITERAL Int type=kotlin.Int value='1'
RETURN type=<no-type> SET_VAR x type=<no-type>
GET_VAR x type=kotlin.Int CALL .minus type=kotlin.Int operator=MINUSEQ
$this: GET_VAR x type=kotlin.Int
other: LITERAL Int type=kotlin.Int value='2'
SET_VAR x type=<no-type>
CALL .times type=kotlin.Int operator=MULTEQ
$this: GET_VAR x type=kotlin.Int
other: LITERAL Int type=kotlin.Int value='3'
SET_VAR x type=<no-type>
CALL .div type=kotlin.Int operator=DIVEQ
$this: GET_VAR x type=kotlin.Int
other: LITERAL Int type=kotlin.Int value='4'
SET_VAR x type=<no-type>
CALL .mod type=kotlin.Int operator=PERCEQ
$this: GET_VAR x type=kotlin.Int
other: LITERAL Int type=kotlin.Int value='5'
IrFunction public fun testProperty(): kotlin.Unit
IrExpressionBody
BLOCK type=kotlin.Int hasResult=true isDesugared=false
SET_PROPERTY .ptype=<no-type>
$value: CALL .plus type=kotlin.Int operator=PLUSEQ
$this: GET_PROPERTY .p type=kotlin.Int
other: LITERAL Int type=kotlin.Int value='1'
SET_PROPERTY .ptype=<no-type>
$value: CALL .minus type=kotlin.Int operator=MINUSEQ
$this: GET_PROPERTY .p type=kotlin.Int
other: LITERAL Int type=kotlin.Int value='2'
SET_PROPERTY .ptype=<no-type>
$value: CALL .times type=kotlin.Int operator=MULTEQ
$this: GET_PROPERTY .p type=kotlin.Int
other: LITERAL Int type=kotlin.Int value='3'
SET_PROPERTY .ptype=<no-type>
$value: CALL .div type=kotlin.Int operator=DIVEQ
$this: GET_PROPERTY .p type=kotlin.Int
other: LITERAL Int type=kotlin.Int value='4'
SET_PROPERTY .ptype=<no-type>
$value: CALL .mod type=kotlin.Int operator=PERCEQ
$this: GET_PROPERTY .p type=kotlin.Int
other: LITERAL Int type=kotlin.Int value='5'
+22 -2
View File
@@ -1,8 +1,28 @@
// <<< augmentedAssignment2.txt
class A class A
operator fun A.plusAssign(s: String) {} operator fun A.plusAssign(s: String) {}
operator fun A.minusAssign(s: String) {}
operator fun A.timesAssign(s: String) {}
operator fun A.divAssign(s: String) {}
operator fun A.modAssign(s: String) {}
fun test() { // <<< augmentedAssignment2.txt val p = A()
fun testVariable() {
val a = A() val a = A()
a += "" a += "+="
a -= "-="
a *= "*="
a /= "/="
a %= "*="
}
fun testProperty() {
p += "+="
p -= "-="
p *= "*="
p /= "/="
p %= "*="
} }
+58 -8
View File
@@ -1,8 +1,58 @@
IrFunction public fun test(): kotlin.Unit IrFile /augmentedAssignment2.kt
IrExpressionBody DUMMY A
BLOCK type=<no-type> hasResult=false isDesugared=false IrFunction public operator fun A.plusAssign(/*0*/ s: kotlin.String): kotlin.Unit
VAR val a: A IrExpressionBody
CALL .<init> type=A operator= BLOCK type=<no-type> hasResult=false isDesugared=false
CALL .plusAssign type=kotlin.Unit operator=PLUSEQ IrFunction public operator fun A.minusAssign(/*0*/ s: kotlin.String): kotlin.Unit
$receiver: GET_VAR a type=A IrExpressionBody
s: ? IrStringConcatenationExpressionImpl type=kotlin.String BLOCK type=<no-type> hasResult=false isDesugared=false
IrFunction public operator fun A.timesAssign(/*0*/ s: kotlin.String): kotlin.Unit
IrExpressionBody
BLOCK type=<no-type> hasResult=false isDesugared=false
IrFunction public operator fun A.divAssign(/*0*/ s: kotlin.String): kotlin.Unit
IrExpressionBody
BLOCK type=<no-type> hasResult=false isDesugared=false
IrFunction public operator fun A.modAssign(/*0*/ s: kotlin.String): kotlin.Unit
IrExpressionBody
BLOCK type=<no-type> hasResult=false isDesugared=false
IrProperty public val p: A getter=null setter=null
IrExpressionBody
CALL .<init> type=A operator=
IrFunction public fun testVariable(): kotlin.Unit
IrExpressionBody
BLOCK type=<no-type> hasResult=false isDesugared=false
VAR val a: A
CALL .<init> type=A operator=
CALL .plusAssign type=kotlin.Unit operator=PLUSEQ
$receiver: GET_VAR a type=A
s: LITERAL String type=kotlin.String value='+='
CALL .minusAssign type=kotlin.Unit operator=MINUSEQ
$receiver: GET_VAR a type=A
s: LITERAL String type=kotlin.String value='-='
CALL .timesAssign type=kotlin.Unit operator=MULTEQ
$receiver: GET_VAR a type=A
s: LITERAL String type=kotlin.String value='*='
CALL .divAssign type=kotlin.Unit operator=DIVEQ
$receiver: GET_VAR a type=A
s: LITERAL String type=kotlin.String value='/='
CALL .modAssign type=kotlin.Unit operator=PERCEQ
$receiver: GET_VAR a type=A
s: LITERAL String type=kotlin.String value='*='
IrFunction public fun testProperty(): kotlin.Unit
IrExpressionBody
BLOCK type=<no-type> hasResult=false isDesugared=false
CALL .plusAssign type=kotlin.Unit operator=PLUSEQ
$receiver: GET_PROPERTY .p type=A
s: LITERAL String type=kotlin.String value='+='
CALL .minusAssign type=kotlin.Unit operator=MINUSEQ
$receiver: GET_PROPERTY .p type=A
s: LITERAL String type=kotlin.String value='-='
CALL .timesAssign type=kotlin.Unit operator=MULTEQ
$receiver: GET_PROPERTY .p type=A
s: LITERAL String type=kotlin.String value='*='
CALL .divAssign type=kotlin.Unit operator=DIVEQ
$receiver: GET_PROPERTY .p type=A
s: LITERAL String type=kotlin.String value='/='
CALL .modAssign type=kotlin.Unit operator=PERCEQ
$receiver: GET_PROPERTY .p type=A
s: LITERAL String type=kotlin.String value='*='
@@ -0,0 +1,9 @@
// <<< augmentedAssignmentArray.txt
fun foo(): IntArray = intArrayOf(1, 2, 3)
fun testVariable() {
var x = foo()
x[0] += 1
foo()[0] *= 2
}
@@ -0,0 +1,36 @@
IrFile /augmentedAssignmentArray.kt
IrFunction public fun foo(): kotlin.IntArray
IrExpressionBody
BLOCK type=<no-type> hasResult=false isDesugared=true
RETURN type=<no-type>
CALL .intArrayOf type=kotlin.IntArray operator=
elements: DUMMY vararg type=kotlin.Int
IrFunction public fun testVariable(): kotlin.Unit
IrExpressionBody
BLOCK type=<no-type> hasResult=false isDesugared=false
VAR var x: kotlin.IntArray
CALL .foo type=kotlin.IntArray operator=
VAR val tmp0: kotlin.IntArray
GET_VAR x type=kotlin.IntArray
VAR val tmp1: kotlin.Int
LITERAL Int type=kotlin.Int value='0'
CALL .set type=kotlin.Unit operator=PLUSEQ
$this: GET_VAR tmp0 type=kotlin.IntArray
index: GET_VAR tmp1 type=kotlin.Int
value: CALL .plus type=kotlin.Int operator=PLUSEQ
$this: CALL .get type=kotlin.Int operator=PLUSEQ
$this: GET_VAR tmp0 type=kotlin.IntArray
index: GET_VAR tmp1 type=kotlin.Int
other: LITERAL Int type=kotlin.Int value='1'
VAR val tmp2: kotlin.IntArray
CALL .foo type=kotlin.IntArray operator=
VAR val tmp3: kotlin.Int
LITERAL Int type=kotlin.Int value='0'
CALL .set type=kotlin.Unit operator=MULTEQ
$this: GET_VAR tmp2 type=kotlin.IntArray
index: GET_VAR tmp3 type=kotlin.Int
value: CALL .times type=kotlin.Int operator=MULTEQ
$this: CALL .get type=kotlin.Int operator=MULTEQ
$this: GET_VAR tmp2 type=kotlin.IntArray
index: GET_VAR tmp3 type=kotlin.Int
other: LITERAL Int type=kotlin.Int value='2'
@@ -35,6 +35,12 @@ public class IrTextTestCaseGenerated extends AbstractIrTextTestCase {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/ir/irText"), Pattern.compile("^(.+)\\.kt$"), true); KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/ir/irText"), Pattern.compile("^(.+)\\.kt$"), true);
} }
@TestMetadata("arrayAssignment.kt")
public void testArrayAssignment() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/arrayAssignment.kt");
doTest(fileName);
}
@TestMetadata("assignments.kt") @TestMetadata("assignments.kt")
public void testAssignments() throws Exception { public void testAssignments() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/assignments.kt"); String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/assignments.kt");
@@ -53,6 +59,12 @@ public class IrTextTestCaseGenerated extends AbstractIrTextTestCase {
doTest(fileName); doTest(fileName);
} }
@TestMetadata("augmentedAssignmentArray.kt")
public void testAugmentedAssignmentArray() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/augmentedAssignmentArray.kt");
doTest(fileName);
}
@TestMetadata("boxOk.kt") @TestMetadata("boxOk.kt")
public void testBoxOk() throws Exception { public void testBoxOk() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/boxOk.kt"); String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/boxOk.kt");