| OLD | NEW |
| 1 // Copyright 2017 the V8 project authors. All rights reserved. | 1 // Copyright 2017 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-gen.h" | 5 #include "src/builtins/builtins-regexp-gen.h" |
| 6 | 6 |
| 7 #include "src/builtins/builtins-constructor.h" | 7 #include "src/builtins/builtins-constructor.h" |
| 8 #include "src/builtins/builtins-utils-gen.h" | 8 #include "src/builtins/builtins-utils-gen.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 458 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 469 | 469 |
| 470 // ES#sec-regexp.prototype.exec | 470 // ES#sec-regexp.prototype.exec |
| 471 // RegExp.prototype.exec ( string ) | 471 // RegExp.prototype.exec ( string ) |
| 472 // Implements the core of RegExp.prototype.exec but without actually | 472 // Implements the core of RegExp.prototype.exec but without actually |
| 473 // constructing the JSRegExpResult. Returns either null (if the RegExp did not | 473 // constructing the JSRegExpResult. Returns either null (if the RegExp did not |
| 474 // match) or a fixed array containing match indices as returned by | 474 // match) or a fixed array containing match indices as returned by |
| 475 // RegExpExecStub. | 475 // RegExpExecStub. |
| 476 Node* RegExpBuiltinsAssembler::RegExpPrototypeExecBodyWithoutResult( | 476 Node* RegExpBuiltinsAssembler::RegExpPrototypeExecBodyWithoutResult( |
| 477 Node* const context, Node* const regexp, Node* const string, | 477 Node* const context, Node* const regexp, Node* const string, |
| 478 Label* if_didnotmatch, const bool is_fastpath) { | 478 Label* if_didnotmatch, const bool is_fastpath) { |
| 479 Isolate* const isolate = this->isolate(); | |
| 480 | |
| 481 Node* const null = NullConstant(); | 479 Node* const null = NullConstant(); |
| 482 Node* const int_zero = IntPtrConstant(0); | 480 Node* const int_zero = IntPtrConstant(0); |
| 483 Node* const smi_zero = SmiConstant(Smi::kZero); | 481 Node* const smi_zero = SmiConstant(Smi::kZero); |
| 484 | 482 |
| 485 if (!is_fastpath) { | 483 if (!is_fastpath) { |
| 486 ThrowIfNotInstanceType(context, regexp, JS_REGEXP_TYPE, | 484 ThrowIfNotInstanceType(context, regexp, JS_REGEXP_TYPE, |
| 487 "RegExp.prototype.exec"); | 485 "RegExp.prototype.exec"); |
| 488 } | 486 } |
| 489 | 487 |
| 490 CSA_ASSERT(this, IsStringInstanceType(LoadInstanceType(string))); | 488 CSA_ASSERT(this, IsStringInstanceType(LoadInstanceType(string))); |
| 491 CSA_ASSERT(this, HasInstanceType(regexp, JS_REGEXP_TYPE)); | 489 CSA_ASSERT(this, HasInstanceType(regexp, JS_REGEXP_TYPE)); |
| 492 | 490 |
| 493 Variable var_result(this, MachineRepresentation::kTagged); | 491 Variable var_result(this, MachineRepresentation::kTagged); |
| 494 Label out(this); | 492 Label out(this); |
| 495 | 493 |
| 496 // Load lastIndex. | 494 // Load lastIndex. |
| 497 Variable var_lastindex(this, MachineRepresentation::kTagged); | 495 Variable var_lastindex(this, MachineRepresentation::kTagged); |
| 498 { | 496 { |
| 499 Node* const regexp_lastindex = LoadLastIndex(context, regexp, is_fastpath); | 497 Node* const regexp_lastindex = LoadLastIndex(context, regexp, is_fastpath); |
| 500 var_lastindex.Bind(regexp_lastindex); | 498 var_lastindex.Bind(regexp_lastindex); |
| 501 | 499 |
| 502 // Omit ToLength if lastindex is a non-negative smi. | 500 // Omit ToLength if lastindex is a non-negative smi. |
| 503 Label call_tolength(this, Label::kDeferred), next(this); | 501 Label call_tolength(this, Label::kDeferred), next(this); |
| 504 Branch(TaggedIsPositiveSmi(regexp_lastindex), &next, &call_tolength); | 502 Branch(TaggedIsPositiveSmi(regexp_lastindex), &next, &call_tolength); |
| 505 | 503 |
| 506 Bind(&call_tolength); | 504 Bind(&call_tolength); |
| 507 { | 505 { |
| 508 Callable tolength_callable = CodeFactory::ToLength(isolate); | |
| 509 var_lastindex.Bind( | 506 var_lastindex.Bind( |
| 510 CallStub(tolength_callable, context, regexp_lastindex)); | 507 CallBuiltin(Builtins::kToLength, context, regexp_lastindex)); |
| 511 Goto(&next); | 508 Goto(&next); |
| 512 } | 509 } |
| 513 | 510 |
| 514 Bind(&next); | 511 Bind(&next); |
| 515 } | 512 } |
| 516 | 513 |
| 517 // Check whether the regexp is global or sticky, which determines whether we | 514 // Check whether the regexp is global or sticky, which determines whether we |
| 518 // update last index later on. | 515 // update last index later on. |
| 519 Node* const flags = LoadObjectField(regexp, JSRegExp::kFlagsOffset); | 516 Node* const flags = LoadObjectField(regexp, JSRegExp::kFlagsOffset); |
| 520 Node* const is_global_or_sticky = WordAnd( | 517 Node* const is_global_or_sticky = WordAnd( |
| (...skipping 119 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 640 | 637 |
| 641 Branch(IsJSReceiverInstanceType(value_instance_type), &out, &throw_exception); | 638 Branch(IsJSReceiverInstanceType(value_instance_type), &out, &throw_exception); |
| 642 | 639 |
| 643 // The {value} is not a compatible receiver for this method. | 640 // The {value} is not a compatible receiver for this method. |
| 644 Bind(&throw_exception); | 641 Bind(&throw_exception); |
| 645 { | 642 { |
| 646 Node* const message_id = SmiConstant(Smi::FromInt(msg_template)); | 643 Node* const message_id = SmiConstant(Smi::FromInt(msg_template)); |
| 647 Node* const method_name_str = HeapConstant( | 644 Node* const method_name_str = HeapConstant( |
| 648 isolate()->factory()->NewStringFromAsciiChecked(method_name, TENURED)); | 645 isolate()->factory()->NewStringFromAsciiChecked(method_name, TENURED)); |
| 649 | 646 |
| 650 Callable callable = CodeFactory::ToString(isolate()); | 647 Node* const value_str = |
| 651 Node* const value_str = CallStub(callable, context, maybe_receiver); | 648 CallBuiltin(Builtins::kToString, context, maybe_receiver); |
| 652 | 649 |
| 653 CallRuntime(Runtime::kThrowTypeError, context, message_id, method_name_str, | 650 CallRuntime(Runtime::kThrowTypeError, context, message_id, method_name_str, |
| 654 value_str); | 651 value_str); |
| 655 Unreachable(); | 652 Unreachable(); |
| 656 } | 653 } |
| 657 | 654 |
| 658 Bind(&out); | 655 Bind(&out); |
| 659 return var_value_map.value(); | 656 return var_value_map.value(); |
| 660 } | 657 } |
| 661 | 658 |
| (...skipping 962 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1624 Variable var_length_; | 1621 Variable var_length_; |
| 1625 Variable var_capacity_; | 1622 Variable var_capacity_; |
| 1626 }; | 1623 }; |
| 1627 | 1624 |
| 1628 } // namespace | 1625 } // namespace |
| 1629 | 1626 |
| 1630 void RegExpBuiltinsAssembler::RegExpPrototypeMatchBody(Node* const context, | 1627 void RegExpBuiltinsAssembler::RegExpPrototypeMatchBody(Node* const context, |
| 1631 Node* const regexp, | 1628 Node* const regexp, |
| 1632 Node* const string, | 1629 Node* const string, |
| 1633 const bool is_fastpath) { | 1630 const bool is_fastpath) { |
| 1634 Isolate* const isolate = this->isolate(); | |
| 1635 | |
| 1636 Node* const null = NullConstant(); | 1631 Node* const null = NullConstant(); |
| 1637 Node* const int_zero = IntPtrConstant(0); | 1632 Node* const int_zero = IntPtrConstant(0); |
| 1638 Node* const smi_zero = SmiConstant(Smi::kZero); | 1633 Node* const smi_zero = SmiConstant(Smi::kZero); |
| 1639 | 1634 |
| 1640 Node* const is_global = | 1635 Node* const is_global = |
| 1641 FlagGetter(context, regexp, JSRegExp::kGlobal, is_fastpath); | 1636 FlagGetter(context, regexp, JSRegExp::kGlobal, is_fastpath); |
| 1642 | 1637 |
| 1643 Label if_isglobal(this), if_isnotglobal(this); | 1638 Label if_isglobal(this), if_isnotglobal(this); |
| 1644 Branch(is_global, &if_isglobal, &if_isnotglobal); | 1639 Branch(is_global, &if_isglobal, &if_isnotglobal); |
| 1645 | 1640 |
| (...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1738 | 1733 |
| 1739 // Store the match, growing the fixed array if needed. | 1734 // Store the match, growing the fixed array if needed. |
| 1740 | 1735 |
| 1741 array.Push(match); | 1736 array.Push(match); |
| 1742 | 1737 |
| 1743 // Advance last index if the match is the empty string. | 1738 // Advance last index if the match is the empty string. |
| 1744 | 1739 |
| 1745 Node* const match_length = LoadStringLength(match); | 1740 Node* const match_length = LoadStringLength(match); |
| 1746 GotoIfNot(SmiEqual(match_length, smi_zero), &loop); | 1741 GotoIfNot(SmiEqual(match_length, smi_zero), &loop); |
| 1747 | 1742 |
| 1748 Node* last_index = LoadLastIndex(context, regexp, is_fastpath); | 1743 Node* const last_index = |
| 1749 | 1744 CallBuiltin(Builtins::kToLength, context, |
| 1750 Callable tolength_callable = CodeFactory::ToLength(isolate); | 1745 LoadLastIndex(context, regexp, is_fastpath)); |
| 1751 last_index = CallStub(tolength_callable, context, last_index); | |
| 1752 | |
| 1753 Node* const new_last_index = | 1746 Node* const new_last_index = |
| 1754 AdvanceStringIndex(string, last_index, is_unicode); | 1747 AdvanceStringIndex(string, last_index, is_unicode); |
| 1755 | 1748 |
| 1756 StoreLastIndex(context, regexp, new_last_index, is_fastpath); | 1749 StoreLastIndex(context, regexp, new_last_index, is_fastpath); |
| 1757 | 1750 |
| 1758 Goto(&loop); | 1751 Goto(&loop); |
| 1759 } | 1752 } |
| 1760 } | 1753 } |
| 1761 | 1754 |
| 1762 Bind(&out); | 1755 Bind(&out); |
| (...skipping 455 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2218 "RegExp.prototype.@@split"); | 2211 "RegExp.prototype.@@split"); |
| 2219 Node* const receiver = maybe_receiver; | 2212 Node* const receiver = maybe_receiver; |
| 2220 | 2213 |
| 2221 // Convert {maybe_string} to a String. | 2214 // Convert {maybe_string} to a String. |
| 2222 Node* const string = ToString(context, maybe_string); | 2215 Node* const string = ToString(context, maybe_string); |
| 2223 | 2216 |
| 2224 Label stub(this), runtime(this, Label::kDeferred); | 2217 Label stub(this), runtime(this, Label::kDeferred); |
| 2225 BranchIfFastRegExp(context, map, &stub, &runtime); | 2218 BranchIfFastRegExp(context, map, &stub, &runtime); |
| 2226 | 2219 |
| 2227 Bind(&stub); | 2220 Bind(&stub); |
| 2228 Callable split_callable = CodeFactory::RegExpSplit(isolate()); | 2221 Return(CallBuiltin(Builtins::kRegExpSplit, context, receiver, string, |
| 2229 Return(CallStub(split_callable, context, receiver, string, maybe_limit)); | 2222 maybe_limit)); |
| 2230 | 2223 |
| 2231 Bind(&runtime); | 2224 Bind(&runtime); |
| 2232 Return(CallRuntime(Runtime::kRegExpSplit, context, receiver, string, | 2225 Return(CallRuntime(Runtime::kRegExpSplit, context, receiver, string, |
| 2233 maybe_limit)); | 2226 maybe_limit)); |
| 2234 } | 2227 } |
| 2235 | 2228 |
| 2236 Node* RegExpBuiltinsAssembler::ReplaceGlobalCallableFastPath( | 2229 Node* RegExpBuiltinsAssembler::ReplaceGlobalCallableFastPath( |
| 2237 Node* context, Node* regexp, Node* string, Node* replace_callable) { | 2230 Node* context, Node* regexp, Node* string, Node* replace_callable) { |
| 2238 // The fast path is reached only if {receiver} is a global unmodified | 2231 // The fast path is reached only if {receiver} is a global unmodified |
| 2239 // JSRegExp instance and {replace_callable} is callable. | 2232 // JSRegExp instance and {replace_callable} is callable. |
| (...skipping 317 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2557 runtime(this, Label::kDeferred); | 2550 runtime(this, Label::kDeferred); |
| 2558 | 2551 |
| 2559 // 2. Is {replace_value} callable? | 2552 // 2. Is {replace_value} callable? |
| 2560 GotoIf(TaggedIsSmi(replace_value), &checkreplacestring); | 2553 GotoIf(TaggedIsSmi(replace_value), &checkreplacestring); |
| 2561 Branch(IsCallableMap(LoadMap(replace_value)), &if_iscallable, | 2554 Branch(IsCallableMap(LoadMap(replace_value)), &if_iscallable, |
| 2562 &checkreplacestring); | 2555 &checkreplacestring); |
| 2563 | 2556 |
| 2564 // 3. Does ToString({replace_value}) contain '$'? | 2557 // 3. Does ToString({replace_value}) contain '$'? |
| 2565 Bind(&checkreplacestring); | 2558 Bind(&checkreplacestring); |
| 2566 { | 2559 { |
| 2567 Callable tostring_callable = CodeFactory::ToString(isolate()); | |
| 2568 Node* const replace_string = | 2560 Node* const replace_string = |
| 2569 CallStub(tostring_callable, context, replace_value); | 2561 CallBuiltin(Builtins::kToString, context, replace_value); |
| 2570 | 2562 |
| 2571 Callable indexof_callable = CodeFactory::StringIndexOf(isolate()); | |
| 2572 Node* const dollar_string = HeapConstant( | 2563 Node* const dollar_string = HeapConstant( |
| 2573 isolate()->factory()->LookupSingleCharacterStringFromCode('$')); | 2564 isolate()->factory()->LookupSingleCharacterStringFromCode('$')); |
| 2574 Node* const dollar_ix = CallStub(indexof_callable, context, replace_string, | 2565 Node* const dollar_ix = |
| 2575 dollar_string, SmiConstant(0)); | 2566 CallBuiltin(Builtins::kStringIndexOf, context, replace_string, |
| 2567 dollar_string, SmiConstant(0)); |
| 2576 GotoIfNot(SmiEqual(dollar_ix, SmiConstant(-1)), &runtime); | 2568 GotoIfNot(SmiEqual(dollar_ix, SmiConstant(-1)), &runtime); |
| 2577 | 2569 |
| 2578 Return( | 2570 Return( |
| 2579 ReplaceSimpleStringFastPath(context, regexp, string, replace_string)); | 2571 ReplaceSimpleStringFastPath(context, regexp, string, replace_string)); |
| 2580 } | 2572 } |
| 2581 | 2573 |
| 2582 // {regexp} is unmodified and {replace_value} is callable. | 2574 // {regexp} is unmodified and {replace_value} is callable. |
| 2583 Bind(&if_iscallable); | 2575 Bind(&if_iscallable); |
| 2584 { | 2576 { |
| 2585 Node* const replace_fn = replace_value; | 2577 Node* const replace_fn = replace_value; |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2629 // } | 2621 // } |
| 2630 // } | 2622 // } |
| 2631 | 2623 |
| 2632 // Ensure {maybe_receiver} is a JSReceiver. | 2624 // Ensure {maybe_receiver} is a JSReceiver. |
| 2633 Node* const map = ThrowIfNotJSReceiver( | 2625 Node* const map = ThrowIfNotJSReceiver( |
| 2634 context, maybe_receiver, MessageTemplate::kIncompatibleMethodReceiver, | 2626 context, maybe_receiver, MessageTemplate::kIncompatibleMethodReceiver, |
| 2635 "RegExp.prototype.@@replace"); | 2627 "RegExp.prototype.@@replace"); |
| 2636 Node* const receiver = maybe_receiver; | 2628 Node* const receiver = maybe_receiver; |
| 2637 | 2629 |
| 2638 // Convert {maybe_string} to a String. | 2630 // Convert {maybe_string} to a String. |
| 2639 Callable tostring_callable = CodeFactory::ToString(isolate()); | 2631 Node* const string = CallBuiltin(Builtins::kToString, context, maybe_string); |
| 2640 Node* const string = CallStub(tostring_callable, context, maybe_string); | |
| 2641 | 2632 |
| 2642 // Fast-path checks: 1. Is the {receiver} an unmodified JSRegExp instance? | 2633 // Fast-path checks: 1. Is the {receiver} an unmodified JSRegExp instance? |
| 2643 Label stub(this), runtime(this, Label::kDeferred); | 2634 Label stub(this), runtime(this, Label::kDeferred); |
| 2644 BranchIfFastRegExp(context, map, &stub, &runtime); | 2635 BranchIfFastRegExp(context, map, &stub, &runtime); |
| 2645 | 2636 |
| 2646 Bind(&stub); | 2637 Bind(&stub); |
| 2647 Callable replace_callable = CodeFactory::RegExpReplace(isolate()); | 2638 Return(CallBuiltin(Builtins::kRegExpReplace, context, receiver, string, |
| 2648 Return(CallStub(replace_callable, context, receiver, string, replace_value)); | 2639 replace_value)); |
| 2649 | 2640 |
| 2650 Bind(&runtime); | 2641 Bind(&runtime); |
| 2651 Return(CallRuntime(Runtime::kRegExpReplace, context, receiver, string, | 2642 Return(CallRuntime(Runtime::kRegExpReplace, context, receiver, string, |
| 2652 replace_value)); | 2643 replace_value)); |
| 2653 } | 2644 } |
| 2654 | 2645 |
| 2655 // Simple string matching functionality for internal use which does not modify | 2646 // Simple string matching functionality for internal use which does not modify |
| 2656 // the last match info. | 2647 // the last match info. |
| 2657 TF_BUILTIN(RegExpInternalMatch, RegExpBuiltinsAssembler) { | 2648 TF_BUILTIN(RegExpInternalMatch, RegExpBuiltinsAssembler) { |
| 2658 Node* const regexp = Parameter(1); | 2649 Node* const regexp = Parameter(1); |
| (...skipping 19 matching lines...) Expand all Loading... |
| 2678 Bind(&if_matched); | 2669 Bind(&if_matched); |
| 2679 { | 2670 { |
| 2680 Node* result = | 2671 Node* result = |
| 2681 ConstructNewResultFromMatchInfo(context, regexp, match_indices, string); | 2672 ConstructNewResultFromMatchInfo(context, regexp, match_indices, string); |
| 2682 Return(result); | 2673 Return(result); |
| 2683 } | 2674 } |
| 2684 } | 2675 } |
| 2685 | 2676 |
| 2686 } // namespace internal | 2677 } // namespace internal |
| 2687 } // namespace v8 | 2678 } // namespace v8 |
| OLD | NEW |