From 38aecb14a251fbd61fa22f540fffb38ad86f463b Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Thu, 13 Apr 2017 15:25:02 +0300 Subject: [PATCH] Improve Number.hashCode implementation in JavaScript Fix KT-13577 --- js/js.libraries/src/js/core.js | 48 ++++++++++++++++--- .../js/test/semantics/BoxJsTestGenerated.java | 6 +++ .../testData/box/number/hashCode.kt | 20 ++++++++ 3 files changed, 67 insertions(+), 7 deletions(-) create mode 100644 js/js.translator/testData/box/number/hashCode.kt diff --git a/js/js.libraries/src/js/core.js b/js/js.libraries/src/js/core.js index 515b7947c19..36b202d0244 100644 --- a/js/js.libraries/src/js/core.js +++ b/js/js.libraries/src/js/core.js @@ -27,7 +27,7 @@ Kotlin.equals = function (obj1, obj2) { return obj2 !== obj2; } - if (typeof obj1 == "object" && typeof obj1.equals === "function") { + if (typeof obj1 === "object" && typeof obj1.equals === "function") { return obj1.equals(obj2); } @@ -38,16 +38,17 @@ Kotlin.hashCode = function (obj) { if (obj == null) { return 0; } - if ("function" == typeof obj.hashCode) { + if ("function" === typeof obj.hashCode) { return obj.hashCode(); } var objType = typeof obj; - if ("object" == objType || "function" == objType) { + if ("object" === objType || "function" === objType) { return getObjectHashCode(obj); - } else if ("number" == objType) { - // TODO: a more elaborate code is needed for floating point values. - return obj | 0; - } if ("boolean" == objType) { + } + else if ("number" === objType) { + return numberHashCode(obj); + } + if ("boolean" === objType) { return Number(obj) } @@ -55,6 +56,39 @@ Kotlin.hashCode = function (obj) { return getStringHashCode(str); }; +var numberHashCode; + +if (typeof ArrayBuffer === "function") { + var bufferForNumberConversion = new ArrayBuffer(8); + var arrayForDoubleConversion = new Float64Array(bufferForNumberConversion); + var arrayForIntegerConversion = new Int32Array(bufferForNumberConversion); + + // Detect endiannes of ArrayBuffer + var lowerIntegerIndex = 0; + var upperIntegerIndex = 1; + (function() { + arrayForDoubleConversion[0] = 1.2; + if (arrayForIntegerConversion[0] !== 0x3FF33333) { + lowerIntegerIndex = 1; + upperIntegerIndex = 0; + } + })(); + numberHashCode = function(obj) { + if ((obj | 0) === obj) { + return obj | 0; + } + else { + arrayForDoubleConversion[0] = obj; + return (arrayForIntegerConversion[lowerIntegerIndex] * 31 | 0) + arrayForIntegerConversion[upperIntegerIndex] | 0; + } + } +} +else { + numberHashCode = function(obj) { + return obj | 0; + } +} + Kotlin.toString = function (o) { if (o == null) { return "null"; diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java index a644260e76d..e0849b39142 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java @@ -6275,6 +6275,12 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest { doTest(fileName); } + @TestMetadata("hashCode.kt") + public void testHashCode() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/number/hashCode.kt"); + doTest(fileName); + } + @TestMetadata("hexadecimalConstant.kt") public void testHexadecimalConstant() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/number/hexadecimalConstant.kt"); diff --git a/js/js.translator/testData/box/number/hashCode.kt b/js/js.translator/testData/box/number/hashCode.kt new file mode 100644 index 00000000000..3551a3a621c --- /dev/null +++ b/js/js.translator/testData/box/number/hashCode.kt @@ -0,0 +1,20 @@ +fun box(): String { + + + var value = (3).hashCode() + if (value != 3) return "fail1: $value" + + value = (3.14).hashCode() + if (value != 319176039) return "fail2: $value" + + value = (3.14159).hashCode() + if (value != -1321819243) return "fail3: $value" + + value = (1e80).hashCode() + if (value != 314940496) return "fail4: $value" + + value = (1e81).hashCode() + if (value != 1519485350) return "fail5: $value" + + return "OK" +}