JS: Array.toString is const, Array.contentToString is shallow, Array.contentDeepToString is deep and self-reference aware

#KT-13582
This commit is contained in:
Ilya Gorbunov
2016-09-23 21:21:07 +03:00
parent f3da656d6e
commit dceec89572
6 changed files with 87 additions and 58 deletions
+15 -13
View File
@@ -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
View File
@@ -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;
};
+5 -3
View File
@@ -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
+32 -14
View File
@@ -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.
"""
}