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
21namespace v8 {
22namespace internal {
23
24RegExpMacroAssembler::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
31bool RegExpMacroAssembler::has_backtrack_limit() const {
32  return backtrack_limit_ != JSRegExp::kNoBacktrackLimit;
33}
34
35// static
36int 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
65int 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
107namespace {
108
109uint32_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
118constexpr 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
125int 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
132bool 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
145Handle<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
169Handle<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
184uint32_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
231void 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
243void RegExpMacroAssembler::CheckPosition(int cp_offset,
244                                         Label* on_outside_input) {
245  LoadCurrentCharacter(cp_offset, on_outside_input, true);
246}
247
248void 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
262void 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
280bool 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
288int 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.
368int 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
416int 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.
428int 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
463const 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
507Address 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