Home | History | Annotate | Download | only in builtins
      1 // Copyright 2016 the V8 project authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "src/builtins/builtins-utils.h"
      6 #include "src/builtins/builtins.h"
      7 
      8 #include "src/code-factory.h"
      9 #include "src/regexp/jsregexp.h"
     10 #include "src/regexp/regexp-utils.h"
     11 #include "src/string-builder.h"
     12 
     13 namespace v8 {
     14 namespace internal {
     15 
     16 // -----------------------------------------------------------------------------
     17 // ES6 section 21.2 RegExp Objects
     18 
     19 namespace {
     20 
     21 Handle<String> PatternFlags(Isolate* isolate, Handle<JSRegExp> regexp) {
     22   static const int kMaxFlagsLength = 5 + 1;  // 5 flags and '\0';
     23   char flags_string[kMaxFlagsLength];
     24   int i = 0;
     25 
     26   const JSRegExp::Flags flags = regexp->GetFlags();
     27 
     28   if ((flags & JSRegExp::kGlobal) != 0) flags_string[i++] = 'g';
     29   if ((flags & JSRegExp::kIgnoreCase) != 0) flags_string[i++] = 'i';
     30   if ((flags & JSRegExp::kMultiline) != 0) flags_string[i++] = 'm';
     31   if ((flags & JSRegExp::kUnicode) != 0) flags_string[i++] = 'u';
     32   if ((flags & JSRegExp::kSticky) != 0) flags_string[i++] = 'y';
     33 
     34   DCHECK_LT(i, kMaxFlagsLength);
     35   memset(&flags_string[i], '\0', kMaxFlagsLength - i);
     36 
     37   return isolate->factory()->NewStringFromAsciiChecked(flags_string);
     38 }
     39 
     40 // ES#sec-regexpinitialize
     41 // Runtime Semantics: RegExpInitialize ( obj, pattern, flags )
     42 MUST_USE_RESULT MaybeHandle<JSRegExp> RegExpInitialize(Isolate* isolate,
     43                                                        Handle<JSRegExp> regexp,
     44                                                        Handle<Object> pattern,
     45                                                        Handle<Object> flags) {
     46   Handle<String> pattern_string;
     47   if (pattern->IsUndefined(isolate)) {
     48     pattern_string = isolate->factory()->empty_string();
     49   } else {
     50     ASSIGN_RETURN_ON_EXCEPTION(isolate, pattern_string,
     51                                Object::ToString(isolate, pattern), JSRegExp);
     52   }
     53 
     54   Handle<String> flags_string;
     55   if (flags->IsUndefined(isolate)) {
     56     flags_string = isolate->factory()->empty_string();
     57   } else {
     58     ASSIGN_RETURN_ON_EXCEPTION(isolate, flags_string,
     59                                Object::ToString(isolate, flags), JSRegExp);
     60   }
     61 
     62   // TODO(jgruber): We could avoid the flags back and forth conversions.
     63   return JSRegExp::Initialize(regexp, pattern_string, flags_string);
     64 }
     65 
     66 }  // namespace
     67 
     68 // ES#sec-regexp-pattern-flags
     69 // RegExp ( pattern, flags )
     70 BUILTIN(RegExpConstructor) {
     71   HandleScope scope(isolate);
     72 
     73   Handle<HeapObject> new_target = args.new_target();
     74   Handle<Object> pattern = args.atOrUndefined(isolate, 1);
     75   Handle<Object> flags = args.atOrUndefined(isolate, 2);
     76 
     77   Handle<JSFunction> target = isolate->regexp_function();
     78 
     79   bool pattern_is_regexp;
     80   {
     81     Maybe<bool> maybe_pattern_is_regexp =
     82         RegExpUtils::IsRegExp(isolate, pattern);
     83     if (maybe_pattern_is_regexp.IsNothing()) {
     84       DCHECK(isolate->has_pending_exception());
     85       return isolate->heap()->exception();
     86     }
     87     pattern_is_regexp = maybe_pattern_is_regexp.FromJust();
     88   }
     89 
     90   if (new_target->IsUndefined(isolate)) {
     91     new_target = target;
     92 
     93     // ES6 section 21.2.3.1 step 3.b
     94     if (pattern_is_regexp && flags->IsUndefined(isolate)) {
     95       Handle<Object> pattern_constructor;
     96       ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
     97           isolate, pattern_constructor,
     98           Object::GetProperty(pattern,
     99                               isolate->factory()->constructor_string()));
    100 
    101       if (pattern_constructor.is_identical_to(new_target)) {
    102         return *pattern;
    103       }
    104     }
    105   }
    106 
    107   if (pattern->IsJSRegExp()) {
    108     Handle<JSRegExp> regexp_pattern = Handle<JSRegExp>::cast(pattern);
    109 
    110     if (flags->IsUndefined(isolate)) {
    111       flags = PatternFlags(isolate, regexp_pattern);
    112     }
    113     pattern = handle(regexp_pattern->source(), isolate);
    114   } else if (pattern_is_regexp) {
    115     Handle<Object> pattern_source;
    116     ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
    117         isolate, pattern_source,
    118         Object::GetProperty(pattern, isolate->factory()->source_string()));
    119 
    120     if (flags->IsUndefined(isolate)) {
    121       ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
    122           isolate, flags,
    123           Object::GetProperty(pattern, isolate->factory()->flags_string()));
    124     }
    125     pattern = pattern_source;
    126   }
    127 
    128   Handle<JSReceiver> new_target_receiver = Handle<JSReceiver>::cast(new_target);
    129 
    130   // TODO(jgruber): Fast-path for target == new_target == unmodified JSRegExp.
    131 
    132   Handle<JSObject> object;
    133   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
    134       isolate, object, JSObject::New(target, new_target_receiver));
    135   Handle<JSRegExp> regexp = Handle<JSRegExp>::cast(object);
    136 
    137   RETURN_RESULT_OR_FAILURE(isolate,
    138                            RegExpInitialize(isolate, regexp, pattern, flags));
    139 }
    140 
    141 BUILTIN(RegExpPrototypeCompile) {
    142   HandleScope scope(isolate);
    143   CHECK_RECEIVER(JSRegExp, regexp, "RegExp.prototype.compile");
    144 
    145   Handle<Object> pattern = args.atOrUndefined(isolate, 1);
    146   Handle<Object> flags = args.atOrUndefined(isolate, 2);
    147 
    148   if (pattern->IsJSRegExp()) {
    149     Handle<JSRegExp> pattern_regexp = Handle<JSRegExp>::cast(pattern);
    150 
    151     if (!flags->IsUndefined(isolate)) {
    152       THROW_NEW_ERROR_RETURN_FAILURE(
    153           isolate, NewTypeError(MessageTemplate::kRegExpFlags));
    154     }
    155 
    156     flags = PatternFlags(isolate, pattern_regexp);
    157     ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
    158         isolate, pattern,
    159         Object::GetProperty(pattern, isolate->factory()->source_string()));
    160   }
    161 
    162   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
    163       isolate, regexp, RegExpInitialize(isolate, regexp, pattern, flags));
    164 
    165   // Return undefined for compatibility with JSC.
    166   // See http://crbug.com/585775 for web compat details.
    167 
    168   return isolate->heap()->undefined_value();
    169 }
    170 
    171 namespace {
    172 
    173 compiler::Node* FastLoadLastIndex(CodeStubAssembler* a, compiler::Node* context,
    174                                   compiler::Node* regexp) {
    175   // Load the in-object field.
    176   static const int field_offset =
    177       JSRegExp::kSize + JSRegExp::kLastIndexFieldIndex * kPointerSize;
    178   return a->LoadObjectField(regexp, field_offset);
    179 }
    180 
    181 compiler::Node* SlowLoadLastIndex(CodeStubAssembler* a, compiler::Node* context,
    182                                   compiler::Node* regexp) {
    183   // Load through the GetProperty stub.
    184   typedef compiler::Node Node;
    185 
    186   Node* const name =
    187       a->HeapConstant(a->isolate()->factory()->lastIndex_string());
    188   Callable getproperty_callable = CodeFactory::GetProperty(a->isolate());
    189   return a->CallStub(getproperty_callable, context, regexp, name);
    190 }
    191 
    192 compiler::Node* LoadLastIndex(CodeStubAssembler* a, compiler::Node* context,
    193                               compiler::Node* has_initialmap,
    194                               compiler::Node* regexp) {
    195   typedef CodeStubAssembler::Variable Variable;
    196   typedef CodeStubAssembler::Label Label;
    197 
    198   Variable var_value(a, MachineRepresentation::kTagged);
    199 
    200   Label out(a), if_unmodified(a), if_modified(a);
    201   a->Branch(has_initialmap, &if_unmodified, &if_modified);
    202 
    203   a->Bind(&if_unmodified);
    204   {
    205     var_value.Bind(FastLoadLastIndex(a, context, regexp));
    206     a->Goto(&out);
    207   }
    208 
    209   a->Bind(&if_modified);
    210   {
    211     var_value.Bind(SlowLoadLastIndex(a, context, regexp));
    212     a->Goto(&out);
    213   }
    214 
    215   a->Bind(&out);
    216   return var_value.value();
    217 }
    218 
    219 // The fast-path of StoreLastIndex when regexp is guaranteed to be an unmodified
    220 // JSRegExp instance.
    221 void FastStoreLastIndex(CodeStubAssembler* a, compiler::Node* context,
    222                         compiler::Node* regexp, compiler::Node* value) {
    223   // Store the in-object field.
    224   static const int field_offset =
    225       JSRegExp::kSize + JSRegExp::kLastIndexFieldIndex * kPointerSize;
    226   a->StoreObjectField(regexp, field_offset, value);
    227 }
    228 
    229 void SlowStoreLastIndex(CodeStubAssembler* a, compiler::Node* context,
    230                         compiler::Node* regexp, compiler::Node* value) {
    231   // Store through runtime.
    232   // TODO(ishell): Use SetPropertyStub here once available.
    233   typedef compiler::Node Node;
    234 
    235   Node* const name =
    236       a->HeapConstant(a->isolate()->factory()->lastIndex_string());
    237   Node* const language_mode = a->SmiConstant(Smi::FromInt(STRICT));
    238   a->CallRuntime(Runtime::kSetProperty, context, regexp, name, value,
    239                  language_mode);
    240 }
    241 
    242 void StoreLastIndex(CodeStubAssembler* a, compiler::Node* context,
    243                     compiler::Node* has_initialmap, compiler::Node* regexp,
    244                     compiler::Node* value) {
    245   typedef CodeStubAssembler::Label Label;
    246 
    247   Label out(a), if_unmodified(a), if_modified(a);
    248   a->Branch(has_initialmap, &if_unmodified, &if_modified);
    249 
    250   a->Bind(&if_unmodified);
    251   {
    252     FastStoreLastIndex(a, context, regexp, value);
    253     a->Goto(&out);
    254   }
    255 
    256   a->Bind(&if_modified);
    257   {
    258     SlowStoreLastIndex(a, context, regexp, value);
    259     a->Goto(&out);
    260   }
    261 
    262   a->Bind(&out);
    263 }
    264 
    265 compiler::Node* ConstructNewResultFromMatchInfo(Isolate* isolate,
    266                                                 CodeStubAssembler* a,
    267                                                 compiler::Node* context,
    268                                                 compiler::Node* match_info,
    269                                                 compiler::Node* string) {
    270   typedef CodeStubAssembler::Variable Variable;
    271   typedef CodeStubAssembler::Label Label;
    272   typedef compiler::Node Node;
    273 
    274   Label out(a);
    275 
    276   CodeStubAssembler::ParameterMode mode = CodeStubAssembler::INTPTR_PARAMETERS;
    277   Node* const num_indices = a->SmiUntag(a->LoadFixedArrayElement(
    278       match_info, a->IntPtrConstant(RegExpMatchInfo::kNumberOfCapturesIndex), 0,
    279       mode));
    280   Node* const num_results = a->SmiTag(a->WordShr(num_indices, 1));
    281   Node* const start = a->LoadFixedArrayElement(
    282       match_info, a->IntPtrConstant(RegExpMatchInfo::kFirstCaptureIndex), 0,
    283       mode);
    284   Node* const end = a->LoadFixedArrayElement(
    285       match_info, a->IntPtrConstant(RegExpMatchInfo::kFirstCaptureIndex + 1), 0,
    286       mode);
    287 
    288   // Calculate the substring of the first match before creating the result array
    289   // to avoid an unnecessary write barrier storing the first result.
    290   Node* const first = a->SubString(context, string, start, end);
    291 
    292   Node* const result =
    293       a->AllocateRegExpResult(context, num_results, start, string);
    294   Node* const result_elements = a->LoadElements(result);
    295 
    296   a->StoreFixedArrayElement(result_elements, a->IntPtrConstant(0), first,
    297                             SKIP_WRITE_BARRIER);
    298 
    299   a->GotoIf(a->SmiEqual(num_results, a->SmiConstant(Smi::FromInt(1))), &out);
    300 
    301   // Store all remaining captures.
    302   Node* const limit = a->IntPtrAdd(
    303       a->IntPtrConstant(RegExpMatchInfo::kFirstCaptureIndex), num_indices);
    304 
    305   Variable var_from_cursor(a, MachineType::PointerRepresentation());
    306   Variable var_to_cursor(a, MachineType::PointerRepresentation());
    307 
    308   var_from_cursor.Bind(
    309       a->IntPtrConstant(RegExpMatchInfo::kFirstCaptureIndex + 2));
    310   var_to_cursor.Bind(a->IntPtrConstant(1));
    311 
    312   Variable* vars[] = {&var_from_cursor, &var_to_cursor};
    313   Label loop(a, 2, vars);
    314 
    315   a->Goto(&loop);
    316   a->Bind(&loop);
    317   {
    318     Node* const from_cursor = var_from_cursor.value();
    319     Node* const to_cursor = var_to_cursor.value();
    320     Node* const start = a->LoadFixedArrayElement(match_info, from_cursor);
    321 
    322     Label next_iter(a);
    323     a->GotoIf(a->SmiEqual(start, a->SmiConstant(Smi::FromInt(-1))), &next_iter);
    324 
    325     Node* const from_cursor_plus1 =
    326         a->IntPtrAdd(from_cursor, a->IntPtrConstant(1));
    327     Node* const end = a->LoadFixedArrayElement(match_info, from_cursor_plus1);
    328 
    329     Node* const capture = a->SubString(context, string, start, end);
    330     a->StoreFixedArrayElement(result_elements, to_cursor, capture);
    331     a->Goto(&next_iter);
    332 
    333     a->Bind(&next_iter);
    334     var_from_cursor.Bind(a->IntPtrAdd(from_cursor, a->IntPtrConstant(2)));
    335     var_to_cursor.Bind(a->IntPtrAdd(to_cursor, a->IntPtrConstant(1)));
    336     a->Branch(a->UintPtrLessThan(var_from_cursor.value(), limit), &loop, &out);
    337   }
    338 
    339   a->Bind(&out);
    340   return result;
    341 }
    342 
    343 // ES#sec-regexp.prototype.exec
    344 // RegExp.prototype.exec ( string )
    345 compiler::Node* RegExpPrototypeExecInternal(CodeStubAssembler* a,
    346                                             compiler::Node* context,
    347                                             compiler::Node* maybe_receiver,
    348                                             compiler::Node* maybe_string) {
    349   typedef CodeStubAssembler::Variable Variable;
    350   typedef CodeStubAssembler::Label Label;
    351   typedef compiler::Node Node;
    352 
    353   Isolate* const isolate = a->isolate();
    354 
    355   Node* const null = a->NullConstant();
    356   Node* const int_zero = a->IntPtrConstant(0);
    357   Node* const smi_zero = a->SmiConstant(Smi::kZero);
    358 
    359   Variable var_result(a, MachineRepresentation::kTagged);
    360   Label out(a);
    361 
    362   // Ensure {maybe_receiver} is a JSRegExp.
    363   Node* const regexp_map = a->ThrowIfNotInstanceType(
    364       context, maybe_receiver, JS_REGEXP_TYPE, "RegExp.prototype.exec");
    365   Node* const regexp = maybe_receiver;
    366 
    367   // Check whether the regexp instance is unmodified.
    368   Node* const native_context = a->LoadNativeContext(context);
    369   Node* const regexp_fun =
    370       a->LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX);
    371   Node* const initial_map =
    372       a->LoadObjectField(regexp_fun, JSFunction::kPrototypeOrInitialMapOffset);
    373   Node* const has_initialmap = a->WordEqual(regexp_map, initial_map);
    374 
    375   // Convert {maybe_string} to a string.
    376   Callable tostring_callable = CodeFactory::ToString(isolate);
    377   Node* const string = a->CallStub(tostring_callable, context, maybe_string);
    378   Node* const string_length = a->LoadStringLength(string);
    379 
    380   // Check whether the regexp is global or sticky, which determines whether we
    381   // update last index later on.
    382   Node* const flags = a->LoadObjectField(regexp, JSRegExp::kFlagsOffset);
    383   Node* const is_global_or_sticky =
    384       a->WordAnd(a->SmiUntag(flags),
    385                  a->IntPtrConstant(JSRegExp::kGlobal | JSRegExp::kSticky));
    386   Node* const should_update_last_index =
    387       a->WordNotEqual(is_global_or_sticky, int_zero);
    388 
    389   // Grab and possibly update last index.
    390   Label run_exec(a);
    391   Variable var_lastindex(a, MachineRepresentation::kTagged);
    392   {
    393     Label if_doupdate(a), if_dontupdate(a);
    394     a->Branch(should_update_last_index, &if_doupdate, &if_dontupdate);
    395 
    396     a->Bind(&if_doupdate);
    397     {
    398       Node* const regexp_lastindex =
    399           LoadLastIndex(a, context, has_initialmap, regexp);
    400 
    401       Callable tolength_callable = CodeFactory::ToLength(isolate);
    402       Node* const lastindex =
    403           a->CallStub(tolength_callable, context, regexp_lastindex);
    404       var_lastindex.Bind(lastindex);
    405 
    406       Label if_isoob(a, Label::kDeferred);
    407       a->GotoUnless(a->TaggedIsSmi(lastindex), &if_isoob);
    408       a->GotoUnless(a->SmiLessThanOrEqual(lastindex, string_length), &if_isoob);
    409       a->Goto(&run_exec);
    410 
    411       a->Bind(&if_isoob);
    412       {
    413         StoreLastIndex(a, context, has_initialmap, regexp, smi_zero);
    414         var_result.Bind(null);
    415         a->Goto(&out);
    416       }
    417     }
    418 
    419     a->Bind(&if_dontupdate);
    420     {
    421       var_lastindex.Bind(smi_zero);
    422       a->Goto(&run_exec);
    423     }
    424   }
    425 
    426   Node* match_indices;
    427   Label successful_match(a);
    428   a->Bind(&run_exec);
    429   {
    430     // Get last match info from the context.
    431     Node* const last_match_info = a->LoadContextElement(
    432         native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX);
    433 
    434     // Call the exec stub.
    435     Callable exec_callable = CodeFactory::RegExpExec(isolate);
    436     match_indices = a->CallStub(exec_callable, context, regexp, string,
    437                                 var_lastindex.value(), last_match_info);
    438 
    439     // {match_indices} is either null or the RegExpMatchInfo array.
    440     // Return early if exec failed, possibly updating last index.
    441     a->GotoUnless(a->WordEqual(match_indices, null), &successful_match);
    442 
    443     Label return_null(a);
    444     a->GotoUnless(should_update_last_index, &return_null);
    445 
    446     StoreLastIndex(a, context, has_initialmap, regexp, smi_zero);
    447     a->Goto(&return_null);
    448 
    449     a->Bind(&return_null);
    450     var_result.Bind(null);
    451     a->Goto(&out);
    452   }
    453 
    454   Label construct_result(a);
    455   a->Bind(&successful_match);
    456   {
    457     a->GotoUnless(should_update_last_index, &construct_result);
    458 
    459     // Update the new last index from {match_indices}.
    460     Node* const new_lastindex = a->LoadFixedArrayElement(
    461         match_indices,
    462         a->IntPtrConstant(RegExpMatchInfo::kFirstCaptureIndex + 1));
    463 
    464     StoreLastIndex(a, context, has_initialmap, regexp, new_lastindex);
    465     a->Goto(&construct_result);
    466 
    467     a->Bind(&construct_result);
    468     {
    469       Node* result = ConstructNewResultFromMatchInfo(isolate, a, context,
    470                                                      match_indices, string);
    471       var_result.Bind(result);
    472       a->Goto(&out);
    473     }
    474   }
    475 
    476   a->Bind(&out);
    477   return var_result.value();
    478 }
    479 
    480 }  // namespace
    481 
    482 // ES#sec-regexp.prototype.exec
    483 // RegExp.prototype.exec ( string )
    484 void Builtins::Generate_RegExpPrototypeExec(CodeStubAssembler* a) {
    485   typedef compiler::Node Node;
    486 
    487   Node* const maybe_receiver = a->Parameter(0);
    488   Node* const maybe_string = a->Parameter(1);
    489   Node* const context = a->Parameter(4);
    490 
    491   Node* const result =
    492       RegExpPrototypeExecInternal(a, context, maybe_receiver, maybe_string);
    493   a->Return(result);
    494 }
    495 
    496 namespace {
    497 
    498 compiler::Node* ThrowIfNotJSReceiver(CodeStubAssembler* a, Isolate* isolate,
    499                                      compiler::Node* context,
    500                                      compiler::Node* value,
    501                                      MessageTemplate::Template msg_template,
    502                                      char const* method_name) {
    503   typedef compiler::Node Node;
    504   typedef CodeStubAssembler::Label Label;
    505   typedef CodeStubAssembler::Variable Variable;
    506 
    507   Label out(a), throw_exception(a, Label::kDeferred);
    508   Variable var_value_map(a, MachineRepresentation::kTagged);
    509 
    510   a->GotoIf(a->TaggedIsSmi(value), &throw_exception);
    511 
    512   // Load the instance type of the {value}.
    513   var_value_map.Bind(a->LoadMap(value));
    514   Node* const value_instance_type =
    515       a->LoadMapInstanceType(var_value_map.value());
    516 
    517   a->Branch(a->IsJSReceiverInstanceType(value_instance_type), &out,
    518             &throw_exception);
    519 
    520   // The {value} is not a compatible receiver for this method.
    521   a->Bind(&throw_exception);
    522   {
    523     Node* const message_id = a->SmiConstant(Smi::FromInt(msg_template));
    524     Node* const method_name_str = a->HeapConstant(
    525         isolate->factory()->NewStringFromAsciiChecked(method_name, TENURED));
    526 
    527     Callable callable = CodeFactory::ToString(isolate);
    528     Node* const value_str = a->CallStub(callable, context, value);
    529 
    530     a->CallRuntime(Runtime::kThrowTypeError, context, message_id,
    531                    method_name_str, value_str);
    532     var_value_map.Bind(a->UndefinedConstant());
    533     a->Goto(&out);  // Never reached.
    534   }
    535 
    536   a->Bind(&out);
    537   return var_value_map.value();
    538 }
    539 
    540 compiler::Node* IsInitialRegExpMap(CodeStubAssembler* a,
    541                                    compiler::Node* context,
    542                                    compiler::Node* map) {
    543   typedef compiler::Node Node;
    544 
    545   Node* const native_context = a->LoadNativeContext(context);
    546   Node* const regexp_fun =
    547       a->LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX);
    548   Node* const initial_map =
    549       a->LoadObjectField(regexp_fun, JSFunction::kPrototypeOrInitialMapOffset);
    550   Node* const has_initialmap = a->WordEqual(map, initial_map);
    551 
    552   return has_initialmap;
    553 }
    554 
    555 // RegExp fast path implementations rely on unmodified JSRegExp instances.
    556 // We use a fairly coarse granularity for this and simply check whether both
    557 // the regexp itself is unmodified (i.e. its map has not changed) and its
    558 // prototype is unmodified.
    559 void BranchIfFastPath(CodeStubAssembler* a, compiler::Node* context,
    560                       compiler::Node* map,
    561                       CodeStubAssembler::Label* if_isunmodified,
    562                       CodeStubAssembler::Label* if_ismodified) {
    563   typedef compiler::Node Node;
    564 
    565   Node* const native_context = a->LoadNativeContext(context);
    566   Node* const regexp_fun =
    567       a->LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX);
    568   Node* const initial_map =
    569       a->LoadObjectField(regexp_fun, JSFunction::kPrototypeOrInitialMapOffset);
    570   Node* const has_initialmap = a->WordEqual(map, initial_map);
    571 
    572   a->GotoUnless(has_initialmap, if_ismodified);
    573 
    574   Node* const initial_proto_initial_map = a->LoadContextElement(
    575       native_context, Context::REGEXP_PROTOTYPE_MAP_INDEX);
    576   Node* const proto_map = a->LoadMap(a->LoadMapPrototype(map));
    577   Node* const proto_has_initialmap =
    578       a->WordEqual(proto_map, initial_proto_initial_map);
    579 
    580   // TODO(ishell): Update this check once map changes for constant field
    581   // tracking are landing.
    582 
    583   a->Branch(proto_has_initialmap, if_isunmodified, if_ismodified);
    584 }
    585 
    586 }  // namespace
    587 
    588 void Builtins::Generate_RegExpPrototypeFlagsGetter(CodeStubAssembler* a) {
    589   typedef CodeStubAssembler::Variable Variable;
    590   typedef CodeStubAssembler::Label Label;
    591   typedef compiler::Node Node;
    592 
    593   Node* const receiver = a->Parameter(0);
    594   Node* const context = a->Parameter(3);
    595 
    596   Isolate* isolate = a->isolate();
    597   Node* const int_zero = a->IntPtrConstant(0);
    598   Node* const int_one = a->IntPtrConstant(1);
    599 
    600   Node* const map = ThrowIfNotJSReceiver(a, isolate, context, receiver,
    601                                          MessageTemplate::kRegExpNonObject,
    602                                          "RegExp.prototype.flags");
    603 
    604   Variable var_length(a, MachineType::PointerRepresentation());
    605   Variable var_flags(a, MachineType::PointerRepresentation());
    606 
    607   // First, count the number of characters we will need and check which flags
    608   // are set.
    609 
    610   var_length.Bind(int_zero);
    611 
    612   Label if_isunmodifiedjsregexp(a),
    613       if_isnotunmodifiedjsregexp(a, Label::kDeferred);
    614   a->Branch(IsInitialRegExpMap(a, context, map), &if_isunmodifiedjsregexp,
    615             &if_isnotunmodifiedjsregexp);
    616 
    617   Label construct_string(a);
    618   a->Bind(&if_isunmodifiedjsregexp);
    619   {
    620     // Refer to JSRegExp's flag property on the fast-path.
    621     Node* const flags_smi =
    622         a->LoadObjectField(receiver, JSRegExp::kFlagsOffset);
    623     Node* const flags_intptr = a->SmiUntag(flags_smi);
    624     var_flags.Bind(flags_intptr);
    625 
    626     Label label_global(a), label_ignorecase(a), label_multiline(a),
    627         label_unicode(a), label_sticky(a);
    628 
    629 #define CASE_FOR_FLAG(FLAG, LABEL, NEXT_LABEL)                        \
    630   do {                                                                \
    631     a->Bind(&LABEL);                                                  \
    632     Node* const mask = a->IntPtrConstant(FLAG);                       \
    633     a->GotoIf(a->WordEqual(a->WordAnd(flags_intptr, mask), int_zero), \
    634               &NEXT_LABEL);                                           \
    635     var_length.Bind(a->IntPtrAdd(var_length.value(), int_one));       \
    636     a->Goto(&NEXT_LABEL);                                             \
    637   } while (false)
    638 
    639     a->Goto(&label_global);
    640     CASE_FOR_FLAG(JSRegExp::kGlobal, label_global, label_ignorecase);
    641     CASE_FOR_FLAG(JSRegExp::kIgnoreCase, label_ignorecase, label_multiline);
    642     CASE_FOR_FLAG(JSRegExp::kMultiline, label_multiline, label_unicode);
    643     CASE_FOR_FLAG(JSRegExp::kUnicode, label_unicode, label_sticky);
    644     CASE_FOR_FLAG(JSRegExp::kSticky, label_sticky, construct_string);
    645 #undef CASE_FOR_FLAG
    646   }
    647 
    648   a->Bind(&if_isnotunmodifiedjsregexp);
    649   {
    650     // Fall back to GetProperty stub on the slow-path.
    651     var_flags.Bind(int_zero);
    652 
    653     Callable getproperty_callable = CodeFactory::GetProperty(a->isolate());
    654     Label label_global(a), label_ignorecase(a), label_multiline(a),
    655         label_unicode(a), label_sticky(a);
    656 
    657 #define CASE_FOR_FLAG(NAME, FLAG, LABEL, NEXT_LABEL)                          \
    658   do {                                                                        \
    659     a->Bind(&LABEL);                                                          \
    660     Node* const name =                                                        \
    661         a->HeapConstant(isolate->factory()->NewStringFromAsciiChecked(NAME)); \
    662     Node* const flag =                                                        \
    663         a->CallStub(getproperty_callable, context, receiver, name);           \
    664     Label if_isflagset(a);                                                    \
    665     a->BranchIfToBooleanIsTrue(flag, &if_isflagset, &NEXT_LABEL);             \
    666     a->Bind(&if_isflagset);                                                   \
    667     var_length.Bind(a->IntPtrAdd(var_length.value(), int_one));               \
    668     var_flags.Bind(a->WordOr(var_flags.value(), a->IntPtrConstant(FLAG)));    \
    669     a->Goto(&NEXT_LABEL);                                                     \
    670   } while (false)
    671 
    672     a->Goto(&label_global);
    673     CASE_FOR_FLAG("global", JSRegExp::kGlobal, label_global, label_ignorecase);
    674     CASE_FOR_FLAG("ignoreCase", JSRegExp::kIgnoreCase, label_ignorecase,
    675                   label_multiline);
    676     CASE_FOR_FLAG("multiline", JSRegExp::kMultiline, label_multiline,
    677                   label_unicode);
    678     CASE_FOR_FLAG("unicode", JSRegExp::kUnicode, label_unicode, label_sticky);
    679     CASE_FOR_FLAG("sticky", JSRegExp::kSticky, label_sticky, construct_string);
    680 #undef CASE_FOR_FLAG
    681   }
    682 
    683   // Allocate a string of the required length and fill it with the corresponding
    684   // char for each set flag.
    685 
    686   a->Bind(&construct_string);
    687   {
    688     Node* const result =
    689         a->AllocateSeqOneByteString(context, var_length.value());
    690     Node* const flags_intptr = var_flags.value();
    691 
    692     Variable var_offset(a, MachineType::PointerRepresentation());
    693     var_offset.Bind(
    694         a->IntPtrConstant(SeqOneByteString::kHeaderSize - kHeapObjectTag));
    695 
    696     Label label_global(a), label_ignorecase(a), label_multiline(a),
    697         label_unicode(a), label_sticky(a), out(a);
    698 
    699 #define CASE_FOR_FLAG(FLAG, CHAR, LABEL, NEXT_LABEL)                  \
    700   do {                                                                \
    701     a->Bind(&LABEL);                                                  \
    702     Node* const mask = a->IntPtrConstant(FLAG);                       \
    703     a->GotoIf(a->WordEqual(a->WordAnd(flags_intptr, mask), int_zero), \
    704               &NEXT_LABEL);                                           \
    705     Node* const value = a->IntPtrConstant(CHAR);                      \
    706     a->StoreNoWriteBarrier(MachineRepresentation::kWord8, result,     \
    707                            var_offset.value(), value);                \
    708     var_offset.Bind(a->IntPtrAdd(var_offset.value(), int_one));       \
    709     a->Goto(&NEXT_LABEL);                                             \
    710   } while (false)
    711 
    712     a->Goto(&label_global);
    713     CASE_FOR_FLAG(JSRegExp::kGlobal, 'g', label_global, label_ignorecase);
    714     CASE_FOR_FLAG(JSRegExp::kIgnoreCase, 'i', label_ignorecase,
    715                   label_multiline);
    716     CASE_FOR_FLAG(JSRegExp::kMultiline, 'm', label_multiline, label_unicode);
    717     CASE_FOR_FLAG(JSRegExp::kUnicode, 'u', label_unicode, label_sticky);
    718     CASE_FOR_FLAG(JSRegExp::kSticky, 'y', label_sticky, out);
    719 #undef CASE_FOR_FLAG
    720 
    721     a->Bind(&out);
    722     a->Return(result);
    723   }
    724 }
    725 
    726 // ES6 21.2.5.10.
    727 BUILTIN(RegExpPrototypeSourceGetter) {
    728   HandleScope scope(isolate);
    729 
    730   Handle<Object> recv = args.receiver();
    731   if (!recv->IsJSRegExp()) {
    732     Handle<JSFunction> regexp_fun = isolate->regexp_function();
    733     if (*recv == regexp_fun->prototype()) {
    734       isolate->CountUsage(v8::Isolate::kRegExpPrototypeSourceGetter);
    735       return *isolate->factory()->NewStringFromAsciiChecked("(?:)");
    736     }
    737     THROW_NEW_ERROR_RETURN_FAILURE(
    738         isolate, NewTypeError(MessageTemplate::kRegExpNonRegExp,
    739                               isolate->factory()->NewStringFromAsciiChecked(
    740                                   "RegExp.prototype.source")));
    741   }
    742 
    743   Handle<JSRegExp> regexp = Handle<JSRegExp>::cast(recv);
    744   return regexp->source();
    745 }
    746 
    747 BUILTIN(RegExpPrototypeToString) {
    748   HandleScope scope(isolate);
    749   CHECK_RECEIVER(JSReceiver, recv, "RegExp.prototype.toString");
    750 
    751   if (*recv == isolate->regexp_function()->prototype()) {
    752     isolate->CountUsage(v8::Isolate::kRegExpPrototypeToString);
    753   }
    754 
    755   IncrementalStringBuilder builder(isolate);
    756 
    757   builder.AppendCharacter('/');
    758   {
    759     Handle<Object> source;
    760     ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
    761         isolate, source,
    762         JSReceiver::GetProperty(recv, isolate->factory()->source_string()));
    763     Handle<String> source_str;
    764     ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, source_str,
    765                                        Object::ToString(isolate, source));
    766     builder.AppendString(source_str);
    767   }
    768 
    769   builder.AppendCharacter('/');
    770   {
    771     Handle<Object> flags;
    772     ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
    773         isolate, flags,
    774         JSReceiver::GetProperty(recv, isolate->factory()->flags_string()));
    775     Handle<String> flags_str;
    776     ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, flags_str,
    777                                        Object::ToString(isolate, flags));
    778     builder.AppendString(flags_str);
    779   }
    780 
    781   RETURN_RESULT_OR_FAILURE(isolate, builder.Finish());
    782 }
    783 
    784 // ES6 21.2.4.2.
    785 BUILTIN(RegExpPrototypeSpeciesGetter) {
    786   HandleScope scope(isolate);
    787   return *args.receiver();
    788 }
    789 
    790 namespace {
    791 
    792 // Fast-path implementation for flag checks on an unmodified JSRegExp instance.
    793 compiler::Node* FastFlagGetter(CodeStubAssembler* a,
    794                                compiler::Node* const regexp,
    795                                JSRegExp::Flag flag) {
    796   typedef compiler::Node Node;
    797 
    798   Node* const smi_zero = a->SmiConstant(Smi::kZero);
    799   Node* const flags = a->LoadObjectField(regexp, JSRegExp::kFlagsOffset);
    800   Node* const mask = a->SmiConstant(Smi::FromInt(flag));
    801   Node* const is_flag_set = a->WordNotEqual(a->WordAnd(flags, mask), smi_zero);
    802 
    803   return is_flag_set;
    804 }
    805 
    806 void Generate_FlagGetter(CodeStubAssembler* a, JSRegExp::Flag flag,
    807                          v8::Isolate::UseCounterFeature counter,
    808                          const char* method_name) {
    809   typedef CodeStubAssembler::Label Label;
    810   typedef compiler::Node Node;
    811 
    812   Node* const receiver = a->Parameter(0);
    813   Node* const context = a->Parameter(3);
    814 
    815   Isolate* isolate = a->isolate();
    816 
    817   // Check whether we have an unmodified regexp instance.
    818   Label if_isunmodifiedjsregexp(a),
    819       if_isnotunmodifiedjsregexp(a, Label::kDeferred);
    820 
    821   a->GotoIf(a->TaggedIsSmi(receiver), &if_isnotunmodifiedjsregexp);
    822 
    823   Node* const receiver_map = a->LoadMap(receiver);
    824   Node* const instance_type = a->LoadMapInstanceType(receiver_map);
    825 
    826   a->Branch(a->Word32Equal(instance_type, a->Int32Constant(JS_REGEXP_TYPE)),
    827             &if_isunmodifiedjsregexp, &if_isnotunmodifiedjsregexp);
    828 
    829   a->Bind(&if_isunmodifiedjsregexp);
    830   {
    831     // Refer to JSRegExp's flag property on the fast-path.
    832     Node* const is_flag_set = FastFlagGetter(a, receiver, flag);
    833     a->Return(a->Select(is_flag_set, a->TrueConstant(), a->FalseConstant()));
    834   }
    835 
    836   a->Bind(&if_isnotunmodifiedjsregexp);
    837   {
    838     Node* const native_context = a->LoadNativeContext(context);
    839     Node* const regexp_fun =
    840         a->LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX);
    841     Node* const initial_map = a->LoadObjectField(
    842         regexp_fun, JSFunction::kPrototypeOrInitialMapOffset);
    843     Node* const initial_prototype = a->LoadMapPrototype(initial_map);
    844 
    845     Label if_isprototype(a), if_isnotprototype(a);
    846     a->Branch(a->WordEqual(receiver, initial_prototype), &if_isprototype,
    847               &if_isnotprototype);
    848 
    849     a->Bind(&if_isprototype);
    850     {
    851       Node* const counter_smi = a->SmiConstant(Smi::FromInt(counter));
    852       a->CallRuntime(Runtime::kIncrementUseCounter, context, counter_smi);
    853       a->Return(a->UndefinedConstant());
    854     }
    855 
    856     a->Bind(&if_isnotprototype);
    857     {
    858       Node* const message_id =
    859           a->SmiConstant(Smi::FromInt(MessageTemplate::kRegExpNonRegExp));
    860       Node* const method_name_str = a->HeapConstant(
    861           isolate->factory()->NewStringFromAsciiChecked(method_name));
    862       a->CallRuntime(Runtime::kThrowTypeError, context, message_id,
    863                      method_name_str);
    864       a->Return(a->UndefinedConstant());  // Never reached.
    865     }
    866   }
    867 }
    868 
    869 }  // namespace
    870 
    871 // ES6 21.2.5.4.
    872 void Builtins::Generate_RegExpPrototypeGlobalGetter(CodeStubAssembler* a) {
    873   Generate_FlagGetter(a, JSRegExp::kGlobal,
    874                       v8::Isolate::kRegExpPrototypeOldFlagGetter,
    875                       "RegExp.prototype.global");
    876 }
    877 
    878 // ES6 21.2.5.5.
    879 void Builtins::Generate_RegExpPrototypeIgnoreCaseGetter(CodeStubAssembler* a) {
    880   Generate_FlagGetter(a, JSRegExp::kIgnoreCase,
    881                       v8::Isolate::kRegExpPrototypeOldFlagGetter,
    882                       "RegExp.prototype.ignoreCase");
    883 }
    884 
    885 // ES6 21.2.5.7.
    886 void Builtins::Generate_RegExpPrototypeMultilineGetter(CodeStubAssembler* a) {
    887   Generate_FlagGetter(a, JSRegExp::kMultiline,
    888                       v8::Isolate::kRegExpPrototypeOldFlagGetter,
    889                       "RegExp.prototype.multiline");
    890 }
    891 
    892 // ES6 21.2.5.12.
    893 void Builtins::Generate_RegExpPrototypeStickyGetter(CodeStubAssembler* a) {
    894   Generate_FlagGetter(a, JSRegExp::kSticky,
    895                       v8::Isolate::kRegExpPrototypeStickyGetter,
    896                       "RegExp.prototype.sticky");
    897 }
    898 
    899 // ES6 21.2.5.15.
    900 void Builtins::Generate_RegExpPrototypeUnicodeGetter(CodeStubAssembler* a) {
    901   Generate_FlagGetter(a, JSRegExp::kUnicode,
    902                       v8::Isolate::kRegExpPrototypeUnicodeGetter,
    903                       "RegExp.prototype.unicode");
    904 }
    905 
    906 // The properties $1..$9 are the first nine capturing substrings of the last
    907 // successful match, or ''.  The function RegExpMakeCaptureGetter will be
    908 // called with indices from 1 to 9.
    909 #define DEFINE_CAPTURE_GETTER(i)                        \
    910   BUILTIN(RegExpCapture##i##Getter) {                   \
    911     HandleScope scope(isolate);                         \
    912     return *RegExpUtils::GenericCaptureGetter(          \
    913         isolate, isolate->regexp_last_match_info(), i); \
    914   }
    915 DEFINE_CAPTURE_GETTER(1)
    916 DEFINE_CAPTURE_GETTER(2)
    917 DEFINE_CAPTURE_GETTER(3)
    918 DEFINE_CAPTURE_GETTER(4)
    919 DEFINE_CAPTURE_GETTER(5)
    920 DEFINE_CAPTURE_GETTER(6)
    921 DEFINE_CAPTURE_GETTER(7)
    922 DEFINE_CAPTURE_GETTER(8)
    923 DEFINE_CAPTURE_GETTER(9)
    924 #undef DEFINE_CAPTURE_GETTER
    925 
    926 // The properties `input` and `$_` are aliases for each other.  When this
    927 // value is set, the value it is set to is coerced to a string.
    928 // Getter and setter for the input.
    929 
    930 BUILTIN(RegExpInputGetter) {
    931   HandleScope scope(isolate);
    932   Handle<Object> obj(isolate->regexp_last_match_info()->LastInput(), isolate);
    933   return obj->IsUndefined(isolate) ? isolate->heap()->empty_string()
    934                                    : String::cast(*obj);
    935 }
    936 
    937 BUILTIN(RegExpInputSetter) {
    938   HandleScope scope(isolate);
    939   Handle<Object> value = args.atOrUndefined(isolate, 1);
    940   Handle<String> str;
    941   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, str,
    942                                      Object::ToString(isolate, value));
    943   isolate->regexp_last_match_info()->SetLastInput(*str);
    944   return isolate->heap()->undefined_value();
    945 }
    946 
    947 // Getters for the static properties lastMatch, lastParen, leftContext, and
    948 // rightContext of the RegExp constructor.  The properties are computed based
    949 // on the captures array of the last successful match and the subject string
    950 // of the last successful match.
    951 BUILTIN(RegExpLastMatchGetter) {
    952   HandleScope scope(isolate);
    953   return *RegExpUtils::GenericCaptureGetter(
    954       isolate, isolate->regexp_last_match_info(), 0);
    955 }
    956 
    957 BUILTIN(RegExpLastParenGetter) {
    958   HandleScope scope(isolate);
    959   Handle<RegExpMatchInfo> match_info = isolate->regexp_last_match_info();
    960   const int length = match_info->NumberOfCaptureRegisters();
    961   if (length <= 2) return isolate->heap()->empty_string();  // No captures.
    962 
    963   DCHECK_EQ(0, length % 2);
    964   const int last_capture = (length / 2) - 1;
    965 
    966   // We match the SpiderMonkey behavior: return the substring defined by the
    967   // last pair (after the first pair) of elements of the capture array even if
    968   // it is empty.
    969   return *RegExpUtils::GenericCaptureGetter(isolate, match_info, last_capture);
    970 }
    971 
    972 BUILTIN(RegExpLeftContextGetter) {
    973   HandleScope scope(isolate);
    974   Handle<RegExpMatchInfo> match_info = isolate->regexp_last_match_info();
    975   const int start_index = match_info->Capture(0);
    976   Handle<String> last_subject(match_info->LastSubject());
    977   return *isolate->factory()->NewSubString(last_subject, 0, start_index);
    978 }
    979 
    980 BUILTIN(RegExpRightContextGetter) {
    981   HandleScope scope(isolate);
    982   Handle<RegExpMatchInfo> match_info = isolate->regexp_last_match_info();
    983   const int start_index = match_info->Capture(1);
    984   Handle<String> last_subject(match_info->LastSubject());
    985   const int len = last_subject->length();
    986   return *isolate->factory()->NewSubString(last_subject, start_index, len);
    987 }
    988 
    989 namespace {
    990 
    991 // ES#sec-regexpexec Runtime Semantics: RegExpExec ( R, S )
    992 compiler::Node* RegExpExec(CodeStubAssembler* a, compiler::Node* context,
    993                            compiler::Node* recv, compiler::Node* string) {
    994   typedef CodeStubAssembler::Variable Variable;
    995   typedef CodeStubAssembler::Label Label;
    996   typedef compiler::Node Node;
    997 
    998   Isolate* isolate = a->isolate();
    999 
   1000   Node* const null = a->NullConstant();
   1001 
   1002   Variable var_result(a, MachineRepresentation::kTagged);
   1003   Label out(a), call_builtin_exec(a), slow_path(a, Label::kDeferred);
   1004 
   1005   Node* const map = a->LoadMap(recv);
   1006   BranchIfFastPath(a, context, map, &call_builtin_exec, &slow_path);
   1007 
   1008   a->Bind(&call_builtin_exec);
   1009   {
   1010     Node* const result = RegExpPrototypeExecInternal(a, context, recv, string);
   1011     var_result.Bind(result);
   1012     a->Goto(&out);
   1013   }
   1014 
   1015   a->Bind(&slow_path);
   1016   {
   1017     // Take the slow path of fetching the exec property, calling it, and
   1018     // verifying its return value.
   1019 
   1020     // Get the exec property.
   1021     Node* const name = a->HeapConstant(isolate->factory()->exec_string());
   1022     Callable getproperty_callable = CodeFactory::GetProperty(a->isolate());
   1023     Node* const exec = a->CallStub(getproperty_callable, context, recv, name);
   1024 
   1025     // Is {exec} callable?
   1026     Label if_iscallable(a), if_isnotcallable(a);
   1027 
   1028     a->GotoIf(a->TaggedIsSmi(exec), &if_isnotcallable);
   1029 
   1030     Node* const exec_map = a->LoadMap(exec);
   1031     a->Branch(a->IsCallableMap(exec_map), &if_iscallable, &if_isnotcallable);
   1032 
   1033     a->Bind(&if_iscallable);
   1034     {
   1035       Callable call_callable = CodeFactory::Call(isolate);
   1036       Node* const result =
   1037           a->CallJS(call_callable, context, exec, recv, string);
   1038 
   1039       var_result.Bind(result);
   1040       a->GotoIf(a->WordEqual(result, null), &out);
   1041 
   1042       ThrowIfNotJSReceiver(a, isolate, context, result,
   1043                            MessageTemplate::kInvalidRegExpExecResult, "unused");
   1044 
   1045       a->Goto(&out);
   1046     }
   1047 
   1048     a->Bind(&if_isnotcallable);
   1049     {
   1050       a->ThrowIfNotInstanceType(context, recv, JS_REGEXP_TYPE,
   1051                                 "RegExp.prototype.exec");
   1052       a->Goto(&call_builtin_exec);
   1053     }
   1054   }
   1055 
   1056   a->Bind(&out);
   1057   return var_result.value();
   1058 }
   1059 
   1060 }  // namespace
   1061 
   1062 // ES#sec-regexp.prototype.test
   1063 // RegExp.prototype.test ( S )
   1064 void Builtins::Generate_RegExpPrototypeTest(CodeStubAssembler* a) {
   1065   typedef compiler::Node Node;
   1066 
   1067   Isolate* const isolate = a->isolate();
   1068 
   1069   Node* const maybe_receiver = a->Parameter(0);
   1070   Node* const maybe_string = a->Parameter(1);
   1071   Node* const context = a->Parameter(4);
   1072 
   1073   // Ensure {maybe_receiver} is a JSReceiver.
   1074   ThrowIfNotJSReceiver(a, isolate, context, maybe_receiver,
   1075                        MessageTemplate::kIncompatibleMethodReceiver,
   1076                        "RegExp.prototype.test");
   1077   Node* const receiver = maybe_receiver;
   1078 
   1079   // Convert {maybe_string} to a String.
   1080   Node* const string = a->ToString(context, maybe_string);
   1081 
   1082   // Call exec.
   1083   Node* const match_indices = RegExpExec(a, context, receiver, string);
   1084 
   1085   // Return true iff exec matched successfully.
   1086   Node* const result = a->Select(a->WordEqual(match_indices, a->NullConstant()),
   1087                                  a->FalseConstant(), a->TrueConstant());
   1088   a->Return(result);
   1089 }
   1090 
   1091 // ES#sec-regexp.prototype-@@match
   1092 // RegExp.prototype [ @@match ] ( string )
   1093 BUILTIN(RegExpPrototypeMatch) {
   1094   HandleScope scope(isolate);
   1095   CHECK_RECEIVER(JSReceiver, recv, "RegExp.prototype.@@match");
   1096 
   1097   Handle<Object> string_obj = args.atOrUndefined(isolate, 1);
   1098 
   1099   Handle<String> string;
   1100   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, string,
   1101                                      Object::ToString(isolate, string_obj));
   1102 
   1103   Handle<Object> global_obj;
   1104   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
   1105       isolate, global_obj,
   1106       JSReceiver::GetProperty(recv, isolate->factory()->global_string()));
   1107   const bool global = global_obj->BooleanValue();
   1108 
   1109   if (!global) {
   1110     RETURN_RESULT_OR_FAILURE(
   1111         isolate,
   1112         RegExpUtils::RegExpExec(isolate, recv, string,
   1113                                 isolate->factory()->undefined_value()));
   1114   }
   1115 
   1116   Handle<Object> unicode_obj;
   1117   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
   1118       isolate, unicode_obj,
   1119       JSReceiver::GetProperty(recv, isolate->factory()->unicode_string()));
   1120   const bool unicode = unicode_obj->BooleanValue();
   1121 
   1122   RETURN_FAILURE_ON_EXCEPTION(isolate,
   1123                               RegExpUtils::SetLastIndex(isolate, recv, 0));
   1124 
   1125   static const int kInitialArraySize = 8;
   1126   Handle<FixedArray> elems =
   1127       isolate->factory()->NewFixedArrayWithHoles(kInitialArraySize);
   1128 
   1129   int n = 0;
   1130   for (;; n++) {
   1131     Handle<Object> result;
   1132     ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
   1133         isolate, result,
   1134         RegExpUtils::RegExpExec(isolate, recv, string,
   1135                                 isolate->factory()->undefined_value()));
   1136 
   1137     if (result->IsNull(isolate)) {
   1138       if (n == 0) return isolate->heap()->null_value();
   1139       break;
   1140     }
   1141 
   1142     Handle<Object> match_obj;
   1143     ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, match_obj,
   1144                                        Object::GetElement(isolate, result, 0));
   1145 
   1146     Handle<String> match;
   1147     ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, match,
   1148                                        Object::ToString(isolate, match_obj));
   1149 
   1150     elems = FixedArray::SetAndGrow(elems, n, match);
   1151 
   1152     if (match->length() == 0) {
   1153       RETURN_FAILURE_ON_EXCEPTION(isolate, RegExpUtils::SetAdvancedStringIndex(
   1154                                                isolate, recv, string, unicode));
   1155     }
   1156   }
   1157 
   1158   elems->Shrink(n);
   1159   return *isolate->factory()->NewJSArrayWithElements(elems);
   1160 }
   1161 
   1162 namespace {
   1163 
   1164 void Generate_RegExpPrototypeSearchBody(CodeStubAssembler* a,
   1165                                         compiler::Node* const receiver,
   1166                                         compiler::Node* const string,
   1167                                         compiler::Node* const context,
   1168                                         bool is_fastpath) {
   1169   typedef CodeStubAssembler::Label Label;
   1170   typedef compiler::Node Node;
   1171 
   1172   Isolate* const isolate = a->isolate();
   1173 
   1174   Node* const smi_zero = a->SmiConstant(Smi::kZero);
   1175 
   1176   // Grab the initial value of last index.
   1177   Node* const previous_last_index =
   1178       is_fastpath ? FastLoadLastIndex(a, context, receiver)
   1179                   : SlowLoadLastIndex(a, context, receiver);
   1180 
   1181   // Ensure last index is 0.
   1182   if (is_fastpath) {
   1183     FastStoreLastIndex(a, context, receiver, smi_zero);
   1184   } else {
   1185     Label next(a);
   1186     a->GotoIf(a->SameValue(previous_last_index, smi_zero, context), &next);
   1187 
   1188     SlowStoreLastIndex(a, context, receiver, smi_zero);
   1189     a->Goto(&next);
   1190     a->Bind(&next);
   1191   }
   1192 
   1193   // Call exec.
   1194   Node* const match_indices =
   1195       is_fastpath ? RegExpPrototypeExecInternal(a, context, receiver, string)
   1196                   : RegExpExec(a, context, receiver, string);
   1197 
   1198   // Reset last index if necessary.
   1199   if (is_fastpath) {
   1200     FastStoreLastIndex(a, context, receiver, previous_last_index);
   1201   } else {
   1202     Label next(a);
   1203     Node* const current_last_index = SlowLoadLastIndex(a, context, receiver);
   1204 
   1205     a->GotoIf(a->SameValue(current_last_index, previous_last_index, context),
   1206               &next);
   1207 
   1208     SlowStoreLastIndex(a, context, receiver, previous_last_index);
   1209     a->Goto(&next);
   1210     a->Bind(&next);
   1211   }
   1212 
   1213   // Return -1 if no match was found.
   1214   {
   1215     Label next(a);
   1216     a->GotoUnless(a->WordEqual(match_indices, a->NullConstant()), &next);
   1217     a->Return(a->SmiConstant(-1));
   1218     a->Bind(&next);
   1219   }
   1220 
   1221   // Return the index of the match.
   1222   {
   1223     Label fast_result(a), slow_result(a, Label::kDeferred);
   1224 
   1225     Node* const native_context = a->LoadNativeContext(context);
   1226     Node* const initial_regexp_result_map =
   1227         a->LoadContextElement(native_context, Context::REGEXP_RESULT_MAP_INDEX);
   1228     Node* const match_indices_map = a->LoadMap(match_indices);
   1229 
   1230     a->Branch(a->WordEqual(match_indices_map, initial_regexp_result_map),
   1231               &fast_result, &slow_result);
   1232 
   1233     a->Bind(&fast_result);
   1234     {
   1235       Node* const index =
   1236           a->LoadObjectField(match_indices, JSRegExpResult::kIndexOffset,
   1237                              MachineType::AnyTagged());
   1238       a->Return(index);
   1239     }
   1240 
   1241     a->Bind(&slow_result);
   1242     {
   1243       Node* const name = a->HeapConstant(isolate->factory()->index_string());
   1244       Callable getproperty_callable = CodeFactory::GetProperty(a->isolate());
   1245       Node* const index =
   1246           a->CallStub(getproperty_callable, context, match_indices, name);
   1247       a->Return(index);
   1248     }
   1249   }
   1250 }
   1251 
   1252 }  // namespace
   1253 
   1254 // ES#sec-regexp.prototype-@@search
   1255 // RegExp.prototype [ @@search ] ( string )
   1256 void Builtins::Generate_RegExpPrototypeSearch(CodeStubAssembler* a) {
   1257   typedef CodeStubAssembler::Label Label;
   1258   typedef compiler::Node Node;
   1259 
   1260   Isolate* const isolate = a->isolate();
   1261 
   1262   Node* const maybe_receiver = a->Parameter(0);
   1263   Node* const maybe_string = a->Parameter(1);
   1264   Node* const context = a->Parameter(4);
   1265 
   1266   // Ensure {maybe_receiver} is a JSReceiver.
   1267   Node* const map =
   1268       ThrowIfNotJSReceiver(a, isolate, context, maybe_receiver,
   1269                            MessageTemplate::kIncompatibleMethodReceiver,
   1270                            "RegExp.prototype.@@search");
   1271   Node* const receiver = maybe_receiver;
   1272 
   1273   // Convert {maybe_string} to a String.
   1274   Node* const string = a->ToString(context, maybe_string);
   1275 
   1276   Label fast_path(a), slow_path(a);
   1277   BranchIfFastPath(a, context, map, &fast_path, &slow_path);
   1278 
   1279   a->Bind(&fast_path);
   1280   Generate_RegExpPrototypeSearchBody(a, receiver, string, context, true);
   1281 
   1282   a->Bind(&slow_path);
   1283   Generate_RegExpPrototypeSearchBody(a, receiver, string, context, false);
   1284 }
   1285 
   1286 namespace {
   1287 
   1288 MUST_USE_RESULT MaybeHandle<Object> ToUint32(Isolate* isolate,
   1289                                              Handle<Object> object,
   1290                                              uint32_t* out) {
   1291   if (object->IsUndefined(isolate)) {
   1292     *out = kMaxUInt32;
   1293     return object;
   1294   }
   1295 
   1296   Handle<Object> number;
   1297   ASSIGN_RETURN_ON_EXCEPTION(isolate, number, Object::ToNumber(object), Object);
   1298   *out = NumberToUint32(*number);
   1299   return object;
   1300 }
   1301 
   1302 bool AtSurrogatePair(Isolate* isolate, Handle<String> string, int index) {
   1303   if (index + 1 >= string->length()) return false;
   1304   const uint16_t first = string->Get(index);
   1305   if (first < 0xD800 || first > 0xDBFF) return false;
   1306   const uint16_t second = string->Get(index + 1);
   1307   return (second >= 0xDC00 && second <= 0xDFFF);
   1308 }
   1309 
   1310 Handle<JSArray> NewJSArrayWithElements(Isolate* isolate,
   1311                                        Handle<FixedArray> elems,
   1312                                        int num_elems) {
   1313   elems->Shrink(num_elems);
   1314   return isolate->factory()->NewJSArrayWithElements(elems);
   1315 }
   1316 
   1317 MaybeHandle<JSArray> RegExpSplit(Isolate* isolate, Handle<JSRegExp> regexp,
   1318                                  Handle<String> string,
   1319                                  Handle<Object> limit_obj) {
   1320   Factory* factory = isolate->factory();
   1321 
   1322   uint32_t limit;
   1323   RETURN_ON_EXCEPTION(isolate, ToUint32(isolate, limit_obj, &limit), JSArray);
   1324 
   1325   const int length = string->length();
   1326 
   1327   if (limit == 0) return factory->NewJSArray(0);
   1328 
   1329   Handle<RegExpMatchInfo> last_match_info = isolate->regexp_last_match_info();
   1330 
   1331   if (length == 0) {
   1332     Handle<Object> match_indices;
   1333     ASSIGN_RETURN_ON_EXCEPTION(
   1334         isolate, match_indices,
   1335         RegExpImpl::Exec(regexp, string, 0, last_match_info), JSArray);
   1336 
   1337     if (!match_indices->IsNull(isolate)) return factory->NewJSArray(0);
   1338 
   1339     Handle<FixedArray> elems = factory->NewUninitializedFixedArray(1);
   1340     elems->set(0, *string);
   1341     return factory->NewJSArrayWithElements(elems);
   1342   }
   1343 
   1344   int current_index = 0;
   1345   int start_index = 0;
   1346   int start_match = 0;
   1347 
   1348   static const int kInitialArraySize = 8;
   1349   Handle<FixedArray> elems = factory->NewFixedArrayWithHoles(kInitialArraySize);
   1350   int num_elems = 0;
   1351 
   1352   while (true) {
   1353     if (start_index == length) {
   1354       Handle<String> substr =
   1355           factory->NewSubString(string, current_index, length);
   1356       elems = FixedArray::SetAndGrow(elems, num_elems++, substr);
   1357       break;
   1358     }
   1359 
   1360     Handle<Object> match_indices_obj;
   1361     ASSIGN_RETURN_ON_EXCEPTION(
   1362         isolate, match_indices_obj,
   1363         RegExpImpl::Exec(regexp, string, start_index,
   1364                          isolate->regexp_last_match_info()),
   1365         JSArray);
   1366 
   1367     if (match_indices_obj->IsNull(isolate)) {
   1368       Handle<String> substr =
   1369           factory->NewSubString(string, current_index, length);
   1370       elems = FixedArray::SetAndGrow(elems, num_elems++, substr);
   1371       break;
   1372     }
   1373 
   1374     auto match_indices = Handle<RegExpMatchInfo>::cast(match_indices_obj);
   1375 
   1376     start_match = match_indices->Capture(0);
   1377 
   1378     if (start_match == length) {
   1379       Handle<String> substr =
   1380           factory->NewSubString(string, current_index, length);
   1381       elems = FixedArray::SetAndGrow(elems, num_elems++, substr);
   1382       break;
   1383     }
   1384 
   1385     const int end_index = match_indices->Capture(1);
   1386 
   1387     if (start_index == end_index && end_index == current_index) {
   1388       const bool unicode = (regexp->GetFlags() & JSRegExp::kUnicode) != 0;
   1389       if (unicode && AtSurrogatePair(isolate, string, start_index)) {
   1390         start_index += 2;
   1391       } else {
   1392         start_index += 1;
   1393       }
   1394       continue;
   1395     }
   1396 
   1397     {
   1398       Handle<String> substr =
   1399           factory->NewSubString(string, current_index, start_match);
   1400       elems = FixedArray::SetAndGrow(elems, num_elems++, substr);
   1401     }
   1402 
   1403     if (static_cast<uint32_t>(num_elems) == limit) break;
   1404 
   1405     for (int i = 2; i < match_indices->NumberOfCaptureRegisters(); i += 2) {
   1406       const int start = match_indices->Capture(i);
   1407       const int end = match_indices->Capture(i + 1);
   1408 
   1409       if (end != -1) {
   1410         Handle<String> substr = factory->NewSubString(string, start, end);
   1411         elems = FixedArray::SetAndGrow(elems, num_elems++, substr);
   1412       } else {
   1413         elems = FixedArray::SetAndGrow(elems, num_elems++,
   1414                                        factory->undefined_value());
   1415       }
   1416 
   1417       if (static_cast<uint32_t>(num_elems) == limit) {
   1418         return NewJSArrayWithElements(isolate, elems, num_elems);
   1419       }
   1420     }
   1421 
   1422     start_index = current_index = end_index;
   1423   }
   1424 
   1425   return NewJSArrayWithElements(isolate, elems, num_elems);
   1426 }
   1427 
   1428 // ES##sec-speciesconstructor
   1429 // SpeciesConstructor ( O, defaultConstructor )
   1430 MUST_USE_RESULT MaybeHandle<Object> SpeciesConstructor(
   1431     Isolate* isolate, Handle<JSReceiver> recv,
   1432     Handle<JSFunction> default_ctor) {
   1433   Handle<Object> ctor_obj;
   1434   ASSIGN_RETURN_ON_EXCEPTION(
   1435       isolate, ctor_obj,
   1436       JSObject::GetProperty(recv, isolate->factory()->constructor_string()),
   1437       Object);
   1438 
   1439   if (ctor_obj->IsUndefined(isolate)) return default_ctor;
   1440 
   1441   if (!ctor_obj->IsJSReceiver()) {
   1442     THROW_NEW_ERROR(isolate,
   1443                     NewTypeError(MessageTemplate::kConstructorNotReceiver),
   1444                     Object);
   1445   }
   1446 
   1447   Handle<JSReceiver> ctor = Handle<JSReceiver>::cast(ctor_obj);
   1448 
   1449   Handle<Object> species;
   1450   ASSIGN_RETURN_ON_EXCEPTION(
   1451       isolate, species,
   1452       JSObject::GetProperty(ctor, isolate->factory()->species_symbol()),
   1453       Object);
   1454 
   1455   if (species->IsNull(isolate) || species->IsUndefined(isolate)) {
   1456     return default_ctor;
   1457   }
   1458 
   1459   if (species->IsConstructor()) return species;
   1460 
   1461   THROW_NEW_ERROR(
   1462       isolate, NewTypeError(MessageTemplate::kSpeciesNotConstructor), Object);
   1463 }
   1464 
   1465 }  // namespace
   1466 
   1467 // ES#sec-regexp.prototype-@@split
   1468 // RegExp.prototype [ @@split ] ( string, limit )
   1469 BUILTIN(RegExpPrototypeSplit) {
   1470   HandleScope scope(isolate);
   1471   CHECK_RECEIVER(JSReceiver, recv, "RegExp.prototype.@@split");
   1472 
   1473   Factory* factory = isolate->factory();
   1474 
   1475   Handle<Object> string_obj = args.atOrUndefined(isolate, 1);
   1476   Handle<Object> limit_obj = args.atOrUndefined(isolate, 2);
   1477 
   1478   Handle<String> string;
   1479   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, string,
   1480                                      Object::ToString(isolate, string_obj));
   1481 
   1482   if (RegExpUtils::IsUnmodifiedRegExp(isolate, recv)) {
   1483     RETURN_RESULT_OR_FAILURE(
   1484         isolate,
   1485         RegExpSplit(isolate, Handle<JSRegExp>::cast(recv), string, limit_obj));
   1486   }
   1487 
   1488   Handle<JSFunction> regexp_fun = isolate->regexp_function();
   1489   Handle<Object> ctor;
   1490   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
   1491       isolate, ctor, SpeciesConstructor(isolate, recv, regexp_fun));
   1492 
   1493   Handle<Object> flags_obj;
   1494   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
   1495       isolate, flags_obj, JSObject::GetProperty(recv, factory->flags_string()));
   1496 
   1497   Handle<String> flags;
   1498   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, flags,
   1499                                      Object::ToString(isolate, flags_obj));
   1500 
   1501   Handle<String> u_str = factory->LookupSingleCharacterStringFromCode('u');
   1502   const bool unicode = (String::IndexOf(isolate, flags, u_str, 0) >= 0);
   1503 
   1504   Handle<String> y_str = factory->LookupSingleCharacterStringFromCode('y');
   1505   const bool sticky = (String::IndexOf(isolate, flags, y_str, 0) >= 0);
   1506 
   1507   Handle<String> new_flags = flags;
   1508   if (!sticky) {
   1509     ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, new_flags,
   1510                                        factory->NewConsString(flags, y_str));
   1511   }
   1512 
   1513   Handle<JSReceiver> splitter;
   1514   {
   1515     const int argc = 2;
   1516 
   1517     ScopedVector<Handle<Object>> argv(argc);
   1518     argv[0] = recv;
   1519     argv[1] = new_flags;
   1520 
   1521     Handle<JSFunction> ctor_fun = Handle<JSFunction>::cast(ctor);
   1522     Handle<Object> splitter_obj;
   1523     ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
   1524         isolate, splitter_obj, Execution::New(ctor_fun, argc, argv.start()));
   1525 
   1526     splitter = Handle<JSReceiver>::cast(splitter_obj);
   1527   }
   1528 
   1529   uint32_t limit;
   1530   RETURN_FAILURE_ON_EXCEPTION(isolate, ToUint32(isolate, limit_obj, &limit));
   1531 
   1532   const int length = string->length();
   1533 
   1534   if (limit == 0) return *factory->NewJSArray(0);
   1535 
   1536   if (length == 0) {
   1537     Handle<Object> result;
   1538     ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
   1539         isolate, result, RegExpUtils::RegExpExec(isolate, splitter, string,
   1540                                                  factory->undefined_value()));
   1541 
   1542     if (!result->IsNull(isolate)) return *factory->NewJSArray(0);
   1543 
   1544     Handle<FixedArray> elems = factory->NewUninitializedFixedArray(1);
   1545     elems->set(0, *string);
   1546     return *factory->NewJSArrayWithElements(elems);
   1547   }
   1548 
   1549   // TODO(jgruber): Wrap this in a helper class.
   1550   static const int kInitialArraySize = 8;
   1551   Handle<FixedArray> elems = factory->NewFixedArrayWithHoles(kInitialArraySize);
   1552   int num_elems = 0;
   1553 
   1554   int string_index = 0;
   1555   int prev_string_index = 0;
   1556   while (string_index < length) {
   1557     RETURN_FAILURE_ON_EXCEPTION(
   1558         isolate, RegExpUtils::SetLastIndex(isolate, splitter, string_index));
   1559 
   1560     Handle<Object> result;
   1561     ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
   1562         isolate, result, RegExpUtils::RegExpExec(isolate, splitter, string,
   1563                                                  factory->undefined_value()));
   1564 
   1565     if (result->IsNull(isolate)) {
   1566       string_index = RegExpUtils::AdvanceStringIndex(isolate, string,
   1567                                                      string_index, unicode);
   1568       continue;
   1569     }
   1570 
   1571     // TODO(jgruber): Extract toLength of some property into function.
   1572     Handle<Object> last_index_obj;
   1573     ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
   1574         isolate, last_index_obj, RegExpUtils::GetLastIndex(isolate, splitter));
   1575 
   1576     ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
   1577         isolate, last_index_obj, Object::ToLength(isolate, last_index_obj));
   1578     const int last_index = Handle<Smi>::cast(last_index_obj)->value();
   1579 
   1580     const int end = std::min(last_index, length);
   1581     if (end == prev_string_index) {
   1582       string_index = RegExpUtils::AdvanceStringIndex(isolate, string,
   1583                                                      string_index, unicode);
   1584       continue;
   1585     }
   1586 
   1587     {
   1588       Handle<String> substr =
   1589           factory->NewSubString(string, prev_string_index, string_index);
   1590       elems = FixedArray::SetAndGrow(elems, num_elems++, substr);
   1591       if (static_cast<uint32_t>(num_elems) == limit) {
   1592         return *NewJSArrayWithElements(isolate, elems, num_elems);
   1593       }
   1594     }
   1595 
   1596     prev_string_index = end;
   1597 
   1598     Handle<Object> num_captures_obj;
   1599     ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
   1600         isolate, num_captures_obj,
   1601         Object::GetProperty(result, isolate->factory()->length_string()));
   1602 
   1603     ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
   1604         isolate, num_captures_obj, Object::ToLength(isolate, num_captures_obj));
   1605     const int num_captures =
   1606         std::max(Handle<Smi>::cast(num_captures_obj)->value(), 0);
   1607 
   1608     for (int i = 1; i < num_captures; i++) {
   1609       Handle<Object> capture;
   1610       ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
   1611           isolate, capture, Object::GetElement(isolate, result, i));
   1612       elems = FixedArray::SetAndGrow(elems, num_elems++, capture);
   1613       if (static_cast<uint32_t>(num_elems) == limit) {
   1614         return *NewJSArrayWithElements(isolate, elems, num_elems);
   1615       }
   1616     }
   1617 
   1618     string_index = prev_string_index;
   1619   }
   1620 
   1621   {
   1622     Handle<String> substr =
   1623         factory->NewSubString(string, prev_string_index, length);
   1624     elems = FixedArray::SetAndGrow(elems, num_elems++, substr);
   1625   }
   1626 
   1627   return *NewJSArrayWithElements(isolate, elems, num_elems);
   1628 }
   1629 
   1630 namespace {
   1631 
   1632 compiler::Node* ReplaceGlobalCallableFastPath(
   1633     CodeStubAssembler* a, compiler::Node* context, compiler::Node* regexp,
   1634     compiler::Node* subject_string, compiler::Node* replace_callable) {
   1635   // The fast path is reached only if {receiver} is a global unmodified
   1636   // JSRegExp instance and {replace_callable} is callable.
   1637 
   1638   typedef CodeStubAssembler::Variable Variable;
   1639   typedef CodeStubAssembler::Label Label;
   1640   typedef compiler::Node Node;
   1641 
   1642   Isolate* const isolate = a->isolate();
   1643 
   1644   Node* const null = a->NullConstant();
   1645   Node* const undefined = a->UndefinedConstant();
   1646   Node* const int_zero = a->IntPtrConstant(0);
   1647   Node* const int_one = a->IntPtrConstant(1);
   1648   Node* const smi_zero = a->SmiConstant(Smi::kZero);
   1649 
   1650   Node* const native_context = a->LoadNativeContext(context);
   1651 
   1652   Label out(a);
   1653   Variable var_result(a, MachineRepresentation::kTagged);
   1654 
   1655   // Set last index to 0.
   1656   FastStoreLastIndex(a, context, regexp, smi_zero);
   1657 
   1658   // Allocate {result_array}.
   1659   Node* result_array;
   1660   {
   1661     ElementsKind kind = FAST_ELEMENTS;
   1662     Node* const array_map = a->LoadJSArrayElementsMap(kind, native_context);
   1663     Node* const capacity = a->IntPtrConstant(16);
   1664     Node* const length = smi_zero;
   1665     Node* const allocation_site = nullptr;
   1666     CodeStubAssembler::ParameterMode capacity_mode =
   1667         CodeStubAssembler::INTPTR_PARAMETERS;
   1668 
   1669     result_array = a->AllocateJSArray(kind, array_map, capacity, length,
   1670                                       allocation_site, capacity_mode);
   1671   }
   1672 
   1673   // Call into runtime for RegExpExecMultiple.
   1674   Node* last_match_info = a->LoadContextElement(
   1675       native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX);
   1676   Node* const res =
   1677       a->CallRuntime(Runtime::kRegExpExecMultiple, context, regexp,
   1678                      subject_string, last_match_info, result_array);
   1679 
   1680   // Reset last index to 0.
   1681   FastStoreLastIndex(a, context, regexp, smi_zero);
   1682 
   1683   // If no matches, return the subject string.
   1684   var_result.Bind(subject_string);
   1685   a->GotoIf(a->WordEqual(res, null), &out);
   1686 
   1687   // Reload last match info since it might have changed.
   1688   last_match_info = a->LoadContextElement(
   1689       native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX);
   1690 
   1691   Node* const res_length = a->LoadJSArrayLength(res);
   1692   Node* const res_elems = a->LoadElements(res);
   1693   CSA_ASSERT(a, a->HasInstanceType(res_elems, FIXED_ARRAY_TYPE));
   1694 
   1695   CodeStubAssembler::ParameterMode mode = CodeStubAssembler::INTPTR_PARAMETERS;
   1696   Node* const num_capture_registers = a->LoadFixedArrayElement(
   1697       last_match_info,
   1698       a->IntPtrConstant(RegExpMatchInfo::kNumberOfCapturesIndex), 0, mode);
   1699 
   1700   Label if_hasexplicitcaptures(a), if_noexplicitcaptures(a), create_result(a);
   1701   a->Branch(a->SmiEqual(num_capture_registers, a->SmiConstant(Smi::FromInt(2))),
   1702             &if_noexplicitcaptures, &if_hasexplicitcaptures);
   1703 
   1704   a->Bind(&if_noexplicitcaptures);
   1705   {
   1706     // If the number of captures is two then there are no explicit captures in
   1707     // the regexp, just the implicit capture that captures the whole match. In
   1708     // this case we can simplify quite a bit and end up with something faster.
   1709     // The builder will consist of some integers that indicate slices of the
   1710     // input string and some replacements that were returned from the replace
   1711     // function.
   1712 
   1713     Variable var_match_start(a, MachineRepresentation::kTagged);
   1714     var_match_start.Bind(smi_zero);
   1715 
   1716     Node* const end = a->SmiUntag(res_length);
   1717     Variable var_i(a, MachineType::PointerRepresentation());
   1718     var_i.Bind(int_zero);
   1719 
   1720     Variable* vars[] = {&var_i, &var_match_start};
   1721     Label loop(a, 2, vars);
   1722     a->Goto(&loop);
   1723     a->Bind(&loop);
   1724     {
   1725       Node* const i = var_i.value();
   1726       a->GotoUnless(a->IntPtrLessThan(i, end), &create_result);
   1727 
   1728       CodeStubAssembler::ParameterMode mode =
   1729           CodeStubAssembler::INTPTR_PARAMETERS;
   1730       Node* const elem = a->LoadFixedArrayElement(res_elems, i, 0, mode);
   1731 
   1732       Label if_issmi(a), if_isstring(a), loop_epilogue(a);
   1733       a->Branch(a->TaggedIsSmi(elem), &if_issmi, &if_isstring);
   1734 
   1735       a->Bind(&if_issmi);
   1736       {
   1737         // Integers represent slices of the original string.
   1738         Label if_isnegativeorzero(a), if_ispositive(a);
   1739         a->BranchIfSmiLessThanOrEqual(elem, smi_zero, &if_isnegativeorzero,
   1740                                       &if_ispositive);
   1741 
   1742         a->Bind(&if_ispositive);
   1743         {
   1744           Node* const int_elem = a->SmiUntag(elem);
   1745           Node* const new_match_start =
   1746               a->IntPtrAdd(a->WordShr(int_elem, a->IntPtrConstant(11)),
   1747                            a->WordAnd(int_elem, a->IntPtrConstant(0x7ff)));
   1748           var_match_start.Bind(a->SmiTag(new_match_start));
   1749           a->Goto(&loop_epilogue);
   1750         }
   1751 
   1752         a->Bind(&if_isnegativeorzero);
   1753         {
   1754           Node* const next_i = a->IntPtrAdd(i, int_one);
   1755           var_i.Bind(next_i);
   1756 
   1757           Node* const next_elem =
   1758               a->LoadFixedArrayElement(res_elems, next_i, 0, mode);
   1759 
   1760           Node* const new_match_start = a->SmiSub(next_elem, elem);
   1761           var_match_start.Bind(new_match_start);
   1762           a->Goto(&loop_epilogue);
   1763         }
   1764       }
   1765 
   1766       a->Bind(&if_isstring);
   1767       {
   1768         CSA_ASSERT(a, a->IsStringInstanceType(a->LoadInstanceType(elem)));
   1769 
   1770         Callable call_callable = CodeFactory::Call(isolate);
   1771         Node* const replacement_obj =
   1772             a->CallJS(call_callable, context, replace_callable, undefined, elem,
   1773                       var_match_start.value(), subject_string);
   1774 
   1775         Node* const replacement_str = a->ToString(context, replacement_obj);
   1776         a->StoreFixedArrayElement(res_elems, i, replacement_str);
   1777 
   1778         Node* const elem_length = a->LoadStringLength(elem);
   1779         Node* const new_match_start =
   1780             a->SmiAdd(var_match_start.value(), elem_length);
   1781         var_match_start.Bind(new_match_start);
   1782 
   1783         a->Goto(&loop_epilogue);
   1784       }
   1785 
   1786       a->Bind(&loop_epilogue);
   1787       {
   1788         var_i.Bind(a->IntPtrAdd(var_i.value(), int_one));
   1789         a->Goto(&loop);
   1790       }
   1791     }
   1792   }
   1793 
   1794   a->Bind(&if_hasexplicitcaptures);
   1795   {
   1796     CodeStubAssembler::ParameterMode mode =
   1797         CodeStubAssembler::INTPTR_PARAMETERS;
   1798 
   1799     Node* const from = int_zero;
   1800     Node* const to = a->SmiUntag(res_length);
   1801     const int increment = 1;
   1802 
   1803     a->BuildFastLoop(
   1804         MachineType::PointerRepresentation(), from, to,
   1805         [res_elems, isolate, native_context, context, undefined,
   1806          replace_callable, mode](CodeStubAssembler* a, Node* index) {
   1807           Node* const elem =
   1808               a->LoadFixedArrayElement(res_elems, index, 0, mode);
   1809 
   1810           Label do_continue(a);
   1811           a->GotoIf(a->TaggedIsSmi(elem), &do_continue);
   1812 
   1813           // elem must be an Array.
   1814           // Use the apply argument as backing for global RegExp properties.
   1815 
   1816           CSA_ASSERT(a, a->HasInstanceType(elem, JS_ARRAY_TYPE));
   1817 
   1818           // TODO(jgruber): Remove indirection through Call->ReflectApply.
   1819           Callable call_callable = CodeFactory::Call(isolate);
   1820           Node* const reflect_apply = a->LoadContextElement(
   1821               native_context, Context::REFLECT_APPLY_INDEX);
   1822 
   1823           Node* const replacement_obj =
   1824               a->CallJS(call_callable, context, reflect_apply, undefined,
   1825                         replace_callable, undefined, elem);
   1826 
   1827           // Overwrite the i'th element in the results with the string we got
   1828           // back from the callback function.
   1829 
   1830           Node* const replacement_str = a->ToString(context, replacement_obj);
   1831           a->StoreFixedArrayElement(res_elems, index, replacement_str,
   1832                                     UPDATE_WRITE_BARRIER, mode);
   1833 
   1834           a->Goto(&do_continue);
   1835           a->Bind(&do_continue);
   1836         },
   1837         increment, CodeStubAssembler::IndexAdvanceMode::kPost);
   1838 
   1839     a->Goto(&create_result);
   1840   }
   1841 
   1842   a->Bind(&create_result);
   1843   {
   1844     Node* const result = a->CallRuntime(Runtime::kStringBuilderConcat, context,
   1845                                         res, res_length, subject_string);
   1846     var_result.Bind(result);
   1847     a->Goto(&out);
   1848   }
   1849 
   1850   a->Bind(&out);
   1851   return var_result.value();
   1852 }
   1853 
   1854 compiler::Node* ReplaceSimpleStringFastPath(CodeStubAssembler* a,
   1855                                             compiler::Node* context,
   1856                                             compiler::Node* regexp,
   1857                                             compiler::Node* subject_string,
   1858                                             compiler::Node* replace_string) {
   1859   // The fast path is reached only if {receiver} is an unmodified
   1860   // JSRegExp instance, {replace_value} is non-callable, and
   1861   // ToString({replace_value}) does not contain '$', i.e. we're doing a simple
   1862   // string replacement.
   1863 
   1864   typedef CodeStubAssembler::Variable Variable;
   1865   typedef CodeStubAssembler::Label Label;
   1866   typedef compiler::Node Node;
   1867 
   1868   Isolate* const isolate = a->isolate();
   1869 
   1870   Node* const null = a->NullConstant();
   1871   Node* const int_zero = a->IntPtrConstant(0);
   1872   Node* const smi_zero = a->SmiConstant(Smi::kZero);
   1873 
   1874   Label out(a);
   1875   Variable var_result(a, MachineRepresentation::kTagged);
   1876 
   1877   // Load the last match info.
   1878   Node* const native_context = a->LoadNativeContext(context);
   1879   Node* const last_match_info = a->LoadContextElement(
   1880       native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX);
   1881 
   1882   // Is {regexp} global?
   1883   Label if_isglobal(a), if_isnonglobal(a);
   1884   Node* const flags = a->LoadObjectField(regexp, JSRegExp::kFlagsOffset);
   1885   Node* const is_global =
   1886       a->WordAnd(a->SmiUntag(flags), a->IntPtrConstant(JSRegExp::kGlobal));
   1887   a->Branch(a->WordEqual(is_global, int_zero), &if_isnonglobal, &if_isglobal);
   1888 
   1889   a->Bind(&if_isglobal);
   1890   {
   1891     // Hand off global regexps to runtime.
   1892     FastStoreLastIndex(a, context, regexp, smi_zero);
   1893     Node* const result =
   1894         a->CallRuntime(Runtime::kStringReplaceGlobalRegExpWithString, context,
   1895                        subject_string, regexp, replace_string, last_match_info);
   1896     var_result.Bind(result);
   1897     a->Goto(&out);
   1898   }
   1899 
   1900   a->Bind(&if_isnonglobal);
   1901   {
   1902     // Run exec, then manually construct the resulting string.
   1903     Callable exec_callable = CodeFactory::RegExpExec(isolate);
   1904     Node* const match_indices =
   1905         a->CallStub(exec_callable, context, regexp, subject_string, smi_zero,
   1906                     last_match_info);
   1907 
   1908     Label if_matched(a), if_didnotmatch(a);
   1909     a->Branch(a->WordEqual(match_indices, null), &if_didnotmatch, &if_matched);
   1910 
   1911     a->Bind(&if_didnotmatch);
   1912     {
   1913       FastStoreLastIndex(a, context, regexp, smi_zero);
   1914       var_result.Bind(subject_string);
   1915       a->Goto(&out);
   1916     }
   1917 
   1918     a->Bind(&if_matched);
   1919     {
   1920       CodeStubAssembler::ParameterMode mode =
   1921           CodeStubAssembler::INTPTR_PARAMETERS;
   1922 
   1923       Node* const subject_start = smi_zero;
   1924       Node* const match_start = a->LoadFixedArrayElement(
   1925           match_indices, a->IntPtrConstant(RegExpMatchInfo::kFirstCaptureIndex),
   1926           0, mode);
   1927       Node* const match_end = a->LoadFixedArrayElement(
   1928           match_indices,
   1929           a->IntPtrConstant(RegExpMatchInfo::kFirstCaptureIndex + 1), 0, mode);
   1930       Node* const subject_end = a->LoadStringLength(subject_string);
   1931 
   1932       Label if_replaceisempty(a), if_replaceisnotempty(a);
   1933       Node* const replace_length = a->LoadStringLength(replace_string);
   1934       a->Branch(a->SmiEqual(replace_length, smi_zero), &if_replaceisempty,
   1935                 &if_replaceisnotempty);
   1936 
   1937       a->Bind(&if_replaceisempty);
   1938       {
   1939         // TODO(jgruber): We could skip many of the checks that using SubString
   1940         // here entails.
   1941 
   1942         Node* const first_part =
   1943             a->SubString(context, subject_string, subject_start, match_start);
   1944         Node* const second_part =
   1945             a->SubString(context, subject_string, match_end, subject_end);
   1946 
   1947         Node* const result = a->StringAdd(context, first_part, second_part);
   1948         var_result.Bind(result);
   1949         a->Goto(&out);
   1950       }
   1951 
   1952       a->Bind(&if_replaceisnotempty);
   1953       {
   1954         Node* const first_part =
   1955             a->SubString(context, subject_string, subject_start, match_start);
   1956         Node* const second_part = replace_string;
   1957         Node* const third_part =
   1958             a->SubString(context, subject_string, match_end, subject_end);
   1959 
   1960         Node* result = a->StringAdd(context, first_part, second_part);
   1961         result = a->StringAdd(context, result, third_part);
   1962 
   1963         var_result.Bind(result);
   1964         a->Goto(&out);
   1965       }
   1966     }
   1967   }
   1968 
   1969   a->Bind(&out);
   1970   return var_result.value();
   1971 }
   1972 
   1973 }  // namespace
   1974 
   1975 // ES#sec-regexp.prototype-@@replace
   1976 // RegExp.prototype [ @@replace ] ( string, replaceValue )
   1977 void Builtins::Generate_RegExpPrototypeReplace(CodeStubAssembler* a) {
   1978   typedef CodeStubAssembler::Label Label;
   1979   typedef compiler::Node Node;
   1980 
   1981   Isolate* const isolate = a->isolate();
   1982 
   1983   Node* const maybe_receiver = a->Parameter(0);
   1984   Node* const maybe_string = a->Parameter(1);
   1985   Node* const replace_value = a->Parameter(2);
   1986   Node* const context = a->Parameter(5);
   1987 
   1988   Node* const int_zero = a->IntPtrConstant(0);
   1989 
   1990   // Ensure {maybe_receiver} is a JSReceiver.
   1991   Node* const map =
   1992       ThrowIfNotJSReceiver(a, isolate, context, maybe_receiver,
   1993                            MessageTemplate::kIncompatibleMethodReceiver,
   1994                            "RegExp.prototype.@@replace");
   1995   Node* const receiver = maybe_receiver;
   1996 
   1997   // Convert {maybe_string} to a String.
   1998   Callable tostring_callable = CodeFactory::ToString(isolate);
   1999   Node* const string = a->CallStub(tostring_callable, context, maybe_string);
   2000 
   2001   // Fast-path checks: 1. Is the {receiver} an unmodified JSRegExp instance?
   2002   Label checkreplacecallable(a), runtime(a, Label::kDeferred), fastpath(a);
   2003   BranchIfFastPath(a, context, map, &checkreplacecallable, &runtime);
   2004 
   2005   a->Bind(&checkreplacecallable);
   2006   Node* const regexp = receiver;
   2007 
   2008   // 2. Is {replace_value} callable?
   2009   Label checkreplacestring(a), if_iscallable(a);
   2010   a->GotoIf(a->TaggedIsSmi(replace_value), &checkreplacestring);
   2011 
   2012   Node* const replace_value_map = a->LoadMap(replace_value);
   2013   a->Branch(a->IsCallableMap(replace_value_map), &if_iscallable,
   2014             &checkreplacestring);
   2015 
   2016   // 3. Does ToString({replace_value}) contain '$'?
   2017   a->Bind(&checkreplacestring);
   2018   {
   2019     Node* const replace_string =
   2020         a->CallStub(tostring_callable, context, replace_value);
   2021 
   2022     Node* const dollar_char = a->IntPtrConstant('$');
   2023     Node* const smi_minusone = a->SmiConstant(Smi::FromInt(-1));
   2024     a->GotoUnless(a->SmiEqual(a->StringIndexOfChar(context, replace_string,
   2025                                                    dollar_char, int_zero),
   2026                               smi_minusone),
   2027                   &runtime);
   2028 
   2029     a->Return(ReplaceSimpleStringFastPath(a, context, regexp, string,
   2030                                           replace_string));
   2031   }
   2032 
   2033   // {regexp} is unmodified and {replace_value} is callable.
   2034   a->Bind(&if_iscallable);
   2035   {
   2036     Node* const replace_callable = replace_value;
   2037 
   2038     // Check if the {regexp} is global.
   2039     Label if_isglobal(a), if_isnotglobal(a);
   2040     Node* const is_global = FastFlagGetter(a, regexp, JSRegExp::kGlobal);
   2041     a->Branch(is_global, &if_isglobal, &if_isnotglobal);
   2042 
   2043     a->Bind(&if_isglobal);
   2044     {
   2045       Node* const result = ReplaceGlobalCallableFastPath(
   2046           a, context, regexp, string, replace_callable);
   2047       a->Return(result);
   2048     }
   2049 
   2050     a->Bind(&if_isnotglobal);
   2051     {
   2052       Node* const result =
   2053           a->CallRuntime(Runtime::kStringReplaceNonGlobalRegExpWithFunction,
   2054                          context, string, regexp, replace_callable);
   2055       a->Return(result);
   2056     }
   2057   }
   2058 
   2059   a->Bind(&runtime);
   2060   {
   2061     Node* const result = a->CallRuntime(Runtime::kRegExpReplace, context,
   2062                                         receiver, string, replace_value);
   2063     a->Return(result);
   2064   }
   2065 }
   2066 
   2067 // Simple string matching functionality for internal use which does not modify
   2068 // the last match info.
   2069 void Builtins::Generate_RegExpInternalMatch(CodeStubAssembler* a) {
   2070   typedef CodeStubAssembler::Label Label;
   2071   typedef compiler::Node Node;
   2072 
   2073   Isolate* const isolate = a->isolate();
   2074 
   2075   Node* const regexp = a->Parameter(1);
   2076   Node* const string = a->Parameter(2);
   2077   Node* const context = a->Parameter(5);
   2078 
   2079   Node* const null = a->NullConstant();
   2080   Node* const smi_zero = a->SmiConstant(Smi::FromInt(0));
   2081 
   2082   Node* const native_context = a->LoadNativeContext(context);
   2083   Node* const internal_match_info = a->LoadContextElement(
   2084       native_context, Context::REGEXP_INTERNAL_MATCH_INFO_INDEX);
   2085 
   2086   Callable exec_callable = CodeFactory::RegExpExec(isolate);
   2087   Node* const match_indices = a->CallStub(
   2088       exec_callable, context, regexp, string, smi_zero, internal_match_info);
   2089 
   2090   Label if_matched(a), if_didnotmatch(a);
   2091   a->Branch(a->WordEqual(match_indices, null), &if_didnotmatch, &if_matched);
   2092 
   2093   a->Bind(&if_didnotmatch);
   2094   a->Return(null);
   2095 
   2096   a->Bind(&if_matched);
   2097   {
   2098     Node* result = ConstructNewResultFromMatchInfo(isolate, a, context,
   2099                                                    match_indices, string);
   2100     a->Return(result);
   2101   }
   2102 }
   2103 
   2104 }  // namespace internal
   2105 }  // namespace v8
   2106