tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

regexp-macro-assembler.cc (20739B)


      1 // Copyright 2012 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 #include "irregexp/imported/regexp-macro-assembler.h"
      6 
      7 #include "irregexp/imported/regexp-stack.h"
      8 #include "irregexp/imported/special-case.h"
      9 
     10 #ifdef V8_INTL_SUPPORT
     11 #include "unicode/uchar.h"
     12 #include "unicode/unistr.h"
     13 #endif  // V8_INTL_SUPPORT
     14 
     15 namespace v8 {
     16 namespace internal {
     17 
     18 RegExpMacroAssembler::RegExpMacroAssembler(Isolate* isolate, Zone* zone)
     19    : slow_safe_compiler_(false),
     20      backtrack_limit_(JSRegExp::kNoBacktrackLimit),
     21      global_mode_(NOT_GLOBAL),
     22      isolate_(isolate),
     23      zone_(zone) {}
     24 
     25 bool RegExpMacroAssembler::has_backtrack_limit() const {
     26  return backtrack_limit_ != JSRegExp::kNoBacktrackLimit;
     27 }
     28 
     29 // static
     30 int RegExpMacroAssembler::CaseInsensitiveCompareNonUnicode(Address byte_offset1,
     31                                                           Address byte_offset2,
     32                                                           size_t byte_length,
     33                                                           Isolate* isolate) {
     34 #ifdef V8_INTL_SUPPORT
     35  // This function is not allowed to cause a garbage collection.
     36  // A GC might move the calling generated code and invalidate the
     37  // return address on the stack.
     38  DisallowGarbageCollection no_gc;
     39  DCHECK_EQ(0, byte_length % 2);
     40  size_t length = byte_length / 2;
     41  base::uc16* substring1 = reinterpret_cast<base::uc16*>(byte_offset1);
     42  base::uc16* substring2 = reinterpret_cast<base::uc16*>(byte_offset2);
     43 
     44  for (size_t i = 0; i < length; i++) {
     45    UChar32 c1 = RegExpCaseFolding::Canonicalize(substring1[i]);
     46    UChar32 c2 = RegExpCaseFolding::Canonicalize(substring2[i]);
     47    if (c1 != c2) {
     48      return 0;
     49    }
     50  }
     51  return 1;
     52 #else
     53  return CaseInsensitiveCompareUnicode(byte_offset1, byte_offset2, byte_length,
     54                                       isolate);
     55 #endif
     56 }
     57 
     58 // static
     59 int RegExpMacroAssembler::CaseInsensitiveCompareUnicode(Address byte_offset1,
     60                                                        Address byte_offset2,
     61                                                        size_t byte_length,
     62                                                        Isolate* isolate) {
     63  // This function is not allowed to cause a garbage collection.
     64  // A GC might move the calling generated code and invalidate the
     65  // return address on the stack.
     66  DisallowGarbageCollection no_gc;
     67  DCHECK_EQ(0, byte_length % 2);
     68 
     69 #ifdef V8_INTL_SUPPORT
     70  int32_t length = static_cast<int32_t>(byte_length >> 1);
     71  icu::UnicodeString uni_str_1(reinterpret_cast<const char16_t*>(byte_offset1),
     72                               length);
     73  return uni_str_1.caseCompare(reinterpret_cast<const char16_t*>(byte_offset2),
     74                               length, U_FOLD_CASE_DEFAULT) == 0;
     75 #else
     76  base::uc16* substring1 = reinterpret_cast<base::uc16*>(byte_offset1);
     77  base::uc16* substring2 = reinterpret_cast<base::uc16*>(byte_offset2);
     78  size_t length = byte_length >> 1;
     79  DCHECK_NOT_NULL(isolate);
     80  unibrow::Mapping<unibrow::Ecma262Canonicalize>* canonicalize =
     81      isolate->regexp_macro_assembler_canonicalize();
     82  for (size_t i = 0; i < length; i++) {
     83    unibrow::uchar c1 = substring1[i];
     84    unibrow::uchar c2 = substring2[i];
     85    if (c1 != c2) {
     86      unibrow::uchar s1[1] = {c1};
     87      canonicalize->get(c1, '\0', s1);
     88      if (s1[0] != c2) {
     89        unibrow::uchar s2[1] = {c2};
     90        canonicalize->get(c2, '\0', s2);
     91        if (s1[0] != s2[0]) {
     92          return 0;
     93        }
     94      }
     95    }
     96  }
     97  return 1;
     98 #endif  // V8_INTL_SUPPORT
     99 }
    100 
    101 namespace {
    102 
    103 uint32_t Hash(const ZoneList<CharacterRange>* ranges) {
    104  size_t seed = 0;
    105  for (int i = 0; i < ranges->length(); i++) {
    106    const CharacterRange& r = ranges->at(i);
    107    seed = base::hash_combine(seed, r.from(), r.to());
    108  }
    109  return static_cast<uint32_t>(seed);
    110 }
    111 
    112 constexpr base::uc32 MaskEndOfRangeMarker(base::uc32 c) {
    113  // CharacterRanges may use 0x10ffff as the end-of-range marker irrespective
    114  // of whether the regexp IsUnicode or not; translate the marker value here.
    115  DCHECK_IMPLIES(c > kMaxUInt16, c == String::kMaxCodePoint);
    116  return c & 0xffff;
    117 }
    118 
    119 int RangeArrayLengthFor(const ZoneList<CharacterRange>* ranges) {
    120  const int ranges_length = ranges->length();
    121  return MaskEndOfRangeMarker(ranges->at(ranges_length - 1).to()) == kMaxUInt16
    122             ? ranges_length * 2 - 1
    123             : ranges_length * 2;
    124 }
    125 
    126 bool Equals(const ZoneList<CharacterRange>* lhs,
    127            const DirectHandle<FixedUInt16Array>& rhs) {
    128  const int rhs_length = rhs->length();
    129  if (rhs_length != RangeArrayLengthFor(lhs)) return false;
    130  for (int i = 0; i < lhs->length(); i++) {
    131    const CharacterRange& r = lhs->at(i);
    132    if (rhs->get(i * 2 + 0) != r.from()) return false;
    133    if (i * 2 + 1 == rhs_length) break;
    134    if (rhs->get(i * 2 + 1) != r.to() + 1) return false;
    135  }
    136  return true;
    137 }
    138 
    139 Handle<FixedUInt16Array> MakeRangeArray(
    140    Isolate* isolate, const ZoneList<CharacterRange>* ranges) {
    141  const int ranges_length = ranges->length();
    142  const int range_array_length = RangeArrayLengthFor(ranges);
    143  Handle<FixedUInt16Array> range_array =
    144      FixedUInt16Array::New(isolate, range_array_length);
    145  for (int i = 0; i < ranges_length; i++) {
    146    const CharacterRange& r = ranges->at(i);
    147    DCHECK_LE(r.from(), kMaxUInt16);
    148    range_array->set(i * 2 + 0, r.from());
    149    const base::uc32 to = MaskEndOfRangeMarker(r.to());
    150    if (i == ranges_length - 1 && to == kMaxUInt16) {
    151      DCHECK_EQ(range_array_length, ranges_length * 2 - 1);
    152      break;  // Avoid overflow by leaving the last range open-ended.
    153    }
    154    DCHECK_LT(to, kMaxUInt16);
    155    range_array->set(i * 2 + 1, to + 1);  // Exclusive.
    156  }
    157  return range_array;
    158 }
    159 
    160 }  // namespace
    161 
    162 Handle<ByteArray> NativeRegExpMacroAssembler::GetOrAddRangeArray(
    163    const ZoneList<CharacterRange>* ranges) {
    164  const uint32_t hash = Hash(ranges);
    165 
    166  if (range_array_cache_.count(hash) != 0) {
    167    Handle<FixedUInt16Array> range_array = range_array_cache_[hash];
    168    if (Equals(ranges, range_array)) return range_array;
    169  }
    170 
    171  Handle<FixedUInt16Array> range_array = MakeRangeArray(isolate(), ranges);
    172  range_array_cache_[hash] = range_array;
    173  return range_array;
    174 }
    175 
    176 // static
    177 uint32_t RegExpMacroAssembler::IsCharacterInRangeArray(uint32_t current_char,
    178                                                       Address raw_byte_array) {
    179  // Use uint32_t to avoid complexity around bool return types (which may be
    180  // optimized to use only the least significant byte).
    181  static constexpr uint32_t kTrue = 1;
    182  static constexpr uint32_t kFalse = 0;
    183 
    184  Tagged<FixedUInt16Array> ranges =
    185      Cast<FixedUInt16Array>(Tagged<Object>(raw_byte_array));
    186  DCHECK_GE(ranges->length(), 1);
    187 
    188  // Shortcut for fully out of range chars.
    189  if (current_char < ranges->get(0)) return kFalse;
    190  if (current_char >= ranges->get(ranges->length() - 1)) {
    191    // The last range may be open-ended.
    192    return (ranges->length() % 2) == 0 ? kFalse : kTrue;
    193  }
    194 
    195  // Binary search for the matching range. `ranges` is encoded as
    196  // [from0, to0, from1, to1, ..., fromN, toN], or
    197  // [from0, to0, from1, to1, ..., fromN] (open-ended last interval).
    198 
    199  int mid, lower = 0, upper = ranges->length();
    200  do {
    201    mid = lower + (upper - lower) / 2;
    202    const base::uc16 elem = ranges->get(mid);
    203    if (current_char < elem) {
    204      upper = mid;
    205    } else if (current_char > elem) {
    206      lower = mid + 1;
    207    } else {
    208      DCHECK_EQ(current_char, elem);
    209      break;
    210    }
    211  } while (lower < upper);
    212 
    213  const bool current_char_ge_last_elem = current_char >= ranges->get(mid);
    214  const int current_range_start_index =
    215      current_char_ge_last_elem ? mid : mid - 1;
    216 
    217  // Ranges start at even indices and end at odd indices.
    218  return (current_range_start_index % 2) == 0 ? kTrue : kFalse;
    219 }
    220 
    221 void RegExpMacroAssembler::CheckNotInSurrogatePair(int cp_offset,
    222                                                   Label* on_failure) {
    223  Label ok;
    224  // Check that current character is not a trail surrogate.
    225  LoadCurrentCharacter(cp_offset, &ok);
    226  CheckCharacterNotInRange(kTrailSurrogateStart, kTrailSurrogateEnd, &ok);
    227  // Check that previous character is not a lead surrogate.
    228  LoadCurrentCharacter(cp_offset - 1, &ok);
    229  CheckCharacterInRange(kLeadSurrogateStart, kLeadSurrogateEnd, on_failure);
    230  Bind(&ok);
    231 }
    232 
    233 void RegExpMacroAssembler::CheckPosition(int cp_offset,
    234                                         Label* on_outside_input) {
    235  LoadCurrentCharacter(cp_offset, on_outside_input, true);
    236 }
    237 
    238 void RegExpMacroAssembler::LoadCurrentCharacter(int cp_offset,
    239                                                Label* on_end_of_input,
    240                                                bool check_bounds,
    241                                                int characters,
    242                                                int eats_at_least) {
    243  // By default, eats_at_least = characters.
    244  if (eats_at_least == kUseCharactersValue) {
    245    eats_at_least = characters;
    246  }
    247 
    248  LoadCurrentCharacterImpl(cp_offset, on_end_of_input, check_bounds, characters,
    249                           eats_at_least);
    250 }
    251 
    252 void NativeRegExpMacroAssembler::LoadCurrentCharacterImpl(
    253    int cp_offset, Label* on_end_of_input, bool check_bounds, int characters,
    254    int eats_at_least) {
    255  // It's possible to preload a small number of characters when each success
    256  // path requires a large number of characters, but not the reverse.
    257  DCHECK_GE(eats_at_least, characters);
    258 
    259  CHECK(base::IsInRange(cp_offset, kMinCPOffset, kMaxCPOffset));
    260  if (check_bounds) {
    261    if (cp_offset >= 0) {
    262      CheckPosition(cp_offset + eats_at_least - 1, on_end_of_input);
    263    } else {
    264      CheckPosition(cp_offset, on_end_of_input);
    265    }
    266  }
    267  LoadCurrentCharacterUnchecked(cp_offset, characters);
    268 }
    269 
    270 bool NativeRegExpMacroAssembler::CanReadUnaligned() const {
    271  return v8_flags.enable_regexp_unaligned_accesses && !slow_safe();
    272 }
    273 
    274 #ifndef COMPILING_IRREGEXP_FOR_EXTERNAL_EMBEDDER
    275 
    276 // This method may only be called after an interrupt.
    277 // static
    278 int NativeRegExpMacroAssembler::CheckStackGuardState(
    279    Isolate* isolate, int start_index, RegExp::CallOrigin call_origin,
    280    Address* return_address, Tagged<InstructionStream> re_code,
    281    Address* subject, const uint8_t** input_start, const uint8_t** input_end,
    282    uintptr_t gap) {
    283  DisallowGarbageCollection no_gc;
    284  Address old_pc = PointerAuthentication::AuthenticatePC(return_address, 0);
    285  DCHECK_LE(re_code->instruction_start(), old_pc);
    286  DCHECK_LE(old_pc, re_code->code(kAcquireLoad)->instruction_end());
    287 
    288  StackLimitCheck check(isolate);
    289  bool js_has_overflowed = check.JsHasOverflowed(gap);
    290 
    291  if (call_origin == RegExp::CallOrigin::kFromJs) {
    292    // Direct calls from JavaScript can be interrupted in two ways:
    293    // 1. A real stack overflow, in which case we let the caller throw the
    294    //    exception.
    295    // 2. The stack guard was used to interrupt execution for another purpose,
    296    //    forcing the call through the runtime system.
    297 
    298    // Bug(v8:9540) Investigate why this method is called from JS although no
    299    // stackoverflow or interrupt is pending on ARM64. We return 0 in this case
    300    // to continue execution normally.
    301    if (js_has_overflowed) {
    302      return EXCEPTION;
    303    } else if (check.InterruptRequested()) {
    304      return RETRY;
    305    } else {
    306      return 0;
    307    }
    308  }
    309  DCHECK(call_origin == RegExp::CallOrigin::kFromRuntime);
    310 
    311  // Prepare for possible GC.
    312  HandleScope handles(isolate);
    313  DirectHandle<InstructionStream> code_handle(re_code, isolate);
    314  DirectHandle<String> subject_handle(Cast<String>(Tagged<Object>(*subject)),
    315                                      isolate);
    316  bool is_one_byte = String::IsOneByteRepresentationUnderneath(*subject_handle);
    317  int return_value = 0;
    318 
    319  {
    320    DisableGCMole no_gc_mole;
    321    if (js_has_overflowed) {
    322      AllowGarbageCollection yes_gc;
    323      isolate->StackOverflow();
    324      return_value = EXCEPTION;
    325    } else if (check.InterruptRequested()) {
    326      AllowGarbageCollection yes_gc;
    327      Tagged<Object> result = isolate->stack_guard()->HandleInterrupts();
    328      if (IsExceptionHole(result, isolate)) return_value = EXCEPTION;
    329    }
    330 
    331    // We are not using operator == here because it does a slow DCHECK
    332    // CheckObjectComparisonAllowed() which might crash when trying to access
    333    // the page header of the stale pointer.
    334    if (!code_handle->SafeEquals(re_code)) {  // Return address no longer valid
    335      // Overwrite the return address on the stack.
    336      intptr_t delta = code_handle->address() - re_code.address();
    337      Address new_pc = old_pc + delta;
    338      // TODO(v8:10026): avoid replacing a signed pointer.
    339      PointerAuthentication::ReplacePC(return_address, new_pc, 0);
    340    }
    341  }
    342 
    343  // If we continue, we need to update the subject string addresses.
    344  if (return_value == 0) {
    345    // String encoding might have changed.
    346    if (String::IsOneByteRepresentationUnderneath(*subject_handle) !=
    347        is_one_byte) {
    348      // If we changed between an LATIN1 and an UC16 string, the specialized
    349      // code cannot be used, and we need to restart regexp matching from
    350      // scratch (including, potentially, compiling a new version of the code).
    351      return_value = RETRY;
    352    } else {
    353      *subject = subject_handle->ptr();
    354      intptr_t byte_length = *input_end - *input_start;
    355      *input_start = subject_handle->AddressOfCharacterAt(start_index, no_gc);
    356      *input_end = *input_start + byte_length;
    357    }
    358  }
    359  return return_value;
    360 }
    361 
    362 // Returns a {Result} sentinel, or the number of successful matches.
    363 int NativeRegExpMacroAssembler::Match(DirectHandle<IrRegExpData> regexp_data,
    364                                      DirectHandle<String> subject,
    365                                      int* offsets_vector,
    366                                      int offsets_vector_length,
    367                                      int previous_index, Isolate* isolate) {
    368  DCHECK(subject->IsFlat());
    369  DCHECK_LE(0, previous_index);
    370  DCHECK_LE(previous_index, subject->length());
    371 
    372  // No allocations before calling the regexp, but we can't use
    373  // DisallowGarbageCollection, since regexps might be preempted, and another
    374  // thread might do allocation anyway.
    375 
    376  Tagged<String> subject_ptr = *subject;
    377  // Character offsets into string.
    378  int start_offset = previous_index;
    379  int char_length = subject_ptr->length() - start_offset;
    380  int slice_offset = 0;
    381 
    382  // The string has been flattened, so if it is a cons string it contains the
    383  // full string in the first part.
    384  if (StringShape(subject_ptr).IsCons()) {
    385    DCHECK_EQ(0, Cast<ConsString>(subject_ptr)->second()->length());
    386    subject_ptr = Cast<ConsString>(subject_ptr)->first();
    387  } else if (StringShape(subject_ptr).IsSliced()) {
    388    Tagged<SlicedString> slice = Cast<SlicedString>(subject_ptr);
    389    subject_ptr = slice->parent();
    390    slice_offset = slice->offset();
    391  }
    392  if (StringShape(subject_ptr).IsThin()) {
    393    subject_ptr = Cast<ThinString>(subject_ptr)->actual();
    394  }
    395  // Ensure that an underlying string has the same representation.
    396  bool is_one_byte = subject_ptr->IsOneByteRepresentation();
    397  DCHECK(IsExternalString(subject_ptr) || IsSeqString(subject_ptr));
    398  // String is now either Sequential or External
    399  int char_size_shift = is_one_byte ? 0 : 1;
    400 
    401  DisallowGarbageCollection no_gc;
    402  const uint8_t* input_start =
    403      subject_ptr->AddressOfCharacterAt(start_offset + slice_offset, no_gc);
    404  int byte_length = char_length << char_size_shift;
    405  const uint8_t* input_end = input_start + byte_length;
    406  return Execute(*subject, start_offset, input_start, input_end, offsets_vector,
    407                 offsets_vector_length, isolate, *regexp_data);
    408 }
    409 
    410 // static
    411 int NativeRegExpMacroAssembler::ExecuteForTesting(
    412    Tagged<String> input, int start_offset, const uint8_t* input_start,
    413    const uint8_t* input_end, int* output, int output_size, Isolate* isolate,
    414    Tagged<JSRegExp> regexp) {
    415  Tagged<RegExpData> data = regexp->data(isolate);
    416  return Execute(input, start_offset, input_start, input_end, output,
    417                 output_size, isolate, SbxCast<IrRegExpData>(data));
    418 }
    419 
    420 // Returns a {Result} sentinel, or the number of successful matches.
    421 int NativeRegExpMacroAssembler::Execute(
    422    Tagged<String>
    423        input,  // This needs to be the unpacked (sliced, cons) string.
    424    int start_offset, const uint8_t* input_start, const uint8_t* input_end,
    425    int* output, int output_size, Isolate* isolate,
    426    Tagged<IrRegExpData> regexp_data) {
    427  bool is_one_byte = String::IsOneByteRepresentationUnderneath(input);
    428  Tagged<Code> code = regexp_data->code(isolate, is_one_byte);
    429  RegExp::CallOrigin call_origin = RegExp::CallOrigin::kFromRuntime;
    430 
    431  using RegexpMatcherSig =
    432      // NOLINTNEXTLINE(readability/casting)
    433      int(Address input_string, int start_offset, const uint8_t* input_start,
    434          const uint8_t* input_end, int* output, int output_size,
    435          int call_origin, Isolate* isolate, Address regexp_data);
    436 
    437  auto fn = GeneratedCode<RegexpMatcherSig>::FromCode(isolate, code);
    438  int result = fn.CallSandboxed(input.ptr(), start_offset, input_start,
    439                                input_end, output, output_size, call_origin,
    440                                isolate, regexp_data.ptr());
    441  DCHECK_GE(result, SMALLEST_REGEXP_RESULT);
    442 
    443  if (result == EXCEPTION && !isolate->has_exception()) {
    444    // We detected a stack overflow (on the backtrack stack) in RegExp code,
    445    // but haven't created the exception yet. Additionally, we allow heap
    446    // allocation because even though it invalidates {input_start} and
    447    // {input_end}, we are about to return anyway.
    448    AllowGarbageCollection allow_allocation;
    449    isolate->StackOverflow();
    450  }
    451  return result;
    452 }
    453 
    454 #endif  // !COMPILING_IRREGEXP_FOR_EXTERNAL_EMBEDDER
    455 
    456 // clang-format off
    457 const uint8_t NativeRegExpMacroAssembler::word_character_map[] = {
    458    0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
    459    0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
    460    0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
    461    0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
    462 
    463    0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
    464    0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
    465    0xFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu,  // '0' - '7'
    466    0xFFu, 0xFFu, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,  // '8' - '9'
    467 
    468    0x00u, 0xFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu,  // 'A' - 'G'
    469    0xFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu,  // 'H' - 'O'
    470    0xFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu,  // 'P' - 'W'
    471    0xFFu, 0xFFu, 0xFFu, 0x00u, 0x00u, 0x00u, 0x00u, 0xFFu,  // 'X' - 'Z', '_'
    472 
    473    0x00u, 0xFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu,  // 'a' - 'g'
    474    0xFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu,  // 'h' - 'o'
    475    0xFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu,  // 'p' - 'w'
    476    0xFFu, 0xFFu, 0xFFu, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,  // 'x' - 'z'
    477    // Latin-1 range
    478    0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
    479    0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
    480    0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
    481    0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
    482 
    483    0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
    484    0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
    485    0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
    486    0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
    487 
    488    0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
    489    0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
    490    0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
    491    0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
    492 
    493    0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
    494    0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
    495    0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
    496    0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
    497 };
    498 // clang-format on
    499 
    500 // static
    501 Address NativeRegExpMacroAssembler::GrowStack(Isolate* isolate) {
    502  DisallowGarbageCollection no_gc;
    503 
    504  RegExpStack* regexp_stack = isolate->regexp_stack();
    505  const size_t old_size = regexp_stack->memory_size();
    506 
    507 #ifdef DEBUG
    508  const Address old_stack_top = regexp_stack->memory_top();
    509  const Address old_stack_pointer = regexp_stack->stack_pointer();
    510  CHECK_LE(old_stack_pointer, old_stack_top);
    511  CHECK_LE(static_cast<size_t>(old_stack_top - old_stack_pointer), old_size);
    512 #endif  // DEBUG
    513 
    514  Address new_stack_base = regexp_stack->EnsureCapacity(old_size * 2);
    515  if (new_stack_base == kNullAddress) return kNullAddress;
    516 
    517  return regexp_stack->stack_pointer();
    518 }
    519 
    520 }  // namespace internal
    521 }  // namespace v8