IR: remove IrCallMatcher/IrFunctionMatcher and usages

There doesn't seem to be much value over a simple imperative code which
checks exactly the same things in the same order. The main downside of
the removed API is that it was more difficult to debug.
This commit is contained in:
Alexander Udalov
2022-12-11 23:24:11 +01:00
parent 18950b448c
commit d2b66e5004
16 changed files with 131 additions and 360 deletions
@@ -11,7 +11,6 @@ import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext
import org.jetbrains.kotlin.backend.common.ir.Symbols import org.jetbrains.kotlin.backend.common.ir.Symbols
import org.jetbrains.kotlin.backend.common.lower.loops.* import org.jetbrains.kotlin.backend.common.lower.loops.*
import org.jetbrains.kotlin.backend.common.lower.loops.handlers.* import org.jetbrains.kotlin.backend.common.lower.loops.handlers.*
import org.jetbrains.kotlin.backend.common.lower.matchers.SimpleCalleeMatcher
import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
import org.jetbrains.kotlin.builtins.PrimitiveType import org.jetbrains.kotlin.builtins.PrimitiveType
import org.jetbrains.kotlin.builtins.UnsignedType import org.jetbrains.kotlin.builtins.UnsignedType
@@ -29,10 +28,7 @@ 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.symbols.IrSymbol import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.defaultType import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.util.functions
import org.jetbrains.kotlin.ir.util.render
import org.jetbrains.kotlin.ir.util.shallowCopy
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.util.OperatorNameConventions import org.jetbrains.kotlin.util.OperatorNameConventions
@@ -60,14 +56,14 @@ private class Transformer(
val context: CommonBackendContext, val context: CommonBackendContext,
val container: IrSymbolOwner val container: IrSymbolOwner
) : IrElementTransformerVoidWithContext() { ) : IrElementTransformerVoidWithContext() {
private val headerInfoBuilder = RangeHeaderInfoBuilder(context, this::getScopeOwnerSymbol) private val headerInfoBuilder = RangeHeaderInfoBuilder(context, this::getScopeOwnerSymbol)
fun getScopeOwnerSymbol() = currentScope?.scope?.scopeOwnerSymbol ?: container.symbol fun getScopeOwnerSymbol() = currentScope?.scope?.scopeOwnerSymbol ?: container.symbol
val stdlibExtensionContainsCallMatcher = SimpleCalleeMatcher { private fun matchStdlibExtensionContainsCall(expression: IrCall): Boolean {
extensionReceiver { it != null && it.type.isSubtypeOfClass(context.ir.symbols.closedRange) } val callee = expression.symbol.owner
fqName { it == FqName("kotlin.ranges.${OperatorNameConventions.CONTAINS}") } return callee.valueParameters.size == 1 &&
parameterCount { it == 1 } callee.extensionReceiverParameter?.type?.isSubtypeOfClass(context.ir.symbols.closedRange) == true &&
callee.kotlinFqName == FqName("kotlin.ranges.${OperatorNameConventions.CONTAINS}")
} }
override fun visitCall(expression: IrCall): IrExpression { override fun visitCall(expression: IrCall): IrExpression {
@@ -89,7 +85,7 @@ private class Transformer(
return super.visitCall(expression) // Preserve the call to not(). return super.visitCall(expression) // Preserve the call to not().
} }
if (expression.extensionReceiver != null && !stdlibExtensionContainsCallMatcher(expression)) { if (expression.extensionReceiver != null && !matchStdlibExtensionContainsCall(expression)) {
// We can only optimize calls to the stdlib extension functions and not a user-defined extension. // We can only optimize calls to the stdlib extension functions and not a user-defined extension.
// TODO: This breaks the optimization for *Range.reversed().contains(). The function called there is the extension function // TODO: This breaks the optimization for *Range.reversed().contains(). The function called there is the extension function
// Iterable.contains(). Figure out if we can safely match on that as well. // Iterable.contains(). Figure out if we can safely match on that as well.
@@ -412,15 +408,13 @@ internal open class RangeHeaderInfoBuilder(context: CommonBackendContext, scopeO
/** Builds a [HeaderInfo] for closed floating-point ranges built using the `rangeTo` function. */ /** Builds a [HeaderInfo] for closed floating-point ranges built using the `rangeTo` function. */
internal object FloatingPointRangeToHandler : HeaderInfoHandler<IrCall, Nothing?> { internal object FloatingPointRangeToHandler : HeaderInfoHandler<IrCall, Nothing?> {
private val matcher = SimpleCalleeMatcher { override fun matchIterable(expression: IrCall): Boolean {
fqName { it == FqName("kotlin.ranges.${OperatorNameConventions.RANGE_TO}") } val callee = expression.symbol.owner
extensionReceiver { it != null && it.type.run { isFloat() || isDouble() } } return callee.valueParameters.singleOrNull()?.type?.let { it.isFloat() || it.isDouble() } == true &&
parameterCount { it == 1 } callee.extensionReceiverParameter?.type?.let { it.isFloat() || it.isDouble() } == true &&
parameter(0) { it.type.run { isFloat() || isDouble() } } callee.kotlinFqName == FqName("kotlin.ranges.${OperatorNameConventions.RANGE_TO}")
} }
override fun matchIterable(expression: IrCall): Boolean = matcher(expression)
override fun build(expression: IrCall, data: Nothing?, scopeOwner: IrSymbol) = override fun build(expression: IrCall, data: Nothing?, scopeOwner: IrSymbol) =
FloatingPointRangeHeaderInfo( FloatingPointRangeHeaderInfo(
start = expression.extensionReceiver!!, start = expression.extensionReceiver!!,
@@ -429,15 +423,14 @@ internal object FloatingPointRangeToHandler : HeaderInfoHandler<IrCall, Nothing?
} }
/** Builds a [HeaderInfo] for ranges of Comparables built using the `rangeTo` extension function. */ /** Builds a [HeaderInfo] for ranges of Comparables built using the `rangeTo` extension function. */
internal class ComparableRangeToHandler(context: CommonBackendContext) : HeaderInfoHandler<IrCall, Nothing?> { internal class ComparableRangeToHandler(private val context: CommonBackendContext) : HeaderInfoHandler<IrCall, Nothing?> {
private val matcher = SimpleCalleeMatcher { override fun matchIterable(expression: IrCall): Boolean {
fqName { it == FqName("kotlin.ranges.${OperatorNameConventions.RANGE_TO}") } val callee = expression.symbol.owner
extensionReceiver { it != null && it.type.isSubtypeOfClass(context.ir.symbols.comparable) } return callee.valueParameters.size == 1 &&
parameterCount { it == 1 } callee.extensionReceiverParameter?.type?.isSubtypeOfClass(context.ir.symbols.comparable) == true &&
callee.kotlinFqName == FqName("kotlin.ranges.${OperatorNameConventions.RANGE_TO}")
} }
override fun matchIterable(expression: IrCall): Boolean = matcher(expression)
override fun build(expression: IrCall, data: Nothing?, scopeOwner: IrSymbol) = override fun build(expression: IrCall, data: Nothing?, scopeOwner: IrSymbol) =
ComparableRangeInfo( ComparableRangeInfo(
start = expression.extensionReceiver!!, start = expression.extensionReceiver!!,
@@ -7,7 +7,6 @@ package org.jetbrains.kotlin.backend.common.lower
import org.jetbrains.kotlin.backend.common.CommonBackendContext import org.jetbrains.kotlin.backend.common.CommonBackendContext
import org.jetbrains.kotlin.backend.common.FileLoweringPass import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.lower.matchers.SimpleCalleeMatcher
import org.jetbrains.kotlin.ir.declarations.IrFile import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.expressions.IrCall import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.expressions.IrConst import org.jetbrains.kotlin.ir.expressions.IrConst
@@ -15,6 +14,7 @@ import org.jetbrains.kotlin.ir.expressions.IrConstKind
import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
import org.jetbrains.kotlin.ir.types.isString import org.jetbrains.kotlin.ir.types.isString
import org.jetbrains.kotlin.ir.util.kotlinFqName
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
@@ -26,8 +26,8 @@ class StringTrimLowering(val context: CommonBackendContext) : FileLoweringPass,
override fun visitCall(expression: IrCall): IrExpression { override fun visitCall(expression: IrCall): IrExpression {
return when { return when {
trimIndentMatcher(expression) -> maybeComputeTrimIndent(expression) matchTrimIndent(expression) -> maybeComputeTrimIndent(expression)
trimMarginMatcher(expression) -> maybeComputeTrimMargin(expression) matchTrimMargin(expression) -> maybeComputeTrimMargin(expression)
else -> super.visitCall(expression) else -> super.visitCall(expression)
} }
} }
@@ -64,20 +64,21 @@ class StringTrimLowering(val context: CommonBackendContext) : FileLoweringPass,
return null return null
} }
private val trimIndentMatcher = SimpleCalleeMatcher { private fun matchTrimIndent(expression: IrCall): Boolean {
extensionReceiver { it != null && it.type.isString() } val callee = expression.symbol.owner
fqName { it == TRIM_INDENT_FQ_NAME } return callee.valueParameters.isEmpty() &&
parameterCount { it == 0 } callee.extensionReceiverParameter?.type?.isString() == true &&
callee.kotlinFqName == TRIM_INDENT_FQ_NAME
} }
private val trimMarginMatcher = SimpleCalleeMatcher { private fun matchTrimMargin(expression: IrCall): Boolean {
extensionReceiver { it != null && it.type.isString() } val callee = expression.symbol.owner
fqName { it == TRIM_MARGIN_FQ_NAME } return callee.valueParameters.singleOrNull()?.type?.isString() == true &&
parameterCount { it == 1 } callee.extensionReceiverParameter?.type?.isString() == true &&
parameter(0) { it.type.isString() } callee.kotlinFqName == TRIM_MARGIN_FQ_NAME
} }
private val TRIM_MARGIN_FQ_NAME = FqName.fromSegments(listOf("kotlin", "text", "trimMargin")) private val TRIM_MARGIN_FQ_NAME = FqName("kotlin.text.trimMargin")
private val TRIM_INDENT_FQ_NAME = FqName.fromSegments(listOf("kotlin", "text", "trimIndent")) private val TRIM_INDENT_FQ_NAME = FqName("kotlin.text.trimIndent")
} }
} }
@@ -8,7 +8,6 @@ package org.jetbrains.kotlin.backend.common.lower.loops
import org.jetbrains.kotlin.backend.common.CommonBackendContext import org.jetbrains.kotlin.backend.common.CommonBackendContext
import org.jetbrains.kotlin.backend.common.ir.Symbols import org.jetbrains.kotlin.backend.common.ir.Symbols
import org.jetbrains.kotlin.backend.common.lower.loops.handlers.* import org.jetbrains.kotlin.backend.common.lower.loops.handlers.*
import org.jetbrains.kotlin.backend.common.lower.matchers.IrCallMatcher
import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.IrStatement import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.declarations.IrVariable import org.jetbrains.kotlin.ir.declarations.IrVariable
@@ -242,14 +241,13 @@ internal interface HeaderInfoHandler<E : IrExpression, D> {
* Matches the `iterator()` call that produced the iterable; if the call matches (or the matcher is null), * Matches the `iterator()` call that produced the iterable; if the call matches (or the matcher is null),
* the handler can build a [HeaderInfo] from the iterable. * the handler can build a [HeaderInfo] from the iterable.
*/ */
val iteratorCallMatcher: IrCallMatcher? fun matchIteratorCall(call: IrCall): Boolean = true
get() = null
/** Builds a [HeaderInfo] from the expression. */ /** Builds a [HeaderInfo] from the expression. */
fun build(expression: E, data: D, scopeOwner: IrSymbol): HeaderInfo? fun build(expression: E, data: D, scopeOwner: IrSymbol): HeaderInfo?
fun handle(expression: E, iteratorCall: IrCall?, data: D, scopeOwner: IrSymbol) = fun handle(expression: E, iteratorCall: IrCall?, data: D, scopeOwner: IrSymbol): HeaderInfo? =
if ((iteratorCall == null || iteratorCallMatcher == null || iteratorCallMatcher!!(iteratorCall)) && matchIterable(expression)) { if ((iteratorCall == null || matchIteratorCall(iteratorCall)) && matchIterable(expression)) {
build(expression, data, scopeOwner) build(expression, data, scopeOwner)
} else { } else {
null null
@@ -22,7 +22,8 @@ import org.jetbrains.kotlin.util.OperatorNameConventions
internal class DefaultIterableHandler(private val context: CommonBackendContext) : HeaderInfoHandler<IrExpression, Nothing?> { internal class DefaultIterableHandler(private val context: CommonBackendContext) : HeaderInfoHandler<IrExpression, Nothing?> {
private val iterableClassSymbol = context.ir.symbols.iterable private val iterableClassSymbol = context.ir.symbols.iterable
override fun matchIterable(expression: IrExpression) = expression.type.isSubtypeOfClass(iterableClassSymbol) override fun matchIterable(expression: IrExpression): Boolean =
expression.type.isSubtypeOfClass(iterableClassSymbol)
override fun build(expression: IrExpression, data: Nothing?, scopeOwner: IrSymbol): HeaderInfo = override fun build(expression: IrExpression, data: Nothing?, scopeOwner: IrSymbol): HeaderInfo =
with(context.createIrBuilder(scopeOwner, expression.startOffset, expression.endOffset)) { with(context.createIrBuilder(scopeOwner, expression.startOffset, expression.endOffset)) {
@@ -24,11 +24,8 @@ internal class DefaultProgressionHandler(
private val symbols = context.ir.symbols private val symbols = context.ir.symbols
private val rangeClassesTypes = symbols.rangeClasses.map { it.defaultType }.toSet() private val rangeClassesTypes = symbols.rangeClasses.map { it.defaultType }.toSet()
override fun matchIterable(expression: IrExpression) = ProgressionType.fromIrType( override fun matchIterable(expression: IrExpression): Boolean =
expression.type, ProgressionType.fromIrType(expression.type, symbols, allowUnsignedBounds) != null
symbols,
allowUnsignedBounds
) != null
override fun build(expression: IrExpression, data: Nothing?, scopeOwner: IrSymbol): HeaderInfo = override fun build(expression: IrExpression, data: Nothing?, scopeOwner: IrSymbol): HeaderInfo =
with(context.createIrBuilder(scopeOwner, expression.startOffset, expression.endOffset)) { with(context.createIrBuilder(scopeOwner, expression.startOffset, expression.endOffset)) {
@@ -22,7 +22,7 @@ import org.jetbrains.kotlin.util.OperatorNameConventions
internal class DefaultSequenceHandler(private val context: CommonBackendContext) : HeaderInfoHandler<IrExpression, Nothing?> { internal class DefaultSequenceHandler(private val context: CommonBackendContext) : HeaderInfoHandler<IrExpression, Nothing?> {
private val sequenceClassSymbol = context.ir.symbols.sequence private val sequenceClassSymbol = context.ir.symbols.sequence
override fun matchIterable(expression: IrExpression) = override fun matchIterable(expression: IrExpression): Boolean =
sequenceClassSymbol != null && expression.type.isSubtypeOfClass(sequenceClassSymbol) sequenceClassSymbol != null && expression.type.isSubtypeOfClass(sequenceClassSymbol)
override fun build(expression: IrExpression, data: Nothing?, scopeOwner: IrSymbol): HeaderInfo = override fun build(expression: IrExpression, data: Nothing?, scopeOwner: IrSymbol): HeaderInfo =
@@ -8,8 +8,6 @@ package org.jetbrains.kotlin.backend.common.lower.loops.handlers
import org.jetbrains.kotlin.backend.common.CommonBackendContext import org.jetbrains.kotlin.backend.common.CommonBackendContext
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.common.lower.loops.* import org.jetbrains.kotlin.backend.common.lower.loops.*
import org.jetbrains.kotlin.backend.common.lower.matchers.SimpleCalleeMatcher
import org.jetbrains.kotlin.backend.common.lower.matchers.singleArgumentExtension
import org.jetbrains.kotlin.ir.builders.irInt import org.jetbrains.kotlin.ir.builders.irInt
import org.jetbrains.kotlin.ir.expressions.IrCall import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.expressions.IrConst import org.jetbrains.kotlin.ir.expressions.IrConst
@@ -17,22 +15,21 @@ import org.jetbrains.kotlin.ir.expressions.IrConstKind
import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
import org.jetbrains.kotlin.ir.symbols.IrSymbol import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.util.kotlinFqName
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
/** Builds a [HeaderInfo] for progressions built using the `downTo` extension function. */ /** Builds a [HeaderInfo] for progressions built using the `downTo` extension function. */
internal class DownToHandler(private val context: CommonBackendContext) : HeaderInfoHandler<IrCall, ProgressionType> { internal class DownToHandler(private val context: CommonBackendContext) : HeaderInfoHandler<IrCall, ProgressionType> {
private val preferJavaLikeCounterLoop = context.preferJavaLikeCounterLoop private val preferJavaLikeCounterLoop = context.preferJavaLikeCounterLoop
private val progressionElementTypes = context.ir.symbols.progressionElementTypes private val progressionElementTypes = context.ir.symbols.progressionElementTypes
private val matcher = SimpleCalleeMatcher { override fun matchIterable(expression: IrCall): Boolean {
singleArgumentExtension(FqName("kotlin.ranges.downTo"), progressionElementTypes) val callee = expression.symbol.owner
parameterCount { it == 1 } return callee.valueParameters.singleOrNull()?.type in progressionElementTypes &&
parameter(0) { it.type in progressionElementTypes } callee.extensionReceiverParameter?.type in progressionElementTypes &&
callee.kotlinFqName == FqName("kotlin.ranges.downTo")
} }
override fun matchIterable(expression: IrCall): Boolean = matcher(expression)
override fun build(expression: IrCall, data: ProgressionType, scopeOwner: IrSymbol) = override fun build(expression: IrCall, data: ProgressionType, scopeOwner: IrSymbol) =
with(context.createIrBuilder(scopeOwner, expression.startOffset, expression.endOffset)) { with(context.createIrBuilder(scopeOwner, expression.startOffset, expression.endOffset)) {
val first = expression.extensionReceiver!! val first = expression.extensionReceiver!!
@@ -10,9 +10,6 @@ import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.common.lower.loops.HeaderInfo import org.jetbrains.kotlin.backend.common.lower.loops.HeaderInfo
import org.jetbrains.kotlin.backend.common.lower.loops.HeaderInfoHandler import org.jetbrains.kotlin.backend.common.lower.loops.HeaderInfoHandler
import org.jetbrains.kotlin.backend.common.lower.loops.IndexedGetHeaderInfo import org.jetbrains.kotlin.backend.common.lower.loops.IndexedGetHeaderInfo
import org.jetbrains.kotlin.backend.common.lower.matchers.Quantifier
import org.jetbrains.kotlin.backend.common.lower.matchers.SimpleCalleeMatcher
import org.jetbrains.kotlin.backend.common.lower.matchers.createIrCallMatcher
import org.jetbrains.kotlin.ir.builders.createTmpVariable import org.jetbrains.kotlin.ir.builders.createTmpVariable
import org.jetbrains.kotlin.ir.builders.irCall import org.jetbrains.kotlin.ir.builders.irCall
import org.jetbrains.kotlin.ir.builders.irGet import org.jetbrains.kotlin.ir.builders.irGet
@@ -77,22 +74,14 @@ abstract class IndexedGetIterationHandler(
internal class ArrayIterationHandler(context: CommonBackendContext) : IndexedGetIterationHandler(context, canCacheLast = true) { internal class ArrayIterationHandler(context: CommonBackendContext) : IndexedGetIterationHandler(context, canCacheLast = true) {
private val supportsUnsignedArrays = context.optimizeLoopsOverUnsignedArrays private val supportsUnsignedArrays = context.optimizeLoopsOverUnsignedArrays
override fun matchIterable(expression: IrExpression) = override fun matchIterable(expression: IrExpression): Boolean {
expression.type.run { isArrayType() } || if (expression.type.isArrayType()) return true
expression.run {
this is IrCall && reversedArrayMatcher(this)
}
private val reversedArrayMatcher = val callee = (expression as? IrCall)?.symbol?.owner ?: return false
createIrCallMatcher(Quantifier.ANY) { return callee.valueParameters.isEmpty() &&
callee { callee.extensionReceiverParameter?.type?.let { it.isArray() || it.isPrimitiveArray() } == true &&
fqName { it == FqName("kotlin.collections.reversed") } callee.kotlinFqName == FqName("kotlin.collections.reversed")
extensionReceiver { }
it != null && it.type.run { isArray() || isPrimitiveArray() }
}
parameterCount { it == 0 }
}
}
override val IrType.sizePropertyGetter override val IrType.sizePropertyGetter
get() = getClass()!!.getPropertyGetter("size")!!.owner get() = getClass()!!.getPropertyGetter("size")!!.owner
@@ -123,17 +112,21 @@ internal class ArrayIterationHandler(context: CommonBackendContext) : IndexedGet
* Note: The value for "last" can NOT be cached (i.e., stored in a variable) because the size/length can change within the loop. This means * Note: The value for "last" can NOT be cached (i.e., stored in a variable) because the size/length can change within the loop. This means
* that "last" is re-evaluated with each iteration of the loop. * that "last" is re-evaluated with each iteration of the loop.
*/ */
internal open class CharSequenceIterationHandler(context: CommonBackendContext, canCacheLast: Boolean = false) : internal open class CharSequenceIterationHandler(
IndexedGetIterationHandler(context, canCacheLast) { context: CommonBackendContext,
override fun matchIterable(expression: IrExpression) = expression.type.isSubtypeOfClass(context.ir.symbols.charSequence) canCacheLast: Boolean = false
) : IndexedGetIterationHandler(context, canCacheLast) {
override fun matchIterable(expression: IrExpression): Boolean =
expression.type.isSubtypeOfClass(context.ir.symbols.charSequence)
// We only want to handle the known extension function for CharSequence in the standard library (top level `kotlin.text.iterator`). // We only want to handle the known extension function for CharSequence in the standard library (top level `kotlin.text.iterator`).
// The behavior of this iterator is well-defined and can be lowered. CharSequences can have their own iterators, either as a member or // The behavior of this iterator is well-defined and can be lowered. CharSequences can have their own iterators, either as a member or
// extension function, and the behavior of those custom iterators is unknown. // extension function, and the behavior of those custom iterators is unknown.
override val iteratorCallMatcher = SimpleCalleeMatcher { override fun matchIteratorCall(call: IrCall): Boolean {
extensionReceiver { it != null && it.type.run { isCharSequence() } } val callee = call.symbol.owner
fqName { it == FqName("kotlin.text.${OperatorNameConventions.ITERATOR}") } return callee.valueParameters.isEmpty() &&
parameterCount { it == 0 } callee.extensionReceiverParameter?.type?.isCharSequence() == true &&
callee.kotlinFqName == FqName("kotlin.text.${OperatorNameConventions.ITERATOR}")
} }
override val IrType.sizePropertyGetter: IrSimpleFunction override val IrType.sizePropertyGetter: IrSimpleFunction
@@ -149,7 +142,8 @@ internal open class CharSequenceIterationHandler(context: CommonBackendContext,
* Note: The value for "last" CAN be cached for Strings as they are immutable and the size/length cannot change. * Note: The value for "last" CAN be cached for Strings as they are immutable and the size/length cannot change.
*/ */
internal class StringIterationHandler(context: CommonBackendContext) : CharSequenceIterationHandler(context, canCacheLast = true) { internal class StringIterationHandler(context: CommonBackendContext) : CharSequenceIterationHandler(context, canCacheLast = true) {
override fun matchIterable(expression: IrExpression) = expression.type.isString() override fun matchIterable(expression: IrExpression): Boolean =
expression.type.isString()
override val IrType.sizePropertyGetter: IrSimpleFunction override val IrType.sizePropertyGetter: IrSimpleFunction
get() = context.ir.symbols.string.getPropertyGetter("length")!!.owner get() = context.ir.symbols.string.getPropertyGetter("length")!!.owner
@@ -8,7 +8,6 @@ package org.jetbrains.kotlin.backend.common.lower.loops.handlers
import org.jetbrains.kotlin.backend.common.CommonBackendContext import org.jetbrains.kotlin.backend.common.CommonBackendContext
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.common.lower.loops.* import org.jetbrains.kotlin.backend.common.lower.loops.*
import org.jetbrains.kotlin.backend.common.lower.matchers.SimpleCalleeMatcher
import org.jetbrains.kotlin.ir.builders.irCall import org.jetbrains.kotlin.ir.builders.irCall
import org.jetbrains.kotlin.ir.builders.irInt import org.jetbrains.kotlin.ir.builders.irInt
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
@@ -19,6 +18,7 @@ import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.getPropertyGetter import org.jetbrains.kotlin.ir.util.getPropertyGetter
import org.jetbrains.kotlin.ir.util.isPrimitiveArray import org.jetbrains.kotlin.ir.util.isPrimitiveArray
import org.jetbrains.kotlin.ir.util.isUnsignedArray import org.jetbrains.kotlin.ir.util.isUnsignedArray
import org.jetbrains.kotlin.ir.util.kotlinFqName
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
/** Builds a [HeaderInfo] for progressions built using the `indices` extension property. */ /** Builds a [HeaderInfo] for progressions built using the `indices` extension property. */
@@ -62,14 +62,13 @@ internal abstract class IndicesHandler(protected val context: CommonBackendConte
} }
internal class CollectionIndicesHandler(context: CommonBackendContext) : IndicesHandler(context) { internal class CollectionIndicesHandler(context: CommonBackendContext) : IndicesHandler(context) {
private val matcher = SimpleCalleeMatcher { override fun matchIterable(expression: IrCall): Boolean {
extensionReceiver { it?.type?.isCollection() == true } val callee = expression.symbol.owner
fqName { it == FqName("kotlin.collections.<get-indices>") } return callee.valueParameters.isEmpty() &&
parameterCount { it == 0 } callee.extensionReceiverParameter?.type?.isCollection() == true &&
callee.kotlinFqName == FqName("kotlin.collections.<get-indices>")
} }
override fun matchIterable(expression: IrCall): Boolean = matcher(expression)
override val IrType.sizePropertyGetter: IrSimpleFunction override val IrType.sizePropertyGetter: IrSimpleFunction
get() = context.ir.symbols.collection.getPropertyGetter("size")!!.owner get() = context.ir.symbols.collection.getPropertyGetter("size")!!.owner
} }
@@ -77,31 +76,27 @@ internal class CollectionIndicesHandler(context: CommonBackendContext) : Indices
internal class ArrayIndicesHandler(context: CommonBackendContext) : IndicesHandler(context) { internal class ArrayIndicesHandler(context: CommonBackendContext) : IndicesHandler(context) {
private val supportsUnsignedArrays = context.optimizeLoopsOverUnsignedArrays private val supportsUnsignedArrays = context.optimizeLoopsOverUnsignedArrays
private val matcher = SimpleCalleeMatcher { override fun matchIterable(expression: IrCall): Boolean {
extensionReceiver { val callee = expression.symbol.owner
it != null && it.type.run { return callee.valueParameters.isEmpty() &&
isArray() || isPrimitiveArray() || (supportsUnsignedArrays && isUnsignedArray()) callee.extensionReceiverParameter?.type?.let {
} it.isArray() || it.isPrimitiveArray() || (supportsUnsignedArrays && it.isUnsignedArray())
} } == true &&
fqName { it == FqName("kotlin.collections.<get-indices>") } callee.kotlinFqName == FqName("kotlin.collections.<get-indices>")
parameterCount { it == 0 }
} }
override fun matchIterable(expression: IrCall): Boolean = matcher(expression)
override val IrType.sizePropertyGetter: IrSimpleFunction override val IrType.sizePropertyGetter: IrSimpleFunction
get() = getClass()!!.getPropertyGetter("size")!!.owner get() = getClass()!!.getPropertyGetter("size")!!.owner
} }
internal class CharSequenceIndicesHandler(context: CommonBackendContext) : IndicesHandler(context) { internal class CharSequenceIndicesHandler(context: CommonBackendContext) : IndicesHandler(context) {
private val matcher = SimpleCalleeMatcher { override fun matchIterable(expression: IrCall): Boolean {
extensionReceiver { it != null && it.type.run { isCharSequence() } } val callee = expression.symbol.owner
fqName { it == FqName("kotlin.text.<get-indices>") } return callee.valueParameters.isEmpty() &&
parameterCount { it == 0 } callee.extensionReceiverParameter?.type?.isCharSequence() == true &&
callee.kotlinFqName == FqName("kotlin.text.<get-indices>")
} }
override fun matchIterable(expression: IrCall): Boolean = matcher(expression)
override val IrType.sizePropertyGetter: IrSimpleFunction override val IrType.sizePropertyGetter: IrSimpleFunction
get() = context.ir.symbols.charSequence.getPropertyGetter("length")!!.owner get() = context.ir.symbols.charSequence.getPropertyGetter("length")!!.owner
} }
@@ -8,7 +8,6 @@ package org.jetbrains.kotlin.backend.common.lower.loops.handlers
import org.jetbrains.kotlin.backend.common.CommonBackendContext import org.jetbrains.kotlin.backend.common.CommonBackendContext
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.common.lower.loops.* import org.jetbrains.kotlin.backend.common.lower.loops.*
import org.jetbrains.kotlin.backend.common.lower.matchers.SimpleCalleeMatcher
import org.jetbrains.kotlin.ir.builders.irInt import org.jetbrains.kotlin.ir.builders.irInt
import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
@@ -19,18 +18,15 @@ import org.jetbrains.kotlin.util.OperatorNameConventions
/** Builds a [HeaderInfo] for progressions built using the `rangeTo` function. */ /** Builds a [HeaderInfo] for progressions built using the `rangeTo` function. */
internal class RangeToHandler(private val context: CommonBackendContext) : HeaderInfoHandler<IrCall, ProgressionType> { internal class RangeToHandler(private val context: CommonBackendContext) : HeaderInfoHandler<IrCall, ProgressionType> {
private val preferJavaLikeCounterLoop = context.preferJavaLikeCounterLoop private val preferJavaLikeCounterLoop = context.preferJavaLikeCounterLoop
private val progressionElementTypes = context.ir.symbols.progressionElementTypes private val progressionElementTypes = context.ir.symbols.progressionElementTypes
private val matcher = SimpleCalleeMatcher { override fun matchIterable(expression: IrCall): Boolean {
dispatchReceiver { it != null && it.type in progressionElementTypes } val callee = expression.symbol.owner
fqName { it.pathSegments().last() == OperatorNameConventions.RANGE_TO } return callee.valueParameters.singleOrNull()?.type in progressionElementTypes &&
parameterCount { it == 1 } callee.dispatchReceiverParameter?.type in progressionElementTypes &&
parameter(0) { it.type in progressionElementTypes } callee.name == OperatorNameConventions.RANGE_TO
} }
override fun matchIterable(expression: IrCall): Boolean = matcher(expression)
override fun build(expression: IrCall, data: ProgressionType, scopeOwner: IrSymbol) = override fun build(expression: IrCall, data: ProgressionType, scopeOwner: IrSymbol) =
with(context.createIrBuilder(scopeOwner, expression.startOffset, expression.endOffset)) { with(context.createIrBuilder(scopeOwner, expression.startOffset, expression.endOffset)) {
val first = expression.dispatchReceiver!! val first = expression.dispatchReceiver!!
@@ -8,11 +8,10 @@ package org.jetbrains.kotlin.backend.common.lower.loops.handlers
import org.jetbrains.kotlin.backend.common.CommonBackendContext import org.jetbrains.kotlin.backend.common.CommonBackendContext
import org.jetbrains.kotlin.backend.common.lower.loops.HeaderInfoBuilder import org.jetbrains.kotlin.backend.common.lower.loops.HeaderInfoBuilder
import org.jetbrains.kotlin.backend.common.lower.loops.HeaderInfoHandler import org.jetbrains.kotlin.backend.common.lower.loops.HeaderInfoHandler
import org.jetbrains.kotlin.backend.common.lower.matchers.Quantifier
import org.jetbrains.kotlin.backend.common.lower.matchers.createIrCallMatcher
import org.jetbrains.kotlin.ir.expressions.IrCall import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.symbols.IrSymbol import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.types.defaultType import org.jetbrains.kotlin.ir.types.defaultType
import org.jetbrains.kotlin.ir.util.kotlinFqName
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
/** Builds a [HeaderInfo] for calls to reverse an iterable. */ /** Builds a [HeaderInfo] for calls to reverse an iterable. */
@@ -20,20 +19,13 @@ internal class ReversedHandler(context: CommonBackendContext, private val visito
HeaderInfoHandler<IrCall, Nothing?> { HeaderInfoHandler<IrCall, Nothing?> {
private val progressionClassesTypes = context.ir.symbols.progressionClasses.map { it.defaultType }.toSet() private val progressionClassesTypes = context.ir.symbols.progressionClasses.map { it.defaultType }.toSet()
// Use Quantifier.ANY so we can handle all reversed iterables in the same manner. override fun matchIterable(expression: IrCall): Boolean {
private val matcher = // TODO: Handle reversed String, Progression.withIndex(), etc.
createIrCallMatcher(Quantifier.ANY) { val callee = expression.symbol.owner
// Matcher for reversed progression. return callee.valueParameters.isEmpty() &&
callee { callee.extensionReceiverParameter?.type in progressionClassesTypes &&
fqName { it == FqName("kotlin.ranges.reversed") } callee.kotlinFqName == FqName("kotlin.ranges.reversed")
extensionReceiver { it != null && it.type in progressionClassesTypes } }
parameterCount { it == 0 }
}
// TODO: Handle reversed String, Progression.withIndex(), etc.
}
override fun matchIterable(expression: IrCall): Boolean = matcher(expression)
// Reverse the HeaderInfo from the underlying progression or array (if any). // Reverse the HeaderInfo from the underlying progression or array (if any).
override fun build(expression: IrCall, data: Nothing?, scopeOwner: IrSymbol) = override fun build(expression: IrCall, data: Nothing?, scopeOwner: IrSymbol) =
@@ -9,8 +9,6 @@ import org.jetbrains.kotlin.backend.common.CommonBackendContext
import org.jetbrains.kotlin.backend.common.lower.DeclarationIrBuilder import org.jetbrains.kotlin.backend.common.lower.DeclarationIrBuilder
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.common.lower.loops.* import org.jetbrains.kotlin.backend.common.lower.loops.*
import org.jetbrains.kotlin.backend.common.lower.matchers.SimpleCalleeMatcher
import org.jetbrains.kotlin.backend.common.lower.matchers.singleArgumentExtension
import org.jetbrains.kotlin.ir.IrStatement import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.builders.* import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.declarations.IrVariable import org.jetbrains.kotlin.ir.declarations.IrVariable
@@ -18,10 +16,11 @@ import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.addArgument import org.jetbrains.kotlin.ir.expressions.addArgument
import org.jetbrains.kotlin.ir.symbols.IrSymbol import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.types.defaultType import org.jetbrains.kotlin.ir.types.classOrNull
import org.jetbrains.kotlin.ir.types.isInt import org.jetbrains.kotlin.ir.types.isInt
import org.jetbrains.kotlin.ir.types.isLong import org.jetbrains.kotlin.ir.types.isLong
import org.jetbrains.kotlin.ir.util.defaultType import org.jetbrains.kotlin.ir.util.defaultType
import org.jetbrains.kotlin.ir.util.kotlinFqName
import org.jetbrains.kotlin.ir.util.shallowCopy import org.jetbrains.kotlin.ir.util.shallowCopy
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import kotlin.math.absoluteValue import kotlin.math.absoluteValue
@@ -30,17 +29,13 @@ import kotlin.math.absoluteValue
internal class StepHandler( internal class StepHandler(
private val context: CommonBackendContext, private val visitor: HeaderInfoBuilder private val context: CommonBackendContext, private val visitor: HeaderInfoBuilder
) : HeaderInfoHandler<IrCall, ProgressionType> { ) : HeaderInfoHandler<IrCall, ProgressionType> {
private val symbols = context.ir.symbols override fun matchIterable(expression: IrCall): Boolean {
val callee = expression.symbol.owner
private val matcher = SimpleCalleeMatcher { return callee.valueParameters.singleOrNull()?.type?.let { it.isInt() || it.isLong() } == true &&
singleArgumentExtension( callee.extensionReceiverParameter?.type?.classOrNull in context.ir.symbols.progressionClasses &&
FqName("kotlin.ranges.step"), callee.kotlinFqName == FqName("kotlin.ranges.step")
symbols.progressionClasses.map { it.defaultType })
parameter(0) { it.type.isInt() || it.type.isLong() }
} }
override fun matchIterable(expression: IrCall): Boolean = matcher(expression)
override fun build(expression: IrCall, data: ProgressionType, scopeOwner: IrSymbol): HeaderInfo? = override fun build(expression: IrCall, data: ProgressionType, scopeOwner: IrSymbol): HeaderInfo? =
with(context.createIrBuilder(scopeOwner, expression.startOffset, expression.endOffset)) { with(context.createIrBuilder(scopeOwner, expression.startOffset, expression.endOffset)) {
// Retrieve the HeaderInfo from the underlying progression (if any). // Retrieve the HeaderInfo from the underlying progression (if any).
@@ -8,26 +8,23 @@ package org.jetbrains.kotlin.backend.common.lower.loops.handlers
import org.jetbrains.kotlin.backend.common.CommonBackendContext import org.jetbrains.kotlin.backend.common.CommonBackendContext
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.common.lower.loops.* import org.jetbrains.kotlin.backend.common.lower.loops.*
import org.jetbrains.kotlin.backend.common.lower.matchers.SimpleCalleeMatcher
import org.jetbrains.kotlin.backend.common.lower.matchers.singleArgumentExtension
import org.jetbrains.kotlin.ir.builders.irInt import org.jetbrains.kotlin.ir.builders.irInt
import org.jetbrains.kotlin.ir.expressions.IrCall import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.symbols.IrSymbol import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.util.kotlinFqName
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
/** Builds a [HeaderInfo] for progressions built using the `until` extension function. */ /** Builds a [HeaderInfo] for progressions built using the `until` extension function. */
internal class UntilHandler(private val context: CommonBackendContext) : HeaderInfoHandler<IrCall, ProgressionType> { internal class UntilHandler(private val context: CommonBackendContext) : HeaderInfoHandler<IrCall, ProgressionType> {
private val symbols = context.ir.symbols private val progressionElementTypes = context.ir.symbols.progressionElementTypes
private val progressionElementTypes = symbols.progressionElementTypes
private val matcher = SimpleCalleeMatcher { override fun matchIterable(expression: IrCall): Boolean {
singleArgumentExtension(FqName("kotlin.ranges.until"), progressionElementTypes) val callee = expression.symbol.owner
parameterCount { it == 1 } return callee.valueParameters.singleOrNull()?.type in progressionElementTypes &&
parameter(0) { it.type in progressionElementTypes } callee.extensionReceiverParameter?.type in progressionElementTypes &&
callee.kotlinFqName == FqName("kotlin.ranges.until")
} }
override fun matchIterable(expression: IrCall): Boolean = matcher(expression)
override fun build(expression: IrCall, data: ProgressionType, scopeOwner: IrSymbol): HeaderInfo? = override fun build(expression: IrCall, data: ProgressionType, scopeOwner: IrSymbol): HeaderInfo? =
with(context.createIrBuilder(scopeOwner, expression.startOffset, expression.endOffset)) { with(context.createIrBuilder(scopeOwner, expression.startOffset, expression.endOffset)) {
ProgressionHeaderInfo( ProgressionHeaderInfo(
@@ -10,8 +10,6 @@ import org.jetbrains.kotlin.backend.common.lower.loops.HeaderInfo
import org.jetbrains.kotlin.backend.common.lower.loops.HeaderInfoHandler import org.jetbrains.kotlin.backend.common.lower.loops.HeaderInfoHandler
import org.jetbrains.kotlin.backend.common.lower.loops.NestedHeaderInfoBuilderForWithIndex import org.jetbrains.kotlin.backend.common.lower.loops.NestedHeaderInfoBuilderForWithIndex
import org.jetbrains.kotlin.backend.common.lower.loops.WithIndexHeaderInfo import org.jetbrains.kotlin.backend.common.lower.loops.WithIndexHeaderInfo
import org.jetbrains.kotlin.backend.common.lower.matchers.Quantifier
import org.jetbrains.kotlin.backend.common.lower.matchers.createIrCallMatcher
import org.jetbrains.kotlin.ir.expressions.IrCall import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.symbols.IrSymbol import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.types.isArray import org.jetbrains.kotlin.ir.types.isArray
@@ -20,40 +18,32 @@ import org.jetbrains.kotlin.ir.types.isSequence
import org.jetbrains.kotlin.ir.types.isSubtypeOfClass import org.jetbrains.kotlin.ir.types.isSubtypeOfClass
import org.jetbrains.kotlin.ir.util.isPrimitiveArray import org.jetbrains.kotlin.ir.util.isPrimitiveArray
import org.jetbrains.kotlin.ir.util.isUnsignedArray import org.jetbrains.kotlin.ir.util.isUnsignedArray
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.ir.util.kotlinFqName
/** Builds a [HeaderInfo] for calls to `withIndex()`. */ /** Builds a [HeaderInfo] for calls to `withIndex()`. */
internal class WithIndexHandler( internal class WithIndexHandler(
context: CommonBackendContext, private val visitor: NestedHeaderInfoBuilderForWithIndex private val context: CommonBackendContext,
private val visitor: NestedHeaderInfoBuilderForWithIndex
) : HeaderInfoHandler<IrCall, Nothing?> { ) : HeaderInfoHandler<IrCall, Nothing?> {
private val supportsUnsignedArrays = context.optimizeLoopsOverUnsignedArrays private val supportsUnsignedArrays = context.optimizeLoopsOverUnsignedArrays
// Use Quantifier.ANY so we can handle all `withIndex()` calls in the same manner. override fun matchIterable(expression: IrCall): Boolean {
private val matcher = val callee = expression.symbol.owner
createIrCallMatcher(Quantifier.ANY) { if (callee.valueParameters.isNotEmpty() || callee.name.asString() != "withIndex") return false
callee {
fqName { it == FqName("kotlin.collections.withIndex") }
extensionReceiver {
it != null && it.type.run {
isArray() || isPrimitiveArray() || isIterable() ||
(supportsUnsignedArrays && isUnsignedArray())
}
}
parameterCount { it == 0 }
}
callee {
fqName { it == FqName("kotlin.text.withIndex") }
extensionReceiver { it != null && it.type.isSubtypeOfClass(context.ir.symbols.charSequence) }
parameterCount { it == 0 }
}
callee {
fqName { it == FqName("kotlin.sequences.withIndex") }
extensionReceiver { it != null && it.type.run { isSequence() } }
parameterCount { it == 0 }
}
}
override fun matchIterable(expression: IrCall): Boolean = matcher(expression) return when (callee.kotlinFqName.asString()) {
"kotlin.collections.withIndex" ->
callee.extensionReceiverParameter?.type?.run {
isArray() || isPrimitiveArray() || isIterable() ||
(supportsUnsignedArrays && isUnsignedArray())
} == true
"kotlin.text.withIndex" ->
callee.extensionReceiverParameter?.type?.isSubtypeOfClass(context.ir.symbols.charSequence) == true
"kotlin.sequences.withIndex" ->
callee.extensionReceiverParameter?.type?.isSequence() == true
else -> false
}
}
override fun build(expression: IrCall, data: Nothing?, scopeOwner: IrSymbol): HeaderInfo? { override fun build(expression: IrCall, data: Nothing?, scopeOwner: IrSymbol): HeaderInfo? {
// WithIndexHeaderInfo is a composite that contains the HeaderInfo for the underlying iterable (if any). // WithIndexHeaderInfo is a composite that contains the HeaderInfo for the underlying iterable (if any).
@@ -1,77 +0,0 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* 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 enum class Quantifier { ALL, ANY }
internal open class IrCallMatcherContainer(private val quantifier: Quantifier) : 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) = when (quantifier) {
Quantifier.ALL -> matchers.all { it(call) }
Quantifier.ANY -> matchers.any { it(call) }
}
}
internal fun createIrCallMatcher(
quantifier: Quantifier = Quantifier.ALL,
restrictions: IrCallMatcherContainer.() -> Unit
) =
IrCallMatcherContainer(quantifier).apply(restrictions)
@@ -1,98 +0,0 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* 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.IrType
import org.jetbrains.kotlin.ir.util.kotlinFqName
import org.jetbrains.kotlin.name.FqName
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.kotlinFqName)
}
}
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<IrType>
): IrFunctionMatcherContainer {
extensionReceiver { it != null && it.type in types }
parameterCount { it == 1 }
fqName { it == fqName }
return this
}