Bug 1013922 - Avoid flattenning strings after each concatenation. r=jorendorff
authorNicolas B. Pierron <nicolas.b.pierron@mozilla.com>
Wed, 21 May 2014 08:15:47 -0700
changeset 184211 9161c60e682a44361d98e712b9587d1e18442537
parent 184210 59d8d82211f23679eb279d95a4d5c49025be0eea
child 184212 b919c7e64d390ef30aa83709330e2288e3a550d0
push id26814
push userryanvm@gmail.com
push dateWed, 21 May 2014 19:50:12 +0000
treeherdermozilla-central@7aee2fa0f655 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjorendorff
bugs1013922
milestone32.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 1013922 - Avoid flattenning strings after each concatenation. r=jorendorff
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();