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