Bug 1013922 - Avoid flattenning strings after each concatenation. r=jorendorff, a=lsblakk
authorNicolas B. Pierron <nicolas.b.pierron@mozilla.com>
Wed, 21 May 2014 08:15:47 -0700
changeset 192361 af6bb6bacb0e
parent 192360 9b9c4281ccb2
child 192362 eac674ed7cfe
push id3583
push userryanvm@gmail.com
push date2014-05-22 12:45 +0000
Treeherderresults
reviewersjorendorff, lsblakk
bugs1013922
milestone30.0
Bug 1013922 - Avoid flattenning strings after each concatenation. r=jorendorff, a=lsblakk
js/src/frontend/FoldConstants.cpp
js/src/jit-test/tests/basic/bug1013922.js
--- a/js/src/frontend/FoldConstants.cpp
+++ b/js/src/frontend/FoldConstants.cpp
@@ -605,16 +605,17 @@ Fold(ExclusiveContext *cx, ParseNode **p
             //
             // isStringConcat is true if we know the operation we're looking at
             // will be string concatenation at runtime.  As soon as we see a
             // string, we know that every addition to the right of it will be
             // string concatenation, even if both operands are numbers:
             // ("s" + x + 1 + 2 === "s" + x + "12").
             //
             bool isStringConcat = false;
+            RootedString foldedStr(cx);
 
             // (number + string) is definitely concatenation, but only at the
             // front of the list: (x + 1 + "2" !== x + "12") when x is a
             // number.
             if (pn1->isKind(PNK_NUMBER) && pn2 && pn2->isKind(PNK_STRING))
                 isStringConcat = true;
 
             while (pn2) {
@@ -624,35 +625,47 @@ Fold(ExclusiveContext *cx, ParseNode **p
                     (pn1->isKind(PNK_STRING) || pn1->isKind(PNK_NUMBER)) &&
                     (pn2->isKind(PNK_STRING) || pn2->isKind(PNK_NUMBER)))
                 {
                     // Fold string concatenation of literals.
                     if (pn1->isKind(PNK_NUMBER) && !FoldType(cx, pn1, PNK_STRING))
                         return false;
                     if (pn2->isKind(PNK_NUMBER) && !FoldType(cx, pn2, PNK_STRING))
                         return false;
-                    RootedString left(cx, pn1->pn_atom);
+                    if (!foldedStr)
+                        foldedStr = pn1->pn_atom;
                     RootedString right(cx, pn2->pn_atom);
-                    RootedString str(cx, ConcatStrings<CanGC>(cx, left, right));
-                    if (!str)
-                        return false;
-                    pn1->pn_atom = AtomizeString(cx, str);
-                    if (!pn1->pn_atom)
+                    foldedStr = ConcatStrings<CanGC>(cx, foldedStr, right);
+                    if (!foldedStr)
                         return false;
                     pn1->pn_next = pn2->pn_next;
                     handler.freeTree(pn2);
                     pn2 = pn1->pn_next;
                     pn->pn_count--;
                     folded = true;
                 } else {
+                    if (foldedStr) {
+                        // Convert the rope of folded strings into an Atom.
+                        pn1->pn_atom = AtomizeString(cx, foldedStr);
+                        if (!pn1->pn_atom)
+                            return false;
+                        foldedStr = nullptr;
+                    }
                     pn1 = pn2;
                     pn2 = pn2->pn_next;
                 }
             }
 
+            if (foldedStr) {
+                // Convert the rope of folded strings into an Atom.
+                pn1->pn_atom = AtomizeString(cx, foldedStr);
+                if (!pn1->pn_atom)
+                    return false;
+            }
+
             if (folded) {
                 if (pn->pn_count == 1) {
                     // We reduced the list to one constant. There is no
                     // addition anymore. Replace the PNK_ADD node with the
                     // single PNK_STRING or PNK_NUMBER node.
                     ReplaceNode(pnp, pn1);
                     pn = pn1;
                 } else if (!pn2) {
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/bug1013922.js
@@ -0,0 +1,67 @@
+String.prototype.repeat = function(num) {
+    return new Array(num + 1).join(this);
+}
+
+function set_to_length(length, frag_size)
+{
+    var fragment = "'" + "x".repeat(frag_size) + "' + ";
+    var frags = Math.floor((length - 1)/frag_size);
+    var code = "var x = " + fragment.repeat(frags) + "'" +
+        "x".repeat(length - frags * frag_size) + "';";
+
+    try {
+        eval(code);
+    }
+    catch(err) {
+        if (err.message && err.message == "Out of memory")
+            return -1;
+        if (err == "out of memory")
+            return -1;
+        throw(err); /* Oops, broke something. */
+    }
+
+    return code.length;
+}
+
+var first_fail;
+var first_pass;
+var frag_size;
+var pass_code_length;
+
+function search_up()
+{
+    if (set_to_length(first_fail, frag_size) < 0) {
+        setTimeout(binary_search, 0);
+        return;
+    }
+
+    first_fail *= 2;
+}
+
+function binary_search()
+{
+    if (first_fail - first_pass > 1) {
+        var length = (first_pass + first_fail) / 2;
+        var code_len = set_to_length(length, frag_size);
+        if (code_len > 0) {
+            first_pass = length;
+            pass_code_length = code_len;
+        } else
+            first_fail = length;
+        setTimeout(binary_search, 0);
+        return;
+    }
+}
+
+function run_find_limit()
+{
+    frag_size = 64;
+    first_fail = 65536; /* A guess */
+    first_pass = 0;
+    pass_code_length = 0;
+
+    for (var i=0; i<5; i++)
+	search_up(0);
+}
+
+run_find_limit();