diff --git a/js/js.translator/testData/box/regression/stdlibTestSnippets/abstractCollectionToArray.kt b/js/js.translator/testData/box/regression/stdlibTestSnippets/abstractCollectionToArray.kt index 0147c536496..1b353b4e906 100644 --- a/js/js.translator/testData/box/regression/stdlibTestSnippets/abstractCollectionToArray.kt +++ b/js/js.translator/testData/box/regression/stdlibTestSnippets/abstractCollectionToArray.kt @@ -24,7 +24,7 @@ fun abstractCollectionToArray() { assertTrue("toArray1" in coll.invocations || "toArray2" in coll.invocations) val arr2: Array = coll.toArray(Array(coll.size + 1) { "" }) - assertEquals(data + listOf(null), arr2.asList()) + assertEquals(data + listOf(""), arr2.asList()) } fun box(): String { diff --git a/kotlin-native/runtime/src/main/kotlin/kotlin/collections/ArraysNative.kt b/kotlin-native/runtime/src/main/kotlin/kotlin/collections/ArraysNative.kt index de95ebf79d4..b9dc9d1ea12 100644 --- a/kotlin-native/runtime/src/main/kotlin/kotlin/collections/ArraysNative.kt +++ b/kotlin-native/runtime/src/main/kotlin/kotlin/collections/ArraysNative.kt @@ -30,8 +30,5 @@ internal actual fun copyToArrayImpl(collection: Collection<*>, array: Array< while (iterator.hasNext()) { array[index++] = iterator.next() as T } - if (index < array.size) { - return array.copyOf(index) as Array - } - return array + return terminateCollectionToArray(collection.size, array) } \ No newline at end of file diff --git a/libraries/stdlib/js/src/kotlin/collectionJs.kt b/libraries/stdlib/js/src/kotlin/collectionJs.kt index 19897f480a4..7b0f88d61eb 100644 --- a/libraries/stdlib/js/src/kotlin/collectionJs.kt +++ b/libraries/stdlib/js/src/kotlin/collectionJs.kt @@ -54,12 +54,11 @@ internal actual fun copyToArrayImpl(collection: Collection<*>, array: Array< while (iterator.hasNext()) { array[index++] = iterator.next().unsafeCast() } - if (index < array.size) { - array[index] = null.unsafeCast() - } - return array + return terminateCollectionToArray(collection.size, array) } +internal actual fun terminateCollectionToArray(collectionSize: Int, array: Array): Array = array + /** * Returns a new read-only list containing only the specified object [element]. * diff --git a/libraries/stdlib/js/src/kotlin/collections/ArrayList.kt b/libraries/stdlib/js/src/kotlin/collections/ArrayList.kt index ac30de18500..775020afd1f 100644 --- a/libraries/stdlib/js/src/kotlin/collections/ArrayList.kt +++ b/libraries/stdlib/js/src/kotlin/collections/ArrayList.kt @@ -171,11 +171,7 @@ public actual open class ArrayList internal constructor(private var array: Ar (this.array as Array).copyInto(array) - if (array.size > size) { - array[size] = null as T // null-terminate - } - - return array + return terminateCollectionToArray(size, array) } override fun toArray(): Array { diff --git a/libraries/stdlib/jvm/src/kotlin/collections/CollectionsJVM.kt b/libraries/stdlib/jvm/src/kotlin/collections/CollectionsJVM.kt index 1276fdfb0d9..e3f59ba1259 100644 --- a/libraries/stdlib/jvm/src/kotlin/collections/CollectionsJVM.kt +++ b/libraries/stdlib/jvm/src/kotlin/collections/CollectionsJVM.kt @@ -85,6 +85,14 @@ internal actual inline fun copyToArrayImpl(collection: Collection<*>): Array copyToArrayImpl(collection: Collection<*>, array: Array): Array = kotlin.jvm.internal.collectionToArray(collection, array as Array) as Array +internal actual fun terminateCollectionToArray(collectionSize: Int, array: Array): Array { + if (collectionSize < array.size) { + @Suppress("UNCHECKED_CAST") + array[collectionSize] = null as T // null-terminate + } + return array +} + // copies typed varargs array to array of objects internal actual fun Array.copyToArrayOfAny(isVarargs: Boolean): Array = if (isVarargs && this.javaClass == Array::class.java) diff --git a/libraries/stdlib/jvm/src/kotlin/collections/builders/ListBuilder.kt b/libraries/stdlib/jvm/src/kotlin/collections/builders/ListBuilder.kt index fce9bc74a07..30dca898637 100644 --- a/libraries/stdlib/jvm/src/kotlin/collections/builders/ListBuilder.kt +++ b/libraries/stdlib/jvm/src/kotlin/collections/builders/ListBuilder.kt @@ -176,12 +176,7 @@ internal class ListBuilder private constructor( @Suppress("UNCHECKED_CAST") (array as Array).copyInto(destination, 0, startIndex = offset, endIndex = offset + length) - if (destination.size > length) { - @Suppress("UNCHECKED_CAST") - destination[length] = null as T // null-terminate - } - - return destination + return terminateCollectionToArray(length, destination) } override fun toArray(): Array { diff --git a/libraries/stdlib/native-wasm/src/kotlin/collections/ArrayList.kt b/libraries/stdlib/native-wasm/src/kotlin/collections/ArrayList.kt index 07efd8aa09a..2bc3da02598 100644 --- a/libraries/stdlib/native-wasm/src/kotlin/collections/ArrayList.kt +++ b/libraries/stdlib/native-wasm/src/kotlin/collections/ArrayList.kt @@ -221,11 +221,7 @@ actual class ArrayList private constructor( (backingArray as Array).copyInto(array, 0, startIndex = offset, endIndex = offset + length) - if (array.size > length) { - array[length] = null as T // null-terminate - } - - return array + return terminateCollectionToArray(length, array) } override fun toArray(): Array { diff --git a/libraries/stdlib/native-wasm/src/kotlin/collections/Arrays.kt b/libraries/stdlib/native-wasm/src/kotlin/collections/Arrays.kt index 93fce876762..95529e442e6 100644 --- a/libraries/stdlib/native-wasm/src/kotlin/collections/Arrays.kt +++ b/libraries/stdlib/native-wasm/src/kotlin/collections/Arrays.kt @@ -93,6 +93,8 @@ internal actual fun copyToArrayImpl(collection: Collection<*>): Array { return array } +internal actual fun terminateCollectionToArray(collectionSize: Int, array: Array): Array = array + /** * Returns a new array which is a copy of the original array with new elements filled with null values. */ diff --git a/libraries/stdlib/src/kotlin/collections/ArrayDeque.kt b/libraries/stdlib/src/kotlin/collections/ArrayDeque.kt index d0a2cf695bc..ee34475d733 100644 --- a/libraries/stdlib/src/kotlin/collections/ArrayDeque.kt +++ b/libraries/stdlib/src/kotlin/collections/ArrayDeque.kt @@ -541,12 +541,9 @@ public class ArrayDeque : AbstractMutableList { elementData.copyInto(dest, destinationOffset = 0, startIndex = head, endIndex = elementData.size) elementData.copyInto(dest, destinationOffset = elementData.size - head, startIndex = 0, endIndex = tail) } - if (dest.size > size) { - dest[size] = null // null-terminate - } @Suppress("UNCHECKED_CAST") - return dest as Array + return terminateCollectionToArray(size, dest) as Array } @Suppress("NOTHING_TO_OVERRIDE") diff --git a/libraries/stdlib/src/kotlin/collections/Collections.kt b/libraries/stdlib/src/kotlin/collections/Collections.kt index 0600dc61a58..0135a431491 100644 --- a/libraries/stdlib/src/kotlin/collections/Collections.kt +++ b/libraries/stdlib/src/kotlin/collections/Collections.kt @@ -479,3 +479,9 @@ internal fun throwIndexOverflow() { throw ArithmeticException("Index overflow ha @SinceKotlin("1.3") internal fun throwCountOverflow() { throw ArithmeticException("Count overflow has happened.") } +/** + * In JVM if the size of [array] is bigger than [collectionSize], sets `array[collectionSize] = null`. + * In other platforms does nothing. + * Returns the given [array]. + */ +internal expect fun terminateCollectionToArray(collectionSize: Int, array: Array): Array diff --git a/libraries/stdlib/src/kotlin/collections/SlidingWindow.kt b/libraries/stdlib/src/kotlin/collections/SlidingWindow.kt index f8321fcb842..b14511c6542 100644 --- a/libraries/stdlib/src/kotlin/collections/SlidingWindow.kt +++ b/libraries/stdlib/src/kotlin/collections/SlidingWindow.kt @@ -146,9 +146,8 @@ private class RingBuffer(private val buffer: Array, filledSize: Int) : widx++ idx++ } - if (result.size > this.size) result[this.size] = null - return result as Array + return terminateCollectionToArray(size, result) as Array } override fun toArray(): Array { diff --git a/libraries/stdlib/test/collections/ArrayDequeTest.kt b/libraries/stdlib/test/collections/ArrayDequeTest.kt index 64dabd64b92..e04993e1bb8 100644 --- a/libraries/stdlib/test/collections/ArrayDequeTest.kt +++ b/libraries/stdlib/test/collections/ArrayDequeTest.kt @@ -5,6 +5,7 @@ package test.collections +import test.* import test.collections.behaviors.iteratorBehavior import test.collections.behaviors.listIteratorBehavior import test.collections.behaviors.listIteratorProperties @@ -622,12 +623,20 @@ class ArrayDequeTest { val dest = Array(expected.size + 2) { it + 100 } - @Suppress("UNCHECKED_CAST") - val nullTerminatedExpected = (expected as Array) + null + (expected.size + 101) + val expectedDest = buildList { + addAll(expected) + if (TestPlatform.current == TestPlatform.Jvm) { + add(null) + } else { + add(expected.size + 100) + } + add(expected.size + 101) + }.toTypedArray() + val actual = deque.testToArray(dest) assertTrue( - nullTerminatedExpected contentEquals actual, - message = "Expected: ${nullTerminatedExpected.contentToString()}, Actual: ${actual.contentToString()}" + expectedDest contentEquals actual, + message = "Expected: ${expectedDest.contentToString()}, Actual: ${actual.contentToString()}" ) } diff --git a/libraries/stdlib/test/collections/CollectionTest.kt b/libraries/stdlib/test/collections/CollectionTest.kt index 11af487d7db..61d8a294816 100644 --- a/libraries/stdlib/test/collections/CollectionTest.kt +++ b/libraries/stdlib/test/collections/CollectionTest.kt @@ -1311,7 +1311,12 @@ class CollectionTest { assertTrue("toArray1" in coll.invocations || "toArray2" in coll.invocations) val arr2: Array = coll.toArray(Array(coll.size + 1) { "" }) - assertEquals(data + listOf(null), arr2.asList()) + testOnlyOn(TestPlatform.Jvm) { + assertEquals(data + listOf(null), arr2.asList()) + } + testExceptOn(TestPlatform.Jvm) { + assertEquals(data + listOf(""), arr2.asList()) + } } @Test diff --git a/libraries/stdlib/wasm/src/kotlin/collections/ArraysWasm.kt b/libraries/stdlib/wasm/src/kotlin/collections/ArraysWasm.kt index 34cea96564e..4b75b27a71e 100644 --- a/libraries/stdlib/wasm/src/kotlin/collections/ArraysWasm.kt +++ b/libraries/stdlib/wasm/src/kotlin/collections/ArraysWasm.kt @@ -34,8 +34,5 @@ internal actual fun copyToArrayImpl(collection: Collection<*>, array: Array< while (iterator.hasNext()) { array[index++] = iterator.next() as T } - if (index < array.size) { - (array as Array).fill(null, index) - } - return array + return terminateCollectionToArray(collection.size, array) } \ No newline at end of file