diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/range/ArrayIndicesRangeValue.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/range/ArrayIndicesRangeValue.kt index 9164d00d611..76ce340dfe7 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/range/ArrayIndicesRangeValue.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/range/ArrayIndicesRangeValue.kt @@ -19,14 +19,17 @@ package org.jetbrains.kotlin.codegen.range import org.jetbrains.kotlin.codegen.ExpressionCodegen import org.jetbrains.kotlin.codegen.StackValue import org.jetbrains.kotlin.codegen.generateCallReceiver +import org.jetbrains.kotlin.codegen.range.forLoop.ForInDefinitelySafeSimpleProgressionLoopGenerator import org.jetbrains.kotlin.codegen.range.forLoop.ForInSimpleProgressionLoopGenerator +import org.jetbrains.kotlin.codegen.range.forLoop.ForLoopGenerator import org.jetbrains.kotlin.descriptors.CallableDescriptor import org.jetbrains.kotlin.psi.KtForExpression import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.org.objectweb.asm.Type -class ArrayIndicesRangeValue(rangeCall: ResolvedCall): PrimitiveNumberRangeIntrinsicRangeValue(rangeCall) { +class ArrayIndicesRangeValue(rangeCall: ResolvedCall) : + PrimitiveNumberRangeIntrinsicRangeValue(rangeCall), ReversableRangeValue { private val expectedReceiverType: KotlinType = ExpressionCodegen.getExpectedReceiverType(rangeCall) @@ -44,4 +47,9 @@ class ArrayIndicesRangeValue(rangeCall: ResolvedCall): P override fun createForLoopGenerator(codegen: ExpressionCodegen, forExpression: KtForExpression) = ForInSimpleProgressionLoopGenerator.fromBoundedValueWithStep1(codegen, forExpression, getBoundedValue(codegen)) + + override fun createForInReversedLoopGenerator(codegen: ExpressionCodegen, forExpression: KtForExpression): ForLoopGenerator = + ForInDefinitelySafeSimpleProgressionLoopGenerator.fromBoundedValueWithStepMinus1( + codegen, forExpression, getBoundedValue(codegen) + ) } \ No newline at end of file diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/range/forLoop/ForInDefinitelySafeSimpleProgressionLoopGenerator.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/range/forLoop/ForInDefinitelySafeSimpleProgressionLoopGenerator.kt new file mode 100644 index 00000000000..ece344c6919 --- /dev/null +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/range/forLoop/ForInDefinitelySafeSimpleProgressionLoopGenerator.kt @@ -0,0 +1,129 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.codegen.range.forLoop + +import org.jetbrains.kotlin.codegen.ExpressionCodegen +import org.jetbrains.kotlin.codegen.StackValue +import org.jetbrains.kotlin.codegen.range.SimpleBoundedValue +import org.jetbrains.kotlin.psi.KtForExpression +import org.jetbrains.org.objectweb.asm.Label +import org.jetbrains.org.objectweb.asm.Type + +/** + * Generates "naive" for loop: + * + * ``` + * i = [| startValue |]; + * [| if (!isStartInclusive) inc i |]; + * end = [| endValue |]; + * while ([| cmp(i, end) |]) ... { + * [| body |]; + * inc i; + * } + * ``` + * + * This generator is suitable for cases where arithmetic overflow is impossible. + */ +class ForInDefinitelySafeSimpleProgressionLoopGenerator( + codegen: ExpressionCodegen, + forExpression: KtForExpression, + private val startValue: StackValue, + private val isStartInclusive: Boolean, + private val endValue: StackValue, + private val isEndInclusive: Boolean, + step: Int +) : AbstractForInRangeLoopGenerator(codegen, forExpression, step) { + + constructor( + codegen: ExpressionCodegen, + forExpression: KtForExpression, + boundedValue: SimpleBoundedValue, + step: Int + ) : this( + codegen, forExpression, + startValue = if (step == 1) boundedValue.lowBound else boundedValue.highBound, + isStartInclusive = if (step == 1) boundedValue.isLowInclusive else boundedValue.isHighInclusive, + endValue = if (step == 1) boundedValue.highBound else boundedValue.lowBound, + isEndInclusive = if (step == 1) boundedValue.isHighInclusive else boundedValue.isLowInclusive, + step = step + ) + + companion object { + fun fromBoundedValueWithStep1(codegen: ExpressionCodegen, forExpression: KtForExpression, boundedValue: SimpleBoundedValue) = + ForInDefinitelySafeSimpleProgressionLoopGenerator(codegen, forExpression, boundedValue, 1) + + fun fromBoundedValueWithStepMinus1(codegen: ExpressionCodegen, forExpression: KtForExpression, boundedValue: SimpleBoundedValue) = + ForInDefinitelySafeSimpleProgressionLoopGenerator(codegen, forExpression, boundedValue, -1) + } + + override fun storeRangeStartAndEnd() { + loopParameter().store(startValue, v) + // Skip 1st element if start is not inclusive. + if (!isStartInclusive) incrementLoopVariable() + + StackValue.local(endVar, asmElementType).store(endValue, v) + } + + override fun checkEmptyLoop(loopExit: Label) { + } + + override fun checkPreCondition(loopExit: Label) { + loopParameter().put(asmElementType, v) + v.load(endVar, asmElementType) + if (asmElementType.sort == Type.LONG) { + v.lcmp() + if (step > 0) { + if (isEndInclusive) { + v.ifgt(loopExit) + } + else { + v.ifge(loopExit) + } + } + else { + if (isEndInclusive) { + v.iflt(loopExit) + } + else { + v.ifle(loopExit) + } + } + } + else { + if (step > 0) { + if (isEndInclusive) { + v.ificmpgt(loopExit) + } + else { + v.ificmpge(loopExit) + } + } + else { + if (isEndInclusive) { + v.ificmplt(loopExit) + } + else { + v.ificmple(loopExit) + } + } + } + } + + override fun checkPostConditionAndIncrement(loopExit: Label) { + incrementLoopVariable() + } +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/ranges/forInReversed/forInReversedArrayIndices.kt b/compiler/testData/codegen/box/ranges/forInReversed/forInReversedArrayIndices.kt new file mode 100644 index 00000000000..b36db60681f --- /dev/null +++ b/compiler/testData/codegen/box/ranges/forInReversed/forInReversedArrayIndices.kt @@ -0,0 +1,13 @@ +// WITH_RUNTIME +import kotlin.test.* + +fun box(): String { + val arr = intArrayOf(1, 1, 1, 1) + var sum = 0 + for (i in arr.indices.reversed()) { + sum = sum * 10 + i + arr[i] + } + assertEquals(4321, sum) + + return "OK" +} \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/forLoop/forInReversed/forInReversedArrayIndices.kt b/compiler/testData/codegen/bytecodeText/forLoop/forInReversed/forInReversedArrayIndices.kt new file mode 100644 index 00000000000..7074d1a59f7 --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/forLoop/forInReversed/forInReversedArrayIndices.kt @@ -0,0 +1,15 @@ +// WITH_RUNTIME +import kotlin.test.* + +fun box(): String { + val arr = intArrayOf(1, 1, 1, 1) + var sum = 0 + for (i in arr.indices.reversed()) { + sum = sum * 10 + i + arr[i] + } + assertEquals(4321, sum) + + return "OK" +} + +// 0 reversed \ No newline at end of file diff --git a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java index 0aba06eb35b..b315a585861 100644 --- a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java @@ -15241,6 +15241,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/ranges/forInReversed"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true); } + @TestMetadata("forInReversedArrayIndices.kt") + public void testForInReversedArrayIndices() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInReversed/forInReversedArrayIndices.kt"); + doTest(fileName); + } + @TestMetadata("forInReversedEmptyRange.kt") public void testForInReversedEmptyRange() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInReversed/forInReversedEmptyRange.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index 56de95c8077..96082a09654 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -15241,6 +15241,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/ranges/forInReversed"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true); } + @TestMetadata("forInReversedArrayIndices.kt") + public void testForInReversedArrayIndices() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInReversed/forInReversedArrayIndices.kt"); + doTest(fileName); + } + @TestMetadata("forInReversedEmptyRange.kt") public void testForInReversedEmptyRange() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInReversed/forInReversedEmptyRange.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java index 8931aa96042..2cdabf3ec4f 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java @@ -1408,6 +1408,12 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/bytecodeText/forLoop/forInReversed"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true); } + @TestMetadata("forInReversedArrayIndices.kt") + public void testForInReversedArrayIndices() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/forLoop/forInReversed/forInReversedArrayIndices.kt"); + doTest(fileName); + } + @TestMetadata("forInReversedEmptyRangeLiteral.kt") public void testForInReversedEmptyRangeLiteral() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/forLoop/forInReversed/forInReversedEmptyRangeLiteral.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index 98dfc96f65b..b11f6358efa 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -15241,6 +15241,12 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/ranges/forInReversed"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true); } + @TestMetadata("forInReversedArrayIndices.kt") + public void testForInReversedArrayIndices() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInReversed/forInReversedArrayIndices.kt"); + doTest(fileName); + } + @TestMetadata("forInReversedEmptyRange.kt") public void testForInReversedEmptyRange() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInReversed/forInReversedEmptyRange.kt"); diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java index 048edbfa531..88518f85012 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java @@ -16657,6 +16657,12 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/ranges/forInReversed"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JS, true); } + @TestMetadata("forInReversedArrayIndices.kt") + public void testForInReversedArrayIndices() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInReversed/forInReversedArrayIndices.kt"); + doTest(fileName); + } + @TestMetadata("forInReversedEmptyRange.kt") public void testForInReversedEmptyRange() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInReversed/forInReversedEmptyRange.kt");