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-regexp.h"
      6 
      7 #include "src/builtins/builtins-constructor.h"
      8 #include "src/builtins/builtins-utils.h"
      9 #include "src/builtins/builtins.h"
     10 #include "src/code-factory.h"
     11 #include "src/code-stub-assembler.h"
     12 #include "src/counters.h"
     13 #include "src/objects-inl.h"
     14 #include "src/objects/regexp-match-info.h"
     15 #include "src/regexp/jsregexp.h"
     16 #include "src/regexp/regexp-utils.h"
     17 #include "src/string-builder.h"
     18 
     19 namespace v8 {
     20 namespace internal {
     21 
     22 typedef CodeStubAssembler::ParameterMode ParameterMode;
     23 
     24 
     25 // -----------------------------------------------------------------------------
     26 // ES6 section 21.2 RegExp Objects
     27 
     28 Node* RegExpBuiltinsAssembler::FastLoadLastIndex(Node* regexp) {
     29   // Load the in-object field.
     30   static const int field_offset =
     31       JSRegExp::kSize + JSRegExp::kLastIndexFieldIndex * kPointerSize;
     32   return LoadObjectField(regexp, field_offset);
     33 }
     34 
     35 Node* RegExpBuiltinsAssembler::SlowLoadLastIndex(Node* context, Node* regexp) {
     36   // Load through the GetProperty stub.
     37   Node* const name = HeapConstant(isolate()->factory()->lastIndex_string());
     38   Callable getproperty_callable = CodeFactory::GetProperty(isolate());
     39   return CallStub(getproperty_callable, context, regexp, name);
     40 }
     41 
     42 Node* RegExpBuiltinsAssembler::LoadLastIndex(Node* context, Node* regexp,
     43                                              bool is_fastpath) {
     44   return is_fastpath ? FastLoadLastIndex(regexp)
     45                      : SlowLoadLastIndex(context, regexp);
     46 }
     47 
     48 // The fast-path of StoreLastIndex when regexp is guaranteed to be an unmodified
     49 // JSRegExp instance.
     50 void RegExpBuiltinsAssembler::FastStoreLastIndex(Node* regexp, Node* value) {
     51   // Store the in-object field.
     52   static const int field_offset =
     53       JSRegExp::kSize + JSRegExp::kLastIndexFieldIndex * kPointerSize;
     54   StoreObjectField(regexp, field_offset, value);
     55 }
     56 
     57 void RegExpBuiltinsAssembler::SlowStoreLastIndex(Node* context, Node* regexp,
     58                                                  Node* value) {
     59   // Store through runtime.
     60   // TODO(ishell): Use SetPropertyStub here once available.
     61   Node* const name = HeapConstant(isolate()->factory()->lastIndex_string());
     62   Node* const language_mode = SmiConstant(Smi::FromInt(STRICT));
     63   CallRuntime(Runtime::kSetProperty, context, regexp, name, value,
     64               language_mode);
     65 }
     66 
     67 void RegExpBuiltinsAssembler::StoreLastIndex(Node* context, Node* regexp,
     68                                              Node* value, bool is_fastpath) {
     69   if (is_fastpath) {
     70     FastStoreLastIndex(regexp, value);
     71   } else {
     72     SlowStoreLastIndex(context, regexp, value);
     73   }
     74 }
     75 
     76 Node* RegExpBuiltinsAssembler::ConstructNewResultFromMatchInfo(
     77     Node* const context, Node* const regexp, Node* const match_info,
     78     Node* const string) {
     79   Label named_captures(this), out(this);
     80 
     81   Node* const num_indices = SmiUntag(LoadFixedArrayElement(
     82       match_info, RegExpMatchInfo::kNumberOfCapturesIndex));
     83   Node* const num_results = SmiTag(WordShr(num_indices, 1));
     84   Node* const start =
     85       LoadFixedArrayElement(match_info, RegExpMatchInfo::kFirstCaptureIndex);
     86   Node* const end = LoadFixedArrayElement(
     87       match_info, RegExpMatchInfo::kFirstCaptureIndex + 1);
     88 
     89   // Calculate the substring of the first match before creating the result array
     90   // to avoid an unnecessary write barrier storing the first result.
     91   Node* const first = SubString(context, string, start, end);
     92 
     93   Node* const result =
     94       AllocateRegExpResult(context, num_results, start, string);
     95   Node* const result_elements = LoadElements(result);
     96 
     97   StoreFixedArrayElement(result_elements, 0, first, SKIP_WRITE_BARRIER);
     98 
     99   // If no captures exist we can skip named capture handling as well.
    100   GotoIf(SmiEqual(num_results, SmiConstant(1)), &out);
    101 
    102   // Store all remaining captures.
    103   Node* const limit = IntPtrAdd(
    104       IntPtrConstant(RegExpMatchInfo::kFirstCaptureIndex), num_indices);
    105 
    106   Variable var_from_cursor(
    107       this, MachineType::PointerRepresentation(),
    108       IntPtrConstant(RegExpMatchInfo::kFirstCaptureIndex + 2));
    109   Variable var_to_cursor(this, MachineType::PointerRepresentation(),
    110                          IntPtrConstant(1));
    111 
    112   Variable* vars[] = {&var_from_cursor, &var_to_cursor};
    113   Label loop(this, 2, vars);
    114 
    115   Goto(&loop);
    116   Bind(&loop);
    117   {
    118     Node* const from_cursor = var_from_cursor.value();
    119     Node* const to_cursor = var_to_cursor.value();
    120     Node* const start = LoadFixedArrayElement(match_info, from_cursor);
    121 
    122     Label next_iter(this);
    123     GotoIf(SmiEqual(start, SmiConstant(-1)), &next_iter);
    124 
    125     Node* const from_cursor_plus1 = IntPtrAdd(from_cursor, IntPtrConstant(1));
    126     Node* const end = LoadFixedArrayElement(match_info, from_cursor_plus1);
    127 
    128     Node* const capture = SubString(context, string, start, end);
    129     StoreFixedArrayElement(result_elements, to_cursor, capture);
    130     Goto(&next_iter);
    131 
    132     Bind(&next_iter);
    133     var_from_cursor.Bind(IntPtrAdd(from_cursor, IntPtrConstant(2)));
    134     var_to_cursor.Bind(IntPtrAdd(to_cursor, IntPtrConstant(1)));
    135     Branch(UintPtrLessThan(var_from_cursor.value(), limit), &loop,
    136            &named_captures);
    137   }
    138 
    139   Bind(&named_captures);
    140   {
    141     // We reach this point only if captures exist, implying that this is an
    142     // IRREGEXP JSRegExp.
    143 
    144     CSA_ASSERT(this, HasInstanceType(regexp, JS_REGEXP_TYPE));
    145     CSA_ASSERT(this, SmiGreaterThan(num_results, SmiConstant(1)));
    146 
    147     // Preparations for named capture properties. Exit early if the result does
    148     // not have any named captures to minimize performance impact.
    149 
    150     Node* const data = LoadObjectField(regexp, JSRegExp::kDataOffset);
    151     CSA_ASSERT(this, SmiEqual(LoadFixedArrayElement(data, JSRegExp::kTagIndex),
    152                               SmiConstant(JSRegExp::IRREGEXP)));
    153 
    154     // The names fixed array associates names at even indices with a capture
    155     // index at odd indices.
    156     Node* const names =
    157         LoadFixedArrayElement(data, JSRegExp::kIrregexpCaptureNameMapIndex);
    158     GotoIf(SmiEqual(names, SmiConstant(0)), &out);
    159 
    160     // Allocate a new object to store the named capture properties.
    161     // TODO(jgruber): Could be optimized by adding the object map to the heap
    162     // root list.
    163 
    164     Node* const native_context = LoadNativeContext(context);
    165     Node* const map = LoadContextElement(
    166         native_context, Context::SLOW_OBJECT_WITH_NULL_PROTOTYPE_MAP);
    167     Node* const properties =
    168         AllocateNameDictionary(NameDictionary::kInitialCapacity);
    169 
    170     Node* const group_object = AllocateJSObjectFromMap(map, properties);
    171 
    172     // Store it on the result as a 'group' property.
    173 
    174     {
    175       Node* const name = HeapConstant(isolate()->factory()->group_string());
    176       CallRuntime(Runtime::kCreateDataProperty, context, result, name,
    177                   group_object);
    178     }
    179 
    180     // One or more named captures exist, add a property for each one.
    181 
    182     CSA_ASSERT(this, HasInstanceType(names, FIXED_ARRAY_TYPE));
    183     Node* const names_length = LoadAndUntagFixedArrayBaseLength(names);
    184     CSA_ASSERT(this, IntPtrGreaterThan(names_length, IntPtrConstant(0)));
    185 
    186     Variable var_i(this, MachineType::PointerRepresentation());
    187     var_i.Bind(IntPtrConstant(0));
    188 
    189     Variable* vars[] = {&var_i};
    190     const int vars_count = sizeof(vars) / sizeof(vars[0]);
    191     Label loop(this, vars_count, vars);
    192 
    193     Goto(&loop);
    194     Bind(&loop);
    195     {
    196       Node* const i = var_i.value();
    197       Node* const i_plus_1 = IntPtrAdd(i, IntPtrConstant(1));
    198       Node* const i_plus_2 = IntPtrAdd(i_plus_1, IntPtrConstant(1));
    199 
    200       Node* const name = LoadFixedArrayElement(names, i);
    201       Node* const index = LoadFixedArrayElement(names, i_plus_1);
    202       Node* const capture =
    203           LoadFixedArrayElement(result_elements, SmiUntag(index));
    204 
    205       CallRuntime(Runtime::kCreateDataProperty, context, group_object, name,
    206                   capture);
    207 
    208       var_i.Bind(i_plus_2);
    209       Branch(IntPtrGreaterThanOrEqual(var_i.value(), names_length), &out,
    210              &loop);
    211     }
    212   }
    213 
    214   Bind(&out);
    215   return result;
    216 }
    217 
    218 // ES#sec-regexp.prototype.exec
    219 // RegExp.prototype.exec ( string )
    220 // Implements the core of RegExp.prototype.exec but without actually
    221 // constructing the JSRegExpResult. Returns either null (if the RegExp did not
    222 // match) or a fixed array containing match indices as returned by
    223 // RegExpExecStub.
    224 Node* RegExpBuiltinsAssembler::RegExpPrototypeExecBodyWithoutResult(
    225     Node* const context, Node* const regexp, Node* const string,
    226     Label* if_didnotmatch, const bool is_fastpath) {
    227   Isolate* const isolate = this->isolate();
    228 
    229   Node* const null = NullConstant();
    230   Node* const int_zero = IntPtrConstant(0);
    231   Node* const smi_zero = SmiConstant(Smi::kZero);
    232 
    233   if (!is_fastpath) {
    234     ThrowIfNotInstanceType(context, regexp, JS_REGEXP_TYPE,
    235                            "RegExp.prototype.exec");
    236   }
    237 
    238   CSA_ASSERT(this, IsStringInstanceType(LoadInstanceType(string)));
    239   CSA_ASSERT(this, HasInstanceType(regexp, JS_REGEXP_TYPE));
    240 
    241   Variable var_result(this, MachineRepresentation::kTagged);
    242   Label out(this);
    243 
    244   // Load lastIndex.
    245   Variable var_lastindex(this, MachineRepresentation::kTagged);
    246   {
    247     Node* const regexp_lastindex = LoadLastIndex(context, regexp, is_fastpath);
    248     var_lastindex.Bind(regexp_lastindex);
    249 
    250     if (is_fastpath) {
    251       // ToLength on a positive smi is a nop and can be skipped.
    252       CSA_ASSERT(this, TaggedIsPositiveSmi(regexp_lastindex));
    253     } else {
    254       // Omit ToLength if lastindex is a non-negative smi.
    255       Label call_tolength(this, Label::kDeferred), next(this);
    256       Branch(TaggedIsPositiveSmi(regexp_lastindex), &next, &call_tolength);
    257 
    258       Bind(&call_tolength);
    259       {
    260         Callable tolength_callable = CodeFactory::ToLength(isolate);
    261         var_lastindex.Bind(
    262             CallStub(tolength_callable, context, regexp_lastindex));
    263         Goto(&next);
    264       }
    265 
    266       Bind(&next);
    267     }
    268   }
    269 
    270   // Check whether the regexp is global or sticky, which determines whether we
    271   // update last index later on.
    272   Node* const flags = LoadObjectField(regexp, JSRegExp::kFlagsOffset);
    273   Node* const is_global_or_sticky = WordAnd(
    274       SmiUntag(flags), IntPtrConstant(JSRegExp::kGlobal | JSRegExp::kSticky));
    275   Node* const should_update_last_index =
    276       WordNotEqual(is_global_or_sticky, int_zero);
    277 
    278   // Grab and possibly update last index.
    279   Label run_exec(this);
    280   {
    281     Label if_doupdate(this), if_dontupdate(this);
    282     Branch(should_update_last_index, &if_doupdate, &if_dontupdate);
    283 
    284     Bind(&if_doupdate);
    285     {
    286       Node* const lastindex = var_lastindex.value();
    287 
    288       Label if_isoob(this, Label::kDeferred);
    289       GotoIfNot(TaggedIsSmi(lastindex), &if_isoob);
    290       Node* const string_length = LoadStringLength(string);
    291       GotoIfNot(SmiLessThanOrEqual(lastindex, string_length), &if_isoob);
    292       Goto(&run_exec);
    293 
    294       Bind(&if_isoob);
    295       {
    296         StoreLastIndex(context, regexp, smi_zero, is_fastpath);
    297         var_result.Bind(null);
    298         Goto(if_didnotmatch);
    299       }
    300     }
    301 
    302     Bind(&if_dontupdate);
    303     {
    304       var_lastindex.Bind(smi_zero);
    305       Goto(&run_exec);
    306     }
    307   }
    308 
    309   Node* match_indices;
    310   Label successful_match(this);
    311   Bind(&run_exec);
    312   {
    313     // Get last match info from the context.
    314     Node* const native_context = LoadNativeContext(context);
    315     Node* const last_match_info = LoadContextElement(
    316         native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX);
    317 
    318     // Call the exec stub.
    319     Callable exec_callable = CodeFactory::RegExpExec(isolate);
    320     match_indices = CallStub(exec_callable, context, regexp, string,
    321                              var_lastindex.value(), last_match_info);
    322     var_result.Bind(match_indices);
    323 
    324     // {match_indices} is either null or the RegExpMatchInfo array.
    325     // Return early if exec failed, possibly updating last index.
    326     GotoIfNot(WordEqual(match_indices, null), &successful_match);
    327 
    328     GotoIfNot(should_update_last_index, if_didnotmatch);
    329 
    330     StoreLastIndex(context, regexp, smi_zero, is_fastpath);
    331     Goto(if_didnotmatch);
    332   }
    333 
    334   Bind(&successful_match);
    335   {
    336     GotoIfNot(should_update_last_index, &out);
    337 
    338     // Update the new last index from {match_indices}.
    339     Node* const new_lastindex = LoadFixedArrayElement(
    340         match_indices, RegExpMatchInfo::kFirstCaptureIndex + 1);
    341 
    342     StoreLastIndex(context, regexp, new_lastindex, is_fastpath);
    343     Goto(&out);
    344   }
    345 
    346   Bind(&out);
    347   return var_result.value();
    348 }
    349 
    350 // ES#sec-regexp.prototype.exec
    351 // RegExp.prototype.exec ( string )
    352 Node* RegExpBuiltinsAssembler::RegExpPrototypeExecBody(Node* const context,
    353                                                        Node* const regexp,
    354                                                        Node* const string,
    355                                                        const bool is_fastpath) {
    356   Node* const null = NullConstant();
    357 
    358   Variable var_result(this, MachineRepresentation::kTagged);
    359 
    360   Label if_didnotmatch(this), out(this);
    361   Node* const indices_or_null = RegExpPrototypeExecBodyWithoutResult(
    362       context, regexp, string, &if_didnotmatch, is_fastpath);
    363 
    364   // Successful match.
    365   {
    366     Node* const match_indices = indices_or_null;
    367     Node* const result =
    368         ConstructNewResultFromMatchInfo(context, regexp, match_indices, string);
    369     var_result.Bind(result);
    370     Goto(&out);
    371   }
    372 
    373   Bind(&if_didnotmatch);
    374   {
    375     var_result.Bind(null);
    376     Goto(&out);
    377   }
    378 
    379   Bind(&out);
    380   return var_result.value();
    381 }
    382 
    383 Node* RegExpBuiltinsAssembler::ThrowIfNotJSReceiver(
    384     Node* context, Node* maybe_receiver, MessageTemplate::Template msg_template,
    385     char const* method_name) {
    386   Label out(this), throw_exception(this, Label::kDeferred);
    387   Variable var_value_map(this, MachineRepresentation::kTagged);
    388 
    389   GotoIf(TaggedIsSmi(maybe_receiver), &throw_exception);
    390 
    391   // Load the instance type of the {value}.
    392   var_value_map.Bind(LoadMap(maybe_receiver));
    393   Node* const value_instance_type = LoadMapInstanceType(var_value_map.value());
    394 
    395   Branch(IsJSReceiverInstanceType(value_instance_type), &out, &throw_exception);
    396 
    397   // The {value} is not a compatible receiver for this method.
    398   Bind(&throw_exception);
    399   {
    400     Node* const message_id = SmiConstant(Smi::FromInt(msg_template));
    401     Node* const method_name_str = HeapConstant(
    402         isolate()->factory()->NewStringFromAsciiChecked(method_name, TENURED));
    403 
    404     Callable callable = CodeFactory::ToString(isolate());
    405     Node* const value_str = CallStub(callable, context, maybe_receiver);
    406 
    407     CallRuntime(Runtime::kThrowTypeError, context, message_id, method_name_str,
    408                 value_str);
    409     Unreachable();
    410   }
    411 
    412   Bind(&out);
    413   return var_value_map.value();
    414 }
    415 
    416 Node* RegExpBuiltinsAssembler::IsInitialRegExpMap(Node* context, Node* object,
    417                                                   Node* map) {
    418   Label out(this);
    419   Variable var_result(this, MachineRepresentation::kWord32);
    420 
    421   Node* const native_context = LoadNativeContext(context);
    422   Node* const regexp_fun =
    423       LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX);
    424   Node* const initial_map =
    425       LoadObjectField(regexp_fun, JSFunction::kPrototypeOrInitialMapOffset);
    426   Node* const has_initialmap = WordEqual(map, initial_map);
    427 
    428   var_result.Bind(has_initialmap);
    429   GotoIfNot(has_initialmap, &out);
    430 
    431   // The smi check is required to omit ToLength(lastIndex) calls with possible
    432   // user-code execution on the fast path.
    433   Node* const last_index = FastLoadLastIndex(object);
    434   var_result.Bind(TaggedIsPositiveSmi(last_index));
    435   Goto(&out);
    436 
    437   Bind(&out);
    438   return var_result.value();
    439 }
    440 
    441 // RegExp fast path implementations rely on unmodified JSRegExp instances.
    442 // We use a fairly coarse granularity for this and simply check whether both
    443 // the regexp itself is unmodified (i.e. its map has not changed), its
    444 // prototype is unmodified, and lastIndex is a non-negative smi.
    445 void RegExpBuiltinsAssembler::BranchIfFastRegExp(Node* const context,
    446                                                  Node* const object,
    447                                                  Node* const map,
    448                                                  Label* const if_isunmodified,
    449                                                  Label* const if_ismodified) {
    450   CSA_ASSERT(this, WordEqual(LoadMap(object), map));
    451 
    452   // TODO(ishell): Update this check once map changes for constant field
    453   // tracking are landing.
    454 
    455   Node* const native_context = LoadNativeContext(context);
    456   Node* const regexp_fun =
    457       LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX);
    458   Node* const initial_map =
    459       LoadObjectField(regexp_fun, JSFunction::kPrototypeOrInitialMapOffset);
    460   Node* const has_initialmap = WordEqual(map, initial_map);
    461 
    462   GotoIfNot(has_initialmap, if_ismodified);
    463 
    464   Node* const initial_proto_initial_map =
    465       LoadContextElement(native_context, Context::REGEXP_PROTOTYPE_MAP_INDEX);
    466   Node* const proto_map = LoadMap(LoadMapPrototype(map));
    467   Node* const proto_has_initialmap =
    468       WordEqual(proto_map, initial_proto_initial_map);
    469 
    470   GotoIfNot(proto_has_initialmap, if_ismodified);
    471 
    472   // The smi check is required to omit ToLength(lastIndex) calls with possible
    473   // user-code execution on the fast path.
    474   Node* const last_index = FastLoadLastIndex(object);
    475   Branch(TaggedIsPositiveSmi(last_index), if_isunmodified, if_ismodified);
    476 }
    477 
    478 Node* RegExpBuiltinsAssembler::IsFastRegExpMap(Node* const context,
    479                                                Node* const object,
    480                                                Node* const map) {
    481   Label yup(this), nope(this), out(this);
    482   Variable var_result(this, MachineRepresentation::kWord32);
    483 
    484   BranchIfFastRegExp(context, object, map, &yup, &nope);
    485 
    486   Bind(&yup);
    487   var_result.Bind(Int32Constant(1));
    488   Goto(&out);
    489 
    490   Bind(&nope);
    491   var_result.Bind(Int32Constant(0));
    492   Goto(&out);
    493 
    494   Bind(&out);
    495   return var_result.value();
    496 }
    497 
    498 void RegExpBuiltinsAssembler::BranchIfFastRegExpResult(Node* context, Node* map,
    499                                                        Label* if_isunmodified,
    500                                                        Label* if_ismodified) {
    501   Node* const native_context = LoadNativeContext(context);
    502   Node* const initial_regexp_result_map =
    503       LoadContextElement(native_context, Context::REGEXP_RESULT_MAP_INDEX);
    504 
    505   Branch(WordEqual(map, initial_regexp_result_map), if_isunmodified,
    506          if_ismodified);
    507 }
    508 
    509 // ES#sec-regexp.prototype.exec
    510 // RegExp.prototype.exec ( string )
    511 TF_BUILTIN(RegExpPrototypeExec, RegExpBuiltinsAssembler) {
    512   Node* const maybe_receiver = Parameter(0);
    513   Node* const maybe_string = Parameter(1);
    514   Node* const context = Parameter(4);
    515 
    516   // Ensure {maybe_receiver} is a JSRegExp.
    517   ThrowIfNotInstanceType(context, maybe_receiver, JS_REGEXP_TYPE,
    518                          "RegExp.prototype.exec");
    519   Node* const receiver = maybe_receiver;
    520 
    521   // Convert {maybe_string} to a String.
    522   Node* const string = ToString(context, maybe_string);
    523 
    524   Label if_isfastpath(this), if_isslowpath(this);
    525   Branch(IsInitialRegExpMap(context, receiver, LoadMap(receiver)),
    526          &if_isfastpath, &if_isslowpath);
    527 
    528   Bind(&if_isfastpath);
    529   {
    530     Node* const result =
    531         RegExpPrototypeExecBody(context, receiver, string, true);
    532     Return(result);
    533   }
    534 
    535   Bind(&if_isslowpath);
    536   {
    537     Node* const result =
    538         RegExpPrototypeExecBody(context, receiver, string, false);
    539     Return(result);
    540   }
    541 }
    542 
    543 Node* RegExpBuiltinsAssembler::FlagsGetter(Node* const context,
    544                                            Node* const regexp,
    545                                            bool is_fastpath) {
    546   Isolate* isolate = this->isolate();
    547 
    548   Node* const int_zero = IntPtrConstant(0);
    549   Node* const int_one = IntPtrConstant(1);
    550   Variable var_length(this, MachineType::PointerRepresentation(), int_zero);
    551   Variable var_flags(this, MachineType::PointerRepresentation());
    552 
    553   // First, count the number of characters we will need and check which flags
    554   // are set.
    555 
    556   if (is_fastpath) {
    557     // Refer to JSRegExp's flag property on the fast-path.
    558     Node* const flags_smi = LoadObjectField(regexp, JSRegExp::kFlagsOffset);
    559     Node* const flags_intptr = SmiUntag(flags_smi);
    560     var_flags.Bind(flags_intptr);
    561 
    562 #define CASE_FOR_FLAG(FLAG)                                  \
    563   do {                                                       \
    564     Label next(this);                                        \
    565     GotoIfNot(IsSetWord(flags_intptr, FLAG), &next);         \
    566     var_length.Bind(IntPtrAdd(var_length.value(), int_one)); \
    567     Goto(&next);                                             \
    568     Bind(&next);                                             \
    569   } while (false)
    570 
    571     CASE_FOR_FLAG(JSRegExp::kGlobal);
    572     CASE_FOR_FLAG(JSRegExp::kIgnoreCase);
    573     CASE_FOR_FLAG(JSRegExp::kMultiline);
    574     CASE_FOR_FLAG(JSRegExp::kUnicode);
    575     CASE_FOR_FLAG(JSRegExp::kSticky);
    576 #undef CASE_FOR_FLAG
    577   } else {
    578     DCHECK(!is_fastpath);
    579 
    580     // Fall back to GetProperty stub on the slow-path.
    581     var_flags.Bind(int_zero);
    582 
    583     Callable getproperty_callable = CodeFactory::GetProperty(isolate);
    584 
    585 #define CASE_FOR_FLAG(NAME, FLAG)                                             \
    586   do {                                                                        \
    587     Label next(this);                                                         \
    588     Node* const name =                                                        \
    589         HeapConstant(isolate->factory()->InternalizeUtf8String(NAME));        \
    590     Node* const flag = CallStub(getproperty_callable, context, regexp, name); \
    591     Label if_isflagset(this);                                                 \
    592     BranchIfToBooleanIsTrue(flag, &if_isflagset, &next);                      \
    593     Bind(&if_isflagset);                                                      \
    594     var_length.Bind(IntPtrAdd(var_length.value(), int_one));                  \
    595     var_flags.Bind(WordOr(var_flags.value(), IntPtrConstant(FLAG)));          \
    596     Goto(&next);                                                              \
    597     Bind(&next);                                                              \
    598   } while (false)
    599 
    600     CASE_FOR_FLAG("global", JSRegExp::kGlobal);
    601     CASE_FOR_FLAG("ignoreCase", JSRegExp::kIgnoreCase);
    602     CASE_FOR_FLAG("multiline", JSRegExp::kMultiline);
    603     CASE_FOR_FLAG("unicode", JSRegExp::kUnicode);
    604     CASE_FOR_FLAG("sticky", JSRegExp::kSticky);
    605 #undef CASE_FOR_FLAG
    606   }
    607 
    608   // Allocate a string of the required length and fill it with the corresponding
    609   // char for each set flag.
    610 
    611   {
    612     Node* const result = AllocateSeqOneByteString(context, var_length.value());
    613     Node* const flags_intptr = var_flags.value();
    614 
    615     Variable var_offset(
    616         this, MachineType::PointerRepresentation(),
    617         IntPtrConstant(SeqOneByteString::kHeaderSize - kHeapObjectTag));
    618 
    619 #define CASE_FOR_FLAG(FLAG, CHAR)                              \
    620   do {                                                         \
    621     Label next(this);                                          \
    622     GotoIfNot(IsSetWord(flags_intptr, FLAG), &next);           \
    623     Node* const value = Int32Constant(CHAR);                   \
    624     StoreNoWriteBarrier(MachineRepresentation::kWord8, result, \
    625                         var_offset.value(), value);            \
    626     var_offset.Bind(IntPtrAdd(var_offset.value(), int_one));   \
    627     Goto(&next);                                               \
    628     Bind(&next);                                               \
    629   } while (false)
    630 
    631     CASE_FOR_FLAG(JSRegExp::kGlobal, 'g');
    632     CASE_FOR_FLAG(JSRegExp::kIgnoreCase, 'i');
    633     CASE_FOR_FLAG(JSRegExp::kMultiline, 'm');
    634     CASE_FOR_FLAG(JSRegExp::kUnicode, 'u');
    635     CASE_FOR_FLAG(JSRegExp::kSticky, 'y');
    636 #undef CASE_FOR_FLAG
    637 
    638     return result;
    639   }
    640 }
    641 
    642 // ES#sec-isregexp IsRegExp ( argument )
    643 Node* RegExpBuiltinsAssembler::IsRegExp(Node* const context,
    644                                         Node* const maybe_receiver) {
    645   Label out(this), if_isregexp(this);
    646 
    647   Variable var_result(this, MachineRepresentation::kWord32, Int32Constant(0));
    648 
    649   GotoIf(TaggedIsSmi(maybe_receiver), &out);
    650   GotoIfNot(IsJSReceiver(maybe_receiver), &out);
    651 
    652   Node* const receiver = maybe_receiver;
    653 
    654   // Check @@match.
    655   {
    656     Callable getproperty_callable = CodeFactory::GetProperty(isolate());
    657     Node* const name = HeapConstant(isolate()->factory()->match_symbol());
    658     Node* const value = CallStub(getproperty_callable, context, receiver, name);
    659 
    660     Label match_isundefined(this), match_isnotundefined(this);
    661     Branch(IsUndefined(value), &match_isundefined, &match_isnotundefined);
    662 
    663     Bind(&match_isundefined);
    664     Branch(HasInstanceType(receiver, JS_REGEXP_TYPE), &if_isregexp, &out);
    665 
    666     Bind(&match_isnotundefined);
    667     BranchIfToBooleanIsTrue(value, &if_isregexp, &out);
    668   }
    669 
    670   Bind(&if_isregexp);
    671   var_result.Bind(Int32Constant(1));
    672   Goto(&out);
    673 
    674   Bind(&out);
    675   return var_result.value();
    676 }
    677 
    678 // ES#sec-regexpinitialize
    679 // Runtime Semantics: RegExpInitialize ( obj, pattern, flags )
    680 Node* RegExpBuiltinsAssembler::RegExpInitialize(Node* const context,
    681                                                 Node* const regexp,
    682                                                 Node* const maybe_pattern,
    683                                                 Node* const maybe_flags) {
    684   // Normalize pattern.
    685   Node* const pattern =
    686       Select(IsUndefined(maybe_pattern), [=] { return EmptyStringConstant(); },
    687              [=] { return ToString(context, maybe_pattern); },
    688              MachineRepresentation::kTagged);
    689 
    690   // Normalize flags.
    691   Node* const flags =
    692       Select(IsUndefined(maybe_flags), [=] { return EmptyStringConstant(); },
    693              [=] { return ToString(context, maybe_flags); },
    694              MachineRepresentation::kTagged);
    695 
    696   // Initialize.
    697 
    698   return CallRuntime(Runtime::kRegExpInitializeAndCompile, context, regexp,
    699                      pattern, flags);
    700 }
    701 
    702 TF_BUILTIN(RegExpPrototypeFlagsGetter, RegExpBuiltinsAssembler) {
    703   Node* const maybe_receiver = Parameter(0);
    704   Node* const context = Parameter(3);
    705 
    706   Node* const map = ThrowIfNotJSReceiver(context, maybe_receiver,
    707                                          MessageTemplate::kRegExpNonObject,
    708                                          "RegExp.prototype.flags");
    709   Node* const receiver = maybe_receiver;
    710 
    711   Label if_isfastpath(this), if_isslowpath(this, Label::kDeferred);
    712   Branch(IsInitialRegExpMap(context, receiver, map), &if_isfastpath,
    713          &if_isslowpath);
    714 
    715   Bind(&if_isfastpath);
    716   Return(FlagsGetter(context, receiver, true));
    717 
    718   Bind(&if_isslowpath);
    719   Return(FlagsGetter(context, receiver, false));
    720 }
    721 
    722 // ES#sec-regexp-pattern-flags
    723 // RegExp ( pattern, flags )
    724 TF_BUILTIN(RegExpConstructor, RegExpBuiltinsAssembler) {
    725   Node* const pattern = Parameter(1);
    726   Node* const flags = Parameter(2);
    727   Node* const new_target = Parameter(3);
    728   Node* const context = Parameter(5);
    729 
    730   Isolate* isolate = this->isolate();
    731 
    732   Variable var_flags(this, MachineRepresentation::kTagged, flags);
    733   Variable var_pattern(this, MachineRepresentation::kTagged, pattern);
    734   Variable var_new_target(this, MachineRepresentation::kTagged, new_target);
    735 
    736   Node* const native_context = LoadNativeContext(context);
    737   Node* const regexp_function =
    738       LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX);
    739 
    740   Node* const pattern_is_regexp = IsRegExp(context, pattern);
    741 
    742   {
    743     Label next(this);
    744 
    745     GotoIfNot(IsUndefined(new_target), &next);
    746     var_new_target.Bind(regexp_function);
    747 
    748     GotoIfNot(pattern_is_regexp, &next);
    749     GotoIfNot(IsUndefined(flags), &next);
    750 
    751     Callable getproperty_callable = CodeFactory::GetProperty(isolate);
    752     Node* const name = HeapConstant(isolate->factory()->constructor_string());
    753     Node* const value = CallStub(getproperty_callable, context, pattern, name);
    754 
    755     GotoIfNot(WordEqual(value, regexp_function), &next);
    756     Return(pattern);
    757 
    758     Bind(&next);
    759   }
    760 
    761   {
    762     Label next(this), if_patternisfastregexp(this),
    763         if_patternisslowregexp(this);
    764     GotoIf(TaggedIsSmi(pattern), &next);
    765 
    766     GotoIf(HasInstanceType(pattern, JS_REGEXP_TYPE), &if_patternisfastregexp);
    767 
    768     Branch(pattern_is_regexp, &if_patternisslowregexp, &next);
    769 
    770     Bind(&if_patternisfastregexp);
    771     {
    772       Node* const source = LoadObjectField(pattern, JSRegExp::kSourceOffset);
    773       var_pattern.Bind(source);
    774 
    775       {
    776         Label inner_next(this);
    777         GotoIfNot(IsUndefined(flags), &inner_next);
    778 
    779         Node* const value = FlagsGetter(context, pattern, true);
    780         var_flags.Bind(value);
    781         Goto(&inner_next);
    782 
    783         Bind(&inner_next);
    784       }
    785 
    786       Goto(&next);
    787     }
    788 
    789     Bind(&if_patternisslowregexp);
    790     {
    791       Callable getproperty_callable = CodeFactory::GetProperty(isolate);
    792 
    793       {
    794         Node* const name = HeapConstant(isolate->factory()->source_string());
    795         Node* const value =
    796             CallStub(getproperty_callable, context, pattern, name);
    797         var_pattern.Bind(value);
    798       }
    799 
    800       {
    801         Label inner_next(this);
    802         GotoIfNot(IsUndefined(flags), &inner_next);
    803 
    804         Node* const name = HeapConstant(isolate->factory()->flags_string());
    805         Node* const value =
    806             CallStub(getproperty_callable, context, pattern, name);
    807         var_flags.Bind(value);
    808         Goto(&inner_next);
    809 
    810         Bind(&inner_next);
    811       }
    812 
    813       Goto(&next);
    814     }
    815 
    816     Bind(&next);
    817   }
    818 
    819   // Allocate.
    820 
    821   Variable var_regexp(this, MachineRepresentation::kTagged);
    822   {
    823     Label allocate_jsregexp(this), allocate_generic(this, Label::kDeferred),
    824         next(this);
    825     Branch(WordEqual(var_new_target.value(), regexp_function),
    826            &allocate_jsregexp, &allocate_generic);
    827 
    828     Bind(&allocate_jsregexp);
    829     {
    830       Node* const initial_map = LoadObjectField(
    831           regexp_function, JSFunction::kPrototypeOrInitialMapOffset);
    832       Node* const regexp = AllocateJSObjectFromMap(initial_map);
    833       var_regexp.Bind(regexp);
    834       Goto(&next);
    835     }
    836 
    837     Bind(&allocate_generic);
    838     {
    839       ConstructorBuiltinsAssembler constructor_assembler(this->state());
    840       Node* const regexp = constructor_assembler.EmitFastNewObject(
    841           context, regexp_function, var_new_target.value());
    842       var_regexp.Bind(regexp);
    843       Goto(&next);
    844     }
    845 
    846     Bind(&next);
    847   }
    848 
    849   Node* const result = RegExpInitialize(context, var_regexp.value(),
    850                                         var_pattern.value(), var_flags.value());
    851   Return(result);
    852 }
    853 
    854 // ES#sec-regexp.prototype.compile
    855 // RegExp.prototype.compile ( pattern, flags )
    856 TF_BUILTIN(RegExpPrototypeCompile, RegExpBuiltinsAssembler) {
    857   Node* const maybe_receiver = Parameter(0);
    858   Node* const maybe_pattern = Parameter(1);
    859   Node* const maybe_flags = Parameter(2);
    860   Node* const context = Parameter(5);
    861 
    862   ThrowIfNotInstanceType(context, maybe_receiver, JS_REGEXP_TYPE,
    863                          "RegExp.prototype.compile");
    864   Node* const receiver = maybe_receiver;
    865 
    866   Variable var_flags(this, MachineRepresentation::kTagged, maybe_flags);
    867   Variable var_pattern(this, MachineRepresentation::kTagged, maybe_pattern);
    868 
    869   // Handle a JSRegExp pattern.
    870   {
    871     Label next(this);
    872 
    873     GotoIf(TaggedIsSmi(maybe_pattern), &next);
    874     GotoIfNot(HasInstanceType(maybe_pattern, JS_REGEXP_TYPE), &next);
    875 
    876     Node* const pattern = maybe_pattern;
    877 
    878     // {maybe_flags} must be undefined in this case, otherwise throw.
    879     {
    880       Label next(this);
    881       GotoIf(IsUndefined(maybe_flags), &next);
    882 
    883       Node* const message_id = SmiConstant(MessageTemplate::kRegExpFlags);
    884       TailCallRuntime(Runtime::kThrowTypeError, context, message_id);
    885 
    886       Bind(&next);
    887     }
    888 
    889     Node* const new_flags = FlagsGetter(context, pattern, true);
    890     Node* const new_pattern = LoadObjectField(pattern, JSRegExp::kSourceOffset);
    891 
    892     var_flags.Bind(new_flags);
    893     var_pattern.Bind(new_pattern);
    894 
    895     Goto(&next);
    896     Bind(&next);
    897   }
    898 
    899   Node* const result = RegExpInitialize(context, receiver, var_pattern.value(),
    900                                         var_flags.value());
    901   Return(result);
    902 }
    903 
    904 // ES6 21.2.5.10.
    905 TF_BUILTIN(RegExpPrototypeSourceGetter, RegExpBuiltinsAssembler) {
    906   Node* const receiver = Parameter(0);
    907   Node* const context = Parameter(3);
    908 
    909   // Check whether we have an unmodified regexp instance.
    910   Label if_isjsregexp(this), if_isnotjsregexp(this, Label::kDeferred);
    911 
    912   GotoIf(TaggedIsSmi(receiver), &if_isnotjsregexp);
    913   Branch(HasInstanceType(receiver, JS_REGEXP_TYPE), &if_isjsregexp,
    914          &if_isnotjsregexp);
    915 
    916   Bind(&if_isjsregexp);
    917   {
    918     Node* const source = LoadObjectField(receiver, JSRegExp::kSourceOffset);
    919     Return(source);
    920   }
    921 
    922   Bind(&if_isnotjsregexp);
    923   {
    924     Isolate* isolate = this->isolate();
    925     Node* const native_context = LoadNativeContext(context);
    926     Node* const regexp_fun =
    927         LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX);
    928     Node* const initial_map =
    929         LoadObjectField(regexp_fun, JSFunction::kPrototypeOrInitialMapOffset);
    930     Node* const initial_prototype = LoadMapPrototype(initial_map);
    931 
    932     Label if_isprototype(this), if_isnotprototype(this);
    933     Branch(WordEqual(receiver, initial_prototype), &if_isprototype,
    934            &if_isnotprototype);
    935 
    936     Bind(&if_isprototype);
    937     {
    938       const int counter = v8::Isolate::kRegExpPrototypeSourceGetter;
    939       Node* const counter_smi = SmiConstant(counter);
    940       CallRuntime(Runtime::kIncrementUseCounter, context, counter_smi);
    941 
    942       Node* const result =
    943           HeapConstant(isolate->factory()->NewStringFromAsciiChecked("(?:)"));
    944       Return(result);
    945     }
    946 
    947     Bind(&if_isnotprototype);
    948     {
    949       Node* const message_id =
    950           SmiConstant(Smi::FromInt(MessageTemplate::kRegExpNonRegExp));
    951       Node* const method_name_str =
    952           HeapConstant(isolate->factory()->NewStringFromAsciiChecked(
    953               "RegExp.prototype.source"));
    954       TailCallRuntime(Runtime::kThrowTypeError, context, message_id,
    955                       method_name_str);
    956     }
    957   }
    958 }
    959 
    960 BUILTIN(RegExpPrototypeToString) {
    961   HandleScope scope(isolate);
    962   CHECK_RECEIVER(JSReceiver, recv, "RegExp.prototype.toString");
    963 
    964   if (*recv == isolate->regexp_function()->prototype()) {
    965     isolate->CountUsage(v8::Isolate::kRegExpPrototypeToString);
    966   }
    967 
    968   IncrementalStringBuilder builder(isolate);
    969 
    970   builder.AppendCharacter('/');
    971   {
    972     Handle<Object> source;
    973     ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
    974         isolate, source,
    975         JSReceiver::GetProperty(recv, isolate->factory()->source_string()));
    976     Handle<String> source_str;
    977     ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, source_str,
    978                                        Object::ToString(isolate, source));
    979     builder.AppendString(source_str);
    980   }
    981 
    982   builder.AppendCharacter('/');
    983   {
    984     Handle<Object> flags;
    985     ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
    986         isolate, flags,
    987         JSReceiver::GetProperty(recv, isolate->factory()->flags_string()));
    988     Handle<String> flags_str;
    989     ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, flags_str,
    990                                        Object::ToString(isolate, flags));
    991     builder.AppendString(flags_str);
    992   }
    993 
    994   RETURN_RESULT_OR_FAILURE(isolate, builder.Finish());
    995 }
    996 
    997 // Fast-path implementation for flag checks on an unmodified JSRegExp instance.
    998 Node* RegExpBuiltinsAssembler::FastFlagGetter(Node* const regexp,
    999                                               JSRegExp::Flag flag) {
   1000   Node* const smi_zero = SmiConstant(Smi::kZero);
   1001   Node* const flags = LoadObjectField(regexp, JSRegExp::kFlagsOffset);
   1002   Node* const mask = SmiConstant(Smi::FromInt(flag));
   1003   Node* const is_flag_set = WordNotEqual(SmiAnd(flags, mask), smi_zero);
   1004 
   1005   return is_flag_set;
   1006 }
   1007 
   1008 // Load through the GetProperty stub.
   1009 Node* RegExpBuiltinsAssembler::SlowFlagGetter(Node* const context,
   1010                                               Node* const regexp,
   1011                                               JSRegExp::Flag flag) {
   1012   Factory* factory = isolate()->factory();
   1013 
   1014   Label out(this);
   1015   Variable var_result(this, MachineRepresentation::kWord32);
   1016 
   1017   Node* name;
   1018 
   1019   switch (flag) {
   1020     case JSRegExp::kGlobal:
   1021       name = HeapConstant(factory->global_string());
   1022       break;
   1023     case JSRegExp::kIgnoreCase:
   1024       name = HeapConstant(factory->ignoreCase_string());
   1025       break;
   1026     case JSRegExp::kMultiline:
   1027       name = HeapConstant(factory->multiline_string());
   1028       break;
   1029     case JSRegExp::kSticky:
   1030       name = HeapConstant(factory->sticky_string());
   1031       break;
   1032     case JSRegExp::kUnicode:
   1033       name = HeapConstant(factory->unicode_string());
   1034       break;
   1035     default:
   1036       UNREACHABLE();
   1037   }
   1038 
   1039   Callable getproperty_callable = CodeFactory::GetProperty(isolate());
   1040   Node* const value = CallStub(getproperty_callable, context, regexp, name);
   1041 
   1042   Label if_true(this), if_false(this);
   1043   BranchIfToBooleanIsTrue(value, &if_true, &if_false);
   1044 
   1045   Bind(&if_true);
   1046   {
   1047     var_result.Bind(Int32Constant(1));
   1048     Goto(&out);
   1049   }
   1050 
   1051   Bind(&if_false);
   1052   {
   1053     var_result.Bind(Int32Constant(0));
   1054     Goto(&out);
   1055   }
   1056 
   1057   Bind(&out);
   1058   return var_result.value();
   1059 }
   1060 
   1061 Node* RegExpBuiltinsAssembler::FlagGetter(Node* const context,
   1062                                           Node* const regexp,
   1063                                           JSRegExp::Flag flag,
   1064                                           bool is_fastpath) {
   1065   return is_fastpath ? FastFlagGetter(regexp, flag)
   1066                      : SlowFlagGetter(context, regexp, flag);
   1067 }
   1068 
   1069 void RegExpBuiltinsAssembler::FlagGetter(JSRegExp::Flag flag,
   1070                                          v8::Isolate::UseCounterFeature counter,
   1071                                          const char* method_name) {
   1072   Node* const receiver = Parameter(0);
   1073   Node* const context = Parameter(3);
   1074 
   1075   Isolate* isolate = this->isolate();
   1076 
   1077   // Check whether we have an unmodified regexp instance.
   1078   Label if_isunmodifiedjsregexp(this),
   1079       if_isnotunmodifiedjsregexp(this, Label::kDeferred);
   1080 
   1081   GotoIf(TaggedIsSmi(receiver), &if_isnotunmodifiedjsregexp);
   1082 
   1083   Node* const receiver_map = LoadMap(receiver);
   1084   Node* const instance_type = LoadMapInstanceType(receiver_map);
   1085 
   1086   Branch(Word32Equal(instance_type, Int32Constant(JS_REGEXP_TYPE)),
   1087          &if_isunmodifiedjsregexp, &if_isnotunmodifiedjsregexp);
   1088 
   1089   Bind(&if_isunmodifiedjsregexp);
   1090   {
   1091     // Refer to JSRegExp's flag property on the fast-path.
   1092     Node* const is_flag_set = FastFlagGetter(receiver, flag);
   1093     Return(SelectBooleanConstant(is_flag_set));
   1094   }
   1095 
   1096   Bind(&if_isnotunmodifiedjsregexp);
   1097   {
   1098     Node* const native_context = LoadNativeContext(context);
   1099     Node* const regexp_fun =
   1100         LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX);
   1101     Node* const initial_map =
   1102         LoadObjectField(regexp_fun, JSFunction::kPrototypeOrInitialMapOffset);
   1103     Node* const initial_prototype = LoadMapPrototype(initial_map);
   1104 
   1105     Label if_isprototype(this), if_isnotprototype(this);
   1106     Branch(WordEqual(receiver, initial_prototype), &if_isprototype,
   1107            &if_isnotprototype);
   1108 
   1109     Bind(&if_isprototype);
   1110     {
   1111       Node* const counter_smi = SmiConstant(Smi::FromInt(counter));
   1112       CallRuntime(Runtime::kIncrementUseCounter, context, counter_smi);
   1113       Return(UndefinedConstant());
   1114     }
   1115 
   1116     Bind(&if_isnotprototype);
   1117     {
   1118       Node* const message_id =
   1119           SmiConstant(Smi::FromInt(MessageTemplate::kRegExpNonRegExp));
   1120       Node* const method_name_str = HeapConstant(
   1121           isolate->factory()->NewStringFromAsciiChecked(method_name));
   1122       CallRuntime(Runtime::kThrowTypeError, context, message_id,
   1123                   method_name_str);
   1124       Unreachable();
   1125     }
   1126   }
   1127 }
   1128 
   1129 // ES6 21.2.5.4.
   1130 TF_BUILTIN(RegExpPrototypeGlobalGetter, RegExpBuiltinsAssembler) {
   1131   FlagGetter(JSRegExp::kGlobal, v8::Isolate::kRegExpPrototypeOldFlagGetter,
   1132              "RegExp.prototype.global");
   1133 }
   1134 
   1135 // ES6 21.2.5.5.
   1136 TF_BUILTIN(RegExpPrototypeIgnoreCaseGetter, RegExpBuiltinsAssembler) {
   1137   FlagGetter(JSRegExp::kIgnoreCase, v8::Isolate::kRegExpPrototypeOldFlagGetter,
   1138              "RegExp.prototype.ignoreCase");
   1139 }
   1140 
   1141 // ES6 21.2.5.7.
   1142 TF_BUILTIN(RegExpPrototypeMultilineGetter, RegExpBuiltinsAssembler) {
   1143   FlagGetter(JSRegExp::kMultiline, v8::Isolate::kRegExpPrototypeOldFlagGetter,
   1144              "RegExp.prototype.multiline");
   1145 }
   1146 
   1147 // ES6 21.2.5.12.
   1148 TF_BUILTIN(RegExpPrototypeStickyGetter, RegExpBuiltinsAssembler) {
   1149   FlagGetter(JSRegExp::kSticky, v8::Isolate::kRegExpPrototypeStickyGetter,
   1150              "RegExp.prototype.sticky");
   1151 }
   1152 
   1153 // ES6 21.2.5.15.
   1154 TF_BUILTIN(RegExpPrototypeUnicodeGetter, RegExpBuiltinsAssembler) {
   1155   FlagGetter(JSRegExp::kUnicode, v8::Isolate::kRegExpPrototypeUnicodeGetter,
   1156              "RegExp.prototype.unicode");
   1157 }
   1158 
   1159 // The properties $1..$9 are the first nine capturing substrings of the last
   1160 // successful match, or ''.  The function RegExpMakeCaptureGetter will be
   1161 // called with indices from 1 to 9.
   1162 #define DEFINE_CAPTURE_GETTER(i)                        \
   1163   BUILTIN(RegExpCapture##i##Getter) {                   \
   1164     HandleScope scope(isolate);                         \
   1165     return *RegExpUtils::GenericCaptureGetter(          \
   1166         isolate, isolate->regexp_last_match_info(), i); \
   1167   }
   1168 DEFINE_CAPTURE_GETTER(1)
   1169 DEFINE_CAPTURE_GETTER(2)
   1170 DEFINE_CAPTURE_GETTER(3)
   1171 DEFINE_CAPTURE_GETTER(4)
   1172 DEFINE_CAPTURE_GETTER(5)
   1173 DEFINE_CAPTURE_GETTER(6)
   1174 DEFINE_CAPTURE_GETTER(7)
   1175 DEFINE_CAPTURE_GETTER(8)
   1176 DEFINE_CAPTURE_GETTER(9)
   1177 #undef DEFINE_CAPTURE_GETTER
   1178 
   1179 // The properties `input` and `$_` are aliases for each other.  When this
   1180 // value is set, the value it is set to is coerced to a string.
   1181 // Getter and setter for the input.
   1182 
   1183 BUILTIN(RegExpInputGetter) {
   1184   HandleScope scope(isolate);
   1185   Handle<Object> obj(isolate->regexp_last_match_info()->LastInput(), isolate);
   1186   return obj->IsUndefined(isolate) ? isolate->heap()->empty_string()
   1187                                    : String::cast(*obj);
   1188 }
   1189 
   1190 BUILTIN(RegExpInputSetter) {
   1191   HandleScope scope(isolate);
   1192   Handle<Object> value = args.atOrUndefined(isolate, 1);
   1193   Handle<String> str;
   1194   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, str,
   1195                                      Object::ToString(isolate, value));
   1196   isolate->regexp_last_match_info()->SetLastInput(*str);
   1197   return isolate->heap()->undefined_value();
   1198 }
   1199 
   1200 // Getters for the static properties lastMatch, lastParen, leftContext, and
   1201 // rightContext of the RegExp constructor.  The properties are computed based
   1202 // on the captures array of the last successful match and the subject string
   1203 // of the last successful match.
   1204 BUILTIN(RegExpLastMatchGetter) {
   1205   HandleScope scope(isolate);
   1206   return *RegExpUtils::GenericCaptureGetter(
   1207       isolate, isolate->regexp_last_match_info(), 0);
   1208 }
   1209 
   1210 BUILTIN(RegExpLastParenGetter) {
   1211   HandleScope scope(isolate);
   1212   Handle<RegExpMatchInfo> match_info = isolate->regexp_last_match_info();
   1213   const int length = match_info->NumberOfCaptureRegisters();
   1214   if (length <= 2) return isolate->heap()->empty_string();  // No captures.
   1215 
   1216   DCHECK_EQ(0, length % 2);
   1217   const int last_capture = (length / 2) - 1;
   1218 
   1219   // We match the SpiderMonkey behavior: return the substring defined by the
   1220   // last pair (after the first pair) of elements of the capture array even if
   1221   // it is empty.
   1222   return *RegExpUtils::GenericCaptureGetter(isolate, match_info, last_capture);
   1223 }
   1224 
   1225 BUILTIN(RegExpLeftContextGetter) {
   1226   HandleScope scope(isolate);
   1227   Handle<RegExpMatchInfo> match_info = isolate->regexp_last_match_info();
   1228   const int start_index = match_info->Capture(0);
   1229   Handle<String> last_subject(match_info->LastSubject());
   1230   return *isolate->factory()->NewSubString(last_subject, 0, start_index);
   1231 }
   1232 
   1233 BUILTIN(RegExpRightContextGetter) {
   1234   HandleScope scope(isolate);
   1235   Handle<RegExpMatchInfo> match_info = isolate->regexp_last_match_info();
   1236   const int start_index = match_info->Capture(1);
   1237   Handle<String> last_subject(match_info->LastSubject());
   1238   const int len = last_subject->length();
   1239   return *isolate->factory()->NewSubString(last_subject, start_index, len);
   1240 }
   1241 
   1242 // ES#sec-regexpexec Runtime Semantics: RegExpExec ( R, S )
   1243 Node* RegExpBuiltinsAssembler::RegExpExec(Node* context, Node* regexp,
   1244                                           Node* string) {
   1245   Isolate* isolate = this->isolate();
   1246 
   1247   Node* const null = NullConstant();
   1248 
   1249   Variable var_result(this, MachineRepresentation::kTagged);
   1250   Label out(this), if_isfastpath(this), if_isslowpath(this);
   1251 
   1252   Node* const map = LoadMap(regexp);
   1253   BranchIfFastRegExp(context, regexp, map, &if_isfastpath, &if_isslowpath);
   1254 
   1255   Bind(&if_isfastpath);
   1256   {
   1257     Node* const result = RegExpPrototypeExecBody(context, regexp, string, true);
   1258     var_result.Bind(result);
   1259     Goto(&out);
   1260   }
   1261 
   1262   Bind(&if_isslowpath);
   1263   {
   1264     // Take the slow path of fetching the exec property, calling it, and
   1265     // verifying its return value.
   1266 
   1267     // Get the exec property.
   1268     Node* const name = HeapConstant(isolate->factory()->exec_string());
   1269     Callable getproperty_callable = CodeFactory::GetProperty(isolate);
   1270     Node* const exec = CallStub(getproperty_callable, context, regexp, name);
   1271 
   1272     // Is {exec} callable?
   1273     Label if_iscallable(this), if_isnotcallable(this);
   1274 
   1275     GotoIf(TaggedIsSmi(exec), &if_isnotcallable);
   1276 
   1277     Node* const exec_map = LoadMap(exec);
   1278     Branch(IsCallableMap(exec_map), &if_iscallable, &if_isnotcallable);
   1279 
   1280     Bind(&if_iscallable);
   1281     {
   1282       Callable call_callable = CodeFactory::Call(isolate);
   1283       Node* const result = CallJS(call_callable, context, exec, regexp, string);
   1284 
   1285       var_result.Bind(result);
   1286       GotoIf(WordEqual(result, null), &out);
   1287 
   1288       ThrowIfNotJSReceiver(context, result,
   1289                            MessageTemplate::kInvalidRegExpExecResult, "unused");
   1290 
   1291       Goto(&out);
   1292     }
   1293 
   1294     Bind(&if_isnotcallable);
   1295     {
   1296       ThrowIfNotInstanceType(context, regexp, JS_REGEXP_TYPE,
   1297                              "RegExp.prototype.exec");
   1298 
   1299       Node* const result =
   1300           RegExpPrototypeExecBody(context, regexp, string, false);
   1301       var_result.Bind(result);
   1302       Goto(&out);
   1303     }
   1304   }
   1305 
   1306   Bind(&out);
   1307   return var_result.value();
   1308 }
   1309 
   1310 // ES#sec-regexp.prototype.test
   1311 // RegExp.prototype.test ( S )
   1312 TF_BUILTIN(RegExpPrototypeTest, RegExpBuiltinsAssembler) {
   1313   Node* const maybe_receiver = Parameter(0);
   1314   Node* const maybe_string = Parameter(1);
   1315   Node* const context = Parameter(4);
   1316 
   1317   // Ensure {maybe_receiver} is a JSReceiver.
   1318   ThrowIfNotJSReceiver(context, maybe_receiver,
   1319                        MessageTemplate::kIncompatibleMethodReceiver,
   1320                        "RegExp.prototype.test");
   1321   Node* const receiver = maybe_receiver;
   1322 
   1323   // Convert {maybe_string} to a String.
   1324   Node* const string = ToString(context, maybe_string);
   1325 
   1326   Label fast_path(this), slow_path(this);
   1327   BranchIfFastRegExp(context, receiver, LoadMap(receiver), &fast_path,
   1328                      &slow_path);
   1329 
   1330   Bind(&fast_path);
   1331   {
   1332     Label if_didnotmatch(this);
   1333     RegExpPrototypeExecBodyWithoutResult(context, receiver, string,
   1334                                          &if_didnotmatch, true);
   1335     Return(TrueConstant());
   1336 
   1337     Bind(&if_didnotmatch);
   1338     Return(FalseConstant());
   1339   }
   1340 
   1341   Bind(&slow_path);
   1342   {
   1343     // Call exec.
   1344     Node* const match_indices = RegExpExec(context, receiver, string);
   1345 
   1346     // Return true iff exec matched successfully.
   1347     Node* const result =
   1348         SelectBooleanConstant(WordNotEqual(match_indices, NullConstant()));
   1349     Return(result);
   1350   }
   1351 }
   1352 
   1353 Node* RegExpBuiltinsAssembler::AdvanceStringIndex(Node* const string,
   1354                                                   Node* const index,
   1355                                                   Node* const is_unicode,
   1356                                                   bool is_fastpath) {
   1357   CSA_ASSERT(this, IsHeapNumberMap(LoadReceiverMap(index)));
   1358   if (is_fastpath) CSA_ASSERT(this, TaggedIsPositiveSmi(index));
   1359 
   1360   // Default to last_index + 1.
   1361   Node* const index_plus_one = NumberInc(index);
   1362   Variable var_result(this, MachineRepresentation::kTagged, index_plus_one);
   1363 
   1364   // Advancing the index has some subtle issues involving the distinction
   1365   // between Smis and HeapNumbers. There's three cases:
   1366   // * {index} is a Smi, {index_plus_one} is a Smi. The standard case.
   1367   // * {index} is a Smi, {index_plus_one} overflows into a HeapNumber.
   1368   //   In this case we can return the result early, because
   1369   //   {index_plus_one} > {string}.length.
   1370   // * {index} is a HeapNumber, {index_plus_one} is a HeapNumber. This can only
   1371   //   occur when {index} is outside the Smi range since we normalize
   1372   //   explicitly. Again we can return early.
   1373   if (is_fastpath) {
   1374     // Must be in Smi range on the fast path. We control the value of {index}
   1375     // on all call-sites and can never exceed the length of the string.
   1376     STATIC_ASSERT(String::kMaxLength + 2 < Smi::kMaxValue);
   1377     CSA_ASSERT(this, TaggedIsPositiveSmi(index_plus_one));
   1378   }
   1379 
   1380   Label if_isunicode(this), out(this);
   1381   GotoIfNot(is_unicode, &out);
   1382 
   1383   // Keep this unconditional (even on the fast path) just to be safe.
   1384   Branch(TaggedIsPositiveSmi(index_plus_one), &if_isunicode, &out);
   1385 
   1386   Bind(&if_isunicode);
   1387   {
   1388     Node* const string_length = LoadStringLength(string);
   1389     GotoIfNot(SmiLessThan(index_plus_one, string_length), &out);
   1390 
   1391     Node* const lead = StringCharCodeAt(string, index);
   1392     GotoIfNot(Word32Equal(Word32And(lead, Int32Constant(0xFC00)),
   1393                           Int32Constant(0xD800)),
   1394               &out);
   1395 
   1396     Node* const trail = StringCharCodeAt(string, index_plus_one);
   1397     GotoIfNot(Word32Equal(Word32And(trail, Int32Constant(0xFC00)),
   1398                           Int32Constant(0xDC00)),
   1399               &out);
   1400 
   1401     // At a surrogate pair, return index + 2.
   1402     Node* const index_plus_two = NumberInc(index_plus_one);
   1403     var_result.Bind(index_plus_two);
   1404 
   1405     Goto(&out);
   1406   }
   1407 
   1408   Bind(&out);
   1409   return var_result.value();
   1410 }
   1411 
   1412 namespace {
   1413 
   1414 // Utility class implementing a growable fixed array through CSA.
   1415 class GrowableFixedArray {
   1416   typedef CodeStubAssembler::Label Label;
   1417   typedef CodeStubAssembler::Variable Variable;
   1418 
   1419  public:
   1420   explicit GrowableFixedArray(CodeStubAssembler* a)
   1421       : assembler_(a),
   1422         var_array_(a, MachineRepresentation::kTagged),
   1423         var_length_(a, MachineType::PointerRepresentation()),
   1424         var_capacity_(a, MachineType::PointerRepresentation()) {
   1425     Initialize();
   1426   }
   1427 
   1428   Node* length() const { return var_length_.value(); }
   1429 
   1430   Variable* var_array() { return &var_array_; }
   1431   Variable* var_length() { return &var_length_; }
   1432   Variable* var_capacity() { return &var_capacity_; }
   1433 
   1434   void Push(Node* const value) {
   1435     CodeStubAssembler* a = assembler_;
   1436 
   1437     Node* const length = var_length_.value();
   1438     Node* const capacity = var_capacity_.value();
   1439 
   1440     Label grow(a), store(a);
   1441     a->Branch(a->IntPtrEqual(capacity, length), &grow, &store);
   1442 
   1443     a->Bind(&grow);
   1444     {
   1445       Node* const new_capacity = NewCapacity(a, capacity);
   1446       Node* const new_array = ResizeFixedArray(length, new_capacity);
   1447 
   1448       var_capacity_.Bind(new_capacity);
   1449       var_array_.Bind(new_array);
   1450       a->Goto(&store);
   1451     }
   1452 
   1453     a->Bind(&store);
   1454     {
   1455       Node* const array = var_array_.value();
   1456       a->StoreFixedArrayElement(array, length, value);
   1457 
   1458       Node* const new_length = a->IntPtrAdd(length, a->IntPtrConstant(1));
   1459       var_length_.Bind(new_length);
   1460     }
   1461   }
   1462 
   1463   Node* ToJSArray(Node* const context) {
   1464     CodeStubAssembler* a = assembler_;
   1465 
   1466     const ElementsKind kind = FAST_ELEMENTS;
   1467 
   1468     Node* const native_context = a->LoadNativeContext(context);
   1469     Node* const array_map = a->LoadJSArrayElementsMap(kind, native_context);
   1470 
   1471     // Shrink to fit if necessary.
   1472     {
   1473       Label next(a);
   1474 
   1475       Node* const length = var_length_.value();
   1476       Node* const capacity = var_capacity_.value();
   1477 
   1478       a->GotoIf(a->WordEqual(length, capacity), &next);
   1479 
   1480       Node* const array = ResizeFixedArray(length, length);
   1481       var_array_.Bind(array);
   1482       var_capacity_.Bind(length);
   1483       a->Goto(&next);
   1484 
   1485       a->Bind(&next);
   1486     }
   1487 
   1488     Node* const result_length = a->SmiTag(length());
   1489     Node* const result = a->AllocateUninitializedJSArrayWithoutElements(
   1490         kind, array_map, result_length, nullptr);
   1491 
   1492     // Note: We do not currently shrink the fixed array.
   1493 
   1494     a->StoreObjectField(result, JSObject::kElementsOffset, var_array_.value());
   1495 
   1496     return result;
   1497   }
   1498 
   1499  private:
   1500   void Initialize() {
   1501     CodeStubAssembler* a = assembler_;
   1502 
   1503     const ElementsKind kind = FAST_ELEMENTS;
   1504 
   1505     static const int kInitialArraySize = 8;
   1506     Node* const capacity = a->IntPtrConstant(kInitialArraySize);
   1507     Node* const array = a->AllocateFixedArray(kind, capacity);
   1508 
   1509     a->FillFixedArrayWithValue(kind, array, a->IntPtrConstant(0), capacity,
   1510                                Heap::kTheHoleValueRootIndex);
   1511 
   1512     var_array_.Bind(array);
   1513     var_capacity_.Bind(capacity);
   1514     var_length_.Bind(a->IntPtrConstant(0));
   1515   }
   1516 
   1517   Node* NewCapacity(CodeStubAssembler* a, Node* const current_capacity) {
   1518     CSA_ASSERT(a, a->IntPtrGreaterThan(current_capacity, a->IntPtrConstant(0)));
   1519 
   1520     // Growth rate is analog to JSObject::NewElementsCapacity:
   1521     // new_capacity = (current_capacity + (current_capacity >> 1)) + 16.
   1522 
   1523     Node* const new_capacity = a->IntPtrAdd(
   1524         a->IntPtrAdd(current_capacity, a->WordShr(current_capacity, 1)),
   1525         a->IntPtrConstant(16));
   1526 
   1527     return new_capacity;
   1528   }
   1529 
   1530   // Creates a new array with {new_capacity} and copies the first
   1531   // {element_count} elements from the current array.
   1532   Node* ResizeFixedArray(Node* const element_count, Node* const new_capacity) {
   1533     CodeStubAssembler* a = assembler_;
   1534 
   1535     CSA_ASSERT(a, a->IntPtrGreaterThan(element_count, a->IntPtrConstant(0)));
   1536     CSA_ASSERT(a, a->IntPtrGreaterThan(new_capacity, a->IntPtrConstant(0)));
   1537     CSA_ASSERT(a, a->IntPtrGreaterThanOrEqual(new_capacity, element_count));
   1538 
   1539     const ElementsKind kind = FAST_ELEMENTS;
   1540     const WriteBarrierMode barrier_mode = UPDATE_WRITE_BARRIER;
   1541     const ParameterMode mode = CodeStubAssembler::INTPTR_PARAMETERS;
   1542     const CodeStubAssembler::AllocationFlags flags =
   1543         CodeStubAssembler::kAllowLargeObjectAllocation;
   1544 
   1545     Node* const from_array = var_array_.value();
   1546     Node* const to_array =
   1547         a->AllocateFixedArray(kind, new_capacity, mode, flags);
   1548     a->CopyFixedArrayElements(kind, from_array, kind, to_array, element_count,
   1549                               new_capacity, barrier_mode, mode);
   1550 
   1551     return to_array;
   1552   }
   1553 
   1554  private:
   1555   CodeStubAssembler* const assembler_;
   1556   Variable var_array_;
   1557   Variable var_length_;
   1558   Variable var_capacity_;
   1559 };
   1560 
   1561 }  // namespace
   1562 
   1563 void RegExpBuiltinsAssembler::RegExpPrototypeMatchBody(Node* const context,
   1564                                                        Node* const regexp,
   1565                                                        Node* const string,
   1566                                                        const bool is_fastpath) {
   1567   Isolate* const isolate = this->isolate();
   1568 
   1569   Node* const null = NullConstant();
   1570   Node* const int_zero = IntPtrConstant(0);
   1571   Node* const smi_zero = SmiConstant(Smi::kZero);
   1572 
   1573   Node* const is_global =
   1574       FlagGetter(context, regexp, JSRegExp::kGlobal, is_fastpath);
   1575 
   1576   Label if_isglobal(this), if_isnotglobal(this);
   1577   Branch(is_global, &if_isglobal, &if_isnotglobal);
   1578 
   1579   Bind(&if_isnotglobal);
   1580   {
   1581     Node* const result =
   1582         is_fastpath ? RegExpPrototypeExecBody(context, regexp, string, true)
   1583                     : RegExpExec(context, regexp, string);
   1584     Return(result);
   1585   }
   1586 
   1587   Bind(&if_isglobal);
   1588   {
   1589     Node* const is_unicode =
   1590         FlagGetter(context, regexp, JSRegExp::kUnicode, is_fastpath);
   1591 
   1592     StoreLastIndex(context, regexp, smi_zero, is_fastpath);
   1593 
   1594     // Allocate an array to store the resulting match strings.
   1595 
   1596     GrowableFixedArray array(this);
   1597 
   1598     // Loop preparations. Within the loop, collect results from RegExpExec
   1599     // and store match strings in the array.
   1600 
   1601     Variable* vars[] = {array.var_array(), array.var_length(),
   1602                         array.var_capacity()};
   1603     Label loop(this, 3, vars), out(this);
   1604     Goto(&loop);
   1605 
   1606     Bind(&loop);
   1607     {
   1608       Variable var_match(this, MachineRepresentation::kTagged);
   1609 
   1610       Label if_didmatch(this), if_didnotmatch(this);
   1611       if (is_fastpath) {
   1612         // On the fast path, grab the matching string from the raw match index
   1613         // array.
   1614         Node* const match_indices = RegExpPrototypeExecBodyWithoutResult(
   1615             context, regexp, string, &if_didnotmatch, true);
   1616 
   1617         Node* const match_from = LoadFixedArrayElement(
   1618             match_indices, RegExpMatchInfo::kFirstCaptureIndex);
   1619         Node* const match_to = LoadFixedArrayElement(
   1620             match_indices, RegExpMatchInfo::kFirstCaptureIndex + 1);
   1621 
   1622         Node* match = SubString(context, string, match_from, match_to);
   1623         var_match.Bind(match);
   1624 
   1625         Goto(&if_didmatch);
   1626       } else {
   1627         DCHECK(!is_fastpath);
   1628         Node* const result = RegExpExec(context, regexp, string);
   1629 
   1630         Label load_match(this);
   1631         Branch(WordEqual(result, null), &if_didnotmatch, &load_match);
   1632 
   1633         Bind(&load_match);
   1634         {
   1635           Label fast_result(this), slow_result(this);
   1636           BranchIfFastRegExpResult(context, LoadMap(result), &fast_result,
   1637                                    &slow_result);
   1638 
   1639           Bind(&fast_result);
   1640           {
   1641             Node* const result_fixed_array = LoadElements(result);
   1642             Node* const match = LoadFixedArrayElement(result_fixed_array, 0);
   1643 
   1644             // The match is guaranteed to be a string on the fast path.
   1645             CSA_ASSERT(this, IsStringInstanceType(LoadInstanceType(match)));
   1646 
   1647             var_match.Bind(match);
   1648             Goto(&if_didmatch);
   1649           }
   1650 
   1651           Bind(&slow_result);
   1652           {
   1653             // TODO(ishell): Use GetElement stub once it's available.
   1654             Node* const name = smi_zero;
   1655             Callable getproperty_callable = CodeFactory::GetProperty(isolate);
   1656             Node* const match =
   1657                 CallStub(getproperty_callable, context, result, name);
   1658 
   1659             var_match.Bind(ToString(context, match));
   1660             Goto(&if_didmatch);
   1661           }
   1662         }
   1663       }
   1664 
   1665       Bind(&if_didnotmatch);
   1666       {
   1667         // Return null if there were no matches, otherwise just exit the loop.
   1668         GotoIfNot(IntPtrEqual(array.length(), int_zero), &out);
   1669         Return(null);
   1670       }
   1671 
   1672       Bind(&if_didmatch);
   1673       {
   1674         Node* match = var_match.value();
   1675 
   1676         // Store the match, growing the fixed array if needed.
   1677 
   1678         array.Push(match);
   1679 
   1680         // Advance last index if the match is the empty string.
   1681 
   1682         Node* const match_length = LoadStringLength(match);
   1683         GotoIfNot(SmiEqual(match_length, smi_zero), &loop);
   1684 
   1685         Node* last_index = LoadLastIndex(context, regexp, is_fastpath);
   1686         if (is_fastpath) {
   1687           CSA_ASSERT(this, TaggedIsPositiveSmi(last_index));
   1688         } else {
   1689           Callable tolength_callable = CodeFactory::ToLength(isolate);
   1690           last_index = CallStub(tolength_callable, context, last_index);
   1691         }
   1692 
   1693         Node* const new_last_index =
   1694             AdvanceStringIndex(string, last_index, is_unicode, is_fastpath);
   1695 
   1696         if (is_fastpath) {
   1697           // On the fast path, we can be certain that lastIndex can never be
   1698           // incremented to overflow the Smi range since the maximal string
   1699           // length is less than the maximal Smi value.
   1700           STATIC_ASSERT(String::kMaxLength < Smi::kMaxValue);
   1701           CSA_ASSERT(this, TaggedIsPositiveSmi(new_last_index));
   1702         }
   1703 
   1704         StoreLastIndex(context, regexp, new_last_index, is_fastpath);
   1705 
   1706         Goto(&loop);
   1707       }
   1708     }
   1709 
   1710     Bind(&out);
   1711     {
   1712       // Wrap the match in a JSArray.
   1713 
   1714       Node* const result = array.ToJSArray(context);
   1715       Return(result);
   1716     }
   1717   }
   1718 }
   1719 
   1720 // ES#sec-regexp.prototype-@@match
   1721 // RegExp.prototype [ @@match ] ( string )
   1722 TF_BUILTIN(RegExpPrototypeMatch, RegExpBuiltinsAssembler) {
   1723   Node* const maybe_receiver = Parameter(0);
   1724   Node* const maybe_string = Parameter(1);
   1725   Node* const context = Parameter(4);
   1726 
   1727   // Ensure {maybe_receiver} is a JSReceiver.
   1728   ThrowIfNotJSReceiver(context, maybe_receiver,
   1729                        MessageTemplate::kIncompatibleMethodReceiver,
   1730                        "RegExp.prototype.@@match");
   1731   Node* const receiver = maybe_receiver;
   1732 
   1733   // Convert {maybe_string} to a String.
   1734   Node* const string = ToString(context, maybe_string);
   1735 
   1736   Label fast_path(this), slow_path(this);
   1737   BranchIfFastRegExp(context, receiver, LoadMap(receiver), &fast_path,
   1738                      &slow_path);
   1739 
   1740   Bind(&fast_path);
   1741   RegExpPrototypeMatchBody(context, receiver, string, true);
   1742 
   1743   Bind(&slow_path);
   1744   RegExpPrototypeMatchBody(context, receiver, string, false);
   1745 }
   1746 
   1747 void RegExpBuiltinsAssembler::RegExpPrototypeSearchBodyFast(
   1748     Node* const context, Node* const regexp, Node* const string) {
   1749   // Grab the initial value of last index.
   1750   Node* const previous_last_index = FastLoadLastIndex(regexp);
   1751 
   1752   // Ensure last index is 0.
   1753   FastStoreLastIndex(regexp, SmiConstant(Smi::kZero));
   1754 
   1755   // Call exec.
   1756   Label if_didnotmatch(this);
   1757   Node* const match_indices = RegExpPrototypeExecBodyWithoutResult(
   1758       context, regexp, string, &if_didnotmatch, true);
   1759 
   1760   // Successful match.
   1761   {
   1762     // Reset last index.
   1763     FastStoreLastIndex(regexp, previous_last_index);
   1764 
   1765     // Return the index of the match.
   1766     Node* const index = LoadFixedArrayElement(
   1767         match_indices, RegExpMatchInfo::kFirstCaptureIndex);
   1768     Return(index);
   1769   }
   1770 
   1771   Bind(&if_didnotmatch);
   1772   {
   1773     // Reset last index and return -1.
   1774     FastStoreLastIndex(regexp, previous_last_index);
   1775     Return(SmiConstant(-1));
   1776   }
   1777 }
   1778 
   1779 void RegExpBuiltinsAssembler::RegExpPrototypeSearchBodySlow(
   1780     Node* const context, Node* const regexp, Node* const string) {
   1781   Isolate* const isolate = this->isolate();
   1782 
   1783   Node* const smi_zero = SmiConstant(Smi::kZero);
   1784 
   1785   // Grab the initial value of last index.
   1786   Node* const previous_last_index = SlowLoadLastIndex(context, regexp);
   1787 
   1788   // Ensure last index is 0.
   1789   {
   1790     Label next(this);
   1791     GotoIf(SameValue(previous_last_index, smi_zero, context), &next);
   1792 
   1793     SlowStoreLastIndex(context, regexp, smi_zero);
   1794     Goto(&next);
   1795     Bind(&next);
   1796   }
   1797 
   1798   // Call exec.
   1799   Node* const exec_result = RegExpExec(context, regexp, string);
   1800 
   1801   // Reset last index if necessary.
   1802   {
   1803     Label next(this);
   1804     Node* const current_last_index = SlowLoadLastIndex(context, regexp);
   1805 
   1806     GotoIf(SameValue(current_last_index, previous_last_index, context), &next);
   1807 
   1808     SlowStoreLastIndex(context, regexp, previous_last_index);
   1809     Goto(&next);
   1810 
   1811     Bind(&next);
   1812   }
   1813 
   1814   // Return -1 if no match was found.
   1815   {
   1816     Label next(this);
   1817     GotoIfNot(WordEqual(exec_result, NullConstant()), &next);
   1818     Return(SmiConstant(-1));
   1819     Bind(&next);
   1820   }
   1821 
   1822   // Return the index of the match.
   1823   {
   1824     Label fast_result(this), slow_result(this, Label::kDeferred);
   1825     BranchIfFastRegExpResult(context, LoadMap(exec_result), &fast_result,
   1826                              &slow_result);
   1827 
   1828     Bind(&fast_result);
   1829     {
   1830       Node* const index =
   1831           LoadObjectField(exec_result, JSRegExpResult::kIndexOffset);
   1832       Return(index);
   1833     }
   1834 
   1835     Bind(&slow_result);
   1836     {
   1837       Node* const name = HeapConstant(isolate->factory()->index_string());
   1838       Callable getproperty_callable = CodeFactory::GetProperty(isolate);
   1839       Node* const index =
   1840           CallStub(getproperty_callable, context, exec_result, name);
   1841       Return(index);
   1842     }
   1843   }
   1844 }
   1845 
   1846 // ES#sec-regexp.prototype-@@search
   1847 // RegExp.prototype [ @@search ] ( string )
   1848 TF_BUILTIN(RegExpPrototypeSearch, RegExpBuiltinsAssembler) {
   1849   Node* const maybe_receiver = Parameter(0);
   1850   Node* const maybe_string = Parameter(1);
   1851   Node* const context = Parameter(4);
   1852 
   1853   // Ensure {maybe_receiver} is a JSReceiver.
   1854   ThrowIfNotJSReceiver(context, maybe_receiver,
   1855                        MessageTemplate::kIncompatibleMethodReceiver,
   1856                        "RegExp.prototype.@@search");
   1857   Node* const receiver = maybe_receiver;
   1858 
   1859   // Convert {maybe_string} to a String.
   1860   Node* const string = ToString(context, maybe_string);
   1861 
   1862   Label fast_path(this), slow_path(this);
   1863   BranchIfFastRegExp(context, receiver, LoadMap(receiver), &fast_path,
   1864                      &slow_path);
   1865 
   1866   Bind(&fast_path);
   1867   RegExpPrototypeSearchBodyFast(context, receiver, string);
   1868 
   1869   Bind(&slow_path);
   1870   RegExpPrototypeSearchBodySlow(context, receiver, string);
   1871 }
   1872 
   1873 // Generates the fast path for @@split. {regexp} is an unmodified JSRegExp,
   1874 // {string} is a String, and {limit} is a Smi.
   1875 void RegExpBuiltinsAssembler::RegExpPrototypeSplitBody(Node* const context,
   1876                                                        Node* const regexp,
   1877                                                        Node* const string,
   1878                                                        Node* const limit) {
   1879   Isolate* isolate = this->isolate();
   1880 
   1881   Node* const null = NullConstant();
   1882   Node* const smi_zero = SmiConstant(0);
   1883   Node* const int_zero = IntPtrConstant(0);
   1884   Node* const int_limit = SmiUntag(limit);
   1885 
   1886   const ElementsKind kind = FAST_ELEMENTS;
   1887   const ParameterMode mode = CodeStubAssembler::INTPTR_PARAMETERS;
   1888 
   1889   Node* const allocation_site = nullptr;
   1890   Node* const native_context = LoadNativeContext(context);
   1891   Node* const array_map = LoadJSArrayElementsMap(kind, native_context);
   1892 
   1893   Label return_empty_array(this, Label::kDeferred);
   1894 
   1895   // If limit is zero, return an empty array.
   1896   {
   1897     Label next(this), if_limitiszero(this, Label::kDeferred);
   1898     Branch(SmiEqual(limit, smi_zero), &return_empty_array, &next);
   1899     Bind(&next);
   1900   }
   1901 
   1902   Node* const string_length = LoadStringLength(string);
   1903 
   1904   // If passed the empty {string}, return either an empty array or a singleton
   1905   // array depending on whether the {regexp} matches.
   1906   {
   1907     Label next(this), if_stringisempty(this, Label::kDeferred);
   1908     Branch(SmiEqual(string_length, smi_zero), &if_stringisempty, &next);
   1909 
   1910     Bind(&if_stringisempty);
   1911     {
   1912       Node* const last_match_info = LoadContextElement(
   1913           native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX);
   1914 
   1915       Callable exec_callable = CodeFactory::RegExpExec(isolate);
   1916       Node* const match_indices = CallStub(exec_callable, context, regexp,
   1917                                            string, smi_zero, last_match_info);
   1918 
   1919       Label return_singleton_array(this);
   1920       Branch(WordEqual(match_indices, null), &return_singleton_array,
   1921              &return_empty_array);
   1922 
   1923       Bind(&return_singleton_array);
   1924       {
   1925         Node* const length = SmiConstant(1);
   1926         Node* const capacity = IntPtrConstant(1);
   1927         Node* const result = AllocateJSArray(kind, array_map, capacity, length,
   1928                                              allocation_site, mode);
   1929 
   1930         Node* const fixed_array = LoadElements(result);
   1931         StoreFixedArrayElement(fixed_array, 0, string);
   1932 
   1933         Return(result);
   1934       }
   1935     }
   1936 
   1937     Bind(&next);
   1938   }
   1939 
   1940   // Loop preparations.
   1941 
   1942   GrowableFixedArray array(this);
   1943 
   1944   Variable var_last_matched_until(this, MachineRepresentation::kTagged);
   1945   Variable var_next_search_from(this, MachineRepresentation::kTagged);
   1946 
   1947   var_last_matched_until.Bind(smi_zero);
   1948   var_next_search_from.Bind(smi_zero);
   1949 
   1950   Variable* vars[] = {array.var_array(), array.var_length(),
   1951                       array.var_capacity(), &var_last_matched_until,
   1952                       &var_next_search_from};
   1953   const int vars_count = sizeof(vars) / sizeof(vars[0]);
   1954   Label loop(this, vars_count, vars), push_suffix_and_out(this), out(this);
   1955   Goto(&loop);
   1956 
   1957   Bind(&loop);
   1958   {
   1959     Node* const next_search_from = var_next_search_from.value();
   1960     Node* const last_matched_until = var_last_matched_until.value();
   1961 
   1962     // We're done if we've reached the end of the string.
   1963     {
   1964       Label next(this);
   1965       Branch(SmiEqual(next_search_from, string_length), &push_suffix_and_out,
   1966              &next);
   1967       Bind(&next);
   1968     }
   1969 
   1970     // Search for the given {regexp}.
   1971 
   1972     Node* const last_match_info = LoadContextElement(
   1973         native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX);
   1974 
   1975     Callable exec_callable = CodeFactory::RegExpExec(isolate);
   1976     Node* const match_indices = CallStub(exec_callable, context, regexp, string,
   1977                                          next_search_from, last_match_info);
   1978 
   1979     // We're done if no match was found.
   1980     {
   1981       Label next(this);
   1982       Branch(WordEqual(match_indices, null), &push_suffix_and_out, &next);
   1983       Bind(&next);
   1984     }
   1985 
   1986     Node* const match_from = LoadFixedArrayElement(
   1987         match_indices, RegExpMatchInfo::kFirstCaptureIndex);
   1988 
   1989     // We're done if the match starts beyond the string.
   1990     {
   1991       Label next(this);
   1992       Branch(WordEqual(match_from, string_length), &push_suffix_and_out, &next);
   1993       Bind(&next);
   1994     }
   1995 
   1996     Node* const match_to = LoadFixedArrayElement(
   1997         match_indices, RegExpMatchInfo::kFirstCaptureIndex + 1);
   1998 
   1999     // Advance index and continue if the match is empty.
   2000     {
   2001       Label next(this);
   2002 
   2003       GotoIfNot(SmiEqual(match_to, next_search_from), &next);
   2004       GotoIfNot(SmiEqual(match_to, last_matched_until), &next);
   2005 
   2006       Node* const is_unicode = FastFlagGetter(regexp, JSRegExp::kUnicode);
   2007       Node* const new_next_search_from =
   2008           AdvanceStringIndex(string, next_search_from, is_unicode, true);
   2009       var_next_search_from.Bind(new_next_search_from);
   2010       Goto(&loop);
   2011 
   2012       Bind(&next);
   2013     }
   2014 
   2015     // A valid match was found, add the new substring to the array.
   2016     {
   2017       Node* const from = last_matched_until;
   2018       Node* const to = match_from;
   2019 
   2020       Node* const substr = SubString(context, string, from, to);
   2021       array.Push(substr);
   2022 
   2023       GotoIf(WordEqual(array.length(), int_limit), &out);
   2024     }
   2025 
   2026     // Add all captures to the array.
   2027     {
   2028       Node* const num_registers = LoadFixedArrayElement(
   2029           match_indices, RegExpMatchInfo::kNumberOfCapturesIndex);
   2030       Node* const int_num_registers = SmiUntag(num_registers);
   2031 
   2032       Variable var_reg(this, MachineType::PointerRepresentation());
   2033       var_reg.Bind(IntPtrConstant(2));
   2034 
   2035       Variable* vars[] = {array.var_array(), array.var_length(),
   2036                           array.var_capacity(), &var_reg};
   2037       const int vars_count = sizeof(vars) / sizeof(vars[0]);
   2038       Label nested_loop(this, vars_count, vars), nested_loop_out(this);
   2039       Branch(IntPtrLessThan(var_reg.value(), int_num_registers), &nested_loop,
   2040              &nested_loop_out);
   2041 
   2042       Bind(&nested_loop);
   2043       {
   2044         Node* const reg = var_reg.value();
   2045         Node* const from = LoadFixedArrayElement(
   2046             match_indices, reg,
   2047             RegExpMatchInfo::kFirstCaptureIndex * kPointerSize, mode);
   2048         Node* const to = LoadFixedArrayElement(
   2049             match_indices, reg,
   2050             (RegExpMatchInfo::kFirstCaptureIndex + 1) * kPointerSize, mode);
   2051 
   2052         Label select_capture(this), select_undefined(this), store_value(this);
   2053         Variable var_value(this, MachineRepresentation::kTagged);
   2054         Branch(SmiEqual(to, SmiConstant(-1)), &select_undefined,
   2055                &select_capture);
   2056 
   2057         Bind(&select_capture);
   2058         {
   2059           Node* const substr = SubString(context, string, from, to);
   2060           var_value.Bind(substr);
   2061           Goto(&store_value);
   2062         }
   2063 
   2064         Bind(&select_undefined);
   2065         {
   2066           Node* const undefined = UndefinedConstant();
   2067           var_value.Bind(undefined);
   2068           Goto(&store_value);
   2069         }
   2070 
   2071         Bind(&store_value);
   2072         {
   2073           array.Push(var_value.value());
   2074           GotoIf(WordEqual(array.length(), int_limit), &out);
   2075 
   2076           Node* const new_reg = IntPtrAdd(reg, IntPtrConstant(2));
   2077           var_reg.Bind(new_reg);
   2078 
   2079           Branch(IntPtrLessThan(new_reg, int_num_registers), &nested_loop,
   2080                  &nested_loop_out);
   2081         }
   2082       }
   2083 
   2084       Bind(&nested_loop_out);
   2085     }
   2086 
   2087     var_last_matched_until.Bind(match_to);
   2088     var_next_search_from.Bind(match_to);
   2089     Goto(&loop);
   2090   }
   2091 
   2092   Bind(&push_suffix_and_out);
   2093   {
   2094     Node* const from = var_last_matched_until.value();
   2095     Node* const to = string_length;
   2096 
   2097     Node* const substr = SubString(context, string, from, to);
   2098     array.Push(substr);
   2099 
   2100     Goto(&out);
   2101   }
   2102 
   2103   Bind(&out);
   2104   {
   2105     Node* const result = array.ToJSArray(context);
   2106     Return(result);
   2107   }
   2108 
   2109   Bind(&return_empty_array);
   2110   {
   2111     Node* const length = smi_zero;
   2112     Node* const capacity = int_zero;
   2113     Node* const result = AllocateJSArray(kind, array_map, capacity, length,
   2114                                          allocation_site, mode);
   2115     Return(result);
   2116   }
   2117 }
   2118 
   2119 // Helper that skips a few initial checks.
   2120 TF_BUILTIN(RegExpSplit, RegExpBuiltinsAssembler) {
   2121   typedef RegExpSplitDescriptor Descriptor;
   2122 
   2123   Node* const regexp = Parameter(Descriptor::kReceiver);
   2124   Node* const string = Parameter(Descriptor::kString);
   2125   Node* const maybe_limit = Parameter(Descriptor::kLimit);
   2126   Node* const context = Parameter(Descriptor::kContext);
   2127 
   2128   CSA_ASSERT(this, IsFastRegExpMap(context, regexp, LoadMap(regexp)));
   2129   CSA_ASSERT(this, IsString(string));
   2130 
   2131   // TODO(jgruber): Even if map checks send us to the fast path, we still need
   2132   // to verify the constructor property and jump to the slow path if it has
   2133   // been changed.
   2134 
   2135   // Convert {maybe_limit} to a uint32, capping at the maximal smi value.
   2136   Variable var_limit(this, MachineRepresentation::kTagged, maybe_limit);
   2137   Label if_limitissmimax(this), limit_done(this), runtime(this);
   2138 
   2139   GotoIf(IsUndefined(maybe_limit), &if_limitissmimax);
   2140   GotoIf(TaggedIsPositiveSmi(maybe_limit), &limit_done);
   2141 
   2142   Node* const limit = ToUint32(context, maybe_limit);
   2143   {
   2144     // ToUint32(limit) could potentially change the shape of the RegExp
   2145     // object. Recheck that we are still on the fast path and bail to runtime
   2146     // otherwise.
   2147     {
   2148       Label next(this);
   2149       BranchIfFastRegExp(context, regexp, LoadMap(regexp), &next, &runtime);
   2150       Bind(&next);
   2151     }
   2152 
   2153     GotoIfNot(TaggedIsSmi(limit), &if_limitissmimax);
   2154 
   2155     var_limit.Bind(limit);
   2156     Goto(&limit_done);
   2157   }
   2158 
   2159   Bind(&if_limitissmimax);
   2160   {
   2161     // TODO(jgruber): In this case, we can probably avoid generation of limit
   2162     // checks in Generate_RegExpPrototypeSplitBody.
   2163     var_limit.Bind(SmiConstant(Smi::kMaxValue));
   2164     Goto(&limit_done);
   2165   }
   2166 
   2167   Bind(&limit_done);
   2168   {
   2169     Node* const limit = var_limit.value();
   2170     RegExpPrototypeSplitBody(context, regexp, string, limit);
   2171   }
   2172 
   2173   Bind(&runtime);
   2174   {
   2175     // The runtime call passes in limit to ensure the second ToUint32(limit)
   2176     // call is not observable.
   2177     CSA_ASSERT(this, IsHeapNumberMap(LoadReceiverMap(limit)));
   2178     Return(CallRuntime(Runtime::kRegExpSplit, context, regexp, string, limit));
   2179   }
   2180 }
   2181 
   2182 // ES#sec-regexp.prototype-@@split
   2183 // RegExp.prototype [ @@split ] ( string, limit )
   2184 TF_BUILTIN(RegExpPrototypeSplit, RegExpBuiltinsAssembler) {
   2185   Node* const maybe_receiver = Parameter(0);
   2186   Node* const maybe_string = Parameter(1);
   2187   Node* const maybe_limit = Parameter(2);
   2188   Node* const context = Parameter(5);
   2189 
   2190   // Ensure {maybe_receiver} is a JSReceiver.
   2191   ThrowIfNotJSReceiver(context, maybe_receiver,
   2192                        MessageTemplate::kIncompatibleMethodReceiver,
   2193                        "RegExp.prototype.@@split");
   2194   Node* const receiver = maybe_receiver;
   2195 
   2196   // Convert {maybe_string} to a String.
   2197   Node* const string = ToString(context, maybe_string);
   2198 
   2199   Label stub(this), runtime(this, Label::kDeferred);
   2200   BranchIfFastRegExp(context, receiver, LoadMap(receiver), &stub, &runtime);
   2201 
   2202   Bind(&stub);
   2203   Callable split_callable = CodeFactory::RegExpSplit(isolate());
   2204   Return(CallStub(split_callable, context, receiver, string, maybe_limit));
   2205 
   2206   Bind(&runtime);
   2207   Return(CallRuntime(Runtime::kRegExpSplit, context, receiver, string,
   2208                      maybe_limit));
   2209 }
   2210 
   2211 Node* RegExpBuiltinsAssembler::ReplaceGlobalCallableFastPath(
   2212     Node* context, Node* regexp, Node* string, Node* replace_callable) {
   2213   // The fast path is reached only if {receiver} is a global unmodified
   2214   // JSRegExp instance and {replace_callable} is callable.
   2215 
   2216   Isolate* const isolate = this->isolate();
   2217 
   2218   Node* const null = NullConstant();
   2219   Node* const undefined = UndefinedConstant();
   2220   Node* const int_zero = IntPtrConstant(0);
   2221   Node* const int_one = IntPtrConstant(1);
   2222   Node* const smi_zero = SmiConstant(Smi::kZero);
   2223 
   2224   Node* const native_context = LoadNativeContext(context);
   2225 
   2226   Label out(this);
   2227   Variable var_result(this, MachineRepresentation::kTagged);
   2228 
   2229   // Set last index to 0.
   2230   FastStoreLastIndex(regexp, smi_zero);
   2231 
   2232   // Allocate {result_array}.
   2233   Node* result_array;
   2234   {
   2235     ElementsKind kind = FAST_ELEMENTS;
   2236     Node* const array_map = LoadJSArrayElementsMap(kind, native_context);
   2237     Node* const capacity = IntPtrConstant(16);
   2238     Node* const length = smi_zero;
   2239     Node* const allocation_site = nullptr;
   2240     ParameterMode capacity_mode = CodeStubAssembler::INTPTR_PARAMETERS;
   2241 
   2242     result_array = AllocateJSArray(kind, array_map, capacity, length,
   2243                                    allocation_site, capacity_mode);
   2244   }
   2245 
   2246   // Call into runtime for RegExpExecMultiple.
   2247   Node* last_match_info =
   2248       LoadContextElement(native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX);
   2249   Node* const res = CallRuntime(Runtime::kRegExpExecMultiple, context, regexp,
   2250                                 string, last_match_info, result_array);
   2251 
   2252   // Reset last index to 0.
   2253   FastStoreLastIndex(regexp, smi_zero);
   2254 
   2255   // If no matches, return the subject string.
   2256   var_result.Bind(string);
   2257   GotoIf(WordEqual(res, null), &out);
   2258 
   2259   // Reload last match info since it might have changed.
   2260   last_match_info =
   2261       LoadContextElement(native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX);
   2262 
   2263   Node* const res_length = LoadJSArrayLength(res);
   2264   Node* const res_elems = LoadElements(res);
   2265   CSA_ASSERT(this, HasInstanceType(res_elems, FIXED_ARRAY_TYPE));
   2266 
   2267   Node* const num_capture_registers = LoadFixedArrayElement(
   2268       last_match_info, RegExpMatchInfo::kNumberOfCapturesIndex);
   2269 
   2270   Label if_hasexplicitcaptures(this), if_noexplicitcaptures(this),
   2271       create_result(this);
   2272   Branch(SmiEqual(num_capture_registers, SmiConstant(Smi::FromInt(2))),
   2273          &if_noexplicitcaptures, &if_hasexplicitcaptures);
   2274 
   2275   Bind(&if_noexplicitcaptures);
   2276   {
   2277     // If the number of captures is two then there are no explicit captures in
   2278     // the regexp, just the implicit capture that captures the whole match. In
   2279     // this case we can simplify quite a bit and end up with something faster.
   2280     // The builder will consist of some integers that indicate slices of the
   2281     // input string and some replacements that were returned from the replace
   2282     // function.
   2283 
   2284     Variable var_match_start(this, MachineRepresentation::kTagged);
   2285     var_match_start.Bind(smi_zero);
   2286 
   2287     Node* const end = SmiUntag(res_length);
   2288     Variable var_i(this, MachineType::PointerRepresentation());
   2289     var_i.Bind(int_zero);
   2290 
   2291     Variable* vars[] = {&var_i, &var_match_start};
   2292     Label loop(this, 2, vars);
   2293     Goto(&loop);
   2294     Bind(&loop);
   2295     {
   2296       Node* const i = var_i.value();
   2297       GotoIfNot(IntPtrLessThan(i, end), &create_result);
   2298 
   2299       Node* const elem = LoadFixedArrayElement(res_elems, i);
   2300 
   2301       Label if_issmi(this), if_isstring(this), loop_epilogue(this);
   2302       Branch(TaggedIsSmi(elem), &if_issmi, &if_isstring);
   2303 
   2304       Bind(&if_issmi);
   2305       {
   2306         // Integers represent slices of the original string.
   2307         Label if_isnegativeorzero(this), if_ispositive(this);
   2308         BranchIfSmiLessThanOrEqual(elem, smi_zero, &if_isnegativeorzero,
   2309                                    &if_ispositive);
   2310 
   2311         Bind(&if_ispositive);
   2312         {
   2313           Node* const int_elem = SmiUntag(elem);
   2314           Node* const new_match_start =
   2315               IntPtrAdd(WordShr(int_elem, IntPtrConstant(11)),
   2316                         WordAnd(int_elem, IntPtrConstant(0x7ff)));
   2317           var_match_start.Bind(SmiTag(new_match_start));
   2318           Goto(&loop_epilogue);
   2319         }
   2320 
   2321         Bind(&if_isnegativeorzero);
   2322         {
   2323           Node* const next_i = IntPtrAdd(i, int_one);
   2324           var_i.Bind(next_i);
   2325 
   2326           Node* const next_elem = LoadFixedArrayElement(res_elems, next_i);
   2327 
   2328           Node* const new_match_start = SmiSub(next_elem, elem);
   2329           var_match_start.Bind(new_match_start);
   2330           Goto(&loop_epilogue);
   2331         }
   2332       }
   2333 
   2334       Bind(&if_isstring);
   2335       {
   2336         CSA_ASSERT(this, IsStringInstanceType(LoadInstanceType(elem)));
   2337 
   2338         Callable call_callable = CodeFactory::Call(isolate);
   2339         Node* const replacement_obj =
   2340             CallJS(call_callable, context, replace_callable, undefined, elem,
   2341                    var_match_start.value(), string);
   2342 
   2343         Node* const replacement_str = ToString(context, replacement_obj);
   2344         StoreFixedArrayElement(res_elems, i, replacement_str);
   2345 
   2346         Node* const elem_length = LoadStringLength(elem);
   2347         Node* const new_match_start =
   2348             SmiAdd(var_match_start.value(), elem_length);
   2349         var_match_start.Bind(new_match_start);
   2350 
   2351         Goto(&loop_epilogue);
   2352       }
   2353 
   2354       Bind(&loop_epilogue);
   2355       {
   2356         var_i.Bind(IntPtrAdd(var_i.value(), int_one));
   2357         Goto(&loop);
   2358       }
   2359     }
   2360   }
   2361 
   2362   Bind(&if_hasexplicitcaptures);
   2363   {
   2364     Node* const from = int_zero;
   2365     Node* const to = SmiUntag(res_length);
   2366     const int increment = 1;
   2367 
   2368     BuildFastLoop(
   2369         from, to,
   2370         [this, res_elems, isolate, native_context, context, undefined,
   2371          replace_callable](Node* index) {
   2372           Node* const elem = LoadFixedArrayElement(res_elems, index);
   2373 
   2374           Label do_continue(this);
   2375           GotoIf(TaggedIsSmi(elem), &do_continue);
   2376 
   2377           // elem must be an Array.
   2378           // Use the apply argument as backing for global RegExp properties.
   2379 
   2380           CSA_ASSERT(this, HasInstanceType(elem, JS_ARRAY_TYPE));
   2381 
   2382           // TODO(jgruber): Remove indirection through Call->ReflectApply.
   2383           Callable call_callable = CodeFactory::Call(isolate);
   2384           Node* const reflect_apply =
   2385               LoadContextElement(native_context, Context::REFLECT_APPLY_INDEX);
   2386 
   2387           Node* const replacement_obj =
   2388               CallJS(call_callable, context, reflect_apply, undefined,
   2389                      replace_callable, undefined, elem);
   2390 
   2391           // Overwrite the i'th element in the results with the string we got
   2392           // back from the callback function.
   2393 
   2394           Node* const replacement_str = ToString(context, replacement_obj);
   2395           StoreFixedArrayElement(res_elems, index, replacement_str);
   2396 
   2397           Goto(&do_continue);
   2398           Bind(&do_continue);
   2399         },
   2400         increment, CodeStubAssembler::INTPTR_PARAMETERS,
   2401         CodeStubAssembler::IndexAdvanceMode::kPost);
   2402 
   2403     Goto(&create_result);
   2404   }
   2405 
   2406   Bind(&create_result);
   2407   {
   2408     Node* const result = CallRuntime(Runtime::kStringBuilderConcat, context,
   2409                                      res, res_length, string);
   2410     var_result.Bind(result);
   2411     Goto(&out);
   2412   }
   2413 
   2414   Bind(&out);
   2415   return var_result.value();
   2416 }
   2417 
   2418 Node* RegExpBuiltinsAssembler::ReplaceSimpleStringFastPath(
   2419     Node* context, Node* regexp, Node* string, Node* replace_string) {
   2420   // The fast path is reached only if {receiver} is an unmodified
   2421   // JSRegExp instance, {replace_value} is non-callable, and
   2422   // ToString({replace_value}) does not contain '$', i.e. we're doing a simple
   2423   // string replacement.
   2424 
   2425   Node* const int_zero = IntPtrConstant(0);
   2426   Node* const smi_zero = SmiConstant(Smi::kZero);
   2427 
   2428   Label out(this);
   2429   Variable var_result(this, MachineRepresentation::kTagged);
   2430 
   2431   // Load the last match info.
   2432   Node* const native_context = LoadNativeContext(context);
   2433   Node* const last_match_info =
   2434       LoadContextElement(native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX);
   2435 
   2436   // Is {regexp} global?
   2437   Label if_isglobal(this), if_isnonglobal(this);
   2438   Node* const flags = LoadObjectField(regexp, JSRegExp::kFlagsOffset);
   2439   Node* const is_global =
   2440       WordAnd(SmiUntag(flags), IntPtrConstant(JSRegExp::kGlobal));
   2441   Branch(WordEqual(is_global, int_zero), &if_isnonglobal, &if_isglobal);
   2442 
   2443   Bind(&if_isglobal);
   2444   {
   2445     // Hand off global regexps to runtime.
   2446     FastStoreLastIndex(regexp, smi_zero);
   2447     Node* const result =
   2448         CallRuntime(Runtime::kStringReplaceGlobalRegExpWithString, context,
   2449                     string, regexp, replace_string, last_match_info);
   2450     var_result.Bind(result);
   2451     Goto(&out);
   2452   }
   2453 
   2454   Bind(&if_isnonglobal);
   2455   {
   2456     // Run exec, then manually construct the resulting string.
   2457     Label if_didnotmatch(this);
   2458     Node* const match_indices = RegExpPrototypeExecBodyWithoutResult(
   2459         context, regexp, string, &if_didnotmatch, true);
   2460 
   2461     // Successful match.
   2462     {
   2463       Node* const subject_start = smi_zero;
   2464       Node* const match_start = LoadFixedArrayElement(
   2465           match_indices, RegExpMatchInfo::kFirstCaptureIndex);
   2466       Node* const match_end = LoadFixedArrayElement(
   2467           match_indices, RegExpMatchInfo::kFirstCaptureIndex + 1);
   2468       Node* const subject_end = LoadStringLength(string);
   2469 
   2470       Label if_replaceisempty(this), if_replaceisnotempty(this);
   2471       Node* const replace_length = LoadStringLength(replace_string);
   2472       Branch(SmiEqual(replace_length, smi_zero), &if_replaceisempty,
   2473              &if_replaceisnotempty);
   2474 
   2475       Bind(&if_replaceisempty);
   2476       {
   2477         // TODO(jgruber): We could skip many of the checks that using SubString
   2478         // here entails.
   2479 
   2480         Node* const first_part =
   2481             SubString(context, string, subject_start, match_start);
   2482         Node* const second_part =
   2483             SubString(context, string, match_end, subject_end);
   2484 
   2485         Node* const result = StringAdd(context, first_part, second_part);
   2486         var_result.Bind(result);
   2487         Goto(&out);
   2488       }
   2489 
   2490       Bind(&if_replaceisnotempty);
   2491       {
   2492         Node* const first_part =
   2493             SubString(context, string, subject_start, match_start);
   2494         Node* const second_part = replace_string;
   2495         Node* const third_part =
   2496             SubString(context, string, match_end, subject_end);
   2497 
   2498         Node* result = StringAdd(context, first_part, second_part);
   2499         result = StringAdd(context, result, third_part);
   2500 
   2501         var_result.Bind(result);
   2502         Goto(&out);
   2503       }
   2504     }
   2505 
   2506     Bind(&if_didnotmatch);
   2507     {
   2508       var_result.Bind(string);
   2509       Goto(&out);
   2510     }
   2511   }
   2512 
   2513   Bind(&out);
   2514   return var_result.value();
   2515 }
   2516 
   2517 // Helper that skips a few initial checks.
   2518 TF_BUILTIN(RegExpReplace, RegExpBuiltinsAssembler) {
   2519   typedef RegExpReplaceDescriptor Descriptor;
   2520 
   2521   Node* const regexp = Parameter(Descriptor::kReceiver);
   2522   Node* const string = Parameter(Descriptor::kString);
   2523   Node* const replace_value = Parameter(Descriptor::kReplaceValue);
   2524   Node* const context = Parameter(Descriptor::kContext);
   2525 
   2526   CSA_ASSERT(this, IsFastRegExpMap(context, regexp, LoadMap(regexp)));
   2527   CSA_ASSERT(this, IsString(string));
   2528 
   2529   Label checkreplacestring(this), if_iscallable(this),
   2530       runtime(this, Label::kDeferred);
   2531 
   2532   // 2. Is {replace_value} callable?
   2533   GotoIf(TaggedIsSmi(replace_value), &checkreplacestring);
   2534   Branch(IsCallableMap(LoadMap(replace_value)), &if_iscallable,
   2535          &checkreplacestring);
   2536 
   2537   // 3. Does ToString({replace_value}) contain '$'?
   2538   Bind(&checkreplacestring);
   2539   {
   2540     Callable tostring_callable = CodeFactory::ToString(isolate());
   2541     Node* const replace_string =
   2542         CallStub(tostring_callable, context, replace_value);
   2543 
   2544     // ToString(replaceValue) could potentially change the shape of the RegExp
   2545     // object. Recheck that we are still on the fast path and bail to runtime
   2546     // otherwise.
   2547     {
   2548       Label next(this);
   2549       BranchIfFastRegExp(context, regexp, LoadMap(regexp), &next, &runtime);
   2550       Bind(&next);
   2551     }
   2552 
   2553     Callable indexof_callable = CodeFactory::StringIndexOf(isolate());
   2554     Node* const dollar_string = HeapConstant(
   2555         isolate()->factory()->LookupSingleCharacterStringFromCode('$'));
   2556     Node* const dollar_ix = CallStub(indexof_callable, context, replace_string,
   2557                                      dollar_string, SmiConstant(0));
   2558     GotoIfNot(SmiEqual(dollar_ix, SmiConstant(-1)), &runtime);
   2559 
   2560     Return(
   2561         ReplaceSimpleStringFastPath(context, regexp, string, replace_string));
   2562   }
   2563 
   2564   // {regexp} is unmodified and {replace_value} is callable.
   2565   Bind(&if_iscallable);
   2566   {
   2567     Node* const replace_fn = replace_value;
   2568 
   2569     // Check if the {regexp} is global.
   2570     Label if_isglobal(this), if_isnotglobal(this);
   2571 
   2572     Node* const is_global = FastFlagGetter(regexp, JSRegExp::kGlobal);
   2573     Branch(is_global, &if_isglobal, &if_isnotglobal);
   2574 
   2575     Bind(&if_isglobal);
   2576     Return(ReplaceGlobalCallableFastPath(context, regexp, string, replace_fn));
   2577 
   2578     Bind(&if_isnotglobal);
   2579     Return(CallRuntime(Runtime::kStringReplaceNonGlobalRegExpWithFunction,
   2580                        context, string, regexp, replace_fn));
   2581   }
   2582 
   2583   Bind(&runtime);
   2584   Return(CallRuntime(Runtime::kRegExpReplace, context, regexp, string,
   2585                      replace_value));
   2586 }
   2587 
   2588 // ES#sec-regexp.prototype-@@replace
   2589 // RegExp.prototype [ @@replace ] ( string, replaceValue )
   2590 TF_BUILTIN(RegExpPrototypeReplace, RegExpBuiltinsAssembler) {
   2591   Node* const maybe_receiver = Parameter(0);
   2592   Node* const maybe_string = Parameter(1);
   2593   Node* const replace_value = Parameter(2);
   2594   Node* const context = Parameter(5);
   2595 
   2596   // RegExpPrototypeReplace is a bit of a beast - a summary of dispatch logic:
   2597   //
   2598   // if (!IsFastRegExp(receiver)) CallRuntime(RegExpReplace)
   2599   // if (IsCallable(replace)) {
   2600   //   if (IsGlobal(receiver)) {
   2601   //     // Called 'fast-path' but contains several runtime calls.
   2602   //     ReplaceGlobalCallableFastPath()
   2603   //   } else {
   2604   //     CallRuntime(StringReplaceNonGlobalRegExpWithFunction)
   2605   //   }
   2606   // } else {
   2607   //   if (replace.contains("$")) {
   2608   //     CallRuntime(RegExpReplace)
   2609   //   } else {
   2610   //     ReplaceSimpleStringFastPath()  // Bails to runtime for global regexps.
   2611   //   }
   2612   // }
   2613 
   2614   // Ensure {maybe_receiver} is a JSReceiver.
   2615   ThrowIfNotJSReceiver(context, maybe_receiver,
   2616                        MessageTemplate::kIncompatibleMethodReceiver,
   2617                        "RegExp.prototype.@@replace");
   2618   Node* const receiver = maybe_receiver;
   2619 
   2620   // Convert {maybe_string} to a String.
   2621   Callable tostring_callable = CodeFactory::ToString(isolate());
   2622   Node* const string = CallStub(tostring_callable, context, maybe_string);
   2623 
   2624   // Fast-path checks: 1. Is the {receiver} an unmodified JSRegExp instance?
   2625   Label stub(this), runtime(this, Label::kDeferred);
   2626   BranchIfFastRegExp(context, receiver, LoadMap(receiver), &stub, &runtime);
   2627 
   2628   Bind(&stub);
   2629   Callable replace_callable = CodeFactory::RegExpReplace(isolate());
   2630   Return(CallStub(replace_callable, context, receiver, string, replace_value));
   2631 
   2632   Bind(&runtime);
   2633   Return(CallRuntime(Runtime::kRegExpReplace, context, receiver, string,
   2634                      replace_value));
   2635 }
   2636 
   2637 // Simple string matching functionality for internal use which does not modify
   2638 // the last match info.
   2639 TF_BUILTIN(RegExpInternalMatch, RegExpBuiltinsAssembler) {
   2640   Node* const regexp = Parameter(1);
   2641   Node* const string = Parameter(2);
   2642   Node* const context = Parameter(5);
   2643 
   2644   Node* const null = NullConstant();
   2645   Node* const smi_zero = SmiConstant(Smi::FromInt(0));
   2646 
   2647   Node* const native_context = LoadNativeContext(context);
   2648   Node* const internal_match_info = LoadContextElement(
   2649       native_context, Context::REGEXP_INTERNAL_MATCH_INFO_INDEX);
   2650 
   2651   Callable exec_callable = CodeFactory::RegExpExec(isolate());
   2652   Node* const match_indices = CallStub(exec_callable, context, regexp, string,
   2653                                        smi_zero, internal_match_info);
   2654 
   2655   Label if_matched(this), if_didnotmatch(this);
   2656   Branch(WordEqual(match_indices, null), &if_didnotmatch, &if_matched);
   2657 
   2658   Bind(&if_didnotmatch);
   2659   Return(null);
   2660 
   2661   Bind(&if_matched);
   2662   {
   2663     Node* result =
   2664         ConstructNewResultFromMatchInfo(context, regexp, match_indices, string);
   2665     Return(result);
   2666   }
   2667 }
   2668 
   2669 }  // namespace internal
   2670 }  // namespace v8
   2671