1 // Copyright 2011 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 // A simple interpreter for the Irregexp byte code. 6 7 #ifdef V8_INTERPRETED_REGEXP 8 9 #include "src/regexp/interpreter-irregexp.h" 10 11 #include "src/ast/ast.h" 12 #include "src/objects-inl.h" 13 #include "src/regexp/bytecodes-irregexp.h" 14 #include "src/regexp/jsregexp.h" 15 #include "src/regexp/regexp-macro-assembler.h" 16 #include "src/unicode.h" 17 #include "src/utils.h" 18 19 #ifdef V8_I18N_SUPPORT 20 #include "unicode/uchar.h" 21 #endif // V8_I18N_SUPPORT 22 23 namespace v8 { 24 namespace internal { 25 26 typedef unibrow::Mapping<unibrow::Ecma262Canonicalize> Canonicalize; 27 28 static bool BackRefMatchesNoCase(Isolate* isolate, int from, int current, 29 int len, Vector<const uc16> subject, 30 bool unicode) { 31 Address offset_a = 32 reinterpret_cast<Address>(const_cast<uc16*>(&subject.at(from))); 33 Address offset_b = 34 reinterpret_cast<Address>(const_cast<uc16*>(&subject.at(current))); 35 size_t length = len * kUC16Size; 36 return RegExpMacroAssembler::CaseInsensitiveCompareUC16( 37 offset_a, offset_b, length, unicode ? nullptr : isolate) == 1; 38 } 39 40 41 static bool BackRefMatchesNoCase(Isolate* isolate, int from, int current, 42 int len, Vector<const uint8_t> subject, 43 bool unicode) { 44 // For Latin1 characters the unicode flag makes no difference. 45 for (int i = 0; i < len; i++) { 46 unsigned int old_char = subject[from++]; 47 unsigned int new_char = subject[current++]; 48 if (old_char == new_char) continue; 49 // Convert both characters to lower case. 50 old_char |= 0x20; 51 new_char |= 0x20; 52 if (old_char != new_char) return false; 53 // Not letters in the ASCII range and Latin-1 range. 54 if (!(old_char - 'a' <= 'z' - 'a') && 55 !(old_char - 224 <= 254 - 224 && old_char != 247)) { 56 return false; 57 } 58 } 59 return true; 60 } 61 62 63 #ifdef DEBUG 64 static void TraceInterpreter(const byte* code_base, 65 const byte* pc, 66 int stack_depth, 67 int current_position, 68 uint32_t current_char, 69 int bytecode_length, 70 const char* bytecode_name) { 71 if (FLAG_trace_regexp_bytecodes) { 72 bool printable = (current_char < 127 && current_char >= 32); 73 const char* format = 74 printable ? 75 "pc = %02x, sp = %d, curpos = %d, curchar = %08x (%c), bc = %s" : 76 "pc = %02x, sp = %d, curpos = %d, curchar = %08x .%c., bc = %s"; 77 PrintF(format, 78 pc - code_base, 79 stack_depth, 80 current_position, 81 current_char, 82 printable ? current_char : '.', 83 bytecode_name); 84 for (int i = 0; i < bytecode_length; i++) { 85 printf(", %02x", pc[i]); 86 } 87 printf(" "); 88 for (int i = 1; i < bytecode_length; i++) { 89 unsigned char b = pc[i]; 90 if (b < 127 && b >= 32) { 91 printf("%c", b); 92 } else { 93 printf("."); 94 } 95 } 96 printf("\n"); 97 } 98 } 99 100 101 #define BYTECODE(name) \ 102 case BC_##name: \ 103 TraceInterpreter(code_base, \ 104 pc, \ 105 static_cast<int>(backtrack_sp - backtrack_stack_base), \ 106 current, \ 107 current_char, \ 108 BC_##name##_LENGTH, \ 109 #name); 110 #else 111 #define BYTECODE(name) \ 112 case BC_##name: 113 #endif 114 115 116 static int32_t Load32Aligned(const byte* pc) { 117 DCHECK((reinterpret_cast<intptr_t>(pc) & 3) == 0); 118 return *reinterpret_cast<const int32_t *>(pc); 119 } 120 121 122 static int32_t Load16Aligned(const byte* pc) { 123 DCHECK((reinterpret_cast<intptr_t>(pc) & 1) == 0); 124 return *reinterpret_cast<const uint16_t *>(pc); 125 } 126 127 128 // A simple abstraction over the backtracking stack used by the interpreter. 129 // This backtracking stack does not grow automatically, but it ensures that the 130 // the memory held by the stack is released or remembered in a cache if the 131 // matching terminates. 132 class BacktrackStack { 133 public: 134 BacktrackStack() { data_ = NewArray<int>(kBacktrackStackSize); } 135 136 ~BacktrackStack() { 137 DeleteArray(data_); 138 } 139 140 int* data() const { return data_; } 141 142 int max_size() const { return kBacktrackStackSize; } 143 144 private: 145 static const int kBacktrackStackSize = 10000; 146 147 int* data_; 148 149 DISALLOW_COPY_AND_ASSIGN(BacktrackStack); 150 }; 151 152 153 template <typename Char> 154 static RegExpImpl::IrregexpResult RawMatch(Isolate* isolate, 155 const byte* code_base, 156 Vector<const Char> subject, 157 int* registers, 158 int current, 159 uint32_t current_char) { 160 const byte* pc = code_base; 161 // BacktrackStack ensures that the memory allocated for the backtracking stack 162 // is returned to the system or cached if there is no stack being cached at 163 // the moment. 164 BacktrackStack backtrack_stack; 165 int* backtrack_stack_base = backtrack_stack.data(); 166 int* backtrack_sp = backtrack_stack_base; 167 int backtrack_stack_space = backtrack_stack.max_size(); 168 #ifdef DEBUG 169 if (FLAG_trace_regexp_bytecodes) { 170 PrintF("\n\nStart bytecode interpreter\n\n"); 171 } 172 #endif 173 while (true) { 174 int32_t insn = Load32Aligned(pc); 175 switch (insn & BYTECODE_MASK) { 176 BYTECODE(BREAK) 177 UNREACHABLE(); 178 return RegExpImpl::RE_FAILURE; 179 BYTECODE(PUSH_CP) 180 if (--backtrack_stack_space < 0) { 181 return RegExpImpl::RE_EXCEPTION; 182 } 183 *backtrack_sp++ = current; 184 pc += BC_PUSH_CP_LENGTH; 185 break; 186 BYTECODE(PUSH_BT) 187 if (--backtrack_stack_space < 0) { 188 return RegExpImpl::RE_EXCEPTION; 189 } 190 *backtrack_sp++ = Load32Aligned(pc + 4); 191 pc += BC_PUSH_BT_LENGTH; 192 break; 193 BYTECODE(PUSH_REGISTER) 194 if (--backtrack_stack_space < 0) { 195 return RegExpImpl::RE_EXCEPTION; 196 } 197 *backtrack_sp++ = registers[insn >> BYTECODE_SHIFT]; 198 pc += BC_PUSH_REGISTER_LENGTH; 199 break; 200 BYTECODE(SET_REGISTER) 201 registers[insn >> BYTECODE_SHIFT] = Load32Aligned(pc + 4); 202 pc += BC_SET_REGISTER_LENGTH; 203 break; 204 BYTECODE(ADVANCE_REGISTER) 205 registers[insn >> BYTECODE_SHIFT] += Load32Aligned(pc + 4); 206 pc += BC_ADVANCE_REGISTER_LENGTH; 207 break; 208 BYTECODE(SET_REGISTER_TO_CP) 209 registers[insn >> BYTECODE_SHIFT] = current + Load32Aligned(pc + 4); 210 pc += BC_SET_REGISTER_TO_CP_LENGTH; 211 break; 212 BYTECODE(SET_CP_TO_REGISTER) 213 current = registers[insn >> BYTECODE_SHIFT]; 214 pc += BC_SET_CP_TO_REGISTER_LENGTH; 215 break; 216 BYTECODE(SET_REGISTER_TO_SP) 217 registers[insn >> BYTECODE_SHIFT] = 218 static_cast<int>(backtrack_sp - backtrack_stack_base); 219 pc += BC_SET_REGISTER_TO_SP_LENGTH; 220 break; 221 BYTECODE(SET_SP_TO_REGISTER) 222 backtrack_sp = backtrack_stack_base + registers[insn >> BYTECODE_SHIFT]; 223 backtrack_stack_space = backtrack_stack.max_size() - 224 static_cast<int>(backtrack_sp - backtrack_stack_base); 225 pc += BC_SET_SP_TO_REGISTER_LENGTH; 226 break; 227 BYTECODE(POP_CP) 228 backtrack_stack_space++; 229 --backtrack_sp; 230 current = *backtrack_sp; 231 pc += BC_POP_CP_LENGTH; 232 break; 233 BYTECODE(POP_BT) 234 backtrack_stack_space++; 235 --backtrack_sp; 236 pc = code_base + *backtrack_sp; 237 break; 238 BYTECODE(POP_REGISTER) 239 backtrack_stack_space++; 240 --backtrack_sp; 241 registers[insn >> BYTECODE_SHIFT] = *backtrack_sp; 242 pc += BC_POP_REGISTER_LENGTH; 243 break; 244 BYTECODE(FAIL) 245 return RegExpImpl::RE_FAILURE; 246 BYTECODE(SUCCEED) 247 return RegExpImpl::RE_SUCCESS; 248 BYTECODE(ADVANCE_CP) 249 current += insn >> BYTECODE_SHIFT; 250 pc += BC_ADVANCE_CP_LENGTH; 251 break; 252 BYTECODE(GOTO) 253 pc = code_base + Load32Aligned(pc + 4); 254 break; 255 BYTECODE(ADVANCE_CP_AND_GOTO) 256 current += insn >> BYTECODE_SHIFT; 257 pc = code_base + Load32Aligned(pc + 4); 258 break; 259 BYTECODE(CHECK_GREEDY) 260 if (current == backtrack_sp[-1]) { 261 backtrack_sp--; 262 backtrack_stack_space++; 263 pc = code_base + Load32Aligned(pc + 4); 264 } else { 265 pc += BC_CHECK_GREEDY_LENGTH; 266 } 267 break; 268 BYTECODE(LOAD_CURRENT_CHAR) { 269 int pos = current + (insn >> BYTECODE_SHIFT); 270 if (pos >= subject.length() || pos < 0) { 271 pc = code_base + Load32Aligned(pc + 4); 272 } else { 273 current_char = subject[pos]; 274 pc += BC_LOAD_CURRENT_CHAR_LENGTH; 275 } 276 break; 277 } 278 BYTECODE(LOAD_CURRENT_CHAR_UNCHECKED) { 279 int pos = current + (insn >> BYTECODE_SHIFT); 280 current_char = subject[pos]; 281 pc += BC_LOAD_CURRENT_CHAR_UNCHECKED_LENGTH; 282 break; 283 } 284 BYTECODE(LOAD_2_CURRENT_CHARS) { 285 int pos = current + (insn >> BYTECODE_SHIFT); 286 if (pos + 2 > subject.length() || pos < 0) { 287 pc = code_base + Load32Aligned(pc + 4); 288 } else { 289 Char next = subject[pos + 1]; 290 current_char = 291 (subject[pos] | (next << (kBitsPerByte * sizeof(Char)))); 292 pc += BC_LOAD_2_CURRENT_CHARS_LENGTH; 293 } 294 break; 295 } 296 BYTECODE(LOAD_2_CURRENT_CHARS_UNCHECKED) { 297 int pos = current + (insn >> BYTECODE_SHIFT); 298 Char next = subject[pos + 1]; 299 current_char = (subject[pos] | (next << (kBitsPerByte * sizeof(Char)))); 300 pc += BC_LOAD_2_CURRENT_CHARS_UNCHECKED_LENGTH; 301 break; 302 } 303 BYTECODE(LOAD_4_CURRENT_CHARS) { 304 DCHECK(sizeof(Char) == 1); 305 int pos = current + (insn >> BYTECODE_SHIFT); 306 if (pos + 4 > subject.length() || pos < 0) { 307 pc = code_base + Load32Aligned(pc + 4); 308 } else { 309 Char next1 = subject[pos + 1]; 310 Char next2 = subject[pos + 2]; 311 Char next3 = subject[pos + 3]; 312 current_char = (subject[pos] | 313 (next1 << 8) | 314 (next2 << 16) | 315 (next3 << 24)); 316 pc += BC_LOAD_4_CURRENT_CHARS_LENGTH; 317 } 318 break; 319 } 320 BYTECODE(LOAD_4_CURRENT_CHARS_UNCHECKED) { 321 DCHECK(sizeof(Char) == 1); 322 int pos = current + (insn >> BYTECODE_SHIFT); 323 Char next1 = subject[pos + 1]; 324 Char next2 = subject[pos + 2]; 325 Char next3 = subject[pos + 3]; 326 current_char = (subject[pos] | 327 (next1 << 8) | 328 (next2 << 16) | 329 (next3 << 24)); 330 pc += BC_LOAD_4_CURRENT_CHARS_UNCHECKED_LENGTH; 331 break; 332 } 333 BYTECODE(CHECK_4_CHARS) { 334 uint32_t c = Load32Aligned(pc + 4); 335 if (c == current_char) { 336 pc = code_base + Load32Aligned(pc + 8); 337 } else { 338 pc += BC_CHECK_4_CHARS_LENGTH; 339 } 340 break; 341 } 342 BYTECODE(CHECK_CHAR) { 343 uint32_t c = (insn >> BYTECODE_SHIFT); 344 if (c == current_char) { 345 pc = code_base + Load32Aligned(pc + 4); 346 } else { 347 pc += BC_CHECK_CHAR_LENGTH; 348 } 349 break; 350 } 351 BYTECODE(CHECK_NOT_4_CHARS) { 352 uint32_t c = Load32Aligned(pc + 4); 353 if (c != current_char) { 354 pc = code_base + Load32Aligned(pc + 8); 355 } else { 356 pc += BC_CHECK_NOT_4_CHARS_LENGTH; 357 } 358 break; 359 } 360 BYTECODE(CHECK_NOT_CHAR) { 361 uint32_t c = (insn >> BYTECODE_SHIFT); 362 if (c != current_char) { 363 pc = code_base + Load32Aligned(pc + 4); 364 } else { 365 pc += BC_CHECK_NOT_CHAR_LENGTH; 366 } 367 break; 368 } 369 BYTECODE(AND_CHECK_4_CHARS) { 370 uint32_t c = Load32Aligned(pc + 4); 371 if (c == (current_char & Load32Aligned(pc + 8))) { 372 pc = code_base + Load32Aligned(pc + 12); 373 } else { 374 pc += BC_AND_CHECK_4_CHARS_LENGTH; 375 } 376 break; 377 } 378 BYTECODE(AND_CHECK_CHAR) { 379 uint32_t c = (insn >> BYTECODE_SHIFT); 380 if (c == (current_char & Load32Aligned(pc + 4))) { 381 pc = code_base + Load32Aligned(pc + 8); 382 } else { 383 pc += BC_AND_CHECK_CHAR_LENGTH; 384 } 385 break; 386 } 387 BYTECODE(AND_CHECK_NOT_4_CHARS) { 388 uint32_t c = Load32Aligned(pc + 4); 389 if (c != (current_char & Load32Aligned(pc + 8))) { 390 pc = code_base + Load32Aligned(pc + 12); 391 } else { 392 pc += BC_AND_CHECK_NOT_4_CHARS_LENGTH; 393 } 394 break; 395 } 396 BYTECODE(AND_CHECK_NOT_CHAR) { 397 uint32_t c = (insn >> BYTECODE_SHIFT); 398 if (c != (current_char & Load32Aligned(pc + 4))) { 399 pc = code_base + Load32Aligned(pc + 8); 400 } else { 401 pc += BC_AND_CHECK_NOT_CHAR_LENGTH; 402 } 403 break; 404 } 405 BYTECODE(MINUS_AND_CHECK_NOT_CHAR) { 406 uint32_t c = (insn >> BYTECODE_SHIFT); 407 uint32_t minus = Load16Aligned(pc + 4); 408 uint32_t mask = Load16Aligned(pc + 6); 409 if (c != ((current_char - minus) & mask)) { 410 pc = code_base + Load32Aligned(pc + 8); 411 } else { 412 pc += BC_MINUS_AND_CHECK_NOT_CHAR_LENGTH; 413 } 414 break; 415 } 416 BYTECODE(CHECK_CHAR_IN_RANGE) { 417 uint32_t from = Load16Aligned(pc + 4); 418 uint32_t to = Load16Aligned(pc + 6); 419 if (from <= current_char && current_char <= to) { 420 pc = code_base + Load32Aligned(pc + 8); 421 } else { 422 pc += BC_CHECK_CHAR_IN_RANGE_LENGTH; 423 } 424 break; 425 } 426 BYTECODE(CHECK_CHAR_NOT_IN_RANGE) { 427 uint32_t from = Load16Aligned(pc + 4); 428 uint32_t to = Load16Aligned(pc + 6); 429 if (from > current_char || current_char > to) { 430 pc = code_base + Load32Aligned(pc + 8); 431 } else { 432 pc += BC_CHECK_CHAR_NOT_IN_RANGE_LENGTH; 433 } 434 break; 435 } 436 BYTECODE(CHECK_BIT_IN_TABLE) { 437 int mask = RegExpMacroAssembler::kTableMask; 438 byte b = pc[8 + ((current_char & mask) >> kBitsPerByteLog2)]; 439 int bit = (current_char & (kBitsPerByte - 1)); 440 if ((b & (1 << bit)) != 0) { 441 pc = code_base + Load32Aligned(pc + 4); 442 } else { 443 pc += BC_CHECK_BIT_IN_TABLE_LENGTH; 444 } 445 break; 446 } 447 BYTECODE(CHECK_LT) { 448 uint32_t limit = (insn >> BYTECODE_SHIFT); 449 if (current_char < limit) { 450 pc = code_base + Load32Aligned(pc + 4); 451 } else { 452 pc += BC_CHECK_LT_LENGTH; 453 } 454 break; 455 } 456 BYTECODE(CHECK_GT) { 457 uint32_t limit = (insn >> BYTECODE_SHIFT); 458 if (current_char > limit) { 459 pc = code_base + Load32Aligned(pc + 4); 460 } else { 461 pc += BC_CHECK_GT_LENGTH; 462 } 463 break; 464 } 465 BYTECODE(CHECK_REGISTER_LT) 466 if (registers[insn >> BYTECODE_SHIFT] < Load32Aligned(pc + 4)) { 467 pc = code_base + Load32Aligned(pc + 8); 468 } else { 469 pc += BC_CHECK_REGISTER_LT_LENGTH; 470 } 471 break; 472 BYTECODE(CHECK_REGISTER_GE) 473 if (registers[insn >> BYTECODE_SHIFT] >= Load32Aligned(pc + 4)) { 474 pc = code_base + Load32Aligned(pc + 8); 475 } else { 476 pc += BC_CHECK_REGISTER_GE_LENGTH; 477 } 478 break; 479 BYTECODE(CHECK_REGISTER_EQ_POS) 480 if (registers[insn >> BYTECODE_SHIFT] == current) { 481 pc = code_base + Load32Aligned(pc + 4); 482 } else { 483 pc += BC_CHECK_REGISTER_EQ_POS_LENGTH; 484 } 485 break; 486 BYTECODE(CHECK_NOT_REGS_EQUAL) 487 if (registers[insn >> BYTECODE_SHIFT] == 488 registers[Load32Aligned(pc + 4)]) { 489 pc += BC_CHECK_NOT_REGS_EQUAL_LENGTH; 490 } else { 491 pc = code_base + Load32Aligned(pc + 8); 492 } 493 break; 494 BYTECODE(CHECK_NOT_BACK_REF) { 495 int from = registers[insn >> BYTECODE_SHIFT]; 496 int len = registers[(insn >> BYTECODE_SHIFT) + 1] - from; 497 if (from >= 0 && len > 0) { 498 if (current + len > subject.length() || 499 CompareChars(&subject[from], &subject[current], len) != 0) { 500 pc = code_base + Load32Aligned(pc + 4); 501 break; 502 } 503 current += len; 504 } 505 pc += BC_CHECK_NOT_BACK_REF_LENGTH; 506 break; 507 } 508 BYTECODE(CHECK_NOT_BACK_REF_BACKWARD) { 509 int from = registers[insn >> BYTECODE_SHIFT]; 510 int len = registers[(insn >> BYTECODE_SHIFT) + 1] - from; 511 if (from >= 0 && len > 0) { 512 if (current - len < 0 || 513 CompareChars(&subject[from], &subject[current - len], len) != 0) { 514 pc = code_base + Load32Aligned(pc + 4); 515 break; 516 } 517 current -= len; 518 } 519 pc += BC_CHECK_NOT_BACK_REF_BACKWARD_LENGTH; 520 break; 521 } 522 BYTECODE(CHECK_NOT_BACK_REF_NO_CASE_UNICODE) 523 BYTECODE(CHECK_NOT_BACK_REF_NO_CASE) { 524 bool unicode = 525 (insn & BYTECODE_MASK) == BC_CHECK_NOT_BACK_REF_NO_CASE_UNICODE; 526 int from = registers[insn >> BYTECODE_SHIFT]; 527 int len = registers[(insn >> BYTECODE_SHIFT) + 1] - from; 528 if (from >= 0 && len > 0) { 529 if (current + len > subject.length() || 530 !BackRefMatchesNoCase(isolate, from, current, len, subject, 531 unicode)) { 532 pc = code_base + Load32Aligned(pc + 4); 533 break; 534 } 535 current += len; 536 } 537 pc += BC_CHECK_NOT_BACK_REF_NO_CASE_LENGTH; 538 break; 539 } 540 BYTECODE(CHECK_NOT_BACK_REF_NO_CASE_UNICODE_BACKWARD) 541 BYTECODE(CHECK_NOT_BACK_REF_NO_CASE_BACKWARD) { 542 bool unicode = (insn & BYTECODE_MASK) == 543 BC_CHECK_NOT_BACK_REF_NO_CASE_UNICODE_BACKWARD; 544 int from = registers[insn >> BYTECODE_SHIFT]; 545 int len = registers[(insn >> BYTECODE_SHIFT) + 1] - from; 546 if (from >= 0 && len > 0) { 547 if (current - len < 0 || 548 !BackRefMatchesNoCase(isolate, from, current - len, len, subject, 549 unicode)) { 550 pc = code_base + Load32Aligned(pc + 4); 551 break; 552 } 553 current -= len; 554 } 555 pc += BC_CHECK_NOT_BACK_REF_NO_CASE_BACKWARD_LENGTH; 556 break; 557 } 558 BYTECODE(CHECK_AT_START) 559 if (current == 0) { 560 pc = code_base + Load32Aligned(pc + 4); 561 } else { 562 pc += BC_CHECK_AT_START_LENGTH; 563 } 564 break; 565 BYTECODE(CHECK_NOT_AT_START) 566 if (current + (insn >> BYTECODE_SHIFT) == 0) { 567 pc += BC_CHECK_NOT_AT_START_LENGTH; 568 } else { 569 pc = code_base + Load32Aligned(pc + 4); 570 } 571 break; 572 BYTECODE(SET_CURRENT_POSITION_FROM_END) { 573 int by = static_cast<uint32_t>(insn) >> BYTECODE_SHIFT; 574 if (subject.length() - current > by) { 575 current = subject.length() - by; 576 current_char = subject[current - 1]; 577 } 578 pc += BC_SET_CURRENT_POSITION_FROM_END_LENGTH; 579 break; 580 } 581 default: 582 UNREACHABLE(); 583 break; 584 } 585 } 586 } 587 588 589 RegExpImpl::IrregexpResult IrregexpInterpreter::Match( 590 Isolate* isolate, 591 Handle<ByteArray> code_array, 592 Handle<String> subject, 593 int* registers, 594 int start_position) { 595 DCHECK(subject->IsFlat()); 596 597 DisallowHeapAllocation no_gc; 598 const byte* code_base = code_array->GetDataStartAddress(); 599 uc16 previous_char = '\n'; 600 String::FlatContent subject_content = subject->GetFlatContent(); 601 if (subject_content.IsOneByte()) { 602 Vector<const uint8_t> subject_vector = subject_content.ToOneByteVector(); 603 if (start_position != 0) previous_char = subject_vector[start_position - 1]; 604 return RawMatch(isolate, 605 code_base, 606 subject_vector, 607 registers, 608 start_position, 609 previous_char); 610 } else { 611 DCHECK(subject_content.IsTwoByte()); 612 Vector<const uc16> subject_vector = subject_content.ToUC16Vector(); 613 if (start_position != 0) previous_char = subject_vector[start_position - 1]; 614 return RawMatch(isolate, 615 code_base, 616 subject_vector, 617 registers, 618 start_position, 619 previous_char); 620 } 621 } 622 623 } // namespace internal 624 } // namespace v8 625 626 #endif // V8_INTERPRETED_REGEXP 627