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 |
| 582 void Builtins::Generate_RegExpPrototypeFlagsGetter(CodeAssemblerState* state) { | 430 namespace { |
| 583 CodeStubAssembler a(state); | |
| 584 | 431 |
| 585 Node* const receiver = a.Parameter(0); | 432 Node* FlagsGetter(CodeStubAssembler* a, Node* const receiver, |
| 586 Node* const context = a.Parameter(3); | 433 Node* const context, bool is_fastpath) { |
| 434 Isolate* isolate = a->isolate(); | |
| 587 | 435 |
| 588 Isolate* isolate = a.isolate(); | 436 Node* const int_zero = a->IntPtrConstant(0); |
| 589 Node* const int_zero = a.IntPtrConstant(0); | 437 Node* const int_one = a->IntPtrConstant(1); |
| 590 Node* const int_one = a.IntPtrConstant(1); | 438 CVariable var_length(a, MachineType::PointerRepresentation()); |
| 591 | 439 CVariable var_flags(a, MachineType::PointerRepresentation()); |
| 592 Node* const map = ThrowIfNotJSReceiver(&a, isolate, context, receiver, | |
| 593 MessageTemplate::kRegExpNonObject, | |
| 594 "RegExp.prototype.flags"); | |
| 595 | |
| 596 CVariable var_length(&a, MachineType::PointerRepresentation()); | |
| 597 CVariable var_flags(&a, MachineType::PointerRepresentation()); | |
| 598 | 440 |
| 599 // First, count the number of characters we will need and check which flags | 441 // First, count the number of characters we will need and check which flags |
| 600 // are set. | 442 // are set. |
| 601 | 443 |
| 602 var_length.Bind(int_zero); | 444 var_length.Bind(int_zero); |
| 603 | 445 |
| 604 CLabel if_isunmodifiedjsregexp(&a), | 446 CLabel construct_string(a); |
| 605 if_isnotunmodifiedjsregexp(&a, CLabel::kDeferred); | 447 if (is_fastpath) { |
| 606 a.Branch(IsInitialRegExpMap(&a, context, map), &if_isunmodifiedjsregexp, | |
| 607 &if_isnotunmodifiedjsregexp); | |
| 608 | |
| 609 CLabel construct_string(&a); | |
| 610 a.Bind(&if_isunmodifiedjsregexp); | |
| 611 { | |
| 612 // Refer to JSRegExp's flag property on the fast-path. | 448 // Refer to JSRegExp's flag property on the fast-path. |
| 613 Node* const flags_smi = a.LoadObjectField(receiver, JSRegExp::kFlagsOffset); | 449 Node* const flags_smi = |
| 614 Node* const flags_intptr = a.SmiUntag(flags_smi); | 450 a->LoadObjectField(receiver, JSRegExp::kFlagsOffset); |
| 451 Node* const flags_intptr = a->SmiUntag(flags_smi); | |
| 615 var_flags.Bind(flags_intptr); | 452 var_flags.Bind(flags_intptr); |
| 616 | 453 |
| 617 CLabel label_global(&a), label_ignorecase(&a), label_multiline(&a), | 454 CLabel label_global(a), label_ignorecase(a), label_multiline(a), |
| 618 label_unicode(&a), label_sticky(&a); | 455 label_unicode(a), label_sticky(a); |
| 619 | 456 |
| 620 #define CASE_FOR_FLAG(FLAG, LABEL, NEXT_LABEL) \ | 457 #define CASE_FOR_FLAG(FLAG, LABEL, NEXT_LABEL) \ |
|
Igor Sheludko
2016/12/05 09:35:51
While you are here, I think you can simplify the m
jgruber
2016/12/05 12:28:48
Done.
| |
| 621 do { \ | 458 do { \ |
| 622 a.Bind(&LABEL); \ | 459 a->Bind(&LABEL); \ |
| 623 Node* const mask = a.IntPtrConstant(FLAG); \ | 460 Node* const mask = a->IntPtrConstant(FLAG); \ |
| 624 a.GotoIf(a.WordEqual(a.WordAnd(flags_intptr, mask), int_zero), \ | 461 a->GotoIf(a->WordEqual(a->WordAnd(flags_intptr, mask), int_zero), \ |
|
Igor Sheludko
2016/12/05 09:35:51
You need something like IsWordSet(flags_intptr, FL
jgruber
2016/12/05 12:28:48
Done.
| |
| 625 &NEXT_LABEL); \ | 462 &NEXT_LABEL); \ |
| 626 var_length.Bind(a.IntPtrAdd(var_length.value(), int_one)); \ | 463 var_length.Bind(a->IntPtrAdd(var_length.value(), int_one)); \ |
| 627 a.Goto(&NEXT_LABEL); \ | 464 a->Goto(&NEXT_LABEL); \ |
| 628 } while (false) | 465 } while (false) |
| 629 | 466 |
| 630 a.Goto(&label_global); | 467 a->Goto(&label_global); |
| 631 CASE_FOR_FLAG(JSRegExp::kGlobal, label_global, label_ignorecase); | 468 CASE_FOR_FLAG(JSRegExp::kGlobal, label_global, label_ignorecase); |
| 632 CASE_FOR_FLAG(JSRegExp::kIgnoreCase, label_ignorecase, label_multiline); | 469 CASE_FOR_FLAG(JSRegExp::kIgnoreCase, label_ignorecase, label_multiline); |
| 633 CASE_FOR_FLAG(JSRegExp::kMultiline, label_multiline, label_unicode); | 470 CASE_FOR_FLAG(JSRegExp::kMultiline, label_multiline, label_unicode); |
| 634 CASE_FOR_FLAG(JSRegExp::kUnicode, label_unicode, label_sticky); | 471 CASE_FOR_FLAG(JSRegExp::kUnicode, label_unicode, label_sticky); |
| 635 CASE_FOR_FLAG(JSRegExp::kSticky, label_sticky, construct_string); | 472 CASE_FOR_FLAG(JSRegExp::kSticky, label_sticky, construct_string); |
| 636 #undef CASE_FOR_FLAG | 473 #undef CASE_FOR_FLAG |
| 637 } | 474 } else { |
| 475 DCHECK(!is_fastpath); | |
| 638 | 476 |
| 639 a.Bind(&if_isnotunmodifiedjsregexp); | |
| 640 { | |
| 641 // Fall back to GetProperty stub on the slow-path. | 477 // Fall back to GetProperty stub on the slow-path. |
| 642 var_flags.Bind(int_zero); | 478 var_flags.Bind(int_zero); |
| 643 | 479 |
| 644 Callable getproperty_callable = CodeFactory::GetProperty(a.isolate()); | 480 Callable getproperty_callable = CodeFactory::GetProperty(a->isolate()); |
| 645 CLabel label_global(&a), label_ignorecase(&a), label_multiline(&a), | 481 CLabel label_global(a), label_ignorecase(a), label_multiline(a), |
| 646 label_unicode(&a), label_sticky(&a); | 482 label_unicode(a), label_sticky(a); |
| 647 | 483 |
| 648 #define CASE_FOR_FLAG(NAME, FLAG, LABEL, NEXT_LABEL) \ | 484 #define CASE_FOR_FLAG(NAME, FLAG, LABEL, NEXT_LABEL) \ |
|
Igor Sheludko
2016/12/05 09:35:51
Same here.
jgruber
2016/12/05 12:28:48
Done.
| |
| 649 do { \ | 485 do { \ |
| 650 a.Bind(&LABEL); \ | 486 a->Bind(&LABEL); \ |
| 651 Node* const name = \ | 487 Node* const name = \ |
| 652 a.HeapConstant(isolate->factory()->NewStringFromAsciiChecked(NAME)); \ | 488 a->HeapConstant(isolate->factory()->NewStringFromAsciiChecked(NAME)); \ |
|
Igor Sheludko
2016/12/05 09:35:51
You can speed up the slow lookups a bit if you cre
jgruber
2016/12/05 12:28:48
Done.
| |
| 653 Node* const flag = \ | 489 Node* const flag = \ |
| 654 a.CallStub(getproperty_callable, context, receiver, name); \ | 490 a->CallStub(getproperty_callable, context, receiver, name); \ |
| 655 CLabel if_isflagset(&a); \ | 491 CLabel if_isflagset(a); \ |
| 656 a.BranchIfToBooleanIsTrue(flag, &if_isflagset, &NEXT_LABEL); \ | 492 a->BranchIfToBooleanIsTrue(flag, &if_isflagset, &NEXT_LABEL); \ |
| 657 a.Bind(&if_isflagset); \ | 493 a->Bind(&if_isflagset); \ |
| 658 var_length.Bind(a.IntPtrAdd(var_length.value(), int_one)); \ | 494 var_length.Bind(a->IntPtrAdd(var_length.value(), int_one)); \ |
| 659 var_flags.Bind(a.WordOr(var_flags.value(), a.IntPtrConstant(FLAG))); \ | 495 var_flags.Bind(a->WordOr(var_flags.value(), a->IntPtrConstant(FLAG))); \ |
| 660 a.Goto(&NEXT_LABEL); \ | 496 a->Goto(&NEXT_LABEL); \ |
| 661 } while (false) | 497 } while (false) |
| 662 | 498 |
| 663 a.Goto(&label_global); | 499 a->Goto(&label_global); |
| 664 CASE_FOR_FLAG("global", JSRegExp::kGlobal, label_global, label_ignorecase); | 500 CASE_FOR_FLAG("global", JSRegExp::kGlobal, label_global, label_ignorecase); |
| 665 CASE_FOR_FLAG("ignoreCase", JSRegExp::kIgnoreCase, label_ignorecase, | 501 CASE_FOR_FLAG("ignoreCase", JSRegExp::kIgnoreCase, label_ignorecase, |
| 666 label_multiline); | 502 label_multiline); |
| 667 CASE_FOR_FLAG("multiline", JSRegExp::kMultiline, label_multiline, | 503 CASE_FOR_FLAG("multiline", JSRegExp::kMultiline, label_multiline, |
| 668 label_unicode); | 504 label_unicode); |
| 669 CASE_FOR_FLAG("unicode", JSRegExp::kUnicode, label_unicode, label_sticky); | 505 CASE_FOR_FLAG("unicode", JSRegExp::kUnicode, label_unicode, label_sticky); |
| 670 CASE_FOR_FLAG("sticky", JSRegExp::kSticky, label_sticky, construct_string); | 506 CASE_FOR_FLAG("sticky", JSRegExp::kSticky, label_sticky, construct_string); |
| 671 #undef CASE_FOR_FLAG | 507 #undef CASE_FOR_FLAG |
| 672 } | 508 } |
| 673 | 509 |
| 674 // Allocate a string of the required length and fill it with the corresponding | 510 // Allocate a string of the required length and fill it with the corresponding |
| 675 // char for each set flag. | 511 // char for each set flag. |
| 676 | 512 |
| 677 a.Bind(&construct_string); | 513 a->Bind(&construct_string); |
| 678 { | 514 { |
| 679 Node* const result = | 515 Node* const result = |
| 680 a.AllocateSeqOneByteString(context, var_length.value()); | 516 a->AllocateSeqOneByteString(context, var_length.value()); |
| 681 Node* const flags_intptr = var_flags.value(); | 517 Node* const flags_intptr = var_flags.value(); |
| 682 | 518 |
| 683 CVariable var_offset(&a, MachineType::PointerRepresentation()); | 519 CVariable var_offset(a, MachineType::PointerRepresentation()); |
| 684 var_offset.Bind( | 520 var_offset.Bind( |
| 685 a.IntPtrConstant(SeqOneByteString::kHeaderSize - kHeapObjectTag)); | 521 a->IntPtrConstant(SeqOneByteString::kHeaderSize - kHeapObjectTag)); |
| 686 | 522 |
| 687 CLabel label_global(&a), label_ignorecase(&a), label_multiline(&a), | 523 CLabel label_global(a), label_ignorecase(a), label_multiline(a), |
| 688 label_unicode(&a), label_sticky(&a), out(&a); | 524 label_unicode(a), label_sticky(a), out(a); |
| 689 | 525 |
| 690 #define CASE_FOR_FLAG(FLAG, CHAR, LABEL, NEXT_LABEL) \ | 526 #define CASE_FOR_FLAG(FLAG, CHAR, LABEL, NEXT_LABEL) \ |
|
Igor Sheludko
2016/12/05 09:35:51
Same here.
jgruber
2016/12/05 12:28:48
Done.
| |
| 691 do { \ | 527 do { \ |
| 692 a.Bind(&LABEL); \ | 528 a->Bind(&LABEL); \ |
| 693 Node* const mask = a.IntPtrConstant(FLAG); \ | 529 Node* const mask = a->IntPtrConstant(FLAG); \ |
| 694 a.GotoIf(a.WordEqual(a.WordAnd(flags_intptr, mask), int_zero), \ | 530 a->GotoIf(a->WordEqual(a->WordAnd(flags_intptr, mask), int_zero), \ |
|
Igor Sheludko
2016/12/05 09:35:51
IsSetWord()
jgruber
2016/12/05 12:28:48
Done.
| |
| 695 &NEXT_LABEL); \ | 531 &NEXT_LABEL); \ |
| 696 Node* const value = a.IntPtrConstant(CHAR); \ | 532 Node* const value = a->IntPtrConstant(CHAR); \ |
| 697 a.StoreNoWriteBarrier(MachineRepresentation::kWord8, result, \ | 533 a->StoreNoWriteBarrier(MachineRepresentation::kWord8, result, \ |
| 698 var_offset.value(), value); \ | 534 var_offset.value(), value); \ |
| 699 var_offset.Bind(a.IntPtrAdd(var_offset.value(), int_one)); \ | 535 var_offset.Bind(a->IntPtrAdd(var_offset.value(), int_one)); \ |
| 700 a.Goto(&NEXT_LABEL); \ | 536 a->Goto(&NEXT_LABEL); \ |
| 701 } while (false) | 537 } while (false) |
| 702 | 538 |
| 703 a.Goto(&label_global); | 539 a->Goto(&label_global); |
| 704 CASE_FOR_FLAG(JSRegExp::kGlobal, 'g', label_global, label_ignorecase); | 540 CASE_FOR_FLAG(JSRegExp::kGlobal, 'g', label_global, label_ignorecase); |
| 705 CASE_FOR_FLAG(JSRegExp::kIgnoreCase, 'i', label_ignorecase, | 541 CASE_FOR_FLAG(JSRegExp::kIgnoreCase, 'i', label_ignorecase, |
| 706 label_multiline); | 542 label_multiline); |
| 707 CASE_FOR_FLAG(JSRegExp::kMultiline, 'm', label_multiline, label_unicode); | 543 CASE_FOR_FLAG(JSRegExp::kMultiline, 'm', label_multiline, label_unicode); |
| 708 CASE_FOR_FLAG(JSRegExp::kUnicode, 'u', label_unicode, label_sticky); | 544 CASE_FOR_FLAG(JSRegExp::kUnicode, 'u', label_unicode, label_sticky); |
| 709 CASE_FOR_FLAG(JSRegExp::kSticky, 'y', label_sticky, out); | 545 CASE_FOR_FLAG(JSRegExp::kSticky, 'y', label_sticky, out); |
| 710 #undef CASE_FOR_FLAG | 546 #undef CASE_FOR_FLAG |
| 711 | 547 |
| 712 a.Bind(&out); | 548 a->Bind(&out); |
| 713 a.Return(result); | 549 return result; |
| 714 } | 550 } |
| 551 } | |
| 552 | |
| 553 // ES#sec-isregexp IsRegExp ( argument ) | |
| 554 Node* IsRegExp(CodeStubAssembler* a, Node* const context, | |
| 555 Node* const maybe_receiver) { | |
| 556 CLabel out(a), if_isregexp(a); | |
| 557 | |
| 558 CVariable var_result(a, MachineType::PointerRepresentation()); | |
| 559 var_result.Bind(a->IntPtrConstant(0)); | |
| 560 | |
| 561 a->GotoIf(a->TaggedIsSmi(maybe_receiver), &out); | |
| 562 a->GotoUnless(a->IsJSReceiver(maybe_receiver), &out); | |
| 563 | |
| 564 Node* const receiver = maybe_receiver; | |
| 565 | |
| 566 // Check @@match. | |
| 567 { | |
| 568 Callable getproperty_callable = CodeFactory::GetProperty(a->isolate()); | |
| 569 Node* const name = a->HeapConstant(a->isolate()->factory()->match_symbol()); | |
| 570 Node* const value = | |
| 571 a->CallStub(getproperty_callable, context, receiver, name); | |
| 572 | |
| 573 CLabel match_isundefined(a), match_isnotundefined(a); | |
| 574 a->Branch(a->IsUndefined(value), &match_isundefined, &match_isnotundefined); | |
| 575 | |
| 576 a->Bind(&match_isundefined); | |
| 577 a->Branch(a->HasInstanceType(receiver, JS_REGEXP_TYPE), &if_isregexp, &out); | |
| 578 | |
| 579 a->Bind(&match_isnotundefined); | |
| 580 a->BranchIfToBooleanIsTrue(value, &if_isregexp, &out); | |
| 581 } | |
| 582 | |
| 583 a->Bind(&if_isregexp); | |
| 584 var_result.Bind(a->IntPtrConstant(1)); | |
| 585 a->Goto(&out); | |
| 586 | |
| 587 a->Bind(&out); | |
| 588 return var_result.value(); | |
| 589 } | |
| 590 | |
| 591 // ES#sec-regexpinitialize | |
| 592 // Runtime Semantics: RegExpInitialize ( obj, pattern, flags ) | |
| 593 Node* RegExpInitialize(CodeStubAssembler* a, Node* const context, | |
| 594 Node* const regexp, Node* const maybe_pattern, | |
| 595 Node* const maybe_flags) { | |
| 596 CVariable var_flags(a, MachineRepresentation::kTagged); | |
| 597 CVariable var_pattern(a, MachineRepresentation::kTagged); | |
| 598 | |
| 599 // Normalize pattern. | |
| 600 { | |
| 601 CLabel next(a), if_isundefined(a), if_notundefined(a); | |
| 602 a->Branch(a->IsUndefined(maybe_pattern), &if_isundefined, &if_notundefined); | |
|
Igor Sheludko
2016/12/05 09:35:51
Add a comment
// TODO(ishell): Use Select() once
Igor Sheludko
2016/12/05 11:35:14
The new Select() is landed and you can use it!
jgruber
2016/12/05 12:28:48
Nice, I liike!
| |
| 603 | |
| 604 a->Bind(&if_isundefined); | |
| 605 { | |
| 606 var_pattern.Bind(a->EmptyStringConstant()); | |
| 607 a->Goto(&next); | |
| 608 } | |
| 609 | |
| 610 a->Bind(&if_notundefined); | |
| 611 { | |
| 612 Node* const pattern = a->ToString(context, maybe_pattern); | |
| 613 var_pattern.Bind(pattern); | |
| 614 a->Goto(&next); | |
| 615 } | |
| 616 | |
| 617 a->Bind(&next); | |
| 618 } | |
| 619 | |
| 620 // Normalize flags. | |
| 621 { | |
| 622 CLabel next(a), if_isundefined(a), if_notundefined(a); | |
| 623 a->Branch(a->IsUndefined(maybe_flags), &if_isundefined, &if_notundefined); | |
|
Igor Sheludko
2016/12/05 09:35:51
Same here.
jgruber
2016/12/05 12:28:48
Done.
| |
| 624 | |
| 625 a->Bind(&if_isundefined); | |
| 626 { | |
| 627 var_flags.Bind(a->EmptyStringConstant()); | |
| 628 a->Goto(&next); | |
| 629 } | |
| 630 | |
| 631 a->Bind(&if_notundefined); | |
| 632 { | |
| 633 Node* const flags = a->ToString(context, maybe_flags); | |
| 634 var_flags.Bind(flags); | |
| 635 a->Goto(&next); | |
| 636 } | |
| 637 | |
| 638 a->Bind(&next); | |
| 639 } | |
| 640 | |
| 641 // Initialize. | |
| 642 | |
| 643 { | |
| 644 Node* const result = | |
| 645 a->CallRuntime(Runtime::kRegExpInitializeAndCompile, context, regexp, | |
| 646 var_pattern.value(), var_flags.value()); | |
| 647 return result; | |
| 648 } | |
| 649 } | |
| 650 | |
| 651 } // namespace | |
| 652 | |
| 653 void Builtins::Generate_RegExpPrototypeFlagsGetter(CodeAssemblerState* state) { | |
| 654 CodeStubAssembler a(state); | |
| 655 | |
| 656 Isolate* isolate = a.isolate(); | |
| 657 | |
| 658 Node* const maybe_receiver = a.Parameter(0); | |
| 659 Node* const context = a.Parameter(3); | |
| 660 | |
| 661 Node* const map = ThrowIfNotJSReceiver(&a, isolate, context, maybe_receiver, | |
| 662 MessageTemplate::kRegExpNonObject, | |
| 663 "RegExp.prototype.flags"); | |
| 664 Node* const receiver = maybe_receiver; | |
| 665 | |
| 666 CLabel if_isfastpath(&a), if_isslowpath(&a, CLabel::kDeferred); | |
| 667 a.Branch(IsInitialRegExpMap(&a, context, map), &if_isfastpath, | |
| 668 &if_isslowpath); | |
| 669 | |
| 670 a.Bind(&if_isfastpath); | |
| 671 a.Return(FlagsGetter(&a, receiver, context, true)); | |
| 672 | |
| 673 a.Bind(&if_isslowpath); | |
| 674 a.Return(FlagsGetter(&a, receiver, context, false)); | |
| 675 } | |
| 676 | |
| 677 // ES#sec-regexp-pattern-flags | |
| 678 // RegExp ( pattern, flags ) | |
| 679 void Builtins::Generate_RegExpConstructor(CodeAssemblerState* state) { | |
| 680 CodeStubAssembler a(state); | |
| 681 | |
| 682 Node* const pattern = a.Parameter(1); | |
| 683 Node* const flags = a.Parameter(2); | |
| 684 Node* const new_target = a.Parameter(3); | |
| 685 Node* const context = a.Parameter(5); | |
| 686 | |
| 687 Isolate* isolate = a.isolate(); | |
| 688 | |
| 689 CVariable var_flags(&a, MachineRepresentation::kTagged); | |
| 690 CVariable var_pattern(&a, MachineRepresentation::kTagged); | |
| 691 CVariable var_new_target(&a, MachineRepresentation::kTagged); | |
| 692 | |
| 693 var_flags.Bind(flags); | |
| 694 var_pattern.Bind(pattern); | |
| 695 var_new_target.Bind(new_target); | |
| 696 | |
| 697 Node* const native_context = a.LoadNativeContext(context); | |
| 698 Node* const regexp_function = | |
| 699 a.LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX); | |
| 700 | |
| 701 Node* const pattern_is_regexp = IsRegExp(&a, context, pattern); | |
| 702 | |
| 703 { | |
| 704 CLabel next(&a); | |
| 705 | |
| 706 a.GotoUnless(a.IsUndefined(new_target), &next); | |
| 707 var_new_target.Bind(regexp_function); | |
| 708 | |
| 709 a.GotoUnless(pattern_is_regexp, &next); | |
| 710 a.GotoUnless(a.IsUndefined(flags), &next); | |
| 711 | |
| 712 Callable getproperty_callable = CodeFactory::GetProperty(isolate); | |
|
Igor Sheludko
2016/12/05 11:28:14
I guess you are going to optimize it further in a
jgruber
2016/12/05 12:28:48
Only if it seems necessary. The RegExp constructor
| |
| 713 Node* const name = a.HeapConstant(isolate->factory()->constructor_string()); | |
| 714 Node* const value = | |
| 715 a.CallStub(getproperty_callable, context, pattern, name); | |
| 716 | |
| 717 a.GotoUnless(a.WordEqual(value, regexp_function), &next); | |
| 718 a.Return(pattern); | |
| 719 | |
| 720 a.Bind(&next); | |
| 721 } | |
| 722 | |
| 723 { | |
| 724 CLabel next(&a), if_patternisfastregexp(&a), if_patternisslowregexp(&a); | |
| 725 a.GotoIf(a.TaggedIsSmi(pattern), &next); | |
| 726 | |
| 727 a.GotoIf(a.HasInstanceType(pattern, JS_REGEXP_TYPE), | |
| 728 &if_patternisfastregexp); | |
|
Igor Sheludko
2016/12/05 11:28:14
The instance type check only tells you that the pa
jgruber
2016/12/05 12:28:48
Yup, I'm aware of that, and this implements semant
Igor Sheludko
2016/12/05 12:43:40
Ah. Sorry. I missed the fact that they are already
| |
| 729 | |
| 730 a.Branch(pattern_is_regexp, &if_patternisslowregexp, &next); | |
| 731 | |
| 732 a.Bind(&if_patternisfastregexp); | |
| 733 { | |
| 734 Node* const source = a.LoadObjectField(pattern, JSRegExp::kSourceOffset); | |
|
Igor Sheludko
2016/12/05 11:28:14
Here.
jgruber
2016/12/05 12:28:48
See above.
| |
| 735 var_pattern.Bind(source); | |
| 736 | |
| 737 { | |
| 738 CLabel inner_next(&a); | |
| 739 a.GotoUnless(a.IsUndefined(flags), &inner_next); | |
| 740 | |
| 741 Node* const value = FlagsGetter(&a, pattern, context, true); | |
|
Igor Sheludko
2016/12/05 11:28:14
And here.
jgruber
2016/12/05 12:28:47
See above.
| |
| 742 var_flags.Bind(value); | |
| 743 a.Goto(&inner_next); | |
| 744 | |
| 745 a.Bind(&inner_next); | |
| 746 } | |
| 747 | |
| 748 a.Goto(&next); | |
| 749 } | |
| 750 | |
| 751 a.Bind(&if_patternisslowregexp); | |
| 752 { | |
| 753 Callable getproperty_callable = CodeFactory::GetProperty(isolate); | |
| 754 | |
| 755 { | |
| 756 Node* const name = a.HeapConstant(isolate->factory()->source_string()); | |
| 757 Node* const value = | |
| 758 a.CallStub(getproperty_callable, context, pattern, name); | |
| 759 var_pattern.Bind(value); | |
| 760 } | |
| 761 | |
| 762 { | |
| 763 CLabel inner_next(&a); | |
| 764 a.GotoUnless(a.IsUndefined(flags), &inner_next); | |
| 765 | |
| 766 Node* const name = a.HeapConstant(isolate->factory()->flags_string()); | |
| 767 Node* const value = | |
| 768 a.CallStub(getproperty_callable, context, pattern, name); | |
| 769 var_flags.Bind(value); | |
| 770 a.Goto(&inner_next); | |
| 771 | |
| 772 a.Bind(&inner_next); | |
| 773 } | |
| 774 | |
| 775 a.Goto(&next); | |
| 776 } | |
| 777 | |
| 778 a.Bind(&next); | |
| 779 } | |
| 780 | |
| 781 // Allocate. | |
| 782 | |
| 783 CVariable var_regexp(&a, MachineRepresentation::kTagged); | |
| 784 { | |
| 785 CLabel allocate_jsregexp(&a), allocate_generic(&a, CLabel::kDeferred), | |
| 786 next(&a); | |
| 787 a.Branch(a.WordEqual(var_new_target.value(), regexp_function), | |
| 788 &allocate_jsregexp, &allocate_generic); | |
| 789 | |
| 790 a.Bind(&allocate_jsregexp); | |
| 791 { | |
| 792 Node* const initial_map = a.LoadObjectField( | |
| 793 regexp_function, JSFunction::kPrototypeOrInitialMapOffset); | |
| 794 Node* const regexp = a.AllocateJSObjectFromMap(initial_map); | |
| 795 var_regexp.Bind(regexp); | |
| 796 a.Goto(&next); | |
| 797 } | |
| 798 | |
| 799 a.Bind(&allocate_generic); | |
| 800 { | |
| 801 Callable fastnewobject_callable = CodeFactory::FastNewObject(isolate); | |
| 802 Node* const regexp = a.CallStub(fastnewobject_callable, context, | |
| 803 regexp_function, var_new_target.value()); | |
| 804 var_regexp.Bind(regexp); | |
| 805 a.Goto(&next); | |
| 806 } | |
| 807 | |
| 808 a.Bind(&next); | |
| 809 } | |
| 810 | |
| 811 Node* const result = RegExpInitialize(&a, context, var_regexp.value(), | |
| 812 var_pattern.value(), var_flags.value()); | |
| 813 a.Return(result); | |
| 814 } | |
| 815 | |
| 816 // ES#sec-regexp.prototype.compile | |
| 817 // RegExp.prototype.compile ( pattern, flags ) | |
| 818 void Builtins::Generate_RegExpPrototypeCompile(CodeAssemblerState* state) { | |
| 819 CodeStubAssembler a(state); | |
| 820 | |
| 821 Node* const maybe_receiver = a.Parameter(0); | |
| 822 Node* const maybe_pattern = a.Parameter(1); | |
| 823 Node* const maybe_flags = a.Parameter(2); | |
| 824 Node* const context = a.Parameter(5); | |
| 825 | |
| 826 a.ThrowIfNotInstanceType(context, maybe_receiver, JS_REGEXP_TYPE, | |
| 827 "RegExp.prototype.compile"); | |
| 828 Node* const receiver = maybe_receiver; | |
| 829 | |
| 830 CVariable var_flags(&a, MachineRepresentation::kTagged); | |
| 831 CVariable var_pattern(&a, MachineRepresentation::kTagged); | |
| 832 | |
| 833 var_flags.Bind(maybe_flags); | |
| 834 var_pattern.Bind(maybe_pattern); | |
| 835 | |
| 836 // Handle a JSRegExp pattern. | |
| 837 { | |
| 838 CLabel next(&a); | |
| 839 | |
| 840 a.GotoIf(a.TaggedIsSmi(maybe_pattern), &next); | |
| 841 a.GotoUnless(a.HasInstanceType(maybe_pattern, JS_REGEXP_TYPE), &next); | |
| 842 | |
| 843 Node* const pattern = maybe_pattern; | |
| 844 | |
| 845 // {maybe_flags} must be undefined in this case, otherwise throw. | |
| 846 { | |
| 847 CLabel next(&a); | |
| 848 a.GotoIf(a.IsUndefined(maybe_flags), &next); | |
| 849 | |
| 850 Node* const message_id = a.SmiConstant(MessageTemplate::kRegExpFlags); | |
| 851 a.TailCallRuntime(Runtime::kThrowTypeError, context, message_id); | |
| 852 | |
| 853 a.Bind(&next); | |
| 854 } | |
| 855 | |
| 856 Node* const new_flags = FlagsGetter(&a, pattern, context, true); | |
|
Igor Sheludko
2016/12/05 11:28:14
Same here.
jgruber
2016/12/05 12:28:48
See above.
| |
| 857 Node* const new_pattern = | |
| 858 a.LoadObjectField(pattern, JSRegExp::kSourceOffset); | |
| 859 | |
| 860 var_flags.Bind(new_flags); | |
| 861 var_pattern.Bind(new_pattern); | |
| 862 | |
| 863 a.Goto(&next); | |
| 864 a.Bind(&next); | |
| 865 } | |
| 866 | |
| 867 RegExpInitialize(&a, context, receiver, var_pattern.value(), | |
| 868 var_flags.value()); | |
| 869 | |
| 870 // Return undefined for compatibility with JSC. | |
| 871 // See http://crbug.com/585775 for web compat details. | |
| 872 | |
| 873 a.Return(a.UndefinedConstant()); | |
| 715 } | 874 } |
| 716 | 875 |
| 717 // ES6 21.2.5.10. | 876 // ES6 21.2.5.10. |
| 718 void Builtins::Generate_RegExpPrototypeSourceGetter(CodeAssemblerState* state) { | 877 void Builtins::Generate_RegExpPrototypeSourceGetter(CodeAssemblerState* state) { |
| 719 CodeStubAssembler a(state); | 878 CodeStubAssembler a(state); |
| 720 Node* const receiver = a.Parameter(0); | 879 Node* const receiver = a.Parameter(0); |
| 721 Node* const context = a.Parameter(3); | 880 Node* const context = a.Parameter(3); |
| 722 | 881 |
| 723 // Check whether we have an unmodified regexp instance. | 882 // Check whether we have an unmodified regexp instance. |
| 724 CLabel if_isjsregexp(&a), if_isnotjsregexp(&a, CLabel::kDeferred); | 883 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); | 2599 a.Bind(&if_matched); |
| 2441 { | 2600 { |
| 2442 Node* result = ConstructNewResultFromMatchInfo(isolate, &a, context, | 2601 Node* result = ConstructNewResultFromMatchInfo(isolate, &a, context, |
| 2443 match_indices, string); | 2602 match_indices, string); |
| 2444 a.Return(result); | 2603 a.Return(result); |
| 2445 } | 2604 } |
| 2446 } | 2605 } |
| 2447 | 2606 |
| 2448 } // namespace internal | 2607 } // namespace internal |
| 2449 } // namespace v8 | 2608 } // namespace v8 |
| OLD | NEW |