From 4a5d8534c1f17f0a68f762537f0273cb244a2135 Mon Sep 17 00:00:00 2001 From: Ilya Gorbunov Date: Mon, 17 Apr 2017 23:49:43 +0300 Subject: [PATCH] Fix array iterators to adhere to Iterator contract #KT-17453 Fixed Change expected exceptions in tests. --- .../codegen/box/arrays/primitiveArrays.kt | 16 +++++++-------- .../src/kotlin/jvm/internal/ArrayIterator.kt | 2 +- .../src/kotlin/jvm/internal/ArrayIterators.kt | 16 +++++++-------- .../generators/builtins/arrayIterators.kt | 2 +- js/js.libraries/src/core/builtins.kt | 18 ++++++++--------- .../stdlib/test/collections/ArraysTest.kt | 20 ++++++++++++++++++- 6 files changed, 46 insertions(+), 28 deletions(-) diff --git a/compiler/testData/codegen/box/arrays/primitiveArrays.kt b/compiler/testData/codegen/box/arrays/primitiveArrays.kt index 769610cb168..0948cacd472 100644 --- a/compiler/testData/codegen/box/arrays/primitiveArrays.kt +++ b/compiler/testData/codegen/box/arrays/primitiveArrays.kt @@ -19,7 +19,7 @@ fun box(): String { assertEquals(true, booleanArrayOf(true).iterator().nextBoolean()) assertEquals(true, booleanArrayOf(true).iterator().next()) assertFalse(booleanArrayOf().iterator().hasNext()) - assertTrue(assertFails { booleanArrayOf().iterator().next() } is IndexOutOfBoundsException) + assertTrue(assertFails { booleanArrayOf().iterator().next() } is NoSuchElementException) assertTrue(eqByte(byteArrayOf(0), ByteArray(1))) assertTrue(eqByte(byteArrayOf(1, 2, 3), ByteArray(3) { (it + 1).toByte() })) @@ -34,7 +34,7 @@ fun box(): String { assertEquals(1, byteArrayOf(1).iterator().nextByte()) assertEquals(1, byteArrayOf(1).iterator().next()) assertFalse(byteArrayOf().iterator().hasNext()) - assertTrue(assertFails { byteArrayOf().iterator().next() } is IndexOutOfBoundsException) + assertTrue(assertFails { byteArrayOf().iterator().next() } is NoSuchElementException) assertTrue(eqShort(shortArrayOf(0), ShortArray(1))) assertTrue(eqShort(shortArrayOf(1, 2, 3), ShortArray(3) { (it + 1).toShort() })) @@ -49,7 +49,7 @@ fun box(): String { assertEquals(1, shortArrayOf(1).iterator().nextShort()) assertEquals(1, shortArrayOf(1).iterator().next()) assertFalse(shortArrayOf().iterator().hasNext()) - assertTrue(assertFails { shortArrayOf().iterator().next() } is IndexOutOfBoundsException) + assertTrue(assertFails { shortArrayOf().iterator().next() } is NoSuchElementException) assertTrue(eqChar(charArrayOf(0.toChar()), CharArray(1))) assertTrue(eqChar(charArrayOf('a', 'b', 'c'), CharArray(3) { 'a' + it })) @@ -64,7 +64,7 @@ fun box(): String { assertEquals('a', charArrayOf('a').iterator().nextChar()) assertEquals('a', charArrayOf('a').iterator().next()) assertFalse(charArrayOf().iterator().hasNext()) - assertTrue(assertFails { charArrayOf().iterator().next() } is IndexOutOfBoundsException) + assertTrue(assertFails { charArrayOf().iterator().next() } is NoSuchElementException) assertTrue(eqInt(intArrayOf(0), IntArray(1))) assertTrue(eqInt(intArrayOf(1, 2, 3), IntArray(3) { it + 1 })) @@ -79,7 +79,7 @@ fun box(): String { assertEquals(1, intArrayOf(1).iterator().nextInt()) assertEquals(1, intArrayOf(1).iterator().next()) assertFalse(intArrayOf().iterator().hasNext()) - assertTrue(assertFails { intArrayOf().iterator().next() } is IndexOutOfBoundsException) + assertTrue(assertFails { intArrayOf().iterator().next() } is NoSuchElementException) assertTrue(eqFloat(floatArrayOf(0f), FloatArray(1))) assertTrue(eqFloat(floatArrayOf(1f, 2f, 3f), FloatArray(3) { (it + 1).toFloat() })) @@ -94,7 +94,7 @@ fun box(): String { assertEquals(1f, floatArrayOf(1f).iterator().nextFloat()) assertEquals(1f, floatArrayOf(1f).iterator().next()) assertFalse(floatArrayOf().iterator().hasNext()) - assertTrue(assertFails { floatArrayOf().iterator().next() } is IndexOutOfBoundsException) + assertTrue(assertFails { floatArrayOf().iterator().next() } is NoSuchElementException) assertTrue(eqDouble(doubleArrayOf(0.0), DoubleArray(1))) assertTrue(eqDouble(doubleArrayOf(1.0, 2.0, 3.0), DoubleArray(3) { (it + 1).toDouble() })) @@ -109,7 +109,7 @@ fun box(): String { assertEquals(1.0, doubleArrayOf(1.0).iterator().nextDouble()) assertEquals(1.0, doubleArrayOf(1.0).iterator().next()) assertFalse(doubleArrayOf().iterator().hasNext()) - assertTrue(assertFails { doubleArrayOf().iterator().next() } is IndexOutOfBoundsException) + assertTrue(assertFails { doubleArrayOf().iterator().next() } is NoSuchElementException) assertTrue(eqLong(longArrayOf(0), LongArray(1))) assertTrue(eqLong(longArrayOf(1, 2, 3), LongArray(3) { it + 1L })) @@ -124,7 +124,7 @@ fun box(): String { assertEquals(1L, longArrayOf(1).iterator().nextLong()) assertEquals(1L, longArrayOf(1).iterator().next()) assertFalse(longArrayOf().iterator().hasNext()) - assertTrue(assertFails { longArrayOf().iterator().next() } is IndexOutOfBoundsException) + assertTrue(assertFails { longArrayOf().iterator().next() } is NoSuchElementException) // If `is` checks work... if (intArrayOf() is IntArray) { diff --git a/core/runtime.jvm/src/kotlin/jvm/internal/ArrayIterator.kt b/core/runtime.jvm/src/kotlin/jvm/internal/ArrayIterator.kt index dc057af2ef9..110057a4856 100644 --- a/core/runtime.jvm/src/kotlin/jvm/internal/ArrayIterator.kt +++ b/core/runtime.jvm/src/kotlin/jvm/internal/ArrayIterator.kt @@ -19,7 +19,7 @@ package kotlin.jvm.internal private class ArrayIterator(val array: Array) : Iterator { private var index = 0 override fun hasNext() = index < array.size - override fun next() = array[index++] + override fun next() = try { array[index++] } catch (e: ArrayIndexOutOfBoundsException) { throw NoSuchElementException(e.message) } } public fun iterator(array: Array): Iterator = ArrayIterator(array) diff --git a/core/runtime.jvm/src/kotlin/jvm/internal/ArrayIterators.kt b/core/runtime.jvm/src/kotlin/jvm/internal/ArrayIterators.kt index 2813f38e28d..30b209ae958 100644 --- a/core/runtime.jvm/src/kotlin/jvm/internal/ArrayIterators.kt +++ b/core/runtime.jvm/src/kotlin/jvm/internal/ArrayIterators.kt @@ -21,49 +21,49 @@ package kotlin.jvm.internal private class ArrayByteIterator(private val array: ByteArray) : ByteIterator() { private var index = 0 override fun hasNext() = index < array.size - override fun nextByte() = array[index++] + override fun nextByte() = try { array[index++] } catch (e: ArrayIndexOutOfBoundsException) { throw NoSuchElementException(e.message) } } private class ArrayCharIterator(private val array: CharArray) : CharIterator() { private var index = 0 override fun hasNext() = index < array.size - override fun nextChar() = array[index++] + override fun nextChar() = try { array[index++] } catch (e: ArrayIndexOutOfBoundsException) { throw NoSuchElementException(e.message) } } private class ArrayShortIterator(private val array: ShortArray) : ShortIterator() { private var index = 0 override fun hasNext() = index < array.size - override fun nextShort() = array[index++] + override fun nextShort() = try { array[index++] } catch (e: ArrayIndexOutOfBoundsException) { throw NoSuchElementException(e.message) } } private class ArrayIntIterator(private val array: IntArray) : IntIterator() { private var index = 0 override fun hasNext() = index < array.size - override fun nextInt() = array[index++] + override fun nextInt() = try { array[index++] } catch (e: ArrayIndexOutOfBoundsException) { throw NoSuchElementException(e.message) } } private class ArrayLongIterator(private val array: LongArray) : LongIterator() { private var index = 0 override fun hasNext() = index < array.size - override fun nextLong() = array[index++] + override fun nextLong() = try { array[index++] } catch (e: ArrayIndexOutOfBoundsException) { throw NoSuchElementException(e.message) } } private class ArrayFloatIterator(private val array: FloatArray) : FloatIterator() { private var index = 0 override fun hasNext() = index < array.size - override fun nextFloat() = array[index++] + override fun nextFloat() = try { array[index++] } catch (e: ArrayIndexOutOfBoundsException) { throw NoSuchElementException(e.message) } } private class ArrayDoubleIterator(private val array: DoubleArray) : DoubleIterator() { private var index = 0 override fun hasNext() = index < array.size - override fun nextDouble() = array[index++] + override fun nextDouble() = try { array[index++] } catch (e: ArrayIndexOutOfBoundsException) { throw NoSuchElementException(e.message) } } private class ArrayBooleanIterator(private val array: BooleanArray) : BooleanIterator() { private var index = 0 override fun hasNext() = index < array.size - override fun nextBoolean() = array[index++] + override fun nextBoolean() = try { array[index++] } catch (e: ArrayIndexOutOfBoundsException) { throw NoSuchElementException(e.message) } } public fun iterator(array: ByteArray): ByteIterator = ArrayByteIterator(array) diff --git a/generators/src/org/jetbrains/kotlin/generators/builtins/arrayIterators.kt b/generators/src/org/jetbrains/kotlin/generators/builtins/arrayIterators.kt index 9b310072ca8..f91d49e5fce 100644 --- a/generators/src/org/jetbrains/kotlin/generators/builtins/arrayIterators.kt +++ b/generators/src/org/jetbrains/kotlin/generators/builtins/arrayIterators.kt @@ -29,7 +29,7 @@ class GenerateArrayIterators(out: PrintWriter) : BuiltInsSourceGenerator(out) { out.println("private class Array${s}Iterator(private val array: ${s}Array) : ${s}Iterator() {") out.println(" private var index = 0") out.println(" override fun hasNext() = index < array.size") - out.println(" override fun next$s() = array[index++]") + out.println(" override fun next$s() = try { array[index++] } catch (e: ArrayIndexOutOfBoundsException) { throw NoSuchElementException(e.message) }") out.println("}") out.println() } diff --git a/js/js.libraries/src/core/builtins.kt b/js/js.libraries/src/core/builtins.kt index fee6909283c..5d925b86c96 100644 --- a/js/js.libraries/src/core/builtins.kt +++ b/js/js.libraries/src/core/builtins.kt @@ -21,7 +21,7 @@ internal fun arrayIterator(array: dynamic, type: String?) = when (type) { object : Iterator { var index = 0 override fun hasNext() = index < arr.size - override fun next() = if (index < arr.size) arr[index++] else throw IndexOutOfBoundsException("$index") + override fun next() = if (index < arr.size) arr[index++] else throw NoSuchElementException("$index") } } "BooleanArray" -> booleanArrayIterator(array) @@ -39,56 +39,56 @@ internal fun arrayIterator(array: dynamic, type: String?) = when (type) { internal fun booleanArrayIterator(array: BooleanArray) = object : BooleanIterator() { var index = 0 override fun hasNext() = index < array.size - override fun nextBoolean() = if (index < array.size) array[index++] else throw IndexOutOfBoundsException("$index") + override fun nextBoolean() = if (index < array.size) array[index++] else throw NoSuchElementException("$index") } @JsName("byteArrayIterator") internal fun byteArrayIterator(array: ByteArray) = object : ByteIterator() { var index = 0 override fun hasNext() = index < array.size - override fun nextByte() = if (index < array.size) array[index++] else throw IndexOutOfBoundsException("$index") + override fun nextByte() = if (index < array.size) array[index++] else throw NoSuchElementException("$index") } @JsName("shortArrayIterator") internal fun shortArrayIterator(array: ShortArray) = object : ShortIterator() { var index = 0 override fun hasNext() = index < array.size - override fun nextShort() = if (index < array.size) array[index++] else throw IndexOutOfBoundsException("$index") + override fun nextShort() = if (index < array.size) array[index++] else throw NoSuchElementException("$index") } @JsName("charArrayIterator") internal fun charArrayIterator(array: CharArray) = object : CharIterator() { var index = 0 override fun hasNext() = index < array.size - override fun nextChar() = if (index < array.size) array[index++] else throw IndexOutOfBoundsException("$index") + override fun nextChar() = if (index < array.size) array[index++] else throw NoSuchElementException("$index") } @JsName("intArrayIterator") internal fun intArrayIterator(array: IntArray) = object : IntIterator() { var index = 0 override fun hasNext() = index < array.size - override fun nextInt() = if (index < array.size) array[index++] else throw IndexOutOfBoundsException("$index") + override fun nextInt() = if (index < array.size) array[index++] else throw NoSuchElementException("$index") } @JsName("floatArrayIterator") internal fun floatArrayIterator(array: FloatArray) = object : FloatIterator() { var index = 0 override fun hasNext() = index < array.size - override fun nextFloat() = if (index < array.size) array[index++] else throw IndexOutOfBoundsException("$index") + override fun nextFloat() = if (index < array.size) array[index++] else throw NoSuchElementException("$index") } @JsName("doubleArrayIterator") internal fun doubleArrayIterator(array: DoubleArray) = object : DoubleIterator() { var index = 0 override fun hasNext() = index < array.size - override fun nextDouble() = if (index < array.size) array[index++] else throw IndexOutOfBoundsException("$index") + override fun nextDouble() = if (index < array.size) array[index++] else throw NoSuchElementException("$index") } @JsName("longArrayIterator") internal fun longArrayIterator(array: LongArray) = object : LongIterator() { var index = 0 override fun hasNext() = index < array.size - override fun nextLong() = if (index < array.size) array[index++] else throw IndexOutOfBoundsException("$index") + override fun nextLong() = if (index < array.size) array[index++] else throw NoSuchElementException("$index") } @JsName("PropertyMetadata") diff --git a/libraries/stdlib/test/collections/ArraysTest.kt b/libraries/stdlib/test/collections/ArraysTest.kt index 6e8fbe907d1..f04d4867af3 100644 --- a/libraries/stdlib/test/collections/ArraysTest.kt +++ b/libraries/stdlib/test/collections/ArraysTest.kt @@ -16,7 +16,7 @@ package test.collections -import test.collections.behaviors.listBehavior +import test.collections.behaviors.* import test.comparisons.STRING_CASE_INSENSITIVE_ORDER import kotlin.test.* import org.junit.Test @@ -636,6 +636,24 @@ class ArraysTest { assertArrayNotSameButEquals(booleanArrayOf(true, false, true), booleanArrayOf(true, false, true, true).sliceArray(coll)) } + @Test fun iterators() { + fun checkContract(array: T, toList: T.() -> List, iterator: T.() -> Iterator) = + compare(array.toList().iterator(), array.iterator()) { + iteratorBehavior() + } + + checkContract(arrayOf("a", "b", "c"), { toList() }, { iterator() }) + checkContract(intArrayOf(), { toList() }, { iterator() }) + checkContract(intArrayOf(1, 2, 3), { toList() }, { iterator() }) + checkContract(shortArrayOf(1, 2, 3), { toList() }, { iterator() }) + checkContract(byteArrayOf(1, 2, 3), { toList() }, { iterator() }) + checkContract(longArrayOf(1L, 2L, 3L), { toList() }, { iterator() }) + checkContract(doubleArrayOf(2.0, 3.0, 9.0), { toList() }, { iterator() }) + checkContract(floatArrayOf(2f, 3f, 9f), { toList() }, { iterator() }) + checkContract(charArrayOf('a', 'b', 'c'), { toList() }, { iterator() }) + checkContract(booleanArrayOf(true, false), { toList() }, { iterator() }) + } + @Test fun asIterable() { val arr1 = intArrayOf(1, 2, 3, 4, 5) val iter1 = arr1.asIterable()