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
19namespace v8 {
20namespace internal {
21
22typedef CodeStubAssembler::ParameterMode ParameterMode;
23
24
25// -----------------------------------------------------------------------------
26// ES6 section 21.2 RegExp Objects
27
28Node* 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
35Node* 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
42Node* 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.
50void 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
57void 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
67void 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
76Node* 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.
224Node* 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 )
352Node* 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
383Node* 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
416Node* 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.
445void 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
478Node* 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
498void 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 )
511TF_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
543Node* 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 )
643Node* 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 )
680Node* 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
702TF_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 )
724TF_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 )
856TF_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.
905TF_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
960BUILTIN(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.
998Node* 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.
1009Node* 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
1061Node* 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
1069void 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.
1130TF_BUILTIN(RegExpPrototypeGlobalGetter, RegExpBuiltinsAssembler) {
1131  FlagGetter(JSRegExp::kGlobal, v8::Isolate::kRegExpPrototypeOldFlagGetter,
1132             "RegExp.prototype.global");
1133}
1134
1135// ES6 21.2.5.5.
1136TF_BUILTIN(RegExpPrototypeIgnoreCaseGetter, RegExpBuiltinsAssembler) {
1137  FlagGetter(JSRegExp::kIgnoreCase, v8::Isolate::kRegExpPrototypeOldFlagGetter,
1138             "RegExp.prototype.ignoreCase");
1139}
1140
1141// ES6 21.2.5.7.
1142TF_BUILTIN(RegExpPrototypeMultilineGetter, RegExpBuiltinsAssembler) {
1143  FlagGetter(JSRegExp::kMultiline, v8::Isolate::kRegExpPrototypeOldFlagGetter,
1144             "RegExp.prototype.multiline");
1145}
1146
1147// ES6 21.2.5.12.
1148TF_BUILTIN(RegExpPrototypeStickyGetter, RegExpBuiltinsAssembler) {
1149  FlagGetter(JSRegExp::kSticky, v8::Isolate::kRegExpPrototypeStickyGetter,
1150             "RegExp.prototype.sticky");
1151}
1152
1153// ES6 21.2.5.15.
1154TF_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  }
1168DEFINE_CAPTURE_GETTER(1)
1169DEFINE_CAPTURE_GETTER(2)
1170DEFINE_CAPTURE_GETTER(3)
1171DEFINE_CAPTURE_GETTER(4)
1172DEFINE_CAPTURE_GETTER(5)
1173DEFINE_CAPTURE_GETTER(6)
1174DEFINE_CAPTURE_GETTER(7)
1175DEFINE_CAPTURE_GETTER(8)
1176DEFINE_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
1183BUILTIN(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
1190BUILTIN(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.
1204BUILTIN(RegExpLastMatchGetter) {
1205  HandleScope scope(isolate);
1206  return *RegExpUtils::GenericCaptureGetter(
1207      isolate, isolate->regexp_last_match_info(), 0);
1208}
1209
1210BUILTIN(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
1225BUILTIN(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
1233BUILTIN(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 )
1243Node* 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 )
1312TF_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
1353Node* 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
1412namespace {
1413
1414// Utility class implementing a growable fixed array through CSA.
1415class 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
1563void 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 )
1722TF_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
1747void 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
1779void 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 )
1848TF_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.
1875void 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.
2120TF_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 )
2184TF_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
2211Node* 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
2418Node* 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.
2518TF_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 )
2590TF_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.
2639TF_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