Bug 972961 - Fix a stack overflow in FoldConstants.cpp. r=luke.
authorJason Orendorff <jorendorff@mozilla.com>
Mon, 10 Mar 2014 16:28:44 -0500
changeset 191071 236e257bf505324d68fe3552677c981672001848
parent 191070 02400c717fa61b09b91b5f08326d50404eaad3b8
child 191072 6635d1edc7497a5fa346d0921013d0fde248c0bb
push id474
push userasasaki@mozilla.com
push dateMon, 02 Jun 2014 21:01:02 +0000
treeherdermozilla-release@967f4cf1b31c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersluke
bugs972961
milestone30.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 972961 - Fix a stack overflow in FoldConstants.cpp. r=luke.
js/src/frontend/FoldConstants.cpp
js/src/jit-test/tests/basic/bug972961.js
--- a/js/src/frontend/FoldConstants.cpp
+++ b/js/src/frontend/FoldConstants.cpp
@@ -23,56 +23,78 @@ using namespace js;
 using namespace js::frontend;
 
 using mozilla::IsNaN;
 using mozilla::IsNegative;
 using mozilla::NegativeInfinity;
 using mozilla::PositiveInfinity;
 using JS::GenericNaN;
 
-static ParseNode *
-ContainsVarOrConst(ParseNode *pn)
+static bool
+ContainsVarOrConst(ExclusiveContext *cx, ParseNode *pn, ParseNode **resultp)
 {
-    if (!pn)
-        return nullptr;
-    if (pn->isKind(PNK_VAR) || pn->isKind(PNK_CONST))
-        return pn;
+    JS_CHECK_RECURSION(cx, return false);
+
+    if (!pn) {
+        *resultp = nullptr;
+        return true;
+    }
+    if (pn->isKind(PNK_VAR) || pn->isKind(PNK_CONST)) {
+        *resultp = pn;
+        return true;
+    }
     switch (pn->getArity()) {
       case PN_LIST:
         for (ParseNode *pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
-            if (ParseNode *pnt = ContainsVarOrConst(pn2))
-                return pnt;
+            if (!ContainsVarOrConst(cx, pn2, resultp))
+                return false;
+            if (*resultp)
+                return true;
         }
         break;
+
       case PN_TERNARY:
-        if (ParseNode *pnt = ContainsVarOrConst(pn->pn_kid1))
-            return pnt;
-        if (ParseNode *pnt = ContainsVarOrConst(pn->pn_kid2))
-            return pnt;
-        return ContainsVarOrConst(pn->pn_kid3);
+        if (!ContainsVarOrConst(cx, pn->pn_kid1, resultp))
+            return false;
+        if (*resultp)
+            return true;
+        if (!ContainsVarOrConst(cx, pn->pn_kid2, resultp))
+            return false;
+        if (*resultp)
+            return true;
+        return ContainsVarOrConst(cx, pn->pn_kid3, resultp);
+
       case PN_BINARY:
       case PN_BINARY_OBJ:
-        /*
-         * Limit recursion if pn is a binary expression, which can't contain a
-         * var statement.
-         */
-        if (!pn->isOp(JSOP_NOP))
-            return nullptr;
-        if (ParseNode *pnt = ContainsVarOrConst(pn->pn_left))
-            return pnt;
-        return ContainsVarOrConst(pn->pn_right);
+        // Limit recursion if pn is a binary expression, which can't contain a
+        // var statement.
+        if (!pn->isOp(JSOP_NOP)) {
+            *resultp = nullptr;
+            return true;
+        }
+        if (!ContainsVarOrConst(cx, pn->pn_left, resultp))
+            return false;
+        if (*resultp)
+            return true;
+        return ContainsVarOrConst(cx, pn->pn_right, resultp);
+
       case PN_UNARY:
-        if (!pn->isOp(JSOP_NOP))
-            return nullptr;
-        return ContainsVarOrConst(pn->pn_kid);
+        if (!pn->isOp(JSOP_NOP)) {
+            *resultp = nullptr;
+            return true;
+        }
+        return ContainsVarOrConst(cx, pn->pn_kid, resultp);
+
       case PN_NAME:
-        return ContainsVarOrConst(pn->maybeExpr());
+        return ContainsVarOrConst(cx, pn->maybeExpr(), resultp);
+
       default:;
     }
-    return nullptr;
+    *resultp = nullptr;
+    return true;
 }
 
 /*
  * Fold from one constant type to another.
  * XXX handles only strings and numbers for now
  */
 static bool
 FoldType(ExclusiveContext *cx, ParseNode *pn, ParseNodeKind kind)
@@ -404,18 +426,27 @@ Fold(ExclusiveContext *cx, ParseNode **p
     //
     // pn is the immediate child in question. Its descendents were already
     // constant-folded above, so we're done.
     if (sc == SyntacticContext::Delete)
         return true;
 
     switch (pn->getKind()) {
       case PNK_IF:
-        if (ContainsVarOrConst(pn2) || ContainsVarOrConst(pn3))
-            break;
+        {
+            ParseNode *decl;
+            if (!ContainsVarOrConst(cx, pn2, &decl))
+                return false;
+            if (decl)
+                break;
+            if (!ContainsVarOrConst(cx, pn3, &decl))
+                return false;
+            if (decl)
+                break;
+        }
         /* FALL THROUGH */
 
       case PNK_CONDITIONAL:
         /* Reduce 'if (C) T; else E' into T for true C, E for false. */
         switch (pn1->getKind()) {
           case PNK_NUMBER:
             if (pn1->pn_dval == 0 || IsNaN(pn1->pn_dval))
                 pn2 = pn3;
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/bug972961.js
@@ -0,0 +1,40 @@
+// Bug 972961 - Test compiler with very long chains of property accesses
+
+// Return true if we can compile a chain of n property accesses,
+// false if we cannot. Throw if compilation fails in an unexpected way.
+function test(n) {
+    print("testing " + n);
+    try {
+        eval('if (false) {' + Array(n).join(("a.")) + 'a}');
+    } catch (exc) {
+        // Expected outcome if the expression is too deeply nested is an InternalError.
+        if (!(exc instanceof InternalError))
+            throw exc;
+        print(exc.message);
+        return false;
+    }
+    print("no exception");
+    return true;
+}
+
+// Find out how long a chain is enough to break the compiler.
+var n = 4, LIMIT = 0x000fffff;
+var lo = 1, hi = 1;
+while (n <= LIMIT && test(n)) {
+    lo = n;
+    n *= 4;
+}
+
+// Using binary search, find a pass/fail boundary (in order to
+// test the edge case).
+if (n <= LIMIT) {
+    hi = n;
+    while (lo !== hi) {
+        var mid = Math.floor((lo + hi) / 2);
+        if (test(mid))
+            lo = mid + 1;
+        else
+            hi = mid;
+    }
+    print((lo - 1) + " attributes should be enough for anyone");
+}