[ESNext][BigInt] Implement support for "==" operation
authorticaiolima@gmail.com <ticaiolima@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 10 May 2018 04:25:49 +0000 (04:25 +0000)
committerticaiolima@gmail.com <ticaiolima@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 10 May 2018 04:25:49 +0000 (04:25 +0000)
https://bugs.webkit.org/show_bug.cgi?id=184474

Reviewed by Yusuke Suzuki.

JSTests:

* stress/big-int-equals-basic.js: Added.
* stress/big-int-equals-to-primitive-precedence.js: Added.
* stress/big-int-equals-wrapped-value.js: Added.

Source/JavaScriptCore:

This patch is implementing support of BigInt for equals operator
following the spec semantics[1].

[1] - https://tc39.github.io/proposal-bigint/#sec-abstract-equality-comparison

* runtime/JSBigInt.cpp:
(JSC::JSBigInt::parseInt):
(JSC::JSBigInt::stringToBigInt):
(JSC::JSBigInt::toString):
(JSC::JSBigInt::setDigit):
(JSC::JSBigInt::equalsToNumber):
(JSC::JSBigInt::compareToDouble):
* runtime/JSBigInt.h:
* runtime/JSCJSValueInlines.h:
(JSC::JSValue::equalSlowCaseInline):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@231629 268f45cc-cd09-0410-ab3c-d52691b4dbfc

JSTests/ChangeLog
JSTests/stress/big-int-equals-basic.js [new file with mode: 0644]
JSTests/stress/big-int-equals-to-primitive-precedence.js [new file with mode: 0644]
JSTests/stress/big-int-equals-wrapped-value.js [new file with mode: 0644]
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/runtime/JSBigInt.cpp
Source/JavaScriptCore/runtime/JSBigInt.h
Source/JavaScriptCore/runtime/JSCJSValueInlines.h

index 39f48b4..515972e 100644 (file)
@@ -1,3 +1,14 @@
+2018-05-09  Caio Lima  <ticaiolima@gmail.com>
+
+        [ESNext][BigInt] Implement support for "==" operation
+        https://bugs.webkit.org/show_bug.cgi?id=184474
+
+        Reviewed by Yusuke Suzuki.
+
+        * stress/big-int-equals-basic.js: Added.
+        * stress/big-int-equals-to-primitive-precedence.js: Added.
+        * stress/big-int-equals-wrapped-value.js: Added.
+
 2018-05-08  Valerie R Young  <valerie@bocoup.com>
 
         test262/Runner.pm: move input files to JSTests/test262
diff --git a/JSTests/stress/big-int-equals-basic.js b/JSTests/stress/big-int-equals-basic.js
new file mode 100644 (file)
index 0000000..59ba028
--- /dev/null
@@ -0,0 +1,124 @@
+//@ runBigIntEnabled
+
+function assert(a, e, m) {
+    if (a !== e)
+        throw new Error(m);
+}
+
+function testEquals(a, b, e) {
+    assert(a == b, e, a + " == " + b + " should be " + e);
+    assert(b == a, e, b + " == " + a + " should be " + e);
+}
+
+function testEqualsWithMessage(a, b, e, m) {
+    assert(a == b, e, m);
+    assert(b == a, e, m);
+}
+
+// BigInt - BigInt
+testEquals(1n, 1n, true);
+testEquals(1928392129312n, 1n, false);
+testEquals(0n, 1n, false);
+testEquals(0n, 0n, true);
+testEquals(817283912931n, 817283912931n, true);
+testEquals(0xFFD817283AF9129E31n, 0xFFD817283AF9129E31n, true);
+testEquals(0xAFFD817283AF9129E31n, 0xFFD817283AF9129E31n, false);
+testEquals(4719490697266344402481n, BigInt("-4719490697266344402481"), false);
+testEquals(BigInt("-4719490697266344402481"), BigInt("4719490697266344402481"), false);
+testEquals(BigInt("-4719490697266344402481"), BigInt("-4719490697266344402481"), true);
+testEquals(BigInt("-17"), BigInt("-17"), true);
+
+// BigInt - String
+
+testEquals(1n, "1", true);
+testEquals(1928392129312n, "1", false);
+testEquals(0n, "1", false);
+testEquals(0n, "0", true);
+testEquals(817283912931n, "817283912931", true);
+testEquals(0xFFD817283AF9129E31n, "4719490697266344402481", true);
+testEquals(0xAFFD817283AF9129E31n, "4719490697266344402481", false);
+
+// BigInt - Number
+
+testEquals(0n, 0, true);
+testEquals(0n, -0, true);
+testEquals(-0, 0n, true);
+testEquals(0n, 0.000000000001, false);
+testEquals(0n, 1, false);
+testEquals(1, 0n, false);
+testEquals(1n, 0.999999999999, false);
+testEquals(1n, 1, true);
+testEquals(0n, Number.MIN_VALUE, false);
+testEquals(0n, -Number.MIN_VALUE, false);
+testEquals(BigInt("-10"), Number.MIN_VALUE, false);
+testEquals(1n, Number.MAX_VALUE, false);
+testEquals(1n, -Number.MAX_VALUE, false);
+testEquals(0xfffffffffffff7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffn, Number.MAX_VALUE, false);
+testEquals(0xfffffffffffff800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000n, Number.MAX_VALUE, true);
+testEquals(0xfffffffffffff800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001n, Number.MAX_VALUE, false);
+testEquals(230000000000000000000, 230000000000000000000n, true);
+testEquals(10n, NaN, false);
+testEquals(10n, undefined, false);
+testEquals(10n, null, false);
+testEquals(10n, Infinity, false);
+testEquals(10n, -Infinity, false);
+testEquals(BigInt("-2147483648"), -2147483648, true); // Testing INT32_MIN
+testEquals(BigInt("-2147483647"), -2147483648, false);
+testEquals(BigInt("2147483647"), -2147483648, false);
+testEquals(BigInt("2147483648"), -2147483648, false);
+testEquals(BigInt("2147483647"), 2147483647, true);
+testEquals(BigInt("2147483648"), 2147483647, false);
+
+// BigInt - Boolean
+
+testEquals(BigInt("-1"), false, false);
+testEquals(BigInt("-1"), true, false);
+testEquals(0n, false, true);
+testEquals(0n, true, false);
+testEquals(1n, false, false);
+testEquals(1n, true, true);
+testEquals(2n, false, false);
+testEquals(2n, true, false);
+
+// BigInt - Object
+
+testEquals(0n, Object(0n), true);
+testEquals(0n, Object(1n), false);
+testEquals(1n, Object(0n), false);
+testEquals(1n, Object(1n), true);
+testEquals(2n, Object(0n), false);
+testEquals(2n, Object(1n), false);
+testEquals(2n, Object(2n), true);
+testEquals(0n, {}, false);
+testEquals(0n, {valueOf: function() { return 0n; }}, true);
+testEquals(0n, {valueOf: function() { return 1n; }}, false);
+testEquals(0n, {toString: function() { return "0"; }}, true);
+testEquals(0n, {toString: function() { return "1"; }}, false);
+testEquals(900719925474099101n, {valueOf: function() { return 900719925474099101n; }}, true);
+testEquals(900719925474099101n, {valueOf: function() { return 900719925474099102n; }}, false);
+testEquals(900719925474099101n, {toString: function() { return "900719925474099101"; }}, true);
+testEquals(900719925474099101n, {toString: function() { return "900719925474099102"; }}, false);
+
+try {
+    let o = {valueOf: function() { throw new Error("my error"); }};
+    o == 1n;
+    throw new Error("Exception in ToPrimitive not catched");
+} catch(e) {
+    assert(e.message, "my error", "Wrong exception in ToPrimitive");
+}
+
+try {
+    let o = {toString: function() { throw new Error("my error"); }};
+    o == 1n;
+    throw new Error("Exception in ToString not catched");
+} catch(e) {
+    assert(e.message, "my error", "Wrong exception in ToString");
+}
+
+// BigInt - Symbol
+
+testEqualsWithMessage(0n, Symbol("1"), false, "0n == Symbol(1)");
+testEqualsWithMessage(Symbol("1"), 0n, false, "Symbol(1) == 0n");
+testEqualsWithMessage(1n, Symbol("1"), false, "1n == Symbol(1)");
+testEqualsWithMessage(Symbol("1"), 1n, false, "Symbol(1) == 1n");
+
diff --git a/JSTests/stress/big-int-equals-to-primitive-precedence.js b/JSTests/stress/big-int-equals-to-primitive-precedence.js
new file mode 100644 (file)
index 0000000..f5ed564
--- /dev/null
@@ -0,0 +1,39 @@
+//@ runBigIntEnabled
+
+assert = {
+    sameValue: function (input, expected, message) {
+        if (input !== expected)
+            throw new Error(message);
+    }
+};
+
+function testEquals(x, y, z, message) {
+    assert.sameValue(x == y, z, message);
+    assert.sameValue(y == x, z, message);
+}
+
+testEquals(Object(2n), 1n, false, "ToPrimitive: unbox object with internal slot");
+
+let o = {
+    [Symbol.toPrimitive]: function() {
+        return 2n;
+    },
+    valueOf: function () {
+        throw new Error("Should never execute it");
+    },
+    toString: function () {
+        throw new Error("Should never execute it");
+    }
+};
+testEquals(o, 2n, true, "ToPrimitive: @@toPrimitive");
+
+o = {
+    valueOf: function() {
+        return 2n;
+    },
+    toString: function () {
+        throw new Error("Should never execute it");
+    }
+};
+testEquals(o, 1n, false, "ToPrimitive: valueOf");
+
diff --git a/JSTests/stress/big-int-equals-wrapped-value.js b/JSTests/stress/big-int-equals-wrapped-value.js
new file mode 100644 (file)
index 0000000..6ed89be
--- /dev/null
@@ -0,0 +1,37 @@
+//@ runBigIntEnabled
+
+assert = {
+    sameValue: function (input, expected, message) {
+        if (input !== expected)
+            throw new Error(message);
+    }
+};
+
+function testEquals(x, y, z, message) {
+    assert.sameValue(x == y, z, message);
+    assert.sameValue(y == x, z, message);
+}
+
+testEquals(Object(2n), 1n, false, "ToPrimitive: unbox object with internal slot");
+
+let o = {
+    [Symbol.toPrimitive]: function() {
+        return 1n;
+    }
+};
+testEquals(o, 1n, true, "ToPrimitive: @@toPrimitive");
+
+o = {
+    valueOf: function() {
+        return 2n;
+    }
+};
+testEquals(o, 2n, true, "ToPrimitive: valueOf");
+
+o = {
+    toString: function() {
+        return 2n;
+    }
+}
+testEquals(o, 1n, false, "ToPrimitive: toString");
+
index 429bd8b..377da69 100644 (file)
@@ -1,3 +1,26 @@
+2018-05-09  Caio Lima  <ticaiolima@gmail.com>
+
+        [ESNext][BigInt] Implement support for "==" operation
+        https://bugs.webkit.org/show_bug.cgi?id=184474
+
+        Reviewed by Yusuke Suzuki.
+
+        This patch is implementing support of BigInt for equals operator
+        following the spec semantics[1].
+
+        [1] - https://tc39.github.io/proposal-bigint/#sec-abstract-equality-comparison
+
+        * runtime/JSBigInt.cpp:
+        (JSC::JSBigInt::parseInt):
+        (JSC::JSBigInt::stringToBigInt):
+        (JSC::JSBigInt::toString):
+        (JSC::JSBigInt::setDigit):
+        (JSC::JSBigInt::equalsToNumber):
+        (JSC::JSBigInt::compareToDouble):
+        * runtime/JSBigInt.h:
+        * runtime/JSCJSValueInlines.h:
+        (JSC::JSValue::equalSlowCaseInline):
+
 2018-05-09  Filip Pizlo  <fpizlo@apple.com>
 
         Speed up AbstractInterpreter::executeEdges
index 46c2a6c..5fad08f 100644 (file)
@@ -208,21 +208,26 @@ std::optional<uint8_t> JSBigInt::singleDigitValueForString()
     return { };
 }
 
-JSBigInt* JSBigInt::parseInt(ExecState* state, StringView s)
+JSBigInt* JSBigInt::parseInt(ExecState* state, StringView s, ErrorParseMode parserMode)
 {
     if (s.is8Bit())
-        return parseInt(state, s.characters8(), s.length());
-    return parseInt(state, s.characters16(), s.length());
+        return parseInt(state, s.characters8(), s.length(), parserMode);
+    return parseInt(state, s.characters16(), s.length(), parserMode);
 }
 
-JSBigInt* JSBigInt::parseInt(ExecState* state, VM& vm, StringView s, uint8_t radix)
+JSBigInt* JSBigInt::parseInt(ExecState* state, VM& vm, StringView s, uint8_t radix, ErrorParseMode parserMode)
 {
     if (s.is8Bit())
-        return parseInt(state, vm, s.characters8(), s.length(), 0, radix, false);
-    return parseInt(state, vm, s.characters16(), s.length(), 0, radix, false);
+        return parseInt(state, vm, s.characters8(), s.length(), 0, radix, parserMode, false);
+    return parseInt(state, vm, s.characters16(), s.length(), 0, radix, parserMode, false);
 }
 
-String JSBigInt::toString(ExecState& state, int radix)
+JSBigInt* JSBigInt::stringToBigInt(ExecState* state, StringView s)
+{
+    return parseInt(state, s, ErrorParseMode::IgnoreExceptions);
+}
+
+String JSBigInt::toString(ExecState& state, unsigned radix)
 {
     if (this->isZero())
         return state.vm().smallStrings.singleCharacterStringRep('0');
@@ -699,24 +704,24 @@ inline size_t JSBigInt::offsetOfData()
 }
 
 template <typename CharType>
-JSBigInt* JSBigInt::parseInt(ExecState* state, CharType*  data, int length)
+JSBigInt* JSBigInt::parseInt(ExecState* state, CharType*  data, unsigned length, ErrorParseMode errorParseMode)
 {
     VM& vm = state->vm();
 
-    int p = 0;
+    unsigned p = 0;
     while (p < length && isStrWhiteSpace(data[p]))
         ++p;
 
     // Check Radix from frist characters
     if (static_cast<unsigned>(p) + 1 < static_cast<unsigned>(length) && data[p] == '0') {
         if (isASCIIAlphaCaselessEqual(data[p + 1], 'b'))
-            return parseInt(state, vm, data, length, p + 2, 2, false);
+            return parseInt(state, vm, data, length, p + 2, 2, errorParseMode, false);
         
         if (isASCIIAlphaCaselessEqual(data[p + 1], 'x'))
-            return parseInt(state, vm, data, length, p + 2, 16, false);
+            return parseInt(state, vm, data, length, p + 2, 16, errorParseMode, false);
         
         if (isASCIIAlphaCaselessEqual(data[p + 1], 'o'))
-            return parseInt(state, vm, data, length, p + 2, 8, false);
+            return parseInt(state, vm, data, length, p + 2, 8, errorParseMode, false);
     }
 
     bool sign = false;
@@ -729,7 +734,7 @@ JSBigInt* JSBigInt::parseInt(ExecState* state, CharType*  data, int length)
         }
     }
 
-    JSBigInt* result = parseInt(state, vm, data, length, p, 10);
+    JSBigInt* result = parseInt(state, vm, data, length, p, 10, errorParseMode);
 
     if (result && !result->isZero())
         result->setSign(sign);
@@ -738,16 +743,17 @@ JSBigInt* JSBigInt::parseInt(ExecState* state, CharType*  data, int length)
 }
 
 template <typename CharType>
-JSBigInt* JSBigInt::parseInt(ExecState* state, VM& vm, CharType* data, int length, int startIndex, int radix, bool allowEmptyString)
+JSBigInt* JSBigInt::parseInt(ExecState* state, VM& vm, CharType* data, unsigned length, unsigned startIndex, unsigned radix, ErrorParseMode errorParseMode, bool allowEmptyString)
 {
     ASSERT(length >= 0);
-    int p = startIndex;
+    unsigned p = startIndex;
 
     auto scope = DECLARE_THROW_SCOPE(vm);
 
     if (!allowEmptyString && startIndex == length) {
         ASSERT(state);
-        throwVMError(state, scope, createSyntaxError(state, "Failed to parse String to BigInt"));
+        if (errorParseMode == ErrorParseMode::ThrowExceptions)
+            throwVMError(state, scope, createSyntaxError(state, "Failed to parse String to BigInt"));
         return nullptr;
     }
 
@@ -757,7 +763,7 @@ JSBigInt* JSBigInt::parseInt(ExecState* state, VM& vm, CharType* data, int lengt
 
     int endIndex = length - 1;
     // Removing trailing spaces
-    while (endIndex >= p && isStrWhiteSpace(data[endIndex]))
+    while (endIndex >= static_cast<int>(p) && isStrWhiteSpace(data[endIndex]))
         --endIndex;
 
     length = endIndex + 1;
@@ -774,7 +780,7 @@ JSBigInt* JSBigInt::parseInt(ExecState* state, VM& vm, CharType* data, int lengt
 
     result->initialize(InitializationType::WithZero);
 
-    for (int i = p; i < length; i++, p++) {
+    for (unsigned i = p; i < length; i++, p++) {
         uint32_t digit;
         if (data[i] >= '0' && data[i] < limit0)
             digit = data[i] - '0';
@@ -792,7 +798,8 @@ JSBigInt* JSBigInt::parseInt(ExecState* state, VM& vm, CharType* data, int lengt
         return result->rightTrim(vm);
 
     ASSERT(state);
-    throwVMError(state, scope, createSyntaxError(state, "Failed to parse String to BigInt"));
+    if (errorParseMode == ErrorParseMode::ThrowExceptions)
+        throwVMError(state, scope, createSyntaxError(state, "Failed to parse String to BigInt"));
 
     return nullptr;
 }
@@ -813,10 +820,153 @@ inline void JSBigInt::setDigit(int n, Digit value)
     ASSERT(n >= 0 && n < length());
     dataStorage()[n] = value;
 }
-
 JSObject* JSBigInt::toObject(ExecState* exec, JSGlobalObject* globalObject) const
 {
     return BigIntObject::create(exec->vm(), globalObject, const_cast<JSBigInt*>(this));
 }
 
+bool JSBigInt::equalsToNumber(JSValue numValue)
+{
+    ASSERT(numValue.isNumber());
+    
+    if (numValue.isInt32()) {
+        int value = numValue.asInt32();
+        if (!value)
+            return this->isZero();
+
+        return (this->length() == 1) && (this->sign() == (value < 0)) && (this->digit(0) == static_cast<Digit>(std::abs(static_cast<int64_t>(value))));
+    }
+    
+    double value = numValue.asDouble();
+    return compareToDouble(this, value) == ComparisonResult::Equal;
+}
+
+JSBigInt::ComparisonResult JSBigInt::compareToDouble(JSBigInt* x, double y)
+{
+    // This algorithm expect that the double format is IEEE 754
+
+    uint64_t doubleBits = bitwise_cast<uint64_t>(y);
+    int rawExponent = static_cast<int>(doubleBits >> 52) & 0x7FF;
+
+    if (rawExponent == 0x7FF) {
+        if (std::isnan(y))
+            return ComparisonResult::Undefined;
+
+        return (y == std::numeric_limits<double>::infinity()) ? ComparisonResult::LessThan : ComparisonResult::GreaterThan;
+    }
+
+    bool xSign = x->sign();
+    
+    // Note that this is different from the double's sign bit for -0. That's
+    // intentional because -0 must be treated like 0.
+    bool ySign = y < 0;
+    if (xSign != ySign)
+        return xSign ? ComparisonResult::LessThan : ComparisonResult::GreaterThan;
+
+    if (!y) {
+        ASSERT(!xSign);
+        return x->isZero() ? ComparisonResult::Equal : ComparisonResult::GreaterThan;
+    }
+
+    if (x->isZero())
+        return ComparisonResult::LessThan;
+
+    uint64_t mantissa = doubleBits & 0x000FFFFFFFFFFFFF;
+
+    // Non-finite doubles are handled above.
+    ASSERT(rawExponent != 0x7FF);
+    int exponent = rawExponent - 0x3FF;
+    if (exponent < 0) {
+        // The absolute value of the double is less than 1. Only 0n has an
+        // absolute value smaller than that, but we've already covered that case.
+        return xSign ? ComparisonResult::LessThan : ComparisonResult::GreaterThan;
+    }
+
+    int xLength = x->length();
+    Digit xMSD = x->digit(xLength - 1);
+    int msdLeadingZeros = sizeof(xMSD) == 8  ? clz64(xMSD) : clz32(xMSD);
+
+    int xBitLength = xLength * digitBits - msdLeadingZeros;
+    int yBitLength = exponent + 1;
+    if (xBitLength < yBitLength)
+        return xSign? ComparisonResult::GreaterThan : ComparisonResult::LessThan;
+
+    if (xBitLength > yBitLength)
+        return xSign ? ComparisonResult::LessThan : ComparisonResult::GreaterThan;
+    
+    // At this point, we know that signs and bit lengths (i.e. position of
+    // the most significant bit in exponent-free representation) are identical.
+    // {x} is not zero, {y} is finite and not denormal.
+    // Now we virtually convert the double to an integer by shifting its
+    // mantissa according to its exponent, so it will align with the BigInt {x},
+    // and then we compare them bit for bit until we find a difference or the
+    // least significant bit.
+    //                    <----- 52 ------> <-- virtual trailing zeroes -->
+    // y / mantissa:     1yyyyyyyyyyyyyyyyy 0000000000000000000000000000000
+    // x / digits:    0001xxxx xxxxxxxx xxxxxxxx ...
+    //                    <-->          <------>
+    //              msdTopBit         digitBits
+    //
+    mantissa |= 0x0010000000000000;
+    const int mantissaTopBit = 52; // 0-indexed.
+
+    // 0-indexed position of {x}'s most significant bit within the {msd}.
+    int msdTopBit = digitBits - 1 - msdLeadingZeros;
+    ASSERT(msdTopBit == (xBitLength - 1) % digitBits);
+    
+    // Shifted chunk of {mantissa} for comparing with {digit}.
+    Digit compareMantissa;
+
+    // Number of unprocessed bits in {mantissa}. We'll keep them shifted to
+    // the left (i.e. most significant part) of the underlying uint64_t.
+    int remainingMantissaBits = 0;
+    
+    // First, compare the most significant digit against the beginning of
+    // the mantissa and then we align them.
+    if (msdTopBit < mantissaTopBit) {
+        remainingMantissaBits = (mantissaTopBit - msdTopBit);
+        compareMantissa = static_cast<Digit>(mantissa >> remainingMantissaBits);
+        mantissa = mantissa << (64 - remainingMantissaBits);
+    } else {
+        compareMantissa = static_cast<Digit>(mantissa << (msdTopBit - mantissaTopBit));
+        mantissa = 0;
+    }
+
+    if (xMSD > compareMantissa)
+        return xSign ? ComparisonResult::LessThan : ComparisonResult::GreaterThan;
+
+    if (xMSD < compareMantissa)
+        return xSign ? ComparisonResult::GreaterThan : ComparisonResult::LessThan;
+    
+    // Then, compare additional digits against any remaining mantissa bits.
+    for (int digitIndex = xLength - 2; digitIndex >= 0; digitIndex--) {
+        if (remainingMantissaBits > 0) {
+            remainingMantissaBits -= digitBits;
+            if (sizeof(mantissa) != sizeof(xMSD)) {
+                compareMantissa = static_cast<Digit>(mantissa >> (64 - digitBits));
+                // "& 63" to appease compilers. digitBits is 32 here anyway.
+                mantissa = mantissa << (digitBits & 63);
+            } else {
+                compareMantissa = static_cast<Digit>(mantissa);
+                mantissa = 0;
+            }
+        } else
+            compareMantissa = 0;
+
+        Digit digit = x->digit(digitIndex);
+        if (digit > compareMantissa)
+            return xSign ? ComparisonResult::LessThan : ComparisonResult::GreaterThan;
+        if (digit < compareMantissa)
+            return xSign ? ComparisonResult::GreaterThan : ComparisonResult::LessThan;
+    }
+
+    // Integer parts are equal; check whether {y} has a fractional part.
+    if (mantissa) {
+        ASSERT(remainingMantissaBits > 0);
+        return xSign ? ComparisonResult::GreaterThan : ComparisonResult::LessThan;
+    }
+
+    return ComparisonResult::Equal;
+}
+
 } // namespace JSC
index 298c348..b9bf751 100644 (file)
@@ -73,13 +73,20 @@ public:
     void setLength(int length) { m_length = length; }
     int length() const { return m_length; }
 
-    static JSBigInt* parseInt(ExecState*, VM&, StringView, uint8_t radix);
-    static JSBigInt* parseInt(ExecState*, StringView);
+    enum ErrorParseMode {
+        ThrowExceptions,
+        IgnoreExceptions
+    };
+
+    static JSBigInt* parseInt(ExecState*, VM&, StringView, uint8_t radix, ErrorParseMode = ThrowExceptions);
+    static JSBigInt* parseInt(ExecState*, StringView, ErrorParseMode = ThrowExceptions);
+    static JSBigInt* stringToBigInt(ExecState*, StringView);
 
     std::optional<uint8_t> singleDigitValueForString();
-    String toString(ExecState&, int radix);
+    String toString(ExecState&, unsigned radix);
     
     JS_EXPORT_PRIVATE static bool equals(JSBigInt*, JSBigInt*);
+    bool equalsToNumber(JSValue);
 
     bool getPrimitiveNumber(ExecState*, double& number, JSValue& result) const;
     double toNumber(ExecState*) const;
@@ -87,6 +94,14 @@ public:
     JSObject* toObject(ExecState*, JSGlobalObject*) const;
     
 private:
+
+    enum ComparisonResult {
+        Equal,
+        Undefined,
+        GreaterThan,
+        LessThan
+    };
+
     using Digit = uintptr_t;
     static constexpr const int bitsPerByte = 8;
     static constexpr const int digitBits = sizeof(Digit) * bitsPerByte;
@@ -116,11 +131,13 @@ private:
 
     bool isZero();
 
+    ComparisonResult static compareToDouble(JSBigInt* x, double y);
+
     template <typename CharType>
-    static JSBigInt* parseInt(ExecState*, CharType*  data, int length);
+    static JSBigInt* parseInt(ExecState*, CharType*  data, unsigned length, ErrorParseMode);
 
     template <typename CharType>
-    static JSBigInt* parseInt(ExecState*, VM&, CharType* data, int length, int startIndex, int radix, bool allowEmptyString = true);
+    static JSBigInt* parseInt(ExecState*, VM&, CharType* data, unsigned length, unsigned startIndex, unsigned radix, ErrorParseMode, bool allowEmptyString = true);
 
     static JSBigInt* allocateFor(ExecState*, VM&, int radix, int charcount);
 
index d0d3799..23d3b26 100644 (file)
@@ -981,6 +981,26 @@ ALWAYS_INLINE bool JSValue::equalSlowCaseInline(ExecState* exec, JSValue v1, JSV
             return asString(v1)->equal(exec, asString(v2));
         }
 
+        if (v1.isBigInt() && s2) {
+            JSBigInt* n = JSBigInt::stringToBigInt(exec, asString(v2)->value(exec));
+            RETURN_IF_EXCEPTION(scope, false);
+            if (!n)
+                return false;
+            
+            v2 = JSValue(n);
+            continue;
+        }
+
+        if (s1 && v2.isBigInt()) {
+            JSBigInt* n = JSBigInt::stringToBigInt(exec, asString(v1)->value(exec));
+            RETURN_IF_EXCEPTION(scope, false);
+            if (!n)
+                return false;
+            
+            v1 = JSValue(n);
+            continue;
+        }
+
         if (v1.isUndefinedOrNull()) {
             if (v2.isUndefinedOrNull())
                 return true;
@@ -1034,10 +1054,27 @@ ALWAYS_INLINE bool JSValue::equalSlowCaseInline(ExecState* exec, JSValue v1, JSV
         if (v1.isBoolean()) {
             if (v2.isNumber())
                 return static_cast<double>(v1.asBoolean()) == v2.asNumber();
+            else if (v2.isBigInt()) {
+                v1 = JSValue(v1.toNumber(exec));
+                continue;
+            }
         } else if (v2.isBoolean()) {
             if (v1.isNumber())
                 return v1.asNumber() == static_cast<double>(v2.asBoolean());
+            else if (v1.isBigInt()) {
+                v2 = JSValue(v2.toNumber(exec));
+                continue;
+            }
         }
+        
+        if (v1.isBigInt() && v2.isBigInt())
+            return JSBigInt::equals(asBigInt(v1), asBigInt(v2));
+        
+        if (v1.isBigInt() && v2.isNumber())
+            return asBigInt(v1)->equalsToNumber(v2);
+
+        if (v2.isBigInt() && v1.isNumber())
+            return asBigInt(v2)->equalsToNumber(v1);
 
         return v1 == v2;
     } while (true);