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 220 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 231 ThrowIfNotInstanceType(context, regexp, JS_REGEXP_TYPE, | 231 ThrowIfNotInstanceType(context, regexp, JS_REGEXP_TYPE, |
| 232 "RegExp.prototype.exec"); | 232 "RegExp.prototype.exec"); |
| 233 } | 233 } |
| 234 | 234 |
| 235 CSA_ASSERT(this, IsStringInstanceType(LoadInstanceType(string))); | 235 CSA_ASSERT(this, IsStringInstanceType(LoadInstanceType(string))); |
| 236 CSA_ASSERT(this, HasInstanceType(regexp, JS_REGEXP_TYPE)); | 236 CSA_ASSERT(this, HasInstanceType(regexp, JS_REGEXP_TYPE)); |
| 237 | 237 |
| 238 Variable var_result(this, MachineRepresentation::kTagged); | 238 Variable var_result(this, MachineRepresentation::kTagged); |
| 239 Label out(this); | 239 Label out(this); |
| 240 | 240 |
| 241 Node* const native_context = LoadNativeContext(context); | |
| 242 Node* const string_length = LoadStringLength(string); | |
| 243 | |
| 244 // Load lastIndex. | 241 // Load lastIndex. |
| 245 Variable var_lastindex(this, MachineRepresentation::kTagged); | 242 Variable var_lastindex(this, MachineRepresentation::kTagged); |
| 246 { | 243 { |
| 247 Node* const regexp_lastindex = LoadLastIndex(context, regexp, is_fastpath); | 244 Node* const regexp_lastindex = LoadLastIndex(context, regexp, is_fastpath); |
| 248 var_lastindex.Bind(regexp_lastindex); | 245 var_lastindex.Bind(regexp_lastindex); |
| 249 | 246 |
| 250 // Omit ToLength if lastindex is a non-negative smi. | 247 // Omit ToLength if lastindex is a non-negative smi. |
| 251 Label call_tolength(this, Label::kDeferred), next(this); | 248 Label call_tolength(this, Label::kDeferred), next(this); |
| 252 Branch(TaggedIsPositiveSmi(regexp_lastindex), &next, &call_tolength); | 249 Branch(TaggedIsPositiveSmi(regexp_lastindex), &next, &call_tolength); |
| 253 | 250 |
| (...skipping 21 matching lines...) Expand all Loading... | |
| 275 { | 272 { |
| 276 Label if_doupdate(this), if_dontupdate(this); | 273 Label if_doupdate(this), if_dontupdate(this); |
| 277 Branch(should_update_last_index, &if_doupdate, &if_dontupdate); | 274 Branch(should_update_last_index, &if_doupdate, &if_dontupdate); |
| 278 | 275 |
| 279 Bind(&if_doupdate); | 276 Bind(&if_doupdate); |
| 280 { | 277 { |
| 281 Node* const lastindex = var_lastindex.value(); | 278 Node* const lastindex = var_lastindex.value(); |
| 282 | 279 |
| 283 Label if_isoob(this, Label::kDeferred); | 280 Label if_isoob(this, Label::kDeferred); |
| 284 GotoUnless(TaggedIsSmi(lastindex), &if_isoob); | 281 GotoUnless(TaggedIsSmi(lastindex), &if_isoob); |
| 282 Node* const string_length = LoadStringLength(string); | |
|
jgruber
2017/02/13 12:15:38
This and below are drive-by cleanups that push loa
| |
| 285 GotoUnless(SmiLessThanOrEqual(lastindex, string_length), &if_isoob); | 283 GotoUnless(SmiLessThanOrEqual(lastindex, string_length), &if_isoob); |
| 286 Goto(&run_exec); | 284 Goto(&run_exec); |
| 287 | 285 |
| 288 Bind(&if_isoob); | 286 Bind(&if_isoob); |
| 289 { | 287 { |
| 290 StoreLastIndex(context, regexp, smi_zero, is_fastpath); | 288 StoreLastIndex(context, regexp, smi_zero, is_fastpath); |
| 291 var_result.Bind(null); | 289 var_result.Bind(null); |
| 292 Goto(if_didnotmatch); | 290 Goto(if_didnotmatch); |
| 293 } | 291 } |
| 294 } | 292 } |
| 295 | 293 |
| 296 Bind(&if_dontupdate); | 294 Bind(&if_dontupdate); |
| 297 { | 295 { |
| 298 var_lastindex.Bind(smi_zero); | 296 var_lastindex.Bind(smi_zero); |
| 299 Goto(&run_exec); | 297 Goto(&run_exec); |
| 300 } | 298 } |
| 301 } | 299 } |
| 302 | 300 |
| 303 Node* match_indices; | 301 Node* match_indices; |
| 304 Label successful_match(this); | 302 Label successful_match(this); |
| 305 Bind(&run_exec); | 303 Bind(&run_exec); |
| 306 { | 304 { |
| 307 // Get last match info from the context. | 305 // Get last match info from the context. |
| 306 Node* const native_context = LoadNativeContext(context); | |
| 308 Node* const last_match_info = LoadContextElement( | 307 Node* const last_match_info = LoadContextElement( |
| 309 native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX); | 308 native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX); |
| 310 | 309 |
| 311 // Call the exec stub. | 310 // Call the exec stub. |
| 312 Callable exec_callable = CodeFactory::RegExpExec(isolate); | 311 Callable exec_callable = CodeFactory::RegExpExec(isolate); |
| 313 match_indices = CallStub(exec_callable, context, regexp, string, | 312 match_indices = CallStub(exec_callable, context, regexp, string, |
| 314 var_lastindex.value(), last_match_info); | 313 var_lastindex.value(), last_match_info); |
| 315 var_result.Bind(match_indices); | 314 var_result.Bind(match_indices); |
| 316 | 315 |
| 317 // {match_indices} is either null or the RegExpMatchInfo array. | 316 // {match_indices} is either null or the RegExpMatchInfo array. |
| (...skipping 2012 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2330 return var_result.value(); | 2329 return var_result.value(); |
| 2331 } | 2330 } |
| 2332 | 2331 |
| 2333 Node* RegExpBuiltinsAssembler::ReplaceSimpleStringFastPath( | 2332 Node* RegExpBuiltinsAssembler::ReplaceSimpleStringFastPath( |
| 2334 Node* context, Node* regexp, Node* string, Node* replace_string) { | 2333 Node* context, Node* regexp, Node* string, Node* replace_string) { |
| 2335 // The fast path is reached only if {receiver} is an unmodified | 2334 // The fast path is reached only if {receiver} is an unmodified |
| 2336 // JSRegExp instance, {replace_value} is non-callable, and | 2335 // JSRegExp instance, {replace_value} is non-callable, and |
| 2337 // ToString({replace_value}) does not contain '$', i.e. we're doing a simple | 2336 // ToString({replace_value}) does not contain '$', i.e. we're doing a simple |
| 2338 // string replacement. | 2337 // string replacement. |
| 2339 | 2338 |
| 2340 Isolate* const isolate = this->isolate(); | |
| 2341 | |
| 2342 Node* const null = NullConstant(); | |
| 2343 Node* const int_zero = IntPtrConstant(0); | 2339 Node* const int_zero = IntPtrConstant(0); |
| 2344 Node* const smi_zero = SmiConstant(Smi::kZero); | 2340 Node* const smi_zero = SmiConstant(Smi::kZero); |
| 2345 | 2341 |
| 2346 Label out(this); | 2342 Label out(this); |
| 2347 Variable var_result(this, MachineRepresentation::kTagged); | 2343 Variable var_result(this, MachineRepresentation::kTagged); |
| 2348 | 2344 |
| 2349 // Load the last match info. | 2345 // Load the last match info. |
| 2350 Node* const native_context = LoadNativeContext(context); | 2346 Node* const native_context = LoadNativeContext(context); |
| 2351 Node* const last_match_info = | 2347 Node* const last_match_info = |
| 2352 LoadContextElement(native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX); | 2348 LoadContextElement(native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX); |
| (...skipping 12 matching lines...) Expand all Loading... | |
| 2365 Node* const result = | 2361 Node* const result = |
| 2366 CallRuntime(Runtime::kStringReplaceGlobalRegExpWithString, context, | 2362 CallRuntime(Runtime::kStringReplaceGlobalRegExpWithString, context, |
| 2367 string, regexp, replace_string, last_match_info); | 2363 string, regexp, replace_string, last_match_info); |
| 2368 var_result.Bind(result); | 2364 var_result.Bind(result); |
| 2369 Goto(&out); | 2365 Goto(&out); |
| 2370 } | 2366 } |
| 2371 | 2367 |
| 2372 Bind(&if_isnonglobal); | 2368 Bind(&if_isnonglobal); |
| 2373 { | 2369 { |
| 2374 // Run exec, then manually construct the resulting string. | 2370 // Run exec, then manually construct the resulting string. |
| 2375 Callable exec_callable = CodeFactory::RegExpExec(isolate); | 2371 Label if_didnotmatch(this); |
| 2376 Node* const match_indices = CallStub(exec_callable, context, regexp, string, | 2372 Node* const match_indices = RegExpPrototypeExecBodyWithoutResult( |
|
jgruber
2017/02/13 12:15:38
Replaced RE.p.exec emulation by inlined version of
| |
| 2377 smi_zero, last_match_info); | 2373 context, regexp, string, &if_didnotmatch, true); |
| 2378 | 2374 |
| 2379 Label if_matched(this), if_didnotmatch(this); | 2375 // Successful match. |
| 2380 Branch(WordEqual(match_indices, null), &if_didnotmatch, &if_matched); | |
| 2381 | |
| 2382 Bind(&if_didnotmatch); | |
| 2383 { | |
| 2384 FastStoreLastIndex(regexp, smi_zero); | |
| 2385 var_result.Bind(string); | |
| 2386 Goto(&out); | |
| 2387 } | |
| 2388 | |
| 2389 Bind(&if_matched); | |
| 2390 { | 2376 { |
| 2391 Node* const subject_start = smi_zero; | 2377 Node* const subject_start = smi_zero; |
| 2392 Node* const match_start = LoadFixedArrayElement( | 2378 Node* const match_start = LoadFixedArrayElement( |
| 2393 match_indices, RegExpMatchInfo::kFirstCaptureIndex); | 2379 match_indices, RegExpMatchInfo::kFirstCaptureIndex); |
| 2394 Node* const match_end = LoadFixedArrayElement( | 2380 Node* const match_end = LoadFixedArrayElement( |
| 2395 match_indices, RegExpMatchInfo::kFirstCaptureIndex + 1); | 2381 match_indices, RegExpMatchInfo::kFirstCaptureIndex + 1); |
| 2396 Node* const subject_end = LoadStringLength(string); | 2382 Node* const subject_end = LoadStringLength(string); |
| 2397 | 2383 |
| 2398 Label if_replaceisempty(this), if_replaceisnotempty(this); | 2384 Label if_replaceisempty(this), if_replaceisnotempty(this); |
| 2399 Node* const replace_length = LoadStringLength(replace_string); | 2385 Node* const replace_length = LoadStringLength(replace_string); |
| (...skipping 23 matching lines...) Expand all Loading... | |
| 2423 Node* const third_part = | 2409 Node* const third_part = |
| 2424 SubString(context, string, match_end, subject_end); | 2410 SubString(context, string, match_end, subject_end); |
| 2425 | 2411 |
| 2426 Node* result = StringAdd(context, first_part, second_part); | 2412 Node* result = StringAdd(context, first_part, second_part); |
| 2427 result = StringAdd(context, result, third_part); | 2413 result = StringAdd(context, result, third_part); |
| 2428 | 2414 |
| 2429 var_result.Bind(result); | 2415 var_result.Bind(result); |
| 2430 Goto(&out); | 2416 Goto(&out); |
| 2431 } | 2417 } |
| 2432 } | 2418 } |
| 2419 | |
| 2420 Bind(&if_didnotmatch); | |
| 2421 { | |
| 2422 var_result.Bind(string); | |
| 2423 Goto(&out); | |
| 2424 } | |
| 2433 } | 2425 } |
| 2434 | 2426 |
| 2435 Bind(&out); | 2427 Bind(&out); |
| 2436 return var_result.value(); | 2428 return var_result.value(); |
| 2437 } | 2429 } |
| 2438 | 2430 |
| 2439 // Helper that skips a few initial checks. | 2431 // Helper that skips a few initial checks. |
| 2440 TF_BUILTIN(RegExpReplace, RegExpBuiltinsAssembler) { | 2432 TF_BUILTIN(RegExpReplace, RegExpBuiltinsAssembler) { |
| 2441 typedef RegExpReplaceDescriptor Descriptor; | 2433 typedef RegExpReplaceDescriptor Descriptor; |
| 2442 | 2434 |
| (...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2499 } | 2491 } |
| 2500 | 2492 |
| 2501 // ES#sec-regexp.prototype-@@replace | 2493 // ES#sec-regexp.prototype-@@replace |
| 2502 // RegExp.prototype [ @@replace ] ( string, replaceValue ) | 2494 // RegExp.prototype [ @@replace ] ( string, replaceValue ) |
| 2503 TF_BUILTIN(RegExpPrototypeReplace, RegExpBuiltinsAssembler) { | 2495 TF_BUILTIN(RegExpPrototypeReplace, RegExpBuiltinsAssembler) { |
| 2504 Node* const maybe_receiver = Parameter(0); | 2496 Node* const maybe_receiver = Parameter(0); |
| 2505 Node* const maybe_string = Parameter(1); | 2497 Node* const maybe_string = Parameter(1); |
| 2506 Node* const replace_value = Parameter(2); | 2498 Node* const replace_value = Parameter(2); |
| 2507 Node* const context = Parameter(5); | 2499 Node* const context = Parameter(5); |
| 2508 | 2500 |
| 2501 // RegExpPrototypeReplace is a bit of a beast - a summary of dispatch logic: | |
| 2502 // | |
| 2503 // if (!IsFastRegExp(receiver)) CallRuntime(RegExpReplace) | |
| 2504 // if (IsCallable(replace)) { | |
| 2505 // if (IsGlobal(receiver)) { | |
| 2506 // // Called 'fast-path' but contains several runtime calls. | |
| 2507 // ReplaceGlobalCallableFastPath() | |
| 2508 // } else { | |
| 2509 // CallRuntime(StringReplaceNonGlobalRegExpWithFunction) | |
| 2510 // } | |
| 2511 // } else { | |
| 2512 // if (replace.contains("$")) { | |
| 2513 // CallRuntime(RegExpReplace) | |
| 2514 // } else { | |
| 2515 // ReplaceSimpleStringFastPath() // Bails to runtime for global regexps. | |
| 2516 // } | |
| 2517 // } | |
| 2518 | |
| 2509 // Ensure {maybe_receiver} is a JSReceiver. | 2519 // Ensure {maybe_receiver} is a JSReceiver. |
| 2510 Node* const map = ThrowIfNotJSReceiver( | 2520 Node* const map = ThrowIfNotJSReceiver( |
| 2511 context, maybe_receiver, MessageTemplate::kIncompatibleMethodReceiver, | 2521 context, maybe_receiver, MessageTemplate::kIncompatibleMethodReceiver, |
| 2512 "RegExp.prototype.@@replace"); | 2522 "RegExp.prototype.@@replace"); |
| 2513 Node* const receiver = maybe_receiver; | 2523 Node* const receiver = maybe_receiver; |
| 2514 | 2524 |
| 2515 // Convert {maybe_string} to a String. | 2525 // Convert {maybe_string} to a String. |
| 2516 Callable tostring_callable = CodeFactory::ToString(isolate()); | 2526 Callable tostring_callable = CodeFactory::ToString(isolate()); |
| 2517 Node* const string = CallStub(tostring_callable, context, maybe_string); | 2527 Node* const string = CallStub(tostring_callable, context, maybe_string); |
| 2518 | 2528 |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2556 Bind(&if_matched); | 2566 Bind(&if_matched); |
| 2557 { | 2567 { |
| 2558 Node* result = | 2568 Node* result = |
| 2559 ConstructNewResultFromMatchInfo(context, regexp, match_indices, string); | 2569 ConstructNewResultFromMatchInfo(context, regexp, match_indices, string); |
| 2560 Return(result); | 2570 Return(result); |
| 2561 } | 2571 } |
| 2562 } | 2572 } |
| 2563 | 2573 |
| 2564 } // namespace internal | 2574 } // namespace internal |
| 2565 } // namespace v8 | 2575 } // namespace v8 |
| OLD | NEW |