V8 API Reference, 7.2.502.16 (for Deno 0.2.4)
builtins-regexp-gen.cc
1 // Copyright 2017 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-gen.h"
6 
7 #include "src/builtins/builtins-constructor-gen.h"
8 #include "src/builtins/builtins-utils-gen.h"
9 #include "src/builtins/builtins.h"
10 #include "src/builtins/growable-fixed-array-gen.h"
11 #include "src/code-factory.h"
12 #include "src/code-stub-assembler.h"
13 #include "src/counters.h"
14 #include "src/heap/factory-inl.h"
15 #include "src/objects/js-regexp-string-iterator.h"
16 #include "src/objects/js-regexp.h"
17 #include "src/objects/regexp-match-info.h"
18 #include "src/regexp/regexp-macro-assembler.h"
19 
20 namespace v8 {
21 namespace internal {
22 
23 using compiler::Node;
24 template <class T>
25 using TNode = compiler::TNode<T>;
26 
27 TNode<Smi> RegExpBuiltinsAssembler::SmiZero() { return SmiConstant(0); }
28 
29 TNode<IntPtrT> RegExpBuiltinsAssembler::IntPtrZero() {
30  return IntPtrConstant(0);
31 }
32 
33 // -----------------------------------------------------------------------------
34 // ES6 section 21.2 RegExp Objects
35 
36 TNode<JSRegExpResult> RegExpBuiltinsAssembler::AllocateRegExpResult(
37  TNode<Context> context, TNode<Smi> length, TNode<Smi> index,
38  TNode<String> input) {
39 #ifdef DEBUG
40  TNode<Smi> max_length = SmiConstant(JSArray::kInitialMaxFastElementArray);
41  CSA_ASSERT(this, SmiLessThanOrEqual(length, max_length));
42 #endif // DEBUG
43 
44  // Allocate the JSRegExpResult together with its elements fixed array.
45  // Initial preparations first.
46 
47  TNode<IntPtrT> length_intptr = SmiUntag(length);
48  const ElementsKind elements_kind = PACKED_ELEMENTS;
49 
50  TNode<IntPtrT> elements_size = GetFixedArrayAllocationSize(
51  length_intptr, elements_kind, INTPTR_PARAMETERS);
52  TNode<IntPtrT> total_size =
53  IntPtrAdd(elements_size, IntPtrConstant(JSRegExpResult::kSize));
54 
55  static const int kRegExpResultOffset = 0;
56  static const int kElementsOffset =
57  kRegExpResultOffset + JSRegExpResult::kSize;
58 
59  // The folded allocation.
60 
61  TNode<HeapObject> result = Allocate(total_size);
62  TNode<HeapObject> elements = InnerAllocate(result, kElementsOffset);
63 
64  // Initialize the JSRegExpResult.
65 
66  TNode<Context> native_context = LoadNativeContext(context);
67  TNode<Map> map = CAST(
68  LoadContextElement(native_context, Context::REGEXP_RESULT_MAP_INDEX));
69  StoreMapNoWriteBarrier(result, map);
70 
71  StoreObjectFieldNoWriteBarrier(result, JSArray::kPropertiesOrHashOffset,
72  EmptyFixedArrayConstant());
73  StoreObjectFieldNoWriteBarrier(result, JSArray::kElementsOffset, elements);
74  StoreObjectFieldNoWriteBarrier(result, JSArray::kLengthOffset, length);
75 
76  StoreObjectFieldNoWriteBarrier(result, JSRegExpResult::kIndexOffset, index);
77  StoreObjectFieldNoWriteBarrier(result, JSRegExpResult::kInputOffset, input);
78  StoreObjectFieldNoWriteBarrier(result, JSRegExpResult::kGroupsOffset,
79  UndefinedConstant());
80 
81  // Initialize the elements.
82 
83  DCHECK(!IsDoubleElementsKind(elements_kind));
84  const RootIndex map_index = RootIndex::kFixedArrayMap;
85  DCHECK(RootsTable::IsImmortalImmovable(map_index));
86  StoreMapNoWriteBarrier(elements, map_index);
87  StoreObjectFieldNoWriteBarrier(elements, FixedArray::kLengthOffset, length);
88 
89  FillFixedArrayWithValue(elements_kind, elements, IntPtrZero(), length_intptr,
90  RootIndex::kUndefinedValue);
91 
92  return CAST(result);
93 }
94 
95 TNode<Object> RegExpBuiltinsAssembler::RegExpCreate(
96  TNode<Context> context, TNode<Context> native_context,
97  TNode<Object> maybe_string, TNode<String> flags) {
98  TNode<JSFunction> regexp_function =
99  CAST(LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX));
100  TNode<Map> initial_map = CAST(LoadObjectField(
101  regexp_function, JSFunction::kPrototypeOrInitialMapOffset));
102  return RegExpCreate(context, initial_map, maybe_string, flags);
103 }
104 
105 TNode<Object> RegExpBuiltinsAssembler::RegExpCreate(TNode<Context> context,
106  TNode<Map> initial_map,
107  TNode<Object> maybe_string,
108  TNode<String> flags) {
109  TNode<String> pattern = Select<String>(
110  IsUndefined(maybe_string), [=] { return EmptyStringConstant(); },
111  [=] { return ToString_Inline(context, maybe_string); });
112  TNode<Object> regexp = CAST(AllocateJSObjectFromMap(initial_map));
113  return CallRuntime(Runtime::kRegExpInitializeAndCompile, context, regexp,
114  pattern, flags);
115 }
116 
117 TNode<Object> RegExpBuiltinsAssembler::FastLoadLastIndex(
118  TNode<JSRegExp> regexp) {
119  // Load the in-object field.
120  static const int field_offset =
121  JSRegExp::kSize + JSRegExp::kLastIndexFieldIndex * kPointerSize;
122  return LoadObjectField(regexp, field_offset);
123 }
124 
125 TNode<Object> RegExpBuiltinsAssembler::SlowLoadLastIndex(TNode<Context> context,
126  TNode<Object> regexp) {
127  return GetProperty(context, regexp, isolate()->factory()->lastIndex_string());
128 }
129 
130 TNode<Object> RegExpBuiltinsAssembler::LoadLastIndex(TNode<Context> context,
131  TNode<Object> regexp,
132  bool is_fastpath) {
133  return is_fastpath ? FastLoadLastIndex(CAST(regexp))
134  : SlowLoadLastIndex(context, regexp);
135 }
136 
137 // The fast-path of StoreLastIndex when regexp is guaranteed to be an unmodified
138 // JSRegExp instance.
139 void RegExpBuiltinsAssembler::FastStoreLastIndex(Node* regexp, Node* value) {
140  // Store the in-object field.
141  static const int field_offset =
142  JSRegExp::kSize + JSRegExp::kLastIndexFieldIndex * kPointerSize;
143  StoreObjectField(regexp, field_offset, value);
144 }
145 
146 void RegExpBuiltinsAssembler::SlowStoreLastIndex(Node* context, Node* regexp,
147  Node* value) {
148  Node* const name = HeapConstant(isolate()->factory()->lastIndex_string());
149  SetPropertyStrict(CAST(context), CAST(regexp), CAST(name), CAST(value));
150 }
151 
152 void RegExpBuiltinsAssembler::StoreLastIndex(Node* context, Node* regexp,
153  Node* value, bool is_fastpath) {
154  if (is_fastpath) {
155  FastStoreLastIndex(regexp, value);
156  } else {
157  SlowStoreLastIndex(context, regexp, value);
158  }
159 }
160 
161 TNode<JSRegExpResult> RegExpBuiltinsAssembler::ConstructNewResultFromMatchInfo(
162  TNode<Context> context, TNode<JSReceiver> maybe_regexp,
163  TNode<RegExpMatchInfo> match_info, TNode<String> string) {
164  Label named_captures(this), out(this);
165 
166  TNode<IntPtrT> num_indices = SmiUntag(CAST(LoadFixedArrayElement(
167  match_info, RegExpMatchInfo::kNumberOfCapturesIndex)));
168  TNode<Smi> num_results = SmiTag(WordShr(num_indices, 1));
169  TNode<Smi> start = CAST(
170  LoadFixedArrayElement(match_info, RegExpMatchInfo::kFirstCaptureIndex));
171  TNode<Smi> end = CAST(LoadFixedArrayElement(
172  match_info, RegExpMatchInfo::kFirstCaptureIndex + 1));
173 
174  // Calculate the substring of the first match before creating the result array
175  // to avoid an unnecessary write barrier storing the first result.
176 
177  TNode<String> first =
178  CAST(CallBuiltin(Builtins::kSubString, context, string, start, end));
179 
180  TNode<JSRegExpResult> result =
181  AllocateRegExpResult(context, num_results, start, string);
182  TNode<FixedArray> result_elements = CAST(LoadElements(result));
183 
184  StoreFixedArrayElement(result_elements, 0, first, SKIP_WRITE_BARRIER);
185 
186  // If no captures exist we can skip named capture handling as well.
187  GotoIf(SmiEqual(num_results, SmiConstant(1)), &out);
188 
189  // Store all remaining captures.
190  TNode<IntPtrT> limit = IntPtrAdd(
191  IntPtrConstant(RegExpMatchInfo::kFirstCaptureIndex), num_indices);
192 
193  TVARIABLE(IntPtrT, var_from_cursor,
194  IntPtrConstant(RegExpMatchInfo::kFirstCaptureIndex + 2));
195  TVARIABLE(IntPtrT, var_to_cursor, IntPtrConstant(1));
196 
197  Variable* vars[] = {&var_from_cursor, &var_to_cursor};
198  Label loop(this, 2, vars);
199 
200  Goto(&loop);
201  BIND(&loop);
202  {
203  TNode<IntPtrT> from_cursor = var_from_cursor.value();
204  TNode<IntPtrT> to_cursor = var_to_cursor.value();
205  TNode<Smi> start = CAST(LoadFixedArrayElement(match_info, from_cursor));
206 
207  Label next_iter(this);
208  GotoIf(SmiEqual(start, SmiConstant(-1)), &next_iter);
209 
210  TNode<IntPtrT> from_cursor_plus1 =
211  IntPtrAdd(from_cursor, IntPtrConstant(1));
212  TNode<Smi> end = CAST(LoadFixedArrayElement(match_info, from_cursor_plus1));
213 
214  TNode<String> capture =
215  CAST(CallBuiltin(Builtins::kSubString, context, string, start, end));
216  StoreFixedArrayElement(result_elements, to_cursor, capture);
217  Goto(&next_iter);
218 
219  BIND(&next_iter);
220  var_from_cursor = IntPtrAdd(from_cursor, IntPtrConstant(2));
221  var_to_cursor = IntPtrAdd(to_cursor, IntPtrConstant(1));
222  Branch(UintPtrLessThan(var_from_cursor.value(), limit), &loop,
223  &named_captures);
224  }
225 
226  BIND(&named_captures);
227  {
228  CSA_ASSERT(this, SmiGreaterThan(num_results, SmiConstant(1)));
229 
230  // We reach this point only if captures exist, implying that this is an
231  // IRREGEXP JSRegExp.
232 
233  TNode<JSRegExp> regexp = CAST(maybe_regexp);
234 
235  // Preparations for named capture properties. Exit early if the result does
236  // not have any named captures to minimize performance impact.
237 
238  TNode<FixedArray> data =
239  CAST(LoadObjectField(regexp, JSRegExp::kDataOffset));
240  CSA_ASSERT(this,
241  SmiEqual(CAST(LoadFixedArrayElement(data, JSRegExp::kTagIndex)),
242  SmiConstant(JSRegExp::IRREGEXP)));
243 
244  // The names fixed array associates names at even indices with a capture
245  // index at odd indices.
246  TNode<Object> maybe_names =
247  LoadFixedArrayElement(data, JSRegExp::kIrregexpCaptureNameMapIndex);
248  GotoIf(WordEqual(maybe_names, SmiZero()), &out);
249 
250  // Allocate a new object to store the named capture properties.
251  // TODO(jgruber): Could be optimized by adding the object map to the heap
252  // root list.
253 
254  TNode<Context> native_context = LoadNativeContext(context);
255  TNode<Map> map = CAST(LoadContextElement(
256  native_context, Context::SLOW_OBJECT_WITH_NULL_PROTOTYPE_MAP));
257  TNode<NameDictionary> properties =
258  AllocateNameDictionary(NameDictionary::kInitialCapacity);
259 
260  TNode<JSObject> group_object =
261  CAST(AllocateJSObjectFromMap(map, properties));
262  StoreObjectField(result, JSRegExpResult::kGroupsOffset, group_object);
263 
264  // One or more named captures exist, add a property for each one.
265 
266  TNode<FixedArray> names = CAST(maybe_names);
267  TNode<IntPtrT> names_length = LoadAndUntagFixedArrayBaseLength(names);
268  CSA_ASSERT(this, IntPtrGreaterThan(names_length, IntPtrZero()));
269 
270  TVARIABLE(IntPtrT, var_i, IntPtrZero());
271 
272  Variable* vars[] = {&var_i};
273  const int vars_count = sizeof(vars) / sizeof(vars[0]);
274  Label loop(this, vars_count, vars);
275 
276  Goto(&loop);
277  BIND(&loop);
278  {
279  TNode<IntPtrT> i = var_i.value();
280  TNode<IntPtrT> i_plus_1 = IntPtrAdd(i, IntPtrConstant(1));
281  TNode<IntPtrT> i_plus_2 = IntPtrAdd(i_plus_1, IntPtrConstant(1));
282 
283  TNode<String> name = CAST(LoadFixedArrayElement(names, i));
284  TNode<Smi> index = CAST(LoadFixedArrayElement(names, i_plus_1));
285  TNode<HeapObject> capture =
286  CAST(LoadFixedArrayElement(result_elements, SmiUntag(index)));
287 
288  // TODO(jgruber): Calling into runtime to create each property is slow.
289  // Either we should create properties entirely in CSA (should be doable),
290  // or only call runtime once and loop there.
291  CallRuntime(Runtime::kCreateDataProperty, context, group_object, name,
292  capture);
293 
294  var_i = i_plus_2;
295  Branch(IntPtrGreaterThanOrEqual(var_i.value(), names_length), &out,
296  &loop);
297  }
298  }
299 
300  BIND(&out);
301  return result;
302 }
303 
304 void RegExpBuiltinsAssembler::GetStringPointers(
305  Node* const string_data, Node* const offset, Node* const last_index,
306  Node* const string_length, String::Encoding encoding,
307  Variable* var_string_start, Variable* var_string_end) {
308  DCHECK_EQ(var_string_start->rep(), MachineType::PointerRepresentation());
309  DCHECK_EQ(var_string_end->rep(), MachineType::PointerRepresentation());
310 
311  const ElementsKind kind = (encoding == String::ONE_BYTE_ENCODING)
312  ? UINT8_ELEMENTS
313  : UINT16_ELEMENTS;
314 
315  Node* const from_offset = ElementOffsetFromIndex(
316  IntPtrAdd(offset, last_index), kind, INTPTR_PARAMETERS);
317  var_string_start->Bind(IntPtrAdd(string_data, from_offset));
318 
319  Node* const to_offset = ElementOffsetFromIndex(
320  IntPtrAdd(offset, string_length), kind, INTPTR_PARAMETERS);
321  var_string_end->Bind(IntPtrAdd(string_data, to_offset));
322 }
323 
324 TNode<HeapObject> RegExpBuiltinsAssembler::RegExpExecInternal(
325  TNode<Context> context, TNode<JSRegExp> regexp, TNode<String> string,
326  TNode<Number> last_index, TNode<RegExpMatchInfo> match_info) {
327 // Just jump directly to runtime if native RegExp is not selected at compile
328 // time or if regexp entry in generated code is turned off runtime switch or
329 // at compilation.
330 #ifdef V8_INTERPRETED_REGEXP
331  return CAST(CallRuntime(Runtime::kRegExpExec, context, regexp, string,
332  last_index, match_info));
333 #else // V8_INTERPRETED_REGEXP
334  ToDirectStringAssembler to_direct(state(), string);
335 
336  TVARIABLE(HeapObject, var_result);
337  Label out(this), atom(this), runtime(this, Label::kDeferred);
338 
339  // External constants.
340  TNode<ExternalReference> isolate_address =
341  ExternalConstant(ExternalReference::isolate_address(isolate()));
342  TNode<ExternalReference> regexp_stack_memory_address_address =
343  ExternalConstant(
344  ExternalReference::address_of_regexp_stack_memory_address(isolate()));
345  TNode<ExternalReference> regexp_stack_memory_size_address = ExternalConstant(
346  ExternalReference::address_of_regexp_stack_memory_size(isolate()));
347  TNode<ExternalReference> static_offsets_vector_address = ExternalConstant(
348  ExternalReference::address_of_static_offsets_vector(isolate()));
349 
350  // At this point, last_index is definitely a canonicalized non-negative
351  // number, which implies that any non-Smi last_index is greater than
352  // the maximal string length. If lastIndex > string.length then the matcher
353  // must fail.
354 
355  Label if_failure(this);
356 
357  CSA_ASSERT(this, IsNumberNormalized(last_index));
358  CSA_ASSERT(this, IsNumberPositive(last_index));
359  GotoIf(TaggedIsNotSmi(last_index), &if_failure);
360 
361  TNode<IntPtrT> int_string_length = LoadStringLengthAsWord(string);
362  TNode<IntPtrT> int_last_index = SmiUntag(CAST(last_index));
363 
364  GotoIf(UintPtrGreaterThan(int_last_index, int_string_length), &if_failure);
365 
366  // Since the RegExp has been compiled, data contains a fixed array.
367  TNode<FixedArray> data = CAST(LoadObjectField(regexp, JSRegExp::kDataOffset));
368  {
369  // Dispatch on the type of the RegExp.
370  {
371  Label next(this), unreachable(this, Label::kDeferred);
372  TNode<Int32T> tag = LoadAndUntagToWord32FixedArrayElement(
373  data, IntPtrConstant(JSRegExp::kTagIndex));
374 
375  int32_t values[] = {
376  JSRegExp::IRREGEXP, JSRegExp::ATOM, JSRegExp::NOT_COMPILED,
377  };
378  Label* labels[] = {&next, &atom, &runtime};
379 
380  STATIC_ASSERT(arraysize(values) == arraysize(labels));
381  Switch(tag, &unreachable, values, labels, arraysize(values));
382 
383  BIND(&unreachable);
384  Unreachable();
385 
386  BIND(&next);
387  }
388 
389  // Check (number_of_captures + 1) * 2 <= offsets vector size
390  // Or number_of_captures <= offsets vector size / 2 - 1
391  TNode<Smi> capture_count =
392  CAST(LoadFixedArrayElement(data, JSRegExp::kIrregexpCaptureCountIndex));
393 
394  const int kOffsetsSize = Isolate::kJSRegexpStaticOffsetsVectorSize;
395  STATIC_ASSERT(kOffsetsSize >= 2);
396  GotoIf(SmiAbove(capture_count, SmiConstant(kOffsetsSize / 2 - 1)),
397  &runtime);
398  }
399 
400  // Ensure that a RegExp stack is allocated. This check is after branching off
401  // for ATOM regexps to avoid unnecessary trips to runtime.
402  {
403  TNode<IntPtrT> stack_size = UncheckedCast<IntPtrT>(
404  Load(MachineType::IntPtr(), regexp_stack_memory_size_address));
405  GotoIf(IntPtrEqual(stack_size, IntPtrZero()), &runtime);
406  }
407 
408  // Unpack the string if possible.
409 
410  to_direct.TryToDirect(&runtime);
411 
412  // Load the irregexp code object and offsets into the subject string. Both
413  // depend on whether the string is one- or two-byte.
414 
415  TVARIABLE(RawPtrT, var_string_start);
416  TVARIABLE(RawPtrT, var_string_end);
417  TVARIABLE(Object, var_code);
418 
419  {
420  TNode<RawPtrT> direct_string_data = to_direct.PointerToData(&runtime);
421 
422  Label next(this), if_isonebyte(this), if_istwobyte(this, Label::kDeferred);
423  Branch(IsOneByteStringInstanceType(to_direct.instance_type()),
424  &if_isonebyte, &if_istwobyte);
425 
426  BIND(&if_isonebyte);
427  {
428  GetStringPointers(direct_string_data, to_direct.offset(), int_last_index,
429  int_string_length, String::ONE_BYTE_ENCODING,
430  &var_string_start, &var_string_end);
431  var_code =
432  LoadFixedArrayElement(data, JSRegExp::kIrregexpLatin1CodeIndex);
433  Goto(&next);
434  }
435 
436  BIND(&if_istwobyte);
437  {
438  GetStringPointers(direct_string_data, to_direct.offset(), int_last_index,
439  int_string_length, String::TWO_BYTE_ENCODING,
440  &var_string_start, &var_string_end);
441  var_code = LoadFixedArrayElement(data, JSRegExp::kIrregexpUC16CodeIndex);
442  Goto(&next);
443  }
444 
445  BIND(&next);
446  }
447 
448  // Check that the irregexp code has been generated for the actual string
449  // encoding. If it has, the field contains a code object; and otherwise it
450  // contains the uninitialized sentinel as a smi.
451 #ifdef DEBUG
452  {
453  Label next(this);
454  GotoIfNot(TaggedIsSmi(var_code.value()), &next);
455  CSA_ASSERT(this, SmiEqual(CAST(var_code.value()),
456  SmiConstant(JSRegExp::kUninitializedValue)));
457  Goto(&next);
458  BIND(&next);
459  }
460 #endif
461 
462  GotoIf(TaggedIsSmi(var_code.value()), &runtime);
463  TNode<Code> code = CAST(var_code.value());
464 
465  Label if_success(this), if_exception(this, Label::kDeferred);
466  {
467  IncrementCounter(isolate()->counters()->regexp_entry_native(), 1);
468 
469  // Set up args for the final call into generated Irregexp code.
470 
471  MachineType type_int32 = MachineType::Int32();
472  MachineType type_tagged = MachineType::AnyTagged();
473  MachineType type_ptr = MachineType::Pointer();
474 
475  // Result: A NativeRegExpMacroAssembler::Result return code.
476  MachineType retval_type = type_int32;
477 
478  // Argument 0: Original subject string.
479  MachineType arg0_type = type_tagged;
480  TNode<String> arg0 = string;
481 
482  // Argument 1: Previous index.
483  MachineType arg1_type = type_int32;
484  TNode<Int32T> arg1 = TruncateIntPtrToInt32(int_last_index);
485 
486  // Argument 2: Start of string data.
487  MachineType arg2_type = type_ptr;
488  TNode<RawPtrT> arg2 = var_string_start.value();
489 
490  // Argument 3: End of string data.
491  MachineType arg3_type = type_ptr;
492  TNode<RawPtrT> arg3 = var_string_end.value();
493 
494  // Argument 4: static offsets vector buffer.
495  MachineType arg4_type = type_ptr;
496  TNode<ExternalReference> arg4 = static_offsets_vector_address;
497 
498  // Argument 5: Set the number of capture registers to zero to force global
499  // regexps to behave as non-global. This does not affect non-global
500  // regexps.
501  MachineType arg5_type = type_int32;
502  TNode<Int32T> arg5 = Int32Constant(0);
503 
504  // Argument 6: Start (high end) of backtracking stack memory area.
505  TNode<RawPtrT> stack_start = UncheckedCast<RawPtrT>(
506  Load(MachineType::Pointer(), regexp_stack_memory_address_address));
507  TNode<IntPtrT> stack_size = UncheckedCast<IntPtrT>(
508  Load(MachineType::IntPtr(), regexp_stack_memory_size_address));
509  TNode<RawPtrT> stack_end =
510  ReinterpretCast<RawPtrT>(IntPtrAdd(stack_start, stack_size));
511 
512  MachineType arg6_type = type_ptr;
513  TNode<RawPtrT> arg6 = stack_end;
514 
515  // Argument 7: Indicate that this is a direct call from JavaScript.
516  MachineType arg7_type = type_int32;
517  TNode<Int32T> arg7 = Int32Constant(1);
518 
519  // Argument 8: Pass current isolate address.
520  MachineType arg8_type = type_ptr;
521  TNode<ExternalReference> arg8 = isolate_address;
522 
523  TNode<RawPtrT> code_entry = ReinterpretCast<RawPtrT>(
524  IntPtrAdd(BitcastTaggedToWord(code),
525  IntPtrConstant(Code::kHeaderSize - kHeapObjectTag)));
526 
527  TNode<Int32T> result = UncheckedCast<Int32T>(CallCFunction9(
528  retval_type, arg0_type, arg1_type, arg2_type, arg3_type, arg4_type,
529  arg5_type, arg6_type, arg7_type, arg8_type, code_entry, arg0, arg1,
530  arg2, arg3, arg4, arg5, arg6, arg7, arg8));
531 
532  // Check the result.
533  // We expect exactly one result since we force the called regexp to behave
534  // as non-global.
535  TNode<IntPtrT> int_result = ChangeInt32ToIntPtr(result);
536  GotoIf(IntPtrEqual(int_result,
537  IntPtrConstant(NativeRegExpMacroAssembler::SUCCESS)),
538  &if_success);
539  GotoIf(IntPtrEqual(int_result,
540  IntPtrConstant(NativeRegExpMacroAssembler::FAILURE)),
541  &if_failure);
542  GotoIf(IntPtrEqual(int_result,
543  IntPtrConstant(NativeRegExpMacroAssembler::EXCEPTION)),
544  &if_exception);
545 
546  CSA_ASSERT(this,
547  IntPtrEqual(int_result,
548  IntPtrConstant(NativeRegExpMacroAssembler::RETRY)));
549  Goto(&runtime);
550  }
551 
552  BIND(&if_success);
553  {
554  // Check that the last match info has space for the capture registers and
555  // the additional information. Ensure no overflow in add.
556  STATIC_ASSERT(FixedArray::kMaxLength < kMaxInt - FixedArray::kLengthOffset);
557  TNode<Smi> available_slots =
558  SmiSub(LoadFixedArrayBaseLength(match_info),
559  SmiConstant(RegExpMatchInfo::kLastMatchOverhead));
560  TNode<Smi> capture_count =
561  CAST(LoadFixedArrayElement(data, JSRegExp::kIrregexpCaptureCountIndex));
562  // Calculate number of register_count = (capture_count + 1) * 2.
563  TNode<Smi> register_count =
564  SmiShl(SmiAdd(capture_count, SmiConstant(1)), 1);
565  GotoIf(SmiGreaterThan(register_count, available_slots), &runtime);
566 
567  // Fill match_info.
568 
569  StoreFixedArrayElement(match_info, RegExpMatchInfo::kNumberOfCapturesIndex,
570  register_count, SKIP_WRITE_BARRIER);
571  StoreFixedArrayElement(match_info, RegExpMatchInfo::kLastSubjectIndex,
572  string);
573  StoreFixedArrayElement(match_info, RegExpMatchInfo::kLastInputIndex,
574  string);
575 
576  // Fill match and capture offsets in match_info.
577  {
578  TNode<IntPtrT> limit_offset = ElementOffsetFromIndex(
579  register_count, INT32_ELEMENTS, SMI_PARAMETERS, 0);
580 
581  TNode<IntPtrT> to_offset = ElementOffsetFromIndex(
582  IntPtrConstant(RegExpMatchInfo::kFirstCaptureIndex), PACKED_ELEMENTS,
583  INTPTR_PARAMETERS, RegExpMatchInfo::kHeaderSize - kHeapObjectTag);
584  TVARIABLE(IntPtrT, var_to_offset, to_offset);
585 
586  VariableList vars({&var_to_offset}, zone());
587  BuildFastLoop(
588  vars, IntPtrZero(), limit_offset,
589  [=, &var_to_offset](Node* offset) {
590  TNode<Int32T> value = UncheckedCast<Int32T>(Load(
591  MachineType::Int32(), static_offsets_vector_address, offset));
592  TNode<Smi> smi_value = SmiFromInt32(value);
593  StoreNoWriteBarrier(MachineRepresentation::kTagged, match_info,
594  var_to_offset.value(), smi_value);
595  Increment(&var_to_offset, kPointerSize);
596  },
597  kInt32Size, INTPTR_PARAMETERS, IndexAdvanceMode::kPost);
598  }
599 
600  var_result = match_info;
601  Goto(&out);
602  }
603 
604  BIND(&if_failure);
605  {
606  var_result = NullConstant();
607  Goto(&out);
608  }
609 
610  BIND(&if_exception);
611  {
612 // A stack overflow was detected in RegExp code.
613 #ifdef DEBUG
614  TNode<ExternalReference> pending_exception_address =
615  ExternalConstant(ExternalReference::Create(
616  IsolateAddressId::kPendingExceptionAddress, isolate()));
617  CSA_ASSERT(this, IsTheHole(Load(MachineType::AnyTagged(),
618  pending_exception_address)));
619 #endif // DEBUG
620  CallRuntime(Runtime::kThrowStackOverflow, context);
621  Unreachable();
622  }
623 
624  BIND(&runtime);
625  {
626  var_result = CAST(CallRuntime(Runtime::kRegExpExec, context, regexp, string,
627  last_index, match_info));
628  Goto(&out);
629  }
630 
631  BIND(&atom);
632  {
633  // TODO(jgruber): A call with 4 args stresses register allocation, this
634  // should probably just be inlined.
635  var_result = CAST(CallBuiltin(Builtins::kRegExpExecAtom, context, regexp,
636  string, last_index, match_info));
637  Goto(&out);
638  }
639 
640  BIND(&out);
641  return var_result.value();
642 #endif // V8_INTERPRETED_REGEXP
643 }
644 
645 // ES#sec-regexp.prototype.exec
646 // RegExp.prototype.exec ( string )
647 // Implements the core of RegExp.prototype.exec but without actually
648 // constructing the JSRegExpResult. Returns a fixed array containing match
649 // indices as returned by RegExpExecStub on successful match, and jumps to
650 // if_didnotmatch otherwise.
651 TNode<RegExpMatchInfo>
652 RegExpBuiltinsAssembler::RegExpPrototypeExecBodyWithoutResult(
653  TNode<Context> context, TNode<JSReceiver> maybe_regexp,
654  TNode<String> string, Label* if_didnotmatch, const bool is_fastpath) {
655  if (!is_fastpath) {
656  ThrowIfNotInstanceType(context, maybe_regexp, JS_REGEXP_TYPE,
657  "RegExp.prototype.exec");
658  }
659 
660  TNode<JSRegExp> regexp = CAST(maybe_regexp);
661 
662  TVARIABLE(HeapObject, var_result);
663  Label out(this);
664 
665  // Load lastIndex.
666  TVARIABLE(Number, var_lastindex);
667  {
668  TNode<Object> regexp_lastindex =
669  LoadLastIndex(context, regexp, is_fastpath);
670 
671  if (is_fastpath) {
672  // ToLength on a positive smi is a nop and can be skipped.
673  CSA_ASSERT(this, TaggedIsPositiveSmi(regexp_lastindex));
674  var_lastindex = CAST(regexp_lastindex);
675  } else {
676  // Omit ToLength if lastindex is a non-negative smi.
677  Label call_tolength(this, Label::kDeferred), is_smi(this), next(this);
678  Branch(TaggedIsPositiveSmi(regexp_lastindex), &is_smi, &call_tolength);
679 
680  BIND(&call_tolength);
681  var_lastindex = ToLength_Inline(context, regexp_lastindex);
682  Goto(&next);
683 
684  BIND(&is_smi);
685  var_lastindex = CAST(regexp_lastindex);
686  Goto(&next);
687 
688  BIND(&next);
689  }
690  }
691 
692  // Check whether the regexp is global or sticky, which determines whether we
693  // update last index later on.
694  TNode<Smi> flags = CAST(LoadObjectField(regexp, JSRegExp::kFlagsOffset));
695  TNode<IntPtrT> is_global_or_sticky = WordAnd(
696  SmiUntag(flags), IntPtrConstant(JSRegExp::kGlobal | JSRegExp::kSticky));
697  TNode<BoolT> should_update_last_index =
698  WordNotEqual(is_global_or_sticky, IntPtrZero());
699 
700  // Grab and possibly update last index.
701  Label run_exec(this);
702  {
703  Label if_doupdate(this), if_dontupdate(this);
704  Branch(should_update_last_index, &if_doupdate, &if_dontupdate);
705 
706  BIND(&if_doupdate);
707  {
708  Label if_isoob(this, Label::kDeferred);
709  GotoIfNot(TaggedIsSmi(var_lastindex.value()), &if_isoob);
710  TNode<Smi> string_length = LoadStringLengthAsSmi(string);
711  GotoIfNot(SmiLessThanOrEqual(CAST(var_lastindex.value()), string_length),
712  &if_isoob);
713  Goto(&run_exec);
714 
715  BIND(&if_isoob);
716  {
717  StoreLastIndex(context, regexp, SmiZero(), is_fastpath);
718  Goto(if_didnotmatch);
719  }
720  }
721 
722  BIND(&if_dontupdate);
723  {
724  var_lastindex = SmiZero();
725  Goto(&run_exec);
726  }
727  }
728 
729  TNode<HeapObject> match_indices;
730  Label successful_match(this);
731  BIND(&run_exec);
732  {
733  // Get last match info from the context.
734  TNode<Context> native_context = LoadNativeContext(context);
735  TNode<RegExpMatchInfo> last_match_info = CAST(LoadContextElement(
736  native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX));
737 
738  // Call the exec stub.
739  match_indices = RegExpExecInternal(context, regexp, string,
740  var_lastindex.value(), last_match_info);
741  var_result = match_indices;
742 
743  // {match_indices} is either null or the RegExpMatchInfo array.
744  // Return early if exec failed, possibly updating last index.
745  GotoIfNot(IsNull(match_indices), &successful_match);
746 
747  GotoIfNot(should_update_last_index, if_didnotmatch);
748 
749  StoreLastIndex(context, regexp, SmiZero(), is_fastpath);
750  Goto(if_didnotmatch);
751  }
752 
753  BIND(&successful_match);
754  {
755  GotoIfNot(should_update_last_index, &out);
756 
757  // Update the new last index from {match_indices}.
758  TNode<Number> new_lastindex = CAST(LoadFixedArrayElement(
759  CAST(match_indices), RegExpMatchInfo::kFirstCaptureIndex + 1));
760 
761  StoreLastIndex(context, regexp, new_lastindex, is_fastpath);
762  Goto(&out);
763  }
764 
765  BIND(&out);
766  return CAST(var_result.value());
767 }
768 
769 // ES#sec-regexp.prototype.exec
770 // RegExp.prototype.exec ( string )
771 TNode<HeapObject> RegExpBuiltinsAssembler::RegExpPrototypeExecBody(
772  TNode<Context> context, TNode<JSReceiver> maybe_regexp,
773  TNode<String> string, const bool is_fastpath) {
774  TVARIABLE(HeapObject, var_result);
775 
776  Label if_didnotmatch(this), out(this);
777  TNode<RegExpMatchInfo> match_indices = RegExpPrototypeExecBodyWithoutResult(
778  context, maybe_regexp, string, &if_didnotmatch, is_fastpath);
779 
780  // Successful match.
781  {
782  var_result = ConstructNewResultFromMatchInfo(context, maybe_regexp,
783  match_indices, string);
784  Goto(&out);
785  }
786 
787  BIND(&if_didnotmatch);
788  {
789  var_result = NullConstant();
790  Goto(&out);
791  }
792 
793  BIND(&out);
794  return var_result.value();
795 }
796 
797 Node* RegExpBuiltinsAssembler::ThrowIfNotJSReceiver(
798  Node* context, Node* maybe_receiver, MessageTemplate msg_template,
799  char const* method_name) {
800  Label out(this), throw_exception(this, Label::kDeferred);
801  VARIABLE(var_value_map, MachineRepresentation::kTagged);
802 
803  GotoIf(TaggedIsSmi(maybe_receiver), &throw_exception);
804 
805  // Load the instance type of the {value}.
806  var_value_map.Bind(LoadMap(maybe_receiver));
807  Node* const value_instance_type = LoadMapInstanceType(var_value_map.value());
808 
809  Branch(IsJSReceiverInstanceType(value_instance_type), &out, &throw_exception);
810 
811  // The {value} is not a compatible receiver for this method.
812  BIND(&throw_exception);
813  {
814  Node* const value_str =
815  CallBuiltin(Builtins::kToString, context, maybe_receiver);
816  ThrowTypeError(context, msg_template, StringConstant(method_name),
817  value_str);
818  }
819 
820  BIND(&out);
821  return var_value_map.value();
822 }
823 
824 Node* RegExpBuiltinsAssembler::IsFastRegExpNoPrototype(Node* const context,
825  Node* const object,
826  Node* const map) {
827  Label out(this);
828  VARIABLE(var_result, MachineRepresentation::kWord32);
829 
830 #ifdef V8_ENABLE_FORCE_SLOW_PATH
831  var_result.Bind(Int32Constant(0));
832  GotoIfForceSlowPath(&out);
833 #endif
834 
835  Node* const native_context = LoadNativeContext(context);
836  Node* const regexp_fun =
837  LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX);
838  Node* const initial_map =
839  LoadObjectField(regexp_fun, JSFunction::kPrototypeOrInitialMapOffset);
840  Node* const has_initialmap = WordEqual(map, initial_map);
841 
842  var_result.Bind(has_initialmap);
843  GotoIfNot(has_initialmap, &out);
844 
845  // The smi check is required to omit ToLength(lastIndex) calls with possible
846  // user-code execution on the fast path.
847  Node* const last_index = FastLoadLastIndex(CAST(object));
848  var_result.Bind(TaggedIsPositiveSmi(last_index));
849  Goto(&out);
850 
851  BIND(&out);
852  return var_result.value();
853 }
854 
855 // We also return true if exec is undefined (and hence per spec)
856 // the original {exec} will be used.
857 TNode<BoolT> RegExpBuiltinsAssembler::IsFastRegExpWithOriginalExec(
858  TNode<Context> context, TNode<JSRegExp> object) {
859  CSA_ASSERT(this, TaggedIsNotSmi(object));
860  Label out(this);
861  Label check_last_index(this);
862  TVARIABLE(BoolT, var_result);
863 
864 #ifdef V8_ENABLE_FORCE_SLOW_PATH
865  var_result = BoolConstant(false);
866  GotoIfForceSlowPath(&out);
867 #endif
868 
869  TNode<BoolT> is_regexp = HasInstanceType(object, JS_REGEXP_TYPE);
870 
871  var_result = is_regexp;
872  GotoIfNot(is_regexp, &out);
873 
874  TNode<Context> native_context = LoadNativeContext(context);
875  TNode<Object> original_exec =
876  LoadContextElement(native_context, Context::REGEXP_EXEC_FUNCTION_INDEX);
877 
878  TNode<Object> regexp_exec =
879  GetProperty(context, object, isolate()->factory()->exec_string());
880 
881  TNode<BoolT> has_initialexec = WordEqual(regexp_exec, original_exec);
882  var_result = has_initialexec;
883  GotoIf(has_initialexec, &check_last_index);
884  TNode<BoolT> is_undefined = IsUndefined(regexp_exec);
885  var_result = is_undefined;
886  GotoIfNot(is_undefined, &out);
887  Goto(&check_last_index);
888 
889  BIND(&check_last_index);
890  // The smi check is required to omit ToLength(lastIndex) calls with possible
891  // user-code execution on the fast path.
892  TNode<Object> last_index = FastLoadLastIndex(object);
893  var_result = TaggedIsPositiveSmi(last_index);
894  Goto(&out);
895 
896  BIND(&out);
897  return var_result.value();
898 }
899 
900 Node* RegExpBuiltinsAssembler::IsFastRegExpNoPrototype(Node* const context,
901  Node* const object) {
902  CSA_ASSERT(this, TaggedIsNotSmi(object));
903  return IsFastRegExpNoPrototype(context, object, LoadMap(object));
904 }
905 
906 // RegExp fast path implementations rely on unmodified JSRegExp instances.
907 // We use a fairly coarse granularity for this and simply check whether both
908 // the regexp itself is unmodified (i.e. its map has not changed), its
909 // prototype is unmodified, and lastIndex is a non-negative smi.
910 void RegExpBuiltinsAssembler::BranchIfFastRegExp(
911  Node* const context, Node* const object, Node* const map,
912  base::Optional<DescriptorIndexAndName> additional_property_to_check,
913  Label* const if_isunmodified, Label* const if_ismodified) {
914  CSA_ASSERT(this, WordEqual(LoadMap(object), map));
915 
916  GotoIfForceSlowPath(if_ismodified);
917 
918  // This should only be needed for String.p.(split||matchAll), but we are
919  // conservative here.
920  GotoIf(IsRegExpSpeciesProtectorCellInvalid(), if_ismodified);
921 
922  Node* const native_context = LoadNativeContext(context);
923  Node* const regexp_fun =
924  LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX);
925  Node* const initial_map =
926  LoadObjectField(regexp_fun, JSFunction::kPrototypeOrInitialMapOffset);
927  Node* const has_initialmap = WordEqual(map, initial_map);
928 
929  GotoIfNot(has_initialmap, if_ismodified);
930 
931  Node* const initial_proto_initial_map =
932  LoadContextElement(native_context, Context::REGEXP_PROTOTYPE_MAP_INDEX);
933 
934  DescriptorIndexAndName properties_to_check[2];
935  int property_count = 0;
936  properties_to_check[property_count++] = DescriptorIndexAndName{
937  JSRegExp::kExecFunctionDescriptorIndex, RootIndex::kexec_string};
938  if (additional_property_to_check) {
939  properties_to_check[property_count++] = *additional_property_to_check;
940  }
941 
942  GotoIfInitialPrototypePropertiesModified(
943  CAST(map), CAST(initial_proto_initial_map),
944  Vector<DescriptorIndexAndName>(properties_to_check, property_count),
945  if_ismodified);
946 
947  // The smi check is required to omit ToLength(lastIndex) calls with possible
948  // user-code execution on the fast path.
949  Node* const last_index = FastLoadLastIndex(CAST(object));
950  Branch(TaggedIsPositiveSmi(last_index), if_isunmodified, if_ismodified);
951 }
952 
953 void RegExpBuiltinsAssembler::BranchIfFastRegExp(Node* const context,
954  Node* const object,
955  Label* const if_isunmodified,
956  Label* const if_ismodified) {
957  CSA_ASSERT(this, TaggedIsNotSmi(object));
958  BranchIfFastRegExp(context, object, LoadMap(object), base::nullopt,
959  if_isunmodified, if_ismodified);
960 }
961 
962 TNode<BoolT> RegExpBuiltinsAssembler::IsFastRegExp(SloppyTNode<Context> context,
963  SloppyTNode<Object> object) {
964  Label yup(this), nope(this), out(this);
965  TVARIABLE(BoolT, var_result);
966 
967  BranchIfFastRegExp(context, object, &yup, &nope);
968 
969  BIND(&yup);
970  var_result = Int32TrueConstant();
971  Goto(&out);
972 
973  BIND(&nope);
974  var_result = Int32FalseConstant();
975  Goto(&out);
976 
977  BIND(&out);
978  return var_result.value();
979 }
980 
981 void RegExpBuiltinsAssembler::BranchIfFastRegExpResult(Node* const context,
982  Node* const object,
983  Label* if_isunmodified,
984  Label* if_ismodified) {
985  // Could be a Smi.
986  Node* const map = LoadReceiverMap(object);
987 
988  Node* const native_context = LoadNativeContext(context);
989  Node* const initial_regexp_result_map =
990  LoadContextElement(native_context, Context::REGEXP_RESULT_MAP_INDEX);
991 
992  Branch(WordEqual(map, initial_regexp_result_map), if_isunmodified,
993  if_ismodified);
994 }
995 
996 // Slow path stub for RegExpPrototypeExec to decrease code size.
997 TF_BUILTIN(RegExpPrototypeExecSlow, RegExpBuiltinsAssembler) {
998  TNode<JSRegExp> regexp = CAST(Parameter(Descriptor::kReceiver));
999  TNode<String> string = CAST(Parameter(Descriptor::kString));
1000  TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1001 
1002  Return(RegExpPrototypeExecBody(context, regexp, string, false));
1003 }
1004 
1005 // Fast path stub for ATOM regexps. String matching is done by StringIndexOf,
1006 // and {match_info} is updated on success.
1007 // The slow path is implemented in RegExpImpl::AtomExec.
1008 TF_BUILTIN(RegExpExecAtom, RegExpBuiltinsAssembler) {
1009  TNode<JSRegExp> regexp = CAST(Parameter(Descriptor::kRegExp));
1010  TNode<String> subject_string = CAST(Parameter(Descriptor::kString));
1011  TNode<Smi> last_index = CAST(Parameter(Descriptor::kLastIndex));
1012  TNode<FixedArray> match_info = CAST(Parameter(Descriptor::kMatchInfo));
1013  TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1014 
1015  CSA_ASSERT(this, TaggedIsPositiveSmi(last_index));
1016 
1017  TNode<FixedArray> data = CAST(LoadObjectField(regexp, JSRegExp::kDataOffset));
1018  CSA_ASSERT(this,
1019  SmiEqual(CAST(LoadFixedArrayElement(data, JSRegExp::kTagIndex)),
1020  SmiConstant(JSRegExp::ATOM)));
1021 
1022  // Callers ensure that last_index is in-bounds.
1023  CSA_ASSERT(this,
1024  UintPtrLessThanOrEqual(SmiUntag(last_index),
1025  LoadStringLengthAsWord(subject_string)));
1026 
1027  Node* const needle_string =
1028  LoadFixedArrayElement(data, JSRegExp::kAtomPatternIndex);
1029  CSA_ASSERT(this, IsString(needle_string));
1030 
1031  TNode<Smi> const match_from =
1032  CAST(CallBuiltin(Builtins::kStringIndexOf, context, subject_string,
1033  needle_string, last_index));
1034 
1035  Label if_failure(this), if_success(this);
1036  Branch(SmiEqual(match_from, SmiConstant(-1)), &if_failure, &if_success);
1037 
1038  BIND(&if_success);
1039  {
1040  CSA_ASSERT(this, TaggedIsPositiveSmi(match_from));
1041  CSA_ASSERT(this, UintPtrLessThan(SmiUntag(match_from),
1042  LoadStringLengthAsWord(subject_string)));
1043 
1044  const int kNumRegisters = 2;
1045  STATIC_ASSERT(RegExpMatchInfo::kInitialCaptureIndices >= kNumRegisters);
1046 
1047  TNode<Smi> const match_to =
1048  SmiAdd(match_from, LoadStringLengthAsSmi(needle_string));
1049 
1050  StoreFixedArrayElement(match_info, RegExpMatchInfo::kNumberOfCapturesIndex,
1051  SmiConstant(kNumRegisters), SKIP_WRITE_BARRIER);
1052  StoreFixedArrayElement(match_info, RegExpMatchInfo::kLastSubjectIndex,
1053  subject_string);
1054  StoreFixedArrayElement(match_info, RegExpMatchInfo::kLastInputIndex,
1055  subject_string);
1056  StoreFixedArrayElement(match_info, RegExpMatchInfo::kFirstCaptureIndex,
1057  match_from, SKIP_WRITE_BARRIER);
1058  StoreFixedArrayElement(match_info, RegExpMatchInfo::kFirstCaptureIndex + 1,
1059  match_to, SKIP_WRITE_BARRIER);
1060 
1061  Return(match_info);
1062  }
1063 
1064  BIND(&if_failure);
1065  Return(NullConstant());
1066 }
1067 
1068 TF_BUILTIN(RegExpExecInternal, RegExpBuiltinsAssembler) {
1069  TNode<JSRegExp> regexp = CAST(Parameter(Descriptor::kRegExp));
1070  TNode<String> string = CAST(Parameter(Descriptor::kString));
1071  TNode<Number> last_index = CAST(Parameter(Descriptor::kLastIndex));
1072  TNode<RegExpMatchInfo> match_info = CAST(Parameter(Descriptor::kMatchInfo));
1073  TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1074 
1075  CSA_ASSERT(this, IsNumberNormalized(last_index));
1076  CSA_ASSERT(this, IsNumberPositive(last_index));
1077 
1078  Return(RegExpExecInternal(context, regexp, string, last_index, match_info));
1079 }
1080 
1081 // ES#sec-regexp.prototype.exec
1082 // RegExp.prototype.exec ( string )
1083 TF_BUILTIN(RegExpPrototypeExec, RegExpBuiltinsAssembler) {
1084  TNode<Object> maybe_receiver = CAST(Parameter(Descriptor::kReceiver));
1085  TNode<Object> maybe_string = CAST(Parameter(Descriptor::kString));
1086  TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1087 
1088  // Ensure {maybe_receiver} is a JSRegExp.
1089  ThrowIfNotInstanceType(context, maybe_receiver, JS_REGEXP_TYPE,
1090  "RegExp.prototype.exec");
1091  TNode<JSRegExp> receiver = CAST(maybe_receiver);
1092 
1093  // Convert {maybe_string} to a String.
1094  TNode<String> string = ToString_Inline(context, maybe_string);
1095 
1096  Label if_isfastpath(this), if_isslowpath(this);
1097  Branch(IsFastRegExpNoPrototype(context, receiver), &if_isfastpath,
1098  &if_isslowpath);
1099 
1100  BIND(&if_isfastpath);
1101  Return(RegExpPrototypeExecBody(context, receiver, string, true));
1102 
1103  BIND(&if_isslowpath);
1104  Return(CallBuiltin(Builtins::kRegExpPrototypeExecSlow, context, receiver,
1105  string));
1106 }
1107 
1108 Node* RegExpBuiltinsAssembler::FlagsGetter(Node* const context,
1109  Node* const regexp,
1110  bool is_fastpath) {
1111  Isolate* isolate = this->isolate();
1112 
1113  TNode<IntPtrT> const int_one = IntPtrConstant(1);
1114  TVARIABLE(Uint32T, var_length, Uint32Constant(0));
1115  TVARIABLE(IntPtrT, var_flags);
1116 
1117  // First, count the number of characters we will need and check which flags
1118  // are set.
1119 
1120  if (is_fastpath) {
1121  // Refer to JSRegExp's flag property on the fast-path.
1122  CSA_ASSERT(this, IsJSRegExp(regexp));
1123  Node* const flags_smi = LoadObjectField(regexp, JSRegExp::kFlagsOffset);
1124  var_flags = SmiUntag(flags_smi);
1125 
1126 #define CASE_FOR_FLAG(FLAG) \
1127  do { \
1128  Label next(this); \
1129  GotoIfNot(IsSetWord(var_flags.value(), FLAG), &next); \
1130  var_length = Uint32Add(var_length.value(), Uint32Constant(1)); \
1131  Goto(&next); \
1132  BIND(&next); \
1133  } while (false)
1134 
1135  CASE_FOR_FLAG(JSRegExp::kGlobal);
1136  CASE_FOR_FLAG(JSRegExp::kIgnoreCase);
1137  CASE_FOR_FLAG(JSRegExp::kMultiline);
1138  CASE_FOR_FLAG(JSRegExp::kDotAll);
1139  CASE_FOR_FLAG(JSRegExp::kUnicode);
1140  CASE_FOR_FLAG(JSRegExp::kSticky);
1141 #undef CASE_FOR_FLAG
1142  } else {
1143  DCHECK(!is_fastpath);
1144 
1145  // Fall back to GetProperty stub on the slow-path.
1146  var_flags = IntPtrZero();
1147 
1148 #define CASE_FOR_FLAG(NAME, FLAG) \
1149  do { \
1150  Label next(this); \
1151  Node* const flag = GetProperty( \
1152  context, regexp, isolate->factory()->InternalizeUtf8String(NAME)); \
1153  Label if_isflagset(this); \
1154  BranchIfToBooleanIsTrue(flag, &if_isflagset, &next); \
1155  BIND(&if_isflagset); \
1156  var_length = Uint32Add(var_length.value(), Uint32Constant(1)); \
1157  var_flags = Signed(WordOr(var_flags.value(), IntPtrConstant(FLAG))); \
1158  Goto(&next); \
1159  BIND(&next); \
1160  } while (false)
1161 
1162  CASE_FOR_FLAG("global", JSRegExp::kGlobal);
1163  CASE_FOR_FLAG("ignoreCase", JSRegExp::kIgnoreCase);
1164  CASE_FOR_FLAG("multiline", JSRegExp::kMultiline);
1165  CASE_FOR_FLAG("dotAll", JSRegExp::kDotAll);
1166  CASE_FOR_FLAG("unicode", JSRegExp::kUnicode);
1167  CASE_FOR_FLAG("sticky", JSRegExp::kSticky);
1168 #undef CASE_FOR_FLAG
1169  }
1170 
1171  // Allocate a string of the required length and fill it with the corresponding
1172  // char for each set flag.
1173 
1174  {
1175  Node* const result = AllocateSeqOneByteString(context, var_length.value());
1176 
1177  VARIABLE(var_offset, MachineType::PointerRepresentation(),
1178  IntPtrConstant(SeqOneByteString::kHeaderSize - kHeapObjectTag));
1179 
1180 #define CASE_FOR_FLAG(FLAG, CHAR) \
1181  do { \
1182  Label next(this); \
1183  GotoIfNot(IsSetWord(var_flags.value(), FLAG), &next); \
1184  Node* const value = Int32Constant(CHAR); \
1185  StoreNoWriteBarrier(MachineRepresentation::kWord8, result, \
1186  var_offset.value(), value); \
1187  var_offset.Bind(IntPtrAdd(var_offset.value(), int_one)); \
1188  Goto(&next); \
1189  BIND(&next); \
1190  } while (false)
1191 
1192  CASE_FOR_FLAG(JSRegExp::kGlobal, 'g');
1193  CASE_FOR_FLAG(JSRegExp::kIgnoreCase, 'i');
1194  CASE_FOR_FLAG(JSRegExp::kMultiline, 'm');
1195  CASE_FOR_FLAG(JSRegExp::kDotAll, 's');
1196  CASE_FOR_FLAG(JSRegExp::kUnicode, 'u');
1197  CASE_FOR_FLAG(JSRegExp::kSticky, 'y');
1198 #undef CASE_FOR_FLAG
1199 
1200  return result;
1201  }
1202 }
1203 
1204 // ES#sec-isregexp IsRegExp ( argument )
1205 Node* RegExpBuiltinsAssembler::IsRegExp(Node* const context,
1206  Node* const maybe_receiver) {
1207  Label out(this), if_isregexp(this);
1208 
1209  VARIABLE(var_result, MachineRepresentation::kWord32, Int32Constant(0));
1210 
1211  GotoIf(TaggedIsSmi(maybe_receiver), &out);
1212  GotoIfNot(IsJSReceiver(maybe_receiver), &out);
1213 
1214  Node* const receiver = maybe_receiver;
1215 
1216  // Check @@match.
1217  {
1218  Node* const value =
1219  GetProperty(context, receiver, isolate()->factory()->match_symbol());
1220 
1221  Label match_isundefined(this), match_isnotundefined(this);
1222  Branch(IsUndefined(value), &match_isundefined, &match_isnotundefined);
1223 
1224  BIND(&match_isundefined);
1225  Branch(IsJSRegExp(receiver), &if_isregexp, &out);
1226 
1227  BIND(&match_isnotundefined);
1228  BranchIfToBooleanIsTrue(value, &if_isregexp, &out);
1229  }
1230 
1231  BIND(&if_isregexp);
1232  var_result.Bind(Int32Constant(1));
1233  Goto(&out);
1234 
1235  BIND(&out);
1236  return var_result.value();
1237 }
1238 
1239 // ES#sec-regexpinitialize
1240 // Runtime Semantics: RegExpInitialize ( obj, pattern, flags )
1241 Node* RegExpBuiltinsAssembler::RegExpInitialize(Node* const context,
1242  Node* const regexp,
1243  Node* const maybe_pattern,
1244  Node* const maybe_flags) {
1245  CSA_ASSERT(this, IsJSRegExp(regexp));
1246 
1247  // Normalize pattern.
1248  TNode<Object> const pattern = Select<Object>(
1249  IsUndefined(maybe_pattern), [=] { return EmptyStringConstant(); },
1250  [=] { return ToString_Inline(context, maybe_pattern); });
1251 
1252  // Normalize flags.
1253  TNode<Object> const flags = Select<Object>(
1254  IsUndefined(maybe_flags), [=] { return EmptyStringConstant(); },
1255  [=] { return ToString_Inline(context, maybe_flags); });
1256 
1257  // Initialize.
1258 
1259  return CallRuntime(Runtime::kRegExpInitializeAndCompile, context, regexp,
1260  pattern, flags);
1261 }
1262 
1263 // ES #sec-get-regexp.prototype.flags
1264 TF_BUILTIN(RegExpPrototypeFlagsGetter, RegExpBuiltinsAssembler) {
1265  TNode<Object> maybe_receiver = CAST(Parameter(Descriptor::kReceiver));
1266  TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1267 
1268  TNode<Map> map = CAST(ThrowIfNotJSReceiver(context, maybe_receiver,
1269  MessageTemplate::kRegExpNonObject,
1270  "RegExp.prototype.flags"));
1271  TNode<JSReceiver> receiver = CAST(maybe_receiver);
1272 
1273  Label if_isfastpath(this), if_isslowpath(this, Label::kDeferred);
1274  BranchIfFastRegExp(context, receiver, map, base::nullopt, &if_isfastpath,
1275  &if_isslowpath);
1276 
1277  BIND(&if_isfastpath);
1278  Return(FlagsGetter(context, receiver, true));
1279 
1280  BIND(&if_isslowpath);
1281  Return(FlagsGetter(context, receiver, false));
1282 }
1283 
1284 // ES#sec-regexp-pattern-flags
1285 // RegExp ( pattern, flags )
1286 TF_BUILTIN(RegExpConstructor, RegExpBuiltinsAssembler) {
1287  TNode<Object> pattern = CAST(Parameter(Descriptor::kPattern));
1288  TNode<Object> flags = CAST(Parameter(Descriptor::kFlags));
1289  TNode<Object> new_target = CAST(Parameter(Descriptor::kJSNewTarget));
1290  TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1291 
1292  Isolate* isolate = this->isolate();
1293 
1294  VARIABLE(var_flags, MachineRepresentation::kTagged, flags);
1295  VARIABLE(var_pattern, MachineRepresentation::kTagged, pattern);
1296  VARIABLE(var_new_target, MachineRepresentation::kTagged, new_target);
1297 
1298  Node* const native_context = LoadNativeContext(context);
1299  Node* const regexp_function =
1300  LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX);
1301 
1302  Node* const pattern_is_regexp = IsRegExp(context, pattern);
1303 
1304  {
1305  Label next(this);
1306 
1307  GotoIfNot(IsUndefined(new_target), &next);
1308  var_new_target.Bind(regexp_function);
1309 
1310  GotoIfNot(pattern_is_regexp, &next);
1311  GotoIfNot(IsUndefined(flags), &next);
1312 
1313  Node* const value =
1314  GetProperty(context, pattern, isolate->factory()->constructor_string());
1315 
1316  GotoIfNot(WordEqual(value, regexp_function), &next);
1317  Return(pattern);
1318 
1319  BIND(&next);
1320  }
1321 
1322  {
1323  Label next(this), if_patternisfastregexp(this),
1324  if_patternisslowregexp(this);
1325  GotoIf(TaggedIsSmi(pattern), &next);
1326 
1327  GotoIf(IsJSRegExp(CAST(pattern)), &if_patternisfastregexp);
1328 
1329  Branch(pattern_is_regexp, &if_patternisslowregexp, &next);
1330 
1331  BIND(&if_patternisfastregexp);
1332  {
1333  Node* const source =
1334  LoadObjectField(CAST(pattern), JSRegExp::kSourceOffset);
1335  var_pattern.Bind(source);
1336 
1337  {
1338  Label inner_next(this);
1339  GotoIfNot(IsUndefined(flags), &inner_next);
1340 
1341  Node* const value = FlagsGetter(context, pattern, true);
1342  var_flags.Bind(value);
1343  Goto(&inner_next);
1344 
1345  BIND(&inner_next);
1346  }
1347 
1348  Goto(&next);
1349  }
1350 
1351  BIND(&if_patternisslowregexp);
1352  {
1353  {
1354  Node* const value =
1355  GetProperty(context, pattern, isolate->factory()->source_string());
1356  var_pattern.Bind(value);
1357  }
1358 
1359  {
1360  Label inner_next(this);
1361  GotoIfNot(IsUndefined(flags), &inner_next);
1362 
1363  Node* const value =
1364  GetProperty(context, pattern, isolate->factory()->flags_string());
1365  var_flags.Bind(value);
1366  Goto(&inner_next);
1367 
1368  BIND(&inner_next);
1369  }
1370 
1371  Goto(&next);
1372  }
1373 
1374  BIND(&next);
1375  }
1376 
1377  // Allocate.
1378 
1379  VARIABLE(var_regexp, MachineRepresentation::kTagged);
1380  {
1381  Label allocate_jsregexp(this), allocate_generic(this, Label::kDeferred),
1382  next(this);
1383  Branch(WordEqual(var_new_target.value(), regexp_function),
1384  &allocate_jsregexp, &allocate_generic);
1385 
1386  BIND(&allocate_jsregexp);
1387  {
1388  Node* const initial_map = LoadObjectField(
1389  regexp_function, JSFunction::kPrototypeOrInitialMapOffset);
1390  Node* const regexp = AllocateJSObjectFromMap(initial_map);
1391  var_regexp.Bind(regexp);
1392  Goto(&next);
1393  }
1394 
1395  BIND(&allocate_generic);
1396  {
1397  ConstructorBuiltinsAssembler constructor_assembler(this->state());
1398  Node* const regexp = constructor_assembler.EmitFastNewObject(
1399  context, regexp_function, var_new_target.value());
1400  var_regexp.Bind(regexp);
1401  Goto(&next);
1402  }
1403 
1404  BIND(&next);
1405  }
1406 
1407  Node* const result = RegExpInitialize(context, var_regexp.value(),
1408  var_pattern.value(), var_flags.value());
1409  Return(result);
1410 }
1411 
1412 // ES#sec-regexp.prototype.compile
1413 // RegExp.prototype.compile ( pattern, flags )
1414 TF_BUILTIN(RegExpPrototypeCompile, RegExpBuiltinsAssembler) {
1415  TNode<Object> maybe_receiver = CAST(Parameter(Descriptor::kReceiver));
1416  TNode<Object> maybe_pattern = CAST(Parameter(Descriptor::kPattern));
1417  TNode<Object> maybe_flags = CAST(Parameter(Descriptor::kFlags));
1418  TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1419 
1420  ThrowIfNotInstanceType(context, maybe_receiver, JS_REGEXP_TYPE,
1421  "RegExp.prototype.compile");
1422  Node* const receiver = maybe_receiver;
1423 
1424  VARIABLE(var_flags, MachineRepresentation::kTagged, maybe_flags);
1425  VARIABLE(var_pattern, MachineRepresentation::kTagged, maybe_pattern);
1426 
1427  // Handle a JSRegExp pattern.
1428  {
1429  Label next(this);
1430 
1431  GotoIf(TaggedIsSmi(maybe_pattern), &next);
1432  GotoIfNot(IsJSRegExp(CAST(maybe_pattern)), &next);
1433 
1434  Node* const pattern = maybe_pattern;
1435 
1436  // {maybe_flags} must be undefined in this case, otherwise throw.
1437  {
1438  Label next(this);
1439  GotoIf(IsUndefined(maybe_flags), &next);
1440 
1441  ThrowTypeError(context, MessageTemplate::kRegExpFlags);
1442 
1443  BIND(&next);
1444  }
1445 
1446  Node* const new_flags = FlagsGetter(context, pattern, true);
1447  Node* const new_pattern = LoadObjectField(pattern, JSRegExp::kSourceOffset);
1448 
1449  var_flags.Bind(new_flags);
1450  var_pattern.Bind(new_pattern);
1451 
1452  Goto(&next);
1453  BIND(&next);
1454  }
1455 
1456  Node* const result = RegExpInitialize(context, receiver, var_pattern.value(),
1457  var_flags.value());
1458  Return(result);
1459 }
1460 
1461 // ES6 21.2.5.10.
1462 // ES #sec-get-regexp.prototype.source
1463 TF_BUILTIN(RegExpPrototypeSourceGetter, RegExpBuiltinsAssembler) {
1464  TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
1465  TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1466 
1467  // Check whether we have an unmodified regexp instance.
1468  Label if_isjsregexp(this), if_isnotjsregexp(this, Label::kDeferred);
1469 
1470  GotoIf(TaggedIsSmi(receiver), &if_isnotjsregexp);
1471  Branch(IsJSRegExp(CAST(receiver)), &if_isjsregexp, &if_isnotjsregexp);
1472 
1473  BIND(&if_isjsregexp);
1474  Return(LoadObjectField(CAST(receiver), JSRegExp::kSourceOffset));
1475 
1476  BIND(&if_isnotjsregexp);
1477  {
1478  Isolate* isolate = this->isolate();
1479  Node* const native_context = LoadNativeContext(context);
1480  Node* const regexp_fun =
1481  LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX);
1482  Node* const initial_map =
1483  LoadObjectField(regexp_fun, JSFunction::kPrototypeOrInitialMapOffset);
1484  Node* const initial_prototype = LoadMapPrototype(initial_map);
1485 
1486  Label if_isprototype(this), if_isnotprototype(this);
1487  Branch(WordEqual(receiver, initial_prototype), &if_isprototype,
1488  &if_isnotprototype);
1489 
1490  BIND(&if_isprototype);
1491  {
1492  const int counter = v8::Isolate::kRegExpPrototypeSourceGetter;
1493  Node* const counter_smi = SmiConstant(counter);
1494  CallRuntime(Runtime::kIncrementUseCounter, context, counter_smi);
1495 
1496  Node* const result =
1497  HeapConstant(isolate->factory()->NewStringFromAsciiChecked("(?:)"));
1498  Return(result);
1499  }
1500 
1501  BIND(&if_isnotprototype);
1502  {
1503  ThrowTypeError(context, MessageTemplate::kRegExpNonRegExp,
1504  "RegExp.prototype.source");
1505  }
1506  }
1507 }
1508 
1509 // Fast-path implementation for flag checks on an unmodified JSRegExp instance.
1510 TNode<Int32T> RegExpBuiltinsAssembler::FastFlagGetter(TNode<JSRegExp> regexp,
1511  JSRegExp::Flag flag) {
1512  TNode<Smi> flags = CAST(LoadObjectField(regexp, JSRegExp::kFlagsOffset));
1513  TNode<Smi> mask = SmiConstant(flag);
1514  return SmiToInt32(SmiShr(SmiAnd(flags, mask), JSRegExp::FlagShiftBits(flag)));
1515 }
1516 
1517 // Load through the GetProperty stub.
1518 TNode<Int32T> RegExpBuiltinsAssembler::SlowFlagGetter(TNode<Context> context,
1519  TNode<Object> regexp,
1520  JSRegExp::Flag flag) {
1521  Label out(this);
1522  TVARIABLE(Int32T, var_result);
1523 
1524  Handle<String> name;
1525  switch (flag) {
1526  case JSRegExp::kGlobal:
1527  name = isolate()->factory()->global_string();
1528  break;
1529  case JSRegExp::kIgnoreCase:
1530  name = isolate()->factory()->ignoreCase_string();
1531  break;
1532  case JSRegExp::kMultiline:
1533  name = isolate()->factory()->multiline_string();
1534  break;
1535  case JSRegExp::kDotAll:
1536  UNREACHABLE(); // Never called for dotAll.
1537  break;
1538  case JSRegExp::kSticky:
1539  name = isolate()->factory()->sticky_string();
1540  break;
1541  case JSRegExp::kUnicode:
1542  name = isolate()->factory()->unicode_string();
1543  break;
1544  default:
1545  UNREACHABLE();
1546  }
1547 
1548  TNode<Object> value = GetProperty(context, regexp, name);
1549 
1550  Label if_true(this), if_false(this);
1551  BranchIfToBooleanIsTrue(value, &if_true, &if_false);
1552 
1553  BIND(&if_true);
1554  var_result = Int32Constant(1);
1555  Goto(&out);
1556 
1557  BIND(&if_false);
1558  var_result = Int32Constant(0);
1559  Goto(&out);
1560 
1561  BIND(&out);
1562  return var_result.value();
1563 }
1564 
1565 TNode<Int32T> RegExpBuiltinsAssembler::FlagGetter(TNode<Context> context,
1566  TNode<Object> regexp,
1567  JSRegExp::Flag flag,
1568  bool is_fastpath) {
1569  return is_fastpath ? FastFlagGetter(CAST(regexp), flag)
1570  : SlowFlagGetter(context, regexp, flag);
1571 }
1572 
1573 void RegExpBuiltinsAssembler::FlagGetter(Node* context, Node* receiver,
1574  JSRegExp::Flag flag, int counter,
1575  const char* method_name) {
1576  // Check whether we have an unmodified regexp instance.
1577  Label if_isunmodifiedjsregexp(this),
1578  if_isnotunmodifiedjsregexp(this, Label::kDeferred);
1579 
1580  GotoIf(TaggedIsSmi(receiver), &if_isnotunmodifiedjsregexp);
1581  Branch(IsJSRegExp(receiver), &if_isunmodifiedjsregexp,
1582  &if_isnotunmodifiedjsregexp);
1583 
1584  BIND(&if_isunmodifiedjsregexp);
1585  {
1586  // Refer to JSRegExp's flag property on the fast-path.
1587  Node* const is_flag_set = FastFlagGetter(CAST(receiver), flag);
1588  Return(SelectBooleanConstant(is_flag_set));
1589  }
1590 
1591  BIND(&if_isnotunmodifiedjsregexp);
1592  {
1593  Node* const native_context = LoadNativeContext(context);
1594  Node* const regexp_fun =
1595  LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX);
1596  Node* const initial_map =
1597  LoadObjectField(regexp_fun, JSFunction::kPrototypeOrInitialMapOffset);
1598  Node* const initial_prototype = LoadMapPrototype(initial_map);
1599 
1600  Label if_isprototype(this), if_isnotprototype(this);
1601  Branch(WordEqual(receiver, initial_prototype), &if_isprototype,
1602  &if_isnotprototype);
1603 
1604  BIND(&if_isprototype);
1605  {
1606  if (counter != -1) {
1607  Node* const counter_smi = SmiConstant(counter);
1608  CallRuntime(Runtime::kIncrementUseCounter, context, counter_smi);
1609  }
1610  Return(UndefinedConstant());
1611  }
1612 
1613  BIND(&if_isnotprototype);
1614  { ThrowTypeError(context, MessageTemplate::kRegExpNonRegExp, method_name); }
1615  }
1616 }
1617 
1618 // ES6 21.2.5.4.
1619 // ES #sec-get-regexp.prototype.global
1620 TF_BUILTIN(RegExpPrototypeGlobalGetter, RegExpBuiltinsAssembler) {
1621  TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1622  TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
1623  FlagGetter(context, receiver, JSRegExp::kGlobal,
1624  v8::Isolate::kRegExpPrototypeOldFlagGetter,
1625  "RegExp.prototype.global");
1626 }
1627 
1628 // ES6 21.2.5.5.
1629 // ES #sec-get-regexp.prototype.ignorecase
1630 TF_BUILTIN(RegExpPrototypeIgnoreCaseGetter, RegExpBuiltinsAssembler) {
1631  TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1632  TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
1633  FlagGetter(context, receiver, JSRegExp::kIgnoreCase,
1634  v8::Isolate::kRegExpPrototypeOldFlagGetter,
1635  "RegExp.prototype.ignoreCase");
1636 }
1637 
1638 // ES6 21.2.5.7.
1639 // ES #sec-get-regexp.prototype.multiline
1640 TF_BUILTIN(RegExpPrototypeMultilineGetter, RegExpBuiltinsAssembler) {
1641  TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1642  TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
1643  FlagGetter(context, receiver, JSRegExp::kMultiline,
1644  v8::Isolate::kRegExpPrototypeOldFlagGetter,
1645  "RegExp.prototype.multiline");
1646 }
1647 
1648 // ES #sec-get-regexp.prototype.dotAll
1649 TF_BUILTIN(RegExpPrototypeDotAllGetter, RegExpBuiltinsAssembler) {
1650  TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1651  TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
1652  static const int kNoCounter = -1;
1653  FlagGetter(context, receiver, JSRegExp::kDotAll, kNoCounter,
1654  "RegExp.prototype.dotAll");
1655 }
1656 
1657 // ES6 21.2.5.12.
1658 // ES #sec-get-regexp.prototype.sticky
1659 TF_BUILTIN(RegExpPrototypeStickyGetter, RegExpBuiltinsAssembler) {
1660  TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1661  TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
1662  FlagGetter(context, receiver, JSRegExp::kSticky,
1663  v8::Isolate::kRegExpPrototypeStickyGetter,
1664  "RegExp.prototype.sticky");
1665 }
1666 
1667 // ES6 21.2.5.15.
1668 // ES #sec-get-regexp.prototype.unicode
1669 TF_BUILTIN(RegExpPrototypeUnicodeGetter, RegExpBuiltinsAssembler) {
1670  TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1671  TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
1672  FlagGetter(context, receiver, JSRegExp::kUnicode,
1673  v8::Isolate::kRegExpPrototypeUnicodeGetter,
1674  "RegExp.prototype.unicode");
1675 }
1676 
1677 // ES#sec-regexpexec Runtime Semantics: RegExpExec ( R, S )
1678 Node* RegExpBuiltinsAssembler::RegExpExec(Node* context, Node* regexp,
1679  Node* string) {
1680  VARIABLE(var_result, MachineRepresentation::kTagged);
1681  Label out(this);
1682 
1683  // Take the slow path of fetching the exec property, calling it, and
1684  // verifying its return value.
1685 
1686  // Get the exec property.
1687  Node* const exec =
1688  GetProperty(context, regexp, isolate()->factory()->exec_string());
1689 
1690  // Is {exec} callable?
1691  Label if_iscallable(this), if_isnotcallable(this);
1692 
1693  GotoIf(TaggedIsSmi(exec), &if_isnotcallable);
1694 
1695  Node* const exec_map = LoadMap(exec);
1696  Branch(IsCallableMap(exec_map), &if_iscallable, &if_isnotcallable);
1697 
1698  BIND(&if_iscallable);
1699  {
1700  Callable call_callable = CodeFactory::Call(isolate());
1701  Node* const result = CallJS(call_callable, context, exec, regexp, string);
1702 
1703  var_result.Bind(result);
1704  GotoIf(IsNull(result), &out);
1705 
1706  ThrowIfNotJSReceiver(context, result,
1707  MessageTemplate::kInvalidRegExpExecResult, "");
1708 
1709  Goto(&out);
1710  }
1711 
1712  BIND(&if_isnotcallable);
1713  {
1714  ThrowIfNotInstanceType(context, regexp, JS_REGEXP_TYPE,
1715  "RegExp.prototype.exec");
1716 
1717  Node* const result = CallBuiltin(Builtins::kRegExpPrototypeExecSlow,
1718  context, regexp, string);
1719  var_result.Bind(result);
1720  Goto(&out);
1721  }
1722 
1723  BIND(&out);
1724  return var_result.value();
1725 }
1726 
1727 // ES#sec-regexp.prototype.test
1728 // RegExp.prototype.test ( S )
1729 TF_BUILTIN(RegExpPrototypeTest, RegExpBuiltinsAssembler) {
1730  TNode<Object> maybe_receiver = CAST(Parameter(Descriptor::kReceiver));
1731  TNode<Object> maybe_string = CAST(Parameter(Descriptor::kString));
1732  TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1733 
1734  // Ensure {maybe_receiver} is a JSReceiver.
1735  ThrowIfNotJSReceiver(context, maybe_receiver,
1736  MessageTemplate::kIncompatibleMethodReceiver,
1737  "RegExp.prototype.test");
1738  TNode<JSReceiver> receiver = CAST(maybe_receiver);
1739 
1740  // Convert {maybe_string} to a String.
1741  TNode<String> string = ToString_Inline(context, maybe_string);
1742 
1743  Label fast_path(this), slow_path(this);
1744  BranchIfFastRegExp(context, receiver, &fast_path, &slow_path);
1745 
1746  BIND(&fast_path);
1747  {
1748  Label if_didnotmatch(this);
1749  RegExpPrototypeExecBodyWithoutResult(context, receiver, string,
1750  &if_didnotmatch, true);
1751  Return(TrueConstant());
1752 
1753  BIND(&if_didnotmatch);
1754  Return(FalseConstant());
1755  }
1756 
1757  BIND(&slow_path);
1758  {
1759  // Call exec.
1760  TNode<HeapObject> match_indices =
1761  CAST(RegExpExec(context, receiver, string));
1762 
1763  // Return true iff exec matched successfully.
1764  Return(SelectBooleanConstant(IsNotNull(match_indices)));
1765  }
1766 }
1767 
1768 TF_BUILTIN(RegExpPrototypeTestFast, RegExpBuiltinsAssembler) {
1769  TNode<JSRegExp> regexp = CAST(Parameter(Descriptor::kReceiver));
1770  TNode<String> string = CAST(Parameter(Descriptor::kString));
1771  TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1772 
1773  Label if_didnotmatch(this);
1774  CSA_ASSERT(this, IsFastRegExpWithOriginalExec(context, regexp));
1775  RegExpPrototypeExecBodyWithoutResult(context, regexp, string, &if_didnotmatch,
1776  true);
1777  Return(TrueConstant());
1778 
1779  BIND(&if_didnotmatch);
1780  Return(FalseConstant());
1781 }
1782 
1783 Node* RegExpBuiltinsAssembler::AdvanceStringIndex(Node* const string,
1784  Node* const index,
1785  Node* const is_unicode,
1786  bool is_fastpath) {
1787  CSA_ASSERT(this, IsString(string));
1788  CSA_ASSERT(this, IsNumberNormalized(index));
1789  if (is_fastpath) CSA_ASSERT(this, TaggedIsPositiveSmi(index));
1790 
1791  // Default to last_index + 1.
1792  Node* const index_plus_one = NumberInc(index);
1793  VARIABLE(var_result, MachineRepresentation::kTagged, index_plus_one);
1794 
1795  // Advancing the index has some subtle issues involving the distinction
1796  // between Smis and HeapNumbers. There's three cases:
1797  // * {index} is a Smi, {index_plus_one} is a Smi. The standard case.
1798  // * {index} is a Smi, {index_plus_one} overflows into a HeapNumber.
1799  // In this case we can return the result early, because
1800  // {index_plus_one} > {string}.length.
1801  // * {index} is a HeapNumber, {index_plus_one} is a HeapNumber. This can only
1802  // occur when {index} is outside the Smi range since we normalize
1803  // explicitly. Again we can return early.
1804  if (is_fastpath) {
1805  // Must be in Smi range on the fast path. We control the value of {index}
1806  // on all call-sites and can never exceed the length of the string.
1807  STATIC_ASSERT(String::kMaxLength + 2 < Smi::kMaxValue);
1808  CSA_ASSERT(this, TaggedIsPositiveSmi(index_plus_one));
1809  }
1810 
1811  Label if_isunicode(this), out(this);
1812  GotoIfNot(is_unicode, &out);
1813 
1814  // Keep this unconditional (even on the fast path) just to be safe.
1815  Branch(TaggedIsPositiveSmi(index_plus_one), &if_isunicode, &out);
1816 
1817  BIND(&if_isunicode);
1818  {
1819  TNode<IntPtrT> const string_length = LoadStringLengthAsWord(string);
1820  TNode<IntPtrT> untagged_plus_one = SmiUntag(index_plus_one);
1821  GotoIfNot(IntPtrLessThan(untagged_plus_one, string_length), &out);
1822 
1823  Node* const lead = StringCharCodeAt(string, SmiUntag(index));
1824  GotoIfNot(Word32Equal(Word32And(lead, Int32Constant(0xFC00)),
1825  Int32Constant(0xD800)),
1826  &out);
1827 
1828  Node* const trail = StringCharCodeAt(string, untagged_plus_one);
1829  GotoIfNot(Word32Equal(Word32And(trail, Int32Constant(0xFC00)),
1830  Int32Constant(0xDC00)),
1831  &out);
1832 
1833  // At a surrogate pair, return index + 2.
1834  Node* const index_plus_two = NumberInc(index_plus_one);
1835  var_result.Bind(index_plus_two);
1836 
1837  Goto(&out);
1838  }
1839 
1840  BIND(&out);
1841  return var_result.value();
1842 }
1843 
1844 void RegExpBuiltinsAssembler::RegExpPrototypeMatchBody(Node* const context,
1845  Node* const regexp,
1846  TNode<String> string,
1847  const bool is_fastpath) {
1848  if (is_fastpath) CSA_ASSERT(this, IsFastRegExp(context, regexp));
1849 
1850  Node* const is_global =
1851  FlagGetter(CAST(context), CAST(regexp), JSRegExp::kGlobal, is_fastpath);
1852 
1853  Label if_isglobal(this), if_isnotglobal(this);
1854  Branch(is_global, &if_isglobal, &if_isnotglobal);
1855 
1856  BIND(&if_isnotglobal);
1857  {
1858  Node* const result =
1859  is_fastpath
1860  ? RegExpPrototypeExecBody(CAST(context), CAST(regexp), string, true)
1861  : RegExpExec(context, regexp, string);
1862  Return(result);
1863  }
1864 
1865  BIND(&if_isglobal);
1866  {
1867  Node* const is_unicode = FlagGetter(CAST(context), CAST(regexp),
1868  JSRegExp::kUnicode, is_fastpath);
1869 
1870  StoreLastIndex(context, regexp, SmiZero(), is_fastpath);
1871 
1872  // Allocate an array to store the resulting match strings.
1873 
1874  GrowableFixedArray array(state());
1875 
1876  // Loop preparations. Within the loop, collect results from RegExpExec
1877  // and store match strings in the array.
1878 
1879  Variable* vars[] = {array.var_array(), array.var_length(),
1880  array.var_capacity()};
1881  Label loop(this, 3, vars), out(this);
1882  Goto(&loop);
1883 
1884  BIND(&loop);
1885  {
1886  VARIABLE(var_match, MachineRepresentation::kTagged);
1887 
1888  Label if_didmatch(this), if_didnotmatch(this);
1889  if (is_fastpath) {
1890  // On the fast path, grab the matching string from the raw match index
1891  // array.
1892  TNode<RegExpMatchInfo> match_indices =
1893  RegExpPrototypeExecBodyWithoutResult(CAST(context), CAST(regexp),
1894  string, &if_didnotmatch, true);
1895 
1896  Node* const match_from = LoadFixedArrayElement(
1897  match_indices, RegExpMatchInfo::kFirstCaptureIndex);
1898  Node* const match_to = LoadFixedArrayElement(
1899  match_indices, RegExpMatchInfo::kFirstCaptureIndex + 1);
1900 
1901  var_match.Bind(CallBuiltin(Builtins::kSubString, context, string,
1902  match_from, match_to));
1903  Goto(&if_didmatch);
1904  } else {
1905  DCHECK(!is_fastpath);
1906  Node* const result = RegExpExec(context, regexp, string);
1907 
1908  Label load_match(this);
1909  Branch(IsNull(result), &if_didnotmatch, &load_match);
1910 
1911  BIND(&load_match);
1912  var_match.Bind(
1913  ToString_Inline(context, GetProperty(context, result, SmiZero())));
1914  Goto(&if_didmatch);
1915  }
1916 
1917  BIND(&if_didnotmatch);
1918  {
1919  // Return null if there were no matches, otherwise just exit the loop.
1920  GotoIfNot(IntPtrEqual(array.length(), IntPtrZero()), &out);
1921  Return(NullConstant());
1922  }
1923 
1924  BIND(&if_didmatch);
1925  {
1926  Node* match = var_match.value();
1927 
1928  // Store the match, growing the fixed array if needed.
1929 
1930  array.Push(CAST(match));
1931 
1932  // Advance last index if the match is the empty string.
1933 
1934  TNode<Smi> const match_length = LoadStringLengthAsSmi(match);
1935  GotoIfNot(SmiEqual(match_length, SmiZero()), &loop);
1936 
1937  Node* last_index =
1938  LoadLastIndex(CAST(context), CAST(regexp), is_fastpath);
1939  if (is_fastpath) {
1940  CSA_ASSERT(this, TaggedIsPositiveSmi(last_index));
1941  } else {
1942  last_index = ToLength_Inline(context, last_index);
1943  }
1944 
1945  Node* const new_last_index =
1946  AdvanceStringIndex(string, last_index, is_unicode, is_fastpath);
1947 
1948  if (is_fastpath) {
1949  // On the fast path, we can be certain that lastIndex can never be
1950  // incremented to overflow the Smi range since the maximal string
1951  // length is less than the maximal Smi value.
1952  STATIC_ASSERT(String::kMaxLength < Smi::kMaxValue);
1953  CSA_ASSERT(this, TaggedIsPositiveSmi(new_last_index));
1954  }
1955 
1956  StoreLastIndex(context, regexp, new_last_index, is_fastpath);
1957 
1958  Goto(&loop);
1959  }
1960  }
1961 
1962  BIND(&out);
1963  {
1964  // Wrap the match in a JSArray.
1965 
1966  Node* const result = array.ToJSArray(CAST(context));
1967  Return(result);
1968  }
1969  }
1970 }
1971 
1972 // ES#sec-regexp.prototype-@@match
1973 // RegExp.prototype [ @@match ] ( string )
1974 TF_BUILTIN(RegExpPrototypeMatch, RegExpBuiltinsAssembler) {
1975  TNode<Object> maybe_receiver = CAST(Parameter(Descriptor::kReceiver));
1976  TNode<Object> maybe_string = CAST(Parameter(Descriptor::kString));
1977  TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1978 
1979  // Ensure {maybe_receiver} is a JSReceiver.
1980  ThrowIfNotJSReceiver(context, maybe_receiver,
1981  MessageTemplate::kIncompatibleMethodReceiver,
1982  "RegExp.prototype.@@match");
1983  Node* const receiver = maybe_receiver;
1984 
1985  // Convert {maybe_string} to a String.
1986  TNode<String> const string = ToString_Inline(context, maybe_string);
1987 
1988  Label fast_path(this), slow_path(this);
1989  BranchIfFastRegExp(context, receiver, &fast_path, &slow_path);
1990 
1991  BIND(&fast_path);
1992  // TODO(pwong): Could be optimized to remove the overhead of calling the
1993  // builtin (at the cost of a larger builtin).
1994  Return(CallBuiltin(Builtins::kRegExpMatchFast, context, receiver, string));
1995 
1996  BIND(&slow_path);
1997  RegExpPrototypeMatchBody(context, receiver, string, false);
1998 }
1999 
2000 void RegExpMatchAllAssembler::Generate(TNode<Context> context,
2001  TNode<Context> native_context,
2002  TNode<Object> receiver,
2003  TNode<Object> maybe_string) {
2004  // 1. Let R be the this value.
2005  // 2. If Type(R) is not Object, throw a TypeError exception.
2006  ThrowIfNotJSReceiver(context, receiver,
2007  MessageTemplate::kIncompatibleMethodReceiver,
2008  "RegExp.prototype.@@matchAll");
2009 
2010  // 3. Let S be ? ToString(O).
2011  TNode<String> string = ToString_Inline(context, maybe_string);
2012 
2013  TVARIABLE(Object, var_matcher);
2014  TVARIABLE(Int32T, var_global);
2015  TVARIABLE(Int32T, var_unicode);
2016  Label create_iterator(this), if_fast_regexp(this),
2017  if_slow_regexp(this, Label::kDeferred);
2018 
2019  BranchIfFastRegExp(context, receiver, &if_fast_regexp, &if_slow_regexp);
2020  BIND(&if_fast_regexp);
2021  {
2022  TNode<JSRegExp> fast_regexp = CAST(receiver);
2023  TNode<Object> source =
2024  LoadObjectField(fast_regexp, JSRegExp::kSourceOffset);
2025 
2026  // 4. Let C be ? SpeciesConstructor(R, %RegExp%).
2027  // 5. Let flags be ? ToString(? Get(R, "flags")).
2028  // 6. Let matcher be ? Construct(C, « R, flags »).
2029  TNode<String> flags = CAST(FlagsGetter(context, fast_regexp, true));
2030  var_matcher = RegExpCreate(context, native_context, source, flags);
2031  CSA_ASSERT(this, IsFastRegExp(context, var_matcher.value()));
2032 
2033  // 7. Let lastIndex be ? ToLength(? Get(R, "lastIndex")).
2034  // 8. Perform ? Set(matcher, "lastIndex", lastIndex, true).
2035  FastStoreLastIndex(var_matcher.value(), FastLoadLastIndex(fast_regexp));
2036 
2037  // 9. If flags contains "g", let global be true.
2038  // 10. Else, let global be false.
2039  var_global = FastFlagGetter(CAST(var_matcher.value()), JSRegExp::kGlobal);
2040 
2041  // 11. If flags contains "u", let fullUnicode be true.
2042  // 12. Else, let fullUnicode be false.
2043  var_unicode = FastFlagGetter(CAST(var_matcher.value()), JSRegExp::kUnicode);
2044  Goto(&create_iterator);
2045  }
2046 
2047  BIND(&if_slow_regexp);
2048  {
2049  // 4. Let C be ? SpeciesConstructor(R, %RegExp%).
2050  TNode<JSFunction> regexp_fun = CAST(
2051  LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX));
2052  TNode<JSReceiver> species_constructor =
2053  SpeciesConstructor(native_context, receiver, regexp_fun);
2054 
2055  // 5. Let flags be ? ToString(? Get(R, "flags")).
2056  TNode<Object> flags =
2057  GetProperty(context, receiver, isolate()->factory()->flags_string());
2058  TNode<String> flags_string = ToString_Inline(context, flags);
2059 
2060  // 6. Let matcher be ? Construct(C, « R, flags »).
2061  var_matcher =
2062  Construct(context, species_constructor, receiver, flags_string);
2063 
2064  // 7. Let lastIndex be ? ToLength(? Get(R, "lastIndex")).
2065  TNode<Number> last_index =
2066  ToLength_Inline(context, SlowLoadLastIndex(context, receiver));
2067 
2068  // 8. Perform ? Set(matcher, "lastIndex", lastIndex, true).
2069  SlowStoreLastIndex(context, var_matcher.value(), last_index);
2070 
2071  // 9. If flags contains "g", let global be true.
2072  // 10. Else, let global be false.
2073  TNode<String> global_char_string = StringConstant("g");
2074  TNode<Smi> global_ix =
2075  CAST(CallBuiltin(Builtins::kStringIndexOf, context, flags_string,
2076  global_char_string, SmiZero()));
2077  var_global =
2078  SelectInt32Constant(SmiEqual(global_ix, SmiConstant(-1)), 0, 1);
2079 
2080  // 11. If flags contains "u", let fullUnicode be true.
2081  // 12. Else, let fullUnicode be false.
2082  TNode<String> unicode_char_string = StringConstant("u");
2083  TNode<Smi> unicode_ix =
2084  CAST(CallBuiltin(Builtins::kStringIndexOf, context, flags_string,
2085  unicode_char_string, SmiZero()));
2086  var_unicode =
2087  SelectInt32Constant(SmiEqual(unicode_ix, SmiConstant(-1)), 0, 1);
2088  Goto(&create_iterator);
2089  }
2090 
2091  BIND(&create_iterator);
2092  {
2093  // 13. Return ! CreateRegExpStringIterator(matcher, S, global, fullUnicode).
2094  TNode<Object> iterator =
2095  CreateRegExpStringIterator(native_context, var_matcher.value(), string,
2096  var_global.value(), var_unicode.value());
2097  Return(iterator);
2098  }
2099 }
2100 
2101 // ES#sec-createregexpstringiterator
2102 // CreateRegExpStringIterator ( R, S, global, fullUnicode )
2103 TNode<Object> RegExpMatchAllAssembler::CreateRegExpStringIterator(
2104  TNode<Context> native_context, TNode<Object> regexp, TNode<String> string,
2105  TNode<Int32T> global, TNode<Int32T> full_unicode) {
2106  TNode<Map> map = CAST(LoadContextElement(
2107  native_context,
2108  Context::INITIAL_REGEXP_STRING_ITERATOR_PROTOTYPE_MAP_INDEX));
2109 
2110  // 4. Let iterator be ObjectCreate(%RegExpStringIteratorPrototype%, «
2111  // [[IteratingRegExp]], [[IteratedString]], [[Global]], [[Unicode]],
2112  // [[Done]] »).
2113  TNode<Object> iterator = Allocate(JSRegExpStringIterator::kSize);
2114  StoreMapNoWriteBarrier(iterator, map);
2115  StoreObjectFieldRoot(iterator,
2116  JSRegExpStringIterator::kPropertiesOrHashOffset,
2117  RootIndex::kEmptyFixedArray);
2118  StoreObjectFieldRoot(iterator, JSRegExpStringIterator::kElementsOffset,
2119  RootIndex::kEmptyFixedArray);
2120 
2121  // 5. Set iterator.[[IteratingRegExp]] to R.
2122  StoreObjectFieldNoWriteBarrier(
2123  iterator, JSRegExpStringIterator::kIteratingRegExpOffset, regexp);
2124 
2125  // 6. Set iterator.[[IteratedString]] to S.
2126  StoreObjectFieldNoWriteBarrier(
2127  iterator, JSRegExpStringIterator::kIteratedStringOffset, string);
2128 
2129 #ifdef DEBUG
2130  // Verify global and full_unicode can be bitwise shifted without masking.
2131  TNode<Int32T> zero = Int32Constant(0);
2132  TNode<Int32T> one = Int32Constant(1);
2133  CSA_ASSERT(this,
2134  Word32Or(Word32Equal(global, zero), Word32Equal(global, one)));
2135  CSA_ASSERT(this, Word32Or(Word32Equal(full_unicode, zero),
2136  Word32Equal(full_unicode, one)));
2137 #endif // DEBUG
2138 
2139  // 7. Set iterator.[[Global]] to global.
2140  // 8. Set iterator.[[Unicode]] to fullUnicode.
2141  // 9. Set iterator.[[Done]] to false.
2142  TNode<Word32T> global_flag =
2143  Word32Shl(global, Int32Constant(JSRegExpStringIterator::kGlobalBit));
2144  TNode<Word32T> unicode_flag = Word32Shl(
2145  full_unicode, Int32Constant(JSRegExpStringIterator::kUnicodeBit));
2146  TNode<Word32T> iterator_flags = Word32Or(global_flag, unicode_flag);
2147  StoreObjectFieldNoWriteBarrier(iterator, JSRegExpStringIterator::kFlagsOffset,
2148  SmiFromInt32(Signed(iterator_flags)));
2149 
2150  return iterator;
2151 }
2152 
2153 // https://tc39.github.io/proposal-string-matchall/
2154 // RegExp.prototype [ @@matchAll ] ( string )
2155 TF_BUILTIN(RegExpPrototypeMatchAll, RegExpMatchAllAssembler) {
2156  TNode<Context> context = CAST(Parameter(Descriptor::kContext));
2157  TNode<Context> native_context = LoadNativeContext(context);
2158  TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
2159  TNode<Object> maybe_string = CAST(Parameter(Descriptor::kString));
2160  Generate(context, native_context, receiver, maybe_string);
2161 }
2162 
2163 // Helper that skips a few initial checks. and assumes...
2164 // 1) receiver is a "fast" RegExp
2165 // 2) pattern is a string
2166 TF_BUILTIN(RegExpMatchFast, RegExpBuiltinsAssembler) {
2167  TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
2168  TNode<String> string = CAST(Parameter(Descriptor::kPattern));
2169  TNode<Context> context = CAST(Parameter(Descriptor::kContext));
2170 
2171  RegExpPrototypeMatchBody(context, receiver, string, true);
2172 }
2173 
2174 void RegExpBuiltinsAssembler::RegExpPrototypeSearchBodyFast(
2175  Node* const context, Node* const regexp, Node* const string) {
2176  CSA_ASSERT(this, IsFastRegExp(context, regexp));
2177  CSA_ASSERT(this, IsString(string));
2178 
2179  // Grab the initial value of last index.
2180  Node* const previous_last_index = FastLoadLastIndex(CAST(regexp));
2181 
2182  // Ensure last index is 0.
2183  FastStoreLastIndex(regexp, SmiZero());
2184 
2185  // Call exec.
2186  Label if_didnotmatch(this);
2187  TNode<RegExpMatchInfo> match_indices = RegExpPrototypeExecBodyWithoutResult(
2188  CAST(context), CAST(regexp), CAST(string), &if_didnotmatch, true);
2189 
2190  // Successful match.
2191  {
2192  // Reset last index.
2193  FastStoreLastIndex(regexp, previous_last_index);
2194 
2195  // Return the index of the match.
2196  Node* const index = LoadFixedArrayElement(
2197  match_indices, RegExpMatchInfo::kFirstCaptureIndex);
2198  Return(index);
2199  }
2200 
2201  BIND(&if_didnotmatch);
2202  {
2203  // Reset last index and return -1.
2204  FastStoreLastIndex(regexp, previous_last_index);
2205  Return(SmiConstant(-1));
2206  }
2207 }
2208 
2209 void RegExpBuiltinsAssembler::RegExpPrototypeSearchBodySlow(
2210  Node* const context, Node* const regexp, Node* const string) {
2211  CSA_ASSERT(this, IsJSReceiver(regexp));
2212  CSA_ASSERT(this, IsString(string));
2213 
2214  Isolate* const isolate = this->isolate();
2215 
2216  Node* const smi_zero = SmiZero();
2217 
2218  // Grab the initial value of last index.
2219  Node* const previous_last_index =
2220  SlowLoadLastIndex(CAST(context), CAST(regexp));
2221 
2222  // Ensure last index is 0.
2223  {
2224  Label next(this), slow(this, Label::kDeferred);
2225  BranchIfSameValue(previous_last_index, smi_zero, &next, &slow);
2226 
2227  BIND(&slow);
2228  SlowStoreLastIndex(context, regexp, smi_zero);
2229  Goto(&next);
2230  BIND(&next);
2231  }
2232 
2233  // Call exec.
2234  Node* const exec_result = RegExpExec(context, regexp, string);
2235 
2236  // Reset last index if necessary.
2237  {
2238  Label next(this), slow(this, Label::kDeferred);
2239  Node* const current_last_index =
2240  SlowLoadLastIndex(CAST(context), CAST(regexp));
2241 
2242  BranchIfSameValue(current_last_index, previous_last_index, &next, &slow);
2243 
2244  BIND(&slow);
2245  SlowStoreLastIndex(context, regexp, previous_last_index);
2246  Goto(&next);
2247  BIND(&next);
2248  }
2249 
2250  // Return -1 if no match was found.
2251  {
2252  Label next(this);
2253  GotoIfNot(IsNull(exec_result), &next);
2254  Return(SmiConstant(-1));
2255  BIND(&next);
2256  }
2257 
2258  // Return the index of the match.
2259  {
2260  Label fast_result(this), slow_result(this, Label::kDeferred);
2261  BranchIfFastRegExpResult(context, exec_result, &fast_result, &slow_result);
2262 
2263  BIND(&fast_result);
2264  {
2265  Node* const index =
2266  LoadObjectField(exec_result, JSRegExpResult::kIndexOffset);
2267  Return(index);
2268  }
2269 
2270  BIND(&slow_result);
2271  {
2272  Return(GetProperty(context, exec_result,
2273  isolate->factory()->index_string()));
2274  }
2275  }
2276 }
2277 
2278 // ES#sec-regexp.prototype-@@search
2279 // RegExp.prototype [ @@search ] ( string )
2280 TF_BUILTIN(RegExpPrototypeSearch, RegExpBuiltinsAssembler) {
2281  TNode<Object> maybe_receiver = CAST(Parameter(Descriptor::kReceiver));
2282  TNode<Object> maybe_string = CAST(Parameter(Descriptor::kString));
2283  TNode<Context> context = CAST(Parameter(Descriptor::kContext));
2284 
2285  // Ensure {maybe_receiver} is a JSReceiver.
2286  ThrowIfNotJSReceiver(context, maybe_receiver,
2287  MessageTemplate::kIncompatibleMethodReceiver,
2288  "RegExp.prototype.@@search");
2289  Node* const receiver = maybe_receiver;
2290 
2291  // Convert {maybe_string} to a String.
2292  TNode<String> const string = ToString_Inline(context, maybe_string);
2293 
2294  Label fast_path(this), slow_path(this);
2295  BranchIfFastRegExp(context, receiver, &fast_path, &slow_path);
2296 
2297  BIND(&fast_path);
2298  // TODO(pwong): Could be optimized to remove the overhead of calling the
2299  // builtin (at the cost of a larger builtin).
2300  Return(CallBuiltin(Builtins::kRegExpSearchFast, context, receiver, string));
2301 
2302  BIND(&slow_path);
2303  RegExpPrototypeSearchBodySlow(context, receiver, string);
2304 }
2305 
2306 // Helper that skips a few initial checks. and assumes...
2307 // 1) receiver is a "fast" RegExp
2308 // 2) pattern is a string
2309 TF_BUILTIN(RegExpSearchFast, RegExpBuiltinsAssembler) {
2310  TNode<JSRegExp> receiver = CAST(Parameter(Descriptor::kReceiver));
2311  TNode<String> string = CAST(Parameter(Descriptor::kPattern));
2312  TNode<Context> context = CAST(Parameter(Descriptor::kContext));
2313 
2314  RegExpPrototypeSearchBodyFast(context, receiver, string);
2315 }
2316 
2317 // Generates the fast path for @@split. {regexp} is an unmodified, non-sticky
2318 // JSRegExp, {string} is a String, and {limit} is a Smi.
2319 void RegExpBuiltinsAssembler::RegExpPrototypeSplitBody(Node* const context,
2320  Node* const regexp,
2321  TNode<String> string,
2322  TNode<Smi> const limit) {
2323  CSA_ASSERT(this, IsFastRegExp(context, regexp));
2324  CSA_ASSERT(this,
2325  Word32BinaryNot(FastFlagGetter(CAST(regexp), JSRegExp::kSticky)));
2326 
2327  TNode<IntPtrT> const int_limit = SmiUntag(limit);
2328 
2329  const ElementsKind kind = PACKED_ELEMENTS;
2330  const ParameterMode mode = CodeStubAssembler::INTPTR_PARAMETERS;
2331 
2332  Node* const allocation_site = nullptr;
2333  Node* const native_context = LoadNativeContext(context);
2334  TNode<Map> array_map = LoadJSArrayElementsMap(kind, native_context);
2335 
2336  Label return_empty_array(this, Label::kDeferred);
2337 
2338  // If limit is zero, return an empty array.
2339  {
2340  Label next(this), if_limitiszero(this, Label::kDeferred);
2341  Branch(SmiEqual(limit, SmiZero()), &return_empty_array, &next);
2342  BIND(&next);
2343  }
2344 
2345  TNode<Smi> const string_length = LoadStringLengthAsSmi(string);
2346 
2347  // If passed the empty {string}, return either an empty array or a singleton
2348  // array depending on whether the {regexp} matches.
2349  {
2350  Label next(this), if_stringisempty(this, Label::kDeferred);
2351  Branch(SmiEqual(string_length, SmiZero()), &if_stringisempty, &next);
2352 
2353  BIND(&if_stringisempty);
2354  {
2355  Node* const last_match_info = LoadContextElement(
2356  native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX);
2357 
2358  Node* const match_indices =
2359  CallBuiltin(Builtins::kRegExpExecInternal, context, regexp, string,
2360  SmiZero(), last_match_info);
2361 
2362  Label return_singleton_array(this);
2363  Branch(IsNull(match_indices), &return_singleton_array,
2364  &return_empty_array);
2365 
2366  BIND(&return_singleton_array);
2367  {
2368  TNode<Smi> length = SmiConstant(1);
2369  TNode<IntPtrT> capacity = IntPtrConstant(1);
2370  TNode<JSArray> result = AllocateJSArray(kind, array_map, capacity,
2371  length, allocation_site, mode);
2372 
2373  TNode<FixedArray> fixed_array = CAST(LoadElements(result));
2374  StoreFixedArrayElement(fixed_array, 0, string);
2375 
2376  Return(result);
2377  }
2378  }
2379 
2380  BIND(&next);
2381  }
2382 
2383  // Loop preparations.
2384 
2385  GrowableFixedArray array(state());
2386 
2387  TVARIABLE(Smi, var_last_matched_until, SmiZero());
2388  TVARIABLE(Smi, var_next_search_from, SmiZero());
2389 
2390  Variable* vars[] = {array.var_array(), array.var_length(),
2391  array.var_capacity(), &var_last_matched_until,
2392  &var_next_search_from};
2393  const int vars_count = sizeof(vars) / sizeof(vars[0]);
2394  Label loop(this, vars_count, vars), push_suffix_and_out(this), out(this);
2395  Goto(&loop);
2396 
2397  BIND(&loop);
2398  {
2399  TNode<Smi> const next_search_from = var_next_search_from.value();
2400  TNode<Smi> const last_matched_until = var_last_matched_until.value();
2401 
2402  // We're done if we've reached the end of the string.
2403  {
2404  Label next(this);
2405  Branch(SmiEqual(next_search_from, string_length), &push_suffix_and_out,
2406  &next);
2407  BIND(&next);
2408  }
2409 
2410  // Search for the given {regexp}.
2411 
2412  Node* const last_match_info = LoadContextElement(
2413  native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX);
2414 
2415  TNode<HeapObject> const match_indices_ho =
2416  CAST(CallBuiltin(Builtins::kRegExpExecInternal, context, regexp, string,
2417  next_search_from, last_match_info));
2418 
2419  // We're done if no match was found.
2420  {
2421  Label next(this);
2422  Branch(IsNull(match_indices_ho), &push_suffix_and_out, &next);
2423  BIND(&next);
2424  }
2425 
2426  TNode<FixedArray> match_indices = CAST(match_indices_ho);
2427  TNode<Smi> const match_from = CAST(LoadFixedArrayElement(
2428  match_indices, RegExpMatchInfo::kFirstCaptureIndex));
2429 
2430  // We're done if the match starts beyond the string.
2431  {
2432  Label next(this);
2433  Branch(SmiEqual(match_from, string_length), &push_suffix_and_out, &next);
2434  BIND(&next);
2435  }
2436 
2437  TNode<Smi> const match_to = CAST(LoadFixedArrayElement(
2438  match_indices, RegExpMatchInfo::kFirstCaptureIndex + 1));
2439 
2440  // Advance index and continue if the match is empty.
2441  {
2442  Label next(this);
2443 
2444  GotoIfNot(SmiEqual(match_to, next_search_from), &next);
2445  GotoIfNot(SmiEqual(match_to, last_matched_until), &next);
2446 
2447  Node* const is_unicode = FastFlagGetter(CAST(regexp), JSRegExp::kUnicode);
2448  Node* const new_next_search_from =
2449  AdvanceStringIndex(string, next_search_from, is_unicode, true);
2450  var_next_search_from = CAST(new_next_search_from);
2451  Goto(&loop);
2452 
2453  BIND(&next);
2454  }
2455 
2456  // A valid match was found, add the new substring to the array.
2457  {
2458  TNode<Smi> const from = last_matched_until;
2459  TNode<Smi> const to = match_from;
2460  array.Push(CallBuiltin(Builtins::kSubString, context, string, from, to));
2461  GotoIf(WordEqual(array.length(), int_limit), &out);
2462  }
2463 
2464  // Add all captures to the array.
2465  {
2466  Node* const num_registers = LoadFixedArrayElement(
2467  match_indices, RegExpMatchInfo::kNumberOfCapturesIndex);
2468  Node* const int_num_registers = SmiUntag(num_registers);
2469 
2470  VARIABLE(var_reg, MachineType::PointerRepresentation());
2471  var_reg.Bind(IntPtrConstant(2));
2472 
2473  Variable* vars[] = {array.var_array(), array.var_length(),
2474  array.var_capacity(), &var_reg};
2475  const int vars_count = sizeof(vars) / sizeof(vars[0]);
2476  Label nested_loop(this, vars_count, vars), nested_loop_out(this);
2477  Branch(IntPtrLessThan(var_reg.value(), int_num_registers), &nested_loop,
2478  &nested_loop_out);
2479 
2480  BIND(&nested_loop);
2481  {
2482  Node* const reg = var_reg.value();
2483  Node* const from = LoadFixedArrayElement(
2484  match_indices, reg,
2485  RegExpMatchInfo::kFirstCaptureIndex * kPointerSize, mode);
2486  TNode<Smi> const to = CAST(LoadFixedArrayElement(
2487  match_indices, reg,
2488  (RegExpMatchInfo::kFirstCaptureIndex + 1) * kPointerSize, mode));
2489 
2490  Label select_capture(this), select_undefined(this), store_value(this);
2491  VARIABLE(var_value, MachineRepresentation::kTagged);
2492  Branch(SmiEqual(to, SmiConstant(-1)), &select_undefined,
2493  &select_capture);
2494 
2495  BIND(&select_capture);
2496  {
2497  var_value.Bind(
2498  CallBuiltin(Builtins::kSubString, context, string, from, to));
2499  Goto(&store_value);
2500  }
2501 
2502  BIND(&select_undefined);
2503  {
2504  var_value.Bind(UndefinedConstant());
2505  Goto(&store_value);
2506  }
2507 
2508  BIND(&store_value);
2509  {
2510  array.Push(CAST(var_value.value()));
2511  GotoIf(WordEqual(array.length(), int_limit), &out);
2512 
2513  Node* const new_reg = IntPtrAdd(reg, IntPtrConstant(2));
2514  var_reg.Bind(new_reg);
2515 
2516  Branch(IntPtrLessThan(new_reg, int_num_registers), &nested_loop,
2517  &nested_loop_out);
2518  }
2519  }
2520 
2521  BIND(&nested_loop_out);
2522  }
2523 
2524  var_last_matched_until = match_to;
2525  var_next_search_from = match_to;
2526  Goto(&loop);
2527  }
2528 
2529  BIND(&push_suffix_and_out);
2530  {
2531  Node* const from = var_last_matched_until.value();
2532  Node* const to = string_length;
2533  array.Push(CallBuiltin(Builtins::kSubString, context, string, from, to));
2534  Goto(&out);
2535  }
2536 
2537  BIND(&out);
2538  {
2539  Node* const result = array.ToJSArray(CAST(context));
2540  Return(result);
2541  }
2542 
2543  BIND(&return_empty_array);
2544  {
2545  TNode<Smi> length = SmiZero();
2546  TNode<IntPtrT> capacity = IntPtrZero();
2547  TNode<JSArray> result = AllocateJSArray(kind, array_map, capacity, length,
2548  allocation_site, mode);
2549  Return(result);
2550  }
2551 }
2552 
2553 // Helper that skips a few initial checks.
2554 TF_BUILTIN(RegExpSplit, RegExpBuiltinsAssembler) {
2555  TNode<JSRegExp> regexp = CAST(Parameter(Descriptor::kRegExp));
2556  TNode<String> string = CAST(Parameter(Descriptor::kString));
2557  TNode<Object> maybe_limit = CAST(Parameter(Descriptor::kLimit));
2558  TNode<Context> context = CAST(Parameter(Descriptor::kContext));
2559 
2560  CSA_ASSERT(this, IsFastRegExp(context, regexp));
2561 
2562  // TODO(jgruber): Even if map checks send us to the fast path, we still need
2563  // to verify the constructor property and jump to the slow path if it has
2564  // been changed.
2565 
2566  // Verify {maybe_limit}.
2567 
2568  VARIABLE(var_limit, MachineRepresentation::kTagged, maybe_limit);
2569  Label if_limitissmimax(this), runtime(this, Label::kDeferred);
2570 
2571  {
2572  Label next(this);
2573 
2574  GotoIf(IsUndefined(maybe_limit), &if_limitissmimax);
2575  Branch(TaggedIsPositiveSmi(maybe_limit), &next, &runtime);
2576 
2577  // We need to be extra-strict and require the given limit to be either
2578  // undefined or a positive smi. We can't call ToUint32(maybe_limit) since
2579  // that might move us onto the slow path, resulting in ordering spec
2580  // violations (see https://crbug.com/801171).
2581 
2582  BIND(&if_limitissmimax);
2583  {
2584  // TODO(jgruber): In this case, we can probably avoid generation of limit
2585  // checks in Generate_RegExpPrototypeSplitBody.
2586  var_limit.Bind(SmiConstant(Smi::kMaxValue));
2587  Goto(&next);
2588  }
2589 
2590  BIND(&next);
2591  }
2592 
2593  // Due to specific shortcuts we take on the fast path (specifically, we don't
2594  // allocate a new regexp instance as specced), we need to ensure that the
2595  // given regexp is non-sticky to avoid invalid results. See crbug.com/v8/6706.
2596 
2597  GotoIf(FastFlagGetter(regexp, JSRegExp::kSticky), &runtime);
2598 
2599  // We're good to go on the fast path, which is inlined here.
2600 
2601  RegExpPrototypeSplitBody(context, regexp, string, CAST(var_limit.value()));
2602 
2603  BIND(&runtime);
2604  Return(CallRuntime(Runtime::kRegExpSplit, context, regexp, string,
2605  var_limit.value()));
2606 }
2607 
2608 // ES#sec-regexp.prototype-@@split
2609 // RegExp.prototype [ @@split ] ( string, limit )
2610 TF_BUILTIN(RegExpPrototypeSplit, RegExpBuiltinsAssembler) {
2611  const int kStringArg = 0;
2612  const int kLimitArg = 1;
2613 
2614  TNode<IntPtrT> argc =
2615  ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount));
2616  CodeStubArguments args(this, argc);
2617 
2618  TNode<Object> maybe_receiver = args.GetReceiver();
2619  TNode<Object> maybe_string = args.GetOptionalArgumentValue(kStringArg);
2620  TNode<Object> maybe_limit = args.GetOptionalArgumentValue(kLimitArg);
2621  TNode<Context> context = CAST(Parameter(Descriptor::kContext));
2622 
2623  // Ensure {maybe_receiver} is a JSReceiver.
2624  ThrowIfNotJSReceiver(context, maybe_receiver,
2625  MessageTemplate::kIncompatibleMethodReceiver,
2626  "RegExp.prototype.@@split");
2627  Node* const receiver = maybe_receiver;
2628 
2629  // Convert {maybe_string} to a String.
2630  TNode<String> const string = ToString_Inline(context, maybe_string);
2631 
2632  Label stub(this), runtime(this, Label::kDeferred);
2633  BranchIfFastRegExp(context, receiver, &stub, &runtime);
2634 
2635  BIND(&stub);
2636  args.PopAndReturn(CallBuiltin(Builtins::kRegExpSplit, context, receiver,
2637  string, maybe_limit));
2638 
2639  BIND(&runtime);
2640  args.PopAndReturn(CallRuntime(Runtime::kRegExpSplit, context, receiver,
2641  string, maybe_limit));
2642 }
2643 
2644 Node* RegExpBuiltinsAssembler::ReplaceGlobalCallableFastPath(
2645  Node* context, Node* regexp, Node* string, Node* replace_callable) {
2646  // The fast path is reached only if {receiver} is a global unmodified
2647  // JSRegExp instance and {replace_callable} is callable.
2648 
2649  CSA_ASSERT(this, IsFastRegExp(context, regexp));
2650  CSA_ASSERT(this, IsCallable(replace_callable));
2651  CSA_ASSERT(this, IsString(string));
2652 
2653  Isolate* const isolate = this->isolate();
2654 
2655  Node* const undefined = UndefinedConstant();
2656  TNode<IntPtrT> int_one = IntPtrConstant(1);
2657 
2658  Node* const native_context = LoadNativeContext(context);
2659 
2660  Label out(this);
2661  VARIABLE(var_result, MachineRepresentation::kTagged);
2662 
2663  // Set last index to 0.
2664  FastStoreLastIndex(regexp, SmiZero());
2665 
2666  // Allocate {result_array}.
2667  Node* result_array;
2668  {
2669  ElementsKind kind = PACKED_ELEMENTS;
2670  TNode<Map> array_map = LoadJSArrayElementsMap(kind, native_context);
2671  TNode<IntPtrT> capacity = IntPtrConstant(16);
2672  TNode<Smi> length = SmiZero();
2673  Node* const allocation_site = nullptr;
2674  ParameterMode capacity_mode = CodeStubAssembler::INTPTR_PARAMETERS;
2675 
2676  result_array = AllocateJSArray(kind, array_map, capacity, length,
2677  allocation_site, capacity_mode);
2678  }
2679 
2680  // Call into runtime for RegExpExecMultiple.
2681  TNode<FixedArray> last_match_info = CAST(LoadContextElement(
2682  native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX));
2683  Node* const res = CallRuntime(Runtime::kRegExpExecMultiple, context, regexp,
2684  string, last_match_info, result_array);
2685 
2686  // Reset last index to 0.
2687  FastStoreLastIndex(regexp, SmiZero());
2688 
2689  // If no matches, return the subject string.
2690  var_result.Bind(string);
2691  GotoIf(IsNull(res), &out);
2692 
2693  // Reload last match info since it might have changed.
2694  last_match_info = CAST(LoadContextElement(
2695  native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX));
2696 
2697  Node* const res_length = LoadJSArrayLength(res);
2698  TNode<FixedArray> const res_elems = CAST(LoadElements(res));
2699 
2700  TNode<Smi> const num_capture_registers = CAST(LoadFixedArrayElement(
2701  last_match_info, RegExpMatchInfo::kNumberOfCapturesIndex));
2702 
2703  Label if_hasexplicitcaptures(this), if_noexplicitcaptures(this),
2704  create_result(this);
2705  Branch(SmiEqual(num_capture_registers, SmiConstant(2)),
2706  &if_noexplicitcaptures, &if_hasexplicitcaptures);
2707 
2708  BIND(&if_noexplicitcaptures);
2709  {
2710  // If the number of captures is two then there are no explicit captures in
2711  // the regexp, just the implicit capture that captures the whole match. In
2712  // this case we can simplify quite a bit and end up with something faster.
2713  // The builder will consist of some integers that indicate slices of the
2714  // input string and some replacements that were returned from the replace
2715  // function.
2716 
2717  TVARIABLE(Smi, var_match_start, SmiZero());
2718 
2719  TNode<IntPtrT> const end = SmiUntag(res_length);
2720  TVARIABLE(IntPtrT, var_i, IntPtrZero());
2721 
2722  Variable* vars[] = {&var_i, &var_match_start};
2723  Label loop(this, 2, vars);
2724  Goto(&loop);
2725  BIND(&loop);
2726  {
2727  GotoIfNot(IntPtrLessThan(var_i.value(), end), &create_result);
2728 
2729  Node* const elem = LoadFixedArrayElement(res_elems, var_i.value());
2730 
2731  Label if_issmi(this), if_isstring(this), loop_epilogue(this);
2732  Branch(TaggedIsSmi(elem), &if_issmi, &if_isstring);
2733 
2734  BIND(&if_issmi);
2735  {
2736  TNode<Smi> smi_elem = CAST(elem);
2737  // Integers represent slices of the original string.
2738  Label if_isnegativeorzero(this), if_ispositive(this);
2739  BranchIfSmiLessThanOrEqual(smi_elem, SmiZero(), &if_isnegativeorzero,
2740  &if_ispositive);
2741 
2742  BIND(&if_ispositive);
2743  {
2744  TNode<IntPtrT> int_elem = SmiUntag(smi_elem);
2745  TNode<IntPtrT> new_match_start =
2746  Signed(IntPtrAdd(WordShr(int_elem, IntPtrConstant(11)),
2747  WordAnd(int_elem, IntPtrConstant(0x7FF))));
2748  var_match_start = SmiTag(new_match_start);
2749  Goto(&loop_epilogue);
2750  }
2751 
2752  BIND(&if_isnegativeorzero);
2753  {
2754  var_i = IntPtrAdd(var_i.value(), int_one);
2755 
2756  TNode<Smi> const next_elem =
2757  CAST(LoadFixedArrayElement(res_elems, var_i.value()));
2758 
2759  var_match_start = SmiSub(next_elem, smi_elem);
2760  Goto(&loop_epilogue);
2761  }
2762  }
2763 
2764  BIND(&if_isstring);
2765  {
2766  CSA_ASSERT(this, IsString(elem));
2767 
2768  Callable call_callable = CodeFactory::Call(isolate);
2769  TNode<Smi> match_start = var_match_start.value();
2770  Node* const replacement_obj =
2771  CallJS(call_callable, context, replace_callable, undefined, elem,
2772  match_start, string);
2773 
2774  TNode<String> const replacement_str =
2775  ToString_Inline(context, replacement_obj);
2776  StoreFixedArrayElement(res_elems, var_i.value(), replacement_str);
2777 
2778  TNode<Smi> const elem_length = LoadStringLengthAsSmi(elem);
2779  var_match_start = SmiAdd(match_start, elem_length);
2780 
2781  Goto(&loop_epilogue);
2782  }
2783 
2784  BIND(&loop_epilogue);
2785  {
2786  var_i = IntPtrAdd(var_i.value(), int_one);
2787  Goto(&loop);
2788  }
2789  }
2790  }
2791 
2792  BIND(&if_hasexplicitcaptures);
2793  {
2794  Node* const from = IntPtrZero();
2795  Node* const to = SmiUntag(res_length);
2796  const int increment = 1;
2797 
2798  BuildFastLoop(from, to,
2799  [this, res_elems, isolate, native_context, context, undefined,
2800  replace_callable](Node* index) {
2801  Node* const elem = LoadFixedArrayElement(res_elems, index);
2802 
2803  Label do_continue(this);
2804  GotoIf(TaggedIsSmi(elem), &do_continue);
2805 
2806  // elem must be an Array.
2807  // Use the apply argument as backing for global RegExp
2808  // properties.
2809 
2810  CSA_ASSERT(this, HasInstanceType(elem, JS_ARRAY_TYPE));
2811 
2812  // TODO(jgruber): Remove indirection through
2813  // Call->ReflectApply.
2814  Callable call_callable = CodeFactory::Call(isolate);
2815  Node* const reflect_apply = LoadContextElement(
2816  native_context, Context::REFLECT_APPLY_INDEX);
2817 
2818  Node* const replacement_obj =
2819  CallJS(call_callable, context, reflect_apply, undefined,
2820  replace_callable, undefined, elem);
2821 
2822  // Overwrite the i'th element in the results with the string
2823  // we got back from the callback function.
2824 
2825  TNode<String> const replacement_str =
2826  ToString_Inline(context, replacement_obj);
2827  StoreFixedArrayElement(res_elems, index, replacement_str);
2828 
2829  Goto(&do_continue);
2830  BIND(&do_continue);
2831  },
2832  increment, CodeStubAssembler::INTPTR_PARAMETERS,
2833  CodeStubAssembler::IndexAdvanceMode::kPost);
2834 
2835  Goto(&create_result);
2836  }
2837 
2838  BIND(&create_result);
2839  {
2840  Node* const result = CallRuntime(Runtime::kStringBuilderConcat, context,
2841  res, res_length, string);
2842  var_result.Bind(result);
2843  Goto(&out);
2844  }
2845 
2846  BIND(&out);
2847  return var_result.value();
2848 }
2849 
2850 Node* RegExpBuiltinsAssembler::ReplaceSimpleStringFastPath(
2851  Node* context, Node* regexp, TNode<String> string,
2852  TNode<String> replace_string) {
2853  // The fast path is reached only if {receiver} is an unmodified
2854  // JSRegExp instance, {replace_value} is non-callable, and
2855  // ToString({replace_value}) does not contain '$', i.e. we're doing a simple
2856  // string replacement.
2857 
2858  CSA_ASSERT(this, IsFastRegExp(context, regexp));
2859 
2860  const bool kIsFastPath = true;
2861 
2862  TVARIABLE(String, var_result, EmptyStringConstant());
2863  VARIABLE(var_last_match_end, MachineRepresentation::kTagged, SmiZero());
2864  VARIABLE(var_is_unicode, MachineRepresentation::kWord32, Int32Constant(0));
2865  Variable* vars[] = {&var_result, &var_last_match_end};
2866  Label out(this), loop(this, 2, vars), loop_end(this),
2867  if_nofurthermatches(this);
2868 
2869  // Is {regexp} global?
2870  Node* const is_global = FastFlagGetter(CAST(regexp), JSRegExp::kGlobal);
2871  GotoIfNot(is_global, &loop);
2872 
2873  var_is_unicode.Bind(FastFlagGetter(CAST(regexp), JSRegExp::kUnicode));
2874  FastStoreLastIndex(regexp, SmiZero());
2875  Goto(&loop);
2876 
2877  BIND(&loop);
2878  {
2879  TNode<RegExpMatchInfo> var_match_indices =
2880  RegExpPrototypeExecBodyWithoutResult(CAST(context), CAST(regexp),
2881  string, &if_nofurthermatches,
2882  kIsFastPath);
2883 
2884  // Successful match.
2885  {
2886  TNode<Smi> const match_start = CAST(LoadFixedArrayElement(
2887  var_match_indices, RegExpMatchInfo::kFirstCaptureIndex));
2888  TNode<Smi> const match_end = CAST(LoadFixedArrayElement(
2889  var_match_indices, RegExpMatchInfo::kFirstCaptureIndex + 1));
2890 
2891  TNode<Smi> const replace_length = LoadStringLengthAsSmi(replace_string);
2892 
2893  // TODO(jgruber): We could skip many of the checks that using SubString
2894  // here entails.
2895  TNode<String> first_part =
2896  CAST(CallBuiltin(Builtins::kSubString, context, string,
2897  var_last_match_end.value(), match_start));
2898  var_result = CAST(CallBuiltin(Builtins::kStringAdd_CheckNone, context,
2899  var_result.value(), first_part));
2900 
2901  GotoIf(SmiEqual(replace_length, SmiZero()), &loop_end);
2902 
2903  var_result = CAST(CallBuiltin(Builtins::kStringAdd_CheckNone, context,
2904  var_result.value(), replace_string));
2905  Goto(&loop_end);
2906 
2907  BIND(&loop_end);
2908  {
2909  var_last_match_end.Bind(match_end);
2910  // Non-global case ends here after the first replacement.
2911  GotoIfNot(is_global, &if_nofurthermatches);
2912 
2913  GotoIf(SmiNotEqual(match_end, match_start), &loop);
2914  // If match is the empty string, we have to increment lastIndex.
2915  Node* const this_index = FastLoadLastIndex(CAST(regexp));
2916  Node* const next_index = AdvanceStringIndex(
2917  string, this_index, var_is_unicode.value(), kIsFastPath);
2918  FastStoreLastIndex(regexp, next_index);
2919  Goto(&loop);
2920  }
2921  }
2922  }
2923 
2924  BIND(&if_nofurthermatches);
2925  {
2926  TNode<Smi> const string_length = LoadStringLengthAsSmi(string);
2927  TNode<String> last_part =
2928  CAST(CallBuiltin(Builtins::kSubString, context, string,
2929  var_last_match_end.value(), string_length));
2930  var_result = CAST(CallBuiltin(Builtins::kStringAdd_CheckNone, context,
2931  var_result.value(), last_part));
2932  Goto(&out);
2933  }
2934 
2935  BIND(&out);
2936  return var_result.value();
2937 }
2938 
2939 // Helper that skips a few initial checks.
2940 TF_BUILTIN(RegExpReplace, RegExpBuiltinsAssembler) {
2941  TNode<JSRegExp> regexp = CAST(Parameter(Descriptor::kRegExp));
2942  TNode<String> string = CAST(Parameter(Descriptor::kString));
2943  TNode<Object> replace_value = CAST(Parameter(Descriptor::kReplaceValue));
2944  TNode<Context> context = CAST(Parameter(Descriptor::kContext));
2945 
2946  CSA_ASSERT(this, IsFastRegExp(context, regexp));
2947 
2948  Label checkreplacestring(this), if_iscallable(this),
2949  runtime(this, Label::kDeferred);
2950 
2951  // 2. Is {replace_value} callable?
2952  GotoIf(TaggedIsSmi(replace_value), &checkreplacestring);
2953  Branch(IsCallableMap(LoadMap(CAST(replace_value))), &if_iscallable,
2954  &checkreplacestring);
2955 
2956  // 3. Does ToString({replace_value}) contain '$'?
2957  BIND(&checkreplacestring);
2958  {
2959  TNode<String> const replace_string =
2960  ToString_Inline(context, replace_value);
2961 
2962  // ToString(replaceValue) could potentially change the shape of the RegExp
2963  // object. Recheck that we are still on the fast path and bail to runtime
2964  // otherwise.
2965  {
2966  Label next(this);
2967  BranchIfFastRegExp(context, regexp, &next, &runtime);
2968  BIND(&next);
2969  }
2970 
2971  TNode<String> const dollar_string = HeapConstant(
2972  isolate()->factory()->LookupSingleCharacterStringFromCode('$'));
2973  TNode<Smi> const dollar_ix =
2974  CAST(CallBuiltin(Builtins::kStringIndexOf, context, replace_string,
2975  dollar_string, SmiZero()));
2976  GotoIfNot(SmiEqual(dollar_ix, SmiConstant(-1)), &runtime);
2977 
2978  Return(
2979  ReplaceSimpleStringFastPath(context, regexp, string, replace_string));
2980  }
2981 
2982  // {regexp} is unmodified and {replace_value} is callable.
2983  BIND(&if_iscallable);
2984  {
2985  Node* const replace_fn = replace_value;
2986 
2987  // Check if the {regexp} is global.
2988  Label if_isglobal(this), if_isnotglobal(this);
2989 
2990  Node* const is_global = FastFlagGetter(regexp, JSRegExp::kGlobal);
2991  Branch(is_global, &if_isglobal, &if_isnotglobal);
2992 
2993  BIND(&if_isglobal);
2994  Return(ReplaceGlobalCallableFastPath(context, regexp, string, replace_fn));
2995 
2996  BIND(&if_isnotglobal);
2997  Return(CallRuntime(Runtime::kStringReplaceNonGlobalRegExpWithFunction,
2998  context, string, regexp, replace_fn));
2999  }
3000 
3001  BIND(&runtime);
3002  Return(CallRuntime(Runtime::kRegExpReplace, context, regexp, string,
3003  replace_value));
3004 }
3005 
3006 // ES#sec-regexp.prototype-@@replace
3007 // RegExp.prototype [ @@replace ] ( string, replaceValue )
3008 TF_BUILTIN(RegExpPrototypeReplace, RegExpBuiltinsAssembler) {
3009  const int kStringArg = 0;
3010  const int kReplaceValueArg = 1;
3011 
3012  TNode<IntPtrT> argc =
3013  ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount));
3014  CodeStubArguments args(this, argc);
3015 
3016  TNode<Object> maybe_receiver = args.GetReceiver();
3017  TNode<Object> maybe_string = args.GetOptionalArgumentValue(kStringArg);
3018  TNode<Object> replace_value = args.GetOptionalArgumentValue(kReplaceValueArg);
3019  TNode<Context> context = CAST(Parameter(Descriptor::kContext));
3020 
3021  // RegExpPrototypeReplace is a bit of a beast - a summary of dispatch logic:
3022  //
3023  // if (!IsFastRegExp(receiver)) CallRuntime(RegExpReplace)
3024  // if (IsCallable(replace)) {
3025  // if (IsGlobal(receiver)) {
3026  // // Called 'fast-path' but contains several runtime calls.
3027  // ReplaceGlobalCallableFastPath()
3028  // } else {
3029  // CallRuntime(StringReplaceNonGlobalRegExpWithFunction)
3030  // }
3031  // } else {
3032  // if (replace.contains("$")) {
3033  // CallRuntime(RegExpReplace)
3034  // } else {
3035  // ReplaceSimpleStringFastPath()
3036  // }
3037  // }
3038 
3039  // Ensure {maybe_receiver} is a JSReceiver.
3040  ThrowIfNotJSReceiver(context, maybe_receiver,
3041  MessageTemplate::kIncompatibleMethodReceiver,
3042  "RegExp.prototype.@@replace");
3043  Node* const receiver = maybe_receiver;
3044 
3045  // Convert {maybe_string} to a String.
3046  TNode<String> const string = ToString_Inline(context, maybe_string);
3047 
3048  // Fast-path checks: 1. Is the {receiver} an unmodified JSRegExp instance?
3049  Label stub(this), runtime(this, Label::kDeferred);
3050  BranchIfFastRegExp(context, receiver, &stub, &runtime);
3051 
3052  BIND(&stub);
3053  args.PopAndReturn(CallBuiltin(Builtins::kRegExpReplace, context, receiver,
3054  string, replace_value));
3055 
3056  BIND(&runtime);
3057  args.PopAndReturn(CallRuntime(Runtime::kRegExpReplace, context, receiver,
3058  string, replace_value));
3059 }
3060 
3061 // Simple string matching functionality for internal use which does not modify
3062 // the last match info.
3063 TF_BUILTIN(RegExpInternalMatch, RegExpBuiltinsAssembler) {
3064  TNode<JSRegExp> regexp = CAST(Parameter(Descriptor::kRegExp));
3065  TNode<String> string = CAST(Parameter(Descriptor::kString));
3066  TNode<Context> context = CAST(Parameter(Descriptor::kContext));
3067 
3068  TNode<Context> native_context = LoadNativeContext(context);
3069  TNode<RegExpMatchInfo> internal_match_info = CAST(LoadContextElement(
3070  native_context, Context::REGEXP_INTERNAL_MATCH_INFO_INDEX));
3071  TNode<HeapObject> maybe_match_indices =
3072  CAST(CallBuiltin(Builtins::kRegExpExecInternal, context, regexp, string,
3073  SmiZero(), internal_match_info));
3074  TNode<Oddball> null = NullConstant();
3075  Label if_matched(this);
3076  GotoIfNot(WordEqual(maybe_match_indices, null), &if_matched);
3077  Return(null);
3078 
3079  BIND(&if_matched);
3080  TNode<RegExpMatchInfo> match_indices = CAST(maybe_match_indices);
3081  Return(
3082  ConstructNewResultFromMatchInfo(context, regexp, match_indices, string));
3083 }
3084 
3086  public:
3088  : RegExpBuiltinsAssembler(state) {}
3089 
3090  protected:
3091  TNode<Smi> LoadFlags(TNode<HeapObject> iterator) {
3092  return LoadObjectField<Smi>(iterator, JSRegExpStringIterator::kFlagsOffset);
3093  }
3094 
3095  TNode<BoolT> HasDoneFlag(TNode<Smi> flags) {
3096  return UncheckedCast<BoolT>(
3097  IsSetSmi(flags, 1 << JSRegExpStringIterator::kDoneBit));
3098  }
3099 
3100  TNode<BoolT> HasGlobalFlag(TNode<Smi> flags) {
3101  return UncheckedCast<BoolT>(
3102  IsSetSmi(flags, 1 << JSRegExpStringIterator::kGlobalBit));
3103  }
3104 
3105  TNode<BoolT> HasUnicodeFlag(TNode<Smi> flags) {
3106  return UncheckedCast<BoolT>(
3107  IsSetSmi(flags, 1 << JSRegExpStringIterator::kUnicodeBit));
3108  }
3109 
3110  void SetDoneFlag(TNode<HeapObject> iterator, TNode<Smi> flags) {
3111  TNode<Smi> new_flags =
3112  SmiOr(flags, SmiConstant(1 << JSRegExpStringIterator::kDoneBit));
3113  StoreObjectFieldNoWriteBarrier(
3114  iterator, JSRegExpStringIterator::kFlagsOffset, new_flags);
3115  }
3116 };
3117 
3118 // https://tc39.github.io/proposal-string-matchall/
3119 // %RegExpStringIteratorPrototype%.next ( )
3120 TF_BUILTIN(RegExpStringIteratorPrototypeNext, RegExpStringIteratorAssembler) {
3121  const char* method_name = "%RegExpStringIterator%.prototype.next";
3122  TNode<Context> context = CAST(Parameter(Descriptor::kContext));
3123  TNode<Object> maybe_receiver = CAST(Parameter(Descriptor::kReceiver));
3124 
3125  Label if_match(this), if_no_match(this, Label::kDeferred),
3126  return_empty_done_result(this, Label::kDeferred);
3127 
3128  // 1. Let O be the this value.
3129  // 2. If Type(O) is not Object, throw a TypeError exception.
3130  // 3. If O does not have all of the internal slots of a RegExp String Iterator
3131  // Object Instance (see 5.3), throw a TypeError exception.
3132  ThrowIfNotInstanceType(context, maybe_receiver,
3133  JS_REGEXP_STRING_ITERATOR_TYPE, method_name);
3134  TNode<HeapObject> receiver = CAST(maybe_receiver);
3135 
3136  // 4. If O.[[Done]] is true, then
3137  // a. Return ! CreateIterResultObject(undefined, true).
3138  TNode<Smi> flags = LoadFlags(receiver);
3139  GotoIf(HasDoneFlag(flags), &return_empty_done_result);
3140 
3141  // 5. Let R be O.[[IteratingRegExp]].
3142  TNode<Object> iterating_regexp =
3143  LoadObjectField(receiver, JSRegExpStringIterator::kIteratingRegExpOffset);
3144 
3145  // TODO(jgruber): Verify that this is guaranteed.
3146  CSA_CHECK(this, TaggedIsNotSmi(iterating_regexp));
3147  CSA_CHECK(this, IsJSReceiver(CAST(iterating_regexp)));
3148 
3149  // 6. Let S be O.[[IteratedString]].
3150  TNode<String> iterating_string = CAST(
3151  LoadObjectField(receiver, JSRegExpStringIterator::kIteratedStringOffset));
3152 
3153  // 7. Let global be O.[[Global]].
3154  // See if_match.
3155 
3156  // 8. Let fullUnicode be O.[[Unicode]].
3157  // See if_global.
3158 
3159  // 9. Let match be ? RegExpExec(R, S).
3160  TVARIABLE(Object, var_match);
3161  TVARIABLE(BoolT, var_is_fast_regexp);
3162  {
3163  Label if_fast(this), if_slow(this, Label::kDeferred);
3164  BranchIfFastRegExp(context, iterating_regexp, &if_fast, &if_slow);
3165 
3166  BIND(&if_fast);
3167  {
3168  TNode<RegExpMatchInfo> match_indices =
3169  RegExpPrototypeExecBodyWithoutResult(context, CAST(iterating_regexp),
3170  iterating_string, &if_no_match,
3171  true);
3172  var_match = ConstructNewResultFromMatchInfo(
3173  context, CAST(iterating_regexp), match_indices, iterating_string);
3174  var_is_fast_regexp = Int32TrueConstant();
3175  Goto(&if_match);
3176  }
3177 
3178  BIND(&if_slow);
3179  {
3180  var_match = CAST(RegExpExec(context, iterating_regexp, iterating_string));
3181  var_is_fast_regexp = Int32FalseConstant();
3182  Branch(IsNull(var_match.value()), &if_no_match, &if_match);
3183  }
3184  }
3185 
3186  // 10. If match is null, then
3187  BIND(&if_no_match);
3188  {
3189  // a. Set O.[[Done]] to true.
3190  SetDoneFlag(receiver, flags);
3191 
3192  // b. Return ! CreateIterResultObject(undefined, true).
3193  Goto(&return_empty_done_result);
3194  }
3195  // 11. Else,
3196  BIND(&if_match);
3197  {
3198  Label if_global(this), if_not_global(this, Label::kDeferred),
3199  return_result(this);
3200 
3201  // a. If global is true,
3202  Branch(HasGlobalFlag(flags), &if_global, &if_not_global);
3203  BIND(&if_global);
3204  {
3205  Label if_fast(this), if_slow(this, Label::kDeferred);
3206 
3207  // ii. If matchStr is the empty string,
3208  Branch(var_is_fast_regexp.value(), &if_fast, &if_slow);
3209  BIND(&if_fast);
3210  {
3211  // i. Let matchStr be ? ToString(? Get(match, "0")).
3212  CSA_ASSERT_BRANCH(this, [&](Label* ok, Label* not_ok) {
3213  BranchIfFastRegExpResult(context, var_match.value(), ok, not_ok);
3214  });
3215  CSA_ASSERT(this,
3216  SmiNotEqual(LoadFastJSArrayLength(CAST(var_match.value())),
3217  SmiZero()));
3218  TNode<FixedArray> result_fixed_array =
3219  CAST(LoadElements(CAST(var_match.value())));
3220  TNode<String> match_str =
3221  CAST(LoadFixedArrayElement(result_fixed_array, 0));
3222 
3223  // When iterating_regexp is fast, we assume it stays fast even after
3224  // accessing the first match from the RegExp result.
3225  CSA_ASSERT(this, IsFastRegExp(context, iterating_regexp));
3226  GotoIfNot(IsEmptyString(match_str), &return_result);
3227 
3228  // 1. Let thisIndex be ? ToLength(? Get(R, "lastIndex")).
3229  TNode<Smi> this_index = CAST(FastLoadLastIndex(CAST(iterating_regexp)));
3230  CSA_ASSERT(this, TaggedIsSmi(this_index));
3231 
3232  // 2. Let nextIndex be ! AdvanceStringIndex(S, thisIndex, fullUnicode).
3233  TNode<Smi> next_index = CAST(AdvanceStringIndex(
3234  iterating_string, this_index, HasUnicodeFlag(flags), true));
3235  CSA_ASSERT(this, TaggedIsSmi(next_index));
3236 
3237  // 3. Perform ? Set(R, "lastIndex", nextIndex, true).
3238  FastStoreLastIndex(iterating_regexp, next_index);
3239 
3240  // iii. Return ! CreateIterResultObject(match, false).
3241  Goto(&return_result);
3242  }
3243  BIND(&if_slow);
3244  {
3245  // i. Let matchStr be ? ToString(? Get(match, "0")).
3246  TNode<String> match_str = ToString_Inline(
3247  context, GetProperty(context, var_match.value(), SmiZero()));
3248 
3249  GotoIfNot(IsEmptyString(match_str), &return_result);
3250 
3251  // 1. Let thisIndex be ? ToLength(? Get(R, "lastIndex")).
3252  TNode<Object> last_index = SlowLoadLastIndex(context, iterating_regexp);
3253  TNode<Number> this_index = ToLength_Inline(context, last_index);
3254 
3255  // 2. Let nextIndex be ! AdvanceStringIndex(S, thisIndex, fullUnicode).
3256  TNode<Object> next_index = CAST(AdvanceStringIndex(
3257  iterating_string, this_index, HasUnicodeFlag(flags), false));
3258 
3259  // 3. Perform ? Set(R, "lastIndex", nextIndex, true).
3260  SlowStoreLastIndex(context, iterating_regexp, next_index);
3261 
3262  // iii. Return ! CreateIterResultObject(match, false).
3263  Goto(&return_result);
3264  }
3265  }
3266  // b. Else,
3267  BIND(&if_not_global);
3268  {
3269  // i. Set O.[[Done]] to true.
3270  SetDoneFlag(receiver, flags);
3271 
3272  // ii. Return ! CreateIterResultObject(match, false).
3273  Goto(&return_result);
3274  }
3275  BIND(&return_result);
3276  {
3277  Return(AllocateJSIteratorResult(context, var_match.value(),
3278  FalseConstant()));
3279  }
3280  }
3281  BIND(&return_empty_done_result);
3282  Return(
3283  AllocateJSIteratorResult(context, UndefinedConstant(), TrueConstant()));
3284 }
3285 
3286 } // namespace internal
3287 } // namespace v8
Definition: libplatform.h:13