various math operations don't properly check for an exception after calling toNumber...
authorsbarati@apple.com <sbarati@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 6 Aug 2016 00:46:50 +0000 (00:46 +0000)
committersbarati@apple.com <sbarati@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 6 Aug 2016 00:46:50 +0000 (00:46 +0000)
https://bugs.webkit.org/show_bug.cgi?id=160154

Reviewed by Mark Lam.

JSTests:

* stress/to-number-throws-correct-exception.js: Added.
(test.let.test.runTest.):
(test.let.test.runTest.get f):
(test.let.test.runTest):
(test.let.test):
(test):
(test2.runTest.):
(test2.runTest.get f):
(test2.runTest):
(test2):

Source/JavaScriptCore:

We must check for an exception after calling toNumber() on the lhs
because this can throw an exception. If we called toNumber() on
the rhs without first checking for an exception after the toNumber()
on the lhs, this can lead us to execute effectful code or deviate
from the standard in subtle ways. I fixed this bug in various places
by always checking for an exception after calling toNumber() on the
lhs for the various bit and arithmetic operations.

This patch also found a commutativity bug inside DFGStrengthReduction.
We could end up commuting the lhs and rhs of say an "|" expression
even when the lhs/rhs may not be numbers. This is wrong because
executing toNumber() on the lhs/rhs has strict ordering guarantees
by the specification and is observable by user programs.

* dfg/DFGOperations.cpp:
* dfg/DFGStrengthReductionPhase.cpp:
(JSC::DFG::StrengthReductionPhase::handleCommutativity):
* jit/JITOperations.cpp:
* runtime/CommonSlowPaths.cpp:
(JSC::SLOW_PATH_DECL):
* runtime/Operations.cpp:
(JSC::jsAddSlowCase):

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

JSTests/ChangeLog
JSTests/stress/to-number-throws-correct-exception.js [new file with mode: 0644]
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/dfg/DFGOperations.cpp
Source/JavaScriptCore/dfg/DFGStrengthReductionPhase.cpp
Source/JavaScriptCore/jit/JITOperations.cpp
Source/JavaScriptCore/runtime/CommonSlowPaths.cpp
Source/JavaScriptCore/runtime/Operations.cpp

index 1b41ba8..58eca85 100644 (file)
@@ -1,5 +1,23 @@
 2016-08-05  Saam Barati  <sbarati@apple.com>
 
+        various math operations don't properly check for an exception after calling toNumber() on the lhs
+        https://bugs.webkit.org/show_bug.cgi?id=160154
+
+        Reviewed by Mark Lam.
+
+        * stress/to-number-throws-correct-exception.js: Added.
+        (test.let.test.runTest.):
+        (test.let.test.runTest.get f):
+        (test.let.test.runTest):
+        (test.let.test):
+        (test):
+        (test2.runTest.):
+        (test2.runTest.get f):
+        (test2.runTest):
+        (test2):
+
+2016-08-05  Saam Barati  <sbarati@apple.com>
+
         Assertion failure when accessing TDZ variable in catch through eval
         https://bugs.webkit.org/show_bug.cgi?id=160554
 
diff --git a/JSTests/stress/to-number-throws-correct-exception.js b/JSTests/stress/to-number-throws-correct-exception.js
new file mode 100644 (file)
index 0000000..382a730
--- /dev/null
@@ -0,0 +1,121 @@
+function test(op) {
+    let test = `
+        function runTest(iters) {
+            let shouldThrow = false;
+            let a = {
+                valueOf() { 
+                    if (shouldThrow)
+                        throw "a";
+                    return 0;
+                }
+            };
+            let {proxy: b, revoke} = Proxy.revocable({}, {
+                get: function(target, prop) {
+                    if (prop === "valueOf") {
+                        if (shouldThrow)
+                            throw new Error("Should not be here!");
+                        return function() {
+                            return 0;
+                        }
+                    }
+                }
+            });
+            function f(a, b) {
+                return a ${op} b;
+            }
+            noInline(f);
+            for (let i = 0; i < iters; i++) {
+                f(a, b);
+            }
+
+            shouldThrow = true;
+            let validException = false;
+            let exception = null;
+            revoke();
+            try {
+                f(a, b);
+            } catch(e) {
+                validException = e === "a";
+                exception = e;
+            }
+            if (!validException)
+                throw new Error("Bad operation: " + exception.toString() + " with iters: " + iters);
+        }
+        runTest(2);
+        runTest(10);
+        runTest(50);
+        runTest(1000);
+        runTest(10000);
+    `;
+    eval(test);
+}
+let ops = [
+    "+"
+    , "-"
+    , "*"
+    , "**"
+    , "/"
+    , "%"
+    , "&"
+    , "|"
+    , "^"
+    , ">>"
+    , ">>>"
+    , "<<"
+];
+for (let op of ops)
+    test(op);
+
+function test2(op) {
+    function runTest(iters) {
+        let test = `
+            let shouldThrow = false;
+            let a = {
+                valueOf() { 
+                    if (shouldThrow)
+                        throw "a";
+                    return 0;
+                }
+            };
+            let {proxy: b, revoke} = Proxy.revocable({}, {
+                get: function(target, prop) {
+                    if (prop === "valueOf") {
+                        if (shouldThrow)
+                            throw new Error("Should not be here!");
+                        return function() {
+                            return 0;
+                        }
+                    }
+                }
+            });
+            function f(a, b) {
+                return a ${op} b;
+            }
+            noInline(f);
+            for (let i = 0; i < ${iters}; i++) {
+                f(a, b);
+            }
+
+            shouldThrow = true;
+            let validException = false;
+            let exception = null;
+            revoke();
+            try {
+                f(a, b);
+            } catch(e) {
+                validException = e === "a";
+                exception = e;
+            }
+            if (!validException)
+                throw new Error("Bad operation: " + exception.toString() + " with iters: " + ${iters});
+        `;
+        eval(Math.random() + ";" + test);
+    }
+    runTest(2);
+    runTest(10);
+    runTest(50);
+    runTest(1000);
+    runTest(10000);
+}
+for (let op of ops)
+    test2(op);
index 9c1b204..f7d0fb1 100644 (file)
@@ -1,3 +1,33 @@
+2016-08-05  Saam Barati  <sbarati@apple.com>
+
+        various math operations don't properly check for an exception after calling toNumber() on the lhs
+        https://bugs.webkit.org/show_bug.cgi?id=160154
+
+        Reviewed by Mark Lam.
+
+        We must check for an exception after calling toNumber() on the lhs
+        because this can throw an exception. If we called toNumber() on
+        the rhs without first checking for an exception after the toNumber()
+        on the lhs, this can lead us to execute effectful code or deviate
+        from the standard in subtle ways. I fixed this bug in various places
+        by always checking for an exception after calling toNumber() on the
+        lhs for the various bit and arithmetic operations.
+
+        This patch also found a commutativity bug inside DFGStrengthReduction.
+        We could end up commuting the lhs and rhs of say an "|" expression
+        even when the lhs/rhs may not be numbers. This is wrong because
+        executing toNumber() on the lhs/rhs has strict ordering guarantees
+        by the specification and is observable by user programs.
+
+        * dfg/DFGOperations.cpp:
+        * dfg/DFGStrengthReductionPhase.cpp:
+        (JSC::DFG::StrengthReductionPhase::handleCommutativity):
+        * jit/JITOperations.cpp:
+        * runtime/CommonSlowPaths.cpp:
+        (JSC::SLOW_PATH_DECL):
+        * runtime/Operations.cpp:
+        (JSC::jsAddSlowCase):
+
 2016-08-05  Michael Saboff  <msaboff@apple.com>
 
         compilePutByValForIntTypedArray() has a slow path in the middle of its processing
index 69a16eb..ed6db16 100644 (file)
@@ -210,6 +210,8 @@ EncodedJSValue JIT_OPERATION operationValueBitAnd(ExecState* exec, EncodedJSValu
     JSValue op2 = JSValue::decode(encodedOp2);
 
     int32_t a = op1.toInt32(exec);
+    if (UNLIKELY(vm->exception()))
+        return JSValue::encode(JSValue());
     int32_t b = op2.toInt32(exec);
     return JSValue::encode(jsNumber(a & b));
 }
@@ -223,6 +225,8 @@ EncodedJSValue JIT_OPERATION operationValueBitOr(ExecState* exec, EncodedJSValue
     JSValue op2 = JSValue::decode(encodedOp2);
 
     int32_t a = op1.toInt32(exec);
+    if (UNLIKELY(vm->exception()))
+        return JSValue::encode(JSValue());
     int32_t b = op2.toInt32(exec);
     return JSValue::encode(jsNumber(a | b));
 }
@@ -236,6 +240,8 @@ EncodedJSValue JIT_OPERATION operationValueBitXor(ExecState* exec, EncodedJSValu
     JSValue op2 = JSValue::decode(encodedOp2);
 
     int32_t a = op1.toInt32(exec);
+    if (UNLIKELY(vm->exception()))
+        return JSValue::encode(JSValue());
     int32_t b = op2.toInt32(exec);
     return JSValue::encode(jsNumber(a ^ b));
 }
@@ -249,6 +255,8 @@ EncodedJSValue JIT_OPERATION operationValueBitLShift(ExecState* exec, EncodedJSV
     JSValue op2 = JSValue::decode(encodedOp2);
 
     int32_t a = op1.toInt32(exec);
+    if (UNLIKELY(vm->exception()))
+        return JSValue::encode(JSValue());
     uint32_t b = op2.toUInt32(exec);
     return JSValue::encode(jsNumber(a << (b & 0x1f)));
 }
@@ -262,6 +270,8 @@ EncodedJSValue JIT_OPERATION operationValueBitRShift(ExecState* exec, EncodedJSV
     JSValue op2 = JSValue::decode(encodedOp2);
 
     int32_t a = op1.toInt32(exec);
+    if (UNLIKELY(vm->exception()))
+        return JSValue::encode(JSValue());
     uint32_t b = op2.toUInt32(exec);
     return JSValue::encode(jsNumber(a >> (b & 0x1f)));
 }
@@ -275,6 +285,8 @@ EncodedJSValue JIT_OPERATION operationValueBitURShift(ExecState* exec, EncodedJS
     JSValue op2 = JSValue::decode(encodedOp2);
 
     uint32_t a = op1.toUInt32(exec);
+    if (UNLIKELY(vm->exception()))
+        return JSValue::encode(JSValue());
     uint32_t b = op2.toUInt32(exec);
     return JSValue::encode(jsNumber(static_cast<int32_t>(a >> (b & 0x1f))));
 }
@@ -304,6 +316,8 @@ EncodedJSValue JIT_OPERATION operationValueDiv(ExecState* exec, EncodedJSValue e
     JSValue op2 = JSValue::decode(encodedOp2);
 
     double a = op1.toNumber(exec);
+    if (UNLIKELY(vm->exception()))
+        return JSValue::encode(JSValue());
     double b = op2.toNumber(exec);
     return JSValue::encode(jsNumber(a / b));
 }
index 586bd96..2b2ea9e 100644 (file)
@@ -744,13 +744,18 @@ private:
     
     void handleCommutativity()
     {
+        // It's definitely not sound to swap the lhs and rhs when we may be performing effectful
+        // calls on the lhs/rhs for valueOf.
+        if (m_node->child1().useKind() == UntypedUse || m_node->child2().useKind() == UntypedUse)
+            return;
+
         // If the right side is a constant then there is nothing left to do.
         if (m_node->child2()->hasConstant())
             return;
         
         // This case ensures that optimizations that look for x + const don't also have
         // to look for const + x.
-        if (m_node->child1()->hasConstant()) {
+        if (m_node->child1()->hasConstant() && !m_node->child1()->asJSValue().isCell()) {
             std::swap(m_node->child1(), m_node->child2());
             m_changed = true;
             return;
index 40c53f8..d21d868 100644 (file)
@@ -2354,17 +2354,19 @@ EncodedJSValue JIT_OPERATION operationValueAddNoOptimize(ExecState* exec, Encode
     return JSValue::encode(result);
 }
 
-ALWAYS_INLINE static EncodedJSValue unprofiledMul(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2)
+ALWAYS_INLINE static EncodedJSValue unprofiledMul(VM& vm, ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2)
 {
     JSValue op1 = JSValue::decode(encodedOp1);
     JSValue op2 = JSValue::decode(encodedOp2);
 
     double a = op1.toNumber(exec);
+    if (UNLIKELY(vm.exception()))
+        return JSValue::encode(JSValue());
     double b = op2.toNumber(exec);
     return JSValue::encode(jsNumber(a * b));
 }
 
-ALWAYS_INLINE static EncodedJSValue profiledMul(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, ArithProfile* arithProfile, bool shouldObserveLHSAndRHSTypes = true)
+ALWAYS_INLINE static EncodedJSValue profiledMul(VM& vm, ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, ArithProfile* arithProfile, bool shouldObserveLHSAndRHSTypes = true)
 {
     JSValue op1 = JSValue::decode(encodedOp1);
     JSValue op2 = JSValue::decode(encodedOp2);
@@ -2373,7 +2375,11 @@ ALWAYS_INLINE static EncodedJSValue profiledMul(ExecState* exec, EncodedJSValue
         arithProfile->observeLHSAndRHS(op1, op2);
 
     double a = op1.toNumber(exec);
+    if (UNLIKELY(vm.exception()))
+        return JSValue::encode(JSValue());
     double b = op2.toNumber(exec);
+    if (UNLIKELY(vm.exception()))
+        return JSValue::encode(JSValue());
     
     JSValue result = jsNumber(a * b);
     arithProfile->observeResult(result);
@@ -2385,7 +2391,7 @@ EncodedJSValue JIT_OPERATION operationValueMul(ExecState* exec, EncodedJSValue e
     VM* vm = &exec->vm();
     NativeCallFrameTracer tracer(vm, exec);
 
-    return unprofiledMul(exec, encodedOp1, encodedOp2);
+    return unprofiledMul(*vm, exec, encodedOp1, encodedOp2);
 }
 
 EncodedJSValue JIT_OPERATION operationValueMulNoOptimize(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, JITMulIC*)
@@ -2393,7 +2399,7 @@ EncodedJSValue JIT_OPERATION operationValueMulNoOptimize(ExecState* exec, Encode
     VM* vm = &exec->vm();
     NativeCallFrameTracer tracer(vm, exec);
 
-    return unprofiledMul(exec, encodedOp1, encodedOp2);
+    return unprofiledMul(*vm, exec, encodedOp1, encodedOp2);
 }
 
 EncodedJSValue JIT_OPERATION operationValueMulOptimize(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, JITMulIC* mulIC)
@@ -2410,7 +2416,7 @@ EncodedJSValue JIT_OPERATION operationValueMulOptimize(ExecState* exec, EncodedJ
     exec->codeBlock()->dumpMathICStats();
 #endif
 
-    return unprofiledMul(exec, encodedOp1, encodedOp2);
+    return unprofiledMul(*vm, exec, encodedOp1, encodedOp2);
 }
 
 EncodedJSValue JIT_OPERATION operationValueMulProfiled(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, ArithProfile* arithProfile)
@@ -2418,7 +2424,7 @@ EncodedJSValue JIT_OPERATION operationValueMulProfiled(ExecState* exec, EncodedJ
     VM* vm = &exec->vm();
     NativeCallFrameTracer tracer(vm, exec);
 
-    return profiledMul(exec, encodedOp1, encodedOp2, arithProfile);
+    return profiledMul(*vm, exec, encodedOp1, encodedOp2, arithProfile);
 }
 
 EncodedJSValue JIT_OPERATION operationValueMulProfiledOptimize(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, ArithProfile* arithProfile, JITMulIC* mulIC)
@@ -2434,7 +2440,7 @@ EncodedJSValue JIT_OPERATION operationValueMulProfiledOptimize(ExecState* exec,
     exec->codeBlock()->dumpMathICStats();
 #endif
 
-    return profiledMul(exec, encodedOp1, encodedOp2, arithProfile, false);
+    return profiledMul(*vm, exec, encodedOp1, encodedOp2, arithProfile, false);
 }
 
 EncodedJSValue JIT_OPERATION operationValueMulProfiledNoOptimize(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, ArithProfile* arithProfile, JITMulIC*)
@@ -2442,20 +2448,22 @@ EncodedJSValue JIT_OPERATION operationValueMulProfiledNoOptimize(ExecState* exec
     VM* vm = &exec->vm();
     NativeCallFrameTracer tracer(vm, exec);
 
-    return profiledMul(exec, encodedOp1, encodedOp2, arithProfile);
+    return profiledMul(*vm, exec, encodedOp1, encodedOp2, arithProfile);
 }
 
-ALWAYS_INLINE static EncodedJSValue unprofiledSub(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2)
+ALWAYS_INLINE static EncodedJSValue unprofiledSub(VM& vm, ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2)
 {
     JSValue op1 = JSValue::decode(encodedOp1);
     JSValue op2 = JSValue::decode(encodedOp2);
 
     double a = op1.toNumber(exec);
+    if (UNLIKELY(vm.exception()))
+        return JSValue::encode(JSValue());
     double b = op2.toNumber(exec);
     return JSValue::encode(jsNumber(a - b));
 }
 
-ALWAYS_INLINE static EncodedJSValue profiledSub(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, ArithProfile* arithProfile, bool shouldObserveLHSAndRHSTypes = true)
+ALWAYS_INLINE static EncodedJSValue profiledSub(VM& vm, ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, ArithProfile* arithProfile, bool shouldObserveLHSAndRHSTypes = true)
 {
     JSValue op1 = JSValue::decode(encodedOp1);
     JSValue op2 = JSValue::decode(encodedOp2);
@@ -2464,7 +2472,11 @@ ALWAYS_INLINE static EncodedJSValue profiledSub(ExecState* exec, EncodedJSValue
         arithProfile->observeLHSAndRHS(op1, op2);
 
     double a = op1.toNumber(exec);
+    if (UNLIKELY(vm.exception()))
+        return JSValue::encode(JSValue());
     double b = op2.toNumber(exec);
+    if (UNLIKELY(vm.exception()))
+        return JSValue::encode(JSValue());
     
     JSValue result = jsNumber(a - b);
     arithProfile->observeResult(result);
@@ -2475,7 +2487,7 @@ EncodedJSValue JIT_OPERATION operationValueSub(ExecState* exec, EncodedJSValue e
 {
     VM* vm = &exec->vm();
     NativeCallFrameTracer tracer(vm, exec);
-    return unprofiledSub(exec, encodedOp1, encodedOp2);
+    return unprofiledSub(*vm, exec, encodedOp1, encodedOp2);
 }
 
 EncodedJSValue JIT_OPERATION operationValueSubProfiled(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, ArithProfile* arithProfile)
@@ -2483,7 +2495,7 @@ EncodedJSValue JIT_OPERATION operationValueSubProfiled(ExecState* exec, EncodedJ
     VM* vm = &exec->vm();
     NativeCallFrameTracer tracer(vm, exec);
 
-    return profiledSub(exec, encodedOp1, encodedOp2, arithProfile);
+    return profiledSub(*vm, exec, encodedOp1, encodedOp2, arithProfile);
 }
 
 EncodedJSValue JIT_OPERATION operationValueSubOptimize(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, JITSubIC* subIC)
@@ -2500,7 +2512,7 @@ EncodedJSValue JIT_OPERATION operationValueSubOptimize(ExecState* exec, EncodedJ
     exec->codeBlock()->dumpMathICStats();
 #endif
 
-    return unprofiledSub(exec, encodedOp1, encodedOp2);
+    return unprofiledSub(*vm, exec, encodedOp1, encodedOp2);
 }
 
 EncodedJSValue JIT_OPERATION operationValueSubNoOptimize(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, JITSubIC*)
@@ -2508,7 +2520,7 @@ EncodedJSValue JIT_OPERATION operationValueSubNoOptimize(ExecState* exec, Encode
     VM* vm = &exec->vm();
     NativeCallFrameTracer tracer(vm, exec);
 
-    return unprofiledSub(exec, encodedOp1, encodedOp2);
+    return unprofiledSub(*vm, exec, encodedOp1, encodedOp2);
 }
 
 EncodedJSValue JIT_OPERATION operationValueSubProfiledOptimize(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, ArithProfile* arithProfile, JITSubIC* subIC)
@@ -2524,7 +2536,7 @@ EncodedJSValue JIT_OPERATION operationValueSubProfiledOptimize(ExecState* exec,
     exec->codeBlock()->dumpMathICStats();
 #endif
 
-    return profiledSub(exec, encodedOp1, encodedOp2, arithProfile, false);
+    return profiledSub(*vm, exec, encodedOp1, encodedOp2, arithProfile, false);
 }
 
 EncodedJSValue JIT_OPERATION operationValueSubProfiledNoOptimize(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, ArithProfile* arithProfile, JITSubIC*)
@@ -2532,7 +2544,7 @@ EncodedJSValue JIT_OPERATION operationValueSubProfiledNoOptimize(ExecState* exec
     VM* vm = &exec->vm();
     NativeCallFrameTracer tracer(vm, exec);
 
-    return profiledSub(exec, encodedOp1, encodedOp2, arithProfile);
+    return profiledSub(*vm, exec, encodedOp1, encodedOp2, arithProfile);
 }
 
 void JIT_OPERATION operationProcessTypeProfilerLog(ExecState* exec)
index 6b7b21c..fc2a912 100644 (file)
@@ -434,6 +434,8 @@ SLOW_PATH_DECL(slow_path_mul)
     JSValue left = OP_C(2).jsValue();
     JSValue right = OP_C(3).jsValue();
     double a = left.toNumber(exec);
+    if (UNLIKELY(vm.exception()))
+        RETURN(JSValue());
     double b = right.toNumber(exec);
     JSValue result = jsNumber(a * b);
     RETURN_WITH_PROFILING(result, {
@@ -447,6 +449,8 @@ SLOW_PATH_DECL(slow_path_sub)
     JSValue left = OP_C(2).jsValue();
     JSValue right = OP_C(3).jsValue();
     double a = left.toNumber(exec);
+    if (UNLIKELY(vm.exception()))
+        RETURN(JSValue());
     double b = right.toNumber(exec);
     JSValue result = jsNumber(a - b);
     RETURN_WITH_PROFILING(result, {
@@ -460,7 +464,11 @@ SLOW_PATH_DECL(slow_path_div)
     JSValue left = OP_C(2).jsValue();
     JSValue right = OP_C(3).jsValue();
     double a = left.toNumber(exec);
+    if (UNLIKELY(vm.exception()))
+        RETURN(JSValue());
     double b = right.toNumber(exec);
+    if (UNLIKELY(vm.exception()))
+        RETURN(JSValue());
     JSValue result = jsNumber(a / b);
     RETURN_WITH_PROFILING(result, {
         updateArithProfileForBinaryArithOp(exec, pc, result, left, right);
@@ -471,6 +479,8 @@ SLOW_PATH_DECL(slow_path_mod)
 {
     BEGIN();
     double a = OP_C(2).jsValue().toNumber(exec);
+    if (UNLIKELY(vm.exception()))
+        RETURN(JSValue());
     double b = OP_C(3).jsValue().toNumber(exec);
     RETURN(jsNumber(jsMod(a, b)));
 }
@@ -479,7 +489,11 @@ SLOW_PATH_DECL(slow_path_pow)
 {
     BEGIN();
     double a = OP_C(2).jsValue().toNumber(exec);
+    if (UNLIKELY(vm.exception()))
+        RETURN(JSValue());
     double b = OP_C(3).jsValue().toNumber(exec);
+    if (UNLIKELY(vm.exception()))
+        RETURN(JSValue());
     RETURN(jsNumber(operationMathPow(a, b)));
 }
 
@@ -487,6 +501,8 @@ SLOW_PATH_DECL(slow_path_lshift)
 {
     BEGIN();
     int32_t a = OP_C(2).jsValue().toInt32(exec);
+    if (UNLIKELY(vm.exception()))
+        RETURN(JSValue());
     uint32_t b = OP_C(3).jsValue().toUInt32(exec);
     RETURN(jsNumber(a << (b & 31)));
 }
@@ -495,6 +511,8 @@ SLOW_PATH_DECL(slow_path_rshift)
 {
     BEGIN();
     int32_t a = OP_C(2).jsValue().toInt32(exec);
+    if (UNLIKELY(vm.exception()))
+        RETURN(JSValue());
     uint32_t b = OP_C(3).jsValue().toUInt32(exec);
     RETURN(jsNumber(a >> (b & 31)));
 }
@@ -503,6 +521,8 @@ SLOW_PATH_DECL(slow_path_urshift)
 {
     BEGIN();
     uint32_t a = OP_C(2).jsValue().toUInt32(exec);
+    if (UNLIKELY(vm.exception()))
+        RETURN(JSValue());
     uint32_t b = OP_C(3).jsValue().toUInt32(exec);
     RETURN(jsNumber(static_cast<int32_t>(a >> (b & 31))));
 }
@@ -518,6 +538,8 @@ SLOW_PATH_DECL(slow_path_bitand)
 {
     BEGIN();
     int32_t a = OP_C(2).jsValue().toInt32(exec);
+    if (UNLIKELY(vm.exception()))
+        RETURN(JSValue());
     int32_t b = OP_C(3).jsValue().toInt32(exec);
     RETURN(jsNumber(a & b));
 }
@@ -526,6 +548,8 @@ SLOW_PATH_DECL(slow_path_bitor)
 {
     BEGIN();
     int32_t a = OP_C(2).jsValue().toInt32(exec);
+    if (UNLIKELY(vm.exception()))
+        RETURN(JSValue());
     int32_t b = OP_C(3).jsValue().toInt32(exec);
     RETURN(jsNumber(a | b));
 }
@@ -534,6 +558,8 @@ SLOW_PATH_DECL(slow_path_bitxor)
 {
     BEGIN();
     int32_t a = OP_C(2).jsValue().toInt32(exec);
+    if (UNLIKELY(vm.exception()))
+        RETURN(JSValue());
     int32_t b = OP_C(3).jsValue().toInt32(exec);
     RETURN(jsNumber(a ^ b));
 }
index 01a33a1..af16d93 100644 (file)
@@ -43,8 +43,13 @@ bool JSValue::strictEqualSlowCase(ExecState* exec, JSValue v1, JSValue v2)
 NEVER_INLINE JSValue jsAddSlowCase(CallFrame* callFrame, JSValue v1, JSValue v2)
 {
     // exception for the Date exception in defaultValue()
+    VM& vm = callFrame->vm();
     JSValue p1 = v1.toPrimitive(callFrame);
+    if (UNLIKELY(vm.exception()))
+        return JSValue();
     JSValue p2 = v2.toPrimitive(callFrame);
+    if (UNLIKELY(vm.exception()))
+        return JSValue();
 
     if (p1.isString())
         return jsString(callFrame, asString(p1), p2.toString(callFrame));