diff options
Diffstat (limited to 'deps/v8/src/builtins/builtins-regexp.cc')
-rw-r--r-- | deps/v8/src/builtins/builtins-regexp.cc | 3066 |
1 files changed, 1746 insertions, 1320 deletions
diff --git a/deps/v8/src/builtins/builtins-regexp.cc b/deps/v8/src/builtins/builtins-regexp.cc index 5f8d18be43..2191268441 100644 --- a/deps/v8/src/builtins/builtins-regexp.cc +++ b/deps/v8/src/builtins/builtins-regexp.cc @@ -2,10 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "src/builtins/builtins-constructor.h" #include "src/builtins/builtins-utils.h" #include "src/builtins/builtins.h" - #include "src/code-factory.h" +#include "src/code-stub-assembler.h" #include "src/regexp/jsregexp.h" #include "src/regexp/regexp-utils.h" #include "src/string-builder.h" @@ -13,541 +14,400 @@ namespace v8 { namespace internal { +typedef compiler::Node Node; +typedef CodeStubAssembler::ParameterMode ParameterMode; +typedef compiler::CodeAssemblerState CodeAssemblerState; + +class RegExpBuiltinsAssembler : public CodeStubAssembler { + public: + explicit RegExpBuiltinsAssembler(CodeAssemblerState* state) + : CodeStubAssembler(state) {} + + protected: + Node* FastLoadLastIndex(Node* regexp); + Node* SlowLoadLastIndex(Node* context, Node* regexp); + Node* LoadLastIndex(Node* context, Node* regexp, bool is_fastpath); + + void FastStoreLastIndex(Node* regexp, Node* value); + void SlowStoreLastIndex(Node* context, Node* regexp, Node* value); + void StoreLastIndex(Node* context, Node* regexp, Node* value, + bool is_fastpath); + + Node* ConstructNewResultFromMatchInfo(Node* context, Node* match_info, + Node* string); + + Node* RegExpPrototypeExecBodyWithoutResult(Node* const context, + Node* const regexp, + Node* const string, + Label* if_didnotmatch, + const bool is_fastpath); + Node* RegExpPrototypeExecBody(Node* const context, Node* const regexp, + Node* const string, const bool is_fastpath); + + Node* ThrowIfNotJSReceiver(Node* context, Node* maybe_receiver, + MessageTemplate::Template msg_template, + char const* method_name); + + Node* IsInitialRegExpMap(Node* context, Node* map); + void BranchIfFastRegExp(Node* context, Node* map, Label* if_isunmodified, + Label* if_ismodified); + void BranchIfFastRegExpResult(Node* context, Node* map, + Label* if_isunmodified, Label* if_ismodified); + + Node* FlagsGetter(Node* const context, Node* const regexp, bool is_fastpath); + + Node* FastFlagGetter(Node* const regexp, JSRegExp::Flag flag); + Node* SlowFlagGetter(Node* const context, Node* const regexp, + JSRegExp::Flag flag); + Node* FlagGetter(Node* const context, Node* const regexp, JSRegExp::Flag flag, + bool is_fastpath); + void FlagGetter(JSRegExp::Flag flag, v8::Isolate::UseCounterFeature counter, + const char* method_name); + + Node* IsRegExp(Node* const context, Node* const maybe_receiver); + Node* RegExpInitialize(Node* const context, Node* const regexp, + Node* const maybe_pattern, Node* const maybe_flags); + + Node* RegExpExec(Node* context, Node* regexp, Node* string); + + Node* AdvanceStringIndex(Node* const string, Node* const index, + Node* const is_unicode); + + void RegExpPrototypeMatchBody(Node* const context, Node* const regexp, + Node* const string, const bool is_fastpath); + + void RegExpPrototypeSearchBodyFast(Node* const context, Node* const regexp, + Node* const string); + void RegExpPrototypeSearchBodySlow(Node* const context, Node* const regexp, + Node* const string); + + void RegExpPrototypeSplitBody(Node* const context, Node* const regexp, + Node* const string, Node* const limit); + + Node* ReplaceGlobalCallableFastPath(Node* context, Node* regexp, Node* string, + Node* replace_callable); + Node* ReplaceSimpleStringFastPath(Node* context, Node* regexp, Node* string, + Node* replace_string); +}; + // ----------------------------------------------------------------------------- // ES6 section 21.2 RegExp Objects -namespace { - -Handle<String> PatternFlags(Isolate* isolate, Handle<JSRegExp> regexp) { - static const int kMaxFlagsLength = 5 + 1; // 5 flags and '\0'; - char flags_string[kMaxFlagsLength]; - int i = 0; - - const JSRegExp::Flags flags = regexp->GetFlags(); - - if ((flags & JSRegExp::kGlobal) != 0) flags_string[i++] = 'g'; - if ((flags & JSRegExp::kIgnoreCase) != 0) flags_string[i++] = 'i'; - if ((flags & JSRegExp::kMultiline) != 0) flags_string[i++] = 'm'; - if ((flags & JSRegExp::kUnicode) != 0) flags_string[i++] = 'u'; - if ((flags & JSRegExp::kSticky) != 0) flags_string[i++] = 'y'; - - DCHECK_LT(i, kMaxFlagsLength); - memset(&flags_string[i], '\0', kMaxFlagsLength - i); - - return isolate->factory()->NewStringFromAsciiChecked(flags_string); -} - -// ES#sec-regexpinitialize -// Runtime Semantics: RegExpInitialize ( obj, pattern, flags ) -MUST_USE_RESULT MaybeHandle<JSRegExp> RegExpInitialize(Isolate* isolate, - Handle<JSRegExp> regexp, - Handle<Object> pattern, - Handle<Object> flags) { - Handle<String> pattern_string; - if (pattern->IsUndefined(isolate)) { - pattern_string = isolate->factory()->empty_string(); - } else { - ASSIGN_RETURN_ON_EXCEPTION(isolate, pattern_string, - Object::ToString(isolate, pattern), JSRegExp); - } - - Handle<String> flags_string; - if (flags->IsUndefined(isolate)) { - flags_string = isolate->factory()->empty_string(); - } else { - ASSIGN_RETURN_ON_EXCEPTION(isolate, flags_string, - Object::ToString(isolate, flags), JSRegExp); - } - - // TODO(jgruber): We could avoid the flags back and forth conversions. - return JSRegExp::Initialize(regexp, pattern_string, flags_string); -} - -} // namespace - -// ES#sec-regexp-pattern-flags -// RegExp ( pattern, flags ) -BUILTIN(RegExpConstructor) { - HandleScope scope(isolate); - - Handle<HeapObject> new_target = args.new_target(); - Handle<Object> pattern = args.atOrUndefined(isolate, 1); - Handle<Object> flags = args.atOrUndefined(isolate, 2); - - Handle<JSFunction> target = isolate->regexp_function(); - - bool pattern_is_regexp; - { - Maybe<bool> maybe_pattern_is_regexp = - RegExpUtils::IsRegExp(isolate, pattern); - if (maybe_pattern_is_regexp.IsNothing()) { - DCHECK(isolate->has_pending_exception()); - return isolate->heap()->exception(); - } - pattern_is_regexp = maybe_pattern_is_regexp.FromJust(); - } - - if (new_target->IsUndefined(isolate)) { - new_target = target; - - // ES6 section 21.2.3.1 step 3.b - if (pattern_is_regexp && flags->IsUndefined(isolate)) { - Handle<Object> pattern_constructor; - ASSIGN_RETURN_FAILURE_ON_EXCEPTION( - isolate, pattern_constructor, - Object::GetProperty(pattern, - isolate->factory()->constructor_string())); - - if (pattern_constructor.is_identical_to(new_target)) { - return *pattern; - } - } - } - - if (pattern->IsJSRegExp()) { - Handle<JSRegExp> regexp_pattern = Handle<JSRegExp>::cast(pattern); - - if (flags->IsUndefined(isolate)) { - flags = PatternFlags(isolate, regexp_pattern); - } - pattern = handle(regexp_pattern->source(), isolate); - } else if (pattern_is_regexp) { - Handle<Object> pattern_source; - ASSIGN_RETURN_FAILURE_ON_EXCEPTION( - isolate, pattern_source, - Object::GetProperty(pattern, isolate->factory()->source_string())); - - if (flags->IsUndefined(isolate)) { - ASSIGN_RETURN_FAILURE_ON_EXCEPTION( - isolate, flags, - Object::GetProperty(pattern, isolate->factory()->flags_string())); - } - pattern = pattern_source; - } - - Handle<JSReceiver> new_target_receiver = Handle<JSReceiver>::cast(new_target); - - // TODO(jgruber): Fast-path for target == new_target == unmodified JSRegExp. - - Handle<JSObject> object; - ASSIGN_RETURN_FAILURE_ON_EXCEPTION( - isolate, object, JSObject::New(target, new_target_receiver)); - Handle<JSRegExp> regexp = Handle<JSRegExp>::cast(object); - - RETURN_RESULT_OR_FAILURE(isolate, - RegExpInitialize(isolate, regexp, pattern, flags)); -} - -BUILTIN(RegExpPrototypeCompile) { - HandleScope scope(isolate); - CHECK_RECEIVER(JSRegExp, regexp, "RegExp.prototype.compile"); - - Handle<Object> pattern = args.atOrUndefined(isolate, 1); - Handle<Object> flags = args.atOrUndefined(isolate, 2); - - if (pattern->IsJSRegExp()) { - Handle<JSRegExp> pattern_regexp = Handle<JSRegExp>::cast(pattern); - - if (!flags->IsUndefined(isolate)) { - THROW_NEW_ERROR_RETURN_FAILURE( - isolate, NewTypeError(MessageTemplate::kRegExpFlags)); - } - - flags = PatternFlags(isolate, pattern_regexp); - ASSIGN_RETURN_FAILURE_ON_EXCEPTION( - isolate, pattern, - Object::GetProperty(pattern, isolate->factory()->source_string())); - } - - ASSIGN_RETURN_FAILURE_ON_EXCEPTION( - isolate, regexp, RegExpInitialize(isolate, regexp, pattern, flags)); - - // Return undefined for compatibility with JSC. - // See http://crbug.com/585775 for web compat details. - - return isolate->heap()->undefined_value(); -} - -namespace { - -compiler::Node* FastLoadLastIndex(CodeStubAssembler* a, compiler::Node* context, - compiler::Node* regexp) { +Node* RegExpBuiltinsAssembler::FastLoadLastIndex(Node* regexp) { // Load the in-object field. static const int field_offset = JSRegExp::kSize + JSRegExp::kLastIndexFieldIndex * kPointerSize; - return a->LoadObjectField(regexp, field_offset); + return LoadObjectField(regexp, field_offset); } -compiler::Node* SlowLoadLastIndex(CodeStubAssembler* a, compiler::Node* context, - compiler::Node* regexp) { +Node* RegExpBuiltinsAssembler::SlowLoadLastIndex(Node* context, Node* regexp) { // Load through the GetProperty stub. - typedef compiler::Node Node; - - Node* const name = - a->HeapConstant(a->isolate()->factory()->lastIndex_string()); - Callable getproperty_callable = CodeFactory::GetProperty(a->isolate()); - return a->CallStub(getproperty_callable, context, regexp, name); + Node* const name = HeapConstant(isolate()->factory()->lastIndex_string()); + Callable getproperty_callable = CodeFactory::GetProperty(isolate()); + return CallStub(getproperty_callable, context, regexp, name); } -compiler::Node* LoadLastIndex(CodeStubAssembler* a, compiler::Node* context, - compiler::Node* has_initialmap, - compiler::Node* regexp) { - typedef CodeStubAssembler::Variable Variable; - typedef CodeStubAssembler::Label Label; - - Variable var_value(a, MachineRepresentation::kTagged); - - Label 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* RegExpBuiltinsAssembler::LoadLastIndex(Node* context, Node* regexp, + bool is_fastpath) { + return is_fastpath ? FastLoadLastIndex(regexp) + : SlowLoadLastIndex(context, regexp); } // The fast-path of StoreLastIndex when regexp is guaranteed to be an unmodified // JSRegExp instance. -void FastStoreLastIndex(CodeStubAssembler* a, compiler::Node* context, - compiler::Node* regexp, compiler::Node* value) { +void RegExpBuiltinsAssembler::FastStoreLastIndex(Node* regexp, Node* value) { // Store the in-object field. static const int field_offset = JSRegExp::kSize + JSRegExp::kLastIndexFieldIndex * kPointerSize; - a->StoreObjectField(regexp, field_offset, value); + StoreObjectField(regexp, field_offset, value); } -void SlowStoreLastIndex(CodeStubAssembler* a, compiler::Node* context, - compiler::Node* regexp, compiler::Node* value) { +void RegExpBuiltinsAssembler::SlowStoreLastIndex(Node* context, Node* regexp, + Node* value) { // Store through runtime. // TODO(ishell): Use SetPropertyStub here once available. - typedef compiler::Node Node; - - Node* const name = - a->HeapConstant(a->isolate()->factory()->lastIndex_string()); - Node* const language_mode = a->SmiConstant(Smi::FromInt(STRICT)); - a->CallRuntime(Runtime::kSetProperty, context, regexp, name, value, - language_mode); + Node* const name = HeapConstant(isolate()->factory()->lastIndex_string()); + Node* const language_mode = SmiConstant(Smi::FromInt(STRICT)); + CallRuntime(Runtime::kSetProperty, context, regexp, name, value, + language_mode); } -void StoreLastIndex(CodeStubAssembler* a, compiler::Node* context, - compiler::Node* has_initialmap, compiler::Node* regexp, - compiler::Node* value) { - typedef CodeStubAssembler::Label Label; - - Label 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); - { - SlowStoreLastIndex(a, context, regexp, value); - a->Goto(&out); +void RegExpBuiltinsAssembler::StoreLastIndex(Node* context, Node* regexp, + Node* value, bool is_fastpath) { + if (is_fastpath) { + FastStoreLastIndex(regexp, value); + } else { + SlowStoreLastIndex(context, regexp, value); } - - a->Bind(&out); } -compiler::Node* ConstructNewResultFromMatchInfo(Isolate* isolate, - CodeStubAssembler* a, - compiler::Node* context, - compiler::Node* match_info, - compiler::Node* string) { - typedef CodeStubAssembler::Variable Variable; - typedef CodeStubAssembler::Label Label; - typedef compiler::Node Node; - - Label out(a); - - CodeStubAssembler::ParameterMode mode = CodeStubAssembler::INTPTR_PARAMETERS; - Node* const num_indices = a->SmiUntag(a->LoadFixedArrayElement( - match_info, a->IntPtrConstant(RegExpMatchInfo::kNumberOfCapturesIndex), 0, - mode)); - Node* const num_results = a->SmiTag(a->WordShr(num_indices, 1)); - Node* const start = a->LoadFixedArrayElement( - match_info, a->IntPtrConstant(RegExpMatchInfo::kFirstCaptureIndex), 0, - mode); - Node* const end = a->LoadFixedArrayElement( - match_info, a->IntPtrConstant(RegExpMatchInfo::kFirstCaptureIndex + 1), 0, - mode); +Node* RegExpBuiltinsAssembler::ConstructNewResultFromMatchInfo(Node* context, + Node* match_info, + Node* string) { + Label out(this); + + Node* const num_indices = SmiUntag(LoadFixedArrayElement( + match_info, RegExpMatchInfo::kNumberOfCapturesIndex)); + Node* const num_results = SmiTag(WordShr(num_indices, 1)); + Node* const start = + LoadFixedArrayElement(match_info, RegExpMatchInfo::kFirstCaptureIndex); + Node* const end = LoadFixedArrayElement( + match_info, RegExpMatchInfo::kFirstCaptureIndex + 1); // Calculate the substring of the first match before creating the result array // to avoid an unnecessary write barrier storing the first result. - Node* const first = a->SubString(context, string, start, end); + Node* const first = SubString(context, string, start, end); Node* const result = - a->AllocateRegExpResult(context, num_results, start, string); - Node* const result_elements = a->LoadElements(result); + AllocateRegExpResult(context, num_results, start, string); + Node* const result_elements = LoadElements(result); - a->StoreFixedArrayElement(result_elements, a->IntPtrConstant(0), first, - SKIP_WRITE_BARRIER); + StoreFixedArrayElement(result_elements, 0, first, SKIP_WRITE_BARRIER); - a->GotoIf(a->SmiEqual(num_results, a->SmiConstant(Smi::FromInt(1))), &out); + GotoIf(SmiEqual(num_results, SmiConstant(Smi::FromInt(1))), &out); // Store all remaining captures. - Node* const limit = a->IntPtrAdd( - a->IntPtrConstant(RegExpMatchInfo::kFirstCaptureIndex), num_indices); + Node* const limit = IntPtrAdd( + IntPtrConstant(RegExpMatchInfo::kFirstCaptureIndex), num_indices); - Variable var_from_cursor(a, MachineType::PointerRepresentation()); - Variable var_to_cursor(a, MachineType::PointerRepresentation()); + Variable var_from_cursor(this, MachineType::PointerRepresentation()); + Variable var_to_cursor(this, MachineType::PointerRepresentation()); - var_from_cursor.Bind( - a->IntPtrConstant(RegExpMatchInfo::kFirstCaptureIndex + 2)); - var_to_cursor.Bind(a->IntPtrConstant(1)); + var_from_cursor.Bind(IntPtrConstant(RegExpMatchInfo::kFirstCaptureIndex + 2)); + var_to_cursor.Bind(IntPtrConstant(1)); Variable* vars[] = {&var_from_cursor, &var_to_cursor}; - Label loop(a, 2, vars); + Label loop(this, 2, vars); - a->Goto(&loop); - a->Bind(&loop); + Goto(&loop); + Bind(&loop); { Node* const from_cursor = var_from_cursor.value(); Node* const to_cursor = var_to_cursor.value(); - Node* const start = a->LoadFixedArrayElement(match_info, from_cursor); + Node* const start = LoadFixedArrayElement(match_info, from_cursor); - Label next_iter(a); - a->GotoIf(a->SmiEqual(start, a->SmiConstant(Smi::FromInt(-1))), &next_iter); + Label next_iter(this); + GotoIf(SmiEqual(start, SmiConstant(Smi::FromInt(-1))), &next_iter); - Node* const from_cursor_plus1 = - a->IntPtrAdd(from_cursor, a->IntPtrConstant(1)); - Node* const end = a->LoadFixedArrayElement(match_info, from_cursor_plus1); + Node* const from_cursor_plus1 = IntPtrAdd(from_cursor, IntPtrConstant(1)); + Node* const end = LoadFixedArrayElement(match_info, from_cursor_plus1); - Node* const capture = a->SubString(context, string, start, end); - a->StoreFixedArrayElement(result_elements, to_cursor, capture); - a->Goto(&next_iter); + Node* const capture = SubString(context, string, start, end); + StoreFixedArrayElement(result_elements, to_cursor, capture); + Goto(&next_iter); - a->Bind(&next_iter); - var_from_cursor.Bind(a->IntPtrAdd(from_cursor, a->IntPtrConstant(2))); - var_to_cursor.Bind(a->IntPtrAdd(to_cursor, a->IntPtrConstant(1))); - a->Branch(a->UintPtrLessThan(var_from_cursor.value(), limit), &loop, &out); + Bind(&next_iter); + var_from_cursor.Bind(IntPtrAdd(from_cursor, IntPtrConstant(2))); + var_to_cursor.Bind(IntPtrAdd(to_cursor, IntPtrConstant(1))); + Branch(UintPtrLessThan(var_from_cursor.value(), limit), &loop, &out); } - a->Bind(&out); + Bind(&out); return result; } // ES#sec-regexp.prototype.exec // RegExp.prototype.exec ( string ) -compiler::Node* RegExpPrototypeExecInternal(CodeStubAssembler* a, - compiler::Node* context, - compiler::Node* maybe_receiver, - compiler::Node* maybe_string) { - typedef CodeStubAssembler::Variable Variable; - typedef CodeStubAssembler::Label Label; - typedef compiler::Node Node; +// 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* RegExpBuiltinsAssembler::RegExpPrototypeExecBodyWithoutResult( + Node* const context, Node* const regexp, Node* const string, + Label* if_didnotmatch, const bool is_fastpath) { + Isolate* const isolate = this->isolate(); + + Node* const null = NullConstant(); + Node* const int_zero = IntPtrConstant(0); + Node* const smi_zero = SmiConstant(Smi::kZero); + + if (!is_fastpath) { + ThrowIfNotInstanceType(context, regexp, JS_REGEXP_TYPE, + "RegExp.prototype.exec"); + } - Isolate* const isolate = a->isolate(); + CSA_ASSERT(this, IsStringInstanceType(LoadInstanceType(string))); + CSA_ASSERT(this, HasInstanceType(regexp, JS_REGEXP_TYPE)); - Node* const null = a->NullConstant(); - Node* const int_zero = a->IntPtrConstant(0); - Node* const smi_zero = a->SmiConstant(Smi::kZero); + Variable var_result(this, MachineRepresentation::kTagged); + Label out(this); - Variable var_result(a, MachineRepresentation::kTagged); - Label 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); + Node* const native_context = LoadNativeContext(context); + Node* const string_length = LoadStringLength(string); // Check whether the regexp is global or sticky, which determines whether we // update last index later on. - Node* const flags = a->LoadObjectField(regexp, JSRegExp::kFlagsOffset); - Node* const is_global_or_sticky = - a->WordAnd(a->SmiUntag(flags), - a->IntPtrConstant(JSRegExp::kGlobal | JSRegExp::kSticky)); + Node* const flags = LoadObjectField(regexp, JSRegExp::kFlagsOffset); + Node* const is_global_or_sticky = WordAnd( + SmiUntag(flags), IntPtrConstant(JSRegExp::kGlobal | JSRegExp::kSticky)); Node* const should_update_last_index = - a->WordNotEqual(is_global_or_sticky, int_zero); + WordNotEqual(is_global_or_sticky, int_zero); // Grab and possibly update last index. - Label run_exec(a); - Variable var_lastindex(a, MachineRepresentation::kTagged); + Label run_exec(this); + Variable var_lastindex(this, MachineRepresentation::kTagged); { - Label if_doupdate(a), if_dontupdate(a); - a->Branch(should_update_last_index, &if_doupdate, &if_dontupdate); + Label if_doupdate(this), if_dontupdate(this); + Branch(should_update_last_index, &if_doupdate, &if_dontupdate); - a->Bind(&if_doupdate); + Bind(&if_doupdate); { Node* const regexp_lastindex = - LoadLastIndex(a, context, has_initialmap, regexp); + LoadLastIndex(context, regexp, is_fastpath); + var_lastindex.Bind(regexp_lastindex); + + // Omit ToLength if lastindex is a non-negative smi. + { + Label call_tolength(this, Label::kDeferred), next(this); + Branch(TaggedIsPositiveSmi(regexp_lastindex), &next, &call_tolength); - Callable tolength_callable = CodeFactory::ToLength(isolate); - Node* const lastindex = - a->CallStub(tolength_callable, context, regexp_lastindex); - var_lastindex.Bind(lastindex); + Bind(&call_tolength); + { + Callable tolength_callable = CodeFactory::ToLength(isolate); + var_lastindex.Bind( + CallStub(tolength_callable, context, regexp_lastindex)); + Goto(&next); + } + + Bind(&next); + } - Label if_isoob(a, Label::kDeferred); - a->GotoUnless(a->TaggedIsSmi(lastindex), &if_isoob); - a->GotoUnless(a->SmiLessThanOrEqual(lastindex, string_length), &if_isoob); - a->Goto(&run_exec); + Node* const lastindex = var_lastindex.value(); - a->Bind(&if_isoob); + Label if_isoob(this, Label::kDeferred); + GotoUnless(TaggedIsSmi(lastindex), &if_isoob); + GotoUnless(SmiLessThanOrEqual(lastindex, string_length), &if_isoob); + Goto(&run_exec); + + Bind(&if_isoob); { - StoreLastIndex(a, context, has_initialmap, regexp, smi_zero); + StoreLastIndex(context, regexp, smi_zero, is_fastpath); var_result.Bind(null); - a->Goto(&out); + Goto(if_didnotmatch); } } - a->Bind(&if_dontupdate); + Bind(&if_dontupdate); { var_lastindex.Bind(smi_zero); - a->Goto(&run_exec); + Goto(&run_exec); } } Node* match_indices; - Label successful_match(a); - a->Bind(&run_exec); + Label successful_match(this); + Bind(&run_exec); { // Get last match info from the context. - Node* const last_match_info = a->LoadContextElement( + Node* const last_match_info = LoadContextElement( native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX); // Call the exec stub. Callable exec_callable = CodeFactory::RegExpExec(isolate); - match_indices = a->CallStub(exec_callable, context, regexp, string, - var_lastindex.value(), last_match_info); + match_indices = 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); - - Label return_null(a); - a->GotoUnless(should_update_last_index, &return_null); + GotoUnless(WordEqual(match_indices, null), &successful_match); - StoreLastIndex(a, context, has_initialmap, regexp, smi_zero); - a->Goto(&return_null); + GotoUnless(should_update_last_index, if_didnotmatch); - a->Bind(&return_null); - var_result.Bind(null); - a->Goto(&out); + StoreLastIndex(context, regexp, smi_zero, is_fastpath); + Goto(if_didnotmatch); } - Label construct_result(a); - a->Bind(&successful_match); + Bind(&successful_match); { - a->GotoUnless(should_update_last_index, &construct_result); + 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); + Node* const new_lastindex = LoadFixedArrayElement( + match_indices, RegExpMatchInfo::kFirstCaptureIndex + 1); - a->Bind(&construct_result); - { - Node* result = ConstructNewResultFromMatchInfo(isolate, a, context, - match_indices, string); - var_result.Bind(result); - a->Goto(&out); - } + StoreLastIndex(context, regexp, new_lastindex, is_fastpath); + Goto(&out); } - a->Bind(&out); + Bind(&out); return var_result.value(); } -} // namespace - // ES#sec-regexp.prototype.exec // RegExp.prototype.exec ( string ) -void Builtins::Generate_RegExpPrototypeExec(CodeStubAssembler* a) { - typedef compiler::Node Node; +Node* RegExpBuiltinsAssembler::RegExpPrototypeExecBody(Node* const context, + Node* const regexp, + Node* const string, + const bool is_fastpath) { + Node* const null = NullConstant(); - Node* const maybe_receiver = a->Parameter(0); - Node* const maybe_string = a->Parameter(1); - Node* const context = a->Parameter(4); + Variable var_result(this, MachineRepresentation::kTagged); - Node* const result = - RegExpPrototypeExecInternal(a, context, maybe_receiver, maybe_string); - a->Return(result); -} + Label if_didnotmatch(this), out(this); + Node* const indices_or_null = RegExpPrototypeExecBodyWithoutResult( + context, regexp, string, &if_didnotmatch, is_fastpath); -namespace { + // Successful match. + { + Node* const match_indices = indices_or_null; + Node* const result = + ConstructNewResultFromMatchInfo(context, match_indices, string); + var_result.Bind(result); + Goto(&out); + } -compiler::Node* ThrowIfNotJSReceiver(CodeStubAssembler* a, Isolate* isolate, - compiler::Node* context, - compiler::Node* value, - MessageTemplate::Template msg_template, - char const* method_name) { - typedef compiler::Node Node; - typedef CodeStubAssembler::Label Label; - typedef CodeStubAssembler::Variable Variable; + Bind(&if_didnotmatch); + { + var_result.Bind(null); + Goto(&out); + } - Label out(a), throw_exception(a, Label::kDeferred); - Variable var_value_map(a, MachineRepresentation::kTagged); + Bind(&out); + return var_result.value(); +} - a->GotoIf(a->TaggedIsSmi(value), &throw_exception); +Node* RegExpBuiltinsAssembler::ThrowIfNotJSReceiver( + Node* context, Node* maybe_receiver, MessageTemplate::Template msg_template, + char const* method_name) { + Label out(this), throw_exception(this, Label::kDeferred); + Variable var_value_map(this, MachineRepresentation::kTagged); + + GotoIf(TaggedIsSmi(maybe_receiver), &throw_exception); // Load the instance type of the {value}. - var_value_map.Bind(a->LoadMap(value)); - Node* const value_instance_type = - a->LoadMapInstanceType(var_value_map.value()); + var_value_map.Bind(LoadMap(maybe_receiver)); + Node* const value_instance_type = LoadMapInstanceType(var_value_map.value()); - a->Branch(a->IsJSReceiverInstanceType(value_instance_type), &out, - &throw_exception); + Branch(IsJSReceiverInstanceType(value_instance_type), &out, &throw_exception); // The {value} is not a compatible receiver for this method. - a->Bind(&throw_exception); + Bind(&throw_exception); { - Node* const message_id = a->SmiConstant(Smi::FromInt(msg_template)); - Node* const method_name_str = a->HeapConstant( - isolate->factory()->NewStringFromAsciiChecked(method_name, TENURED)); + Node* const message_id = SmiConstant(Smi::FromInt(msg_template)); + Node* const method_name_str = HeapConstant( + isolate()->factory()->NewStringFromAsciiChecked(method_name, TENURED)); - Callable callable = CodeFactory::ToString(isolate); - Node* const value_str = a->CallStub(callable, context, value); + Callable callable = CodeFactory::ToString(isolate()); + Node* const value_str = CallStub(callable, context, maybe_receiver); - a->CallRuntime(Runtime::kThrowTypeError, context, message_id, - method_name_str, value_str); - var_value_map.Bind(a->UndefinedConstant()); - a->Goto(&out); // Never reached. + CallRuntime(Runtime::kThrowTypeError, context, message_id, method_name_str, + value_str); + var_value_map.Bind(UndefinedConstant()); + Goto(&out); // Never reached. } - a->Bind(&out); + Bind(&out); return var_value_map.value(); } -compiler::Node* IsInitialRegExpMap(CodeStubAssembler* a, - compiler::Node* context, - compiler::Node* map) { - typedef compiler::Node Node; - - Node* const native_context = a->LoadNativeContext(context); +Node* RegExpBuiltinsAssembler::IsInitialRegExpMap(Node* context, Node* map) { + Node* const native_context = LoadNativeContext(context); Node* const regexp_fun = - a->LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX); + LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX); Node* const initial_map = - a->LoadObjectField(regexp_fun, JSFunction::kPrototypeOrInitialMapOffset); - Node* const has_initialmap = a->WordEqual(map, initial_map); + LoadObjectField(regexp_fun, JSFunction::kPrototypeOrInitialMapOffset); + Node* const has_initialmap = WordEqual(map, initial_map); return has_initialmap; } @@ -556,192 +416,499 @@ compiler::Node* IsInitialRegExpMap(CodeStubAssembler* a, // We use a fairly coarse granularity for this and simply check whether both // the regexp itself is unmodified (i.e. its map has not changed) and its // prototype is unmodified. -void BranchIfFastPath(CodeStubAssembler* a, compiler::Node* context, - compiler::Node* map, - CodeStubAssembler::Label* if_isunmodified, - CodeStubAssembler::Label* if_ismodified) { - typedef compiler::Node Node; - - Node* const native_context = a->LoadNativeContext(context); +void RegExpBuiltinsAssembler::BranchIfFastRegExp(Node* context, Node* map, + Label* if_isunmodified, + Label* if_ismodified) { + Node* const native_context = LoadNativeContext(context); Node* const regexp_fun = - a->LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX); + LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX); Node* const initial_map = - a->LoadObjectField(regexp_fun, JSFunction::kPrototypeOrInitialMapOffset); - Node* const has_initialmap = a->WordEqual(map, initial_map); + LoadObjectField(regexp_fun, JSFunction::kPrototypeOrInitialMapOffset); + Node* const has_initialmap = WordEqual(map, initial_map); - a->GotoUnless(has_initialmap, if_ismodified); + GotoUnless(has_initialmap, if_ismodified); - Node* const initial_proto_initial_map = a->LoadContextElement( - native_context, Context::REGEXP_PROTOTYPE_MAP_INDEX); - Node* const proto_map = a->LoadMap(a->LoadMapPrototype(map)); + Node* const initial_proto_initial_map = + LoadContextElement(native_context, Context::REGEXP_PROTOTYPE_MAP_INDEX); + Node* const proto_map = LoadMap(LoadMapPrototype(map)); Node* const proto_has_initialmap = - a->WordEqual(proto_map, initial_proto_initial_map); + WordEqual(proto_map, initial_proto_initial_map); // TODO(ishell): Update this check once map changes for constant field // tracking are landing. - a->Branch(proto_has_initialmap, if_isunmodified, if_ismodified); + Branch(proto_has_initialmap, if_isunmodified, if_ismodified); } -} // namespace +void RegExpBuiltinsAssembler::BranchIfFastRegExpResult(Node* context, Node* map, + Label* if_isunmodified, + Label* if_ismodified) { + Node* const native_context = LoadNativeContext(context); + Node* const initial_regexp_result_map = + LoadContextElement(native_context, Context::REGEXP_RESULT_MAP_INDEX); -void Builtins::Generate_RegExpPrototypeFlagsGetter(CodeStubAssembler* a) { - typedef CodeStubAssembler::Variable Variable; - typedef CodeStubAssembler::Label Label; - typedef compiler::Node Node; + Branch(WordEqual(map, initial_regexp_result_map), if_isunmodified, + if_ismodified); +} - Node* const receiver = a->Parameter(0); - Node* const context = a->Parameter(3); +// ES#sec-regexp.prototype.exec +// RegExp.prototype.exec ( string ) +TF_BUILTIN(RegExpPrototypeExec, RegExpBuiltinsAssembler) { + Node* const maybe_receiver = Parameter(0); + Node* const maybe_string = Parameter(1); + Node* const context = Parameter(4); - Isolate* isolate = a->isolate(); - Node* const int_zero = a->IntPtrConstant(0); - Node* const int_one = a->IntPtrConstant(1); + // Ensure {maybe_receiver} is a JSRegExp. + Node* const regexp_map = ThrowIfNotInstanceType( + context, maybe_receiver, JS_REGEXP_TYPE, "RegExp.prototype.exec"); + Node* const receiver = maybe_receiver; - Node* const map = ThrowIfNotJSReceiver(a, isolate, context, receiver, - MessageTemplate::kRegExpNonObject, - "RegExp.prototype.flags"); + // Convert {maybe_string} to a String. + Node* const string = ToString(context, maybe_string); - Variable var_length(a, MachineType::PointerRepresentation()); - Variable var_flags(a, MachineType::PointerRepresentation()); + Label if_isfastpath(this), if_isslowpath(this); + Branch(IsInitialRegExpMap(context, regexp_map), &if_isfastpath, + &if_isslowpath); + + Bind(&if_isfastpath); + { + Node* const result = + RegExpPrototypeExecBody(context, receiver, string, true); + Return(result); + } + + Bind(&if_isslowpath); + { + Node* const result = + RegExpPrototypeExecBody(context, receiver, string, false); + Return(result); + } +} + +Node* RegExpBuiltinsAssembler::FlagsGetter(Node* const context, + Node* const regexp, + bool is_fastpath) { + Isolate* isolate = this->isolate(); + + Node* const int_zero = IntPtrConstant(0); + Node* const int_one = IntPtrConstant(1); + Variable var_length(this, MachineType::PointerRepresentation()); + Variable var_flags(this, MachineType::PointerRepresentation()); // First, count the number of characters we will need and check which flags // are set. var_length.Bind(int_zero); - Label if_isunmodifiedjsregexp(a), - if_isnotunmodifiedjsregexp(a, Label::kDeferred); - a->Branch(IsInitialRegExpMap(a, context, map), &if_isunmodifiedjsregexp, - &if_isnotunmodifiedjsregexp); - - Label construct_string(a); - a->Bind(&if_isunmodifiedjsregexp); - { + if (is_fastpath) { // Refer to JSRegExp's flag property on the fast-path. - Node* const flags_smi = - a->LoadObjectField(receiver, JSRegExp::kFlagsOffset); - Node* const flags_intptr = a->SmiUntag(flags_smi); + Node* const flags_smi = LoadObjectField(regexp, JSRegExp::kFlagsOffset); + Node* const flags_intptr = SmiUntag(flags_smi); var_flags.Bind(flags_intptr); - Label label_global(a), label_ignorecase(a), label_multiline(a), - label_unicode(a), label_sticky(a); - -#define CASE_FOR_FLAG(FLAG, LABEL, NEXT_LABEL) \ - do { \ - a->Bind(&LABEL); \ - Node* const mask = a->IntPtrConstant(FLAG); \ - a->GotoIf(a->WordEqual(a->WordAnd(flags_intptr, mask), int_zero), \ - &NEXT_LABEL); \ - var_length.Bind(a->IntPtrAdd(var_length.value(), int_one)); \ - a->Goto(&NEXT_LABEL); \ +#define CASE_FOR_FLAG(FLAG) \ + do { \ + Label next(this); \ + GotoUnless(IsSetWord(flags_intptr, FLAG), &next); \ + var_length.Bind(IntPtrAdd(var_length.value(), int_one)); \ + Goto(&next); \ + Bind(&next); \ } while (false) - a->Goto(&label_global); - CASE_FOR_FLAG(JSRegExp::kGlobal, label_global, label_ignorecase); - CASE_FOR_FLAG(JSRegExp::kIgnoreCase, label_ignorecase, label_multiline); - CASE_FOR_FLAG(JSRegExp::kMultiline, label_multiline, label_unicode); - CASE_FOR_FLAG(JSRegExp::kUnicode, label_unicode, label_sticky); - CASE_FOR_FLAG(JSRegExp::kSticky, label_sticky, construct_string); + CASE_FOR_FLAG(JSRegExp::kGlobal); + CASE_FOR_FLAG(JSRegExp::kIgnoreCase); + CASE_FOR_FLAG(JSRegExp::kMultiline); + CASE_FOR_FLAG(JSRegExp::kUnicode); + CASE_FOR_FLAG(JSRegExp::kSticky); #undef CASE_FOR_FLAG - } + } else { + DCHECK(!is_fastpath); - a->Bind(&if_isnotunmodifiedjsregexp); - { // Fall back to GetProperty stub on the slow-path. var_flags.Bind(int_zero); - Callable getproperty_callable = CodeFactory::GetProperty(a->isolate()); - Label label_global(a), label_ignorecase(a), label_multiline(a), - label_unicode(a), label_sticky(a); + Callable getproperty_callable = CodeFactory::GetProperty(isolate); -#define CASE_FOR_FLAG(NAME, FLAG, LABEL, NEXT_LABEL) \ +#define CASE_FOR_FLAG(NAME, FLAG) \ do { \ - a->Bind(&LABEL); \ + Label next(this); \ Node* const name = \ - a->HeapConstant(isolate->factory()->NewStringFromAsciiChecked(NAME)); \ - Node* const flag = \ - a->CallStub(getproperty_callable, context, receiver, name); \ - Label if_isflagset(a); \ - a->BranchIfToBooleanIsTrue(flag, &if_isflagset, &NEXT_LABEL); \ - a->Bind(&if_isflagset); \ - var_length.Bind(a->IntPtrAdd(var_length.value(), int_one)); \ - var_flags.Bind(a->WordOr(var_flags.value(), a->IntPtrConstant(FLAG))); \ - a->Goto(&NEXT_LABEL); \ + HeapConstant(isolate->factory()->InternalizeUtf8String(NAME)); \ + Node* const flag = CallStub(getproperty_callable, context, regexp, name); \ + Label if_isflagset(this); \ + BranchIfToBooleanIsTrue(flag, &if_isflagset, &next); \ + Bind(&if_isflagset); \ + var_length.Bind(IntPtrAdd(var_length.value(), int_one)); \ + var_flags.Bind(WordOr(var_flags.value(), IntPtrConstant(FLAG))); \ + Goto(&next); \ + Bind(&next); \ } while (false) - a->Goto(&label_global); - CASE_FOR_FLAG("global", JSRegExp::kGlobal, label_global, label_ignorecase); - CASE_FOR_FLAG("ignoreCase", JSRegExp::kIgnoreCase, label_ignorecase, - label_multiline); - CASE_FOR_FLAG("multiline", JSRegExp::kMultiline, label_multiline, - label_unicode); - CASE_FOR_FLAG("unicode", JSRegExp::kUnicode, label_unicode, label_sticky); - CASE_FOR_FLAG("sticky", JSRegExp::kSticky, label_sticky, construct_string); + CASE_FOR_FLAG("global", JSRegExp::kGlobal); + CASE_FOR_FLAG("ignoreCase", JSRegExp::kIgnoreCase); + CASE_FOR_FLAG("multiline", JSRegExp::kMultiline); + CASE_FOR_FLAG("unicode", JSRegExp::kUnicode); + CASE_FOR_FLAG("sticky", JSRegExp::kSticky); #undef CASE_FOR_FLAG } // Allocate a string of the required length and fill it with the corresponding // char for each set flag. - a->Bind(&construct_string); { - Node* const result = - a->AllocateSeqOneByteString(context, var_length.value()); + Node* const result = AllocateSeqOneByteString(context, var_length.value()); Node* const flags_intptr = var_flags.value(); - Variable var_offset(a, MachineType::PointerRepresentation()); + Variable var_offset(this, MachineType::PointerRepresentation()); var_offset.Bind( - a->IntPtrConstant(SeqOneByteString::kHeaderSize - kHeapObjectTag)); - - Label label_global(a), label_ignorecase(a), label_multiline(a), - label_unicode(a), label_sticky(a), out(a); - -#define CASE_FOR_FLAG(FLAG, CHAR, LABEL, NEXT_LABEL) \ - do { \ - a->Bind(&LABEL); \ - Node* const mask = a->IntPtrConstant(FLAG); \ - a->GotoIf(a->WordEqual(a->WordAnd(flags_intptr, mask), int_zero), \ - &NEXT_LABEL); \ - Node* const value = a->IntPtrConstant(CHAR); \ - a->StoreNoWriteBarrier(MachineRepresentation::kWord8, result, \ - var_offset.value(), value); \ - var_offset.Bind(a->IntPtrAdd(var_offset.value(), int_one)); \ - a->Goto(&NEXT_LABEL); \ + IntPtrConstant(SeqOneByteString::kHeaderSize - kHeapObjectTag)); + +#define CASE_FOR_FLAG(FLAG, CHAR) \ + do { \ + Label next(this); \ + GotoUnless(IsSetWord(flags_intptr, FLAG), &next); \ + Node* const value = Int32Constant(CHAR); \ + StoreNoWriteBarrier(MachineRepresentation::kWord8, result, \ + var_offset.value(), value); \ + var_offset.Bind(IntPtrAdd(var_offset.value(), int_one)); \ + Goto(&next); \ + Bind(&next); \ } while (false) - a->Goto(&label_global); - CASE_FOR_FLAG(JSRegExp::kGlobal, 'g', label_global, label_ignorecase); - CASE_FOR_FLAG(JSRegExp::kIgnoreCase, 'i', label_ignorecase, - label_multiline); - CASE_FOR_FLAG(JSRegExp::kMultiline, 'm', label_multiline, label_unicode); - CASE_FOR_FLAG(JSRegExp::kUnicode, 'u', label_unicode, label_sticky); - CASE_FOR_FLAG(JSRegExp::kSticky, 'y', label_sticky, out); + CASE_FOR_FLAG(JSRegExp::kGlobal, 'g'); + CASE_FOR_FLAG(JSRegExp::kIgnoreCase, 'i'); + CASE_FOR_FLAG(JSRegExp::kMultiline, 'm'); + CASE_FOR_FLAG(JSRegExp::kUnicode, 'u'); + CASE_FOR_FLAG(JSRegExp::kSticky, 'y'); #undef CASE_FOR_FLAG - a->Bind(&out); - a->Return(result); + return result; } } -// ES6 21.2.5.10. -BUILTIN(RegExpPrototypeSourceGetter) { - HandleScope scope(isolate); +// ES#sec-isregexp IsRegExp ( argument ) +Node* RegExpBuiltinsAssembler::IsRegExp(Node* const context, + Node* const maybe_receiver) { + Label out(this), if_isregexp(this); + + Variable var_result(this, MachineRepresentation::kWord32); + var_result.Bind(Int32Constant(0)); + + GotoIf(TaggedIsSmi(maybe_receiver), &out); + GotoUnless(IsJSReceiver(maybe_receiver), &out); + + Node* const receiver = maybe_receiver; + + // Check @@match. + { + Callable getproperty_callable = CodeFactory::GetProperty(isolate()); + Node* const name = HeapConstant(isolate()->factory()->match_symbol()); + Node* const value = CallStub(getproperty_callable, context, receiver, name); + + Label match_isundefined(this), match_isnotundefined(this); + Branch(IsUndefined(value), &match_isundefined, &match_isnotundefined); + + Bind(&match_isundefined); + Branch(HasInstanceType(receiver, JS_REGEXP_TYPE), &if_isregexp, &out); + + Bind(&match_isnotundefined); + BranchIfToBooleanIsTrue(value, &if_isregexp, &out); + } + + Bind(&if_isregexp); + var_result.Bind(Int32Constant(1)); + Goto(&out); + + Bind(&out); + return var_result.value(); +} + +// ES#sec-regexpinitialize +// Runtime Semantics: RegExpInitialize ( obj, pattern, flags ) +Node* RegExpBuiltinsAssembler::RegExpInitialize(Node* const context, + Node* const regexp, + Node* const maybe_pattern, + Node* const maybe_flags) { + // Normalize pattern. + Node* const pattern = + Select(IsUndefined(maybe_pattern), [=] { return EmptyStringConstant(); }, + [=] { return ToString(context, maybe_pattern); }, + MachineRepresentation::kTagged); + + // Normalize flags. + Node* const flags = + Select(IsUndefined(maybe_flags), [=] { return EmptyStringConstant(); }, + [=] { return ToString(context, maybe_flags); }, + MachineRepresentation::kTagged); + + // Initialize. + + return CallRuntime(Runtime::kRegExpInitializeAndCompile, context, regexp, + pattern, flags); +} + +TF_BUILTIN(RegExpPrototypeFlagsGetter, RegExpBuiltinsAssembler) { + Node* const maybe_receiver = Parameter(0); + Node* const context = Parameter(3); + + Node* const map = ThrowIfNotJSReceiver(context, maybe_receiver, + MessageTemplate::kRegExpNonObject, + "RegExp.prototype.flags"); + Node* const receiver = maybe_receiver; + + Label if_isfastpath(this), if_isslowpath(this, Label::kDeferred); + Branch(IsInitialRegExpMap(context, map), &if_isfastpath, &if_isslowpath); + + Bind(&if_isfastpath); + Return(FlagsGetter(context, receiver, true)); + + Bind(&if_isslowpath); + Return(FlagsGetter(context, receiver, false)); +} + +// ES#sec-regexp-pattern-flags +// RegExp ( pattern, flags ) +TF_BUILTIN(RegExpConstructor, RegExpBuiltinsAssembler) { + Node* const pattern = Parameter(1); + Node* const flags = Parameter(2); + Node* const new_target = Parameter(3); + Node* const context = Parameter(5); + + Isolate* isolate = this->isolate(); + + Variable var_flags(this, MachineRepresentation::kTagged); + Variable var_pattern(this, MachineRepresentation::kTagged); + Variable var_new_target(this, MachineRepresentation::kTagged); + + var_flags.Bind(flags); + var_pattern.Bind(pattern); + var_new_target.Bind(new_target); + + Node* const native_context = LoadNativeContext(context); + Node* const regexp_function = + LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX); + + Node* const pattern_is_regexp = IsRegExp(context, pattern); + + { + Label next(this); + + GotoUnless(IsUndefined(new_target), &next); + var_new_target.Bind(regexp_function); + + GotoUnless(pattern_is_regexp, &next); + GotoUnless(IsUndefined(flags), &next); + + Callable getproperty_callable = CodeFactory::GetProperty(isolate); + Node* const name = HeapConstant(isolate->factory()->constructor_string()); + Node* const value = CallStub(getproperty_callable, context, pattern, name); + + GotoUnless(WordEqual(value, regexp_function), &next); + Return(pattern); + + Bind(&next); + } + + { + Label next(this), if_patternisfastregexp(this), + if_patternisslowregexp(this); + GotoIf(TaggedIsSmi(pattern), &next); + + GotoIf(HasInstanceType(pattern, JS_REGEXP_TYPE), &if_patternisfastregexp); + + Branch(pattern_is_regexp, &if_patternisslowregexp, &next); + + Bind(&if_patternisfastregexp); + { + Node* const source = LoadObjectField(pattern, JSRegExp::kSourceOffset); + var_pattern.Bind(source); + + { + Label inner_next(this); + GotoUnless(IsUndefined(flags), &inner_next); + + Node* const value = FlagsGetter(context, pattern, true); + var_flags.Bind(value); + Goto(&inner_next); + + Bind(&inner_next); + } + + Goto(&next); + } + + Bind(&if_patternisslowregexp); + { + Callable getproperty_callable = CodeFactory::GetProperty(isolate); + + { + Node* const name = HeapConstant(isolate->factory()->source_string()); + Node* const value = + CallStub(getproperty_callable, context, pattern, name); + var_pattern.Bind(value); + } + + { + Label inner_next(this); + GotoUnless(IsUndefined(flags), &inner_next); + + Node* const name = HeapConstant(isolate->factory()->flags_string()); + Node* const value = + CallStub(getproperty_callable, context, pattern, name); + var_flags.Bind(value); + Goto(&inner_next); + + Bind(&inner_next); + } + + Goto(&next); + } + + Bind(&next); + } + + // Allocate. + + Variable var_regexp(this, MachineRepresentation::kTagged); + { + Label allocate_jsregexp(this), allocate_generic(this, Label::kDeferred), + next(this); + Branch(WordEqual(var_new_target.value(), regexp_function), + &allocate_jsregexp, &allocate_generic); + + Bind(&allocate_jsregexp); + { + Node* const initial_map = LoadObjectField( + regexp_function, JSFunction::kPrototypeOrInitialMapOffset); + Node* const regexp = AllocateJSObjectFromMap(initial_map); + var_regexp.Bind(regexp); + Goto(&next); + } + + Bind(&allocate_generic); + { + ConstructorBuiltinsAssembler constructor_assembler(this->state()); + Node* const regexp = constructor_assembler.EmitFastNewObject( + context, regexp_function, var_new_target.value()); + var_regexp.Bind(regexp); + Goto(&next); + } + + Bind(&next); + } + + Node* const result = RegExpInitialize(context, var_regexp.value(), + var_pattern.value(), var_flags.value()); + Return(result); +} + +// ES#sec-regexp.prototype.compile +// RegExp.prototype.compile ( pattern, flags ) +TF_BUILTIN(RegExpPrototypeCompile, RegExpBuiltinsAssembler) { + Node* const maybe_receiver = Parameter(0); + Node* const maybe_pattern = Parameter(1); + Node* const maybe_flags = Parameter(2); + Node* const context = Parameter(5); + + ThrowIfNotInstanceType(context, maybe_receiver, JS_REGEXP_TYPE, + "RegExp.prototype.compile"); + Node* const receiver = maybe_receiver; + + Variable var_flags(this, MachineRepresentation::kTagged); + Variable var_pattern(this, MachineRepresentation::kTagged); + + var_flags.Bind(maybe_flags); + var_pattern.Bind(maybe_pattern); + + // Handle a JSRegExp pattern. + { + Label next(this); + + GotoIf(TaggedIsSmi(maybe_pattern), &next); + GotoUnless(HasInstanceType(maybe_pattern, JS_REGEXP_TYPE), &next); + + Node* const pattern = maybe_pattern; + + // {maybe_flags} must be undefined in this case, otherwise throw. + { + Label next(this); + GotoIf(IsUndefined(maybe_flags), &next); - Handle<Object> recv = args.receiver(); - if (!recv->IsJSRegExp()) { - Handle<JSFunction> regexp_fun = isolate->regexp_function(); - if (*recv == regexp_fun->prototype()) { - isolate->CountUsage(v8::Isolate::kRegExpPrototypeSourceGetter); - return *isolate->factory()->NewStringFromAsciiChecked("(?:)"); + Node* const message_id = SmiConstant(MessageTemplate::kRegExpFlags); + TailCallRuntime(Runtime::kThrowTypeError, context, message_id); + + Bind(&next); } - THROW_NEW_ERROR_RETURN_FAILURE( - isolate, NewTypeError(MessageTemplate::kRegExpNonRegExp, - isolate->factory()->NewStringFromAsciiChecked( - "RegExp.prototype.source"))); + + Node* const new_flags = FlagsGetter(context, pattern, true); + Node* const new_pattern = LoadObjectField(pattern, JSRegExp::kSourceOffset); + + var_flags.Bind(new_flags); + var_pattern.Bind(new_pattern); + + Goto(&next); + Bind(&next); } - Handle<JSRegExp> regexp = Handle<JSRegExp>::cast(recv); - return regexp->source(); + Node* const result = RegExpInitialize(context, receiver, var_pattern.value(), + var_flags.value()); + Return(result); +} + +// ES6 21.2.5.10. +TF_BUILTIN(RegExpPrototypeSourceGetter, RegExpBuiltinsAssembler) { + Node* const receiver = Parameter(0); + Node* const context = Parameter(3); + + // Check whether we have an unmodified regexp instance. + Label if_isjsregexp(this), if_isnotjsregexp(this, Label::kDeferred); + + GotoIf(TaggedIsSmi(receiver), &if_isnotjsregexp); + Branch(HasInstanceType(receiver, JS_REGEXP_TYPE), &if_isjsregexp, + &if_isnotjsregexp); + + Bind(&if_isjsregexp); + { + Node* const source = LoadObjectField(receiver, JSRegExp::kSourceOffset); + Return(source); + } + + Bind(&if_isnotjsregexp); + { + Isolate* isolate = this->isolate(); + Node* const native_context = LoadNativeContext(context); + Node* const regexp_fun = + LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX); + Node* const initial_map = + LoadObjectField(regexp_fun, JSFunction::kPrototypeOrInitialMapOffset); + Node* const initial_prototype = LoadMapPrototype(initial_map); + + Label if_isprototype(this), if_isnotprototype(this); + Branch(WordEqual(receiver, initial_prototype), &if_isprototype, + &if_isnotprototype); + + Bind(&if_isprototype); + { + const int counter = v8::Isolate::kRegExpPrototypeSourceGetter; + Node* const counter_smi = SmiConstant(counter); + CallRuntime(Runtime::kIncrementUseCounter, context, counter_smi); + + Node* const result = + HeapConstant(isolate->factory()->NewStringFromAsciiChecked("(?:)")); + Return(result); + } + + Bind(&if_isnotprototype); + { + Node* const message_id = + SmiConstant(Smi::FromInt(MessageTemplate::kRegExpNonRegExp)); + Node* const method_name_str = + HeapConstant(isolate->factory()->NewStringFromAsciiChecked( + "RegExp.prototype.source")); + TailCallRuntime(Runtime::kThrowTypeError, context, message_id, + method_name_str); + } + } } BUILTIN(RegExpPrototypeToString) { @@ -781,126 +948,166 @@ BUILTIN(RegExpPrototypeToString) { RETURN_RESULT_OR_FAILURE(isolate, builder.Finish()); } -// ES6 21.2.4.2. -BUILTIN(RegExpPrototypeSpeciesGetter) { - HandleScope scope(isolate); - return *args.receiver(); +// Fast-path implementation for flag checks on an unmodified JSRegExp instance. +Node* RegExpBuiltinsAssembler::FastFlagGetter(Node* const regexp, + JSRegExp::Flag flag) { + Node* const smi_zero = SmiConstant(Smi::kZero); + Node* const flags = LoadObjectField(regexp, JSRegExp::kFlagsOffset); + Node* const mask = SmiConstant(Smi::FromInt(flag)); + Node* const is_flag_set = WordNotEqual(SmiAnd(flags, mask), smi_zero); + + return is_flag_set; } -namespace { +// Load through the GetProperty stub. +Node* RegExpBuiltinsAssembler::SlowFlagGetter(Node* const context, + Node* const regexp, + JSRegExp::Flag flag) { + Factory* factory = isolate()->factory(); -// Fast-path implementation for flag checks on an unmodified JSRegExp instance. -compiler::Node* FastFlagGetter(CodeStubAssembler* a, - compiler::Node* const regexp, - JSRegExp::Flag flag) { - typedef compiler::Node Node; + Label out(this); + Variable var_result(this, MachineRepresentation::kWord32); - Node* const smi_zero = a->SmiConstant(Smi::kZero); - Node* const flags = a->LoadObjectField(regexp, JSRegExp::kFlagsOffset); - Node* const mask = a->SmiConstant(Smi::FromInt(flag)); - Node* const is_flag_set = a->WordNotEqual(a->WordAnd(flags, mask), smi_zero); + Node* name; - return is_flag_set; + switch (flag) { + case JSRegExp::kGlobal: + name = HeapConstant(factory->global_string()); + break; + case JSRegExp::kIgnoreCase: + name = HeapConstant(factory->ignoreCase_string()); + break; + case JSRegExp::kMultiline: + name = HeapConstant(factory->multiline_string()); + break; + case JSRegExp::kSticky: + name = HeapConstant(factory->sticky_string()); + break; + case JSRegExp::kUnicode: + name = HeapConstant(factory->unicode_string()); + break; + default: + UNREACHABLE(); + } + + Callable getproperty_callable = CodeFactory::GetProperty(isolate()); + Node* const value = CallStub(getproperty_callable, context, regexp, name); + + Label if_true(this), if_false(this); + BranchIfToBooleanIsTrue(value, &if_true, &if_false); + + Bind(&if_true); + { + var_result.Bind(Int32Constant(1)); + Goto(&out); + } + + Bind(&if_false); + { + var_result.Bind(Int32Constant(0)); + Goto(&out); + } + + Bind(&out); + return var_result.value(); } -void Generate_FlagGetter(CodeStubAssembler* a, JSRegExp::Flag flag, - v8::Isolate::UseCounterFeature counter, - const char* method_name) { - typedef CodeStubAssembler::Label Label; - typedef compiler::Node Node; +Node* RegExpBuiltinsAssembler::FlagGetter(Node* const context, + Node* const regexp, + JSRegExp::Flag flag, + bool is_fastpath) { + return is_fastpath ? FastFlagGetter(regexp, flag) + : SlowFlagGetter(context, regexp, flag); +} - Node* const receiver = a->Parameter(0); - Node* const context = a->Parameter(3); +void RegExpBuiltinsAssembler::FlagGetter(JSRegExp::Flag flag, + v8::Isolate::UseCounterFeature counter, + const char* method_name) { + Node* const receiver = Parameter(0); + Node* const context = Parameter(3); - Isolate* isolate = a->isolate(); + Isolate* isolate = this->isolate(); // Check whether we have an unmodified regexp instance. - Label if_isunmodifiedjsregexp(a), - if_isnotunmodifiedjsregexp(a, Label::kDeferred); + Label if_isunmodifiedjsregexp(this), + if_isnotunmodifiedjsregexp(this, Label::kDeferred); - a->GotoIf(a->TaggedIsSmi(receiver), &if_isnotunmodifiedjsregexp); + GotoIf(TaggedIsSmi(receiver), &if_isnotunmodifiedjsregexp); - Node* const receiver_map = a->LoadMap(receiver); - Node* const instance_type = a->LoadMapInstanceType(receiver_map); + Node* const receiver_map = LoadMap(receiver); + Node* const instance_type = LoadMapInstanceType(receiver_map); - a->Branch(a->Word32Equal(instance_type, a->Int32Constant(JS_REGEXP_TYPE)), - &if_isunmodifiedjsregexp, &if_isnotunmodifiedjsregexp); + Branch(Word32Equal(instance_type, Int32Constant(JS_REGEXP_TYPE)), + &if_isunmodifiedjsregexp, &if_isnotunmodifiedjsregexp); - a->Bind(&if_isunmodifiedjsregexp); + Bind(&if_isunmodifiedjsregexp); { // Refer to JSRegExp's flag property on the fast-path. - Node* const is_flag_set = FastFlagGetter(a, receiver, flag); - a->Return(a->Select(is_flag_set, a->TrueConstant(), a->FalseConstant())); + Node* const is_flag_set = FastFlagGetter(receiver, flag); + Return(SelectBooleanConstant(is_flag_set)); } - a->Bind(&if_isnotunmodifiedjsregexp); + Bind(&if_isnotunmodifiedjsregexp); { - Node* const native_context = a->LoadNativeContext(context); + Node* const native_context = 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 initial_prototype = a->LoadMapPrototype(initial_map); + LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX); + Node* const initial_map = + LoadObjectField(regexp_fun, JSFunction::kPrototypeOrInitialMapOffset); + Node* const initial_prototype = LoadMapPrototype(initial_map); - Label if_isprototype(a), if_isnotprototype(a); - a->Branch(a->WordEqual(receiver, initial_prototype), &if_isprototype, - &if_isnotprototype); + Label if_isprototype(this), if_isnotprototype(this); + Branch(WordEqual(receiver, initial_prototype), &if_isprototype, + &if_isnotprototype); - a->Bind(&if_isprototype); + Bind(&if_isprototype); { - Node* const counter_smi = a->SmiConstant(Smi::FromInt(counter)); - a->CallRuntime(Runtime::kIncrementUseCounter, context, counter_smi); - a->Return(a->UndefinedConstant()); + Node* const counter_smi = SmiConstant(Smi::FromInt(counter)); + CallRuntime(Runtime::kIncrementUseCounter, context, counter_smi); + Return(UndefinedConstant()); } - a->Bind(&if_isnotprototype); + Bind(&if_isnotprototype); { Node* const message_id = - a->SmiConstant(Smi::FromInt(MessageTemplate::kRegExpNonRegExp)); - Node* const method_name_str = a->HeapConstant( + SmiConstant(Smi::FromInt(MessageTemplate::kRegExpNonRegExp)); + Node* const method_name_str = HeapConstant( isolate->factory()->NewStringFromAsciiChecked(method_name)); - a->CallRuntime(Runtime::kThrowTypeError, context, message_id, - method_name_str); - a->Return(a->UndefinedConstant()); // Never reached. + CallRuntime(Runtime::kThrowTypeError, context, message_id, + method_name_str); + Return(UndefinedConstant()); // Never reached. } } } -} // namespace - // ES6 21.2.5.4. -void Builtins::Generate_RegExpPrototypeGlobalGetter(CodeStubAssembler* a) { - Generate_FlagGetter(a, JSRegExp::kGlobal, - v8::Isolate::kRegExpPrototypeOldFlagGetter, - "RegExp.prototype.global"); +TF_BUILTIN(RegExpPrototypeGlobalGetter, RegExpBuiltinsAssembler) { + FlagGetter(JSRegExp::kGlobal, v8::Isolate::kRegExpPrototypeOldFlagGetter, + "RegExp.prototype.global"); } // ES6 21.2.5.5. -void Builtins::Generate_RegExpPrototypeIgnoreCaseGetter(CodeStubAssembler* a) { - Generate_FlagGetter(a, JSRegExp::kIgnoreCase, - v8::Isolate::kRegExpPrototypeOldFlagGetter, - "RegExp.prototype.ignoreCase"); +TF_BUILTIN(RegExpPrototypeIgnoreCaseGetter, RegExpBuiltinsAssembler) { + FlagGetter(JSRegExp::kIgnoreCase, v8::Isolate::kRegExpPrototypeOldFlagGetter, + "RegExp.prototype.ignoreCase"); } // ES6 21.2.5.7. -void Builtins::Generate_RegExpPrototypeMultilineGetter(CodeStubAssembler* a) { - Generate_FlagGetter(a, JSRegExp::kMultiline, - v8::Isolate::kRegExpPrototypeOldFlagGetter, - "RegExp.prototype.multiline"); +TF_BUILTIN(RegExpPrototypeMultilineGetter, RegExpBuiltinsAssembler) { + FlagGetter(JSRegExp::kMultiline, v8::Isolate::kRegExpPrototypeOldFlagGetter, + "RegExp.prototype.multiline"); } // ES6 21.2.5.12. -void Builtins::Generate_RegExpPrototypeStickyGetter(CodeStubAssembler* a) { - Generate_FlagGetter(a, JSRegExp::kSticky, - v8::Isolate::kRegExpPrototypeStickyGetter, - "RegExp.prototype.sticky"); +TF_BUILTIN(RegExpPrototypeStickyGetter, RegExpBuiltinsAssembler) { + FlagGetter(JSRegExp::kSticky, v8::Isolate::kRegExpPrototypeStickyGetter, + "RegExp.prototype.sticky"); } // ES6 21.2.5.15. -void Builtins::Generate_RegExpPrototypeUnicodeGetter(CodeStubAssembler* a) { - Generate_FlagGetter(a, JSRegExp::kUnicode, - v8::Isolate::kRegExpPrototypeUnicodeGetter, - "RegExp.prototype.unicode"); +TF_BUILTIN(RegExpPrototypeUnicodeGetter, RegExpBuiltinsAssembler) { + FlagGetter(JSRegExp::kUnicode, v8::Isolate::kRegExpPrototypeUnicodeGetter, + "RegExp.prototype.unicode"); } // The properties $1..$9 are the first nine capturing substrings of the last @@ -986,722 +1193,977 @@ BUILTIN(RegExpRightContextGetter) { return *isolate->factory()->NewSubString(last_subject, start_index, len); } -namespace { - // ES#sec-regexpexec Runtime Semantics: RegExpExec ( R, S ) -compiler::Node* RegExpExec(CodeStubAssembler* a, compiler::Node* context, - compiler::Node* recv, compiler::Node* string) { - typedef CodeStubAssembler::Variable Variable; - typedef CodeStubAssembler::Label Label; - typedef compiler::Node Node; +Node* RegExpBuiltinsAssembler::RegExpExec(Node* context, Node* regexp, + Node* string) { + Isolate* isolate = this->isolate(); - Isolate* isolate = a->isolate(); + Node* const null = NullConstant(); - Node* const null = a->NullConstant(); + Variable var_result(this, MachineRepresentation::kTagged); + Label out(this), if_isfastpath(this), if_isslowpath(this); - Variable var_result(a, MachineRepresentation::kTagged); - Label out(a), call_builtin_exec(a), slow_path(a, Label::kDeferred); + Node* const map = LoadMap(regexp); + BranchIfFastRegExp(context, map, &if_isfastpath, &if_isslowpath); - Node* const map = a->LoadMap(recv); - BranchIfFastPath(a, context, map, &call_builtin_exec, &slow_path); - - a->Bind(&call_builtin_exec); + Bind(&if_isfastpath); { - Node* const result = RegExpPrototypeExecInternal(a, context, recv, string); + Node* const result = RegExpPrototypeExecBody(context, regexp, string, true); var_result.Bind(result); - a->Goto(&out); + Goto(&out); } - a->Bind(&slow_path); + Bind(&if_isslowpath); { // Take the slow path of fetching the exec property, calling it, and // verifying its return value. // Get the exec property. - Node* const name = a->HeapConstant(isolate->factory()->exec_string()); - Callable getproperty_callable = CodeFactory::GetProperty(a->isolate()); - Node* const exec = a->CallStub(getproperty_callable, context, recv, name); + Node* const name = HeapConstant(isolate->factory()->exec_string()); + Callable getproperty_callable = CodeFactory::GetProperty(isolate); + Node* const exec = CallStub(getproperty_callable, context, regexp, name); // Is {exec} callable? - Label if_iscallable(a), if_isnotcallable(a); + Label if_iscallable(this), if_isnotcallable(this); - a->GotoIf(a->TaggedIsSmi(exec), &if_isnotcallable); + GotoIf(TaggedIsSmi(exec), &if_isnotcallable); - Node* const exec_map = a->LoadMap(exec); - a->Branch(a->IsCallableMap(exec_map), &if_iscallable, &if_isnotcallable); + Node* const exec_map = LoadMap(exec); + Branch(IsCallableMap(exec_map), &if_iscallable, &if_isnotcallable); - a->Bind(&if_iscallable); + Bind(&if_iscallable); { Callable call_callable = CodeFactory::Call(isolate); - Node* const result = - a->CallJS(call_callable, context, exec, recv, string); + Node* const result = CallJS(call_callable, context, exec, regexp, string); var_result.Bind(result); - a->GotoIf(a->WordEqual(result, null), &out); + GotoIf(WordEqual(result, null), &out); - ThrowIfNotJSReceiver(a, isolate, context, result, + ThrowIfNotJSReceiver(context, result, MessageTemplate::kInvalidRegExpExecResult, "unused"); - a->Goto(&out); + Goto(&out); } - a->Bind(&if_isnotcallable); + Bind(&if_isnotcallable); { - a->ThrowIfNotInstanceType(context, recv, JS_REGEXP_TYPE, - "RegExp.prototype.exec"); - a->Goto(&call_builtin_exec); + ThrowIfNotInstanceType(context, regexp, JS_REGEXP_TYPE, + "RegExp.prototype.exec"); + + Node* const result = + RegExpPrototypeExecBody(context, regexp, string, false); + var_result.Bind(result); + Goto(&out); } } - a->Bind(&out); + Bind(&out); return var_result.value(); } -} // namespace - // ES#sec-regexp.prototype.test // RegExp.prototype.test ( S ) -void Builtins::Generate_RegExpPrototypeTest(CodeStubAssembler* a) { - typedef compiler::Node Node; - - Isolate* const isolate = a->isolate(); - - Node* const maybe_receiver = a->Parameter(0); - Node* const maybe_string = a->Parameter(1); - Node* const context = a->Parameter(4); +TF_BUILTIN(RegExpPrototypeTest, RegExpBuiltinsAssembler) { + Node* const maybe_receiver = Parameter(0); + Node* const maybe_string = Parameter(1); + Node* const context = Parameter(4); // Ensure {maybe_receiver} is a JSReceiver. - ThrowIfNotJSReceiver(a, isolate, context, maybe_receiver, - MessageTemplate::kIncompatibleMethodReceiver, - "RegExp.prototype.test"); + Node* const map = ThrowIfNotJSReceiver( + context, maybe_receiver, MessageTemplate::kIncompatibleMethodReceiver, + "RegExp.prototype.test"); Node* const receiver = maybe_receiver; // Convert {maybe_string} to a String. - Node* const string = a->ToString(context, maybe_string); + Node* const string = ToString(context, maybe_string); - // Call exec. - Node* const match_indices = RegExpExec(a, context, receiver, string); - - // Return true iff exec matched successfully. - Node* const result = a->Select(a->WordEqual(match_indices, a->NullConstant()), - a->FalseConstant(), a->TrueConstant()); - a->Return(result); -} - -// ES#sec-regexp.prototype-@@match -// RegExp.prototype [ @@match ] ( string ) -BUILTIN(RegExpPrototypeMatch) { - HandleScope scope(isolate); - CHECK_RECEIVER(JSReceiver, recv, "RegExp.prototype.@@match"); + Label fast_path(this), slow_path(this); + BranchIfFastRegExp(context, map, &fast_path, &slow_path); - Handle<Object> string_obj = args.atOrUndefined(isolate, 1); + Bind(&fast_path); + { + Label if_didnotmatch(this); + RegExpPrototypeExecBodyWithoutResult(context, receiver, string, + &if_didnotmatch, true); + Return(TrueConstant()); - Handle<String> string; - ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, string, - Object::ToString(isolate, string_obj)); + Bind(&if_didnotmatch); + Return(FalseConstant()); + } - Handle<Object> global_obj; - ASSIGN_RETURN_FAILURE_ON_EXCEPTION( - isolate, global_obj, - JSReceiver::GetProperty(recv, isolate->factory()->global_string())); - const bool global = global_obj->BooleanValue(); + Bind(&slow_path); + { + // Call exec. + Node* const match_indices = RegExpExec(context, receiver, string); - if (!global) { - RETURN_RESULT_OR_FAILURE( - isolate, - RegExpUtils::RegExpExec(isolate, recv, string, - isolate->factory()->undefined_value())); + // Return true iff exec matched successfully. + Node* const result = + SelectBooleanConstant(WordNotEqual(match_indices, NullConstant())); + Return(result); } +} - Handle<Object> unicode_obj; - ASSIGN_RETURN_FAILURE_ON_EXCEPTION( - isolate, unicode_obj, - JSReceiver::GetProperty(recv, isolate->factory()->unicode_string())); - const bool unicode = unicode_obj->BooleanValue(); - - RETURN_FAILURE_ON_EXCEPTION(isolate, - RegExpUtils::SetLastIndex(isolate, recv, 0)); +Node* RegExpBuiltinsAssembler::AdvanceStringIndex(Node* const string, + Node* const index, + Node* const is_unicode) { + Variable var_result(this, MachineRepresentation::kTagged); - static const int kInitialArraySize = 8; - Handle<FixedArray> elems = - isolate->factory()->NewFixedArrayWithHoles(kInitialArraySize); + // Default to last_index + 1. + Node* const index_plus_one = SmiAdd(index, SmiConstant(1)); + var_result.Bind(index_plus_one); - int n = 0; - for (;; n++) { - Handle<Object> result; - ASSIGN_RETURN_FAILURE_ON_EXCEPTION( - isolate, result, - RegExpUtils::RegExpExec(isolate, recv, string, - isolate->factory()->undefined_value())); + Label if_isunicode(this), out(this); + Branch(is_unicode, &if_isunicode, &out); - if (result->IsNull(isolate)) { - if (n == 0) return isolate->heap()->null_value(); - break; - } + Bind(&if_isunicode); + { + Node* const string_length = LoadStringLength(string); + GotoUnless(SmiLessThan(index_plus_one, string_length), &out); - Handle<Object> match_obj; - ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, match_obj, - Object::GetElement(isolate, result, 0)); + Node* const lead = StringCharCodeAt(string, index); + GotoUnless(Word32Equal(Word32And(lead, Int32Constant(0xFC00)), + Int32Constant(0xD800)), + &out); - Handle<String> match; - ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, match, - Object::ToString(isolate, match_obj)); + Node* const trail = StringCharCodeAt(string, index_plus_one); + GotoUnless(Word32Equal(Word32And(trail, Int32Constant(0xFC00)), + Int32Constant(0xDC00)), + &out); - elems = FixedArray::SetAndGrow(elems, n, match); + // At a surrogate pair, return index + 2. + Node* const index_plus_two = SmiAdd(index, SmiConstant(2)); + var_result.Bind(index_plus_two); - if (match->length() == 0) { - RETURN_FAILURE_ON_EXCEPTION(isolate, RegExpUtils::SetAdvancedStringIndex( - isolate, recv, string, unicode)); - } + Goto(&out); } - elems->Shrink(n); - return *isolate->factory()->NewJSArrayWithElements(elems); + Bind(&out); + return var_result.value(); } namespace { -void Generate_RegExpPrototypeSearchBody(CodeStubAssembler* a, - compiler::Node* const receiver, - compiler::Node* const string, - compiler::Node* const context, - bool is_fastpath) { +// Utility class implementing a growable fixed array through CSA. +class GrowableFixedArray { typedef CodeStubAssembler::Label Label; - typedef compiler::Node Node; + typedef CodeStubAssembler::Variable Variable; - Isolate* const isolate = a->isolate(); + public: + explicit GrowableFixedArray(CodeStubAssembler* a) + : assembler_(a), + var_array_(a, MachineRepresentation::kTagged), + var_length_(a, MachineType::PointerRepresentation()), + var_capacity_(a, MachineType::PointerRepresentation()) { + Initialize(); + } - Node* const smi_zero = a->SmiConstant(Smi::kZero); + Node* length() const { return var_length_.value(); } - // Grab the initial value of last index. - Node* const previous_last_index = - is_fastpath ? FastLoadLastIndex(a, context, receiver) - : SlowLoadLastIndex(a, context, receiver); + Variable* var_array() { return &var_array_; } + Variable* var_length() { return &var_length_; } + Variable* var_capacity() { return &var_capacity_; } - // Ensure last index is 0. - if (is_fastpath) { - FastStoreLastIndex(a, context, receiver, smi_zero); - } else { - Label next(a); - a->GotoIf(a->SameValue(previous_last_index, smi_zero, context), &next); + void Push(Node* const value) { + CodeStubAssembler* a = assembler_; - SlowStoreLastIndex(a, context, receiver, smi_zero); - a->Goto(&next); - a->Bind(&next); - } + Node* const length = var_length_.value(); + Node* const capacity = var_capacity_.value(); - // Call exec. - Node* const match_indices = - is_fastpath ? RegExpPrototypeExecInternal(a, context, receiver, string) - : RegExpExec(a, context, receiver, string); + Label grow(a), store(a); + a->Branch(a->IntPtrEqual(capacity, length), &grow, &store); - // Reset last index if necessary. - if (is_fastpath) { - FastStoreLastIndex(a, context, receiver, previous_last_index); - } else { - Label next(a); - Node* const current_last_index = SlowLoadLastIndex(a, context, receiver); + a->Bind(&grow); + { + Node* const new_capacity = NewCapacity(a, capacity); + Node* const new_array = ResizeFixedArray(length, new_capacity); - a->GotoIf(a->SameValue(current_last_index, previous_last_index, context), - &next); + var_capacity_.Bind(new_capacity); + var_array_.Bind(new_array); + a->Goto(&store); + } - SlowStoreLastIndex(a, context, receiver, previous_last_index); - a->Goto(&next); - a->Bind(&next); - } + a->Bind(&store); + { + Node* const array = var_array_.value(); + a->StoreFixedArrayElement(array, length, value); - // Return -1 if no match was found. - { - Label next(a); - a->GotoUnless(a->WordEqual(match_indices, a->NullConstant()), &next); - a->Return(a->SmiConstant(-1)); - a->Bind(&next); + Node* const new_length = a->IntPtrAdd(length, a->IntPtrConstant(1)); + var_length_.Bind(new_length); + } } - // Return the index of the match. - { - Label fast_result(a), slow_result(a, Label::kDeferred); + Node* ToJSArray(Node* const context) { + CodeStubAssembler* a = assembler_; - 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); + const ElementsKind kind = FAST_ELEMENTS; - a->Branch(a->WordEqual(match_indices_map, initial_regexp_result_map), - &fast_result, &slow_result); + Node* const native_context = a->LoadNativeContext(context); + Node* const array_map = a->LoadJSArrayElementsMap(kind, native_context); - a->Bind(&fast_result); + // Shrink to fit if necessary. { - Node* const index = - a->LoadObjectField(match_indices, JSRegExpResult::kIndexOffset, - MachineType::AnyTagged()); - a->Return(index); - } + Label next(a); - a->Bind(&slow_result); - { - 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); + Node* const length = var_length_.value(); + Node* const capacity = var_capacity_.value(); + + a->GotoIf(a->WordEqual(length, capacity), &next); + + Node* const array = ResizeFixedArray(length, length); + var_array_.Bind(array); + var_capacity_.Bind(length); + a->Goto(&next); + + a->Bind(&next); } - } -} -} // namespace + Node* const result_length = a->SmiTag(length()); + Node* const result = a->AllocateUninitializedJSArrayWithoutElements( + kind, array_map, result_length, nullptr); -// ES#sec-regexp.prototype-@@search -// RegExp.prototype [ @@search ] ( string ) -void Builtins::Generate_RegExpPrototypeSearch(CodeStubAssembler* a) { - typedef CodeStubAssembler::Label Label; - typedef compiler::Node Node; + // Note: We do not currently shrink the fixed array. - Isolate* const isolate = a->isolate(); + a->StoreObjectField(result, JSObject::kElementsOffset, var_array_.value()); - Node* const maybe_receiver = a->Parameter(0); - Node* const maybe_string = a->Parameter(1); - Node* const context = a->Parameter(4); + return result; + } - // 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; + private: + void Initialize() { + CodeStubAssembler* a = assembler_; - // Convert {maybe_string} to a String. - Node* const string = a->ToString(context, maybe_string); + const ElementsKind kind = FAST_ELEMENTS; - Label fast_path(a), slow_path(a); - BranchIfFastPath(a, context, map, &fast_path, &slow_path); + static const int kInitialArraySize = 8; + Node* const capacity = a->IntPtrConstant(kInitialArraySize); + Node* const array = a->AllocateFixedArray(kind, capacity); - a->Bind(&fast_path); - Generate_RegExpPrototypeSearchBody(a, receiver, string, context, true); + a->FillFixedArrayWithValue(kind, array, a->IntPtrConstant(0), capacity, + Heap::kTheHoleValueRootIndex); - a->Bind(&slow_path); - Generate_RegExpPrototypeSearchBody(a, receiver, string, context, false); -} + var_array_.Bind(array); + var_capacity_.Bind(capacity); + var_length_.Bind(a->IntPtrConstant(0)); + } -namespace { + Node* NewCapacity(CodeStubAssembler* a, Node* const current_capacity) { + CSA_ASSERT(a, a->IntPtrGreaterThan(current_capacity, a->IntPtrConstant(0))); + + // Growth rate is analog to JSObject::NewElementsCapacity: + // new_capacity = (current_capacity + (current_capacity >> 1)) + 16. -MUST_USE_RESULT MaybeHandle<Object> ToUint32(Isolate* isolate, - Handle<Object> object, - uint32_t* out) { - if (object->IsUndefined(isolate)) { - *out = kMaxUInt32; - return object; + Node* const new_capacity = a->IntPtrAdd( + a->IntPtrAdd(current_capacity, a->WordShr(current_capacity, 1)), + a->IntPtrConstant(16)); + + return new_capacity; } - Handle<Object> number; - ASSIGN_RETURN_ON_EXCEPTION(isolate, number, Object::ToNumber(object), Object); - *out = NumberToUint32(*number); - return object; -} + // Creates a new array with {new_capacity} and copies the first + // {element_count} elements from the current array. + Node* ResizeFixedArray(Node* const element_count, Node* const new_capacity) { + CodeStubAssembler* a = assembler_; -bool AtSurrogatePair(Isolate* isolate, Handle<String> string, int index) { - if (index + 1 >= string->length()) return false; - const uint16_t first = string->Get(index); - if (first < 0xD800 || first > 0xDBFF) return false; - const uint16_t second = string->Get(index + 1); - return (second >= 0xDC00 && second <= 0xDFFF); -} + CSA_ASSERT(a, a->IntPtrGreaterThan(element_count, a->IntPtrConstant(0))); + CSA_ASSERT(a, a->IntPtrGreaterThan(new_capacity, a->IntPtrConstant(0))); + CSA_ASSERT(a, a->IntPtrGreaterThanOrEqual(new_capacity, element_count)); -Handle<JSArray> NewJSArrayWithElements(Isolate* isolate, - Handle<FixedArray> elems, - int num_elems) { - elems->Shrink(num_elems); - return isolate->factory()->NewJSArrayWithElements(elems); -} + const ElementsKind kind = FAST_ELEMENTS; + const WriteBarrierMode barrier_mode = UPDATE_WRITE_BARRIER; + const ParameterMode mode = CodeStubAssembler::INTPTR_PARAMETERS; + const CodeStubAssembler::AllocationFlags flags = + CodeStubAssembler::kAllowLargeObjectAllocation; -MaybeHandle<JSArray> RegExpSplit(Isolate* isolate, Handle<JSRegExp> regexp, - Handle<String> string, - Handle<Object> limit_obj) { - Factory* factory = isolate->factory(); + Node* const from_array = var_array_.value(); + Node* const to_array = + a->AllocateFixedArray(kind, new_capacity, mode, flags); + a->CopyFixedArrayElements(kind, from_array, kind, to_array, element_count, + new_capacity, barrier_mode, mode); - uint32_t limit; - RETURN_ON_EXCEPTION(isolate, ToUint32(isolate, limit_obj, &limit), JSArray); + return to_array; + } - const int length = string->length(); + private: + CodeStubAssembler* const assembler_; + Variable var_array_; + Variable var_length_; + Variable var_capacity_; +}; - if (limit == 0) return factory->NewJSArray(0); +} // namespace + +void RegExpBuiltinsAssembler::RegExpPrototypeMatchBody(Node* const context, + Node* const regexp, + Node* const string, + const bool is_fastpath) { + Isolate* const isolate = this->isolate(); - Handle<RegExpMatchInfo> last_match_info = isolate->regexp_last_match_info(); + Node* const null = NullConstant(); + Node* const int_zero = IntPtrConstant(0); + Node* const smi_zero = SmiConstant(Smi::kZero); - if (length == 0) { - Handle<Object> match_indices; - ASSIGN_RETURN_ON_EXCEPTION( - isolate, match_indices, - RegExpImpl::Exec(regexp, string, 0, last_match_info), JSArray); + Node* const is_global = + FlagGetter(context, regexp, JSRegExp::kGlobal, is_fastpath); - if (!match_indices->IsNull(isolate)) return factory->NewJSArray(0); + Label if_isglobal(this), if_isnotglobal(this); + Branch(is_global, &if_isglobal, &if_isnotglobal); - Handle<FixedArray> elems = factory->NewUninitializedFixedArray(1); - elems->set(0, *string); - return factory->NewJSArrayWithElements(elems); + Bind(&if_isnotglobal); + { + Node* const result = + is_fastpath ? RegExpPrototypeExecBody(context, regexp, string, true) + : RegExpExec(context, regexp, string); + Return(result); } - int current_index = 0; - int start_index = 0; - int start_match = 0; + Bind(&if_isglobal); + { + Node* const is_unicode = + FlagGetter(context, regexp, JSRegExp::kUnicode, is_fastpath); - static const int kInitialArraySize = 8; - Handle<FixedArray> elems = factory->NewFixedArrayWithHoles(kInitialArraySize); - int num_elems = 0; + StoreLastIndex(context, regexp, smi_zero, is_fastpath); - while (true) { - if (start_index == length) { - Handle<String> substr = - factory->NewSubString(string, current_index, length); - elems = FixedArray::SetAndGrow(elems, num_elems++, substr); - break; - } + // Allocate an array to store the resulting match strings. - Handle<Object> match_indices_obj; - ASSIGN_RETURN_ON_EXCEPTION( - isolate, match_indices_obj, - RegExpImpl::Exec(regexp, string, start_index, - isolate->regexp_last_match_info()), - JSArray); - - if (match_indices_obj->IsNull(isolate)) { - Handle<String> substr = - factory->NewSubString(string, current_index, length); - elems = FixedArray::SetAndGrow(elems, num_elems++, substr); - break; - } + GrowableFixedArray array(this); - auto match_indices = Handle<RegExpMatchInfo>::cast(match_indices_obj); + // Loop preparations. Within the loop, collect results from RegExpExec + // and store match strings in the array. - start_match = match_indices->Capture(0); + Variable* vars[] = {array.var_array(), array.var_length(), + array.var_capacity()}; + Label loop(this, 3, vars), out(this); + Goto(&loop); - if (start_match == length) { - Handle<String> substr = - factory->NewSubString(string, current_index, length); - elems = FixedArray::SetAndGrow(elems, num_elems++, substr); - break; - } + Bind(&loop); + { + Variable var_match(this, MachineRepresentation::kTagged); - const int end_index = match_indices->Capture(1); + Label if_didmatch(this), if_didnotmatch(this); + if (is_fastpath) { + // On the fast path, grab the matching string from the raw match index + // array. + Node* const match_indices = RegExpPrototypeExecBodyWithoutResult( + context, regexp, string, &if_didnotmatch, true); - if (start_index == end_index && end_index == current_index) { - const bool unicode = (regexp->GetFlags() & JSRegExp::kUnicode) != 0; - if (unicode && AtSurrogatePair(isolate, string, start_index)) { - start_index += 2; + Node* const match_from = LoadFixedArrayElement( + match_indices, RegExpMatchInfo::kFirstCaptureIndex); + Node* const match_to = LoadFixedArrayElement( + match_indices, RegExpMatchInfo::kFirstCaptureIndex + 1); + + Node* match = SubString(context, string, match_from, match_to); + var_match.Bind(match); + + Goto(&if_didmatch); } else { - start_index += 1; + DCHECK(!is_fastpath); + Node* const result = RegExpExec(context, regexp, string); + + Label load_match(this); + Branch(WordEqual(result, null), &if_didnotmatch, &load_match); + + Bind(&load_match); + { + Label fast_result(this), slow_result(this); + BranchIfFastRegExpResult(context, LoadMap(result), &fast_result, + &slow_result); + + Bind(&fast_result); + { + Node* const result_fixed_array = LoadElements(result); + Node* const match = LoadFixedArrayElement(result_fixed_array, 0); + + // The match is guaranteed to be a string on the fast path. + CSA_ASSERT(this, IsStringInstanceType(LoadInstanceType(match))); + + var_match.Bind(match); + Goto(&if_didmatch); + } + + Bind(&slow_result); + { + // TODO(ishell): Use GetElement stub once it's available. + Node* const name = smi_zero; + Callable getproperty_callable = CodeFactory::GetProperty(isolate); + Node* const match = + CallStub(getproperty_callable, context, result, name); + + var_match.Bind(ToString(context, match)); + Goto(&if_didmatch); + } + } + } + + Bind(&if_didnotmatch); + { + // Return null if there were no matches, otherwise just exit the loop. + GotoUnless(IntPtrEqual(array.length(), int_zero), &out); + Return(null); + } + + Bind(&if_didmatch); + { + Node* match = var_match.value(); + + // Store the match, growing the fixed array if needed. + + array.Push(match); + + // Advance last index if the match is the empty string. + + Node* const match_length = LoadStringLength(match); + GotoUnless(SmiEqual(match_length, smi_zero), &loop); + + Node* last_index = LoadLastIndex(context, regexp, is_fastpath); + + Callable tolength_callable = CodeFactory::ToLength(isolate); + last_index = CallStub(tolength_callable, context, last_index); + + Node* const new_last_index = + AdvanceStringIndex(string, last_index, is_unicode); + + StoreLastIndex(context, regexp, new_last_index, is_fastpath); + + Goto(&loop); } - continue; } + Bind(&out); { - Handle<String> substr = - factory->NewSubString(string, current_index, start_match); - elems = FixedArray::SetAndGrow(elems, num_elems++, substr); + // Wrap the match in a JSArray. + + Node* const result = array.ToJSArray(context); + Return(result); } + } +} + +// ES#sec-regexp.prototype-@@match +// RegExp.prototype [ @@match ] ( string ) +TF_BUILTIN(RegExpPrototypeMatch, RegExpBuiltinsAssembler) { + Node* const maybe_receiver = Parameter(0); + Node* const maybe_string = Parameter(1); + Node* const context = Parameter(4); + + // Ensure {maybe_receiver} is a JSReceiver. + Node* const map = ThrowIfNotJSReceiver( + context, maybe_receiver, MessageTemplate::kIncompatibleMethodReceiver, + "RegExp.prototype.@@match"); + Node* const receiver = maybe_receiver; - if (static_cast<uint32_t>(num_elems) == limit) break; + // Convert {maybe_string} to a String. + Node* const string = ToString(context, maybe_string); - for (int i = 2; i < match_indices->NumberOfCaptureRegisters(); i += 2) { - const int start = match_indices->Capture(i); - const int end = match_indices->Capture(i + 1); + Label fast_path(this), slow_path(this); + BranchIfFastRegExp(context, map, &fast_path, &slow_path); - if (end != -1) { - Handle<String> substr = factory->NewSubString(string, start, end); - elems = FixedArray::SetAndGrow(elems, num_elems++, substr); - } else { - elems = FixedArray::SetAndGrow(elems, num_elems++, - factory->undefined_value()); - } + Bind(&fast_path); + RegExpPrototypeMatchBody(context, receiver, string, true); - if (static_cast<uint32_t>(num_elems) == limit) { - return NewJSArrayWithElements(isolate, elems, num_elems); - } - } + Bind(&slow_path); + RegExpPrototypeMatchBody(context, receiver, string, false); +} - start_index = current_index = end_index; +void RegExpBuiltinsAssembler::RegExpPrototypeSearchBodyFast( + Node* const context, Node* const regexp, Node* const string) { + // Grab the initial value of last index. + Node* const previous_last_index = FastLoadLastIndex(regexp); + + // Ensure last index is 0. + FastStoreLastIndex(regexp, SmiConstant(Smi::kZero)); + + // Call exec. + Label if_didnotmatch(this); + Node* const match_indices = RegExpPrototypeExecBodyWithoutResult( + context, regexp, string, &if_didnotmatch, true); + + // Successful match. + { + // Reset last index. + FastStoreLastIndex(regexp, previous_last_index); + + // Return the index of the match. + Node* const index = LoadFixedArrayElement( + match_indices, RegExpMatchInfo::kFirstCaptureIndex); + Return(index); } - return NewJSArrayWithElements(isolate, elems, num_elems); + Bind(&if_didnotmatch); + { + // Reset last index and return -1. + FastStoreLastIndex(regexp, previous_last_index); + Return(SmiConstant(-1)); + } } -// ES##sec-speciesconstructor -// SpeciesConstructor ( O, defaultConstructor ) -MUST_USE_RESULT MaybeHandle<Object> SpeciesConstructor( - Isolate* isolate, Handle<JSReceiver> recv, - Handle<JSFunction> default_ctor) { - Handle<Object> ctor_obj; - ASSIGN_RETURN_ON_EXCEPTION( - isolate, ctor_obj, - JSObject::GetProperty(recv, isolate->factory()->constructor_string()), - Object); +void RegExpBuiltinsAssembler::RegExpPrototypeSearchBodySlow( + Node* const context, Node* const regexp, Node* const string) { + Isolate* const isolate = this->isolate(); - if (ctor_obj->IsUndefined(isolate)) return default_ctor; + Node* const smi_zero = SmiConstant(Smi::kZero); - if (!ctor_obj->IsJSReceiver()) { - THROW_NEW_ERROR(isolate, - NewTypeError(MessageTemplate::kConstructorNotReceiver), - Object); + // Grab the initial value of last index. + Node* const previous_last_index = SlowLoadLastIndex(context, regexp); + + // Ensure last index is 0. + { + Label next(this); + GotoIf(SameValue(previous_last_index, smi_zero, context), &next); + + SlowStoreLastIndex(context, regexp, smi_zero); + Goto(&next); + Bind(&next); } - Handle<JSReceiver> ctor = Handle<JSReceiver>::cast(ctor_obj); + // Call exec. + Node* const exec_result = RegExpExec(context, regexp, string); + + // Reset last index if necessary. + { + Label next(this); + Node* const current_last_index = SlowLoadLastIndex(context, regexp); + + GotoIf(SameValue(current_last_index, previous_last_index, context), &next); - Handle<Object> species; - ASSIGN_RETURN_ON_EXCEPTION( - isolate, species, - JSObject::GetProperty(ctor, isolate->factory()->species_symbol()), - Object); + SlowStoreLastIndex(context, regexp, previous_last_index); + Goto(&next); - if (species->IsNull(isolate) || species->IsUndefined(isolate)) { - return default_ctor; + Bind(&next); } - if (species->IsConstructor()) return species; + // Return -1 if no match was found. + { + Label next(this); + GotoUnless(WordEqual(exec_result, NullConstant()), &next); + Return(SmiConstant(-1)); + Bind(&next); + } + + // Return the index of the match. + { + Label fast_result(this), slow_result(this, Label::kDeferred); + BranchIfFastRegExpResult(context, LoadMap(exec_result), &fast_result, + &slow_result); + + Bind(&fast_result); + { + Node* const index = + LoadObjectField(exec_result, JSRegExpResult::kIndexOffset); + Return(index); + } - THROW_NEW_ERROR( - isolate, NewTypeError(MessageTemplate::kSpeciesNotConstructor), Object); + Bind(&slow_result); + { + Node* const name = HeapConstant(isolate->factory()->index_string()); + Callable getproperty_callable = CodeFactory::GetProperty(isolate); + Node* const index = + CallStub(getproperty_callable, context, exec_result, name); + Return(index); + } + } } -} // namespace +// ES#sec-regexp.prototype-@@search +// RegExp.prototype [ @@search ] ( string ) +TF_BUILTIN(RegExpPrototypeSearch, RegExpBuiltinsAssembler) { + Node* const maybe_receiver = Parameter(0); + Node* const maybe_string = Parameter(1); + Node* const context = Parameter(4); -// ES#sec-regexp.prototype-@@split -// RegExp.prototype [ @@split ] ( string, limit ) -BUILTIN(RegExpPrototypeSplit) { - HandleScope scope(isolate); - CHECK_RECEIVER(JSReceiver, recv, "RegExp.prototype.@@split"); + // Ensure {maybe_receiver} is a JSReceiver. + Node* const map = ThrowIfNotJSReceiver( + context, maybe_receiver, MessageTemplate::kIncompatibleMethodReceiver, + "RegExp.prototype.@@search"); + Node* const receiver = maybe_receiver; - Factory* factory = isolate->factory(); + // Convert {maybe_string} to a String. + Node* const string = ToString(context, maybe_string); - Handle<Object> string_obj = args.atOrUndefined(isolate, 1); - Handle<Object> limit_obj = args.atOrUndefined(isolate, 2); + Label fast_path(this), slow_path(this); + BranchIfFastRegExp(context, map, &fast_path, &slow_path); - Handle<String> string; - ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, string, - Object::ToString(isolate, string_obj)); + Bind(&fast_path); + RegExpPrototypeSearchBodyFast(context, receiver, string); - if (RegExpUtils::IsUnmodifiedRegExp(isolate, recv)) { - RETURN_RESULT_OR_FAILURE( - isolate, - RegExpSplit(isolate, Handle<JSRegExp>::cast(recv), string, limit_obj)); - } + Bind(&slow_path); + RegExpPrototypeSearchBodySlow(context, receiver, string); +} - Handle<JSFunction> regexp_fun = isolate->regexp_function(); - Handle<Object> ctor; - ASSIGN_RETURN_FAILURE_ON_EXCEPTION( - isolate, ctor, SpeciesConstructor(isolate, recv, regexp_fun)); +// Generates the fast path for @@split. {regexp} is an unmodified JSRegExp, +// {string} is a String, and {limit} is a Smi. +void RegExpBuiltinsAssembler::RegExpPrototypeSplitBody(Node* const context, + Node* const regexp, + Node* const string, + Node* const limit) { + Isolate* isolate = this->isolate(); - Handle<Object> flags_obj; - ASSIGN_RETURN_FAILURE_ON_EXCEPTION( - isolate, flags_obj, JSObject::GetProperty(recv, factory->flags_string())); + Node* const null = NullConstant(); + Node* const smi_zero = SmiConstant(0); + Node* const int_zero = IntPtrConstant(0); + Node* const int_limit = SmiUntag(limit); - Handle<String> flags; - ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, flags, - Object::ToString(isolate, flags_obj)); + const ElementsKind kind = FAST_ELEMENTS; + const ParameterMode mode = CodeStubAssembler::INTPTR_PARAMETERS; - Handle<String> u_str = factory->LookupSingleCharacterStringFromCode('u'); - const bool unicode = (String::IndexOf(isolate, flags, u_str, 0) >= 0); + Node* const allocation_site = nullptr; + Node* const native_context = LoadNativeContext(context); + Node* const array_map = LoadJSArrayElementsMap(kind, native_context); - Handle<String> y_str = factory->LookupSingleCharacterStringFromCode('y'); - const bool sticky = (String::IndexOf(isolate, flags, y_str, 0) >= 0); + Label return_empty_array(this, Label::kDeferred); - Handle<String> new_flags = flags; - if (!sticky) { - ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, new_flags, - factory->NewConsString(flags, y_str)); + // If limit is zero, return an empty array. + { + Label next(this), if_limitiszero(this, Label::kDeferred); + Branch(SmiEqual(limit, smi_zero), &return_empty_array, &next); + Bind(&next); } - Handle<JSReceiver> splitter; + Node* const string_length = LoadStringLength(string); + + // If passed the empty {string}, return either an empty array or a singleton + // array depending on whether the {regexp} matches. { - const int argc = 2; + Label next(this), if_stringisempty(this, Label::kDeferred); + Branch(SmiEqual(string_length, smi_zero), &if_stringisempty, &next); - ScopedVector<Handle<Object>> argv(argc); - argv[0] = recv; - argv[1] = new_flags; + Bind(&if_stringisempty); + { + Node* const last_match_info = LoadContextElement( + native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX); - Handle<JSFunction> ctor_fun = Handle<JSFunction>::cast(ctor); - Handle<Object> splitter_obj; - ASSIGN_RETURN_FAILURE_ON_EXCEPTION( - isolate, splitter_obj, Execution::New(ctor_fun, argc, argv.start())); + Callable exec_callable = CodeFactory::RegExpExec(isolate); + Node* const match_indices = CallStub(exec_callable, context, regexp, + string, smi_zero, last_match_info); + + Label return_singleton_array(this); + Branch(WordEqual(match_indices, null), &return_singleton_array, + &return_empty_array); + + Bind(&return_singleton_array); + { + Node* const length = SmiConstant(1); + Node* const capacity = IntPtrConstant(1); + Node* const result = AllocateJSArray(kind, array_map, capacity, length, + allocation_site, mode); + + Node* const fixed_array = LoadElements(result); + StoreFixedArrayElement(fixed_array, 0, string); - splitter = Handle<JSReceiver>::cast(splitter_obj); + Return(result); + } + } + + Bind(&next); } - uint32_t limit; - RETURN_FAILURE_ON_EXCEPTION(isolate, ToUint32(isolate, limit_obj, &limit)); + // Loop preparations. - const int length = string->length(); + GrowableFixedArray array(this); - if (limit == 0) return *factory->NewJSArray(0); + Variable var_last_matched_until(this, MachineRepresentation::kTagged); + Variable var_next_search_from(this, MachineRepresentation::kTagged); - if (length == 0) { - Handle<Object> result; - ASSIGN_RETURN_FAILURE_ON_EXCEPTION( - isolate, result, RegExpUtils::RegExpExec(isolate, splitter, string, - factory->undefined_value())); + var_last_matched_until.Bind(smi_zero); + var_next_search_from.Bind(smi_zero); - if (!result->IsNull(isolate)) return *factory->NewJSArray(0); + Variable* vars[] = {array.var_array(), array.var_length(), + array.var_capacity(), &var_last_matched_until, + &var_next_search_from}; + const int vars_count = sizeof(vars) / sizeof(vars[0]); + Label loop(this, vars_count, vars), push_suffix_and_out(this), out(this); + Goto(&loop); - Handle<FixedArray> elems = factory->NewUninitializedFixedArray(1); - elems->set(0, *string); - return *factory->NewJSArrayWithElements(elems); - } + Bind(&loop); + { + Node* const next_search_from = var_next_search_from.value(); + Node* const last_matched_until = var_last_matched_until.value(); - // TODO(jgruber): Wrap this in a helper class. - static const int kInitialArraySize = 8; - Handle<FixedArray> elems = factory->NewFixedArrayWithHoles(kInitialArraySize); - int num_elems = 0; + // We're done if we've reached the end of the string. + { + Label next(this); + Branch(SmiEqual(next_search_from, string_length), &push_suffix_and_out, + &next); + Bind(&next); + } - int string_index = 0; - int prev_string_index = 0; - while (string_index < length) { - RETURN_FAILURE_ON_EXCEPTION( - isolate, RegExpUtils::SetLastIndex(isolate, splitter, string_index)); + // Search for the given {regexp}. - Handle<Object> result; - ASSIGN_RETURN_FAILURE_ON_EXCEPTION( - isolate, result, RegExpUtils::RegExpExec(isolate, splitter, string, - factory->undefined_value())); + Node* const last_match_info = LoadContextElement( + native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX); + + Callable exec_callable = CodeFactory::RegExpExec(isolate); + Node* const match_indices = CallStub(exec_callable, context, regexp, string, + next_search_from, last_match_info); - if (result->IsNull(isolate)) { - string_index = RegExpUtils::AdvanceStringIndex(isolate, string, - string_index, unicode); - continue; + // We're done if no match was found. + { + Label next(this); + Branch(WordEqual(match_indices, null), &push_suffix_and_out, &next); + Bind(&next); } - // TODO(jgruber): Extract toLength of some property into function. - Handle<Object> last_index_obj; - ASSIGN_RETURN_FAILURE_ON_EXCEPTION( - isolate, last_index_obj, RegExpUtils::GetLastIndex(isolate, splitter)); + Node* const match_from = LoadFixedArrayElement( + match_indices, RegExpMatchInfo::kFirstCaptureIndex); - ASSIGN_RETURN_FAILURE_ON_EXCEPTION( - isolate, last_index_obj, Object::ToLength(isolate, last_index_obj)); - const int last_index = Handle<Smi>::cast(last_index_obj)->value(); - - const int end = std::min(last_index, length); - if (end == prev_string_index) { - string_index = RegExpUtils::AdvanceStringIndex(isolate, string, - string_index, unicode); - continue; + // We're done if the match starts beyond the string. + { + Label next(this); + Branch(WordEqual(match_from, string_length), &push_suffix_and_out, &next); + Bind(&next); } + Node* const match_to = LoadFixedArrayElement( + match_indices, RegExpMatchInfo::kFirstCaptureIndex + 1); + + // Advance index and continue if the match is empty. { - Handle<String> substr = - factory->NewSubString(string, prev_string_index, string_index); - elems = FixedArray::SetAndGrow(elems, num_elems++, substr); - if (static_cast<uint32_t>(num_elems) == limit) { - return *NewJSArrayWithElements(isolate, elems, num_elems); - } + Label next(this); + + GotoUnless(SmiEqual(match_to, next_search_from), &next); + GotoUnless(SmiEqual(match_to, last_matched_until), &next); + + Node* const is_unicode = FastFlagGetter(regexp, JSRegExp::kUnicode); + Node* const new_next_search_from = + AdvanceStringIndex(string, next_search_from, is_unicode); + var_next_search_from.Bind(new_next_search_from); + Goto(&loop); + + Bind(&next); + } + + // A valid match was found, add the new substring to the array. + { + Node* const from = last_matched_until; + Node* const to = match_from; + + Node* const substr = SubString(context, string, from, to); + array.Push(substr); + + GotoIf(WordEqual(array.length(), int_limit), &out); } - prev_string_index = end; + // Add all captures to the array. + { + Node* const num_registers = LoadFixedArrayElement( + match_indices, RegExpMatchInfo::kNumberOfCapturesIndex); + Node* const int_num_registers = SmiUntag(num_registers); + + Variable var_reg(this, MachineType::PointerRepresentation()); + var_reg.Bind(IntPtrConstant(2)); - Handle<Object> num_captures_obj; - ASSIGN_RETURN_FAILURE_ON_EXCEPTION( - isolate, num_captures_obj, - Object::GetProperty(result, isolate->factory()->length_string())); + Variable* vars[] = {array.var_array(), array.var_length(), + array.var_capacity(), &var_reg}; + const int vars_count = sizeof(vars) / sizeof(vars[0]); + Label nested_loop(this, vars_count, vars), nested_loop_out(this); + Branch(IntPtrLessThan(var_reg.value(), int_num_registers), &nested_loop, + &nested_loop_out); - ASSIGN_RETURN_FAILURE_ON_EXCEPTION( - isolate, num_captures_obj, Object::ToLength(isolate, num_captures_obj)); - const int num_captures = - std::max(Handle<Smi>::cast(num_captures_obj)->value(), 0); - - for (int i = 1; i < num_captures; i++) { - Handle<Object> capture; - ASSIGN_RETURN_FAILURE_ON_EXCEPTION( - isolate, capture, Object::GetElement(isolate, result, i)); - elems = FixedArray::SetAndGrow(elems, num_elems++, capture); - if (static_cast<uint32_t>(num_elems) == limit) { - return *NewJSArrayWithElements(isolate, elems, num_elems); + Bind(&nested_loop); + { + Node* const reg = var_reg.value(); + Node* const from = LoadFixedArrayElement( + match_indices, reg, + RegExpMatchInfo::kFirstCaptureIndex * kPointerSize, mode); + Node* const to = LoadFixedArrayElement( + match_indices, reg, + (RegExpMatchInfo::kFirstCaptureIndex + 1) * kPointerSize, mode); + + Label select_capture(this), select_undefined(this), store_value(this); + Variable var_value(this, MachineRepresentation::kTagged); + Branch(SmiEqual(to, SmiConstant(-1)), &select_undefined, + &select_capture); + + Bind(&select_capture); + { + Node* const substr = SubString(context, string, from, to); + var_value.Bind(substr); + Goto(&store_value); + } + + Bind(&select_undefined); + { + Node* const undefined = UndefinedConstant(); + var_value.Bind(undefined); + Goto(&store_value); + } + + Bind(&store_value); + { + array.Push(var_value.value()); + GotoIf(WordEqual(array.length(), int_limit), &out); + + Node* const new_reg = IntPtrAdd(reg, IntPtrConstant(2)); + var_reg.Bind(new_reg); + + Branch(IntPtrLessThan(new_reg, int_num_registers), &nested_loop, + &nested_loop_out); + } } + + Bind(&nested_loop_out); } - string_index = prev_string_index; + var_last_matched_until.Bind(match_to); + var_next_search_from.Bind(match_to); + Goto(&loop); } + Bind(&push_suffix_and_out); { - Handle<String> substr = - factory->NewSubString(string, prev_string_index, length); - elems = FixedArray::SetAndGrow(elems, num_elems++, substr); + Node* const from = var_last_matched_until.value(); + Node* const to = string_length; + + Node* const substr = SubString(context, string, from, to); + array.Push(substr); + + Goto(&out); } - return *NewJSArrayWithElements(isolate, elems, num_elems); + Bind(&out); + { + Node* const result = array.ToJSArray(context); + Return(result); + } + + Bind(&return_empty_array); + { + Node* const length = smi_zero; + Node* const capacity = int_zero; + Node* const result = AllocateJSArray(kind, array_map, capacity, length, + allocation_site, mode); + Return(result); + } } -namespace { +// ES#sec-regexp.prototype-@@split +// RegExp.prototype [ @@split ] ( string, limit ) +TF_BUILTIN(RegExpPrototypeSplit, RegExpBuiltinsAssembler) { + Node* const maybe_receiver = Parameter(0); + Node* const maybe_string = Parameter(1); + Node* const maybe_limit = Parameter(2); + Node* const context = Parameter(5); + + // Ensure {maybe_receiver} is a JSReceiver. + Node* const map = ThrowIfNotJSReceiver( + context, maybe_receiver, MessageTemplate::kIncompatibleMethodReceiver, + "RegExp.prototype.@@split"); + Node* const receiver = maybe_receiver; + + // Convert {maybe_string} to a String. + Node* const string = ToString(context, maybe_string); + + Label fast_path(this), slow_path(this); + BranchIfFastRegExp(context, map, &fast_path, &slow_path); -compiler::Node* ReplaceGlobalCallableFastPath( - CodeStubAssembler* a, compiler::Node* context, compiler::Node* regexp, - compiler::Node* subject_string, compiler::Node* replace_callable) { + Bind(&fast_path); + { + // TODO(jgruber): Even if map checks send us to the fast path, we still need + // to verify the constructor property and jump to the slow path if it has + // been changed. + + // Convert {maybe_limit} to a uint32, capping at the maximal smi value. + Variable var_limit(this, MachineRepresentation::kTagged); + Label if_limitissmimax(this), limit_done(this); + + GotoIf(IsUndefined(maybe_limit), &if_limitissmimax); + + { + Node* const limit = ToUint32(context, maybe_limit); + GotoUnless(TaggedIsSmi(limit), &if_limitissmimax); + + var_limit.Bind(limit); + Goto(&limit_done); + } + + Bind(&if_limitissmimax); + { + // TODO(jgruber): In this case, we can probably generation of limit checks + // in Generate_RegExpPrototypeSplitBody. + Node* const smi_max = SmiConstant(Smi::kMaxValue); + var_limit.Bind(smi_max); + Goto(&limit_done); + } + + Bind(&limit_done); + { + Node* const limit = var_limit.value(); + RegExpPrototypeSplitBody(context, receiver, string, limit); + } + } + + Bind(&slow_path); + { + Node* const result = CallRuntime(Runtime::kRegExpSplit, context, receiver, + string, maybe_limit); + Return(result); + } +} + +Node* RegExpBuiltinsAssembler::ReplaceGlobalCallableFastPath( + Node* context, Node* regexp, Node* string, Node* replace_callable) { // The fast path is reached only if {receiver} is a global unmodified // JSRegExp instance and {replace_callable} is callable. - typedef CodeStubAssembler::Variable Variable; - typedef CodeStubAssembler::Label Label; - typedef compiler::Node Node; - - Isolate* const isolate = a->isolate(); + Isolate* const isolate = this->isolate(); - Node* const null = a->NullConstant(); - Node* const undefined = a->UndefinedConstant(); - Node* const int_zero = a->IntPtrConstant(0); - Node* const int_one = a->IntPtrConstant(1); - Node* const smi_zero = a->SmiConstant(Smi::kZero); + Node* const null = NullConstant(); + Node* const undefined = UndefinedConstant(); + Node* const int_zero = IntPtrConstant(0); + Node* const int_one = IntPtrConstant(1); + Node* const smi_zero = SmiConstant(Smi::kZero); - Node* const native_context = a->LoadNativeContext(context); + Node* const native_context = LoadNativeContext(context); - Label out(a); - Variable var_result(a, MachineRepresentation::kTagged); + Label out(this); + Variable var_result(this, MachineRepresentation::kTagged); // Set last index to 0. - FastStoreLastIndex(a, context, regexp, smi_zero); + FastStoreLastIndex(regexp, smi_zero); // Allocate {result_array}. Node* result_array; { ElementsKind kind = FAST_ELEMENTS; - Node* const array_map = a->LoadJSArrayElementsMap(kind, native_context); - Node* const capacity = a->IntPtrConstant(16); + Node* const array_map = LoadJSArrayElementsMap(kind, native_context); + Node* const capacity = IntPtrConstant(16); Node* const length = smi_zero; Node* const allocation_site = nullptr; - CodeStubAssembler::ParameterMode capacity_mode = - CodeStubAssembler::INTPTR_PARAMETERS; + ParameterMode capacity_mode = CodeStubAssembler::INTPTR_PARAMETERS; - result_array = a->AllocateJSArray(kind, array_map, capacity, length, - allocation_site, capacity_mode); + result_array = AllocateJSArray(kind, array_map, capacity, length, + allocation_site, capacity_mode); } // Call into runtime for RegExpExecMultiple. - Node* last_match_info = a->LoadContextElement( - native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX); - Node* const res = - a->CallRuntime(Runtime::kRegExpExecMultiple, context, regexp, - subject_string, last_match_info, result_array); + Node* last_match_info = + LoadContextElement(native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX); + Node* const res = CallRuntime(Runtime::kRegExpExecMultiple, context, regexp, + string, last_match_info, result_array); // Reset last index to 0. - FastStoreLastIndex(a, context, regexp, smi_zero); + FastStoreLastIndex(regexp, smi_zero); // If no matches, return the subject string. - var_result.Bind(subject_string); - a->GotoIf(a->WordEqual(res, null), &out); + var_result.Bind(string); + GotoIf(WordEqual(res, null), &out); // Reload last match info since it might have changed. - last_match_info = a->LoadContextElement( - native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX); + last_match_info = + LoadContextElement(native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX); - Node* const res_length = a->LoadJSArrayLength(res); - Node* const res_elems = a->LoadElements(res); - CSA_ASSERT(a, a->HasInstanceType(res_elems, FIXED_ARRAY_TYPE)); + Node* const res_length = LoadJSArrayLength(res); + Node* const res_elems = LoadElements(res); + CSA_ASSERT(this, HasInstanceType(res_elems, FIXED_ARRAY_TYPE)); - CodeStubAssembler::ParameterMode mode = CodeStubAssembler::INTPTR_PARAMETERS; - Node* const num_capture_registers = a->LoadFixedArrayElement( - last_match_info, - a->IntPtrConstant(RegExpMatchInfo::kNumberOfCapturesIndex), 0, mode); + Node* const num_capture_registers = LoadFixedArrayElement( + last_match_info, RegExpMatchInfo::kNumberOfCapturesIndex); - Label if_hasexplicitcaptures(a), if_noexplicitcaptures(a), create_result(a); - a->Branch(a->SmiEqual(num_capture_registers, a->SmiConstant(Smi::FromInt(2))), - &if_noexplicitcaptures, &if_hasexplicitcaptures); + Label if_hasexplicitcaptures(this), if_noexplicitcaptures(this), + create_result(this); + Branch(SmiEqual(num_capture_registers, SmiConstant(Smi::FromInt(2))), + &if_noexplicitcaptures, &if_hasexplicitcaptures); - a->Bind(&if_noexplicitcaptures); + Bind(&if_noexplicitcaptures); { // If the number of captures is two then there are no explicit captures in // the regexp, just the implicit capture that captures the whole match. In @@ -1710,394 +2172,358 @@ compiler::Node* ReplaceGlobalCallableFastPath( // input string and some replacements that were returned from the replace // function. - Variable var_match_start(a, MachineRepresentation::kTagged); + Variable var_match_start(this, MachineRepresentation::kTagged); var_match_start.Bind(smi_zero); - Node* const end = a->SmiUntag(res_length); - Variable var_i(a, MachineType::PointerRepresentation()); + Node* const end = SmiUntag(res_length); + Variable var_i(this, MachineType::PointerRepresentation()); var_i.Bind(int_zero); Variable* vars[] = {&var_i, &var_match_start}; - Label loop(a, 2, vars); - a->Goto(&loop); - a->Bind(&loop); + Label loop(this, 2, vars); + Goto(&loop); + Bind(&loop); { Node* const i = var_i.value(); - a->GotoUnless(a->IntPtrLessThan(i, end), &create_result); + GotoUnless(IntPtrLessThan(i, end), &create_result); - CodeStubAssembler::ParameterMode mode = - CodeStubAssembler::INTPTR_PARAMETERS; - Node* const elem = a->LoadFixedArrayElement(res_elems, i, 0, mode); + Node* const elem = LoadFixedArrayElement(res_elems, i); - Label if_issmi(a), if_isstring(a), loop_epilogue(a); - a->Branch(a->TaggedIsSmi(elem), &if_issmi, &if_isstring); + Label if_issmi(this), if_isstring(this), loop_epilogue(this); + Branch(TaggedIsSmi(elem), &if_issmi, &if_isstring); - a->Bind(&if_issmi); + Bind(&if_issmi); { // Integers represent slices of the original string. - Label if_isnegativeorzero(a), if_ispositive(a); - a->BranchIfSmiLessThanOrEqual(elem, smi_zero, &if_isnegativeorzero, - &if_ispositive); + Label if_isnegativeorzero(this), if_ispositive(this); + BranchIfSmiLessThanOrEqual(elem, smi_zero, &if_isnegativeorzero, + &if_ispositive); - a->Bind(&if_ispositive); + Bind(&if_ispositive); { - Node* const int_elem = a->SmiUntag(elem); + Node* const int_elem = SmiUntag(elem); Node* const new_match_start = - a->IntPtrAdd(a->WordShr(int_elem, a->IntPtrConstant(11)), - a->WordAnd(int_elem, a->IntPtrConstant(0x7ff))); - var_match_start.Bind(a->SmiTag(new_match_start)); - a->Goto(&loop_epilogue); + IntPtrAdd(WordShr(int_elem, IntPtrConstant(11)), + WordAnd(int_elem, IntPtrConstant(0x7ff))); + var_match_start.Bind(SmiTag(new_match_start)); + Goto(&loop_epilogue); } - a->Bind(&if_isnegativeorzero); + Bind(&if_isnegativeorzero); { - Node* const next_i = a->IntPtrAdd(i, int_one); + Node* const next_i = IntPtrAdd(i, int_one); var_i.Bind(next_i); - Node* const next_elem = - a->LoadFixedArrayElement(res_elems, next_i, 0, mode); + Node* const next_elem = LoadFixedArrayElement(res_elems, next_i); - Node* const new_match_start = a->SmiSub(next_elem, elem); + Node* const new_match_start = SmiSub(next_elem, elem); var_match_start.Bind(new_match_start); - a->Goto(&loop_epilogue); + Goto(&loop_epilogue); } } - a->Bind(&if_isstring); + Bind(&if_isstring); { - CSA_ASSERT(a, a->IsStringInstanceType(a->LoadInstanceType(elem))); + CSA_ASSERT(this, IsStringInstanceType(LoadInstanceType(elem))); Callable call_callable = CodeFactory::Call(isolate); Node* const replacement_obj = - a->CallJS(call_callable, context, replace_callable, undefined, elem, - var_match_start.value(), subject_string); + CallJS(call_callable, context, replace_callable, undefined, elem, + var_match_start.value(), string); - Node* const replacement_str = a->ToString(context, replacement_obj); - a->StoreFixedArrayElement(res_elems, i, replacement_str); + Node* const replacement_str = ToString(context, replacement_obj); + StoreFixedArrayElement(res_elems, i, replacement_str); - Node* const elem_length = a->LoadStringLength(elem); + Node* const elem_length = LoadStringLength(elem); Node* const new_match_start = - a->SmiAdd(var_match_start.value(), elem_length); + SmiAdd(var_match_start.value(), elem_length); var_match_start.Bind(new_match_start); - a->Goto(&loop_epilogue); + Goto(&loop_epilogue); } - a->Bind(&loop_epilogue); + Bind(&loop_epilogue); { - var_i.Bind(a->IntPtrAdd(var_i.value(), int_one)); - a->Goto(&loop); + var_i.Bind(IntPtrAdd(var_i.value(), int_one)); + Goto(&loop); } } } - a->Bind(&if_hasexplicitcaptures); + Bind(&if_hasexplicitcaptures); { - CodeStubAssembler::ParameterMode mode = - CodeStubAssembler::INTPTR_PARAMETERS; - Node* const from = int_zero; - Node* const to = a->SmiUntag(res_length); + Node* const to = SmiUntag(res_length); const int increment = 1; - a->BuildFastLoop( + BuildFastLoop( MachineType::PointerRepresentation(), from, to, - [res_elems, isolate, native_context, context, undefined, - replace_callable, mode](CodeStubAssembler* a, Node* index) { - Node* const elem = - a->LoadFixedArrayElement(res_elems, index, 0, mode); + [this, res_elems, isolate, native_context, context, undefined, + replace_callable](Node* index) { + Node* const elem = LoadFixedArrayElement(res_elems, index); - Label do_continue(a); - a->GotoIf(a->TaggedIsSmi(elem), &do_continue); + Label do_continue(this); + GotoIf(TaggedIsSmi(elem), &do_continue); // elem must be an Array. // Use the apply argument as backing for global RegExp properties. - CSA_ASSERT(a, a->HasInstanceType(elem, JS_ARRAY_TYPE)); + CSA_ASSERT(this, HasInstanceType(elem, JS_ARRAY_TYPE)); // TODO(jgruber): Remove indirection through Call->ReflectApply. Callable call_callable = CodeFactory::Call(isolate); - Node* const reflect_apply = a->LoadContextElement( - native_context, Context::REFLECT_APPLY_INDEX); + Node* const reflect_apply = + LoadContextElement(native_context, Context::REFLECT_APPLY_INDEX); Node* const replacement_obj = - a->CallJS(call_callable, context, reflect_apply, undefined, - replace_callable, undefined, elem); + CallJS(call_callable, context, reflect_apply, undefined, + replace_callable, undefined, elem); // Overwrite the i'th element in the results with the string we got // back from the callback function. - Node* const replacement_str = a->ToString(context, replacement_obj); - a->StoreFixedArrayElement(res_elems, index, replacement_str, - UPDATE_WRITE_BARRIER, mode); + Node* const replacement_str = ToString(context, replacement_obj); + StoreFixedArrayElement(res_elems, index, replacement_str); - a->Goto(&do_continue); - a->Bind(&do_continue); + Goto(&do_continue); + Bind(&do_continue); }, increment, CodeStubAssembler::IndexAdvanceMode::kPost); - a->Goto(&create_result); + Goto(&create_result); } - a->Bind(&create_result); + Bind(&create_result); { - Node* const result = a->CallRuntime(Runtime::kStringBuilderConcat, context, - res, res_length, subject_string); + Node* const result = CallRuntime(Runtime::kStringBuilderConcat, context, + res, res_length, string); var_result.Bind(result); - a->Goto(&out); + Goto(&out); } - a->Bind(&out); + Bind(&out); return var_result.value(); } -compiler::Node* ReplaceSimpleStringFastPath(CodeStubAssembler* a, - compiler::Node* context, - compiler::Node* regexp, - compiler::Node* subject_string, - compiler::Node* replace_string) { +Node* RegExpBuiltinsAssembler::ReplaceSimpleStringFastPath( + Node* context, Node* regexp, Node* string, Node* replace_string) { // The fast path is reached only if {receiver} is an unmodified // JSRegExp instance, {replace_value} is non-callable, and // ToString({replace_value}) does not contain '$', i.e. we're doing a simple // string replacement. - typedef CodeStubAssembler::Variable Variable; - typedef CodeStubAssembler::Label Label; - typedef compiler::Node Node; + Isolate* const isolate = this->isolate(); - Isolate* const isolate = a->isolate(); + Node* const null = NullConstant(); + Node* const int_zero = IntPtrConstant(0); + Node* const smi_zero = SmiConstant(Smi::kZero); - Node* const null = a->NullConstant(); - Node* const int_zero = a->IntPtrConstant(0); - Node* const smi_zero = a->SmiConstant(Smi::kZero); - - Label out(a); - Variable var_result(a, MachineRepresentation::kTagged); + Label out(this); + Variable var_result(this, MachineRepresentation::kTagged); // Load the last match info. - Node* const native_context = a->LoadNativeContext(context); - Node* const last_match_info = a->LoadContextElement( - native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX); + Node* const native_context = LoadNativeContext(context); + Node* const last_match_info = + LoadContextElement(native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX); // Is {regexp} global? - Label if_isglobal(a), if_isnonglobal(a); - Node* const flags = a->LoadObjectField(regexp, JSRegExp::kFlagsOffset); + Label if_isglobal(this), if_isnonglobal(this); + Node* const flags = LoadObjectField(regexp, JSRegExp::kFlagsOffset); Node* const is_global = - a->WordAnd(a->SmiUntag(flags), a->IntPtrConstant(JSRegExp::kGlobal)); - a->Branch(a->WordEqual(is_global, int_zero), &if_isnonglobal, &if_isglobal); + WordAnd(SmiUntag(flags), IntPtrConstant(JSRegExp::kGlobal)); + Branch(WordEqual(is_global, int_zero), &if_isnonglobal, &if_isglobal); - a->Bind(&if_isglobal); + Bind(&if_isglobal); { // Hand off global regexps to runtime. - FastStoreLastIndex(a, context, regexp, smi_zero); + FastStoreLastIndex(regexp, smi_zero); Node* const result = - a->CallRuntime(Runtime::kStringReplaceGlobalRegExpWithString, context, - subject_string, regexp, replace_string, last_match_info); + CallRuntime(Runtime::kStringReplaceGlobalRegExpWithString, context, + string, regexp, replace_string, last_match_info); var_result.Bind(result); - a->Goto(&out); + Goto(&out); } - a->Bind(&if_isnonglobal); + Bind(&if_isnonglobal); { // Run exec, then manually construct the resulting string. Callable exec_callable = CodeFactory::RegExpExec(isolate); - Node* const match_indices = - a->CallStub(exec_callable, context, regexp, subject_string, smi_zero, - last_match_info); + Node* const match_indices = CallStub(exec_callable, context, regexp, string, + smi_zero, last_match_info); - Label if_matched(a), if_didnotmatch(a); - a->Branch(a->WordEqual(match_indices, null), &if_didnotmatch, &if_matched); + Label if_matched(this), if_didnotmatch(this); + Branch(WordEqual(match_indices, null), &if_didnotmatch, &if_matched); - a->Bind(&if_didnotmatch); + Bind(&if_didnotmatch); { - FastStoreLastIndex(a, context, regexp, smi_zero); - var_result.Bind(subject_string); - a->Goto(&out); + FastStoreLastIndex(regexp, smi_zero); + var_result.Bind(string); + Goto(&out); } - a->Bind(&if_matched); + Bind(&if_matched); { - CodeStubAssembler::ParameterMode mode = - CodeStubAssembler::INTPTR_PARAMETERS; - Node* const subject_start = smi_zero; - Node* const match_start = a->LoadFixedArrayElement( - match_indices, a->IntPtrConstant(RegExpMatchInfo::kFirstCaptureIndex), - 0, mode); - Node* const match_end = a->LoadFixedArrayElement( - match_indices, - a->IntPtrConstant(RegExpMatchInfo::kFirstCaptureIndex + 1), 0, mode); - Node* const subject_end = a->LoadStringLength(subject_string); - - Label if_replaceisempty(a), if_replaceisnotempty(a); - Node* const replace_length = a->LoadStringLength(replace_string); - a->Branch(a->SmiEqual(replace_length, smi_zero), &if_replaceisempty, - &if_replaceisnotempty); - - a->Bind(&if_replaceisempty); + Node* const match_start = LoadFixedArrayElement( + match_indices, RegExpMatchInfo::kFirstCaptureIndex); + Node* const match_end = LoadFixedArrayElement( + match_indices, RegExpMatchInfo::kFirstCaptureIndex + 1); + Node* const subject_end = LoadStringLength(string); + + Label if_replaceisempty(this), if_replaceisnotempty(this); + Node* const replace_length = LoadStringLength(replace_string); + Branch(SmiEqual(replace_length, smi_zero), &if_replaceisempty, + &if_replaceisnotempty); + + Bind(&if_replaceisempty); { // TODO(jgruber): We could skip many of the checks that using SubString // here entails. Node* const first_part = - a->SubString(context, subject_string, subject_start, match_start); + SubString(context, string, subject_start, match_start); Node* const second_part = - a->SubString(context, subject_string, match_end, subject_end); + SubString(context, string, match_end, subject_end); - Node* const result = a->StringAdd(context, first_part, second_part); + Node* const result = StringAdd(context, first_part, second_part); var_result.Bind(result); - a->Goto(&out); + Goto(&out); } - a->Bind(&if_replaceisnotempty); + Bind(&if_replaceisnotempty); { Node* const first_part = - a->SubString(context, subject_string, subject_start, match_start); + SubString(context, string, subject_start, match_start); Node* const second_part = replace_string; Node* const third_part = - a->SubString(context, subject_string, match_end, subject_end); + SubString(context, string, match_end, subject_end); - Node* result = a->StringAdd(context, first_part, second_part); - result = a->StringAdd(context, result, third_part); + Node* result = StringAdd(context, first_part, second_part); + result = StringAdd(context, result, third_part); var_result.Bind(result); - a->Goto(&out); + Goto(&out); } } } - a->Bind(&out); + Bind(&out); return var_result.value(); } -} // namespace - // ES#sec-regexp.prototype-@@replace // RegExp.prototype [ @@replace ] ( string, replaceValue ) -void Builtins::Generate_RegExpPrototypeReplace(CodeStubAssembler* a) { - typedef CodeStubAssembler::Label Label; - typedef compiler::Node Node; - - Isolate* const isolate = a->isolate(); - - Node* const maybe_receiver = a->Parameter(0); - Node* const maybe_string = a->Parameter(1); - Node* const replace_value = a->Parameter(2); - Node* const context = a->Parameter(5); - - Node* const int_zero = a->IntPtrConstant(0); +TF_BUILTIN(RegExpPrototypeReplace, RegExpBuiltinsAssembler) { + Node* const maybe_receiver = Parameter(0); + Node* const maybe_string = Parameter(1); + Node* const replace_value = Parameter(2); + Node* const context = Parameter(5); // Ensure {maybe_receiver} is a JSReceiver. - Node* const map = - ThrowIfNotJSReceiver(a, isolate, context, maybe_receiver, - MessageTemplate::kIncompatibleMethodReceiver, - "RegExp.prototype.@@replace"); + Node* const map = ThrowIfNotJSReceiver( + context, maybe_receiver, MessageTemplate::kIncompatibleMethodReceiver, + "RegExp.prototype.@@replace"); Node* const receiver = maybe_receiver; // Convert {maybe_string} to a String. - Callable tostring_callable = CodeFactory::ToString(isolate); - Node* const string = a->CallStub(tostring_callable, context, maybe_string); + Callable tostring_callable = CodeFactory::ToString(isolate()); + Node* const string = CallStub(tostring_callable, context, maybe_string); // Fast-path checks: 1. Is the {receiver} an unmodified JSRegExp instance? - Label checkreplacecallable(a), runtime(a, Label::kDeferred), fastpath(a); - BranchIfFastPath(a, context, map, &checkreplacecallable, &runtime); + Label checkreplacecallable(this), runtime(this, Label::kDeferred), + fastpath(this); + BranchIfFastRegExp(context, map, &checkreplacecallable, &runtime); - a->Bind(&checkreplacecallable); + Bind(&checkreplacecallable); Node* const regexp = receiver; // 2. Is {replace_value} callable? - Label checkreplacestring(a), if_iscallable(a); - a->GotoIf(a->TaggedIsSmi(replace_value), &checkreplacestring); + Label checkreplacestring(this), if_iscallable(this); + GotoIf(TaggedIsSmi(replace_value), &checkreplacestring); - Node* const replace_value_map = a->LoadMap(replace_value); - a->Branch(a->IsCallableMap(replace_value_map), &if_iscallable, - &checkreplacestring); + Node* const replace_value_map = LoadMap(replace_value); + Branch(IsCallableMap(replace_value_map), &if_iscallable, &checkreplacestring); // 3. Does ToString({replace_value}) contain '$'? - a->Bind(&checkreplacestring); + Bind(&checkreplacestring); { Node* const replace_string = - a->CallStub(tostring_callable, context, replace_value); + CallStub(tostring_callable, context, replace_value); - Node* const dollar_char = a->IntPtrConstant('$'); - Node* const smi_minusone = a->SmiConstant(Smi::FromInt(-1)); - a->GotoUnless(a->SmiEqual(a->StringIndexOfChar(context, replace_string, - dollar_char, int_zero), - smi_minusone), - &runtime); + Node* const dollar_char = Int32Constant('$'); + Node* const smi_minusone = SmiConstant(Smi::FromInt(-1)); + GotoUnless(SmiEqual(StringIndexOfChar(context, replace_string, dollar_char, + SmiConstant(0)), + smi_minusone), + &runtime); - a->Return(ReplaceSimpleStringFastPath(a, context, regexp, string, - replace_string)); + Return( + ReplaceSimpleStringFastPath(context, regexp, string, replace_string)); } // {regexp} is unmodified and {replace_value} is callable. - a->Bind(&if_iscallable); + Bind(&if_iscallable); { Node* const replace_callable = replace_value; // Check if the {regexp} is global. - Label if_isglobal(a), if_isnotglobal(a); - Node* const is_global = FastFlagGetter(a, regexp, JSRegExp::kGlobal); - a->Branch(is_global, &if_isglobal, &if_isnotglobal); + Label if_isglobal(this), if_isnotglobal(this); + Node* const is_global = FastFlagGetter(regexp, JSRegExp::kGlobal); + Branch(is_global, &if_isglobal, &if_isnotglobal); - a->Bind(&if_isglobal); + Bind(&if_isglobal); { Node* const result = ReplaceGlobalCallableFastPath( - a, context, regexp, string, replace_callable); - a->Return(result); + context, regexp, string, replace_callable); + Return(result); } - a->Bind(&if_isnotglobal); + Bind(&if_isnotglobal); { Node* const result = - a->CallRuntime(Runtime::kStringReplaceNonGlobalRegExpWithFunction, - context, string, regexp, replace_callable); - a->Return(result); + CallRuntime(Runtime::kStringReplaceNonGlobalRegExpWithFunction, + context, string, regexp, replace_callable); + Return(result); } } - a->Bind(&runtime); + Bind(&runtime); { - Node* const result = a->CallRuntime(Runtime::kRegExpReplace, context, - receiver, string, replace_value); - a->Return(result); + Node* const result = CallRuntime(Runtime::kRegExpReplace, context, receiver, + string, replace_value); + Return(result); } } // Simple string matching functionality for internal use which does not modify // the last match info. -void Builtins::Generate_RegExpInternalMatch(CodeStubAssembler* a) { - typedef CodeStubAssembler::Label Label; - typedef compiler::Node Node; - - Isolate* const isolate = a->isolate(); - - Node* const regexp = a->Parameter(1); - Node* const string = a->Parameter(2); - Node* const context = a->Parameter(5); +TF_BUILTIN(RegExpInternalMatch, RegExpBuiltinsAssembler) { + Node* const regexp = Parameter(1); + Node* const string = Parameter(2); + Node* const context = Parameter(5); - Node* const null = a->NullConstant(); - Node* const smi_zero = a->SmiConstant(Smi::FromInt(0)); + Node* const null = NullConstant(); + Node* const smi_zero = SmiConstant(Smi::FromInt(0)); - Node* const native_context = a->LoadNativeContext(context); - Node* const internal_match_info = a->LoadContextElement( + Node* const native_context = LoadNativeContext(context); + Node* const internal_match_info = LoadContextElement( native_context, Context::REGEXP_INTERNAL_MATCH_INFO_INDEX); - Callable exec_callable = CodeFactory::RegExpExec(isolate); - Node* const match_indices = a->CallStub( - exec_callable, context, regexp, string, smi_zero, internal_match_info); + Callable exec_callable = CodeFactory::RegExpExec(isolate()); + Node* const match_indices = CallStub(exec_callable, context, regexp, string, + smi_zero, internal_match_info); - Label if_matched(a), if_didnotmatch(a); - a->Branch(a->WordEqual(match_indices, null), &if_didnotmatch, &if_matched); + Label if_matched(this), if_didnotmatch(this); + Branch(WordEqual(match_indices, null), &if_didnotmatch, &if_matched); - a->Bind(&if_didnotmatch); - a->Return(null); + Bind(&if_didnotmatch); + Return(null); - a->Bind(&if_matched); + Bind(&if_matched); { - Node* result = ConstructNewResultFromMatchInfo(isolate, a, context, - match_indices, string); - a->Return(result); + Node* result = + ConstructNewResultFromMatchInfo(context, match_indices, string); + Return(result); } } |