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 |