1 // Copyright 2016 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/builtins/builtins-utils.h" 6 #include "src/builtins/builtins.h" 7 8 #include "src/code-factory.h" 9 #include "src/regexp/jsregexp.h" 10 #include "src/regexp/regexp-utils.h" 11 #include "src/string-builder.h" 12 13 namespace v8 { 14 namespace internal { 15 16 // ----------------------------------------------------------------------------- 17 // ES6 section 21.2 RegExp Objects 18 19 namespace { 20 21 Handle<String> PatternFlags(Isolate* isolate, Handle<JSRegExp> regexp) { 22 static const int kMaxFlagsLength = 5 + 1; // 5 flags and '\0'; 23 char flags_string[kMaxFlagsLength]; 24 int i = 0; 25 26 const JSRegExp::Flags flags = regexp->GetFlags(); 27 28 if ((flags & JSRegExp::kGlobal) != 0) flags_string[i++] = 'g'; 29 if ((flags & JSRegExp::kIgnoreCase) != 0) flags_string[i++] = 'i'; 30 if ((flags & JSRegExp::kMultiline) != 0) flags_string[i++] = 'm'; 31 if ((flags & JSRegExp::kUnicode) != 0) flags_string[i++] = 'u'; 32 if ((flags & JSRegExp::kSticky) != 0) flags_string[i++] = 'y'; 33 34 DCHECK_LT(i, kMaxFlagsLength); 35 memset(&flags_string[i], '\0', kMaxFlagsLength - i); 36 37 return isolate->factory()->NewStringFromAsciiChecked(flags_string); 38 } 39 40 // ES#sec-regexpinitialize 41 // Runtime Semantics: RegExpInitialize ( obj, pattern, flags ) 42 MUST_USE_RESULT MaybeHandle<JSRegExp> RegExpInitialize(Isolate* isolate, 43 Handle<JSRegExp> regexp, 44 Handle<Object> pattern, 45 Handle<Object> flags) { 46 Handle<String> pattern_string; 47 if (pattern->IsUndefined(isolate)) { 48 pattern_string = isolate->factory()->empty_string(); 49 } else { 50 ASSIGN_RETURN_ON_EXCEPTION(isolate, pattern_string, 51 Object::ToString(isolate, pattern), JSRegExp); 52 } 53 54 Handle<String> flags_string; 55 if (flags->IsUndefined(isolate)) { 56 flags_string = isolate->factory()->empty_string(); 57 } else { 58 ASSIGN_RETURN_ON_EXCEPTION(isolate, flags_string, 59 Object::ToString(isolate, flags), JSRegExp); 60 } 61 62 // TODO(jgruber): We could avoid the flags back and forth conversions. 63 return JSRegExp::Initialize(regexp, pattern_string, flags_string); 64 } 65 66 } // namespace 67 68 // ES#sec-regexp-pattern-flags 69 // RegExp ( pattern, flags ) 70 BUILTIN(RegExpConstructor) { 71 HandleScope scope(isolate); 72 73 Handle<HeapObject> new_target = args.new_target(); 74 Handle<Object> pattern = args.atOrUndefined(isolate, 1); 75 Handle<Object> flags = args.atOrUndefined(isolate, 2); 76 77 Handle<JSFunction> target = isolate->regexp_function(); 78 79 bool pattern_is_regexp; 80 { 81 Maybe<bool> maybe_pattern_is_regexp = 82 RegExpUtils::IsRegExp(isolate, pattern); 83 if (maybe_pattern_is_regexp.IsNothing()) { 84 DCHECK(isolate->has_pending_exception()); 85 return isolate->heap()->exception(); 86 } 87 pattern_is_regexp = maybe_pattern_is_regexp.FromJust(); 88 } 89 90 if (new_target->IsUndefined(isolate)) { 91 new_target = target; 92 93 // ES6 section 21.2.3.1 step 3.b 94 if (pattern_is_regexp && flags->IsUndefined(isolate)) { 95 Handle<Object> pattern_constructor; 96 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( 97 isolate, pattern_constructor, 98 Object::GetProperty(pattern, 99 isolate->factory()->constructor_string())); 100 101 if (pattern_constructor.is_identical_to(new_target)) { 102 return *pattern; 103 } 104 } 105 } 106 107 if (pattern->IsJSRegExp()) { 108 Handle<JSRegExp> regexp_pattern = Handle<JSRegExp>::cast(pattern); 109 110 if (flags->IsUndefined(isolate)) { 111 flags = PatternFlags(isolate, regexp_pattern); 112 } 113 pattern = handle(regexp_pattern->source(), isolate); 114 } else if (pattern_is_regexp) { 115 Handle<Object> pattern_source; 116 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( 117 isolate, pattern_source, 118 Object::GetProperty(pattern, isolate->factory()->source_string())); 119 120 if (flags->IsUndefined(isolate)) { 121 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( 122 isolate, flags, 123 Object::GetProperty(pattern, isolate->factory()->flags_string())); 124 } 125 pattern = pattern_source; 126 } 127 128 Handle<JSReceiver> new_target_receiver = Handle<JSReceiver>::cast(new_target); 129 130 // TODO(jgruber): Fast-path for target == new_target == unmodified JSRegExp. 131 132 Handle<JSObject> object; 133 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( 134 isolate, object, JSObject::New(target, new_target_receiver)); 135 Handle<JSRegExp> regexp = Handle<JSRegExp>::cast(object); 136 137 RETURN_RESULT_OR_FAILURE(isolate, 138 RegExpInitialize(isolate, regexp, pattern, flags)); 139 } 140 141 BUILTIN(RegExpPrototypeCompile) { 142 HandleScope scope(isolate); 143 CHECK_RECEIVER(JSRegExp, regexp, "RegExp.prototype.compile"); 144 145 Handle<Object> pattern = args.atOrUndefined(isolate, 1); 146 Handle<Object> flags = args.atOrUndefined(isolate, 2); 147 148 if (pattern->IsJSRegExp()) { 149 Handle<JSRegExp> pattern_regexp = Handle<JSRegExp>::cast(pattern); 150 151 if (!flags->IsUndefined(isolate)) { 152 THROW_NEW_ERROR_RETURN_FAILURE( 153 isolate, NewTypeError(MessageTemplate::kRegExpFlags)); 154 } 155 156 flags = PatternFlags(isolate, pattern_regexp); 157 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( 158 isolate, pattern, 159 Object::GetProperty(pattern, isolate->factory()->source_string())); 160 } 161 162 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( 163 isolate, regexp, RegExpInitialize(isolate, regexp, pattern, flags)); 164 165 // Return undefined for compatibility with JSC. 166 // See http://crbug.com/585775 for web compat details. 167 168 return isolate->heap()->undefined_value(); 169 } 170 171 namespace { 172 173 compiler::Node* FastLoadLastIndex(CodeStubAssembler* a, compiler::Node* context, 174 compiler::Node* regexp) { 175 // Load the in-object field. 176 static const int field_offset = 177 JSRegExp::kSize + JSRegExp::kLastIndexFieldIndex * kPointerSize; 178 return a->LoadObjectField(regexp, field_offset); 179 } 180 181 compiler::Node* SlowLoadLastIndex(CodeStubAssembler* a, compiler::Node* context, 182 compiler::Node* regexp) { 183 // Load through the GetProperty stub. 184 typedef compiler::Node Node; 185 186 Node* const name = 187 a->HeapConstant(a->isolate()->factory()->lastIndex_string()); 188 Callable getproperty_callable = CodeFactory::GetProperty(a->isolate()); 189 return a->CallStub(getproperty_callable, context, regexp, name); 190 } 191 192 compiler::Node* LoadLastIndex(CodeStubAssembler* a, compiler::Node* context, 193 compiler::Node* has_initialmap, 194 compiler::Node* regexp) { 195 typedef CodeStubAssembler::Variable Variable; 196 typedef CodeStubAssembler::Label Label; 197 198 Variable var_value(a, MachineRepresentation::kTagged); 199 200 Label out(a), if_unmodified(a), if_modified(a); 201 a->Branch(has_initialmap, &if_unmodified, &if_modified); 202 203 a->Bind(&if_unmodified); 204 { 205 var_value.Bind(FastLoadLastIndex(a, context, regexp)); 206 a->Goto(&out); 207 } 208 209 a->Bind(&if_modified); 210 { 211 var_value.Bind(SlowLoadLastIndex(a, context, regexp)); 212 a->Goto(&out); 213 } 214 215 a->Bind(&out); 216 return var_value.value(); 217 } 218 219 // The fast-path of StoreLastIndex when regexp is guaranteed to be an unmodified 220 // JSRegExp instance. 221 void FastStoreLastIndex(CodeStubAssembler* a, compiler::Node* context, 222 compiler::Node* regexp, compiler::Node* value) { 223 // Store the in-object field. 224 static const int field_offset = 225 JSRegExp::kSize + JSRegExp::kLastIndexFieldIndex * kPointerSize; 226 a->StoreObjectField(regexp, field_offset, value); 227 } 228 229 void SlowStoreLastIndex(CodeStubAssembler* a, compiler::Node* context, 230 compiler::Node* regexp, compiler::Node* value) { 231 // Store through runtime. 232 // TODO(ishell): Use SetPropertyStub here once available. 233 typedef compiler::Node Node; 234 235 Node* const name = 236 a->HeapConstant(a->isolate()->factory()->lastIndex_string()); 237 Node* const language_mode = a->SmiConstant(Smi::FromInt(STRICT)); 238 a->CallRuntime(Runtime::kSetProperty, context, regexp, name, value, 239 language_mode); 240 } 241 242 void StoreLastIndex(CodeStubAssembler* a, compiler::Node* context, 243 compiler::Node* has_initialmap, compiler::Node* regexp, 244 compiler::Node* value) { 245 typedef CodeStubAssembler::Label Label; 246 247 Label out(a), if_unmodified(a), if_modified(a); 248 a->Branch(has_initialmap, &if_unmodified, &if_modified); 249 250 a->Bind(&if_unmodified); 251 { 252 FastStoreLastIndex(a, context, regexp, value); 253 a->Goto(&out); 254 } 255 256 a->Bind(&if_modified); 257 { 258 SlowStoreLastIndex(a, context, regexp, value); 259 a->Goto(&out); 260 } 261 262 a->Bind(&out); 263 } 264 265 compiler::Node* ConstructNewResultFromMatchInfo(Isolate* isolate, 266 CodeStubAssembler* a, 267 compiler::Node* context, 268 compiler::Node* match_info, 269 compiler::Node* string) { 270 typedef CodeStubAssembler::Variable Variable; 271 typedef CodeStubAssembler::Label Label; 272 typedef compiler::Node Node; 273 274 Label out(a); 275 276 CodeStubAssembler::ParameterMode mode = CodeStubAssembler::INTPTR_PARAMETERS; 277 Node* const num_indices = a->SmiUntag(a->LoadFixedArrayElement( 278 match_info, a->IntPtrConstant(RegExpMatchInfo::kNumberOfCapturesIndex), 0, 279 mode)); 280 Node* const num_results = a->SmiTag(a->WordShr(num_indices, 1)); 281 Node* const start = a->LoadFixedArrayElement( 282 match_info, a->IntPtrConstant(RegExpMatchInfo::kFirstCaptureIndex), 0, 283 mode); 284 Node* const end = a->LoadFixedArrayElement( 285 match_info, a->IntPtrConstant(RegExpMatchInfo::kFirstCaptureIndex + 1), 0, 286 mode); 287 288 // Calculate the substring of the first match before creating the result array 289 // to avoid an unnecessary write barrier storing the first result. 290 Node* const first = a->SubString(context, string, start, end); 291 292 Node* const result = 293 a->AllocateRegExpResult(context, num_results, start, string); 294 Node* const result_elements = a->LoadElements(result); 295 296 a->StoreFixedArrayElement(result_elements, a->IntPtrConstant(0), first, 297 SKIP_WRITE_BARRIER); 298 299 a->GotoIf(a->SmiEqual(num_results, a->SmiConstant(Smi::FromInt(1))), &out); 300 301 // Store all remaining captures. 302 Node* const limit = a->IntPtrAdd( 303 a->IntPtrConstant(RegExpMatchInfo::kFirstCaptureIndex), num_indices); 304 305 Variable var_from_cursor(a, MachineType::PointerRepresentation()); 306 Variable var_to_cursor(a, MachineType::PointerRepresentation()); 307 308 var_from_cursor.Bind( 309 a->IntPtrConstant(RegExpMatchInfo::kFirstCaptureIndex + 2)); 310 var_to_cursor.Bind(a->IntPtrConstant(1)); 311 312 Variable* vars[] = {&var_from_cursor, &var_to_cursor}; 313 Label loop(a, 2, vars); 314 315 a->Goto(&loop); 316 a->Bind(&loop); 317 { 318 Node* const from_cursor = var_from_cursor.value(); 319 Node* const to_cursor = var_to_cursor.value(); 320 Node* const start = a->LoadFixedArrayElement(match_info, from_cursor); 321 322 Label next_iter(a); 323 a->GotoIf(a->SmiEqual(start, a->SmiConstant(Smi::FromInt(-1))), &next_iter); 324 325 Node* const from_cursor_plus1 = 326 a->IntPtrAdd(from_cursor, a->IntPtrConstant(1)); 327 Node* const end = a->LoadFixedArrayElement(match_info, from_cursor_plus1); 328 329 Node* const capture = a->SubString(context, string, start, end); 330 a->StoreFixedArrayElement(result_elements, to_cursor, capture); 331 a->Goto(&next_iter); 332 333 a->Bind(&next_iter); 334 var_from_cursor.Bind(a->IntPtrAdd(from_cursor, a->IntPtrConstant(2))); 335 var_to_cursor.Bind(a->IntPtrAdd(to_cursor, a->IntPtrConstant(1))); 336 a->Branch(a->UintPtrLessThan(var_from_cursor.value(), limit), &loop, &out); 337 } 338 339 a->Bind(&out); 340 return result; 341 } 342 343 // ES#sec-regexp.prototype.exec 344 // RegExp.prototype.exec ( string ) 345 compiler::Node* RegExpPrototypeExecInternal(CodeStubAssembler* a, 346 compiler::Node* context, 347 compiler::Node* maybe_receiver, 348 compiler::Node* maybe_string) { 349 typedef CodeStubAssembler::Variable Variable; 350 typedef CodeStubAssembler::Label Label; 351 typedef compiler::Node Node; 352 353 Isolate* const isolate = a->isolate(); 354 355 Node* const null = a->NullConstant(); 356 Node* const int_zero = a->IntPtrConstant(0); 357 Node* const smi_zero = a->SmiConstant(Smi::kZero); 358 359 Variable var_result(a, MachineRepresentation::kTagged); 360 Label out(a); 361 362 // Ensure {maybe_receiver} is a JSRegExp. 363 Node* const regexp_map = a->ThrowIfNotInstanceType( 364 context, maybe_receiver, JS_REGEXP_TYPE, "RegExp.prototype.exec"); 365 Node* const regexp = maybe_receiver; 366 367 // Check whether the regexp instance is unmodified. 368 Node* const native_context = a->LoadNativeContext(context); 369 Node* const regexp_fun = 370 a->LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX); 371 Node* const initial_map = 372 a->LoadObjectField(regexp_fun, JSFunction::kPrototypeOrInitialMapOffset); 373 Node* const has_initialmap = a->WordEqual(regexp_map, initial_map); 374 375 // Convert {maybe_string} to a string. 376 Callable tostring_callable = CodeFactory::ToString(isolate); 377 Node* const string = a->CallStub(tostring_callable, context, maybe_string); 378 Node* const string_length = a->LoadStringLength(string); 379 380 // Check whether the regexp is global or sticky, which determines whether we 381 // update last index later on. 382 Node* const flags = a->LoadObjectField(regexp, JSRegExp::kFlagsOffset); 383 Node* const is_global_or_sticky = 384 a->WordAnd(a->SmiUntag(flags), 385 a->IntPtrConstant(JSRegExp::kGlobal | JSRegExp::kSticky)); 386 Node* const should_update_last_index = 387 a->WordNotEqual(is_global_or_sticky, int_zero); 388 389 // Grab and possibly update last index. 390 Label run_exec(a); 391 Variable var_lastindex(a, MachineRepresentation::kTagged); 392 { 393 Label if_doupdate(a), if_dontupdate(a); 394 a->Branch(should_update_last_index, &if_doupdate, &if_dontupdate); 395 396 a->Bind(&if_doupdate); 397 { 398 Node* const regexp_lastindex = 399 LoadLastIndex(a, context, has_initialmap, regexp); 400 401 Callable tolength_callable = CodeFactory::ToLength(isolate); 402 Node* const lastindex = 403 a->CallStub(tolength_callable, context, regexp_lastindex); 404 var_lastindex.Bind(lastindex); 405 406 Label if_isoob(a, Label::kDeferred); 407 a->GotoUnless(a->TaggedIsSmi(lastindex), &if_isoob); 408 a->GotoUnless(a->SmiLessThanOrEqual(lastindex, string_length), &if_isoob); 409 a->Goto(&run_exec); 410 411 a->Bind(&if_isoob); 412 { 413 StoreLastIndex(a, context, has_initialmap, regexp, smi_zero); 414 var_result.Bind(null); 415 a->Goto(&out); 416 } 417 } 418 419 a->Bind(&if_dontupdate); 420 { 421 var_lastindex.Bind(smi_zero); 422 a->Goto(&run_exec); 423 } 424 } 425 426 Node* match_indices; 427 Label successful_match(a); 428 a->Bind(&run_exec); 429 { 430 // Get last match info from the context. 431 Node* const last_match_info = a->LoadContextElement( 432 native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX); 433 434 // Call the exec stub. 435 Callable exec_callable = CodeFactory::RegExpExec(isolate); 436 match_indices = a->CallStub(exec_callable, context, regexp, string, 437 var_lastindex.value(), last_match_info); 438 439 // {match_indices} is either null or the RegExpMatchInfo array. 440 // Return early if exec failed, possibly updating last index. 441 a->GotoUnless(a->WordEqual(match_indices, null), &successful_match); 442 443 Label return_null(a); 444 a->GotoUnless(should_update_last_index, &return_null); 445 446 StoreLastIndex(a, context, has_initialmap, regexp, smi_zero); 447 a->Goto(&return_null); 448 449 a->Bind(&return_null); 450 var_result.Bind(null); 451 a->Goto(&out); 452 } 453 454 Label construct_result(a); 455 a->Bind(&successful_match); 456 { 457 a->GotoUnless(should_update_last_index, &construct_result); 458 459 // Update the new last index from {match_indices}. 460 Node* const new_lastindex = a->LoadFixedArrayElement( 461 match_indices, 462 a->IntPtrConstant(RegExpMatchInfo::kFirstCaptureIndex + 1)); 463 464 StoreLastIndex(a, context, has_initialmap, regexp, new_lastindex); 465 a->Goto(&construct_result); 466 467 a->Bind(&construct_result); 468 { 469 Node* result = ConstructNewResultFromMatchInfo(isolate, a, context, 470 match_indices, string); 471 var_result.Bind(result); 472 a->Goto(&out); 473 } 474 } 475 476 a->Bind(&out); 477 return var_result.value(); 478 } 479 480 } // namespace 481 482 // ES#sec-regexp.prototype.exec 483 // RegExp.prototype.exec ( string ) 484 void Builtins::Generate_RegExpPrototypeExec(CodeStubAssembler* a) { 485 typedef compiler::Node Node; 486 487 Node* const maybe_receiver = a->Parameter(0); 488 Node* const maybe_string = a->Parameter(1); 489 Node* const context = a->Parameter(4); 490 491 Node* const result = 492 RegExpPrototypeExecInternal(a, context, maybe_receiver, maybe_string); 493 a->Return(result); 494 } 495 496 namespace { 497 498 compiler::Node* ThrowIfNotJSReceiver(CodeStubAssembler* a, Isolate* isolate, 499 compiler::Node* context, 500 compiler::Node* value, 501 MessageTemplate::Template msg_template, 502 char const* method_name) { 503 typedef compiler::Node Node; 504 typedef CodeStubAssembler::Label Label; 505 typedef CodeStubAssembler::Variable Variable; 506 507 Label out(a), throw_exception(a, Label::kDeferred); 508 Variable var_value_map(a, MachineRepresentation::kTagged); 509 510 a->GotoIf(a->TaggedIsSmi(value), &throw_exception); 511 512 // Load the instance type of the {value}. 513 var_value_map.Bind(a->LoadMap(value)); 514 Node* const value_instance_type = 515 a->LoadMapInstanceType(var_value_map.value()); 516 517 a->Branch(a->IsJSReceiverInstanceType(value_instance_type), &out, 518 &throw_exception); 519 520 // The {value} is not a compatible receiver for this method. 521 a->Bind(&throw_exception); 522 { 523 Node* const message_id = a->SmiConstant(Smi::FromInt(msg_template)); 524 Node* const method_name_str = a->HeapConstant( 525 isolate->factory()->NewStringFromAsciiChecked(method_name, TENURED)); 526 527 Callable callable = CodeFactory::ToString(isolate); 528 Node* const value_str = a->CallStub(callable, context, value); 529 530 a->CallRuntime(Runtime::kThrowTypeError, context, message_id, 531 method_name_str, value_str); 532 var_value_map.Bind(a->UndefinedConstant()); 533 a->Goto(&out); // Never reached. 534 } 535 536 a->Bind(&out); 537 return var_value_map.value(); 538 } 539 540 compiler::Node* IsInitialRegExpMap(CodeStubAssembler* a, 541 compiler::Node* context, 542 compiler::Node* map) { 543 typedef compiler::Node Node; 544 545 Node* const native_context = a->LoadNativeContext(context); 546 Node* const regexp_fun = 547 a->LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX); 548 Node* const initial_map = 549 a->LoadObjectField(regexp_fun, JSFunction::kPrototypeOrInitialMapOffset); 550 Node* const has_initialmap = a->WordEqual(map, initial_map); 551 552 return has_initialmap; 553 } 554 555 // RegExp fast path implementations rely on unmodified JSRegExp instances. 556 // We use a fairly coarse granularity for this and simply check whether both 557 // the regexp itself is unmodified (i.e. its map has not changed) and its 558 // prototype is unmodified. 559 void BranchIfFastPath(CodeStubAssembler* a, compiler::Node* context, 560 compiler::Node* map, 561 CodeStubAssembler::Label* if_isunmodified, 562 CodeStubAssembler::Label* if_ismodified) { 563 typedef compiler::Node Node; 564 565 Node* const native_context = a->LoadNativeContext(context); 566 Node* const regexp_fun = 567 a->LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX); 568 Node* const initial_map = 569 a->LoadObjectField(regexp_fun, JSFunction::kPrototypeOrInitialMapOffset); 570 Node* const has_initialmap = a->WordEqual(map, initial_map); 571 572 a->GotoUnless(has_initialmap, if_ismodified); 573 574 Node* const initial_proto_initial_map = a->LoadContextElement( 575 native_context, Context::REGEXP_PROTOTYPE_MAP_INDEX); 576 Node* const proto_map = a->LoadMap(a->LoadMapPrototype(map)); 577 Node* const proto_has_initialmap = 578 a->WordEqual(proto_map, initial_proto_initial_map); 579 580 // TODO(ishell): Update this check once map changes for constant field 581 // tracking are landing. 582 583 a->Branch(proto_has_initialmap, if_isunmodified, if_ismodified); 584 } 585 586 } // namespace 587 588 void Builtins::Generate_RegExpPrototypeFlagsGetter(CodeStubAssembler* a) { 589 typedef CodeStubAssembler::Variable Variable; 590 typedef CodeStubAssembler::Label Label; 591 typedef compiler::Node Node; 592 593 Node* const receiver = a->Parameter(0); 594 Node* const context = a->Parameter(3); 595 596 Isolate* isolate = a->isolate(); 597 Node* const int_zero = a->IntPtrConstant(0); 598 Node* const int_one = a->IntPtrConstant(1); 599 600 Node* const map = ThrowIfNotJSReceiver(a, isolate, context, receiver, 601 MessageTemplate::kRegExpNonObject, 602 "RegExp.prototype.flags"); 603 604 Variable var_length(a, MachineType::PointerRepresentation()); 605 Variable var_flags(a, MachineType::PointerRepresentation()); 606 607 // First, count the number of characters we will need and check which flags 608 // are set. 609 610 var_length.Bind(int_zero); 611 612 Label if_isunmodifiedjsregexp(a), 613 if_isnotunmodifiedjsregexp(a, Label::kDeferred); 614 a->Branch(IsInitialRegExpMap(a, context, map), &if_isunmodifiedjsregexp, 615 &if_isnotunmodifiedjsregexp); 616 617 Label construct_string(a); 618 a->Bind(&if_isunmodifiedjsregexp); 619 { 620 // Refer to JSRegExp's flag property on the fast-path. 621 Node* const flags_smi = 622 a->LoadObjectField(receiver, JSRegExp::kFlagsOffset); 623 Node* const flags_intptr = a->SmiUntag(flags_smi); 624 var_flags.Bind(flags_intptr); 625 626 Label label_global(a), label_ignorecase(a), label_multiline(a), 627 label_unicode(a), label_sticky(a); 628 629 #define CASE_FOR_FLAG(FLAG, LABEL, NEXT_LABEL) \ 630 do { \ 631 a->Bind(&LABEL); \ 632 Node* const mask = a->IntPtrConstant(FLAG); \ 633 a->GotoIf(a->WordEqual(a->WordAnd(flags_intptr, mask), int_zero), \ 634 &NEXT_LABEL); \ 635 var_length.Bind(a->IntPtrAdd(var_length.value(), int_one)); \ 636 a->Goto(&NEXT_LABEL); \ 637 } while (false) 638 639 a->Goto(&label_global); 640 CASE_FOR_FLAG(JSRegExp::kGlobal, label_global, label_ignorecase); 641 CASE_FOR_FLAG(JSRegExp::kIgnoreCase, label_ignorecase, label_multiline); 642 CASE_FOR_FLAG(JSRegExp::kMultiline, label_multiline, label_unicode); 643 CASE_FOR_FLAG(JSRegExp::kUnicode, label_unicode, label_sticky); 644 CASE_FOR_FLAG(JSRegExp::kSticky, label_sticky, construct_string); 645 #undef CASE_FOR_FLAG 646 } 647 648 a->Bind(&if_isnotunmodifiedjsregexp); 649 { 650 // Fall back to GetProperty stub on the slow-path. 651 var_flags.Bind(int_zero); 652 653 Callable getproperty_callable = CodeFactory::GetProperty(a->isolate()); 654 Label label_global(a), label_ignorecase(a), label_multiline(a), 655 label_unicode(a), label_sticky(a); 656 657 #define CASE_FOR_FLAG(NAME, FLAG, LABEL, NEXT_LABEL) \ 658 do { \ 659 a->Bind(&LABEL); \ 660 Node* const name = \ 661 a->HeapConstant(isolate->factory()->NewStringFromAsciiChecked(NAME)); \ 662 Node* const flag = \ 663 a->CallStub(getproperty_callable, context, receiver, name); \ 664 Label if_isflagset(a); \ 665 a->BranchIfToBooleanIsTrue(flag, &if_isflagset, &NEXT_LABEL); \ 666 a->Bind(&if_isflagset); \ 667 var_length.Bind(a->IntPtrAdd(var_length.value(), int_one)); \ 668 var_flags.Bind(a->WordOr(var_flags.value(), a->IntPtrConstant(FLAG))); \ 669 a->Goto(&NEXT_LABEL); \ 670 } while (false) 671 672 a->Goto(&label_global); 673 CASE_FOR_FLAG("global", JSRegExp::kGlobal, label_global, label_ignorecase); 674 CASE_FOR_FLAG("ignoreCase", JSRegExp::kIgnoreCase, label_ignorecase, 675 label_multiline); 676 CASE_FOR_FLAG("multiline", JSRegExp::kMultiline, label_multiline, 677 label_unicode); 678 CASE_FOR_FLAG("unicode", JSRegExp::kUnicode, label_unicode, label_sticky); 679 CASE_FOR_FLAG("sticky", JSRegExp::kSticky, label_sticky, construct_string); 680 #undef CASE_FOR_FLAG 681 } 682 683 // Allocate a string of the required length and fill it with the corresponding 684 // char for each set flag. 685 686 a->Bind(&construct_string); 687 { 688 Node* const result = 689 a->AllocateSeqOneByteString(context, var_length.value()); 690 Node* const flags_intptr = var_flags.value(); 691 692 Variable var_offset(a, MachineType::PointerRepresentation()); 693 var_offset.Bind( 694 a->IntPtrConstant(SeqOneByteString::kHeaderSize - kHeapObjectTag)); 695 696 Label label_global(a), label_ignorecase(a), label_multiline(a), 697 label_unicode(a), label_sticky(a), out(a); 698 699 #define CASE_FOR_FLAG(FLAG, CHAR, LABEL, NEXT_LABEL) \ 700 do { \ 701 a->Bind(&LABEL); \ 702 Node* const mask = a->IntPtrConstant(FLAG); \ 703 a->GotoIf(a->WordEqual(a->WordAnd(flags_intptr, mask), int_zero), \ 704 &NEXT_LABEL); \ 705 Node* const value = a->IntPtrConstant(CHAR); \ 706 a->StoreNoWriteBarrier(MachineRepresentation::kWord8, result, \ 707 var_offset.value(), value); \ 708 var_offset.Bind(a->IntPtrAdd(var_offset.value(), int_one)); \ 709 a->Goto(&NEXT_LABEL); \ 710 } while (false) 711 712 a->Goto(&label_global); 713 CASE_FOR_FLAG(JSRegExp::kGlobal, 'g', label_global, label_ignorecase); 714 CASE_FOR_FLAG(JSRegExp::kIgnoreCase, 'i', label_ignorecase, 715 label_multiline); 716 CASE_FOR_FLAG(JSRegExp::kMultiline, 'm', label_multiline, label_unicode); 717 CASE_FOR_FLAG(JSRegExp::kUnicode, 'u', label_unicode, label_sticky); 718 CASE_FOR_FLAG(JSRegExp::kSticky, 'y', label_sticky, out); 719 #undef CASE_FOR_FLAG 720 721 a->Bind(&out); 722 a->Return(result); 723 } 724 } 725 726 // ES6 21.2.5.10. 727 BUILTIN(RegExpPrototypeSourceGetter) { 728 HandleScope scope(isolate); 729 730 Handle<Object> recv = args.receiver(); 731 if (!recv->IsJSRegExp()) { 732 Handle<JSFunction> regexp_fun = isolate->regexp_function(); 733 if (*recv == regexp_fun->prototype()) { 734 isolate->CountUsage(v8::Isolate::kRegExpPrototypeSourceGetter); 735 return *isolate->factory()->NewStringFromAsciiChecked("(?:)"); 736 } 737 THROW_NEW_ERROR_RETURN_FAILURE( 738 isolate, NewTypeError(MessageTemplate::kRegExpNonRegExp, 739 isolate->factory()->NewStringFromAsciiChecked( 740 "RegExp.prototype.source"))); 741 } 742 743 Handle<JSRegExp> regexp = Handle<JSRegExp>::cast(recv); 744 return regexp->source(); 745 } 746 747 BUILTIN(RegExpPrototypeToString) { 748 HandleScope scope(isolate); 749 CHECK_RECEIVER(JSReceiver, recv, "RegExp.prototype.toString"); 750 751 if (*recv == isolate->regexp_function()->prototype()) { 752 isolate->CountUsage(v8::Isolate::kRegExpPrototypeToString); 753 } 754 755 IncrementalStringBuilder builder(isolate); 756 757 builder.AppendCharacter('/'); 758 { 759 Handle<Object> source; 760 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( 761 isolate, source, 762 JSReceiver::GetProperty(recv, isolate->factory()->source_string())); 763 Handle<String> source_str; 764 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, source_str, 765 Object::ToString(isolate, source)); 766 builder.AppendString(source_str); 767 } 768 769 builder.AppendCharacter('/'); 770 { 771 Handle<Object> flags; 772 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( 773 isolate, flags, 774 JSReceiver::GetProperty(recv, isolate->factory()->flags_string())); 775 Handle<String> flags_str; 776 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, flags_str, 777 Object::ToString(isolate, flags)); 778 builder.AppendString(flags_str); 779 } 780 781 RETURN_RESULT_OR_FAILURE(isolate, builder.Finish()); 782 } 783 784 // ES6 21.2.4.2. 785 BUILTIN(RegExpPrototypeSpeciesGetter) { 786 HandleScope scope(isolate); 787 return *args.receiver(); 788 } 789 790 namespace { 791 792 // Fast-path implementation for flag checks on an unmodified JSRegExp instance. 793 compiler::Node* FastFlagGetter(CodeStubAssembler* a, 794 compiler::Node* const regexp, 795 JSRegExp::Flag flag) { 796 typedef compiler::Node Node; 797 798 Node* const smi_zero = a->SmiConstant(Smi::kZero); 799 Node* const flags = a->LoadObjectField(regexp, JSRegExp::kFlagsOffset); 800 Node* const mask = a->SmiConstant(Smi::FromInt(flag)); 801 Node* const is_flag_set = a->WordNotEqual(a->WordAnd(flags, mask), smi_zero); 802 803 return is_flag_set; 804 } 805 806 void Generate_FlagGetter(CodeStubAssembler* a, JSRegExp::Flag flag, 807 v8::Isolate::UseCounterFeature counter, 808 const char* method_name) { 809 typedef CodeStubAssembler::Label Label; 810 typedef compiler::Node Node; 811 812 Node* const receiver = a->Parameter(0); 813 Node* const context = a->Parameter(3); 814 815 Isolate* isolate = a->isolate(); 816 817 // Check whether we have an unmodified regexp instance. 818 Label if_isunmodifiedjsregexp(a), 819 if_isnotunmodifiedjsregexp(a, Label::kDeferred); 820 821 a->GotoIf(a->TaggedIsSmi(receiver), &if_isnotunmodifiedjsregexp); 822 823 Node* const receiver_map = a->LoadMap(receiver); 824 Node* const instance_type = a->LoadMapInstanceType(receiver_map); 825 826 a->Branch(a->Word32Equal(instance_type, a->Int32Constant(JS_REGEXP_TYPE)), 827 &if_isunmodifiedjsregexp, &if_isnotunmodifiedjsregexp); 828 829 a->Bind(&if_isunmodifiedjsregexp); 830 { 831 // Refer to JSRegExp's flag property on the fast-path. 832 Node* const is_flag_set = FastFlagGetter(a, receiver, flag); 833 a->Return(a->Select(is_flag_set, a->TrueConstant(), a->FalseConstant())); 834 } 835 836 a->Bind(&if_isnotunmodifiedjsregexp); 837 { 838 Node* const native_context = a->LoadNativeContext(context); 839 Node* const regexp_fun = 840 a->LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX); 841 Node* const initial_map = a->LoadObjectField( 842 regexp_fun, JSFunction::kPrototypeOrInitialMapOffset); 843 Node* const initial_prototype = a->LoadMapPrototype(initial_map); 844 845 Label if_isprototype(a), if_isnotprototype(a); 846 a->Branch(a->WordEqual(receiver, initial_prototype), &if_isprototype, 847 &if_isnotprototype); 848 849 a->Bind(&if_isprototype); 850 { 851 Node* const counter_smi = a->SmiConstant(Smi::FromInt(counter)); 852 a->CallRuntime(Runtime::kIncrementUseCounter, context, counter_smi); 853 a->Return(a->UndefinedConstant()); 854 } 855 856 a->Bind(&if_isnotprototype); 857 { 858 Node* const message_id = 859 a->SmiConstant(Smi::FromInt(MessageTemplate::kRegExpNonRegExp)); 860 Node* const method_name_str = a->HeapConstant( 861 isolate->factory()->NewStringFromAsciiChecked(method_name)); 862 a->CallRuntime(Runtime::kThrowTypeError, context, message_id, 863 method_name_str); 864 a->Return(a->UndefinedConstant()); // Never reached. 865 } 866 } 867 } 868 869 } // namespace 870 871 // ES6 21.2.5.4. 872 void Builtins::Generate_RegExpPrototypeGlobalGetter(CodeStubAssembler* a) { 873 Generate_FlagGetter(a, JSRegExp::kGlobal, 874 v8::Isolate::kRegExpPrototypeOldFlagGetter, 875 "RegExp.prototype.global"); 876 } 877 878 // ES6 21.2.5.5. 879 void Builtins::Generate_RegExpPrototypeIgnoreCaseGetter(CodeStubAssembler* a) { 880 Generate_FlagGetter(a, JSRegExp::kIgnoreCase, 881 v8::Isolate::kRegExpPrototypeOldFlagGetter, 882 "RegExp.prototype.ignoreCase"); 883 } 884 885 // ES6 21.2.5.7. 886 void Builtins::Generate_RegExpPrototypeMultilineGetter(CodeStubAssembler* a) { 887 Generate_FlagGetter(a, JSRegExp::kMultiline, 888 v8::Isolate::kRegExpPrototypeOldFlagGetter, 889 "RegExp.prototype.multiline"); 890 } 891 892 // ES6 21.2.5.12. 893 void Builtins::Generate_RegExpPrototypeStickyGetter(CodeStubAssembler* a) { 894 Generate_FlagGetter(a, JSRegExp::kSticky, 895 v8::Isolate::kRegExpPrototypeStickyGetter, 896 "RegExp.prototype.sticky"); 897 } 898 899 // ES6 21.2.5.15. 900 void Builtins::Generate_RegExpPrototypeUnicodeGetter(CodeStubAssembler* a) { 901 Generate_FlagGetter(a, JSRegExp::kUnicode, 902 v8::Isolate::kRegExpPrototypeUnicodeGetter, 903 "RegExp.prototype.unicode"); 904 } 905 906 // The properties $1..$9 are the first nine capturing substrings of the last 907 // successful match, or ''. The function RegExpMakeCaptureGetter will be 908 // called with indices from 1 to 9. 909 #define DEFINE_CAPTURE_GETTER(i) \ 910 BUILTIN(RegExpCapture##i##Getter) { \ 911 HandleScope scope(isolate); \ 912 return *RegExpUtils::GenericCaptureGetter( \ 913 isolate, isolate->regexp_last_match_info(), i); \ 914 } 915 DEFINE_CAPTURE_GETTER(1) 916 DEFINE_CAPTURE_GETTER(2) 917 DEFINE_CAPTURE_GETTER(3) 918 DEFINE_CAPTURE_GETTER(4) 919 DEFINE_CAPTURE_GETTER(5) 920 DEFINE_CAPTURE_GETTER(6) 921 DEFINE_CAPTURE_GETTER(7) 922 DEFINE_CAPTURE_GETTER(8) 923 DEFINE_CAPTURE_GETTER(9) 924 #undef DEFINE_CAPTURE_GETTER 925 926 // The properties `input` and `$_` are aliases for each other. When this 927 // value is set, the value it is set to is coerced to a string. 928 // Getter and setter for the input. 929 930 BUILTIN(RegExpInputGetter) { 931 HandleScope scope(isolate); 932 Handle<Object> obj(isolate->regexp_last_match_info()->LastInput(), isolate); 933 return obj->IsUndefined(isolate) ? isolate->heap()->empty_string() 934 : String::cast(*obj); 935 } 936 937 BUILTIN(RegExpInputSetter) { 938 HandleScope scope(isolate); 939 Handle<Object> value = args.atOrUndefined(isolate, 1); 940 Handle<String> str; 941 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, str, 942 Object::ToString(isolate, value)); 943 isolate->regexp_last_match_info()->SetLastInput(*str); 944 return isolate->heap()->undefined_value(); 945 } 946 947 // Getters for the static properties lastMatch, lastParen, leftContext, and 948 // rightContext of the RegExp constructor. The properties are computed based 949 // on the captures array of the last successful match and the subject string 950 // of the last successful match. 951 BUILTIN(RegExpLastMatchGetter) { 952 HandleScope scope(isolate); 953 return *RegExpUtils::GenericCaptureGetter( 954 isolate, isolate->regexp_last_match_info(), 0); 955 } 956 957 BUILTIN(RegExpLastParenGetter) { 958 HandleScope scope(isolate); 959 Handle<RegExpMatchInfo> match_info = isolate->regexp_last_match_info(); 960 const int length = match_info->NumberOfCaptureRegisters(); 961 if (length <= 2) return isolate->heap()->empty_string(); // No captures. 962 963 DCHECK_EQ(0, length % 2); 964 const int last_capture = (length / 2) - 1; 965 966 // We match the SpiderMonkey behavior: return the substring defined by the 967 // last pair (after the first pair) of elements of the capture array even if 968 // it is empty. 969 return *RegExpUtils::GenericCaptureGetter(isolate, match_info, last_capture); 970 } 971 972 BUILTIN(RegExpLeftContextGetter) { 973 HandleScope scope(isolate); 974 Handle<RegExpMatchInfo> match_info = isolate->regexp_last_match_info(); 975 const int start_index = match_info->Capture(0); 976 Handle<String> last_subject(match_info->LastSubject()); 977 return *isolate->factory()->NewSubString(last_subject, 0, start_index); 978 } 979 980 BUILTIN(RegExpRightContextGetter) { 981 HandleScope scope(isolate); 982 Handle<RegExpMatchInfo> match_info = isolate->regexp_last_match_info(); 983 const int start_index = match_info->Capture(1); 984 Handle<String> last_subject(match_info->LastSubject()); 985 const int len = last_subject->length(); 986 return *isolate->factory()->NewSubString(last_subject, start_index, len); 987 } 988 989 namespace { 990 991 // ES#sec-regexpexec Runtime Semantics: RegExpExec ( R, S ) 992 compiler::Node* RegExpExec(CodeStubAssembler* a, compiler::Node* context, 993 compiler::Node* recv, compiler::Node* string) { 994 typedef CodeStubAssembler::Variable Variable; 995 typedef CodeStubAssembler::Label Label; 996 typedef compiler::Node Node; 997 998 Isolate* isolate = a->isolate(); 999 1000 Node* const null = a->NullConstant(); 1001 1002 Variable var_result(a, MachineRepresentation::kTagged); 1003 Label out(a), call_builtin_exec(a), slow_path(a, Label::kDeferred); 1004 1005 Node* const map = a->LoadMap(recv); 1006 BranchIfFastPath(a, context, map, &call_builtin_exec, &slow_path); 1007 1008 a->Bind(&call_builtin_exec); 1009 { 1010 Node* const result = RegExpPrototypeExecInternal(a, context, recv, string); 1011 var_result.Bind(result); 1012 a->Goto(&out); 1013 } 1014 1015 a->Bind(&slow_path); 1016 { 1017 // Take the slow path of fetching the exec property, calling it, and 1018 // verifying its return value. 1019 1020 // Get the exec property. 1021 Node* const name = a->HeapConstant(isolate->factory()->exec_string()); 1022 Callable getproperty_callable = CodeFactory::GetProperty(a->isolate()); 1023 Node* const exec = a->CallStub(getproperty_callable, context, recv, name); 1024 1025 // Is {exec} callable? 1026 Label if_iscallable(a), if_isnotcallable(a); 1027 1028 a->GotoIf(a->TaggedIsSmi(exec), &if_isnotcallable); 1029 1030 Node* const exec_map = a->LoadMap(exec); 1031 a->Branch(a->IsCallableMap(exec_map), &if_iscallable, &if_isnotcallable); 1032 1033 a->Bind(&if_iscallable); 1034 { 1035 Callable call_callable = CodeFactory::Call(isolate); 1036 Node* const result = 1037 a->CallJS(call_callable, context, exec, recv, string); 1038 1039 var_result.Bind(result); 1040 a->GotoIf(a->WordEqual(result, null), &out); 1041 1042 ThrowIfNotJSReceiver(a, isolate, context, result, 1043 MessageTemplate::kInvalidRegExpExecResult, "unused"); 1044 1045 a->Goto(&out); 1046 } 1047 1048 a->Bind(&if_isnotcallable); 1049 { 1050 a->ThrowIfNotInstanceType(context, recv, JS_REGEXP_TYPE, 1051 "RegExp.prototype.exec"); 1052 a->Goto(&call_builtin_exec); 1053 } 1054 } 1055 1056 a->Bind(&out); 1057 return var_result.value(); 1058 } 1059 1060 } // namespace 1061 1062 // ES#sec-regexp.prototype.test 1063 // RegExp.prototype.test ( S ) 1064 void Builtins::Generate_RegExpPrototypeTest(CodeStubAssembler* a) { 1065 typedef compiler::Node Node; 1066 1067 Isolate* const isolate = a->isolate(); 1068 1069 Node* const maybe_receiver = a->Parameter(0); 1070 Node* const maybe_string = a->Parameter(1); 1071 Node* const context = a->Parameter(4); 1072 1073 // Ensure {maybe_receiver} is a JSReceiver. 1074 ThrowIfNotJSReceiver(a, isolate, context, maybe_receiver, 1075 MessageTemplate::kIncompatibleMethodReceiver, 1076 "RegExp.prototype.test"); 1077 Node* const receiver = maybe_receiver; 1078 1079 // Convert {maybe_string} to a String. 1080 Node* const string = a->ToString(context, maybe_string); 1081 1082 // Call exec. 1083 Node* const match_indices = RegExpExec(a, context, receiver, string); 1084 1085 // Return true iff exec matched successfully. 1086 Node* const result = a->Select(a->WordEqual(match_indices, a->NullConstant()), 1087 a->FalseConstant(), a->TrueConstant()); 1088 a->Return(result); 1089 } 1090 1091 // ES#sec-regexp.prototype-@@match 1092 // RegExp.prototype [ @@match ] ( string ) 1093 BUILTIN(RegExpPrototypeMatch) { 1094 HandleScope scope(isolate); 1095 CHECK_RECEIVER(JSReceiver, recv, "RegExp.prototype.@@match"); 1096 1097 Handle<Object> string_obj = args.atOrUndefined(isolate, 1); 1098 1099 Handle<String> string; 1100 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, string, 1101 Object::ToString(isolate, string_obj)); 1102 1103 Handle<Object> global_obj; 1104 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( 1105 isolate, global_obj, 1106 JSReceiver::GetProperty(recv, isolate->factory()->global_string())); 1107 const bool global = global_obj->BooleanValue(); 1108 1109 if (!global) { 1110 RETURN_RESULT_OR_FAILURE( 1111 isolate, 1112 RegExpUtils::RegExpExec(isolate, recv, string, 1113 isolate->factory()->undefined_value())); 1114 } 1115 1116 Handle<Object> unicode_obj; 1117 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( 1118 isolate, unicode_obj, 1119 JSReceiver::GetProperty(recv, isolate->factory()->unicode_string())); 1120 const bool unicode = unicode_obj->BooleanValue(); 1121 1122 RETURN_FAILURE_ON_EXCEPTION(isolate, 1123 RegExpUtils::SetLastIndex(isolate, recv, 0)); 1124 1125 static const int kInitialArraySize = 8; 1126 Handle<FixedArray> elems = 1127 isolate->factory()->NewFixedArrayWithHoles(kInitialArraySize); 1128 1129 int n = 0; 1130 for (;; n++) { 1131 Handle<Object> result; 1132 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( 1133 isolate, result, 1134 RegExpUtils::RegExpExec(isolate, recv, string, 1135 isolate->factory()->undefined_value())); 1136 1137 if (result->IsNull(isolate)) { 1138 if (n == 0) return isolate->heap()->null_value(); 1139 break; 1140 } 1141 1142 Handle<Object> match_obj; 1143 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, match_obj, 1144 Object::GetElement(isolate, result, 0)); 1145 1146 Handle<String> match; 1147 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, match, 1148 Object::ToString(isolate, match_obj)); 1149 1150 elems = FixedArray::SetAndGrow(elems, n, match); 1151 1152 if (match->length() == 0) { 1153 RETURN_FAILURE_ON_EXCEPTION(isolate, RegExpUtils::SetAdvancedStringIndex( 1154 isolate, recv, string, unicode)); 1155 } 1156 } 1157 1158 elems->Shrink(n); 1159 return *isolate->factory()->NewJSArrayWithElements(elems); 1160 } 1161 1162 namespace { 1163 1164 void Generate_RegExpPrototypeSearchBody(CodeStubAssembler* a, 1165 compiler::Node* const receiver, 1166 compiler::Node* const string, 1167 compiler::Node* const context, 1168 bool is_fastpath) { 1169 typedef CodeStubAssembler::Label Label; 1170 typedef compiler::Node Node; 1171 1172 Isolate* const isolate = a->isolate(); 1173 1174 Node* const smi_zero = a->SmiConstant(Smi::kZero); 1175 1176 // Grab the initial value of last index. 1177 Node* const previous_last_index = 1178 is_fastpath ? FastLoadLastIndex(a, context, receiver) 1179 : SlowLoadLastIndex(a, context, receiver); 1180 1181 // Ensure last index is 0. 1182 if (is_fastpath) { 1183 FastStoreLastIndex(a, context, receiver, smi_zero); 1184 } else { 1185 Label next(a); 1186 a->GotoIf(a->SameValue(previous_last_index, smi_zero, context), &next); 1187 1188 SlowStoreLastIndex(a, context, receiver, smi_zero); 1189 a->Goto(&next); 1190 a->Bind(&next); 1191 } 1192 1193 // Call exec. 1194 Node* const match_indices = 1195 is_fastpath ? RegExpPrototypeExecInternal(a, context, receiver, string) 1196 : RegExpExec(a, context, receiver, string); 1197 1198 // Reset last index if necessary. 1199 if (is_fastpath) { 1200 FastStoreLastIndex(a, context, receiver, previous_last_index); 1201 } else { 1202 Label next(a); 1203 Node* const current_last_index = SlowLoadLastIndex(a, context, receiver); 1204 1205 a->GotoIf(a->SameValue(current_last_index, previous_last_index, context), 1206 &next); 1207 1208 SlowStoreLastIndex(a, context, receiver, previous_last_index); 1209 a->Goto(&next); 1210 a->Bind(&next); 1211 } 1212 1213 // Return -1 if no match was found. 1214 { 1215 Label next(a); 1216 a->GotoUnless(a->WordEqual(match_indices, a->NullConstant()), &next); 1217 a->Return(a->SmiConstant(-1)); 1218 a->Bind(&next); 1219 } 1220 1221 // Return the index of the match. 1222 { 1223 Label fast_result(a), slow_result(a, Label::kDeferred); 1224 1225 Node* const native_context = a->LoadNativeContext(context); 1226 Node* const initial_regexp_result_map = 1227 a->LoadContextElement(native_context, Context::REGEXP_RESULT_MAP_INDEX); 1228 Node* const match_indices_map = a->LoadMap(match_indices); 1229 1230 a->Branch(a->WordEqual(match_indices_map, initial_regexp_result_map), 1231 &fast_result, &slow_result); 1232 1233 a->Bind(&fast_result); 1234 { 1235 Node* const index = 1236 a->LoadObjectField(match_indices, JSRegExpResult::kIndexOffset, 1237 MachineType::AnyTagged()); 1238 a->Return(index); 1239 } 1240 1241 a->Bind(&slow_result); 1242 { 1243 Node* const name = a->HeapConstant(isolate->factory()->index_string()); 1244 Callable getproperty_callable = CodeFactory::GetProperty(a->isolate()); 1245 Node* const index = 1246 a->CallStub(getproperty_callable, context, match_indices, name); 1247 a->Return(index); 1248 } 1249 } 1250 } 1251 1252 } // namespace 1253 1254 // ES#sec-regexp.prototype-@@search 1255 // RegExp.prototype [ @@search ] ( string ) 1256 void Builtins::Generate_RegExpPrototypeSearch(CodeStubAssembler* a) { 1257 typedef CodeStubAssembler::Label Label; 1258 typedef compiler::Node Node; 1259 1260 Isolate* const isolate = a->isolate(); 1261 1262 Node* const maybe_receiver = a->Parameter(0); 1263 Node* const maybe_string = a->Parameter(1); 1264 Node* const context = a->Parameter(4); 1265 1266 // Ensure {maybe_receiver} is a JSReceiver. 1267 Node* const map = 1268 ThrowIfNotJSReceiver(a, isolate, context, maybe_receiver, 1269 MessageTemplate::kIncompatibleMethodReceiver, 1270 "RegExp.prototype.@@search"); 1271 Node* const receiver = maybe_receiver; 1272 1273 // Convert {maybe_string} to a String. 1274 Node* const string = a->ToString(context, maybe_string); 1275 1276 Label fast_path(a), slow_path(a); 1277 BranchIfFastPath(a, context, map, &fast_path, &slow_path); 1278 1279 a->Bind(&fast_path); 1280 Generate_RegExpPrototypeSearchBody(a, receiver, string, context, true); 1281 1282 a->Bind(&slow_path); 1283 Generate_RegExpPrototypeSearchBody(a, receiver, string, context, false); 1284 } 1285 1286 namespace { 1287 1288 MUST_USE_RESULT MaybeHandle<Object> ToUint32(Isolate* isolate, 1289 Handle<Object> object, 1290 uint32_t* out) { 1291 if (object->IsUndefined(isolate)) { 1292 *out = kMaxUInt32; 1293 return object; 1294 } 1295 1296 Handle<Object> number; 1297 ASSIGN_RETURN_ON_EXCEPTION(isolate, number, Object::ToNumber(object), Object); 1298 *out = NumberToUint32(*number); 1299 return object; 1300 } 1301 1302 bool AtSurrogatePair(Isolate* isolate, Handle<String> string, int index) { 1303 if (index + 1 >= string->length()) return false; 1304 const uint16_t first = string->Get(index); 1305 if (first < 0xD800 || first > 0xDBFF) return false; 1306 const uint16_t second = string->Get(index + 1); 1307 return (second >= 0xDC00 && second <= 0xDFFF); 1308 } 1309 1310 Handle<JSArray> NewJSArrayWithElements(Isolate* isolate, 1311 Handle<FixedArray> elems, 1312 int num_elems) { 1313 elems->Shrink(num_elems); 1314 return isolate->factory()->NewJSArrayWithElements(elems); 1315 } 1316 1317 MaybeHandle<JSArray> RegExpSplit(Isolate* isolate, Handle<JSRegExp> regexp, 1318 Handle<String> string, 1319 Handle<Object> limit_obj) { 1320 Factory* factory = isolate->factory(); 1321 1322 uint32_t limit; 1323 RETURN_ON_EXCEPTION(isolate, ToUint32(isolate, limit_obj, &limit), JSArray); 1324 1325 const int length = string->length(); 1326 1327 if (limit == 0) return factory->NewJSArray(0); 1328 1329 Handle<RegExpMatchInfo> last_match_info = isolate->regexp_last_match_info(); 1330 1331 if (length == 0) { 1332 Handle<Object> match_indices; 1333 ASSIGN_RETURN_ON_EXCEPTION( 1334 isolate, match_indices, 1335 RegExpImpl::Exec(regexp, string, 0, last_match_info), JSArray); 1336 1337 if (!match_indices->IsNull(isolate)) return factory->NewJSArray(0); 1338 1339 Handle<FixedArray> elems = factory->NewUninitializedFixedArray(1); 1340 elems->set(0, *string); 1341 return factory->NewJSArrayWithElements(elems); 1342 } 1343 1344 int current_index = 0; 1345 int start_index = 0; 1346 int start_match = 0; 1347 1348 static const int kInitialArraySize = 8; 1349 Handle<FixedArray> elems = factory->NewFixedArrayWithHoles(kInitialArraySize); 1350 int num_elems = 0; 1351 1352 while (true) { 1353 if (start_index == length) { 1354 Handle<String> substr = 1355 factory->NewSubString(string, current_index, length); 1356 elems = FixedArray::SetAndGrow(elems, num_elems++, substr); 1357 break; 1358 } 1359 1360 Handle<Object> match_indices_obj; 1361 ASSIGN_RETURN_ON_EXCEPTION( 1362 isolate, match_indices_obj, 1363 RegExpImpl::Exec(regexp, string, start_index, 1364 isolate->regexp_last_match_info()), 1365 JSArray); 1366 1367 if (match_indices_obj->IsNull(isolate)) { 1368 Handle<String> substr = 1369 factory->NewSubString(string, current_index, length); 1370 elems = FixedArray::SetAndGrow(elems, num_elems++, substr); 1371 break; 1372 } 1373 1374 auto match_indices = Handle<RegExpMatchInfo>::cast(match_indices_obj); 1375 1376 start_match = match_indices->Capture(0); 1377 1378 if (start_match == length) { 1379 Handle<String> substr = 1380 factory->NewSubString(string, current_index, length); 1381 elems = FixedArray::SetAndGrow(elems, num_elems++, substr); 1382 break; 1383 } 1384 1385 const int end_index = match_indices->Capture(1); 1386 1387 if (start_index == end_index && end_index == current_index) { 1388 const bool unicode = (regexp->GetFlags() & JSRegExp::kUnicode) != 0; 1389 if (unicode && AtSurrogatePair(isolate, string, start_index)) { 1390 start_index += 2; 1391 } else { 1392 start_index += 1; 1393 } 1394 continue; 1395 } 1396 1397 { 1398 Handle<String> substr = 1399 factory->NewSubString(string, current_index, start_match); 1400 elems = FixedArray::SetAndGrow(elems, num_elems++, substr); 1401 } 1402 1403 if (static_cast<uint32_t>(num_elems) == limit) break; 1404 1405 for (int i = 2; i < match_indices->NumberOfCaptureRegisters(); i += 2) { 1406 const int start = match_indices->Capture(i); 1407 const int end = match_indices->Capture(i + 1); 1408 1409 if (end != -1) { 1410 Handle<String> substr = factory->NewSubString(string, start, end); 1411 elems = FixedArray::SetAndGrow(elems, num_elems++, substr); 1412 } else { 1413 elems = FixedArray::SetAndGrow(elems, num_elems++, 1414 factory->undefined_value()); 1415 } 1416 1417 if (static_cast<uint32_t>(num_elems) == limit) { 1418 return NewJSArrayWithElements(isolate, elems, num_elems); 1419 } 1420 } 1421 1422 start_index = current_index = end_index; 1423 } 1424 1425 return NewJSArrayWithElements(isolate, elems, num_elems); 1426 } 1427 1428 // ES##sec-speciesconstructor 1429 // SpeciesConstructor ( O, defaultConstructor ) 1430 MUST_USE_RESULT MaybeHandle<Object> SpeciesConstructor( 1431 Isolate* isolate, Handle<JSReceiver> recv, 1432 Handle<JSFunction> default_ctor) { 1433 Handle<Object> ctor_obj; 1434 ASSIGN_RETURN_ON_EXCEPTION( 1435 isolate, ctor_obj, 1436 JSObject::GetProperty(recv, isolate->factory()->constructor_string()), 1437 Object); 1438 1439 if (ctor_obj->IsUndefined(isolate)) return default_ctor; 1440 1441 if (!ctor_obj->IsJSReceiver()) { 1442 THROW_NEW_ERROR(isolate, 1443 NewTypeError(MessageTemplate::kConstructorNotReceiver), 1444 Object); 1445 } 1446 1447 Handle<JSReceiver> ctor = Handle<JSReceiver>::cast(ctor_obj); 1448 1449 Handle<Object> species; 1450 ASSIGN_RETURN_ON_EXCEPTION( 1451 isolate, species, 1452 JSObject::GetProperty(ctor, isolate->factory()->species_symbol()), 1453 Object); 1454 1455 if (species->IsNull(isolate) || species->IsUndefined(isolate)) { 1456 return default_ctor; 1457 } 1458 1459 if (species->IsConstructor()) return species; 1460 1461 THROW_NEW_ERROR( 1462 isolate, NewTypeError(MessageTemplate::kSpeciesNotConstructor), Object); 1463 } 1464 1465 } // namespace 1466 1467 // ES#sec-regexp.prototype-@@split 1468 // RegExp.prototype [ @@split ] ( string, limit ) 1469 BUILTIN(RegExpPrototypeSplit) { 1470 HandleScope scope(isolate); 1471 CHECK_RECEIVER(JSReceiver, recv, "RegExp.prototype.@@split"); 1472 1473 Factory* factory = isolate->factory(); 1474 1475 Handle<Object> string_obj = args.atOrUndefined(isolate, 1); 1476 Handle<Object> limit_obj = args.atOrUndefined(isolate, 2); 1477 1478 Handle<String> string; 1479 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, string, 1480 Object::ToString(isolate, string_obj)); 1481 1482 if (RegExpUtils::IsUnmodifiedRegExp(isolate, recv)) { 1483 RETURN_RESULT_OR_FAILURE( 1484 isolate, 1485 RegExpSplit(isolate, Handle<JSRegExp>::cast(recv), string, limit_obj)); 1486 } 1487 1488 Handle<JSFunction> regexp_fun = isolate->regexp_function(); 1489 Handle<Object> ctor; 1490 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( 1491 isolate, ctor, SpeciesConstructor(isolate, recv, regexp_fun)); 1492 1493 Handle<Object> flags_obj; 1494 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( 1495 isolate, flags_obj, JSObject::GetProperty(recv, factory->flags_string())); 1496 1497 Handle<String> flags; 1498 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, flags, 1499 Object::ToString(isolate, flags_obj)); 1500 1501 Handle<String> u_str = factory->LookupSingleCharacterStringFromCode('u'); 1502 const bool unicode = (String::IndexOf(isolate, flags, u_str, 0) >= 0); 1503 1504 Handle<String> y_str = factory->LookupSingleCharacterStringFromCode('y'); 1505 const bool sticky = (String::IndexOf(isolate, flags, y_str, 0) >= 0); 1506 1507 Handle<String> new_flags = flags; 1508 if (!sticky) { 1509 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, new_flags, 1510 factory->NewConsString(flags, y_str)); 1511 } 1512 1513 Handle<JSReceiver> splitter; 1514 { 1515 const int argc = 2; 1516 1517 ScopedVector<Handle<Object>> argv(argc); 1518 argv[0] = recv; 1519 argv[1] = new_flags; 1520 1521 Handle<JSFunction> ctor_fun = Handle<JSFunction>::cast(ctor); 1522 Handle<Object> splitter_obj; 1523 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( 1524 isolate, splitter_obj, Execution::New(ctor_fun, argc, argv.start())); 1525 1526 splitter = Handle<JSReceiver>::cast(splitter_obj); 1527 } 1528 1529 uint32_t limit; 1530 RETURN_FAILURE_ON_EXCEPTION(isolate, ToUint32(isolate, limit_obj, &limit)); 1531 1532 const int length = string->length(); 1533 1534 if (limit == 0) return *factory->NewJSArray(0); 1535 1536 if (length == 0) { 1537 Handle<Object> result; 1538 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( 1539 isolate, result, RegExpUtils::RegExpExec(isolate, splitter, string, 1540 factory->undefined_value())); 1541 1542 if (!result->IsNull(isolate)) return *factory->NewJSArray(0); 1543 1544 Handle<FixedArray> elems = factory->NewUninitializedFixedArray(1); 1545 elems->set(0, *string); 1546 return *factory->NewJSArrayWithElements(elems); 1547 } 1548 1549 // TODO(jgruber): Wrap this in a helper class. 1550 static const int kInitialArraySize = 8; 1551 Handle<FixedArray> elems = factory->NewFixedArrayWithHoles(kInitialArraySize); 1552 int num_elems = 0; 1553 1554 int string_index = 0; 1555 int prev_string_index = 0; 1556 while (string_index < length) { 1557 RETURN_FAILURE_ON_EXCEPTION( 1558 isolate, RegExpUtils::SetLastIndex(isolate, splitter, string_index)); 1559 1560 Handle<Object> result; 1561 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( 1562 isolate, result, RegExpUtils::RegExpExec(isolate, splitter, string, 1563 factory->undefined_value())); 1564 1565 if (result->IsNull(isolate)) { 1566 string_index = RegExpUtils::AdvanceStringIndex(isolate, string, 1567 string_index, unicode); 1568 continue; 1569 } 1570 1571 // TODO(jgruber): Extract toLength of some property into function. 1572 Handle<Object> last_index_obj; 1573 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( 1574 isolate, last_index_obj, RegExpUtils::GetLastIndex(isolate, splitter)); 1575 1576 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( 1577 isolate, last_index_obj, Object::ToLength(isolate, last_index_obj)); 1578 const int last_index = Handle<Smi>::cast(last_index_obj)->value(); 1579 1580 const int end = std::min(last_index, length); 1581 if (end == prev_string_index) { 1582 string_index = RegExpUtils::AdvanceStringIndex(isolate, string, 1583 string_index, unicode); 1584 continue; 1585 } 1586 1587 { 1588 Handle<String> substr = 1589 factory->NewSubString(string, prev_string_index, string_index); 1590 elems = FixedArray::SetAndGrow(elems, num_elems++, substr); 1591 if (static_cast<uint32_t>(num_elems) == limit) { 1592 return *NewJSArrayWithElements(isolate, elems, num_elems); 1593 } 1594 } 1595 1596 prev_string_index = end; 1597 1598 Handle<Object> num_captures_obj; 1599 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( 1600 isolate, num_captures_obj, 1601 Object::GetProperty(result, isolate->factory()->length_string())); 1602 1603 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( 1604 isolate, num_captures_obj, Object::ToLength(isolate, num_captures_obj)); 1605 const int num_captures = 1606 std::max(Handle<Smi>::cast(num_captures_obj)->value(), 0); 1607 1608 for (int i = 1; i < num_captures; i++) { 1609 Handle<Object> capture; 1610 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( 1611 isolate, capture, Object::GetElement(isolate, result, i)); 1612 elems = FixedArray::SetAndGrow(elems, num_elems++, capture); 1613 if (static_cast<uint32_t>(num_elems) == limit) { 1614 return *NewJSArrayWithElements(isolate, elems, num_elems); 1615 } 1616 } 1617 1618 string_index = prev_string_index; 1619 } 1620 1621 { 1622 Handle<String> substr = 1623 factory->NewSubString(string, prev_string_index, length); 1624 elems = FixedArray::SetAndGrow(elems, num_elems++, substr); 1625 } 1626 1627 return *NewJSArrayWithElements(isolate, elems, num_elems); 1628 } 1629 1630 namespace { 1631 1632 compiler::Node* ReplaceGlobalCallableFastPath( 1633 CodeStubAssembler* a, compiler::Node* context, compiler::Node* regexp, 1634 compiler::Node* subject_string, compiler::Node* replace_callable) { 1635 // The fast path is reached only if {receiver} is a global unmodified 1636 // JSRegExp instance and {replace_callable} is callable. 1637 1638 typedef CodeStubAssembler::Variable Variable; 1639 typedef CodeStubAssembler::Label Label; 1640 typedef compiler::Node Node; 1641 1642 Isolate* const isolate = a->isolate(); 1643 1644 Node* const null = a->NullConstant(); 1645 Node* const undefined = a->UndefinedConstant(); 1646 Node* const int_zero = a->IntPtrConstant(0); 1647 Node* const int_one = a->IntPtrConstant(1); 1648 Node* const smi_zero = a->SmiConstant(Smi::kZero); 1649 1650 Node* const native_context = a->LoadNativeContext(context); 1651 1652 Label out(a); 1653 Variable var_result(a, MachineRepresentation::kTagged); 1654 1655 // Set last index to 0. 1656 FastStoreLastIndex(a, context, regexp, smi_zero); 1657 1658 // Allocate {result_array}. 1659 Node* result_array; 1660 { 1661 ElementsKind kind = FAST_ELEMENTS; 1662 Node* const array_map = a->LoadJSArrayElementsMap(kind, native_context); 1663 Node* const capacity = a->IntPtrConstant(16); 1664 Node* const length = smi_zero; 1665 Node* const allocation_site = nullptr; 1666 CodeStubAssembler::ParameterMode capacity_mode = 1667 CodeStubAssembler::INTPTR_PARAMETERS; 1668 1669 result_array = a->AllocateJSArray(kind, array_map, capacity, length, 1670 allocation_site, capacity_mode); 1671 } 1672 1673 // Call into runtime for RegExpExecMultiple. 1674 Node* last_match_info = a->LoadContextElement( 1675 native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX); 1676 Node* const res = 1677 a->CallRuntime(Runtime::kRegExpExecMultiple, context, regexp, 1678 subject_string, last_match_info, result_array); 1679 1680 // Reset last index to 0. 1681 FastStoreLastIndex(a, context, regexp, smi_zero); 1682 1683 // If no matches, return the subject string. 1684 var_result.Bind(subject_string); 1685 a->GotoIf(a->WordEqual(res, null), &out); 1686 1687 // Reload last match info since it might have changed. 1688 last_match_info = a->LoadContextElement( 1689 native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX); 1690 1691 Node* const res_length = a->LoadJSArrayLength(res); 1692 Node* const res_elems = a->LoadElements(res); 1693 CSA_ASSERT(a, a->HasInstanceType(res_elems, FIXED_ARRAY_TYPE)); 1694 1695 CodeStubAssembler::ParameterMode mode = CodeStubAssembler::INTPTR_PARAMETERS; 1696 Node* const num_capture_registers = a->LoadFixedArrayElement( 1697 last_match_info, 1698 a->IntPtrConstant(RegExpMatchInfo::kNumberOfCapturesIndex), 0, mode); 1699 1700 Label if_hasexplicitcaptures(a), if_noexplicitcaptures(a), create_result(a); 1701 a->Branch(a->SmiEqual(num_capture_registers, a->SmiConstant(Smi::FromInt(2))), 1702 &if_noexplicitcaptures, &if_hasexplicitcaptures); 1703 1704 a->Bind(&if_noexplicitcaptures); 1705 { 1706 // If the number of captures is two then there are no explicit captures in 1707 // the regexp, just the implicit capture that captures the whole match. In 1708 // this case we can simplify quite a bit and end up with something faster. 1709 // The builder will consist of some integers that indicate slices of the 1710 // input string and some replacements that were returned from the replace 1711 // function. 1712 1713 Variable var_match_start(a, MachineRepresentation::kTagged); 1714 var_match_start.Bind(smi_zero); 1715 1716 Node* const end = a->SmiUntag(res_length); 1717 Variable var_i(a, MachineType::PointerRepresentation()); 1718 var_i.Bind(int_zero); 1719 1720 Variable* vars[] = {&var_i, &var_match_start}; 1721 Label loop(a, 2, vars); 1722 a->Goto(&loop); 1723 a->Bind(&loop); 1724 { 1725 Node* const i = var_i.value(); 1726 a->GotoUnless(a->IntPtrLessThan(i, end), &create_result); 1727 1728 CodeStubAssembler::ParameterMode mode = 1729 CodeStubAssembler::INTPTR_PARAMETERS; 1730 Node* const elem = a->LoadFixedArrayElement(res_elems, i, 0, mode); 1731 1732 Label if_issmi(a), if_isstring(a), loop_epilogue(a); 1733 a->Branch(a->TaggedIsSmi(elem), &if_issmi, &if_isstring); 1734 1735 a->Bind(&if_issmi); 1736 { 1737 // Integers represent slices of the original string. 1738 Label if_isnegativeorzero(a), if_ispositive(a); 1739 a->BranchIfSmiLessThanOrEqual(elem, smi_zero, &if_isnegativeorzero, 1740 &if_ispositive); 1741 1742 a->Bind(&if_ispositive); 1743 { 1744 Node* const int_elem = a->SmiUntag(elem); 1745 Node* const new_match_start = 1746 a->IntPtrAdd(a->WordShr(int_elem, a->IntPtrConstant(11)), 1747 a->WordAnd(int_elem, a->IntPtrConstant(0x7ff))); 1748 var_match_start.Bind(a->SmiTag(new_match_start)); 1749 a->Goto(&loop_epilogue); 1750 } 1751 1752 a->Bind(&if_isnegativeorzero); 1753 { 1754 Node* const next_i = a->IntPtrAdd(i, int_one); 1755 var_i.Bind(next_i); 1756 1757 Node* const next_elem = 1758 a->LoadFixedArrayElement(res_elems, next_i, 0, mode); 1759 1760 Node* const new_match_start = a->SmiSub(next_elem, elem); 1761 var_match_start.Bind(new_match_start); 1762 a->Goto(&loop_epilogue); 1763 } 1764 } 1765 1766 a->Bind(&if_isstring); 1767 { 1768 CSA_ASSERT(a, a->IsStringInstanceType(a->LoadInstanceType(elem))); 1769 1770 Callable call_callable = CodeFactory::Call(isolate); 1771 Node* const replacement_obj = 1772 a->CallJS(call_callable, context, replace_callable, undefined, elem, 1773 var_match_start.value(), subject_string); 1774 1775 Node* const replacement_str = a->ToString(context, replacement_obj); 1776 a->StoreFixedArrayElement(res_elems, i, replacement_str); 1777 1778 Node* const elem_length = a->LoadStringLength(elem); 1779 Node* const new_match_start = 1780 a->SmiAdd(var_match_start.value(), elem_length); 1781 var_match_start.Bind(new_match_start); 1782 1783 a->Goto(&loop_epilogue); 1784 } 1785 1786 a->Bind(&loop_epilogue); 1787 { 1788 var_i.Bind(a->IntPtrAdd(var_i.value(), int_one)); 1789 a->Goto(&loop); 1790 } 1791 } 1792 } 1793 1794 a->Bind(&if_hasexplicitcaptures); 1795 { 1796 CodeStubAssembler::ParameterMode mode = 1797 CodeStubAssembler::INTPTR_PARAMETERS; 1798 1799 Node* const from = int_zero; 1800 Node* const to = a->SmiUntag(res_length); 1801 const int increment = 1; 1802 1803 a->BuildFastLoop( 1804 MachineType::PointerRepresentation(), from, to, 1805 [res_elems, isolate, native_context, context, undefined, 1806 replace_callable, mode](CodeStubAssembler* a, Node* index) { 1807 Node* const elem = 1808 a->LoadFixedArrayElement(res_elems, index, 0, mode); 1809 1810 Label do_continue(a); 1811 a->GotoIf(a->TaggedIsSmi(elem), &do_continue); 1812 1813 // elem must be an Array. 1814 // Use the apply argument as backing for global RegExp properties. 1815 1816 CSA_ASSERT(a, a->HasInstanceType(elem, JS_ARRAY_TYPE)); 1817 1818 // TODO(jgruber): Remove indirection through Call->ReflectApply. 1819 Callable call_callable = CodeFactory::Call(isolate); 1820 Node* const reflect_apply = a->LoadContextElement( 1821 native_context, Context::REFLECT_APPLY_INDEX); 1822 1823 Node* const replacement_obj = 1824 a->CallJS(call_callable, context, reflect_apply, undefined, 1825 replace_callable, undefined, elem); 1826 1827 // Overwrite the i'th element in the results with the string we got 1828 // back from the callback function. 1829 1830 Node* const replacement_str = a->ToString(context, replacement_obj); 1831 a->StoreFixedArrayElement(res_elems, index, replacement_str, 1832 UPDATE_WRITE_BARRIER, mode); 1833 1834 a->Goto(&do_continue); 1835 a->Bind(&do_continue); 1836 }, 1837 increment, CodeStubAssembler::IndexAdvanceMode::kPost); 1838 1839 a->Goto(&create_result); 1840 } 1841 1842 a->Bind(&create_result); 1843 { 1844 Node* const result = a->CallRuntime(Runtime::kStringBuilderConcat, context, 1845 res, res_length, subject_string); 1846 var_result.Bind(result); 1847 a->Goto(&out); 1848 } 1849 1850 a->Bind(&out); 1851 return var_result.value(); 1852 } 1853 1854 compiler::Node* ReplaceSimpleStringFastPath(CodeStubAssembler* a, 1855 compiler::Node* context, 1856 compiler::Node* regexp, 1857 compiler::Node* subject_string, 1858 compiler::Node* replace_string) { 1859 // The fast path is reached only if {receiver} is an unmodified 1860 // JSRegExp instance, {replace_value} is non-callable, and 1861 // ToString({replace_value}) does not contain '$', i.e. we're doing a simple 1862 // string replacement. 1863 1864 typedef CodeStubAssembler::Variable Variable; 1865 typedef CodeStubAssembler::Label Label; 1866 typedef compiler::Node Node; 1867 1868 Isolate* const isolate = a->isolate(); 1869 1870 Node* const null = a->NullConstant(); 1871 Node* const int_zero = a->IntPtrConstant(0); 1872 Node* const smi_zero = a->SmiConstant(Smi::kZero); 1873 1874 Label out(a); 1875 Variable var_result(a, MachineRepresentation::kTagged); 1876 1877 // Load the last match info. 1878 Node* const native_context = a->LoadNativeContext(context); 1879 Node* const last_match_info = a->LoadContextElement( 1880 native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX); 1881 1882 // Is {regexp} global? 1883 Label if_isglobal(a), if_isnonglobal(a); 1884 Node* const flags = a->LoadObjectField(regexp, JSRegExp::kFlagsOffset); 1885 Node* const is_global = 1886 a->WordAnd(a->SmiUntag(flags), a->IntPtrConstant(JSRegExp::kGlobal)); 1887 a->Branch(a->WordEqual(is_global, int_zero), &if_isnonglobal, &if_isglobal); 1888 1889 a->Bind(&if_isglobal); 1890 { 1891 // Hand off global regexps to runtime. 1892 FastStoreLastIndex(a, context, regexp, smi_zero); 1893 Node* const result = 1894 a->CallRuntime(Runtime::kStringReplaceGlobalRegExpWithString, context, 1895 subject_string, regexp, replace_string, last_match_info); 1896 var_result.Bind(result); 1897 a->Goto(&out); 1898 } 1899 1900 a->Bind(&if_isnonglobal); 1901 { 1902 // Run exec, then manually construct the resulting string. 1903 Callable exec_callable = CodeFactory::RegExpExec(isolate); 1904 Node* const match_indices = 1905 a->CallStub(exec_callable, context, regexp, subject_string, smi_zero, 1906 last_match_info); 1907 1908 Label if_matched(a), if_didnotmatch(a); 1909 a->Branch(a->WordEqual(match_indices, null), &if_didnotmatch, &if_matched); 1910 1911 a->Bind(&if_didnotmatch); 1912 { 1913 FastStoreLastIndex(a, context, regexp, smi_zero); 1914 var_result.Bind(subject_string); 1915 a->Goto(&out); 1916 } 1917 1918 a->Bind(&if_matched); 1919 { 1920 CodeStubAssembler::ParameterMode mode = 1921 CodeStubAssembler::INTPTR_PARAMETERS; 1922 1923 Node* const subject_start = smi_zero; 1924 Node* const match_start = a->LoadFixedArrayElement( 1925 match_indices, a->IntPtrConstant(RegExpMatchInfo::kFirstCaptureIndex), 1926 0, mode); 1927 Node* const match_end = a->LoadFixedArrayElement( 1928 match_indices, 1929 a->IntPtrConstant(RegExpMatchInfo::kFirstCaptureIndex + 1), 0, mode); 1930 Node* const subject_end = a->LoadStringLength(subject_string); 1931 1932 Label if_replaceisempty(a), if_replaceisnotempty(a); 1933 Node* const replace_length = a->LoadStringLength(replace_string); 1934 a->Branch(a->SmiEqual(replace_length, smi_zero), &if_replaceisempty, 1935 &if_replaceisnotempty); 1936 1937 a->Bind(&if_replaceisempty); 1938 { 1939 // TODO(jgruber): We could skip many of the checks that using SubString 1940 // here entails. 1941 1942 Node* const first_part = 1943 a->SubString(context, subject_string, subject_start, match_start); 1944 Node* const second_part = 1945 a->SubString(context, subject_string, match_end, subject_end); 1946 1947 Node* const result = a->StringAdd(context, first_part, second_part); 1948 var_result.Bind(result); 1949 a->Goto(&out); 1950 } 1951 1952 a->Bind(&if_replaceisnotempty); 1953 { 1954 Node* const first_part = 1955 a->SubString(context, subject_string, subject_start, match_start); 1956 Node* const second_part = replace_string; 1957 Node* const third_part = 1958 a->SubString(context, subject_string, match_end, subject_end); 1959 1960 Node* result = a->StringAdd(context, first_part, second_part); 1961 result = a->StringAdd(context, result, third_part); 1962 1963 var_result.Bind(result); 1964 a->Goto(&out); 1965 } 1966 } 1967 } 1968 1969 a->Bind(&out); 1970 return var_result.value(); 1971 } 1972 1973 } // namespace 1974 1975 // ES#sec-regexp.prototype-@@replace 1976 // RegExp.prototype [ @@replace ] ( string, replaceValue ) 1977 void Builtins::Generate_RegExpPrototypeReplace(CodeStubAssembler* a) { 1978 typedef CodeStubAssembler::Label Label; 1979 typedef compiler::Node Node; 1980 1981 Isolate* const isolate = a->isolate(); 1982 1983 Node* const maybe_receiver = a->Parameter(0); 1984 Node* const maybe_string = a->Parameter(1); 1985 Node* const replace_value = a->Parameter(2); 1986 Node* const context = a->Parameter(5); 1987 1988 Node* const int_zero = a->IntPtrConstant(0); 1989 1990 // Ensure {maybe_receiver} is a JSReceiver. 1991 Node* const map = 1992 ThrowIfNotJSReceiver(a, isolate, context, maybe_receiver, 1993 MessageTemplate::kIncompatibleMethodReceiver, 1994 "RegExp.prototype.@@replace"); 1995 Node* const receiver = maybe_receiver; 1996 1997 // Convert {maybe_string} to a String. 1998 Callable tostring_callable = CodeFactory::ToString(isolate); 1999 Node* const string = a->CallStub(tostring_callable, context, maybe_string); 2000 2001 // Fast-path checks: 1. Is the {receiver} an unmodified JSRegExp instance? 2002 Label checkreplacecallable(a), runtime(a, Label::kDeferred), fastpath(a); 2003 BranchIfFastPath(a, context, map, &checkreplacecallable, &runtime); 2004 2005 a->Bind(&checkreplacecallable); 2006 Node* const regexp = receiver; 2007 2008 // 2. Is {replace_value} callable? 2009 Label checkreplacestring(a), if_iscallable(a); 2010 a->GotoIf(a->TaggedIsSmi(replace_value), &checkreplacestring); 2011 2012 Node* const replace_value_map = a->LoadMap(replace_value); 2013 a->Branch(a->IsCallableMap(replace_value_map), &if_iscallable, 2014 &checkreplacestring); 2015 2016 // 3. Does ToString({replace_value}) contain '$'? 2017 a->Bind(&checkreplacestring); 2018 { 2019 Node* const replace_string = 2020 a->CallStub(tostring_callable, context, replace_value); 2021 2022 Node* const dollar_char = a->IntPtrConstant('$'); 2023 Node* const smi_minusone = a->SmiConstant(Smi::FromInt(-1)); 2024 a->GotoUnless(a->SmiEqual(a->StringIndexOfChar(context, replace_string, 2025 dollar_char, int_zero), 2026 smi_minusone), 2027 &runtime); 2028 2029 a->Return(ReplaceSimpleStringFastPath(a, context, regexp, string, 2030 replace_string)); 2031 } 2032 2033 // {regexp} is unmodified and {replace_value} is callable. 2034 a->Bind(&if_iscallable); 2035 { 2036 Node* const replace_callable = replace_value; 2037 2038 // Check if the {regexp} is global. 2039 Label if_isglobal(a), if_isnotglobal(a); 2040 Node* const is_global = FastFlagGetter(a, regexp, JSRegExp::kGlobal); 2041 a->Branch(is_global, &if_isglobal, &if_isnotglobal); 2042 2043 a->Bind(&if_isglobal); 2044 { 2045 Node* const result = ReplaceGlobalCallableFastPath( 2046 a, context, regexp, string, replace_callable); 2047 a->Return(result); 2048 } 2049 2050 a->Bind(&if_isnotglobal); 2051 { 2052 Node* const result = 2053 a->CallRuntime(Runtime::kStringReplaceNonGlobalRegExpWithFunction, 2054 context, string, regexp, replace_callable); 2055 a->Return(result); 2056 } 2057 } 2058 2059 a->Bind(&runtime); 2060 { 2061 Node* const result = a->CallRuntime(Runtime::kRegExpReplace, context, 2062 receiver, string, replace_value); 2063 a->Return(result); 2064 } 2065 } 2066 2067 // Simple string matching functionality for internal use which does not modify 2068 // the last match info. 2069 void Builtins::Generate_RegExpInternalMatch(CodeStubAssembler* a) { 2070 typedef CodeStubAssembler::Label Label; 2071 typedef compiler::Node Node; 2072 2073 Isolate* const isolate = a->isolate(); 2074 2075 Node* const regexp = a->Parameter(1); 2076 Node* const string = a->Parameter(2); 2077 Node* const context = a->Parameter(5); 2078 2079 Node* const null = a->NullConstant(); 2080 Node* const smi_zero = a->SmiConstant(Smi::FromInt(0)); 2081 2082 Node* const native_context = a->LoadNativeContext(context); 2083 Node* const internal_match_info = a->LoadContextElement( 2084 native_context, Context::REGEXP_INTERNAL_MATCH_INFO_INDEX); 2085 2086 Callable exec_callable = CodeFactory::RegExpExec(isolate); 2087 Node* const match_indices = a->CallStub( 2088 exec_callable, context, regexp, string, smi_zero, internal_match_info); 2089 2090 Label if_matched(a), if_didnotmatch(a); 2091 a->Branch(a->WordEqual(match_indices, null), &if_didnotmatch, &if_matched); 2092 2093 a->Bind(&if_didnotmatch); 2094 a->Return(null); 2095 2096 a->Bind(&if_matched); 2097 { 2098 Node* result = ConstructNewResultFromMatchInfo(isolate, a, context, 2099 match_indices, string); 2100 a->Return(result); 2101 } 2102 } 2103 2104 } // namespace internal 2105 } // namespace v8 2106