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 1428 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1439 Handle<String> substr = | 1439 Handle<String> substr = |
1440 factory->NewSubString(string, prev_string_index, length); | 1440 factory->NewSubString(string, prev_string_index, length); |
1441 elems = FixedArray::SetAndGrow(elems, num_elems++, substr); | 1441 elems = FixedArray::SetAndGrow(elems, num_elems++, substr); |
1442 } | 1442 } |
1443 | 1443 |
1444 return *NewJSArrayWithElements(isolate, elems, num_elems); | 1444 return *NewJSArrayWithElements(isolate, elems, num_elems); |
1445 } | 1445 } |
1446 | 1446 |
1447 namespace { | 1447 namespace { |
1448 | 1448 |
1449 compiler::Node* ReplaceFastPath(CodeStubAssembler* a, compiler::Node* context, | 1449 compiler::Node* ReplaceGlobalCallableFastPath( |
1450 compiler::Node* regexp, | 1450 CodeStubAssembler* a, compiler::Node* context, compiler::Node* regexp, |
1451 compiler::Node* subject_string, | 1451 compiler::Node* subject_string, compiler::Node* replace_callable) { |
1452 compiler::Node* replace_string) { | 1452 // The fast path is reached only if {receiver} is a global unmodified |
| 1453 // JSRegExp instance and {replace_callable} is callable. |
| 1454 |
| 1455 typedef CodeStubAssembler::Variable Variable; |
| 1456 typedef CodeStubAssembler::Label Label; |
| 1457 typedef compiler::Node Node; |
| 1458 |
| 1459 Isolate* const isolate = a->isolate(); |
| 1460 |
| 1461 Node* const null = a->NullConstant(); |
| 1462 Node* const undefined = a->UndefinedConstant(); |
| 1463 Node* const int_zero = a->IntPtrConstant(0); |
| 1464 Node* const int_one = a->IntPtrConstant(1); |
| 1465 Node* const smi_zero = a->SmiConstant(Smi::kZero); |
| 1466 |
| 1467 Node* const native_context = a->LoadNativeContext(context); |
| 1468 |
| 1469 Label out(a); |
| 1470 Variable var_result(a, MachineRepresentation::kTagged); |
| 1471 |
| 1472 // Set last index to 0. |
| 1473 FastStoreLastIndex(a, context, regexp, smi_zero); |
| 1474 |
| 1475 // Allocate {result_array}. |
| 1476 Node* result_array; |
| 1477 { |
| 1478 ElementsKind kind = FAST_ELEMENTS; |
| 1479 Node* const array_map = a->LoadJSArrayElementsMap(kind, native_context); |
| 1480 Node* const capacity = a->IntPtrConstant(16); |
| 1481 Node* const length = smi_zero; |
| 1482 Node* const allocation_site = nullptr; |
| 1483 CodeStubAssembler::ParameterMode capacity_mode = |
| 1484 CodeStubAssembler::INTPTR_PARAMETERS; |
| 1485 |
| 1486 result_array = a->AllocateJSArray(kind, array_map, capacity, length, |
| 1487 allocation_site, capacity_mode); |
| 1488 } |
| 1489 |
| 1490 // Call into runtime for RegExpExecMultiple. |
| 1491 Node* last_match_info = a->LoadContextElement( |
| 1492 native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX); |
| 1493 Node* const res = |
| 1494 a->CallRuntime(Runtime::kRegExpExecMultiple, context, regexp, |
| 1495 subject_string, last_match_info, result_array); |
| 1496 |
| 1497 // Reset last index to 0. |
| 1498 FastStoreLastIndex(a, context, regexp, smi_zero); |
| 1499 |
| 1500 // If no matches, return the subject string. |
| 1501 var_result.Bind(subject_string); |
| 1502 a->GotoIf(a->WordEqual(res, null), &out); |
| 1503 |
| 1504 // Reload last match info since it might have changed. |
| 1505 last_match_info = a->LoadContextElement( |
| 1506 native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX); |
| 1507 |
| 1508 Node* const res_length = a->LoadJSArrayLength(res); |
| 1509 Node* const res_elems = a->LoadElements(res); |
| 1510 a->AssertInstanceType(res_elems, FIXED_ARRAY_TYPE); |
| 1511 |
| 1512 CodeStubAssembler::ParameterMode mode = CodeStubAssembler::INTPTR_PARAMETERS; |
| 1513 Node* const num_capture_registers = a->LoadFixedArrayElement( |
| 1514 last_match_info, |
| 1515 a->IntPtrConstant(RegExpMatchInfo::kNumberOfCapturesIndex), 0, mode); |
| 1516 |
| 1517 Label if_hasexplicitcaptures(a), if_noexplicitcaptures(a), create_result(a); |
| 1518 a->Branch(a->SmiEqual(num_capture_registers, a->SmiConstant(Smi::FromInt(2))), |
| 1519 &if_noexplicitcaptures, &if_hasexplicitcaptures); |
| 1520 |
| 1521 a->Bind(&if_noexplicitcaptures); |
| 1522 { |
| 1523 // If the number of captures is two then there are no explicit captures in |
| 1524 // the regexp, just the implicit capture that captures the whole match. In |
| 1525 // this case we can simplify quite a bit and end up with something faster. |
| 1526 // The builder will consist of some integers that indicate slices of the |
| 1527 // input string and some replacements that were returned from the replace |
| 1528 // function. |
| 1529 |
| 1530 Variable var_match_start(a, MachineRepresentation::kTagged); |
| 1531 var_match_start.Bind(smi_zero); |
| 1532 |
| 1533 Node* const end = a->SmiUntag(res_length); |
| 1534 Variable var_i(a, MachineType::PointerRepresentation()); |
| 1535 var_i.Bind(int_zero); |
| 1536 |
| 1537 Variable* vars[] = {&var_i, &var_match_start}; |
| 1538 Label loop(a, 2, vars); |
| 1539 a->Goto(&loop); |
| 1540 a->Bind(&loop); |
| 1541 { |
| 1542 Node* const i = var_i.value(); |
| 1543 a->GotoUnless(a->IntPtrLessThan(i, end), &create_result); |
| 1544 |
| 1545 CodeStubAssembler::ParameterMode mode = |
| 1546 CodeStubAssembler::INTPTR_PARAMETERS; |
| 1547 Node* const elem = a->LoadFixedArrayElement(res_elems, i, 0, mode); |
| 1548 |
| 1549 Label if_issmi(a), if_isstring(a), loop_epilogue(a); |
| 1550 a->Branch(a->TaggedIsSmi(elem), &if_issmi, &if_isstring); |
| 1551 |
| 1552 a->Bind(&if_issmi); |
| 1553 { |
| 1554 // Integers represent slices of the original string. |
| 1555 Label if_isnegativeorzero(a), if_ispositive(a); |
| 1556 a->BranchIfSmiLessThanOrEqual(elem, smi_zero, &if_isnegativeorzero, |
| 1557 &if_ispositive); |
| 1558 |
| 1559 a->Bind(&if_ispositive); |
| 1560 { |
| 1561 Node* const int_elem = a->SmiUntag(elem); |
| 1562 Node* const new_match_start = |
| 1563 a->IntPtrAdd(a->WordShr(int_elem, a->IntPtrConstant(11)), |
| 1564 a->WordAnd(int_elem, a->IntPtrConstant(0x7ff))); |
| 1565 var_match_start.Bind(a->SmiTag(new_match_start)); |
| 1566 a->Goto(&loop_epilogue); |
| 1567 } |
| 1568 |
| 1569 a->Bind(&if_isnegativeorzero); |
| 1570 { |
| 1571 Node* const next_i = a->IntPtrAdd(i, int_one); |
| 1572 var_i.Bind(next_i); |
| 1573 |
| 1574 Node* const next_elem = |
| 1575 a->LoadFixedArrayElement(res_elems, next_i, 0, mode); |
| 1576 |
| 1577 Node* const new_match_start = a->SmiSub(next_elem, elem); |
| 1578 var_match_start.Bind(new_match_start); |
| 1579 a->Goto(&loop_epilogue); |
| 1580 } |
| 1581 } |
| 1582 |
| 1583 a->Bind(&if_isstring); |
| 1584 { |
| 1585 a->Assert(a->IsStringInstanceType(a->LoadInstanceType(elem))); |
| 1586 |
| 1587 Callable call_callable = CodeFactory::Call(isolate); |
| 1588 Node* const replacement_obj = |
| 1589 a->CallJS(call_callable, context, replace_callable, undefined, elem, |
| 1590 var_match_start.value(), subject_string); |
| 1591 |
| 1592 Node* const replacement_str = a->ToString(context, replacement_obj); |
| 1593 a->StoreFixedArrayElement(res_elems, i, replacement_str); |
| 1594 |
| 1595 Node* const elem_length = a->LoadStringLength(elem); |
| 1596 Node* const new_match_start = |
| 1597 a->SmiAdd(var_match_start.value(), elem_length); |
| 1598 var_match_start.Bind(new_match_start); |
| 1599 |
| 1600 a->Goto(&loop_epilogue); |
| 1601 } |
| 1602 |
| 1603 a->Bind(&loop_epilogue); |
| 1604 { |
| 1605 var_i.Bind(a->IntPtrAdd(var_i.value(), int_one)); |
| 1606 a->Goto(&loop); |
| 1607 } |
| 1608 } |
| 1609 } |
| 1610 |
| 1611 a->Bind(&if_hasexplicitcaptures); |
| 1612 { |
| 1613 CodeStubAssembler::ParameterMode mode = |
| 1614 CodeStubAssembler::INTPTR_PARAMETERS; |
| 1615 |
| 1616 Node* const from = int_zero; |
| 1617 Node* const to = a->SmiUntag(res_length); |
| 1618 const int increment = 1; |
| 1619 |
| 1620 a->BuildFastLoop( |
| 1621 MachineType::PointerRepresentation(), from, to, |
| 1622 [res_elems, isolate, native_context, context, undefined, |
| 1623 replace_callable, mode](CodeStubAssembler* a, Node* index) { |
| 1624 Node* const elem = |
| 1625 a->LoadFixedArrayElement(res_elems, index, 0, mode); |
| 1626 |
| 1627 Label do_continue(a); |
| 1628 a->GotoIf(a->TaggedIsSmi(elem), &do_continue); |
| 1629 |
| 1630 // elem must be an Array. |
| 1631 // Use the apply argument as backing for global RegExp properties. |
| 1632 |
| 1633 a->AssertInstanceType(elem, JS_ARRAY_TYPE); |
| 1634 |
| 1635 // TODO(jgruber): Remove indirection through Call->ReflectApply. |
| 1636 Callable call_callable = CodeFactory::Call(isolate); |
| 1637 Node* const reflect_apply = a->LoadContextElement( |
| 1638 native_context, Context::REFLECT_APPLY_INDEX); |
| 1639 |
| 1640 Node* const replacement_obj = |
| 1641 a->CallJS(call_callable, context, reflect_apply, undefined, |
| 1642 replace_callable, undefined, elem); |
| 1643 |
| 1644 // Overwrite the i'th element in the results with the string we got |
| 1645 // back from the callback function. |
| 1646 |
| 1647 Node* const replacement_str = a->ToString(context, replacement_obj); |
| 1648 a->StoreFixedArrayElement(res_elems, index, replacement_str, |
| 1649 UPDATE_WRITE_BARRIER, mode); |
| 1650 |
| 1651 a->Goto(&do_continue); |
| 1652 a->Bind(&do_continue); |
| 1653 }, |
| 1654 increment, CodeStubAssembler::IndexAdvanceMode::kPost); |
| 1655 |
| 1656 a->Goto(&create_result); |
| 1657 } |
| 1658 |
| 1659 a->Bind(&create_result); |
| 1660 { |
| 1661 Node* const result = a->CallRuntime(Runtime::kStringBuilderConcat, context, |
| 1662 res, res_length, subject_string); |
| 1663 var_result.Bind(result); |
| 1664 a->Goto(&out); |
| 1665 } |
| 1666 |
| 1667 a->Bind(&out); |
| 1668 return var_result.value(); |
| 1669 } |
| 1670 |
| 1671 compiler::Node* ReplaceSimpleStringFastPath(CodeStubAssembler* a, |
| 1672 compiler::Node* context, |
| 1673 compiler::Node* regexp, |
| 1674 compiler::Node* subject_string, |
| 1675 compiler::Node* replace_string) { |
1453 // The fast path is reached only if {receiver} is an unmodified | 1676 // The fast path is reached only if {receiver} is an unmodified |
1454 // JSRegExp instance, {replace_value} is non-callable, and | 1677 // JSRegExp instance, {replace_value} is non-callable, and |
1455 // ToString({replace_value}) does not contain '$', i.e. we're doing a simple | 1678 // ToString({replace_value}) does not contain '$', i.e. we're doing a simple |
1456 // string replacement. | 1679 // string replacement. |
1457 | 1680 |
1458 typedef CodeStubAssembler::Variable Variable; | 1681 typedef CodeStubAssembler::Variable Variable; |
1459 typedef CodeStubAssembler::Label Label; | 1682 typedef CodeStubAssembler::Label Label; |
1460 typedef compiler::Node Node; | 1683 typedef compiler::Node Node; |
1461 | 1684 |
1462 Isolate* const isolate = a->isolate(); | 1685 Isolate* const isolate = a->isolate(); |
(...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1573 typedef compiler::Node Node; | 1796 typedef compiler::Node Node; |
1574 | 1797 |
1575 Isolate* const isolate = a->isolate(); | 1798 Isolate* const isolate = a->isolate(); |
1576 | 1799 |
1577 Node* const maybe_receiver = a->Parameter(0); | 1800 Node* const maybe_receiver = a->Parameter(0); |
1578 Node* const maybe_string = a->Parameter(1); | 1801 Node* const maybe_string = a->Parameter(1); |
1579 Node* const replace_value = a->Parameter(2); | 1802 Node* const replace_value = a->Parameter(2); |
1580 Node* const context = a->Parameter(5); | 1803 Node* const context = a->Parameter(5); |
1581 | 1804 |
1582 Node* const int_zero = a->IntPtrConstant(0); | 1805 Node* const int_zero = a->IntPtrConstant(0); |
1583 Node* const smi_zero = a->SmiConstant(Smi::kZero); | |
1584 | 1806 |
1585 // Ensure {receiver} is a JSReceiver. | 1807 // Ensure {receiver} is a JSReceiver. |
1586 Node* const map = | 1808 Node* const map = |
1587 ThrowIfNotJSReceiver(a, isolate, context, maybe_receiver, | 1809 ThrowIfNotJSReceiver(a, isolate, context, maybe_receiver, |
1588 MessageTemplate::kIncompatibleMethodReceiver, | 1810 MessageTemplate::kIncompatibleMethodReceiver, |
1589 "RegExp.prototype.@@replace"); | 1811 "RegExp.prototype.@@replace"); |
1590 Node* const receiver = maybe_receiver; | 1812 Node* const receiver = maybe_receiver; |
1591 | 1813 |
1592 // Convert {maybe_string} to a String. | 1814 // Convert {maybe_string} to a String. |
1593 Callable tostring_callable = CodeFactory::ToString(isolate); | 1815 Callable tostring_callable = CodeFactory::ToString(isolate); |
1594 Node* const string = a->CallStub(tostring_callable, context, maybe_string); | 1816 Node* const string = a->CallStub(tostring_callable, context, maybe_string); |
1595 | 1817 |
1596 // Fast-path checks: 1. Is the {receiver} an unmodified JSRegExp instance? | 1818 // Fast-path checks: 1. Is the {receiver} an unmodified JSRegExp instance? |
1597 Label checkreplacecallable(a), runtime(a, Label::kDeferred), fastpath(a); | 1819 Label checkreplacecallable(a), runtime(a, Label::kDeferred), fastpath(a); |
1598 BranchIfFastPath(a, context, map, &checkreplacecallable, &runtime); | 1820 BranchIfFastPath(a, context, map, &checkreplacecallable, &runtime); |
1599 | 1821 |
1600 a->Bind(&checkreplacecallable); | 1822 a->Bind(&checkreplacecallable); |
1601 Node* const regexp = receiver; | 1823 Node* const regexp = receiver; |
1602 | 1824 |
1603 // 2. Is {replace_value} callable? | 1825 // 2. Is {replace_value} callable? |
1604 Label checkreplacestring(a), if_iscallable(a, Label::kDeferred); | 1826 Label checkreplacestring(a), if_iscallable(a); |
1605 a->GotoIf(a->TaggedIsSmi(replace_value), &checkreplacestring); | 1827 a->GotoIf(a->TaggedIsSmi(replace_value), &checkreplacestring); |
1606 | 1828 |
1607 Node* const replace_value_map = a->LoadMap(replace_value); | 1829 Node* const replace_value_map = a->LoadMap(replace_value); |
1608 a->Branch( | 1830 a->Branch( |
1609 a->Word32Equal(a->Word32And(a->LoadMapBitField(replace_value_map), | 1831 a->Word32Equal(a->Word32And(a->LoadMapBitField(replace_value_map), |
1610 a->Int32Constant(1 << Map::kIsCallable)), | 1832 a->Int32Constant(1 << Map::kIsCallable)), |
1611 a->Int32Constant(0)), | 1833 a->Int32Constant(0)), |
1612 &checkreplacestring, &if_iscallable); | 1834 &checkreplacestring, &if_iscallable); |
1613 | 1835 |
1614 // 3. Does ToString({replace_value}) contain '$'? | 1836 // 3. Does ToString({replace_value}) contain '$'? |
1615 a->Bind(&checkreplacestring); | 1837 a->Bind(&checkreplacestring); |
1616 { | 1838 { |
1617 Node* const replace_string = | 1839 Node* const replace_string = |
1618 a->CallStub(tostring_callable, context, replace_value); | 1840 a->CallStub(tostring_callable, context, replace_value); |
1619 | 1841 |
1620 Node* const dollar_char = a->IntPtrConstant('$'); | 1842 Node* const dollar_char = a->IntPtrConstant('$'); |
1621 Node* const smi_minusone = a->SmiConstant(Smi::FromInt(-1)); | 1843 Node* const smi_minusone = a->SmiConstant(Smi::FromInt(-1)); |
1622 a->GotoUnless(a->SmiEqual(a->StringIndexOfChar(context, replace_string, | 1844 a->GotoUnless(a->SmiEqual(a->StringIndexOfChar(context, replace_string, |
1623 dollar_char, int_zero), | 1845 dollar_char, int_zero), |
1624 smi_minusone), | 1846 smi_minusone), |
1625 &runtime); | 1847 &runtime); |
1626 | 1848 |
1627 a->Return(ReplaceFastPath(a, context, regexp, string, replace_string)); | 1849 a->Return(ReplaceSimpleStringFastPath(a, context, regexp, string, |
| 1850 replace_string)); |
1628 } | 1851 } |
1629 | 1852 |
1630 // {regexp} is unmodified and {replace_value} is callable. | 1853 // {regexp} is unmodified and {replace_value} is callable. |
1631 a->Bind(&if_iscallable); | 1854 a->Bind(&if_iscallable); |
1632 { | 1855 { |
1633 Node* const replace_callable = replace_value; | 1856 Node* const replace_callable = replace_value; |
1634 | 1857 |
1635 // Check if the {regexp} is global. | 1858 // Check if the {regexp} is global. |
1636 Label if_isglobal(a), if_isnotglobal(a); | 1859 Label if_isglobal(a), if_isnotglobal(a); |
1637 Node* const is_global = FastFlagGetter(a, regexp, JSRegExp::kGlobal); | 1860 Node* const is_global = FastFlagGetter(a, regexp, JSRegExp::kGlobal); |
1638 a->Branch(is_global, &if_isglobal, &if_isnotglobal); | 1861 a->Branch(is_global, &if_isglobal, &if_isnotglobal); |
1639 | 1862 |
1640 a->Bind(&if_isglobal); | 1863 a->Bind(&if_isglobal); |
1641 { | 1864 { |
1642 FastStoreLastIndex(a, context, regexp, smi_zero); | 1865 Node* const result = ReplaceGlobalCallableFastPath( |
1643 Node* const result = | 1866 a, context, regexp, string, replace_callable); |
1644 a->CallRuntime(Runtime::kStringReplaceGlobalRegExpWithFunction, | |
1645 context, string, regexp, replace_callable); | |
1646 a->Return(result); | 1867 a->Return(result); |
1647 } | 1868 } |
1648 | 1869 |
1649 a->Bind(&if_isnotglobal); | 1870 a->Bind(&if_isnotglobal); |
1650 { | 1871 { |
1651 Node* const result = | 1872 Node* const result = |
1652 a->CallRuntime(Runtime::kStringReplaceNonGlobalRegExpWithFunction, | 1873 a->CallRuntime(Runtime::kStringReplaceNonGlobalRegExpWithFunction, |
1653 context, string, regexp, replace_callable); | 1874 context, string, regexp, replace_callable); |
1654 a->Return(result); | 1875 a->Return(result); |
1655 } | 1876 } |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1695 a->Bind(&if_matched); | 1916 a->Bind(&if_matched); |
1696 { | 1917 { |
1697 Node* result = ConstructNewResultFromMatchInfo(isolate, a, context, | 1918 Node* result = ConstructNewResultFromMatchInfo(isolate, a, context, |
1698 match_indices, string); | 1919 match_indices, string); |
1699 a->Return(result); | 1920 a->Return(result); |
1700 } | 1921 } |
1701 } | 1922 } |
1702 | 1923 |
1703 } // namespace internal | 1924 } // namespace internal |
1704 } // namespace v8 | 1925 } // namespace v8 |
OLD | NEW |