Null-terminate Collection.toArray destination only in JVM

In other platforms the elements following the collection elements
should not be changed.

As a part of efforts to stabilize Native stdlib.
This commit is contained in:
Abduqodiri Qurbonzoda
2023-06-01 12:52:46 +03:00
committed by Space Team
parent 60455daa9d
commit 6fdfd4e8dd
14 changed files with 46 additions and 40 deletions
@@ -24,7 +24,7 @@ fun abstractCollectionToArray() {
assertTrue("toArray1" in coll.invocations || "toArray2" in coll.invocations)
val arr2: Array<String> = coll.toArray(Array(coll.size + 1) { "" })
assertEquals(data + listOf(null), arr2.asList())
assertEquals(data + listOf(""), arr2.asList())
}
fun box(): String {
@@ -30,8 +30,5 @@ internal actual fun <T> copyToArrayImpl(collection: Collection<*>, array: Array<
while (iterator.hasNext()) {
array[index++] = iterator.next() as T
}
if (index < array.size) {
return array.copyOf(index) as Array<T>
}
return array
return terminateCollectionToArray(collection.size, array)
}
@@ -54,12 +54,11 @@ internal actual fun <T> copyToArrayImpl(collection: Collection<*>, array: Array<
while (iterator.hasNext()) {
array[index++] = iterator.next().unsafeCast<T>()
}
if (index < array.size) {
array[index] = null.unsafeCast<T>()
}
return array
return terminateCollectionToArray(collection.size, array)
}
internal actual fun <T> terminateCollectionToArray(collectionSize: Int, array: Array<T>): Array<T> = array
/**
* Returns a new read-only list containing only the specified object [element].
*
@@ -171,11 +171,7 @@ public actual open class ArrayList<E> internal constructor(private var array: Ar
(this.array as Array<T>).copyInto(array)
if (array.size > size) {
array[size] = null as T // null-terminate
}
return array
return terminateCollectionToArray(size, array)
}
override fun toArray(): Array<Any?> {
@@ -85,6 +85,14 @@ internal actual inline fun copyToArrayImpl(collection: Collection<*>): Array<Any
internal actual inline fun <T> copyToArrayImpl(collection: Collection<*>, array: Array<T>): Array<T> =
kotlin.jvm.internal.collectionToArray(collection, array as Array<Any?>) as Array<T>
internal actual fun <T> terminateCollectionToArray(collectionSize: Int, array: Array<T>): Array<T> {
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 <T> Array<out T>.copyToArrayOfAny(isVarargs: Boolean): Array<out Any?> =
if (isVarargs && this.javaClass == Array<Any?>::class.java)
@@ -176,12 +176,7 @@ internal class ListBuilder<E> private constructor(
@Suppress("UNCHECKED_CAST")
(array as Array<T>).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<Any?> {
@@ -221,11 +221,7 @@ actual class ArrayList<E> private constructor(
(backingArray as Array<T>).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<Any?> {
@@ -93,6 +93,8 @@ internal actual fun copyToArrayImpl(collection: Collection<*>): Array<Any?> {
return array
}
internal actual fun <T> terminateCollectionToArray(collectionSize: Int, array: Array<T>): Array<T> = array
/**
* Returns a new array which is a copy of the original array with new elements filled with null values.
*/
@@ -541,12 +541,9 @@ public class ArrayDeque<E> : AbstractMutableList<E> {
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<T>
return terminateCollectionToArray(size, dest) as Array<T>
}
@Suppress("NOTHING_TO_OVERRIDE")
@@ -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 <T> terminateCollectionToArray(collectionSize: Int, array: Array<T>): Array<T>
@@ -146,9 +146,8 @@ private class RingBuffer<T>(private val buffer: Array<Any?>, filledSize: Int) :
widx++
idx++
}
if (result.size > this.size) result[this.size] = null
return result as Array<T>
return terminateCollectionToArray(size, result) as Array<T>
}
override fun toArray(): Array<Any?> {
@@ -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<Any?>) + 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()}"
)
}
@@ -1311,7 +1311,12 @@ class CollectionTest {
assertTrue("toArray1" in coll.invocations || "toArray2" in coll.invocations)
val arr2: Array<String> = 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
@@ -34,8 +34,5 @@ internal actual fun <T> copyToArrayImpl(collection: Collection<*>, array: Array<
while (iterator.hasNext()) {
array[index++] = iterator.next() as T
}
if (index < array.size) {
(array as Array<T?>).fill(null, index)
}
return array
return terminateCollectionToArray(collection.size, array)
}