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 void RegExpBuiltinsAssembler::BranchIfFastRegExp(Node* const context, |
426 Label* if_isunmodified, | 426 Node* const map, |
427 Label* if_ismodified) { | 427 Label* const if_isunmodified, |
| 428 Label* const if_ismodified) { |
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, if_ismodified); |
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 |
443 // TODO(ishell): Update this check once map changes for constant field | 444 // TODO(ishell): Update this check once map changes for constant field |
444 // tracking are landing. | 445 // tracking are landing. |
445 | 446 |
446 Branch(proto_has_initialmap, if_isunmodified, if_ismodified); | 447 Branch(proto_has_initialmap, if_isunmodified, if_ismodified); |
447 } | 448 } |
448 | 449 |
| 450 Node* RegExpBuiltinsAssembler::IsFastRegExpMap(Node* const context, |
| 451 Node* const map) { |
| 452 Label yup(this), nope(this), out(this); |
| 453 Variable var_result(this, MachineRepresentation::kWord32); |
| 454 |
| 455 BranchIfFastRegExp(context, map, &yup, &nope); |
| 456 |
| 457 Bind(&yup); |
| 458 var_result.Bind(Int32Constant(1)); |
| 459 Goto(&out); |
| 460 |
| 461 Bind(&nope); |
| 462 var_result.Bind(Int32Constant(0)); |
| 463 Goto(&out); |
| 464 |
| 465 Bind(&out); |
| 466 return var_result.value(); |
| 467 } |
| 468 |
449 void RegExpBuiltinsAssembler::BranchIfFastRegExpResult(Node* context, Node* map, | 469 void RegExpBuiltinsAssembler::BranchIfFastRegExpResult(Node* context, Node* map, |
450 Label* if_isunmodified, | 470 Label* if_isunmodified, |
451 Label* if_ismodified) { | 471 Label* if_ismodified) { |
452 Node* const native_context = LoadNativeContext(context); | 472 Node* const native_context = LoadNativeContext(context); |
453 Node* const initial_regexp_result_map = | 473 Node* const initial_regexp_result_map = |
454 LoadContextElement(native_context, Context::REGEXP_RESULT_MAP_INDEX); | 474 LoadContextElement(native_context, Context::REGEXP_RESULT_MAP_INDEX); |
455 | 475 |
456 Branch(WordEqual(map, initial_regexp_result_map), if_isunmodified, | 476 Branch(WordEqual(map, initial_regexp_result_map), if_isunmodified, |
457 if_ismodified); | 477 if_ismodified); |
458 } | 478 } |
(...skipping 1563 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2022 Bind(&return_empty_array); | 2042 Bind(&return_empty_array); |
2023 { | 2043 { |
2024 Node* const length = smi_zero; | 2044 Node* const length = smi_zero; |
2025 Node* const capacity = int_zero; | 2045 Node* const capacity = int_zero; |
2026 Node* const result = AllocateJSArray(kind, array_map, capacity, length, | 2046 Node* const result = AllocateJSArray(kind, array_map, capacity, length, |
2027 allocation_site, mode); | 2047 allocation_site, mode); |
2028 Return(result); | 2048 Return(result); |
2029 } | 2049 } |
2030 } | 2050 } |
2031 | 2051 |
| 2052 // Helper that skips a few initial checks. |
| 2053 TF_BUILTIN(RegExpSplit, RegExpBuiltinsAssembler) { |
| 2054 typedef RegExpSplitDescriptor Descriptor; |
| 2055 |
| 2056 Node* const regexp = Parameter(Descriptor::kReceiver); |
| 2057 Node* const string = Parameter(Descriptor::kString); |
| 2058 Node* const maybe_limit = Parameter(Descriptor::kLimit); |
| 2059 Node* const context = Parameter(Descriptor::kContext); |
| 2060 |
| 2061 CSA_ASSERT(this, IsFastRegExpMap(context, LoadMap(regexp))); |
| 2062 CSA_ASSERT(this, IsString(string)); |
| 2063 |
| 2064 // TODO(jgruber): Even if map checks send us to the fast path, we still need |
| 2065 // to verify the constructor property and jump to the slow path if it has |
| 2066 // been changed. |
| 2067 |
| 2068 // Convert {maybe_limit} to a uint32, capping at the maximal smi value. |
| 2069 Variable var_limit(this, MachineRepresentation::kTagged); |
| 2070 Label if_limitissmimax(this), limit_done(this); |
| 2071 |
| 2072 GotoIf(IsUndefined(maybe_limit), &if_limitissmimax); |
| 2073 |
| 2074 { |
| 2075 Node* const limit = ToUint32(context, maybe_limit); |
| 2076 GotoUnless(TaggedIsSmi(limit), &if_limitissmimax); |
| 2077 |
| 2078 var_limit.Bind(limit); |
| 2079 Goto(&limit_done); |
| 2080 } |
| 2081 |
| 2082 Bind(&if_limitissmimax); |
| 2083 { |
| 2084 // TODO(jgruber): In this case, we can probably avoid generation of limit |
| 2085 // checks in Generate_RegExpPrototypeSplitBody. |
| 2086 var_limit.Bind(SmiConstant(Smi::kMaxValue)); |
| 2087 Goto(&limit_done); |
| 2088 } |
| 2089 |
| 2090 Bind(&limit_done); |
| 2091 { |
| 2092 Node* const limit = var_limit.value(); |
| 2093 RegExpPrototypeSplitBody(context, regexp, string, limit); |
| 2094 } |
| 2095 } |
| 2096 |
2032 // ES#sec-regexp.prototype-@@split | 2097 // ES#sec-regexp.prototype-@@split |
2033 // RegExp.prototype [ @@split ] ( string, limit ) | 2098 // RegExp.prototype [ @@split ] ( string, limit ) |
2034 TF_BUILTIN(RegExpPrototypeSplit, RegExpBuiltinsAssembler) { | 2099 TF_BUILTIN(RegExpPrototypeSplit, RegExpBuiltinsAssembler) { |
2035 Node* const maybe_receiver = Parameter(0); | 2100 Node* const maybe_receiver = Parameter(0); |
2036 Node* const maybe_string = Parameter(1); | 2101 Node* const maybe_string = Parameter(1); |
2037 Node* const maybe_limit = Parameter(2); | 2102 Node* const maybe_limit = Parameter(2); |
2038 Node* const context = Parameter(5); | 2103 Node* const context = Parameter(5); |
2039 | 2104 |
2040 // Ensure {maybe_receiver} is a JSReceiver. | 2105 // Ensure {maybe_receiver} is a JSReceiver. |
2041 Node* const map = ThrowIfNotJSReceiver( | 2106 Node* const map = ThrowIfNotJSReceiver( |
2042 context, maybe_receiver, MessageTemplate::kIncompatibleMethodReceiver, | 2107 context, maybe_receiver, MessageTemplate::kIncompatibleMethodReceiver, |
2043 "RegExp.prototype.@@split"); | 2108 "RegExp.prototype.@@split"); |
2044 Node* const receiver = maybe_receiver; | 2109 Node* const receiver = maybe_receiver; |
2045 | 2110 |
2046 // Convert {maybe_string} to a String. | 2111 // Convert {maybe_string} to a String. |
2047 Node* const string = ToString(context, maybe_string); | 2112 Node* const string = ToString(context, maybe_string); |
2048 | 2113 |
2049 Label fast_path(this), slow_path(this); | 2114 Label stub(this), runtime(this, Label::kDeferred); |
2050 BranchIfFastRegExp(context, map, &fast_path, &slow_path); | 2115 BranchIfFastRegExp(context, map, &stub, &runtime); |
2051 | 2116 |
2052 Bind(&fast_path); | 2117 Bind(&stub); |
2053 { | 2118 Callable split_callable = CodeFactory::RegExpSplit(isolate()); |
2054 // TODO(jgruber): Even if map checks send us to the fast path, we still need | 2119 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 | 2120 |
2058 // Convert {maybe_limit} to a uint32, capping at the maximal smi value. | 2121 Bind(&runtime); |
2059 Variable var_limit(this, MachineRepresentation::kTagged); | 2122 Return(CallRuntime(Runtime::kRegExpSplit, context, receiver, string, |
2060 Label if_limitissmimax(this), limit_done(this); | 2123 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 } | 2124 } |
2095 | 2125 |
2096 Node* RegExpBuiltinsAssembler::ReplaceGlobalCallableFastPath( | 2126 Node* RegExpBuiltinsAssembler::ReplaceGlobalCallableFastPath( |
2097 Node* context, Node* regexp, Node* string, Node* replace_callable) { | 2127 Node* context, Node* regexp, Node* string, Node* replace_callable) { |
2098 // The fast path is reached only if {receiver} is a global unmodified | 2128 // The fast path is reached only if {receiver} is a global unmodified |
2099 // JSRegExp instance and {replace_callable} is callable. | 2129 // JSRegExp instance and {replace_callable} is callable. |
2100 | 2130 |
2101 Isolate* const isolate = this->isolate(); | 2131 Isolate* const isolate = this->isolate(); |
2102 | 2132 |
2103 Node* const null = NullConstant(); | 2133 Node* const null = NullConstant(); |
(...skipping 295 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2399 var_result.Bind(result); | 2429 var_result.Bind(result); |
2400 Goto(&out); | 2430 Goto(&out); |
2401 } | 2431 } |
2402 } | 2432 } |
2403 } | 2433 } |
2404 | 2434 |
2405 Bind(&out); | 2435 Bind(&out); |
2406 return var_result.value(); | 2436 return var_result.value(); |
2407 } | 2437 } |
2408 | 2438 |
2409 // ES#sec-regexp.prototype-@@replace | 2439 // Helper that skips a few initial checks. |
2410 // RegExp.prototype [ @@replace ] ( string, replaceValue ) | 2440 TF_BUILTIN(RegExpReplace, RegExpBuiltinsAssembler) { |
2411 TF_BUILTIN(RegExpPrototypeReplace, RegExpBuiltinsAssembler) { | 2441 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 | 2442 |
2417 // Ensure {maybe_receiver} is a JSReceiver. | 2443 Node* const regexp = Parameter(Descriptor::kReceiver); |
2418 Node* const map = ThrowIfNotJSReceiver( | 2444 Node* const string = Parameter(Descriptor::kString); |
2419 context, maybe_receiver, MessageTemplate::kIncompatibleMethodReceiver, | 2445 Node* const replace_value = Parameter(Descriptor::kReplaceValue); |
2420 "RegExp.prototype.@@replace"); | 2446 Node* const context = Parameter(Descriptor::kContext); |
2421 Node* const receiver = maybe_receiver; | |
2422 | 2447 |
2423 // Convert {maybe_string} to a String. | 2448 CSA_ASSERT(this, IsFastRegExpMap(context, LoadMap(regexp))); |
2424 Callable tostring_callable = CodeFactory::ToString(isolate()); | 2449 CSA_ASSERT(this, IsString(string)); |
2425 Node* const string = CallStub(tostring_callable, context, maybe_string); | |
2426 | 2450 |
2427 // Fast-path checks: 1. Is the {receiver} an unmodified JSRegExp instance? | 2451 Label checkreplacestring(this), if_iscallable(this), |
2428 Label checkreplacecallable(this), runtime(this, Label::kDeferred), | 2452 runtime(this, Label::kDeferred); |
2429 fastpath(this); | |
2430 BranchIfFastRegExp(context, map, &checkreplacecallable, &runtime); | |
2431 | |
2432 Bind(&checkreplacecallable); | |
2433 Node* const regexp = receiver; | |
2434 | 2453 |
2435 // 2. Is {replace_value} callable? | 2454 // 2. Is {replace_value} callable? |
2436 Label checkreplacestring(this), if_iscallable(this); | |
2437 GotoIf(TaggedIsSmi(replace_value), &checkreplacestring); | 2455 GotoIf(TaggedIsSmi(replace_value), &checkreplacestring); |
2438 | 2456 Branch(IsCallableMap(LoadMap(replace_value)), &if_iscallable, |
2439 Node* const replace_value_map = LoadMap(replace_value); | 2457 &checkreplacestring); |
2440 Branch(IsCallableMap(replace_value_map), &if_iscallable, &checkreplacestring); | |
2441 | 2458 |
2442 // 3. Does ToString({replace_value}) contain '$'? | 2459 // 3. Does ToString({replace_value}) contain '$'? |
2443 Bind(&checkreplacestring); | 2460 Bind(&checkreplacestring); |
2444 { | 2461 { |
| 2462 Callable tostring_callable = CodeFactory::ToString(isolate()); |
2445 Node* const replace_string = | 2463 Node* const replace_string = |
2446 CallStub(tostring_callable, context, replace_value); | 2464 CallStub(tostring_callable, context, replace_value); |
2447 | 2465 |
2448 Node* const dollar_char = Int32Constant('$'); | 2466 Node* const dollar_char = Int32Constant('$'); |
2449 Node* const smi_minusone = SmiConstant(Smi::FromInt(-1)); | |
2450 GotoUnless(SmiEqual(StringIndexOfChar(context, replace_string, dollar_char, | 2467 GotoUnless(SmiEqual(StringIndexOfChar(context, replace_string, dollar_char, |
2451 SmiConstant(0)), | 2468 SmiConstant(0)), |
2452 smi_minusone), | 2469 SmiConstant(-1)), |
2453 &runtime); | 2470 &runtime); |
2454 | 2471 |
2455 Return( | 2472 Return( |
2456 ReplaceSimpleStringFastPath(context, regexp, string, replace_string)); | 2473 ReplaceSimpleStringFastPath(context, regexp, string, replace_string)); |
2457 } | 2474 } |
2458 | 2475 |
2459 // {regexp} is unmodified and {replace_value} is callable. | 2476 // {regexp} is unmodified and {replace_value} is callable. |
2460 Bind(&if_iscallable); | 2477 Bind(&if_iscallable); |
2461 { | 2478 { |
2462 Node* const replace_callable = replace_value; | 2479 Node* const replace_fn = replace_value; |
2463 | 2480 |
2464 // Check if the {regexp} is global. | 2481 // Check if the {regexp} is global. |
2465 Label if_isglobal(this), if_isnotglobal(this); | 2482 Label if_isglobal(this), if_isnotglobal(this); |
| 2483 |
2466 Node* const is_global = FastFlagGetter(regexp, JSRegExp::kGlobal); | 2484 Node* const is_global = FastFlagGetter(regexp, JSRegExp::kGlobal); |
2467 Branch(is_global, &if_isglobal, &if_isnotglobal); | 2485 Branch(is_global, &if_isglobal, &if_isnotglobal); |
2468 | 2486 |
2469 Bind(&if_isglobal); | 2487 Bind(&if_isglobal); |
2470 { | 2488 Return(ReplaceGlobalCallableFastPath(context, regexp, string, replace_fn)); |
2471 Node* const result = ReplaceGlobalCallableFastPath( | |
2472 context, regexp, string, replace_callable); | |
2473 Return(result); | |
2474 } | |
2475 | 2489 |
2476 Bind(&if_isnotglobal); | 2490 Bind(&if_isnotglobal); |
2477 { | 2491 Return(CallRuntime(Runtime::kStringReplaceNonGlobalRegExpWithFunction, |
2478 Node* const result = | 2492 context, string, regexp, replace_fn)); |
2479 CallRuntime(Runtime::kStringReplaceNonGlobalRegExpWithFunction, | |
2480 context, string, regexp, replace_callable); | |
2481 Return(result); | |
2482 } | |
2483 } | 2493 } |
2484 | 2494 |
2485 Bind(&runtime); | 2495 Bind(&runtime); |
2486 { | 2496 Return(CallRuntime(Runtime::kRegExpReplace, context, regexp, string, |
2487 Node* const result = CallRuntime(Runtime::kRegExpReplace, context, receiver, | 2497 replace_value)); |
2488 string, replace_value); | 2498 } |
2489 Return(result); | 2499 |
2490 } | 2500 // ES#sec-regexp.prototype-@@replace |
| 2501 // RegExp.prototype [ @@replace ] ( string, replaceValue ) |
| 2502 TF_BUILTIN(RegExpPrototypeReplace, RegExpBuiltinsAssembler) { |
| 2503 Node* const maybe_receiver = Parameter(0); |
| 2504 Node* const maybe_string = Parameter(1); |
| 2505 Node* const replace_value = Parameter(2); |
| 2506 Node* const context = Parameter(5); |
| 2507 |
| 2508 // Ensure {maybe_receiver} is a JSReceiver. |
| 2509 Node* const map = ThrowIfNotJSReceiver( |
| 2510 context, maybe_receiver, MessageTemplate::kIncompatibleMethodReceiver, |
| 2511 "RegExp.prototype.@@replace"); |
| 2512 Node* const receiver = maybe_receiver; |
| 2513 |
| 2514 // Convert {maybe_string} to a String. |
| 2515 Callable tostring_callable = CodeFactory::ToString(isolate()); |
| 2516 Node* const string = CallStub(tostring_callable, context, maybe_string); |
| 2517 |
| 2518 // Fast-path checks: 1. Is the {receiver} an unmodified JSRegExp instance? |
| 2519 Label stub(this), runtime(this, Label::kDeferred); |
| 2520 BranchIfFastRegExp(context, map, &stub, &runtime); |
| 2521 |
| 2522 Bind(&stub); |
| 2523 Callable replace_callable = CodeFactory::RegExpReplace(isolate()); |
| 2524 Return(CallStub(replace_callable, context, receiver, string, replace_value)); |
| 2525 |
| 2526 Bind(&runtime); |
| 2527 Return(CallRuntime(Runtime::kRegExpReplace, context, receiver, string, |
| 2528 replace_value)); |
2491 } | 2529 } |
2492 | 2530 |
2493 // Simple string matching functionality for internal use which does not modify | 2531 // Simple string matching functionality for internal use which does not modify |
2494 // the last match info. | 2532 // the last match info. |
2495 TF_BUILTIN(RegExpInternalMatch, RegExpBuiltinsAssembler) { | 2533 TF_BUILTIN(RegExpInternalMatch, RegExpBuiltinsAssembler) { |
2496 Node* const regexp = Parameter(1); | 2534 Node* const regexp = Parameter(1); |
2497 Node* const string = Parameter(2); | 2535 Node* const string = Parameter(2); |
2498 Node* const context = Parameter(5); | 2536 Node* const context = Parameter(5); |
2499 | 2537 |
2500 Node* const null = NullConstant(); | 2538 Node* const null = NullConstant(); |
(...skipping 16 matching lines...) Expand all Loading... |
2517 Bind(&if_matched); | 2555 Bind(&if_matched); |
2518 { | 2556 { |
2519 Node* result = | 2557 Node* result = |
2520 ConstructNewResultFromMatchInfo(context, regexp, match_indices, string); | 2558 ConstructNewResultFromMatchInfo(context, regexp, match_indices, string); |
2521 Return(result); | 2559 Return(result); |
2522 } | 2560 } |
2523 } | 2561 } |
2524 | 2562 |
2525 } // namespace internal | 2563 } // namespace internal |
2526 } // namespace v8 | 2564 } // namespace v8 |
OLD | NEW |