summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--js/src/builtin/RegExp.cpp229
-rw-r--r--js/src/builtin/RegExp.h6
-rw-r--r--js/src/builtin/RegExp.js80
-rw-r--r--js/src/builtin/RegExpGlobalReplaceOpt.h.js23
-rw-r--r--js/src/builtin/RegExpLocalReplaceOpt.h.js18
-rw-r--r--js/src/vm/SelfHosting.cpp36
6 files changed, 279 insertions, 113 deletions
diff --git a/js/src/builtin/RegExp.cpp b/js/src/builtin/RegExp.cpp
index de83b38aed..9b0e1a7cb6 100644
--- a/js/src/builtin/RegExp.cpp
+++ b/js/src/builtin/RegExp.cpp
@@ -1318,10 +1318,10 @@ GetParen(JSLinearString* matched, const JS::Value& capture, JSSubString* out)
template <typename CharT>
static bool
InterpretDollar(JSLinearString* matched, JSLinearString* string, size_t position, size_t tailPos,
- MutableHandle<CapturesVector> captures, JSLinearString* replacement,
+ Handle<CapturesVector> captures, Handle<CapturesVector> namedCaptures, JSLinearString* replacement,
const CharT* replacementBegin, const CharT* currentDollar,
const CharT* replacementEnd,
- JSSubString* out, size_t* skip)
+ JSSubString* out, size_t* skip, uint32_t* currentNamedCapture)
{
MOZ_ASSERT(*currentDollar == '$');
@@ -1363,6 +1363,35 @@ InterpretDollar(JSLinearString* matched, JSLinearString* string, size_t position
return true;
}
+ // '$<': Named Captures
+ if (c == '<') {
+ // Step 1.
+ if (namedCaptures.length() == 0) {
+ return false;
+ }
+
+ // Step 2.b
+ const CharT* nameStart = currentDollar + 2;
+ const CharT* nameEnd = js_strchr_limit(nameStart, '>', replacementEnd);
+
+ // Step 2.c
+ if (!nameEnd) {
+ return false;
+ }
+
+ // Step 2.d
+ // We precompute named capture replacements in InitNamedCaptures.
+ // They are stored in the order in which we will need them, so here
+ // we can just take the next one in the list.
+ size_t nameLength = nameEnd - nameStart;
+ *skip = nameLength + 3; // $<...>
+
+ // Steps 2.d.iii-iv
+ GetParen(matched, namedCaptures[*currentNamedCapture], out);
+ *currentNamedCapture += 1;
+ return true;
+ }
+
*skip = 2;
switch (c) {
default:
@@ -1393,8 +1422,9 @@ InterpretDollar(JSLinearString* matched, JSLinearString* string, size_t position
template <typename CharT>
static bool
FindReplaceLengthString(JSContext* cx, HandleLinearString matched, HandleLinearString string,
- size_t position, size_t tailPos, MutableHandle<CapturesVector> captures,
- HandleLinearString replacement, size_t firstDollarIndex, size_t* sizep)
+ size_t position, size_t tailPos, Handle<CapturesVector> captures,
+ Handle<CapturesVector> namedCaptures, HandleLinearString replacement,
+ size_t firstDollarIndex, size_t* sizep)
{
CheckedInt<uint32_t> replen = replacement->length();
@@ -1403,11 +1433,13 @@ FindReplaceLengthString(JSContext* cx, HandleLinearString matched, HandleLinearS
const CharT* replacementBegin = replacement->chars<CharT>(nogc);
const CharT* currentDollar = replacementBegin + firstDollarIndex;
const CharT* replacementEnd = replacementBegin + replacement->length();
+ uint32_t currentNamedCapture = 0;
do {
JSSubString sub;
size_t skip;
- if (InterpretDollar(matched, string, position, tailPos, captures, replacement,
- replacementBegin, currentDollar, replacementEnd, &sub, &skip))
+ if (InterpretDollar(matched, string, position, tailPos, captures, namedCaptures,
+ replacement, replacementBegin, currentDollar, replacementEnd,
+ &sub, &skip, &currentNamedCapture))
{
if (sub.length > skip)
replen += sub.length - skip;
@@ -1432,14 +1464,14 @@ FindReplaceLengthString(JSContext* cx, HandleLinearString matched, HandleLinearS
static bool
FindReplaceLength(JSContext* cx, HandleLinearString matched, HandleLinearString string,
- size_t position, size_t tailPos, MutableHandle<CapturesVector> captures,
+ size_t position, size_t tailPos, Handle<CapturesVector> captures, Handle<CapturesVector> namedCaptures,
HandleLinearString replacement, size_t firstDollarIndex, size_t* sizep)
{
return replacement->hasLatin1Chars()
? FindReplaceLengthString<Latin1Char>(cx, matched, string, position, tailPos, captures,
- replacement, firstDollarIndex, sizep)
+ namedCaptures, replacement, firstDollarIndex, sizep)
: FindReplaceLengthString<char16_t>(cx, matched, string, position, tailPos, captures,
- replacement, firstDollarIndex, sizep);
+ namedCaptures, replacement, firstDollarIndex, sizep);
}
/*
@@ -1450,7 +1482,7 @@ FindReplaceLength(JSContext* cx, HandleLinearString matched, HandleLinearString
template <typename CharT>
static void
DoReplace(HandleLinearString matched, HandleLinearString string,
- size_t position, size_t tailPos, MutableHandle<CapturesVector> captures,
+ size_t position, size_t tailPos, Handle<CapturesVector> captures, Handle<CapturesVector> namedCaptures,
HandleLinearString replacement, size_t firstDollarIndex, StringBuffer &sb)
{
JS::AutoCheckCannotGC nogc;
@@ -1460,6 +1492,7 @@ DoReplace(HandleLinearString matched, HandleLinearString string,
MOZ_ASSERT(firstDollarIndex < replacement->length());
const CharT* currentDollar = replacementBegin + firstDollarIndex;
const CharT* replacementEnd = replacementBegin + replacement->length();
+ uint32_t currentNamedCapture = 0;
do {
/* Move one of the constant portions of the replacement value. */
size_t len = currentDollar - currentChar;
@@ -1468,8 +1501,8 @@ DoReplace(HandleLinearString matched, HandleLinearString string,
JSSubString sub;
size_t skip;
- if (InterpretDollar(matched, string, position, tailPos, captures, replacement,
- replacementBegin, currentDollar, replacementEnd, &sub, &skip))
+ if (InterpretDollar(matched, string, position, tailPos, captures, namedCaptures, replacement,
+ replacementBegin, currentDollar, replacementEnd, &sub, &skip, &currentNamedCapture))
{
sb.infallibleAppendSubstring(sub.base, sub.offset, sub.length);
currentChar += skip;
@@ -1483,9 +1516,117 @@ DoReplace(HandleLinearString matched, HandleLinearString string,
sb.infallibleAppend(currentChar, replacement->length() - (currentChar - replacementBegin));
}
+/*
+ * This function finds the list of named captures of the form
+ * "$<name>" in a replacement string and converts them into jsids, for
+ * use in InitNamedReplacements.
+ */
+template <typename CharT>
+static bool CollectNames(JSContext* cx, HandleLinearString replacement,
+ size_t firstDollarIndex,
+ MutableHandle<GCVector<jsid>> names) {
+ JS::AutoCheckCannotGC nogc;
+ MOZ_ASSERT(firstDollarIndex < replacement->length());
+
+ const CharT* replacementBegin = replacement->chars<CharT>(nogc);
+ const CharT* currentDollar = replacementBegin + firstDollarIndex;
+ const CharT* replacementEnd = replacementBegin + replacement->length();
+
+ // https://tc39.es/ecma262/#table-45, "$<" section
+ while (currentDollar && currentDollar + 1 < replacementEnd) {
+ if (currentDollar[1] == '<') {
+ // Step 2.b
+ const CharT* nameStart = currentDollar + 2;
+ const CharT* nameEnd = js_strchr_limit(nameStart, '>', replacementEnd);
+
+ // Step 2.c
+ if (!nameEnd) {
+ return true;
+ }
+
+ // Step 2.d.i
+ size_t nameLength = nameEnd - nameStart;
+ JSAtom* atom = AtomizeChars(cx, nameStart, nameLength);
+ if (!atom || !names.append(AtomToId(atom))) {
+ return false;
+ }
+ currentDollar = nameEnd + 1;
+ } else {
+ currentDollar += 2;
+ }
+ currentDollar = js_strchr_limit(currentDollar, '$', replacementEnd);
+ }
+ return true;
+}
+
+/*
+ * When replacing named captures, the spec requires us to perform
+ * `Get(match.groups, name)` for each "$<name>". These `Get`s can be
+ * script-visible; for example, RegExp can be extended with an `exec`
+ * method that wraps `groups` in a proxy. To make sure that we do the
+ * right thing, if a regexp has named captures, we find the named
+ * capture replacements before beginning the actual replacement.
+ * This guarantees that we will call GetProperty once and only once for
+ * each "$<name>" in the replacement string, in the correct order.
+ *
+ * This function precomputes the results of step 2 of the '$<' case
+ * here: https://tc39.es/proposal-regexp-named-groups/#table-45, so
+ * that when we need to access the nth named capture in InterpretDollar,
+ * we can just use the nth value stored in namedCaptures.
+ */
+static bool InitNamedCaptures(JSContext* cx, HandleLinearString replacement,
+ HandleObject groups, size_t firstDollarIndex,
+ MutableHandle<CapturesVector> namedCaptures) {
+ Rooted<GCVector<jsid>> names(cx, GCVector<jsid>(cx));
+ if (replacement->hasLatin1Chars()) {
+ if (!CollectNames<Latin1Char>(cx, replacement, firstDollarIndex, &names)) {
+ return false;
+ }
+ } else {
+ if (!CollectNames<char16_t>(cx, replacement, firstDollarIndex, &names)) {
+ return false;
+ }
+ }
+
+ // https://tc39.es/ecma262/#table-45, "$<" section
+ RootedId id(cx);
+ RootedValue capture(cx);
+ for (uint32_t i = 0; i < names.length(); i++) {
+ // Step 2.d.i
+ id = names[i];
+
+ // Step 2.d.ii
+ if (!GetProperty(cx, groups, groups, id, &capture)) {
+ return false;
+ }
+
+ // Step 2.d.iii
+ if (capture.isUndefined()) {
+ if (!namedCaptures.append(capture)) {
+ return false;
+ }
+ } else {
+ // Step 2.d.iv
+ JSString* str = ToString<CanGC>(cx, capture);
+ if (!str) {
+ return false;
+ }
+ JSLinearString* linear = str->ensureLinear(cx);
+ if (!linear) {
+ return false;
+ }
+ if (!namedCaptures.append(StringValue(linear))) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
static bool
-NeedTwoBytes(HandleLinearString string, HandleLinearString replacement,
- HandleLinearString matched, Handle<CapturesVector> captures)
+NeedTwoBytes(HandleLinearString string, HandleLinearString replacement, HandleLinearString matched,
+ Handle<CapturesVector> captures, Handle<CapturesVector> namedCaptures)
{
if (string->hasTwoByteChars())
return true;
@@ -1502,19 +1643,38 @@ NeedTwoBytes(HandleLinearString string, HandleLinearString replacement,
return true;
}
+ for (size_t i = 0, len = namedCaptures.length(); i < len; i++) {
+ Value capture = namedCaptures[i];
+ if (capture.isUndefined())
+ continue;
+ if (capture.toString()->hasTwoByteChars())
+ return true;
+ }
+
return false;
}
/* ES 2016 draft Mar 25, 2016 21.1.3.14.1. */
bool
-js::RegExpGetSubstitution(JSContext* cx, HandleLinearString matched, HandleLinearString string,
- size_t position, HandleObject capturesObj, HandleLinearString replacement,
- size_t firstDollarIndex, MutableHandleValue rval)
+js::RegExpGetSubstitution(JSContext* cx, HandleArrayObject matchResult, HandleLinearString string,
+ size_t position, HandleLinearString replacement, size_t firstDollarIndex,
+ HandleValue groups, MutableHandleValue rval)
{
MOZ_ASSERT(firstDollarIndex < replacement->length());
// Step 1 (skipped).
+ // Step 10 (reordered).
+ uint32_t matchResultLength = matchResult->length();
+ MOZ_ASSERT(matchResultLength > 0);
+ MOZ_ASSERT(matchResultLength == matchResult->getDenseInitializedLength());
+
+ const Value& matchedValue = matchResult->getDenseElement(0);
+ RootedLinearString matched(cx, matchedValue.toString()->ensureLinear(cx));
+ if (!matched)
+ return false;
+
+
// Step 2.
size_t matchLength = matched->length();
@@ -1523,33 +1683,36 @@ js::RegExpGetSubstitution(JSContext* cx, HandleLinearString matched, HandleLinea
// Step 6.
MOZ_ASSERT(position <= string->length());
- // Step 10 (reordered).
- uint32_t nCaptures;
- if (!GetLengthProperty(cx, capturesObj, &nCaptures))
- return false;
-
+ uint32_t nCaptures = matchResultLength - 1;
Rooted<CapturesVector> captures(cx, CapturesVector(cx));
if (!captures.reserve(nCaptures))
return false;
// Step 7.
- RootedValue capture(cx);
- for (uint32_t i = 0; i < nCaptures; i++) {
- if (!GetElement(cx, capturesObj, capturesObj, i, &capture))
- return false;
+ for (uint32_t i = 1; i <= nCaptures; i++) {
+ const Value& capture = matchResult->getDenseElement(i);
if (capture.isUndefined()) {
captures.infallibleAppend(capture);
continue;
}
- MOZ_ASSERT(capture.isString());
- RootedLinearString captureLinear(cx, capture.toString()->ensureLinear(cx));
+ JSLinearString* captureLinear = capture.toString()->ensureLinear(cx);
if (!captureLinear)
return false;
captures.infallibleAppend(StringValue(captureLinear));
}
+ Rooted<CapturesVector> namedCaptures(cx, CapturesVector(cx));
+ if (groups.isObject()) {
+ RootedObject groupsObj(cx, &groups.toObject());
+ if (!InitNamedCaptures(cx, replacement, groupsObj, firstDollarIndex, &namedCaptures)) {
+ return false;
+ }
+ } else {
+ MOZ_ASSERT(groups.isUndefined());
+ }
+
// Step 8 (skipped).
// Step 9.
@@ -1564,14 +1727,14 @@ js::RegExpGetSubstitution(JSContext* cx, HandleLinearString matched, HandleLinea
// Step 11.
size_t reserveLength;
- if (!FindReplaceLength(cx, matched, string, position, tailPos, &captures, replacement,
- firstDollarIndex, &reserveLength))
+ if (!FindReplaceLength(cx, matched, string, position, tailPos, captures, namedCaptures,
+ replacement, firstDollarIndex, &reserveLength))
{
return false;
}
StringBuffer result(cx);
- if (NeedTwoBytes(string, replacement, matched, captures)) {
+ if (NeedTwoBytes(string, replacement, matched, captures, namedCaptures)) {
if (!result.ensureTwoByteChars())
return false;
}
@@ -1580,10 +1743,10 @@ js::RegExpGetSubstitution(JSContext* cx, HandleLinearString matched, HandleLinea
return false;
if (replacement->hasLatin1Chars()) {
- DoReplace<Latin1Char>(matched, string, position, tailPos, &captures,
+ DoReplace<Latin1Char>(matched, string, position, tailPos, captures, namedCaptures,
replacement, firstDollarIndex, result);
} else {
- DoReplace<char16_t>(matched, string, position, tailPos, &captures,
+ DoReplace<char16_t>(matched, string, position, tailPos, captures, namedCaptures,
replacement, firstDollarIndex, result);
}
diff --git a/js/src/builtin/RegExp.h b/js/src/builtin/RegExp.h
index cb88319ac9..f66c9b1b81 100644
--- a/js/src/builtin/RegExp.h
+++ b/js/src/builtin/RegExp.h
@@ -122,9 +122,9 @@ extern MOZ_MUST_USE bool
RegExpInstanceOptimizableRaw(JSContext* cx, JSObject* obj, JSObject* proto);
extern MOZ_MUST_USE bool
-RegExpGetSubstitution(JSContext* cx, HandleLinearString matched, HandleLinearString string,
- size_t position, HandleObject capturesObj, HandleLinearString replacement,
- size_t firstDollarIndex, MutableHandleValue rval);
+RegExpGetSubstitution(JSContext* cx, HandleArrayObject matchResult, HandleLinearString string,
+ size_t position, HandleLinearString replacement, size_t firstDollarIndex,
+ HandleValue namedCaptures, MutableHandleValue rval);
extern MOZ_MUST_USE bool
GetFirstDollarIndex(JSContext* cx, unsigned argc, Value* vp);
diff --git a/js/src/builtin/RegExp.js b/js/src/builtin/RegExp.js
index 879375b988..ab4d76f4ca 100644
--- a/js/src/builtin/RegExp.js
+++ b/js/src/builtin/RegExp.js
@@ -395,9 +395,8 @@ function RegExpReplaceSlowPath(rx, S, lengthS, replaceValue,
var n, capN, replacement;
if (functionalReplace || firstDollarIndex !== -1) {
- // Steps 14.g-j.
+ // Steps 14.g-k.
replacement = RegExpGetComplexReplacement(result, matched, S, position,
-
nCaptures, replaceValue,
functionalReplace, firstDollarIndex);
} else {
@@ -411,16 +410,21 @@ function RegExpReplaceSlowPath(rx, S, lengthS, replaceValue,
if (capN !== undefined)
ToString(capN);
}
+ // Step 14.j, 14.l., GetSubstitution Step 11.
+ // We don't need namedCaptures, but ToObject is visible to script.
+ var namedCaptures = result.groups;
+ if (namedCaptures !== undefined)
+ ToObject(namedCaptures);
replacement = replaceValue;
}
- // Step 14.l.
+ // Step 14.m.
if (position >= nextSourcePosition) {
- // Step 14.l.ii.
+ // Step 14.m.ii.
accumulatedResult += Substring(S, nextSourcePosition,
position - nextSourcePosition) + replacement;
- // Step 14.l.iii.
+ // Step 14.m.iii.
nextSourcePosition = position + matchLength;
}
}
@@ -433,15 +437,14 @@ function RegExpReplaceSlowPath(rx, S, lengthS, replaceValue,
return accumulatedResult + Substring(S, nextSourcePosition, lengthS - nextSourcePosition);
}
-// ES 2017 draft rev 03bfda119d060aca4099d2b77cf43f6d4f11cfa2 21.2.5.8
-// steps 14.g-k.
+// ES 2021 draft 21.2.5.10
+// https://tc39.es/ecma262/#sec-regexp.prototype-@@replace
+// steps 14.g-l.
// Calculates functional/substitution replaceement from match result.
// Used in the following functions:
// * RegExpGlobalReplaceOptFunc
// * RegExpGlobalReplaceOptElemBase
-// * RegExpGlobalReplaceOptSubst
// * RegExpLocalReplaceOptFunc
-// * RegExpLocalReplaceOptSubst
// * RegExpReplaceSlowPath
function RegExpGetComplexReplacement(result, matched, S, position,
nCaptures, replaceValue,
@@ -451,13 +454,8 @@ function RegExpGetComplexReplacement(result, matched, S, position,
var captures = [];
var capturesLength = 0;
- // Step 14.j.i (reordered).
- // For `nCaptures` <= 4 case, call `replaceValue` directly, otherwise
- // use `std_Function_apply` with all arguments stored in `captures`.
- // In latter case, store `matched` as the first element here, to
- // avoid unshift later.
- if (functionalReplace && nCaptures > 4)
- _DefineDataProperty(captures, capturesLength++, matched);
+ // Step 14.k.i (reordered).
+ _DefineDataProperty(captures, capturesLength++, matched);
// Step 14.g, 14.i, 14.i.iv.
for (var n = 1; n <= nCaptures; n++) {
@@ -473,29 +471,41 @@ function RegExpGetComplexReplacement(result, matched, S, position,
}
// Step 14.j.
+ var namedCaptures = result.groups;
+
+ // Step 14.k.
if (functionalReplace) {
- switch (nCaptures) {
- case 0:
- return ToString(replaceValue(matched, position, S));
- case 1:
- return ToString(replaceValue(matched, SPREAD(captures, 1), position, S));
- case 2:
- return ToString(replaceValue(matched, SPREAD(captures, 2), position, S));
- case 3:
- return ToString(replaceValue(matched, SPREAD(captures, 3), position, S));
- case 4:
- return ToString(replaceValue(matched, SPREAD(captures, 4), position, S));
- default:
- // Steps 14.j.ii-v.
- _DefineDataProperty(captures, capturesLength++, position);
- _DefineDataProperty(captures, capturesLength++, S);
- return ToString(callFunction(std_Function_apply, replaceValue, null, captures));
+ // For `nCaptures` <= 4 case, call `replaceValue` directly, otherwise
+ // use `std_Function_apply` with all arguments stored in `captures`.
+ if (namedCaptures === undefined) {
+ switch (nCaptures) {
+ case 0:
+ return ToString(replaceValue(SPREAD(captures, 1), position, S));
+ case 1:
+ return ToString(replaceValue(SPREAD(captures, 2), position, S));
+ case 2:
+ return ToString(replaceValue(SPREAD(captures, 3), position, S));
+ case 3:
+ return ToString(replaceValue(SPREAD(captures, 4), position, S));
+ case 4:
+ return ToString(replaceValue(SPREAD(captures, 5), position, S));
+ }
+ }
+ // Steps 14.k.ii-v.
+ _DefineDataProperty(captures, capturesLength++, position);
+ _DefineDataProperty(captures, capturesLength++, S);
+ if (namedCaptures !== undefined) {
+ _DefineDataProperty(captures, capturesLength++, namedCaptures);
}
+ return ToString(callFunction(std_Function_apply, replaceValue, undefined, captures));
}
- // Steps 14.k.i.
- return RegExpGetSubstitution(matched, S, position, captures, replaceValue,
- firstDollarIndex);
+ // Step 14.l.
+ if (namedCaptures !== undefined) {
+ namedCaptures = ToObject(namedCaptures);
+ }
+ return RegExpGetSubstitution(captures, S, position, replaceValue, firstDollarIndex,
+ namedCaptures);
}
// ES 2017 draft rev 03bfda119d060aca4099d2b77cf43f6d4f11cfa2 21.2.5.8
diff --git a/js/src/builtin/RegExpGlobalReplaceOpt.h.js b/js/src/builtin/RegExpGlobalReplaceOpt.h.js
index fbe50a3f9c..8b82fc31d4 100644
--- a/js/src/builtin/RegExpGlobalReplaceOpt.h.js
+++ b/js/src/builtin/RegExpGlobalReplaceOpt.h.js
@@ -53,7 +53,7 @@ function FUNC_NAME(rx, S, lengthS, replaceValue, fullUnicode
break;
var nCaptures;
-#if defined(FUNCTIONAL) || defined(SUBSTITUTION)
+#if defined(FUNCTIONAL)
// Steps 14.a-b.
nCaptures = std_Math_max(result.length - 1, 0);
#endif
@@ -68,18 +68,19 @@ function FUNC_NAME(rx, S, lengthS, replaceValue, fullUnicode
var position = result.index;
lastIndex = position + matchLength;
- // Steps g-j.
+ // Steps g-l.
var replacement;
#if defined(FUNCTIONAL)
replacement = RegExpGetComplexReplacement(result, matched, S, position,
-
nCaptures, replaceValue,
true, -1);
-#elif defined(SUBSTITUTION)
- replacement = RegExpGetComplexReplacement(result, matched, S, position,
-
- nCaptures, replaceValue,
- false, firstDollarIndex);
+#elif defined(SUBSTITUTION) // Step l.i
+ var namedCaptures = result.groups;
+ if (namedCaptures !== undefined) {
+ namedCaptures = ToObject(namedCaptures);
+ }
+ // Step l.ii
+ replacement = RegExpGetSubstitution(result, S, position, replaceValue, firstDollarIndex, namedCaptures);
#elif defined(ELEMBASE)
if (IsObject(elemBase)) {
var prop = GetStringDataProperty(elemBase, matched);
@@ -96,7 +97,6 @@ function FUNC_NAME(rx, S, lengthS, replaceValue, fullUnicode
nCaptures = std_Math_max(result.length - 1, 0);
replacement = RegExpGetComplexReplacement(result, matched, S, position,
-
nCaptures, replaceValue,
true, -1);
}
@@ -104,11 +104,11 @@ function FUNC_NAME(rx, S, lengthS, replaceValue, fullUnicode
replacement = replaceValue;
#endif
- // Step 14.l.ii.
+ // Step 14.m.ii.
accumulatedResult += Substring(S, nextSourcePosition,
position - nextSourcePosition) + replacement;
- // Step 14.l.iii.
+ // Step 14.m.iii.
nextSourcePosition = lastIndex;
// Step 11.c.iii.2.
@@ -116,6 +116,7 @@ function FUNC_NAME(rx, S, lengthS, replaceValue, fullUnicode
lastIndex = fullUnicode ? AdvanceStringIndex(S, lastIndex) : lastIndex + 1;
if (lastIndex > lengthS)
break;
+ lastIndex |= 0;
}
}
diff --git a/js/src/builtin/RegExpLocalReplaceOpt.h.js b/js/src/builtin/RegExpLocalReplaceOpt.h.js
index 1acd6a73a4..ac74d17ada 100644
--- a/js/src/builtin/RegExpLocalReplaceOpt.h.js
+++ b/js/src/builtin/RegExpLocalReplaceOpt.h.js
@@ -60,9 +60,9 @@ function FUNC_NAME(rx, S, lengthS, replaceValue
return S;
}
- // Steps 11.c, 12-13, 14.a-b (skipped).
+ // Steps 11.c, 12-13 (skipped).
-#if defined(FUNCTIONAL) || defined(SUBSTITUTION)
+#if defined(FUNCTIONAL)
// Steps 14.a-b.
var nCaptures = std_Math_max(result.length - 1, 0);
#endif
@@ -88,19 +88,21 @@ function FUNC_NAME(rx, S, lengthS, replaceValue
// Steps g-j.
#if defined(FUNCTIONAL)
replacement = RegExpGetComplexReplacement(result, matched, S, position,
-
nCaptures, replaceValue,
true, -1);
#elif defined(SUBSTITUTION)
- replacement = RegExpGetComplexReplacement(result, matched, S, position,
-
- nCaptures, replaceValue,
- false, firstDollarIndex);
+ // Step l.i
+ var namedCaptures = result.groups;
+ if (namedCaptures !== undefined) {
+ namedCaptures = ToObject(namedCaptures);
+ }
+ // Step l.ii
+ replacement = RegExpGetSubstitution(result, S, position, replaceValue, firstDollarIndex, namedCaptures);
#else
replacement = replaceValue;
#endif
- // Step 14.l.ii.
+ // Step 14.m.ii.
var accumulatedResult = Substring(S, 0, position) + replacement;
// Step 15.
diff --git a/js/src/vm/SelfHosting.cpp b/js/src/vm/SelfHosting.cpp
index 06490f24df..686b2e9c28 100644
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -1697,39 +1697,29 @@ static bool
intrinsic_RegExpGetSubstitution(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
-
MOZ_ASSERT(args.length() == 6);
- RootedString matched(cx, args[0].toString());
- RootedString string(cx, args[1].toString());
+ RootedArrayObject matchResult(cx, &args[0].toObject().as<ArrayObject>());
+
+ RootedLinearString string(cx, args[1].toString()->ensureLinear(cx));
+ if (!string)
+ return false;
int32_t position = int32_t(args[2].toNumber());
MOZ_ASSERT(position >= 0);
- RootedObject captures(cx, &args[3].toObject());
-#ifdef DEBUG
- bool isArray = false;
- MOZ_ALWAYS_TRUE(IsArray(cx, captures, &isArray));
- MOZ_ASSERT(isArray);
-#endif
-
- RootedString replacement(cx, args[4].toString());
+ RootedLinearString replacement(cx, args[3].toString()->ensureLinear(cx));
+ if (!replacement)
+ return false;
- int32_t firstDollarIndex = int32_t(args[5].toNumber());
+ int32_t firstDollarIndex = int32_t(args[4].toNumber());
MOZ_ASSERT(firstDollarIndex >= 0);
- RootedLinearString matchedLinear(cx, matched->ensureLinear(cx));
- if (!matchedLinear)
- return false;
- RootedLinearString stringLinear(cx, string->ensureLinear(cx));
- if (!stringLinear)
- return false;
- RootedLinearString replacementLinear(cx, replacement->ensureLinear(cx));
- if (!replacementLinear)
- return false;
+ RootedValue namedCaptures(cx, args[5]);
+ MOZ_ASSERT(namedCaptures.isUndefined() || namedCaptures.isObject());
- return RegExpGetSubstitution(cx, matchedLinear, stringLinear, size_t(position), captures,
- replacementLinear, size_t(firstDollarIndex), args.rval());
+ return RegExpGetSubstitution(cx, matchResult, string, size_t(position), replacement,
+ size_t(firstDollarIndex), namedCaptures, args.rval());
}
static bool