[ESNext][BigInt] Implement support for "*" operation
authorticaiolima@gmail.com <ticaiolima@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 28 Apr 2018 17:37:21 +0000 (17:37 +0000)
committerticaiolima@gmail.com <ticaiolima@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 28 Apr 2018 17:37:21 +0000 (17:37 +0000)
https://bugs.webkit.org/show_bug.cgi?id=183721

Reviewed by Saam Barati.

JSTests:

* bigIntTests.yaml:
* stress/big-int-mul-jit.js: Added.
* stress/big-int-mul-to-primitive-precedence.js: Added.
* stress/big-int-mul-to-primitive.js: Added.
* stress/big-int-mul-type-error.js: Added.
* stress/big-int-mul-wrapped-value.js: Added.
* stress/big-int-multiplication.js: Added.
* stress/big-int-multiply-memory-stress.js: Added.

Source/JavaScriptCore:

Added BigInt support into times binary operator into LLInt and on
JITOperations profiledMul and unprofiledMul. We are also replacing all
uses of int to unsigned when there is no negative values for
variables.

* dfg/DFGConstantFoldingPhase.cpp:
(JSC::DFG::ConstantFoldingPhase::foldConstants):
* jit/JITOperations.cpp:
* runtime/CommonSlowPaths.cpp:
(JSC::SLOW_PATH_DECL):
* runtime/JSBigInt.cpp:
(JSC::JSBigInt::JSBigInt):
(JSC::JSBigInt::allocationSize):
(JSC::JSBigInt::createWithLength):
(JSC::JSBigInt::toString):
(JSC::JSBigInt::multiply):
(JSC::JSBigInt::digitDiv):
(JSC::JSBigInt::internalMultiplyAdd):
(JSC::JSBigInt::multiplyAccumulate):
(JSC::JSBigInt::equals):
(JSC::JSBigInt::absoluteDivSmall):
(JSC::JSBigInt::calculateMaximumCharactersRequired):
(JSC::JSBigInt::toStringGeneric):
(JSC::JSBigInt::rightTrim):
(JSC::JSBigInt::allocateFor):
(JSC::JSBigInt::parseInt):
(JSC::JSBigInt::digit):
(JSC::JSBigInt::setDigit):
* runtime/JSBigInt.h:
* runtime/Operations.h:
(JSC::jsMul):

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

16 files changed:
JSTests/ChangeLog
JSTests/bigIntTests.yaml
JSTests/stress/big-int-mul-jit.js [new file with mode: 0644]
JSTests/stress/big-int-mul-to-primitive-precedence.js [new file with mode: 0644]
JSTests/stress/big-int-mul-to-primitive.js [new file with mode: 0644]
JSTests/stress/big-int-mul-type-error.js [new file with mode: 0644]
JSTests/stress/big-int-mul-wrapped-value.js [new file with mode: 0644]
JSTests/stress/big-int-multiplication.js [new file with mode: 0644]
JSTests/stress/big-int-multiply-memory-stress.js [new file with mode: 0644]
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp
Source/JavaScriptCore/jit/JITOperations.cpp
Source/JavaScriptCore/runtime/CommonSlowPaths.cpp
Source/JavaScriptCore/runtime/JSBigInt.cpp
Source/JavaScriptCore/runtime/JSBigInt.h
Source/JavaScriptCore/runtime/Operations.h

index 1392a0d..a126800 100644 (file)
@@ -1,3 +1,19 @@
+2018-04-28  Caio Lima  <ticaiolima@gmail.com>
+
+        [ESNext][BigInt] Implement support for "*" operation
+        https://bugs.webkit.org/show_bug.cgi?id=183721
+
+        Reviewed by Saam Barati.
+
+        * bigIntTests.yaml:
+        * stress/big-int-mul-jit.js: Added.
+        * stress/big-int-mul-to-primitive-precedence.js: Added.
+        * stress/big-int-mul-to-primitive.js: Added.
+        * stress/big-int-mul-type-error.js: Added.
+        * stress/big-int-mul-wrapped-value.js: Added.
+        * stress/big-int-multiplication.js: Added.
+        * stress/big-int-multiply-memory-stress.js: Added.
+
 2018-04-28  Commit Queue  <commit-queue@webkit.org>
 
         Unreviewed, rolling out r231131.
index 7ae22cb..e947dc2 100644 (file)
 - path: stress/big-int-to-string.js
   cmd: runBigIntEnabled
 
+- path: stress/big-int-mul-jit.js
+  cmd: runBigIntEnabled
+
+- path: stress/big-int-mul-to-primitive-precedence.js
+  cmd: runBigIntEnabled
+
+- path: stress/big-int-mul-to-primitive.js
+  cmd: runBigIntEnabled
+
+- path: stress/big-int-mul-type-error.js
+  cmd: runBigIntEnabled
+
+- path: stress/big-int-mul-wrapped-value.js
+  cmd: runBigIntEnabled
+
+- path: stress/big-int-multiplication.js
+  cmd: runBigIntEnabled
+
+- path: stress/big-int-multiply-memory-stress.js
+  cmd: runBigIntEnabled
+
diff --git a/JSTests/stress/big-int-mul-jit.js b/JSTests/stress/big-int-mul-jit.js
new file mode 100644 (file)
index 0000000..0cbede3
--- /dev/null
@@ -0,0 +1,19 @@
+//@ runBigIntEnabled
+
+let assert = {
+    sameValue: function(i, e, m) {
+        if (i !== e)
+            throw new Error(m);
+    }
+}
+
+function bigIntMul(x, y) {
+    return x * y;
+}
+noInline(bigIntMul);
+
+for (let i = 0; i < 10000; i++) {
+    let r = bigIntMul(3n, 10n);
+    assert.sameValue(r, 30n, 3n + " * " + 10n + " = " + r);
+}
+
diff --git a/JSTests/stress/big-int-mul-to-primitive-precedence.js b/JSTests/stress/big-int-mul-to-primitive-precedence.js
new file mode 100644 (file)
index 0000000..7149a19
--- /dev/null
@@ -0,0 +1,39 @@
+//@ runBigIntEnabled
+
+assert = {
+    sameValue: function (input, expected, message) {
+        if (input !== expected)
+            throw new Error(message);
+    }
+};
+
+function testMul(x, y, z, message) {
+    assert.sameValue(x * y, z, message);
+    assert.sameValue(y * x, z, message);
+}
+
+testMul(Object(2n), 1n, 2n, "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");
+    }
+};
+testMul(o, 1n, 2n, "ToPrimitive: @@toPrimitive");
+
+o = {
+    valueOf: function() {
+        return 2n;
+    },
+    toString: function () {
+        throw new Error("Should never execute it");
+    }
+};
+testMul(o, 1n, 2n, "ToPrimitive: valueOf");
+
diff --git a/JSTests/stress/big-int-mul-to-primitive.js b/JSTests/stress/big-int-mul-to-primitive.js
new file mode 100644 (file)
index 0000000..642e2e8
--- /dev/null
@@ -0,0 +1,35 @@
+//@ runBigIntEnabled
+
+function assert(a) {
+    if (!a)
+        throw new Error("Bad assertion");
+}
+
+assert.sameValue = function (input, expected, message) {
+    if (input !== expected)
+        throw new Error(message);
+}
+
+function testMul(x, y, z) {
+    assert.sameValue(x * y, z, x + " * " + y + " = " + z);
+    assert.sameValue(y * x, z, y + " * " + x + " = " + z);
+}
+
+let o = {
+    [Symbol.toPrimitive]: function () { return 300000000000000n; }
+}
+
+testMul(500000000000438n, o, 150000000000131400000000000000n);
+
+o.valueOf = function () {
+    throw new Error("Should never execute it");
+};
+
+testMul(700000000000438n, o, 210000000000131400000000000000n);
+
+o.toString = function () {
+    throw new Error("Should never execute it");
+};
+
+testMul(700000000000438n, o, 210000000000131400000000000000n);
+
diff --git a/JSTests/stress/big-int-mul-type-error.js b/JSTests/stress/big-int-mul-type-error.js
new file mode 100644 (file)
index 0000000..b517845
--- /dev/null
@@ -0,0 +1,106 @@
+//@ runBigIntEnabled
+
+function assert(a, message) {
+    if (!a)
+        throw new Error(message);
+}
+
+function assertThrowTypeError(a, b, message) {
+    try {
+        let n = a * b;
+        assert(false, message + ": Should throw TypeError, but executed without exception");
+    } catch (e) {
+        assert(e instanceof TypeError, message + ": expected TypeError, got: " + e);
+    }
+}
+
+assertThrowTypeError(30n, "foo", "BigInt * String");
+assertThrowTypeError("bar", 18757382984821n, "String * BigInt");
+assertThrowTypeError(30n, Symbol("foo"), "BigInt * Symbol");
+assertThrowTypeError(Symbol("bar"), 18757382984821n, "Symbol * BigInt");
+assertThrowTypeError(30n, 3320, "BigInt * Int32");
+assertThrowTypeError(33256, 18757382984821n, "Int32 * BigInt");
+assertThrowTypeError(30n, 0.543, "BigInt * Double");
+assertThrowTypeError(230.19293, 18757382984821n, "Double * BigInt");
+assertThrowTypeError(30n, NaN, "BigInt * NaN");
+assertThrowTypeError(NaN, 18757382984821n, "NaN * BigInt");
+assertThrowTypeError(30n, NaN, "BigInt * NaN");
+assertThrowTypeError(NaN, 18757382984821n, "NaN * BigInt");
+assertThrowTypeError(30n, +Infinity, "BigInt * NaN");
+assertThrowTypeError(+Infinity, 18757382984821n, "NaN * BigInt");
+assertThrowTypeError(30n, -Infinity, "BigInt * -Infinity");
+assertThrowTypeError(-Infinity, 18757382984821n, "-Infinity * BigInt");
+assertThrowTypeError(30n, null, "BigInt * null");
+assertThrowTypeError(null, 18757382984821n, "null * BigInt");
+assertThrowTypeError(30n, undefined, "BigInt * undefined");
+assertThrowTypeError(undefined, 18757382984821n, "undefined * BigInt");
+assertThrowTypeError(30n, true, "BigInt * true");
+assertThrowTypeError(true, 18757382984821n, "true * BigInt");
+assertThrowTypeError(30n, false, "BigInt * false");
+assertThrowTypeError(false, 18757382984821n, "false * BigInt");
+
+// Error when returning from object
+
+let o = {
+    valueOf: function () { return Symbol("Foo"); }
+};
+
+assertThrowTypeError(30n, o, "BigInt * Object.valueOf returning Symbol");
+assertThrowTypeError(o, 18757382984821n, "Object.valueOf returning Symbol * BigInt");
+
+o = {
+    valueOf: function () { return 33256; }
+};
+
+assertThrowTypeError(30n, o, "BigInt * Object.valueOf returning Int32");
+assertThrowTypeError(o, 18757382984821n, "Object.valueOf returning Int32 * BigInt");
+
+o = {
+    valueOf: function () { return 0.453; }
+};
+
+assertThrowTypeError(30n, o, "BigInt * Object.valueOf returning Double");
+assertThrowTypeError(o, 18757382984821n, "Object.valueOf returning Double * BigInt");
+
+o = {
+    toString: function () { return Symbol("Foo"); }
+};
+
+assertThrowTypeError(30n, o, "BigInt * Object.toString returning Symbol");
+assertThrowTypeError(o, 18757382984821n, "Object.toString returning Symbol * BigInt");
+
+o = {
+    toString: function () { return 33256; }
+};
+
+assertThrowTypeError(30n, o, "BigInt * Object.toString returning Int32");
+assertThrowTypeError(o, 18757382984821n, "Object.toString returning Int32 * BigInt");
+
+o = {
+    toString: function () { return 0.453; }
+};
+
+assertThrowTypeError(30n, o, "BigInt * Object.toString returning Double");
+assertThrowTypeError(o, 18757382984821n, "Object.toString returning Double * BigInt");
+
+o = {
+    [Symbol.toPrimitive]: function () { return Symbol("Foo"); }
+};
+
+assertThrowTypeError(30n, o, "BigInt * Object.@@toPrimitive returning Symbol");
+assertThrowTypeError(o, 18757382984821n, "Object.@@toPrimitive returning Symbol * BigInt");
+
+o = {
+    [Symbol.toPrimitive]: function () { return 33256; }
+};
+
+assertThrowTypeError(30n, o, "BigInt * Object.@@toPrimitive returning Int32");
+assertThrowTypeError(o, 18757382984821n, "Object.@@toPrimitive returning Int32 * BigInt");
+
+o = {
+    [Symbol.toPrimitive]: function () { return 0.453; }
+};
+
+assertThrowTypeError(30n, o, "BigInt * Object.@@toPrimitive returning Double");
+assertThrowTypeError(o, 18757382984821n, "Object.@@toPrimitive returning Double * BigInt");
+
diff --git a/JSTests/stress/big-int-mul-wrapped-value.js b/JSTests/stress/big-int-mul-wrapped-value.js
new file mode 100644 (file)
index 0000000..b673d8a
--- /dev/null
@@ -0,0 +1,37 @@
+//@ runBigIntEnabled
+
+assert = {
+    sameValue: function (input, expected, message) {
+        if (input !== expected)
+            throw new Error(message);
+    }
+};
+
+function testMul(x, y, z, message) {
+    assert.sameValue(x * y, z, message);
+    assert.sameValue(y * x, z, message);
+}
+
+testMul(Object(2n), 1n, 2n, "ToPrimitive: unbox object with internal slot");
+
+let o = {
+    [Symbol.toPrimitive]: function() {
+        return 2n;
+    }
+};
+testMul(o, 1n, 2n, "ToPrimitive: @@toPrimitive");
+
+o = {
+    valueOf: function() {
+        return 2n;
+    }
+};
+testMul(o, 1n, 2n, "ToPrimitive: valueOf");
+
+o = {
+    toString: function() {
+        return 2n;
+    }
+}
+testMul(o, 1n, 2n, "ToPrimitive: toString");
+
diff --git a/JSTests/stress/big-int-multiplication.js b/JSTests/stress/big-int-multiplication.js
new file mode 100644 (file)
index 0000000..3ecd636
--- /dev/null
@@ -0,0 +1,83 @@
+//@ runBigIntEnabled
+
+// Copyright (C) 2017 Robin Templeton. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+assert = {
+    sameValue: function (input, expected, message) {
+    if (input !== expected)
+        throw new Error(message);
+    }
+};
+
+function testMul(x, y, z) {
+    assert.sameValue(x * y, z, x + " * " + y + " = " + z);
+    assert.sameValue(y * x, z, y + " * " + x + " = " + z);
+}
+
+testMul(0xFEDCBA9876543210n, 0xFEDCBA9876543210n, 0xFDBAC097C8DC5ACCDEEC6CD7A44A4100n);
+testMul(0xFEDCBA9876543210n, 0xFEDCBA98n, 0xFDBAC097530ECA86541D5980n);
+testMul(0xFEDCBA9876543210n, 0x1234n, 0x121F49F49F49F49F4B40n);
+testMul(0xFEDCBA9876543210n, 0x3n, 0x2FC962FC962FC9630n);
+testMul(0xFEDCBA9876543210n, 0x2n, 0x1FDB97530ECA86420n);
+testMul(0xFEDCBA9876543210n, 0x1n, 0xFEDCBA9876543210n);
+testMul(0xFEDCBA9876543210n, 0x0n, 0x0n);
+testMul(0xFEDCBA9876543210n, BigInt("-1"), BigInt("-18364758544493064720"));
+testMul(0xFEDCBA9876543210n, BigInt("-2"), BigInt("-36729517088986129440"));
+testMul(0xFEDCBA9876543210n, BigInt("-3"), BigInt("-55094275633479194160"));
+testMul(0xFEDCBA9876543210n, BigInt("-4660"), BigInt("-85579774817337681595200"));
+testMul(0xFEDCBA9876543210n, BigInt("-4275878551"), BigInt("-78525477154691874604502820720"));
+testMul(0xFEDCBA987654320Fn, 0xFEDCBA987654320Fn, 0xFDBAC097C8DC5ACAE132F7A6B7A1DCE1n);
+testMul(0xFEDCBA987654320Fn, 0xFEDCBA97n, 0xFDBAC09654320FECDEEC6CD9n);
+testMul(0xFEDCBA987654320Fn, 0x3n, 0x2FC962FC962FC962Dn);
+testMul(0xFEDCBA987654320Fn, 0x2n, 0x1FDB97530ECA8641En);
+testMul(0xFEDCBA987654320Fn, 0x1n, 0xFEDCBA987654320Fn);
+testMul(0xFEDCBA987654320Fn, 0x0n, 0x0n);
+testMul(0xFEDCBA987654320Fn, BigInt("-1"), BigInt("-18364758544493064719"));
+testMul(0xFEDCBA987654320Fn, BigInt("-2"), BigInt("-36729517088986129438"));
+testMul(0xFEDCBA987654320Fn, BigInt("-3"), BigInt("-55094275633479194157"));
+testMul(0xFEDCBA987654320Fn, BigInt("-4275878551"), BigInt("-78525477154691874600226942169"));
+testMul(0xFEDCBA987654320Fn, BigInt("-18364758544493064720"), BigInt("-337264356397531028976608289633615613680"));
+testMul(0xFEDCBA98n, 0xFEDCBA98n, 0xFDBAC096DD413A40n);
+testMul(0xFEDCBA98n, 0x1234n, 0x121F49F496E0n);
+testMul(0xFEDCBA98n, 0x3n, 0x2FC962FC8n);
+testMul(0xFEDCBA98n, 0x2n, 0x1FDB97530n);
+testMul(0xFEDCBA98n, 0x1n, 0xFEDCBA98n);
+testMul(0xFEDCBA98n, 0x0n, 0x0n);
+testMul(0xFEDCBA98n, BigInt("-1"), BigInt("-4275878552"));
+testMul(0xFEDCBA98n, BigInt("-2"), BigInt("-8551757104"));
+testMul(0xFEDCBA98n, BigInt("-3"), BigInt("-12827635656"));
+testMul(0xFEDCBA98n, BigInt("-4275878551"), BigInt("-18283137387177738152"));
+testMul(0xFEDCBA98n, BigInt("-18364758544493064720"), BigInt("-78525477173056633148995885440"));
+testMul(0x3n, 0x3n, 0x9n);
+testMul(0x3n, 0x2n, 0x6n);
+testMul(0x3n, 0x1n, 0x3n);
+testMul(0x3n, 0x0n, 0x0n);
+testMul(0x3n, BigInt("-1"), BigInt("-3"));
+testMul(0x3n, BigInt("-2"), BigInt("-6"));
+testMul(0x3n, BigInt("-3"), BigInt("-9"));
+testMul(0x3n, BigInt("-4660"), BigInt("-13980"));
+testMul(0x3n, BigInt("-4275878552"), BigInt("-12827635656"));
+testMul(0x3n, BigInt("-18364758544493064720"), BigInt("-55094275633479194160"));
+testMul(0x0n, 0x0n, 0x0n);
+testMul(0x0n, BigInt("-1"), 0x0n);
+testMul(0x0n, BigInt("-2"), 0x0n);
+testMul(0x0n, BigInt("-3"), 0x0n);
+testMul(0x0n, BigInt("-4275878551"), 0x0n);
+testMul(0x0n, BigInt("-18364758544493064719"), 0x0n);
+testMul(BigInt("-1"), BigInt("-1"), 0x1n);
+testMul(BigInt("-1"), BigInt("-2"), 0x2n);
+testMul(BigInt("-1"), BigInt("-3"), 0x3n);
+testMul(BigInt("-1"), BigInt("-4660"), 0x1234n);
+testMul(BigInt("-1"), BigInt("-4275878551"), 0xFEDCBA97n);
+testMul(BigInt("-1"), BigInt("-4275878552"), 0xFEDCBA98n);
+testMul(BigInt("-1"), BigInt("-18364758544493064719"), 0xFEDCBA987654320Fn);
+testMul(BigInt("-1"), BigInt("-18364758544493064720"), 0xFEDCBA9876543210n);
+testMul(BigInt("-3"), BigInt("-3"), 0x9n);
+testMul(BigInt("-3"), BigInt("-4660"), 0x369Cn);
+testMul(BigInt("-3"), BigInt("-4275878551"), 0x2FC962FC5n);
+testMul(BigInt("-3"), BigInt("-4275878552"), 0x2FC962FC8n);
+testMul(BigInt("-3"), BigInt("-18364758544493064719"), 0x2FC962FC962FC962Dn);
+testMul(BigInt("-3"), BigInt("-18364758544493064720"), 0x2FC962FC962FC9630n);
+testMul(BigInt("-18364758544493064720"), BigInt("-18364758544493064720"), 0xFDBAC097C8DC5ACCDEEC6CD7A44A4100n);
+
diff --git a/JSTests/stress/big-int-multiply-memory-stress.js b/JSTests/stress/big-int-multiply-memory-stress.js
new file mode 100644 (file)
index 0000000..1abaa7e
--- /dev/null
@@ -0,0 +1,15 @@
+//@ runBigIntEnabled
+
+function assert(a) {
+    if (!a)
+        throw new Error("Bad assertion");
+}
+
+let a = 0n;
+let b = 1n;
+for (let i = 0; i < 1000000; i++) {
+    a = b * 30n;
+}
+
+assert(a === 30n);
+
index 6a1191d..14b34c1 100644 (file)
@@ -1,3 +1,42 @@
+2018-04-28  Caio Lima  <ticaiolima@gmail.com>
+
+        [ESNext][BigInt] Implement support for "*" operation
+        https://bugs.webkit.org/show_bug.cgi?id=183721
+
+        Reviewed by Saam Barati.
+
+        Added BigInt support into times binary operator into LLInt and on
+        JITOperations profiledMul and unprofiledMul. We are also replacing all
+        uses of int to unsigned when there is no negative values for
+        variables.
+
+        * dfg/DFGConstantFoldingPhase.cpp:
+        (JSC::DFG::ConstantFoldingPhase::foldConstants):
+        * jit/JITOperations.cpp:
+        * runtime/CommonSlowPaths.cpp:
+        (JSC::SLOW_PATH_DECL):
+        * runtime/JSBigInt.cpp:
+        (JSC::JSBigInt::JSBigInt):
+        (JSC::JSBigInt::allocationSize):
+        (JSC::JSBigInt::createWithLength):
+        (JSC::JSBigInt::toString):
+        (JSC::JSBigInt::multiply):
+        (JSC::JSBigInt::digitDiv):
+        (JSC::JSBigInt::internalMultiplyAdd):
+        (JSC::JSBigInt::multiplyAccumulate):
+        (JSC::JSBigInt::equals):
+        (JSC::JSBigInt::absoluteDivSmall):
+        (JSC::JSBigInt::calculateMaximumCharactersRequired):
+        (JSC::JSBigInt::toStringGeneric):
+        (JSC::JSBigInt::rightTrim):
+        (JSC::JSBigInt::allocateFor):
+        (JSC::JSBigInt::parseInt):
+        (JSC::JSBigInt::digit):
+        (JSC::JSBigInt::setDigit):
+        * runtime/JSBigInt.h:
+        * runtime/Operations.h:
+        (JSC::jsMul):
+
 2018-04-28  Commit Queue  <commit-queue@webkit.org>
 
         Unreviewed, rolling out r231131.
index cee6562..e4650ac 100644 (file)
@@ -146,14 +146,14 @@ private:
                     JSValue child2Constant = m_state.forNode(node->child2().node()).value();
 
                     // FIXME: Revisit this condition when introducing BigInt to JSC.
-                    auto isNonStringCellConstant = [] (JSValue value) {
-                        return value && value.isCell() && !value.isString();
+                    auto isNonStringOrBigIntCellConstant = [] (JSValue value) {
+                        return value && value.isCell() && !value.isString() && !value.isBigInt();
                     };
 
-                    if (isNonStringCellConstant(child1Constant)) {
+                    if (isNonStringOrBigIntCellConstant(child1Constant)) {
                         node->convertToCompareEqPtr(m_graph.freezeStrong(child1Constant.asCell()), node->child2());
                         changed = true;
-                    } else if (isNonStringCellConstant(child2Constant)) {
+                    } else if (isNonStringOrBigIntCellConstant(child2Constant)) {
                         node->convertToCompareEqPtr(m_graph.freezeStrong(child2Constant.asCell()), node->child1());
                         changed = true;
                     }
index 365bb73..62fc1b0 100644 (file)
@@ -2554,34 +2554,23 @@ EncodedJSValue JIT_OPERATION operationValueAddNoOptimize(ExecState* exec, Encode
     return JSValue::encode(result);
 }
 
-ALWAYS_INLINE static EncodedJSValue unprofiledMul(VM& vm, ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2)
+ALWAYS_INLINE static EncodedJSValue unprofiledMul(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2)
 {
-    auto scope = DECLARE_THROW_SCOPE(vm);
     JSValue op1 = JSValue::decode(encodedOp1);
     JSValue op2 = JSValue::decode(encodedOp2);
 
-    double a = op1.toNumber(exec);
-    RETURN_IF_EXCEPTION(scope, encodedJSValue());
-    scope.release();
-    double b = op2.toNumber(exec);
-    return JSValue::encode(jsNumber(a * b));
+    return JSValue::encode(jsMul(exec, op1, op2));
 }
 
-ALWAYS_INLINE static EncodedJSValue profiledMul(VM& vm, ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, ArithProfile& arithProfile, bool shouldObserveLHSAndRHSTypes = true)
+ALWAYS_INLINE static EncodedJSValue profiledMul(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, ArithProfile& arithProfile, bool shouldObserveLHSAndRHSTypes = true)
 {
-    auto scope = DECLARE_THROW_SCOPE(vm);
     JSValue op1 = JSValue::decode(encodedOp1);
     JSValue op2 = JSValue::decode(encodedOp2);
 
     if (shouldObserveLHSAndRHSTypes)
         arithProfile.observeLHSAndRHS(op1, op2);
 
-    double a = op1.toNumber(exec);
-    RETURN_IF_EXCEPTION(scope, encodedJSValue());
-    double b = op2.toNumber(exec);
-    RETURN_IF_EXCEPTION(scope, encodedJSValue());
-    
-    JSValue result = jsNumber(a * b);
+    JSValue result = jsMul(exec, op1, op2);
     arithProfile.observeResult(result);
     return JSValue::encode(result);
 }
@@ -2591,7 +2580,7 @@ EncodedJSValue JIT_OPERATION operationValueMul(ExecState* exec, EncodedJSValue e
     VM* vm = &exec->vm();
     NativeCallFrameTracer tracer(vm, exec);
 
-    return unprofiledMul(*vm, exec, encodedOp1, encodedOp2);
+    return unprofiledMul(exec, encodedOp1, encodedOp2);
 }
 
 EncodedJSValue JIT_OPERATION operationValueMulNoOptimize(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, JITMulIC*)
@@ -2599,7 +2588,7 @@ EncodedJSValue JIT_OPERATION operationValueMulNoOptimize(ExecState* exec, Encode
     VM* vm = &exec->vm();
     NativeCallFrameTracer tracer(vm, exec);
 
-    return unprofiledMul(*vm, exec, encodedOp1, encodedOp2);
+    return unprofiledMul(exec, encodedOp1, encodedOp2);
 }
 
 EncodedJSValue JIT_OPERATION operationValueMulOptimize(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, JITMulIC* mulIC)
@@ -2616,7 +2605,7 @@ EncodedJSValue JIT_OPERATION operationValueMulOptimize(ExecState* exec, EncodedJ
     exec->codeBlock()->dumpMathICStats();
 #endif
 
-    return unprofiledMul(*vm, exec, encodedOp1, encodedOp2);
+    return unprofiledMul(exec, encodedOp1, encodedOp2);
 }
 
 EncodedJSValue JIT_OPERATION operationValueMulProfiled(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, ArithProfile* arithProfile)
@@ -2625,7 +2614,7 @@ EncodedJSValue JIT_OPERATION operationValueMulProfiled(ExecState* exec, EncodedJ
     NativeCallFrameTracer tracer(vm, exec);
 
     ASSERT(arithProfile);
-    return profiledMul(*vm, exec, encodedOp1, encodedOp2, *arithProfile);
+    return profiledMul(exec, encodedOp1, encodedOp2, *arithProfile);
 }
 
 EncodedJSValue JIT_OPERATION operationValueMulProfiledOptimize(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, JITMulIC* mulIC)
@@ -2643,7 +2632,7 @@ EncodedJSValue JIT_OPERATION operationValueMulProfiledOptimize(ExecState* exec,
     exec->codeBlock()->dumpMathICStats();
 #endif
 
-    return profiledMul(*vm, exec, encodedOp1, encodedOp2, *arithProfile, false);
+    return profiledMul(exec, encodedOp1, encodedOp2, *arithProfile, false);
 }
 
 EncodedJSValue JIT_OPERATION operationValueMulProfiledNoOptimize(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, JITMulIC* mulIC)
@@ -2653,7 +2642,7 @@ EncodedJSValue JIT_OPERATION operationValueMulProfiledNoOptimize(ExecState* exec
 
     ArithProfile* arithProfile = mulIC->arithProfile();
     ASSERT(arithProfile);
-    return profiledMul(*vm, exec, encodedOp1, encodedOp2, *arithProfile);
+    return profiledMul(exec, encodedOp1, encodedOp2, *arithProfile);
 }
 
 ALWAYS_INLINE static EncodedJSValue unprofiledNegate(ExecState* exec, EncodedJSValue encodedOperand)
index e6c1a44..1c0391b 100644 (file)
@@ -486,10 +486,26 @@ SLOW_PATH_DECL(slow_path_mul)
     BEGIN();
     JSValue left = OP_C(2).jsValue();
     JSValue right = OP_C(3).jsValue();
-    double a = left.toNumber(exec);
-    if (UNLIKELY(throwScope.exception()))
-        RETURN(JSValue());
-    double b = right.toNumber(exec);
+    JSValue leftPrimitive = left.toPrimitive(exec, PreferNumber); 
+    CHECK_EXCEPTION();
+    JSValue rightPrimitive = right.toPrimitive(exec, PreferNumber);
+    CHECK_EXCEPTION();
+    
+    if (leftPrimitive.isBigInt() || rightPrimitive.isBigInt()) {
+        if (leftPrimitive.isBigInt() && rightPrimitive.isBigInt()) {
+            JSValue result(JSBigInt::multiply(exec, asBigInt(leftPrimitive), asBigInt(rightPrimitive)));
+            RETURN_WITH_PROFILING(result, {
+                updateArithProfileForBinaryArithOp(exec, pc, result, left, right);
+            });
+        }
+
+        THROW(createTypeError(exec, "Invalid mix of BigInt and other type in multiplication."));
+    }
+
+    double a = leftPrimitive.toNumber(exec);
+    CHECK_EXCEPTION();
+    double b = rightPrimitive.toNumber(exec);
+    CHECK_EXCEPTION();
     JSValue result = jsNumber(a * b);
     RETURN_WITH_PROFILING(result, {
         updateArithProfileForBinaryArithOp(exec, pc, result, left, right);
index c51c8b2..9197b9a 100644 (file)
@@ -69,7 +69,7 @@ void JSBigInt::visitChildren(JSCell* cell, SlotVisitor& visitor)
     Base::visitChildren(thisObject, visitor);
 }
 
-JSBigInt::JSBigInt(VM& vm, Structure* structure, int length)
+JSBigInt::JSBigInt(VM& vm, Structure* structure, unsigned length)
     : Base(vm, structure)
     , m_length(length)
 { }
@@ -93,13 +93,13 @@ JSBigInt* JSBigInt::createZero(VM& vm)
     return zeroBigInt;
 }
 
-size_t JSBigInt::allocationSize(int length)
+size_t JSBigInt::allocationSize(unsigned length)
 {
     size_t sizeWithPadding = WTF::roundUpToMultipleOf<sizeof(size_t)>(sizeof(JSBigInt));
     return sizeWithPadding + length * sizeof(Digit);
 }
 
-JSBigInt* JSBigInt::createWithLength(VM& vm, int length)
+JSBigInt* JSBigInt::createWithLength(VM& vm, unsigned length)
 {
     JSBigInt* bigInt = new (NotNull, allocateCell<JSBigInt>(vm.heap, allocationSize(length))) JSBigInt(vm, vm.bigIntStructure.get(), length);
     bigInt->finishCreation(vm);
@@ -223,7 +223,7 @@ JSBigInt* JSBigInt::parseInt(ExecState* state, VM& vm, StringView s, uint8_t rad
     return parseInt(state, vm, s.characters16(), s.length(), 0, radix, false);
 }
 
-String JSBigInt::toString(ExecState& state, int radix)
+String JSBigInt::toString(ExecState& state, unsigned radix)
 {
     if (this->isZero())
         return state.vm().smallStrings.singleCharacterStringRep('0');
@@ -246,6 +246,26 @@ void JSBigInt::inplaceMultiplyAdd(uintptr_t factor, uintptr_t summand)
     internalMultiplyAdd(this, factor, summand, length(), this);
 }
 
+JSBigInt* JSBigInt::multiply(ExecState* state, JSBigInt* x, JSBigInt* y)
+{
+    VM& vm = state->vm();
+
+    if (x->isZero())
+        return x;
+    if (y->isZero())
+        return y;
+
+    unsigned resultLength = x->length() + y->length();
+    JSBigInt* result = JSBigInt::createWithLength(vm, resultLength);
+    result->initialize(InitializationType::WithZero);
+
+    for (unsigned i = 0; i < x->length(); i++)
+        multiplyAccumulate(y, x->digit(i), result, i);
+
+    result->setSign(x->sign() != y->sign());
+    return result->rightTrim(vm);
+}
+
 #if USE(JSVALUE32_64)
 #define HAVE_TWO_DIGIT 1
 typedef uint64_t TwoDigit;
@@ -378,9 +398,9 @@ JSBigInt::Digit JSBigInt::digitDiv(Digit high, Digit low, Digit divisor, Digit&
     static const Digit kHalfDigitBase = 1ull << halfDigitBits;
     // Adapted from Warren, Hacker's Delight, p. 152.
 #if USE(JSVALUE64)
-    int s = clz64(divisor);
+    unsigned s = clz64(divisor);
 #else
-    int s = clz32(divisor);
+    unsigned s = clz32(divisor);
 #endif
     divisor <<= s;
     
@@ -423,14 +443,14 @@ JSBigInt::Digit JSBigInt::digitDiv(Digit high, Digit low, Digit divisor, Digit&
 
 // Multiplies {source} with {factor} and adds {summand} to the result.
 // {result} and {source} may be the same BigInt for inplace modification.
-void JSBigInt::internalMultiplyAdd(JSBigInt* source, Digit factor, Digit summand, int n, JSBigInt* result)
+void JSBigInt::internalMultiplyAdd(JSBigInt* source, Digit factor, Digit summand, unsigned n, JSBigInt* result)
 {
     ASSERT(source->length() >= n);
     ASSERT(result->length() >= n);
 
     Digit carry = summand;
     Digit high = 0;
-    for (int i = 0; i < n; i++) {
+    for (unsigned i = 0; i < n; i++) {
         Digit current = source->digit(i);
         Digit newCarry = 0;
 
@@ -458,6 +478,49 @@ void JSBigInt::internalMultiplyAdd(JSBigInt* source, Digit factor, Digit summand
         ASSERT(!(carry + high));
 }
 
+// Multiplies {multiplicand} with {multiplier} and adds the result to
+// {accumulator}, starting at {accumulatorIndex} for the least-significant
+// digit.
+// Callers must ensure that {accumulator} is big enough to hold the result.
+void JSBigInt::multiplyAccumulate(JSBigInt* multiplicand, Digit multiplier, JSBigInt* accumulator, unsigned accumulatorIndex)
+{
+    ASSERT(accumulator->length() > multiplicand->length() + accumulatorIndex);
+    if (!multiplier)
+        return;
+    
+    Digit carry = 0;
+    Digit high = 0;
+    for (unsigned i = 0; i < multiplicand->length(); i++, accumulatorIndex++) {
+        Digit acc = accumulator->digit(accumulatorIndex);
+        Digit newCarry = 0;
+        
+        // Add last round's carryovers.
+        acc = digitAdd(acc, high, newCarry);
+        acc = digitAdd(acc, carry, newCarry);
+        
+        // Compute this round's multiplication.
+        Digit multiplicandDigit = multiplicand->digit(i);
+        Digit low = digitMul(multiplier, multiplicandDigit, high);
+        acc = digitAdd(acc, low, newCarry);
+        
+        // Store result and prepare for next round.
+        accumulator->setDigit(accumulatorIndex, acc);
+        carry = newCarry;
+    }
+    
+    while (carry || high) {
+        ASSERT(accumulatorIndex < accumulator->length());
+        Digit acc = accumulator->digit(accumulatorIndex);
+        Digit newCarry = 0;
+        acc = digitAdd(acc, high, newCarry);
+        high = 0;
+        acc = digitAdd(acc, carry, newCarry);
+        accumulator->setDigit(accumulatorIndex, acc);
+        carry = newCarry;
+        accumulatorIndex++;
+    }
+}
+
 bool JSBigInt::equals(JSBigInt* x, JSBigInt* y)
 {
     if (x->sign() != y->sign())
@@ -466,7 +529,7 @@ bool JSBigInt::equals(JSBigInt* x, JSBigInt* y)
     if (x->length() != y->length())
         return false;
 
-    for (int i = 0; i < x->length(); i++) {
+    for (unsigned i = 0; i < x->length(); i++) {
         if (x->digit(i) != y->digit(i))
             return false;
     }
@@ -494,7 +557,7 @@ void JSBigInt::absoluteDivSmall(ExecState& state, JSBigInt* x, Digit divisor, JS
         return;
     }
 
-    int length = x->length();
+    unsigned length = x->length();
     if (quotient != nullptr) {
         if (*quotient == nullptr)
             *quotient = JSBigInt::createWithLength(vm, length);
@@ -521,14 +584,14 @@ constexpr uint8_t maxBitsPerCharTable[] = {
     162, 163, 165, 166,                         // 33..36
 };
 
-static const int bitsPerCharTableShift = 5;
+static const unsigned bitsPerCharTableShift = 5;
 static const size_t bitsPerCharTableMultiplier = 1u << bitsPerCharTableShift;
 
 // Compute (an overapproximation of) the length of the resulting string:
 // Divide bit length of the BigInt by bits representable per character.
-uint64_t JSBigInt::calculateMaximumCharactersRequired(int length, int radix, Digit lastDigit, bool sign)
+uint64_t JSBigInt::calculateMaximumCharactersRequired(unsigned length, unsigned radix, Digit lastDigit, bool sign)
 {
-    int leadingZeros;
+    unsigned leadingZeros;
     if (sizeof(lastDigit) == 8)
         leadingZeros = clz64(lastDigit);
     else
@@ -556,7 +619,7 @@ uint64_t JSBigInt::calculateMaximumCharactersRequired(int length, int radix, Dig
     return maximumCharactersRequired;
 }
 
-String JSBigInt::toStringGeneric(ExecState& state, JSBigInt* x, int radix)
+String JSBigInt::toStringGeneric(ExecState& state, JSBigInt* x, unsigned radix)
 {
     // FIXME: [JSC] Revisit usage of StringVector into JSBigInt::toString
     // https://bugs.webkit.org/show_bug.cgi?id=18067
@@ -565,7 +628,7 @@ String JSBigInt::toStringGeneric(ExecState& state, JSBigInt* x, int radix)
     ASSERT(radix >= 2 && radix <= 36);
     ASSERT(!x->isZero());
 
-    int length = x->length();
+    unsigned length = x->length();
     bool sign = x->sign();
 
     uint8_t maxBitsPerChar = maxBitsPerCharTable[radix];
@@ -581,12 +644,12 @@ String JSBigInt::toStringGeneric(ExecState& state, JSBigInt* x, int radix)
     if (length == 1)
         lastDigit = x->digit(0);
     else {
-        int chunkChars = digitBits * bitsPerCharTableMultiplier / maxBitsPerChar;
+        unsigned chunkChars = digitBits * bitsPerCharTableMultiplier / maxBitsPerChar;
         Digit chunkDivisor = digitPow(radix, chunkChars);
 
         // By construction of chunkChars, there can't have been overflow.
         ASSERT(chunkDivisor);
-        int nonZeroDigit = length - 1;
+        unsigned nonZeroDigit = length - 1;
         ASSERT(x->digit(nonZeroDigit));
 
         // {rest} holds the part of the BigInt that we haven't looked at yet.
@@ -602,7 +665,7 @@ String JSBigInt::toStringGeneric(ExecState& state, JSBigInt* x, int radix)
             ASSERT(rest);
 
             dividend = &rest;
-            for (int i = 0; i < chunkChars; i++) {
+            for (unsigned i = 0; i < chunkChars; i++) {
                 resultString.append(radixDigits[chunk % radix]);
                 chunk /= radix;
             }
@@ -627,7 +690,7 @@ String JSBigInt::toStringGeneric(ExecState& state, JSBigInt* x, int radix)
     ASSERT(resultString.size() <= static_cast<size_t>(maximumCharactersRequired));
 
     // Remove leading zeroes.
-    int newSizeNoLeadingZeroes = resultString.size();
+    unsigned newSizeNoLeadingZeroes = resultString.size();
     while (newSizeNoLeadingZeroes  > 1 && resultString[newSizeNoLeadingZeroes - 1] == '0')
         newSizeNoLeadingZeroes--;
 
@@ -646,14 +709,16 @@ JSBigInt* JSBigInt::rightTrim(VM& vm)
     if (isZero())
         return this;
 
+    ASSERT(m_length);
+
     int nonZeroIndex = m_length - 1;
     while (nonZeroIndex >= 0 && !digit(nonZeroIndex))
         nonZeroIndex--;
 
-    if (nonZeroIndex == m_length - 1)
+    if (nonZeroIndex == static_cast<int>(m_length - 1))
         return this;
 
-    int newLength = nonZeroIndex + 1;
+    unsigned newLength = nonZeroIndex + 1;
     JSBigInt* trimmedBigInt = createWithLength(vm, newLength);
     RELEASE_ASSERT(trimmedBigInt);
     std::copy(dataStorage(), dataStorage() + newLength, trimmedBigInt->dataStorage()); 
@@ -663,14 +728,14 @@ JSBigInt* JSBigInt::rightTrim(VM& vm)
     return trimmedBigInt;
 }
 
-JSBigInt* JSBigInt::allocateFor(ExecState* state, VM& vm, int radix, int charcount)
+JSBigInt* JSBigInt::allocateFor(ExecState* state, VM& vm, unsigned radix, unsigned charcount)
 {
     ASSERT(2 <= radix && radix <= 36);
     ASSERT(charcount >= 0);
 
     size_t bitsPerChar = maxBitsPerCharTable[radix];
     size_t chars = charcount;
-    const int roundup = bitsPerCharTableMultiplier - 1;
+    const unsigned roundup = bitsPerCharTableMultiplier - 1;
     if (chars <= (std::numeric_limits<size_t>::max() - roundup) / bitsPerChar) {
         size_t bitsMin = bitsPerChar * chars;
 
@@ -678,7 +743,7 @@ JSBigInt* JSBigInt::allocateFor(ExecState* state, VM& vm, int radix, int charcou
         bitsMin = (bitsMin + roundup) >> bitsPerCharTableShift;
         if (bitsMin <= static_cast<size_t>(maxInt)) {
             // Divide by kDigitsBits, rounding up.
-            int length = (static_cast<int>(bitsMin) + digitBits - 1) / digitBits;
+            unsigned length = (bitsMin + digitBits - 1) / digitBits;
             if (length <= maxLength) {
                 JSBigInt* result = JSBigInt::createWithLength(vm, length);
                 return result;
@@ -719,11 +784,11 @@ 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)
 {
     VM& vm = state->vm();
 
-    int p = 0;
+    unsigned p = 0;
     while (p < length && isStrWhiteSpace(data[p]))
         ++p;
 
@@ -758,10 +823,10 @@ 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, bool allowEmptyString)
 {
     ASSERT(length >= 0);
-    int p = startIndex;
+    unsigned p = startIndex;
 
     auto scope = DECLARE_THROW_SCOPE(vm);
 
@@ -777,7 +842,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;
@@ -785,16 +850,16 @@ JSBigInt* JSBigInt::parseInt(ExecState* state, VM& vm, CharType* data, int lengt
     if (p == length)
         return createZero(vm);
 
-    int limit0 = '0' + (radix < 10 ? radix : 10);
-    int limita = 'a' + (radix - 10);
-    int limitA = 'A' + (radix - 10);
+    unsigned limit0 = '0' + (radix < 10 ? radix : 10);
+    unsigned limita = 'a' + (radix - 10);
+    unsigned limitA = 'A' + (radix - 10);
 
     JSBigInt* result = allocateFor(state, vm, radix, length - p);
     RETURN_IF_EXCEPTION(scope, nullptr);
 
     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';
@@ -822,13 +887,13 @@ JSBigInt::Digit* JSBigInt::dataStorage()
     return reinterpret_cast<Digit*>(reinterpret_cast<char*>(this) + offsetOfData());
 }
 
-JSBigInt::Digit JSBigInt::digit(int n)
+JSBigInt::Digit JSBigInt::digit(unsigned n)
 {
     ASSERT(n >= 0 && n < length());
     return dataStorage()[n];
 }
 
-void JSBigInt::setDigit(int n, Digit value)
+void JSBigInt::setDigit(unsigned n, Digit value)
 {
     ASSERT(n >= 0 && n < length());
     dataStorage()[n] = value;
index 298c348..eb12c00 100644 (file)
@@ -41,7 +41,7 @@ class JSBigInt final : public JSCell {
 
 public:
 
-    JSBigInt(VM&, Structure*, int length);
+    JSBigInt(VM&, Structure*, unsigned length);
 
     enum class InitializationType { None, WithZero };
     void initialize(InitializationType);
@@ -49,12 +49,11 @@ public:
     static void visitChildren(JSCell*, SlotVisitor&);
 
     static size_t estimatedSize(JSCell*);
-    static size_t allocationSize(int length);
+    static size_t allocationSize(unsigned length);
 
     static Structure* createStructure(VM&, JSGlobalObject*, JSValue prototype);
-
     static JSBigInt* createZero(VM&);
-    static JSBigInt* createWithLength(VM&, int length);
+    static JSBigInt* createWithLength(VM&, unsigned length);
 
     static JSBigInt* createFrom(VM&, int32_t value);
     static JSBigInt* createFrom(VM&, uint32_t value);
@@ -70,14 +69,14 @@ public:
     void setSign(bool sign) { m_sign = sign; }
     bool sign() const { return m_sign; }
 
-    void setLength(int length) { m_length = length; }
-    int length() const { return m_length; }
+    void setLength(unsigned length) { m_length = length; }
+    unsigned length() const { return m_length; }
 
     static JSBigInt* parseInt(ExecState*, VM&, StringView, uint8_t radix);
     static JSBigInt* parseInt(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*);
 
@@ -85,12 +84,14 @@ public:
     double toNumber(ExecState*) const;
 
     JSObject* toObject(ExecState*, JSGlobalObject*) const;
+
+    static JSBigInt* multiply(ExecState*, JSBigInt* x, JSBigInt* y);
     
 private:
     using Digit = uintptr_t;
-    static constexpr const int bitsPerByte = 8;
-    static constexpr const int digitBits = sizeof(Digit) * bitsPerByte;
-    static constexpr const int halfDigitBits = digitBits / 2;
+    static constexpr const unsigned bitsPerByte = 8;
+    static constexpr const unsigned digitBits = sizeof(Digit) * bitsPerByte;
+    static constexpr const unsigned halfDigitBits = digitBits / 2;
     static constexpr const Digit halfDigitMask = (1ull << halfDigitBits) - 1;
     static constexpr const int maxInt = 0x7FFFFFFF;
     
@@ -98,12 +99,13 @@ private:
     // maxInt / digitBits. However, we use a lower limit for now, because
     // raising it later is easier than lowering it.
     // Support up to 1 million bits.
-    static const int maxLength = 1024 * 1024 / (sizeof(void*) * bitsPerByte);
+    static const unsigned maxLength = 1024 * 1024 / (sizeof(void*) * bitsPerByte);
     
-    static uint64_t calculateMaximumCharactersRequired(int length, int radix, Digit lastDigit, bool sign);
+    static uint64_t calculateMaximumCharactersRequired(unsigned length, unsigned radix, Digit lastDigit, bool sign);
     
     static void absoluteDivSmall(ExecState&, JSBigInt* x, Digit divisor, JSBigInt** quotient, Digit& remainder);
-    static void internalMultiplyAdd(JSBigInt* source, Digit factor, Digit summand, int, JSBigInt* result);
+    static void internalMultiplyAdd(JSBigInt* source, Digit factor, Digit summand, unsigned, JSBigInt* result);
+    static void multiplyAccumulate(JSBigInt* multiplicand, Digit multiplier, JSBigInt* accumulator, unsigned accumulatorIndex);
 
     // Digit arithmetic helpers.
     static Digit digitAdd(Digit a, Digit b, Digit& carry);
@@ -112,17 +114,17 @@ private:
     static Digit digitDiv(Digit high, Digit low, Digit divisor, Digit& remainder);
     static Digit digitPow(Digit base, Digit exponent);
 
-    static String toStringGeneric(ExecState&, JSBigInt*, int radix);
+    static String toStringGeneric(ExecState&, JSBigInt*, unsigned radix);
 
     bool isZero();
 
     template <typename CharType>
-    static JSBigInt* parseInt(ExecState*, CharType*  data, int length);
+    static JSBigInt* parseInt(ExecState*, CharType*  data, unsigned length);
 
     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, bool allowEmptyString = true);
 
-    static JSBigInt* allocateFor(ExecState*, VM&, int radix, int charcount);
+    static JSBigInt* allocateFor(ExecState*, VM&, unsigned radix, unsigned charcount);
 
     JSBigInt* rightTrim(VM&);
 
@@ -131,10 +133,10 @@ private:
     static size_t offsetOfData();
     Digit* dataStorage();
 
-    Digit digit(int);
-    void setDigit(int, Digit);
+    Digit digit(unsigned);
+    void setDigit(unsigned, Digit);
         
-    int m_length;
+    unsigned m_length;
     bool m_sign;
 };
 
index 1349aec..e265815 100644 (file)
@@ -23,6 +23,7 @@
 
 #include "CallFrame.h"
 #include "ExceptionHelpers.h"
+#include "JSBigInt.h"
 #include "JSCJSValue.h"
 
 namespace JSC {
@@ -256,6 +257,31 @@ ALWAYS_INLINE JSValue jsAdd(CallFrame* callFrame, JSValue v1, JSValue v2)
     return jsAddSlowCase(callFrame, v1, v2);
 }
 
+ALWAYS_INLINE JSValue jsMul(ExecState* state, JSValue v1, JSValue v2)
+{
+    VM& vm = state->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
+    JSValue leftNumber = v1.toPrimitive(state, PreferNumber);
+    RETURN_IF_EXCEPTION(scope, { });
+    JSValue rightNumber = v2.toPrimitive(state, PreferNumber);
+    RETURN_IF_EXCEPTION(scope, { });
+
+    if (leftNumber.isBigInt() || rightNumber.isBigInt()) {
+        if (leftNumber.isBigInt() && rightNumber.isBigInt())
+            return JSBigInt::multiply(state, asBigInt(leftNumber), asBigInt(rightNumber));
+
+        throwTypeError(state, scope, ASCIILiteral("Invalid mix of BigInt and other type in multiplication."));
+        return { };
+    }
+
+    double leftValue =  leftNumber.toNumber(state);
+    RETURN_IF_EXCEPTION(scope, { });
+    double rightValue =  rightNumber.toNumber(state);
+    RETURN_IF_EXCEPTION(scope, { });
+    return jsNumber(leftValue * rightValue);
+}
+
 inline bool scribbleFreeCells()
 {
     return !ASSERT_DISABLED || Options::scribbleFreeCells();