OLD | NEW |
1 // Copyright 2016 the V8 project authors. All rights reserved. | 1 // Copyright 2016 the V8 project authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "src/builtins/builtins-utils.h" | 5 #include "src/builtins/builtins-utils.h" |
6 #include "src/builtins/builtins.h" | 6 #include "src/builtins/builtins.h" |
7 | 7 |
8 #include "src/code-factory.h" | 8 #include "src/code-factory.h" |
9 #include "src/regexp/jsregexp.h" | 9 #include "src/regexp/jsregexp.h" |
10 #include "src/regexp/regexp-utils.h" | 10 #include "src/regexp/regexp-utils.h" |
(...skipping 189 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
200 a->HeapConstant(a->isolate()->factory()->lastIndex_string()); | 200 a->HeapConstant(a->isolate()->factory()->lastIndex_string()); |
201 Callable getproperty_callable = CodeFactory::GetProperty(a->isolate()); | 201 Callable getproperty_callable = CodeFactory::GetProperty(a->isolate()); |
202 var_value.Bind(a->CallStub(getproperty_callable, context, regexp, name)); | 202 var_value.Bind(a->CallStub(getproperty_callable, context, regexp, name)); |
203 a->Goto(&out); | 203 a->Goto(&out); |
204 } | 204 } |
205 | 205 |
206 a->Bind(&out); | 206 a->Bind(&out); |
207 return var_value.value(); | 207 return var_value.value(); |
208 } | 208 } |
209 | 209 |
| 210 // The fast-path of StoreLastIndex when regexp is guaranteed to be an unmodified |
| 211 // JSRegExp instance. |
| 212 void FastStoreLastIndex(CodeStubAssembler* a, compiler::Node* context, |
| 213 compiler::Node* regexp, compiler::Node* value) { |
| 214 // Store the in-object field. |
| 215 static const int field_offset = |
| 216 JSRegExp::kSize + JSRegExp::kLastIndexFieldIndex * kPointerSize; |
| 217 a->StoreObjectField(regexp, field_offset, value); |
| 218 } |
| 219 |
210 void StoreLastIndex(CodeStubAssembler* a, compiler::Node* context, | 220 void StoreLastIndex(CodeStubAssembler* a, compiler::Node* context, |
211 compiler::Node* has_initialmap, compiler::Node* regexp, | 221 compiler::Node* has_initialmap, compiler::Node* regexp, |
212 compiler::Node* value) { | 222 compiler::Node* value) { |
213 typedef CodeStubAssembler::Label Label; | 223 typedef CodeStubAssembler::Label Label; |
214 typedef compiler::Node Node; | 224 typedef compiler::Node Node; |
215 | 225 |
216 Label out(a), if_unmodified(a), if_modified(a, Label::kDeferred); | 226 Label out(a), if_unmodified(a), if_modified(a, Label::kDeferred); |
217 a->Branch(has_initialmap, &if_unmodified, &if_modified); | 227 a->Branch(has_initialmap, &if_unmodified, &if_modified); |
218 | 228 |
219 a->Bind(&if_unmodified); | 229 a->Bind(&if_unmodified); |
220 { | 230 { |
221 // Store the in-object field. | 231 FastStoreLastIndex(a, context, regexp, value); |
222 static const int field_offset = | |
223 JSRegExp::kSize + JSRegExp::kLastIndexFieldIndex * kPointerSize; | |
224 a->StoreObjectField(regexp, field_offset, value); | |
225 a->Goto(&out); | 232 a->Goto(&out); |
226 } | 233 } |
227 | 234 |
228 a->Bind(&if_modified); | 235 a->Bind(&if_modified); |
229 { | 236 { |
230 // Store through runtime. | 237 // Store through runtime. |
231 // TODO(ishell): Use SetPropertyStub here once available. | 238 // TODO(ishell): Use SetPropertyStub here once available. |
232 Node* const name = | 239 Node* const name = |
233 a->HeapConstant(a->isolate()->factory()->lastIndex_string()); | 240 a->HeapConstant(a->isolate()->factory()->lastIndex_string()); |
234 Node* const language_mode = a->SmiConstant(Smi::FromInt(STRICT)); | 241 Node* const language_mode = a->SmiConstant(Smi::FromInt(STRICT)); |
(...skipping 212 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
447 a->Return(result); | 454 a->Return(result); |
448 } | 455 } |
449 } | 456 } |
450 } | 457 } |
451 | 458 |
452 namespace { | 459 namespace { |
453 | 460 |
454 compiler::Node* ThrowIfNotJSReceiver(CodeStubAssembler* a, Isolate* isolate, | 461 compiler::Node* ThrowIfNotJSReceiver(CodeStubAssembler* a, Isolate* isolate, |
455 compiler::Node* context, | 462 compiler::Node* context, |
456 compiler::Node* value, | 463 compiler::Node* value, |
| 464 MessageTemplate::Template msg_template, |
457 char const* method_name) { | 465 char const* method_name) { |
458 typedef compiler::Node Node; | 466 typedef compiler::Node Node; |
459 typedef CodeStubAssembler::Label Label; | 467 typedef CodeStubAssembler::Label Label; |
460 typedef CodeStubAssembler::Variable Variable; | 468 typedef CodeStubAssembler::Variable Variable; |
461 | 469 |
462 Label out(a), throw_exception(a, Label::kDeferred); | 470 Label out(a), throw_exception(a, Label::kDeferred); |
463 Variable var_value_map(a, MachineRepresentation::kTagged); | 471 Variable var_value_map(a, MachineRepresentation::kTagged); |
464 | 472 |
465 a->GotoIf(a->TaggedIsSmi(value), &throw_exception); | 473 a->GotoIf(a->TaggedIsSmi(value), &throw_exception); |
466 | 474 |
467 // Load the instance type of the {value}. | 475 // Load the instance type of the {value}. |
468 var_value_map.Bind(a->LoadMap(value)); | 476 var_value_map.Bind(a->LoadMap(value)); |
469 Node* const value_instance_type = | 477 Node* const value_instance_type = |
470 a->LoadMapInstanceType(var_value_map.value()); | 478 a->LoadMapInstanceType(var_value_map.value()); |
471 | 479 |
472 a->Branch(a->IsJSReceiverInstanceType(value_instance_type), &out, | 480 a->Branch(a->IsJSReceiverInstanceType(value_instance_type), &out, |
473 &throw_exception); | 481 &throw_exception); |
474 | 482 |
475 // The {value} is not a compatible receiver for this method. | 483 // The {value} is not a compatible receiver for this method. |
476 a->Bind(&throw_exception); | 484 a->Bind(&throw_exception); |
477 { | 485 { |
478 Node* const message_id = | 486 Node* const message_id = a->SmiConstant(Smi::FromInt(msg_template)); |
479 a->SmiConstant(Smi::FromInt(MessageTemplate::kRegExpNonObject)); | |
480 Node* const method_name_str = a->HeapConstant( | 487 Node* const method_name_str = a->HeapConstant( |
481 isolate->factory()->NewStringFromAsciiChecked(method_name, TENURED)); | 488 isolate->factory()->NewStringFromAsciiChecked(method_name, TENURED)); |
482 | 489 |
483 Callable callable = CodeFactory::ToString(isolate); | 490 Callable callable = CodeFactory::ToString(isolate); |
484 Node* const value_str = a->CallStub(callable, context, value); | 491 Node* const value_str = a->CallStub(callable, context, value); |
485 | 492 |
486 a->CallRuntime(Runtime::kThrowTypeError, context, message_id, | 493 a->CallRuntime(Runtime::kThrowTypeError, context, message_id, |
487 method_name_str, value_str); | 494 method_name_str, value_str); |
488 var_value_map.Bind(a->UndefinedConstant()); | 495 var_value_map.Bind(a->UndefinedConstant()); |
489 a->Goto(&out); // Never reached. | 496 a->Goto(&out); // Never reached. |
(...skipping 11 matching lines...) Expand all Loading... |
501 Node* const native_context = a->LoadNativeContext(context); | 508 Node* const native_context = a->LoadNativeContext(context); |
502 Node* const regexp_fun = | 509 Node* const regexp_fun = |
503 a->LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX); | 510 a->LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX); |
504 Node* const initial_map = | 511 Node* const initial_map = |
505 a->LoadObjectField(regexp_fun, JSFunction::kPrototypeOrInitialMapOffset); | 512 a->LoadObjectField(regexp_fun, JSFunction::kPrototypeOrInitialMapOffset); |
506 Node* const has_initialmap = a->WordEqual(map, initial_map); | 513 Node* const has_initialmap = a->WordEqual(map, initial_map); |
507 | 514 |
508 return has_initialmap; | 515 return has_initialmap; |
509 } | 516 } |
510 | 517 |
| 518 // RegExp fast path implementations rely on unmodified JSRegExp instances. |
| 519 // We use a fairly coarse granularity for this and simply check whether both |
| 520 // the regexp itself is unmodified (i.e. its map has not changed) and its |
| 521 // prototype is unmodified. |
| 522 void BranchIfFastPath(CodeStubAssembler* a, compiler::Node* context, |
| 523 compiler::Node* map, |
| 524 CodeStubAssembler::Label* if_isunmodified, |
| 525 CodeStubAssembler::Label* if_ismodified) { |
| 526 typedef compiler::Node Node; |
| 527 |
| 528 Node* const native_context = a->LoadNativeContext(context); |
| 529 Node* const regexp_fun = |
| 530 a->LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX); |
| 531 Node* const initial_map = |
| 532 a->LoadObjectField(regexp_fun, JSFunction::kPrototypeOrInitialMapOffset); |
| 533 Node* const has_initialmap = a->WordEqual(map, initial_map); |
| 534 |
| 535 a->GotoUnless(has_initialmap, if_ismodified); |
| 536 |
| 537 Node* const initial_proto_initial_map = a->LoadContextElement( |
| 538 native_context, Context::REGEXP_PROTOTYPE_MAP_INDEX); |
| 539 Node* const proto_map = a->LoadMap(a->LoadMapPrototype(map)); |
| 540 Node* const proto_has_initialmap = |
| 541 a->WordEqual(proto_map, initial_proto_initial_map); |
| 542 |
| 543 // TODO(ishell): Update this check once map changes for constant field |
| 544 // tracking are landing. |
| 545 |
| 546 a->Branch(proto_has_initialmap, if_isunmodified, if_ismodified); |
| 547 } |
| 548 |
511 } // namespace | 549 } // namespace |
512 | 550 |
513 void Builtins::Generate_RegExpPrototypeFlagsGetter(CodeStubAssembler* a) { | 551 void Builtins::Generate_RegExpPrototypeFlagsGetter(CodeStubAssembler* a) { |
514 typedef CodeStubAssembler::Variable Variable; | 552 typedef CodeStubAssembler::Variable Variable; |
515 typedef CodeStubAssembler::Label Label; | 553 typedef CodeStubAssembler::Label Label; |
516 typedef compiler::Node Node; | 554 typedef compiler::Node Node; |
517 | 555 |
518 Node* const receiver = a->Parameter(0); | 556 Node* const receiver = a->Parameter(0); |
519 Node* const context = a->Parameter(3); | 557 Node* const context = a->Parameter(3); |
520 | 558 |
521 Isolate* isolate = a->isolate(); | 559 Isolate* isolate = a->isolate(); |
522 Node* const int_zero = a->IntPtrConstant(0); | 560 Node* const int_zero = a->IntPtrConstant(0); |
523 Node* const int_one = a->IntPtrConstant(1); | 561 Node* const int_one = a->IntPtrConstant(1); |
524 | 562 |
525 Node* const map = ThrowIfNotJSReceiver(a, isolate, context, receiver, | 563 Node* const map = ThrowIfNotJSReceiver(a, isolate, context, receiver, |
| 564 MessageTemplate::kRegExpNonObject, |
526 "RegExp.prototype.flags"); | 565 "RegExp.prototype.flags"); |
527 | 566 |
528 Variable var_length(a, MachineType::PointerRepresentation()); | 567 Variable var_length(a, MachineType::PointerRepresentation()); |
529 Variable var_flags(a, MachineType::PointerRepresentation()); | 568 Variable var_flags(a, MachineType::PointerRepresentation()); |
530 | 569 |
531 // First, count the number of characters we will need and check which flags | 570 // First, count the number of characters we will need and check which flags |
532 // are set. | 571 // are set. |
533 | 572 |
534 var_length.Bind(int_zero); | 573 var_length.Bind(int_zero); |
535 | 574 |
(...skipping 276 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
812 "RegExp.prototype.sticky"); | 851 "RegExp.prototype.sticky"); |
813 } | 852 } |
814 | 853 |
815 // ES6 21.2.5.15. | 854 // ES6 21.2.5.15. |
816 void Builtins::Generate_RegExpPrototypeUnicodeGetter(CodeStubAssembler* a) { | 855 void Builtins::Generate_RegExpPrototypeUnicodeGetter(CodeStubAssembler* a) { |
817 Generate_FlagGetter(a, JSRegExp::kUnicode, | 856 Generate_FlagGetter(a, JSRegExp::kUnicode, |
818 v8::Isolate::kRegExpPrototypeUnicodeGetter, | 857 v8::Isolate::kRegExpPrototypeUnicodeGetter, |
819 "RegExp.prototype.unicode"); | 858 "RegExp.prototype.unicode"); |
820 } | 859 } |
821 | 860 |
822 | |
823 // The properties $1..$9 are the first nine capturing substrings of the last | 861 // The properties $1..$9 are the first nine capturing substrings of the last |
824 // successful match, or ''. The function RegExpMakeCaptureGetter will be | 862 // successful match, or ''. The function RegExpMakeCaptureGetter will be |
825 // called with indices from 1 to 9. | 863 // called with indices from 1 to 9. |
826 #define DEFINE_CAPTURE_GETTER(i) \ | 864 #define DEFINE_CAPTURE_GETTER(i) \ |
827 BUILTIN(RegExpCapture##i##Getter) { \ | 865 BUILTIN(RegExpCapture##i##Getter) { \ |
828 HandleScope scope(isolate); \ | 866 HandleScope scope(isolate); \ |
829 return *RegExpUtils::GenericCaptureGetter( \ | 867 return *RegExpUtils::GenericCaptureGetter( \ |
830 isolate, isolate->regexp_last_match_info(), i); \ | 868 isolate, isolate->regexp_last_match_info(), i); \ |
831 } | 869 } |
832 DEFINE_CAPTURE_GETTER(1) | 870 DEFINE_CAPTURE_GETTER(1) |
(...skipping 343 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1176 } | 1214 } |
1177 | 1215 |
1178 { | 1216 { |
1179 Handle<String> substr = | 1217 Handle<String> substr = |
1180 factory->NewSubString(string, current_index, start_match); | 1218 factory->NewSubString(string, current_index, start_match); |
1181 elems = FixedArray::SetAndGrow(elems, num_elems++, substr); | 1219 elems = FixedArray::SetAndGrow(elems, num_elems++, substr); |
1182 } | 1220 } |
1183 | 1221 |
1184 if (num_elems == limit) break; | 1222 if (num_elems == limit) break; |
1185 | 1223 |
1186 // TODO(jgruber): Refactor GetLastMatchInfo methods to take an input | |
1187 // argument. | |
1188 Handle<Object> num_captures_obj = | 1224 Handle<Object> num_captures_obj = |
1189 JSReceiver::GetElement(isolate, match_indices, | 1225 JSReceiver::GetElement(isolate, match_indices, |
1190 RegExpImpl::kLastCaptureCount) | 1226 RegExpImpl::kLastCaptureCount) |
1191 .ToHandleChecked(); | 1227 .ToHandleChecked(); |
1192 const int match_indices_len = Handle<Smi>::cast(num_captures_obj)->value() + | 1228 const int match_indices_len = Handle<Smi>::cast(num_captures_obj)->value() + |
1193 RegExpImpl::kFirstCapture; | 1229 RegExpImpl::kFirstCapture; |
1194 | 1230 |
1195 for (int i = RegExpImpl::kFirstCapture + 2; i < match_indices_len;) { | 1231 for (int i = RegExpImpl::kFirstCapture + 2; i < match_indices_len;) { |
1196 Handle<Object> start_obj = | 1232 Handle<Object> start_obj = |
1197 JSReceiver::GetElement(isolate, match_indices, i++).ToHandleChecked(); | 1233 JSReceiver::GetElement(isolate, match_indices, i++).ToHandleChecked(); |
(...skipping 223 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1421 | 1457 |
1422 { | 1458 { |
1423 Handle<String> substr = | 1459 Handle<String> substr = |
1424 factory->NewSubString(string, prev_string_index, length); | 1460 factory->NewSubString(string, prev_string_index, length); |
1425 elems = FixedArray::SetAndGrow(elems, num_elems++, substr); | 1461 elems = FixedArray::SetAndGrow(elems, num_elems++, substr); |
1426 } | 1462 } |
1427 | 1463 |
1428 return *NewJSArrayWithElements(isolate, elems, num_elems); | 1464 return *NewJSArrayWithElements(isolate, elems, num_elems); |
1429 } | 1465 } |
1430 | 1466 |
| 1467 namespace { |
| 1468 |
| 1469 compiler::Node* ReplaceFastPath(CodeStubAssembler* a, compiler::Node* context, |
| 1470 compiler::Node* regexp, |
| 1471 compiler::Node* subject_string, |
| 1472 compiler::Node* replace_string) { |
| 1473 // The fast path is reached only if {receiver} is an unmodified |
| 1474 // JSRegExp instance, {replace_value} is non-callable, and |
| 1475 // ToString({replace_value}) does not contain '$', i.e. we're doing a simple |
| 1476 // string replacement. |
| 1477 |
| 1478 typedef CodeStubAssembler::Variable Variable; |
| 1479 typedef CodeStubAssembler::Label Label; |
| 1480 typedef compiler::Node Node; |
| 1481 |
| 1482 Isolate* const isolate = a->isolate(); |
| 1483 |
| 1484 Node* const null = a->NullConstant(); |
| 1485 Node* const int_zero = a->IntPtrConstant(0); |
| 1486 Node* const smi_zero = a->SmiConstant(Smi::kZero); |
| 1487 |
| 1488 Label out(a); |
| 1489 Variable var_result(a, MachineRepresentation::kTagged); |
| 1490 |
| 1491 // Load the last match info. |
| 1492 Node* const native_context = a->LoadNativeContext(context); |
| 1493 Node* const last_match_info = a->LoadContextElement( |
| 1494 native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX); |
| 1495 |
| 1496 // Is {regexp} global? |
| 1497 Label if_isglobal(a), if_isnonglobal(a); |
| 1498 Node* const flags = a->LoadObjectField(regexp, JSRegExp::kFlagsOffset); |
| 1499 Node* const is_global = |
| 1500 a->WordAnd(a->SmiUntag(flags), a->IntPtrConstant(JSRegExp::kGlobal)); |
| 1501 a->Branch(a->WordEqual(is_global, int_zero), &if_isnonglobal, &if_isglobal); |
| 1502 |
| 1503 a->Bind(&if_isglobal); |
| 1504 { |
| 1505 // Hand off global regexps to runtime. |
| 1506 FastStoreLastIndex(a, context, regexp, smi_zero); |
| 1507 Node* const result = |
| 1508 a->CallRuntime(Runtime::kStringReplaceGlobalRegExpWithString, context, |
| 1509 subject_string, regexp, replace_string, last_match_info); |
| 1510 var_result.Bind(result); |
| 1511 a->Goto(&out); |
| 1512 } |
| 1513 |
| 1514 a->Bind(&if_isnonglobal); |
| 1515 { |
| 1516 // Run exec, then manually construct the resulting string. |
| 1517 Callable exec_callable = CodeFactory::RegExpExec(isolate); |
| 1518 Node* const match_indices = |
| 1519 a->CallStub(exec_callable, context, regexp, subject_string, smi_zero, |
| 1520 last_match_info); |
| 1521 |
| 1522 Label if_matched(a), if_didnotmatch(a); |
| 1523 a->Branch(a->WordEqual(match_indices, null), &if_didnotmatch, &if_matched); |
| 1524 |
| 1525 a->Bind(&if_didnotmatch); |
| 1526 { |
| 1527 FastStoreLastIndex(a, context, regexp, smi_zero); |
| 1528 var_result.Bind(subject_string); |
| 1529 a->Goto(&out); |
| 1530 } |
| 1531 |
| 1532 a->Bind(&if_matched); |
| 1533 { |
| 1534 Node* const match_elements = a->LoadElements(match_indices); |
| 1535 CodeStubAssembler::ParameterMode mode = |
| 1536 CodeStubAssembler::INTPTR_PARAMETERS; |
| 1537 |
| 1538 Node* const subject_start = smi_zero; |
| 1539 Node* const match_start = a->LoadFixedArrayElement( |
| 1540 match_elements, a->IntPtrConstant(RegExpImpl::kFirstCapture), 0, |
| 1541 mode); |
| 1542 Node* const match_end = a->LoadFixedArrayElement( |
| 1543 match_elements, a->IntPtrConstant(RegExpImpl::kFirstCapture + 1), 0, |
| 1544 mode); |
| 1545 Node* const subject_end = a->LoadStringLength(subject_string); |
| 1546 |
| 1547 Label if_replaceisempty(a), if_replaceisnotempty(a); |
| 1548 Node* const replace_length = a->LoadStringLength(replace_string); |
| 1549 a->Branch(a->SmiEqual(replace_length, smi_zero), &if_replaceisempty, |
| 1550 &if_replaceisnotempty); |
| 1551 |
| 1552 a->Bind(&if_replaceisempty); |
| 1553 { |
| 1554 // TODO(jgruber): We could skip many of the checks that using SubString |
| 1555 // here entails. |
| 1556 |
| 1557 Node* const first_part = |
| 1558 a->SubString(context, subject_string, subject_start, match_start); |
| 1559 Node* const second_part = |
| 1560 a->SubString(context, subject_string, match_end, subject_end); |
| 1561 |
| 1562 Node* const result = a->StringConcat(context, first_part, second_part); |
| 1563 var_result.Bind(result); |
| 1564 a->Goto(&out); |
| 1565 } |
| 1566 |
| 1567 a->Bind(&if_replaceisnotempty); |
| 1568 { |
| 1569 Node* const first_part = |
| 1570 a->SubString(context, subject_string, subject_start, match_start); |
| 1571 Node* const second_part = replace_string; |
| 1572 Node* const third_part = |
| 1573 a->SubString(context, subject_string, match_end, subject_end); |
| 1574 |
| 1575 Node* result = a->StringConcat(context, first_part, second_part); |
| 1576 result = a->StringConcat(context, result, third_part); |
| 1577 |
| 1578 var_result.Bind(result); |
| 1579 a->Goto(&out); |
| 1580 } |
| 1581 } |
| 1582 } |
| 1583 |
| 1584 a->Bind(&out); |
| 1585 return var_result.value(); |
| 1586 } |
| 1587 |
| 1588 } // namespace |
| 1589 |
| 1590 // ES#sec-regexp.prototype-@@replace |
| 1591 // RegExp.prototype [ @@replace ] ( string, replaceValue ) |
| 1592 void Builtins::Generate_RegExpPrototypeReplace(CodeStubAssembler* a) { |
| 1593 typedef CodeStubAssembler::Label Label; |
| 1594 typedef compiler::Node Node; |
| 1595 |
| 1596 Isolate* const isolate = a->isolate(); |
| 1597 |
| 1598 Node* const maybe_receiver = a->Parameter(0); |
| 1599 Node* const maybe_string = a->Parameter(1); |
| 1600 Node* const replace_value = a->Parameter(2); |
| 1601 Node* const context = a->Parameter(5); |
| 1602 |
| 1603 Node* const int_zero = a->IntPtrConstant(0); |
| 1604 |
| 1605 // Ensure {receiver} is a JSReceiver. |
| 1606 Node* const map = |
| 1607 ThrowIfNotJSReceiver(a, isolate, context, maybe_receiver, |
| 1608 MessageTemplate::kIncompatibleMethodReceiver, |
| 1609 "RegExp.prototype.@@replace"); |
| 1610 Node* const receiver = maybe_receiver; |
| 1611 |
| 1612 // Convert {maybe_string} to a String. |
| 1613 Callable tostring_callable = CodeFactory::ToString(isolate); |
| 1614 Node* const string = a->CallStub(tostring_callable, context, maybe_string); |
| 1615 |
| 1616 // Fast-path checks: 1. Is the {receiver} an unmodified JSRegExp instance? |
| 1617 Label checkreplacecallable(a), runtime(a, Label::kDeferred), fastpath(a); |
| 1618 BranchIfFastPath(a, context, map, &checkreplacecallable, &runtime); |
| 1619 |
| 1620 a->Bind(&checkreplacecallable); |
| 1621 Node* const regexp = receiver; |
| 1622 |
| 1623 // 2. Is {replace_value} callable? |
| 1624 Label checkreplacestring(a); |
| 1625 a->GotoIf(a->TaggedIsSmi(replace_value), &checkreplacestring); |
| 1626 |
| 1627 Node* const replace_value_map = a->LoadMap(replace_value); |
| 1628 a->Branch( |
| 1629 a->Word32Equal(a->Word32And(a->LoadMapBitField(replace_value_map), |
| 1630 a->Int32Constant(1 << Map::kIsCallable)), |
| 1631 a->Int32Constant(0)), |
| 1632 &checkreplacestring, &runtime); |
| 1633 |
| 1634 // 3. Does ToString({replace_value}) contain '$'? |
| 1635 a->Bind(&checkreplacestring); |
| 1636 { |
| 1637 Node* const replace_string = |
| 1638 a->CallStub(tostring_callable, context, replace_value); |
| 1639 |
| 1640 Node* const dollar_char = a->IntPtrConstant('$'); |
| 1641 Node* const smi_minusone = a->SmiConstant(Smi::FromInt(-1)); |
| 1642 a->GotoUnless(a->SmiEqual(a->StringIndexOfChar(context, replace_string, |
| 1643 dollar_char, int_zero), |
| 1644 smi_minusone), |
| 1645 &runtime); |
| 1646 |
| 1647 a->Return(ReplaceFastPath(a, context, regexp, string, replace_string)); |
| 1648 } |
| 1649 |
| 1650 a->Bind(&runtime); |
| 1651 { |
| 1652 Node* const result = a->CallRuntime(Runtime::kRegExpReplace, context, |
| 1653 receiver, string, replace_value); |
| 1654 a->Return(result); |
| 1655 } |
| 1656 } |
| 1657 |
1431 } // namespace internal | 1658 } // namespace internal |
1432 } // namespace v8 | 1659 } // namespace v8 |
OLD | NEW |