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 id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewersterrence
bugs1019585
milestone33.0a1
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;