mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-10-25 00:27:29 +09:00
Merge remote-tracking branch 'origin/tracking' into custom
This commit is contained in:
commit
5e9f3d0667
30 changed files with 4166 additions and 1417 deletions
|
|
@ -21,6 +21,7 @@
|
|||
|
||||
#include "vm/NativeObject-inl.h"
|
||||
|
||||
|
||||
using namespace js;
|
||||
using namespace js::unicode;
|
||||
|
||||
|
|
@ -28,12 +29,15 @@ using mozilla::ArrayLength;
|
|||
using mozilla::CheckedInt;
|
||||
using mozilla::Maybe;
|
||||
|
||||
using CapturesVector = GCVector<Value, 4>;
|
||||
|
||||
/*
|
||||
* ES 2017 draft rev 6a13789aa9e7c6de4e96b7d3e24d9e6eba6584ad 21.2.5.2.2
|
||||
* steps 3, 16-25.
|
||||
* ES 2021 draft 21.2.5.2.2: Steps 16-28
|
||||
* https://tc39.es/ecma262/#sec-regexpbuiltinexec
|
||||
*/
|
||||
bool
|
||||
js::CreateRegExpMatchResult(JSContext* cx, HandleString input, const MatchPairs& matches,
|
||||
js::CreateRegExpMatchResult(JSContext* cx, RegExpShared& re,
|
||||
HandleString input, const MatchPairs& matches,
|
||||
MutableHandleValue rval)
|
||||
{
|
||||
MOZ_ASSERT(input);
|
||||
|
|
@ -46,6 +50,7 @@ js::CreateRegExpMatchResult(JSContext* cx, HandleString input, const MatchPairs&
|
|||
* 1..pairCount-1: paren matches
|
||||
* input: input string
|
||||
* index: start index for the match
|
||||
* groups: named capture groups for the match
|
||||
*/
|
||||
|
||||
/* Get the templateObject that defines the shape and type of the output object */
|
||||
|
|
@ -53,15 +58,16 @@ js::CreateRegExpMatchResult(JSContext* cx, HandleString input, const MatchPairs&
|
|||
if (!templateObject)
|
||||
return false;
|
||||
|
||||
// Step 16
|
||||
size_t numPairs = matches.length();
|
||||
MOZ_ASSERT(numPairs > 0);
|
||||
|
||||
/* Step 17. */
|
||||
/* Step 18-19. */
|
||||
RootedArrayObject arr(cx, NewDenseFullyAllocatedArrayWithTemplate(cx, numPairs, templateObject));
|
||||
if (!arr)
|
||||
return false;
|
||||
|
||||
/* Steps 22-24.
|
||||
/* Steps 22-23 and 27 a-e
|
||||
* Store a Value for each pair. */
|
||||
for (size_t i = 0; i < numPairs; i++) {
|
||||
const MatchPair& pair = matches[i];
|
||||
|
|
@ -79,6 +85,40 @@ js::CreateRegExpMatchResult(JSContext* cx, HandleString input, const MatchPairs&
|
|||
}
|
||||
}
|
||||
|
||||
// Step 24 (reordered)
|
||||
RootedPlainObject groups(cx);
|
||||
if (re.numNamedCaptures() > 0) {
|
||||
// construct a new object from the template saved on RegExpShared
|
||||
RootedPlainObject groupsTemplate(cx, re.getGroupsTemplate());
|
||||
groups = NewObjectWithGivenProto<PlainObject>(cx, nullptr);
|
||||
groups->setGroup(groupsTemplate->group());
|
||||
|
||||
// Step 27 f.
|
||||
// The groups template object stores the names of the named captures in the
|
||||
// the order in which they are defined.
|
||||
// Grab the index into the match vector from the template object and define the
|
||||
// corresponding property on the result
|
||||
AutoIdVector keys(cx);
|
||||
if (!GetPropertyKeys(cx, groupsTemplate, 0, &keys)) {
|
||||
return false;
|
||||
}
|
||||
MOZ_ASSERT(keys.length() == re.numNamedCaptures());
|
||||
RootedId key(cx);
|
||||
RootedValue ival(cx);
|
||||
RootedValue val(cx);
|
||||
for (size_t i = 0; i < keys.length(); i++) {
|
||||
key = keys[i];
|
||||
// fetch the group's match index...
|
||||
if (!NativeGetProperty(cx, groupsTemplate, key, &ival))
|
||||
return false;
|
||||
// ... and set it on groups
|
||||
val = arr->getDenseElement(ival.toInt32());
|
||||
if (!NativeDefineProperty(cx, groups, key, val, nullptr, nullptr, JSPROP_ENUMERATE)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Step 20 (reordered).
|
||||
* Set the |index| property. (TemplateObject positions it in slot 0) */
|
||||
arr->setSlot(0, Int32Value(matches[0].start));
|
||||
|
|
@ -87,6 +127,10 @@ js::CreateRegExpMatchResult(JSContext* cx, HandleString input, const MatchPairs&
|
|||
* Set the |input| property. (TemplateObject positions it in slot 1) */
|
||||
arr->setSlot(1, StringValue(input));
|
||||
|
||||
// Steps 25-26 (reordered)
|
||||
// Set the |groups| property.
|
||||
arr->setSlot(2, groups ? ObjectValue(*groups) : UndefinedValue());
|
||||
|
||||
#ifdef DEBUG
|
||||
RootedValue test(cx);
|
||||
RootedId id(cx, NameToId(cx->names().index));
|
||||
|
|
@ -168,7 +212,7 @@ js::ExecuteRegExpLegacy(JSContext* cx, RegExpStatics* res, Handle<RegExpObject*>
|
|||
return true;
|
||||
}
|
||||
|
||||
return CreateRegExpMatchResult(cx, input, matches, rval);
|
||||
return CreateRegExpMatchResult(cx, *shared, input, matches, rval);
|
||||
}
|
||||
|
||||
static bool
|
||||
|
|
@ -1025,7 +1069,11 @@ RegExpMatcherImpl(JSContext* cx, HandleObject regexp, HandleString string,
|
|||
}
|
||||
|
||||
/* Steps 16-25 */
|
||||
return CreateRegExpMatchResult(cx, string, matches, rval);
|
||||
Rooted<RegExpObject*> reobj(cx, ®exp->as<RegExpObject>());
|
||||
RegExpGuard shared(cx);
|
||||
if (!RegExpObject::getShared(cx, reobj, &shared))
|
||||
return false;
|
||||
return CreateRegExpMatchResult(cx, *shared, string, matches, rval);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -1067,8 +1115,13 @@ js::RegExpMatcherRaw(JSContext* cx, HandleObject regexp, HandleString input,
|
|||
|
||||
// The MatchPairs will always be passed in, but RegExp execution was
|
||||
// successful only if the pairs have actually been filled in.
|
||||
if (maybeMatches && maybeMatches->pairsRaw()[0] >= 0)
|
||||
return CreateRegExpMatchResult(cx, input, *maybeMatches, output);
|
||||
if (maybeMatches && maybeMatches->pairsRaw()[0] >= 0) {
|
||||
Rooted<RegExpObject*> reobj(cx, ®exp->as<RegExpObject>());
|
||||
RegExpGuard shared(cx);
|
||||
if (!RegExpObject::getShared(cx, reobj, &shared))
|
||||
return false;
|
||||
return CreateRegExpMatchResult(cx, *shared, input, *maybeMatches, output);
|
||||
}
|
||||
return RegExpMatcherImpl(cx, regexp, input, lastIndex,
|
||||
UpdateRegExpStatics, output);
|
||||
}
|
||||
|
|
@ -1265,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<GCVector<Value>> 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 == '$');
|
||||
|
||||
|
|
@ -1310,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:
|
||||
|
|
@ -1340,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<GCVector<Value>> 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();
|
||||
|
||||
|
|
@ -1350,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;
|
||||
|
|
@ -1379,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<GCVector<Value>> 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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -1397,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<GCVector<Value>> captures,
|
||||
size_t position, size_t tailPos, Handle<CapturesVector> captures, Handle<CapturesVector> namedCaptures,
|
||||
HandleLinearString replacement, size_t firstDollarIndex, StringBuffer &sb)
|
||||
{
|
||||
JS::AutoCheckCannotGC nogc;
|
||||
|
|
@ -1407,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;
|
||||
|
|
@ -1415,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;
|
||||
|
|
@ -1430,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<GCVector<Value>> captures)
|
||||
NeedTwoBytes(HandleLinearString string, HandleLinearString replacement, HandleLinearString matched,
|
||||
Handle<CapturesVector> captures, Handle<CapturesVector> namedCaptures)
|
||||
{
|
||||
if (string->hasTwoByteChars())
|
||||
return true;
|
||||
|
|
@ -1449,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();
|
||||
|
||||
|
|
@ -1470,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;
|
||||
|
||||
Rooted<GCVector<Value>> captures(cx, GCVector<Value>(cx));
|
||||
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.
|
||||
|
|
@ -1511,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;
|
||||
}
|
||||
|
|
@ -1527,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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -36,7 +36,8 @@ ExecuteRegExpLegacy(JSContext* cx, RegExpStatics* res, Handle<RegExpObject*> reo
|
|||
|
||||
/* Translation from MatchPairs to a JS array in regexp_exec()'s output format. */
|
||||
MOZ_MUST_USE bool
|
||||
CreateRegExpMatchResult(JSContext* cx, HandleString input, const MatchPairs& matches,
|
||||
CreateRegExpMatchResult(JSContext* cx, RegExpShared& re,
|
||||
HandleString input, const MatchPairs& matches,
|
||||
MutableHandleValue rval);
|
||||
|
||||
extern MOZ_MUST_USE bool
|
||||
|
|
@ -121,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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -3861,10 +3861,10 @@ ConvertRegExpTreeToObject(JSContext* cx, irregexp::RegExpTree* tree)
|
|||
return nullptr;
|
||||
return obj;
|
||||
}
|
||||
if (tree->IsLookahead()) {
|
||||
if (!StringProp(cx, obj, "type", "Lookahead"))
|
||||
if (tree->IsLookaround()) {
|
||||
if (!StringProp(cx, obj, "type", "Lookaround"))
|
||||
return nullptr;
|
||||
irregexp::RegExpLookahead* t = tree->AsLookahead();
|
||||
irregexp::RegExpLookaround* t = tree->AsLookaround();
|
||||
if (!BooleanProp(cx, obj, "is_positive", t->is_positive()))
|
||||
return nullptr;
|
||||
if (!TreeProp(cx, obj, "body", t->body()))
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@
|
|||
|
||||
#include "frontend/BytecodeCompiler.h"
|
||||
#include "frontend/ReservedWords.h"
|
||||
#include "irregexp/FeatureFlags.h"
|
||||
#include "js/CharacterEncoding.h"
|
||||
#include "js/UniquePtr.h"
|
||||
#include "vm/HelperThreads.h"
|
||||
|
|
@ -1942,6 +1943,8 @@ TokenStream::getTokenInternal(TokenKind* ttp, Modifier modifier)
|
|||
reflags = RegExpFlag(reflags | UnicodeFlag);
|
||||
else if (c == 's' && !(reflags & DotAllFlag))
|
||||
reflags = RegExpFlag(reflags | DotAllFlag);
|
||||
else if (c == 'v' && irregexp::kParseFlagUnicodeSetsAsUnicode && !(reflags & UnicodeFlag))
|
||||
reflags = RegExpFlag(reflags | UnicodeFlag);
|
||||
else
|
||||
break;
|
||||
getChar();
|
||||
|
|
|
|||
20
js/src/irregexp/FeatureFlags.h
Normal file
20
js/src/irregexp/FeatureFlags.h
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef irregexp_FeatureFlags_h
|
||||
#define irregexp_FeatureFlags_h
|
||||
|
||||
namespace js {
|
||||
|
||||
namespace irregexp {
|
||||
|
||||
// Feature flag to treat /../v as /../u (https://v8.dev/features/regexp-v-flag)
|
||||
// We don't support Set Notation or the changed Case Insenstive handling
|
||||
// but we have Property Sequences and want them in unit test runs.
|
||||
static const bool kParseFlagUnicodeSetsAsUnicode = false;
|
||||
|
||||
} } // namespace js::irregexp
|
||||
|
||||
#endif // irregexp_FeatureFlags_h
|
||||
103
js/src/irregexp/InfallibleVector.h
Normal file
103
js/src/irregexp/InfallibleVector.h
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
// Copyright 2012 the V8 project authors. All rights reserved.
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following
|
||||
// disclaimer in the documentation and/or other materials provided
|
||||
// with the distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived
|
||||
// from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#ifndef V8_INFALLIBLEVECTOR_H_
|
||||
#define V8_INFALLIBLEVECTOR_H_
|
||||
|
||||
namespace js {
|
||||
namespace irregexp {
|
||||
|
||||
// InfallibleVector is like Vector, but all its methods are infallible (they
|
||||
// crash on OOM). We use this class instead of Vector to avoid a ton of
|
||||
// MOZ_MUST_USE warnings in irregexp code (imported from V8).
|
||||
template<typename T, size_t N>
|
||||
class InfallibleVector
|
||||
{
|
||||
Vector<T, N, LifoAllocPolicy<Infallible>> vector_;
|
||||
|
||||
InfallibleVector(const InfallibleVector&) = delete;
|
||||
void operator=(const InfallibleVector&) = delete;
|
||||
|
||||
public:
|
||||
explicit InfallibleVector(const LifoAllocPolicy<Infallible>& alloc) : vector_(alloc) {}
|
||||
|
||||
void append(const T& t) { MOZ_ALWAYS_TRUE(vector_.append(t)); }
|
||||
void append(const T* begin, size_t length) { MOZ_ALWAYS_TRUE(vector_.append(begin, length)); }
|
||||
|
||||
// Move a number of elements in a zonelist to another position
|
||||
// in the same list. Handles overlapping source and target areas.
|
||||
void moveReplace(int from, int to, int count)
|
||||
{
|
||||
T* array = begin();
|
||||
if (from < to) {
|
||||
for (int i = count - 1; i >= 0; i--)
|
||||
array[to + i] = array[from + i];
|
||||
} else {
|
||||
for (int i = 0; i < count; i++)
|
||||
array[to + i] = array[from + i];
|
||||
}
|
||||
}
|
||||
|
||||
void clear() { vector_.clear(); }
|
||||
void popBack() { vector_.popBack(); }
|
||||
void reserve(size_t n) { MOZ_ALWAYS_TRUE(vector_.reserve(n)); }
|
||||
|
||||
|
||||
size_t length() const { return vector_.length(); }
|
||||
T popCopy() { return vector_.popCopy(); }
|
||||
|
||||
T* begin() { return vector_.begin(); }
|
||||
const T* begin() const { return vector_.begin(); }
|
||||
|
||||
T* end() { return vector_.end(); }
|
||||
const T* end() const { return vector_.end(); }
|
||||
|
||||
T& operator[](size_t index) { return vector_[index]; }
|
||||
const T& operator[](size_t index) const { return vector_[index]; }
|
||||
|
||||
InfallibleVector& operator=(InfallibleVector&& rhs) { vector_ = Move(rhs.vector_); return *this; }
|
||||
|
||||
bool equals(const InfallibleVector& other) const {
|
||||
if (length() != other.length()) {
|
||||
return false;
|
||||
}
|
||||
return 0 == memcmp(begin(), other.begin(), length() * sizeof(T));
|
||||
}
|
||||
inline bool operator==(const InfallibleVector& rhs) const {
|
||||
return equals(rhs);
|
||||
}
|
||||
};
|
||||
|
||||
typedef InfallibleVector<char16_t, 10> CharacterVector;
|
||||
typedef InfallibleVector<CharacterVector*, 1> CharacterVectorVector;
|
||||
typedef InfallibleVector<int32_t, 10> IntegerVector;
|
||||
|
||||
} } // namespace js::irregexp
|
||||
|
||||
#endif // V8_INFALLIBLEVECTOR_H_
|
||||
|
|
@ -71,13 +71,13 @@ NativeRegExpMacroAssembler::NativeRegExpMacroAssembler(LifoAlloc* alloc, RegExpS
|
|||
// Find physical registers for each compiler register.
|
||||
AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
|
||||
|
||||
temp0 = regs.takeAny();
|
||||
temp1 = regs.takeAny();
|
||||
temp2 = regs.takeAny();
|
||||
input_end_pointer = regs.takeAny();
|
||||
current_character = regs.takeAny();
|
||||
current_position = regs.takeAny();
|
||||
backtrack_stack_pointer = regs.takeAny();
|
||||
temp0 = regs.takeAny();
|
||||
temp1 = regs.takeAny();
|
||||
temp2 = regs.takeAny();
|
||||
|
||||
JitSpew(JitSpew_Codegen,
|
||||
"Starting RegExp (input_end_pointer %s) (current_character %s)"
|
||||
|
|
@ -548,39 +548,28 @@ NativeRegExpMacroAssembler::Bind(Label* label)
|
|||
}
|
||||
|
||||
void
|
||||
NativeRegExpMacroAssembler::CheckAtStart(Label* on_at_start)
|
||||
NativeRegExpMacroAssembler::CheckAtStartImpl(int cp_offset, Label* on_cond,
|
||||
Assembler::Condition cond) {
|
||||
masm.computeEffectiveAddress(BaseIndex(input_end_pointer, current_position, TimesOne, cp_offset * char_size()), temp0);
|
||||
|
||||
Address inputStart(masm.getStackPointer(), offsetof(FrameData, inputStart));
|
||||
masm.branchPtr(cond, inputStart, temp0, BranchOrBacktrack(on_cond));
|
||||
}
|
||||
|
||||
void
|
||||
NativeRegExpMacroAssembler::CheckAtStart(int cp_offset, Label* on_at_start)
|
||||
{
|
||||
JitSpew(SPEW_PREFIX "CheckAtStart");
|
||||
|
||||
Label not_at_start;
|
||||
|
||||
// Did we start the match at the start of the string at all?
|
||||
Address startIndex(masm.getStackPointer(), offsetof(FrameData, startIndex));
|
||||
masm.branchPtr(Assembler::NotEqual, startIndex, ImmWord(0), ¬_at_start);
|
||||
|
||||
// If we did, are we still at the start of the input?
|
||||
masm.computeEffectiveAddress(BaseIndex(input_end_pointer, current_position, TimesOne), temp0);
|
||||
|
||||
Address inputStart(masm.getStackPointer(), offsetof(FrameData, inputStart));
|
||||
masm.branchPtr(Assembler::Equal, inputStart, temp0, BranchOrBacktrack(on_at_start));
|
||||
|
||||
masm.bind(¬_at_start);
|
||||
CheckAtStartImpl(cp_offset, on_at_start, Assembler::Equal);
|
||||
}
|
||||
|
||||
void
|
||||
NativeRegExpMacroAssembler::CheckNotAtStart(Label* on_not_at_start)
|
||||
NativeRegExpMacroAssembler::CheckNotAtStart(int cp_offset, Label* on_not_at_start)
|
||||
{
|
||||
JitSpew(SPEW_PREFIX "CheckNotAtStart");
|
||||
|
||||
// Did we start the match at the start of the string at all?
|
||||
Address startIndex(masm.getStackPointer(), offsetof(FrameData, startIndex));
|
||||
masm.branchPtr(Assembler::NotEqual, startIndex, ImmWord(0), BranchOrBacktrack(on_not_at_start));
|
||||
|
||||
// If we did, are we still at the start of the input?
|
||||
masm.computeEffectiveAddress(BaseIndex(input_end_pointer, current_position, TimesOne), temp0);
|
||||
|
||||
Address inputStart(masm.getStackPointer(), offsetof(FrameData, inputStart));
|
||||
masm.branchPtr(Assembler::NotEqual, inputStart, temp0, BranchOrBacktrack(on_not_at_start));
|
||||
CheckAtStartImpl(cp_offset, on_not_at_start, Assembler::NotEqual);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -659,175 +648,59 @@ NativeRegExpMacroAssembler::CheckGreedyLoop(Label* on_tos_equals_current_positio
|
|||
}
|
||||
|
||||
void
|
||||
NativeRegExpMacroAssembler::CheckNotBackReference(int start_reg, Label* on_no_match)
|
||||
NativeRegExpMacroAssembler::CheckNotBackReferenceImpl(int start_reg, bool read_backward,
|
||||
Label* on_no_match,
|
||||
bool unicode, bool ignore_case)
|
||||
{
|
||||
JitSpew(SPEW_PREFIX "CheckNotBackReference(%d)", start_reg);
|
||||
|
||||
Label fallthrough;
|
||||
Label success;
|
||||
Label fail;
|
||||
|
||||
// Find length of back-referenced capture.
|
||||
masm.loadPtr(register_location(start_reg), current_character);
|
||||
masm.loadPtr(register_location(start_reg + 1), temp0);
|
||||
masm.subPtr(current_character, temp0); // Length to check.
|
||||
|
||||
// Fail on partial or illegal capture (start of capture after end of capture).
|
||||
masm.branchPtr(Assembler::LessThan, temp0, ImmWord(0), BranchOrBacktrack(on_no_match));
|
||||
|
||||
// Succeed on empty capture (including no capture).
|
||||
masm.branchPtr(Assembler::Equal, temp0, ImmWord(0), &fallthrough);
|
||||
|
||||
// Check that there are sufficient characters left in the input.
|
||||
masm.movePtr(current_position, temp1);
|
||||
masm.addPtr(temp0, temp1);
|
||||
masm.branchPtr(Assembler::GreaterThan, temp1, ImmWord(0), BranchOrBacktrack(on_no_match));
|
||||
|
||||
// Save register to make it available below.
|
||||
masm.push(backtrack_stack_pointer);
|
||||
|
||||
// Compute pointers to match string and capture string
|
||||
masm.computeEffectiveAddress(BaseIndex(input_end_pointer, current_position, TimesOne), temp1); // Start of match.
|
||||
masm.addPtr(input_end_pointer, current_character); // Start of capture.
|
||||
masm.computeEffectiveAddress(BaseIndex(temp0, temp1, TimesOne), backtrack_stack_pointer); // End of match.
|
||||
|
||||
Label loop;
|
||||
masm.bind(&loop);
|
||||
if (mode_ == ASCII) {
|
||||
masm.load8ZeroExtend(Address(current_character, 0), temp0);
|
||||
masm.load8ZeroExtend(Address(temp1, 0), temp2);
|
||||
} else {
|
||||
MOZ_ASSERT(mode_ == CHAR16);
|
||||
masm.load16ZeroExtend(Address(current_character, 0), temp0);
|
||||
masm.load16ZeroExtend(Address(temp1, 0), temp2);
|
||||
}
|
||||
masm.branch32(Assembler::NotEqual, temp0, temp2, &fail);
|
||||
|
||||
// Increment pointers into capture and match string.
|
||||
masm.addPtr(Imm32(char_size()), current_character);
|
||||
masm.addPtr(Imm32(char_size()), temp1);
|
||||
|
||||
// Check if we have reached end of match area.
|
||||
masm.branchPtr(Assembler::Below, temp1, backtrack_stack_pointer, &loop);
|
||||
masm.jump(&success);
|
||||
|
||||
masm.bind(&fail);
|
||||
|
||||
// Restore backtrack stack pointer.
|
||||
masm.pop(backtrack_stack_pointer);
|
||||
JumpOrBacktrack(on_no_match);
|
||||
|
||||
masm.bind(&success);
|
||||
|
||||
// Move current character position to position after match.
|
||||
masm.movePtr(backtrack_stack_pointer, current_position);
|
||||
masm.subPtr(input_end_pointer, current_position);
|
||||
|
||||
// Restore backtrack stack pointer.
|
||||
masm.pop(backtrack_stack_pointer);
|
||||
|
||||
masm.bind(&fallthrough);
|
||||
}
|
||||
|
||||
void
|
||||
NativeRegExpMacroAssembler::CheckNotBackReferenceIgnoreCase(int start_reg, Label* on_no_match,
|
||||
bool unicode)
|
||||
{
|
||||
JitSpew(SPEW_PREFIX "CheckNotBackReferenceIgnoreCase(%d, %d)", start_reg, unicode);
|
||||
|
||||
Label fallthrough;
|
||||
|
||||
// Captures are stored as a sequential pair of registers.
|
||||
// Find the length of the back-referenced capture and load the
|
||||
// capture's start index into current_character_
|
||||
masm.loadPtr(register_location(start_reg), current_character); // Index of start of capture
|
||||
masm.loadPtr(register_location(start_reg + 1), temp1); // Index of end of capture
|
||||
masm.subPtr(current_character, temp1); // Length of capture.
|
||||
|
||||
// The length of a capture should not be negative. This can only happen
|
||||
// if the end of the capture is unrecorded, or at a point earlier than
|
||||
// the start of the capture.
|
||||
masm.branchPtr(Assembler::LessThan, temp1, ImmWord(0), BranchOrBacktrack(on_no_match));
|
||||
masm.loadPtr(register_location(start_reg + 1), temp0); // Index of end of capture
|
||||
masm.subPtr(current_character, temp0); // Length of capture.
|
||||
|
||||
// If length is zero, either the capture is empty or it is completely
|
||||
// uncaptured. In either case succeed immediately.
|
||||
masm.branchPtr(Assembler::Equal, temp1, ImmWord(0), &fallthrough);
|
||||
masm.branchPtr(Assembler::Equal, temp0, ImmWord(0), &fallthrough);
|
||||
|
||||
// Check that there are sufficient characters left in the input.
|
||||
masm.movePtr(current_position, temp0);
|
||||
masm.addPtr(temp1, temp0);
|
||||
masm.branchPtr(Assembler::GreaterThan, temp0, ImmWord(0), BranchOrBacktrack(on_no_match));
|
||||
|
||||
if (mode_ == ASCII) {
|
||||
Label success, fail;
|
||||
|
||||
// Save register contents to make the registers available below. After
|
||||
// this, the temp0, temp2, and current_position registers are available.
|
||||
masm.push(current_position);
|
||||
|
||||
masm.addPtr(input_end_pointer, current_character); // Start of capture.
|
||||
masm.addPtr(input_end_pointer, current_position); // Start of text to match against capture.
|
||||
masm.addPtr(current_position, temp1); // End of text to match against capture.
|
||||
|
||||
Label loop, loop_increment;
|
||||
masm.bind(&loop);
|
||||
masm.load8ZeroExtend(Address(current_position, 0), temp0);
|
||||
masm.load8ZeroExtend(Address(current_character, 0), temp2);
|
||||
masm.branch32(Assembler::Equal, temp0, temp2, &loop_increment);
|
||||
|
||||
// Mismatch, try case-insensitive match (converting letters to lower-case).
|
||||
masm.or32(Imm32(0x20), temp0); // Convert match character to lower-case.
|
||||
|
||||
// Is temp0 a lowercase letter?
|
||||
Label convert_capture;
|
||||
masm.computeEffectiveAddress(Address(temp0, -'a'), temp2);
|
||||
masm.branch32(Assembler::BelowOrEqual, temp2, Imm32(static_cast<int32_t>('z' - 'a')),
|
||||
&convert_capture);
|
||||
|
||||
// Latin-1: Check for values in range [224,254] but not 247.
|
||||
masm.sub32(Imm32(224 - 'a'), temp2);
|
||||
masm.branch32(Assembler::Above, temp2, Imm32(254 - 224), &fail);
|
||||
|
||||
// Check for 247.
|
||||
masm.branch32(Assembler::Equal, temp2, Imm32(247 - 224), &fail);
|
||||
|
||||
masm.bind(&convert_capture);
|
||||
|
||||
// Also convert capture character.
|
||||
masm.load8ZeroExtend(Address(current_character, 0), temp2);
|
||||
masm.or32(Imm32(0x20), temp2);
|
||||
|
||||
masm.branch32(Assembler::NotEqual, temp0, temp2, &fail);
|
||||
|
||||
masm.bind(&loop_increment);
|
||||
|
||||
// Increment pointers into match and capture strings.
|
||||
masm.addPtr(Imm32(1), current_character);
|
||||
masm.addPtr(Imm32(1), current_position);
|
||||
|
||||
// Compare to end of match, and loop if not done.
|
||||
masm.branchPtr(Assembler::Below, current_position, temp1, &loop);
|
||||
masm.jump(&success);
|
||||
|
||||
masm.bind(&fail);
|
||||
|
||||
// Restore original values before failing.
|
||||
masm.pop(current_position);
|
||||
JumpOrBacktrack(on_no_match);
|
||||
|
||||
masm.bind(&success);
|
||||
|
||||
// Drop original character position value.
|
||||
masm.addToStackPtr(Imm32(sizeof(uintptr_t)));
|
||||
|
||||
// Compute new value of character position after the matched part.
|
||||
masm.subPtr(input_end_pointer, current_position);
|
||||
if (read_backward) {
|
||||
// If start + len > current, there isn't enough room for a
|
||||
// lookbehind backreference.
|
||||
Address inputStart(masm.getStackPointer(), offsetof(FrameData, inputStart));
|
||||
masm.loadPtr(inputStart, temp1);
|
||||
masm.subPtr(input_end_pointer, temp1);
|
||||
masm.addPtr(temp0, temp1);
|
||||
masm.branchPtr(Assembler::GreaterThan, temp1, current_position,
|
||||
BranchOrBacktrack(on_no_match));
|
||||
} else {
|
||||
MOZ_ASSERT(mode_ == CHAR16);
|
||||
// current_position is the negative offset from the end.
|
||||
// If current + len > 0, there isn't enough room for a backreference.
|
||||
masm.movePtr(current_position, temp1);
|
||||
masm.addPtr(temp0, temp1);
|
||||
masm.branchPtr(Assembler::GreaterThan, temp1, ImmWord(0),
|
||||
BranchOrBacktrack(on_no_match));
|
||||
}
|
||||
|
||||
// Note: temp1 needs to be saved/restored if it is volatile, as it is used after the call.
|
||||
if (mode_ == CHAR16 && ignore_case) {
|
||||
// We call a helper function for case-insensitive non-latin1 strings.
|
||||
// Save volatile regs. temp1, temp2, and current_character
|
||||
// don't need to be saved. current_position needs to be saved
|
||||
// even if it's non-volatile, because we modify it to use as an argument.
|
||||
LiveGeneralRegisterSet volatileRegs(GeneralRegisterSet::Volatile());
|
||||
volatileRegs.takeUnchecked(temp0);
|
||||
volatileRegs.addUnchecked(current_position);
|
||||
volatileRegs.takeUnchecked(temp1);
|
||||
volatileRegs.takeUnchecked(temp2);
|
||||
volatileRegs.takeUnchecked(current_character);
|
||||
masm.PushRegsInMask(volatileRegs);
|
||||
|
||||
// Parameters are
|
||||
// Address byte_offset1 - Address captured substring's start.
|
||||
// Address byte_offset2 - Address of current character position.
|
||||
// size_t byte_length - length of capture in bytes(!)
|
||||
|
||||
// Set byte_offset1.
|
||||
// Start of capture, where current_character already holds string-end negative offset.
|
||||
masm.addPtr(input_end_pointer, current_character);
|
||||
|
|
@ -836,36 +709,145 @@ NativeRegExpMacroAssembler::CheckNotBackReferenceIgnoreCase(int start_reg, Label
|
|||
// Found by adding negative string-end offset of current position
|
||||
// to end of string.
|
||||
masm.addPtr(input_end_pointer, current_position);
|
||||
if (read_backward) {
|
||||
// Offset by length when matching backwards.
|
||||
masm.subPtr(temp1, current_position);
|
||||
}
|
||||
|
||||
// Parameters are
|
||||
// Address byte_offset1 - Address captured substring's start.
|
||||
// Address byte_offset2 - Address of current character position.
|
||||
// size_t byte_length - length of capture in bytes(!)
|
||||
masm.setupUnalignedABICall(temp0);
|
||||
masm.setupUnalignedABICall(temp1);
|
||||
masm.passABIArg(current_character);
|
||||
masm.passABIArg(current_position);
|
||||
masm.passABIArg(temp1);
|
||||
if (!unicode) {
|
||||
int (*fun)(const char16_t*, const char16_t*, size_t) = CaseInsensitiveCompareStrings;
|
||||
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, fun));
|
||||
} else {
|
||||
masm.passABIArg(temp0);
|
||||
if (unicode) {
|
||||
int (*fun)(const char16_t*, const char16_t*, size_t) = CaseInsensitiveCompareUCStrings;
|
||||
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, fun));
|
||||
} else {
|
||||
int (*fun)(const char16_t*, const char16_t*, size_t) = CaseInsensitiveCompareStrings;
|
||||
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, fun));
|
||||
}
|
||||
masm.storeCallInt32Result(temp0);
|
||||
|
||||
masm.storeCallInt32Result(temp1);
|
||||
masm.PopRegsInMask(volatileRegs);
|
||||
|
||||
// Check if function returned non-zero for success or zero for failure.
|
||||
masm.branchTest32(Assembler::Zero, temp0, temp0, BranchOrBacktrack(on_no_match));
|
||||
masm.branchTest32(Assembler::Zero, temp1, temp1, BranchOrBacktrack(on_no_match));
|
||||
|
||||
// On success, increment position by length of capture.
|
||||
masm.addPtr(temp1, current_position);
|
||||
// On success, advance position by length of capture
|
||||
if (read_backward) {
|
||||
masm.subPtr(temp0, current_position);
|
||||
} else {
|
||||
masm.addPtr(temp0, current_position);
|
||||
}
|
||||
} else {
|
||||
MOZ_ASSERT(mode_ == ASCII || !ignore_case);
|
||||
|
||||
// Save register contents to make the registers available below. After
|
||||
// this, the temp1, temp2, and current_position registers are available.
|
||||
masm.push(current_position);
|
||||
|
||||
// Make offset values into pointers
|
||||
masm.addPtr(input_end_pointer, current_character); // Start of capture.
|
||||
masm.addPtr(input_end_pointer, current_position); // Start of text to match against capture.
|
||||
|
||||
if (read_backward) {
|
||||
// Offset by length when matching backwards.
|
||||
masm.subPtr(temp0, current_position);
|
||||
}
|
||||
|
||||
// End of text to match against capture (temp0 is pointer now)
|
||||
masm.addPtr(current_position, temp0);
|
||||
|
||||
Label success, fail, loop;
|
||||
masm.bind(&loop);
|
||||
|
||||
// Load next character from each string.
|
||||
if (mode_ == ASCII) {
|
||||
masm.load8ZeroExtend(Address(current_character, 0), temp1);
|
||||
masm.load8ZeroExtend(Address(current_position, 0), temp2);
|
||||
} else {
|
||||
masm.load16ZeroExtend(Address(current_character, 0), temp1);
|
||||
masm.load16ZeroExtend(Address(current_position, 0), temp2);
|
||||
}
|
||||
|
||||
if (ignore_case) {
|
||||
MOZ_ASSERT(mode_ == ASCII);
|
||||
Label loop_increment, convert_match;
|
||||
|
||||
// Try exact match.
|
||||
masm.branch32(Assembler::Equal, temp1, temp2, &loop_increment);
|
||||
|
||||
// Mismatch, try case-insensitive match (converting letters to lower-case).
|
||||
masm.or32(Imm32(0x20), temp1); // Convert match character to lower-case.
|
||||
|
||||
// Is temp1 a lowercase letter [a,z]?
|
||||
masm.computeEffectiveAddress(Address(temp1, -'a'), temp2);
|
||||
masm.branch32(Assembler::BelowOrEqual, temp2, Imm32(static_cast<int32_t>('z' - 'a')),
|
||||
&convert_match);
|
||||
// Latin-1: Check for values in range [224,254] but not 247 (U+00F7 DIVISION SIGN).
|
||||
masm.sub32(Imm32(224 - 'a'), temp2);
|
||||
masm.branch32(Assembler::Above, temp2, Imm32(254 - 224), &fail);
|
||||
// Check for 247.
|
||||
masm.branch32(Assembler::Equal, temp2, Imm32(247 - 224), &fail);
|
||||
|
||||
// Capture character is lower case. Convert match character to lower case and compare
|
||||
masm.bind(&convert_match);
|
||||
// Reload latin1 character since temp2 was clobbered above
|
||||
masm.load8ZeroExtend(Address(current_position, 0), temp2);
|
||||
masm.or32(Imm32(0x20), temp2);
|
||||
masm.branch32(Assembler::NotEqual, temp1, temp2, &fail);
|
||||
|
||||
masm.bind(&loop_increment);
|
||||
} else {
|
||||
// Fail if characters do not match.
|
||||
masm.branch32(Assembler::NotEqual, temp1, temp2, &fail);
|
||||
}
|
||||
|
||||
// Increment pointers into match and capture strings.
|
||||
masm.addPtr(Imm32(char_size()), current_character);
|
||||
masm.addPtr(Imm32(char_size()), current_position);
|
||||
|
||||
// Loop if we have not reached the end of the match string.
|
||||
masm.branchPtr(Assembler::Below, current_position, temp0, &loop);
|
||||
masm.jump(&success);
|
||||
|
||||
// Restore original values before failing.
|
||||
masm.bind(&fail);
|
||||
masm.pop(current_position);
|
||||
JumpOrBacktrack(on_no_match);
|
||||
|
||||
masm.bind(&success);
|
||||
// Drop original character position value.
|
||||
masm.pop(temp0);
|
||||
|
||||
// current_position is a pointer (now at the end of the consumed characters). Convert it back to an offset.
|
||||
masm.subPtr(input_end_pointer, current_position);
|
||||
|
||||
if (read_backward) {
|
||||
// Subtract match length if we matched backward
|
||||
masm.addPtr(register_location(start_reg), current_position);
|
||||
masm.subPtr(register_location(start_reg + 1), current_position);
|
||||
}
|
||||
}
|
||||
|
||||
// Fallthrough if capture length was zero
|
||||
masm.bind(&fallthrough);
|
||||
}
|
||||
|
||||
void
|
||||
NativeRegExpMacroAssembler::CheckNotBackReference(int start_reg, bool read_backward, Label* on_no_match)
|
||||
{
|
||||
JitSpew(SPEW_PREFIX "CheckNotBackReference(%d)", start_reg);
|
||||
|
||||
CheckNotBackReferenceImpl(start_reg, read_backward, on_no_match, /*unicode = */ false, /*ignore_case = */ false);
|
||||
}
|
||||
|
||||
void
|
||||
NativeRegExpMacroAssembler::CheckNotBackReferenceIgnoreCase(int start_reg, bool read_backward,
|
||||
Label* on_no_match, bool unicode)
|
||||
{
|
||||
JitSpew(SPEW_PREFIX "CheckNotBackReferenceIgnoreCase(%d, %d)", start_reg, unicode);
|
||||
|
||||
CheckNotBackReferenceImpl(start_reg, read_backward, on_no_match, unicode, /*ignore_case = */ true);
|
||||
}
|
||||
|
||||
void
|
||||
NativeRegExpMacroAssembler::CheckNotCharacterAfterMinusAnd(char16_t c, char16_t minus, char16_t and_with,
|
||||
Label* on_not_equal)
|
||||
|
|
@ -961,10 +943,13 @@ NativeRegExpMacroAssembler::LoadCurrentCharacter(int cp_offset, Label* on_end_of
|
|||
{
|
||||
JitSpew(SPEW_PREFIX "LoadCurrentCharacter(%d, %d)", cp_offset, characters);
|
||||
|
||||
MOZ_ASSERT(cp_offset >= -1); // ^ and \b can look behind one character.
|
||||
MOZ_ASSERT(cp_offset < (1<<30)); // Be sane! (And ensure negation works)
|
||||
if (check_bounds)
|
||||
CheckPosition(cp_offset + characters - 1, on_end_of_input);
|
||||
if (cp_offset >= 0) {
|
||||
CheckPosition(cp_offset + characters - 1, on_end_of_input);
|
||||
} else {
|
||||
CheckPosition(cp_offset, on_end_of_input);
|
||||
}
|
||||
LoadCurrentCharacterUnchecked(cp_offset, characters);
|
||||
}
|
||||
|
||||
|
|
@ -972,9 +957,8 @@ void
|
|||
NativeRegExpMacroAssembler::LoadCurrentCharacterUnchecked(int cp_offset, int characters)
|
||||
{
|
||||
JitSpew(SPEW_PREFIX "LoadCurrentCharacterUnchecked(%d, %d)", cp_offset, characters);
|
||||
|
||||
BaseIndex address(input_end_pointer, current_position, TimesOne, cp_offset * char_size());
|
||||
if (mode_ == ASCII) {
|
||||
BaseIndex address(input_end_pointer, current_position, TimesOne, cp_offset);
|
||||
if (characters == 4) {
|
||||
masm.load32(address, current_character);
|
||||
} else if (characters == 2) {
|
||||
|
|
@ -986,7 +970,6 @@ NativeRegExpMacroAssembler::LoadCurrentCharacterUnchecked(int cp_offset, int cha
|
|||
} else {
|
||||
MOZ_ASSERT(mode_ == CHAR16);
|
||||
MOZ_ASSERT(characters <= 2);
|
||||
BaseIndex address(input_end_pointer, current_position, TimesOne, cp_offset * sizeof(char16_t));
|
||||
if (characters == 2)
|
||||
masm.load32(address, current_character);
|
||||
else
|
||||
|
|
@ -1096,10 +1079,11 @@ NativeRegExpMacroAssembler::CheckBacktrackStackLimit()
|
|||
masm.moveStackPtrTo(temp2);
|
||||
|
||||
masm.call(&stack_overflow_label_);
|
||||
masm.bind(&no_stack_overflow);
|
||||
|
||||
// Exit with an exception if the call failed.
|
||||
masm.branchTest32(Assembler::Zero, temp0, temp0, &exit_with_exception_label_);
|
||||
|
||||
masm.bind(&no_stack_overflow);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -1213,8 +1197,21 @@ void
|
|||
NativeRegExpMacroAssembler::CheckPosition(int cp_offset, Label* on_outside_input)
|
||||
{
|
||||
JitSpew(SPEW_PREFIX "CheckPosition(%d)", cp_offset);
|
||||
masm.branchPtr(Assembler::GreaterThanOrEqual, current_position,
|
||||
ImmWord(-cp_offset * char_size()), BranchOrBacktrack(on_outside_input));
|
||||
if (cp_offset >= 0) {
|
||||
// end + current + offset >= end
|
||||
// <=> current + offset >= 0
|
||||
// <=> current >= -offset
|
||||
masm.branchPtr(Assembler::GreaterThanOrEqual, current_position,
|
||||
ImmWord(-cp_offset * char_size()), BranchOrBacktrack(on_outside_input));
|
||||
} else {
|
||||
// negative cp_offset means we're reading backwards, check against start of string
|
||||
// Compute offset address
|
||||
masm.computeEffectiveAddress(BaseIndex(input_end_pointer, current_position, TimesOne, cp_offset * char_size()), temp0);
|
||||
|
||||
// Compare to start of input.
|
||||
Address inputStart(masm.getStackPointer(), offsetof(FrameData, inputStart));
|
||||
masm.branchPtr(Assembler::GreaterThan, inputStart, temp0, BranchOrBacktrack(on_outside_input));
|
||||
}
|
||||
}
|
||||
|
||||
Label*
|
||||
|
|
|
|||
|
|
@ -98,15 +98,16 @@ class MOZ_STACK_CLASS NativeRegExpMacroAssembler final : public RegExpMacroAssem
|
|||
void AdvanceRegister(int reg, int by);
|
||||
void Backtrack();
|
||||
void Bind(jit::Label* label);
|
||||
void CheckAtStart(jit::Label* on_at_start);
|
||||
void CheckAtStart(int cp_offset, jit::Label* on_at_start);
|
||||
void CheckCharacter(unsigned c, jit::Label* on_equal);
|
||||
void CheckCharacterAfterAnd(unsigned c, unsigned and_with, jit::Label* on_equal);
|
||||
void CheckCharacterGT(char16_t limit, jit::Label* on_greater);
|
||||
void CheckCharacterLT(char16_t limit, jit::Label* on_less);
|
||||
void CheckGreedyLoop(jit::Label* on_tos_equals_current_position);
|
||||
void CheckNotAtStart(jit::Label* on_not_at_start);
|
||||
void CheckNotBackReference(int start_reg, jit::Label* on_no_match);
|
||||
void CheckNotBackReferenceIgnoreCase(int start_reg, jit::Label* on_no_match, bool unicode);
|
||||
void CheckNotAtStart(int cp_offset, jit::Label* on_not_at_start);
|
||||
void CheckNotBackReference(int start_reg, bool read_backward, jit::Label* on_no_match);
|
||||
void CheckNotBackReferenceIgnoreCase(int start_reg, bool read_backward,
|
||||
jit::Label* on_no_match, bool unicode);
|
||||
void CheckNotCharacter(unsigned c, jit::Label* on_not_equal);
|
||||
void CheckNotCharacterAfterAnd(unsigned c, unsigned and_with, jit::Label* on_not_equal);
|
||||
void CheckNotCharacterAfterMinusAnd(char16_t c, char16_t minus, char16_t and_with,
|
||||
|
|
@ -202,13 +203,17 @@ class MOZ_STACK_CLASS NativeRegExpMacroAssembler final : public RegExpMacroAssem
|
|||
|
||||
Vector<LabelPatch, 4, SystemAllocPolicy> labelPatches;
|
||||
|
||||
// See RegExpMacroAssembler.cpp for the meaning of these registers.
|
||||
// See NativeRegExpMacroAssembler.cpp for the meaning of these registers.
|
||||
jit::Register input_end_pointer;
|
||||
jit::Register current_character;
|
||||
jit::Register current_position;
|
||||
jit::Register backtrack_stack_pointer;
|
||||
jit::Register temp0, temp1, temp2;
|
||||
|
||||
void CheckAtStartImpl(int cp_offset, jit::Label* on_cond, jit::Assembler::Condition cond);
|
||||
void CheckNotBackReferenceImpl(int start_reg, bool read_backward, jit::Label* on_no_match,
|
||||
bool unicode, bool ignore_case);
|
||||
|
||||
// The frame_pointer-relative location of a regexp register.
|
||||
jit::Address register_location(int register_index) {
|
||||
checkRegister(register_index);
|
||||
|
|
|
|||
|
|
@ -249,16 +249,16 @@ RegExpCapture::CaptureRegisters()
|
|||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// RegExpLookahead
|
||||
// RegExpLookaround
|
||||
|
||||
Interval
|
||||
RegExpLookahead::CaptureRegisters()
|
||||
RegExpLookaround::CaptureRegisters()
|
||||
{
|
||||
return body()->CaptureRegisters();
|
||||
}
|
||||
|
||||
bool
|
||||
RegExpLookahead::IsAnchoredAtStart()
|
||||
RegExpLookaround::IsAnchoredAtStart()
|
||||
{
|
||||
return is_positive() && body()->IsAnchoredAtStart();
|
||||
return is_positive() && type() == LOOKAHEAD && body()->IsAnchoredAtStart();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -234,8 +234,6 @@ class RegExpCharacterClass : public RegExpTree
|
|||
bool is_negated_;
|
||||
};
|
||||
|
||||
typedef InfallibleVector<char16_t, 10> CharacterVector;
|
||||
|
||||
class RegExpAtom : public RegExpTree
|
||||
{
|
||||
public:
|
||||
|
|
@ -341,7 +339,7 @@ class RegExpCapture : public RegExpTree
|
|||
{
|
||||
public:
|
||||
explicit RegExpCapture(RegExpTree* body, int index)
|
||||
: body_(body), index_(index)
|
||||
: body_(body), index_(index), name_(nullptr)
|
||||
{}
|
||||
|
||||
virtual void* Accept(RegExpVisitor* visitor, void* data);
|
||||
|
|
@ -359,34 +357,42 @@ class RegExpCapture : public RegExpTree
|
|||
virtual int min_match() { return body_->min_match(); }
|
||||
virtual int max_match() { return body_->max_match(); }
|
||||
RegExpTree* body() { return body_; }
|
||||
void set_body(RegExpTree* body) { body_ = body; }
|
||||
int index() { return index_; }
|
||||
const CharacterVector* name() const { return name_; }
|
||||
void set_name(const CharacterVector* name) { name_ = name; }
|
||||
static int StartRegister(int index) { return index * 2; }
|
||||
static int EndRegister(int index) { return index * 2 + 1; }
|
||||
|
||||
private:
|
||||
RegExpTree* body_;
|
||||
int index_;
|
||||
const CharacterVector* name_;
|
||||
};
|
||||
|
||||
class RegExpLookahead : public RegExpTree
|
||||
class RegExpLookaround : public RegExpTree
|
||||
{
|
||||
public:
|
||||
RegExpLookahead(RegExpTree* body,
|
||||
bool is_positive,
|
||||
int capture_count,
|
||||
int capture_from)
|
||||
enum Type { LOOKAHEAD, LOOKBEHIND };
|
||||
|
||||
RegExpLookaround(RegExpTree* body,
|
||||
bool is_positive,
|
||||
int capture_count,
|
||||
int capture_from,
|
||||
Type type)
|
||||
: body_(body),
|
||||
is_positive_(is_positive),
|
||||
capture_count_(capture_count),
|
||||
capture_from_(capture_from)
|
||||
capture_from_(capture_from),
|
||||
type_(type)
|
||||
{}
|
||||
|
||||
virtual void* Accept(RegExpVisitor* visitor, void* data);
|
||||
virtual RegExpNode* ToNode(RegExpCompiler* compiler,
|
||||
RegExpNode* on_success);
|
||||
virtual RegExpLookahead* AsLookahead();
|
||||
virtual RegExpLookaround* AsLookaround();
|
||||
virtual Interval CaptureRegisters();
|
||||
virtual bool IsLookahead();
|
||||
virtual bool IsLookaround();
|
||||
virtual bool IsAnchoredAtStart();
|
||||
virtual int min_match() { return 0; }
|
||||
virtual int max_match() { return 0; }
|
||||
|
|
@ -394,12 +400,14 @@ class RegExpLookahead : public RegExpTree
|
|||
bool is_positive() { return is_positive_; }
|
||||
int capture_count() { return capture_count_; }
|
||||
int capture_from() { return capture_from_; }
|
||||
Type type() { return type_; }
|
||||
|
||||
private:
|
||||
RegExpTree* body_;
|
||||
bool is_positive_;
|
||||
int capture_count_;
|
||||
int capture_from_;
|
||||
Type type_;
|
||||
};
|
||||
|
||||
typedef InfallibleVector<RegExpCapture*, 1> RegExpCaptureVector;
|
||||
|
|
@ -408,7 +416,7 @@ class RegExpBackReference : public RegExpTree
|
|||
{
|
||||
public:
|
||||
explicit RegExpBackReference(RegExpCapture* capture)
|
||||
: capture_(capture)
|
||||
: capture_(capture), name_(nullptr)
|
||||
{}
|
||||
|
||||
virtual void* Accept(RegExpVisitor* visitor, void* data);
|
||||
|
|
@ -416,14 +424,22 @@ class RegExpBackReference : public RegExpTree
|
|||
RegExpNode* on_success);
|
||||
virtual RegExpBackReference* AsBackReference();
|
||||
virtual bool IsBackReference();
|
||||
virtual int min_match() { return 0; }
|
||||
virtual int max_match() { return capture_->max_match(); }
|
||||
virtual int min_match() override { return 0; }
|
||||
// The back reference may be recursive, e.g. /(\2)(\1)/. To avoid infinite
|
||||
// recursion, we give up. Ignorance is bliss.
|
||||
int max_match() override { return kInfinity; }
|
||||
int index() { return capture_->index(); }
|
||||
RegExpCapture* capture() { return capture_; }
|
||||
void set_capture(RegExpCapture* capture) { capture_ = capture; }
|
||||
const CharacterVector* name() const { return name_; }
|
||||
void set_name(const CharacterVector* name) { name_ = name; }
|
||||
private:
|
||||
RegExpCapture* capture_;
|
||||
const CharacterVector* name_;
|
||||
};
|
||||
|
||||
typedef InfallibleVector<RegExpBackReference*, 1> RegExpBackReferenceVector;
|
||||
|
||||
class RegExpEmpty : public RegExpTree
|
||||
{
|
||||
public:
|
||||
|
|
|
|||
|
|
@ -81,16 +81,19 @@ V(CHECK_LT, 35, 8) /* bc8 pad8 uc16 addr32 */ \
|
|||
V(CHECK_GT, 36, 8) /* bc8 pad8 uc16 addr32 */ \
|
||||
V(CHECK_NOT_BACK_REF, 37, 8) /* bc8 reg_idx24 addr32 */ \
|
||||
V(CHECK_NOT_BACK_REF_NO_CASE, 38, 8) /* bc8 reg_idx24 addr32 */ \
|
||||
V(CHECK_NOT_REGS_EQUAL, 39, 12) /* bc8 regidx24 reg_idx32 addr32 */ \
|
||||
V(CHECK_REGISTER_LT, 40, 12) /* bc8 reg_idx24 value32 addr32 */ \
|
||||
V(CHECK_REGISTER_GE, 41, 12) /* bc8 reg_idx24 value32 addr32 */ \
|
||||
V(CHECK_REGISTER_EQ_POS, 42, 8) /* bc8 reg_idx24 addr32 */ \
|
||||
V(CHECK_AT_START, 43, 8) /* bc8 pad24 addr32 */ \
|
||||
V(CHECK_NOT_AT_START, 44, 8) /* bc8 pad24 addr32 */ \
|
||||
V(CHECK_GREEDY, 45, 8) /* bc8 pad24 addr32 */ \
|
||||
V(ADVANCE_CP_AND_GOTO, 46, 8) /* bc8 offset24 addr32 */ \
|
||||
V(SET_CURRENT_POSITION_FROM_END, 47, 4) /* bc8 idx24 */ \
|
||||
V(CHECK_NOT_BACK_REF_NO_CASE_UNICODE, 48, 8) /* bc8 reg_idx24 addr32 */
|
||||
V(CHECK_NOT_BACK_REF_BACKWARD, 39, 8) /* bc8 reg_idx24 addr32 */ \
|
||||
V(CHECK_NOT_BACK_REF_NO_CASE_BACKWARD, 40, 8) /* bc8 reg_idx24 addr32 */ \
|
||||
V(CHECK_NOT_REGS_EQUAL, 41, 12) /* bc8 regidx24 reg_idx32 addr32 */ \
|
||||
V(CHECK_REGISTER_LT, 42, 12) /* bc8 reg_idx24 value32 addr32 */ \
|
||||
V(CHECK_REGISTER_GE, 43, 12) /* bc8 reg_idx24 value32 addr32 */ \
|
||||
V(CHECK_REGISTER_EQ_POS, 44, 8) /* bc8 reg_idx24 addr32 */ \
|
||||
V(CHECK_AT_START, 45, 8) /* bc8 pad24 addr32 */ \
|
||||
V(CHECK_NOT_AT_START, 46, 8) /* bc8 pad24 addr32 */ \
|
||||
V(CHECK_GREEDY, 47, 8) /* bc8 pad24 addr32 */ \
|
||||
V(ADVANCE_CP_AND_GOTO, 48, 8) /* bc8 offset24 addr32 */ \
|
||||
V(SET_CURRENT_POSITION_FROM_END, 49, 4) /* bc8 idx24 */ \
|
||||
V(CHECK_NOT_BACK_REF_NO_CASE_UNICODE, 50, 8) /* bc8 reg_idx24 addr32 */ \
|
||||
V(CHECK_NOT_BACK_REF_NO_CASE_BACKWARD_UNICODE, 51, 8) /* bc8 reg_idx24 addr32 */
|
||||
|
||||
#define DECLARE_BYTECODES(name, code, length) \
|
||||
static const int BC_##name = code;
|
||||
|
|
|
|||
2069
js/src/irregexp/RegExpCharRanges.cpp
Normal file
2069
js/src/irregexp/RegExpCharRanges.cpp
Normal file
File diff suppressed because it is too large
Load diff
235
js/src/irregexp/RegExpCharRanges.h
Normal file
235
js/src/irregexp/RegExpCharRanges.h
Normal file
|
|
@ -0,0 +1,235 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
// Copyright 2012 the V8 project authors. All rights reserved.
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following
|
||||
// disclaimer in the documentation and/or other materials provided
|
||||
// with the distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived
|
||||
// from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#ifndef V8_JSREGEXPCHARRANGES_H_
|
||||
#define V8_JSREGEXPCHARRANGES_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "irregexp/RegExpCharacters.h"
|
||||
#include "irregexp/InfallibleVector.h"
|
||||
|
||||
namespace js {
|
||||
|
||||
namespace irregexp {
|
||||
|
||||
// Characters parsed by RegExpParser can be either char16_t or kEndMarker.
|
||||
typedef uint32_t widechar;
|
||||
|
||||
static const int kMaxOneByteCharCode = 0xff;
|
||||
static const int kMaxUtf16CodeUnit = 0xffff;
|
||||
static const size_t kEcma262UnCanonicalizeMaxWidth = 4;
|
||||
static const char16_t kNoCharClass = 0;
|
||||
|
||||
extern const widechar kEmojiFlagSequences[];
|
||||
extern const widechar kEmojiTagSequences[];
|
||||
extern const widechar kEmojiZWJSequences[];
|
||||
|
||||
static inline char16_t
|
||||
MaximumCharacter(bool ascii)
|
||||
{
|
||||
return ascii ? kMaxOneByteCharCode : kMaxUtf16CodeUnit;
|
||||
}
|
||||
|
||||
|
||||
// Returns the number of characters in the equivalence class, omitting those
|
||||
// that cannot occur in the source string if it is a one byte string.
|
||||
int
|
||||
GetCaseIndependentLetters(char16_t character,
|
||||
bool ascii_subject,
|
||||
bool unicode,
|
||||
const char16_t* choices,
|
||||
size_t choices_length,
|
||||
char16_t* letters);
|
||||
|
||||
int
|
||||
GetCaseIndependentLetters(char16_t character,
|
||||
bool ascii_subject,
|
||||
bool unicode,
|
||||
char16_t* letters);
|
||||
|
||||
class CharacterRange;
|
||||
class WideCharRange;
|
||||
typedef InfallibleVector<CharacterRange, 1> CharacterRangeVector;
|
||||
typedef InfallibleVector<WideCharRange, 1> WideCharRangeVector;
|
||||
|
||||
// Represents code units in the range from from_ to to_, both ends are
|
||||
// inclusive.
|
||||
class CharacterRange
|
||||
{
|
||||
public:
|
||||
// static methods for dealing with CharacterRangeVectors
|
||||
|
||||
static void AddClass(const int* elmv, int elmc, CharacterRangeVector* ranges);
|
||||
static void AddClassNegated(const int* elmv, int elmc, CharacterRangeVector* ranges);
|
||||
static void AddClassEscape(LifoAlloc* alloc, char16_t type, CharacterRangeVector* ranges);
|
||||
static void AddClassEscapeUnicode(LifoAlloc* alloc, char16_t type,
|
||||
CharacterRangeVector* ranges, bool ignoreCase);
|
||||
|
||||
// Adds a character or pre-defined character class to character ranges.
|
||||
// If char_class is not kNoCharClass, it's interpreted as a class
|
||||
// escape (i.e., 's' means whitespace, from '\s').
|
||||
static void AddCharOrEscape(LifoAlloc* alloc, CharacterRangeVector* ranges,
|
||||
char16_t char_class, widechar c);
|
||||
static void AddCharOrEscapeUnicode(LifoAlloc* alloc,
|
||||
CharacterRangeVector* ranges,
|
||||
CharacterRangeVector* lead_ranges,
|
||||
CharacterRangeVector* trail_ranges,
|
||||
WideCharRangeVector* wide_ranges,
|
||||
char16_t char_class,
|
||||
widechar c,
|
||||
bool ignore_case);
|
||||
// Simplified version of AddUnicodeRange for single characters
|
||||
static void AddCharUnicode(LifoAlloc* alloc,
|
||||
CharacterRangeVector* ranges,
|
||||
CharacterRangeVector* lead_ranges,
|
||||
CharacterRangeVector* trail_ranges,
|
||||
WideCharRangeVector* wide_ranges,
|
||||
widechar c);
|
||||
static void AddUnicodeRange(LifoAlloc* alloc,
|
||||
CharacterRangeVector* ranges,
|
||||
CharacterRangeVector* lead_ranges,
|
||||
CharacterRangeVector* trail_ranges,
|
||||
WideCharRangeVector* wide_ranges,
|
||||
widechar first,
|
||||
widechar next);
|
||||
|
||||
static bool RangesContainLatin1Equivalents(const CharacterRangeVector& ranges, bool unicode);
|
||||
static bool CompareRanges(const CharacterRangeVector& ranges, const int* special_class, size_t length);
|
||||
static bool CompareInverseRanges(const CharacterRangeVector& ranges, const int* special_class, size_t length);
|
||||
|
||||
// Negate a vector of ranges by subtracting its ranges from a range
|
||||
// encompassing the full range of possible values.
|
||||
template <typename RangeType>
|
||||
static void NegateUnicodeRanges(LifoAlloc* alloc, InfallibleVector<RangeType, 1>** ranges,
|
||||
RangeType full_range);
|
||||
|
||||
// static methods for Unicode Property Escapes
|
||||
static bool AddPropertyClassRange(LifoAlloc* alloc,
|
||||
const std::string& name, const std::string& value,
|
||||
bool negate, bool ignore_case,
|
||||
CharacterRangeVector* ranges,
|
||||
CharacterRangeVector* lead_ranges,
|
||||
CharacterRangeVector* trail_ranges,
|
||||
WideCharRangeVector* wide_ranges);
|
||||
|
||||
// static methods for dealing with canonical CharacterRangeVectors
|
||||
|
||||
// Whether a range list is in canonical form: Ranges ordered by from value,
|
||||
// and ranges non-overlapping and non-adjacent.
|
||||
static bool IsCanonical(const CharacterRangeVector& ranges);
|
||||
|
||||
// Convert range list to canonical form. The characters covered by the ranges
|
||||
// will still be the same, but no character is in more than one range, and
|
||||
// adjacent ranges are merged. The resulting list may be shorter than the
|
||||
// original, but cannot be longer.
|
||||
static void Canonicalize(CharacterRangeVector& ranges);
|
||||
|
||||
static int InsertRangeInCanonicalList(CharacterRangeVector& list, int count, CharacterRange insert);
|
||||
|
||||
// Negate the contents of a character range in canonical form.
|
||||
static void Negate(const LifoAlloc* alloc,
|
||||
CharacterRangeVector src,
|
||||
CharacterRangeVector* dst);
|
||||
public:
|
||||
CharacterRange()
|
||||
: from_(0), to_(0)
|
||||
{}
|
||||
|
||||
CharacterRange(char16_t from, char16_t to)
|
||||
: from_(from), to_(to)
|
||||
{}
|
||||
|
||||
static inline CharacterRange Singleton(char16_t value) {
|
||||
return CharacterRange(value, value);
|
||||
}
|
||||
static inline CharacterRange Range(char16_t from, char16_t to) {
|
||||
MOZ_ASSERT(from <= to);
|
||||
return CharacterRange(from, to);
|
||||
}
|
||||
static inline CharacterRange Everything() {
|
||||
return CharacterRange(0, kMaxUtf16CodeUnit);
|
||||
}
|
||||
static inline CharacterRange LeadSurrogate() {
|
||||
return CharacterRange(unicode::LeadSurrogateMin, unicode::LeadSurrogateMax);
|
||||
}
|
||||
static inline CharacterRange TrailSurrogate() {
|
||||
return CharacterRange(unicode::TrailSurrogateMin, unicode::TrailSurrogateMax);
|
||||
}
|
||||
bool Contains(char16_t i) { return from_ <= i && i <= to_; }
|
||||
char16_t from() const { return from_; }
|
||||
void set_from(char16_t value) { from_ = value; }
|
||||
char16_t to() const { return to_; }
|
||||
void set_to(char16_t value) { to_ = value; }
|
||||
bool is_valid() { return from_ <= to_; }
|
||||
bool IsEverything(char16_t max) { return from_ == 0 && to_ >= max; }
|
||||
bool IsSingleton() { return (from_ == to_); }
|
||||
|
||||
void AddCaseEquivalents(bool is_ascii, bool unicode, CharacterRangeVector* ranges);
|
||||
private:
|
||||
char16_t from_;
|
||||
char16_t to_;
|
||||
};
|
||||
|
||||
|
||||
class WideCharRange
|
||||
{
|
||||
public:
|
||||
WideCharRange()
|
||||
: from_(0), to_(0)
|
||||
{}
|
||||
|
||||
WideCharRange(widechar from, widechar to)
|
||||
: from_(from), to_(to)
|
||||
{}
|
||||
|
||||
static inline WideCharRange Singleton(widechar value) {
|
||||
return WideCharRange(value, value);
|
||||
}
|
||||
static inline WideCharRange Range(widechar from, widechar to) {
|
||||
MOZ_ASSERT(from <= to);
|
||||
return WideCharRange(from, to);
|
||||
}
|
||||
static inline WideCharRange NonBMP() {
|
||||
return WideCharRange(unicode::NonBMPMin, unicode::NonBMPMax);
|
||||
}
|
||||
|
||||
bool Contains(widechar i) const { return from_ <= i && i <= to_; }
|
||||
widechar from() const { return from_; }
|
||||
widechar to() const { return to_; }
|
||||
|
||||
private:
|
||||
widechar from_;
|
||||
widechar to_;
|
||||
};
|
||||
|
||||
|
||||
} } // namespace js::irregexp
|
||||
|
||||
#endif // V8_JSREGEXPCHARRANGES_H_
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -34,6 +34,9 @@
|
|||
|
||||
#include "ds/SplayTree.h"
|
||||
#include "jit/Label.h"
|
||||
|
||||
#include "irregexp/InfallibleVector.h"
|
||||
#include "irregexp/RegExpCharRanges.h"
|
||||
#include "vm/RegExpObject.h"
|
||||
|
||||
namespace js {
|
||||
|
|
@ -57,13 +60,28 @@ struct RegExpCompileData
|
|||
: tree(nullptr),
|
||||
simple(true),
|
||||
contains_anchor(false),
|
||||
capture_count(0)
|
||||
capture_count(0),
|
||||
capture_name_list(nullptr),
|
||||
capture_index_list(nullptr)
|
||||
{}
|
||||
|
||||
// The parsed AST as produced by the RegExpParser.
|
||||
RegExpTree* tree;
|
||||
// True, iff the pattern is a 'simple' atom with zero captures. In other
|
||||
// words, the pattern consists of a string with no metacharacters and special
|
||||
// regexp features, and can be implemented as a standard string search.
|
||||
bool simple;
|
||||
|
||||
// True, iff the pattern is anchored at the start of the string with '^'.
|
||||
bool contains_anchor;
|
||||
|
||||
// The number of capture groups, without the global capture \0.
|
||||
int capture_count;
|
||||
|
||||
// Only use if the pattern contains named captures. If so, this contains a
|
||||
// mapping of capture names to capture indices, as Values.
|
||||
CharacterVectorVector* capture_name_list;
|
||||
IntegerVector* capture_index_list;
|
||||
};
|
||||
|
||||
struct RegExpCode
|
||||
|
|
@ -118,7 +136,7 @@ InterpretCode(JSContext* cx, const uint8_t* byteCode, const CharT* chars, size_t
|
|||
VISIT(Atom) \
|
||||
VISIT(Quantifier) \
|
||||
VISIT(Capture) \
|
||||
VISIT(Lookahead) \
|
||||
VISIT(Lookaround) \
|
||||
VISIT(BackReference) \
|
||||
VISIT(Empty) \
|
||||
VISIT(Text)
|
||||
|
|
@ -127,108 +145,6 @@ InterpretCode(JSContext* cx, const uint8_t* byteCode, const CharT* chars, size_t
|
|||
FOR_EACH_REG_EXP_TREE_TYPE(FORWARD_DECLARE)
|
||||
#undef FORWARD_DECLARE
|
||||
|
||||
// InfallibleVector is like Vector, but all its methods are infallible (they
|
||||
// crash on OOM). We use this class instead of Vector to avoid a ton of
|
||||
// MOZ_MUST_USE warnings in irregexp code (imported from V8).
|
||||
template<typename T, size_t N>
|
||||
class InfallibleVector
|
||||
{
|
||||
Vector<T, N, LifoAllocPolicy<Infallible>> vector_;
|
||||
|
||||
InfallibleVector(const InfallibleVector&) = delete;
|
||||
void operator=(const InfallibleVector&) = delete;
|
||||
|
||||
public:
|
||||
explicit InfallibleVector(const LifoAllocPolicy<Infallible>& alloc) : vector_(alloc) {}
|
||||
|
||||
void append(const T& t) { MOZ_ALWAYS_TRUE(vector_.append(t)); }
|
||||
void append(const T* begin, size_t length) { MOZ_ALWAYS_TRUE(vector_.append(begin, length)); }
|
||||
|
||||
void clear() { vector_.clear(); }
|
||||
void popBack() { vector_.popBack(); }
|
||||
void reserve(size_t n) { MOZ_ALWAYS_TRUE(vector_.reserve(n)); }
|
||||
|
||||
size_t length() const { return vector_.length(); }
|
||||
T popCopy() { return vector_.popCopy(); }
|
||||
|
||||
T* begin() { return vector_.begin(); }
|
||||
const T* begin() const { return vector_.begin(); }
|
||||
|
||||
T& operator[](size_t index) { return vector_[index]; }
|
||||
const T& operator[](size_t index) const { return vector_[index]; }
|
||||
|
||||
InfallibleVector& operator=(InfallibleVector&& rhs) { vector_ = Move(rhs.vector_); return *this; }
|
||||
};
|
||||
|
||||
class CharacterRange;
|
||||
typedef InfallibleVector<CharacterRange, 1> CharacterRangeVector;
|
||||
|
||||
// Represents code units in the range from from_ to to_, both ends are
|
||||
// inclusive.
|
||||
class CharacterRange
|
||||
{
|
||||
public:
|
||||
CharacterRange()
|
||||
: from_(0), to_(0)
|
||||
{}
|
||||
|
||||
CharacterRange(char16_t from, char16_t to)
|
||||
: from_(from), to_(to)
|
||||
{}
|
||||
|
||||
static void AddClassEscape(LifoAlloc* alloc, char16_t type, CharacterRangeVector* ranges);
|
||||
static void AddClassEscapeUnicode(LifoAlloc* alloc, char16_t type,
|
||||
CharacterRangeVector* ranges, bool ignoreCase);
|
||||
|
||||
static inline CharacterRange Singleton(char16_t value) {
|
||||
return CharacterRange(value, value);
|
||||
}
|
||||
static inline CharacterRange Range(char16_t from, char16_t to) {
|
||||
MOZ_ASSERT(from <= to);
|
||||
return CharacterRange(from, to);
|
||||
}
|
||||
static inline CharacterRange Everything() {
|
||||
return CharacterRange(0, 0xFFFF);
|
||||
}
|
||||
bool Contains(char16_t i) { return from_ <= i && i <= to_; }
|
||||
char16_t from() const { return from_; }
|
||||
void set_from(char16_t value) { from_ = value; }
|
||||
char16_t to() const { return to_; }
|
||||
void set_to(char16_t value) { to_ = value; }
|
||||
bool is_valid() { return from_ <= to_; }
|
||||
bool IsEverything(char16_t max) { return from_ == 0 && to_ >= max; }
|
||||
bool IsSingleton() { return (from_ == to_); }
|
||||
void AddCaseEquivalents(bool is_ascii, bool unicode, CharacterRangeVector* ranges);
|
||||
|
||||
static void Split(const LifoAlloc* alloc,
|
||||
CharacterRangeVector base,
|
||||
const Vector<int>& overlay,
|
||||
CharacterRangeVector* included,
|
||||
CharacterRangeVector* excluded);
|
||||
|
||||
// Whether a range list is in canonical form: Ranges ordered by from value,
|
||||
// and ranges non-overlapping and non-adjacent.
|
||||
static bool IsCanonical(const CharacterRangeVector& ranges);
|
||||
|
||||
// Convert range list to canonical form. The characters covered by the ranges
|
||||
// will still be the same, but no character is in more than one range, and
|
||||
// adjacent ranges are merged. The resulting list may be shorter than the
|
||||
// original, but cannot be longer.
|
||||
static void Canonicalize(CharacterRangeVector& ranges);
|
||||
|
||||
// Negate the contents of a character range in canonical form.
|
||||
static void Negate(const LifoAlloc* alloc,
|
||||
CharacterRangeVector src,
|
||||
CharacterRangeVector* dst);
|
||||
|
||||
static const int kStartMarker = (1 << 24);
|
||||
static const int kPayloadMask = (1 << 24) - 1;
|
||||
|
||||
private:
|
||||
char16_t from_;
|
||||
char16_t to_;
|
||||
};
|
||||
|
||||
// A set of unsigned integers that behaves especially well on small
|
||||
// integers (< 32).
|
||||
class OutSet
|
||||
|
|
@ -524,7 +440,7 @@ class RegExpNode
|
|||
int characters_filled_in,
|
||||
bool not_at_start) = 0;
|
||||
|
||||
static const int kNodeIsTooComplexForGreedyLoops = -1;
|
||||
static const int kNodeIsTooComplexForGreedyLoops = INT32_MIN;
|
||||
|
||||
virtual int GreedyLoopTextLength() { return kNodeIsTooComplexForGreedyLoops; }
|
||||
|
||||
|
|
@ -762,15 +678,19 @@ class TextNode : public SeqRegExpNode
|
|||
{
|
||||
public:
|
||||
TextNode(TextElementVector* elements,
|
||||
bool read_backward,
|
||||
RegExpNode* on_success)
|
||||
: SeqRegExpNode(on_success),
|
||||
elements_(elements)
|
||||
elements_(elements),
|
||||
read_backward_(read_backward)
|
||||
{}
|
||||
|
||||
TextNode(RegExpCharacterClass* that,
|
||||
bool read_backward,
|
||||
RegExpNode* on_success)
|
||||
: SeqRegExpNode(on_success),
|
||||
elements_(alloc()->newInfallible<TextElementVector>(*alloc()))
|
||||
elements_(alloc()->newInfallible<TextElementVector>(*alloc())),
|
||||
read_backward_(read_backward)
|
||||
{
|
||||
elements_->append(TextElement::CharClass(that));
|
||||
}
|
||||
|
|
@ -783,6 +703,7 @@ class TextNode : public SeqRegExpNode
|
|||
int characters_filled_in,
|
||||
bool not_at_start);
|
||||
TextElementVector& elements() { return *elements_; }
|
||||
bool read_backward() { return read_backward_; }
|
||||
void MakeCaseIndependent(bool is_ascii, bool unicode);
|
||||
virtual int GreedyLoopTextLength();
|
||||
virtual RegExpNode* GetSuccessorOfOmnivorousTextNode(
|
||||
|
|
@ -813,6 +734,7 @@ class TextNode : public SeqRegExpNode
|
|||
int* checked_up_to);
|
||||
int Length();
|
||||
TextElementVector* elements_;
|
||||
bool read_backward_;
|
||||
};
|
||||
|
||||
class AssertionNode : public SeqRegExpNode
|
||||
|
|
@ -881,15 +803,18 @@ class BackReferenceNode : public SeqRegExpNode
|
|||
public:
|
||||
BackReferenceNode(int start_reg,
|
||||
int end_reg,
|
||||
bool read_backward,
|
||||
RegExpNode* on_success)
|
||||
: SeqRegExpNode(on_success),
|
||||
start_reg_(start_reg),
|
||||
end_reg_(end_reg)
|
||||
end_reg_(end_reg),
|
||||
read_backward_(read_backward)
|
||||
{}
|
||||
|
||||
virtual void Accept(NodeVisitor* visitor);
|
||||
int start_register() { return start_reg_; }
|
||||
int end_register() { return end_reg_; }
|
||||
bool read_backward() { return read_backward_; }
|
||||
virtual void Emit(RegExpCompiler* compiler, Trace* trace);
|
||||
virtual int EatsAtLeast(int still_to_find,
|
||||
int recursion_depth,
|
||||
|
|
@ -908,6 +833,7 @@ class BackReferenceNode : public SeqRegExpNode
|
|||
private:
|
||||
int start_reg_;
|
||||
int end_reg_;
|
||||
bool read_backward_;
|
||||
};
|
||||
|
||||
class EndNode : public RegExpNode
|
||||
|
|
@ -1050,8 +976,11 @@ class ChoiceNode : public RegExpNode
|
|||
bool not_at_start() { return not_at_start_; }
|
||||
void set_not_at_start() { not_at_start_ = true; }
|
||||
void set_being_calculated(bool b) { being_calculated_ = b; }
|
||||
virtual bool try_to_emit_quick_check_for_alternative(int i) { return true; }
|
||||
virtual bool try_to_emit_quick_check_for_alternative(bool is_first) {
|
||||
return true;
|
||||
}
|
||||
virtual RegExpNode* FilterASCII(int depth, bool ignore_case, bool unicode);
|
||||
virtual bool read_backward() { return false; }
|
||||
|
||||
protected:
|
||||
int GreedyLoopTextLengthForAlternative(GuardedAlternative* alternative);
|
||||
|
|
@ -1103,18 +1032,22 @@ class NegativeLookaheadChoiceNode : public ChoiceNode
|
|||
// starts by loading enough characters for the alternative that takes fewest
|
||||
// characters, but on a negative lookahead the negative branch did not take
|
||||
// part in that calculation (EatsAtLeast) so the assumptions don't hold.
|
||||
virtual bool try_to_emit_quick_check_for_alternative(int i) { return i != 0; }
|
||||
bool try_to_emit_quick_check_for_alternative(bool is_first) override {
|
||||
return !is_first;
|
||||
}
|
||||
virtual RegExpNode* FilterASCII(int depth, bool ignore_case, bool unicode);
|
||||
};
|
||||
|
||||
class LoopChoiceNode : public ChoiceNode
|
||||
{
|
||||
public:
|
||||
explicit LoopChoiceNode(LifoAlloc* alloc, bool body_can_be_zero_length)
|
||||
explicit LoopChoiceNode(LifoAlloc* alloc, bool body_can_be_zero_length,
|
||||
bool read_backward)
|
||||
: ChoiceNode(alloc, 2),
|
||||
loop_node_(nullptr),
|
||||
continue_node_(nullptr),
|
||||
body_can_be_zero_length_(body_can_be_zero_length)
|
||||
body_can_be_zero_length_(body_can_be_zero_length),
|
||||
read_backward_(read_backward)
|
||||
{}
|
||||
|
||||
void AddLoopAlternative(GuardedAlternative alt);
|
||||
|
|
@ -1132,6 +1065,7 @@ class LoopChoiceNode : public ChoiceNode
|
|||
RegExpNode* loop_node() { return loop_node_; }
|
||||
RegExpNode* continue_node() { return continue_node_; }
|
||||
bool body_can_be_zero_length() { return body_can_be_zero_length_; }
|
||||
virtual bool read_backward() { return read_backward_; }
|
||||
virtual void Accept(NodeVisitor* visitor);
|
||||
virtual RegExpNode* FilterASCII(int depth, bool ignore_case, bool unicode);
|
||||
|
||||
|
|
@ -1146,6 +1080,7 @@ class LoopChoiceNode : public ChoiceNode
|
|||
RegExpNode* loop_node_;
|
||||
RegExpNode* continue_node_;
|
||||
bool body_can_be_zero_length_;
|
||||
bool read_backward_;
|
||||
};
|
||||
|
||||
// Improve the speed that we scan for an initial point where a non-anchored
|
||||
|
|
@ -1421,8 +1356,8 @@ class Trace
|
|||
}
|
||||
|
||||
TriBool at_start() { return at_start_; }
|
||||
void set_at_start(bool at_start) {
|
||||
at_start_ = at_start ? TRUE_VALUE : FALSE_VALUE;
|
||||
void set_at_start(TriBool at_start) {
|
||||
at_start_ = at_start;
|
||||
}
|
||||
jit::Label* backtrack() { return backtrack_; }
|
||||
jit::Label* loop_label() { return loop_label_; }
|
||||
|
|
|
|||
|
|
@ -221,8 +221,8 @@ irregexp::InterpretCode(JSContext* cx, const uint8_t* byteCode, const CharT* cha
|
|||
}
|
||||
break;
|
||||
BYTECODE(LOAD_CURRENT_CHAR) {
|
||||
size_t pos = current + (insn >> BYTECODE_SHIFT);
|
||||
if (pos >= length) {
|
||||
int pos = current + (insn >> BYTECODE_SHIFT);
|
||||
if (pos >= (int)length || pos < 0) {
|
||||
pc = byteCode + Load32Aligned(pc + 4);
|
||||
} else {
|
||||
current_char = chars[pos];
|
||||
|
|
@ -237,8 +237,8 @@ irregexp::InterpretCode(JSContext* cx, const uint8_t* byteCode, const CharT* cha
|
|||
break;
|
||||
}
|
||||
BYTECODE(LOAD_2_CURRENT_CHARS) {
|
||||
size_t pos = current + (insn >> BYTECODE_SHIFT);
|
||||
if (pos + 2 > length) {
|
||||
int pos = current + (insn >> BYTECODE_SHIFT);
|
||||
if (pos + 2 > (int)length || pos < 0) {
|
||||
pc = byteCode + Load32Aligned(pc + 4);
|
||||
} else {
|
||||
CharT next = chars[pos + 1];
|
||||
|
|
@ -424,6 +424,30 @@ irregexp::InterpretCode(JSContext* cx, const uint8_t* byteCode, const CharT* cha
|
|||
pc += BC_CHECK_NOT_BACK_REF_LENGTH;
|
||||
break;
|
||||
}
|
||||
BYTECODE(CHECK_NOT_BACK_REF_BACKWARD) {
|
||||
int from = registers[insn >> BYTECODE_SHIFT];
|
||||
int len = registers[(insn >> BYTECODE_SHIFT) + 1] - from;
|
||||
if (from < 0 || len <= 0) {
|
||||
pc += BC_CHECK_NOT_BACK_REF_BACKWARD_LENGTH;
|
||||
break;
|
||||
}
|
||||
if (int(current) - len < 0) {
|
||||
pc = byteCode + Load32Aligned(pc + 4);
|
||||
break;
|
||||
} else {
|
||||
int i;
|
||||
for (i = 0; i < len; i++) {
|
||||
if (chars[from + i] != chars[int(current) - len + i]) {
|
||||
pc = byteCode + Load32Aligned(pc + 4);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i < len) break;
|
||||
current -= len;
|
||||
}
|
||||
pc += BC_CHECK_NOT_BACK_REF_BACKWARD_LENGTH;
|
||||
break;
|
||||
}
|
||||
BYTECODE(CHECK_NOT_BACK_REF_NO_CASE) {
|
||||
int from = registers[insn >> BYTECODE_SHIFT];
|
||||
int len = registers[(insn >> BYTECODE_SHIFT) + 1] - from;
|
||||
|
|
@ -464,14 +488,54 @@ irregexp::InterpretCode(JSContext* cx, const uint8_t* byteCode, const CharT* cha
|
|||
}
|
||||
break;
|
||||
}
|
||||
BYTECODE(CHECK_NOT_BACK_REF_NO_CASE_BACKWARD) {
|
||||
int from = registers[insn >> BYTECODE_SHIFT];
|
||||
int len = registers[(insn >> BYTECODE_SHIFT) + 1] - from;
|
||||
if (from < 0 || len <= 0) {
|
||||
pc += BC_CHECK_NOT_BACK_REF_NO_CASE_BACKWARD_LENGTH;
|
||||
break;
|
||||
}
|
||||
if (int(current) - len < 0) {
|
||||
pc = byteCode + Load32Aligned(pc + 4);
|
||||
break;
|
||||
}
|
||||
if (CaseInsensitiveCompareStrings(chars + from, chars + int(current) - len, len * sizeof(CharT))) {
|
||||
current -= len;
|
||||
pc += BC_CHECK_NOT_BACK_REF_NO_CASE_BACKWARD_LENGTH;
|
||||
} else {
|
||||
pc = byteCode + Load32Aligned(pc + 4);
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
BYTECODE(CHECK_NOT_BACK_REF_NO_CASE_BACKWARD_UNICODE) {
|
||||
int from = registers[insn >> BYTECODE_SHIFT];
|
||||
int len = registers[(insn >> BYTECODE_SHIFT) + 1] - from;
|
||||
if (from < 0 || len <= 0) {
|
||||
pc += BC_CHECK_NOT_BACK_REF_NO_CASE_BACKWARD_LENGTH;
|
||||
break;
|
||||
}
|
||||
if (int(current) - len < 0) {
|
||||
pc = byteCode + Load32Aligned(pc + 4);
|
||||
break;
|
||||
}
|
||||
if (CaseInsensitiveCompareUCStrings(chars + from, chars + int(current) - len, len * sizeof(CharT))) {
|
||||
current -= len;
|
||||
pc += BC_CHECK_NOT_BACK_REF_NO_CASE_BACKWARD_LENGTH;
|
||||
} else {
|
||||
pc = byteCode + Load32Aligned(pc + 4);
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
BYTECODE(CHECK_AT_START)
|
||||
if (current == 0)
|
||||
if (current + (insn >> BYTECODE_SHIFT) == 0)
|
||||
pc = byteCode + Load32Aligned(pc + 4);
|
||||
else
|
||||
pc += BC_CHECK_AT_START_LENGTH;
|
||||
break;
|
||||
BYTECODE(CHECK_NOT_AT_START)
|
||||
if (current == 0)
|
||||
if (current + (insn >> BYTECODE_SHIFT) == 0)
|
||||
pc += BC_CHECK_NOT_AT_START_LENGTH;
|
||||
else
|
||||
pc = byteCode + Load32Aligned(pc + 4);
|
||||
|
|
|
|||
|
|
@ -172,9 +172,9 @@ InterpretedRegExpMacroAssembler::Bind(jit::Label* label)
|
|||
}
|
||||
|
||||
void
|
||||
InterpretedRegExpMacroAssembler::CheckAtStart(jit::Label* on_at_start)
|
||||
InterpretedRegExpMacroAssembler::CheckAtStart(int cp_offset, jit::Label* on_at_start)
|
||||
{
|
||||
Emit(BC_CHECK_AT_START, 0);
|
||||
Emit(BC_CHECK_AT_START, cp_offset);
|
||||
EmitOrLink(on_at_start);
|
||||
}
|
||||
|
||||
|
|
@ -225,32 +225,37 @@ InterpretedRegExpMacroAssembler::CheckGreedyLoop(jit::Label* on_tos_equals_curre
|
|||
}
|
||||
|
||||
void
|
||||
InterpretedRegExpMacroAssembler::CheckNotAtStart(jit::Label* on_not_at_start)
|
||||
InterpretedRegExpMacroAssembler::CheckNotAtStart(int cp_offset, jit::Label* on_not_at_start)
|
||||
{
|
||||
Emit(BC_CHECK_NOT_AT_START, 0);
|
||||
Emit(BC_CHECK_NOT_AT_START, cp_offset);
|
||||
EmitOrLink(on_not_at_start);
|
||||
}
|
||||
|
||||
void
|
||||
InterpretedRegExpMacroAssembler::CheckNotBackReference(int start_reg, jit::Label* on_no_match)
|
||||
InterpretedRegExpMacroAssembler::CheckNotBackReference(int start_reg, bool read_backward,
|
||||
jit::Label* on_no_match)
|
||||
{
|
||||
MOZ_ASSERT(start_reg >= 0);
|
||||
MOZ_ASSERT(start_reg <= kMaxRegister);
|
||||
Emit(BC_CHECK_NOT_BACK_REF, start_reg);
|
||||
Emit(read_backward ? BC_CHECK_NOT_BACK_REF_BACKWARD : BC_CHECK_NOT_BACK_REF,
|
||||
start_reg);
|
||||
EmitOrLink(on_no_match);
|
||||
}
|
||||
|
||||
void
|
||||
InterpretedRegExpMacroAssembler::CheckNotBackReferenceIgnoreCase(int start_reg,
|
||||
bool read_backward,
|
||||
jit::Label* on_no_match,
|
||||
bool unicode)
|
||||
{
|
||||
MOZ_ASSERT(start_reg >= 0);
|
||||
MOZ_ASSERT(start_reg <= kMaxRegister);
|
||||
if (unicode)
|
||||
Emit(BC_CHECK_NOT_BACK_REF_NO_CASE_UNICODE, start_reg);
|
||||
Emit(read_backward ? BC_CHECK_NOT_BACK_REF_NO_CASE_BACKWARD_UNICODE : BC_CHECK_NOT_BACK_REF_NO_CASE_UNICODE,
|
||||
start_reg);
|
||||
else
|
||||
Emit(BC_CHECK_NOT_BACK_REF_NO_CASE, start_reg);
|
||||
Emit(read_backward ? BC_CHECK_NOT_BACK_REF_NO_CASE_BACKWARD : BC_CHECK_NOT_BACK_REF_NO_CASE,
|
||||
start_reg);
|
||||
EmitOrLink(on_no_match);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -96,7 +96,7 @@ class MOZ_STACK_CLASS RegExpMacroAssembler
|
|||
virtual void Backtrack() = 0;
|
||||
|
||||
virtual void Bind(jit::Label* label) = 0;
|
||||
virtual void CheckAtStart(jit::Label* on_at_start) = 0;
|
||||
virtual void CheckAtStart(int cp_offset, jit::Label* on_at_start) = 0;
|
||||
|
||||
// Dispatch after looking the current character up in a 2-bits-per-entry
|
||||
// map. The destinations vector has up to 4 labels.
|
||||
|
|
@ -109,10 +109,10 @@ class MOZ_STACK_CLASS RegExpMacroAssembler
|
|||
virtual void CheckCharacterGT(char16_t limit, jit::Label* on_greater) = 0;
|
||||
virtual void CheckCharacterLT(char16_t limit, jit::Label* on_less) = 0;
|
||||
virtual void CheckGreedyLoop(jit::Label* on_tos_equals_current_position) = 0;
|
||||
virtual void CheckNotAtStart(jit::Label* on_not_at_start) = 0;
|
||||
virtual void CheckNotBackReference(int start_reg, jit::Label* on_no_match) = 0;
|
||||
virtual void CheckNotBackReferenceIgnoreCase(int start_reg, jit::Label* on_no_match,
|
||||
bool unicode) = 0;
|
||||
virtual void CheckNotAtStart(int cp_offset, jit::Label* on_not_at_start) = 0;
|
||||
virtual void CheckNotBackReference(int start_reg, bool read_backward, jit::Label* on_no_match) = 0;
|
||||
virtual void CheckNotBackReferenceIgnoreCase(int start_reg, bool read_backward,
|
||||
jit::Label* on_no_match, bool unicode) = 0;
|
||||
|
||||
// Check the current character for a match with a literal character. If we
|
||||
// fail to match then goto the on_failure label. End of input always
|
||||
|
|
@ -238,15 +238,16 @@ class MOZ_STACK_CLASS InterpretedRegExpMacroAssembler final : public RegExpMacro
|
|||
void AdvanceRegister(int reg, int by);
|
||||
void Backtrack();
|
||||
void Bind(jit::Label* label);
|
||||
void CheckAtStart(jit::Label* on_at_start);
|
||||
void CheckAtStart(int cp_offset, jit::Label* on_at_start);
|
||||
void CheckCharacter(unsigned c, jit::Label* on_equal);
|
||||
void CheckCharacterAfterAnd(unsigned c, unsigned and_with, jit::Label* on_equal);
|
||||
void CheckCharacterGT(char16_t limit, jit::Label* on_greater);
|
||||
void CheckCharacterLT(char16_t limit, jit::Label* on_less);
|
||||
void CheckGreedyLoop(jit::Label* on_tos_equals_current_position);
|
||||
void CheckNotAtStart(jit::Label* on_not_at_start);
|
||||
void CheckNotBackReference(int start_reg, jit::Label* on_no_match);
|
||||
void CheckNotBackReferenceIgnoreCase(int start_reg, jit::Label* on_no_match, bool unicode);
|
||||
void CheckNotAtStart(int cp_offset, jit::Label* on_not_at_start);
|
||||
void CheckNotBackReference(int start_reg, bool read_backward, jit::Label* on_no_match);
|
||||
void CheckNotBackReferenceIgnoreCase(int start_reg, bool read_backward,
|
||||
jit::Label* on_no_match, bool unicode);
|
||||
void CheckNotCharacter(unsigned c, jit::Label* on_not_equal);
|
||||
void CheckNotCharacterAfterAnd(unsigned c, unsigned and_with, jit::Label* on_not_equal);
|
||||
void CheckNotCharacterAfterMinusAnd(char16_t c, char16_t minus, char16_t and_with,
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -139,6 +139,7 @@ class RegExpBuilder
|
|||
public:
|
||||
explicit RegExpBuilder(LifoAlloc* alloc);
|
||||
void AddCharacter(char16_t character);
|
||||
void AddUnicodeCharacter(widechar c, bool ignore_case);
|
||||
// "Adds" an empty expression. Does nothing except consume a
|
||||
// following quantifier
|
||||
void AddEmpty();
|
||||
|
|
@ -168,9 +169,6 @@ class RegExpBuilder
|
|||
#endif
|
||||
};
|
||||
|
||||
// Characters parsed by RegExpParser can be either char16_t or kEndMarker.
|
||||
typedef uint32_t widechar;
|
||||
|
||||
template <typename CharT>
|
||||
class RegExpParser
|
||||
{
|
||||
|
|
@ -198,24 +196,44 @@ class RegExpParser
|
|||
bool ParseHexEscape(int length, widechar* value);
|
||||
|
||||
bool ParseBracedHexEscape(widechar* value);
|
||||
bool ParseUnicodeEscape(widechar* value);
|
||||
bool ParseTrailSurrogate(widechar* value);
|
||||
bool ParseRawSurrogatePair(char16_t* lead, char16_t* trail);
|
||||
|
||||
widechar ParseOctalLiteral();
|
||||
|
||||
// Parse the "{name[=value]}" part of a property class escape.
|
||||
bool ParsePropertyClassName(std::string& name, std::string& value);
|
||||
|
||||
// Parses the name of a capture group (?<name>pattern). The name must adhere
|
||||
// to IdentifierName in the ECMAScript standard.
|
||||
const CharacterVector* ParseCaptureGroupName();
|
||||
|
||||
// Tries to parse the input as a back reference. If successful it
|
||||
// stores the result in the output parameter and returns true. If
|
||||
// it fails it will push back the characters read so the same characters
|
||||
// can be reparsed.
|
||||
bool ParseBackReferenceIndex(int* index_out);
|
||||
|
||||
bool ParseClassAtom(char16_t* char_class, widechar *value);
|
||||
// Parse a thing inside a character class. Either add escaped class to the range and return
|
||||
// the matched range as |char_class|, or return a single character as |value|
|
||||
// Unicode ranges can be null if not in Unicode mode
|
||||
bool ParseClassEscape(char16_t* char_class, widechar *value,
|
||||
CharacterRangeVector* ranges,
|
||||
CharacterRangeVector* lead_ranges,
|
||||
CharacterRangeVector* trail_ranges,
|
||||
WideCharRangeVector* wide_ranges);
|
||||
RegExpTree* ReportError(unsigned errorNumber, const char* param = nullptr);
|
||||
void Advance();
|
||||
void Advance(int dist) {
|
||||
next_pos_ += dist - 1;
|
||||
Advance();
|
||||
}
|
||||
|
||||
bool StoreNamedCaptureMap(CharacterVectorVector** names, IntegerVector** indices);
|
||||
// Returns true iff the pattern contains named captures. May call
|
||||
// ScanForCaptures to look ahead at the remaining pattern.
|
||||
bool HasNamedCaptures();
|
||||
|
||||
void Reset(const CharT* pos) {
|
||||
next_pos_ = pos;
|
||||
|
|
@ -228,7 +246,7 @@ class RegExpParser
|
|||
bool simple() { return simple_; }
|
||||
bool contains_anchor() { return contains_anchor_; }
|
||||
void set_contains_anchor() { contains_anchor_ = true; }
|
||||
int captures_started() { return captures_ == nullptr ? 0 : captures_->length(); }
|
||||
int captures_started() { return captures_started_; }
|
||||
const CharT* position() { return next_pos_ - 1; }
|
||||
|
||||
static const int kMaxCaptures = 1 << 16;
|
||||
|
|
@ -238,8 +256,8 @@ class RegExpParser
|
|||
enum SubexpressionType {
|
||||
INITIAL,
|
||||
CAPTURE, // All positive values represent captures.
|
||||
POSITIVE_LOOKAHEAD,
|
||||
NEGATIVE_LOOKAHEAD,
|
||||
POSITIVE_LOOKAROUND,
|
||||
NEGATIVE_LOOKAROUND,
|
||||
GROUPING
|
||||
};
|
||||
|
||||
|
|
@ -248,11 +266,15 @@ class RegExpParser
|
|||
RegExpParserState(LifoAlloc* alloc,
|
||||
RegExpParserState* previous_state,
|
||||
SubexpressionType group_type,
|
||||
int disjunction_capture_index)
|
||||
RegExpLookaround::Type lookaround_type,
|
||||
int disjunction_capture_index,
|
||||
const CharacterVector* capture_name)
|
||||
: previous_state_(previous_state),
|
||||
builder_(alloc->newInfallible<RegExpBuilder>(alloc)),
|
||||
group_type_(group_type),
|
||||
disjunction_capture_index_(disjunction_capture_index)
|
||||
lookaround_type_(lookaround_type),
|
||||
disjunction_capture_index_(disjunction_capture_index),
|
||||
capture_name_(capture_name)
|
||||
{}
|
||||
// Parser state of containing expression, if any.
|
||||
RegExpParserState* previous_state() { return previous_state_; }
|
||||
|
|
@ -261,10 +283,21 @@ class RegExpParser
|
|||
RegExpBuilder* builder() { return builder_; }
|
||||
// Type of regexp being parsed (parenthesized group or entire regexp).
|
||||
SubexpressionType group_type() { return group_type_; }
|
||||
// Lookahead or Lookbehind.
|
||||
RegExpLookaround::Type lookaround_type() { return lookaround_type_; }
|
||||
// Index in captures array of first capture in this sub-expression, if any.
|
||||
// Also the capture index of this sub-expression itself, if group_type
|
||||
// is CAPTURE.
|
||||
int capture_index() { return disjunction_capture_index_; }
|
||||
// The name of the current sub-expression, if group_type is CAPTURE. Only
|
||||
// used for named captures.
|
||||
const CharacterVector* capture_name() const { return capture_name_; }
|
||||
bool IsNamedCapture() const { return capture_name_ != nullptr; }
|
||||
|
||||
// Check whether the parser is inside a capture group with the given index.
|
||||
bool IsInsideCaptureGroup(int index);
|
||||
// Check whether the parser is inside a capture group with the given name.
|
||||
bool IsInsideCaptureGroup(const CharacterVector* name);
|
||||
|
||||
private:
|
||||
// Linked list implementation of stack of states.
|
||||
|
|
@ -273,10 +306,33 @@ class RegExpParser
|
|||
RegExpBuilder* builder_;
|
||||
// Stored disjunction type (capture, look-ahead or grouping), if any.
|
||||
SubexpressionType group_type_;
|
||||
// Stored read direction.
|
||||
RegExpLookaround::Type lookaround_type_;
|
||||
// Stored disjunction's capture index (if any).
|
||||
int disjunction_capture_index_;
|
||||
// Stored capture name (if any).
|
||||
const CharacterVector* const capture_name_;
|
||||
};
|
||||
|
||||
// Return the 1-indexed RegExpCapture object, allocate if necessary.
|
||||
RegExpCapture* GetCapture(int index);
|
||||
|
||||
// Creates a new named capture at the specified index. Must be called exactly
|
||||
// once for each named capture. Fails if a capture with the same name is
|
||||
// encountered.
|
||||
bool CreateNamedCaptureAtIndex(const CharacterVector* name, int index);
|
||||
|
||||
// Find a named capture group by name, or return null if not found
|
||||
RegExpCapture* FindNamedCapture(const CharacterVector* name);
|
||||
|
||||
bool ParseNamedBackReference(RegExpBuilder* builder,
|
||||
RegExpParserState* state);
|
||||
|
||||
// After the initial parsing pass, patch corresponding RegExpCapture objects
|
||||
// into all RegExpBackReferences. This is done after initial parsing in order
|
||||
// to avoid complicating cases in which references comes before the capture.
|
||||
void PatchNamedBackReferences();
|
||||
|
||||
widechar current() { return current_; }
|
||||
bool has_more() { return has_more_; }
|
||||
bool has_next() { return next_pos_ < end_; }
|
||||
|
|
@ -290,9 +346,13 @@ class RegExpParser
|
|||
frontend::TokenStream& ts;
|
||||
LifoAlloc* alloc;
|
||||
RegExpCaptureVector* captures_;
|
||||
// contains the subset of captures_ that have names (for duplicate checking)
|
||||
RegExpCaptureVector* named_captures_;
|
||||
RegExpBackReferenceVector* named_back_references_;
|
||||
const CharT* next_pos_;
|
||||
const CharT* end_;
|
||||
widechar current_;
|
||||
int captures_started_;
|
||||
// The capture count is only valid after we have scanned for captures.
|
||||
int capture_count_;
|
||||
bool has_more_;
|
||||
|
|
@ -303,6 +363,7 @@ class RegExpParser
|
|||
bool simple_;
|
||||
bool contains_anchor_;
|
||||
bool is_scanned_for_captures_;
|
||||
bool has_named_captures_; // Only valid after we have scanned for captures.
|
||||
};
|
||||
|
||||
} } // namespace js::irregexp
|
||||
|
|
|
|||
|
|
@ -1513,6 +1513,16 @@ JitCompartment::generateRegExpMatcherStub(JSContext* cx)
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
// If a regexp has named captures, fall back to the OOL stub, which
|
||||
// will end up calling CreateRegExpMatchResults.
|
||||
Register shared = temp2;
|
||||
masm.loadPtr(Address(regexp, NativeObject::getFixedSlotOffset(RegExpObject::PRIVATE_SLOT)),
|
||||
shared);
|
||||
masm.branchPtr(Assembler::NotEqual,
|
||||
Address(shared, RegExpShared::offsetOfGroupsTemplate()),
|
||||
ImmWord(0),
|
||||
&oolEntry);
|
||||
|
||||
// Construct the result.
|
||||
Register object = temp1;
|
||||
Label matchResultFallback, matchResultJoin;
|
||||
|
|
@ -1523,6 +1533,7 @@ JitCompartment::generateRegExpMatcherStub(JSContext* cx)
|
|||
masm.loadPtr(Address(object, NativeObject::offsetOfSlots()), temp2);
|
||||
masm.storeValue(templateObject->getSlot(0), Address(temp2, 0));
|
||||
masm.storeValue(templateObject->getSlot(1), Address(temp2, sizeof(Value)));
|
||||
masm.storeValue(templateObject->getSlot(2), Address(temp2, 2 * sizeof(Value)));
|
||||
|
||||
size_t elementsOffset = NativeObject::offsetOfFixedElements();
|
||||
|
||||
|
|
@ -1636,6 +1647,7 @@ JitCompartment::generateRegExpMatcherStub(JSContext* cx)
|
|||
MOZ_ASSERT(templateObject->numFixedSlots() == 0);
|
||||
MOZ_ASSERT(templateObject->lookupPure(cx->names().index)->slot() == 0);
|
||||
MOZ_ASSERT(templateObject->lookupPure(cx->names().input)->slot() == 1);
|
||||
MOZ_ASSERT(templateObject->lookupPure(cx->names().groups)->slot() == 2);
|
||||
|
||||
masm.load32(pairsVectorAddress, temp3);
|
||||
masm.storeValue(JSVAL_TYPE_INT32, temp3, Address(temp2, 0));
|
||||
|
|
|
|||
|
|
@ -513,6 +513,12 @@ MSG_DEF(JSMSG_TOO_MANY_PARENS, 0, JSEXN_INTERNALERR, "too many parenthes
|
|||
MSG_DEF(JSMSG_UNICODE_OVERFLOW, 1, JSEXN_SYNTAXERR, "Unicode codepoint must not be greater than 0x10FFFF in {0}")
|
||||
MSG_DEF(JSMSG_UNMATCHED_RIGHT_PAREN, 0, JSEXN_SYNTAXERR, "unmatched ) in regular expression")
|
||||
MSG_DEF(JSMSG_UNTERM_CLASS, 0, JSEXN_SYNTAXERR, "unterminated character class")
|
||||
MSG_DEF(JSMSG_INVALID_PROPERTY_NAME, 0, JSEXN_SYNTAXERR, "invalid property name in regular expression")
|
||||
MSG_DEF(JSMSG_INVALID_CLASS_PROPERTY_NAME, 0, JSEXN_SYNTAXERR, "invalid class property name in regular expression")
|
||||
MSG_DEF(JSMSG_INVALID_CAPTURE_NAME, 0, JSEXN_SYNTAXERR, "invalid capture group name in regular expression")
|
||||
MSG_DEF(JSMSG_DUPLICATE_CAPTURE_NAME, 0, JSEXN_SYNTAXERR, "duplicate capture group name in regular expression")
|
||||
MSG_DEF(JSMSG_INVALID_NAMED_REF, 0, JSEXN_SYNTAXERR, "invalid named reference in regular expression")
|
||||
MSG_DEF(JSMSG_INVALID_NAMED_CAPTURE_REF, 0, JSEXN_SYNTAXERR, "invalid named capture reference in regular expression")
|
||||
|
||||
// Self-hosting
|
||||
MSG_DEF(JSMSG_DEFAULT_LOCALE_ERROR, 0, JSEXN_ERR, "internal error getting the default locale")
|
||||
|
|
|
|||
|
|
@ -153,6 +153,7 @@ UNIFIED_SOURCES += [
|
|||
'irregexp/NativeRegExpMacroAssembler.cpp',
|
||||
'irregexp/RegExpAST.cpp',
|
||||
'irregexp/RegExpCharacters.cpp',
|
||||
'irregexp/RegExpCharRanges.cpp',
|
||||
'irregexp/RegExpEngine.cpp',
|
||||
'irregexp/RegExpInterpreter.cpp',
|
||||
'irregexp/RegExpMacroAssembler.cpp',
|
||||
|
|
|
|||
|
|
@ -162,6 +162,7 @@
|
|||
macro(global, global, "global") \
|
||||
macro(globalThis, globalThis, "globalThis") \
|
||||
macro(group, group, "group") \
|
||||
macro(groups, groups, "groups") \
|
||||
macro(Handle, Handle, "Handle") \
|
||||
macro(has, has, "has") \
|
||||
macro(hasOwn, hasOwn, "hasOwn") \
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
|
||||
#include "builtin/RegExp.h"
|
||||
#include "frontend/TokenStream.h"
|
||||
#include "irregexp/FeatureFlags.h"
|
||||
#ifdef DEBUG
|
||||
#include "irregexp/RegExpBytecode.h"
|
||||
#endif
|
||||
|
|
@ -109,7 +110,7 @@ ScopedMatchPairs::allocOrExpandArray(size_t pairCount)
|
|||
bool
|
||||
VectorMatchPairs::allocOrExpandArray(size_t pairCount)
|
||||
{
|
||||
if (!vec_.resizeUninitialized(sizeof(MatchPair) * pairCount))
|
||||
if (!vec_.resizeUninitialized(pairCount))
|
||||
return false;
|
||||
|
||||
pairs_ = &vec_[0];
|
||||
|
|
@ -950,7 +951,8 @@ js::StringHasRegExpMetaChars(JSLinearString* str)
|
|||
/* RegExpShared */
|
||||
|
||||
RegExpShared::RegExpShared(JSAtom* source, RegExpFlag flags)
|
||||
: source(source), flags(flags), parenCount(0), canStringMatch(false), marked_(false)
|
||||
: source(source), flags(flags), parenCount(0), canStringMatch(false), marked_(false),
|
||||
numNamedCaptures_(0), groupsTemplate_(nullptr)
|
||||
{}
|
||||
|
||||
RegExpShared::~RegExpShared()
|
||||
|
|
@ -1004,6 +1006,56 @@ RegExpShared::compile(JSContext* cx, HandleLinearString input,
|
|||
return compile(cx, pattern, input, mode, force);
|
||||
}
|
||||
|
||||
bool
|
||||
RegExpShared::initializeNamedCaptures(JSContext* cx, irregexp::CharacterVectorVector* names, irregexp::IntegerVector* indices)
|
||||
{
|
||||
MOZ_ASSERT(!groupsTemplate_);
|
||||
MOZ_ASSERT(names);
|
||||
MOZ_ASSERT(indices);
|
||||
MOZ_ASSERT(names->length() == indices->length());
|
||||
|
||||
// The irregexp parser returns named capture information in the form
|
||||
// of two arrays. We create a template object with a property for each
|
||||
// capture name, and store the capture index as Integer in the corresponding value.
|
||||
uint32_t numNamedCaptures = names->length();
|
||||
|
||||
// Create a plain template object.
|
||||
RootedPlainObject templateObject(cx, NewObjectWithGivenProto<PlainObject>(cx, nullptr, TenuredObject));
|
||||
if (!templateObject) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create a new group for the template.
|
||||
Rooted<TaggedProto> proto(cx, templateObject->taggedProto());
|
||||
ObjectGroup* group = ObjectGroupCompartment::makeGroup(cx, templateObject->getClass(), proto);
|
||||
if (!group) {
|
||||
return false;
|
||||
}
|
||||
templateObject->setGroup(group);
|
||||
|
||||
// Initialize the properties of the template.
|
||||
RootedId id(cx);
|
||||
for (uint32_t i = 0; i < numNamedCaptures; i++) {
|
||||
irregexp::CharacterVector* cv = (*names)[i];
|
||||
// Need to explicitly create an Atom (not a String) or it won't get added to the atom table
|
||||
JSAtom* atom = AtomizeChars(cx, cv->begin(), cv->length());
|
||||
if (!atom) {
|
||||
return false;
|
||||
}
|
||||
id = NameToId(atom->asPropertyName());
|
||||
RootedValue idx(cx, Int32Value((*indices)[i]));
|
||||
if (!NativeDefineProperty(cx, templateObject, id, idx,
|
||||
nullptr, nullptr, JSPROP_ENUMERATE)) {
|
||||
return false;
|
||||
}
|
||||
AddTypePropertyId(cx, templateObject, id, TypeSet::Int32Type());
|
||||
}
|
||||
|
||||
groupsTemplate_ = templateObject;
|
||||
numNamedCaptures_ = numNamedCaptures;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
RegExpShared::compile(JSContext* cx, HandleAtom pattern, HandleLinearString input,
|
||||
CompilationMode mode, ForceByteCodeEnum force)
|
||||
|
|
@ -1026,6 +1078,12 @@ RegExpShared::compile(JSContext* cx, HandleAtom pattern, HandleLinearString inpu
|
|||
}
|
||||
|
||||
this->parenCount = data.capture_count;
|
||||
if (data.capture_name_list) {
|
||||
// convert LifoAlloc'd named capture info to NativeObject
|
||||
if (!initializeNamedCaptures(cx, data.capture_name_list, data.capture_index_list)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
irregexp::RegExpCode code = irregexp::CompilePattern(cx, this, &data, input,
|
||||
false /* global() */,
|
||||
|
|
@ -1259,17 +1317,27 @@ RegExpCompartment::createMatchResultTemplateObject(JSContext* cx)
|
|||
return matchResultTemplateObject_; // = nullptr
|
||||
}
|
||||
|
||||
/* Set dummy groups property */
|
||||
RootedValue groupsVal(cx, UndefinedValue());
|
||||
if (!NativeDefineProperty(
|
||||
cx, templateObject, cx->names().groups, groupsVal, nullptr, nullptr, JSPROP_ENUMERATE)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Make sure that the properties are in the right slots.
|
||||
DebugOnly<Shape*> shape = templateObject->lastProperty();
|
||||
MOZ_ASSERT(shape->previous()->slot() == 0 &&
|
||||
shape->previous()->propidRef() == NameToId(cx->names().index));
|
||||
MOZ_ASSERT(shape->slot() == 1 &&
|
||||
shape->propidRef() == NameToId(cx->names().input));
|
||||
MOZ_ASSERT(shape->slot() == 2 &&
|
||||
shape->propidRef() == NameToId(cx->names().groups));
|
||||
MOZ_ASSERT(shape->previous()->slot() == 1 &&
|
||||
shape->previous()->propidRef() == NameToId(cx->names().input));
|
||||
MOZ_ASSERT(shape->previous()->previous()->slot() == 0 &&
|
||||
shape->previous()->previous()->propidRef() == NameToId(cx->names().index));
|
||||
|
||||
// Make sure type information reflects the indexed properties which might
|
||||
// be added.
|
||||
AddTypePropertyId(cx, templateObject, JSID_VOID, TypeSet::StringType());
|
||||
AddTypePropertyId(cx, templateObject, JSID_VOID, TypeSet::UndefinedType());
|
||||
AddTypePropertyId(cx, templateObject, NameToId(cx->names().groups), TypeSet::AnyObjectType());
|
||||
|
||||
matchResultTemplateObject_.set(templateObject);
|
||||
|
||||
|
|
@ -1484,6 +1552,13 @@ ParseRegExpFlags(const CharT* chars, size_t length, RegExpFlag* flagsOut, char16
|
|||
if (!HandleRegExpFlag(UnicodeFlag, flagsOut))
|
||||
return false;
|
||||
break;
|
||||
case 'v':
|
||||
if (irregexp::kParseFlagUnicodeSetsAsUnicode) {
|
||||
if (!HandleRegExpFlag(UnicodeFlag, flagsOut))
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
MOZ_FALLTHROUGH
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
#include "proxy/Proxy.h"
|
||||
#include "vm/ArrayObject.h"
|
||||
#include "vm/Shape.h"
|
||||
#include "irregexp/InfallibleVector.h"
|
||||
|
||||
/*
|
||||
* JavaScript Regular Expressions
|
||||
|
|
@ -133,6 +134,9 @@ class RegExpShared
|
|||
bool canStringMatch;
|
||||
bool marked_;
|
||||
|
||||
uint32_t numNamedCaptures_;
|
||||
GCPtr<PlainObject*> groupsTemplate_;
|
||||
|
||||
RegExpCompilation compilationArray[4];
|
||||
|
||||
static int CompilationIndex(CompilationMode mode, bool latin1) {
|
||||
|
|
@ -187,6 +191,11 @@ class RegExpShared
|
|||
/* Accounts for the "0" (whole match) pair. */
|
||||
size_t pairCount() const { return getParenCount() + 1; }
|
||||
|
||||
// not public due to circular inclusion problems
|
||||
bool initializeNamedCaptures(JSContext* cx, irregexp::CharacterVectorVector* names, irregexp::IntegerVector* indices);
|
||||
PlainObject* getGroupsTemplate() { return groupsTemplate_; }
|
||||
uint32_t numNamedCaptures() const { return numNamedCaptures_; }
|
||||
|
||||
JSAtom* getSource() const { return source; }
|
||||
RegExpFlag getFlags() const { return flags; }
|
||||
bool ignoreCase() const { return flags & IgnoreCaseFlag; }
|
||||
|
|
@ -238,6 +247,10 @@ class RegExpShared
|
|||
+ offsetof(RegExpCompilation, jitCode);
|
||||
}
|
||||
|
||||
static size_t offsetOfGroupsTemplate() {
|
||||
return offsetof(RegExpShared, groupsTemplate_);
|
||||
}
|
||||
|
||||
size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf);
|
||||
|
||||
#ifdef DEBUG
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue