| 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-regexp.h" | 5 #include "src/builtins/builtins-regexp.h" |
| 6 | 6 |
| 7 #include "src/builtins/builtins-constructor.h" | 7 #include "src/builtins/builtins-constructor.h" |
| 8 #include "src/builtins/builtins-utils.h" | 8 #include "src/builtins/builtins-utils.h" |
| 9 #include "src/builtins/builtins.h" | 9 #include "src/builtins/builtins.h" |
| 10 #include "src/code-factory.h" | 10 #include "src/code-factory.h" |
| (...skipping 463 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 474 | 474 |
| 475 // ES#sec-regexp.prototype.exec | 475 // ES#sec-regexp.prototype.exec |
| 476 // RegExp.prototype.exec ( string ) | 476 // RegExp.prototype.exec ( string ) |
| 477 // Implements the core of RegExp.prototype.exec but without actually | 477 // Implements the core of RegExp.prototype.exec but without actually |
| 478 // constructing the JSRegExpResult. Returns either null (if the RegExp did not | 478 // constructing the JSRegExpResult. Returns either null (if the RegExp did not |
| 479 // match) or a fixed array containing match indices as returned by | 479 // match) or a fixed array containing match indices as returned by |
| 480 // RegExpExecStub. | 480 // RegExpExecStub. |
| 481 Node* RegExpBuiltinsAssembler::RegExpPrototypeExecBodyWithoutResult( | 481 Node* RegExpBuiltinsAssembler::RegExpPrototypeExecBodyWithoutResult( |
| 482 Node* const context, Node* const regexp, Node* const string, | 482 Node* const context, Node* const regexp, Node* const string, |
| 483 Label* if_didnotmatch, const bool is_fastpath) { | 483 Label* if_didnotmatch, const bool is_fastpath) { |
| 484 Isolate* const isolate = this->isolate(); | |
| 485 | |
| 486 Node* const null = NullConstant(); | 484 Node* const null = NullConstant(); |
| 487 Node* const int_zero = IntPtrConstant(0); | 485 Node* const int_zero = IntPtrConstant(0); |
| 488 Node* const smi_zero = SmiConstant(Smi::kZero); | 486 Node* const smi_zero = SmiConstant(Smi::kZero); |
| 489 | 487 |
| 490 if (!is_fastpath) { | 488 if (!is_fastpath) { |
| 491 ThrowIfNotInstanceType(context, regexp, JS_REGEXP_TYPE, | 489 ThrowIfNotInstanceType(context, regexp, JS_REGEXP_TYPE, |
| 492 "RegExp.prototype.exec"); | 490 "RegExp.prototype.exec"); |
| 493 } | 491 } |
| 494 | 492 |
| 495 CSA_ASSERT(this, IsStringInstanceType(LoadInstanceType(string))); | 493 CSA_ASSERT(this, IsStringInstanceType(LoadInstanceType(string))); |
| 496 CSA_ASSERT(this, HasInstanceType(regexp, JS_REGEXP_TYPE)); | 494 CSA_ASSERT(this, HasInstanceType(regexp, JS_REGEXP_TYPE)); |
| 497 | 495 |
| 498 Variable var_result(this, MachineRepresentation::kTagged); | 496 Variable var_result(this, MachineRepresentation::kTagged); |
| 499 Label out(this); | 497 Label out(this); |
| 500 | 498 |
| 501 // Load lastIndex. | 499 // Load lastIndex. |
| 502 Variable var_lastindex(this, MachineRepresentation::kTagged); | 500 Variable var_lastindex(this, MachineRepresentation::kTagged); |
| 503 { | 501 { |
| 504 Node* const regexp_lastindex = LoadLastIndex(context, regexp, is_fastpath); | 502 Node* const regexp_lastindex = LoadLastIndex(context, regexp, is_fastpath); |
| 505 var_lastindex.Bind(regexp_lastindex); | 503 var_lastindex.Bind(regexp_lastindex); |
| 506 | 504 |
| 507 // Omit ToLength if lastindex is a non-negative smi. | 505 // Omit ToLength if lastindex is a non-negative smi. |
| 508 Label call_tolength(this, Label::kDeferred), next(this); | 506 Label call_tolength(this, Label::kDeferred), next(this); |
| 509 Branch(TaggedIsPositiveSmi(regexp_lastindex), &next, &call_tolength); | 507 Branch(TaggedIsPositiveSmi(regexp_lastindex), &next, &call_tolength); |
| 510 | 508 |
| 511 Bind(&call_tolength); | 509 Bind(&call_tolength); |
| 512 { | 510 { |
| 513 Callable tolength_callable = CodeFactory::ToLength(isolate); | |
| 514 var_lastindex.Bind( | 511 var_lastindex.Bind( |
| 515 CallStub(tolength_callable, context, regexp_lastindex)); | 512 CallBuiltin(Builtins::kToLength, context, regexp_lastindex)); |
| 516 Goto(&next); | 513 Goto(&next); |
| 517 } | 514 } |
| 518 | 515 |
| 519 Bind(&next); | 516 Bind(&next); |
| 520 } | 517 } |
| 521 | 518 |
| 522 // Check whether the regexp is global or sticky, which determines whether we | 519 // Check whether the regexp is global or sticky, which determines whether we |
| 523 // update last index later on. | 520 // update last index later on. |
| 524 Node* const flags = LoadObjectField(regexp, JSRegExp::kFlagsOffset); | 521 Node* const flags = LoadObjectField(regexp, JSRegExp::kFlagsOffset); |
| 525 Node* const is_global_or_sticky = WordAnd( | 522 Node* const is_global_or_sticky = WordAnd( |
| (...skipping 119 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 645 | 642 |
| 646 Branch(IsJSReceiverInstanceType(value_instance_type), &out, &throw_exception); | 643 Branch(IsJSReceiverInstanceType(value_instance_type), &out, &throw_exception); |
| 647 | 644 |
| 648 // The {value} is not a compatible receiver for this method. | 645 // The {value} is not a compatible receiver for this method. |
| 649 Bind(&throw_exception); | 646 Bind(&throw_exception); |
| 650 { | 647 { |
| 651 Node* const message_id = SmiConstant(Smi::FromInt(msg_template)); | 648 Node* const message_id = SmiConstant(Smi::FromInt(msg_template)); |
| 652 Node* const method_name_str = HeapConstant( | 649 Node* const method_name_str = HeapConstant( |
| 653 isolate()->factory()->NewStringFromAsciiChecked(method_name, TENURED)); | 650 isolate()->factory()->NewStringFromAsciiChecked(method_name, TENURED)); |
| 654 | 651 |
| 655 Callable callable = CodeFactory::ToString(isolate()); | 652 Node* const value_str = |
| 656 Node* const value_str = CallStub(callable, context, maybe_receiver); | 653 CallBuiltin(Builtins::kToString, context, maybe_receiver); |
| 657 | 654 |
| 658 CallRuntime(Runtime::kThrowTypeError, context, message_id, method_name_str, | 655 CallRuntime(Runtime::kThrowTypeError, context, message_id, method_name_str, |
| 659 value_str); | 656 value_str); |
| 660 Unreachable(); | 657 Unreachable(); |
| 661 } | 658 } |
| 662 | 659 |
| 663 Bind(&out); | 660 Bind(&out); |
| 664 return var_value_map.value(); | 661 return var_value_map.value(); |
| 665 } | 662 } |
| 666 | 663 |
| (...skipping 1081 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1748 Variable var_length_; | 1745 Variable var_length_; |
| 1749 Variable var_capacity_; | 1746 Variable var_capacity_; |
| 1750 }; | 1747 }; |
| 1751 | 1748 |
| 1752 } // namespace | 1749 } // namespace |
| 1753 | 1750 |
| 1754 void RegExpBuiltinsAssembler::RegExpPrototypeMatchBody(Node* const context, | 1751 void RegExpBuiltinsAssembler::RegExpPrototypeMatchBody(Node* const context, |
| 1755 Node* const regexp, | 1752 Node* const regexp, |
| 1756 Node* const string, | 1753 Node* const string, |
| 1757 const bool is_fastpath) { | 1754 const bool is_fastpath) { |
| 1758 Isolate* const isolate = this->isolate(); | |
| 1759 | |
| 1760 Node* const null = NullConstant(); | 1755 Node* const null = NullConstant(); |
| 1761 Node* const int_zero = IntPtrConstant(0); | 1756 Node* const int_zero = IntPtrConstant(0); |
| 1762 Node* const smi_zero = SmiConstant(Smi::kZero); | 1757 Node* const smi_zero = SmiConstant(Smi::kZero); |
| 1763 | 1758 |
| 1764 Node* const is_global = | 1759 Node* const is_global = |
| 1765 FlagGetter(context, regexp, JSRegExp::kGlobal, is_fastpath); | 1760 FlagGetter(context, regexp, JSRegExp::kGlobal, is_fastpath); |
| 1766 | 1761 |
| 1767 Label if_isglobal(this), if_isnotglobal(this); | 1762 Label if_isglobal(this), if_isnotglobal(this); |
| 1768 Branch(is_global, &if_isglobal, &if_isnotglobal); | 1763 Branch(is_global, &if_isglobal, &if_isnotglobal); |
| 1769 | 1764 |
| (...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1863 // Store the match, growing the fixed array if needed. | 1858 // Store the match, growing the fixed array if needed. |
| 1864 | 1859 |
| 1865 array.Push(match); | 1860 array.Push(match); |
| 1866 | 1861 |
| 1867 // Advance last index if the match is the empty string. | 1862 // Advance last index if the match is the empty string. |
| 1868 | 1863 |
| 1869 Node* const match_length = LoadStringLength(match); | 1864 Node* const match_length = LoadStringLength(match); |
| 1870 GotoIfNot(SmiEqual(match_length, smi_zero), &loop); | 1865 GotoIfNot(SmiEqual(match_length, smi_zero), &loop); |
| 1871 | 1866 |
| 1872 Node* last_index = LoadLastIndex(context, regexp, is_fastpath); | 1867 Node* last_index = LoadLastIndex(context, regexp, is_fastpath); |
| 1873 | 1868 last_index = CallBuiltin(Builtins::kToLength, context, last_index); |
| 1874 Callable tolength_callable = CodeFactory::ToLength(isolate); | |
| 1875 last_index = CallStub(tolength_callable, context, last_index); | |
| 1876 | 1869 |
| 1877 Node* const new_last_index = | 1870 Node* const new_last_index = |
| 1878 AdvanceStringIndex(string, last_index, is_unicode); | 1871 AdvanceStringIndex(string, last_index, is_unicode); |
| 1879 | 1872 |
| 1880 StoreLastIndex(context, regexp, new_last_index, is_fastpath); | 1873 StoreLastIndex(context, regexp, new_last_index, is_fastpath); |
| 1881 | 1874 |
| 1882 Goto(&loop); | 1875 Goto(&loop); |
| 1883 } | 1876 } |
| 1884 } | 1877 } |
| 1885 | 1878 |
| (...skipping 456 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2342 "RegExp.prototype.@@split"); | 2335 "RegExp.prototype.@@split"); |
| 2343 Node* const receiver = maybe_receiver; | 2336 Node* const receiver = maybe_receiver; |
| 2344 | 2337 |
| 2345 // Convert {maybe_string} to a String. | 2338 // Convert {maybe_string} to a String. |
| 2346 Node* const string = ToString(context, maybe_string); | 2339 Node* const string = ToString(context, maybe_string); |
| 2347 | 2340 |
| 2348 Label stub(this), runtime(this, Label::kDeferred); | 2341 Label stub(this), runtime(this, Label::kDeferred); |
| 2349 BranchIfFastRegExp(context, map, &stub, &runtime); | 2342 BranchIfFastRegExp(context, map, &stub, &runtime); |
| 2350 | 2343 |
| 2351 Bind(&stub); | 2344 Bind(&stub); |
| 2352 Callable split_callable = CodeFactory::RegExpSplit(isolate()); | 2345 Return(CallBuiltin(Builtins::kRegExpSplit, context, receiver, string, |
| 2353 Return(CallStub(split_callable, context, receiver, string, maybe_limit)); | 2346 maybe_limit)); |
| 2354 | 2347 |
| 2355 Bind(&runtime); | 2348 Bind(&runtime); |
| 2356 Return(CallRuntime(Runtime::kRegExpSplit, context, receiver, string, | 2349 Return(CallRuntime(Runtime::kRegExpSplit, context, receiver, string, |
| 2357 maybe_limit)); | 2350 maybe_limit)); |
| 2358 } | 2351 } |
| 2359 | 2352 |
| 2360 Node* RegExpBuiltinsAssembler::ReplaceGlobalCallableFastPath( | 2353 Node* RegExpBuiltinsAssembler::ReplaceGlobalCallableFastPath( |
| 2361 Node* context, Node* regexp, Node* string, Node* replace_callable) { | 2354 Node* context, Node* regexp, Node* string, Node* replace_callable) { |
| 2362 // The fast path is reached only if {receiver} is a global unmodified | 2355 // The fast path is reached only if {receiver} is a global unmodified |
| 2363 // JSRegExp instance and {replace_callable} is callable. | 2356 // JSRegExp instance and {replace_callable} is callable. |
| (...skipping 315 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2679 runtime(this, Label::kDeferred); | 2672 runtime(this, Label::kDeferred); |
| 2680 | 2673 |
| 2681 // 2. Is {replace_value} callable? | 2674 // 2. Is {replace_value} callable? |
| 2682 GotoIf(TaggedIsSmi(replace_value), &checkreplacestring); | 2675 GotoIf(TaggedIsSmi(replace_value), &checkreplacestring); |
| 2683 Branch(IsCallableMap(LoadMap(replace_value)), &if_iscallable, | 2676 Branch(IsCallableMap(LoadMap(replace_value)), &if_iscallable, |
| 2684 &checkreplacestring); | 2677 &checkreplacestring); |
| 2685 | 2678 |
| 2686 // 3. Does ToString({replace_value}) contain '$'? | 2679 // 3. Does ToString({replace_value}) contain '$'? |
| 2687 Bind(&checkreplacestring); | 2680 Bind(&checkreplacestring); |
| 2688 { | 2681 { |
| 2689 Callable tostring_callable = CodeFactory::ToString(isolate()); | |
| 2690 Node* const replace_string = | 2682 Node* const replace_string = |
| 2691 CallStub(tostring_callable, context, replace_value); | 2683 CallBuiltin(Builtins::kToString, context, replace_value); |
| 2692 | 2684 |
| 2693 Callable indexof_callable = CodeFactory::StringIndexOf(isolate()); | |
| 2694 Node* const dollar_string = HeapConstant( | 2685 Node* const dollar_string = HeapConstant( |
| 2695 isolate()->factory()->LookupSingleCharacterStringFromCode('$')); | 2686 isolate()->factory()->LookupSingleCharacterStringFromCode('$')); |
| 2696 Node* const dollar_ix = CallStub(indexof_callable, context, replace_string, | 2687 Node* const dollar_ix = |
| 2697 dollar_string, SmiConstant(0)); | 2688 CallBuiltin(Builtins::kStringIndexOf, context, replace_string, |
| 2689 dollar_string, SmiConstant(0)); |
| 2698 GotoIfNot(SmiEqual(dollar_ix, SmiConstant(-1)), &runtime); | 2690 GotoIfNot(SmiEqual(dollar_ix, SmiConstant(-1)), &runtime); |
| 2699 | 2691 |
| 2700 Return( | 2692 Return( |
| 2701 ReplaceSimpleStringFastPath(context, regexp, string, replace_string)); | 2693 ReplaceSimpleStringFastPath(context, regexp, string, replace_string)); |
| 2702 } | 2694 } |
| 2703 | 2695 |
| 2704 // {regexp} is unmodified and {replace_value} is callable. | 2696 // {regexp} is unmodified and {replace_value} is callable. |
| 2705 Bind(&if_iscallable); | 2697 Bind(&if_iscallable); |
| 2706 { | 2698 { |
| 2707 Node* const replace_fn = replace_value; | 2699 Node* const replace_fn = replace_value; |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2751 // } | 2743 // } |
| 2752 // } | 2744 // } |
| 2753 | 2745 |
| 2754 // Ensure {maybe_receiver} is a JSReceiver. | 2746 // Ensure {maybe_receiver} is a JSReceiver. |
| 2755 Node* const map = ThrowIfNotJSReceiver( | 2747 Node* const map = ThrowIfNotJSReceiver( |
| 2756 context, maybe_receiver, MessageTemplate::kIncompatibleMethodReceiver, | 2748 context, maybe_receiver, MessageTemplate::kIncompatibleMethodReceiver, |
| 2757 "RegExp.prototype.@@replace"); | 2749 "RegExp.prototype.@@replace"); |
| 2758 Node* const receiver = maybe_receiver; | 2750 Node* const receiver = maybe_receiver; |
| 2759 | 2751 |
| 2760 // Convert {maybe_string} to a String. | 2752 // Convert {maybe_string} to a String. |
| 2761 Callable tostring_callable = CodeFactory::ToString(isolate()); | 2753 Node* const string = CallBuiltin(Builtins::kToString, context, maybe_string); |
| 2762 Node* const string = CallStub(tostring_callable, context, maybe_string); | |
| 2763 | 2754 |
| 2764 // Fast-path checks: 1. Is the {receiver} an unmodified JSRegExp instance? | 2755 // Fast-path checks: 1. Is the {receiver} an unmodified JSRegExp instance? |
| 2765 Label stub(this), runtime(this, Label::kDeferred); | 2756 Label stub(this), runtime(this, Label::kDeferred); |
| 2766 BranchIfFastRegExp(context, map, &stub, &runtime); | 2757 BranchIfFastRegExp(context, map, &stub, &runtime); |
| 2767 | 2758 |
| 2768 Bind(&stub); | 2759 Bind(&stub); |
| 2769 Callable replace_callable = CodeFactory::RegExpReplace(isolate()); | 2760 Return(CallBuiltin(Builtins::kRegExpReplace, context, receiver, string, |
| 2770 Return(CallStub(replace_callable, context, receiver, string, replace_value)); | 2761 replace_value)); |
| 2771 | 2762 |
| 2772 Bind(&runtime); | 2763 Bind(&runtime); |
| 2773 Return(CallRuntime(Runtime::kRegExpReplace, context, receiver, string, | 2764 Return(CallRuntime(Runtime::kRegExpReplace, context, receiver, string, |
| 2774 replace_value)); | 2765 replace_value)); |
| 2775 } | 2766 } |
| 2776 | 2767 |
| 2777 // Simple string matching functionality for internal use which does not modify | 2768 // Simple string matching functionality for internal use which does not modify |
| 2778 // the last match info. | 2769 // the last match info. |
| 2779 TF_BUILTIN(RegExpInternalMatch, RegExpBuiltinsAssembler) { | 2770 TF_BUILTIN(RegExpInternalMatch, RegExpBuiltinsAssembler) { |
| 2780 Node* const regexp = Parameter(1); | 2771 Node* const regexp = Parameter(1); |
| (...skipping 19 matching lines...) Expand all Loading... |
| 2800 Bind(&if_matched); | 2791 Bind(&if_matched); |
| 2801 { | 2792 { |
| 2802 Node* result = | 2793 Node* result = |
| 2803 ConstructNewResultFromMatchInfo(context, regexp, match_indices, string); | 2794 ConstructNewResultFromMatchInfo(context, regexp, match_indices, string); |
| 2804 Return(result); | 2795 Return(result); |
| 2805 } | 2796 } |
| 2806 } | 2797 } |
| 2807 | 2798 |
| 2808 } // namespace internal | 2799 } // namespace internal |
| 2809 } // namespace v8 | 2800 } // namespace v8 |
| OLD | NEW |