Improve argument validation in copyOfRange

Make copyOfRange implementation non-inline in order not to expose
copyOfRangeToIndexCheck implementation detail.

It will be possible to make the function non-inline itself later without
that JvmName trick, when apiVersion 1.2 support is discontinued.

#KT-19489
This commit is contained in:
Ilya Gorbunov
2018-08-04 22:07:06 +03:00
parent 58a3b64baf
commit 85af1f5d38
8 changed files with 355 additions and 51 deletions
@@ -5737,48 +5737,75 @@ public expect fun CharArray.copyOf(newSize: Int): CharArray
public expect fun <T> Array<T>.copyOf(newSize: Int): Array<T?>
/**
* Returns new array which is a copy of range of original array.
* Returns a new array which is a copy of the specified range of the original array.
*
* @param fromIndex the start of the range (inclusive), must be in `0..array.size`
* @param toIndex the end of the range (exclusive), must be in `fromIndex..array.size`
*/
@Suppress("NO_ACTUAL_FOR_EXPECT")
public expect fun <T> Array<T>.copyOfRange(fromIndex: Int, toIndex: Int): Array<T>
/**
* Returns new array which is a copy of range of original array.
* Returns a new array which is a copy of the specified range of the original array.
*
* @param fromIndex the start of the range (inclusive), must be in `0..array.size`
* @param toIndex the end of the range (exclusive), must be in `fromIndex..array.size`
*/
public expect fun ByteArray.copyOfRange(fromIndex: Int, toIndex: Int): ByteArray
/**
* Returns new array which is a copy of range of original array.
* Returns a new array which is a copy of the specified range of the original array.
*
* @param fromIndex the start of the range (inclusive), must be in `0..array.size`
* @param toIndex the end of the range (exclusive), must be in `fromIndex..array.size`
*/
public expect fun ShortArray.copyOfRange(fromIndex: Int, toIndex: Int): ShortArray
/**
* Returns new array which is a copy of range of original array.
* Returns a new array which is a copy of the specified range of the original array.
*
* @param fromIndex the start of the range (inclusive), must be in `0..array.size`
* @param toIndex the end of the range (exclusive), must be in `fromIndex..array.size`
*/
public expect fun IntArray.copyOfRange(fromIndex: Int, toIndex: Int): IntArray
/**
* Returns new array which is a copy of range of original array.
* Returns a new array which is a copy of the specified range of the original array.
*
* @param fromIndex the start of the range (inclusive), must be in `0..array.size`
* @param toIndex the end of the range (exclusive), must be in `fromIndex..array.size`
*/
public expect fun LongArray.copyOfRange(fromIndex: Int, toIndex: Int): LongArray
/**
* Returns new array which is a copy of range of original array.
* Returns a new array which is a copy of the specified range of the original array.
*
* @param fromIndex the start of the range (inclusive), must be in `0..array.size`
* @param toIndex the end of the range (exclusive), must be in `fromIndex..array.size`
*/
public expect fun FloatArray.copyOfRange(fromIndex: Int, toIndex: Int): FloatArray
/**
* Returns new array which is a copy of range of original array.
* Returns a new array which is a copy of the specified range of the original array.
*
* @param fromIndex the start of the range (inclusive), must be in `0..array.size`
* @param toIndex the end of the range (exclusive), must be in `fromIndex..array.size`
*/
public expect fun DoubleArray.copyOfRange(fromIndex: Int, toIndex: Int): DoubleArray
/**
* Returns new array which is a copy of range of original array.
* Returns a new array which is a copy of the specified range of the original array.
*
* @param fromIndex the start of the range (inclusive), must be in `0..array.size`
* @param toIndex the end of the range (exclusive), must be in `fromIndex..array.size`
*/
public expect fun BooleanArray.copyOfRange(fromIndex: Int, toIndex: Int): BooleanArray
/**
* Returns new array which is a copy of range of original array.
* Returns a new array which is a copy of the specified range of the original array.
*
* @param fromIndex the start of the range (inclusive), must be in `0..array.size`
* @param toIndex the end of the range (exclusive), must be in `fromIndex..array.size`
*/
public expect fun CharArray.copyOfRange(fromIndex: Int, toIndex: Int): CharArray
+52 -21
View File
@@ -622,71 +622,102 @@ public actual fun <T> Array<out T>.copyOf(newSize: Int): Array<T?> {
}
/**
* Returns new array which is a copy of range of original array.
* Returns a new array which is a copy of the specified range of the original array.
*
* @param fromIndex the start of the range (inclusive), must be in `0..array.size`
* @param toIndex the end of the range (exclusive), must be in `fromIndex..array.size`
*/
@Suppress("ACTUAL_WITHOUT_EXPECT", "NOTHING_TO_INLINE")
public actual inline fun <T> Array<out T>.copyOfRange(fromIndex: Int, toIndex: Int): Array<T> {
@Suppress("ACTUAL_WITHOUT_EXPECT")
public actual fun <T> Array<out T>.copyOfRange(fromIndex: Int, toIndex: Int): Array<T> {
AbstractList.checkRangeIndexes(fromIndex, toIndex, size)
return this.asDynamic().slice(fromIndex, toIndex)
}
/**
* Returns new array which is a copy of range of original array.
* Returns a new array which is a copy of the specified range of the original array.
*
* @param fromIndex the start of the range (inclusive), must be in `0..array.size`
* @param toIndex the end of the range (exclusive), must be in `fromIndex..array.size`
*/
@Suppress("NOTHING_TO_INLINE")
public actual inline fun ByteArray.copyOfRange(fromIndex: Int, toIndex: Int): ByteArray {
public actual fun ByteArray.copyOfRange(fromIndex: Int, toIndex: Int): ByteArray {
AbstractList.checkRangeIndexes(fromIndex, toIndex, size)
return this.asDynamic().slice(fromIndex, toIndex)
}
/**
* Returns new array which is a copy of range of original array.
* Returns a new array which is a copy of the specified range of the original array.
*
* @param fromIndex the start of the range (inclusive), must be in `0..array.size`
* @param toIndex the end of the range (exclusive), must be in `fromIndex..array.size`
*/
@Suppress("NOTHING_TO_INLINE")
public actual inline fun ShortArray.copyOfRange(fromIndex: Int, toIndex: Int): ShortArray {
public actual fun ShortArray.copyOfRange(fromIndex: Int, toIndex: Int): ShortArray {
AbstractList.checkRangeIndexes(fromIndex, toIndex, size)
return this.asDynamic().slice(fromIndex, toIndex)
}
/**
* Returns new array which is a copy of range of original array.
* Returns a new array which is a copy of the specified range of the original array.
*
* @param fromIndex the start of the range (inclusive), must be in `0..array.size`
* @param toIndex the end of the range (exclusive), must be in `fromIndex..array.size`
*/
@Suppress("NOTHING_TO_INLINE")
public actual inline fun IntArray.copyOfRange(fromIndex: Int, toIndex: Int): IntArray {
public actual fun IntArray.copyOfRange(fromIndex: Int, toIndex: Int): IntArray {
AbstractList.checkRangeIndexes(fromIndex, toIndex, size)
return this.asDynamic().slice(fromIndex, toIndex)
}
/**
* Returns new array which is a copy of range of original array.
* Returns a new array which is a copy of the specified range of the original array.
*
* @param fromIndex the start of the range (inclusive), must be in `0..array.size`
* @param toIndex the end of the range (exclusive), must be in `fromIndex..array.size`
*/
public actual fun LongArray.copyOfRange(fromIndex: Int, toIndex: Int): LongArray {
AbstractList.checkRangeIndexes(fromIndex, toIndex, size)
return withType("LongArray", this.asDynamic().slice(fromIndex, toIndex))
}
/**
* Returns new array which is a copy of range of original array.
* Returns a new array which is a copy of the specified range of the original array.
*
* @param fromIndex the start of the range (inclusive), must be in `0..array.size`
* @param toIndex the end of the range (exclusive), must be in `fromIndex..array.size`
*/
@Suppress("NOTHING_TO_INLINE")
public actual inline fun FloatArray.copyOfRange(fromIndex: Int, toIndex: Int): FloatArray {
public actual fun FloatArray.copyOfRange(fromIndex: Int, toIndex: Int): FloatArray {
AbstractList.checkRangeIndexes(fromIndex, toIndex, size)
return this.asDynamic().slice(fromIndex, toIndex)
}
/**
* Returns new array which is a copy of range of original array.
* Returns a new array which is a copy of the specified range of the original array.
*
* @param fromIndex the start of the range (inclusive), must be in `0..array.size`
* @param toIndex the end of the range (exclusive), must be in `fromIndex..array.size`
*/
@Suppress("NOTHING_TO_INLINE")
public actual inline fun DoubleArray.copyOfRange(fromIndex: Int, toIndex: Int): DoubleArray {
public actual fun DoubleArray.copyOfRange(fromIndex: Int, toIndex: Int): DoubleArray {
AbstractList.checkRangeIndexes(fromIndex, toIndex, size)
return this.asDynamic().slice(fromIndex, toIndex)
}
/**
* Returns new array which is a copy of range of original array.
* Returns a new array which is a copy of the specified range of the original array.
*
* @param fromIndex the start of the range (inclusive), must be in `0..array.size`
* @param toIndex the end of the range (exclusive), must be in `fromIndex..array.size`
*/
public actual fun BooleanArray.copyOfRange(fromIndex: Int, toIndex: Int): BooleanArray {
AbstractList.checkRangeIndexes(fromIndex, toIndex, size)
return withType("BooleanArray", this.asDynamic().slice(fromIndex, toIndex))
}
/**
* Returns new array which is a copy of range of original array.
* Returns a new array which is a copy of the specified range of the original array.
*
* @param fromIndex the start of the range (inclusive), must be in `0..array.size`
* @param toIndex the end of the range (exclusive), must be in `fromIndex..array.size`
*/
public actual fun CharArray.copyOfRange(fromIndex: Int, toIndex: Int): CharArray {
AbstractList.checkRangeIndexes(fromIndex, toIndex, size)
return withType("CharArray", this.asDynamic().slice(fromIndex, toIndex))
}
+170 -17
View File
@@ -814,74 +814,227 @@ public actual inline fun <T> Array<T>.copyOf(newSize: Int): Array<T?> {
}
/**
* Returns new array which is a copy of range of original array.
* Returns a new array which is a copy of the specified range of the original array.
*
* @param fromIndex the start of the range (inclusive), must be in `0..array.size`
* @param toIndex the end of the range (exclusive), must be in `fromIndex..array.size`
*/
@JvmName("copyOfRangeInline")
@kotlin.internal.InlineOnly
public actual inline fun <T> Array<T>.copyOfRange(fromIndex: Int, toIndex: Int): Array<T> {
return java.util.Arrays.copyOfRange(this, fromIndex, toIndex)
return if (kotlin.internal.apiVersionIsAtLeast(1, 3, 0)) {
copyOfRangeImpl(fromIndex, toIndex)
} else {
if (toIndex > size) throw IndexOutOfBoundsException("toIndex: $toIndex, size: $size")
java.util.Arrays.copyOfRange(this, fromIndex, toIndex)
}
}
/**
* Returns new array which is a copy of range of original array.
* Returns a new array which is a copy of the specified range of the original array.
*
* @param fromIndex the start of the range (inclusive), must be in `0..array.size`
* @param toIndex the end of the range (exclusive), must be in `fromIndex..array.size`
*/
@JvmName("copyOfRangeInline")
@kotlin.internal.InlineOnly
public actual inline fun ByteArray.copyOfRange(fromIndex: Int, toIndex: Int): ByteArray {
return java.util.Arrays.copyOfRange(this, fromIndex, toIndex)
return if (kotlin.internal.apiVersionIsAtLeast(1, 3, 0)) {
copyOfRangeImpl(fromIndex, toIndex)
} else {
if (toIndex > size) throw IndexOutOfBoundsException("toIndex: $toIndex, size: $size")
java.util.Arrays.copyOfRange(this, fromIndex, toIndex)
}
}
/**
* Returns new array which is a copy of range of original array.
* Returns a new array which is a copy of the specified range of the original array.
*
* @param fromIndex the start of the range (inclusive), must be in `0..array.size`
* @param toIndex the end of the range (exclusive), must be in `fromIndex..array.size`
*/
@JvmName("copyOfRangeInline")
@kotlin.internal.InlineOnly
public actual inline fun ShortArray.copyOfRange(fromIndex: Int, toIndex: Int): ShortArray {
return java.util.Arrays.copyOfRange(this, fromIndex, toIndex)
return if (kotlin.internal.apiVersionIsAtLeast(1, 3, 0)) {
copyOfRangeImpl(fromIndex, toIndex)
} else {
if (toIndex > size) throw IndexOutOfBoundsException("toIndex: $toIndex, size: $size")
java.util.Arrays.copyOfRange(this, fromIndex, toIndex)
}
}
/**
* Returns new array which is a copy of range of original array.
* Returns a new array which is a copy of the specified range of the original array.
*
* @param fromIndex the start of the range (inclusive), must be in `0..array.size`
* @param toIndex the end of the range (exclusive), must be in `fromIndex..array.size`
*/
@JvmName("copyOfRangeInline")
@kotlin.internal.InlineOnly
public actual inline fun IntArray.copyOfRange(fromIndex: Int, toIndex: Int): IntArray {
return java.util.Arrays.copyOfRange(this, fromIndex, toIndex)
return if (kotlin.internal.apiVersionIsAtLeast(1, 3, 0)) {
copyOfRangeImpl(fromIndex, toIndex)
} else {
if (toIndex > size) throw IndexOutOfBoundsException("toIndex: $toIndex, size: $size")
java.util.Arrays.copyOfRange(this, fromIndex, toIndex)
}
}
/**
* Returns new array which is a copy of range of original array.
* Returns a new array which is a copy of the specified range of the original array.
*
* @param fromIndex the start of the range (inclusive), must be in `0..array.size`
* @param toIndex the end of the range (exclusive), must be in `fromIndex..array.size`
*/
@JvmName("copyOfRangeInline")
@kotlin.internal.InlineOnly
public actual inline fun LongArray.copyOfRange(fromIndex: Int, toIndex: Int): LongArray {
return java.util.Arrays.copyOfRange(this, fromIndex, toIndex)
return if (kotlin.internal.apiVersionIsAtLeast(1, 3, 0)) {
copyOfRangeImpl(fromIndex, toIndex)
} else {
if (toIndex > size) throw IndexOutOfBoundsException("toIndex: $toIndex, size: $size")
java.util.Arrays.copyOfRange(this, fromIndex, toIndex)
}
}
/**
* Returns new array which is a copy of range of original array.
* Returns a new array which is a copy of the specified range of the original array.
*
* @param fromIndex the start of the range (inclusive), must be in `0..array.size`
* @param toIndex the end of the range (exclusive), must be in `fromIndex..array.size`
*/
@JvmName("copyOfRangeInline")
@kotlin.internal.InlineOnly
public actual inline fun FloatArray.copyOfRange(fromIndex: Int, toIndex: Int): FloatArray {
return java.util.Arrays.copyOfRange(this, fromIndex, toIndex)
return if (kotlin.internal.apiVersionIsAtLeast(1, 3, 0)) {
copyOfRangeImpl(fromIndex, toIndex)
} else {
if (toIndex > size) throw IndexOutOfBoundsException("toIndex: $toIndex, size: $size")
java.util.Arrays.copyOfRange(this, fromIndex, toIndex)
}
}
/**
* Returns new array which is a copy of range of original array.
* Returns a new array which is a copy of the specified range of the original array.
*
* @param fromIndex the start of the range (inclusive), must be in `0..array.size`
* @param toIndex the end of the range (exclusive), must be in `fromIndex..array.size`
*/
@JvmName("copyOfRangeInline")
@kotlin.internal.InlineOnly
public actual inline fun DoubleArray.copyOfRange(fromIndex: Int, toIndex: Int): DoubleArray {
return java.util.Arrays.copyOfRange(this, fromIndex, toIndex)
return if (kotlin.internal.apiVersionIsAtLeast(1, 3, 0)) {
copyOfRangeImpl(fromIndex, toIndex)
} else {
if (toIndex > size) throw IndexOutOfBoundsException("toIndex: $toIndex, size: $size")
java.util.Arrays.copyOfRange(this, fromIndex, toIndex)
}
}
/**
* Returns new array which is a copy of range of original array.
* Returns a new array which is a copy of the specified range of the original array.
*
* @param fromIndex the start of the range (inclusive), must be in `0..array.size`
* @param toIndex the end of the range (exclusive), must be in `fromIndex..array.size`
*/
@JvmName("copyOfRangeInline")
@kotlin.internal.InlineOnly
public actual inline fun BooleanArray.copyOfRange(fromIndex: Int, toIndex: Int): BooleanArray {
return java.util.Arrays.copyOfRange(this, fromIndex, toIndex)
return if (kotlin.internal.apiVersionIsAtLeast(1, 3, 0)) {
copyOfRangeImpl(fromIndex, toIndex)
} else {
if (toIndex > size) throw IndexOutOfBoundsException("toIndex: $toIndex, size: $size")
java.util.Arrays.copyOfRange(this, fromIndex, toIndex)
}
}
/**
* Returns new array which is a copy of range of original array.
* Returns a new array which is a copy of the specified range of the original array.
*
* @param fromIndex the start of the range (inclusive), must be in `0..array.size`
* @param toIndex the end of the range (exclusive), must be in `fromIndex..array.size`
*/
@JvmName("copyOfRangeInline")
@kotlin.internal.InlineOnly
public actual inline fun CharArray.copyOfRange(fromIndex: Int, toIndex: Int): CharArray {
return if (kotlin.internal.apiVersionIsAtLeast(1, 3, 0)) {
copyOfRangeImpl(fromIndex, toIndex)
} else {
if (toIndex > size) throw IndexOutOfBoundsException("toIndex: $toIndex, size: $size")
java.util.Arrays.copyOfRange(this, fromIndex, toIndex)
}
}
@SinceKotlin("1.3")
@PublishedApi
@JvmName("copyOfRange")
internal fun <T> Array<T>.copyOfRangeImpl(fromIndex: Int, toIndex: Int): Array<T> {
copyOfRangeToIndexCheck(toIndex, size)
return java.util.Arrays.copyOfRange(this, fromIndex, toIndex)
}
@SinceKotlin("1.3")
@PublishedApi
@JvmName("copyOfRange")
internal fun ByteArray.copyOfRangeImpl(fromIndex: Int, toIndex: Int): ByteArray {
copyOfRangeToIndexCheck(toIndex, size)
return java.util.Arrays.copyOfRange(this, fromIndex, toIndex)
}
@SinceKotlin("1.3")
@PublishedApi
@JvmName("copyOfRange")
internal fun ShortArray.copyOfRangeImpl(fromIndex: Int, toIndex: Int): ShortArray {
copyOfRangeToIndexCheck(toIndex, size)
return java.util.Arrays.copyOfRange(this, fromIndex, toIndex)
}
@SinceKotlin("1.3")
@PublishedApi
@JvmName("copyOfRange")
internal fun IntArray.copyOfRangeImpl(fromIndex: Int, toIndex: Int): IntArray {
copyOfRangeToIndexCheck(toIndex, size)
return java.util.Arrays.copyOfRange(this, fromIndex, toIndex)
}
@SinceKotlin("1.3")
@PublishedApi
@JvmName("copyOfRange")
internal fun LongArray.copyOfRangeImpl(fromIndex: Int, toIndex: Int): LongArray {
copyOfRangeToIndexCheck(toIndex, size)
return java.util.Arrays.copyOfRange(this, fromIndex, toIndex)
}
@SinceKotlin("1.3")
@PublishedApi
@JvmName("copyOfRange")
internal fun FloatArray.copyOfRangeImpl(fromIndex: Int, toIndex: Int): FloatArray {
copyOfRangeToIndexCheck(toIndex, size)
return java.util.Arrays.copyOfRange(this, fromIndex, toIndex)
}
@SinceKotlin("1.3")
@PublishedApi
@JvmName("copyOfRange")
internal fun DoubleArray.copyOfRangeImpl(fromIndex: Int, toIndex: Int): DoubleArray {
copyOfRangeToIndexCheck(toIndex, size)
return java.util.Arrays.copyOfRange(this, fromIndex, toIndex)
}
@SinceKotlin("1.3")
@PublishedApi
@JvmName("copyOfRange")
internal fun BooleanArray.copyOfRangeImpl(fromIndex: Int, toIndex: Int): BooleanArray {
copyOfRangeToIndexCheck(toIndex, size)
return java.util.Arrays.copyOfRange(this, fromIndex, toIndex)
}
@SinceKotlin("1.3")
@PublishedApi
@JvmName("copyOfRange")
internal fun CharArray.copyOfRangeImpl(fromIndex: Int, toIndex: Int): CharArray {
copyOfRangeToIndexCheck(toIndex, size)
return java.util.Arrays.copyOfRange(this, fromIndex, toIndex)
}
@@ -43,3 +43,8 @@ internal actual fun <T> arrayOfNulls(reference: Array<T>, size: Int): Array<T> {
@Suppress("UNCHECKED_CAST")
return java.lang.reflect.Array.newInstance(reference.javaClass.componentType, size) as Array<T>
}
@SinceKotlin("1.3")
internal fun copyOfRangeToIndexCheck(toIndex: Int, size: Int) {
if (toIndex > size) throw IndexOutOfBoundsException("toIndex ($toIndex) is greater than size ($size).")
}
@@ -606,6 +606,15 @@ class ArraysTest {
assertEquals(listOf<Short>(200, 100), shortArrayOf(50, 100, 200).slice(2 downTo 1))
assertEquals(listOf(100L, 200L, 30L), longArrayOf(50L, 100L, 200L, 30L).slice(1..3))
assertEquals(listOf(true, false, true), booleanArrayOf(true, false, true, true).slice(iter))
for (range in listOf(-1 until 0, 0 until 2, 2..2)) {
val bounds = "range: $range"
val exClass = IndexOutOfBoundsException::class
assertFailsWith(exClass, bounds) { arrayOf("x").slice(range) }
assertFailsWith(exClass, bounds) { intArrayOf(1).slice(range) }
assertFailsWith(exClass, bounds) { longArrayOf(1L).slice(range) }
assertFailsWith(exClass, bounds) { charArrayOf('C').slice(range) }
}
}
@Test fun sliceArray() {
@@ -625,6 +634,15 @@ class ArraysTest {
// assertArrayNotSameButEquals(shortArrayOf(200, 100), shortArrayOf(50, 100, 200).sliceArray(2 downTo 1))
assertArrayNotSameButEquals(longArrayOf(100L, 200L, 30L), longArrayOf(50L, 100L, 200L, 30L).sliceArray(1..3))
assertArrayNotSameButEquals(booleanArrayOf(true, false, true), booleanArrayOf(true, false, true, true).sliceArray(coll))
for (range in listOf(-1 until 0, 0 until 2, 2..2)) {
val bounds = "range: $range"
val exClass = IndexOutOfBoundsException::class
assertFailsWith(exClass, bounds) { arrayOf("x").sliceArray(range) }
assertFailsWith(exClass, bounds) { intArrayOf(1).sliceArray(range) }
assertFailsWith(exClass, bounds) { longArrayOf(1L).sliceArray(range) }
assertFailsWith(exClass, bounds) { charArrayOf('C').sliceArray(range) }
}
}
@Test fun iterators() {
@@ -738,6 +756,7 @@ class ArraysTest {
}
@Test fun copyOfRange() {
assertArrayNotSameButEquals(arrayOf("b", "c"), arrayOf("a", "b", "c").copyOfRange(1, 3))
assertArrayNotSameButEquals(booleanArrayOf(true, false, true), booleanArrayOf(true, false, true, true).copyOfRange(0, 3))
assertArrayNotSameButEquals(byteArrayOf(0, 1, 2), byteArrayOf(0, 1, 2, 3, 4, 5).copyOfRange(0, 3))
assertArrayNotSameButEquals(shortArrayOf(0, 1, 2), shortArrayOf(0, 1, 2, 3, 4, 5).copyOfRange(0, 3))
@@ -746,6 +765,22 @@ class ArraysTest {
assertArrayNotSameButEquals(floatArrayOf(0F, 1F, 2F), floatArrayOf(0F, 1F, 2F, 3F, 4F, 5F).copyOfRange(0, 3))
assertArrayNotSameButEquals(doubleArrayOf(0.0, 1.0, 2.0), doubleArrayOf(0.0, 1.0, 2.0, 3.0, 4.0, 5.0).copyOfRange(0, 3))
assertArrayNotSameButEquals(charArrayOf('0', '1', '2'), charArrayOf('0', '1', '2', '3', '4', '5').copyOfRange(0, 3))
for (pos in 0..3) {
assertArrayNotSameButEquals(emptyArray(), arrayOf("a", "b", "c").copyOfRange(pos, pos))
assertArrayNotSameButEquals(intArrayOf(), intArrayOf(1, 2, 3).copyOfRange(pos, pos))
assertArrayNotSameButEquals(charArrayOf(), charArrayOf('a', 'b', 'c').copyOfRange(pos, pos))
assertArrayNotSameButEquals(longArrayOf(), LongArray(3) { it.toLong() }.copyOfRange(pos, pos))
}
for ((start, end) in listOf(-1 to 0, 0 to 2, 2 to 2, 1 to 0)) {
val bounds = "start: $start, end: $end"
val exClass = if (start > end) IllegalArgumentException::class else IndexOutOfBoundsException::class
assertFailsWith(exClass, bounds) { arrayOf("x").copyOfRange(start, end) }
assertFailsWith(exClass, bounds) { intArrayOf(1).copyOfRange(start, end) }
assertFailsWith(exClass, bounds) { longArrayOf(1L).copyOfRange(start, end) }
assertFailsWith(exClass, bounds) { charArrayOf('C').copyOfRange(start, end) }
}
}
@@ -26,6 +26,9 @@ class ListSpecificTest {
@Test
fun slice() {
val list = listOf('A', 'B', 'C', 'D')
assertEquals(emptyList(), list.slice(IntRange.EMPTY))
// ABCD
// 0123
assertEquals(listOf('B', 'C', 'D'), list.slice(1..3))
@@ -33,6 +36,13 @@ class ListSpecificTest {
val iter = listOf(2, 0, 3)
assertEquals(listOf('C', 'A', 'D'), list.slice(iter))
for (range in listOf(-1 until 0, 0 until 2, 2..2)) {
val bounds = "range: $range"
val exClass = IndexOutOfBoundsException::class
assertFailsWith(exClass, bounds) { listOf("x").slice(range) }
assertFailsWith(exClass, bounds) { listOf("x").slice(range.asIterable()) }
}
}
@Test
@@ -1142,6 +1142,15 @@ public final class kotlin/collections/ArraysKt {
public static final fun contains ([Ljava/lang/Object;Ljava/lang/Object;)Z
public static final fun contains ([SS)Z
public static final fun contains ([ZZ)Z
public static final fun copyOfRange ([BII)[B
public static final fun copyOfRange ([CII)[C
public static final fun copyOfRange ([DII)[D
public static final fun copyOfRange ([FII)[F
public static final fun copyOfRange ([III)[I
public static final fun copyOfRange ([JII)[J
public static final fun copyOfRange ([Ljava/lang/Object;II)[Ljava/lang/Object;
public static final fun copyOfRange ([SII)[S
public static final fun copyOfRange ([ZII)[Z
public static final fun count ([BLkotlin/jvm/functions/Function1;)I
public static final fun count ([CLkotlin/jvm/functions/Function1;)I
public static final fun count ([DLkotlin/jvm/functions/Function1;)I
@@ -370,11 +370,34 @@ object ArrayOps : TemplateGroupBase() {
}
}
val f_copyOfRangeJvmImpl = fn("copyOfRangeImpl(fromIndex: Int, toIndex: Int)") {
include(InvariantArraysOfObjects, ArraysOfPrimitives)
platforms(Platform.JVM)
} builderWith { primitive ->
since("1.3")
visibility("internal")
annotation("@PublishedApi")
annotation("""@JvmName("copyOfRange")""")
returns("SELF")
body {
"""
copyOfRangeToIndexCheck(toIndex, size)
return java.util.Arrays.copyOfRange(this, fromIndex, toIndex)
"""
}
}
val f_copyOfRange = fn("copyOfRange(fromIndex: Int, toIndex: Int)") {
include(InvariantArraysOfObjects, ArraysOfPrimitives)
} builderWith { primitive ->
doc { "Returns new array which is a copy of range of original array." }
doc {
"""
Returns a new array which is a copy of the specified range of the original array.
@param fromIndex the start of the range (inclusive), must be in `0..array.size`
@param toIndex the end of the range (exclusive), must be in `fromIndex..array.size`
"""
}
returns("SELF")
on(Platform.JS) {
@@ -383,18 +406,29 @@ object ArrayOps : TemplateGroupBase() {
suppress("ACTUAL_WITHOUT_EXPECT") // TODO: KT-21937
returns("Array<T>")
}
when(primitive) {
val rangeCheck = "AbstractList.checkRangeIndexes(fromIndex, toIndex, size)"
when (primitive) {
PrimitiveType.Char, PrimitiveType.Boolean, PrimitiveType.Long ->
body { "return withType(\"${primitive}Array\", this.asDynamic().slice(fromIndex, toIndex))" }
else -> {
inline(suppressWarning = true)
body { "return this.asDynamic().slice(fromIndex, toIndex)" }
}
}
body { rangeCheck + "\n" + body }
}
on(Platform.JVM) {
annotation("""@JvmName("copyOfRangeInline")""")
inlineOnly()
body { "return java.util.Arrays.copyOfRange(this, fromIndex, toIndex)" }
body {
"""
return if (kotlin.internal.apiVersionIsAtLeast(1, 3, 0)) {
copyOfRangeImpl(fromIndex, toIndex)
} else {
if (toIndex > size) throw IndexOutOfBoundsException("toIndex: ${'$'}toIndex, size: ${'$'}size")
java.util.Arrays.copyOfRange(this, fromIndex, toIndex)
}
"""
}
}
on(Platform.Common) {
specialFor(InvariantArraysOfObjects) {