Chromium Code Reviews| Index: src/builtins/builtins-regexp.cc |
| diff --git a/src/builtins/builtins-regexp.cc b/src/builtins/builtins-regexp.cc |
| index c8aa5beb80d099008682b46ccd2e468db0ee09b3..15f52560625d67b0bc0e25e0e3569bc2e9e0e765 100644 |
| --- a/src/builtins/builtins-regexp.cc |
| +++ b/src/builtins/builtins-regexp.cc |
| @@ -176,7 +176,7 @@ BUILTIN(RegExpPrototypeCompile) { |
| namespace { |
| -Node* FastLoadLastIndex(CodeStubAssembler* a, Node* context, Node* regexp) { |
| +Node* FastLoadLastIndex(CodeStubAssembler* a, Node* regexp) { |
| // Load the in-object field. |
| static const int field_offset = |
| JSRegExp::kSize + JSRegExp::kLastIndexFieldIndex * kPointerSize; |
| @@ -191,33 +191,15 @@ Node* SlowLoadLastIndex(CodeStubAssembler* a, Node* context, Node* regexp) { |
| return a->CallStub(getproperty_callable, context, regexp, name); |
| } |
| -Node* LoadLastIndex(CodeStubAssembler* a, Node* context, Node* has_initialmap, |
| - Node* regexp) { |
| - CVariable var_value(a, MachineRepresentation::kTagged); |
| - |
| - CLabel out(a), if_unmodified(a), if_modified(a); |
| - a->Branch(has_initialmap, &if_unmodified, &if_modified); |
| - |
| - a->Bind(&if_unmodified); |
| - { |
| - var_value.Bind(FastLoadLastIndex(a, context, regexp)); |
| - a->Goto(&out); |
| - } |
| - |
| - a->Bind(&if_modified); |
| - { |
| - var_value.Bind(SlowLoadLastIndex(a, context, regexp)); |
| - a->Goto(&out); |
| - } |
| - |
| - a->Bind(&out); |
| - return var_value.value(); |
| +Node* LoadLastIndex(CodeStubAssembler* a, Node* context, Node* regexp, |
| + bool is_fastpath) { |
| + return is_fastpath ? FastLoadLastIndex(a, regexp) |
| + : SlowLoadLastIndex(a, context, regexp); |
| } |
| // The fast-path of StoreLastIndex when regexp is guaranteed to be an unmodified |
| // JSRegExp instance. |
| -void FastStoreLastIndex(CodeStubAssembler* a, Node* context, Node* regexp, |
| - Node* value) { |
| +void FastStoreLastIndex(CodeStubAssembler* a, Node* regexp, Node* value) { |
| // Store the in-object field. |
| static const int field_offset = |
| JSRegExp::kSize + JSRegExp::kLastIndexFieldIndex * kPointerSize; |
| @@ -235,24 +217,13 @@ void SlowStoreLastIndex(CodeStubAssembler* a, Node* context, Node* regexp, |
| language_mode); |
| } |
| -void StoreLastIndex(CodeStubAssembler* a, Node* context, Node* has_initialmap, |
| - Node* regexp, Node* value) { |
| - CLabel out(a), if_unmodified(a), if_modified(a); |
| - a->Branch(has_initialmap, &if_unmodified, &if_modified); |
| - |
| - a->Bind(&if_unmodified); |
| - { |
| - FastStoreLastIndex(a, context, regexp, value); |
| - a->Goto(&out); |
| - } |
| - |
| - a->Bind(&if_modified); |
| - { |
| +void StoreLastIndex(CodeStubAssembler* a, Node* context, Node* regexp, |
| + Node* value, bool is_fastpath) { |
| + if (is_fastpath) { |
| + FastStoreLastIndex(a, regexp, value); |
| + } else { |
| SlowStoreLastIndex(a, context, regexp, value); |
| - a->Goto(&out); |
| } |
| - |
| - a->Bind(&out); |
| } |
| Node* ConstructNewResultFromMatchInfo(Isolate* isolate, CodeStubAssembler* a, |
| @@ -326,35 +297,27 @@ Node* ConstructNewResultFromMatchInfo(Isolate* isolate, CodeStubAssembler* a, |
| return result; |
| } |
| -// ES#sec-regexp.prototype.exec |
| -// RegExp.prototype.exec ( string ) |
| -Node* RegExpPrototypeExecInternal(CodeStubAssembler* a, Node* context, |
| - Node* maybe_receiver, Node* maybe_string) { |
| +Node* Generate_RegExpPrototypeExecBodyWithoutResult( |
|
Igor Sheludko
2016/11/30 22:04:21
Please add a comment (especially about what does t
jgruber
2016/12/01 09:09:09
Done.
|
| + CodeStubAssembler* a, Node* const context, Node* const regexp, |
| + Node* const string, CLabel* if_didnotmatch, const bool is_fastpath) { |
| Isolate* const isolate = a->isolate(); |
| Node* const null = a->NullConstant(); |
| Node* const int_zero = a->IntPtrConstant(0); |
| Node* const smi_zero = a->SmiConstant(Smi::kZero); |
| + if (!is_fastpath) { |
| + a->ThrowIfNotInstanceType(context, regexp, JS_REGEXP_TYPE, |
| + "RegExp.prototype.exec"); |
| + } |
| + |
| + CSA_ASSERT(a, a->IsStringInstanceType(a->LoadInstanceType(string))); |
| + CSA_ASSERT(a, a->HasInstanceType(regexp, JS_REGEXP_TYPE)); |
| + |
| CVariable var_result(a, MachineRepresentation::kTagged); |
| CLabel out(a); |
| - // Ensure {maybe_receiver} is a JSRegExp. |
| - Node* const regexp_map = a->ThrowIfNotInstanceType( |
| - context, maybe_receiver, JS_REGEXP_TYPE, "RegExp.prototype.exec"); |
| - Node* const regexp = maybe_receiver; |
| - |
| - // Check whether the regexp instance is unmodified. |
| Node* const native_context = a->LoadNativeContext(context); |
| - Node* const regexp_fun = |
| - a->LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX); |
| - Node* const initial_map = |
| - a->LoadObjectField(regexp_fun, JSFunction::kPrototypeOrInitialMapOffset); |
| - Node* const has_initialmap = a->WordEqual(regexp_map, initial_map); |
| - |
| - // Convert {maybe_string} to a string. |
| - Callable tostring_callable = CodeFactory::ToString(isolate); |
| - Node* const string = a->CallStub(tostring_callable, context, maybe_string); |
| Node* const string_length = a->LoadStringLength(string); |
| // Check whether the regexp is global or sticky, which determines whether we |
| @@ -376,12 +339,27 @@ Node* RegExpPrototypeExecInternal(CodeStubAssembler* a, Node* context, |
| a->Bind(&if_doupdate); |
| { |
| Node* const regexp_lastindex = |
| - LoadLastIndex(a, context, has_initialmap, regexp); |
| + LoadLastIndex(a, context, regexp, is_fastpath); |
| + var_lastindex.Bind(regexp_lastindex); |
| - Callable tolength_callable = CodeFactory::ToLength(isolate); |
| - Node* const lastindex = |
| - a->CallStub(tolength_callable, context, regexp_lastindex); |
| - var_lastindex.Bind(lastindex); |
| + // Omit ToLength if lastindex is a non-negative smi. |
| + { |
| + CLabel call_tolength(a, CLabel::kDeferred), next(a); |
| + a->Branch(a->WordIsPositiveSmi(regexp_lastindex), &next, |
| + &call_tolength); |
| + |
| + a->Bind(&call_tolength); |
| + { |
| + Callable tolength_callable = CodeFactory::ToLength(isolate); |
| + var_lastindex.Bind( |
| + a->CallStub(tolength_callable, context, regexp_lastindex)); |
| + a->Goto(&next); |
| + } |
| + |
| + a->Bind(&next); |
| + } |
| + |
| + Node* const lastindex = var_lastindex.value(); |
| CLabel if_isoob(a, CLabel::kDeferred); |
| a->GotoUnless(a->TaggedIsSmi(lastindex), &if_isoob); |
| @@ -390,9 +368,9 @@ Node* RegExpPrototypeExecInternal(CodeStubAssembler* a, Node* context, |
| a->Bind(&if_isoob); |
| { |
| - StoreLastIndex(a, context, has_initialmap, regexp, smi_zero); |
| + StoreLastIndex(a, context, regexp, smi_zero, is_fastpath); |
| var_result.Bind(null); |
| - a->Goto(&out); |
| + a->Goto(if_didnotmatch); |
| } |
| } |
| @@ -415,64 +393,71 @@ Node* RegExpPrototypeExecInternal(CodeStubAssembler* a, Node* context, |
| Callable exec_callable = CodeFactory::RegExpExec(isolate); |
| match_indices = a->CallStub(exec_callable, context, regexp, string, |
| var_lastindex.value(), last_match_info); |
| + var_result.Bind(match_indices); |
| // {match_indices} is either null or the RegExpMatchInfo array. |
| // Return early if exec failed, possibly updating last index. |
| a->GotoUnless(a->WordEqual(match_indices, null), &successful_match); |
| - CLabel return_null(a); |
| - a->GotoUnless(should_update_last_index, &return_null); |
| - |
| - StoreLastIndex(a, context, has_initialmap, regexp, smi_zero); |
| - a->Goto(&return_null); |
| + a->GotoUnless(should_update_last_index, if_didnotmatch); |
| - a->Bind(&return_null); |
| - var_result.Bind(null); |
| - a->Goto(&out); |
| + StoreLastIndex(a, context, regexp, smi_zero, is_fastpath); |
| + a->Goto(if_didnotmatch); |
| } |
| - CLabel construct_result(a); |
| a->Bind(&successful_match); |
| { |
| - a->GotoUnless(should_update_last_index, &construct_result); |
| + a->GotoUnless(should_update_last_index, &out); |
| // Update the new last index from {match_indices}. |
| Node* const new_lastindex = a->LoadFixedArrayElement( |
| match_indices, |
| a->IntPtrConstant(RegExpMatchInfo::kFirstCaptureIndex + 1)); |
| - StoreLastIndex(a, context, has_initialmap, regexp, new_lastindex); |
| - a->Goto(&construct_result); |
| - |
| - a->Bind(&construct_result); |
| - { |
| - Node* result = ConstructNewResultFromMatchInfo(isolate, a, context, |
| - match_indices, string); |
| - var_result.Bind(result); |
| - a->Goto(&out); |
| - } |
| + StoreLastIndex(a, context, regexp, new_lastindex, is_fastpath); |
| + a->Goto(&out); |
| } |
| a->Bind(&out); |
| return var_result.value(); |
| } |
| -} // namespace |
| - |
| // ES#sec-regexp.prototype.exec |
| // RegExp.prototype.exec ( string ) |
| -void Builtins::Generate_RegExpPrototypeExec(CodeAssemblerState* state) { |
| - CodeStubAssembler a(state); |
| +Node* Generate_RegExpPrototypeExecBody(CodeStubAssembler* a, |
|
Igor Sheludko
2016/11/30 22:12:08
I think you can drop the Generate_ prefix for read
jgruber
2016/12/01 09:09:09
Done.
|
| + Node* const context, Node* const regexp, |
| + Node* const string, |
| + const bool is_fastpath) { |
| + Isolate* const isolate = a->isolate(); |
| + Node* const null = a->NullConstant(); |
| - Node* const maybe_receiver = a.Parameter(0); |
| - Node* const maybe_string = a.Parameter(1); |
| - Node* const context = a.Parameter(4); |
| + CVariable var_result(a, MachineRepresentation::kTagged); |
| - Node* const result = |
| - RegExpPrototypeExecInternal(&a, context, maybe_receiver, maybe_string); |
| - a.Return(result); |
| + CLabel if_didnotmatch(a), out(a); |
| + Node* const indices_or_null = Generate_RegExpPrototypeExecBodyWithoutResult( |
| + a, context, regexp, string, &if_didnotmatch, is_fastpath); |
| + |
| + // Successful match. |
| + { |
| + Node* const match_indices = indices_or_null; |
| + Node* const result = ConstructNewResultFromMatchInfo(isolate, a, context, |
| + match_indices, string); |
| + var_result.Bind(result); |
| + a->Goto(&out); |
| + } |
| + |
| + a->Bind(&if_didnotmatch); |
| + { |
| + var_result.Bind(null); |
| + a->Goto(&out); |
| + } |
| + |
| + a->Bind(&out); |
| + return var_result.value(); |
| } |
| +} // namespace |
| + |
| namespace { |
| Node* ThrowIfNotJSReceiver(CodeStubAssembler* a, Isolate* isolate, |
| @@ -562,6 +547,42 @@ void BranchIfFastRegExpResult(CodeStubAssembler* a, Node* context, Node* map, |
| } // namespace |
| +// ES#sec-regexp.prototype.exec |
| +// RegExp.prototype.exec ( string ) |
| +void Builtins::Generate_RegExpPrototypeExec(CodeAssemblerState* state) { |
| + CodeStubAssembler a(state); |
| + |
| + Node* const maybe_receiver = a.Parameter(0); |
| + Node* const maybe_string = a.Parameter(1); |
| + Node* const context = a.Parameter(4); |
| + |
| + // Ensure {maybe_receiver} is a JSRegExp. |
| + Node* const regexp_map = a.ThrowIfNotInstanceType( |
| + context, maybe_receiver, JS_REGEXP_TYPE, "RegExp.prototype.exec"); |
| + Node* const receiver = maybe_receiver; |
| + |
| + // Convert {maybe_string} to a String. |
| + Node* const string = a.ToString(context, maybe_string); |
| + |
| + CLabel if_isfastpath(&a), if_isslowpath(&a); |
| + a.Branch(IsInitialRegExpMap(&a, context, regexp_map), &if_isfastpath, |
| + &if_isslowpath); |
| + |
| + a.Bind(&if_isfastpath); |
| + { |
| + Node* const result = |
| + Generate_RegExpPrototypeExecBody(&a, context, receiver, string, true); |
| + a.Return(result); |
| + } |
| + |
| + a.Bind(&if_isslowpath); |
| + { |
| + Node* const result = |
| + Generate_RegExpPrototypeExecBody(&a, context, receiver, string, false); |
| + a.Return(result); |
| + } |
| +} |
| + |
| void Builtins::Generate_RegExpPrototypeFlagsGetter(CodeAssemblerState* state) { |
| CodeStubAssembler a(state); |
| @@ -1033,19 +1054,20 @@ Node* RegExpExec(CodeStubAssembler* a, Node* context, Node* recv, |
| Node* const null = a->NullConstant(); |
| CVariable var_result(a, MachineRepresentation::kTagged); |
| - CLabel out(a), call_builtin_exec(a), slow_path(a, CLabel::kDeferred); |
| + CLabel out(a), if_isfastpath(a), if_isslowpath(a); |
| Node* const map = a->LoadMap(recv); |
| - BranchIfFastPath(a, context, map, &call_builtin_exec, &slow_path); |
| + BranchIfFastPath(a, context, map, &if_isfastpath, &if_isslowpath); |
| - a->Bind(&call_builtin_exec); |
| + a->Bind(&if_isfastpath); |
| { |
| - Node* const result = RegExpPrototypeExecInternal(a, context, recv, string); |
| + Node* const result = |
| + Generate_RegExpPrototypeExecBody(a, context, recv, string, true); |
| var_result.Bind(result); |
| a->Goto(&out); |
| } |
| - a->Bind(&slow_path); |
| + a->Bind(&if_isslowpath); |
| { |
| // Take the slow path of fetching the exec property, calling it, and |
| // verifying its return value. |
| @@ -1082,7 +1104,11 @@ Node* RegExpExec(CodeStubAssembler* a, Node* context, Node* recv, |
| { |
| a->ThrowIfNotInstanceType(context, recv, JS_REGEXP_TYPE, |
| "RegExp.prototype.exec"); |
| - a->Goto(&call_builtin_exec); |
| + |
| + Node* const result = |
| + Generate_RegExpPrototypeExecBody(a, context, recv, string, false); |
| + var_result.Bind(result); |
| + a->Goto(&out); |
| } |
| } |
| @@ -1307,9 +1333,9 @@ void Generate_RegExpPrototypeMatchBody(CodeStubAssembler* a, |
| a->Bind(&if_isnotglobal); |
| { |
| - Node* const result = |
| - is_fastpath ? RegExpPrototypeExecInternal(a, context, regexp, string) |
| - : RegExpExec(a, context, regexp, string); |
| + Node* const result = is_fastpath ? Generate_RegExpPrototypeExecBody( |
| + a, context, regexp, string, true) |
| + : RegExpExec(a, context, regexp, string); |
| a->Return(result); |
| } |
| @@ -1318,11 +1344,7 @@ void Generate_RegExpPrototypeMatchBody(CodeStubAssembler* a, |
| Node* const is_unicode = |
| FlagGetter(a, context, regexp, JSRegExp::kUnicode, is_fastpath); |
| - if (is_fastpath) { |
| - FastStoreLastIndex(a, context, regexp, smi_zero); |
| - } else { |
| - SlowStoreLastIndex(a, context, regexp, smi_zero); |
| - } |
| + StoreLastIndex(a, context, regexp, smi_zero, is_fastpath); |
| // Allocate an array to store the resulting match strings. |
| @@ -1338,9 +1360,9 @@ void Generate_RegExpPrototypeMatchBody(CodeStubAssembler* a, |
| a->Bind(&loop); |
| { |
| - Node* const result = |
| - is_fastpath ? RegExpPrototypeExecInternal(a, context, regexp, string) |
| - : RegExpExec(a, context, regexp, string); |
| + Node* const result = is_fastpath ? Generate_RegExpPrototypeExecBody( |
| + a, context, regexp, string, true) |
| + : RegExpExec(a, context, regexp, string); |
| CLabel if_didmatch(a), if_didnotmatch(a); |
| a->Branch(a->WordEqual(result, null), &if_didnotmatch, &if_didmatch); |
| @@ -1415,8 +1437,7 @@ void Generate_RegExpPrototypeMatchBody(CodeStubAssembler* a, |
| Node* const match_length = a->LoadStringLength(match); |
| a->GotoUnless(a->SmiEqual(match_length, smi_zero), &loop); |
| - Node* last_index = is_fastpath ? FastLoadLastIndex(a, context, regexp) |
| - : SlowLoadLastIndex(a, context, regexp); |
| + Node* last_index = LoadLastIndex(a, context, regexp, is_fastpath); |
| Callable tolength_callable = CodeFactory::ToLength(isolate); |
| last_index = a->CallStub(tolength_callable, context, last_index); |
| @@ -1424,11 +1445,7 @@ void Generate_RegExpPrototypeMatchBody(CodeStubAssembler* a, |
| Node* const new_last_index = |
| AdvanceStringIndex(a, string, last_index, is_unicode); |
| - if (is_fastpath) { |
| - FastStoreLastIndex(a, context, regexp, new_last_index); |
| - } else { |
| - SlowStoreLastIndex(a, context, regexp, new_last_index); |
| - } |
| + StoreLastIndex(a, context, regexp, new_last_index, is_fastpath); |
| a->Goto(&loop); |
| } |
| @@ -1486,12 +1503,11 @@ void Generate_RegExpPrototypeSearchBody(CodeStubAssembler* a, |
| // Grab the initial value of last index. |
| Node* const previous_last_index = |
| - is_fastpath ? FastLoadLastIndex(a, context, receiver) |
| - : SlowLoadLastIndex(a, context, receiver); |
| + LoadLastIndex(a, context, receiver, is_fastpath); |
| // Ensure last index is 0. |
| if (is_fastpath) { |
| - FastStoreLastIndex(a, context, receiver, smi_zero); |
| + FastStoreLastIndex(a, receiver, smi_zero); |
| } else { |
| CLabel next(a); |
| a->GotoIf(a->SameValue(previous_last_index, smi_zero, context), &next); |
| @@ -1503,12 +1519,13 @@ void Generate_RegExpPrototypeSearchBody(CodeStubAssembler* a, |
| // Call exec. |
| Node* const match_indices = |
| - is_fastpath ? RegExpPrototypeExecInternal(a, context, receiver, string) |
| - : RegExpExec(a, context, receiver, string); |
| + is_fastpath |
| + ? Generate_RegExpPrototypeExecBody(a, context, receiver, string, true) |
| + : RegExpExec(a, context, receiver, string); |
| // Reset last index if necessary. |
| if (is_fastpath) { |
| - FastStoreLastIndex(a, context, receiver, previous_last_index); |
| + FastStoreLastIndex(a, receiver, previous_last_index); |
| } else { |
| CLabel next(a); |
| Node* const current_last_index = SlowLoadLastIndex(a, context, receiver); |
| @@ -1934,7 +1951,7 @@ Node* ReplaceGlobalCallableFastPath(CodeStubAssembler* a, Node* context, |
| CVariable var_result(a, MachineRepresentation::kTagged); |
| // Set last index to 0. |
| - FastStoreLastIndex(a, context, regexp, smi_zero); |
| + FastStoreLastIndex(a, regexp, smi_zero); |
| // Allocate {result_array}. |
| Node* result_array; |
| @@ -1958,7 +1975,7 @@ Node* ReplaceGlobalCallableFastPath(CodeStubAssembler* a, Node* context, |
| subject_string, last_match_info, result_array); |
| // Reset last index to 0. |
| - FastStoreLastIndex(a, context, regexp, smi_zero); |
| + FastStoreLastIndex(a, regexp, smi_zero); |
| // If no matches, return the subject string. |
| var_result.Bind(subject_string); |
| @@ -2161,7 +2178,7 @@ Node* ReplaceSimpleStringFastPath(CodeStubAssembler* a, Node* context, |
| a->Bind(&if_isglobal); |
| { |
| // Hand off global regexps to runtime. |
| - FastStoreLastIndex(a, context, regexp, smi_zero); |
| + FastStoreLastIndex(a, regexp, smi_zero); |
| Node* const result = |
| a->CallRuntime(Runtime::kStringReplaceGlobalRegExpWithString, context, |
| subject_string, regexp, replace_string, last_match_info); |
| @@ -2182,7 +2199,7 @@ Node* ReplaceSimpleStringFastPath(CodeStubAssembler* a, Node* context, |
| a->Bind(&if_didnotmatch); |
| { |
| - FastStoreLastIndex(a, context, regexp, smi_zero); |
| + FastStoreLastIndex(a, regexp, smi_zero); |
| var_result.Bind(subject_string); |
| a->Goto(&out); |
| } |