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 229 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
240 | 240 |
241 Variable var_result(this, MachineRepresentation::kTagged); | 241 Variable var_result(this, MachineRepresentation::kTagged); |
242 Label out(this); | 242 Label out(this); |
243 | 243 |
244 // Load lastIndex. | 244 // Load lastIndex. |
245 Variable var_lastindex(this, MachineRepresentation::kTagged); | 245 Variable var_lastindex(this, MachineRepresentation::kTagged); |
246 { | 246 { |
247 Node* const regexp_lastindex = LoadLastIndex(context, regexp, is_fastpath); | 247 Node* const regexp_lastindex = LoadLastIndex(context, regexp, is_fastpath); |
248 var_lastindex.Bind(regexp_lastindex); | 248 var_lastindex.Bind(regexp_lastindex); |
249 | 249 |
250 // Omit ToLength if lastindex is a non-negative smi. | 250 if (is_fastpath) { |
251 Label call_tolength(this, Label::kDeferred), next(this); | 251 // ToLength on a positive smi is a nop and can be skipped. |
252 Branch(TaggedIsPositiveSmi(regexp_lastindex), &next, &call_tolength); | 252 CSA_ASSERT(this, TaggedIsPositiveSmi(regexp_lastindex)); |
| 253 } else { |
| 254 // Omit ToLength if lastindex is a non-negative smi. |
| 255 Label call_tolength(this, Label::kDeferred), next(this); |
| 256 Branch(TaggedIsPositiveSmi(regexp_lastindex), &next, &call_tolength); |
253 | 257 |
254 Bind(&call_tolength); | 258 Bind(&call_tolength); |
255 { | 259 { |
256 Callable tolength_callable = CodeFactory::ToLength(isolate); | 260 Callable tolength_callable = CodeFactory::ToLength(isolate); |
257 var_lastindex.Bind( | 261 var_lastindex.Bind( |
258 CallStub(tolength_callable, context, regexp_lastindex)); | 262 CallStub(tolength_callable, context, regexp_lastindex)); |
259 Goto(&next); | 263 Goto(&next); |
| 264 } |
| 265 |
| 266 Bind(&next); |
260 } | 267 } |
261 | |
262 Bind(&next); | |
263 } | 268 } |
264 | 269 |
265 // Check whether the regexp is global or sticky, which determines whether we | 270 // Check whether the regexp is global or sticky, which determines whether we |
266 // update last index later on. | 271 // update last index later on. |
267 Node* const flags = LoadObjectField(regexp, JSRegExp::kFlagsOffset); | 272 Node* const flags = LoadObjectField(regexp, JSRegExp::kFlagsOffset); |
268 Node* const is_global_or_sticky = WordAnd( | 273 Node* const is_global_or_sticky = WordAnd( |
269 SmiUntag(flags), IntPtrConstant(JSRegExp::kGlobal | JSRegExp::kSticky)); | 274 SmiUntag(flags), IntPtrConstant(JSRegExp::kGlobal | JSRegExp::kSticky)); |
270 Node* const should_update_last_index = | 275 Node* const should_update_last_index = |
271 WordNotEqual(is_global_or_sticky, int_zero); | 276 WordNotEqual(is_global_or_sticky, int_zero); |
272 | 277 |
(...skipping 128 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
401 | 406 |
402 CallRuntime(Runtime::kThrowTypeError, context, message_id, method_name_str, | 407 CallRuntime(Runtime::kThrowTypeError, context, message_id, method_name_str, |
403 value_str); | 408 value_str); |
404 Unreachable(); | 409 Unreachable(); |
405 } | 410 } |
406 | 411 |
407 Bind(&out); | 412 Bind(&out); |
408 return var_value_map.value(); | 413 return var_value_map.value(); |
409 } | 414 } |
410 | 415 |
411 Node* RegExpBuiltinsAssembler::IsInitialRegExpMap(Node* context, Node* map) { | 416 Node* RegExpBuiltinsAssembler::IsInitialRegExpMap(Node* context, Node* object, |
| 417 Node* map) { |
| 418 Label out(this); |
| 419 Variable var_result(this, MachineRepresentation::kWord32); |
| 420 |
412 Node* const native_context = LoadNativeContext(context); | 421 Node* const native_context = LoadNativeContext(context); |
413 Node* const regexp_fun = | 422 Node* const regexp_fun = |
414 LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX); | 423 LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX); |
415 Node* const initial_map = | 424 Node* const initial_map = |
416 LoadObjectField(regexp_fun, JSFunction::kPrototypeOrInitialMapOffset); | 425 LoadObjectField(regexp_fun, JSFunction::kPrototypeOrInitialMapOffset); |
417 Node* const has_initialmap = WordEqual(map, initial_map); | 426 Node* const has_initialmap = WordEqual(map, initial_map); |
418 | 427 |
419 return has_initialmap; | 428 var_result.Bind(has_initialmap); |
| 429 GotoIfNot(has_initialmap, &out); |
| 430 |
| 431 // The smi check is required to omit ToLength(lastIndex) calls with possible |
| 432 // user-code execution on the fast path. |
| 433 Node* const last_index = FastLoadLastIndex(object); |
| 434 var_result.Bind(TaggedIsPositiveSmi(last_index)); |
| 435 Goto(&out); |
| 436 |
| 437 Bind(&out); |
| 438 return var_result.value(); |
420 } | 439 } |
421 | 440 |
422 // RegExp fast path implementations rely on unmodified JSRegExp instances. | 441 // RegExp fast path implementations rely on unmodified JSRegExp instances. |
423 // We use a fairly coarse granularity for this and simply check whether both | 442 // We use a fairly coarse granularity for this and simply check whether both |
424 // the regexp itself is unmodified (i.e. its map has not changed) and its | 443 // the regexp itself is unmodified (i.e. its map has not changed), its |
425 // prototype is unmodified. | 444 // prototype is unmodified, and lastIndex is a non-negative smi. |
426 void RegExpBuiltinsAssembler::BranchIfFastRegExp(Node* const context, | 445 void RegExpBuiltinsAssembler::BranchIfFastRegExp(Node* const context, |
| 446 Node* const object, |
427 Node* const map, | 447 Node* const map, |
428 Label* const if_isunmodified, | 448 Label* const if_isunmodified, |
429 Label* const if_ismodified) { | 449 Label* const if_ismodified) { |
| 450 CSA_ASSERT(this, WordEqual(LoadMap(object), map)); |
| 451 |
| 452 // TODO(ishell): Update this check once map changes for constant field |
| 453 // tracking are landing. |
| 454 |
430 Node* const native_context = LoadNativeContext(context); | 455 Node* const native_context = LoadNativeContext(context); |
431 Node* const regexp_fun = | 456 Node* const regexp_fun = |
432 LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX); | 457 LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX); |
433 Node* const initial_map = | 458 Node* const initial_map = |
434 LoadObjectField(regexp_fun, JSFunction::kPrototypeOrInitialMapOffset); | 459 LoadObjectField(regexp_fun, JSFunction::kPrototypeOrInitialMapOffset); |
435 Node* const has_initialmap = WordEqual(map, initial_map); | 460 Node* const has_initialmap = WordEqual(map, initial_map); |
436 | 461 |
437 GotoIfNot(has_initialmap, if_ismodified); | 462 GotoIfNot(has_initialmap, if_ismodified); |
438 | 463 |
439 Node* const initial_proto_initial_map = | 464 Node* const initial_proto_initial_map = |
440 LoadContextElement(native_context, Context::REGEXP_PROTOTYPE_MAP_INDEX); | 465 LoadContextElement(native_context, Context::REGEXP_PROTOTYPE_MAP_INDEX); |
441 Node* const proto_map = LoadMap(LoadMapPrototype(map)); | 466 Node* const proto_map = LoadMap(LoadMapPrototype(map)); |
442 Node* const proto_has_initialmap = | 467 Node* const proto_has_initialmap = |
443 WordEqual(proto_map, initial_proto_initial_map); | 468 WordEqual(proto_map, initial_proto_initial_map); |
444 | 469 |
445 // TODO(ishell): Update this check once map changes for constant field | 470 GotoIfNot(proto_has_initialmap, if_ismodified); |
446 // tracking are landing. | |
447 | 471 |
448 Branch(proto_has_initialmap, if_isunmodified, if_ismodified); | 472 // The smi check is required to omit ToLength(lastIndex) calls with possible |
| 473 // user-code execution on the fast path. |
| 474 Node* const last_index = FastLoadLastIndex(object); |
| 475 Branch(TaggedIsPositiveSmi(last_index), if_isunmodified, if_ismodified); |
449 } | 476 } |
450 | 477 |
451 Node* RegExpBuiltinsAssembler::IsFastRegExpMap(Node* const context, | 478 Node* RegExpBuiltinsAssembler::IsFastRegExpMap(Node* const context, |
| 479 Node* const object, |
452 Node* const map) { | 480 Node* const map) { |
453 Label yup(this), nope(this), out(this); | 481 Label yup(this), nope(this), out(this); |
454 Variable var_result(this, MachineRepresentation::kWord32); | 482 Variable var_result(this, MachineRepresentation::kWord32); |
455 | 483 |
456 BranchIfFastRegExp(context, map, &yup, &nope); | 484 BranchIfFastRegExp(context, object, map, &yup, &nope); |
457 | 485 |
458 Bind(&yup); | 486 Bind(&yup); |
459 var_result.Bind(Int32Constant(1)); | 487 var_result.Bind(Int32Constant(1)); |
460 Goto(&out); | 488 Goto(&out); |
461 | 489 |
462 Bind(&nope); | 490 Bind(&nope); |
463 var_result.Bind(Int32Constant(0)); | 491 var_result.Bind(Int32Constant(0)); |
464 Goto(&out); | 492 Goto(&out); |
465 | 493 |
466 Bind(&out); | 494 Bind(&out); |
(...skipping 20 matching lines...) Expand all Loading... |
487 | 515 |
488 // Ensure {maybe_receiver} is a JSRegExp. | 516 // Ensure {maybe_receiver} is a JSRegExp. |
489 Node* const regexp_map = ThrowIfNotInstanceType( | 517 Node* const regexp_map = ThrowIfNotInstanceType( |
490 context, maybe_receiver, JS_REGEXP_TYPE, "RegExp.prototype.exec"); | 518 context, maybe_receiver, JS_REGEXP_TYPE, "RegExp.prototype.exec"); |
491 Node* const receiver = maybe_receiver; | 519 Node* const receiver = maybe_receiver; |
492 | 520 |
493 // Convert {maybe_string} to a String. | 521 // Convert {maybe_string} to a String. |
494 Node* const string = ToString(context, maybe_string); | 522 Node* const string = ToString(context, maybe_string); |
495 | 523 |
496 Label if_isfastpath(this), if_isslowpath(this); | 524 Label if_isfastpath(this), if_isslowpath(this); |
497 Branch(IsInitialRegExpMap(context, regexp_map), &if_isfastpath, | 525 Branch(IsInitialRegExpMap(context, receiver, regexp_map), &if_isfastpath, |
498 &if_isslowpath); | 526 &if_isslowpath); |
499 | 527 |
500 Bind(&if_isfastpath); | 528 Bind(&if_isfastpath); |
501 { | 529 { |
502 Node* const result = | 530 Node* const result = |
503 RegExpPrototypeExecBody(context, receiver, string, true); | 531 RegExpPrototypeExecBody(context, receiver, string, true); |
504 Return(result); | 532 Return(result); |
505 } | 533 } |
506 | 534 |
507 Bind(&if_isslowpath); | 535 Bind(&if_isslowpath); |
(...skipping 166 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
674 TF_BUILTIN(RegExpPrototypeFlagsGetter, RegExpBuiltinsAssembler) { | 702 TF_BUILTIN(RegExpPrototypeFlagsGetter, RegExpBuiltinsAssembler) { |
675 Node* const maybe_receiver = Parameter(0); | 703 Node* const maybe_receiver = Parameter(0); |
676 Node* const context = Parameter(3); | 704 Node* const context = Parameter(3); |
677 | 705 |
678 Node* const map = ThrowIfNotJSReceiver(context, maybe_receiver, | 706 Node* const map = ThrowIfNotJSReceiver(context, maybe_receiver, |
679 MessageTemplate::kRegExpNonObject, | 707 MessageTemplate::kRegExpNonObject, |
680 "RegExp.prototype.flags"); | 708 "RegExp.prototype.flags"); |
681 Node* const receiver = maybe_receiver; | 709 Node* const receiver = maybe_receiver; |
682 | 710 |
683 Label if_isfastpath(this), if_isslowpath(this, Label::kDeferred); | 711 Label if_isfastpath(this), if_isslowpath(this, Label::kDeferred); |
684 Branch(IsInitialRegExpMap(context, map), &if_isfastpath, &if_isslowpath); | 712 Branch(IsInitialRegExpMap(context, receiver, map), &if_isfastpath, |
| 713 &if_isslowpath); |
685 | 714 |
686 Bind(&if_isfastpath); | 715 Bind(&if_isfastpath); |
687 Return(FlagsGetter(context, receiver, true)); | 716 Return(FlagsGetter(context, receiver, true)); |
688 | 717 |
689 Bind(&if_isslowpath); | 718 Bind(&if_isslowpath); |
690 Return(FlagsGetter(context, receiver, false)); | 719 Return(FlagsGetter(context, receiver, false)); |
691 } | 720 } |
692 | 721 |
693 // ES#sec-regexp-pattern-flags | 722 // ES#sec-regexp-pattern-flags |
694 // RegExp ( pattern, flags ) | 723 // RegExp ( pattern, flags ) |
(...skipping 519 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1214 Node* RegExpBuiltinsAssembler::RegExpExec(Node* context, Node* regexp, | 1243 Node* RegExpBuiltinsAssembler::RegExpExec(Node* context, Node* regexp, |
1215 Node* string) { | 1244 Node* string) { |
1216 Isolate* isolate = this->isolate(); | 1245 Isolate* isolate = this->isolate(); |
1217 | 1246 |
1218 Node* const null = NullConstant(); | 1247 Node* const null = NullConstant(); |
1219 | 1248 |
1220 Variable var_result(this, MachineRepresentation::kTagged); | 1249 Variable var_result(this, MachineRepresentation::kTagged); |
1221 Label out(this), if_isfastpath(this), if_isslowpath(this); | 1250 Label out(this), if_isfastpath(this), if_isslowpath(this); |
1222 | 1251 |
1223 Node* const map = LoadMap(regexp); | 1252 Node* const map = LoadMap(regexp); |
1224 BranchIfFastRegExp(context, map, &if_isfastpath, &if_isslowpath); | 1253 BranchIfFastRegExp(context, regexp, map, &if_isfastpath, &if_isslowpath); |
1225 | 1254 |
1226 Bind(&if_isfastpath); | 1255 Bind(&if_isfastpath); |
1227 { | 1256 { |
1228 Node* const result = RegExpPrototypeExecBody(context, regexp, string, true); | 1257 Node* const result = RegExpPrototypeExecBody(context, regexp, string, true); |
1229 var_result.Bind(result); | 1258 var_result.Bind(result); |
1230 Goto(&out); | 1259 Goto(&out); |
1231 } | 1260 } |
1232 | 1261 |
1233 Bind(&if_isslowpath); | 1262 Bind(&if_isslowpath); |
1234 { | 1263 { |
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1288 // Ensure {maybe_receiver} is a JSReceiver. | 1317 // Ensure {maybe_receiver} is a JSReceiver. |
1289 Node* const map = ThrowIfNotJSReceiver( | 1318 Node* const map = ThrowIfNotJSReceiver( |
1290 context, maybe_receiver, MessageTemplate::kIncompatibleMethodReceiver, | 1319 context, maybe_receiver, MessageTemplate::kIncompatibleMethodReceiver, |
1291 "RegExp.prototype.test"); | 1320 "RegExp.prototype.test"); |
1292 Node* const receiver = maybe_receiver; | 1321 Node* const receiver = maybe_receiver; |
1293 | 1322 |
1294 // Convert {maybe_string} to a String. | 1323 // Convert {maybe_string} to a String. |
1295 Node* const string = ToString(context, maybe_string); | 1324 Node* const string = ToString(context, maybe_string); |
1296 | 1325 |
1297 Label fast_path(this), slow_path(this); | 1326 Label fast_path(this), slow_path(this); |
1298 BranchIfFastRegExp(context, map, &fast_path, &slow_path); | 1327 BranchIfFastRegExp(context, receiver, map, &fast_path, &slow_path); |
1299 | 1328 |
1300 Bind(&fast_path); | 1329 Bind(&fast_path); |
1301 { | 1330 { |
1302 Label if_didnotmatch(this); | 1331 Label if_didnotmatch(this); |
1303 RegExpPrototypeExecBodyWithoutResult(context, receiver, string, | 1332 RegExpPrototypeExecBodyWithoutResult(context, receiver, string, |
1304 &if_didnotmatch, true); | 1333 &if_didnotmatch, true); |
1305 Return(TrueConstant()); | 1334 Return(TrueConstant()); |
1306 | 1335 |
1307 Bind(&if_didnotmatch); | 1336 Bind(&if_didnotmatch); |
1308 Return(FalseConstant()); | 1337 Return(FalseConstant()); |
(...skipping 314 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1623 // Store the match, growing the fixed array if needed. | 1652 // Store the match, growing the fixed array if needed. |
1624 | 1653 |
1625 array.Push(match); | 1654 array.Push(match); |
1626 | 1655 |
1627 // Advance last index if the match is the empty string. | 1656 // Advance last index if the match is the empty string. |
1628 | 1657 |
1629 Node* const match_length = LoadStringLength(match); | 1658 Node* const match_length = LoadStringLength(match); |
1630 GotoIfNot(SmiEqual(match_length, smi_zero), &loop); | 1659 GotoIfNot(SmiEqual(match_length, smi_zero), &loop); |
1631 | 1660 |
1632 Node* last_index = LoadLastIndex(context, regexp, is_fastpath); | 1661 Node* last_index = LoadLastIndex(context, regexp, is_fastpath); |
1633 | 1662 if (is_fastpath) { |
1634 Callable tolength_callable = CodeFactory::ToLength(isolate); | 1663 CSA_ASSERT(this, TaggedIsPositiveSmi(last_index)); |
1635 last_index = CallStub(tolength_callable, context, last_index); | 1664 } else { |
| 1665 Callable tolength_callable = CodeFactory::ToLength(isolate); |
| 1666 last_index = CallStub(tolength_callable, context, last_index); |
| 1667 } |
1636 | 1668 |
1637 Node* const new_last_index = | 1669 Node* const new_last_index = |
1638 AdvanceStringIndex(string, last_index, is_unicode); | 1670 AdvanceStringIndex(string, last_index, is_unicode); |
1639 | 1671 |
| 1672 if (is_fastpath) { |
| 1673 // On the fast path, we can be certain that lastIndex can never be |
| 1674 // incremented to overflow the Smi range since the maximal string |
| 1675 // length is less than the maximal Smi value. |
| 1676 STATIC_ASSERT(String::kMaxLength < Smi::kMaxValue); |
| 1677 CSA_ASSERT(this, TaggedIsPositiveSmi(new_last_index)); |
| 1678 } |
| 1679 |
1640 StoreLastIndex(context, regexp, new_last_index, is_fastpath); | 1680 StoreLastIndex(context, regexp, new_last_index, is_fastpath); |
1641 | 1681 |
1642 Goto(&loop); | 1682 Goto(&loop); |
1643 } | 1683 } |
1644 } | 1684 } |
1645 | 1685 |
1646 Bind(&out); | 1686 Bind(&out); |
1647 { | 1687 { |
1648 // Wrap the match in a JSArray. | 1688 // Wrap the match in a JSArray. |
1649 | 1689 |
(...skipping 13 matching lines...) Expand all Loading... |
1663 // Ensure {maybe_receiver} is a JSReceiver. | 1703 // Ensure {maybe_receiver} is a JSReceiver. |
1664 Node* const map = ThrowIfNotJSReceiver( | 1704 Node* const map = ThrowIfNotJSReceiver( |
1665 context, maybe_receiver, MessageTemplate::kIncompatibleMethodReceiver, | 1705 context, maybe_receiver, MessageTemplate::kIncompatibleMethodReceiver, |
1666 "RegExp.prototype.@@match"); | 1706 "RegExp.prototype.@@match"); |
1667 Node* const receiver = maybe_receiver; | 1707 Node* const receiver = maybe_receiver; |
1668 | 1708 |
1669 // Convert {maybe_string} to a String. | 1709 // Convert {maybe_string} to a String. |
1670 Node* const string = ToString(context, maybe_string); | 1710 Node* const string = ToString(context, maybe_string); |
1671 | 1711 |
1672 Label fast_path(this), slow_path(this); | 1712 Label fast_path(this), slow_path(this); |
1673 BranchIfFastRegExp(context, map, &fast_path, &slow_path); | 1713 BranchIfFastRegExp(context, receiver, map, &fast_path, &slow_path); |
1674 | 1714 |
1675 Bind(&fast_path); | 1715 Bind(&fast_path); |
1676 RegExpPrototypeMatchBody(context, receiver, string, true); | 1716 RegExpPrototypeMatchBody(context, receiver, string, true); |
1677 | 1717 |
1678 Bind(&slow_path); | 1718 Bind(&slow_path); |
1679 RegExpPrototypeMatchBody(context, receiver, string, false); | 1719 RegExpPrototypeMatchBody(context, receiver, string, false); |
1680 } | 1720 } |
1681 | 1721 |
1682 void RegExpBuiltinsAssembler::RegExpPrototypeSearchBodyFast( | 1722 void RegExpBuiltinsAssembler::RegExpPrototypeSearchBodyFast( |
1683 Node* const context, Node* const regexp, Node* const string) { | 1723 Node* const context, Node* const regexp, Node* const string) { |
(...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1788 // Ensure {maybe_receiver} is a JSReceiver. | 1828 // Ensure {maybe_receiver} is a JSReceiver. |
1789 Node* const map = ThrowIfNotJSReceiver( | 1829 Node* const map = ThrowIfNotJSReceiver( |
1790 context, maybe_receiver, MessageTemplate::kIncompatibleMethodReceiver, | 1830 context, maybe_receiver, MessageTemplate::kIncompatibleMethodReceiver, |
1791 "RegExp.prototype.@@search"); | 1831 "RegExp.prototype.@@search"); |
1792 Node* const receiver = maybe_receiver; | 1832 Node* const receiver = maybe_receiver; |
1793 | 1833 |
1794 // Convert {maybe_string} to a String. | 1834 // Convert {maybe_string} to a String. |
1795 Node* const string = ToString(context, maybe_string); | 1835 Node* const string = ToString(context, maybe_string); |
1796 | 1836 |
1797 Label fast_path(this), slow_path(this); | 1837 Label fast_path(this), slow_path(this); |
1798 BranchIfFastRegExp(context, map, &fast_path, &slow_path); | 1838 BranchIfFastRegExp(context, receiver, map, &fast_path, &slow_path); |
1799 | 1839 |
1800 Bind(&fast_path); | 1840 Bind(&fast_path); |
1801 RegExpPrototypeSearchBodyFast(context, receiver, string); | 1841 RegExpPrototypeSearchBodyFast(context, receiver, string); |
1802 | 1842 |
1803 Bind(&slow_path); | 1843 Bind(&slow_path); |
1804 RegExpPrototypeSearchBodySlow(context, receiver, string); | 1844 RegExpPrototypeSearchBodySlow(context, receiver, string); |
1805 } | 1845 } |
1806 | 1846 |
1807 // Generates the fast path for @@split. {regexp} is an unmodified JSRegExp, | 1847 // Generates the fast path for @@split. {regexp} is an unmodified JSRegExp, |
1808 // {string} is a String, and {limit} is a Smi. | 1848 // {string} is a String, and {limit} is a Smi. |
(...skipping 243 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2052 | 2092 |
2053 // Helper that skips a few initial checks. | 2093 // Helper that skips a few initial checks. |
2054 TF_BUILTIN(RegExpSplit, RegExpBuiltinsAssembler) { | 2094 TF_BUILTIN(RegExpSplit, RegExpBuiltinsAssembler) { |
2055 typedef RegExpSplitDescriptor Descriptor; | 2095 typedef RegExpSplitDescriptor Descriptor; |
2056 | 2096 |
2057 Node* const regexp = Parameter(Descriptor::kReceiver); | 2097 Node* const regexp = Parameter(Descriptor::kReceiver); |
2058 Node* const string = Parameter(Descriptor::kString); | 2098 Node* const string = Parameter(Descriptor::kString); |
2059 Node* const maybe_limit = Parameter(Descriptor::kLimit); | 2099 Node* const maybe_limit = Parameter(Descriptor::kLimit); |
2060 Node* const context = Parameter(Descriptor::kContext); | 2100 Node* const context = Parameter(Descriptor::kContext); |
2061 | 2101 |
2062 CSA_ASSERT(this, IsFastRegExpMap(context, LoadMap(regexp))); | 2102 CSA_ASSERT(this, IsFastRegExpMap(context, regexp, LoadMap(regexp))); |
2063 CSA_ASSERT(this, IsString(string)); | 2103 CSA_ASSERT(this, IsString(string)); |
2064 | 2104 |
2065 // TODO(jgruber): Even if map checks send us to the fast path, we still need | 2105 // TODO(jgruber): Even if map checks send us to the fast path, we still need |
2066 // to verify the constructor property and jump to the slow path if it has | 2106 // to verify the constructor property and jump to the slow path if it has |
2067 // been changed. | 2107 // been changed. |
2068 | 2108 |
2069 // Convert {maybe_limit} to a uint32, capping at the maximal smi value. | 2109 // Convert {maybe_limit} to a uint32, capping at the maximal smi value. |
2070 Variable var_limit(this, MachineRepresentation::kTagged); | 2110 Variable var_limit(this, MachineRepresentation::kTagged, maybe_limit); |
2071 Label if_limitissmimax(this), limit_done(this); | 2111 Label if_limitissmimax(this), limit_done(this), runtime(this); |
2072 | 2112 |
2073 GotoIf(IsUndefined(maybe_limit), &if_limitissmimax); | 2113 GotoIf(IsUndefined(maybe_limit), &if_limitissmimax); |
| 2114 GotoIf(TaggedIsPositiveSmi(maybe_limit), &limit_done); |
2074 | 2115 |
| 2116 Node* const limit = ToUint32(context, maybe_limit); |
2075 { | 2117 { |
2076 Node* const limit = ToUint32(context, maybe_limit); | 2118 // ToUint32(limit) could potentially change the shape of the RegExp |
| 2119 // object. Recheck that we are still on the fast path and bail to runtime |
| 2120 // otherwise. |
| 2121 { |
| 2122 Label next(this); |
| 2123 BranchIfFastRegExp(context, regexp, LoadMap(regexp), &next, &runtime); |
| 2124 Bind(&next); |
| 2125 } |
| 2126 |
2077 GotoIfNot(TaggedIsSmi(limit), &if_limitissmimax); | 2127 GotoIfNot(TaggedIsSmi(limit), &if_limitissmimax); |
2078 | 2128 |
2079 var_limit.Bind(limit); | 2129 var_limit.Bind(limit); |
2080 Goto(&limit_done); | 2130 Goto(&limit_done); |
2081 } | 2131 } |
2082 | 2132 |
2083 Bind(&if_limitissmimax); | 2133 Bind(&if_limitissmimax); |
2084 { | 2134 { |
2085 // TODO(jgruber): In this case, we can probably avoid generation of limit | 2135 // TODO(jgruber): In this case, we can probably avoid generation of limit |
2086 // checks in Generate_RegExpPrototypeSplitBody. | 2136 // checks in Generate_RegExpPrototypeSplitBody. |
2087 var_limit.Bind(SmiConstant(Smi::kMaxValue)); | 2137 var_limit.Bind(SmiConstant(Smi::kMaxValue)); |
2088 Goto(&limit_done); | 2138 Goto(&limit_done); |
2089 } | 2139 } |
2090 | 2140 |
2091 Bind(&limit_done); | 2141 Bind(&limit_done); |
2092 { | 2142 { |
2093 Node* const limit = var_limit.value(); | 2143 Node* const limit = var_limit.value(); |
2094 RegExpPrototypeSplitBody(context, regexp, string, limit); | 2144 RegExpPrototypeSplitBody(context, regexp, string, limit); |
2095 } | 2145 } |
| 2146 |
| 2147 Bind(&runtime); |
| 2148 { |
| 2149 // The runtime call passes in limit to ensure the second ToUint32(limit) |
| 2150 // call is not observable. |
| 2151 CSA_ASSERT(this, IsHeapNumberMap(LoadReceiverMap(limit))); |
| 2152 Return(CallRuntime(Runtime::kRegExpSplit, context, regexp, string, limit)); |
| 2153 } |
2096 } | 2154 } |
2097 | 2155 |
2098 // ES#sec-regexp.prototype-@@split | 2156 // ES#sec-regexp.prototype-@@split |
2099 // RegExp.prototype [ @@split ] ( string, limit ) | 2157 // RegExp.prototype [ @@split ] ( string, limit ) |
2100 TF_BUILTIN(RegExpPrototypeSplit, RegExpBuiltinsAssembler) { | 2158 TF_BUILTIN(RegExpPrototypeSplit, RegExpBuiltinsAssembler) { |
2101 Node* const maybe_receiver = Parameter(0); | 2159 Node* const maybe_receiver = Parameter(0); |
2102 Node* const maybe_string = Parameter(1); | 2160 Node* const maybe_string = Parameter(1); |
2103 Node* const maybe_limit = Parameter(2); | 2161 Node* const maybe_limit = Parameter(2); |
2104 Node* const context = Parameter(5); | 2162 Node* const context = Parameter(5); |
2105 | 2163 |
2106 // Ensure {maybe_receiver} is a JSReceiver. | 2164 // Ensure {maybe_receiver} is a JSReceiver. |
2107 Node* const map = ThrowIfNotJSReceiver( | 2165 Node* const map = ThrowIfNotJSReceiver( |
2108 context, maybe_receiver, MessageTemplate::kIncompatibleMethodReceiver, | 2166 context, maybe_receiver, MessageTemplate::kIncompatibleMethodReceiver, |
2109 "RegExp.prototype.@@split"); | 2167 "RegExp.prototype.@@split"); |
2110 Node* const receiver = maybe_receiver; | 2168 Node* const receiver = maybe_receiver; |
2111 | 2169 |
2112 // Convert {maybe_string} to a String. | 2170 // Convert {maybe_string} to a String. |
2113 Node* const string = ToString(context, maybe_string); | 2171 Node* const string = ToString(context, maybe_string); |
2114 | 2172 |
2115 Label stub(this), runtime(this, Label::kDeferred); | 2173 Label stub(this), runtime(this, Label::kDeferred); |
2116 BranchIfFastRegExp(context, map, &stub, &runtime); | 2174 BranchIfFastRegExp(context, receiver, map, &stub, &runtime); |
2117 | 2175 |
2118 Bind(&stub); | 2176 Bind(&stub); |
2119 Callable split_callable = CodeFactory::RegExpSplit(isolate()); | 2177 Callable split_callable = CodeFactory::RegExpSplit(isolate()); |
2120 Return(CallStub(split_callable, context, receiver, string, maybe_limit)); | 2178 Return(CallStub(split_callable, context, receiver, string, maybe_limit)); |
2121 | 2179 |
2122 Bind(&runtime); | 2180 Bind(&runtime); |
2123 Return(CallRuntime(Runtime::kRegExpSplit, context, receiver, string, | 2181 Return(CallRuntime(Runtime::kRegExpSplit, context, receiver, string, |
2124 maybe_limit)); | 2182 maybe_limit)); |
2125 } | 2183 } |
2126 | 2184 |
(...skipping 305 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2432 | 2490 |
2433 // Helper that skips a few initial checks. | 2491 // Helper that skips a few initial checks. |
2434 TF_BUILTIN(RegExpReplace, RegExpBuiltinsAssembler) { | 2492 TF_BUILTIN(RegExpReplace, RegExpBuiltinsAssembler) { |
2435 typedef RegExpReplaceDescriptor Descriptor; | 2493 typedef RegExpReplaceDescriptor Descriptor; |
2436 | 2494 |
2437 Node* const regexp = Parameter(Descriptor::kReceiver); | 2495 Node* const regexp = Parameter(Descriptor::kReceiver); |
2438 Node* const string = Parameter(Descriptor::kString); | 2496 Node* const string = Parameter(Descriptor::kString); |
2439 Node* const replace_value = Parameter(Descriptor::kReplaceValue); | 2497 Node* const replace_value = Parameter(Descriptor::kReplaceValue); |
2440 Node* const context = Parameter(Descriptor::kContext); | 2498 Node* const context = Parameter(Descriptor::kContext); |
2441 | 2499 |
2442 CSA_ASSERT(this, IsFastRegExpMap(context, LoadMap(regexp))); | 2500 CSA_ASSERT(this, IsFastRegExpMap(context, regexp, LoadMap(regexp))); |
2443 CSA_ASSERT(this, IsString(string)); | 2501 CSA_ASSERT(this, IsString(string)); |
2444 | 2502 |
2445 Label checkreplacestring(this), if_iscallable(this), | 2503 Label checkreplacestring(this), if_iscallable(this), |
2446 runtime(this, Label::kDeferred); | 2504 runtime(this, Label::kDeferred); |
2447 | 2505 |
2448 // 2. Is {replace_value} callable? | 2506 // 2. Is {replace_value} callable? |
2449 GotoIf(TaggedIsSmi(replace_value), &checkreplacestring); | 2507 GotoIf(TaggedIsSmi(replace_value), &checkreplacestring); |
2450 Branch(IsCallableMap(LoadMap(replace_value)), &if_iscallable, | 2508 Branch(IsCallableMap(LoadMap(replace_value)), &if_iscallable, |
2451 &checkreplacestring); | 2509 &checkreplacestring); |
2452 | 2510 |
2453 // 3. Does ToString({replace_value}) contain '$'? | 2511 // 3. Does ToString({replace_value}) contain '$'? |
2454 Bind(&checkreplacestring); | 2512 Bind(&checkreplacestring); |
2455 { | 2513 { |
2456 Callable tostring_callable = CodeFactory::ToString(isolate()); | 2514 Callable tostring_callable = CodeFactory::ToString(isolate()); |
2457 Node* const replace_string = | 2515 Node* const replace_string = |
2458 CallStub(tostring_callable, context, replace_value); | 2516 CallStub(tostring_callable, context, replace_value); |
2459 | 2517 |
| 2518 // ToString(replaceValue) could potentially change the shape of the RegExp |
| 2519 // object. Recheck that we are still on the fast path and bail to runtime |
| 2520 // otherwise. |
| 2521 { |
| 2522 Label next(this); |
| 2523 BranchIfFastRegExp(context, regexp, LoadMap(regexp), &next, &runtime); |
| 2524 Bind(&next); |
| 2525 } |
| 2526 |
2460 Callable indexof_callable = CodeFactory::StringIndexOf(isolate()); | 2527 Callable indexof_callable = CodeFactory::StringIndexOf(isolate()); |
2461 Node* const dollar_string = HeapConstant( | 2528 Node* const dollar_string = HeapConstant( |
2462 isolate()->factory()->LookupSingleCharacterStringFromCode('$')); | 2529 isolate()->factory()->LookupSingleCharacterStringFromCode('$')); |
2463 Node* const dollar_ix = CallStub(indexof_callable, context, replace_string, | 2530 Node* const dollar_ix = CallStub(indexof_callable, context, replace_string, |
2464 dollar_string, SmiConstant(0)); | 2531 dollar_string, SmiConstant(0)); |
2465 GotoIfNot(SmiEqual(dollar_ix, SmiConstant(-1)), &runtime); | 2532 GotoIfNot(SmiEqual(dollar_ix, SmiConstant(-1)), &runtime); |
2466 | 2533 |
2467 Return( | 2534 Return( |
2468 ReplaceSimpleStringFastPath(context, regexp, string, replace_string)); | 2535 ReplaceSimpleStringFastPath(context, regexp, string, replace_string)); |
2469 } | 2536 } |
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2523 context, maybe_receiver, MessageTemplate::kIncompatibleMethodReceiver, | 2590 context, maybe_receiver, MessageTemplate::kIncompatibleMethodReceiver, |
2524 "RegExp.prototype.@@replace"); | 2591 "RegExp.prototype.@@replace"); |
2525 Node* const receiver = maybe_receiver; | 2592 Node* const receiver = maybe_receiver; |
2526 | 2593 |
2527 // Convert {maybe_string} to a String. | 2594 // Convert {maybe_string} to a String. |
2528 Callable tostring_callable = CodeFactory::ToString(isolate()); | 2595 Callable tostring_callable = CodeFactory::ToString(isolate()); |
2529 Node* const string = CallStub(tostring_callable, context, maybe_string); | 2596 Node* const string = CallStub(tostring_callable, context, maybe_string); |
2530 | 2597 |
2531 // Fast-path checks: 1. Is the {receiver} an unmodified JSRegExp instance? | 2598 // Fast-path checks: 1. Is the {receiver} an unmodified JSRegExp instance? |
2532 Label stub(this), runtime(this, Label::kDeferred); | 2599 Label stub(this), runtime(this, Label::kDeferred); |
2533 BranchIfFastRegExp(context, map, &stub, &runtime); | 2600 BranchIfFastRegExp(context, receiver, map, &stub, &runtime); |
2534 | 2601 |
2535 Bind(&stub); | 2602 Bind(&stub); |
2536 Callable replace_callable = CodeFactory::RegExpReplace(isolate()); | 2603 Callable replace_callable = CodeFactory::RegExpReplace(isolate()); |
2537 Return(CallStub(replace_callable, context, receiver, string, replace_value)); | 2604 Return(CallStub(replace_callable, context, receiver, string, replace_value)); |
2538 | 2605 |
2539 Bind(&runtime); | 2606 Bind(&runtime); |
2540 Return(CallRuntime(Runtime::kRegExpReplace, context, receiver, string, | 2607 Return(CallRuntime(Runtime::kRegExpReplace, context, receiver, string, |
2541 replace_value)); | 2608 replace_value)); |
2542 } | 2609 } |
2543 | 2610 |
(...skipping 24 matching lines...) Expand all Loading... |
2568 Bind(&if_matched); | 2635 Bind(&if_matched); |
2569 { | 2636 { |
2570 Node* result = | 2637 Node* result = |
2571 ConstructNewResultFromMatchInfo(context, regexp, match_indices, string); | 2638 ConstructNewResultFromMatchInfo(context, regexp, match_indices, string); |
2572 Return(result); | 2639 Return(result); |
2573 } | 2640 } |
2574 } | 2641 } |
2575 | 2642 |
2576 } // namespace internal | 2643 } // namespace internal |
2577 } // namespace v8 | 2644 } // namespace v8 |
OLD | NEW |