JS: Array.toString is const, Array.contentToString is shallow, Array.contentDeepToString is deep and self-reference aware
#KT-13582
This commit is contained in:
@@ -646,7 +646,7 @@ public infix fun <T> Array<out T>.contentDeepEquals(other: Array<out T>): 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 <T> Array<out T>.contentToString(): String {
|
||||
@@ -654,7 +654,7 @@ public fun <T> Array<out T>.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 <T> Array<out T>.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")
|
||||
|
||||
+17
-2
@@ -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;
|
||||
};
|
||||
|
||||
@@ -5163,8 +5163,8 @@ public inline infix fun <T> Array<out T>.contentDeepEquals(other: Array<out T>):
|
||||
|
||||
/**
|
||||
* 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 <T> Array<out T>.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
|
||||
|
||||
@@ -23,19 +23,23 @@ import kotlin.test.*
|
||||
import org.junit.Test as test
|
||||
import kotlin.comparisons.*
|
||||
|
||||
fun <T> assertArrayNotSameButEquals(expected: Array<out T>, actual: Array<out T>, 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 <T> assertArrayNotSameButEquals(expected: Array<out T>, actual: Array<out T>, 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<String>? = null
|
||||
val y: Array<out String>? = null
|
||||
@@ -230,15 +234,29 @@ class ArraysTest {
|
||||
assertEquals("[aa, 1, null, [d]]", arr.contentDeepToString())
|
||||
}
|
||||
|
||||
@test fun contentDeepToStringNoRecursion() {
|
||||
// a[b[a, b]]
|
||||
val b = arrayOfNulls<Any>(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<Any?>("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())
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -56,9 +56,7 @@ fun arrays(): List<GenericFunction> {
|
||||
"""
|
||||
}
|
||||
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<GenericFunction> {
|
||||
"""
|
||||
}
|
||||
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<GenericFunction> {
|
||||
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<GenericFunction> {
|
||||
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<GenericFunction> {
|
||||
"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<GenericFunction> {
|
||||
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 ->
|
||||
|
||||
@@ -198,7 +198,7 @@ fun specialJS(): List<GenericFunction> {
|
||||
|
||||
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<GenericFunction> {
|
||||
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<GenericFunction> {
|
||||
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.
|
||||
"""
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user