Minimal porting of ForLoopsLowering from kotlin-native.
Diffs resulting from the port: https://github.com/punzki/kotlin-native/compare/master...punzki:for-loop-diff When ForLoopsLowering is added to the JVM lowering phases, this causes some of the forLoop bytecode tests to pass, and many still fail due to differences in behavior compared to non-IR backend: - Generated conditions for lowered loop - Generated temporary variables - Supported iterables (e.g., withIndex(), CharSequences) - Means of incrementing induction variable The phase will be added once more TODOs are resolved and it is more functionally complete, to prevent breaking for loops.
This commit is contained in:
committed by
max-kammerer
parent
f861b10798
commit
656f6855bb
+257
@@ -0,0 +1,257 @@
|
||||
/*
|
||||
* 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.backend.common.lower.loops
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.CommonBackendContext
|
||||
import org.jetbrains.kotlin.backend.common.FileLoweringPass
|
||||
import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext
|
||||
import org.jetbrains.kotlin.backend.common.lower.DeclarationIrBuilder
|
||||
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
|
||||
import org.jetbrains.kotlin.backend.common.lower.irIfThen
|
||||
import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
|
||||
import org.jetbrains.kotlin.ir.IrStatement
|
||||
import org.jetbrains.kotlin.ir.builders.*
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFile
|
||||
import org.jetbrains.kotlin.ir.declarations.IrVariable
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrCompositeImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
|
||||
import org.jetbrains.kotlin.ir.symbols.IrVariableSymbol
|
||||
import org.jetbrains.kotlin.ir.types.toKotlinType
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
|
||||
val forLoopsPhase = makeIrFilePhase(
|
||||
::ForLoopsLowering,
|
||||
name = "ForLoopsLowering",
|
||||
description = "For loops lowering"
|
||||
)
|
||||
|
||||
/**
|
||||
* This lowering pass optimizes for-loops.
|
||||
*
|
||||
* Replace iteration over ranges (X.indices, a..b, etc.) and arrays with
|
||||
* simple while loop over primitive induction variable.
|
||||
*/
|
||||
internal class ForLoopsLowering(val context: CommonBackendContext) : FileLoweringPass {
|
||||
|
||||
private val headerInfoBuilder = HeaderInfoBuilder(context)
|
||||
|
||||
override fun lower(irFile: IrFile) {
|
||||
val oldLoopToNewLoop = mutableMapOf<IrLoop, IrLoop>()
|
||||
val transformer = RangeLoopTransformer(context, oldLoopToNewLoop, headerInfoBuilder)
|
||||
irFile.transformChildrenVoid(transformer)
|
||||
// Update references in break/continue.
|
||||
irFile.transformChildrenVoid(object : IrElementTransformerVoid() {
|
||||
override fun visitBreakContinue(jump: IrBreakContinue): IrExpression {
|
||||
oldLoopToNewLoop[jump.loop]?.let { jump.loop = it }
|
||||
return jump
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private class RangeLoopTransformer(
|
||||
val context: CommonBackendContext,
|
||||
val oldLoopToNewLoop: MutableMap<IrLoop, IrLoop>,
|
||||
headerInfoBuilder: HeaderInfoBuilder
|
||||
) : IrElementTransformerVoidWithContext() {
|
||||
|
||||
private val symbols = context.ir.symbols
|
||||
private val iteratorToLoopHeader = mutableMapOf<IrVariableSymbol, ForLoopHeader>()
|
||||
private val headerProcessor = HeaderProcessor(context, headerInfoBuilder, this::getScopeOwnerSymbol)
|
||||
|
||||
fun getScopeOwnerSymbol() = currentScope!!.scope.scopeOwnerSymbol
|
||||
|
||||
override fun visitVariable(declaration: IrVariable): IrStatement {
|
||||
val initializer = declaration.initializer
|
||||
if (initializer == null || initializer !is IrCall) {
|
||||
return super.visitVariable(declaration)
|
||||
}
|
||||
return when (initializer.origin) {
|
||||
IrStatementOrigin.FOR_LOOP_ITERATOR ->
|
||||
processHeader(declaration)
|
||||
IrStatementOrigin.FOR_LOOP_NEXT ->
|
||||
processNext(declaration)
|
||||
else -> null
|
||||
} ?: super.visitVariable(declaration)
|
||||
}
|
||||
|
||||
private fun processHeader(variable: IrVariable): IrStatement? {
|
||||
assert(variable.symbol !in iteratorToLoopHeader)
|
||||
val forLoopInfo = headerProcessor.processHeader(variable)
|
||||
?: return null
|
||||
iteratorToLoopHeader[variable.symbol] = forLoopInfo
|
||||
return IrCompositeImpl(
|
||||
variable.startOffset,
|
||||
variable.endOffset,
|
||||
context.irBuiltIns.unitType,
|
||||
null,
|
||||
forLoopInfo.declarations
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* This loop
|
||||
*
|
||||
* for (i in first..last step foo) { ... }
|
||||
*
|
||||
* is represented in IR in such a manner:
|
||||
*
|
||||
* val it = (first..last step foo).iterator()
|
||||
* while (it.hasNext()) {
|
||||
* val i = it.next()
|
||||
* ...
|
||||
* }
|
||||
*
|
||||
* We transform it into the following loop:
|
||||
*
|
||||
* var it = first
|
||||
* if (it <= last) { // (it >= last if the progression is decreasing)
|
||||
* do {
|
||||
* val i = it++
|
||||
* ...
|
||||
* } while (i != last)
|
||||
* }
|
||||
*
|
||||
* In case of iteration over array we transform it into following:
|
||||
* while (i <= array.size - 1) {
|
||||
* val element = array[i]
|
||||
* i++
|
||||
* ...
|
||||
* }
|
||||
*/
|
||||
// TODO: Lower `for (i in a until b)` to loop with precondition: for (i = a; i < b; a++);
|
||||
override fun visitWhileLoop(loop: IrWhileLoop): IrExpression {
|
||||
if (loop.origin != IrStatementOrigin.FOR_LOOP_INNER_WHILE) {
|
||||
return super.visitWhileLoop(loop)
|
||||
}
|
||||
return with(context.createIrBuilder(getScopeOwnerSymbol(), loop.startOffset, loop.endOffset)) {
|
||||
val newBody = loop.body?.transform(this@RangeLoopTransformer, null)?.let {
|
||||
if (it is IrContainerExpression && !it.isTransparentScope) {
|
||||
IrCompositeImpl(startOffset, endOffset, it.type, it.origin, it.statements)
|
||||
} else {
|
||||
it
|
||||
}
|
||||
}
|
||||
val loopHeader = getLoopHeader(loop.condition)
|
||||
?: return super.visitWhileLoop(loop)
|
||||
val newLoop = loopHeader.buildBody(this, loop, newBody)
|
||||
oldLoopToNewLoop[loop] = newLoop
|
||||
// Build a check for an empty progression before the loop.
|
||||
if (loopHeader is ProgressionLoopHeader) {
|
||||
buildEmptinessCheck(newLoop, loopHeader)
|
||||
} else {
|
||||
newLoop
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun DeclarationIrBuilder.buildMinValueCondition(forLoopHeader: ForLoopHeader): IrExpression {
|
||||
// Condition for a corner case: for (i in a until Int.MIN_VALUE) {}.
|
||||
// Check if forLoopHeader.bound > MIN_VALUE.
|
||||
val progressionType = forLoopHeader.progressionType
|
||||
val irBuiltIns = context.irBuiltIns
|
||||
val minConst = when (progressionType) {
|
||||
ProgressionType.INT_PROGRESSION -> IrConstImpl
|
||||
.int(startOffset, endOffset, irBuiltIns.intType, Int.MIN_VALUE)
|
||||
ProgressionType.CHAR_PROGRESSION -> IrConstImpl
|
||||
.char(startOffset, endOffset, irBuiltIns.charType, 0.toChar())
|
||||
ProgressionType.LONG_PROGRESSION -> IrConstImpl
|
||||
.long(startOffset, endOffset, irBuiltIns.longType, Long.MIN_VALUE)
|
||||
}
|
||||
val compareTo = symbols.getBinaryOperator(
|
||||
OperatorNameConventions.COMPARE_TO,
|
||||
forLoopHeader.bound.type.toKotlinType(),
|
||||
minConst.type.toKotlinType()
|
||||
)
|
||||
return irCall(irBuiltIns.greaterFunByOperandType[irBuiltIns.int]?.symbol!!).apply {
|
||||
val compareToCall = irCall(compareTo).apply {
|
||||
dispatchReceiver = irGet(forLoopHeader.bound)
|
||||
putValueArgument(0, minConst)
|
||||
}
|
||||
putValueArgument(0, compareToCall)
|
||||
putValueArgument(1, irInt(0))
|
||||
}
|
||||
}
|
||||
|
||||
private fun DeclarationIrBuilder.buildEmptinessCheck(loop: IrLoop, loopHeader: ProgressionLoopHeader): IrExpression {
|
||||
val builtIns = context.irBuiltIns
|
||||
val comparingBuiltIn = loopHeader.comparingFunction(builtIns)
|
||||
|
||||
// Check if inductionVariable <= last (or >= in case of downTo).
|
||||
// TODO: Use comparingBuiltIn directly
|
||||
val compareTo = symbols.getBinaryOperator(
|
||||
OperatorNameConventions.COMPARE_TO,
|
||||
loopHeader.inductionVariable.type.toKotlinType(),
|
||||
loopHeader.last.type.toKotlinType()
|
||||
)
|
||||
|
||||
val check = irCall(comparingBuiltIn).apply {
|
||||
putValueArgument(
|
||||
0,
|
||||
irCallOp(compareTo, compareTo.owner.returnType, irGet(loopHeader.inductionVariable), irGet(loopHeader.last))
|
||||
)
|
||||
putValueArgument(1, irInt(0))
|
||||
}
|
||||
|
||||
// Process closed and open ranges in different manners.
|
||||
return if (loopHeader.closed) {
|
||||
irIfThen(check, loop) // if (inductionVariable <= last) { loop }
|
||||
} else {
|
||||
// Take into account a corner case: for (i in a until Int.MIN_VALUE) {}.
|
||||
// if (inductionVariable <= last && bound > MIN_VALUE) { loop }
|
||||
irIfThen(check, irIfThen(buildMinValueCondition(loopHeader), loop))
|
||||
}
|
||||
}
|
||||
|
||||
private fun getLoopHeader(oldCondition: IrExpression): ForLoopHeader? {
|
||||
if (oldCondition !is IrCall || oldCondition.origin != IrStatementOrigin.FOR_LOOP_HAS_NEXT) {
|
||||
return null
|
||||
}
|
||||
val irIteratorAccess = oldCondition.dispatchReceiver as? IrGetValue
|
||||
?: throw AssertionError()
|
||||
// Return null if we didn't lower the corresponding header.
|
||||
return iteratorToLoopHeader[irIteratorAccess.symbol]
|
||||
}
|
||||
|
||||
// Lower getting a next induction variable value.
|
||||
fun processNext(variable: IrVariable): IrExpression? {
|
||||
val initializer = variable.initializer as IrCall
|
||||
|
||||
val iterator = initializer.dispatchReceiver as? IrGetValue
|
||||
?: throw AssertionError()
|
||||
val forLoopInfo = iteratorToLoopHeader[iterator.symbol]
|
||||
?: return null // If we didn't lower a corresponding header.
|
||||
// TODO: Use PLUS_ASSIGN to increment
|
||||
val plusOperator = symbols.getBinaryOperator(
|
||||
OperatorNameConventions.PLUS,
|
||||
forLoopInfo.inductionVariable.type.toKotlinType(),
|
||||
forLoopInfo.step.type.toKotlinType()
|
||||
)
|
||||
if (forLoopInfo is ProgressionLoopHeader) {
|
||||
forLoopInfo.loopVariable = variable
|
||||
}
|
||||
return with(context.createIrBuilder(getScopeOwnerSymbol(), initializer.startOffset, initializer.endOffset)) {
|
||||
variable.initializer = forLoopInfo.initializeLoopVariable(symbols, this)
|
||||
val increment = irSetVar(
|
||||
forLoopInfo.inductionVariable.symbol, irCallOp(
|
||||
plusOperator, plusOperator.owner.returnType,
|
||||
irGet(forLoopInfo.inductionVariable),
|
||||
irGet(forLoopInfo.step)
|
||||
)
|
||||
)
|
||||
IrCompositeImpl(
|
||||
variable.startOffset,
|
||||
variable.endOffset,
|
||||
context.irBuiltIns.unitType,
|
||||
IrStatementOrigin.FOR_LOOP_NEXT,
|
||||
listOf(variable, increment)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
+122
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* 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.backend.common.lower.loops
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.CommonBackendContext
|
||||
import org.jetbrains.kotlin.backend.common.lower.matchers.IrCallMatcher
|
||||
import org.jetbrains.kotlin.backend.common.utils.isSubtypeOf
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.declarations.IrValueDeclaration
|
||||
import org.jetbrains.kotlin.ir.declarations.IrVariable
|
||||
import org.jetbrains.kotlin.ir.expressions.IrCall
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||
import org.jetbrains.kotlin.ir.types.IrType
|
||||
import org.jetbrains.kotlin.ir.util.defaultType
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult
|
||||
|
||||
// TODO: Handle withIndex()
|
||||
// TODO: Handle reversed()
|
||||
// TODO: Handle direct iteration on Ranges/Progressions (e.g., NOT using rangeTo or step)
|
||||
// TODO: Handle Strings/CharSequences
|
||||
|
||||
enum class ProgressionType(val numberCastFunctionName: Name) {
|
||||
INT_PROGRESSION(Name.identifier("toInt")),
|
||||
LONG_PROGRESSION(Name.identifier("toLong")),
|
||||
CHAR_PROGRESSION(Name.identifier("toChar"));
|
||||
}
|
||||
|
||||
// Information about loop that is required by HeaderProcessor to build ForLoopHeader
|
||||
internal sealed class HeaderInfo(
|
||||
val progressionType: ProgressionType,
|
||||
val lowerBound: IrExpression,
|
||||
val upperBound: IrExpression,
|
||||
val step: IrExpression?, // null value denotes default step (1)
|
||||
val increasing: Boolean,
|
||||
val needLastCalculation: Boolean,
|
||||
val closed: Boolean
|
||||
)
|
||||
|
||||
internal class ProgressionHeaderInfo(
|
||||
progressionType: ProgressionType,
|
||||
lowerBound: IrExpression,
|
||||
upperBound: IrExpression,
|
||||
step: IrExpression? = null,
|
||||
increasing: Boolean = true,
|
||||
needLastCalculation: Boolean = false,
|
||||
closed: Boolean = true
|
||||
) : HeaderInfo(progressionType, lowerBound, upperBound, step, increasing, needLastCalculation, closed)
|
||||
|
||||
internal class ArrayHeaderInfo(
|
||||
lowerBound: IrExpression,
|
||||
upperBound: IrExpression,
|
||||
val arrayDeclaration: IrValueDeclaration
|
||||
) : HeaderInfo(
|
||||
ProgressionType.INT_PROGRESSION,
|
||||
lowerBound,
|
||||
upperBound,
|
||||
step = null,
|
||||
increasing = true,
|
||||
needLastCalculation = false,
|
||||
closed = false
|
||||
)
|
||||
|
||||
internal interface HeaderInfoHandler<T> {
|
||||
val matcher: IrCallMatcher
|
||||
|
||||
fun build(call: IrCall, data: T): HeaderInfo?
|
||||
|
||||
fun handle(irCall: IrCall, data: T) = if (matcher(irCall)) {
|
||||
build(irCall, data)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
internal typealias ProgressionHandler = HeaderInfoHandler<ProgressionType>
|
||||
|
||||
// We need to wrap builder into visitor because of `StepHandler` which has to visit its subtree
|
||||
// to get information about underlying progression.
|
||||
private class ProgressionHeaderInfoBuilder(val context: CommonBackendContext) : IrElementVisitor<HeaderInfo?, Nothing?> {
|
||||
|
||||
private val symbols = context.ir.symbols
|
||||
|
||||
private val progressionElementTypes = symbols.integerClassesTypes + symbols.char.descriptor.defaultType
|
||||
|
||||
private val progressionHandlers = listOf(
|
||||
IndicesHandler(context),
|
||||
UntilHandler(progressionElementTypes),
|
||||
DownToHandler(progressionElementTypes),
|
||||
StepHandler(context, this),
|
||||
RangeToHandler(progressionElementTypes)
|
||||
)
|
||||
|
||||
private fun IrType.getProgressionType(): ProgressionType? = when {
|
||||
isSubtypeOf(symbols.charProgression.owner.defaultType) -> ProgressionType.CHAR_PROGRESSION
|
||||
isSubtypeOf(symbols.intProgression.owner.defaultType) -> ProgressionType.INT_PROGRESSION
|
||||
isSubtypeOf(symbols.longProgression.owner.defaultType) -> ProgressionType.LONG_PROGRESSION
|
||||
else -> null
|
||||
}
|
||||
|
||||
override fun visitElement(element: IrElement, data: Nothing?): HeaderInfo? = null
|
||||
|
||||
override fun visitCall(expression: IrCall, data: Nothing?): HeaderInfo? {
|
||||
val progressionType = expression.type.getProgressionType()
|
||||
?: return null
|
||||
return progressionHandlers.firstNotNullResult { it.handle(expression, progressionType) }
|
||||
}
|
||||
}
|
||||
|
||||
internal class HeaderInfoBuilder(context: CommonBackendContext) {
|
||||
private val progressionInfoBuilder = ProgressionHeaderInfoBuilder(context)
|
||||
private val arrayIterationHandler = ArrayIterationHandler(context)
|
||||
|
||||
fun build(variable: IrVariable): HeaderInfo? {
|
||||
val initializer = variable.initializer!! as IrCall
|
||||
return arrayIterationHandler.handle(initializer, null)
|
||||
?: initializer.dispatchReceiver?.accept(progressionInfoBuilder, null)
|
||||
}
|
||||
}
|
||||
+300
@@ -0,0 +1,300 @@
|
||||
/*
|
||||
* 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.backend.common.lower.loops
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.CommonBackendContext
|
||||
import org.jetbrains.kotlin.backend.common.ir.Symbols
|
||||
import org.jetbrains.kotlin.backend.common.lower.DeclarationIrBuilder
|
||||
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
|
||||
import org.jetbrains.kotlin.backend.common.utils.isSubtypeOf
|
||||
import org.jetbrains.kotlin.ir.IrStatement
|
||||
import org.jetbrains.kotlin.ir.builders.irCall
|
||||
import org.jetbrains.kotlin.ir.builders.irGet
|
||||
import org.jetbrains.kotlin.ir.builders.irImplicitCast
|
||||
import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin
|
||||
import org.jetbrains.kotlin.ir.declarations.IrVariable
|
||||
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.IrLoop
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrDoWhileLoopImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrWhileLoopImpl
|
||||
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrSymbol
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
import org.jetbrains.kotlin.ir.types.impl.IrStarProjectionImpl
|
||||
import org.jetbrains.kotlin.ir.util.functions
|
||||
import org.jetbrains.kotlin.ir.util.isNullable
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
|
||||
/** Contains information about variables used in the loop. */
|
||||
internal sealed class ForLoopHeader(
|
||||
val inductionVariable: IrVariable,
|
||||
val bound: IrVariable,
|
||||
val last: IrVariable,
|
||||
val step: IrVariable,
|
||||
val progressionType: ProgressionType
|
||||
) {
|
||||
abstract fun initializeLoopVariable(symbols: Symbols<CommonBackendContext>, builder: DeclarationIrBuilder): IrExpression
|
||||
|
||||
abstract val declarations: List<IrStatement>
|
||||
|
||||
abstract fun buildBody(builder: DeclarationIrBuilder, loop: IrLoop, newBody: IrExpression?): IrLoop
|
||||
}
|
||||
|
||||
internal class ProgressionLoopHeader(
|
||||
headerInfo: ProgressionHeaderInfo,
|
||||
inductionVariable: IrVariable,
|
||||
bound: IrVariable,
|
||||
last: IrVariable,
|
||||
step: IrVariable,
|
||||
var loopVariable: IrVariable? = null
|
||||
) : ForLoopHeader(inductionVariable, bound, last, step, headerInfo.progressionType) {
|
||||
|
||||
val closed = headerInfo.closed
|
||||
|
||||
private val increasing = headerInfo.increasing
|
||||
|
||||
fun comparingFunction(builtIns: IrBuiltIns) = if (increasing)
|
||||
builtIns.lessOrEqualFunByOperandType[builtIns.int]?.symbol!!
|
||||
else
|
||||
builtIns.greaterOrEqualFunByOperandType[builtIns.int]?.symbol!!
|
||||
|
||||
override fun initializeLoopVariable(symbols: Symbols<CommonBackendContext>, builder: DeclarationIrBuilder) = with(builder) {
|
||||
irGet(inductionVariable)
|
||||
}
|
||||
|
||||
override val declarations: List<IrStatement>
|
||||
get() = listOf(inductionVariable, step, bound, last)
|
||||
|
||||
override fun buildBody(builder: DeclarationIrBuilder, loop: IrLoop, newBody: IrExpression?): IrLoop = with(builder) {
|
||||
assert(loopVariable != null)
|
||||
val newCondition = irCall(context.irBuiltIns.booleanNotSymbol).apply {
|
||||
putValueArgument(0, irCall(context.irBuiltIns.eqeqSymbol).apply {
|
||||
putValueArgument(0, irGet(loopVariable!!))
|
||||
putValueArgument(1, irGet(last))
|
||||
})
|
||||
}
|
||||
IrDoWhileLoopImpl(loop.startOffset, loop.endOffset, loop.type, loop.origin).apply {
|
||||
label = loop.label
|
||||
condition = newCondition
|
||||
body = newBody
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class ArrayLoopHeader(
|
||||
headerInfo: ArrayHeaderInfo,
|
||||
inductionVariable: IrVariable,
|
||||
bound: IrVariable,
|
||||
last: IrVariable,
|
||||
step: IrVariable
|
||||
) : ForLoopHeader(inductionVariable, bound, last, step, ProgressionType.INT_PROGRESSION) {
|
||||
|
||||
private val arrayDeclaration = headerInfo.arrayDeclaration
|
||||
|
||||
override fun initializeLoopVariable(symbols: Symbols<CommonBackendContext>, builder: DeclarationIrBuilder) = with(builder) {
|
||||
val arrayClass = (arrayDeclaration.type.classifierOrNull) as IrClassSymbol
|
||||
val arrayGetFun = arrayClass.owner.functions.find { it.name.toString() == "get" }!!
|
||||
irCall(arrayGetFun).apply {
|
||||
dispatchReceiver = irGet(arrayDeclaration)
|
||||
putValueArgument(0, irGet(inductionVariable))
|
||||
}
|
||||
}
|
||||
|
||||
override val declarations: List<IrStatement>
|
||||
get() = listOf(arrayDeclaration, inductionVariable, step, bound, last)
|
||||
|
||||
override fun buildBody(builder: DeclarationIrBuilder, loop: IrLoop, newBody: IrExpression?): IrLoop = with(builder) {
|
||||
val builtIns = context.irBuiltIns
|
||||
val callee = builtIns.lessOrEqualFunByOperandType[builtIns.int]?.symbol!!
|
||||
val newCondition = irCall(callee).apply {
|
||||
putValueArgument(0, irGet(inductionVariable))
|
||||
putValueArgument(1, irGet(last))
|
||||
}
|
||||
IrWhileLoopImpl(loop.startOffset, loop.endOffset, loop.type, loop.origin).apply {
|
||||
label = loop.label
|
||||
condition = newCondition
|
||||
body = newBody
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun ProgressionType.elementType(context: CommonBackendContext): IrType = when (this) {
|
||||
ProgressionType.INT_PROGRESSION -> context.irBuiltIns.intType
|
||||
ProgressionType.LONG_PROGRESSION -> context.irBuiltIns.longType
|
||||
ProgressionType.CHAR_PROGRESSION -> context.irBuiltIns.charType
|
||||
}
|
||||
|
||||
// Given the for loop iterator variable, extract information about iterable subject
|
||||
// and create ForLoopHeader from it.
|
||||
internal class HeaderProcessor(
|
||||
private val context: CommonBackendContext,
|
||||
private val headerInfoBuilder: HeaderInfoBuilder,
|
||||
private val scopeOwnerSymbol: () -> IrSymbol
|
||||
) {
|
||||
|
||||
private val symbols = context.ir.symbols
|
||||
|
||||
fun processHeader(variable: IrVariable): ForLoopHeader? {
|
||||
|
||||
assert(variable.origin == IrDeclarationOrigin.FOR_LOOP_ITERATOR)
|
||||
// Create iterator type with star projections.
|
||||
val iteratorType = symbols.iterator.run {
|
||||
createType(
|
||||
hasQuestionMark = false,
|
||||
arguments = descriptor.declaredTypeParameters.map { IrStarProjectionImpl }
|
||||
)
|
||||
}
|
||||
|
||||
if (!variable.type.isSubtypeOf(iteratorType)) {
|
||||
return null
|
||||
}
|
||||
val builder = context.createIrBuilder(scopeOwnerSymbol(), variable.startOffset, variable.endOffset)
|
||||
// Collect loop info and form the loop header composite.
|
||||
val progressionInfo = headerInfoBuilder.build(variable)
|
||||
?: return null
|
||||
if (progressionInfo is ArrayHeaderInfo) {
|
||||
progressionInfo.arrayDeclaration.parent = variable.parent
|
||||
}
|
||||
with(builder) {
|
||||
with(progressionInfo) {
|
||||
/**
|
||||
* For this loop:
|
||||
* `for (i in a() .. b() step c() step d())`
|
||||
* We need to call functions in the following order: a, b, c, d.
|
||||
* So we call b() before step calculations and then call last element calculation function (if required).
|
||||
*/
|
||||
// Due to features of PSI2IR we can obtain nullable arguments here while actually
|
||||
// they are non-nullable (the frontend takes care about this). So we need to cast them to non-nullable.
|
||||
val inductionVariable = scope.createTemporaryVariable(
|
||||
lowerBound.castIfNecessary(progressionType),
|
||||
nameHint = "inductionVariable",
|
||||
isMutable = true,
|
||||
origin = IrDeclarationOrigin.FOR_LOOP_IMPLICIT_VARIABLE
|
||||
)
|
||||
|
||||
val upperBoundTmpVariable = scope.createTemporaryVariable(
|
||||
ensureNotNullable(upperBound.castIfNecessary(progressionType)),
|
||||
nameHint = "upperBound",
|
||||
origin = IrDeclarationOrigin.FOR_LOOP_IMPLICIT_VARIABLE
|
||||
)
|
||||
|
||||
val stepExpression = if (step != null) {
|
||||
ensureNotNullable(if (increasing) step else step.unaryMinus())
|
||||
} else {
|
||||
defaultStep(startOffset, endOffset)
|
||||
}
|
||||
|
||||
val stepValue = scope.createTemporaryVariable(
|
||||
stepExpression,
|
||||
nameHint = "step",
|
||||
origin = IrDeclarationOrigin.FOR_LOOP_IMPLICIT_VARIABLE
|
||||
)
|
||||
|
||||
// Calculate the last element of the progression
|
||||
// The last element can be:
|
||||
// boundValue, if step is 1 and the range is closed.
|
||||
// boundValue - 1, if step is 1 and the range is open.
|
||||
// getProgressionLast(inductionVariable, boundValue, step), if step != 1 and the range is closed.
|
||||
// getProgressionLast(inductionVariable, boundValue - 1, step), if step != 1 and the range is open.
|
||||
val lastExpression = if (closed) {
|
||||
irGet(upperBoundTmpVariable)
|
||||
} else {
|
||||
val decrementSymbol = symbols.getUnaryOperator(OperatorNameConventions.DEC, upperBoundTmpVariable.type.toKotlinType())
|
||||
irCall(decrementSymbol.owner).apply {
|
||||
dispatchReceiver = irGet(upperBoundTmpVariable)
|
||||
}
|
||||
}
|
||||
// In case of `step` we need to calculate the last element.
|
||||
val lastElement =
|
||||
if (needLastCalculation) {
|
||||
TODO("Implement and use irGetProgressionLast")
|
||||
// irGetProgressionLast(progressionType, inductionVariable, lastExpression, stepValue)
|
||||
} else {
|
||||
lastExpression
|
||||
}
|
||||
val lastValue = scope.createTemporaryVariable(
|
||||
lastElement,
|
||||
nameHint = "last",
|
||||
origin = IrDeclarationOrigin.FOR_LOOP_IMPLICIT_VARIABLE
|
||||
)
|
||||
|
||||
return when (progressionInfo) {
|
||||
is ArrayHeaderInfo -> ArrayLoopHeader(
|
||||
progressionInfo,
|
||||
inductionVariable,
|
||||
upperBoundTmpVariable,
|
||||
lastValue,
|
||||
stepValue
|
||||
)
|
||||
is ProgressionHeaderInfo -> ProgressionLoopHeader(
|
||||
progressionInfo,
|
||||
inductionVariable,
|
||||
upperBoundTmpVariable,
|
||||
lastValue,
|
||||
stepValue
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun IrExpression.castIfNecessary(progressionType: ProgressionType): IrExpression {
|
||||
return if (type.toKotlinType() == progressionType.elementType(context).toKotlinType()) {
|
||||
this
|
||||
} else {
|
||||
val function = symbols.getFunction(progressionType.numberCastFunctionName, type.toKotlinType())
|
||||
IrCallImpl(startOffset, endOffset, function.owner.returnType, function)
|
||||
.apply { dispatchReceiver = this@castIfNecessary }
|
||||
}
|
||||
}
|
||||
|
||||
private fun DeclarationIrBuilder.ensureNotNullable(expression: IrExpression) =
|
||||
if (expression.type is IrSimpleType && expression.type.isNullable()) {
|
||||
irImplicitCast(expression, expression.type.makeNotNull())
|
||||
} else {
|
||||
expression
|
||||
}
|
||||
|
||||
private fun IrExpression.unaryMinus(): IrExpression {
|
||||
val unaryOperator = symbols.getUnaryOperator(OperatorNameConventions.UNARY_MINUS, type.toKotlinType())
|
||||
return IrCallImpl(startOffset, endOffset, unaryOperator.owner.returnType, unaryOperator).apply {
|
||||
dispatchReceiver = this@unaryMinus
|
||||
}
|
||||
}
|
||||
|
||||
private fun HeaderInfo.defaultStep(startOffset: Int, endOffset: Int): IrExpression {
|
||||
val type = progressionType.elementType(context)
|
||||
val step = if (increasing) 1 else -1
|
||||
return when {
|
||||
type.isInt() || type.isChar() ->
|
||||
IrConstImpl.int(startOffset, endOffset, context.irBuiltIns.intType, step)
|
||||
type.isLong() ->
|
||||
IrConstImpl.long(startOffset, endOffset, context.irBuiltIns.longType, step.toLong())
|
||||
else -> throw IllegalArgumentException()
|
||||
}
|
||||
}
|
||||
|
||||
// private fun irGetProgressionLast(
|
||||
// progressionType: ProgressionType,
|
||||
// first: IrVariable,
|
||||
// lastExpression: IrExpression,
|
||||
// step: IrVariable
|
||||
// ): IrExpression {
|
||||
// val symbol = symbols.getProgressionLast[progressionType.elementType(context).toKotlinType()]
|
||||
// ?: throw IllegalArgumentException("No `getProgressionLast` for type ${step.type} ${lastExpression.type}")
|
||||
// val startOffset = lastExpression.startOffset
|
||||
// val endOffset = lastExpression.endOffset
|
||||
// return IrCallImpl(startOffset, endOffset, symbol.owner.returnType, symbol).apply {
|
||||
// putValueArgument(0, IrGetValueImpl(startOffset, endOffset, first.type, first.symbol))
|
||||
// putValueArgument(1, lastExpression)
|
||||
// putValueArgument(2, IrGetValueImpl(startOffset, endOffset, step.type, step.symbol))
|
||||
// }
|
||||
// }
|
||||
}
|
||||
+192
@@ -0,0 +1,192 @@
|
||||
/*
|
||||
* 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.backend.common.lower.loops
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.CommonBackendContext
|
||||
import org.jetbrains.kotlin.backend.common.ir.Symbols
|
||||
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
|
||||
import org.jetbrains.kotlin.backend.common.lower.matchers.IrCallMatcher
|
||||
import org.jetbrains.kotlin.backend.common.lower.matchers.SimpleCalleeMatcher
|
||||
import org.jetbrains.kotlin.backend.common.lower.matchers.createIrCallMatcher
|
||||
import org.jetbrains.kotlin.backend.common.lower.matchers.singleArgumentExtension
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.ir.builders.irCall
|
||||
import org.jetbrains.kotlin.ir.builders.irGet
|
||||
import org.jetbrains.kotlin.ir.builders.irInt
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
import org.jetbrains.kotlin.ir.util.properties
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.types.SimpleType
|
||||
|
||||
// TODO: What if functions like Int.rangeTo(Char) will be added to stdlib later?
|
||||
internal class RangeToHandler(val progressionElementTypes: Collection<SimpleType>) : ProgressionHandler {
|
||||
override val matcher = SimpleCalleeMatcher {
|
||||
dispatchReceiver { it != null && it.type.toKotlinType() in progressionElementTypes }
|
||||
fqName { it.pathSegments().last() == Name.identifier("rangeTo") }
|
||||
parameterCount { it == 1 }
|
||||
parameter(0) { it.type.toKotlinType() in progressionElementTypes }
|
||||
}
|
||||
|
||||
override fun build(call: IrCall, data: ProgressionType) =
|
||||
ProgressionHeaderInfo(data, call.dispatchReceiver!!, call.getValueArgument(0)!!)
|
||||
}
|
||||
|
||||
internal class DownToHandler(val progressionElementTypes: Collection<SimpleType>) : ProgressionHandler {
|
||||
override val matcher = SimpleCalleeMatcher {
|
||||
singleArgumentExtension(FqName("kotlin.ranges.downTo"), progressionElementTypes)
|
||||
parameter(0) { it.type.toKotlinType() in progressionElementTypes }
|
||||
}
|
||||
|
||||
override fun build(call: IrCall, data: ProgressionType): HeaderInfo? =
|
||||
ProgressionHeaderInfo(
|
||||
data,
|
||||
call.extensionReceiver!!,
|
||||
call.getValueArgument(0)!!,
|
||||
increasing = false
|
||||
)
|
||||
}
|
||||
|
||||
internal class UntilHandler(val progressionElementTypes: Collection<SimpleType>) : ProgressionHandler {
|
||||
override val matcher = SimpleCalleeMatcher {
|
||||
singleArgumentExtension(FqName("kotlin.ranges.until"), progressionElementTypes)
|
||||
parameter(0) { it.type.toKotlinType() in progressionElementTypes }
|
||||
}
|
||||
|
||||
override fun build(call: IrCall, data: ProgressionType): HeaderInfo? =
|
||||
ProgressionHeaderInfo(
|
||||
data,
|
||||
call.extensionReceiver!!,
|
||||
call.getValueArgument(0)!!,
|
||||
closed = false
|
||||
)
|
||||
}
|
||||
|
||||
internal class IndicesHandler(val context: CommonBackendContext) : ProgressionHandler {
|
||||
|
||||
override val matcher = createIrCallMatcher {
|
||||
callee {
|
||||
fqName { it == FqName("kotlin.collections.<get-indices>") }
|
||||
parameterCount { it == 0 }
|
||||
}
|
||||
// TODO: Handle Collection<*>.indices
|
||||
// TODO: Handle CharSequence.indices
|
||||
extensionReceiver { it != null && KotlinBuiltIns.isArrayOrPrimitiveArray(it.type.toKotlinType()) }
|
||||
}
|
||||
|
||||
override fun build(call: IrCall, data: ProgressionType): HeaderInfo? =
|
||||
with(context.createIrBuilder(call.symbol, call.startOffset, call.endOffset)) {
|
||||
val lowerBound = irInt(0)
|
||||
val arrayClass = (call.extensionReceiver!!.type.classifierOrNull) as IrClassSymbol
|
||||
val arraySizeProperty = arrayClass.owner.properties.find { it.name.toString() == "size" }!!
|
||||
val upperBound = irCall(arraySizeProperty.getter!!).apply {
|
||||
dispatchReceiver = call.extensionReceiver
|
||||
}
|
||||
ProgressionHeaderInfo(data, lowerBound, upperBound, closed = false)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
internal class StepHandler(context: CommonBackendContext, val visitor: IrElementVisitor<HeaderInfo?, Nothing?>) : ProgressionHandler {
|
||||
private val symbols = context.ir.symbols
|
||||
|
||||
override val matcher: IrCallMatcher = SimpleCalleeMatcher {
|
||||
singleArgumentExtension(FqName("kotlin.ranges.step"), symbols.progressionClassesTypes)
|
||||
parameter(0) { it.type.isInt() || it.type.isLong() }
|
||||
}
|
||||
|
||||
private fun isDefaultStep(step: IrExpression?) =
|
||||
step == null || (step is IrConst<*> && step.isOne())
|
||||
|
||||
override fun build(call: IrCall, data: ProgressionType): HeaderInfo? {
|
||||
val nestedInfo = call.extensionReceiver!!.accept(visitor, null)
|
||||
?: return null
|
||||
|
||||
// Due to KT-27607 nested non-default steps could lead to incorrect behaviour.
|
||||
// So disable optimization of such rare cases for now.
|
||||
if (!isDefaultStep(nestedInfo.step)) {
|
||||
return null
|
||||
}
|
||||
|
||||
val newStep = call.getValueArgument(0)!!
|
||||
val (newStepCheck, needBoundCalculation) = irCheckProgressionStep(symbols, data, newStep)
|
||||
return ProgressionHeaderInfo(
|
||||
data,
|
||||
nestedInfo.lowerBound,
|
||||
nestedInfo.upperBound,
|
||||
newStepCheck,
|
||||
nestedInfo.increasing,
|
||||
needBoundCalculation,
|
||||
nestedInfo.closed
|
||||
)
|
||||
}
|
||||
|
||||
private fun IrConst<*>.isOne() = when (kind) {
|
||||
IrConstKind.Long -> value as Long == 1L
|
||||
IrConstKind.Int -> value as Int == 1
|
||||
else -> false
|
||||
}
|
||||
|
||||
private fun IrExpression.isPositiveConst() = this is IrConst<*> &&
|
||||
((kind == IrConstKind.Long && value as Long > 0) || (kind == IrConstKind.Int && value as Int > 0))
|
||||
|
||||
// Used only by the assert.
|
||||
private fun stepHasRightType(step: IrExpression, progressionType: ProgressionType) = when (progressionType) {
|
||||
|
||||
ProgressionType.CHAR_PROGRESSION,
|
||||
ProgressionType.INT_PROGRESSION -> step.type.makeNotNull().isInt()
|
||||
|
||||
ProgressionType.LONG_PROGRESSION -> step.type.makeNotNull().isLong()
|
||||
}
|
||||
|
||||
private fun irCheckProgressionStep(symbols: Symbols<CommonBackendContext>, progressionType: ProgressionType, step: IrExpression) =
|
||||
if (step.isPositiveConst()) {
|
||||
step to !(step as IrConst<*>).isOne()
|
||||
} else
|
||||
TODO("Implement call to checkProgressionStep")
|
||||
// {
|
||||
// // The frontend checks if the step has a right type (Long for LongProgression and Int for {Int/Char}Progression)
|
||||
// // so there is no need to cast it.
|
||||
// assert(stepHasRightType(step, progressionType))
|
||||
//
|
||||
// val symbol = symbols.checkProgressionStep[step.type.makeNotNull().toKotlinType()]
|
||||
// ?: throw IllegalArgumentException("No `checkProgressionStep` for type ${step.type}")
|
||||
// IrCallImpl(step.startOffset, step.endOffset, symbol.owner.returnType, symbol).apply {
|
||||
// putValueArgument(0, step)
|
||||
// } to true
|
||||
// }
|
||||
}
|
||||
|
||||
internal class ArrayIterationHandler(val context: CommonBackendContext) : HeaderInfoHandler<Nothing?> {
|
||||
|
||||
// No support for rare cases like `T : IntArray` for now.
|
||||
override val matcher = createIrCallMatcher {
|
||||
origin { it == IrStatementOrigin.FOR_LOOP_ITERATOR }
|
||||
dispatchReceiver { it != null && KotlinBuiltIns.isArrayOrPrimitiveArray(it.type.toKotlinType()) }
|
||||
}
|
||||
|
||||
// Consider case like `for (elem in A) { f(elem) }`
|
||||
// If we lower it to `for (i in A.indices) { f(A[i]) }`
|
||||
// Then we will break program behaviour if A is an expression with side-effect.
|
||||
// Instead, we lower it to
|
||||
// ```
|
||||
// val a = A
|
||||
// for (i in a.indices) { f(a[i]) }
|
||||
// ```
|
||||
override fun build(call: IrCall, data: Nothing?): HeaderInfo? =
|
||||
with(context.createIrBuilder(call.symbol, call.startOffset, call.endOffset)) {
|
||||
val arrayReference = scope.createTemporaryVariable(call.dispatchReceiver!!)
|
||||
val arrayClass = (arrayReference.type.classifierOrNull) as IrClassSymbol
|
||||
val arraySizeProperty = arrayClass.owner.properties.find { it.name.toString() == "size" }!!
|
||||
val upperBound = irCall(arraySizeProperty.getter!!).apply {
|
||||
dispatchReceiver = irGet(arrayReference)
|
||||
}
|
||||
ArrayHeaderInfo(irInt(0), upperBound, arrayReference)
|
||||
}
|
||||
}
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* 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.backend.common.lower.matchers
|
||||
|
||||
import org.jetbrains.kotlin.ir.expressions.IrCall
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin
|
||||
|
||||
|
||||
internal interface IrCallMatcher : (IrCall) -> Boolean
|
||||
|
||||
/**
|
||||
* IrCallMatcher that puts restrictions only on its callee.
|
||||
*/
|
||||
internal class SimpleCalleeMatcher(
|
||||
restrictions: IrFunctionMatcherContainer.() -> Unit
|
||||
) : IrCallMatcher {
|
||||
|
||||
private val calleeRestriction: IrFunctionMatcher = createIrFunctionRestrictions(restrictions)
|
||||
|
||||
override fun invoke(call: IrCall) = calleeRestriction(call.symbol.owner)
|
||||
}
|
||||
|
||||
internal class IrCallExtensionReceiverMatcher(
|
||||
val restriction: (IrExpression?) -> Boolean
|
||||
) : IrCallMatcher {
|
||||
override fun invoke(call: IrCall) = restriction(call.extensionReceiver)
|
||||
}
|
||||
|
||||
internal class IrCallDispatchReceiverMatcher(
|
||||
val restriction: (IrExpression?) -> Boolean
|
||||
) : IrCallMatcher {
|
||||
override fun invoke(call: IrCall) = restriction(call.dispatchReceiver)
|
||||
}
|
||||
|
||||
internal class IrCallOriginMatcher(
|
||||
val restriction: (IrStatementOrigin?) -> Boolean
|
||||
) : IrCallMatcher {
|
||||
override fun invoke(call: IrCall) = restriction(call.origin)
|
||||
}
|
||||
|
||||
internal open class IrCallMatcherContainer : IrCallMatcher {
|
||||
|
||||
private val matchers = mutableListOf<IrCallMatcher>()
|
||||
|
||||
fun add(matcher: IrCallMatcher) {
|
||||
matchers += matcher
|
||||
}
|
||||
|
||||
fun extensionReceiver(restriction: (IrExpression?) -> Boolean) =
|
||||
add(IrCallExtensionReceiverMatcher(restriction))
|
||||
|
||||
fun origin(restriction: (IrStatementOrigin?) -> Boolean) =
|
||||
add(IrCallOriginMatcher(restriction))
|
||||
|
||||
fun callee(restrictions: IrFunctionMatcherContainer.() -> Unit) {
|
||||
add(SimpleCalleeMatcher(restrictions))
|
||||
}
|
||||
|
||||
fun dispatchReceiver(restriction: (IrExpression?) -> Boolean) =
|
||||
add(IrCallDispatchReceiverMatcher(restriction))
|
||||
|
||||
override fun invoke(call: IrCall) = matchers.all { it(call) }
|
||||
}
|
||||
|
||||
internal fun createIrCallMatcher(restrictions: IrCallMatcherContainer.() -> Unit) =
|
||||
IrCallMatcherContainer().apply(restrictions)
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* 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.backend.common.lower.matchers
|
||||
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
||||
import org.jetbrains.kotlin.ir.declarations.IrValueParameter
|
||||
import org.jetbrains.kotlin.ir.types.toKotlinType
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
||||
import org.jetbrains.kotlin.types.SimpleType
|
||||
|
||||
internal interface IrFunctionMatcher : (IrFunction) -> Boolean
|
||||
|
||||
internal class ParameterMatcher(
|
||||
val index: Int,
|
||||
val restriction: (IrValueParameter) -> Boolean
|
||||
) : IrFunctionMatcher {
|
||||
|
||||
override fun invoke(function: IrFunction): Boolean {
|
||||
val params = function.valueParameters
|
||||
return params.size > index && restriction(params[index])
|
||||
}
|
||||
}
|
||||
|
||||
internal class DispatchReceiverMatcher(
|
||||
val restriction: (IrValueParameter?) -> Boolean
|
||||
) : IrFunctionMatcher {
|
||||
|
||||
override fun invoke(function: IrFunction): Boolean {
|
||||
return restriction(function.dispatchReceiverParameter)
|
||||
}
|
||||
}
|
||||
|
||||
internal class ExtensionReceiverMatcher(
|
||||
val restriction: (IrValueParameter?) -> Boolean
|
||||
) : IrFunctionMatcher {
|
||||
|
||||
override fun invoke(function: IrFunction): Boolean {
|
||||
return restriction(function.extensionReceiverParameter)
|
||||
}
|
||||
}
|
||||
|
||||
internal class ParameterCountMatcher(
|
||||
val restriction: (Int) -> Boolean
|
||||
) : IrFunctionMatcher {
|
||||
|
||||
override fun invoke(function: IrFunction): Boolean {
|
||||
return restriction(function.valueParameters.size)
|
||||
}
|
||||
}
|
||||
|
||||
internal class FqNameMatcher(
|
||||
val restriction: (FqName) -> Boolean
|
||||
) : IrFunctionMatcher {
|
||||
|
||||
override fun invoke(function: IrFunction): Boolean {
|
||||
return restriction(function.descriptor.fqNameSafe)
|
||||
}
|
||||
}
|
||||
|
||||
internal open class IrFunctionMatcherContainer : IrFunctionMatcher {
|
||||
private val restrictions = mutableListOf<IrFunctionMatcher>()
|
||||
|
||||
fun add(restriction: IrFunctionMatcher) {
|
||||
restrictions += restriction
|
||||
}
|
||||
|
||||
fun fqName(restriction: (FqName) -> Boolean) =
|
||||
add(FqNameMatcher(restriction))
|
||||
|
||||
fun parameterCount(restriction: (Int) -> Boolean) =
|
||||
add(ParameterCountMatcher(restriction))
|
||||
|
||||
fun extensionReceiver(restriction: (IrValueParameter?) -> Boolean) =
|
||||
add(ExtensionReceiverMatcher(restriction))
|
||||
|
||||
fun dispatchReceiver(restriction: (IrValueParameter?) -> Boolean) =
|
||||
add(DispatchReceiverMatcher(restriction))
|
||||
|
||||
fun parameter(index: Int, restriction: (IrValueParameter) -> Boolean) =
|
||||
add(ParameterMatcher(index, restriction))
|
||||
|
||||
override fun invoke(function: IrFunction) = restrictions.all { it(function) }
|
||||
}
|
||||
|
||||
internal fun createIrFunctionRestrictions(restrictions: IrFunctionMatcherContainer.() -> Unit) =
|
||||
IrFunctionMatcherContainer().apply(restrictions)
|
||||
|
||||
internal fun IrFunctionMatcherContainer.singleArgumentExtension(
|
||||
fqName: FqName,
|
||||
types: Collection<SimpleType>
|
||||
): IrFunctionMatcherContainer {
|
||||
extensionReceiver { it != null && it.type.toKotlinType() in types }
|
||||
parameterCount { it == 1 }
|
||||
fqName { it == fqName }
|
||||
return this
|
||||
}
|
||||
Reference in New Issue
Block a user