1 // Copyright 2016 the V8 project authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef V8_REGEXP_REGEXP_PARSER_H_ 6 #define V8_REGEXP_REGEXP_PARSER_H_ 7 8 #include "src/objects.h" 9 #include "src/regexp/regexp-ast.h" 10 #include "src/zone/zone.h" 11 12 namespace v8 { 13 namespace internal { 14 15 struct RegExpCompileData; 16 17 18 // A BufferedZoneList is an automatically growing list, just like (and backed 19 // by) a ZoneList, that is optimized for the case of adding and removing 20 // a single element. The last element added is stored outside the backing list, 21 // and if no more than one element is ever added, the ZoneList isn't even 22 // allocated. 23 // Elements must not be NULL pointers. 24 template <typename T, int initial_size> 25 class BufferedZoneList { 26 public: BufferedZoneList()27 BufferedZoneList() : list_(NULL), last_(NULL) {} 28 29 // Adds element at end of list. This element is buffered and can 30 // be read using last() or removed using RemoveLast until a new Add or until 31 // RemoveLast or GetList has been called. Add(T * value,Zone * zone)32 void Add(T* value, Zone* zone) { 33 if (last_ != NULL) { 34 if (list_ == NULL) { 35 list_ = new (zone) ZoneList<T*>(initial_size, zone); 36 } 37 list_->Add(last_, zone); 38 } 39 last_ = value; 40 } 41 last()42 T* last() { 43 DCHECK(last_ != NULL); 44 return last_; 45 } 46 RemoveLast()47 T* RemoveLast() { 48 DCHECK(last_ != NULL); 49 T* result = last_; 50 if ((list_ != NULL) && (list_->length() > 0)) 51 last_ = list_->RemoveLast(); 52 else 53 last_ = NULL; 54 return result; 55 } 56 Get(int i)57 T* Get(int i) { 58 DCHECK((0 <= i) && (i < length())); 59 if (list_ == NULL) { 60 DCHECK_EQ(0, i); 61 return last_; 62 } else { 63 if (i == list_->length()) { 64 DCHECK(last_ != NULL); 65 return last_; 66 } else { 67 return list_->at(i); 68 } 69 } 70 } 71 Clear()72 void Clear() { 73 list_ = NULL; 74 last_ = NULL; 75 } 76 length()77 int length() { 78 int length = (list_ == NULL) ? 0 : list_->length(); 79 return length + ((last_ == NULL) ? 0 : 1); 80 } 81 GetList(Zone * zone)82 ZoneList<T*>* GetList(Zone* zone) { 83 if (list_ == NULL) { 84 list_ = new (zone) ZoneList<T*>(initial_size, zone); 85 } 86 if (last_ != NULL) { 87 list_->Add(last_, zone); 88 last_ = NULL; 89 } 90 return list_; 91 } 92 93 private: 94 ZoneList<T*>* list_; 95 T* last_; 96 }; 97 98 99 // Accumulates RegExp atoms and assertions into lists of terms and alternatives. 100 class RegExpBuilder : public ZoneObject { 101 public: 102 RegExpBuilder(Zone* zone, bool ignore_case, bool unicode); 103 void AddCharacter(uc16 character); 104 void AddUnicodeCharacter(uc32 character); 105 void AddEscapedUnicodeCharacter(uc32 character); 106 // "Adds" an empty expression. Does nothing except consume a 107 // following quantifier 108 void AddEmpty(); 109 void AddCharacterClass(RegExpCharacterClass* cc); 110 void AddCharacterClassForDesugaring(uc32 c); 111 void AddAtom(RegExpTree* tree); 112 void AddTerm(RegExpTree* tree); 113 void AddAssertion(RegExpTree* tree); 114 void NewAlternative(); // '|' 115 bool AddQuantifierToAtom(int min, int max, 116 RegExpQuantifier::QuantifierType type); 117 RegExpTree* ToRegExp(); 118 119 private: 120 static const uc16 kNoPendingSurrogate = 0; 121 void AddLeadSurrogate(uc16 lead_surrogate); 122 void AddTrailSurrogate(uc16 trail_surrogate); 123 void FlushPendingSurrogate(); 124 void FlushCharacters(); 125 void FlushText(); 126 void FlushTerms(); 127 bool NeedsDesugaringForUnicode(RegExpCharacterClass* cc); 128 bool NeedsDesugaringForIgnoreCase(uc32 c); zone()129 Zone* zone() const { return zone_; } ignore_case()130 bool ignore_case() const { return ignore_case_; } unicode()131 bool unicode() const { return unicode_; } 132 133 Zone* zone_; 134 bool pending_empty_; 135 bool ignore_case_; 136 bool unicode_; 137 ZoneList<uc16>* characters_; 138 uc16 pending_surrogate_; 139 BufferedZoneList<RegExpTree, 2> terms_; 140 BufferedZoneList<RegExpTree, 2> text_; 141 BufferedZoneList<RegExpTree, 2> alternatives_; 142 #ifdef DEBUG 143 enum { ADD_NONE, ADD_CHAR, ADD_TERM, ADD_ASSERT, ADD_ATOM } last_added_; 144 #define LAST(x) last_added_ = x; 145 #else 146 #define LAST(x) 147 #endif 148 }; 149 150 151 class RegExpParser BASE_EMBEDDED { 152 public: 153 RegExpParser(FlatStringReader* in, Handle<String>* error, 154 JSRegExp::Flags flags, Isolate* isolate, Zone* zone); 155 156 static bool ParseRegExp(Isolate* isolate, Zone* zone, FlatStringReader* input, 157 JSRegExp::Flags flags, RegExpCompileData* result); 158 159 RegExpTree* ParsePattern(); 160 RegExpTree* ParseDisjunction(); 161 RegExpTree* ParseGroup(); 162 RegExpTree* ParseCharacterClass(); 163 164 // Parses a {...,...} quantifier and stores the range in the given 165 // out parameters. 166 bool ParseIntervalQuantifier(int* min_out, int* max_out); 167 168 // Parses and returns a single escaped character. The character 169 // must not be 'b' or 'B' since they are usually handle specially. 170 uc32 ParseClassCharacterEscape(); 171 172 // Checks whether the following is a length-digit hexadecimal number, 173 // and sets the value if it is. 174 bool ParseHexEscape(int length, uc32* value); 175 bool ParseUnicodeEscape(uc32* value); 176 bool ParseUnlimitedLengthHexNumber(int max_value, uc32* value); 177 bool ParsePropertyClass(ZoneList<CharacterRange>* result, bool negate); 178 179 uc32 ParseOctalLiteral(); 180 181 // Tries to parse the input as a back reference. If successful it 182 // stores the result in the output parameter and returns true. If 183 // it fails it will push back the characters read so the same characters 184 // can be reparsed. 185 bool ParseBackReferenceIndex(int* index_out); 186 187 bool ParseClassProperty(ZoneList<CharacterRange>* result); 188 CharacterRange ParseClassAtom(uc16* char_class); 189 RegExpTree* ReportError(Vector<const char> message); 190 void Advance(); 191 void Advance(int dist); 192 void Reset(int pos); 193 194 // Reports whether the pattern might be used as a literal search string. 195 // Only use if the result of the parse is a single atom node. 196 bool simple(); contains_anchor()197 bool contains_anchor() { return contains_anchor_; } set_contains_anchor()198 void set_contains_anchor() { contains_anchor_ = true; } captures_started()199 int captures_started() { return captures_started_; } position()200 int position() { return next_pos_ - 1; } failed()201 bool failed() { return failed_; } ignore_case()202 bool ignore_case() const { return ignore_case_; } multiline()203 bool multiline() const { return multiline_; } unicode()204 bool unicode() const { return unicode_; } 205 206 static bool IsSyntaxCharacterOrSlash(uc32 c); 207 208 static const int kMaxCaptures = 1 << 16; 209 static const uc32 kEndMarker = (1 << 21); 210 211 private: 212 enum SubexpressionType { 213 INITIAL, 214 CAPTURE, // All positive values represent captures. 215 POSITIVE_LOOKAROUND, 216 NEGATIVE_LOOKAROUND, 217 GROUPING 218 }; 219 220 class RegExpParserState : public ZoneObject { 221 public: RegExpParserState(RegExpParserState * previous_state,SubexpressionType group_type,RegExpLookaround::Type lookaround_type,int disjunction_capture_index,const ZoneVector<uc16> * capture_name,bool ignore_case,bool unicode,Zone * zone)222 RegExpParserState(RegExpParserState* previous_state, 223 SubexpressionType group_type, 224 RegExpLookaround::Type lookaround_type, 225 int disjunction_capture_index, 226 const ZoneVector<uc16>* capture_name, bool ignore_case, 227 bool unicode, Zone* zone) 228 : previous_state_(previous_state), 229 builder_(new (zone) RegExpBuilder(zone, ignore_case, unicode)), 230 group_type_(group_type), 231 lookaround_type_(lookaround_type), 232 disjunction_capture_index_(disjunction_capture_index), 233 capture_name_(capture_name) {} 234 // Parser state of containing expression, if any. previous_state()235 RegExpParserState* previous_state() { return previous_state_; } IsSubexpression()236 bool IsSubexpression() { return previous_state_ != NULL; } 237 // RegExpBuilder building this regexp's AST. builder()238 RegExpBuilder* builder() { return builder_; } 239 // Type of regexp being parsed (parenthesized group or entire regexp). group_type()240 SubexpressionType group_type() { return group_type_; } 241 // Lookahead or Lookbehind. lookaround_type()242 RegExpLookaround::Type lookaround_type() { return lookaround_type_; } 243 // Index in captures array of first capture in this sub-expression, if any. 244 // Also the capture index of this sub-expression itself, if group_type 245 // is CAPTURE. capture_index()246 int capture_index() { return disjunction_capture_index_; } 247 // The name of the current sub-expression, if group_type is CAPTURE. Only 248 // used for named captures. capture_name()249 const ZoneVector<uc16>* capture_name() { return capture_name_; } 250 IsNamedCapture()251 bool IsNamedCapture() const { return capture_name_ != nullptr; } 252 253 // Check whether the parser is inside a capture group with the given index. 254 bool IsInsideCaptureGroup(int index); 255 // Check whether the parser is inside a capture group with the given name. 256 bool IsInsideCaptureGroup(const ZoneVector<uc16>* name); 257 258 private: 259 // Linked list implementation of stack of states. 260 RegExpParserState* previous_state_; 261 // Builder for the stored disjunction. 262 RegExpBuilder* builder_; 263 // Stored disjunction type (capture, look-ahead or grouping), if any. 264 SubexpressionType group_type_; 265 // Stored read direction. 266 RegExpLookaround::Type lookaround_type_; 267 // Stored disjunction's capture index (if any). 268 int disjunction_capture_index_; 269 // Stored capture name (if any). 270 const ZoneVector<uc16>* capture_name_; 271 }; 272 273 // Return the 1-indexed RegExpCapture object, allocate if necessary. 274 RegExpCapture* GetCapture(int index); 275 276 // Creates a new named capture at the specified index. Must be called exactly 277 // once for each named capture. Fails if a capture with the same name is 278 // encountered. 279 bool CreateNamedCaptureAtIndex(const ZoneVector<uc16>* name, int index); 280 281 // Parses the name of a capture group (?<name>pattern). The name must adhere 282 // to IdentifierName in the ECMAScript standard. 283 const ZoneVector<uc16>* ParseCaptureGroupName(); 284 285 bool ParseNamedBackReference(RegExpBuilder* builder, 286 RegExpParserState* state); 287 288 // After the initial parsing pass, patch corresponding RegExpCapture objects 289 // into all RegExpBackReferences. This is done after initial parsing in order 290 // to avoid complicating cases in which references comes before the capture. 291 void PatchNamedBackReferences(); 292 293 Handle<FixedArray> CreateCaptureNameMap(); 294 isolate()295 Isolate* isolate() { return isolate_; } zone()296 Zone* zone() const { return zone_; } 297 current()298 uc32 current() { return current_; } has_more()299 bool has_more() { return has_more_; } has_next()300 bool has_next() { return next_pos_ < in()->length(); } 301 uc32 Next(); 302 template <bool update_position> 303 uc32 ReadNext(); in()304 FlatStringReader* in() { return in_; } 305 void ScanForCaptures(); 306 307 Isolate* isolate_; 308 Zone* zone_; 309 Handle<String>* error_; 310 ZoneList<RegExpCapture*>* captures_; 311 ZoneList<RegExpCapture*>* named_captures_; 312 ZoneList<RegExpBackReference*>* named_back_references_; 313 FlatStringReader* in_; 314 uc32 current_; 315 bool ignore_case_; 316 bool multiline_; 317 bool unicode_; 318 int next_pos_; 319 int captures_started_; 320 // The capture count is only valid after we have scanned for captures. 321 int capture_count_; 322 bool has_more_; 323 bool simple_; 324 bool contains_anchor_; 325 bool is_scanned_for_captures_; 326 bool failed_; 327 }; 328 329 } // namespace internal 330 } // namespace v8 331 332 #endif // V8_REGEXP_REGEXP_PARSER_H_ 333