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