diff --git a/js/src/jsstr.cpp b/js/src/jsstr.cpp index 4aa5e3659c32..ea566643a7f8 100644 --- a/js/src/jsstr.cpp +++ b/js/src/jsstr.cpp @@ -2308,6 +2308,114 @@ BuildDollarReplacement(JSContext *cx, JSString *textstrArg, JSLinearString *reps return true; } +struct StringRange +{ + size_t start; + size_t length; + + StringRange(size_t s, size_t l) + : start(s), length(l) + { } +}; + +static JSString * +AppendSubstrings(JSContext *cx, Handle stableStr, + const StringRange *ranges, size_t rangesLen) +{ + JS_ASSERT(rangesLen); + + /* For single substrings, construct a dependent string. */ + if (rangesLen == 1) + return js_NewDependentString(cx, stableStr, ranges[0].start, ranges[0].length); + + /* Collect substrings into a rope. */ + RopeBuilder rope(cx); + for (size_t i = 0; i < rangesLen; i++) { + const StringRange &sr = ranges[i]; + + RootedString substr(cx, js_NewDependentString(cx, stableStr, sr.start, sr.length)); + if (!substr) + return NULL; + + /* Appending to the rope permanently roots the substring. */ + rope.append(substr); + } + + return rope.result(); +} + +static bool +str_replace_regexp_remove(JSContext *cx, CallArgs args, HandleString str, RegExpShared &re) +{ + Rooted stableStr(cx, str->ensureStable(cx)); + if (!stableStr) + return false; + + Vector ranges; + + StableCharPtr chars = stableStr->chars(); + size_t charsLen = stableStr->length(); + + MatchPair match; + size_t startIndex = 0; /* Index used for iterating through the string. */ + size_t lastIndex = 0; /* Index after last successful match. */ + + /* Accumulate StringRanges for unmatched substrings. */ + while (startIndex <= charsLen) { + if (!JS_CHECK_OPERATION_LIMIT(cx)) + return false; + + RegExpRunStatus status = re.executeMatchOnly(cx, chars, charsLen, &startIndex, match); + if (status == RegExpRunStatus_Error) + return false; + if (status == RegExpRunStatus_Success_NotFound) + break; + + /* Include the latest unmatched substring. */ + if (size_t(match.start) > lastIndex) { + if (!ranges.append(StringRange(lastIndex, match.start - lastIndex))) + return false; + } + + lastIndex = startIndex; + + /* Non-global removal executes at most once. */ + if (!re.global()) + break; + + if (match.isEmpty()) + lastIndex++; + } + + /* If unmatched, return the input string. */ + if (!lastIndex) { + args.rval().setString(str); + return true; + } + + /* The last successful match updates the RegExpStatics. */ + cx->regExpStatics()->updateLazily(cx, stableStr, &re, lastIndex); + + /* Include any remaining part of the string. */ + if (lastIndex < charsLen) { + if (!ranges.append(StringRange(lastIndex, charsLen - lastIndex))) + return false; + } + + /* Handle the empty string before calling .begin(). */ + if (ranges.empty()) { + args.rval().setString(cx->runtime->emptyString); + return true; + } + + JSString *result = AppendSubstrings(cx, stableStr, ranges.begin(), ranges.length()); + if (!result) + return false; + + args.rval().setString(result); + return true; +} + static inline bool str_replace_regexp(JSContext *cx, CallArgs args, ReplaceData &rdata) { @@ -2320,6 +2428,12 @@ str_replace_regexp(JSContext *cx, CallArgs args, ReplaceData &rdata) RegExpStatics *res = cx->regExpStatics(); RegExpShared &re = rdata.g.regExp(); + /* Optimize removal. */ + if (rdata.repstr && rdata.repstr->length() == 0 && !rdata.dollar) { + JS_ASSERT(!rdata.lambda && !rdata.elembase); + return str_replace_regexp_remove(cx, args, rdata.str, re); + } + Value tmp; if (!DoMatch(cx, res, rdata.str, re, ReplaceRegExpCallback, &rdata, REPLACE_ARGS, &tmp)) return false; diff --git a/js/src/vm/RegExpObject.cpp b/js/src/vm/RegExpObject.cpp index 50cbb36324b9..b74c48db20c8 100644 --- a/js/src/vm/RegExpObject.cpp +++ b/js/src/vm/RegExpObject.cpp @@ -122,8 +122,8 @@ MatchPairs::initArray(size_t pairCount) /* Initialize all MatchPair objects to invalid locations. */ for (size_t i = 0; i < pairCount; i++) { - pairs_[i].start = size_t(-1); - pairs_[i].limit = size_t(-1); + pairs_[i].start = -1; + pairs_[i].limit = -1; } return true;