Index: src/builtins/builtins-regexp.cc |
diff --git a/src/builtins/builtins-regexp.cc b/src/builtins/builtins-regexp.cc |
index d993e82f2eae7ee0a2545f712ae43f22b0810db6..57ff578f51559feae7de67a8d8ab1e31d0332b7d 100644 |
--- a/src/builtins/builtins-regexp.cc |
+++ b/src/builtins/builtins-regexp.cc |
@@ -1144,57 +1144,106 @@ BUILTIN(RegExpPrototypeMatch) { |
// ES#sec-regexp.prototype-@@search |
// RegExp.prototype [ @@search ] ( string ) |
-BUILTIN(RegExpPrototypeSearch) { |
- HandleScope scope(isolate); |
- CHECK_RECEIVER(JSReceiver, recv, "RegExp.prototype.@@search"); |
+void Builtins::Generate_RegExpPrototypeSearch(CodeStubAssembler* a) { |
+ typedef CodeStubAssembler::Label Label; |
+ typedef compiler::Node Node; |
- Handle<Object> string_obj = args.atOrUndefined(isolate, 1); |
+ Isolate* const isolate = a->isolate(); |
- Handle<String> string; |
- ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, string, |
- Object::ToString(isolate, string_obj)); |
+ Node* const maybe_receiver = a->Parameter(0); |
+ Node* const maybe_string = a->Parameter(1); |
+ Node* const context = a->Parameter(4); |
+ |
+ Node* const smi_zero = a->SmiConstant(Smi::kZero); |
+ |
+ // TODO(jgruber): If we need to optimize this further, we could add an entire |
+ // separate fast-path, skipping all follow-up map checks since we can |
+ // guarantee they won't change. The downside is more code to maintain. |
+ |
+ // Ensure {maybe_receiver} is a JSReceiver. |
+ Node* const map = |
+ ThrowIfNotJSReceiver(a, isolate, context, maybe_receiver, |
+ MessageTemplate::kIncompatibleMethodReceiver, |
+ "RegExp.prototype.@@search"); |
+ Node* const receiver = maybe_receiver; |
+ |
+ // Convert {maybe_string} to a String. |
+ Node* const string = a->ToString(context, maybe_string); |
+ |
+ // Grab the initial value of last index. |
+ Node* has_initialmap = IsInitialRegExpMap(a, context, map); |
+ Node* const previous_last_index = |
+ LoadLastIndex(a, context, has_initialmap, receiver); |
+ |
+ // Ensure last index is 0. |
+ { |
+ Label next(a); |
+ a->GotoIf(a->WordEqual(previous_last_index, smi_zero), &next); |
- Handle<Object> previous_last_index_obj; |
- ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, previous_last_index_obj, |
- RegExpUtils::GetLastIndex(isolate, recv)); |
+ StoreLastIndex(a, context, has_initialmap, receiver, smi_zero); |
Yang
2016/10/25 07:30:12
The spec actually suggests to always set the last
jgruber
2016/10/25 08:40:57
The spec has changed fairly recently to avoid unne
|
+ a->Goto(&next); |
- if (!previous_last_index_obj->IsSmi() || |
- Smi::cast(*previous_last_index_obj)->value() != 0) { |
- RETURN_FAILURE_ON_EXCEPTION(isolate, |
- RegExpUtils::SetLastIndex(isolate, recv, 0)); |
+ a->Bind(&next); |
} |
- Handle<Object> result; |
- ASSIGN_RETURN_FAILURE_ON_EXCEPTION( |
- isolate, result, |
- RegExpUtils::RegExpExec(isolate, recv, string, |
- isolate->factory()->undefined_value())); |
- |
- Handle<Object> current_last_index_obj; |
- ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, current_last_index_obj, |
- RegExpUtils::GetLastIndex(isolate, recv)); |
- |
- Maybe<bool> is_last_index_unchanged = |
- Object::Equals(current_last_index_obj, previous_last_index_obj); |
- if (is_last_index_unchanged.IsNothing()) return isolate->pending_exception(); |
Yang
2016/10/25 07:30:13
This does not seem correct. To throw an exception
jgruber
2016/10/25 08:40:57
Great catch. Looking at this further, we actually
|
- if (!is_last_index_unchanged.FromJust()) { |
- if (previous_last_index_obj->IsSmi()) { |
- RETURN_FAILURE_ON_EXCEPTION( |
- isolate, |
- RegExpUtils::SetLastIndex( |
- isolate, recv, Smi::cast(*previous_last_index_obj)->value())); |
- } else { |
- RETURN_FAILURE_ON_EXCEPTION( |
- isolate, |
- Object::SetProperty(recv, isolate->factory()->lastIndex_string(), |
- previous_last_index_obj, STRICT)); |
- } |
+ // Call exec. |
+ Node* const match_indices = RegExpExec(a, context, receiver, string); |
+ |
+ // Reset last index if necessary. |
+ { |
+ Label next(a); |
+ |
+ // Recheck the map since it might have changed. |
+ has_initialmap = IsInitialRegExpMap(a, context, map); |
+ |
+ Node* const current_last_index = |
+ LoadLastIndex(a, context, has_initialmap, receiver); |
Yang
2016/10/25 07:30:12
Again, the spec suggests to always overwrite the l
jgruber
2016/10/25 08:40:57
The spec has changed recently (see above).
|
+ |
+ a->GotoIf(a->WordEqual(current_last_index, previous_last_index), &next); |
+ |
+ StoreLastIndex(a, context, has_initialmap, receiver, previous_last_index); |
+ a->Goto(&next); |
+ |
+ a->Bind(&next); |
} |
- if (result->IsNull(isolate)) return Smi::FromInt(-1); |
+ // Return -1 if no match was found. |
+ { |
+ Label next(a); |
+ a->GotoUnless(a->WordEqual(match_indices, a->NullConstant()), &next); |
+ a->Return(a->SmiConstant(Smi::FromInt(-1))); |
+ a->Bind(&next); |
+ } |
+ |
+ // Return the index of the match. |
+ { |
+ Label fast_path(a), slow_path(a, Label::kDeferred); |
+ |
+ Node* const native_context = a->LoadNativeContext(context); |
+ Node* const initial_regexp_result_map = |
+ a->LoadContextElement(native_context, Context::REGEXP_RESULT_MAP_INDEX); |
+ Node* const match_indices_map = a->LoadMap(match_indices); |
+ |
+ a->Branch(a->WordEqual(match_indices_map, initial_regexp_result_map), |
+ &fast_path, &slow_path); |
- RETURN_RESULT_OR_FAILURE( |
- isolate, Object::GetProperty(result, isolate->factory()->index_string())); |
+ a->Bind(&fast_path); |
+ { |
+ Node* const index = |
+ a->LoadObjectField(match_indices, JSRegExpResult::kIndexOffset, |
+ MachineType::AnyTagged()); |
+ a->Return(index); |
+ } |
+ |
+ a->Bind(&slow_path); |
+ { |
+ Node* const name = a->HeapConstant(isolate->factory()->index_string()); |
+ Callable getproperty_callable = CodeFactory::GetProperty(a->isolate()); |
+ Node* const index = |
+ a->CallStub(getproperty_callable, context, match_indices, name); |
+ a->Return(index); |
+ } |
+ } |
} |
namespace { |