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")
|
@library("arrayToString")
|
||||||
public fun <T> Array<out T>.contentToString(): String {
|
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")
|
@library("arrayToString")
|
||||||
public fun ByteArray.contentToString(): String {
|
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")
|
@library("arrayToString")
|
||||||
public fun ShortArray.contentToString(): String {
|
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")
|
@library("arrayToString")
|
||||||
public fun IntArray.contentToString(): String {
|
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")
|
@library("arrayToString")
|
||||||
public fun LongArray.contentToString(): String {
|
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")
|
@library("arrayToString")
|
||||||
public fun FloatArray.contentToString(): String {
|
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")
|
@library("arrayToString")
|
||||||
public fun DoubleArray.contentToString(): String {
|
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")
|
@library("arrayToString")
|
||||||
public fun BooleanArray.contentToString(): String {
|
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")
|
@library("arrayToString")
|
||||||
public fun CharArray.contentToString(): String {
|
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.
|
* 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 {
|
public fun <T> Array<out T>.contentDeepToString(): String {
|
||||||
return noImpl
|
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].
|
* Returns a hash code based on the contents of this array as if it is [List].
|
||||||
*
|
|
||||||
* Nested arrays are treated as lists too.
|
* Nested arrays are treated as lists too.
|
||||||
|
*
|
||||||
* If any of arrays contains itself on any nesting level the behavior is undefined.
|
* If any of arrays contains itself on any nesting level the behavior is undefined.
|
||||||
*/
|
*/
|
||||||
@library("arrayDeepHashCode")
|
@library("arrayDeepHashCode")
|
||||||
|
|||||||
+17
-2
@@ -86,7 +86,7 @@
|
|||||||
return "null";
|
return "null";
|
||||||
}
|
}
|
||||||
else if (Array.isArray(o)) {
|
else if (Array.isArray(o)) {
|
||||||
return Kotlin.arrayToString(o);
|
return "[...]";
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return o.toString();
|
return o.toString();
|
||||||
@@ -97,6 +97,21 @@
|
|||||||
return "[" + a.map(Kotlin.toString).join(", ") + "]";
|
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) {
|
Kotlin.compareTo = function (a, b) {
|
||||||
var typeA = typeof a;
|
var typeA = typeof a;
|
||||||
var typeB = typeof a;
|
var typeB = typeof a;
|
||||||
@@ -398,7 +413,7 @@
|
|||||||
var result = 1;
|
var result = 1;
|
||||||
for (var i = 0, n = arr.length; i < n; i++) {
|
for (var i = 0, n = arr.length; i < n; i++) {
|
||||||
var e = arr[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;
|
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].
|
* Returns a hash code based on the contents of this array as if it is [List].
|
||||||
*
|
|
||||||
* Nested arrays are treated as lists too.
|
* Nested arrays are treated as lists too.
|
||||||
|
*
|
||||||
* If any of arrays contains itself on any nesting level the behavior is undefined.
|
* If any of arrays contains itself on any nesting level the behavior is undefined.
|
||||||
*/
|
*/
|
||||||
@kotlin.jvm.JvmVersion
|
@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.
|
* 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.jvm.JvmVersion
|
||||||
@kotlin.internal.InlineOnly
|
@kotlin.internal.InlineOnly
|
||||||
|
|||||||
@@ -23,19 +23,23 @@ import kotlin.test.*
|
|||||||
import org.junit.Test as test
|
import org.junit.Test as test
|
||||||
import kotlin.comparisons.*
|
import kotlin.comparisons.*
|
||||||
|
|
||||||
fun <T> assertArrayNotSameButEquals(expected: Array<out T>, actual: Array<out T>, 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: 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: 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: 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: 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: 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: 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: 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: BooleanArray, actual: BooleanArray, message: String = "") { assertTrue(expected !== actual && expected contentEquals actual, message) }
|
||||||
|
|
||||||
|
|
||||||
class ArraysTest {
|
class ArraysTest {
|
||||||
|
|
||||||
|
data class Value(val value: Int) {
|
||||||
|
override fun hashCode(): Int = value
|
||||||
|
}
|
||||||
|
|
||||||
@test fun orEmptyNull() {
|
@test fun orEmptyNull() {
|
||||||
val x: Array<String>? = null
|
val x: Array<String>? = null
|
||||||
val y: Array<out String>? = null
|
val y: Array<out String>? = null
|
||||||
@@ -230,15 +234,29 @@ class ArraysTest {
|
|||||||
assertEquals("[aa, 1, null, [d]]", arr.contentDeepToString())
|
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() {
|
@test fun contentHashCode() {
|
||||||
val arr = arrayOf("a", 1, null)
|
val arr = arrayOf("a", 1, null, Value(5))
|
||||||
assertEquals(arr.asList().hashCode(), arr.contentHashCode())
|
assertEquals(listOf(*arr).hashCode(), arr.contentHashCode())
|
||||||
|
assertEquals((1*31 + 2)*31 + 3, arrayOf(Value(2), Value(3)).contentHashCode())
|
||||||
}
|
}
|
||||||
|
|
||||||
@test fun contentDeepHashCode() {
|
@test fun contentDeepHashCode() {
|
||||||
val arr = arrayOf<Any?>("aa", 1, null, charArrayOf('d'))
|
val arr = arrayOf(null, Value(2), arrayOf(Value(3)))
|
||||||
val list = arr.map { if (it is CharArray) it.asList() else it }
|
assertEquals(((1*31 + 0)*31 + 2) * 31 + (1 * 31 + 3), arr.contentDeepHashCode())
|
||||||
assertEquals(list.hashCode(), arr.contentDeepHashCode())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -56,9 +56,7 @@ fun arrays(): List<GenericFunction> {
|
|||||||
"""
|
"""
|
||||||
}
|
}
|
||||||
returns("Boolean")
|
returns("Boolean")
|
||||||
body {
|
body { "return Arrays.equals(this, other)" }
|
||||||
"return Arrays.equals(this, other)"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
templates add f("contentDeepEquals(other: SELF)") {
|
templates add f("contentDeepEquals(other: SELF)") {
|
||||||
@@ -76,9 +74,7 @@ fun arrays(): List<GenericFunction> {
|
|||||||
"""
|
"""
|
||||||
}
|
}
|
||||||
returns("Boolean")
|
returns("Boolean")
|
||||||
body {
|
body { "return Arrays.deepEquals(this, other)" }
|
||||||
"return Arrays.deepEquals(this, other)"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
templates add f("contentToString()") {
|
templates add f("contentToString()") {
|
||||||
@@ -87,9 +83,7 @@ fun arrays(): List<GenericFunction> {
|
|||||||
inline(Inline.Only)
|
inline(Inline.Only)
|
||||||
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 [List]." }
|
||||||
returns("String")
|
returns("String")
|
||||||
body {
|
body { "return Arrays.toString(this)" }
|
||||||
"return Arrays.toString(this)"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
templates add f("contentDeepToString()") {
|
templates add f("contentDeepToString()") {
|
||||||
@@ -98,15 +92,15 @@ fun arrays(): List<GenericFunction> {
|
|||||||
inline(Inline.Only)
|
inline(Inline.Only)
|
||||||
doc {
|
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.
|
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")
|
returns("String")
|
||||||
body {
|
body { "return Arrays.deepToString(this)" }
|
||||||
"return Arrays.deepToString(this)"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
templates add f("contentHashCode()") {
|
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 a hash code based on the contents of this array as if it is [List]."
|
||||||
}
|
}
|
||||||
returns("Int")
|
returns("Int")
|
||||||
body {
|
body { "return Arrays.hashCode(this)" }
|
||||||
"return Arrays.hashCode(this)"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
templates add f("contentDeepHashCode()") {
|
templates add f("contentDeepHashCode()") {
|
||||||
@@ -129,15 +121,13 @@ fun arrays(): List<GenericFunction> {
|
|||||||
doc {
|
doc {
|
||||||
"""
|
"""
|
||||||
Returns a hash code based on the contents of this array as if it is [List].
|
Returns a hash code based on the contents of this array as if it is [List].
|
||||||
|
|
||||||
Nested arrays are treated as lists too.
|
Nested arrays are treated as lists too.
|
||||||
|
|
||||||
If any of arrays contains itself on any nesting level the behavior is undefined.
|
If any of arrays contains itself on any nesting level the behavior is undefined.
|
||||||
"""
|
"""
|
||||||
}
|
}
|
||||||
returns("Int")
|
returns("Int")
|
||||||
body {
|
body { "return Arrays.deepHashCode(this)" }
|
||||||
"return Arrays.deepHashCode(this)"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
templates addAll PrimitiveType.defaultPrimitives.map { primitive ->
|
templates addAll PrimitiveType.defaultPrimitives.map { primitive ->
|
||||||
|
|||||||
@@ -198,7 +198,7 @@ fun specialJS(): List<GenericFunction> {
|
|||||||
|
|
||||||
templates add f("contentToString()") {
|
templates add f("contentToString()") {
|
||||||
only(ArraysOfObjects, ArraysOfPrimitives)
|
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")""")
|
annotations("""@library("arrayToString")""")
|
||||||
returns("String")
|
returns("String")
|
||||||
body { "return noImpl" }
|
body { "return noImpl" }
|
||||||
@@ -208,12 +208,14 @@ fun specialJS(): List<GenericFunction> {
|
|||||||
only(ArraysOfObjects)
|
only(ArraysOfObjects)
|
||||||
doc {
|
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.
|
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")
|
returns("String")
|
||||||
body { "return noImpl" }
|
body { "return noImpl" }
|
||||||
}
|
}
|
||||||
@@ -233,8 +235,8 @@ fun specialJS(): List<GenericFunction> {
|
|||||||
doc {
|
doc {
|
||||||
"""
|
"""
|
||||||
Returns a hash code based on the contents of this array as if it is [List].
|
Returns a hash code based on the contents of this array as if it is [List].
|
||||||
|
|
||||||
Nested arrays are treated as lists too.
|
Nested arrays are treated as lists too.
|
||||||
|
|
||||||
If any of arrays contains itself on any nesting level the behavior is undefined.
|
If any of arrays contains itself on any nesting level the behavior is undefined.
|
||||||
"""
|
"""
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user