diff --git a/js/js.libraries/src/core/generated/_ArraysJs.kt b/js/js.libraries/src/core/generated/_ArraysJs.kt index 217d5379068..a18f75380d2 100644 --- a/js/js.libraries/src/core/generated/_ArraysJs.kt +++ b/js/js.libraries/src/core/generated/_ArraysJs.kt @@ -646,7 +646,7 @@ public infix fun Array.contentDeepEquals(other: Array): Boolea } /** - * Returns a string representation of the contents of the specified array as if it is [List]. + * Returns a string representation of the contents of the specified array as if it is a [List]. */ @library("arrayToString") public fun Array.contentToString(): String { @@ -654,7 +654,7 @@ public fun Array.contentToString(): String { } /** - * Returns a string representation of the contents of the specified array as if it is [List]. + * Returns a string representation of the contents of the specified array as if it is a [List]. */ @library("arrayToString") public fun ByteArray.contentToString(): String { @@ -662,7 +662,7 @@ public fun ByteArray.contentToString(): String { } /** - * Returns a string representation of the contents of the specified array as if it is [List]. + * Returns a string representation of the contents of the specified array as if it is a [List]. */ @library("arrayToString") public fun ShortArray.contentToString(): String { @@ -670,7 +670,7 @@ public fun ShortArray.contentToString(): String { } /** - * Returns a string representation of the contents of the specified array as if it is [List]. + * Returns a string representation of the contents of the specified array as if it is a [List]. */ @library("arrayToString") public fun IntArray.contentToString(): String { @@ -678,7 +678,7 @@ public fun IntArray.contentToString(): String { } /** - * Returns a string representation of the contents of the specified array as if it is [List]. + * Returns a string representation of the contents of the specified array as if it is a [List]. */ @library("arrayToString") public fun LongArray.contentToString(): String { @@ -686,7 +686,7 @@ public fun LongArray.contentToString(): String { } /** - * Returns a string representation of the contents of the specified array as if it is [List]. + * Returns a string representation of the contents of the specified array as if it is a [List]. */ @library("arrayToString") public fun FloatArray.contentToString(): String { @@ -694,7 +694,7 @@ public fun FloatArray.contentToString(): String { } /** - * Returns a string representation of the contents of the specified array as if it is [List]. + * Returns a string representation of the contents of the specified array as if it is a [List]. */ @library("arrayToString") public fun DoubleArray.contentToString(): String { @@ -702,7 +702,7 @@ public fun DoubleArray.contentToString(): String { } /** - * Returns a string representation of the contents of the specified array as if it is [List]. + * Returns a string representation of the contents of the specified array as if it is a [List]. */ @library("arrayToString") public fun BooleanArray.contentToString(): String { @@ -710,7 +710,7 @@ public fun BooleanArray.contentToString(): String { } /** - * Returns a string representation of the contents of the specified array as if it is [List]. + * Returns a string representation of the contents of the specified array as if it is a [List]. */ @library("arrayToString") public fun CharArray.contentToString(): String { @@ -718,11 +718,13 @@ public fun CharArray.contentToString(): String { } /** - * Returns a string representation of the contents of this array as if it is [List]. - * + * Returns a string representation of the contents of this array as if it is a [List]. * Nested arrays are treated as lists too. + * + * If any of arrays contains itself on any nesting level that reference + * is rendered as `"[...]"` to prevent recursion. */ -@library("arrayToString") +@library("arrayDeepToString") public fun Array.contentDeepToString(): String { return noImpl } @@ -801,8 +803,8 @@ public fun CharArray.contentHashCode(): Int { /** * Returns a hash code based on the contents of this array as if it is [List]. - * * Nested arrays are treated as lists too. + * * If any of arrays contains itself on any nesting level the behavior is undefined. */ @library("arrayDeepHashCode") diff --git a/js/js.translator/testData/kotlin_lib.js b/js/js.translator/testData/kotlin_lib.js index efa8cf4115e..b9e8c4c5e8d 100644 --- a/js/js.translator/testData/kotlin_lib.js +++ b/js/js.translator/testData/kotlin_lib.js @@ -86,7 +86,7 @@ return "null"; } else if (Array.isArray(o)) { - return Kotlin.arrayToString(o); + return "[...]"; } else { return o.toString(); @@ -97,6 +97,21 @@ return "[" + a.map(Kotlin.toString).join(", ") + "]"; }; + Kotlin.arrayDeepToString = function (a, visited) { + visited = visited || [a]; + return "[" + a.map(function(e) { + if (Array.isArray(e) && visited.indexOf(e) < 0) { + visited.push(e); + var result = Kotlin.arrayDeepToString(e, visited); + visited.pop(); + return result; + } + else { + return Kotlin.toString(e); + } + }).join(", ") + "]"; + }; + Kotlin.compareTo = function (a, b) { var typeA = typeof a; var typeB = typeof a; @@ -398,7 +413,7 @@ var result = 1; for (var i = 0, n = arr.length; i < n; i++) { var e = arr[i]; - result = ((31 * result | 0) + (Array.isArray(e) ? Kotlin.arrayDeepHashCode(e) : Kotlin.hashCode(arr[i]))) | 0; + result = ((31 * result | 0) + (Array.isArray(e) ? Kotlin.arrayDeepHashCode(e) : Kotlin.hashCode(e))) | 0; } return result; }; diff --git a/libraries/stdlib/src/generated/_Arrays.kt b/libraries/stdlib/src/generated/_Arrays.kt index 79185891674..5f7aa7ab4fb 100644 --- a/libraries/stdlib/src/generated/_Arrays.kt +++ b/libraries/stdlib/src/generated/_Arrays.kt @@ -5163,8 +5163,8 @@ public inline infix fun Array.contentDeepEquals(other: Array): /** * Returns a hash code based on the contents of this array as if it is [List]. - * * Nested arrays are treated as lists too. + * * If any of arrays contains itself on any nesting level the behavior is undefined. */ @kotlin.jvm.JvmVersion @@ -5174,9 +5174,11 @@ public inline fun Array.contentDeepHashCode(): Int { } /** - * Returns a string representation of the contents of this array as if it is [List]. - * + * Returns a string representation of the contents of this array as if it is a [List]. * Nested arrays are treated as lists too. + * + * If any of arrays contains itself on any nesting level that reference + * is rendered as `"[...]"` to prevent recursion. */ @kotlin.jvm.JvmVersion @kotlin.internal.InlineOnly diff --git a/libraries/stdlib/test/collections/ArraysTest.kt b/libraries/stdlib/test/collections/ArraysTest.kt index c9748dc61cb..e114968a081 100644 --- a/libraries/stdlib/test/collections/ArraysTest.kt +++ b/libraries/stdlib/test/collections/ArraysTest.kt @@ -23,19 +23,23 @@ import kotlin.test.* import org.junit.Test as test import kotlin.comparisons.* -fun assertArrayNotSameButEquals(expected: Array, actual: Array, message: String = "") { assertTrue(expected !== actual && expected contentEquals actual, message); } -fun assertArrayNotSameButEquals(expected: IntArray, actual: IntArray, message: String = "") { assertTrue(expected !== actual && expected contentEquals actual, message); } -fun assertArrayNotSameButEquals(expected: LongArray, actual: LongArray, message: String = "") { assertTrue(expected !== actual && expected contentEquals actual, message); } -fun assertArrayNotSameButEquals(expected: ShortArray, actual: ShortArray, message: String = "") { assertTrue(expected !== actual && expected contentEquals actual, message); } -fun assertArrayNotSameButEquals(expected: ByteArray, actual: ByteArray, message: String = "") { assertTrue(expected !== actual && expected contentEquals actual, message); } -fun assertArrayNotSameButEquals(expected: DoubleArray, actual: DoubleArray, message: String = "") { assertTrue(expected !== actual && expected contentEquals actual, message); } -fun assertArrayNotSameButEquals(expected: FloatArray, actual: FloatArray, message: String = "") { assertTrue(expected !== actual && expected contentEquals actual, message); } -fun assertArrayNotSameButEquals(expected: CharArray, actual: CharArray, message: String = "") { assertTrue(expected !== actual && expected contentEquals actual, message); } -fun assertArrayNotSameButEquals(expected: BooleanArray, actual: BooleanArray, message: String = "") { assertTrue(expected !== actual && expected contentEquals actual, message); } +fun assertArrayNotSameButEquals(expected: Array, actual: Array, message: String = "") { assertTrue(expected !== actual && expected contentEquals actual, message) } +fun assertArrayNotSameButEquals(expected: IntArray, actual: IntArray, message: String = "") { assertTrue(expected !== actual && expected contentEquals actual, message) } +fun assertArrayNotSameButEquals(expected: LongArray, actual: LongArray, message: String = "") { assertTrue(expected !== actual && expected contentEquals actual, message) } +fun assertArrayNotSameButEquals(expected: ShortArray, actual: ShortArray, message: String = "") { assertTrue(expected !== actual && expected contentEquals actual, message) } +fun assertArrayNotSameButEquals(expected: ByteArray, actual: ByteArray, message: String = "") { assertTrue(expected !== actual && expected contentEquals actual, message) } +fun assertArrayNotSameButEquals(expected: DoubleArray, actual: DoubleArray, message: String = "") { assertTrue(expected !== actual && expected contentEquals actual, message) } +fun assertArrayNotSameButEquals(expected: FloatArray, actual: FloatArray, message: String = "") { assertTrue(expected !== actual && expected contentEquals actual, message) } +fun assertArrayNotSameButEquals(expected: CharArray, actual: CharArray, message: String = "") { assertTrue(expected !== actual && expected contentEquals actual, message) } +fun assertArrayNotSameButEquals(expected: BooleanArray, actual: BooleanArray, message: String = "") { assertTrue(expected !== actual && expected contentEquals actual, message) } class ArraysTest { + data class Value(val value: Int) { + override fun hashCode(): Int = value + } + @test fun orEmptyNull() { val x: Array? = null val y: Array? = null @@ -230,15 +234,29 @@ class ArraysTest { assertEquals("[aa, 1, null, [d]]", arr.contentDeepToString()) } + @test fun contentDeepToStringNoRecursion() { + // a[b[a, b]] + val b = arrayOfNulls(2) + val a = arrayOf(b) + b[0] = a + b[1] = b + a.toString() + assertTrue(true, "toString does not cycle") + a.contentToString() + assertTrue(true, "contentToString does not cycle") + val result = a.contentDeepToString() + assertEquals("[[[...], [...]]]", result) + } + @test fun contentHashCode() { - val arr = arrayOf("a", 1, null) - assertEquals(arr.asList().hashCode(), arr.contentHashCode()) + val arr = arrayOf("a", 1, null, Value(5)) + assertEquals(listOf(*arr).hashCode(), arr.contentHashCode()) + assertEquals((1*31 + 2)*31 + 3, arrayOf(Value(2), Value(3)).contentHashCode()) } @test fun contentDeepHashCode() { - val arr = arrayOf("aa", 1, null, charArrayOf('d')) - val list = arr.map { if (it is CharArray) it.asList() else it } - assertEquals(list.hashCode(), arr.contentDeepHashCode()) + val arr = arrayOf(null, Value(2), arrayOf(Value(3))) + assertEquals(((1*31 + 0)*31 + 2) * 31 + (1 * 31 + 3), arr.contentDeepHashCode()) } diff --git a/libraries/tools/kotlin-stdlib-gen/src/templates/Arrays.kt b/libraries/tools/kotlin-stdlib-gen/src/templates/Arrays.kt index fc1a91ce19f..c8bc909b2a7 100644 --- a/libraries/tools/kotlin-stdlib-gen/src/templates/Arrays.kt +++ b/libraries/tools/kotlin-stdlib-gen/src/templates/Arrays.kt @@ -56,9 +56,7 @@ fun arrays(): List { """ } returns("Boolean") - body { - "return Arrays.equals(this, other)" - } + body { "return Arrays.equals(this, other)" } } templates add f("contentDeepEquals(other: SELF)") { @@ -76,9 +74,7 @@ fun arrays(): List { """ } returns("Boolean") - body { - "return Arrays.deepEquals(this, other)" - } + body { "return Arrays.deepEquals(this, other)" } } templates add f("contentToString()") { @@ -87,9 +83,7 @@ fun arrays(): List { inline(Inline.Only) doc { "Returns a string representation of the contents of the specified array as if it is [List]." } returns("String") - body { - "return Arrays.toString(this)" - } + body { "return Arrays.toString(this)" } } templates add f("contentDeepToString()") { @@ -98,15 +92,15 @@ fun arrays(): List { inline(Inline.Only) doc { """ - Returns a string representation of the contents of this array as if it is [List]. - + Returns a string representation of the contents of this array as if it is a [List]. Nested arrays are treated as lists too. + + If any of arrays contains itself on any nesting level that reference + is rendered as `"[...]"` to prevent recursion. """ } returns("String") - body { - "return Arrays.deepToString(this)" - } + body { "return Arrays.deepToString(this)" } } templates add f("contentHashCode()") { @@ -117,9 +111,7 @@ fun arrays(): List { "Returns a hash code based on the contents of this array as if it is [List]." } returns("Int") - body { - "return Arrays.hashCode(this)" - } + body { "return Arrays.hashCode(this)" } } templates add f("contentDeepHashCode()") { @@ -129,15 +121,13 @@ fun arrays(): List { doc { """ Returns a hash code based on the contents of this array as if it is [List]. - Nested arrays are treated as lists too. + If any of arrays contains itself on any nesting level the behavior is undefined. """ } returns("Int") - body { - "return Arrays.deepHashCode(this)" - } + body { "return Arrays.deepHashCode(this)" } } templates addAll PrimitiveType.defaultPrimitives.map { primitive -> diff --git a/libraries/tools/kotlin-stdlib-gen/src/templates/SpecialJS.kt b/libraries/tools/kotlin-stdlib-gen/src/templates/SpecialJS.kt index ef21f09e45f..7e52479fcc4 100644 --- a/libraries/tools/kotlin-stdlib-gen/src/templates/SpecialJS.kt +++ b/libraries/tools/kotlin-stdlib-gen/src/templates/SpecialJS.kt @@ -198,7 +198,7 @@ fun specialJS(): List { templates add f("contentToString()") { only(ArraysOfObjects, ArraysOfPrimitives) - doc { "Returns a string representation of the contents of the specified array as if it is [List]." } + doc { "Returns a string representation of the contents of the specified array as if it is a [List]." } annotations("""@library("arrayToString")""") returns("String") body { "return noImpl" } @@ -208,12 +208,14 @@ fun specialJS(): List { only(ArraysOfObjects) doc { """ - Returns a string representation of the contents of this array as if it is [List]. - + Returns a string representation of the contents of this array as if it is a [List]. Nested arrays are treated as lists too. + + If any of arrays contains itself on any nesting level that reference + is rendered as `"[...]"` to prevent recursion. """ } - annotations("""@library("arrayToString")""") // arrayToString is already deep + annotations("""@library("arrayDeepToString")""") returns("String") body { "return noImpl" } } @@ -233,8 +235,8 @@ fun specialJS(): List { doc { """ Returns a hash code based on the contents of this array as if it is [List]. - Nested arrays are treated as lists too. + If any of arrays contains itself on any nesting level the behavior is undefined. """ }