Bug 1019585 part 5 - Make BuildDollarReplacement handle Latin1 strings. r=terrence
authorJan de Mooij <jdemooij@mozilla.com>
Thu, 19 Jun 2014 12:59:46 +0200
changeset 189605 e589c195f61d7b3b2b1598a260811e5c360946a5
parent 189604 95ce40562f510e1c5f2ab25ab6228d23c0ff59ce
child 189606 0c8a072f624f48ef7544ab90b91994347b1613ca
push id26992
push userkwierso@gmail.com
push dateFri, 20 Jun 2014 01:07:53 +0000
treeherdermozilla-central@bdac18bd6c74 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersterrence
bugs1019585
milestone33.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 1019585 part 5 - Make BuildDollarReplacement handle Latin1 strings. r=terrence
js/src/jit-test/tests/latin1/replace.js
js/src/jsstr.cpp
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/latin1/replace.js
@@ -0,0 +1,42 @@
+function testDollarReplacement() {
+    // Latin1 input, pat and replacement
+    var s = toLatin1("Foobarbaz123");
+    var pat = toLatin1("bar");
+    assertEq(s.replace(pat, toLatin1("AA")), "FooAAbaz123");
+    assertEq(s.replace(pat, toLatin1("A$$A")), "FooA$Abaz123");
+    assertEq(s.replace(pat, toLatin1("A$`A")), "FooAFooAbaz123");
+    assertEq(s.replace(pat, toLatin1("A$&A")), "FooAbarAbaz123");
+    assertEq(s.replace(pat, toLatin1("A$'A")), "FooAbaz123Abaz123");
+
+    // Latin1 input and pat, TwoByte replacement
+    assertEq(s.replace(pat, "A\u1200"), "FooA\u1200baz123");
+    assertEq(s.replace(pat, "A$$\u1200"), "FooA$\u1200baz123");
+    assertEq(s.replace(pat, "A$`\u1200"), "FooAFoo\u1200baz123");
+    assertEq(s.replace(pat, "A$&\u1200"), "FooAbar\u1200baz123");
+    assertEq(s.replace(pat, "A$'\u1200"), "FooAbaz123\u1200baz123");
+
+    // TwoByte input, Latin1 pat and replacement
+    s = "Foobarbaz123\u1200";
+    assertEq(s.replace(pat, toLatin1("A")), "FooAbaz123\u1200");
+    assertEq(s.replace(pat, toLatin1("A$$")), "FooA$baz123\u1200");
+    assertEq(s.replace(pat, toLatin1("A$`")), "FooAFoobaz123\u1200");
+    assertEq(s.replace(pat, toLatin1("A$&")), "FooAbarbaz123\u1200");
+    assertEq(s.replace(pat, toLatin1("A$'")), "FooAbaz123\u1200baz123\u1200");
+
+    // TwoByte input and pat, Latin1 replacement
+    s = "Foobar\u1200baz123";
+    pat += "\u1200";
+    assertEq(s.replace(pat, toLatin1("AB")), "FooABbaz123");
+    assertEq(s.replace(pat, toLatin1("A$$B")), "FooA$Bbaz123");
+    assertEq(s.replace(pat, toLatin1("A$`B")), "FooAFooBbaz123");
+    assertEq(s.replace(pat, toLatin1("A$&B")), "FooAbar\u1200Bbaz123");
+    assertEq(s.replace(pat, toLatin1("A$'B")), "FooAbaz123Bbaz123");
+
+    // TwoByte input, pat and replacement
+    assertEq(s.replace(pat, "A\u1300"), "FooA\u1300baz123");
+    assertEq(s.replace(pat, "A$$\u1300"), "FooA$\u1300baz123");
+    assertEq(s.replace(pat, "A$`\u1300"), "FooAFoo\u1300baz123");
+    assertEq(s.replace(pat, "A$&\u1300"), "FooAbar\u1200\u1300baz123");
+    assertEq(s.replace(pat, "A$'\u1300"), "FooAbaz123\u1300baz123");
+}
+testDollarReplacement();
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -2899,16 +2899,67 @@ BuildFlatReplacement(JSContext *cx, Hand
             return false;
         }
     }
 
     rval.setString(builder.result());
     return true;
 }
 
+template <typename CharT>
+static bool
+AppendDollarReplacement(StringBuffer &newReplaceChars, size_t firstDollarIndex,
+                        const FlatMatch &fm, JSLinearString *text,
+                        const CharT *repChars, size_t repLength)
+{
+    JS_ASSERT(firstDollarIndex < repLength);
+
+    size_t matchStart = fm.match();
+    size_t matchLimit = matchStart + fm.patternLength();
+
+    /* Move the pre-dollar chunk in bulk. */
+    newReplaceChars.infallibleAppend(repChars, firstDollarIndex);
+
+    /* Move the rest char-by-char, interpreting dollars as we encounter them. */
+    const CharT *repLimit = repChars + repLength;
+    for (const CharT *it = repChars + firstDollarIndex; it < repLimit; ++it) {
+        if (*it != '$' || it == repLimit - 1) {
+            if (!newReplaceChars.append(*it))
+                return false;
+            continue;
+        }
+
+        switch (*(it + 1)) {
+          case '$': /* Eat one of the dollars. */
+            if (!newReplaceChars.append(*it))
+                return false;
+            break;
+          case '&':
+            if (!newReplaceChars.appendSubstring(text, matchStart, matchLimit - matchStart))
+                return false;
+            break;
+          case '`':
+            if (!newReplaceChars.appendSubstring(text, 0, matchStart))
+                return false;
+            break;
+          case '\'':
+            if (!newReplaceChars.appendSubstring(text, matchLimit, text->length() - matchLimit))
+                return false;
+            break;
+          default: /* The dollar we saw was not special (no matter what its mother told it). */
+            if (!newReplaceChars.append(*it))
+                return false;
+            continue;
+        }
+        ++it; /* We always eat an extra char in the above switch. */
+    }
+
+    return true;
+}
+
 /*
  * Perform a linear-scan dollar substitution on the replacement text,
  * constructing a result string that looks like:
  *
  *      newstring = string[:matchStart] + dollarSub(replaceValue) + string[matchLimit:]
  */
 static inline bool
 BuildDollarReplacement(JSContext *cx, JSString *textstrArg, JSLinearString *repstr,
@@ -2930,55 +2981,28 @@ BuildDollarReplacement(JSContext *cx, JS
      */
     StringBuffer newReplaceChars(cx);
     if (repstr->hasTwoByteChars() && !newReplaceChars.ensureTwoByteChars())
         return false;
 
     if (!newReplaceChars.reserve(textstr->length() - fm.patternLength() + repstr->length()))
         return false;
 
-    JS_ASSERT(firstDollarIndex < repstr->length());
-
-    /* Move the pre-dollar chunk in bulk. */
-    newReplaceChars.infallibleAppend(repstr->chars(), firstDollarIndex);
-
-    /* Move the rest char-by-char, interpreting dollars as we encounter them. */
-    const jschar *textchars = textstr->chars();
-    const jschar *repstrLimit = repstr->chars() + repstr->length();
-    for (const jschar *it = repstr->chars() + firstDollarIndex; it < repstrLimit; ++it) {
-        if (*it != '$' || it == repstrLimit - 1) {
-            if (!newReplaceChars.append(*it))
-                return false;
-            continue;
-        }
-
-        switch (*(it + 1)) {
-          case '$': /* Eat one of the dollars. */
-            if (!newReplaceChars.append(*it))
-                return false;
-            break;
-          case '&':
-            if (!newReplaceChars.append(textchars + matchStart, textchars + matchLimit))
-                return false;
-            break;
-          case '`':
-            if (!newReplaceChars.append(textchars, textchars + matchStart))
-                return false;
-            break;
-          case '\'':
-            if (!newReplaceChars.append(textchars + matchLimit, textchars + textstr->length()))
-                return false;
-            break;
-          default: /* The dollar we saw was not special (no matter what its mother told it). */
-            if (!newReplaceChars.append(*it))
-                return false;
-            continue;
-        }
-        ++it; /* We always eat an extra char in the above switch. */
+    bool res;
+    if (repstr->hasLatin1Chars()) {
+        AutoCheckCannotGC nogc;
+        res = AppendDollarReplacement(newReplaceChars, firstDollarIndex, fm, textstr,
+                                      repstr->latin1Chars(nogc), repstr->length());
+    } else {
+        AutoCheckCannotGC nogc;
+        res = AppendDollarReplacement(newReplaceChars, firstDollarIndex, fm, textstr,
+                                      repstr->twoByteChars(nogc), repstr->length());
     }
+    if (!res)
+        return false;
 
     RootedString leftSide(cx, js_NewDependentString(cx, textstr, 0, matchStart));
     if (!leftSide)
         return false;
 
     RootedString newReplace(cx, newReplaceChars.finishString());
     if (!newReplace)
         return false;