| 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 |