Chromium Code Reviews| 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 456 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 467 a->Return(result); | 467 a->Return(result); |
| 468 } | 468 } |
| 469 } | 469 } |
| 470 } | 470 } |
| 471 | 471 |
| 472 namespace { | 472 namespace { |
| 473 | 473 |
| 474 compiler::Node* ThrowIfNotJSReceiver(CodeStubAssembler* a, Isolate* isolate, | 474 compiler::Node* ThrowIfNotJSReceiver(CodeStubAssembler* a, Isolate* isolate, |
| 475 compiler::Node* context, | 475 compiler::Node* context, |
| 476 compiler::Node* value, | 476 compiler::Node* value, |
| 477 MessageTemplate::Template msg_template, | |
| 477 char const* method_name) { | 478 char const* method_name) { |
| 478 typedef compiler::Node Node; | 479 typedef compiler::Node Node; |
| 479 typedef CodeStubAssembler::Label Label; | 480 typedef CodeStubAssembler::Label Label; |
| 480 typedef CodeStubAssembler::Variable Variable; | 481 typedef CodeStubAssembler::Variable Variable; |
| 481 | 482 |
| 482 Label out(a), throw_exception(a, Label::kDeferred); | 483 Label out(a), throw_exception(a, Label::kDeferred); |
| 483 Variable var_value_map(a, MachineRepresentation::kTagged); | 484 Variable var_value_map(a, MachineRepresentation::kTagged); |
| 484 | 485 |
| 485 a->GotoIf(a->WordIsSmi(value), &throw_exception); | 486 a->GotoIf(a->WordIsSmi(value), &throw_exception); |
| 486 | 487 |
| 487 // Load the instance type of the {value}. | 488 // Load the instance type of the {value}. |
| 488 var_value_map.Bind(a->LoadMap(value)); | 489 var_value_map.Bind(a->LoadMap(value)); |
| 489 Node* const value_instance_type = | 490 Node* const value_instance_type = |
| 490 a->LoadMapInstanceType(var_value_map.value()); | 491 a->LoadMapInstanceType(var_value_map.value()); |
| 491 | 492 |
| 492 a->Branch(a->IsJSReceiverInstanceType(value_instance_type), &out, | 493 a->Branch(a->IsJSReceiverInstanceType(value_instance_type), &out, |
| 493 &throw_exception); | 494 &throw_exception); |
| 494 | 495 |
| 495 // The {value} is not a compatible receiver for this method. | 496 // The {value} is not a compatible receiver for this method. |
| 496 a->Bind(&throw_exception); | 497 a->Bind(&throw_exception); |
| 497 { | 498 { |
| 498 Node* const message_id = | 499 Node* const message_id = a->SmiConstant(Smi::FromInt(msg_template)); |
| 499 a->SmiConstant(Smi::FromInt(MessageTemplate::kRegExpNonObject)); | |
| 500 Node* const method_name_str = a->HeapConstant( | 500 Node* const method_name_str = a->HeapConstant( |
| 501 isolate->factory()->NewStringFromAsciiChecked(method_name, TENURED)); | 501 isolate->factory()->NewStringFromAsciiChecked(method_name, TENURED)); |
| 502 | 502 |
| 503 Callable callable = CodeFactory::ToString(isolate); | 503 Callable callable = CodeFactory::ToString(isolate); |
| 504 Node* const value_str = a->CallStub(callable, context, value); | 504 Node* const value_str = a->CallStub(callable, context, value); |
| 505 | 505 |
| 506 a->CallRuntime(Runtime::kThrowTypeError, context, message_id, | 506 a->CallRuntime(Runtime::kThrowTypeError, context, message_id, |
| 507 method_name_str, value_str); | 507 method_name_str, value_str); |
| 508 var_value_map.Bind(a->UndefinedConstant()); | 508 var_value_map.Bind(a->UndefinedConstant()); |
| 509 a->Goto(&out); // Never reached. | 509 a->Goto(&out); // Never reached. |
| (...skipping 11 matching lines...) Expand all Loading... | |
| 521 Node* const native_context = a->LoadNativeContext(context); | 521 Node* const native_context = a->LoadNativeContext(context); |
| 522 Node* const regexp_fun = | 522 Node* const regexp_fun = |
| 523 a->LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX); | 523 a->LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX); |
| 524 Node* const initial_map = | 524 Node* const initial_map = |
| 525 a->LoadObjectField(regexp_fun, JSFunction::kPrototypeOrInitialMapOffset); | 525 a->LoadObjectField(regexp_fun, JSFunction::kPrototypeOrInitialMapOffset); |
| 526 Node* const has_initialmap = a->WordEqual(map, initial_map); | 526 Node* const has_initialmap = a->WordEqual(map, initial_map); |
| 527 | 527 |
| 528 return has_initialmap; | 528 return has_initialmap; |
| 529 } | 529 } |
| 530 | 530 |
| 531 // RegExp fast path implementations rely on unmodified JSRegExp instances. | |
| 532 // We use a fairly coarse granularity for this and simply check whether both | |
| 533 // the regexp itself is unmodified (i.e. its map has not changed) and its | |
| 534 // prototype is unmodified. | |
| 535 void BranchIfFastPath(CodeStubAssembler* a, compiler::Node* context, | |
| 536 compiler::Node* map, | |
| 537 CodeStubAssembler::Label* if_isunmodified, | |
| 538 CodeStubAssembler::Label* if_ismodified) { | |
| 539 typedef compiler::Node Node; | |
| 540 | |
| 541 Node* const native_context = a->LoadNativeContext(context); | |
| 542 Node* const regexp_fun = | |
| 543 a->LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX); | |
| 544 Node* const initial_map = | |
| 545 a->LoadObjectField(regexp_fun, JSFunction::kPrototypeOrInitialMapOffset); | |
| 546 Node* const has_initialmap = a->WordEqual(map, initial_map); | |
| 547 | |
| 548 a->GotoUnless(has_initialmap, if_ismodified); | |
| 549 | |
| 550 Node* const initial_proto_initial_map = a->LoadContextElement( | |
| 551 native_context, Context::REGEXP_PROTOTYPE_MAP_INDEX); | |
| 552 Node* const proto_map = a->LoadMap(a->LoadMapPrototype(map)); | |
| 553 Node* const proto_has_initialmap = | |
| 554 a->WordEqual(proto_map, initial_proto_initial_map); | |
|
Yang
2016/10/11 13:26:35
If we modify a property on the regexp prototype, d
jgruber
2016/10/13 07:39:44
Added.
| |
| 555 | |
| 556 // TODO(ishell): Update this check once map changes for constant field | |
| 557 // tracking are landing. | |
| 558 | |
| 559 a->Branch(proto_has_initialmap, if_isunmodified, if_ismodified); | |
| 560 } | |
| 561 | |
| 531 } // namespace | 562 } // namespace |
| 532 | 563 |
| 533 void Builtins::Generate_RegExpPrototypeFlagsGetter(CodeStubAssembler* a) { | 564 void Builtins::Generate_RegExpPrototypeFlagsGetter(CodeStubAssembler* a) { |
| 534 typedef CodeStubAssembler::Variable Variable; | 565 typedef CodeStubAssembler::Variable Variable; |
| 535 typedef CodeStubAssembler::Label Label; | 566 typedef CodeStubAssembler::Label Label; |
| 536 typedef compiler::Node Node; | 567 typedef compiler::Node Node; |
| 537 | 568 |
| 538 Node* const receiver = a->Parameter(0); | 569 Node* const receiver = a->Parameter(0); |
| 539 Node* const context = a->Parameter(3); | 570 Node* const context = a->Parameter(3); |
| 540 | 571 |
| 541 Isolate* isolate = a->isolate(); | 572 Isolate* isolate = a->isolate(); |
| 542 Node* const int_zero = a->IntPtrConstant(0); | 573 Node* const int_zero = a->IntPtrConstant(0); |
| 543 Node* const int_one = a->IntPtrConstant(1); | 574 Node* const int_one = a->IntPtrConstant(1); |
| 544 | 575 |
| 545 Node* const map = ThrowIfNotJSReceiver(a, isolate, context, receiver, | 576 Node* const map = ThrowIfNotJSReceiver(a, isolate, context, receiver, |
| 577 MessageTemplate::kRegExpNonObject, | |
| 546 "RegExp.prototype.flags"); | 578 "RegExp.prototype.flags"); |
| 547 | 579 |
| 548 Variable var_length(a, MachineType::PointerRepresentation()); | 580 Variable var_length(a, MachineType::PointerRepresentation()); |
| 549 Variable var_flags(a, MachineType::PointerRepresentation()); | 581 Variable var_flags(a, MachineType::PointerRepresentation()); |
| 550 | 582 |
| 551 // First, count the number of characters we will need and check which flags | 583 // First, count the number of characters we will need and check which flags |
| 552 // are set. | 584 // are set. |
| 553 | 585 |
| 554 var_length.Bind(int_zero); | 586 var_length.Bind(int_zero); |
| 555 | 587 |
| (...skipping 276 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 832 "RegExp.prototype.sticky"); | 864 "RegExp.prototype.sticky"); |
| 833 } | 865 } |
| 834 | 866 |
| 835 // ES6 21.2.5.15. | 867 // ES6 21.2.5.15. |
| 836 void Builtins::Generate_RegExpPrototypeUnicodeGetter(CodeStubAssembler* a) { | 868 void Builtins::Generate_RegExpPrototypeUnicodeGetter(CodeStubAssembler* a) { |
| 837 Generate_FlagGetter(a, JSRegExp::kUnicode, | 869 Generate_FlagGetter(a, JSRegExp::kUnicode, |
| 838 v8::Isolate::kRegExpPrototypeUnicodeGetter, | 870 v8::Isolate::kRegExpPrototypeUnicodeGetter, |
| 839 "RegExp.prototype.unicode"); | 871 "RegExp.prototype.unicode"); |
| 840 } | 872 } |
| 841 | 873 |
| 842 | |
| 843 // The properties $1..$9 are the first nine capturing substrings of the last | 874 // The properties $1..$9 are the first nine capturing substrings of the last |
| 844 // successful match, or ''. The function RegExpMakeCaptureGetter will be | 875 // successful match, or ''. The function RegExpMakeCaptureGetter will be |
| 845 // called with indices from 1 to 9. | 876 // called with indices from 1 to 9. |
| 846 #define DEFINE_CAPTURE_GETTER(i) \ | 877 #define DEFINE_CAPTURE_GETTER(i) \ |
| 847 BUILTIN(RegExpCapture##i##Getter) { \ | 878 BUILTIN(RegExpCapture##i##Getter) { \ |
| 848 HandleScope scope(isolate); \ | 879 HandleScope scope(isolate); \ |
| 849 return *RegExpUtils::GenericCaptureGetter( \ | 880 return *RegExpUtils::GenericCaptureGetter( \ |
| 850 isolate, isolate->regexp_last_match_info(), i); \ | 881 isolate, isolate->regexp_last_match_info(), i); \ |
| 851 } | 882 } |
| 852 DEFINE_CAPTURE_GETTER(1) | 883 DEFINE_CAPTURE_GETTER(1) |
| (...skipping 336 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1189 } | 1220 } |
| 1190 | 1221 |
| 1191 { | 1222 { |
| 1192 Handle<String> substr = | 1223 Handle<String> substr = |
| 1193 factory->NewSubString(string, current_index, start_match); | 1224 factory->NewSubString(string, current_index, start_match); |
| 1194 elems = FixedArray::SetAndGrow(elems, num_elems++, substr); | 1225 elems = FixedArray::SetAndGrow(elems, num_elems++, substr); |
| 1195 } | 1226 } |
| 1196 | 1227 |
| 1197 if (num_elems == limit) break; | 1228 if (num_elems == limit) break; |
| 1198 | 1229 |
| 1199 // TODO(jgruber): Refactor GetLastMatchInfo methods to take an input | |
| 1200 // argument. | |
| 1201 Handle<Object> num_captures_obj = | 1230 Handle<Object> num_captures_obj = |
| 1202 JSReceiver::GetElement(isolate, match_indices, | 1231 JSReceiver::GetElement(isolate, match_indices, |
| 1203 RegExpImpl::kLastCaptureCount) | 1232 RegExpImpl::kLastCaptureCount) |
| 1204 .ToHandleChecked(); | 1233 .ToHandleChecked(); |
| 1205 const int match_indices_len = Handle<Smi>::cast(num_captures_obj)->value() + | 1234 const int match_indices_len = Handle<Smi>::cast(num_captures_obj)->value() + |
| 1206 RegExpImpl::kFirstCapture; | 1235 RegExpImpl::kFirstCapture; |
| 1207 | 1236 |
| 1208 for (int i = RegExpImpl::kFirstCapture + 2; i < match_indices_len;) { | 1237 for (int i = RegExpImpl::kFirstCapture + 2; i < match_indices_len;) { |
| 1209 Handle<Object> start_obj = | 1238 Handle<Object> start_obj = |
| 1210 JSReceiver::GetElement(isolate, match_indices, i++).ToHandleChecked(); | 1239 JSReceiver::GetElement(isolate, match_indices, i++).ToHandleChecked(); |
| (...skipping 221 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1432 Handle<String> substr = | 1461 Handle<String> substr = |
| 1433 factory->NewSubString(string, prev_string_index, length); | 1462 factory->NewSubString(string, prev_string_index, length); |
| 1434 elems = FixedArray::SetAndGrow(elems, num_elems++, substr); | 1463 elems = FixedArray::SetAndGrow(elems, num_elems++, substr); |
| 1435 } | 1464 } |
| 1436 | 1465 |
| 1437 out: | 1466 out: |
| 1438 elems->Shrink(num_elems); | 1467 elems->Shrink(num_elems); |
| 1439 return *factory->NewJSArrayWithElements(elems); | 1468 return *factory->NewJSArrayWithElements(elems); |
| 1440 } | 1469 } |
| 1441 | 1470 |
| 1471 namespace { | |
| 1472 | |
| 1473 compiler::Node* ReplaceFastPath(CodeStubAssembler* a, compiler::Node* context, | |
| 1474 compiler::Node* regexp, | |
| 1475 compiler::Node* subject_string, | |
| 1476 compiler::Node* replace_string) { | |
| 1477 // The fast path is reached only if {receiver} is an unmodified | |
| 1478 // JSRegExp instance, {replace_value} is non-callable, and | |
| 1479 // ToString({replace_value}) does not contain '$', i.e. we're doing a simple | |
| 1480 // string replacement. | |
| 1481 | |
| 1482 typedef CodeStubAssembler::Variable Variable; | |
| 1483 typedef CodeStubAssembler::Label Label; | |
| 1484 typedef compiler::Node Node; | |
| 1485 | |
| 1486 Isolate* const isolate = a->isolate(); | |
| 1487 | |
| 1488 Node* const null = a->NullConstant(); | |
| 1489 Node* const int_zero = a->IntPtrConstant(0); | |
| 1490 Node* const smi_zero = a->SmiConstant(Smi::FromInt(0)); | |
| 1491 | |
| 1492 Label out(a); | |
| 1493 Variable var_result(a, MachineRepresentation::kTagged); | |
| 1494 | |
| 1495 // At this point, we know that {regexp} has an initial map. | |
| 1496 // TODO(jgruber): Refactor to not require this node. | |
| 1497 Node* const has_initialmap = a->WordEqual(int_zero, int_zero); // True. | |
|
Yang
2016/10/11 13:26:35
This would be the same as a->TrueConstant. Let's i
jgruber
2016/10/13 07:39:44
Done.
| |
| 1498 | |
| 1499 // Load the last match info. | |
| 1500 Node* const native_context = a->LoadNativeContext(context); | |
| 1501 Node* const last_match_info = a->LoadContextElement( | |
| 1502 native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX); | |
| 1503 | |
| 1504 // Is {regexp} global? | |
| 1505 Label if_isglobal(a), if_isnonglobal(a); | |
| 1506 Node* const flags = a->LoadObjectField(regexp, JSRegExp::kFlagsOffset); | |
| 1507 Node* const is_global = | |
| 1508 a->WordAnd(a->SmiUntag(flags), a->IntPtrConstant(JSRegExp::kGlobal)); | |
| 1509 a->Branch(a->WordEqual(is_global, int_zero), &if_isnonglobal, &if_isglobal); | |
| 1510 | |
| 1511 a->Bind(&if_isglobal); | |
| 1512 { | |
| 1513 // Hand off global regexps to runtime. | |
| 1514 StoreLastIndex(a, context, has_initialmap, regexp, smi_zero); | |
| 1515 Node* const result = | |
| 1516 a->CallRuntime(Runtime::kStringReplaceGlobalRegExpWithString, context, | |
| 1517 subject_string, regexp, replace_string, last_match_info); | |
| 1518 var_result.Bind(result); | |
| 1519 a->Goto(&out); | |
| 1520 } | |
| 1521 | |
| 1522 a->Bind(&if_isnonglobal); | |
| 1523 { | |
| 1524 // Run exec, then manually construct the resulting string. | |
| 1525 Callable exec_callable = CodeFactory::RegExpExec(isolate); | |
| 1526 Node* const match_indices = | |
| 1527 a->CallStub(exec_callable, context, regexp, subject_string, smi_zero, | |
| 1528 last_match_info); | |
| 1529 | |
| 1530 Label if_matched(a), if_didnotmatch(a); | |
| 1531 a->Branch(a->WordEqual(match_indices, null), &if_didnotmatch, &if_matched); | |
| 1532 | |
| 1533 a->Bind(&if_didnotmatch); | |
| 1534 { | |
| 1535 StoreLastIndex(a, context, has_initialmap, regexp, smi_zero); | |
| 1536 | |
| 1537 var_result.Bind(subject_string); | |
| 1538 a->Goto(&out); | |
| 1539 } | |
| 1540 | |
| 1541 a->Bind(&if_matched); | |
| 1542 { | |
| 1543 Node* const match_elements = a->LoadElements(match_indices); | |
| 1544 CodeStubAssembler::ParameterMode mode = | |
| 1545 CodeStubAssembler::INTPTR_PARAMETERS; | |
| 1546 | |
| 1547 Node* const subject_start = smi_zero; | |
| 1548 Node* const match_start = a->LoadFixedArrayElement( | |
| 1549 match_elements, a->IntPtrConstant(RegExpImpl::kFirstCapture), 0, | |
| 1550 mode); | |
| 1551 Node* const match_end = a->LoadFixedArrayElement( | |
| 1552 match_elements, a->IntPtrConstant(RegExpImpl::kFirstCapture + 1), 0, | |
| 1553 mode); | |
| 1554 Node* const subject_end = a->LoadStringLength(subject_string); | |
| 1555 | |
| 1556 Label if_replaceisempty(a), if_replaceisnotempty(a); | |
| 1557 Node* const replace_length = a->LoadStringLength(replace_string); | |
| 1558 a->Branch(a->SmiEqual(replace_length, smi_zero), &if_replaceisempty, | |
| 1559 &if_replaceisnotempty); | |
| 1560 | |
| 1561 a->Bind(&if_replaceisempty); | |
| 1562 { | |
| 1563 // TODO(jgruber): We could skip many of the checks that using SubString | |
| 1564 // here entails. | |
| 1565 | |
| 1566 Node* const first_part = | |
| 1567 a->SubString(context, subject_string, subject_start, match_start); | |
| 1568 Node* const second_part = | |
| 1569 a->SubString(context, subject_string, match_end, subject_end); | |
| 1570 | |
| 1571 Node* const result = a->StringConcat(context, first_part, second_part); | |
| 1572 var_result.Bind(result); | |
| 1573 a->Goto(&out); | |
| 1574 } | |
| 1575 | |
| 1576 a->Bind(&if_replaceisnotempty); | |
| 1577 { | |
| 1578 Node* const first_part = | |
| 1579 a->SubString(context, subject_string, subject_start, match_start); | |
| 1580 Node* const second_part = replace_string; | |
| 1581 Node* const third_part = | |
| 1582 a->SubString(context, subject_string, match_end, subject_end); | |
| 1583 | |
| 1584 Node* result = a->StringConcat(context, first_part, second_part); | |
| 1585 result = a->StringConcat(context, result, third_part); | |
| 1586 | |
| 1587 var_result.Bind(result); | |
| 1588 a->Goto(&out); | |
| 1589 } | |
| 1590 } | |
| 1591 } | |
| 1592 | |
| 1593 a->Bind(&out); | |
| 1594 return var_result.value(); | |
| 1595 } | |
| 1596 | |
| 1597 } // namespace | |
| 1598 | |
| 1599 // ES#sec-regexp.prototype-@@replace | |
| 1600 // RegExp.prototype [ @@replace ] ( string, replaceValue ) | |
| 1601 void Builtins::Generate_RegExpPrototypeReplace(CodeStubAssembler* a) { | |
| 1602 typedef CodeStubAssembler::Label Label; | |
| 1603 typedef compiler::Node Node; | |
| 1604 | |
| 1605 Isolate* const isolate = a->isolate(); | |
| 1606 | |
| 1607 Node* const maybe_receiver = a->Parameter(0); | |
| 1608 Node* const maybe_string = a->Parameter(1); | |
| 1609 Node* const replace_value = a->Parameter(2); | |
| 1610 Node* const context = a->Parameter(5); | |
| 1611 | |
| 1612 Node* const int_zero = a->IntPtrConstant(0); | |
| 1613 | |
| 1614 // Ensure {receiver} is a JSReceiver. | |
| 1615 Node* const map = | |
| 1616 ThrowIfNotJSReceiver(a, isolate, context, maybe_receiver, | |
| 1617 MessageTemplate::kIncompatibleMethodReceiver, | |
| 1618 "RegExp.prototype.@@replace"); | |
| 1619 Node* const receiver = maybe_receiver; | |
| 1620 | |
| 1621 // Convert {maybe_string} to a String. | |
| 1622 Callable tostring_callable = CodeFactory::ToString(isolate); | |
| 1623 Node* const string = a->CallStub(tostring_callable, context, maybe_string); | |
| 1624 | |
| 1625 // Fast-path checks: 1. Is the {receiver} an unmodified JSRegExp instance? | |
| 1626 Label checkreplacecallable(a), runtime(a, Label::kDeferred), fastpath(a); | |
| 1627 BranchIfFastPath(a, context, map, &checkreplacecallable, &runtime); | |
| 1628 | |
| 1629 a->Bind(&checkreplacecallable); | |
| 1630 Node* const regexp = receiver; | |
| 1631 | |
| 1632 // 2. Is {replace_value} callable? | |
| 1633 Label checkreplacestring(a); | |
| 1634 a->GotoIf(a->WordIsSmi(replace_value), &checkreplacestring); | |
| 1635 | |
| 1636 Node* const replace_value_map = a->LoadMap(replace_value); | |
| 1637 a->Branch( | |
| 1638 a->Word32Equal(a->Word32And(a->LoadMapBitField(replace_value_map), | |
| 1639 a->Int32Constant(1 << Map::kIsCallable)), | |
| 1640 a->Int32Constant(0)), | |
| 1641 &checkreplacestring, &runtime); | |
| 1642 | |
| 1643 // 3. Does ToString({replace_value}) contain '$'? | |
| 1644 a->Bind(&checkreplacestring); | |
| 1645 { | |
| 1646 Node* const replace_string = | |
| 1647 a->CallStub(tostring_callable, context, replace_value); | |
| 1648 | |
| 1649 Node* const dollar_char = a->IntPtrConstant('$'); | |
| 1650 Node* const smi_minusone = a->SmiConstant(Smi::FromInt(-1)); | |
| 1651 a->GotoUnless(a->SmiEqual(a->StringIndexOfChar(context, replace_string, | |
| 1652 dollar_char, int_zero), | |
| 1653 smi_minusone), | |
| 1654 &runtime); | |
| 1655 | |
| 1656 a->Return(ReplaceFastPath(a, context, regexp, string, replace_string)); | |
| 1657 } | |
| 1658 | |
| 1659 a->Bind(&runtime); | |
| 1660 { | |
| 1661 Node* const result = a->CallRuntime(Runtime::kRegExpReplace, context, | |
| 1662 receiver, string, replace_value); | |
| 1663 a->Return(result); | |
| 1664 } | |
| 1665 } | |
| 1666 | |
| 1442 } // namespace internal | 1667 } // namespace internal |
| 1443 } // namespace v8 | 1668 } // namespace v8 |
| OLD | NEW |