diff --git a/compiler/testData/codegen/box/toArray/toArrayShouldBePublic.kt b/compiler/testData/codegen/box/toArray/toArrayShouldBePublic.kt index 3e0d76bede1..78f1b92f895 100644 --- a/compiler/testData/codegen/box/toArray/toArrayShouldBePublic.kt +++ b/compiler/testData/codegen/box/toArray/toArrayShouldBePublic.kt @@ -8,10 +8,10 @@ open class SingletonCollection(val value: T) : AbstractCollection() { override val size = 1 override fun iterator(): Iterator = listOf(value).iterator() - protected fun toArray(): Array = + protected override final fun toArray(): Array = arrayOf(value) - protected fun toArray(a: Array): Array { + protected override final fun toArray(a: Array): Array { a[0] = value as E return a } diff --git a/compiler/testData/codegen/box/toArray/toArrayShouldBePublicWithJava.kt b/compiler/testData/codegen/box/toArray/toArrayShouldBePublicWithJava.kt index ff00b887294..097d62f3907 100644 --- a/compiler/testData/codegen/box/toArray/toArrayShouldBePublicWithJava.kt +++ b/compiler/testData/codegen/box/toArray/toArrayShouldBePublicWithJava.kt @@ -8,10 +8,10 @@ open class SingletonCollection(val value: T) : AbstractCollection() { override val size = 1 override fun iterator(): Iterator = listOf(value).iterator() - protected open fun toArray(): Array = + protected override fun toArray(): Array = arrayOf(value) - protected open fun toArray(a: Array): Array { + protected override fun toArray(a: Array): Array { a[0] = value as E return a } diff --git a/js/js.libraries/src/core/collections.kt b/js/js.libraries/src/core/collections.kt index 5fbb4db08b1..2b904daf902 100644 --- a/js/js.libraries/src/core/collections.kt +++ b/js/js.libraries/src/core/collections.kt @@ -27,7 +27,7 @@ public inline fun Collection.toTypedArray(): Array = copyToArray(this) @JsName("copyToArray") internal fun copyToArray(collection: Collection): Array { return if (collection.asDynamic().toArray !== undefined) - collection.asDynamic().toArray() + collection.asDynamic().toArray().unsafeCast>() else copyToArrayImpl(collection).unsafeCast>() } @@ -41,6 +41,22 @@ internal fun copyToArrayImpl(collection: Collection<*>): Array { return array } +@JsName("copyToExistingArrayImpl") +internal fun copyToArrayImpl(collection: Collection<*>, array: Array): Array { + if (array.size < collection.size) + return copyToArrayImpl(collection).unsafeCast>() + + val iterator = collection.iterator() + var index = 0 + while (iterator.hasNext()) { + array[index++] = iterator.next().unsafeCast() + } + if (index < array.size) { + array[index] = null.unsafeCast() + } + return array +} + @library("arrayToString") internal fun arrayToString(array: Array<*>): String = noImpl diff --git a/js/js.libraries/src/core/collections/AbstractMutableCollection.kt b/js/js.libraries/src/core/collections/AbstractMutableCollection.kt index 1ec443bcc37..51a4ea583c1 100644 --- a/js/js.libraries/src/core/collections/AbstractMutableCollection.kt +++ b/js/js.libraries/src/core/collections/AbstractMutableCollection.kt @@ -51,9 +51,6 @@ public abstract class AbstractMutableCollection protected constructor() : Abs } } - // TODO: move somehow to AbstractCollection: can't move now, because it cannot be protected on JVM, just public - protected open fun toArray(): Array = copyToArrayImpl(this) - open fun toJSON(): Any = this.toArray() } diff --git a/libraries/stdlib/common/src/kotlin/CollectionsH.kt b/libraries/stdlib/common/src/kotlin/CollectionsH.kt index 66bc88c0659..513b122a008 100644 --- a/libraries/stdlib/common/src/kotlin/CollectionsH.kt +++ b/libraries/stdlib/common/src/kotlin/CollectionsH.kt @@ -158,4 +158,8 @@ header operator fun MutableMap.set(key: K, value: V): Unit // from Grouping.kt public header fun Grouping.eachCount(): Map -// public header inline fun Grouping.eachSumOf(valueSelector: (T) -> Int): Map \ No newline at end of file +// public header inline fun Grouping.eachSumOf(valueSelector: (T) -> Int): Map + +internal header fun copyToArrayImpl(collection: Collection<*>): Array + +internal header fun copyToArrayImpl(collection: Collection<*>, array: Array): Array diff --git a/libraries/stdlib/src/kotlin/collections/AbstractCollection.kt b/libraries/stdlib/src/kotlin/collections/AbstractCollection.kt index 3036c71763b..4ffb177adb0 100644 --- a/libraries/stdlib/src/kotlin/collections/AbstractCollection.kt +++ b/libraries/stdlib/src/kotlin/collections/AbstractCollection.kt @@ -31,7 +31,14 @@ public abstract class AbstractCollection protected constructor() : Collec if (it === this) "(this Collection)" else it.toString() } + /** + * Returns new array of type `Array` with the elements of this collection. + */ + protected open fun toArray(): Array = copyToArrayImpl(this) + /** + * Fills the provided [array] or creates new array of the same type + * and fills it with the elements of this collection. + */ + protected open fun toArray(array: Array): Array = copyToArrayImpl(this, array) } - - diff --git a/libraries/stdlib/src/kotlin/collections/Collections.kt b/libraries/stdlib/src/kotlin/collections/Collections.kt index 1fd4b18facd..77ba31809d1 100644 --- a/libraries/stdlib/src/kotlin/collections/Collections.kt +++ b/libraries/stdlib/src/kotlin/collections/Collections.kt @@ -182,6 +182,16 @@ internal fun List.optimizeReadOnlyList() = when (size) { else -> this } +@JvmVersion +@kotlin.internal.InlineOnly +internal inline fun copyToArrayImpl(collection: Collection<*>): Array = + kotlin.jvm.internal.CollectionToArray.toArray(collection) + +@JvmVersion +@kotlin.internal.InlineOnly +internal inline fun copyToArrayImpl(collection: Collection<*>, array: Array): Array = + kotlin.jvm.internal.CollectionToArray.toArray(collection, array) + // copies typed varargs array to array of objects @JvmVersion private fun Array.copyToArrayOfAny(isVarargs: Boolean): Array = diff --git a/libraries/stdlib/test/collections/CollectionTest.kt b/libraries/stdlib/test/collections/CollectionTest.kt index 8bce7f3ed5b..0b61a9a83c5 100644 --- a/libraries/stdlib/test/collections/CollectionTest.kt +++ b/libraries/stdlib/test/collections/CollectionTest.kt @@ -905,4 +905,30 @@ class CollectionTest { assertTrue(listOf(1) is RandomAccess, "Default singleton list is RandomAccess") assertTrue(emptyList() is RandomAccess, "Empty list is RandomAccess") } + + @Test fun abstractCollectionToArray() { + class TestCollection(val data: Collection) : AbstractCollection() { + val invocations = mutableListOf() + override val size get() = data.size + override fun iterator() = data.iterator() + + override fun toArray(): Array { + invocations += "toArray1" + return data.toTypedArray() + } + public override fun toArray(array: Array): Array { + invocations += "toArray2" + return super.toArray(array) + } + } + val data = listOf("abc", "def") + val coll = TestCollection(data) + + val arr1 = coll.toTypedArray() + assertEquals(data, arr1.asList()) + assertTrue("toArray1" in coll.invocations || "toArray2" in coll.invocations) + + val arr2: Array = coll.toArray(Array(coll.size + 1) { "" }) + assertEquals(data + listOf(null), arr2.asList()) + } }