mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-10-21 22:57:31 +09:00
No issue - Refactor CharacterRange + Unicode handling into module for maintainability
Collects code from RegExpParser and RegExpEngine (regexp-compiler-tonode.cc) Simplify parsing AtomEscape/CharacterClassEscape
This commit is contained in:
parent
6eead2deb4
commit
d2e0e199b7
7 changed files with 936 additions and 792 deletions
623
js/src/irregexp/RegExpCharRanges.cpp
Normal file
623
js/src/irregexp/RegExpCharRanges.cpp
Normal file
|
|
@ -0,0 +1,623 @@
|
||||||
|
/* -*- 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.
|
||||||
|
|
||||||
|
#include "irregexp/RegExpCharRanges.h"
|
||||||
|
|
||||||
|
// Generated table
|
||||||
|
#include "irregexp/RegExpCharacters-inl.h"
|
||||||
|
|
||||||
|
using namespace js::irregexp;
|
||||||
|
|
||||||
|
using mozilla::ArrayLength;
|
||||||
|
|
||||||
|
void
|
||||||
|
CharacterRange::AddCaseEquivalents(bool is_ascii, bool unicode, CharacterRangeVector* ranges)
|
||||||
|
{
|
||||||
|
char16_t bottom = from();
|
||||||
|
char16_t top = to();
|
||||||
|
|
||||||
|
if (is_ascii && !RangeContainsLatin1Equivalents(*this, unicode)) {
|
||||||
|
if (bottom > kMaxOneByteCharCode)
|
||||||
|
return;
|
||||||
|
if (top > kMaxOneByteCharCode)
|
||||||
|
top = kMaxOneByteCharCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (char16_t c = bottom;; c++) {
|
||||||
|
char16_t chars[kEcma262UnCanonicalizeMaxWidth];
|
||||||
|
size_t length = GetCaseIndependentLetters(c, is_ascii, unicode, chars);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < length; i++) {
|
||||||
|
char16_t other = chars[i];
|
||||||
|
if (other == c)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Try to combine with an existing range.
|
||||||
|
bool found = false;
|
||||||
|
for (size_t i = 0; i < ranges->length(); i++) {
|
||||||
|
CharacterRange& range = (*ranges)[i];
|
||||||
|
if (range.Contains(other)) {
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
} else if (other == range.from() - 1) {
|
||||||
|
range.set_from(other);
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
} else if (other == range.to() + 1) {
|
||||||
|
range.set_to(other);
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found)
|
||||||
|
ranges->append(CharacterRange::Singleton(other));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c == top)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* static */
|
||||||
|
void
|
||||||
|
CharacterRange::AddClass(const int* elmv, int elmc, CharacterRangeVector* ranges)
|
||||||
|
{
|
||||||
|
elmc--;
|
||||||
|
MOZ_ASSERT(elmv[elmc] == 0x10000);
|
||||||
|
for (int i = 0; i < elmc; i += 2) {
|
||||||
|
MOZ_ASSERT(elmv[i] < elmv[i + 1]);
|
||||||
|
ranges->append(CharacterRange(elmv[i], elmv[i + 1] - 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* static */ void
|
||||||
|
CharacterRange::AddClassNegated(const int* elmv, int elmc, CharacterRangeVector* ranges)
|
||||||
|
{
|
||||||
|
elmc--;
|
||||||
|
MOZ_ASSERT(elmv[elmc] == 0x10000);
|
||||||
|
MOZ_ASSERT(elmv[0] != 0x0000);
|
||||||
|
MOZ_ASSERT(elmv[elmc-1] != kMaxUtf16CodeUnit);
|
||||||
|
char16_t last = 0x0000;
|
||||||
|
for (int i = 0; i < elmc; i += 2) {
|
||||||
|
MOZ_ASSERT(last <= elmv[i] - 1);
|
||||||
|
MOZ_ASSERT(elmv[i] < elmv[i + 1]);
|
||||||
|
ranges->append(CharacterRange(last, elmv[i] - 1));
|
||||||
|
last = elmv[i + 1];
|
||||||
|
}
|
||||||
|
ranges->append(CharacterRange(last, kMaxUtf16CodeUnit));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* static */ void
|
||||||
|
CharacterRange::AddClassEscape(LifoAlloc* alloc, char16_t type,
|
||||||
|
CharacterRangeVector* ranges)
|
||||||
|
{
|
||||||
|
switch (type) {
|
||||||
|
case 's':
|
||||||
|
AddClass(kSpaceRanges, kSpaceRangeCount, ranges);
|
||||||
|
break;
|
||||||
|
case 'S':
|
||||||
|
AddClassNegated(kSpaceRanges, kSpaceRangeCount, ranges);
|
||||||
|
break;
|
||||||
|
case 'w':
|
||||||
|
AddClass(kWordRanges, kWordRangeCount, ranges);
|
||||||
|
break;
|
||||||
|
case 'W':
|
||||||
|
AddClassNegated(kWordRanges, kWordRangeCount, ranges);
|
||||||
|
break;
|
||||||
|
case 'd':
|
||||||
|
AddClass(kDigitRanges, kDigitRangeCount, ranges);
|
||||||
|
break;
|
||||||
|
case 'D':
|
||||||
|
AddClassNegated(kDigitRanges, kDigitRangeCount, ranges);
|
||||||
|
break;
|
||||||
|
case '.':
|
||||||
|
AddClassNegated(kLineTerminatorRanges, kLineTerminatorRangeCount, ranges);
|
||||||
|
break;
|
||||||
|
// This is not a character range as defined by the spec but a
|
||||||
|
// convenient shorthand for a character class that matches any
|
||||||
|
// character.
|
||||||
|
case '*':
|
||||||
|
ranges->append(CharacterRange::Everything());
|
||||||
|
break;
|
||||||
|
// This is the set of characters matched by the $ and ^ symbols
|
||||||
|
// in multiline mode.
|
||||||
|
case 'n':
|
||||||
|
AddClass(kLineTerminatorRanges, kLineTerminatorRangeCount, ranges);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
MOZ_CRASH("Bad character class escape");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add class escape, excluding surrogate pair range.
|
||||||
|
/* static */ void
|
||||||
|
CharacterRange::AddClassEscapeUnicode(LifoAlloc* alloc, char16_t type,
|
||||||
|
CharacterRangeVector* ranges, bool ignore_case)
|
||||||
|
{
|
||||||
|
switch (type) {
|
||||||
|
case 's':
|
||||||
|
case 'd':
|
||||||
|
return AddClassEscape(alloc, type, ranges);
|
||||||
|
break;
|
||||||
|
case 'S':
|
||||||
|
AddClassNegated(kSpaceAndSurrogateRanges, kSpaceAndSurrogateRangeCount, ranges);
|
||||||
|
break;
|
||||||
|
case 'w':
|
||||||
|
if (ignore_case)
|
||||||
|
AddClass(kIgnoreCaseWordRanges, kIgnoreCaseWordRangeCount, ranges);
|
||||||
|
else
|
||||||
|
AddClassEscape(alloc, type, ranges);
|
||||||
|
break;
|
||||||
|
case 'W':
|
||||||
|
if (ignore_case) {
|
||||||
|
AddClass(kNegatedIgnoreCaseWordAndSurrogateRanges,
|
||||||
|
kNegatedIgnoreCaseWordAndSurrogateRangeCount, ranges);
|
||||||
|
} else {
|
||||||
|
AddClassNegated(kWordAndSurrogateRanges, kWordAndSurrogateRangeCount, ranges);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'D':
|
||||||
|
AddClassNegated(kDigitAndSurrogateRanges, kDigitAndSurrogateRangeCount, ranges);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
MOZ_CRASH("Bad type!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* static */ void
|
||||||
|
CharacterRange::AddCharOrEscape(LifoAlloc* alloc, CharacterRangeVector* ranges,
|
||||||
|
char16_t char_class, widechar c)
|
||||||
|
{
|
||||||
|
if (char_class != kNoCharClass)
|
||||||
|
AddClassEscape(alloc, char_class, ranges);
|
||||||
|
else
|
||||||
|
ranges->append(CharacterRange::Singleton(c));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* static */ void
|
||||||
|
CharacterRange::AddCharOrEscapeUnicode(LifoAlloc* alloc,
|
||||||
|
CharacterRangeVector* ranges,
|
||||||
|
CharacterRangeVector* lead_ranges,
|
||||||
|
CharacterRangeVector* trail_ranges,
|
||||||
|
WideCharRangeVector* wide_ranges,
|
||||||
|
char16_t char_class,
|
||||||
|
widechar c,
|
||||||
|
bool ignore_case)
|
||||||
|
{
|
||||||
|
if (char_class != kNoCharClass) {
|
||||||
|
AddClassEscapeUnicode(alloc, char_class, ranges, ignore_case);
|
||||||
|
switch (char_class) {
|
||||||
|
case 'S':
|
||||||
|
case 'W':
|
||||||
|
case 'D':
|
||||||
|
lead_ranges->append(CharacterRange::LeadSurrogate());
|
||||||
|
trail_ranges->append(CharacterRange::TrailSurrogate());
|
||||||
|
wide_ranges->append(WideCharRange::NonBMP());
|
||||||
|
break;
|
||||||
|
case '.':
|
||||||
|
MOZ_CRASH("Bad char_class!");
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (unicode::IsLeadSurrogate(c))
|
||||||
|
lead_ranges->append(CharacterRange::Singleton(c));
|
||||||
|
else if (unicode::IsTrailSurrogate(c))
|
||||||
|
trail_ranges->append(CharacterRange::Singleton(c));
|
||||||
|
else if (c >= unicode::NonBMPMin)
|
||||||
|
wide_ranges->append(WideCharRange::Singleton(c));
|
||||||
|
else
|
||||||
|
ranges->append(CharacterRange::Singleton(c));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* static */ void
|
||||||
|
CharacterRange::AddCharUnicode(LifoAlloc* alloc,
|
||||||
|
CharacterRangeVector* ranges,
|
||||||
|
CharacterRangeVector* lead_ranges,
|
||||||
|
CharacterRangeVector* trail_ranges,
|
||||||
|
WideCharRangeVector* wide_ranges,
|
||||||
|
widechar c)
|
||||||
|
{
|
||||||
|
if (unicode::IsLeadSurrogate(c))
|
||||||
|
lead_ranges->append(CharacterRange::Singleton(c));
|
||||||
|
else if (unicode::IsTrailSurrogate(c))
|
||||||
|
trail_ranges->append(CharacterRange::Singleton(c));
|
||||||
|
else if (c >= unicode::NonBMPMin)
|
||||||
|
wide_ranges->append(WideCharRange::Singleton(c));
|
||||||
|
else
|
||||||
|
ranges->append(CharacterRange::Singleton(c));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* static */ void
|
||||||
|
CharacterRange::AddUnicodeRange(LifoAlloc* alloc,
|
||||||
|
CharacterRangeVector* ranges,
|
||||||
|
CharacterRangeVector* lead_ranges,
|
||||||
|
CharacterRangeVector* trail_ranges,
|
||||||
|
WideCharRangeVector* wide_ranges,
|
||||||
|
widechar first,
|
||||||
|
widechar next)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(first <= next);
|
||||||
|
if (first < unicode::LeadSurrogateMin) {
|
||||||
|
if (next < unicode::LeadSurrogateMin) {
|
||||||
|
ranges->append(CharacterRange::Range(first, next));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ranges->append(CharacterRange::Range(first, unicode::LeadSurrogateMin - 1));
|
||||||
|
first = unicode::LeadSurrogateMin;
|
||||||
|
}
|
||||||
|
if (first <= unicode::LeadSurrogateMax) {
|
||||||
|
if (next <= unicode::LeadSurrogateMax) {
|
||||||
|
lead_ranges->append(CharacterRange::Range(first, next));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
lead_ranges->append(CharacterRange::Range(first, unicode::LeadSurrogateMax));
|
||||||
|
first = unicode::LeadSurrogateMax + 1;
|
||||||
|
}
|
||||||
|
MOZ_ASSERT(unicode::LeadSurrogateMax + 1 == unicode::TrailSurrogateMin);
|
||||||
|
if (first <= unicode::TrailSurrogateMax) {
|
||||||
|
if (next <= unicode::TrailSurrogateMax) {
|
||||||
|
trail_ranges->append(CharacterRange::Range(first, next));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
trail_ranges->append(CharacterRange::Range(first, unicode::TrailSurrogateMax));
|
||||||
|
first = unicode::TrailSurrogateMax + 1;
|
||||||
|
}
|
||||||
|
if (first <= unicode::UTF16Max) {
|
||||||
|
if (next <= unicode::UTF16Max) {
|
||||||
|
ranges->append(CharacterRange::Range(first, next));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ranges->append(CharacterRange::Range(first, unicode::UTF16Max));
|
||||||
|
first = unicode::NonBMPMin;
|
||||||
|
}
|
||||||
|
MOZ_ASSERT(unicode::UTF16Max + 1 == unicode::NonBMPMin);
|
||||||
|
wide_ranges->append(WideCharRange::Range(first, next));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* static */ bool
|
||||||
|
CharacterRange::RangesContainLatin1Equivalents(const CharacterRangeVector& ranges, bool unicode)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < ranges.length(); i++) {
|
||||||
|
// TODO(dcarney): this could be a lot more efficient.
|
||||||
|
if (RangeContainsLatin1Equivalents(ranges[i], unicode))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* static */ bool
|
||||||
|
CharacterRange::CompareRanges(const CharacterRangeVector& ranges, const int* special_class, size_t length)
|
||||||
|
{
|
||||||
|
length--; // Remove final 0x10000.
|
||||||
|
MOZ_ASSERT(special_class[length] == 0x10000);
|
||||||
|
if (ranges.length() * 2 != length)
|
||||||
|
return false;
|
||||||
|
for (size_t i = 0; i < length; i += 2) {
|
||||||
|
CharacterRange range = ranges[i >> 1];
|
||||||
|
if (range.from() != special_class[i] || range.to() != special_class[i + 1] - 1)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* static */ bool
|
||||||
|
CharacterRange::CompareInverseRanges(const CharacterRangeVector& ranges, const int* special_class, size_t length)
|
||||||
|
{
|
||||||
|
length--; // Remove final 0x10000.
|
||||||
|
MOZ_ASSERT(special_class[length] == 0x10000);
|
||||||
|
MOZ_ASSERT(ranges.length() != 0);
|
||||||
|
MOZ_ASSERT(length != 0);
|
||||||
|
MOZ_ASSERT(special_class[0] != 0);
|
||||||
|
if (ranges.length() != (length >> 1) + 1)
|
||||||
|
return false;
|
||||||
|
CharacterRange range = ranges[0];
|
||||||
|
if (range.from() != 0)
|
||||||
|
return false;
|
||||||
|
for (size_t i = 0; i < length; i += 2) {
|
||||||
|
if (special_class[i] != (range.to() + 1))
|
||||||
|
return false;
|
||||||
|
range = ranges[(i >> 1) + 1];
|
||||||
|
if (special_class[i+1] != range.from())
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (range.to() != 0xffff)
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename RangeType>
|
||||||
|
/* static */ void
|
||||||
|
CharacterRange::NegateUnicodeRanges(LifoAlloc* alloc, InfallibleVector<RangeType, 1>** ranges,
|
||||||
|
RangeType full_range)
|
||||||
|
{
|
||||||
|
typedef InfallibleVector<RangeType, 1> RangeVector;
|
||||||
|
RangeVector* tmp_ranges = alloc->newInfallible<RangeVector>(*alloc);
|
||||||
|
tmp_ranges->append(full_range);
|
||||||
|
RangeVector* result_ranges = alloc->newInfallible<RangeVector>(*alloc);
|
||||||
|
|
||||||
|
// Perform the following calculation:
|
||||||
|
// result_ranges = tmp_ranges - ranges
|
||||||
|
// with the following steps:
|
||||||
|
// result_ranges = tmp_ranges - ranges[0]
|
||||||
|
// SWAP(result_ranges, tmp_ranges)
|
||||||
|
// result_ranges = tmp_ranges - ranges[1]
|
||||||
|
// SWAP(result_ranges, tmp_ranges)
|
||||||
|
// ...
|
||||||
|
// result_ranges = tmp_ranges - ranges[N-1]
|
||||||
|
// SWAP(result_ranges, tmp_ranges)
|
||||||
|
// The last SWAP is just for simplicity of the loop.
|
||||||
|
for (size_t i = 0; i < (*ranges)->length(); i++) {
|
||||||
|
result_ranges->clear();
|
||||||
|
|
||||||
|
const RangeType& range = (**ranges)[i];
|
||||||
|
for (size_t j = 0; j < tmp_ranges->length(); j++) {
|
||||||
|
const RangeType& tmpRange = (*tmp_ranges)[j];
|
||||||
|
auto from1 = tmpRange.from();
|
||||||
|
auto to1 = tmpRange.to();
|
||||||
|
auto from2 = range.from();
|
||||||
|
auto to2 = range.to();
|
||||||
|
|
||||||
|
if (from1 < from2) {
|
||||||
|
if (to1 < from2) {
|
||||||
|
result_ranges->append(tmpRange);
|
||||||
|
} else if (to1 <= to2) {
|
||||||
|
result_ranges->append(RangeType::Range(from1, from2 - 1));
|
||||||
|
} else {
|
||||||
|
result_ranges->append(RangeType::Range(from1, from2 - 1));
|
||||||
|
result_ranges->append(RangeType::Range(to2 + 1, to1));
|
||||||
|
}
|
||||||
|
} else if (from1 <= to2) {
|
||||||
|
if (to1 > to2)
|
||||||
|
result_ranges->append(RangeType::Range(to2 + 1, to1));
|
||||||
|
} else {
|
||||||
|
result_ranges->append(tmpRange);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto tmp = tmp_ranges;
|
||||||
|
tmp_ranges = result_ranges;
|
||||||
|
result_ranges = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
// After the loop, result is pointed at by tmp_ranges, instead of
|
||||||
|
// result_ranges.
|
||||||
|
*ranges = tmp_ranges;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Explicit specialization for NegateUnicodeRanges
|
||||||
|
template void CharacterRange::NegateUnicodeRanges<CharacterRange>(LifoAlloc* alloc, InfallibleVector<CharacterRange, 1>** ranges, CharacterRange full_range);
|
||||||
|
template void CharacterRange::NegateUnicodeRanges<WideCharRange>(LifoAlloc* alloc, InfallibleVector<WideCharRange, 1>** ranges, WideCharRange full_range);
|
||||||
|
|
||||||
|
/* static */ bool
|
||||||
|
CharacterRange::IsCanonical(const CharacterRangeVector& ranges)
|
||||||
|
{
|
||||||
|
int n = ranges.length();
|
||||||
|
if (n <= 1)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
int max = ranges[0].to();
|
||||||
|
for (int i = 1; i < n; i++) {
|
||||||
|
CharacterRange next_range = ranges[i];
|
||||||
|
if (next_range.from() <= max + 1)
|
||||||
|
return false;
|
||||||
|
max = next_range.to();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* static */ void
|
||||||
|
CharacterRange::Canonicalize(CharacterRangeVector& character_ranges)
|
||||||
|
{
|
||||||
|
if (character_ranges.length() <= 1) return;
|
||||||
|
// Check whether ranges are already canonical (increasing, non-overlapping,
|
||||||
|
// non-adjacent).
|
||||||
|
int n = character_ranges.length();
|
||||||
|
int max = character_ranges[0].to();
|
||||||
|
int i = 1;
|
||||||
|
while (i < n) {
|
||||||
|
CharacterRange current = character_ranges[i];
|
||||||
|
if (current.from() <= max + 1) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
max = current.to();
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
// Canonical until the i'th range. If that's all of them, we are done.
|
||||||
|
if (i == n) return;
|
||||||
|
|
||||||
|
// The ranges at index i and forward are not canonicalized. Make them so by
|
||||||
|
// doing the equivalent of insertion sort (inserting each into the previous
|
||||||
|
// list, in order).
|
||||||
|
// Notice that inserting a range can reduce the number of ranges in the
|
||||||
|
// result due to combining of adjacent and overlapping ranges.
|
||||||
|
int read = i; // Range to insert.
|
||||||
|
size_t num_canonical = i; // Length of canonicalized part of list.
|
||||||
|
do {
|
||||||
|
num_canonical = InsertRangeInCanonicalList(character_ranges,
|
||||||
|
num_canonical,
|
||||||
|
character_ranges[read]);
|
||||||
|
read++;
|
||||||
|
} while (read < n);
|
||||||
|
|
||||||
|
while (character_ranges.length() > num_canonical)
|
||||||
|
character_ranges.popBack();
|
||||||
|
|
||||||
|
MOZ_ASSERT(IsCanonical(character_ranges));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* static */ int
|
||||||
|
CharacterRange::InsertRangeInCanonicalList(CharacterRangeVector& list,
|
||||||
|
int count,
|
||||||
|
CharacterRange insert)
|
||||||
|
{
|
||||||
|
// Inserts a range into list[0..count[, which must be sorted
|
||||||
|
// by from value and non-overlapping and non-adjacent, using at most
|
||||||
|
// list[0..count] for the result. Returns the number of resulting
|
||||||
|
// canonicalized ranges. Inserting a range may collapse existing ranges into
|
||||||
|
// fewer ranges, so the return value can be anything in the range 1..count+1.
|
||||||
|
char16_t from = insert.from();
|
||||||
|
char16_t to = insert.to();
|
||||||
|
int start_pos = 0;
|
||||||
|
int end_pos = count;
|
||||||
|
for (int i = count - 1; i >= 0; i--) {
|
||||||
|
CharacterRange current = list[i];
|
||||||
|
if (current.from() > to + 1) {
|
||||||
|
end_pos = i;
|
||||||
|
} else if (current.to() + 1 < from) {
|
||||||
|
start_pos = i + 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inserted range overlaps, or is adjacent to, ranges at positions
|
||||||
|
// [start_pos..end_pos[. Ranges before start_pos or at or after end_pos are
|
||||||
|
// not affected by the insertion.
|
||||||
|
// If start_pos == end_pos, the range must be inserted before start_pos.
|
||||||
|
// if start_pos < end_pos, the entire range from start_pos to end_pos
|
||||||
|
// must be merged with the insert range.
|
||||||
|
|
||||||
|
if (start_pos == end_pos) {
|
||||||
|
// Insert between existing ranges at position start_pos.
|
||||||
|
if (start_pos < count) {
|
||||||
|
list.moveReplace(start_pos, start_pos + 1, count - start_pos);
|
||||||
|
}
|
||||||
|
list[start_pos] = insert;
|
||||||
|
return count + 1;
|
||||||
|
}
|
||||||
|
if (start_pos + 1 == end_pos) {
|
||||||
|
// Replace single existing range at position start_pos.
|
||||||
|
CharacterRange to_replace = list[start_pos];
|
||||||
|
int new_from = Min(to_replace.from(), from);
|
||||||
|
int new_to = Max(to_replace.to(), to);
|
||||||
|
list[start_pos] = CharacterRange(new_from, new_to);
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
// Replace a number of existing ranges from start_pos to end_pos - 1.
|
||||||
|
// Move the remaining ranges down.
|
||||||
|
|
||||||
|
int new_from = Min(list[start_pos].from(), from);
|
||||||
|
int new_to = Max(list[end_pos - 1].to(), to);
|
||||||
|
if (end_pos < count) {
|
||||||
|
list.moveReplace(end_pos, start_pos + 1, count - end_pos);
|
||||||
|
}
|
||||||
|
list[start_pos] = CharacterRange(new_from, new_to);
|
||||||
|
return count - (end_pos - start_pos) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
irregexp::GetCaseIndependentLetters(char16_t character,
|
||||||
|
bool ascii_subject,
|
||||||
|
bool unicode,
|
||||||
|
const char16_t* choices,
|
||||||
|
size_t choices_length,
|
||||||
|
char16_t* letters)
|
||||||
|
{
|
||||||
|
size_t count = 0;
|
||||||
|
for (size_t i = 0; i < choices_length; i++) {
|
||||||
|
char16_t c = choices[i];
|
||||||
|
|
||||||
|
// Skip characters that can't appear in one byte strings.
|
||||||
|
if (!unicode && ascii_subject && c > kMaxOneByteCharCode)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Watch for duplicates.
|
||||||
|
bool found = false;
|
||||||
|
for (size_t j = 0; j < count; j++) {
|
||||||
|
if (letters[j] == c) {
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (found)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
letters[count++] = c;
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
irregexp::GetCaseIndependentLetters(char16_t character,
|
||||||
|
bool ascii_subject,
|
||||||
|
bool unicode,
|
||||||
|
char16_t* letters)
|
||||||
|
{
|
||||||
|
if (unicode) {
|
||||||
|
const char16_t choices[] = {
|
||||||
|
character,
|
||||||
|
unicode::FoldCase(character),
|
||||||
|
unicode::ReverseFoldCase1(character),
|
||||||
|
unicode::ReverseFoldCase2(character),
|
||||||
|
unicode::ReverseFoldCase3(character),
|
||||||
|
};
|
||||||
|
return GetCaseIndependentLetters(character, ascii_subject, unicode,
|
||||||
|
choices, ArrayLength(choices), letters);
|
||||||
|
}
|
||||||
|
|
||||||
|
char16_t upper = unicode::ToUpperCase(character);
|
||||||
|
unicode::CodepointsWithSameUpperCase others(character);
|
||||||
|
char16_t other1 = others.other1();
|
||||||
|
char16_t other2 = others.other2();
|
||||||
|
char16_t other3 = others.other3();
|
||||||
|
|
||||||
|
// ES 2017 draft 996af87b7072b3c3dd2b1def856c66f456102215 21.2.4.2
|
||||||
|
// step 3.g.
|
||||||
|
// The standard requires that non-ASCII characters cannot have ASCII
|
||||||
|
// character codes in their equivalence class, even though this
|
||||||
|
// situation occurs multiple times in the Unicode tables.
|
||||||
|
static const unsigned kMaxAsciiCharCode = 127;
|
||||||
|
if (upper <= kMaxAsciiCharCode) {
|
||||||
|
if (character > kMaxAsciiCharCode) {
|
||||||
|
// If Canonicalize(character) == character, all other characters
|
||||||
|
// should be ignored.
|
||||||
|
return GetCaseIndependentLetters(character, ascii_subject, unicode,
|
||||||
|
&character, 1, letters);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (other1 > kMaxAsciiCharCode)
|
||||||
|
other1 = character;
|
||||||
|
if (other2 > kMaxAsciiCharCode)
|
||||||
|
other2 = character;
|
||||||
|
if (other3 > kMaxAsciiCharCode)
|
||||||
|
other3 = character;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char16_t choices[] = {
|
||||||
|
character,
|
||||||
|
upper,
|
||||||
|
other1,
|
||||||
|
other2,
|
||||||
|
other3
|
||||||
|
};
|
||||||
|
return GetCaseIndependentLetters(character, ascii_subject, unicode,
|
||||||
|
choices, ArrayLength(choices), letters);
|
||||||
|
}
|
||||||
220
js/src/irregexp/RegExpCharRanges.h
Normal file
220
js/src/irregexp/RegExpCharRanges.h
Normal file
|
|
@ -0,0 +1,220 @@
|
||||||
|
/* -*- 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 "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;
|
||||||
|
|
||||||
|
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 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_
|
||||||
|
|
@ -35,13 +35,9 @@
|
||||||
#include "jit/ExecutableAllocator.h"
|
#include "jit/ExecutableAllocator.h"
|
||||||
#include "jit/JitCommon.h"
|
#include "jit/JitCommon.h"
|
||||||
|
|
||||||
// Generated table
|
|
||||||
#include "irregexp/RegExpCharacters-inl.h"
|
|
||||||
|
|
||||||
using namespace js;
|
using namespace js;
|
||||||
using namespace js::irregexp;
|
using namespace js::irregexp;
|
||||||
|
|
||||||
using mozilla::ArrayLength;
|
|
||||||
using mozilla::DebugOnly;
|
using mozilla::DebugOnly;
|
||||||
using mozilla::Maybe;
|
using mozilla::Maybe;
|
||||||
|
|
||||||
|
|
@ -64,317 +60,6 @@ RegExpNode::RegExpNode(LifoAlloc* alloc)
|
||||||
bm_info_[0] = bm_info_[1] = nullptr;
|
bm_info_[0] = bm_info_[1] = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const int kMaxOneByteCharCode = 0xff;
|
|
||||||
static const int kMaxUtf16CodeUnit = 0xffff;
|
|
||||||
|
|
||||||
static char16_t
|
|
||||||
MaximumCharacter(bool ascii)
|
|
||||||
{
|
|
||||||
return ascii ? kMaxOneByteCharCode : kMaxUtf16CodeUnit;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
AddClass(const int* elmv, int elmc,
|
|
||||||
CharacterRangeVector* ranges)
|
|
||||||
{
|
|
||||||
elmc--;
|
|
||||||
MOZ_ASSERT(elmv[elmc] == 0x10000);
|
|
||||||
for (int i = 0; i < elmc; i += 2) {
|
|
||||||
MOZ_ASSERT(elmv[i] < elmv[i + 1]);
|
|
||||||
ranges->append(CharacterRange(elmv[i], elmv[i + 1] - 1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
AddClassNegated(const int* elmv,
|
|
||||||
int elmc,
|
|
||||||
CharacterRangeVector* ranges)
|
|
||||||
{
|
|
||||||
elmc--;
|
|
||||||
MOZ_ASSERT(elmv[elmc] == 0x10000);
|
|
||||||
MOZ_ASSERT(elmv[0] != 0x0000);
|
|
||||||
MOZ_ASSERT(elmv[elmc-1] != kMaxUtf16CodeUnit);
|
|
||||||
char16_t last = 0x0000;
|
|
||||||
for (int i = 0; i < elmc; i += 2) {
|
|
||||||
MOZ_ASSERT(last <= elmv[i] - 1);
|
|
||||||
MOZ_ASSERT(elmv[i] < elmv[i + 1]);
|
|
||||||
ranges->append(CharacterRange(last, elmv[i] - 1));
|
|
||||||
last = elmv[i + 1];
|
|
||||||
}
|
|
||||||
ranges->append(CharacterRange(last, kMaxUtf16CodeUnit));
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
CharacterRange::AddClassEscape(LifoAlloc* alloc, char16_t type,
|
|
||||||
CharacterRangeVector* ranges)
|
|
||||||
{
|
|
||||||
switch (type) {
|
|
||||||
case 's':
|
|
||||||
AddClass(kSpaceRanges, kSpaceRangeCount, ranges);
|
|
||||||
break;
|
|
||||||
case 'S':
|
|
||||||
AddClassNegated(kSpaceRanges, kSpaceRangeCount, ranges);
|
|
||||||
break;
|
|
||||||
case 'w':
|
|
||||||
AddClass(kWordRanges, kWordRangeCount, ranges);
|
|
||||||
break;
|
|
||||||
case 'W':
|
|
||||||
AddClassNegated(kWordRanges, kWordRangeCount, ranges);
|
|
||||||
break;
|
|
||||||
case 'd':
|
|
||||||
AddClass(kDigitRanges, kDigitRangeCount, ranges);
|
|
||||||
break;
|
|
||||||
case 'D':
|
|
||||||
AddClassNegated(kDigitRanges, kDigitRangeCount, ranges);
|
|
||||||
break;
|
|
||||||
case '.':
|
|
||||||
AddClassNegated(kLineTerminatorRanges, kLineTerminatorRangeCount, ranges);
|
|
||||||
break;
|
|
||||||
// This is not a character range as defined by the spec but a
|
|
||||||
// convenient shorthand for a character class that matches any
|
|
||||||
// character.
|
|
||||||
case '*':
|
|
||||||
ranges->append(CharacterRange::Everything());
|
|
||||||
break;
|
|
||||||
// This is the set of characters matched by the $ and ^ symbols
|
|
||||||
// in multiline mode.
|
|
||||||
case 'n':
|
|
||||||
AddClass(kLineTerminatorRanges, kLineTerminatorRangeCount, ranges);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
MOZ_CRASH("Bad character class escape");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add class escape, excluding surrogate pair range.
|
|
||||||
void
|
|
||||||
CharacterRange::AddClassEscapeUnicode(LifoAlloc* alloc, char16_t type,
|
|
||||||
CharacterRangeVector* ranges, bool ignore_case)
|
|
||||||
{
|
|
||||||
switch (type) {
|
|
||||||
case 's':
|
|
||||||
case 'd':
|
|
||||||
return AddClassEscape(alloc, type, ranges);
|
|
||||||
break;
|
|
||||||
case 'S':
|
|
||||||
AddClassNegated(kSpaceAndSurrogateRanges, kSpaceAndSurrogateRangeCount, ranges);
|
|
||||||
break;
|
|
||||||
case 'w':
|
|
||||||
if (ignore_case)
|
|
||||||
AddClass(kIgnoreCaseWordRanges, kIgnoreCaseWordRangeCount, ranges);
|
|
||||||
else
|
|
||||||
AddClassEscape(alloc, type, ranges);
|
|
||||||
break;
|
|
||||||
case 'W':
|
|
||||||
if (ignore_case) {
|
|
||||||
AddClass(kNegatedIgnoreCaseWordAndSurrogateRanges,
|
|
||||||
kNegatedIgnoreCaseWordAndSurrogateRangeCount, ranges);
|
|
||||||
} else {
|
|
||||||
AddClassNegated(kWordAndSurrogateRanges, kWordAndSurrogateRangeCount, ranges);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'D':
|
|
||||||
AddClassNegated(kDigitAndSurrogateRanges, kDigitAndSurrogateRangeCount, ranges);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
MOZ_CRASH("Bad type!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool
|
|
||||||
RangesContainLatin1Equivalents(const CharacterRangeVector& ranges, bool unicode)
|
|
||||||
{
|
|
||||||
for (size_t i = 0; i < ranges.length(); i++) {
|
|
||||||
// TODO(dcarney): this could be a lot more efficient.
|
|
||||||
if (RangeContainsLatin1Equivalents(ranges[i], unicode))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const size_t kEcma262UnCanonicalizeMaxWidth = 4;
|
|
||||||
|
|
||||||
// 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.
|
|
||||||
static int
|
|
||||||
GetCaseIndependentLetters(char16_t character,
|
|
||||||
bool ascii_subject,
|
|
||||||
bool unicode,
|
|
||||||
const char16_t* choices,
|
|
||||||
size_t choices_length,
|
|
||||||
char16_t* letters)
|
|
||||||
{
|
|
||||||
size_t count = 0;
|
|
||||||
for (size_t i = 0; i < choices_length; i++) {
|
|
||||||
char16_t c = choices[i];
|
|
||||||
|
|
||||||
// Skip characters that can't appear in one byte strings.
|
|
||||||
if (!unicode && ascii_subject && c > kMaxOneByteCharCode)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// Watch for duplicates.
|
|
||||||
bool found = false;
|
|
||||||
for (size_t j = 0; j < count; j++) {
|
|
||||||
if (letters[j] == c) {
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (found)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
letters[count++] = c;
|
|
||||||
}
|
|
||||||
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
GetCaseIndependentLetters(char16_t character,
|
|
||||||
bool ascii_subject,
|
|
||||||
bool unicode,
|
|
||||||
char16_t* letters)
|
|
||||||
{
|
|
||||||
if (unicode) {
|
|
||||||
const char16_t choices[] = {
|
|
||||||
character,
|
|
||||||
unicode::FoldCase(character),
|
|
||||||
unicode::ReverseFoldCase1(character),
|
|
||||||
unicode::ReverseFoldCase2(character),
|
|
||||||
unicode::ReverseFoldCase3(character),
|
|
||||||
};
|
|
||||||
return GetCaseIndependentLetters(character, ascii_subject, unicode,
|
|
||||||
choices, ArrayLength(choices), letters);
|
|
||||||
}
|
|
||||||
|
|
||||||
char16_t upper = unicode::ToUpperCase(character);
|
|
||||||
unicode::CodepointsWithSameUpperCase others(character);
|
|
||||||
char16_t other1 = others.other1();
|
|
||||||
char16_t other2 = others.other2();
|
|
||||||
char16_t other3 = others.other3();
|
|
||||||
|
|
||||||
// ES 2017 draft 996af87b7072b3c3dd2b1def856c66f456102215 21.2.4.2
|
|
||||||
// step 3.g.
|
|
||||||
// The standard requires that non-ASCII characters cannot have ASCII
|
|
||||||
// character codes in their equivalence class, even though this
|
|
||||||
// situation occurs multiple times in the Unicode tables.
|
|
||||||
static const unsigned kMaxAsciiCharCode = 127;
|
|
||||||
if (upper <= kMaxAsciiCharCode) {
|
|
||||||
if (character > kMaxAsciiCharCode) {
|
|
||||||
// If Canonicalize(character) == character, all other characters
|
|
||||||
// should be ignored.
|
|
||||||
return GetCaseIndependentLetters(character, ascii_subject, unicode,
|
|
||||||
&character, 1, letters);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (other1 > kMaxAsciiCharCode)
|
|
||||||
other1 = character;
|
|
||||||
if (other2 > kMaxAsciiCharCode)
|
|
||||||
other2 = character;
|
|
||||||
if (other3 > kMaxAsciiCharCode)
|
|
||||||
other3 = character;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char16_t choices[] = {
|
|
||||||
character,
|
|
||||||
upper,
|
|
||||||
other1,
|
|
||||||
other2,
|
|
||||||
other3
|
|
||||||
};
|
|
||||||
return GetCaseIndependentLetters(character, ascii_subject, unicode,
|
|
||||||
choices, ArrayLength(choices), letters);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
CharacterRange::AddCaseEquivalents(bool is_ascii, bool unicode, CharacterRangeVector* ranges)
|
|
||||||
{
|
|
||||||
char16_t bottom = from();
|
|
||||||
char16_t top = to();
|
|
||||||
|
|
||||||
if (is_ascii && !RangeContainsLatin1Equivalents(*this, unicode)) {
|
|
||||||
if (bottom > kMaxOneByteCharCode)
|
|
||||||
return;
|
|
||||||
if (top > kMaxOneByteCharCode)
|
|
||||||
top = kMaxOneByteCharCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (char16_t c = bottom;; c++) {
|
|
||||||
char16_t chars[kEcma262UnCanonicalizeMaxWidth];
|
|
||||||
size_t length = GetCaseIndependentLetters(c, is_ascii, unicode, chars);
|
|
||||||
|
|
||||||
for (size_t i = 0; i < length; i++) {
|
|
||||||
char16_t other = chars[i];
|
|
||||||
if (other == c)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// Try to combine with an existing range.
|
|
||||||
bool found = false;
|
|
||||||
for (size_t i = 0; i < ranges->length(); i++) {
|
|
||||||
CharacterRange& range = (*ranges)[i];
|
|
||||||
if (range.Contains(other)) {
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
} else if (other == range.from() - 1) {
|
|
||||||
range.set_from(other);
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
} else if (other == range.to() + 1) {
|
|
||||||
range.set_to(other);
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!found)
|
|
||||||
ranges->append(CharacterRange::Singleton(other));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (c == top)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool
|
|
||||||
CompareInverseRanges(const CharacterRangeVector& ranges, const int* special_class, size_t length)
|
|
||||||
{
|
|
||||||
length--; // Remove final 0x10000.
|
|
||||||
MOZ_ASSERT(special_class[length] == 0x10000);
|
|
||||||
MOZ_ASSERT(ranges.length() != 0);
|
|
||||||
MOZ_ASSERT(length != 0);
|
|
||||||
MOZ_ASSERT(special_class[0] != 0);
|
|
||||||
if (ranges.length() != (length >> 1) + 1)
|
|
||||||
return false;
|
|
||||||
CharacterRange range = ranges[0];
|
|
||||||
if (range.from() != 0)
|
|
||||||
return false;
|
|
||||||
for (size_t i = 0; i < length; i += 2) {
|
|
||||||
if (special_class[i] != (range.to() + 1))
|
|
||||||
return false;
|
|
||||||
range = ranges[(i >> 1) + 1];
|
|
||||||
if (special_class[i+1] != range.from())
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (range.to() != 0xffff)
|
|
||||||
return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool
|
|
||||||
CompareRanges(const CharacterRangeVector& ranges, const int* special_class, size_t length)
|
|
||||||
{
|
|
||||||
length--; // Remove final 0x10000.
|
|
||||||
MOZ_ASSERT(special_class[length] == 0x10000);
|
|
||||||
if (ranges.length() * 2 != length)
|
|
||||||
return false;
|
|
||||||
for (size_t i = 0; i < length; i += 2) {
|
|
||||||
CharacterRange range = ranges[i >> 1];
|
|
||||||
if (range.from() != special_class[i] || range.to() != special_class[i + 1] - 1)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
RegExpCharacterClass::is_standard(LifoAlloc* alloc)
|
RegExpCharacterClass::is_standard(LifoAlloc* alloc)
|
||||||
{
|
{
|
||||||
|
|
@ -384,168 +69,37 @@ RegExpCharacterClass::is_standard(LifoAlloc* alloc)
|
||||||
return false;
|
return false;
|
||||||
if (set_.is_standard())
|
if (set_.is_standard())
|
||||||
return true;
|
return true;
|
||||||
if (CompareRanges(set_.ranges(alloc), kSpaceRanges, kSpaceRangeCount)) {
|
if (CharacterRange::CompareRanges(set_.ranges(alloc), kSpaceRanges, kSpaceRangeCount)) {
|
||||||
set_.set_standard_set_type('s');
|
set_.set_standard_set_type('s');
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (CompareInverseRanges(set_.ranges(alloc), kSpaceRanges, kSpaceRangeCount)) {
|
if (CharacterRange::CompareInverseRanges(set_.ranges(alloc), kSpaceRanges, kSpaceRangeCount)) {
|
||||||
set_.set_standard_set_type('S');
|
set_.set_standard_set_type('S');
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (CompareInverseRanges(set_.ranges(alloc),
|
if (CharacterRange::CompareInverseRanges(set_.ranges(alloc),
|
||||||
kLineTerminatorRanges,
|
kLineTerminatorRanges,
|
||||||
kLineTerminatorRangeCount)) {
|
kLineTerminatorRangeCount)) {
|
||||||
set_.set_standard_set_type('.');
|
set_.set_standard_set_type('.');
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (CompareRanges(set_.ranges(alloc),
|
if (CharacterRange::CompareRanges(set_.ranges(alloc),
|
||||||
kLineTerminatorRanges,
|
kLineTerminatorRanges,
|
||||||
kLineTerminatorRangeCount)) {
|
kLineTerminatorRangeCount)) {
|
||||||
set_.set_standard_set_type('n');
|
set_.set_standard_set_type('n');
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (CompareRanges(set_.ranges(alloc), kWordRanges, kWordRangeCount)) {
|
if (CharacterRange::CompareRanges(set_.ranges(alloc), kWordRanges, kWordRangeCount)) {
|
||||||
set_.set_standard_set_type('w');
|
set_.set_standard_set_type('w');
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (CompareInverseRanges(set_.ranges(alloc), kWordRanges, kWordRangeCount)) {
|
if (CharacterRange::CompareInverseRanges(set_.ranges(alloc), kWordRanges, kWordRangeCount)) {
|
||||||
set_.set_standard_set_type('W');
|
set_.set_standard_set_type('W');
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
|
||||||
CharacterRange::IsCanonical(const CharacterRangeVector& ranges)
|
|
||||||
{
|
|
||||||
int n = ranges.length();
|
|
||||||
if (n <= 1)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
int max = ranges[0].to();
|
|
||||||
for (int i = 1; i < n; i++) {
|
|
||||||
CharacterRange next_range = ranges[i];
|
|
||||||
if (next_range.from() <= max + 1)
|
|
||||||
return false;
|
|
||||||
max = next_range.to();
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Move a number of elements in a zonelist to another position
|
|
||||||
// in the same list. Handles overlapping source and target areas.
|
|
||||||
static
|
|
||||||
void MoveRanges(CharacterRangeVector& list, int from, int to, int count)
|
|
||||||
{
|
|
||||||
// Ranges are potentially overlapping.
|
|
||||||
if (from < to) {
|
|
||||||
for (int i = count - 1; i >= 0; i--)
|
|
||||||
list[to + i] = list[from + i];
|
|
||||||
} else {
|
|
||||||
for (int i = 0; i < count; i++)
|
|
||||||
list[to + i] = list[from + i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
InsertRangeInCanonicalList(CharacterRangeVector& list,
|
|
||||||
int count,
|
|
||||||
CharacterRange insert)
|
|
||||||
{
|
|
||||||
// Inserts a range into list[0..count[, which must be sorted
|
|
||||||
// by from value and non-overlapping and non-adjacent, using at most
|
|
||||||
// list[0..count] for the result. Returns the number of resulting
|
|
||||||
// canonicalized ranges. Inserting a range may collapse existing ranges into
|
|
||||||
// fewer ranges, so the return value can be anything in the range 1..count+1.
|
|
||||||
char16_t from = insert.from();
|
|
||||||
char16_t to = insert.to();
|
|
||||||
int start_pos = 0;
|
|
||||||
int end_pos = count;
|
|
||||||
for (int i = count - 1; i >= 0; i--) {
|
|
||||||
CharacterRange current = list[i];
|
|
||||||
if (current.from() > to + 1) {
|
|
||||||
end_pos = i;
|
|
||||||
} else if (current.to() + 1 < from) {
|
|
||||||
start_pos = i + 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Inserted range overlaps, or is adjacent to, ranges at positions
|
|
||||||
// [start_pos..end_pos[. Ranges before start_pos or at or after end_pos are
|
|
||||||
// not affected by the insertion.
|
|
||||||
// If start_pos == end_pos, the range must be inserted before start_pos.
|
|
||||||
// if start_pos < end_pos, the entire range from start_pos to end_pos
|
|
||||||
// must be merged with the insert range.
|
|
||||||
|
|
||||||
if (start_pos == end_pos) {
|
|
||||||
// Insert between existing ranges at position start_pos.
|
|
||||||
if (start_pos < count) {
|
|
||||||
MoveRanges(list, start_pos, start_pos + 1, count - start_pos);
|
|
||||||
}
|
|
||||||
list[start_pos] = insert;
|
|
||||||
return count + 1;
|
|
||||||
}
|
|
||||||
if (start_pos + 1 == end_pos) {
|
|
||||||
// Replace single existing range at position start_pos.
|
|
||||||
CharacterRange to_replace = list[start_pos];
|
|
||||||
int new_from = Min(to_replace.from(), from);
|
|
||||||
int new_to = Max(to_replace.to(), to);
|
|
||||||
list[start_pos] = CharacterRange(new_from, new_to);
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
// Replace a number of existing ranges from start_pos to end_pos - 1.
|
|
||||||
// Move the remaining ranges down.
|
|
||||||
|
|
||||||
int new_from = Min(list[start_pos].from(), from);
|
|
||||||
int new_to = Max(list[end_pos - 1].to(), to);
|
|
||||||
if (end_pos < count) {
|
|
||||||
MoveRanges(list, end_pos, start_pos + 1, count - end_pos);
|
|
||||||
}
|
|
||||||
list[start_pos] = CharacterRange(new_from, new_to);
|
|
||||||
return count - (end_pos - start_pos) + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
CharacterRange::Canonicalize(CharacterRangeVector& character_ranges)
|
|
||||||
{
|
|
||||||
if (character_ranges.length() <= 1) return;
|
|
||||||
// Check whether ranges are already canonical (increasing, non-overlapping,
|
|
||||||
// non-adjacent).
|
|
||||||
int n = character_ranges.length();
|
|
||||||
int max = character_ranges[0].to();
|
|
||||||
int i = 1;
|
|
||||||
while (i < n) {
|
|
||||||
CharacterRange current = character_ranges[i];
|
|
||||||
if (current.from() <= max + 1) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
max = current.to();
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
// Canonical until the i'th range. If that's all of them, we are done.
|
|
||||||
if (i == n) return;
|
|
||||||
|
|
||||||
// The ranges at index i and forward are not canonicalized. Make them so by
|
|
||||||
// doing the equivalent of insertion sort (inserting each into the previous
|
|
||||||
// list, in order).
|
|
||||||
// Notice that inserting a range can reduce the number of ranges in the
|
|
||||||
// result due to combining of adjacent and overlapping ranges.
|
|
||||||
int read = i; // Range to insert.
|
|
||||||
size_t num_canonical = i; // Length of canonicalized part of list.
|
|
||||||
do {
|
|
||||||
num_canonical = InsertRangeInCanonicalList(character_ranges,
|
|
||||||
num_canonical,
|
|
||||||
character_ranges[read]);
|
|
||||||
read++;
|
|
||||||
} while (read < n);
|
|
||||||
|
|
||||||
while (character_ranges.length() > num_canonical)
|
|
||||||
character_ranges.popBack();
|
|
||||||
|
|
||||||
MOZ_ASSERT(CharacterRange::IsCanonical(character_ranges));
|
|
||||||
}
|
|
||||||
|
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
// SeqRegExpNode
|
// SeqRegExpNode
|
||||||
|
|
||||||
|
|
@ -790,7 +344,7 @@ TextNode::FilterASCII(int depth, bool ignore_case, bool unicode)
|
||||||
ranges[0].to() >= kMaxOneByteCharCode)
|
ranges[0].to() >= kMaxOneByteCharCode)
|
||||||
{
|
{
|
||||||
// This will be handled in a later filter.
|
// This will be handled in a later filter.
|
||||||
if (ignore_case && RangesContainLatin1Equivalents(ranges, unicode))
|
if (ignore_case && CharacterRange::RangesContainLatin1Equivalents(ranges, unicode))
|
||||||
continue;
|
continue;
|
||||||
return set_replacement(nullptr);
|
return set_replacement(nullptr);
|
||||||
}
|
}
|
||||||
|
|
@ -799,7 +353,7 @@ TextNode::FilterASCII(int depth, bool ignore_case, bool unicode)
|
||||||
ranges[0].from() > kMaxOneByteCharCode)
|
ranges[0].from() > kMaxOneByteCharCode)
|
||||||
{
|
{
|
||||||
// This will be handled in a later filter.
|
// This will be handled in a later filter.
|
||||||
if (ignore_case && RangesContainLatin1Equivalents(ranges, unicode))
|
if (ignore_case && CharacterRange::RangesContainLatin1Equivalents(ranges, unicode))
|
||||||
continue;
|
continue;
|
||||||
return set_replacement(nullptr);
|
return set_replacement(nullptr);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,9 @@
|
||||||
|
|
||||||
#include "ds/SplayTree.h"
|
#include "ds/SplayTree.h"
|
||||||
#include "jit/Label.h"
|
#include "jit/Label.h"
|
||||||
|
|
||||||
|
#include "irregexp/InfallibleVector.h"
|
||||||
|
#include "irregexp/RegExpCharRanges.h"
|
||||||
#include "vm/RegExpObject.h"
|
#include "vm/RegExpObject.h"
|
||||||
|
|
||||||
namespace js {
|
namespace js {
|
||||||
|
|
@ -142,75 +145,6 @@ InterpretCode(JSContext* cx, const uint8_t* byteCode, const CharT* chars, size_t
|
||||||
FOR_EACH_REG_EXP_TREE_TYPE(FORWARD_DECLARE)
|
FOR_EACH_REG_EXP_TREE_TYPE(FORWARD_DECLARE)
|
||||||
#undef FORWARD_DECLARE
|
#undef FORWARD_DECLARE
|
||||||
|
|
||||||
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
|
// A set of unsigned integers that behaves especially well on small
|
||||||
// integers (< 32).
|
// integers (< 32).
|
||||||
class OutSet
|
class OutSet
|
||||||
|
|
|
||||||
|
|
@ -670,215 +670,6 @@ RegExpParser<CharT>::ParseClassCharacterEscape(widechar* code)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
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_;
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef InfallibleVector<WideCharRange, 1> WideCharRangeVector;
|
|
||||||
|
|
||||||
static inline CharacterRange
|
|
||||||
LeadSurrogateRange()
|
|
||||||
{
|
|
||||||
return CharacterRange::Range(unicode::LeadSurrogateMin, unicode::LeadSurrogateMax);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline CharacterRange
|
|
||||||
TrailSurrogateRange()
|
|
||||||
{
|
|
||||||
return CharacterRange::Range(unicode::TrailSurrogateMin, unicode::TrailSurrogateMax);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline WideCharRange
|
|
||||||
NonBMPRange()
|
|
||||||
{
|
|
||||||
return WideCharRange::Range(unicode::NonBMPMin, unicode::NonBMPMax);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char16_t kNoCharClass = 0;
|
|
||||||
|
|
||||||
// Adds a character or pre-defined character class to character ranges.
|
|
||||||
// If char_class is not kInvalidClass, it's interpreted as a class
|
|
||||||
// escape (i.e., 's' means whitespace, from '\s').
|
|
||||||
static inline void
|
|
||||||
AddCharOrEscape(LifoAlloc* alloc,
|
|
||||||
CharacterRangeVector* ranges,
|
|
||||||
char16_t char_class,
|
|
||||||
widechar c)
|
|
||||||
{
|
|
||||||
if (char_class != kNoCharClass)
|
|
||||||
CharacterRange::AddClassEscape(alloc, char_class, ranges);
|
|
||||||
else
|
|
||||||
ranges->append(CharacterRange::Singleton(c));
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void
|
|
||||||
AddCharOrEscapeUnicode(LifoAlloc* alloc,
|
|
||||||
CharacterRangeVector* ranges,
|
|
||||||
CharacterRangeVector* lead_ranges,
|
|
||||||
CharacterRangeVector* trail_ranges,
|
|
||||||
WideCharRangeVector* wide_ranges,
|
|
||||||
char16_t char_class,
|
|
||||||
widechar c,
|
|
||||||
bool ignore_case)
|
|
||||||
{
|
|
||||||
if (char_class != kNoCharClass) {
|
|
||||||
CharacterRange::AddClassEscapeUnicode(alloc, char_class, ranges, ignore_case);
|
|
||||||
switch (char_class) {
|
|
||||||
case 'S':
|
|
||||||
case 'W':
|
|
||||||
case 'D':
|
|
||||||
lead_ranges->append(LeadSurrogateRange());
|
|
||||||
trail_ranges->append(TrailSurrogateRange());
|
|
||||||
wide_ranges->append(NonBMPRange());
|
|
||||||
break;
|
|
||||||
case '.':
|
|
||||||
MOZ_CRASH("Bad char_class!");
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (unicode::IsLeadSurrogate(c))
|
|
||||||
lead_ranges->append(CharacterRange::Singleton(c));
|
|
||||||
else if (unicode::IsTrailSurrogate(c))
|
|
||||||
trail_ranges->append(CharacterRange::Singleton(c));
|
|
||||||
else if (c >= unicode::NonBMPMin)
|
|
||||||
wide_ranges->append(WideCharRange::Singleton(c));
|
|
||||||
else
|
|
||||||
ranges->append(CharacterRange::Singleton(c));
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void
|
|
||||||
AddUnicodeRange(LifoAlloc* alloc,
|
|
||||||
CharacterRangeVector* ranges,
|
|
||||||
CharacterRangeVector* lead_ranges,
|
|
||||||
CharacterRangeVector* trail_ranges,
|
|
||||||
WideCharRangeVector* wide_ranges,
|
|
||||||
widechar first,
|
|
||||||
widechar next)
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(first <= next);
|
|
||||||
if (first < unicode::LeadSurrogateMin) {
|
|
||||||
if (next < unicode::LeadSurrogateMin) {
|
|
||||||
ranges->append(CharacterRange::Range(first, next));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ranges->append(CharacterRange::Range(first, unicode::LeadSurrogateMin - 1));
|
|
||||||
first = unicode::LeadSurrogateMin;
|
|
||||||
}
|
|
||||||
if (first <= unicode::LeadSurrogateMax) {
|
|
||||||
if (next <= unicode::LeadSurrogateMax) {
|
|
||||||
lead_ranges->append(CharacterRange::Range(first, next));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
lead_ranges->append(CharacterRange::Range(first, unicode::LeadSurrogateMax));
|
|
||||||
first = unicode::LeadSurrogateMax + 1;
|
|
||||||
}
|
|
||||||
MOZ_ASSERT(unicode::LeadSurrogateMax + 1 == unicode::TrailSurrogateMin);
|
|
||||||
if (first <= unicode::TrailSurrogateMax) {
|
|
||||||
if (next <= unicode::TrailSurrogateMax) {
|
|
||||||
trail_ranges->append(CharacterRange::Range(first, next));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
trail_ranges->append(CharacterRange::Range(first, unicode::TrailSurrogateMax));
|
|
||||||
first = unicode::TrailSurrogateMax + 1;
|
|
||||||
}
|
|
||||||
if (first <= unicode::UTF16Max) {
|
|
||||||
if (next <= unicode::UTF16Max) {
|
|
||||||
ranges->append(CharacterRange::Range(first, next));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ranges->append(CharacterRange::Range(first, unicode::UTF16Max));
|
|
||||||
first = unicode::NonBMPMin;
|
|
||||||
}
|
|
||||||
MOZ_ASSERT(unicode::UTF16Max + 1 == unicode::NonBMPMin);
|
|
||||||
wide_ranges->append(WideCharRange::Range(first, next));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Negate a vector of ranges by subtracting its ranges from a range
|
|
||||||
// encompassing the full range of possible values.
|
|
||||||
template <typename RangeType>
|
|
||||||
static inline void
|
|
||||||
NegateUnicodeRanges(LifoAlloc* alloc, InfallibleVector<RangeType, 1>** ranges,
|
|
||||||
RangeType full_range)
|
|
||||||
{
|
|
||||||
typedef InfallibleVector<RangeType, 1> RangeVector;
|
|
||||||
RangeVector* tmp_ranges = alloc->newInfallible<RangeVector>(*alloc);
|
|
||||||
tmp_ranges->append(full_range);
|
|
||||||
RangeVector* result_ranges = alloc->newInfallible<RangeVector>(*alloc);
|
|
||||||
|
|
||||||
// Perform the following calculation:
|
|
||||||
// result_ranges = tmp_ranges - ranges
|
|
||||||
// with the following steps:
|
|
||||||
// result_ranges = tmp_ranges - ranges[0]
|
|
||||||
// SWAP(result_ranges, tmp_ranges)
|
|
||||||
// result_ranges = tmp_ranges - ranges[1]
|
|
||||||
// SWAP(result_ranges, tmp_ranges)
|
|
||||||
// ...
|
|
||||||
// result_ranges = tmp_ranges - ranges[N-1]
|
|
||||||
// SWAP(result_ranges, tmp_ranges)
|
|
||||||
// The last SWAP is just for simplicity of the loop.
|
|
||||||
for (size_t i = 0; i < (*ranges)->length(); i++) {
|
|
||||||
result_ranges->clear();
|
|
||||||
|
|
||||||
const RangeType& range = (**ranges)[i];
|
|
||||||
for (size_t j = 0; j < tmp_ranges->length(); j++) {
|
|
||||||
const RangeType& tmpRange = (*tmp_ranges)[j];
|
|
||||||
auto from1 = tmpRange.from();
|
|
||||||
auto to1 = tmpRange.to();
|
|
||||||
auto from2 = range.from();
|
|
||||||
auto to2 = range.to();
|
|
||||||
|
|
||||||
if (from1 < from2) {
|
|
||||||
if (to1 < from2) {
|
|
||||||
result_ranges->append(tmpRange);
|
|
||||||
} else if (to1 <= to2) {
|
|
||||||
result_ranges->append(RangeType::Range(from1, from2 - 1));
|
|
||||||
} else {
|
|
||||||
result_ranges->append(RangeType::Range(from1, from2 - 1));
|
|
||||||
result_ranges->append(RangeType::Range(to2 + 1, to1));
|
|
||||||
}
|
|
||||||
} else if (from1 <= to2) {
|
|
||||||
if (to1 > to2)
|
|
||||||
result_ranges->append(RangeType::Range(to2 + 1, to1));
|
|
||||||
} else {
|
|
||||||
result_ranges->append(tmpRange);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
auto tmp = tmp_ranges;
|
|
||||||
tmp_ranges = result_ranges;
|
|
||||||
result_ranges = tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
// After the loop, result is pointed at by tmp_ranges, instead of
|
|
||||||
// result_ranges.
|
|
||||||
*ranges = tmp_ranges;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
WideCharRangesContain(WideCharRangeVector* wide_ranges, widechar c)
|
WideCharRangesContain(WideCharRangeVector* wide_ranges, widechar c)
|
||||||
{
|
{
|
||||||
|
|
@ -948,9 +739,9 @@ UnicodeRangesAtom(LifoAlloc* alloc,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_negated) {
|
if (is_negated) {
|
||||||
NegateUnicodeRanges(alloc, &lead_ranges, LeadSurrogateRange());
|
CharacterRange::NegateUnicodeRanges(alloc, &lead_ranges, CharacterRange::LeadSurrogate());
|
||||||
NegateUnicodeRanges(alloc, &trail_ranges, TrailSurrogateRange());
|
CharacterRange::NegateUnicodeRanges(alloc, &trail_ranges, CharacterRange::TrailSurrogate());
|
||||||
NegateUnicodeRanges(alloc, &wide_ranges, NonBMPRange());
|
CharacterRange::NegateUnicodeRanges(alloc, &wide_ranges, WideCharRange::NonBMP());
|
||||||
}
|
}
|
||||||
|
|
||||||
RegExpBuilder* builder = alloc->newInfallible<RegExpBuilder>(alloc);
|
RegExpBuilder* builder = alloc->newInfallible<RegExpBuilder>(alloc);
|
||||||
|
|
@ -958,8 +749,8 @@ UnicodeRangesAtom(LifoAlloc* alloc,
|
||||||
bool added = false;
|
bool added = false;
|
||||||
|
|
||||||
if (is_negated) {
|
if (is_negated) {
|
||||||
ranges->append(LeadSurrogateRange());
|
ranges->append(CharacterRange::LeadSurrogate());
|
||||||
ranges->append(TrailSurrogateRange());
|
ranges->append(CharacterRange::TrailSurrogate());
|
||||||
}
|
}
|
||||||
if (ranges->length() > 0) {
|
if (ranges->length() > 0) {
|
||||||
builder->AddAtom(alloc->newInfallible<RegExpCharacterClass>(ranges, is_negated));
|
builder->AddAtom(alloc->newInfallible<RegExpCharacterClass>(ranges, is_negated));
|
||||||
|
|
@ -1077,9 +868,9 @@ RegExpParser<CharT>::ParseCharacterClass()
|
||||||
}
|
}
|
||||||
|
|
||||||
while (has_more() && current() != ']') {
|
while (has_more() && current() != ']') {
|
||||||
char16_t char_class = kNoCharClass;
|
char16_t char_class_1 = kNoCharClass;
|
||||||
widechar first = 0;
|
widechar char_1 = 0;
|
||||||
if (!ParseClassAtom(&char_class, &first))
|
if (!ParseClassEscape(&char_class_1, &char_1, ranges, lead_ranges, trail_ranges, wide_ranges))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
if (current() == '-') {
|
if (current() == '-') {
|
||||||
Advance();
|
Advance();
|
||||||
|
|
@ -1088,41 +879,49 @@ RegExpParser<CharT>::ParseCharacterClass()
|
||||||
// following code report an error.
|
// following code report an error.
|
||||||
break;
|
break;
|
||||||
} else if (current() == ']') {
|
} else if (current() == ']') {
|
||||||
if (unicode_) {
|
// if the last item was not a class, add it verbatim.
|
||||||
AddCharOrEscapeUnicode(alloc, ranges, lead_ranges, trail_ranges, wide_ranges,
|
if (char_class_1 == kNoCharClass) {
|
||||||
char_class, first, ignore_case_);
|
if (unicode_) {
|
||||||
} else {
|
CharacterRange::AddCharUnicode(alloc, ranges, lead_ranges, trail_ranges, wide_ranges, char_1);
|
||||||
AddCharOrEscape(alloc, ranges, char_class, first);
|
} else {
|
||||||
|
ranges->append(CharacterRange::Singleton(char_1));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
// Hyphen at the end of a class. Treat the '-' verbatim.
|
||||||
ranges->append(CharacterRange::Singleton('-'));
|
ranges->append(CharacterRange::Singleton('-'));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
char16_t char_class_2 = kNoCharClass;
|
char16_t char_class_2 = kNoCharClass;
|
||||||
widechar next = 0;
|
widechar char_2 = 0;
|
||||||
if (!ParseClassAtom(&char_class_2, &next))
|
if (!ParseClassEscape(&char_class_2, &char_2, ranges, lead_ranges, trail_ranges, wide_ranges))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
if (char_class != kNoCharClass || char_class_2 != kNoCharClass) {
|
if (char_class_1 != kNoCharClass || char_class_2 != kNoCharClass) {
|
||||||
if (unicode_)
|
if (unicode_)
|
||||||
return ReportError(JSMSG_RANGE_WITH_CLASS_ESCAPE);
|
return ReportError(JSMSG_RANGE_WITH_CLASS_ESCAPE);
|
||||||
|
|
||||||
// Either end is an escaped character class. Treat the '-' verbatim.
|
// Either end is an escaped character class. Treat the '-' verbatim and add the
|
||||||
AddCharOrEscape(alloc, ranges, char_class, first);
|
// character that isn't a class
|
||||||
|
if (char_class_1 == kNoCharClass)
|
||||||
|
ranges->append(CharacterRange::Singleton(char_1));
|
||||||
ranges->append(CharacterRange::Singleton('-'));
|
ranges->append(CharacterRange::Singleton('-'));
|
||||||
AddCharOrEscape(alloc, ranges, char_class_2, next);
|
if (char_class_1 == kNoCharClass)
|
||||||
|
ranges->append(CharacterRange::Singleton(char_2));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (first > next)
|
if (char_1 > char_2)
|
||||||
return ReportError(JSMSG_BAD_CLASS_RANGE);
|
return ReportError(JSMSG_BAD_CLASS_RANGE);
|
||||||
if (unicode_)
|
if (unicode_)
|
||||||
AddUnicodeRange(alloc, ranges, lead_ranges, trail_ranges,wide_ranges, first, next);
|
CharacterRange::AddUnicodeRange(alloc, ranges, lead_ranges, trail_ranges, wide_ranges, char_1, char_2);
|
||||||
else
|
else
|
||||||
ranges->append(CharacterRange::Range(first, next));
|
ranges->append(CharacterRange::Range(char_1, char_2));
|
||||||
} else {
|
} else {
|
||||||
if (unicode_) {
|
// if the last item was not a class, add it verbatim.
|
||||||
AddCharOrEscapeUnicode(alloc, ranges, lead_ranges, trail_ranges, wide_ranges,
|
if (char_class_1 == kNoCharClass) {
|
||||||
char_class, first, ignore_case_);
|
if (unicode_) {
|
||||||
} else {
|
CharacterRange::AddCharUnicode(alloc, ranges, lead_ranges, trail_ranges, wide_ranges, char_1);
|
||||||
AddCharOrEscape(alloc, ranges, char_class, first);
|
} else {
|
||||||
|
ranges->append(CharacterRange::Singleton(char_1));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1135,22 +934,26 @@ RegExpParser<CharT>::ParseCharacterClass()
|
||||||
is_negated = !is_negated;
|
is_negated = !is_negated;
|
||||||
}
|
}
|
||||||
return alloc->newInfallible<RegExpCharacterClass>(ranges, is_negated);
|
return alloc->newInfallible<RegExpCharacterClass>(ranges, is_negated);
|
||||||
}
|
} else {
|
||||||
|
if (!is_negated && ranges->length() == 0 && lead_ranges->length() == 0 &&
|
||||||
|
trail_ranges->length() == 0 && wide_ranges->length() == 0)
|
||||||
|
{
|
||||||
|
ranges->append(CharacterRange::Everything());
|
||||||
|
return alloc->newInfallible<RegExpCharacterClass>(ranges, true);
|
||||||
|
}
|
||||||
|
|
||||||
if (!is_negated && ranges->length() == 0 && lead_ranges->length() == 0 &&
|
return UnicodeRangesAtom(alloc, ranges, lead_ranges, trail_ranges, wide_ranges, is_negated,
|
||||||
trail_ranges->length() == 0 && wide_ranges->length() == 0)
|
ignore_case_);
|
||||||
{
|
|
||||||
ranges->append(CharacterRange::Everything());
|
|
||||||
return alloc->newInfallible<RegExpCharacterClass>(ranges, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return UnicodeRangesAtom(alloc, ranges, lead_ranges, trail_ranges, wide_ranges, is_negated,
|
|
||||||
ignore_case_);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename CharT>
|
template <typename CharT>
|
||||||
bool
|
bool
|
||||||
RegExpParser<CharT>::ParseClassAtom(char16_t* char_class, widechar* value)
|
RegExpParser<CharT>::ParseClassEscape(char16_t* char_class, widechar *value,
|
||||||
|
CharacterRangeVector* ranges,
|
||||||
|
CharacterRangeVector* lead_ranges,
|
||||||
|
CharacterRangeVector* trail_ranges,
|
||||||
|
WideCharRangeVector* wide_ranges)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(*char_class == kNoCharClass);
|
MOZ_ASSERT(*char_class == kNoCharClass);
|
||||||
widechar first = current();
|
widechar first = current();
|
||||||
|
|
@ -1159,6 +962,13 @@ RegExpParser<CharT>::ParseClassAtom(char16_t* char_class, widechar* value)
|
||||||
case 'w': case 'W': case 'd': case 'D': case 's': case 'S': {
|
case 'w': case 'W': case 'd': case 'D': case 's': case 'S': {
|
||||||
*char_class = Next();
|
*char_class = Next();
|
||||||
Advance(2);
|
Advance(2);
|
||||||
|
// add character range to ranges immediately
|
||||||
|
if (unicode_) {
|
||||||
|
CharacterRange::AddCharOrEscapeUnicode(alloc, ranges, lead_ranges, trail_ranges, wide_ranges,
|
||||||
|
*char_class, 0, ignore_case_);
|
||||||
|
} else {
|
||||||
|
CharacterRange::AddCharOrEscape(alloc, ranges, *char_class, 0);
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case kEndMarker:
|
case kEndMarker:
|
||||||
|
|
@ -1720,8 +1530,8 @@ UnicodeCharacterClassEscapeAtom(LifoAlloc* alloc, char16_t char_class, bool igno
|
||||||
CharacterRangeVector* lead_ranges = alloc->newInfallible<CharacterRangeVector>(*alloc);
|
CharacterRangeVector* lead_ranges = alloc->newInfallible<CharacterRangeVector>(*alloc);
|
||||||
CharacterRangeVector* trail_ranges = alloc->newInfallible<CharacterRangeVector>(*alloc);
|
CharacterRangeVector* trail_ranges = alloc->newInfallible<CharacterRangeVector>(*alloc);
|
||||||
WideCharRangeVector* wide_ranges = alloc->newInfallible<WideCharRangeVector>(*alloc);
|
WideCharRangeVector* wide_ranges = alloc->newInfallible<WideCharRangeVector>(*alloc);
|
||||||
AddCharOrEscapeUnicode(alloc, ranges, lead_ranges, trail_ranges, wide_ranges, char_class, 0,
|
CharacterRange::AddCharOrEscapeUnicode(alloc, ranges, lead_ranges, trail_ranges, wide_ranges,
|
||||||
ignore_case);
|
char_class, 0, ignore_case);
|
||||||
|
|
||||||
return UnicodeRangesAtom(alloc, ranges, lead_ranges, trail_ranges, wide_ranges, false, false);
|
return UnicodeRangesAtom(alloc, ranges, lead_ranges, trail_ranges, wide_ranges, false, false);
|
||||||
}
|
}
|
||||||
|
|
@ -1951,25 +1761,23 @@ RegExpParser<CharT>::ParseDisjunction()
|
||||||
// CharacterClassEscape :: one of
|
// CharacterClassEscape :: one of
|
||||||
// d D s S w W
|
// d D s S w W
|
||||||
case 'D': case 'S': case 'W':
|
case 'D': case 'S': case 'W':
|
||||||
if (unicode_) {
|
|
||||||
Advance();
|
|
||||||
builder->AddAtom(UnicodeCharacterClassEscapeAtom(alloc, current(),
|
|
||||||
ignore_case_));
|
|
||||||
Advance();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
MOZ_FALLTHROUGH;
|
|
||||||
case 'd': case 's': case 'w': {
|
case 'd': case 's': case 'w': {
|
||||||
widechar c = Next();
|
widechar c = Next();
|
||||||
|
bool negated = c <= 'Z';
|
||||||
Advance(2);
|
Advance(2);
|
||||||
CharacterRangeVector* ranges =
|
if (unicode_ && negated) {
|
||||||
alloc->newInfallible<CharacterRangeVector>(*alloc);
|
// must generate negative lookarounds for lone surrogates, done by AddCharOrEscapeUnicode
|
||||||
if (unicode_)
|
builder->AddAtom(UnicodeCharacterClassEscapeAtom(alloc, c, ignore_case_));
|
||||||
CharacterRange::AddClassEscapeUnicode(alloc, c, ranges, ignore_case_);
|
} else {
|
||||||
else
|
// only match positive ranges
|
||||||
CharacterRange::AddClassEscape(alloc, c, ranges);
|
CharacterRangeVector* ranges = alloc->newInfallible<CharacterRangeVector>(*alloc);
|
||||||
RegExpTree* atom = alloc->newInfallible<RegExpCharacterClass>(ranges, false);
|
if (unicode_)
|
||||||
builder->AddAtom(atom);
|
CharacterRange::AddClassEscapeUnicode(alloc, c, ranges, ignore_case_);
|
||||||
|
else
|
||||||
|
CharacterRange::AddClassEscape(alloc, c, ranges);
|
||||||
|
RegExpTree* atom = alloc->newInfallible<RegExpCharacterClass>(ranges, false);
|
||||||
|
builder->AddAtom(atom);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case '1': case '2': case '3': case '4': case '5': case '6':
|
case '1': case '2': case '3': case '4': case '5': case '6':
|
||||||
|
|
|
||||||
|
|
@ -133,9 +133,6 @@ class BufferedVector
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// Characters parsed by RegExpParser can be either char16_t or kEndMarker.
|
|
||||||
typedef uint32_t widechar;
|
|
||||||
|
|
||||||
// Accumulates RegExp atoms and assertions into lists of terms and alternatives.
|
// Accumulates RegExp atoms and assertions into lists of terms and alternatives.
|
||||||
class RegExpBuilder
|
class RegExpBuilder
|
||||||
{
|
{
|
||||||
|
|
@ -215,7 +212,14 @@ class RegExpParser
|
||||||
// can be reparsed.
|
// can be reparsed.
|
||||||
bool ParseBackReferenceIndex(int* index_out);
|
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);
|
RegExpTree* ReportError(unsigned errorNumber, const char* param = nullptr);
|
||||||
void Advance();
|
void Advance();
|
||||||
void Advance(int dist) {
|
void Advance(int dist) {
|
||||||
|
|
|
||||||
|
|
@ -153,6 +153,7 @@ UNIFIED_SOURCES += [
|
||||||
'irregexp/NativeRegExpMacroAssembler.cpp',
|
'irregexp/NativeRegExpMacroAssembler.cpp',
|
||||||
'irregexp/RegExpAST.cpp',
|
'irregexp/RegExpAST.cpp',
|
||||||
'irregexp/RegExpCharacters.cpp',
|
'irregexp/RegExpCharacters.cpp',
|
||||||
|
'irregexp/RegExpCharRanges.cpp',
|
||||||
'irregexp/RegExpEngine.cpp',
|
'irregexp/RegExpEngine.cpp',
|
||||||
'irregexp/RegExpInterpreter.cpp',
|
'irregexp/RegExpInterpreter.cpp',
|
||||||
'irregexp/RegExpMacroAssembler.cpp',
|
'irregexp/RegExpMacroAssembler.cpp',
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue