[BigInt] JSBigInt::createWithLength should throw when length is greater than JSBigInt...
authorticaiolima@gmail.com <ticaiolima@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 13 Nov 2018 15:46:34 +0000 (15:46 +0000)
committerticaiolima@gmail.com <ticaiolima@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 13 Nov 2018 15:46:34 +0000 (15:46 +0000)
https://bugs.webkit.org/show_bug.cgi?id=190836

Reviewed by Saam Barati.

JSTests:

* stress/big-int-out-of-memory-tests.js: Added.

Source/JavaScriptCore:

In this patch we are creating a new method called `JSBigInt::createWithLengthUnchecked`
where we allocate a BigInt trusting the length received as argument.
With this additional method, we now check if length passed to
`JSBigInt::createWithLength` is not greater than JSBigInt::maxLength.
When the length is greater than maxLength, we then throw OOM
exception.
This required change the interface of some JSBigInt operations to
receive `ExecState*` instead of `VM&`. We changed only operations that
can throw because of OOM.
We beleive that this approach of throwing instead of finishing the
execution abruptly is better because JS programs can catch such
exception and handle this issue properly.

* dfg/DFGOperations.cpp:
* jit/JITOperations.cpp:
* runtime/CommonSlowPaths.cpp:
(JSC::SLOW_PATH_DECL):
* runtime/JSBigInt.cpp:
(JSC::JSBigInt::createZero):
(JSC::JSBigInt::tryCreateWithLength):
(JSC::JSBigInt::createWithLengthUnchecked):
(JSC::JSBigInt::createFrom):
(JSC::JSBigInt::multiply):
(JSC::JSBigInt::divide):
(JSC::JSBigInt::copy):
(JSC::JSBigInt::unaryMinus):
(JSC::JSBigInt::remainder):
(JSC::JSBigInt::add):
(JSC::JSBigInt::sub):
(JSC::JSBigInt::bitwiseAnd):
(JSC::JSBigInt::bitwiseOr):
(JSC::JSBigInt::bitwiseXor):
(JSC::JSBigInt::absoluteAdd):
(JSC::JSBigInt::absoluteSub):
(JSC::JSBigInt::absoluteDivWithDigitDivisor):
(JSC::JSBigInt::absoluteDivWithBigIntDivisor):
(JSC::JSBigInt::absoluteLeftShiftAlwaysCopy):
(JSC::JSBigInt::absoluteBitwiseOp):
(JSC::JSBigInt::absoluteAddOne):
(JSC::JSBigInt::absoluteSubOne):
(JSC::JSBigInt::toStringGeneric):
(JSC::JSBigInt::rightTrim):
(JSC::JSBigInt::allocateFor):
(JSC::JSBigInt::createWithLength): Deleted.
* runtime/JSBigInt.h:
* runtime/Operations.cpp:
(JSC::jsAddSlowCase):
* runtime/Operations.h:
(JSC::jsSub):
(JSC::jsMul):

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

JSTests/ChangeLog
JSTests/stress/big-int-out-of-memory-tests.js [new file with mode: 0644]
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/dfg/DFGOperations.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.cpp
Source/JavaScriptCore/runtime/Operations.h

index 77dc666..436683e 100644 (file)
@@ -1,3 +1,12 @@
+2018-11-13  Caio Lima  <ticaiolima@gmail.com>
+
+        [BigInt] JSBigInt::createWithLength should throw when length is greater than JSBigInt::maxLength
+        https://bugs.webkit.org/show_bug.cgi?id=190836
+
+        Reviewed by Saam Barati.
+
+        * stress/big-int-out-of-memory-tests.js: Added.
+
 2018-11-08  Ross Kirsling  <ross.kirsling@sony.com>
 
         U+180E is no longer a whitespace character
diff --git a/JSTests/stress/big-int-out-of-memory-tests.js b/JSTests/stress/big-int-out-of-memory-tests.js
new file mode 100644 (file)
index 0000000..ac39387
--- /dev/null
@@ -0,0 +1,63 @@
+//@ runDefault("--useBigInt=true", "--useDFGJIT=false")
+
+function assert(a, message) {
+    if (!a)
+        throw new Error(message);
+}
+
+function lshift(y) {
+    let out = 1n;
+    for (let i = 0; i < y; i++) {
+        out *= 2n;
+    }
+
+    return out;
+}
+
+let a = lshift(16384 * 63);
+for (let i = 0; i < 256; i++) {
+    a *= 18446744073709551615n;
+}
+
+try {
+    let b = a + 1n;
+    assert(false, "Should throw OutOfMemoryError, but executed without exception");
+} catch(e) {
+    assert(e.message == "Out of memory", "Expected OutOfMemoryError, but got: " + e);
+}
+
+try {
+    let b = a - (-1n);
+    assert(false, "Should throw OutOfMemoryError, but executed without exception");
+} catch(e) {
+    assert(e.message == "Out of memory", "Expected OutOfMemoryError, but got: " + e);
+}
+
+try {
+    let b = a * (-1n);
+    assert(false, "Should throw OutOfMemoryError, but executed without exception");
+} catch(e) {
+    assert(e.message == "Out of memory", "Expected OutOfMemoryError, but got: " + e);
+}
+
+try {
+    let b = a / a;
+    assert(false, "Should throw OutOfMemoryError, but executed without exception");
+} catch(e) {
+    assert(e.message == "Out of memory", "Expected OutOfMemoryError, but got: " + e);
+}
+
+try {
+    let b = -a & -1n;
+    assert(false, "Should throw OutOfMemoryError, but executed without exception");
+} catch(e) {
+    assert(e.message == "Out of memory", "Expected OutOfMemoryError, but got: " + e);
+}
+
+try {
+    let b = a ^ -1n;
+    assert(false, "Should throw OutOfMemoryError, but executed without exception");
+} catch(e) {
+    assert(e.message == "Out of memory", "Expected OutOfMemoryError, but got: " + e);
+}
+
index 10158d4..f8e59b6 100644 (file)
@@ -1,3 +1,61 @@
+2018-11-13  Caio Lima  <ticaiolima@gmail.com>
+
+        [BigInt] JSBigInt::createWithLength should throw when length is greater than JSBigInt::maxLength
+        https://bugs.webkit.org/show_bug.cgi?id=190836
+
+        Reviewed by Saam Barati.
+
+        In this patch we are creating a new method called `JSBigInt::createWithLengthUnchecked`
+        where we allocate a BigInt trusting the length received as argument.
+        With this additional method, we now check if length passed to
+        `JSBigInt::createWithLength` is not greater than JSBigInt::maxLength.
+        When the length is greater than maxLength, we then throw OOM
+        exception.
+        This required change the interface of some JSBigInt operations to
+        receive `ExecState*` instead of `VM&`. We changed only operations that
+        can throw because of OOM.
+        We beleive that this approach of throwing instead of finishing the
+        execution abruptly is better because JS programs can catch such
+        exception and handle this issue properly.
+
+        * dfg/DFGOperations.cpp:
+        * jit/JITOperations.cpp:
+        * runtime/CommonSlowPaths.cpp:
+        (JSC::SLOW_PATH_DECL):
+        * runtime/JSBigInt.cpp:
+        (JSC::JSBigInt::createZero):
+        (JSC::JSBigInt::tryCreateWithLength):
+        (JSC::JSBigInt::createWithLengthUnchecked):
+        (JSC::JSBigInt::createFrom):
+        (JSC::JSBigInt::multiply):
+        (JSC::JSBigInt::divide):
+        (JSC::JSBigInt::copy):
+        (JSC::JSBigInt::unaryMinus):
+        (JSC::JSBigInt::remainder):
+        (JSC::JSBigInt::add):
+        (JSC::JSBigInt::sub):
+        (JSC::JSBigInt::bitwiseAnd):
+        (JSC::JSBigInt::bitwiseOr):
+        (JSC::JSBigInt::bitwiseXor):
+        (JSC::JSBigInt::absoluteAdd):
+        (JSC::JSBigInt::absoluteSub):
+        (JSC::JSBigInt::absoluteDivWithDigitDivisor):
+        (JSC::JSBigInt::absoluteDivWithBigIntDivisor):
+        (JSC::JSBigInt::absoluteLeftShiftAlwaysCopy):
+        (JSC::JSBigInt::absoluteBitwiseOp):
+        (JSC::JSBigInt::absoluteAddOne):
+        (JSC::JSBigInt::absoluteSubOne):
+        (JSC::JSBigInt::toStringGeneric):
+        (JSC::JSBigInt::rightTrim):
+        (JSC::JSBigInt::allocateFor):
+        (JSC::JSBigInt::createWithLength): Deleted.
+        * runtime/JSBigInt.h:
+        * runtime/Operations.cpp:
+        (JSC::jsAddSlowCase):
+        * runtime/Operations.h:
+        (JSC::jsSub):
+        (JSC::jsMul):
+
 2018-11-12  Devin Rousso  <drousso@apple.com>
 
         Web Inspector: Network: show secure certificate details per-request
index df3fd1e..5417fb0 100644 (file)
@@ -349,7 +349,7 @@ EncodedJSValue JIT_OPERATION operationValueBitAnd(ExecState* exec, EncodedJSValu
 
     if (WTF::holds_alternative<JSBigInt*>(leftNumeric) || WTF::holds_alternative<JSBigInt*>(rightNumeric)) {
         if (WTF::holds_alternative<JSBigInt*>(leftNumeric) && WTF::holds_alternative<JSBigInt*>(rightNumeric)) {
-            JSBigInt* result = JSBigInt::bitwiseAnd(*vm, WTF::get<JSBigInt*>(leftNumeric), WTF::get<JSBigInt*>(rightNumeric));
+            JSBigInt* result = JSBigInt::bitwiseAnd(exec, WTF::get<JSBigInt*>(leftNumeric), WTF::get<JSBigInt*>(rightNumeric));
             RETURN_IF_EXCEPTION(scope, encodedJSValue());
             return JSValue::encode(result);
         }
@@ -376,7 +376,7 @@ EncodedJSValue JIT_OPERATION operationValueBitOr(ExecState* exec, EncodedJSValue
 
     if (WTF::holds_alternative<JSBigInt*>(leftNumeric) || WTF::holds_alternative<JSBigInt*>(rightNumeric)) {
         if (WTF::holds_alternative<JSBigInt*>(leftNumeric) && WTF::holds_alternative<JSBigInt*>(rightNumeric)) {
-            JSBigInt* result = JSBigInt::bitwiseOr(*vm, WTF::get<JSBigInt*>(leftNumeric), WTF::get<JSBigInt*>(rightNumeric));
+            JSBigInt* result = JSBigInt::bitwiseOr(exec, WTF::get<JSBigInt*>(leftNumeric), WTF::get<JSBigInt*>(rightNumeric));
             RETURN_IF_EXCEPTION(scope, encodedJSValue());
             return JSValue::encode(result);
         }
@@ -1294,7 +1294,7 @@ JSCell* JIT_OPERATION operationSubBigInt(ExecState* exec, JSCell* op1, JSCell* o
     JSBigInt* leftOperand = jsCast<JSBigInt*>(op1);
     JSBigInt* rightOperand = jsCast<JSBigInt*>(op2);
     
-    return JSBigInt::sub(*vm, leftOperand, rightOperand);
+    return JSBigInt::sub(exec, leftOperand, rightOperand);
 }
 
 JSCell* JIT_OPERATION operationBitAndBigInt(ExecState* exec, JSCell* op1, JSCell* op2)
@@ -1305,7 +1305,7 @@ JSCell* JIT_OPERATION operationBitAndBigInt(ExecState* exec, JSCell* op1, JSCell
     JSBigInt* leftOperand = jsCast<JSBigInt*>(op1);
     JSBigInt* rightOperand = jsCast<JSBigInt*>(op2);
 
-    return JSBigInt::bitwiseAnd(*vm, leftOperand, rightOperand);
+    return JSBigInt::bitwiseAnd(exec, leftOperand, rightOperand);
 }
 
 JSCell* JIT_OPERATION operationAddBigInt(ExecState* exec, JSCell* op1, JSCell* op2)
@@ -1316,7 +1316,7 @@ JSCell* JIT_OPERATION operationAddBigInt(ExecState* exec, JSCell* op1, JSCell* o
     JSBigInt* leftOperand = jsCast<JSBigInt*>(op1);
     JSBigInt* rightOperand = jsCast<JSBigInt*>(op2);
     
-    return JSBigInt::add(*vm, leftOperand, rightOperand);
+    return JSBigInt::add(exec, leftOperand, rightOperand);
 }
 
 JSCell* JIT_OPERATION operationBitOrBigInt(ExecState* exec, JSCell* op1, JSCell* op2)
@@ -1327,7 +1327,7 @@ JSCell* JIT_OPERATION operationBitOrBigInt(ExecState* exec, JSCell* op1, JSCell*
     JSBigInt* leftOperand = jsCast<JSBigInt*>(op1);
     JSBigInt* rightOperand = jsCast<JSBigInt*>(op2);
     
-    return JSBigInt::bitwiseOr(*vm, leftOperand, rightOperand);
+    return JSBigInt::bitwiseOr(exec, leftOperand, rightOperand);
 }
 
 size_t JIT_OPERATION operationCompareStrictEqCell(ExecState* exec, JSCell* op1, JSCell* op2)
index 1e8fe00..511c42f 100644 (file)
@@ -2766,10 +2766,8 @@ EncodedJSValue JIT_OPERATION operationArithNegateOptimize(ExecState* exec, Encod
     JSValue primValue = operand.toPrimitive(exec);
     RETURN_IF_EXCEPTION(scope, encodedJSValue());
     
-    if (primValue.isBigInt()) {
-        JSBigInt* result = JSBigInt::unaryMinus(vm, asBigInt(primValue));
-        return JSValue::encode(result);
-    }
+    if (primValue.isBigInt())
+        return JSValue::encode(JSBigInt::unaryMinus(vm, asBigInt(primValue)));
 
     double number = primValue.toNumber(exec);
     RETURN_IF_EXCEPTION(scope, encodedJSValue());
index 5163c33..ee5ada2 100644 (file)
@@ -563,7 +563,8 @@ SLOW_PATH_DECL(slow_path_sub)
 
     if (WTF::holds_alternative<JSBigInt*>(leftNumeric) || WTF::holds_alternative<JSBigInt*>(rightNumeric)) {
         if (WTF::holds_alternative<JSBigInt*>(leftNumeric) && WTF::holds_alternative<JSBigInt*>(rightNumeric)) {
-            JSBigInt* result = JSBigInt::sub(vm, WTF::get<JSBigInt*>(leftNumeric), WTF::get<JSBigInt*>(rightNumeric));
+            JSBigInt* result = JSBigInt::sub(exec, WTF::get<JSBigInt*>(leftNumeric), WTF::get<JSBigInt*>(rightNumeric));
+            CHECK_EXCEPTION();
             RETURN_WITH_PROFILING(result, {
                 updateArithProfileForBinaryArithOp(exec, pc, result, left, right);
             });
@@ -699,7 +700,7 @@ SLOW_PATH_DECL(slow_path_bitand)
     CHECK_EXCEPTION();
     if (WTF::holds_alternative<JSBigInt*>(leftNumeric) || WTF::holds_alternative<JSBigInt*>(rightNumeric)) {
         if (WTF::holds_alternative<JSBigInt*>(leftNumeric) && WTF::holds_alternative<JSBigInt*>(rightNumeric)) {
-            JSBigInt* result = JSBigInt::bitwiseAnd(vm, WTF::get<JSBigInt*>(leftNumeric), WTF::get<JSBigInt*>(rightNumeric));
+            JSBigInt* result = JSBigInt::bitwiseAnd(exec, WTF::get<JSBigInt*>(leftNumeric), WTF::get<JSBigInt*>(rightNumeric));
             CHECK_EXCEPTION();
             RETURN_PROFILED(result);
         }
@@ -720,7 +721,7 @@ SLOW_PATH_DECL(slow_path_bitor)
     CHECK_EXCEPTION();
     if (WTF::holds_alternative<JSBigInt*>(leftNumeric) || WTF::holds_alternative<JSBigInt*>(rightNumeric)) {
         if (WTF::holds_alternative<JSBigInt*>(leftNumeric) && WTF::holds_alternative<JSBigInt*>(rightNumeric)) {
-            JSBigInt* result = JSBigInt::bitwiseOr(vm, WTF::get<JSBigInt*>(leftNumeric), WTF::get<JSBigInt*>(rightNumeric));
+            JSBigInt* result = JSBigInt::bitwiseOr(exec, WTF::get<JSBigInt*>(leftNumeric), WTF::get<JSBigInt*>(rightNumeric));
             CHECK_EXCEPTION();
             RETURN_PROFILED(result);
         }
@@ -741,7 +742,7 @@ SLOW_PATH_DECL(slow_path_bitxor)
     CHECK_EXCEPTION();
     if (WTF::holds_alternative<JSBigInt*>(leftNumeric) || WTF::holds_alternative<JSBigInt*>(rightNumeric)) {
         if (WTF::holds_alternative<JSBigInt*>(leftNumeric) && WTF::holds_alternative<JSBigInt*>(rightNumeric)) {
-            JSBigInt* result = JSBigInt::bitwiseXor(vm, WTF::get<JSBigInt*>(leftNumeric), WTF::get<JSBigInt*>(rightNumeric));
+            JSBigInt* result = JSBigInt::bitwiseXor(exec, WTF::get<JSBigInt*>(leftNumeric), WTF::get<JSBigInt*>(rightNumeric));
             CHECK_EXCEPTION();
             RETURN(result);
         }
index 4ab5a52..0c6f40d 100644 (file)
@@ -81,7 +81,7 @@ Structure* JSBigInt::createStructure(VM& vm, JSGlobalObject* globalObject, JSVal
 
 JSBigInt* JSBigInt::createZero(VM& vm)
 {
-    JSBigInt* zeroBigInt = createWithLength(vm, 0);
+    JSBigInt* zeroBigInt = createWithLengthUnchecked(vm, 0);
     return zeroBigInt;
 }
 
@@ -91,7 +91,22 @@ inline size_t JSBigInt::allocationSize(unsigned length)
     return sizeWithPadding + length * sizeof(Digit);
 }
 
-JSBigInt* JSBigInt::createWithLength(VM& vm, unsigned length)
+JSBigInt* JSBigInt::tryCreateWithLength(ExecState* exec, unsigned length)
+{
+    VM& vm = exec->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
+    if (UNLIKELY(length > maxLength)) {
+        throwOutOfMemoryError(exec, scope);
+        return nullptr;
+    }
+
+    scope.release();
+
+    return createWithLengthUnchecked(vm, length);
+}
+
+JSBigInt* JSBigInt::createWithLengthUnchecked(VM& vm, unsigned length)
 {
     JSBigInt* bigInt = new (NotNull, allocateCell<JSBigInt>(vm.heap, allocationSize(length))) JSBigInt(vm, vm.bigIntStructure.get(), length);
     bigInt->finishCreation(vm);
@@ -103,8 +118,7 @@ JSBigInt* JSBigInt::createFrom(VM& vm, int32_t value)
     if (!value)
         return createZero(vm);
     
-    JSBigInt* bigInt = createWithLength(vm, 1);
-    
+    JSBigInt* bigInt = createWithLengthUnchecked(vm, 1);
     if (value < 0) {
         bigInt->setDigit(0, static_cast<Digit>(-1 * static_cast<int64_t>(value)));
         bigInt->setSign(true);
@@ -119,7 +133,7 @@ JSBigInt* JSBigInt::createFrom(VM& vm, uint32_t value)
     if (!value)
         return createZero(vm);
     
-    JSBigInt* bigInt = createWithLength(vm, 1);
+    JSBigInt* bigInt = createWithLengthUnchecked(vm, 1);
     bigInt->setDigit(0, static_cast<Digit>(value));
     return bigInt;
 }
@@ -130,8 +144,7 @@ JSBigInt* JSBigInt::createFrom(VM& vm, int64_t value)
         return createZero(vm);
     
     if (sizeof(Digit) == 8) {
-        JSBigInt* bigInt = createWithLength(vm, 1);
-        
+        JSBigInt* bigInt = createWithLengthUnchecked(vm, 1);
         if (value < 0) {
             bigInt->setDigit(0, static_cast<Digit>(static_cast<uint64_t>(-(value + 1)) + 1));
             bigInt->setSign(true);
@@ -141,8 +154,7 @@ JSBigInt* JSBigInt::createFrom(VM& vm, int64_t value)
         return bigInt;
     }
     
-    JSBigInt* bigInt = createWithLength(vm, 2);
-    
+    JSBigInt* bigInt = createWithLengthUnchecked(vm, 2);
     uint64_t tempValue;
     bool sign = false;
     if (value < 0) {
@@ -166,7 +178,7 @@ JSBigInt* JSBigInt::createFrom(VM& vm, bool value)
     if (!value)
         return createZero(vm);
     
-    JSBigInt* bigInt = createWithLength(vm, 1);
+    JSBigInt* bigInt = createWithLengthUnchecked(vm, 1);
     bigInt->setDigit(0, static_cast<Digit>(value));
     return bigInt;
 }
@@ -234,6 +246,7 @@ void JSBigInt::inplaceMultiplyAdd(Digit factor, Digit summand)
 JSBigInt* JSBigInt::multiply(ExecState* exec, JSBigInt* x, JSBigInt* y)
 {
     VM& vm = exec->vm();
+    auto scope = DECLARE_CATCH_SCOPE(vm);
 
     if (x->isZero())
         return x;
@@ -241,7 +254,8 @@ JSBigInt* JSBigInt::multiply(ExecState* exec, JSBigInt* x, JSBigInt* y)
         return y;
 
     unsigned resultLength = x->length() + y->length();
-    JSBigInt* result = JSBigInt::createWithLength(vm, resultLength);
+    JSBigInt* result = JSBigInt::tryCreateWithLength(exec, resultLength);
+    RETURN_IF_EXCEPTION(scope, nullptr);
     result->initialize(InitializationType::WithZero);
 
     for (unsigned i = 0; i < x->length(); i++)
@@ -277,8 +291,10 @@ JSBigInt* JSBigInt::divide(ExecState* exec, JSBigInt* x, JSBigInt* y)
 
         Digit remainder;
         absoluteDivWithDigitDivisor(vm, x, divisor, &quotient, remainder);
-    } else
-        absoluteDivWithBigIntDivisor(vm, x, y, &quotient, nullptr);
+    } else {
+        absoluteDivWithBigIntDivisor(exec, x, y, &quotient, nullptr);
+        RETURN_IF_EXCEPTION(scope, nullptr);
+    }
 
     quotient->setSign(resultSign);
     return quotient->rightTrim(vm);
@@ -288,7 +304,7 @@ JSBigInt* JSBigInt::copy(VM& vm, JSBigInt* x)
 {
     ASSERT(!x->isZero());
 
-    JSBigInt* result = JSBigInt::createWithLength(vm, x->length());
+    JSBigInt* result = JSBigInt::createWithLengthUnchecked(vm, x->length());
     std::copy(x->dataStorage(), x->dataStorage() + x->length(), result->dataStorage());
     result->setSign(x->sign());
     return result;
@@ -331,23 +347,26 @@ JSBigInt* JSBigInt::remainder(ExecState* exec, JSBigInt* x, JSBigInt* y)
         if (!remainderDigit)
             return createZero(vm);
 
-        remainder = createWithLength(vm, 1);
+        remainder = createWithLengthUnchecked(vm, 1);
         remainder->setDigit(0, remainderDigit);
-    } else
-        absoluteDivWithBigIntDivisor(vm, x, y, nullptr, &remainder);
+    } else {
+        absoluteDivWithBigIntDivisor(exec, x, y, nullptr, &remainder);
+        RETURN_IF_EXCEPTION(scope, nullptr);
+    }
 
     remainder->setSign(x->sign());
     return remainder->rightTrim(vm);
 }
 
-JSBigInt* JSBigInt::add(VM& vm, JSBigInt* x, JSBigInt* y)
+JSBigInt* JSBigInt::add(ExecState* exec, JSBigInt* x, JSBigInt* y)
 {
+    VM& vm = exec->vm();
     bool xSign = x->sign();
 
     // x + y == x + y
     // -x + -y == -(x + y)
     if (xSign == y->sign())
-        return absoluteAdd(vm, x, y, xSign);
+        return absoluteAdd(exec, x, y, xSign);
 
     // x + -y == x - y == -(y - x)
     // -x + y == y - x == -(x - y)
@@ -358,13 +377,14 @@ JSBigInt* JSBigInt::add(VM& vm, JSBigInt* x, JSBigInt* y)
     return absoluteSub(vm, y, x, !xSign);
 }
 
-JSBigInt* JSBigInt::sub(VM& vm, JSBigInt* x, JSBigInt* y)
+JSBigInt* JSBigInt::sub(ExecState* exec, JSBigInt* x, JSBigInt* y)
 {
+    VM& vm = exec->vm();
     bool xSign = x->sign();
     if (xSign != y->sign()) {
         // x - (-y) == x + y
         // (-x) - y == -(x + y)
-        return absoluteAdd(vm, x, y, xSign);
+        return absoluteAdd(exec, x, y, xSign);
     }
     // x - y == -(y - x)
     // (-x) - (-y) == y - x == -(x - y)
@@ -375,8 +395,11 @@ JSBigInt* JSBigInt::sub(VM& vm, JSBigInt* x, JSBigInt* y)
     return absoluteSub(vm, y, x, !xSign);
 }
 
-JSBigInt* JSBigInt::bitwiseAnd(VM& vm, JSBigInt* x, JSBigInt* y)
+JSBigInt* JSBigInt::bitwiseAnd(ExecState* exec, JSBigInt* x, JSBigInt* y)
 {
+    VM& vm = exec->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
     if (!x->sign() && !y->sign())
         return absoluteAnd(vm, x, y);
     
@@ -384,11 +407,14 @@ JSBigInt* JSBigInt::bitwiseAnd(VM& vm, JSBigInt* x, JSBigInt* y)
         int resultLength = std::max(x->length(), y->length()) + 1;
         // (-x) & (-y) == ~(x-1) & ~(y-1) == ~((x-1) | (y-1))
         // == -(((x-1) | (y-1)) + 1)
-        JSBigInt* result = absoluteSubOne(vm, x, resultLength);
-        JSBigInt* y1 = absoluteSubOne(vm, y, y->length());
-        result = absoluteOr(vm, result, y1);
+        JSBigInt* result = absoluteSubOne(exec, x, resultLength);
+        RETURN_IF_EXCEPTION(scope, nullptr);
 
-        return absoluteAddOne(vm, result, SignOption::Signed);
+        JSBigInt* y1 = absoluteSubOne(exec, y, y->length());
+        ASSERT(y1);
+        result = absoluteOr(vm, result, y1);
+        scope.release();
+        return absoluteAddOne(exec, result, SignOption::Signed);
     }
 
     ASSERT(x->sign() != y->sign());
@@ -397,11 +423,15 @@ JSBigInt* JSBigInt::bitwiseAnd(VM& vm, JSBigInt* x, JSBigInt* y)
         std::swap(x, y);
     
     // x & (-y) == x & ~(y-1) == x & ~(y-1)
-    return absoluteAndNot(vm, x, absoluteSubOne(vm, y, y->length()));
+    JSBigInt* y1 = absoluteSubOne(exec, y, y->length());
+    ASSERT(y1);
+    return absoluteAndNot(vm, x, y1);
 }
 
-JSBigInt* JSBigInt::bitwiseOr(VM& vm, JSBigInt* x, JSBigInt* y)
+JSBigInt* JSBigInt::bitwiseOr(ExecState* exec, JSBigInt* x, JSBigInt* y)
 {
+    VM& vm = exec->vm();
+
     unsigned resultLength = std::max(x->length(), y->length());
 
     if (!x->sign() && !y->sign())
@@ -410,10 +440,12 @@ JSBigInt* JSBigInt::bitwiseOr(VM& vm, JSBigInt* x, JSBigInt* y)
     if (x->sign() && y->sign()) {
         // (-x) | (-y) == ~(x-1) | ~(y-1) == ~((x-1) & (y-1))
         // == -(((x-1) & (y-1)) + 1)
-        JSBigInt* result = absoluteSubOne(vm, x, resultLength);
-        JSBigInt* y1 = absoluteSubOne(vm, y, y->length());
+        JSBigInt* result = absoluteSubOne(exec, x, resultLength);
+        ASSERT(result);
+        JSBigInt* y1 = absoluteSubOne(exec, y, y->length());
+        ASSERT(y1);
         result = absoluteAnd(vm, result, y1);
-        return absoluteAddOne(vm, result, SignOption::Signed);
+        return absoluteAddOne(exec, result, SignOption::Signed);
     }
     
     ASSERT(x->sign() != y->sign());
@@ -423,13 +455,17 @@ JSBigInt* JSBigInt::bitwiseOr(VM& vm, JSBigInt* x, JSBigInt* y)
         std::swap(x, y);
     
     // x | (-y) == x | ~(y-1) == ~((y-1) &~ x) == -(((y-1) &~ x) + 1)
-    JSBigInt* result = absoluteSubOne(vm, y, resultLength);
+    JSBigInt* result = absoluteSubOne(exec, y, resultLength);
+    ASSERT(result);
     result = absoluteAndNot(vm, result, x);
-    return absoluteAddOne(vm, result, SignOption::Signed);
+    return absoluteAddOne(exec, result, SignOption::Signed);
 }
 
-JSBigInt* JSBigInt::bitwiseXor(VM& vm, JSBigInt* x, JSBigInt* y)
+JSBigInt* JSBigInt::bitwiseXor(ExecState* exec, JSBigInt* x, JSBigInt* y)
 {
+    VM& vm = exec->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
     if (!x->sign() && !y->sign())
         return absoluteXor(vm, x, y);
     
@@ -437,8 +473,10 @@ JSBigInt* JSBigInt::bitwiseXor(VM& vm, JSBigInt* x, JSBigInt* y)
         int resultLength = std::max(x->length(), y->length());
         
         // (-x) ^ (-y) == ~(x-1) ^ ~(y-1) == (x-1) ^ (y-1)
-        JSBigInt* result = absoluteSubOne(vm, x, resultLength);
-        JSBigInt* y1 = absoluteSubOne(vm, y, y->length());
+        JSBigInt* result = absoluteSubOne(exec, x, resultLength);
+        ASSERT(result);
+        JSBigInt* y1 = absoluteSubOne(exec, y, y->length());
+        ASSERT(y1);
         return absoluteXor(vm, result, y1);
     }
     ASSERT(x->sign() != y->sign());
@@ -449,9 +487,12 @@ JSBigInt* JSBigInt::bitwiseXor(VM& vm, JSBigInt* x, JSBigInt* y)
         std::swap(x, y);
     
     // x ^ (-y) == x ^ ~(y-1) == ~(x ^ (y-1)) == -((x ^ (y-1)) + 1)
-    JSBigInt* result = absoluteSubOne(vm, y, resultLength);
+    JSBigInt* result = absoluteSubOne(exec, y, resultLength);
+    RETURN_IF_EXCEPTION(scope, nullptr);
+
     result = absoluteXor(vm, result, x);
-    return absoluteAddOne(vm, result, SignOption::Signed);
+    scope.release();
+    return absoluteAddOne(exec, result, SignOption::Signed);
 }
 
 #if USE(JSVALUE32_64)
@@ -751,10 +792,12 @@ inline JSBigInt::ComparisonResult JSBigInt::absoluteCompare(JSBigInt* x, JSBigIn
     return x->digit(i) > y->digit(i) ? ComparisonResult::GreaterThan : ComparisonResult::LessThan;
 }
 
-JSBigInt* JSBigInt::absoluteAdd(VM& vm, JSBigInt* x, JSBigInt* y, bool resultSign)
+JSBigInt* JSBigInt::absoluteAdd(ExecState* exec, JSBigInt* x, JSBigInt* y, bool resultSign)
 {
+    VM& vm = exec->vm();
+
     if (x->length() < y->length())
-        return absoluteAdd(vm, y, x, resultSign);
+        return absoluteAdd(exec, y, x, resultSign);
 
     if (x->isZero()) {
         ASSERT(y->isZero());
@@ -764,8 +807,9 @@ JSBigInt* JSBigInt::absoluteAdd(VM& vm, JSBigInt* x, JSBigInt* y, bool resultSig
     if (y->isZero())
         return resultSign == x->sign() ? x : unaryMinus(vm, x);
 
-    JSBigInt* result = JSBigInt::createWithLength(vm, x->length() + 1);
-    ASSERT(result);
+    JSBigInt* result = JSBigInt::tryCreateWithLength(exec, x->length() + 1);
+    if (!result)
+        return nullptr;
     Digit carry = 0;
     unsigned i = 0;
     for (; i < y->length(); i++) {
@@ -806,7 +850,8 @@ JSBigInt* JSBigInt::absoluteSub(VM& vm, JSBigInt* x, JSBigInt* y, bool resultSig
     if (comparisonResult == ComparisonResult::Equal)
         return JSBigInt::createZero(vm);
 
-    JSBigInt* result = JSBigInt::createWithLength(vm, x->length());
+    JSBigInt* result = JSBigInt::createWithLengthUnchecked(vm, x->length());
+
     Digit borrow = 0;
     unsigned i = 0;
     for (; i < y->length(); i++) {
@@ -851,7 +896,7 @@ void JSBigInt::absoluteDivWithDigitDivisor(VM& vm, JSBigInt* x, Digit divisor, J
     unsigned length = x->length();
     if (quotient != nullptr) {
         if (*quotient == nullptr)
-            *quotient = JSBigInt::createWithLength(vm, length);
+            *quotient = JSBigInt::createWithLengthUnchecked(vm, length);
 
         for (int i = length - 1; i >= 0; i--) {
             Digit q = digitDiv(remainder, x->digit(i), divisor, remainder);
@@ -869,10 +914,12 @@ void JSBigInt::absoluteDivWithDigitDivisor(VM& vm, JSBigInt* x, Digit divisor, J
 // Both {quotient} and {remainder} are optional, for callers that are only
 // interested in one of them.
 // See Knuth, Volume 2, section 4.3.1, Algorithm D.
-void JSBigInt::absoluteDivWithBigIntDivisor(VM& vm, JSBigInt* dividend, JSBigInt* divisor, JSBigInt** quotient, JSBigInt** remainder)
+void JSBigInt::absoluteDivWithBigIntDivisor(ExecState* exec, JSBigInt* dividend, JSBigInt* divisor, JSBigInt** quotient, JSBigInt** remainder)
 {
     ASSERT(divisor->length() >= 2);
     ASSERT(dividend->length() >= divisor->length());
+    VM& vm = exec->vm();
+    auto scope = DECLARE_CATCH_SCOPE(vm);
 
     // The unusual variable names inside this function are consistent with
     // Knuth's book, as well as with Go's implementation of this algorithm.
@@ -884,11 +931,12 @@ void JSBigInt::absoluteDivWithBigIntDivisor(VM& vm, JSBigInt* dividend, JSBigInt
     // The quotient to be computed.
     JSBigInt* q = nullptr;
     if (quotient != nullptr)
-        q = createWithLength(vm, m + 1);
+        q = createWithLengthUnchecked(exec->vm(), m + 1);
     
     // In each iteration, {qhatv} holds {divisor} * {current quotient digit}.
     // "v" is the book's name for {divisor}, "qhat" the current quotient digit.
-    JSBigInt* qhatv = createWithLength(vm, n + 1);
+    JSBigInt* qhatv = tryCreateWithLength(exec, n + 1);
+    RETURN_IF_EXCEPTION(scope, void());
     
     // D1.
     // Left-shift inputs so that the divisor's MSB is set. This is necessary
@@ -898,12 +946,15 @@ void JSBigInt::absoluteDivWithBigIntDivisor(VM& vm, JSBigInt* dividend, JSBigInt
     Digit lastDigit = divisor->digit(n - 1);
     unsigned shift = sizeof(lastDigit) == 8 ? clz64(lastDigit) : clz32(lastDigit);
 
-    if (shift > 0)
-        divisor = absoluteLeftShiftAlwaysCopy(vm, divisor, shift, LeftShiftMode::SameSizeResult);
+    if (shift > 0) {
+        divisor = absoluteLeftShiftAlwaysCopy(exec, divisor, shift, LeftShiftMode::SameSizeResult);
+        RETURN_IF_EXCEPTION(scope, void());
+    }
 
     // Holds the (continuously updated) remaining part of the dividend, which
     // eventually becomes the remainder.
-    JSBigInt* u = absoluteLeftShiftAlwaysCopy(vm, dividend, shift, LeftShiftMode::AlwaysAddOneDigit);
+    JSBigInt* u = absoluteLeftShiftAlwaysCopy(exec, dividend, shift, LeftShiftMode::AlwaysAddOneDigit);
+    RETURN_IF_EXCEPTION(scope, void());
 
     // D2.
     // Iterate over the dividend's digit (like the "grad school" algorithm).
@@ -1031,14 +1082,16 @@ void JSBigInt::inplaceRightShift(unsigned shift)
 }
 
 // Always copies the input, even when {shift} == 0.
-JSBigInt* JSBigInt::absoluteLeftShiftAlwaysCopy(VM& vm, JSBigInt* x, unsigned shift, LeftShiftMode mode)
+JSBigInt* JSBigInt::absoluteLeftShiftAlwaysCopy(ExecState* exec, JSBigInt* x, unsigned shift, LeftShiftMode mode)
 {
     ASSERT(shift < digitBits);
     ASSERT(!x->isZero());
 
     unsigned n = x->length();
     unsigned resultLength = mode == LeftShiftMode::AlwaysAddOneDigit ? n + 1 : n;
-    JSBigInt* result = createWithLength(vm, resultLength);
+    JSBigInt* result = tryCreateWithLength(exec, resultLength);
+    if (!result)
+        return nullptr;
 
     if (!shift) {
         for (unsigned i = 0; i < n; i++)
@@ -1095,8 +1148,7 @@ inline JSBigInt* JSBigInt::absoluteBitwiseOp(VM& vm, JSBigInt* x, JSBigInt* y, E
 
     ASSERT(numPairs == std::min(xLength, yLength));
     unsigned resultLength = extraDigits == ExtraDigitsHandling::Copy ? xLength : numPairs;
-    JSBigInt* result = createWithLength(vm, resultLength);
-
+    JSBigInt* result = createWithLengthUnchecked(vm, resultLength);
     unsigned i = 0;
     for (; i < numPairs; i++)
         result->setDigit(i, op(x->digit(i), y->digit(i)));
@@ -1144,7 +1196,7 @@ JSBigInt* JSBigInt::absoluteXor(VM& vm, JSBigInt* x, JSBigInt* y)
     return absoluteBitwiseOp(vm, x, y, ExtraDigitsHandling::Copy, SymmetricOp::Symmetric, digitOperation);
 }
     
-JSBigInt* JSBigInt::absoluteAddOne(VM& vm, JSBigInt* x, SignOption signOption)
+JSBigInt* JSBigInt::absoluteAddOne(ExecState* exec, JSBigInt* x, SignOption signOption)
 {
     unsigned inputLength = x->length();
     // The addition will overflow into a new digit if all existing digits are
@@ -1158,7 +1210,9 @@ JSBigInt* JSBigInt::absoluteAddOne(VM& vm, JSBigInt* x, SignOption signOption)
     }
 
     unsigned resultLength = inputLength + willOverflow;
-    JSBigInt* result = createWithLength(vm, resultLength);
+    JSBigInt* result = tryCreateWithLength(exec, resultLength);
+    if (!result)
+        return nullptr;
 
     Digit carry = 1;
     for (unsigned i = 0; i < inputLength; i++) {
@@ -1172,16 +1226,18 @@ JSBigInt* JSBigInt::absoluteAddOne(VM& vm, JSBigInt* x, SignOption signOption)
         ASSERT(!carry);
 
     result->setSign(signOption == SignOption::Signed);
-    return result->rightTrim(vm);
+    return result->rightTrim(exec->vm());
 }
 
-// Like the above, but you can specify that the allocated result should have
-// length {resultLength}, which must be at least as large as {x->length()}.
-JSBigInt* JSBigInt::absoluteSubOne(VM& vm, JSBigInt* x, unsigned resultLength)
+JSBigInt* JSBigInt::absoluteSubOne(ExecState* exec, JSBigInt* x, unsigned resultLength)
 {
     ASSERT(!x->isZero());
     ASSERT(resultLength >= x->length());
-    JSBigInt* result = createWithLength(vm, resultLength);
+    VM& vm = exec->vm();
+    auto scope = DECLARE_CATCH_SCOPE(vm);
+
+    JSBigInt* result = tryCreateWithLength(exec, resultLength);
+    RETURN_IF_EXCEPTION(scope, nullptr);
 
     unsigned length = x->length();
     Digit borrow = 1;
@@ -1354,8 +1410,6 @@ String JSBigInt::toStringGeneric(ExecState* exec, JSBigInt* x, unsigned radix)
         do {
             Digit chunk;
             absoluteDivWithDigitDivisor(vm, *dividend, chunkDivisor, &rest, chunk);
-            ASSERT(rest);
-
             dividend = &rest;
             for (unsigned i = 0; i < chunkChars; i++) {
                 resultString.append(radixDigits[chunk % radix]);
@@ -1414,8 +1468,7 @@ JSBigInt* JSBigInt::rightTrim(VM& vm)
         return this;
 
     unsigned newLength = nonZeroIndex + 1;
-    JSBigInt* trimmedBigInt = createWithLength(vm, newLength);
-    RELEASE_ASSERT(trimmedBigInt);
+    JSBigInt* trimmedBigInt = createWithLengthUnchecked(vm, newLength);
     std::copy(dataStorage(), dataStorage() + newLength, trimmedBigInt->dataStorage()); 
 
     trimmedBigInt->setSign(this->sign());
@@ -1439,7 +1492,7 @@ JSBigInt* JSBigInt::allocateFor(ExecState* exec, VM& vm, unsigned radix, unsigne
             // Divide by kDigitsBits, rounding up.
             unsigned length = (bitsMin + digitBits - 1) / digitBits;
             if (length <= maxLength) {
-                JSBigInt* result = JSBigInt::createWithLength(vm, length);
+                JSBigInt* result = JSBigInt::createWithLengthUnchecked(vm, length);
                 return result;
             }
         }
index a62e138..ab8ff5a 100644 (file)
@@ -51,7 +51,8 @@ public:
 
     static Structure* createStructure(VM&, JSGlobalObject*, JSValue prototype);
     static JSBigInt* createZero(VM&);
-    static JSBigInt* createWithLength(VM&, unsigned length);
+    static JSBigInt* tryCreateWithLength(ExecState*, unsigned length);
+    static JSBigInt* createWithLengthUnchecked(VM&, unsigned length);
 
     static JSBigInt* createFrom(VM&, int32_t value);
     static JSBigInt* createFrom(VM&, uint32_t value);
@@ -108,15 +109,15 @@ public:
     
     ComparisonResult static compareToDouble(JSBigInt* x, double y);
 
-    static JSBigInt* add(VM&, JSBigInt* x, JSBigInt* y);
-    static JSBigInt* sub(VM&, JSBigInt* x, JSBigInt* y);
+    static JSBigInt* add(ExecState*, JSBigInt* x, JSBigInt* y);
+    static JSBigInt* sub(ExecState*, JSBigInt* x, JSBigInt* y);
     static JSBigInt* divide(ExecState*, JSBigInt* x, JSBigInt* y);
     static JSBigInt* remainder(ExecState*, JSBigInt* x, JSBigInt* y);
     static JSBigInt* unaryMinus(VM&, JSBigInt* x);
 
-    static JSBigInt* bitwiseAnd(VM&, JSBigInt* x, JSBigInt* y);
-    static JSBigInt* bitwiseOr(VM&, JSBigInt* x, JSBigInt* y);
-    static JSBigInt* bitwiseXor(VM&, JSBigInt* x, JSBigInt* y);
+    static JSBigInt* bitwiseAnd(ExecState*, JSBigInt* x, JSBigInt* y);
+    static JSBigInt* bitwiseOr(ExecState*, JSBigInt* x, JSBigInt* y);
+    static JSBigInt* bitwiseXor(ExecState*, JSBigInt* x, JSBigInt* y);
 
 private:
 
@@ -139,14 +140,14 @@ private:
     static void absoluteDivWithDigitDivisor(VM&, JSBigInt* x, Digit divisor, JSBigInt** quotient, Digit& remainder);
     static void internalMultiplyAdd(JSBigInt* source, Digit factor, Digit summand, unsigned, JSBigInt* result);
     static void multiplyAccumulate(JSBigInt* multiplicand, Digit multiplier, JSBigInt* accumulator, unsigned accumulatorIndex);
-    static void absoluteDivWithBigIntDivisor(VM&, JSBigInt* dividend, JSBigInt* divisor, JSBigInt** quotient, JSBigInt** remainder);
+    static void absoluteDivWithBigIntDivisor(ExecState*, JSBigInt* dividend, JSBigInt* divisor, JSBigInt** quotient, JSBigInt** remainder);
     
     enum class LeftShiftMode {
         SameSizeResult,
         AlwaysAddOneDigit
     };
     
-    static JSBigInt* absoluteLeftShiftAlwaysCopy(VM&, JSBigInt* x, unsigned shift, LeftShiftMode);
+    static JSBigInt* absoluteLeftShiftAlwaysCopy(ExecState*, JSBigInt* x, unsigned shift, LeftShiftMode);
     static bool productGreaterThan(Digit factor1, Digit factor2, Digit high, Digit low);
 
     Digit absoluteInplaceAdd(JSBigInt* summand, unsigned startIndex);
@@ -176,8 +177,8 @@ private:
         Unsigned
     };
 
-    static JSBigInt* absoluteAddOne(VM&, JSBigInt* x, SignOption);
-    static JSBigInt* absoluteSubOne(VM&, JSBigInt* x, unsigned resultLength);
+    static JSBigInt* absoluteAddOne(ExecState*, JSBigInt* x, SignOption);
+    static JSBigInt* absoluteSubOne(ExecState*, JSBigInt* x, unsigned resultLength);
 
     // Digit arithmetic helpers.
     static Digit digitAdd(Digit a, Digit b, Digit& carry);
@@ -203,7 +204,7 @@ private:
     JSBigInt* rightTrim(VM&);
 
     void inplaceMultiplyAdd(Digit multiplier, Digit part);
-    static JSBigInt* absoluteAdd(VM&, JSBigInt* x, JSBigInt* y, bool resultSign);
+    static JSBigInt* absoluteAdd(ExecState*, JSBigInt* x, JSBigInt* y, bool resultSign);
     static JSBigInt* absoluteSub(VM&, JSBigInt* x, JSBigInt* y, bool resultSign);
     
     static size_t allocationSize(unsigned length);
index a088ad1..836f953 100644 (file)
@@ -69,8 +69,10 @@ NEVER_INLINE JSValue jsAddSlowCase(CallFrame* callFrame, JSValue v1, JSValue v2)
     RETURN_IF_EXCEPTION(scope, { });
 
     if (WTF::holds_alternative<JSBigInt*>(leftNumeric) || WTF::holds_alternative<JSBigInt*>(rightNumeric)) {
-        if (WTF::holds_alternative<JSBigInt*>(leftNumeric) && WTF::holds_alternative<JSBigInt*>(rightNumeric))
-            return JSBigInt::add(vm, WTF::get<JSBigInt*>(leftNumeric), WTF::get<JSBigInt*>(rightNumeric));
+        if (WTF::holds_alternative<JSBigInt*>(leftNumeric) && WTF::holds_alternative<JSBigInt*>(rightNumeric)) {
+            scope.release();
+            return JSBigInt::add(callFrame, WTF::get<JSBigInt*>(leftNumeric), WTF::get<JSBigInt*>(rightNumeric));
+        }
 
         return throwTypeError(callFrame, scope, "Invalid mix of BigInt and other type in addition."_s);
     }
index 61d533c..0ea9829 100644 (file)
@@ -348,8 +348,10 @@ ALWAYS_INLINE JSValue jsSub(ExecState* exec, JSValue v1, JSValue v2)
     RETURN_IF_EXCEPTION(scope, { });
 
     if (WTF::holds_alternative<JSBigInt*>(leftNumeric) || WTF::holds_alternative<JSBigInt*>(rightNumeric)) {
-        if (WTF::holds_alternative<JSBigInt*>(leftNumeric) && WTF::holds_alternative<JSBigInt*>(rightNumeric))
-            return JSBigInt::sub(vm, WTF::get<JSBigInt*>(leftNumeric), WTF::get<JSBigInt*>(rightNumeric));
+        if (WTF::holds_alternative<JSBigInt*>(leftNumeric) && WTF::holds_alternative<JSBigInt*>(rightNumeric)) {
+            scope.release();
+            return JSBigInt::sub(exec, WTF::get<JSBigInt*>(leftNumeric), WTF::get<JSBigInt*>(rightNumeric));
+        }
 
         return throwTypeError(exec, scope, "Invalid mix of BigInt and other type in subtraction."_s);
     }
@@ -368,8 +370,10 @@ ALWAYS_INLINE JSValue jsMul(ExecState* state, JSValue v1, JSValue v2)
     RETURN_IF_EXCEPTION(scope, { });
 
     if (WTF::holds_alternative<JSBigInt*>(leftNumeric) || WTF::holds_alternative<JSBigInt*>(rightNumeric)) {
-        if (WTF::holds_alternative<JSBigInt*>(leftNumeric) && WTF::holds_alternative<JSBigInt*>(rightNumeric))
+        if (WTF::holds_alternative<JSBigInt*>(leftNumeric) && WTF::holds_alternative<JSBigInt*>(rightNumeric)) {
+            scope.release();
             return JSBigInt::multiply(state, WTF::get<JSBigInt*>(leftNumeric), WTF::get<JSBigInt*>(rightNumeric));
+        }
 
         throwTypeError(state, scope, "Invalid mix of BigInt and other type in multiplication."_s);
         return { };