Issue #1285 - Implement named capturing groups for replacing

This commit is contained in:
Martok 2022-12-21 18:53:27 +01:00 committed by roytam1
commit 6eead2deb4
6 changed files with 279 additions and 113 deletions

View file

@ -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);
}

View file

@ -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);

View file

@ -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

View file

@ -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;
}
}

View file

@ -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.

View file

@ -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
RootedLinearString replacement(cx, args[3].toString()->ensureLinear(cx));
if (!replacement)
return false;
RootedString replacement(cx, args[4].toString());
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