JS: Do not create a copy of already cloned vararg array.

JVM: Do not create copy of vararg array it it's already Object[].
This commit is contained in:
Ilya Gorbunov
2016-02-17 19:30:26 +03:00
parent c69d332db9
commit cbd38f007c
3 changed files with 51 additions and 21 deletions
+7 -3
View File
@@ -75,6 +75,10 @@ internal fun <T> arrayPlusCollection(array: dynamic, collection: Collection<T>):
return result
}
// copies vararg array due to different spread vararg behavior in JS.
// After fixing #KT-6491 may return `this`
internal inline fun <T> Array<out T>.varargToArrayOfAny(): Array<out Any?> = this.copyOf()
internal inline fun <T> Array<out T>.copyToArrayOfAny(isVarargs: Boolean): Array<out Any?> =
if (isVarargs)
// no need to copy vararg array in JS
this
else
this.copyOf()
@@ -45,16 +45,16 @@ internal object EmptyList : List<Nothing>, Serializable {
private fun readResolve(): Any = EmptyList
}
internal fun <T> Array<out T>.asCollection(): Collection<T> = ArrayAsCollection(this)
internal fun <T> Array<out T>.asCollection(): Collection<T> = ArrayAsCollection(this, isVarargs = false)
private class ArrayAsCollection<T>(val values: Array<out T>): Collection<T> {
private class ArrayAsCollection<T>(val values: Array<out T>, val isVarargs: Boolean): Collection<T> {
override val size: Int get() = values.size
override fun isEmpty(): Boolean = values.isEmpty()
override fun contains(element: T): Boolean = values.contains(element)
override fun containsAll(elements: Collection<T>): Boolean = elements.all { contains(it) }
override fun iterator(): Iterator<T> = values.iterator()
// override hidden toArray implementation to prevent copying of values array
public fun toArray(): Array<out Any?> = values.varargToArrayOfAny()
public fun toArray(): Array<out Any?> = values.copyToArrayOfAny(isVarargs)
}
/** Returns an empty read-only list. The returned list is serializable (JVM). */
@@ -76,11 +76,11 @@ public fun <T> listOf(element: T): List<T> = Collections.singletonList(element)
/** Returns a new [MutableList] with the given elements. */
public fun <T> mutableListOf(vararg elements: T): MutableList<T>
= if (elements.size == 0) ArrayList() else ArrayList(ArrayAsCollection(elements))
= if (elements.size == 0) ArrayList() else ArrayList(ArrayAsCollection(elements, isVarargs = true))
/** Returns a new [ArrayList] with the given elements. */
public fun <T> arrayListOf(vararg elements: T): ArrayList<T>
= if (elements.size == 0) ArrayList() else ArrayList(ArrayAsCollection(elements))
= if (elements.size == 0) ArrayList() else ArrayList(ArrayAsCollection(elements, isVarargs = true))
/** Returns a new read-only list either of single given element, if it is not null, or empty list it the element is null. The returned list is serializable (JVM). */
public fun <T : Any> listOfNotNull(element: T?): List<T> = if (element != null) listOf(element) else emptyList()
@@ -133,8 +133,12 @@ public inline fun <@kotlin.internal.OnlyInputTypes T> Collection<T>.containsAll(
// copies typed varargs array to array of objects
@JvmVersion
private fun <T> Array<out T>.varargToArrayOfAny(): Array<Any?>
= Arrays.copyOf(this, this.size, Array<Any?>::class.java)
private fun <T> Array<out T>.copyToArrayOfAny(isVarargs: Boolean): Array<Any?> =
if (isVarargs && this.javaClass == Array<Any?>::class.java)
// if the array came from varargs and already is array of Any, copying isn't required
@Suppress("CAST_NEVER_SUCCEEDS") (this as Array<Any?>)
else
Arrays.copyOf(this, this.size, Array<Any?>::class.java)
/**
* Searches this list or its range for the provided [element] index using binary search algorithm.
@@ -35,17 +35,39 @@ class JavautilCollectionsTest {
assertEquals(array.toList(), TEST_LIST)
}
@test fun arrayListDoesNotCreateArrayView() {
val array = arrayOf(1)
val list = arrayListOf(*array)
assertEquals(1, list[0])
array[0] = 2
assertEquals(1, list[0])
@test fun toListDoesNotCreateArrayView() {
snapshotDoesNotCreateView(arrayOf("first", "last"), { it.toList() })
snapshotDoesNotCreateView(arrayOf<Any>("item", 1), { it.toList() })
}
val arrayOfAny = arrayOf<Any>("first")
val listOfAny = arrayListOf(*arrayOfAny)
assertEquals("first", listOfAny[0])
arrayOfAny[0] = "last"
assertEquals("first", listOfAny[0])
@test fun toMutableListDoesNotCreateArrayView() {
snapshotDoesNotCreateView(arrayOf("first", "last"), { it.toMutableList() })
snapshotDoesNotCreateView(arrayOf<Any>("item", 2), { it.toMutableList() })
}
@test fun listOfDoesNotCreateView() {
snapshotDoesNotCreateView(arrayOf("first", "last"), { listOf(*it) })
snapshotDoesNotCreateView(arrayOf<Any>("item", 3), { listOf(*it) })
}
@test fun mutableListOfDoesNotCreateView() {
snapshotDoesNotCreateView(arrayOf("first", "last"), { mutableListOf(*it) })
snapshotDoesNotCreateView(arrayOf<Any>("item", 4), { mutableListOf(*it) })
}
@test fun arrayListDoesNotCreateArrayView() {
snapshotDoesNotCreateView(arrayOf(1, 2), { arrayListOf(*it) })
snapshotDoesNotCreateView(arrayOf<Any>("first", "last"), { arrayListOf(*it) })
}
private fun <T> snapshotDoesNotCreateView(array: Array<T>, snapshot: (Array<T>) -> List<T>) {
val first = array.first()
val last = array.last()
val list = snapshot(array)
assertEquals(first, list[0])
array[0] = last
assertEquals(first, list[0])
}
}