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") @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
View File
@@ -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;
}; };
+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]. * 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
+32 -14
View File
@@ -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.
""" """
} }