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-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 404 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 415 LoadObjectField(regexp_fun, JSFunction::kPrototypeOrInitialMapOffset); | 415 LoadObjectField(regexp_fun, JSFunction::kPrototypeOrInitialMapOffset); |
| 416 Node* const has_initialmap = WordEqual(map, initial_map); | 416 Node* const has_initialmap = WordEqual(map, initial_map); |
| 417 | 417 |
| 418 return has_initialmap; | 418 return has_initialmap; |
| 419 } | 419 } |
| 420 | 420 |
| 421 // RegExp fast path implementations rely on unmodified JSRegExp instances. | 421 // RegExp fast path implementations rely on unmodified JSRegExp instances. |
| 422 // We use a fairly coarse granularity for this and simply check whether both | 422 // We use a fairly coarse granularity for this and simply check whether both |
| 423 // the regexp itself is unmodified (i.e. its map has not changed) and its | 423 // the regexp itself is unmodified (i.e. its map has not changed) and its |
| 424 // prototype is unmodified. | 424 // prototype is unmodified. |
| 425 void RegExpBuiltinsAssembler::BranchIfFastRegExp(Node* context, Node* map, | 425 Node* RegExpBuiltinsAssembler::IsFastRegExpMap(Node* context, Node* map) { |
| 426 Label* if_isunmodified, | 426 Label out(this); |
| 427 Label* if_ismodified) { | 427 Variable var_result(this, MachineRepresentation::kWord32, Int32Constant(0)); |
|
Igor Sheludko
2017/02/03 09:17:54
Although the use cases look nicer with this change
jgruber
2017/02/03 10:29:28
Done.
| |
| 428 | |
| 428 Node* const native_context = LoadNativeContext(context); | 429 Node* const native_context = LoadNativeContext(context); |
| 429 Node* const regexp_fun = | 430 Node* const regexp_fun = |
| 430 LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX); | 431 LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX); |
| 431 Node* const initial_map = | 432 Node* const initial_map = |
| 432 LoadObjectField(regexp_fun, JSFunction::kPrototypeOrInitialMapOffset); | 433 LoadObjectField(regexp_fun, JSFunction::kPrototypeOrInitialMapOffset); |
| 433 Node* const has_initialmap = WordEqual(map, initial_map); | 434 Node* const has_initialmap = WordEqual(map, initial_map); |
| 434 | 435 |
| 435 GotoUnless(has_initialmap, if_ismodified); | 436 GotoUnless(has_initialmap, &out); |
| 436 | 437 |
| 437 Node* const initial_proto_initial_map = | 438 Node* const initial_proto_initial_map = |
| 438 LoadContextElement(native_context, Context::REGEXP_PROTOTYPE_MAP_INDEX); | 439 LoadContextElement(native_context, Context::REGEXP_PROTOTYPE_MAP_INDEX); |
| 439 Node* const proto_map = LoadMap(LoadMapPrototype(map)); | 440 Node* const proto_map = LoadMap(LoadMapPrototype(map)); |
| 440 Node* const proto_has_initialmap = | 441 Node* const proto_has_initialmap = |
| 441 WordEqual(proto_map, initial_proto_initial_map); | 442 WordEqual(proto_map, initial_proto_initial_map); |
| 442 | 443 |
| 444 var_result.Bind(proto_has_initialmap); | |
| 445 Goto(&out); | |
| 446 | |
| 443 // TODO(ishell): Update this check once map changes for constant field | 447 // TODO(ishell): Update this check once map changes for constant field |
| 444 // tracking are landing. | 448 // tracking are landing. |
| 445 | 449 |
| 446 Branch(proto_has_initialmap, if_isunmodified, if_ismodified); | 450 Bind(&out); |
| 451 return var_result.value(); | |
| 447 } | 452 } |
| 448 | 453 |
| 449 void RegExpBuiltinsAssembler::BranchIfFastRegExpResult(Node* context, Node* map, | 454 void RegExpBuiltinsAssembler::BranchIfFastRegExpResult(Node* context, Node* map, |
| 450 Label* if_isunmodified, | 455 Label* if_isunmodified, |
| 451 Label* if_ismodified) { | 456 Label* if_ismodified) { |
| 452 Node* const native_context = LoadNativeContext(context); | 457 Node* const native_context = LoadNativeContext(context); |
| 453 Node* const initial_regexp_result_map = | 458 Node* const initial_regexp_result_map = |
| 454 LoadContextElement(native_context, Context::REGEXP_RESULT_MAP_INDEX); | 459 LoadContextElement(native_context, Context::REGEXP_RESULT_MAP_INDEX); |
| 455 | 460 |
| 456 Branch(WordEqual(map, initial_regexp_result_map), if_isunmodified, | 461 Branch(WordEqual(map, initial_regexp_result_map), if_isunmodified, |
| (...skipping 736 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1193 Node* RegExpBuiltinsAssembler::RegExpExec(Node* context, Node* regexp, | 1198 Node* RegExpBuiltinsAssembler::RegExpExec(Node* context, Node* regexp, |
| 1194 Node* string) { | 1199 Node* string) { |
| 1195 Isolate* isolate = this->isolate(); | 1200 Isolate* isolate = this->isolate(); |
| 1196 | 1201 |
| 1197 Node* const null = NullConstant(); | 1202 Node* const null = NullConstant(); |
| 1198 | 1203 |
| 1199 Variable var_result(this, MachineRepresentation::kTagged); | 1204 Variable var_result(this, MachineRepresentation::kTagged); |
| 1200 Label out(this), if_isfastpath(this), if_isslowpath(this); | 1205 Label out(this), if_isfastpath(this), if_isslowpath(this); |
| 1201 | 1206 |
| 1202 Node* const map = LoadMap(regexp); | 1207 Node* const map = LoadMap(regexp); |
| 1203 BranchIfFastRegExp(context, map, &if_isfastpath, &if_isslowpath); | 1208 Branch(IsFastRegExpMap(context, map), &if_isfastpath, &if_isslowpath); |
| 1204 | 1209 |
| 1205 Bind(&if_isfastpath); | 1210 Bind(&if_isfastpath); |
| 1206 { | 1211 { |
| 1207 Node* const result = RegExpPrototypeExecBody(context, regexp, string, true); | 1212 Node* const result = RegExpPrototypeExecBody(context, regexp, string, true); |
| 1208 var_result.Bind(result); | 1213 var_result.Bind(result); |
| 1209 Goto(&out); | 1214 Goto(&out); |
| 1210 } | 1215 } |
| 1211 | 1216 |
| 1212 Bind(&if_isslowpath); | 1217 Bind(&if_isslowpath); |
| 1213 { | 1218 { |
| (...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1267 // Ensure {maybe_receiver} is a JSReceiver. | 1272 // Ensure {maybe_receiver} is a JSReceiver. |
| 1268 Node* const map = ThrowIfNotJSReceiver( | 1273 Node* const map = ThrowIfNotJSReceiver( |
| 1269 context, maybe_receiver, MessageTemplate::kIncompatibleMethodReceiver, | 1274 context, maybe_receiver, MessageTemplate::kIncompatibleMethodReceiver, |
| 1270 "RegExp.prototype.test"); | 1275 "RegExp.prototype.test"); |
| 1271 Node* const receiver = maybe_receiver; | 1276 Node* const receiver = maybe_receiver; |
| 1272 | 1277 |
| 1273 // Convert {maybe_string} to a String. | 1278 // Convert {maybe_string} to a String. |
| 1274 Node* const string = ToString(context, maybe_string); | 1279 Node* const string = ToString(context, maybe_string); |
| 1275 | 1280 |
| 1276 Label fast_path(this), slow_path(this); | 1281 Label fast_path(this), slow_path(this); |
| 1277 BranchIfFastRegExp(context, map, &fast_path, &slow_path); | 1282 Branch(IsFastRegExpMap(context, map), &fast_path, &slow_path); |
| 1278 | 1283 |
| 1279 Bind(&fast_path); | 1284 Bind(&fast_path); |
| 1280 { | 1285 { |
| 1281 Label if_didnotmatch(this); | 1286 Label if_didnotmatch(this); |
| 1282 RegExpPrototypeExecBodyWithoutResult(context, receiver, string, | 1287 RegExpPrototypeExecBodyWithoutResult(context, receiver, string, |
| 1283 &if_didnotmatch, true); | 1288 &if_didnotmatch, true); |
| 1284 Return(TrueConstant()); | 1289 Return(TrueConstant()); |
| 1285 | 1290 |
| 1286 Bind(&if_didnotmatch); | 1291 Bind(&if_didnotmatch); |
| 1287 Return(FalseConstant()); | 1292 Return(FalseConstant()); |
| (...skipping 354 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1642 // Ensure {maybe_receiver} is a JSReceiver. | 1647 // Ensure {maybe_receiver} is a JSReceiver. |
| 1643 Node* const map = ThrowIfNotJSReceiver( | 1648 Node* const map = ThrowIfNotJSReceiver( |
| 1644 context, maybe_receiver, MessageTemplate::kIncompatibleMethodReceiver, | 1649 context, maybe_receiver, MessageTemplate::kIncompatibleMethodReceiver, |
| 1645 "RegExp.prototype.@@match"); | 1650 "RegExp.prototype.@@match"); |
| 1646 Node* const receiver = maybe_receiver; | 1651 Node* const receiver = maybe_receiver; |
| 1647 | 1652 |
| 1648 // Convert {maybe_string} to a String. | 1653 // Convert {maybe_string} to a String. |
| 1649 Node* const string = ToString(context, maybe_string); | 1654 Node* const string = ToString(context, maybe_string); |
| 1650 | 1655 |
| 1651 Label fast_path(this), slow_path(this); | 1656 Label fast_path(this), slow_path(this); |
| 1652 BranchIfFastRegExp(context, map, &fast_path, &slow_path); | 1657 Branch(IsFastRegExpMap(context, map), &fast_path, &slow_path); |
| 1653 | 1658 |
| 1654 Bind(&fast_path); | 1659 Bind(&fast_path); |
| 1655 RegExpPrototypeMatchBody(context, receiver, string, true); | 1660 RegExpPrototypeMatchBody(context, receiver, string, true); |
| 1656 | 1661 |
| 1657 Bind(&slow_path); | 1662 Bind(&slow_path); |
| 1658 RegExpPrototypeMatchBody(context, receiver, string, false); | 1663 RegExpPrototypeMatchBody(context, receiver, string, false); |
| 1659 } | 1664 } |
| 1660 | 1665 |
| 1661 void RegExpBuiltinsAssembler::RegExpPrototypeSearchBodyFast( | 1666 void RegExpBuiltinsAssembler::RegExpPrototypeSearchBodyFast( |
| 1662 Node* const context, Node* const regexp, Node* const string) { | 1667 Node* const context, Node* const regexp, Node* const string) { |
| (...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1767 // Ensure {maybe_receiver} is a JSReceiver. | 1772 // Ensure {maybe_receiver} is a JSReceiver. |
| 1768 Node* const map = ThrowIfNotJSReceiver( | 1773 Node* const map = ThrowIfNotJSReceiver( |
| 1769 context, maybe_receiver, MessageTemplate::kIncompatibleMethodReceiver, | 1774 context, maybe_receiver, MessageTemplate::kIncompatibleMethodReceiver, |
| 1770 "RegExp.prototype.@@search"); | 1775 "RegExp.prototype.@@search"); |
| 1771 Node* const receiver = maybe_receiver; | 1776 Node* const receiver = maybe_receiver; |
| 1772 | 1777 |
| 1773 // Convert {maybe_string} to a String. | 1778 // Convert {maybe_string} to a String. |
| 1774 Node* const string = ToString(context, maybe_string); | 1779 Node* const string = ToString(context, maybe_string); |
| 1775 | 1780 |
| 1776 Label fast_path(this), slow_path(this); | 1781 Label fast_path(this), slow_path(this); |
| 1777 BranchIfFastRegExp(context, map, &fast_path, &slow_path); | 1782 Branch(IsFastRegExpMap(context, map), &fast_path, &slow_path); |
| 1778 | 1783 |
| 1779 Bind(&fast_path); | 1784 Bind(&fast_path); |
| 1780 RegExpPrototypeSearchBodyFast(context, receiver, string); | 1785 RegExpPrototypeSearchBodyFast(context, receiver, string); |
| 1781 | 1786 |
| 1782 Bind(&slow_path); | 1787 Bind(&slow_path); |
| 1783 RegExpPrototypeSearchBodySlow(context, receiver, string); | 1788 RegExpPrototypeSearchBodySlow(context, receiver, string); |
| 1784 } | 1789 } |
| 1785 | 1790 |
| 1786 // Generates the fast path for @@split. {regexp} is an unmodified JSRegExp, | 1791 // Generates the fast path for @@split. {regexp} is an unmodified JSRegExp, |
| 1787 // {string} is a String, and {limit} is a Smi. | 1792 // {string} is a String, and {limit} is a Smi. |
| (...skipping 234 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2022 Bind(&return_empty_array); | 2027 Bind(&return_empty_array); |
| 2023 { | 2028 { |
| 2024 Node* const length = smi_zero; | 2029 Node* const length = smi_zero; |
| 2025 Node* const capacity = int_zero; | 2030 Node* const capacity = int_zero; |
| 2026 Node* const result = AllocateJSArray(kind, array_map, capacity, length, | 2031 Node* const result = AllocateJSArray(kind, array_map, capacity, length, |
| 2027 allocation_site, mode); | 2032 allocation_site, mode); |
| 2028 Return(result); | 2033 Return(result); |
| 2029 } | 2034 } |
| 2030 } | 2035 } |
| 2031 | 2036 |
| 2037 // Helper that skips a few initial checks. | |
| 2038 TF_BUILTIN(RegExpSplit, RegExpBuiltinsAssembler) { | |
| 2039 typedef RegExpSplitDescriptor Descriptor; | |
| 2040 | |
| 2041 Node* const regexp = Parameter(Descriptor::kReceiver); | |
| 2042 Node* const string = Parameter(Descriptor::kString); | |
| 2043 Node* const maybe_limit = Parameter(Descriptor::kLimit); | |
| 2044 Node* const context = Parameter(Descriptor::kContext); | |
| 2045 | |
| 2046 CSA_ASSERT(this, IsFastRegExpMap(context, LoadMap(regexp))); | |
| 2047 CSA_ASSERT(this, IsString(string)); | |
| 2048 | |
| 2049 // TODO(jgruber): Even if map checks send us to the fast path, we still need | |
| 2050 // to verify the constructor property and jump to the slow path if it has | |
| 2051 // been changed. | |
| 2052 | |
| 2053 // Convert {maybe_limit} to a uint32, capping at the maximal smi value. | |
| 2054 Variable var_limit(this, MachineRepresentation::kTagged); | |
| 2055 Label if_limitissmimax(this), limit_done(this); | |
| 2056 | |
| 2057 GotoIf(IsUndefined(maybe_limit), &if_limitissmimax); | |
| 2058 | |
| 2059 { | |
| 2060 Node* const limit = ToUint32(context, maybe_limit); | |
| 2061 GotoUnless(TaggedIsSmi(limit), &if_limitissmimax); | |
| 2062 | |
| 2063 var_limit.Bind(limit); | |
| 2064 Goto(&limit_done); | |
| 2065 } | |
| 2066 | |
| 2067 Bind(&if_limitissmimax); | |
| 2068 { | |
| 2069 // TODO(jgruber): In this case, we can probably avoid generation of limit | |
| 2070 // checks in Generate_RegExpPrototypeSplitBody. | |
| 2071 var_limit.Bind(SmiConstant(Smi::kMaxValue)); | |
| 2072 Goto(&limit_done); | |
| 2073 } | |
| 2074 | |
| 2075 Bind(&limit_done); | |
| 2076 { | |
| 2077 Node* const limit = var_limit.value(); | |
| 2078 RegExpPrototypeSplitBody(context, regexp, string, limit); | |
| 2079 } | |
| 2080 } | |
| 2081 | |
| 2032 // ES#sec-regexp.prototype-@@split | 2082 // ES#sec-regexp.prototype-@@split |
| 2033 // RegExp.prototype [ @@split ] ( string, limit ) | 2083 // RegExp.prototype [ @@split ] ( string, limit ) |
| 2034 TF_BUILTIN(RegExpPrototypeSplit, RegExpBuiltinsAssembler) { | 2084 TF_BUILTIN(RegExpPrototypeSplit, RegExpBuiltinsAssembler) { |
| 2035 Node* const maybe_receiver = Parameter(0); | 2085 Node* const maybe_receiver = Parameter(0); |
| 2036 Node* const maybe_string = Parameter(1); | 2086 Node* const maybe_string = Parameter(1); |
| 2037 Node* const maybe_limit = Parameter(2); | 2087 Node* const maybe_limit = Parameter(2); |
| 2038 Node* const context = Parameter(5); | 2088 Node* const context = Parameter(5); |
| 2039 | 2089 |
| 2040 // Ensure {maybe_receiver} is a JSReceiver. | 2090 // Ensure {maybe_receiver} is a JSReceiver. |
| 2041 Node* const map = ThrowIfNotJSReceiver( | 2091 Node* const map = ThrowIfNotJSReceiver( |
| 2042 context, maybe_receiver, MessageTemplate::kIncompatibleMethodReceiver, | 2092 context, maybe_receiver, MessageTemplate::kIncompatibleMethodReceiver, |
| 2043 "RegExp.prototype.@@split"); | 2093 "RegExp.prototype.@@split"); |
| 2044 Node* const receiver = maybe_receiver; | 2094 Node* const receiver = maybe_receiver; |
| 2045 | 2095 |
| 2046 // Convert {maybe_string} to a String. | 2096 // Convert {maybe_string} to a String. |
| 2047 Node* const string = ToString(context, maybe_string); | 2097 Node* const string = ToString(context, maybe_string); |
| 2048 | 2098 |
| 2049 Label fast_path(this), slow_path(this); | 2099 Label stub(this), runtime(this, Label::kDeferred); |
| 2050 BranchIfFastRegExp(context, map, &fast_path, &slow_path); | 2100 Branch(IsFastRegExpMap(context, map), &stub, &runtime); |
| 2051 | 2101 |
| 2052 Bind(&fast_path); | 2102 Bind(&stub); |
| 2053 { | 2103 Callable split_callable = CodeFactory::RegExpSplit(isolate()); |
| 2054 // TODO(jgruber): Even if map checks send us to the fast path, we still need | 2104 Return(CallStub(split_callable, context, receiver, string, maybe_limit)); |
| 2055 // to verify the constructor property and jump to the slow path if it has | |
| 2056 // been changed. | |
| 2057 | 2105 |
| 2058 // Convert {maybe_limit} to a uint32, capping at the maximal smi value. | 2106 Bind(&runtime); |
| 2059 Variable var_limit(this, MachineRepresentation::kTagged); | 2107 Return(CallRuntime(Runtime::kRegExpSplit, context, receiver, string, |
| 2060 Label if_limitissmimax(this), limit_done(this); | 2108 maybe_limit)); |
| 2061 | |
| 2062 GotoIf(IsUndefined(maybe_limit), &if_limitissmimax); | |
| 2063 | |
| 2064 { | |
| 2065 Node* const limit = ToUint32(context, maybe_limit); | |
| 2066 GotoUnless(TaggedIsSmi(limit), &if_limitissmimax); | |
| 2067 | |
| 2068 var_limit.Bind(limit); | |
| 2069 Goto(&limit_done); | |
| 2070 } | |
| 2071 | |
| 2072 Bind(&if_limitissmimax); | |
| 2073 { | |
| 2074 // TODO(jgruber): In this case, we can probably generation of limit checks | |
| 2075 // in Generate_RegExpPrototypeSplitBody. | |
| 2076 Node* const smi_max = SmiConstant(Smi::kMaxValue); | |
| 2077 var_limit.Bind(smi_max); | |
| 2078 Goto(&limit_done); | |
| 2079 } | |
| 2080 | |
| 2081 Bind(&limit_done); | |
| 2082 { | |
| 2083 Node* const limit = var_limit.value(); | |
| 2084 RegExpPrototypeSplitBody(context, receiver, string, limit); | |
| 2085 } | |
| 2086 } | |
| 2087 | |
| 2088 Bind(&slow_path); | |
| 2089 { | |
| 2090 Node* const result = CallRuntime(Runtime::kRegExpSplit, context, receiver, | |
| 2091 string, maybe_limit); | |
| 2092 Return(result); | |
| 2093 } | |
| 2094 } | 2109 } |
| 2095 | 2110 |
| 2096 Node* RegExpBuiltinsAssembler::ReplaceGlobalCallableFastPath( | 2111 Node* RegExpBuiltinsAssembler::ReplaceGlobalCallableFastPath( |
| 2097 Node* context, Node* regexp, Node* string, Node* replace_callable) { | 2112 Node* context, Node* regexp, Node* string, Node* replace_callable) { |
| 2098 // The fast path is reached only if {receiver} is a global unmodified | 2113 // The fast path is reached only if {receiver} is a global unmodified |
| 2099 // JSRegExp instance and {replace_callable} is callable. | 2114 // JSRegExp instance and {replace_callable} is callable. |
| 2100 | 2115 |
| 2101 Isolate* const isolate = this->isolate(); | 2116 Isolate* const isolate = this->isolate(); |
| 2102 | 2117 |
| 2103 Node* const null = NullConstant(); | 2118 Node* const null = NullConstant(); |
| (...skipping 295 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2399 var_result.Bind(result); | 2414 var_result.Bind(result); |
| 2400 Goto(&out); | 2415 Goto(&out); |
| 2401 } | 2416 } |
| 2402 } | 2417 } |
| 2403 } | 2418 } |
| 2404 | 2419 |
| 2405 Bind(&out); | 2420 Bind(&out); |
| 2406 return var_result.value(); | 2421 return var_result.value(); |
| 2407 } | 2422 } |
| 2408 | 2423 |
| 2409 // ES#sec-regexp.prototype-@@replace | 2424 // Helper that skips a few initial checks. |
| 2410 // RegExp.prototype [ @@replace ] ( string, replaceValue ) | 2425 TF_BUILTIN(RegExpReplace, RegExpBuiltinsAssembler) { |
| 2411 TF_BUILTIN(RegExpPrototypeReplace, RegExpBuiltinsAssembler) { | 2426 typedef RegExpReplaceDescriptor Descriptor; |
| 2412 Node* const maybe_receiver = Parameter(0); | |
| 2413 Node* const maybe_string = Parameter(1); | |
| 2414 Node* const replace_value = Parameter(2); | |
| 2415 Node* const context = Parameter(5); | |
| 2416 | 2427 |
| 2417 // Ensure {maybe_receiver} is a JSReceiver. | 2428 Node* const regexp = Parameter(Descriptor::kReceiver); |
| 2418 Node* const map = ThrowIfNotJSReceiver( | 2429 Node* const string = Parameter(Descriptor::kString); |
| 2419 context, maybe_receiver, MessageTemplate::kIncompatibleMethodReceiver, | 2430 Node* const replace_value = Parameter(Descriptor::kReplaceValue); |
| 2420 "RegExp.prototype.@@replace"); | 2431 Node* const context = Parameter(Descriptor::kContext); |
| 2421 Node* const receiver = maybe_receiver; | |
| 2422 | 2432 |
| 2423 // Convert {maybe_string} to a String. | 2433 CSA_ASSERT(this, IsFastRegExpMap(context, LoadMap(regexp))); |
| 2424 Callable tostring_callable = CodeFactory::ToString(isolate()); | 2434 CSA_ASSERT(this, IsString(string)); |
| 2425 Node* const string = CallStub(tostring_callable, context, maybe_string); | |
| 2426 | 2435 |
| 2427 // Fast-path checks: 1. Is the {receiver} an unmodified JSRegExp instance? | 2436 Label checkreplacestring(this), if_iscallable(this), |
| 2428 Label checkreplacecallable(this), runtime(this, Label::kDeferred), | 2437 runtime(this, Label::kDeferred); |
| 2429 fastpath(this); | |
| 2430 BranchIfFastRegExp(context, map, &checkreplacecallable, &runtime); | |
| 2431 | |
| 2432 Bind(&checkreplacecallable); | |
| 2433 Node* const regexp = receiver; | |
| 2434 | 2438 |
| 2435 // 2. Is {replace_value} callable? | 2439 // 2. Is {replace_value} callable? |
| 2436 Label checkreplacestring(this), if_iscallable(this); | |
| 2437 GotoIf(TaggedIsSmi(replace_value), &checkreplacestring); | 2440 GotoIf(TaggedIsSmi(replace_value), &checkreplacestring); |
| 2438 | 2441 Branch(IsCallableMap(LoadMap(replace_value)), &if_iscallable, |
| 2439 Node* const replace_value_map = LoadMap(replace_value); | 2442 &checkreplacestring); |
| 2440 Branch(IsCallableMap(replace_value_map), &if_iscallable, &checkreplacestring); | |
| 2441 | 2443 |
| 2442 // 3. Does ToString({replace_value}) contain '$'? | 2444 // 3. Does ToString({replace_value}) contain '$'? |
| 2443 Bind(&checkreplacestring); | 2445 Bind(&checkreplacestring); |
| 2444 { | 2446 { |
| 2447 Callable tostring_callable = CodeFactory::ToString(isolate()); | |
| 2445 Node* const replace_string = | 2448 Node* const replace_string = |
| 2446 CallStub(tostring_callable, context, replace_value); | 2449 CallStub(tostring_callable, context, replace_value); |
| 2447 | 2450 |
| 2448 Node* const dollar_char = Int32Constant('$'); | 2451 Node* const dollar_char = Int32Constant('$'); |
| 2449 Node* const smi_minusone = SmiConstant(Smi::FromInt(-1)); | |
| 2450 GotoUnless(SmiEqual(StringIndexOfChar(context, replace_string, dollar_char, | 2452 GotoUnless(SmiEqual(StringIndexOfChar(context, replace_string, dollar_char, |
| 2451 SmiConstant(0)), | 2453 SmiConstant(0)), |
| 2452 smi_minusone), | 2454 SmiConstant(-1)), |
| 2453 &runtime); | 2455 &runtime); |
| 2454 | 2456 |
| 2455 Return( | 2457 Return( |
| 2456 ReplaceSimpleStringFastPath(context, regexp, string, replace_string)); | 2458 ReplaceSimpleStringFastPath(context, regexp, string, replace_string)); |
| 2457 } | 2459 } |
| 2458 | 2460 |
| 2459 // {regexp} is unmodified and {replace_value} is callable. | 2461 // {regexp} is unmodified and {replace_value} is callable. |
| 2460 Bind(&if_iscallable); | 2462 Bind(&if_iscallable); |
| 2461 { | 2463 { |
| 2462 Node* const replace_callable = replace_value; | 2464 Node* const replace_fn = replace_value; |
| 2463 | 2465 |
| 2464 // Check if the {regexp} is global. | 2466 // Check if the {regexp} is global. |
| 2465 Label if_isglobal(this), if_isnotglobal(this); | 2467 Label if_isglobal(this), if_isnotglobal(this); |
| 2468 | |
| 2466 Node* const is_global = FastFlagGetter(regexp, JSRegExp::kGlobal); | 2469 Node* const is_global = FastFlagGetter(regexp, JSRegExp::kGlobal); |
| 2467 Branch(is_global, &if_isglobal, &if_isnotglobal); | 2470 Branch(is_global, &if_isglobal, &if_isnotglobal); |
| 2468 | 2471 |
| 2469 Bind(&if_isglobal); | 2472 Bind(&if_isglobal); |
| 2470 { | 2473 Return(ReplaceGlobalCallableFastPath(context, regexp, string, replace_fn)); |
| 2471 Node* const result = ReplaceGlobalCallableFastPath( | |
| 2472 context, regexp, string, replace_callable); | |
| 2473 Return(result); | |
| 2474 } | |
| 2475 | 2474 |
| 2476 Bind(&if_isnotglobal); | 2475 Bind(&if_isnotglobal); |
| 2477 { | 2476 Return(CallRuntime(Runtime::kStringReplaceNonGlobalRegExpWithFunction, |
| 2478 Node* const result = | 2477 context, string, regexp, replace_fn)); |
| 2479 CallRuntime(Runtime::kStringReplaceNonGlobalRegExpWithFunction, | |
| 2480 context, string, regexp, replace_callable); | |
| 2481 Return(result); | |
| 2482 } | |
| 2483 } | 2478 } |
| 2484 | 2479 |
| 2485 Bind(&runtime); | 2480 Bind(&runtime); |
| 2486 { | 2481 Return(CallRuntime(Runtime::kRegExpReplace, context, regexp, string, |
| 2487 Node* const result = CallRuntime(Runtime::kRegExpReplace, context, receiver, | 2482 replace_value)); |
| 2488 string, replace_value); | 2483 } |
| 2489 Return(result); | 2484 |
| 2490 } | 2485 // ES#sec-regexp.prototype-@@replace |
| 2486 // RegExp.prototype [ @@replace ] ( string, replaceValue ) | |
| 2487 TF_BUILTIN(RegExpPrototypeReplace, RegExpBuiltinsAssembler) { | |
| 2488 Node* const maybe_receiver = Parameter(0); | |
| 2489 Node* const maybe_string = Parameter(1); | |
| 2490 Node* const replace_value = Parameter(2); | |
| 2491 Node* const context = Parameter(5); | |
| 2492 | |
| 2493 // Ensure {maybe_receiver} is a JSReceiver. | |
| 2494 Node* const map = ThrowIfNotJSReceiver( | |
| 2495 context, maybe_receiver, MessageTemplate::kIncompatibleMethodReceiver, | |
| 2496 "RegExp.prototype.@@replace"); | |
| 2497 Node* const receiver = maybe_receiver; | |
| 2498 | |
| 2499 // Convert {maybe_string} to a String. | |
| 2500 Callable tostring_callable = CodeFactory::ToString(isolate()); | |
| 2501 Node* const string = CallStub(tostring_callable, context, maybe_string); | |
| 2502 | |
| 2503 // Fast-path checks: 1. Is the {receiver} an unmodified JSRegExp instance? | |
| 2504 Label stub(this), runtime(this, Label::kDeferred); | |
| 2505 Branch(IsFastRegExpMap(context, map), &stub, &runtime); | |
| 2506 | |
| 2507 Bind(&stub); | |
| 2508 Callable replace_callable = CodeFactory::RegExpReplace(isolate()); | |
| 2509 Return(CallStub(replace_callable, context, receiver, string, replace_value)); | |
| 2510 | |
| 2511 Bind(&runtime); | |
| 2512 Return(CallRuntime(Runtime::kRegExpReplace, context, receiver, string, | |
| 2513 replace_value)); | |
| 2491 } | 2514 } |
| 2492 | 2515 |
| 2493 // Simple string matching functionality for internal use which does not modify | 2516 // Simple string matching functionality for internal use which does not modify |
| 2494 // the last match info. | 2517 // the last match info. |
| 2495 TF_BUILTIN(RegExpInternalMatch, RegExpBuiltinsAssembler) { | 2518 TF_BUILTIN(RegExpInternalMatch, RegExpBuiltinsAssembler) { |
| 2496 Node* const regexp = Parameter(1); | 2519 Node* const regexp = Parameter(1); |
| 2497 Node* const string = Parameter(2); | 2520 Node* const string = Parameter(2); |
| 2498 Node* const context = Parameter(5); | 2521 Node* const context = Parameter(5); |
| 2499 | 2522 |
| 2500 Node* const null = NullConstant(); | 2523 Node* const null = NullConstant(); |
| (...skipping 16 matching lines...) Expand all Loading... | |
| 2517 Bind(&if_matched); | 2540 Bind(&if_matched); |
| 2518 { | 2541 { |
| 2519 Node* result = | 2542 Node* result = |
| 2520 ConstructNewResultFromMatchInfo(context, regexp, match_indices, string); | 2543 ConstructNewResultFromMatchInfo(context, regexp, match_indices, string); |
| 2521 Return(result); | 2544 Return(result); |
| 2522 } | 2545 } |
| 2523 } | 2546 } |
| 2524 | 2547 |
| 2525 } // namespace internal | 2548 } // namespace internal |
| 2526 } // namespace v8 | 2549 } // namespace v8 |
| OLD | NEW |