Bug 1509768 - Handle the case that String#replace is called with a empty string pattern on a rope. r=evilpie
authorTooru Fujisawa <arai_a@mac.com>
Tue, 27 Nov 2018 19:18:52 +0900
changeset 504711 4f9a88ca20ebd876c51948bc171269502865c3ee
parent 504710 f3361f0816dd2a7e63d1acb92e23b74f0e8f0f30
child 504712 aabcdc32ab3ca9a0eab35055ff8ba1f3a109b019
push id10290
push userffxbld-merge
push dateMon, 03 Dec 2018 16:23:23 +0000
treeherdermozilla-beta@700bed2445e6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersevilpie
bugs1509768
milestone65.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 1509768 - Handle the case that String#replace is called with a empty string pattern on a rope. r=evilpie
js/src/builtin/String.cpp
js/src/builtin/TestingFunctions.cpp
js/src/tests/non262/String/replace-rope-empty.js
--- a/js/src/builtin/String.cpp
+++ b/js/src/builtin/String.cpp
@@ -2730,16 +2730,28 @@ BuildFlatRopeReplacement(JSContext* cx, 
      * through it, building a new rope.
      */
     StringSegmentRange r(cx);
     if (!r.init(textstr)) {
         return nullptr;
     }
 
     RopeBuilder builder(cx);
+
+    /*
+     * Special case when the pattern string is '', which matches to the
+     * head of the string and doesn't overlap with any component of the rope.
+     */
+    if (patternLength == 0) {
+        MOZ_ASSERT(match == 0);
+        if (!builder.append(repstr)) {
+            return nullptr;
+        }
+    }
+
     size_t pos = 0;
     while (!r.empty()) {
         RootedString str(cx, r.front());
         size_t len = str->length();
         size_t strEnd = pos + len;
         if (pos < matchEnd && strEnd > match) {
             /*
              * We need to special-case any part of the rope that overlaps
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -1718,16 +1718,31 @@ NewRope(JSContext* cx, unsigned argc, Va
         return false;
     }
 
     args.rval().setString(str);
     return true;
 }
 
 static bool
+IsRope(JSContext* cx, unsigned argc, Value* vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+
+    if (!args.get(0).isString()) {
+        JS_ReportErrorASCII(cx, "isRope requires a string argument.");
+        return false;
+    }
+
+    JSString* str = args[0].toString();
+    args.rval().setBoolean(str->isRope());
+    return true;
+}
+
+static bool
 EnsureFlatString(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     if (args.length() != 1 || !args[0].isString()) {
         JS_ReportErrorASCII(cx, "ensureFlatString takes exactly one string argument.");
         return false;
     }
@@ -5945,16 +5960,20 @@ static const JSFunctionSpecWithHelp Test
 #endif // defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
 
     JS_FN_HELP("newRope", NewRope, 3, 0,
 "newRope(left, right[, options])",
 "  Creates a rope with the given left/right strings.\n"
 "  Available options:\n"
 "    nursery: bool - force the string to be created in/out of the nursery, if possible.\n"),
 
+    JS_FN_HELP("isRope", IsRope, 1, 0,
+"isRope(str)",
+"  Returns true if the parameter is a rope"),
+
     JS_FN_HELP("settlePromiseNow", SettlePromiseNow, 1, 0,
 "settlePromiseNow(promise)",
 "  'Settle' a 'promise' immediately. This just marks the promise as resolved\n"
 "  with a value of `undefined` and causes the firing of any onPromiseSettled\n"
 "  hooks set on Debugger instances that are observing the given promise's\n"
 "  global as a debuggee."),
     JS_FN_HELP("getWaitForAllPromise", GetWaitForAllPromise, 1, 0,
 "getWaitForAllPromise(densePromisesArray)",
new file mode 100644
--- /dev/null
+++ b/js/src/tests/non262/String/replace-rope-empty.js
@@ -0,0 +1,28 @@
+var BUGNUMBER = 1509768;
+var summary = "String#replace with an empty string pattern on a rope should prepend the replacement string.";
+
+print(BUGNUMBER + ": " + summary);
+
+// Rope is created when the string length >= 24.
+//
+// This testcase depends on that condition to reliably test the code for
+// String#replace on a rope.
+//
+// Please rewrite this testcase when the following assertion fails.
+assertEq(isRope("a".repeat(23)), false);
+assertEq(isRope("a".repeat(24)), true);
+
+// Not a rope.
+assertEq("a".repeat(23).replace("", "foo"),
+         "foo" + "a".repeat(23));
+assertEq("a".repeat(23).replace("", ""),
+         "a".repeat(23));
+
+// A rope.
+assertEq("a".repeat(24).replace("", "foo"),
+         "foo" + "a".repeat(24));
+assertEq("a".repeat(24).replace("", ""),
+         "a".repeat(24));
+
+if (typeof reportCompare === "function")
+    reportCompare(true, true);