diff options
author | Martok <martok@martoks-place.de> | 2022-12-21 18:53:27 +0100 |
---|---|---|
committer | Martok <martok@martoks-place.de> | 2022-12-21 18:53:27 +0100 |
commit | 58f3b2c5810e8644a16119c58f874d1a07e8e004 (patch) | |
tree | 080f25b40b1eee6674eb5d6eeccfbfbea0b50aca /js | |
parent | b356d64f4e69af8cf1f2eaf87d1f328bdd24127e (diff) | |
download | uxp-58f3b2c5810e8644a16119c58f874d1a07e8e004.tar.gz |
Issue #1285 - Implement named capturing groups for replacing
Diffstat (limited to 'js')
-rw-r--r-- | js/src/builtin/RegExp.cpp | 229 | ||||
-rw-r--r-- | js/src/builtin/RegExp.h | 6 | ||||
-rw-r--r-- | js/src/builtin/RegExp.js | 80 | ||||
-rw-r--r-- | js/src/builtin/RegExpGlobalReplaceOpt.h.js | 23 | ||||
-rw-r--r-- | js/src/builtin/RegExpLocalReplaceOpt.h.js | 18 | ||||
-rw-r--r-- | js/src/vm/SelfHosting.cpp | 36 |
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, ¤tNamedCapture)) { 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, ¤tNamedCapture)) { 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 |