diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/HeaderInfo.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/HeaderInfo.kt index 6cb0bfa894b..91a44d4122e 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/HeaderInfo.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/HeaderInfo.kt @@ -212,7 +212,8 @@ internal class HeaderInfoBuilder(context: CommonBackendContext, private val scop ) private val progressionHandlers = listOf( - IndicesHandler(context), + ArrayIndicesHandler(context), + CharSequenceIndicesHandler(context), UntilHandler(context, progressionElementTypes), DownToHandler(context, progressionElementTypes), RangeToHandler(context, progressionElementTypes) diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/ProgressionHandlers.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/ProgressionHandlers.kt index f49aea53252..379ddf9c781 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/ProgressionHandlers.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/ProgressionHandlers.kt @@ -219,22 +219,17 @@ internal class UntilHandler(private val context: CommonBackendContext, private v } /** Builds a [HeaderInfo] for progressions built using the `indices` extension property. */ -internal class IndicesHandler(private val context: CommonBackendContext) : ProgressionHandler { +internal abstract class IndicesHandler(private val context: CommonBackendContext) : ProgressionHandler { - override val matcher = SimpleCalleeMatcher { - // TODO: Handle Collection<*>.indices - // TODO: Handle CharSequence.indices - extensionReceiver { it != null && it.type.run { isArray() || isPrimitiveArray() } } - fqName { it == FqName("kotlin.collections.") } - parameterCount { it == 0 } - } + // TODO: Handle Collection<*>.indices override fun build(expression: IrCall, data: ProgressionType, scopeOwner: IrSymbol): HeaderInfo? = with(context.createIrBuilder(scopeOwner, expression.startOffset, expression.endOffset)) { // `last = array.size - 1` (last is inclusive) for the loop `for (i in array.indices)`. - val arraySizeProperty = expression.extensionReceiver!!.type.getClass()!!.properties.first { it.name.asString() == "size" } - val last = irCall(arraySizeProperty.getter!!).apply { - dispatchReceiver = expression.extensionReceiver + val receiver = expression.extensionReceiver!! + val sizeProperty = receiver.type.getClass()!!.properties.first { it.name.asString() == sizePropertyName() } + val last = irCall(sizeProperty.getter!!).apply { + dispatchReceiver = receiver }.decrement() ProgressionHeaderInfo( @@ -246,6 +241,29 @@ internal class IndicesHandler(private val context: CommonBackendContext) : Progr direction = ProgressionDirection.INCREASING ) } + + internal abstract fun sizePropertyName() : String +} + +internal class ArrayIndicesHandler(context: CommonBackendContext) : IndicesHandler(context) { + override val matcher = SimpleCalleeMatcher { + extensionReceiver { it != null && it.type.run { isArray() || isPrimitiveArray() } } + fqName { it == FqName("kotlin.collections.") } + parameterCount { it == 0 } + } + + override fun sizePropertyName(): String = "size" +} + +internal class CharSequenceIndicesHandler(context: CommonBackendContext) : IndicesHandler(context) { + + override val matcher = SimpleCalleeMatcher { + extensionReceiver { it != null && it.type.run { isSubtypeOfClass(context.ir.symbols.charSequence)} } + fqName { it == FqName("kotlin.text.") } + parameterCount { it == 0 } + } + + override fun sizePropertyName(): String = "length" } /** Builds a [HeaderInfo] for calls to reverse an iterable. */ diff --git a/compiler/testData/codegen/bytecodeText/forLoop/forInIndices/forInCharSequenceIndices.kt b/compiler/testData/codegen/bytecodeText/forLoop/forInIndices/forInCharSequenceIndices.kt index 5305ee10bb4..523003165e8 100644 --- a/compiler/testData/codegen/bytecodeText/forLoop/forInIndices/forInCharSequenceIndices.kt +++ b/compiler/testData/codegen/bytecodeText/forLoop/forInIndices/forInCharSequenceIndices.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR fun test(s: CharSequence): Int { var result = 0 for (i in s.indices) { @@ -7,12 +6,22 @@ fun test(s: CharSequence): Int { return result } +// JVM non-IR uses while. +// JVM IR uses if + do-while. + // 0 iterator // 0 getStart // 0 getEnd // 0 getFirst // 0 getLast +// JVM_TEMPLATES // 0 IF_ICMPGT // 0 IF_ICMPEQ // 1 IF_ICMPGE +// 1 IF + +// JVM_IR_TEMPLATES +// 1 IF_ICMPGT +// 1 IF_ICMPLE +// 2 IF diff --git a/compiler/testData/codegen/bytecodeText/forLoop/forInReversed/forInReversedCharSequenceIndices.kt b/compiler/testData/codegen/bytecodeText/forLoop/forInReversed/forInReversedCharSequenceIndices.kt index a9ef956c592..bfbc7301d47 100644 --- a/compiler/testData/codegen/bytecodeText/forLoop/forInReversed/forInReversedCharSequenceIndices.kt +++ b/compiler/testData/codegen/bytecodeText/forLoop/forInReversed/forInReversedCharSequenceIndices.kt @@ -1,5 +1,3 @@ -// IGNORE_BACKEND: JVM_IR -// NOTE: Enable once CharSequence.indices is handled import kotlin.test.* fun box(): String { @@ -13,6 +11,9 @@ fun box(): String { return "OK" } +// JVM non-IR uses while. +// JVM IR uses if + do-while. + // 0 reversed // 0 iterator // 0 getStart @@ -20,5 +21,12 @@ fun box(): String { // 0 getFirst // 0 getLast // 0 getStep -// 1 IF(_ICMPG|L)T -// 1 IF \ No newline at end of file + +// JVM_TEMPLATES +// 1 IFLT +// 1 IF + +// JVM_IR_TEMPLATES +// 1 IF_ICMPGT +// 1 IF_ICMPLE +// 2 IF