diff --git a/libraries/stdlib/common/src/generated/_Arrays.kt b/libraries/stdlib/common/src/generated/_Arrays.kt index 07914e25f73..10cd0c70d33 100644 --- a/libraries/stdlib/common/src/generated/_Arrays.kt +++ b/libraries/stdlib/common/src/generated/_Arrays.kt @@ -9701,7 +9701,7 @@ public inline fun CharArray.count(): Int { */ public inline fun Array.count(predicate: (T) -> Boolean): Int { var count = 0 - for (element in this) if (predicate(element)) count++ + for (element in this) if (predicate(element)) ++count return count } @@ -9710,7 +9710,7 @@ public inline fun Array.count(predicate: (T) -> Boolean): Int { */ public inline fun ByteArray.count(predicate: (Byte) -> Boolean): Int { var count = 0 - for (element in this) if (predicate(element)) count++ + for (element in this) if (predicate(element)) ++count return count } @@ -9719,7 +9719,7 @@ public inline fun ByteArray.count(predicate: (Byte) -> Boolean): Int { */ public inline fun ShortArray.count(predicate: (Short) -> Boolean): Int { var count = 0 - for (element in this) if (predicate(element)) count++ + for (element in this) if (predicate(element)) ++count return count } @@ -9728,7 +9728,7 @@ public inline fun ShortArray.count(predicate: (Short) -> Boolean): Int { */ public inline fun IntArray.count(predicate: (Int) -> Boolean): Int { var count = 0 - for (element in this) if (predicate(element)) count++ + for (element in this) if (predicate(element)) ++count return count } @@ -9737,7 +9737,7 @@ public inline fun IntArray.count(predicate: (Int) -> Boolean): Int { */ public inline fun LongArray.count(predicate: (Long) -> Boolean): Int { var count = 0 - for (element in this) if (predicate(element)) count++ + for (element in this) if (predicate(element)) ++count return count } @@ -9746,7 +9746,7 @@ public inline fun LongArray.count(predicate: (Long) -> Boolean): Int { */ public inline fun FloatArray.count(predicate: (Float) -> Boolean): Int { var count = 0 - for (element in this) if (predicate(element)) count++ + for (element in this) if (predicate(element)) ++count return count } @@ -9755,7 +9755,7 @@ public inline fun FloatArray.count(predicate: (Float) -> Boolean): Int { */ public inline fun DoubleArray.count(predicate: (Double) -> Boolean): Int { var count = 0 - for (element in this) if (predicate(element)) count++ + for (element in this) if (predicate(element)) ++count return count } @@ -9764,7 +9764,7 @@ public inline fun DoubleArray.count(predicate: (Double) -> Boolean): Int { */ public inline fun BooleanArray.count(predicate: (Boolean) -> Boolean): Int { var count = 0 - for (element in this) if (predicate(element)) count++ + for (element in this) if (predicate(element)) ++count return count } @@ -9773,7 +9773,7 @@ public inline fun BooleanArray.count(predicate: (Boolean) -> Boolean): Int { */ public inline fun CharArray.count(predicate: (Char) -> Boolean): Int { var count = 0 - for (element in this) if (predicate(element)) count++ + for (element in this) if (predicate(element)) ++count return count } @@ -13504,7 +13504,7 @@ public fun Array.average(): Double { var count: Int = 0 for (element in this) { sum += element - count += 1 + ++count } return if (count == 0) Double.NaN else sum / count } @@ -13518,7 +13518,7 @@ public fun Array.average(): Double { var count: Int = 0 for (element in this) { sum += element - count += 1 + ++count } return if (count == 0) Double.NaN else sum / count } @@ -13532,7 +13532,7 @@ public fun Array.average(): Double { var count: Int = 0 for (element in this) { sum += element - count += 1 + ++count } return if (count == 0) Double.NaN else sum / count } @@ -13546,7 +13546,7 @@ public fun Array.average(): Double { var count: Int = 0 for (element in this) { sum += element - count += 1 + ++count } return if (count == 0) Double.NaN else sum / count } @@ -13560,7 +13560,7 @@ public fun Array.average(): Double { var count: Int = 0 for (element in this) { sum += element - count += 1 + ++count } return if (count == 0) Double.NaN else sum / count } @@ -13574,7 +13574,7 @@ public fun Array.average(): Double { var count: Int = 0 for (element in this) { sum += element - count += 1 + ++count } return if (count == 0) Double.NaN else sum / count } @@ -13587,7 +13587,7 @@ public fun ByteArray.average(): Double { var count: Int = 0 for (element in this) { sum += element - count += 1 + ++count } return if (count == 0) Double.NaN else sum / count } @@ -13600,7 +13600,7 @@ public fun ShortArray.average(): Double { var count: Int = 0 for (element in this) { sum += element - count += 1 + ++count } return if (count == 0) Double.NaN else sum / count } @@ -13613,7 +13613,7 @@ public fun IntArray.average(): Double { var count: Int = 0 for (element in this) { sum += element - count += 1 + ++count } return if (count == 0) Double.NaN else sum / count } @@ -13626,7 +13626,7 @@ public fun LongArray.average(): Double { var count: Int = 0 for (element in this) { sum += element - count += 1 + ++count } return if (count == 0) Double.NaN else sum / count } @@ -13639,7 +13639,7 @@ public fun FloatArray.average(): Double { var count: Int = 0 for (element in this) { sum += element - count += 1 + ++count } return if (count == 0) Double.NaN else sum / count } @@ -13652,7 +13652,7 @@ public fun DoubleArray.average(): Double { var count: Int = 0 for (element in this) { sum += element - count += 1 + ++count } return if (count == 0) Double.NaN else sum / count } diff --git a/libraries/stdlib/common/src/generated/_Collections.kt b/libraries/stdlib/common/src/generated/_Collections.kt index 1e8258bb541..c6fcc5f34ad 100644 --- a/libraries/stdlib/common/src/generated/_Collections.kt +++ b/libraries/stdlib/common/src/generated/_Collections.kt @@ -251,6 +251,7 @@ public fun <@kotlin.internal.OnlyInputTypes T> Iterable.indexOf(element: T): if (this is List) return this.indexOf(element) var index = 0 for (item in this) { + checkIndexOverflow(index) if (element == item) return index index++ @@ -272,6 +273,7 @@ public fun <@kotlin.internal.OnlyInputTypes T> List.indexOf(element: T): Int public inline fun Iterable.indexOfFirst(predicate: (T) -> Boolean): Int { var index = 0 for (item in this) { + checkIndexOverflow(index) if (predicate(item)) return index index++ @@ -299,6 +301,7 @@ public inline fun Iterable.indexOfLast(predicate: (T) -> Boolean): Int { var lastIndex = -1 var index = 0 for (item in this) { + checkIndexOverflow(index) if (predicate(item)) lastIndex = index index++ @@ -387,6 +390,7 @@ public fun <@kotlin.internal.OnlyInputTypes T> Iterable.lastIndexOf(element: var lastIndex = -1 var index = 0 for (item in this) { + checkIndexOverflow(index) if (element == item) lastIndex = index index++ @@ -1258,7 +1262,7 @@ public inline fun > Iterable.mapIndex public inline fun > Iterable.mapIndexedTo(destination: C, transform: (index: Int, T) -> R): C { var index = 0 for (item in this) - destination.add(transform(index++, item)) + destination.add(transform(checkIndexOverflow(index++), item)) return destination } @@ -1407,7 +1411,7 @@ public inline fun Iterable.any(predicate: (T) -> Boolean): Boolean { public fun Iterable.count(): Int { if (this is Collection) return size var count = 0 - for (element in this) count++ + for (element in this) checkCountOverflow(++count) return count } @@ -1425,7 +1429,7 @@ public inline fun Collection.count(): Int { public inline fun Iterable.count(predicate: (T) -> Boolean): Int { if (this is Collection && isEmpty()) return 0 var count = 0 - for (element in this) if (predicate(element)) count++ + for (element in this) if (predicate(element)) checkCountOverflow(++count) return count } @@ -1447,7 +1451,7 @@ public inline fun Iterable.fold(initial: R, operation: (acc: R, T) -> public inline fun Iterable.foldIndexed(initial: R, operation: (index: Int, acc: R, T) -> R): R { var index = 0 var accumulator = initial - for (element in this) accumulator = operation(index++, accumulator, element) + for (element in this) accumulator = operation(checkIndexOverflow(index++), accumulator, element) return accumulator } @@ -1498,7 +1502,7 @@ public inline fun Iterable.forEach(action: (T) -> Unit): Unit { */ public inline fun Iterable.forEachIndexed(action: (index: Int, T) -> Unit): Unit { var index = 0 - for (item in this) action(index++, item) + for (item in this) action(checkIndexOverflow(index++), item) } /** @@ -1725,7 +1729,7 @@ public inline fun Iterable.reduceIndexed(operation: (index: Int, a var index = 1 var accumulator: S = iterator.next() while (iterator.hasNext()) { - accumulator = operation(index++, accumulator, iterator.next()) + accumulator = operation(checkIndexOverflow(index++), accumulator, iterator.next()) } return accumulator } @@ -2238,7 +2242,7 @@ public fun Iterable.average(): Double { var count: Int = 0 for (element in this) { sum += element - count += 1 + checkCountOverflow(++count) } return if (count == 0) Double.NaN else sum / count } @@ -2252,7 +2256,7 @@ public fun Iterable.average(): Double { var count: Int = 0 for (element in this) { sum += element - count += 1 + checkCountOverflow(++count) } return if (count == 0) Double.NaN else sum / count } @@ -2266,7 +2270,7 @@ public fun Iterable.average(): Double { var count: Int = 0 for (element in this) { sum += element - count += 1 + checkCountOverflow(++count) } return if (count == 0) Double.NaN else sum / count } @@ -2280,7 +2284,7 @@ public fun Iterable.average(): Double { var count: Int = 0 for (element in this) { sum += element - count += 1 + checkCountOverflow(++count) } return if (count == 0) Double.NaN else sum / count } @@ -2294,7 +2298,7 @@ public fun Iterable.average(): Double { var count: Int = 0 for (element in this) { sum += element - count += 1 + checkCountOverflow(++count) } return if (count == 0) Double.NaN else sum / count } @@ -2308,7 +2312,7 @@ public fun Iterable.average(): Double { var count: Int = 0 for (element in this) { sum += element - count += 1 + checkCountOverflow(++count) } return if (count == 0) Double.NaN else sum / count } diff --git a/libraries/stdlib/common/src/generated/_Maps.kt b/libraries/stdlib/common/src/generated/_Maps.kt index 667076e802b..1009ba3ba0c 100644 --- a/libraries/stdlib/common/src/generated/_Maps.kt +++ b/libraries/stdlib/common/src/generated/_Maps.kt @@ -135,7 +135,7 @@ public inline fun Map.count(): Int { public inline fun Map.count(predicate: (Map.Entry) -> Boolean): Int { if (isEmpty()) return 0 var count = 0 - for (element in this) if (predicate(element)) count++ + for (element in this) if (predicate(element)) ++count return count } diff --git a/libraries/stdlib/common/src/generated/_Sequences.kt b/libraries/stdlib/common/src/generated/_Sequences.kt index 1c720363db3..89770b5b7f9 100644 --- a/libraries/stdlib/common/src/generated/_Sequences.kt +++ b/libraries/stdlib/common/src/generated/_Sequences.kt @@ -146,6 +146,7 @@ public inline fun Sequence.firstOrNull(predicate: (T) -> Boolean): T? { public fun <@kotlin.internal.OnlyInputTypes T> Sequence.indexOf(element: T): Int { var index = 0 for (item in this) { + checkIndexOverflow(index) if (element == item) return index index++ @@ -161,6 +162,7 @@ public fun <@kotlin.internal.OnlyInputTypes T> Sequence.indexOf(element: T): public inline fun Sequence.indexOfFirst(predicate: (T) -> Boolean): Int { var index = 0 for (item in this) { + checkIndexOverflow(index) if (predicate(item)) return index index++ @@ -177,6 +179,7 @@ public inline fun Sequence.indexOfLast(predicate: (T) -> Boolean): Int { var lastIndex = -1 var index = 0 for (item in this) { + checkIndexOverflow(index) if (predicate(item)) lastIndex = index index++ @@ -229,6 +232,7 @@ public fun <@kotlin.internal.OnlyInputTypes T> Sequence.lastIndexOf(element: var lastIndex = -1 var index = 0 for (item in this) { + checkIndexOverflow(index) if (element == item) lastIndex = index index++ @@ -852,7 +856,7 @@ public inline fun > Sequence.mapIndex public inline fun > Sequence.mapIndexedTo(destination: C, transform: (index: Int, T) -> R): C { var index = 0 for (item in this) - destination.add(transform(index++, item)) + destination.add(transform(checkIndexOverflow(index++), item)) return destination } @@ -976,7 +980,7 @@ public inline fun Sequence.any(predicate: (T) -> Boolean): Boolean { */ public fun Sequence.count(): Int { var count = 0 - for (element in this) count++ + for (element in this) checkCountOverflow(++count) return count } @@ -987,7 +991,7 @@ public fun Sequence.count(): Int { */ public inline fun Sequence.count(predicate: (T) -> Boolean): Int { var count = 0 - for (element in this) if (predicate(element)) count++ + for (element in this) if (predicate(element)) checkCountOverflow(++count) return count } @@ -1013,7 +1017,7 @@ public inline fun Sequence.fold(initial: R, operation: (acc: R, T) -> public inline fun Sequence.foldIndexed(initial: R, operation: (index: Int, acc: R, T) -> R): R { var index = 0 var accumulator = initial - for (element in this) accumulator = operation(index++, accumulator, element) + for (element in this) accumulator = operation(checkIndexOverflow(index++), accumulator, element) return accumulator } @@ -1035,7 +1039,7 @@ public inline fun Sequence.forEach(action: (T) -> Unit): Unit { */ public inline fun Sequence.forEachIndexed(action: (index: Int, T) -> Unit): Unit { var index = 0 - for (item in this) action(index++, item) + for (item in this) action(checkIndexOverflow(index++), item) } /** @@ -1293,7 +1297,7 @@ public inline fun Sequence.reduceIndexed(operation: (index: Int, a var index = 1 var accumulator: S = iterator.next() while (iterator.hasNext()) { - accumulator = operation(index++, accumulator, iterator.next()) + accumulator = operation(checkIndexOverflow(index++), accumulator, iterator.next()) } return accumulator } @@ -1696,7 +1700,7 @@ public fun Sequence.average(): Double { var count: Int = 0 for (element in this) { sum += element - count += 1 + checkCountOverflow(++count) } return if (count == 0) Double.NaN else sum / count } @@ -1712,7 +1716,7 @@ public fun Sequence.average(): Double { var count: Int = 0 for (element in this) { sum += element - count += 1 + checkCountOverflow(++count) } return if (count == 0) Double.NaN else sum / count } @@ -1728,7 +1732,7 @@ public fun Sequence.average(): Double { var count: Int = 0 for (element in this) { sum += element - count += 1 + checkCountOverflow(++count) } return if (count == 0) Double.NaN else sum / count } @@ -1744,7 +1748,7 @@ public fun Sequence.average(): Double { var count: Int = 0 for (element in this) { sum += element - count += 1 + checkCountOverflow(++count) } return if (count == 0) Double.NaN else sum / count } @@ -1760,7 +1764,7 @@ public fun Sequence.average(): Double { var count: Int = 0 for (element in this) { sum += element - count += 1 + checkCountOverflow(++count) } return if (count == 0) Double.NaN else sum / count } @@ -1776,7 +1780,7 @@ public fun Sequence.average(): Double { var count: Int = 0 for (element in this) { sum += element - count += 1 + checkCountOverflow(++count) } return if (count == 0) Double.NaN else sum / count } diff --git a/libraries/stdlib/common/src/generated/_Strings.kt b/libraries/stdlib/common/src/generated/_Strings.kt index 8e32c35d2c1..c9adef6aa0f 100644 --- a/libraries/stdlib/common/src/generated/_Strings.kt +++ b/libraries/stdlib/common/src/generated/_Strings.kt @@ -883,7 +883,7 @@ public inline fun CharSequence.count(): Int { */ public inline fun CharSequence.count(predicate: (Char) -> Boolean): Int { var count = 0 - for (element in this) if (predicate(element)) count++ + for (element in this) if (predicate(element)) ++count return count } diff --git a/libraries/stdlib/js/src/kotlin/collections.kt b/libraries/stdlib/js/src/kotlin/collections.kt index bfaed4a2194..68cc558bf40 100644 --- a/libraries/stdlib/js/src/kotlin/collections.kt +++ b/libraries/stdlib/js/src/kotlin/collections.kt @@ -6,6 +6,7 @@ package kotlin.collections import kotlin.comparisons.naturalOrder +import kotlin.internal.InlineOnly import kotlin.random.Random /** Returns the array if it's not `null`, or an empty array otherwise. */ @@ -140,3 +141,22 @@ internal actual inline fun Array.copyToArrayOfAny(isVarargs: Boolean) this else this.copyOf() + + + +@PublishedApi +internal actual fun checkIndexOverflow(index: Int): Int { + if (index < 0) { + throwIndexOverflow() + } + return index +} + +@PublishedApi +internal actual fun checkCountOverflow(count: Int): Int { + if (count < 0) { + throwCountOverflow() + } + return count +} + diff --git a/libraries/stdlib/jvm/src/kotlin/collections/CollectionsJVM.kt b/libraries/stdlib/jvm/src/kotlin/collections/CollectionsJVM.kt index 7df929aa279..632f5e770ce 100644 --- a/libraries/stdlib/jvm/src/kotlin/collections/CollectionsJVM.kt +++ b/libraries/stdlib/jvm/src/kotlin/collections/CollectionsJVM.kt @@ -9,6 +9,8 @@ package kotlin.collections import kotlin.* +import kotlin.internal.InlineOnly +import kotlin.internal.apiVersionIsAtLeast /** * Returns an immutable list containing only the specified object [element]. @@ -42,3 +44,31 @@ internal actual fun Array.copyToArrayOfAny(isVarargs: Boolean): Array @Suppress("UNCHECKED_CAST") (this as Array) else java.util.Arrays.copyOf(this, this.size, Array::class.java) + + +@PublishedApi +@SinceKotlin("1.3") +@InlineOnly +internal actual inline fun checkIndexOverflow(index: Int): Int { + if (index < 0) { + if (apiVersionIsAtLeast(1, 3, 0)) + throwIndexOverflow() + else + throw ArithmeticException("Index overflow has happened.") + } + return index +} + +@PublishedApi +@SinceKotlin("1.3") +@InlineOnly +internal actual inline fun checkCountOverflow(count: Int): Int { + if (count < 0) { + if (apiVersionIsAtLeast(1, 3, 0)) + throwCountOverflow() + else + throw ArithmeticException("Count overflow has happened.") + } + return count +} + diff --git a/libraries/stdlib/jvm/test/collections/IndexOverflowJVMTest.kt b/libraries/stdlib/jvm/test/collections/IndexOverflowJVMTest.kt index 94807b9826d..cd1eab7542d 100644 --- a/libraries/stdlib/jvm/test/collections/IndexOverflowJVMTest.kt +++ b/libraries/stdlib/jvm/test/collections/IndexOverflowJVMTest.kt @@ -37,8 +37,114 @@ class IndexOverflowJVMTest { override fun next(): Long = counter++ } } + + fun assertIndexOverflow(f: () -> Unit) { + val ex = assertFailsWith(block = f) + assertTrue(ex.message!!.contains("index", ignoreCase = true)) + } + + fun assertCountOverflow(f: () -> Unit) { + val ex = assertFailsWith(block = f) + assertTrue(ex.message!!.contains("count", ignoreCase = true)) + } + + fun checkIndexPositive(index: Int) { + if (index < 0) fail("Encountered negative index") + } } + @Test + fun indexOfOverflowSequence() { + assertIndexOverflow { maxIndexSequence.indexOf("j") } + assertIndexOverflow { maxIndexSequence.lastIndexOf("k") } + assertIndexOverflow { maxIndexSequence.indexOfFirst { false } } + assertIndexOverflow { maxIndexSequence.indexOfLast { false } } + } + + @Test + fun indexOfOverflowIterable() { + assertIndexOverflow { maxIndexIterable.indexOf("j") } + assertIndexOverflow { maxIndexIterable.lastIndexOf("k") } + assertIndexOverflow { maxIndexIterable.indexOfFirst { false } } + assertIndexOverflow { maxIndexIterable.indexOfLast { false } } + } + + + @Test + fun forEachIndexedOverflow() { + assertIndexOverflow { maxIndexSequence.forEachIndexed { index, _ -> checkIndexPositive(index) } } + assertIndexOverflow { maxIndexIterable.forEachIndexed { index, _ -> checkIndexPositive(index) } } + } + + @Test + fun withIndexOverflow() { + assertIndexOverflow { maxIndexSequence.withIndex().forEach { (index, _) -> checkIndexPositive(index) } } + assertIndexOverflow { maxIndexIterable.withIndex().forEach { (index, _) -> checkIndexPositive(index) } } + } + + + @Test + fun countOverflow() { + assertCountOverflow { repeatCounted("k").count() } + assertCountOverflow { repeatCounted("k").count { true } } + assertCountOverflow { repeatCounted("k").asIterable().count() } + assertCountOverflow { repeatCounted("k").asIterable().count { true } } + } + + @Test + fun averageCountOverflow() { + assertCountOverflow { repeatCounted(1.0).average() } + assertCountOverflow { repeatCounted(1L).asIterable().average() } + } + + + private class CountingCollection : AbstractMutableCollection() { + private var _size = 0 + + override fun add(element: T): Boolean { + if (_size < 0) error("Collection is too long") + _size++ + return true + } + + override val size: Int get() = _size + + override fun iterator(): MutableIterator = error("not implemented") + } + + @Test + fun mapIndexedOverflow() { + assertIndexOverflow { maxIndexSequence.mapIndexed { index, _ -> checkIndexPositive(index) }.forEach { } } + assertIndexOverflow { maxIndexSequence.mapIndexedTo(CountingCollection()) { index, _ -> checkIndexPositive(index) } } + assertIndexOverflow { maxIndexIterable.mapIndexedTo(CountingCollection()) { index, _ -> checkIndexPositive(index) } } + } + + @Test + fun mapNotNullIndexedOverflow() { + assertIndexOverflow { maxIndexSequence.mapIndexedNotNull { index, _ -> checkIndexPositive(index) }.forEach { } } + assertIndexOverflow { maxIndexSequence.mapIndexedNotNullTo(CountingCollection()) { index, _ -> checkIndexPositive(index) } } + assertIndexOverflow { maxIndexIterable.mapIndexedNotNullTo(CountingCollection()) { index, _ -> checkIndexPositive(index) } } + } + + @Test + fun filterIndexedOverflow() { + assertIndexOverflow { maxIndexSequence.filterIndexed { index, _ -> checkIndexPositive(index); true }.forEach { } } + assertIndexOverflow { maxIndexSequence.filterIndexedTo(CountingCollection()) { index, _ -> checkIndexPositive(index); true } } + assertIndexOverflow { maxIndexIterable.filterIndexedTo(CountingCollection()) { index, _ -> checkIndexPositive(index); true } } + } + + + @Test + fun foldIndexedOverflow() { + assertIndexOverflow { maxIndexSequence.foldIndexed("") { index, acc, s -> checkIndexPositive(index); s } } + assertIndexOverflow { maxIndexIterable.foldIndexed("") { index, acc, s -> checkIndexPositive(index); s } } + } + + @Test + fun reduceIndexedOverflow() { + assertIndexOverflow { maxIndexSequence.reduceIndexed { index, acc, s -> checkIndexPositive(index); s } } + assertIndexOverflow { maxIndexIterable.reduceIndexed { index, acc, s -> checkIndexPositive(index); s } } + } @Test diff --git a/libraries/stdlib/src/kotlin/collections/Collections.kt b/libraries/stdlib/src/kotlin/collections/Collections.kt index 7579ad77d3f..e21d0a18458 100644 --- a/libraries/stdlib/src/kotlin/collections/Collections.kt +++ b/libraries/stdlib/src/kotlin/collections/Collections.kt @@ -364,3 +364,20 @@ private fun rangeCheck(size: Int, fromIndex: Int, toIndex: Int) { } +@PublishedApi +@SinceKotlin("1.3") +internal expect fun checkIndexOverflow(index: Int): Int + +@PublishedApi +@SinceKotlin("1.3") +internal expect fun checkCountOverflow(count: Int): Int + + +@PublishedApi +@SinceKotlin("1.3") +internal fun throwIndexOverflow() { throw ArithmeticException("Index overflow has happened.") } + +@PublishedApi +@SinceKotlin("1.3") +internal fun throwCountOverflow() { throw ArithmeticException("Count overflow has happened.") } + diff --git a/libraries/stdlib/src/kotlin/collections/Iterators.kt b/libraries/stdlib/src/kotlin/collections/Iterators.kt index 7455ee2e959..5b08fb95137 100644 --- a/libraries/stdlib/src/kotlin/collections/Iterators.kt +++ b/libraries/stdlib/src/kotlin/collections/Iterators.kt @@ -37,5 +37,5 @@ public inline fun Iterator.forEach(operation: (T) -> Unit): Unit { internal class IndexingIterator(private val iterator: Iterator) : Iterator> { private var index = 0 final override fun hasNext(): Boolean = iterator.hasNext() - final override fun next(): IndexedValue = IndexedValue(index++, iterator.next()) + final override fun next(): IndexedValue = IndexedValue(checkIndexOverflow(index++), iterator.next()) } diff --git a/libraries/stdlib/src/kotlin/collections/Sequences.kt b/libraries/stdlib/src/kotlin/collections/Sequences.kt index f23d0d64bfa..7430d3ff881 100644 --- a/libraries/stdlib/src/kotlin/collections/Sequences.kt +++ b/libraries/stdlib/src/kotlin/collections/Sequences.kt @@ -178,7 +178,7 @@ constructor(private val sequence: Sequence, private val transformer: (Int, T) val iterator = sequence.iterator() var index = 0 override fun next(): R { - return transformer(index++, iterator.next()) + return transformer(checkIndexOverflow(index++), iterator.next()) } override fun hasNext(): Boolean { @@ -197,7 +197,7 @@ constructor(private val sequence: Sequence) : Sequence> { val iterator = sequence.iterator() var index = 0 override fun next(): IndexedValue { - return IndexedValue(index++, iterator.next()) + return IndexedValue(checkIndexOverflow(index++), iterator.next()) } override fun hasNext(): Boolean { diff --git a/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib-runtime-merged.txt b/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib-runtime-merged.txt index 76a6ec47493..73b60dcee25 100644 --- a/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib-runtime-merged.txt +++ b/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib-runtime-merged.txt @@ -2263,6 +2263,8 @@ public final class kotlin/collections/CollectionsKt { public static final fun takeLast (Ljava/util/List;I)Ljava/util/List; public static final fun takeLastWhile (Ljava/util/List;Lkotlin/jvm/functions/Function1;)Ljava/util/List; public static final fun takeWhile (Ljava/lang/Iterable;Lkotlin/jvm/functions/Function1;)Ljava/util/List; + public static final fun throwCountOverflow ()V + public static final fun throwIndexOverflow ()V public static final fun toBooleanArray (Ljava/util/Collection;)[Z public static final fun toByteArray (Ljava/util/Collection;)[B public static final fun toCharArray (Ljava/util/Collection;)[C diff --git a/libraries/tools/kotlin-stdlib-gen/src/templates/Aggregates.kt b/libraries/tools/kotlin-stdlib-gen/src/templates/Aggregates.kt index 7ecaa483047..2343ccaa862 100644 --- a/libraries/tools/kotlin-stdlib-gen/src/templates/Aggregates.kt +++ b/libraries/tools/kotlin-stdlib-gen/src/templates/Aggregates.kt @@ -155,6 +155,7 @@ object Aggregates : TemplateGroupBase() { doc { "Returns the number of ${f.element.pluralize()} matching the given [predicate]." } returns("Int") body { + fun checkOverflow(value: String) = if (f == Sequences || f == Iterables) "checkCountOverflow($value)" else value """ ${when (f) { Iterables -> "if (this is Collection && isEmpty()) return 0" @@ -162,7 +163,7 @@ object Aggregates : TemplateGroupBase() { else -> "" }} var count = 0 - for (element in this) if (predicate(element)) count++ + for (element in this) if (predicate(element)) ${checkOverflow("++count")} return count """ } @@ -175,10 +176,11 @@ object Aggregates : TemplateGroupBase() { doc { "Returns the number of ${f.element.pluralize()} in this ${f.collection}." } returns("Int") body { + fun checkOverflow(value: String) = if (f == Sequences || f == Iterables) "checkCountOverflow($value)" else value """ ${if (f == Iterables) "if (this is Collection) return size" else ""} var count = 0 - for (element in this) count++ + for (element in this) ${checkOverflow("++count")} return count """ } @@ -474,10 +476,11 @@ object Aggregates : TemplateGroupBase() { typeParam("R") returns("R") body { + fun checkOverflow(value: String) = if (f == Sequences || f == Iterables) "checkIndexOverflow($value)" else value """ var index = 0 var accumulator = initial - for (element in this) accumulator = operation(index++, accumulator, element) + for (element in this) accumulator = operation(${checkOverflow("index++")}, accumulator, element) return accumulator """ } @@ -617,6 +620,7 @@ object Aggregates : TemplateGroupBase() { typeParam("T : S") returns("S") body { + fun checkOverflow(value: String) = if (f == Sequences || f == Iterables) "checkIndexOverflow($value)" else value """ val iterator = this.iterator() if (!iterator.hasNext()) throw UnsupportedOperationException("Empty ${f.doc.collection} can't be reduced.") @@ -624,7 +628,7 @@ object Aggregates : TemplateGroupBase() { var index = 1 var accumulator: S = iterator.next() while (iterator.hasNext()) { - accumulator = operation(index++, accumulator, iterator.next()) + accumulator = operation(${checkOverflow("index++")}, accumulator, iterator.next()) } return accumulator """ @@ -899,9 +903,10 @@ object Aggregates : TemplateGroupBase() { """ } returns("Unit") body { + fun checkOverflow(value: String) = if (f == Sequences || f == Iterables) "checkIndexOverflow($value)" else value """ var index = 0 - for (item in this) action(index++, item) + for (item in this) action(${checkOverflow("index++")}, item) """ } } diff --git a/libraries/tools/kotlin-stdlib-gen/src/templates/Elements.kt b/libraries/tools/kotlin-stdlib-gen/src/templates/Elements.kt index a7b39a9e986..7df89e5301f 100644 --- a/libraries/tools/kotlin-stdlib-gen/src/templates/Elements.kt +++ b/libraries/tools/kotlin-stdlib-gen/src/templates/Elements.kt @@ -52,6 +52,7 @@ object Elements : TemplateGroupBase() { ${if (f == Iterables) "if (this is List) return this.indexOf(element)" else ""} var index = 0 for (item in this) { + checkIndexOverflow(index) if (element == item) return index index++ @@ -106,6 +107,7 @@ object Elements : TemplateGroupBase() { var lastIndex = -1 var index = 0 for (item in this) { + checkIndexOverflow(index) if (element == item) lastIndex = index index++ @@ -157,12 +159,13 @@ object Elements : TemplateGroupBase() { """ var index = 0 for (item in this) { + ${if (f != Lists) "checkIndexOverflow(index)" else ""} if (predicate(item)) return index index++ } return -1 - """ + """.lines().filterNot { it.isBlank() }.joinToString("\n") } body(CharSequences, ArraysOfPrimitives, ArraysOfObjects) { @@ -190,6 +193,7 @@ object Elements : TemplateGroupBase() { var lastIndex = -1 var index = 0 for (item in this) { + checkIndexOverflow(index) if (predicate(item)) lastIndex = index index++ diff --git a/libraries/tools/kotlin-stdlib-gen/src/templates/Mapping.kt b/libraries/tools/kotlin-stdlib-gen/src/templates/Mapping.kt index 16dbd83b167..42ebd520bad 100644 --- a/libraries/tools/kotlin-stdlib-gen/src/templates/Mapping.kt +++ b/libraries/tools/kotlin-stdlib-gen/src/templates/Mapping.kt @@ -204,10 +204,11 @@ object Mapping : TemplateGroupBase() { returns("C") body { + fun checkOverflow(value: String) = if (f == Sequences || f == Iterables) "checkIndexOverflow($value)" else value """ var index = 0 for (item in this) - destination.add(transform(index++, item)) + destination.add(transform(${checkOverflow("index++")}, item)) return destination """ } diff --git a/libraries/tools/kotlin-stdlib-gen/src/templates/Numeric.kt b/libraries/tools/kotlin-stdlib-gen/src/templates/Numeric.kt index e5ccba77372..16e5c040679 100644 --- a/libraries/tools/kotlin-stdlib-gen/src/templates/Numeric.kt +++ b/libraries/tools/kotlin-stdlib-gen/src/templates/Numeric.kt @@ -41,12 +41,13 @@ object Numeric : TemplateGroupBase() { returns("Double") platformName("averageOf") body { + fun checkOverflow(value: String) = if (f == Family.Sequences || f == Family.Iterables) "checkCountOverflow($value)" else value """ var sum: Double = 0.0 var count: Int = 0 for (element in this) { sum += element - count += 1 + ${checkOverflow("++count")} } return if (count == 0) Double.NaN else sum / count """