From 05a8a5271c5bd6060893cb464d8630202c32eb93 Mon Sep 17 00:00:00 2001 From: Martok Date: Wed, 21 Dec 2022 18:53:26 +0100 Subject: [PATCH] No issue - Refactor parsing of unicode escapes to be more general --- js/src/irregexp/RegExpParser.cpp | 125 ++++++++++++++++--------------- js/src/irregexp/RegExpParser.h | 8 +- 2 files changed, 69 insertions(+), 64 deletions(-) diff --git a/js/src/irregexp/RegExpParser.cpp b/js/src/irregexp/RegExpParser.cpp index 1233aa316c..c6b8727048 100644 --- a/js/src/irregexp/RegExpParser.cpp +++ b/js/src/irregexp/RegExpParser.cpp @@ -90,6 +90,26 @@ RegExpBuilder::AddCharacter(char16_t c) #endif } +// forward declare atom helpers from below +static inline RegExpTree* SurrogatePairAtom(LifoAlloc* alloc, char16_t lead, char16_t trail, bool ignore_case); +static inline RegExpTree* LeadSurrogateAtom(LifoAlloc* alloc, char16_t value); +static inline RegExpTree* TrailSurrogateAtom(LifoAlloc* alloc, char16_t value); + +void +RegExpBuilder::AddUnicodeCharacter(widechar c, bool ignore_case) { + if (c > unicode::UTF16Max) { + char16_t lead, trail; + unicode::UTF16Encode(c, &lead, &trail); + AddAtom(SurrogatePairAtom(alloc, lead, trail, ignore_case)); + } else if (unicode::IsLeadSurrogate(c)) { + AddAtom(LeadSurrogateAtom(alloc, c)); + } else if (unicode::IsTrailSurrogate(c)) { + AddAtom(TrailSurrogateAtom(alloc, c)); + } else { + AddCharacter(static_cast(c)); + } +} + void RegExpBuilder::AddEmpty() { @@ -362,6 +382,39 @@ RegExpParser::ParseBracedHexEscape(widechar* value) return true; } +template +bool +RegExpParser::ParseUnicodeEscape(widechar* value) +{ + // Parse a RegExpUnicodeEscapeSequence + // Both \uxxxx and \u{xxxxx} are allowed. \u has already been consumed. + const CharT* start = position(); + if (current() == '{' && unicode_) { + bool result = ParseBracedHexEscape(value); + if (!result) { + Reset(start); + } + return result; + } + // \u but no {, or \u{...} escapes not allowed. + bool result = ParseHexEscape(4, value); + if (result && unicode_ && unicode::IsLeadSurrogate(static_cast(*value)) && current() == '\\') { + // Attempt to read trail surrogate. + const CharT* start = position(); + if (Next() == 'u') { + Advance(2); + widechar trail; + if (ParseHexEscape(4, &trail) && + unicode::IsTrailSurrogate(static_cast(trail))) { + *value = unicode::UTF16Decode(static_cast(*value), static_cast(trail)); + return true; + } + } + Reset(start); + } + return result; +} + template bool RegExpParser::ParseTrailSurrogate(widechar* value) @@ -560,30 +613,13 @@ RegExpParser::ParseClassCharacterEscape(widechar* code) case 'u': { Advance(); widechar value; - if (unicode_) { - if (current() == '{') { - if (!ParseBracedHexEscape(&value)) - return false; - *code = value; - return true; - } - if (ParseHexEscape(4, &value)) { - if (unicode::IsLeadSurrogate(value)) { - widechar trail; - if (ParseTrailSurrogate(&trail)) { - *code = unicode::UTF16Decode(value, trail); - return true; - } - } - *code = value; - return true; - } - ReportError(JSMSG_INVALID_UNICODE_ESCAPE); - return false; + if (ParseUnicodeEscape(&value)) { + *code = value; + return true; } - if (ParseHexEscape(4, &value)) { - *code = value; - return true; + if (unicode_) { + ReportError(JSMSG_INVALID_UNICODE_ESCAPE); + return false; } // If \u is not followed by a four-digit or braced hexadecimal, treat it // as an identity escape. @@ -1789,45 +1825,12 @@ RegExpParser::ParseDisjunction() case 'u': { Advance(2); widechar value; - if (unicode_) { - if (current() == '{') { - if (!ParseBracedHexEscape(&value)) - return nullptr; - if (unicode::IsLeadSurrogate(value)) { - builder->AddAtom(LeadSurrogateAtom(alloc, value)); - } else if (unicode::IsTrailSurrogate(value)) { - builder->AddAtom(TrailSurrogateAtom(alloc, value)); - } else if (value >= unicode::NonBMPMin) { - char16_t lead, trail; - unicode::UTF16Encode(value, &lead, &trail); - builder->AddAtom(SurrogatePairAtom(alloc, lead, trail, - ignore_case_)); - } else { - builder->AddCharacter(value); - } - } else if (ParseHexEscape(4, &value)) { - if (unicode::IsLeadSurrogate(value)) { - widechar trail; - if (ParseTrailSurrogate(&trail)) { - builder->AddAtom(SurrogatePairAtom(alloc, value, trail, - ignore_case_)); - } else { - builder->AddAtom(LeadSurrogateAtom(alloc, value)); - } - } else if (unicode::IsTrailSurrogate(value)) { - builder->AddAtom(TrailSurrogateAtom(alloc, value)); - } else { - builder->AddCharacter(value); - } - } else { - return ReportError(JSMSG_INVALID_UNICODE_ESCAPE); - } - break; - } - if (ParseHexEscape(4, &value)) { - builder->AddCharacter(value); + if (ParseUnicodeEscape(&value)) { + builder->AddUnicodeCharacter(value, ignore_case_); + } else if (!unicode_) { + builder->AddCharacter('u'); } else { - builder->AddCharacter('u'); + return ReportError(JSMSG_INVALID_UNICODE_ESCAPE); } break; } diff --git a/js/src/irregexp/RegExpParser.h b/js/src/irregexp/RegExpParser.h index 72b35718cc..28a2d58220 100644 --- a/js/src/irregexp/RegExpParser.h +++ b/js/src/irregexp/RegExpParser.h @@ -133,12 +133,16 @@ 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. 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 +172,6 @@ class RegExpBuilder #endif }; -// Characters parsed by RegExpParser can be either char16_t or kEndMarker. -typedef uint32_t widechar; - template class RegExpParser { @@ -198,6 +199,7 @@ 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);