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 |