Index: src/builtins/builtins-regexp.cc |
diff --git a/src/builtins/builtins-regexp.cc b/src/builtins/builtins-regexp.cc |
index 259449e6015e056aa800ad56a844c063cb4a508a..fe12bfce32ba8c18cee908442f2d1301e75fc82a 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, |
@@ -328,33 +299,31 @@ Node* ConstructNewResultFromMatchInfo(Isolate* isolate, CodeStubAssembler* a, |
// ES#sec-regexp.prototype.exec |
// RegExp.prototype.exec ( string ) |
-Node* RegExpPrototypeExecInternal(CodeStubAssembler* a, Node* context, |
- Node* maybe_receiver, Node* maybe_string) { |
+// Implements the core of RegExp.prototype.exec but without actually |
+// constructing the JSRegExpResult. Returns either null (if the RegExp did not |
+// match) or a fixed array containing match indices as returned by |
+// RegExpExecStub. |
+Node* RegExpPrototypeExecBodyWithoutResult( |
+ 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 +345,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 +374,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 +399,70 @@ 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* RegExpPrototypeExecBody(CodeStubAssembler* a, 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 = 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 +552,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 = |
+ RegExpPrototypeExecBody(&a, context, receiver, string, true); |
+ a.Return(result); |
+ } |
+ |
+ a.Bind(&if_isslowpath); |
+ { |
+ Node* const result = |
+ RegExpPrototypeExecBody(&a, context, receiver, string, false); |
+ a.Return(result); |
+ } |
+} |
+ |
void Builtins::Generate_RegExpPrototypeFlagsGetter(CodeAssemblerState* state) { |
CodeStubAssembler a(state); |
@@ -1033,19 +1059,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 = |
+ 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 +1109,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 = |
+ RegExpPrototypeExecBody(a, context, recv, string, false); |
+ var_result.Bind(result); |
+ a->Goto(&out); |
} |
} |
@@ -1308,7 +1339,7 @@ void Generate_RegExpPrototypeMatchBody(CodeStubAssembler* a, |
a->Bind(&if_isnotglobal); |
{ |
Node* const result = |
- is_fastpath ? RegExpPrototypeExecInternal(a, context, regexp, string) |
+ is_fastpath ? RegExpPrototypeExecBody(a, context, regexp, string, true) |
: RegExpExec(a, context, regexp, string); |
a->Return(result); |
} |
@@ -1318,11 +1349,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 +1365,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 ? 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 +1442,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 +1450,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 +1508,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 +1524,12 @@ void Generate_RegExpPrototypeSearchBody(CodeStubAssembler* a, |
// Call exec. |
Node* const match_indices = |
- is_fastpath ? RegExpPrototypeExecInternal(a, context, receiver, string) |
+ is_fastpath ? 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); |
@@ -1935,7 +1956,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; |
@@ -1959,7 +1980,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); |
@@ -2162,7 +2183,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); |
@@ -2183,7 +2204,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); |
} |