Teach the bytecode that arithmetic operations can return bigints
https://bugs.webkit.org/show_bug.cgi?id=205416

Reviewed by Yusuke Suzuki.

JSTests:

This file crashes in debug mode without the fix.

* stress/big-int-arithmetic-return-big-int.js: Added.
(fooAdd):
(fooSub):
(fooMul):
(fooDiv):

Source/JavaScriptCore:

Add already has the correct ResultType, but previously Sub/Mult/Div/Mod/Pow/Negate were always claimed to return Number,
and when BigInt is enabled they can also return BigInt.
UnaryPlus is left unchanged as it is invalid on a BigInt (to keep asm.js working as intended).

* parser/NodeConstructors.h:
(JSC::NegateNode::NegateNode):
(JSC::PowNode::PowNode):
(JSC::MultNode::MultNode):
(JSC::DivNode::DivNode):
(JSC::ModNode::ModNode):
(JSC::SubNode::SubNode):
* parser/ResultType.h:
(JSC::ResultType::bigIntOrNumberType): Added.
(JSC::ResultType::forNonAddArith):
(JSC::ResultType::forUnaryArith):


git-svn-id: http://svn.webkit.org/repository/webkit/trunk@254716 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/JSTests/ChangeLog b/JSTests/ChangeLog
index 1a0a529..8f4f3a9 100644
--- a/JSTests/ChangeLog
+++ b/JSTests/ChangeLog
@@ -1,3 +1,18 @@
+2020-01-16  Robin Morisset  <rmorisset@apple.com>
+
+        Teach the bytecode that arithmetic operations can return bigints
+        https://bugs.webkit.org/show_bug.cgi?id=205416
+
+        Reviewed by Yusuke Suzuki.
+
+        This file crashes in debug mode without the fix.
+
+        * stress/big-int-arithmetic-return-big-int.js: Added.
+        (fooAdd):
+        (fooSub):
+        (fooMul):
+        (fooDiv):
+
 2020-01-16  Mark Lam  <mark.lam@apple.com>
 
         operationToObject() should check for a null errorMessage.
diff --git a/JSTests/stress/big-int-arithmetic-return-big-int.js b/JSTests/stress/big-int-arithmetic-return-big-int.js
new file mode 100644
index 0000000..b0b2861
--- /dev/null
+++ b/JSTests/stress/big-int-arithmetic-return-big-int.js
@@ -0,0 +1,33 @@
+//@ runBigIntEnabled
+
+function fooAdd()
+{
+    return (1n+1n) / 1;
+}
+function fooSub()
+{
+    return (1n-1n) / 1;
+}
+function fooMul()
+{
+    return (1n*1n) / 1;
+}
+function fooDiv()
+{
+    return (1n/1n) / 1;
+}
+
+for (var i = 0; i < 10000 ; ++i) {
+    try {
+        fooAdd()
+    } catch {}
+    try {
+        fooSub()
+    } catch {}
+    try {
+        fooMul()
+    } catch {}
+    try {
+        fooDiv()
+    } catch {}
+}
diff --git a/Source/JavaScriptCore/ChangeLog b/Source/JavaScriptCore/ChangeLog
index b8866e0..327fe71 100644
--- a/Source/JavaScriptCore/ChangeLog
+++ b/Source/JavaScriptCore/ChangeLog
@@ -1,5 +1,28 @@
 2020-01-16  Robin Morisset  <rmorisset@apple.com>
 
+        Teach the bytecode that arithmetic operations can return bigints
+        https://bugs.webkit.org/show_bug.cgi?id=205416
+
+        Reviewed by Yusuke Suzuki.
+
+        Add already has the correct ResultType, but previously Sub/Mult/Div/Mod/Pow/Negate were always claimed to return Number,
+        and when BigInt is enabled they can also return BigInt.
+        UnaryPlus is left unchanged as it is invalid on a BigInt (to keep asm.js working as intended).
+
+        * parser/NodeConstructors.h:
+        (JSC::NegateNode::NegateNode):
+        (JSC::PowNode::PowNode):
+        (JSC::MultNode::MultNode):
+        (JSC::DivNode::DivNode):
+        (JSC::ModNode::ModNode):
+        (JSC::SubNode::SubNode):
+        * parser/ResultType.h:
+        (JSC::ResultType::bigIntOrNumberType): Added.
+        (JSC::ResultType::forNonAddArith):
+        (JSC::ResultType::forUnaryArith):
+
+2020-01-16  Robin Morisset  <rmorisset@apple.com>
+
         Use dataLogIf more regularly
         https://bugs.webkit.org/show_bug.cgi?id=206332
 
diff --git a/Source/JavaScriptCore/parser/NodeConstructors.h b/Source/JavaScriptCore/parser/NodeConstructors.h
index 003d089..fd97a0c 100644
--- a/Source/JavaScriptCore/parser/NodeConstructors.h
+++ b/Source/JavaScriptCore/parser/NodeConstructors.h
@@ -516,13 +516,15 @@
     {
     }
 
+    // UnaryPlus is guaranteed to always return a number, never a BigInt.
+    // See https://tc39.es/ecma262/#sec-unary-plus-operator-runtime-semantics-evaluation
     inline UnaryPlusNode::UnaryPlusNode(const JSTokenLocation& location, ExpressionNode* expr)
         : UnaryOpNode(location, ResultType::numberType(), expr, op_to_number)
     {
     }
 
     inline NegateNode::NegateNode(const JSTokenLocation& location, ExpressionNode* expr)
-        : UnaryOpNode(location, ResultType::numberType(), expr, op_negate)
+        : UnaryOpNode(location, ResultType::forUnaryArith(expr->resultDescriptor()), expr, op_negate)
     {
     }
 
@@ -555,23 +557,23 @@
     }
 
     inline PowNode::PowNode(const JSTokenLocation& location, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments)
-        : BinaryOpNode(location, ResultType::numberType(), expr1, expr2, op_pow, rightHasAssignments)
+        : BinaryOpNode(location, ResultType::forNonAddArith(expr1->resultDescriptor(), expr2->resultDescriptor()), expr1, expr2, op_pow, rightHasAssignments)
     {
     }
 
     inline MultNode::MultNode(const JSTokenLocation& location, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments)
-        : BinaryOpNode(location, ResultType::numberType(), expr1, expr2, op_mul, rightHasAssignments)
+        : BinaryOpNode(location, ResultType::forNonAddArith(expr1->resultDescriptor(), expr2->resultDescriptor()), expr1, expr2, op_mul, rightHasAssignments)
     {
     }
 
     inline DivNode::DivNode(const JSTokenLocation& location, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments)
-        : BinaryOpNode(location, ResultType::numberType(), expr1, expr2, op_div, rightHasAssignments)
+        : BinaryOpNode(location, ResultType::forNonAddArith(expr1->resultDescriptor(), expr2->resultDescriptor()), expr1, expr2, op_div, rightHasAssignments)
     {
     }
 
 
     inline ModNode::ModNode(const JSTokenLocation& location, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments)
-        : BinaryOpNode(location, ResultType::numberType(), expr1, expr2, op_mod, rightHasAssignments)
+        : BinaryOpNode(location, ResultType::forNonAddArith(expr1->resultDescriptor(), expr2->resultDescriptor()), expr1, expr2, op_mod, rightHasAssignments)
     {
     }
 
@@ -581,7 +583,7 @@
     }
 
     inline SubNode::SubNode(const JSTokenLocation& location, ExpressionNode* expr1, ExpressionNode* expr2, bool rightHasAssignments)
-        : BinaryOpNode(location, ResultType::numberType(), expr1, expr2, op_sub, rightHasAssignments)
+        : BinaryOpNode(location, ResultType::forNonAddArith(expr1->resultDescriptor(), expr2->resultDescriptor()), expr1, expr2, op_sub, rightHasAssignments)
     {
     }
 
diff --git a/Source/JavaScriptCore/parser/ResultType.h b/Source/JavaScriptCore/parser/ResultType.h
index 5622f28..2ee3eb2 100644
--- a/Source/JavaScriptCore/parser/ResultType.h
+++ b/Source/JavaScriptCore/parser/ResultType.h
@@ -157,6 +157,11 @@
             return ResultType(TypeMaybeBigInt | TypeInt32 | TypeMaybeNumber);
         }
 
+        static constexpr ResultType bigIntOrNumberType()
+        {
+            return ResultType(TypeMaybeBigInt | TypeMaybeNumber);
+        }
+
         static constexpr ResultType unknownType()
         {
             return ResultType(TypeBits);
@@ -173,6 +178,24 @@
             return addResultType();
         }
 
+        static constexpr ResultType forNonAddArith(ResultType op1, ResultType op2)
+        {
+            if (op1.definitelyIsNumber() && op2.definitelyIsNumber())
+                return numberType();
+            if (op1.definitelyIsBigInt() && op2.definitelyIsBigInt())
+                return bigIntType();
+            return bigIntOrNumberType();
+        }
+
+        static constexpr ResultType forUnaryArith(ResultType op)
+        {
+            if (op.definitelyIsNumber())
+                return numberType();
+            if (op.definitelyIsBigInt())
+                return bigIntType();
+            return bigIntOrNumberType();
+        }
+
         // Unlike in C, a logical op produces the value of the
         // last expression evaluated (and not true or false).
         static constexpr ResultType forLogicalOp(ResultType op1, ResultType op2)