Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2016 the V8 project authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "src/builtins/builtins-utils.h" | 5 #include "src/builtins/builtins-utils.h" |
| 6 #include "src/builtins/builtins.h" | 6 #include "src/builtins/builtins.h" |
| 7 | 7 |
| 8 #include "src/code-factory.h" | 8 #include "src/code-factory.h" |
| 9 #include "src/regexp/jsregexp.h" | 9 #include "src/regexp/jsregexp.h" |
| 10 #include "src/regexp/regexp-utils.h" | 10 #include "src/regexp/regexp-utils.h" |
| 11 #include "src/string-builder.h" | 11 #include "src/string-builder.h" |
| 12 | 12 |
| 13 namespace v8 { | 13 namespace v8 { |
| 14 namespace internal { | 14 namespace internal { |
| 15 | 15 |
| 16 typedef compiler::Node Node; | 16 typedef compiler::Node Node; |
| 17 typedef CodeStubAssembler::Label CLabel; | 17 typedef CodeStubAssembler::Label CLabel; |
| 18 typedef CodeStubAssembler::Variable CVariable; | 18 typedef CodeStubAssembler::Variable CVariable; |
| 19 typedef CodeStubAssembler::ParameterMode ParameterMode; | 19 typedef CodeStubAssembler::ParameterMode ParameterMode; |
| 20 typedef compiler::CodeAssemblerState CodeAssemblerState; | 20 typedef compiler::CodeAssemblerState CodeAssemblerState; |
| 21 | 21 |
| 22 // ----------------------------------------------------------------------------- | 22 // ----------------------------------------------------------------------------- |
| 23 // ES6 section 21.2 RegExp Objects | 23 // ES6 section 21.2 RegExp Objects |
| 24 | 24 |
| 25 namespace { | 25 namespace { |
| 26 | 26 |
| 27 Handle<String> PatternFlags(Isolate* isolate, Handle<JSRegExp> regexp) { | |
| 28 static const int kMaxFlagsLength = 5 + 1; // 5 flags and '\0'; | |
| 29 char flags_string[kMaxFlagsLength]; | |
| 30 int i = 0; | |
| 31 | |
| 32 const JSRegExp::Flags flags = regexp->GetFlags(); | |
| 33 | |
| 34 if ((flags & JSRegExp::kGlobal) != 0) flags_string[i++] = 'g'; | |
| 35 if ((flags & JSRegExp::kIgnoreCase) != 0) flags_string[i++] = 'i'; | |
| 36 if ((flags & JSRegExp::kMultiline) != 0) flags_string[i++] = 'm'; | |
| 37 if ((flags & JSRegExp::kUnicode) != 0) flags_string[i++] = 'u'; | |
| 38 if ((flags & JSRegExp::kSticky) != 0) flags_string[i++] = 'y'; | |
| 39 | |
| 40 DCHECK_LT(i, kMaxFlagsLength); | |
| 41 memset(&flags_string[i], '\0', kMaxFlagsLength - i); | |
| 42 | |
| 43 return isolate->factory()->NewStringFromAsciiChecked(flags_string); | |
| 44 } | |
| 45 | |
| 46 // ES#sec-regexpinitialize | |
| 47 // Runtime Semantics: RegExpInitialize ( obj, pattern, flags ) | |
| 48 MUST_USE_RESULT MaybeHandle<JSRegExp> RegExpInitialize(Isolate* isolate, | |
| 49 Handle<JSRegExp> regexp, | |
| 50 Handle<Object> pattern, | |
| 51 Handle<Object> flags) { | |
| 52 Handle<String> pattern_string; | |
| 53 if (pattern->IsUndefined(isolate)) { | |
| 54 pattern_string = isolate->factory()->empty_string(); | |
| 55 } else { | |
| 56 ASSIGN_RETURN_ON_EXCEPTION(isolate, pattern_string, | |
| 57 Object::ToString(isolate, pattern), JSRegExp); | |
| 58 } | |
| 59 | |
| 60 Handle<String> flags_string; | |
| 61 if (flags->IsUndefined(isolate)) { | |
| 62 flags_string = isolate->factory()->empty_string(); | |
| 63 } else { | |
| 64 ASSIGN_RETURN_ON_EXCEPTION(isolate, flags_string, | |
| 65 Object::ToString(isolate, flags), JSRegExp); | |
| 66 } | |
| 67 | |
| 68 // TODO(jgruber): We could avoid the flags back and forth conversions. | |
| 69 return JSRegExp::Initialize(regexp, pattern_string, flags_string); | |
| 70 } | |
| 71 | |
| 72 } // namespace | |
| 73 | |
| 74 // ES#sec-regexp-pattern-flags | |
| 75 // RegExp ( pattern, flags ) | |
| 76 BUILTIN(RegExpConstructor) { | |
| 77 HandleScope scope(isolate); | |
| 78 | |
| 79 Handle<HeapObject> new_target = args.new_target(); | |
| 80 Handle<Object> pattern = args.atOrUndefined(isolate, 1); | |
| 81 Handle<Object> flags = args.atOrUndefined(isolate, 2); | |
| 82 | |
| 83 Handle<JSFunction> target = isolate->regexp_function(); | |
| 84 | |
| 85 bool pattern_is_regexp; | |
| 86 { | |
| 87 Maybe<bool> maybe_pattern_is_regexp = | |
| 88 RegExpUtils::IsRegExp(isolate, pattern); | |
| 89 if (maybe_pattern_is_regexp.IsNothing()) { | |
| 90 DCHECK(isolate->has_pending_exception()); | |
| 91 return isolate->heap()->exception(); | |
| 92 } | |
| 93 pattern_is_regexp = maybe_pattern_is_regexp.FromJust(); | |
| 94 } | |
| 95 | |
| 96 if (new_target->IsUndefined(isolate)) { | |
| 97 new_target = target; | |
| 98 | |
| 99 // ES6 section 21.2.3.1 step 3.b | |
| 100 if (pattern_is_regexp && flags->IsUndefined(isolate)) { | |
| 101 Handle<Object> pattern_constructor; | |
| 102 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( | |
| 103 isolate, pattern_constructor, | |
| 104 Object::GetProperty(pattern, | |
| 105 isolate->factory()->constructor_string())); | |
| 106 | |
| 107 if (pattern_constructor.is_identical_to(new_target)) { | |
| 108 return *pattern; | |
| 109 } | |
| 110 } | |
| 111 } | |
| 112 | |
| 113 if (pattern->IsJSRegExp()) { | |
| 114 Handle<JSRegExp> regexp_pattern = Handle<JSRegExp>::cast(pattern); | |
| 115 | |
| 116 if (flags->IsUndefined(isolate)) { | |
| 117 flags = PatternFlags(isolate, regexp_pattern); | |
| 118 } | |
| 119 pattern = handle(regexp_pattern->source(), isolate); | |
| 120 } else if (pattern_is_regexp) { | |
| 121 Handle<Object> pattern_source; | |
| 122 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( | |
| 123 isolate, pattern_source, | |
| 124 Object::GetProperty(pattern, isolate->factory()->source_string())); | |
| 125 | |
| 126 if (flags->IsUndefined(isolate)) { | |
| 127 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( | |
| 128 isolate, flags, | |
| 129 Object::GetProperty(pattern, isolate->factory()->flags_string())); | |
| 130 } | |
| 131 pattern = pattern_source; | |
| 132 } | |
| 133 | |
| 134 Handle<JSReceiver> new_target_receiver = Handle<JSReceiver>::cast(new_target); | |
| 135 | |
| 136 // TODO(jgruber): Fast-path for target == new_target == unmodified JSRegExp. | |
| 137 | |
| 138 Handle<JSObject> object; | |
| 139 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( | |
| 140 isolate, object, JSObject::New(target, new_target_receiver)); | |
| 141 Handle<JSRegExp> regexp = Handle<JSRegExp>::cast(object); | |
| 142 | |
| 143 RETURN_RESULT_OR_FAILURE(isolate, | |
| 144 RegExpInitialize(isolate, regexp, pattern, flags)); | |
| 145 } | |
| 146 | |
| 147 BUILTIN(RegExpPrototypeCompile) { | |
| 148 HandleScope scope(isolate); | |
| 149 CHECK_RECEIVER(JSRegExp, regexp, "RegExp.prototype.compile"); | |
| 150 | |
| 151 Handle<Object> pattern = args.atOrUndefined(isolate, 1); | |
| 152 Handle<Object> flags = args.atOrUndefined(isolate, 2); | |
| 153 | |
| 154 if (pattern->IsJSRegExp()) { | |
| 155 Handle<JSRegExp> pattern_regexp = Handle<JSRegExp>::cast(pattern); | |
| 156 | |
| 157 if (!flags->IsUndefined(isolate)) { | |
| 158 THROW_NEW_ERROR_RETURN_FAILURE( | |
| 159 isolate, NewTypeError(MessageTemplate::kRegExpFlags)); | |
| 160 } | |
| 161 | |
| 162 flags = PatternFlags(isolate, pattern_regexp); | |
| 163 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( | |
| 164 isolate, pattern, | |
| 165 Object::GetProperty(pattern, isolate->factory()->source_string())); | |
| 166 } | |
| 167 | |
| 168 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( | |
| 169 isolate, regexp, RegExpInitialize(isolate, regexp, pattern, flags)); | |
| 170 | |
| 171 // Return undefined for compatibility with JSC. | |
| 172 // See http://crbug.com/585775 for web compat details. | |
| 173 | |
| 174 return isolate->heap()->undefined_value(); | |
| 175 } | |
| 176 | |
| 177 namespace { | |
| 178 | |
| 179 Node* FastLoadLastIndex(CodeStubAssembler* a, Node* regexp) { | 27 Node* FastLoadLastIndex(CodeStubAssembler* a, Node* regexp) { |
| 180 // Load the in-object field. | 28 // Load the in-object field. |
| 181 static const int field_offset = | 29 static const int field_offset = |
| 182 JSRegExp::kSize + JSRegExp::kLastIndexFieldIndex * kPointerSize; | 30 JSRegExp::kSize + JSRegExp::kLastIndexFieldIndex * kPointerSize; |
| 183 return a->LoadObjectField(regexp, field_offset); | 31 return a->LoadObjectField(regexp, field_offset); |
| 184 } | 32 } |
| 185 | 33 |
| 186 Node* SlowLoadLastIndex(CodeStubAssembler* a, Node* context, Node* regexp) { | 34 Node* SlowLoadLastIndex(CodeStubAssembler* a, Node* context, Node* regexp) { |
| 187 // Load through the GetProperty stub. | 35 // Load through the GetProperty stub. |
| 188 Node* const name = | 36 Node* const name = |
| (...skipping 383 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 572 } | 420 } |
| 573 | 421 |
| 574 a.Bind(&if_isslowpath); | 422 a.Bind(&if_isslowpath); |
| 575 { | 423 { |
| 576 Node* const result = | 424 Node* const result = |
| 577 RegExpPrototypeExecBody(&a, context, receiver, string, false); | 425 RegExpPrototypeExecBody(&a, context, receiver, string, false); |
| 578 a.Return(result); | 426 a.Return(result); |
| 579 } | 427 } |
| 580 } | 428 } |
| 581 | 429 |
| 430 namespace { | |
| 431 | |
| 432 Node* FlagsGetter(CodeStubAssembler* a, Node* const receiver, | |
| 433 Node* const context, bool is_fastpath) { | |
| 434 Isolate* isolate = a->isolate(); | |
| 435 | |
| 436 Node* const int_zero = a->IntPtrConstant(0); | |
| 437 Node* const int_one = a->IntPtrConstant(1); | |
| 438 CVariable var_length(a, MachineType::PointerRepresentation()); | |
| 439 CVariable var_flags(a, MachineType::PointerRepresentation()); | |
| 440 | |
| 441 // First, count the number of characters we will need and check which flags | |
| 442 // are set. | |
| 443 | |
| 444 var_length.Bind(int_zero); | |
| 445 | |
| 446 if (is_fastpath) { | |
| 447 // Refer to JSRegExp's flag property on the fast-path. | |
| 448 Node* const flags_smi = | |
| 449 a->LoadObjectField(receiver, JSRegExp::kFlagsOffset); | |
| 450 Node* const flags_intptr = a->SmiUntag(flags_smi); | |
| 451 var_flags.Bind(flags_intptr); | |
| 452 | |
| 453 #define CASE_FOR_FLAG(FLAG) \ | |
| 454 do { \ | |
| 455 CLabel next(a); \ | |
| 456 a->GotoUnless(a->IsSetWord(flags_intptr, FLAG), &next); \ | |
| 457 var_length.Bind(a->IntPtrAdd(var_length.value(), int_one)); \ | |
| 458 a->Goto(&next); \ | |
| 459 a->Bind(&next); \ | |
| 460 } while (false) | |
| 461 | |
| 462 CASE_FOR_FLAG(JSRegExp::kGlobal); | |
| 463 CASE_FOR_FLAG(JSRegExp::kIgnoreCase); | |
| 464 CASE_FOR_FLAG(JSRegExp::kMultiline); | |
| 465 CASE_FOR_FLAG(JSRegExp::kUnicode); | |
| 466 CASE_FOR_FLAG(JSRegExp::kSticky); | |
| 467 #undef CASE_FOR_FLAG | |
| 468 } else { | |
| 469 DCHECK(!is_fastpath); | |
| 470 | |
| 471 // Fall back to GetProperty stub on the slow-path. | |
| 472 var_flags.Bind(int_zero); | |
| 473 | |
| 474 Callable getproperty_callable = CodeFactory::GetProperty(a->isolate()); | |
| 475 | |
| 476 #define CASE_FOR_FLAG(NAME, FLAG) \ | |
| 477 do { \ | |
| 478 CLabel next(a); \ | |
| 479 Node* const name = \ | |
| 480 a->HeapConstant(isolate->factory()->InternalizeUtf8String(NAME)); \ | |
| 481 Node* const flag = \ | |
| 482 a->CallStub(getproperty_callable, context, receiver, name); \ | |
| 483 CLabel if_isflagset(a); \ | |
| 484 a->BranchIfToBooleanIsTrue(flag, &if_isflagset, &next); \ | |
| 485 a->Bind(&if_isflagset); \ | |
| 486 var_length.Bind(a->IntPtrAdd(var_length.value(), int_one)); \ | |
| 487 var_flags.Bind(a->WordOr(var_flags.value(), a->IntPtrConstant(FLAG))); \ | |
| 488 a->Goto(&next); \ | |
| 489 a->Bind(&next); \ | |
| 490 } while (false) | |
| 491 | |
| 492 CASE_FOR_FLAG("global", JSRegExp::kGlobal); | |
| 493 CASE_FOR_FLAG("ignoreCase", JSRegExp::kIgnoreCase); | |
| 494 CASE_FOR_FLAG("multiline", JSRegExp::kMultiline); | |
| 495 CASE_FOR_FLAG("unicode", JSRegExp::kUnicode); | |
| 496 CASE_FOR_FLAG("sticky", JSRegExp::kSticky); | |
| 497 #undef CASE_FOR_FLAG | |
| 498 } | |
| 499 | |
| 500 // Allocate a string of the required length and fill it with the corresponding | |
| 501 // char for each set flag. | |
| 502 | |
| 503 { | |
| 504 Node* const result = | |
| 505 a->AllocateSeqOneByteString(context, var_length.value()); | |
| 506 Node* const flags_intptr = var_flags.value(); | |
| 507 | |
| 508 CVariable var_offset(a, MachineType::PointerRepresentation()); | |
| 509 var_offset.Bind( | |
| 510 a->IntPtrConstant(SeqOneByteString::kHeaderSize - kHeapObjectTag)); | |
| 511 | |
| 512 #define CASE_FOR_FLAG(FLAG, CHAR) \ | |
| 513 do { \ | |
| 514 CLabel next(a); \ | |
| 515 a->GotoUnless(a->IsSetWord(flags_intptr, FLAG), &next); \ | |
| 516 Node* const value = a->IntPtrConstant(CHAR); \ | |
| 517 a->StoreNoWriteBarrier(MachineRepresentation::kWord8, result, \ | |
| 518 var_offset.value(), value); \ | |
| 519 var_offset.Bind(a->IntPtrAdd(var_offset.value(), int_one)); \ | |
| 520 a->Goto(&next); \ | |
| 521 a->Bind(&next); \ | |
| 522 } while (false) | |
| 523 | |
| 524 CASE_FOR_FLAG(JSRegExp::kGlobal, 'g'); | |
| 525 CASE_FOR_FLAG(JSRegExp::kIgnoreCase, 'i'); | |
| 526 CASE_FOR_FLAG(JSRegExp::kMultiline, 'm'); | |
| 527 CASE_FOR_FLAG(JSRegExp::kUnicode, 'u'); | |
| 528 CASE_FOR_FLAG(JSRegExp::kSticky, 'y'); | |
| 529 #undef CASE_FOR_FLAG | |
| 530 | |
| 531 return result; | |
| 532 } | |
| 533 } | |
| 534 | |
| 535 // ES#sec-isregexp IsRegExp ( argument ) | |
| 536 Node* IsRegExp(CodeStubAssembler* a, Node* const context, | |
| 537 Node* const maybe_receiver) { | |
| 538 CLabel out(a), if_isregexp(a); | |
| 539 | |
| 540 CVariable var_result(a, MachineType::PointerRepresentation()); | |
| 541 var_result.Bind(a->IntPtrConstant(0)); | |
| 542 | |
| 543 a->GotoIf(a->TaggedIsSmi(maybe_receiver), &out); | |
| 544 a->GotoUnless(a->IsJSReceiver(maybe_receiver), &out); | |
| 545 | |
| 546 Node* const receiver = maybe_receiver; | |
| 547 | |
| 548 // Check @@match. | |
| 549 { | |
| 550 Callable getproperty_callable = CodeFactory::GetProperty(a->isolate()); | |
| 551 Node* const name = a->HeapConstant(a->isolate()->factory()->match_symbol()); | |
| 552 Node* const value = | |
| 553 a->CallStub(getproperty_callable, context, receiver, name); | |
| 554 | |
| 555 CLabel match_isundefined(a), match_isnotundefined(a); | |
| 556 a->Branch(a->IsUndefined(value), &match_isundefined, &match_isnotundefined); | |
| 557 | |
| 558 a->Bind(&match_isundefined); | |
| 559 a->Branch(a->HasInstanceType(receiver, JS_REGEXP_TYPE), &if_isregexp, &out); | |
| 560 | |
| 561 a->Bind(&match_isnotundefined); | |
| 562 a->BranchIfToBooleanIsTrue(value, &if_isregexp, &out); | |
| 563 } | |
| 564 | |
| 565 a->Bind(&if_isregexp); | |
| 566 var_result.Bind(a->IntPtrConstant(1)); | |
| 567 a->Goto(&out); | |
| 568 | |
| 569 a->Bind(&out); | |
| 570 return var_result.value(); | |
| 571 } | |
| 572 | |
| 573 // ES#sec-regexpinitialize | |
| 574 // Runtime Semantics: RegExpInitialize ( obj, pattern, flags ) | |
| 575 Node* RegExpInitialize(CodeStubAssembler* a, Node* const context, | |
| 576 Node* const regexp, Node* const maybe_pattern, | |
| 577 Node* const maybe_flags) { | |
| 578 // Normalize pattern. | |
| 579 Node* const pattern = a->Select( | |
| 580 a->IsUndefined(maybe_pattern), [&] { return a->EmptyStringConstant(); }, | |
|
Igor Sheludko
2016/12/05 12:43:41
You don't need reference access to the captured va
jgruber
2016/12/05 13:27:14
Done.
| |
| 581 [&] { return a->ToString(context, maybe_pattern); }, | |
|
Igor Sheludko
2016/12/05 12:43:40
Same here.
jgruber
2016/12/05 13:27:14
Done.
| |
| 582 MachineRepresentation::kTagged); | |
| 583 | |
| 584 // Normalize flags. | |
| 585 Node* const flags = a->Select( | |
| 586 a->IsUndefined(maybe_flags), [&] { return a->EmptyStringConstant(); }, | |
|
Igor Sheludko
2016/12/05 12:43:40
Same here.
jgruber
2016/12/05 13:27:14
Done.
| |
| 587 [&] { return a->ToString(context, maybe_flags); }, | |
|
Igor Sheludko
2016/12/05 12:43:40
And here.
jgruber
2016/12/05 13:27:14
Done.
| |
| 588 MachineRepresentation::kTagged); | |
| 589 | |
| 590 // Initialize. | |
| 591 | |
| 592 return a->CallRuntime(Runtime::kRegExpInitializeAndCompile, context, regexp, | |
| 593 pattern, flags); | |
| 594 } | |
| 595 | |
| 596 } // namespace | |
| 597 | |
| 582 void Builtins::Generate_RegExpPrototypeFlagsGetter(CodeAssemblerState* state) { | 598 void Builtins::Generate_RegExpPrototypeFlagsGetter(CodeAssemblerState* state) { |
| 583 CodeStubAssembler a(state); | 599 CodeStubAssembler a(state); |
| 584 | 600 |
| 585 Node* const receiver = a.Parameter(0); | 601 Isolate* isolate = a.isolate(); |
| 602 | |
| 603 Node* const maybe_receiver = a.Parameter(0); | |
| 586 Node* const context = a.Parameter(3); | 604 Node* const context = a.Parameter(3); |
| 587 | 605 |
| 588 Isolate* isolate = a.isolate(); | 606 Node* const map = ThrowIfNotJSReceiver(&a, isolate, context, maybe_receiver, |
| 589 Node* const int_zero = a.IntPtrConstant(0); | |
| 590 Node* const int_one = a.IntPtrConstant(1); | |
| 591 | |
| 592 Node* const map = ThrowIfNotJSReceiver(&a, isolate, context, receiver, | |
| 593 MessageTemplate::kRegExpNonObject, | 607 MessageTemplate::kRegExpNonObject, |
| 594 "RegExp.prototype.flags"); | 608 "RegExp.prototype.flags"); |
| 595 | 609 Node* const receiver = maybe_receiver; |
| 596 CVariable var_length(&a, MachineType::PointerRepresentation()); | 610 |
| 597 CVariable var_flags(&a, MachineType::PointerRepresentation()); | 611 CLabel if_isfastpath(&a), if_isslowpath(&a, CLabel::kDeferred); |
| 598 | 612 a.Branch(IsInitialRegExpMap(&a, context, map), &if_isfastpath, |
| 599 // First, count the number of characters we will need and check which flags | 613 &if_isslowpath); |
| 600 // are set. | 614 |
| 601 | 615 a.Bind(&if_isfastpath); |
| 602 var_length.Bind(int_zero); | 616 a.Return(FlagsGetter(&a, receiver, context, true)); |
| 603 | 617 |
| 604 CLabel if_isunmodifiedjsregexp(&a), | 618 a.Bind(&if_isslowpath); |
| 605 if_isnotunmodifiedjsregexp(&a, CLabel::kDeferred); | 619 a.Return(FlagsGetter(&a, receiver, context, false)); |
| 606 a.Branch(IsInitialRegExpMap(&a, context, map), &if_isunmodifiedjsregexp, | 620 } |
| 607 &if_isnotunmodifiedjsregexp); | 621 |
| 608 | 622 // ES#sec-regexp-pattern-flags |
| 609 CLabel construct_string(&a); | 623 // RegExp ( pattern, flags ) |
| 610 a.Bind(&if_isunmodifiedjsregexp); | 624 void Builtins::Generate_RegExpConstructor(CodeAssemblerState* state) { |
| 611 { | 625 CodeStubAssembler a(state); |
| 612 // Refer to JSRegExp's flag property on the fast-path. | 626 |
| 613 Node* const flags_smi = a.LoadObjectField(receiver, JSRegExp::kFlagsOffset); | 627 Node* const pattern = a.Parameter(1); |
| 614 Node* const flags_intptr = a.SmiUntag(flags_smi); | 628 Node* const flags = a.Parameter(2); |
| 615 var_flags.Bind(flags_intptr); | 629 Node* const new_target = a.Parameter(3); |
| 616 | 630 Node* const context = a.Parameter(5); |
| 617 CLabel label_global(&a), label_ignorecase(&a), label_multiline(&a), | 631 |
| 618 label_unicode(&a), label_sticky(&a); | 632 Isolate* isolate = a.isolate(); |
| 619 | 633 |
| 620 #define CASE_FOR_FLAG(FLAG, LABEL, NEXT_LABEL) \ | 634 CVariable var_flags(&a, MachineRepresentation::kTagged); |
| 621 do { \ | 635 CVariable var_pattern(&a, MachineRepresentation::kTagged); |
| 622 a.Bind(&LABEL); \ | 636 CVariable var_new_target(&a, MachineRepresentation::kTagged); |
| 623 Node* const mask = a.IntPtrConstant(FLAG); \ | 637 |
| 624 a.GotoIf(a.WordEqual(a.WordAnd(flags_intptr, mask), int_zero), \ | 638 var_flags.Bind(flags); |
| 625 &NEXT_LABEL); \ | 639 var_pattern.Bind(pattern); |
| 626 var_length.Bind(a.IntPtrAdd(var_length.value(), int_one)); \ | 640 var_new_target.Bind(new_target); |
| 627 a.Goto(&NEXT_LABEL); \ | 641 |
| 628 } while (false) | 642 Node* const native_context = a.LoadNativeContext(context); |
| 629 | 643 Node* const regexp_function = |
| 630 a.Goto(&label_global); | 644 a.LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX); |
| 631 CASE_FOR_FLAG(JSRegExp::kGlobal, label_global, label_ignorecase); | 645 |
| 632 CASE_FOR_FLAG(JSRegExp::kIgnoreCase, label_ignorecase, label_multiline); | 646 Node* const pattern_is_regexp = IsRegExp(&a, context, pattern); |
| 633 CASE_FOR_FLAG(JSRegExp::kMultiline, label_multiline, label_unicode); | 647 |
| 634 CASE_FOR_FLAG(JSRegExp::kUnicode, label_unicode, label_sticky); | 648 { |
| 635 CASE_FOR_FLAG(JSRegExp::kSticky, label_sticky, construct_string); | 649 CLabel next(&a); |
| 636 #undef CASE_FOR_FLAG | 650 |
| 637 } | 651 a.GotoUnless(a.IsUndefined(new_target), &next); |
| 638 | 652 var_new_target.Bind(regexp_function); |
| 639 a.Bind(&if_isnotunmodifiedjsregexp); | 653 |
| 640 { | 654 a.GotoUnless(pattern_is_regexp, &next); |
| 641 // Fall back to GetProperty stub on the slow-path. | 655 a.GotoUnless(a.IsUndefined(flags), &next); |
| 642 var_flags.Bind(int_zero); | 656 |
| 643 | 657 Callable getproperty_callable = CodeFactory::GetProperty(isolate); |
| 644 Callable getproperty_callable = CodeFactory::GetProperty(a.isolate()); | 658 Node* const name = a.HeapConstant(isolate->factory()->constructor_string()); |
| 645 CLabel label_global(&a), label_ignorecase(&a), label_multiline(&a), | 659 Node* const value = |
| 646 label_unicode(&a), label_sticky(&a); | 660 a.CallStub(getproperty_callable, context, pattern, name); |
| 647 | 661 |
| 648 #define CASE_FOR_FLAG(NAME, FLAG, LABEL, NEXT_LABEL) \ | 662 a.GotoUnless(a.WordEqual(value, regexp_function), &next); |
| 649 do { \ | 663 a.Return(pattern); |
| 650 a.Bind(&LABEL); \ | 664 |
| 651 Node* const name = \ | 665 a.Bind(&next); |
| 652 a.HeapConstant(isolate->factory()->NewStringFromAsciiChecked(NAME)); \ | 666 } |
| 653 Node* const flag = \ | 667 |
| 654 a.CallStub(getproperty_callable, context, receiver, name); \ | 668 { |
| 655 CLabel if_isflagset(&a); \ | 669 CLabel next(&a), if_patternisfastregexp(&a), if_patternisslowregexp(&a); |
| 656 a.BranchIfToBooleanIsTrue(flag, &if_isflagset, &NEXT_LABEL); \ | 670 a.GotoIf(a.TaggedIsSmi(pattern), &next); |
| 657 a.Bind(&if_isflagset); \ | 671 |
| 658 var_length.Bind(a.IntPtrAdd(var_length.value(), int_one)); \ | 672 a.GotoIf(a.HasInstanceType(pattern, JS_REGEXP_TYPE), |
| 659 var_flags.Bind(a.WordOr(var_flags.value(), a.IntPtrConstant(FLAG))); \ | 673 &if_patternisfastregexp); |
| 660 a.Goto(&NEXT_LABEL); \ | 674 |
| 661 } while (false) | 675 a.Branch(pattern_is_regexp, &if_patternisslowregexp, &next); |
| 662 | 676 |
| 663 a.Goto(&label_global); | 677 a.Bind(&if_patternisfastregexp); |
| 664 CASE_FOR_FLAG("global", JSRegExp::kGlobal, label_global, label_ignorecase); | 678 { |
| 665 CASE_FOR_FLAG("ignoreCase", JSRegExp::kIgnoreCase, label_ignorecase, | 679 Node* const source = a.LoadObjectField(pattern, JSRegExp::kSourceOffset); |
| 666 label_multiline); | 680 var_pattern.Bind(source); |
| 667 CASE_FOR_FLAG("multiline", JSRegExp::kMultiline, label_multiline, | 681 |
| 668 label_unicode); | 682 { |
| 669 CASE_FOR_FLAG("unicode", JSRegExp::kUnicode, label_unicode, label_sticky); | 683 CLabel inner_next(&a); |
| 670 CASE_FOR_FLAG("sticky", JSRegExp::kSticky, label_sticky, construct_string); | 684 a.GotoUnless(a.IsUndefined(flags), &inner_next); |
| 671 #undef CASE_FOR_FLAG | 685 |
| 672 } | 686 Node* const value = FlagsGetter(&a, pattern, context, true); |
| 673 | 687 var_flags.Bind(value); |
| 674 // Allocate a string of the required length and fill it with the corresponding | 688 a.Goto(&inner_next); |
| 675 // char for each set flag. | 689 |
| 676 | 690 a.Bind(&inner_next); |
| 677 a.Bind(&construct_string); | 691 } |
| 678 { | 692 |
| 679 Node* const result = | 693 a.Goto(&next); |
| 680 a.AllocateSeqOneByteString(context, var_length.value()); | 694 } |
| 681 Node* const flags_intptr = var_flags.value(); | 695 |
| 682 | 696 a.Bind(&if_patternisslowregexp); |
| 683 CVariable var_offset(&a, MachineType::PointerRepresentation()); | 697 { |
| 684 var_offset.Bind( | 698 Callable getproperty_callable = CodeFactory::GetProperty(isolate); |
| 685 a.IntPtrConstant(SeqOneByteString::kHeaderSize - kHeapObjectTag)); | 699 |
| 686 | 700 { |
| 687 CLabel label_global(&a), label_ignorecase(&a), label_multiline(&a), | 701 Node* const name = a.HeapConstant(isolate->factory()->source_string()); |
| 688 label_unicode(&a), label_sticky(&a), out(&a); | 702 Node* const value = |
| 689 | 703 a.CallStub(getproperty_callable, context, pattern, name); |
| 690 #define CASE_FOR_FLAG(FLAG, CHAR, LABEL, NEXT_LABEL) \ | 704 var_pattern.Bind(value); |
| 691 do { \ | 705 } |
| 692 a.Bind(&LABEL); \ | 706 |
| 693 Node* const mask = a.IntPtrConstant(FLAG); \ | 707 { |
| 694 a.GotoIf(a.WordEqual(a.WordAnd(flags_intptr, mask), int_zero), \ | 708 CLabel inner_next(&a); |
| 695 &NEXT_LABEL); \ | 709 a.GotoUnless(a.IsUndefined(flags), &inner_next); |
| 696 Node* const value = a.IntPtrConstant(CHAR); \ | 710 |
| 697 a.StoreNoWriteBarrier(MachineRepresentation::kWord8, result, \ | 711 Node* const name = a.HeapConstant(isolate->factory()->flags_string()); |
| 698 var_offset.value(), value); \ | 712 Node* const value = |
| 699 var_offset.Bind(a.IntPtrAdd(var_offset.value(), int_one)); \ | 713 a.CallStub(getproperty_callable, context, pattern, name); |
| 700 a.Goto(&NEXT_LABEL); \ | 714 var_flags.Bind(value); |
| 701 } while (false) | 715 a.Goto(&inner_next); |
| 702 | 716 |
| 703 a.Goto(&label_global); | 717 a.Bind(&inner_next); |
| 704 CASE_FOR_FLAG(JSRegExp::kGlobal, 'g', label_global, label_ignorecase); | 718 } |
| 705 CASE_FOR_FLAG(JSRegExp::kIgnoreCase, 'i', label_ignorecase, | 719 |
| 706 label_multiline); | 720 a.Goto(&next); |
| 707 CASE_FOR_FLAG(JSRegExp::kMultiline, 'm', label_multiline, label_unicode); | 721 } |
| 708 CASE_FOR_FLAG(JSRegExp::kUnicode, 'u', label_unicode, label_sticky); | 722 |
| 709 CASE_FOR_FLAG(JSRegExp::kSticky, 'y', label_sticky, out); | 723 a.Bind(&next); |
| 710 #undef CASE_FOR_FLAG | 724 } |
| 711 | 725 |
| 712 a.Bind(&out); | 726 // Allocate. |
| 713 a.Return(result); | 727 |
| 714 } | 728 CVariable var_regexp(&a, MachineRepresentation::kTagged); |
| 729 { | |
| 730 CLabel allocate_jsregexp(&a), allocate_generic(&a, CLabel::kDeferred), | |
| 731 next(&a); | |
| 732 a.Branch(a.WordEqual(var_new_target.value(), regexp_function), | |
| 733 &allocate_jsregexp, &allocate_generic); | |
| 734 | |
| 735 a.Bind(&allocate_jsregexp); | |
| 736 { | |
| 737 Node* const initial_map = a.LoadObjectField( | |
| 738 regexp_function, JSFunction::kPrototypeOrInitialMapOffset); | |
| 739 Node* const regexp = a.AllocateJSObjectFromMap(initial_map); | |
| 740 var_regexp.Bind(regexp); | |
| 741 a.Goto(&next); | |
| 742 } | |
| 743 | |
| 744 a.Bind(&allocate_generic); | |
| 745 { | |
| 746 Callable fastnewobject_callable = CodeFactory::FastNewObject(isolate); | |
| 747 Node* const regexp = a.CallStub(fastnewobject_callable, context, | |
| 748 regexp_function, var_new_target.value()); | |
| 749 var_regexp.Bind(regexp); | |
| 750 a.Goto(&next); | |
| 751 } | |
| 752 | |
| 753 a.Bind(&next); | |
| 754 } | |
| 755 | |
| 756 Node* const result = RegExpInitialize(&a, context, var_regexp.value(), | |
| 757 var_pattern.value(), var_flags.value()); | |
| 758 a.Return(result); | |
| 759 } | |
| 760 | |
| 761 // ES#sec-regexp.prototype.compile | |
| 762 // RegExp.prototype.compile ( pattern, flags ) | |
| 763 void Builtins::Generate_RegExpPrototypeCompile(CodeAssemblerState* state) { | |
| 764 CodeStubAssembler a(state); | |
| 765 | |
| 766 Node* const maybe_receiver = a.Parameter(0); | |
| 767 Node* const maybe_pattern = a.Parameter(1); | |
| 768 Node* const maybe_flags = a.Parameter(2); | |
| 769 Node* const context = a.Parameter(5); | |
| 770 | |
| 771 a.ThrowIfNotInstanceType(context, maybe_receiver, JS_REGEXP_TYPE, | |
| 772 "RegExp.prototype.compile"); | |
| 773 Node* const receiver = maybe_receiver; | |
| 774 | |
| 775 CVariable var_flags(&a, MachineRepresentation::kTagged); | |
| 776 CVariable var_pattern(&a, MachineRepresentation::kTagged); | |
| 777 | |
| 778 var_flags.Bind(maybe_flags); | |
| 779 var_pattern.Bind(maybe_pattern); | |
| 780 | |
| 781 // Handle a JSRegExp pattern. | |
| 782 { | |
| 783 CLabel next(&a); | |
| 784 | |
| 785 a.GotoIf(a.TaggedIsSmi(maybe_pattern), &next); | |
| 786 a.GotoUnless(a.HasInstanceType(maybe_pattern, JS_REGEXP_TYPE), &next); | |
| 787 | |
| 788 Node* const pattern = maybe_pattern; | |
| 789 | |
| 790 // {maybe_flags} must be undefined in this case, otherwise throw. | |
| 791 { | |
| 792 CLabel next(&a); | |
| 793 a.GotoIf(a.IsUndefined(maybe_flags), &next); | |
| 794 | |
| 795 Node* const message_id = a.SmiConstant(MessageTemplate::kRegExpFlags); | |
| 796 a.TailCallRuntime(Runtime::kThrowTypeError, context, message_id); | |
| 797 | |
| 798 a.Bind(&next); | |
| 799 } | |
| 800 | |
| 801 Node* const new_flags = FlagsGetter(&a, pattern, context, true); | |
| 802 Node* const new_pattern = | |
| 803 a.LoadObjectField(pattern, JSRegExp::kSourceOffset); | |
| 804 | |
| 805 var_flags.Bind(new_flags); | |
| 806 var_pattern.Bind(new_pattern); | |
| 807 | |
| 808 a.Goto(&next); | |
| 809 a.Bind(&next); | |
| 810 } | |
| 811 | |
| 812 RegExpInitialize(&a, context, receiver, var_pattern.value(), | |
| 813 var_flags.value()); | |
| 814 | |
| 815 // Return undefined for compatibility with JSC. | |
| 816 // See http://crbug.com/585775 for web compat details. | |
| 817 | |
| 818 a.Return(a.UndefinedConstant()); | |
| 715 } | 819 } |
| 716 | 820 |
| 717 // ES6 21.2.5.10. | 821 // ES6 21.2.5.10. |
| 718 void Builtins::Generate_RegExpPrototypeSourceGetter(CodeAssemblerState* state) { | 822 void Builtins::Generate_RegExpPrototypeSourceGetter(CodeAssemblerState* state) { |
| 719 CodeStubAssembler a(state); | 823 CodeStubAssembler a(state); |
| 720 Node* const receiver = a.Parameter(0); | 824 Node* const receiver = a.Parameter(0); |
| 721 Node* const context = a.Parameter(3); | 825 Node* const context = a.Parameter(3); |
| 722 | 826 |
| 723 // Check whether we have an unmodified regexp instance. | 827 // Check whether we have an unmodified regexp instance. |
| 724 CLabel if_isjsregexp(&a), if_isnotjsregexp(&a, CLabel::kDeferred); | 828 CLabel if_isjsregexp(&a), if_isnotjsregexp(&a, CLabel::kDeferred); |
| (...skipping 1715 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2440 a.Bind(&if_matched); | 2544 a.Bind(&if_matched); |
| 2441 { | 2545 { |
| 2442 Node* result = ConstructNewResultFromMatchInfo(isolate, &a, context, | 2546 Node* result = ConstructNewResultFromMatchInfo(isolate, &a, context, |
| 2443 match_indices, string); | 2547 match_indices, string); |
| 2444 a.Return(result); | 2548 a.Return(result); |
| 2445 } | 2549 } |
| 2446 } | 2550 } |
| 2447 | 2551 |
| 2448 } // namespace internal | 2552 } // namespace internal |
| 2449 } // namespace v8 | 2553 } // namespace v8 |
| OLD | NEW |