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