From f3da656d6eb09738a989c204dcd9bd0938f2a18d Mon Sep 17 00:00:00 2001 From: Ilya Gorbunov Date: Fri, 23 Sep 2016 16:01:18 +0300 Subject: [PATCH] contentEquals, contentHashCode, contentToString: Add js implementation and tests #KT-13582 --- .../src/core/generated/_ArraysJs.kt | 258 ++++++++++++++++++ js/js.translator/testData/kotlin_lib.js | 38 +++ .../stdlib/test/collections/ArraysTest.kt | 57 +++- .../src/templates/SpecialJS.kt | 79 ++++++ 4 files changed, 423 insertions(+), 9 deletions(-) diff --git a/js/js.libraries/src/core/generated/_ArraysJs.kt b/js/js.libraries/src/core/generated/_ArraysJs.kt index c4d8db5b6ed..217d5379068 100644 --- a/js/js.libraries/src/core/generated/_ArraysJs.kt +++ b/js/js.libraries/src/core/generated/_ArraysJs.kt @@ -552,6 +552,264 @@ public inline operator fun CharArray.plus(elements: CharArray): CharArray { return this.asDynamic().concat(elements) } +/** + * Returns `true` if the two specified arrays are *structurally* equal to one another, + * i.e. contain the same number of the same elements in the same order. + */ +@library("arrayEquals") +public infix fun Array.contentEquals(other: Array): Boolean { + return noImpl +} + +/** + * Returns `true` if the two specified arrays are *structurally* equal to one another, + * i.e. contain the same number of the same elements in the same order. + */ +@library("arrayEquals") +public infix fun ByteArray.contentEquals(other: ByteArray): Boolean { + return noImpl +} + +/** + * Returns `true` if the two specified arrays are *structurally* equal to one another, + * i.e. contain the same number of the same elements in the same order. + */ +@library("arrayEquals") +public infix fun ShortArray.contentEquals(other: ShortArray): Boolean { + return noImpl +} + +/** + * Returns `true` if the two specified arrays are *structurally* equal to one another, + * i.e. contain the same number of the same elements in the same order. + */ +@library("arrayEquals") +public infix fun IntArray.contentEquals(other: IntArray): Boolean { + return noImpl +} + +/** + * Returns `true` if the two specified arrays are *structurally* equal to one another, + * i.e. contain the same number of the same elements in the same order. + */ +@library("arrayEquals") +public infix fun LongArray.contentEquals(other: LongArray): Boolean { + return noImpl +} + +/** + * Returns `true` if the two specified arrays are *structurally* equal to one another, + * i.e. contain the same number of the same elements in the same order. + */ +@library("arrayEquals") +public infix fun FloatArray.contentEquals(other: FloatArray): Boolean { + return noImpl +} + +/** + * Returns `true` if the two specified arrays are *structurally* equal to one another, + * i.e. contain the same number of the same elements in the same order. + */ +@library("arrayEquals") +public infix fun DoubleArray.contentEquals(other: DoubleArray): Boolean { + return noImpl +} + +/** + * Returns `true` if the two specified arrays are *structurally* equal to one another, + * i.e. contain the same number of the same elements in the same order. + */ +@library("arrayEquals") +public infix fun BooleanArray.contentEquals(other: BooleanArray): Boolean { + return noImpl +} + +/** + * Returns `true` if the two specified arrays are *structurally* equal to one another, + * i.e. contain the same number of the same elements in the same order. + */ +@library("arrayEquals") +public infix fun CharArray.contentEquals(other: CharArray): Boolean { + return noImpl +} + +/** + * Returns `true` if the two specified arrays are *deeply* equal to one another, + * i.e. contain the same number of the same elements in the same order. + * + * If two corresponding elements are nested arrays, they are also compared deeply. + * If any of arrays contains itself on any nesting level the behavior is undefined. + */ +@library("arrayDeepEquals") +public infix fun Array.contentDeepEquals(other: Array): Boolean { + return noImpl +} + +/** + * Returns a string representation of the contents of the specified array as if it is [List]. + */ +@library("arrayToString") +public fun Array.contentToString(): String { + return noImpl +} + +/** + * Returns a string representation of the contents of the specified array as if it is [List]. + */ +@library("arrayToString") +public fun ByteArray.contentToString(): String { + return noImpl +} + +/** + * Returns a string representation of the contents of the specified array as if it is [List]. + */ +@library("arrayToString") +public fun ShortArray.contentToString(): String { + return noImpl +} + +/** + * Returns a string representation of the contents of the specified array as if it is [List]. + */ +@library("arrayToString") +public fun IntArray.contentToString(): String { + return noImpl +} + +/** + * Returns a string representation of the contents of the specified array as if it is [List]. + */ +@library("arrayToString") +public fun LongArray.contentToString(): String { + return noImpl +} + +/** + * Returns a string representation of the contents of the specified array as if it is [List]. + */ +@library("arrayToString") +public fun FloatArray.contentToString(): String { + return noImpl +} + +/** + * Returns a string representation of the contents of the specified array as if it is [List]. + */ +@library("arrayToString") +public fun DoubleArray.contentToString(): String { + return noImpl +} + +/** + * Returns a string representation of the contents of the specified array as if it is [List]. + */ +@library("arrayToString") +public fun BooleanArray.contentToString(): String { + return noImpl +} + +/** + * Returns a string representation of the contents of the specified array as if it is [List]. + */ +@library("arrayToString") +public fun CharArray.contentToString(): String { + return noImpl +} + +/** + * Returns a string representation of the contents of this array as if it is [List]. + * + * Nested arrays are treated as lists too. + */ +@library("arrayToString") +public fun Array.contentDeepToString(): String { + return noImpl +} + +/** + * Returns a hash code based on the contents of this array as if it is [List]. + */ +@library("arrayHashCode") +public fun Array.contentHashCode(): Int { + return noImpl +} + +/** + * Returns a hash code based on the contents of this array as if it is [List]. + */ +@library("arrayHashCode") +public fun ByteArray.contentHashCode(): Int { + return noImpl +} + +/** + * Returns a hash code based on the contents of this array as if it is [List]. + */ +@library("arrayHashCode") +public fun ShortArray.contentHashCode(): Int { + return noImpl +} + +/** + * Returns a hash code based on the contents of this array as if it is [List]. + */ +@library("arrayHashCode") +public fun IntArray.contentHashCode(): Int { + return noImpl +} + +/** + * Returns a hash code based on the contents of this array as if it is [List]. + */ +@library("arrayHashCode") +public fun LongArray.contentHashCode(): Int { + return noImpl +} + +/** + * Returns a hash code based on the contents of this array as if it is [List]. + */ +@library("arrayHashCode") +public fun FloatArray.contentHashCode(): Int { + return noImpl +} + +/** + * Returns a hash code based on the contents of this array as if it is [List]. + */ +@library("arrayHashCode") +public fun DoubleArray.contentHashCode(): Int { + return noImpl +} + +/** + * Returns a hash code based on the contents of this array as if it is [List]. + */ +@library("arrayHashCode") +public fun BooleanArray.contentHashCode(): Int { + return noImpl +} + +/** + * Returns a hash code based on the contents of this array as if it is [List]. + */ +@library("arrayHashCode") +public fun CharArray.contentHashCode(): Int { + return noImpl +} + +/** + * 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") +public fun Array.contentDeepHashCode(): Int { + return noImpl +} + /** * Sorts the array in-place according to the order specified by the given [comparison] function. */ diff --git a/js/js.translator/testData/kotlin_lib.js b/js/js.translator/testData/kotlin_lib.js index 44467c21b74..efa8cf4115e 100644 --- a/js/js.translator/testData/kotlin_lib.js +++ b/js/js.translator/testData/kotlin_lib.js @@ -366,6 +366,44 @@ return true; }; + Kotlin.arrayDeepEquals = function (a, b) { + if (a === b) { + return true; + } + if (!Array.isArray(b) || a.length !== b.length) { + return false; + } + + for (var i = 0, n = a.length; i < n; i++) { + if (Array.isArray(a[i])) { + if (!Kotlin.arrayDeepEquals(a[i], b[i])) { + return false; + } + } else if (!Kotlin.equals(a[i], b[i])) { + return false; + } + } + return true; + }; + + Kotlin.arrayHashCode = function (arr) { + var result = 1; + for (var i = 0, n = arr.length; i < n; i++) { + result = ((31 * result | 0) + Kotlin.hashCode(arr[i])) | 0; + } + return result; + }; + + Kotlin.arrayDeepHashCode = function (arr) { + 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; + } + return result; + }; + + var BaseOutput = Kotlin.createClassNow(null, null, { println: function (a) { if (typeof a !== "undefined") this.print(a); diff --git a/libraries/stdlib/test/collections/ArraysTest.kt b/libraries/stdlib/test/collections/ArraysTest.kt index 231d4c8b357..c9748dc61cb 100644 --- a/libraries/stdlib/test/collections/ArraysTest.kt +++ b/libraries/stdlib/test/collections/ArraysTest.kt @@ -23,15 +23,15 @@ import kotlin.test.* import org.junit.Test as test import kotlin.comparisons.* -fun assertArrayNotSameButEquals(expected: Array, actual: Array, message: String = "") { assertTrue(expected !== actual, message); assertEquals(expected.toList(), actual.toList(), message) } -fun assertArrayNotSameButEquals(expected: IntArray, actual: IntArray, message: String = "") { assertTrue(expected !== actual, message); assertEquals(expected.toList(), actual.toList(), message) } -fun assertArrayNotSameButEquals(expected: LongArray, actual: LongArray, message: String = "") { assertTrue(expected !== actual, message); assertEquals(expected.toList(), actual.toList(), message) } -fun assertArrayNotSameButEquals(expected: ShortArray, actual: ShortArray, message: String = "") { assertTrue(expected !== actual, message); assertEquals(expected.toList(), actual.toList(), message) } -fun assertArrayNotSameButEquals(expected: ByteArray, actual: ByteArray, message: String = "") { assertTrue(expected !== actual, message); assertEquals(expected.toList(), actual.toList(), message) } -fun assertArrayNotSameButEquals(expected: DoubleArray, actual: DoubleArray, message: String = "") { assertTrue(expected !== actual, message); assertEquals(expected.toList(), actual.toList(), message) } -fun assertArrayNotSameButEquals(expected: FloatArray, actual: FloatArray, message: String = "") { assertTrue(expected !== actual, message); assertEquals(expected.toList(), actual.toList(), message) } -fun assertArrayNotSameButEquals(expected: CharArray, actual: CharArray, message: String = "") { assertTrue(expected !== actual, message); assertEquals(expected.toList(), actual.toList(), message) } -fun assertArrayNotSameButEquals(expected: BooleanArray, actual: BooleanArray, message: String = "") { assertTrue(expected !== actual, message); assertEquals(expected.toList(), actual.toList(), 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 { @@ -203,6 +203,45 @@ class ArraysTest { assertEquals(false, arr[1]) } + @test fun contentEquals() { + val arr1 = arrayOf("a", 1, null) + val arr2 = arrayOf(*arr1) + assertTrue(arr1 contentEquals arr2) + val arr3 = arr2.reversedArray() + assertFalse(arr1 contentEquals arr3) + } + + @test fun contentDeepEquals() { + val arr1 = arrayOf("a", 1, intArrayOf(2)) + val arr2 = arrayOf("a", 1, intArrayOf(2)) + assertFalse(arr1 contentEquals arr2) + assertTrue(arr1 contentDeepEquals arr2) + arr2[2] = arr1 + assertFalse(arr1 contentDeepEquals arr2) + } + + @test fun contentToString() { + val arr = arrayOf("a", 1, null) + assertEquals(arr.asList().toString(), arr.contentToString()) + } + + @test fun contentDeepToString() { + val arr = arrayOf("aa", 1, null, charArrayOf('d')) + assertEquals("[aa, 1, null, [d]]", arr.contentDeepToString()) + } + + @test fun contentHashCode() { + val arr = arrayOf("a", 1, null) + assertEquals(arr.asList().hashCode(), arr.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()) + } + + @test fun min() { expect(null, { arrayOf().min() }) expect(1, { arrayOf(1).min() }) diff --git a/libraries/tools/kotlin-stdlib-gen/src/templates/SpecialJS.kt b/libraries/tools/kotlin-stdlib-gen/src/templates/SpecialJS.kt index 137ce585e92..ef21f09e45f 100644 --- a/libraries/tools/kotlin-stdlib-gen/src/templates/SpecialJS.kt +++ b/libraries/tools/kotlin-stdlib-gen/src/templates/SpecialJS.kt @@ -165,6 +165,85 @@ fun specialJS(): List { } } + templates add f("contentEquals(other: SELF)") { + only(ArraysOfObjects, ArraysOfPrimitives) + infix(true) + doc { + """ + Returns `true` if the two specified arrays are *structurally* equal to one another, + i.e. contain the same number of the same elements in the same order. + """ + } + annotations("""@library("arrayEquals")""") + returns("Boolean") + body { "return noImpl" } + } + + templates add f("contentDeepEquals(other: SELF)") { + only(ArraysOfObjects) + infix(true) + doc { + """ + Returns `true` if the two specified arrays are *deeply* equal to one another, + i.e. contain the same number of the same elements in the same order. + + If two corresponding elements are nested arrays, they are also compared deeply. + If any of arrays contains itself on any nesting level the behavior is undefined. + """ + } + annotations("""@library("arrayDeepEquals")""") + returns("Boolean") + body { "return noImpl" } + } + + templates add f("contentToString()") { + only(ArraysOfObjects, ArraysOfPrimitives) + doc { "Returns a string representation of the contents of the specified array as if it is [List]." } + annotations("""@library("arrayToString")""") + returns("String") + body { "return noImpl" } + } + + templates add f("contentDeepToString()") { + only(ArraysOfObjects) + doc { + """ + Returns a string representation of the contents of this array as if it is [List]. + + Nested arrays are treated as lists too. + """ + } + annotations("""@library("arrayToString")""") // arrayToString is already deep + returns("String") + body { "return noImpl" } + } + + templates add f("contentHashCode()") { + only(ArraysOfObjects, ArraysOfPrimitives) + doc { + "Returns a hash code based on the contents of this array as if it is [List]." + } + annotations("""@library("arrayHashCode")""") + returns("Int") + body { "return noImpl" } + } + + templates add f("contentDeepHashCode()") { + only(ArraysOfObjects) + 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. + """ + } + annotations("""@library("arrayDeepHashCode")""") + returns("Int") + body { "return noImpl" } + } + + templates add f("sort(comparison: (T, T) -> Int)") { only(ArraysOfObjects, ArraysOfPrimitives) exclude(PrimitiveType.Boolean)