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
SmiZero()27 TNode<Smi> RegExpBuiltinsAssembler::SmiZero() { return SmiConstant(0); }
28
IntPtrZero()29 TNode<IntPtrT> RegExpBuiltinsAssembler::IntPtrZero() {
30 return IntPtrConstant(0);
31 }
32
33 // -----------------------------------------------------------------------------
34 // ES6 section 21.2 RegExp Objects
35
AllocateRegExpResult(TNode<Context> context,TNode<Smi> length,TNode<Smi> index,TNode<String> input)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 Node* result = Allocate(total_size);
62 Node* 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 Heap::RootListIndex map_index = Heap::kFixedArrayMapRootIndex;
85 DCHECK(Heap::RootIsImmortalImmovable(map_index));
86 StoreMapNoWriteBarrier(elements, map_index);
87 StoreObjectFieldNoWriteBarrier(elements, FixedArray::kLengthOffset, length);
88
89 FillFixedArrayWithValue(elements_kind, elements, IntPtrZero(), length_intptr,
90 Heap::kUndefinedValueRootIndex);
91
92 return CAST(result);
93 }
94
RegExpCreate(TNode<Context> context,TNode<Context> native_context,TNode<Object> maybe_string,TNode<String> flags)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
RegExpCreate(TNode<Context> context,TNode<Map> initial_map,TNode<Object> maybe_string,TNode<String> flags)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
FastLoadLastIndex(TNode<JSRegExp> regexp)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
SlowLoadLastIndex(TNode<Context> context,TNode<Object> regexp)125 TNode<Object> RegExpBuiltinsAssembler::SlowLoadLastIndex(TNode<Context> context,
126 TNode<Object> regexp) {
127 return GetProperty(context, regexp, isolate()->factory()->lastIndex_string());
128 }
129
LoadLastIndex(TNode<Context> context,TNode<Object> regexp,bool is_fastpath)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.
FastStoreLastIndex(Node * regexp,Node * value)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
SlowStoreLastIndex(Node * context,Node * regexp,Node * value)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
StoreLastIndex(Node * context,Node * regexp,Node * value,bool is_fastpath)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
ConstructNewResultFromMatchInfo(TNode<Context> context,TNode<JSReceiver> maybe_regexp,TNode<RegExpMatchInfo> match_info,TNode<String> string)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
GetStringPointers(Node * const string_data,Node * const offset,Node * const last_index,Node * const string_length,String::Encoding encoding,Variable * var_string_start,Variable * var_string_end)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
RegExpExecInternal(TNode<Context> context,TNode<JSRegExp> regexp,TNode<String> string,TNode<Number> last_index,TNode<RegExpMatchInfo> match_info)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>
RegExpPrototypeExecBodyWithoutResult(TNode<Context> context,TNode<JSReceiver> maybe_regexp,TNode<String> string,Label * if_didnotmatch,const bool is_fastpath)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 )
RegExpPrototypeExecBody(TNode<Context> context,TNode<JSReceiver> maybe_regexp,TNode<String> string,const bool is_fastpath)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
ThrowIfNotJSReceiver(Node * context,Node * maybe_receiver,MessageTemplate::Template msg_template,char const * method_name)797 Node* RegExpBuiltinsAssembler::ThrowIfNotJSReceiver(
798 Node* context, Node* maybe_receiver, MessageTemplate::Template 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
IsFastRegExpNoPrototype(Node * const context,Node * const object,Node * const map)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.
IsFastRegExpWithOriginalExec(TNode<Context> context,TNode<JSRegExp> object)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(0);
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
IsFastRegExpNoPrototype(Node * const context,Node * const object)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.
BranchIfFastRegExp(Node * const context,Node * const object,Node * const map,Label * const if_isunmodified,Label * const if_ismodified)910 void RegExpBuiltinsAssembler::BranchIfFastRegExp(Node* const context,
911 Node* const object,
912 Node* const map,
913 Label* const if_isunmodified,
914 Label* const if_ismodified) {
915 CSA_ASSERT(this, WordEqual(LoadMap(object), map));
916
917 GotoIfForceSlowPath(if_ismodified);
918
919 // TODO(ishell): Update this check once map changes for constant field
920 // tracking are landing.
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 Node* const proto_map = LoadMap(LoadMapPrototype(map));
934 Node* const proto_has_initialmap =
935 WordEqual(proto_map, initial_proto_initial_map);
936
937 GotoIfNot(proto_has_initialmap, if_ismodified);
938
939 // The smi check is required to omit ToLength(lastIndex) calls with possible
940 // user-code execution on the fast path.
941 Node* const last_index = FastLoadLastIndex(CAST(object));
942 Branch(TaggedIsPositiveSmi(last_index), if_isunmodified, if_ismodified);
943 }
944
BranchIfFastRegExp(Node * const context,Node * const object,Label * const if_isunmodified,Label * const if_ismodified)945 void RegExpBuiltinsAssembler::BranchIfFastRegExp(Node* const context,
946 Node* const object,
947 Label* const if_isunmodified,
948 Label* const if_ismodified) {
949 CSA_ASSERT(this, TaggedIsNotSmi(object));
950 BranchIfFastRegExp(context, object, LoadMap(object), if_isunmodified,
951 if_ismodified);
952 }
953
IsFastRegExp(SloppyTNode<Context> context,SloppyTNode<Object> object)954 TNode<BoolT> RegExpBuiltinsAssembler::IsFastRegExp(SloppyTNode<Context> context,
955 SloppyTNode<Object> object) {
956 Label yup(this), nope(this), out(this);
957 TVARIABLE(BoolT, var_result);
958
959 BranchIfFastRegExp(context, object, &yup, &nope);
960
961 BIND(&yup);
962 var_result = Int32TrueConstant();
963 Goto(&out);
964
965 BIND(&nope);
966 var_result = Int32FalseConstant();
967 Goto(&out);
968
969 BIND(&out);
970 return var_result.value();
971 }
972
BranchIfFastRegExpResult(Node * const context,Node * const object,Label * if_isunmodified,Label * if_ismodified)973 void RegExpBuiltinsAssembler::BranchIfFastRegExpResult(Node* const context,
974 Node* const object,
975 Label* if_isunmodified,
976 Label* if_ismodified) {
977 // Could be a Smi.
978 Node* const map = LoadReceiverMap(object);
979
980 Node* const native_context = LoadNativeContext(context);
981 Node* const initial_regexp_result_map =
982 LoadContextElement(native_context, Context::REGEXP_RESULT_MAP_INDEX);
983
984 Branch(WordEqual(map, initial_regexp_result_map), if_isunmodified,
985 if_ismodified);
986 }
987
988 // Slow path stub for RegExpPrototypeExec to decrease code size.
TF_BUILTIN(RegExpPrototypeExecSlow,RegExpBuiltinsAssembler)989 TF_BUILTIN(RegExpPrototypeExecSlow, RegExpBuiltinsAssembler) {
990 TNode<JSRegExp> regexp = CAST(Parameter(Descriptor::kReceiver));
991 TNode<String> string = CAST(Parameter(Descriptor::kString));
992 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
993
994 Return(RegExpPrototypeExecBody(context, regexp, string, false));
995 }
996
997 // Fast path stub for ATOM regexps. String matching is done by StringIndexOf,
998 // and {match_info} is updated on success.
999 // The slow path is implemented in RegExpImpl::AtomExec.
TF_BUILTIN(RegExpExecAtom,RegExpBuiltinsAssembler)1000 TF_BUILTIN(RegExpExecAtom, RegExpBuiltinsAssembler) {
1001 TNode<JSRegExp> regexp = CAST(Parameter(Descriptor::kRegExp));
1002 TNode<String> subject_string = CAST(Parameter(Descriptor::kString));
1003 TNode<Smi> last_index = CAST(Parameter(Descriptor::kLastIndex));
1004 TNode<FixedArray> match_info = CAST(Parameter(Descriptor::kMatchInfo));
1005 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1006
1007 CSA_ASSERT(this, TaggedIsPositiveSmi(last_index));
1008
1009 TNode<FixedArray> data = CAST(LoadObjectField(regexp, JSRegExp::kDataOffset));
1010 CSA_ASSERT(this,
1011 SmiEqual(CAST(LoadFixedArrayElement(data, JSRegExp::kTagIndex)),
1012 SmiConstant(JSRegExp::ATOM)));
1013
1014 // Callers ensure that last_index is in-bounds.
1015 CSA_ASSERT(this,
1016 UintPtrLessThanOrEqual(SmiUntag(last_index),
1017 LoadStringLengthAsWord(subject_string)));
1018
1019 Node* const needle_string =
1020 LoadFixedArrayElement(data, JSRegExp::kAtomPatternIndex);
1021 CSA_ASSERT(this, IsString(needle_string));
1022
1023 TNode<Smi> const match_from =
1024 CAST(CallBuiltin(Builtins::kStringIndexOf, context, subject_string,
1025 needle_string, last_index));
1026
1027 Label if_failure(this), if_success(this);
1028 Branch(SmiEqual(match_from, SmiConstant(-1)), &if_failure, &if_success);
1029
1030 BIND(&if_success);
1031 {
1032 CSA_ASSERT(this, TaggedIsPositiveSmi(match_from));
1033 CSA_ASSERT(this, UintPtrLessThan(SmiUntag(match_from),
1034 LoadStringLengthAsWord(subject_string)));
1035
1036 const int kNumRegisters = 2;
1037 STATIC_ASSERT(RegExpMatchInfo::kInitialCaptureIndices >= kNumRegisters);
1038
1039 TNode<Smi> const match_to =
1040 SmiAdd(match_from, LoadStringLengthAsSmi(needle_string));
1041
1042 StoreFixedArrayElement(match_info, RegExpMatchInfo::kNumberOfCapturesIndex,
1043 SmiConstant(kNumRegisters), SKIP_WRITE_BARRIER);
1044 StoreFixedArrayElement(match_info, RegExpMatchInfo::kLastSubjectIndex,
1045 subject_string);
1046 StoreFixedArrayElement(match_info, RegExpMatchInfo::kLastInputIndex,
1047 subject_string);
1048 StoreFixedArrayElement(match_info, RegExpMatchInfo::kFirstCaptureIndex,
1049 match_from, SKIP_WRITE_BARRIER);
1050 StoreFixedArrayElement(match_info, RegExpMatchInfo::kFirstCaptureIndex + 1,
1051 match_to, SKIP_WRITE_BARRIER);
1052
1053 Return(match_info);
1054 }
1055
1056 BIND(&if_failure);
1057 Return(NullConstant());
1058 }
1059
TF_BUILTIN(RegExpExecInternal,RegExpBuiltinsAssembler)1060 TF_BUILTIN(RegExpExecInternal, RegExpBuiltinsAssembler) {
1061 TNode<JSRegExp> regexp = CAST(Parameter(Descriptor::kRegExp));
1062 TNode<String> string = CAST(Parameter(Descriptor::kString));
1063 TNode<Number> last_index = CAST(Parameter(Descriptor::kLastIndex));
1064 TNode<RegExpMatchInfo> match_info = CAST(Parameter(Descriptor::kMatchInfo));
1065 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1066
1067 CSA_ASSERT(this, IsNumberNormalized(last_index));
1068 CSA_ASSERT(this, IsNumberPositive(last_index));
1069
1070 Return(RegExpExecInternal(context, regexp, string, last_index, match_info));
1071 }
1072
1073 // ES#sec-regexp.prototype.exec
1074 // RegExp.prototype.exec ( string )
TF_BUILTIN(RegExpPrototypeExec,RegExpBuiltinsAssembler)1075 TF_BUILTIN(RegExpPrototypeExec, RegExpBuiltinsAssembler) {
1076 TNode<Object> maybe_receiver = CAST(Parameter(Descriptor::kReceiver));
1077 TNode<Object> maybe_string = CAST(Parameter(Descriptor::kString));
1078 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1079
1080 // Ensure {maybe_receiver} is a JSRegExp.
1081 ThrowIfNotInstanceType(context, maybe_receiver, JS_REGEXP_TYPE,
1082 "RegExp.prototype.exec");
1083 TNode<JSRegExp> receiver = CAST(maybe_receiver);
1084
1085 // Convert {maybe_string} to a String.
1086 TNode<String> string = ToString_Inline(context, maybe_string);
1087
1088 Label if_isfastpath(this), if_isslowpath(this);
1089 Branch(IsFastRegExpNoPrototype(context, receiver), &if_isfastpath,
1090 &if_isslowpath);
1091
1092 BIND(&if_isfastpath);
1093 Return(RegExpPrototypeExecBody(context, receiver, string, true));
1094
1095 BIND(&if_isslowpath);
1096 Return(CallBuiltin(Builtins::kRegExpPrototypeExecSlow, context, receiver,
1097 string));
1098 }
1099
FlagsGetter(Node * const context,Node * const regexp,bool is_fastpath)1100 Node* RegExpBuiltinsAssembler::FlagsGetter(Node* const context,
1101 Node* const regexp,
1102 bool is_fastpath) {
1103 Isolate* isolate = this->isolate();
1104
1105 TNode<IntPtrT> const int_one = IntPtrConstant(1);
1106 TVARIABLE(Smi, var_length, SmiZero());
1107 TVARIABLE(IntPtrT, var_flags);
1108
1109 // First, count the number of characters we will need and check which flags
1110 // are set.
1111
1112 if (is_fastpath) {
1113 // Refer to JSRegExp's flag property on the fast-path.
1114 CSA_ASSERT(this, IsJSRegExp(regexp));
1115 Node* const flags_smi = LoadObjectField(regexp, JSRegExp::kFlagsOffset);
1116 var_flags = SmiUntag(flags_smi);
1117
1118 #define CASE_FOR_FLAG(FLAG) \
1119 do { \
1120 Label next(this); \
1121 GotoIfNot(IsSetWord(var_flags.value(), FLAG), &next); \
1122 var_length = SmiAdd(var_length.value(), SmiConstant(1)); \
1123 Goto(&next); \
1124 BIND(&next); \
1125 } while (false)
1126
1127 CASE_FOR_FLAG(JSRegExp::kGlobal);
1128 CASE_FOR_FLAG(JSRegExp::kIgnoreCase);
1129 CASE_FOR_FLAG(JSRegExp::kMultiline);
1130 CASE_FOR_FLAG(JSRegExp::kDotAll);
1131 CASE_FOR_FLAG(JSRegExp::kUnicode);
1132 CASE_FOR_FLAG(JSRegExp::kSticky);
1133 #undef CASE_FOR_FLAG
1134 } else {
1135 DCHECK(!is_fastpath);
1136
1137 // Fall back to GetProperty stub on the slow-path.
1138 var_flags = IntPtrZero();
1139
1140 #define CASE_FOR_FLAG(NAME, FLAG) \
1141 do { \
1142 Label next(this); \
1143 Node* const flag = GetProperty( \
1144 context, regexp, isolate->factory()->InternalizeUtf8String(NAME)); \
1145 Label if_isflagset(this); \
1146 BranchIfToBooleanIsTrue(flag, &if_isflagset, &next); \
1147 BIND(&if_isflagset); \
1148 var_length = SmiAdd(var_length.value(), SmiConstant(1)); \
1149 var_flags = Signed(WordOr(var_flags.value(), IntPtrConstant(FLAG))); \
1150 Goto(&next); \
1151 BIND(&next); \
1152 } while (false)
1153
1154 CASE_FOR_FLAG("global", JSRegExp::kGlobal);
1155 CASE_FOR_FLAG("ignoreCase", JSRegExp::kIgnoreCase);
1156 CASE_FOR_FLAG("multiline", JSRegExp::kMultiline);
1157 CASE_FOR_FLAG("dotAll", JSRegExp::kDotAll);
1158 CASE_FOR_FLAG("unicode", JSRegExp::kUnicode);
1159 CASE_FOR_FLAG("sticky", JSRegExp::kSticky);
1160 #undef CASE_FOR_FLAG
1161 }
1162
1163 // Allocate a string of the required length and fill it with the corresponding
1164 // char for each set flag.
1165
1166 {
1167 Node* const result = AllocateSeqOneByteString(context, var_length.value());
1168
1169 VARIABLE(var_offset, MachineType::PointerRepresentation(),
1170 IntPtrConstant(SeqOneByteString::kHeaderSize - kHeapObjectTag));
1171
1172 #define CASE_FOR_FLAG(FLAG, CHAR) \
1173 do { \
1174 Label next(this); \
1175 GotoIfNot(IsSetWord(var_flags.value(), FLAG), &next); \
1176 Node* const value = Int32Constant(CHAR); \
1177 StoreNoWriteBarrier(MachineRepresentation::kWord8, result, \
1178 var_offset.value(), value); \
1179 var_offset.Bind(IntPtrAdd(var_offset.value(), int_one)); \
1180 Goto(&next); \
1181 BIND(&next); \
1182 } while (false)
1183
1184 CASE_FOR_FLAG(JSRegExp::kGlobal, 'g');
1185 CASE_FOR_FLAG(JSRegExp::kIgnoreCase, 'i');
1186 CASE_FOR_FLAG(JSRegExp::kMultiline, 'm');
1187 CASE_FOR_FLAG(JSRegExp::kDotAll, 's');
1188 CASE_FOR_FLAG(JSRegExp::kUnicode, 'u');
1189 CASE_FOR_FLAG(JSRegExp::kSticky, 'y');
1190 #undef CASE_FOR_FLAG
1191
1192 return result;
1193 }
1194 }
1195
1196 // ES#sec-isregexp IsRegExp ( argument )
IsRegExp(Node * const context,Node * const maybe_receiver)1197 Node* RegExpBuiltinsAssembler::IsRegExp(Node* const context,
1198 Node* const maybe_receiver) {
1199 Label out(this), if_isregexp(this);
1200
1201 VARIABLE(var_result, MachineRepresentation::kWord32, Int32Constant(0));
1202
1203 GotoIf(TaggedIsSmi(maybe_receiver), &out);
1204 GotoIfNot(IsJSReceiver(maybe_receiver), &out);
1205
1206 Node* const receiver = maybe_receiver;
1207
1208 // Check @@match.
1209 {
1210 Node* const value =
1211 GetProperty(context, receiver, isolate()->factory()->match_symbol());
1212
1213 Label match_isundefined(this), match_isnotundefined(this);
1214 Branch(IsUndefined(value), &match_isundefined, &match_isnotundefined);
1215
1216 BIND(&match_isundefined);
1217 Branch(IsJSRegExp(receiver), &if_isregexp, &out);
1218
1219 BIND(&match_isnotundefined);
1220 BranchIfToBooleanIsTrue(value, &if_isregexp, &out);
1221 }
1222
1223 BIND(&if_isregexp);
1224 var_result.Bind(Int32Constant(1));
1225 Goto(&out);
1226
1227 BIND(&out);
1228 return var_result.value();
1229 }
1230
1231 // ES#sec-regexpinitialize
1232 // Runtime Semantics: RegExpInitialize ( obj, pattern, flags )
RegExpInitialize(Node * const context,Node * const regexp,Node * const maybe_pattern,Node * const maybe_flags)1233 Node* RegExpBuiltinsAssembler::RegExpInitialize(Node* const context,
1234 Node* const regexp,
1235 Node* const maybe_pattern,
1236 Node* const maybe_flags) {
1237 CSA_ASSERT(this, IsJSRegExp(regexp));
1238
1239 // Normalize pattern.
1240 TNode<Object> const pattern = Select<Object>(
1241 IsUndefined(maybe_pattern), [=] { return EmptyStringConstant(); },
1242 [=] { return ToString_Inline(context, maybe_pattern); });
1243
1244 // Normalize flags.
1245 TNode<Object> const flags = Select<Object>(
1246 IsUndefined(maybe_flags), [=] { return EmptyStringConstant(); },
1247 [=] { return ToString_Inline(context, maybe_flags); });
1248
1249 // Initialize.
1250
1251 return CallRuntime(Runtime::kRegExpInitializeAndCompile, context, regexp,
1252 pattern, flags);
1253 }
1254
1255 // ES #sec-get-regexp.prototype.flags
TF_BUILTIN(RegExpPrototypeFlagsGetter,RegExpBuiltinsAssembler)1256 TF_BUILTIN(RegExpPrototypeFlagsGetter, RegExpBuiltinsAssembler) {
1257 TNode<Object> maybe_receiver = CAST(Parameter(Descriptor::kReceiver));
1258 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1259
1260 TNode<Map> map = CAST(ThrowIfNotJSReceiver(context, maybe_receiver,
1261 MessageTemplate::kRegExpNonObject,
1262 "RegExp.prototype.flags"));
1263 TNode<JSReceiver> receiver = CAST(maybe_receiver);
1264
1265 Label if_isfastpath(this), if_isslowpath(this, Label::kDeferred);
1266 BranchIfFastRegExp(context, receiver, map, &if_isfastpath, &if_isslowpath);
1267
1268 BIND(&if_isfastpath);
1269 Return(FlagsGetter(context, receiver, true));
1270
1271 BIND(&if_isslowpath);
1272 Return(FlagsGetter(context, receiver, false));
1273 }
1274
1275 // ES#sec-regexp-pattern-flags
1276 // RegExp ( pattern, flags )
TF_BUILTIN(RegExpConstructor,RegExpBuiltinsAssembler)1277 TF_BUILTIN(RegExpConstructor, RegExpBuiltinsAssembler) {
1278 TNode<Object> pattern = CAST(Parameter(Descriptor::kPattern));
1279 TNode<Object> flags = CAST(Parameter(Descriptor::kFlags));
1280 TNode<Object> new_target = CAST(Parameter(Descriptor::kJSNewTarget));
1281 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1282
1283 Isolate* isolate = this->isolate();
1284
1285 VARIABLE(var_flags, MachineRepresentation::kTagged, flags);
1286 VARIABLE(var_pattern, MachineRepresentation::kTagged, pattern);
1287 VARIABLE(var_new_target, MachineRepresentation::kTagged, new_target);
1288
1289 Node* const native_context = LoadNativeContext(context);
1290 Node* const regexp_function =
1291 LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX);
1292
1293 Node* const pattern_is_regexp = IsRegExp(context, pattern);
1294
1295 {
1296 Label next(this);
1297
1298 GotoIfNot(IsUndefined(new_target), &next);
1299 var_new_target.Bind(regexp_function);
1300
1301 GotoIfNot(pattern_is_regexp, &next);
1302 GotoIfNot(IsUndefined(flags), &next);
1303
1304 Node* const value =
1305 GetProperty(context, pattern, isolate->factory()->constructor_string());
1306
1307 GotoIfNot(WordEqual(value, regexp_function), &next);
1308 Return(pattern);
1309
1310 BIND(&next);
1311 }
1312
1313 {
1314 Label next(this), if_patternisfastregexp(this),
1315 if_patternisslowregexp(this);
1316 GotoIf(TaggedIsSmi(pattern), &next);
1317
1318 GotoIf(IsJSRegExp(CAST(pattern)), &if_patternisfastregexp);
1319
1320 Branch(pattern_is_regexp, &if_patternisslowregexp, &next);
1321
1322 BIND(&if_patternisfastregexp);
1323 {
1324 Node* const source =
1325 LoadObjectField(CAST(pattern), JSRegExp::kSourceOffset);
1326 var_pattern.Bind(source);
1327
1328 {
1329 Label inner_next(this);
1330 GotoIfNot(IsUndefined(flags), &inner_next);
1331
1332 Node* const value = FlagsGetter(context, pattern, true);
1333 var_flags.Bind(value);
1334 Goto(&inner_next);
1335
1336 BIND(&inner_next);
1337 }
1338
1339 Goto(&next);
1340 }
1341
1342 BIND(&if_patternisslowregexp);
1343 {
1344 {
1345 Node* const value =
1346 GetProperty(context, pattern, isolate->factory()->source_string());
1347 var_pattern.Bind(value);
1348 }
1349
1350 {
1351 Label inner_next(this);
1352 GotoIfNot(IsUndefined(flags), &inner_next);
1353
1354 Node* const value =
1355 GetProperty(context, pattern, isolate->factory()->flags_string());
1356 var_flags.Bind(value);
1357 Goto(&inner_next);
1358
1359 BIND(&inner_next);
1360 }
1361
1362 Goto(&next);
1363 }
1364
1365 BIND(&next);
1366 }
1367
1368 // Allocate.
1369
1370 VARIABLE(var_regexp, MachineRepresentation::kTagged);
1371 {
1372 Label allocate_jsregexp(this), allocate_generic(this, Label::kDeferred),
1373 next(this);
1374 Branch(WordEqual(var_new_target.value(), regexp_function),
1375 &allocate_jsregexp, &allocate_generic);
1376
1377 BIND(&allocate_jsregexp);
1378 {
1379 Node* const initial_map = LoadObjectField(
1380 regexp_function, JSFunction::kPrototypeOrInitialMapOffset);
1381 Node* const regexp = AllocateJSObjectFromMap(initial_map);
1382 var_regexp.Bind(regexp);
1383 Goto(&next);
1384 }
1385
1386 BIND(&allocate_generic);
1387 {
1388 ConstructorBuiltinsAssembler constructor_assembler(this->state());
1389 Node* const regexp = constructor_assembler.EmitFastNewObject(
1390 context, regexp_function, var_new_target.value());
1391 var_regexp.Bind(regexp);
1392 Goto(&next);
1393 }
1394
1395 BIND(&next);
1396 }
1397
1398 Node* const result = RegExpInitialize(context, var_regexp.value(),
1399 var_pattern.value(), var_flags.value());
1400 Return(result);
1401 }
1402
1403 // ES#sec-regexp.prototype.compile
1404 // RegExp.prototype.compile ( pattern, flags )
TF_BUILTIN(RegExpPrototypeCompile,RegExpBuiltinsAssembler)1405 TF_BUILTIN(RegExpPrototypeCompile, RegExpBuiltinsAssembler) {
1406 TNode<Object> maybe_receiver = CAST(Parameter(Descriptor::kReceiver));
1407 TNode<Object> maybe_pattern = CAST(Parameter(Descriptor::kPattern));
1408 TNode<Object> maybe_flags = CAST(Parameter(Descriptor::kFlags));
1409 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1410
1411 ThrowIfNotInstanceType(context, maybe_receiver, JS_REGEXP_TYPE,
1412 "RegExp.prototype.compile");
1413 Node* const receiver = maybe_receiver;
1414
1415 VARIABLE(var_flags, MachineRepresentation::kTagged, maybe_flags);
1416 VARIABLE(var_pattern, MachineRepresentation::kTagged, maybe_pattern);
1417
1418 // Handle a JSRegExp pattern.
1419 {
1420 Label next(this);
1421
1422 GotoIf(TaggedIsSmi(maybe_pattern), &next);
1423 GotoIfNot(IsJSRegExp(CAST(maybe_pattern)), &next);
1424
1425 Node* const pattern = maybe_pattern;
1426
1427 // {maybe_flags} must be undefined in this case, otherwise throw.
1428 {
1429 Label next(this);
1430 GotoIf(IsUndefined(maybe_flags), &next);
1431
1432 ThrowTypeError(context, MessageTemplate::kRegExpFlags);
1433
1434 BIND(&next);
1435 }
1436
1437 Node* const new_flags = FlagsGetter(context, pattern, true);
1438 Node* const new_pattern = LoadObjectField(pattern, JSRegExp::kSourceOffset);
1439
1440 var_flags.Bind(new_flags);
1441 var_pattern.Bind(new_pattern);
1442
1443 Goto(&next);
1444 BIND(&next);
1445 }
1446
1447 Node* const result = RegExpInitialize(context, receiver, var_pattern.value(),
1448 var_flags.value());
1449 Return(result);
1450 }
1451
1452 // ES6 21.2.5.10.
1453 // ES #sec-get-regexp.prototype.source
TF_BUILTIN(RegExpPrototypeSourceGetter,RegExpBuiltinsAssembler)1454 TF_BUILTIN(RegExpPrototypeSourceGetter, RegExpBuiltinsAssembler) {
1455 TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
1456 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1457
1458 // Check whether we have an unmodified regexp instance.
1459 Label if_isjsregexp(this), if_isnotjsregexp(this, Label::kDeferred);
1460
1461 GotoIf(TaggedIsSmi(receiver), &if_isnotjsregexp);
1462 Branch(IsJSRegExp(CAST(receiver)), &if_isjsregexp, &if_isnotjsregexp);
1463
1464 BIND(&if_isjsregexp);
1465 Return(LoadObjectField(CAST(receiver), JSRegExp::kSourceOffset));
1466
1467 BIND(&if_isnotjsregexp);
1468 {
1469 Isolate* isolate = this->isolate();
1470 Node* const native_context = LoadNativeContext(context);
1471 Node* const regexp_fun =
1472 LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX);
1473 Node* const initial_map =
1474 LoadObjectField(regexp_fun, JSFunction::kPrototypeOrInitialMapOffset);
1475 Node* const initial_prototype = LoadMapPrototype(initial_map);
1476
1477 Label if_isprototype(this), if_isnotprototype(this);
1478 Branch(WordEqual(receiver, initial_prototype), &if_isprototype,
1479 &if_isnotprototype);
1480
1481 BIND(&if_isprototype);
1482 {
1483 const int counter = v8::Isolate::kRegExpPrototypeSourceGetter;
1484 Node* const counter_smi = SmiConstant(counter);
1485 CallRuntime(Runtime::kIncrementUseCounter, context, counter_smi);
1486
1487 Node* const result =
1488 HeapConstant(isolate->factory()->NewStringFromAsciiChecked("(?:)"));
1489 Return(result);
1490 }
1491
1492 BIND(&if_isnotprototype);
1493 {
1494 ThrowTypeError(context, MessageTemplate::kRegExpNonRegExp,
1495 "RegExp.prototype.source");
1496 }
1497 }
1498 }
1499
1500 // Fast-path implementation for flag checks on an unmodified JSRegExp instance.
FastFlagGetter(Node * const regexp,JSRegExp::Flag flag)1501 Node* RegExpBuiltinsAssembler::FastFlagGetter(Node* const regexp,
1502 JSRegExp::Flag flag) {
1503 TNode<Smi> const flags =
1504 CAST(LoadObjectField(regexp, JSRegExp::kFlagsOffset));
1505 TNode<Smi> const mask = SmiConstant(flag);
1506 return SmiToInt32(SmiAnd(flags, mask));
1507 }
1508
1509 // Load through the GetProperty stub.
SlowFlagGetter(Node * const context,Node * const regexp,JSRegExp::Flag flag)1510 Node* RegExpBuiltinsAssembler::SlowFlagGetter(Node* const context,
1511 Node* const regexp,
1512 JSRegExp::Flag flag) {
1513 Factory* factory = isolate()->factory();
1514
1515 Label out(this);
1516 VARIABLE(var_result, MachineRepresentation::kWord32);
1517
1518 Handle<String> name;
1519 switch (flag) {
1520 case JSRegExp::kGlobal:
1521 name = factory->global_string();
1522 break;
1523 case JSRegExp::kIgnoreCase:
1524 name = factory->ignoreCase_string();
1525 break;
1526 case JSRegExp::kMultiline:
1527 name = factory->multiline_string();
1528 break;
1529 case JSRegExp::kDotAll:
1530 UNREACHABLE(); // Never called for dotAll.
1531 break;
1532 case JSRegExp::kSticky:
1533 name = factory->sticky_string();
1534 break;
1535 case JSRegExp::kUnicode:
1536 name = factory->unicode_string();
1537 break;
1538 default:
1539 UNREACHABLE();
1540 }
1541
1542 Node* const value = GetProperty(context, regexp, name);
1543
1544 Label if_true(this), if_false(this);
1545 BranchIfToBooleanIsTrue(value, &if_true, &if_false);
1546
1547 BIND(&if_true);
1548 {
1549 var_result.Bind(Int32Constant(1));
1550 Goto(&out);
1551 }
1552
1553 BIND(&if_false);
1554 {
1555 var_result.Bind(Int32Constant(0));
1556 Goto(&out);
1557 }
1558
1559 BIND(&out);
1560 return var_result.value();
1561 }
1562
FlagGetter(Node * const context,Node * const regexp,JSRegExp::Flag flag,bool is_fastpath)1563 Node* RegExpBuiltinsAssembler::FlagGetter(Node* const context,
1564 Node* const regexp,
1565 JSRegExp::Flag flag,
1566 bool is_fastpath) {
1567 return is_fastpath ? FastFlagGetter(regexp, flag)
1568 : SlowFlagGetter(context, regexp, flag);
1569 }
1570
FlagGetter(Node * context,Node * receiver,JSRegExp::Flag flag,int counter,const char * method_name)1571 void RegExpBuiltinsAssembler::FlagGetter(Node* context, Node* receiver,
1572 JSRegExp::Flag flag, int counter,
1573 const char* method_name) {
1574 // Check whether we have an unmodified regexp instance.
1575 Label if_isunmodifiedjsregexp(this),
1576 if_isnotunmodifiedjsregexp(this, Label::kDeferred);
1577
1578 GotoIf(TaggedIsSmi(receiver), &if_isnotunmodifiedjsregexp);
1579 Branch(IsJSRegExp(receiver), &if_isunmodifiedjsregexp,
1580 &if_isnotunmodifiedjsregexp);
1581
1582 BIND(&if_isunmodifiedjsregexp);
1583 {
1584 // Refer to JSRegExp's flag property on the fast-path.
1585 Node* const is_flag_set = FastFlagGetter(receiver, flag);
1586 Return(SelectBooleanConstant(is_flag_set));
1587 }
1588
1589 BIND(&if_isnotunmodifiedjsregexp);
1590 {
1591 Node* const native_context = LoadNativeContext(context);
1592 Node* const regexp_fun =
1593 LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX);
1594 Node* const initial_map =
1595 LoadObjectField(regexp_fun, JSFunction::kPrototypeOrInitialMapOffset);
1596 Node* const initial_prototype = LoadMapPrototype(initial_map);
1597
1598 Label if_isprototype(this), if_isnotprototype(this);
1599 Branch(WordEqual(receiver, initial_prototype), &if_isprototype,
1600 &if_isnotprototype);
1601
1602 BIND(&if_isprototype);
1603 {
1604 if (counter != -1) {
1605 Node* const counter_smi = SmiConstant(counter);
1606 CallRuntime(Runtime::kIncrementUseCounter, context, counter_smi);
1607 }
1608 Return(UndefinedConstant());
1609 }
1610
1611 BIND(&if_isnotprototype);
1612 { ThrowTypeError(context, MessageTemplate::kRegExpNonRegExp, method_name); }
1613 }
1614 }
1615
1616 // ES6 21.2.5.4.
1617 // ES #sec-get-regexp.prototype.global
TF_BUILTIN(RegExpPrototypeGlobalGetter,RegExpBuiltinsAssembler)1618 TF_BUILTIN(RegExpPrototypeGlobalGetter, RegExpBuiltinsAssembler) {
1619 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1620 TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
1621 FlagGetter(context, receiver, JSRegExp::kGlobal,
1622 v8::Isolate::kRegExpPrototypeOldFlagGetter,
1623 "RegExp.prototype.global");
1624 }
1625
1626 // ES6 21.2.5.5.
1627 // ES #sec-get-regexp.prototype.ignorecase
TF_BUILTIN(RegExpPrototypeIgnoreCaseGetter,RegExpBuiltinsAssembler)1628 TF_BUILTIN(RegExpPrototypeIgnoreCaseGetter, RegExpBuiltinsAssembler) {
1629 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1630 TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
1631 FlagGetter(context, receiver, JSRegExp::kIgnoreCase,
1632 v8::Isolate::kRegExpPrototypeOldFlagGetter,
1633 "RegExp.prototype.ignoreCase");
1634 }
1635
1636 // ES6 21.2.5.7.
1637 // ES #sec-get-regexp.prototype.multiline
TF_BUILTIN(RegExpPrototypeMultilineGetter,RegExpBuiltinsAssembler)1638 TF_BUILTIN(RegExpPrototypeMultilineGetter, RegExpBuiltinsAssembler) {
1639 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1640 TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
1641 FlagGetter(context, receiver, JSRegExp::kMultiline,
1642 v8::Isolate::kRegExpPrototypeOldFlagGetter,
1643 "RegExp.prototype.multiline");
1644 }
1645
1646 // ES #sec-get-regexp.prototype.dotAll
TF_BUILTIN(RegExpPrototypeDotAllGetter,RegExpBuiltinsAssembler)1647 TF_BUILTIN(RegExpPrototypeDotAllGetter, RegExpBuiltinsAssembler) {
1648 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1649 TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
1650 static const int kNoCounter = -1;
1651 FlagGetter(context, receiver, JSRegExp::kDotAll, kNoCounter,
1652 "RegExp.prototype.dotAll");
1653 }
1654
1655 // ES6 21.2.5.12.
1656 // ES #sec-get-regexp.prototype.sticky
TF_BUILTIN(RegExpPrototypeStickyGetter,RegExpBuiltinsAssembler)1657 TF_BUILTIN(RegExpPrototypeStickyGetter, RegExpBuiltinsAssembler) {
1658 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1659 TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
1660 FlagGetter(context, receiver, JSRegExp::kSticky,
1661 v8::Isolate::kRegExpPrototypeStickyGetter,
1662 "RegExp.prototype.sticky");
1663 }
1664
1665 // ES6 21.2.5.15.
1666 // ES #sec-get-regexp.prototype.unicode
TF_BUILTIN(RegExpPrototypeUnicodeGetter,RegExpBuiltinsAssembler)1667 TF_BUILTIN(RegExpPrototypeUnicodeGetter, RegExpBuiltinsAssembler) {
1668 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1669 TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
1670 FlagGetter(context, receiver, JSRegExp::kUnicode,
1671 v8::Isolate::kRegExpPrototypeUnicodeGetter,
1672 "RegExp.prototype.unicode");
1673 }
1674
1675 // ES#sec-regexpexec Runtime Semantics: RegExpExec ( R, S )
RegExpExec(Node * context,Node * regexp,Node * string)1676 Node* RegExpBuiltinsAssembler::RegExpExec(Node* context, Node* regexp,
1677 Node* string) {
1678 VARIABLE(var_result, MachineRepresentation::kTagged);
1679 Label out(this);
1680
1681 // Take the slow path of fetching the exec property, calling it, and
1682 // verifying its return value.
1683
1684 // Get the exec property.
1685 Node* const exec =
1686 GetProperty(context, regexp, isolate()->factory()->exec_string());
1687
1688 // Is {exec} callable?
1689 Label if_iscallable(this), if_isnotcallable(this);
1690
1691 GotoIf(TaggedIsSmi(exec), &if_isnotcallable);
1692
1693 Node* const exec_map = LoadMap(exec);
1694 Branch(IsCallableMap(exec_map), &if_iscallable, &if_isnotcallable);
1695
1696 BIND(&if_iscallable);
1697 {
1698 Callable call_callable = CodeFactory::Call(isolate());
1699 Node* const result = CallJS(call_callable, context, exec, regexp, string);
1700
1701 var_result.Bind(result);
1702 GotoIf(IsNull(result), &out);
1703
1704 ThrowIfNotJSReceiver(context, result,
1705 MessageTemplate::kInvalidRegExpExecResult, "");
1706
1707 Goto(&out);
1708 }
1709
1710 BIND(&if_isnotcallable);
1711 {
1712 ThrowIfNotInstanceType(context, regexp, JS_REGEXP_TYPE,
1713 "RegExp.prototype.exec");
1714
1715 Node* const result = CallBuiltin(Builtins::kRegExpPrototypeExecSlow,
1716 context, regexp, string);
1717 var_result.Bind(result);
1718 Goto(&out);
1719 }
1720
1721 BIND(&out);
1722 return var_result.value();
1723 }
1724
1725 // ES#sec-regexp.prototype.test
1726 // RegExp.prototype.test ( S )
TF_BUILTIN(RegExpPrototypeTest,RegExpBuiltinsAssembler)1727 TF_BUILTIN(RegExpPrototypeTest, RegExpBuiltinsAssembler) {
1728 TNode<Object> maybe_receiver = CAST(Parameter(Descriptor::kReceiver));
1729 TNode<Object> maybe_string = CAST(Parameter(Descriptor::kString));
1730 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1731
1732 // Ensure {maybe_receiver} is a JSReceiver.
1733 ThrowIfNotJSReceiver(context, maybe_receiver,
1734 MessageTemplate::kIncompatibleMethodReceiver,
1735 "RegExp.prototype.test");
1736 TNode<JSReceiver> receiver = CAST(maybe_receiver);
1737
1738 // Convert {maybe_string} to a String.
1739 TNode<String> string = ToString_Inline(context, maybe_string);
1740
1741 Label fast_path(this), slow_path(this);
1742 BranchIfFastRegExp(context, receiver, &fast_path, &slow_path);
1743
1744 BIND(&fast_path);
1745 {
1746 Label if_didnotmatch(this);
1747 RegExpPrototypeExecBodyWithoutResult(context, receiver, string,
1748 &if_didnotmatch, true);
1749 Return(TrueConstant());
1750
1751 BIND(&if_didnotmatch);
1752 Return(FalseConstant());
1753 }
1754
1755 BIND(&slow_path);
1756 {
1757 // Call exec.
1758 TNode<HeapObject> match_indices =
1759 CAST(RegExpExec(context, receiver, string));
1760
1761 // Return true iff exec matched successfully.
1762 Return(SelectBooleanConstant(IsNotNull(match_indices)));
1763 }
1764 }
1765
TF_BUILTIN(RegExpPrototypeTestFast,RegExpBuiltinsAssembler)1766 TF_BUILTIN(RegExpPrototypeTestFast, RegExpBuiltinsAssembler) {
1767 TNode<JSRegExp> regexp = CAST(Parameter(Descriptor::kReceiver));
1768 TNode<String> string = CAST(Parameter(Descriptor::kString));
1769 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1770
1771 Label if_didnotmatch(this);
1772 CSA_ASSERT(this, IsFastRegExpWithOriginalExec(context, regexp));
1773 RegExpPrototypeExecBodyWithoutResult(context, regexp, string, &if_didnotmatch,
1774 true);
1775 Return(TrueConstant());
1776
1777 BIND(&if_didnotmatch);
1778 Return(FalseConstant());
1779 }
1780
AdvanceStringIndex(Node * const string,Node * const index,Node * const is_unicode,bool is_fastpath)1781 Node* RegExpBuiltinsAssembler::AdvanceStringIndex(Node* const string,
1782 Node* const index,
1783 Node* const is_unicode,
1784 bool is_fastpath) {
1785 CSA_ASSERT(this, IsString(string));
1786 CSA_ASSERT(this, IsNumberNormalized(index));
1787 if (is_fastpath) CSA_ASSERT(this, TaggedIsPositiveSmi(index));
1788
1789 // Default to last_index + 1.
1790 Node* const index_plus_one = NumberInc(index);
1791 VARIABLE(var_result, MachineRepresentation::kTagged, index_plus_one);
1792
1793 // Advancing the index has some subtle issues involving the distinction
1794 // between Smis and HeapNumbers. There's three cases:
1795 // * {index} is a Smi, {index_plus_one} is a Smi. The standard case.
1796 // * {index} is a Smi, {index_plus_one} overflows into a HeapNumber.
1797 // In this case we can return the result early, because
1798 // {index_plus_one} > {string}.length.
1799 // * {index} is a HeapNumber, {index_plus_one} is a HeapNumber. This can only
1800 // occur when {index} is outside the Smi range since we normalize
1801 // explicitly. Again we can return early.
1802 if (is_fastpath) {
1803 // Must be in Smi range on the fast path. We control the value of {index}
1804 // on all call-sites and can never exceed the length of the string.
1805 STATIC_ASSERT(String::kMaxLength + 2 < Smi::kMaxValue);
1806 CSA_ASSERT(this, TaggedIsPositiveSmi(index_plus_one));
1807 }
1808
1809 Label if_isunicode(this), out(this);
1810 GotoIfNot(is_unicode, &out);
1811
1812 // Keep this unconditional (even on the fast path) just to be safe.
1813 Branch(TaggedIsPositiveSmi(index_plus_one), &if_isunicode, &out);
1814
1815 BIND(&if_isunicode);
1816 {
1817 TNode<IntPtrT> const string_length = LoadStringLengthAsWord(string);
1818 TNode<IntPtrT> untagged_plus_one = SmiUntag(index_plus_one);
1819 GotoIfNot(IntPtrLessThan(untagged_plus_one, string_length), &out);
1820
1821 Node* const lead = StringCharCodeAt(string, SmiUntag(index));
1822 GotoIfNot(Word32Equal(Word32And(lead, Int32Constant(0xFC00)),
1823 Int32Constant(0xD800)),
1824 &out);
1825
1826 Node* const trail = StringCharCodeAt(string, untagged_plus_one);
1827 GotoIfNot(Word32Equal(Word32And(trail, Int32Constant(0xFC00)),
1828 Int32Constant(0xDC00)),
1829 &out);
1830
1831 // At a surrogate pair, return index + 2.
1832 Node* const index_plus_two = NumberInc(index_plus_one);
1833 var_result.Bind(index_plus_two);
1834
1835 Goto(&out);
1836 }
1837
1838 BIND(&out);
1839 return var_result.value();
1840 }
1841
RegExpPrototypeMatchBody(Node * const context,Node * const regexp,TNode<String> string,const bool is_fastpath)1842 void RegExpBuiltinsAssembler::RegExpPrototypeMatchBody(Node* const context,
1843 Node* const regexp,
1844 TNode<String> string,
1845 const bool is_fastpath) {
1846 if (is_fastpath) CSA_ASSERT(this, IsFastRegExp(context, regexp));
1847
1848 Node* const is_global =
1849 FlagGetter(context, regexp, JSRegExp::kGlobal, is_fastpath);
1850
1851 Label if_isglobal(this), if_isnotglobal(this);
1852 Branch(is_global, &if_isglobal, &if_isnotglobal);
1853
1854 BIND(&if_isnotglobal);
1855 {
1856 Node* const result =
1857 is_fastpath
1858 ? RegExpPrototypeExecBody(CAST(context), CAST(regexp), string, true)
1859 : RegExpExec(context, regexp, string);
1860 Return(result);
1861 }
1862
1863 BIND(&if_isglobal);
1864 {
1865 Node* const is_unicode =
1866 FlagGetter(context, regexp, JSRegExp::kUnicode, is_fastpath);
1867
1868 StoreLastIndex(context, regexp, SmiZero(), is_fastpath);
1869
1870 // Allocate an array to store the resulting match strings.
1871
1872 GrowableFixedArray array(state());
1873
1874 // Loop preparations. Within the loop, collect results from RegExpExec
1875 // and store match strings in the array.
1876
1877 Variable* vars[] = {array.var_array(), array.var_length(),
1878 array.var_capacity()};
1879 Label loop(this, 3, vars), out(this);
1880 Goto(&loop);
1881
1882 BIND(&loop);
1883 {
1884 VARIABLE(var_match, MachineRepresentation::kTagged);
1885
1886 Label if_didmatch(this), if_didnotmatch(this);
1887 if (is_fastpath) {
1888 // On the fast path, grab the matching string from the raw match index
1889 // array.
1890 TNode<RegExpMatchInfo> match_indices =
1891 RegExpPrototypeExecBodyWithoutResult(CAST(context), CAST(regexp),
1892 string, &if_didnotmatch, true);
1893
1894 Node* const match_from = LoadFixedArrayElement(
1895 match_indices, RegExpMatchInfo::kFirstCaptureIndex);
1896 Node* const match_to = LoadFixedArrayElement(
1897 match_indices, RegExpMatchInfo::kFirstCaptureIndex + 1);
1898
1899 var_match.Bind(CallBuiltin(Builtins::kSubString, context, string,
1900 match_from, match_to));
1901 Goto(&if_didmatch);
1902 } else {
1903 DCHECK(!is_fastpath);
1904 Node* const result = RegExpExec(context, regexp, string);
1905
1906 Label load_match(this);
1907 Branch(IsNull(result), &if_didnotmatch, &load_match);
1908
1909 BIND(&load_match);
1910 var_match.Bind(
1911 ToString_Inline(context, GetProperty(context, result, SmiZero())));
1912 Goto(&if_didmatch);
1913 }
1914
1915 BIND(&if_didnotmatch);
1916 {
1917 // Return null if there were no matches, otherwise just exit the loop.
1918 GotoIfNot(IntPtrEqual(array.length(), IntPtrZero()), &out);
1919 Return(NullConstant());
1920 }
1921
1922 BIND(&if_didmatch);
1923 {
1924 Node* match = var_match.value();
1925
1926 // Store the match, growing the fixed array if needed.
1927
1928 array.Push(CAST(match));
1929
1930 // Advance last index if the match is the empty string.
1931
1932 TNode<Smi> const match_length = LoadStringLengthAsSmi(match);
1933 GotoIfNot(SmiEqual(match_length, SmiZero()), &loop);
1934
1935 Node* last_index =
1936 LoadLastIndex(CAST(context), CAST(regexp), is_fastpath);
1937 if (is_fastpath) {
1938 CSA_ASSERT(this, TaggedIsPositiveSmi(last_index));
1939 } else {
1940 last_index = ToLength_Inline(context, last_index);
1941 }
1942
1943 Node* const new_last_index =
1944 AdvanceStringIndex(string, last_index, is_unicode, is_fastpath);
1945
1946 if (is_fastpath) {
1947 // On the fast path, we can be certain that lastIndex can never be
1948 // incremented to overflow the Smi range since the maximal string
1949 // length is less than the maximal Smi value.
1950 STATIC_ASSERT(String::kMaxLength < Smi::kMaxValue);
1951 CSA_ASSERT(this, TaggedIsPositiveSmi(new_last_index));
1952 }
1953
1954 StoreLastIndex(context, regexp, new_last_index, is_fastpath);
1955
1956 Goto(&loop);
1957 }
1958 }
1959
1960 BIND(&out);
1961 {
1962 // Wrap the match in a JSArray.
1963
1964 Node* const result = array.ToJSArray(CAST(context));
1965 Return(result);
1966 }
1967 }
1968 }
1969
1970 // ES#sec-regexp.prototype-@@match
1971 // RegExp.prototype [ @@match ] ( string )
TF_BUILTIN(RegExpPrototypeMatch,RegExpBuiltinsAssembler)1972 TF_BUILTIN(RegExpPrototypeMatch, RegExpBuiltinsAssembler) {
1973 TNode<Object> maybe_receiver = CAST(Parameter(Descriptor::kReceiver));
1974 TNode<Object> maybe_string = CAST(Parameter(Descriptor::kString));
1975 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1976
1977 // Ensure {maybe_receiver} is a JSReceiver.
1978 ThrowIfNotJSReceiver(context, maybe_receiver,
1979 MessageTemplate::kIncompatibleMethodReceiver,
1980 "RegExp.prototype.@@match");
1981 Node* const receiver = maybe_receiver;
1982
1983 // Convert {maybe_string} to a String.
1984 TNode<String> const string = ToString_Inline(context, maybe_string);
1985
1986 Label fast_path(this), slow_path(this);
1987 BranchIfFastRegExp(context, receiver, &fast_path, &slow_path);
1988
1989 BIND(&fast_path);
1990 // TODO(pwong): Could be optimized to remove the overhead of calling the
1991 // builtin (at the cost of a larger builtin).
1992 Return(CallBuiltin(Builtins::kRegExpMatchFast, context, receiver, string));
1993
1994 BIND(&slow_path);
1995 RegExpPrototypeMatchBody(context, receiver, string, false);
1996 }
1997
MatchAllIterator(TNode<Context> context,TNode<Context> native_context,TNode<Object> maybe_regexp,TNode<String> string,TNode<BoolT> is_fast_regexp,char const * method_name)1998 TNode<Object> RegExpBuiltinsAssembler::MatchAllIterator(
1999 TNode<Context> context, TNode<Context> native_context,
2000 TNode<Object> maybe_regexp, TNode<String> string,
2001 TNode<BoolT> is_fast_regexp, char const* method_name) {
2002 Label create_iterator(this), if_fast_regexp(this),
2003 if_slow_regexp(this, Label::kDeferred), if_not_regexp(this);
2004
2005 // 1. Let S be ? ToString(O).
2006 // Handled by the caller of MatchAllIterator.
2007 CSA_ASSERT(this, IsString(string));
2008
2009 TVARIABLE(Object, var_matcher);
2010 TVARIABLE(Int32T, var_global);
2011 TVARIABLE(Int32T, var_unicode);
2012
2013 // 2. If ? IsRegExp(R) is true, then
2014 GotoIf(is_fast_regexp, &if_fast_regexp);
2015 Branch(IsRegExp(context, maybe_regexp), &if_slow_regexp, &if_not_regexp);
2016 BIND(&if_fast_regexp);
2017 {
2018 CSA_ASSERT(this, IsFastRegExp(context, maybe_regexp));
2019 TNode<JSRegExp> fast_regexp = CAST(maybe_regexp);
2020 TNode<Object> source =
2021 LoadObjectField(fast_regexp, JSRegExp::kSourceOffset);
2022 TNode<String> flags = CAST(FlagsGetter(context, fast_regexp, true));
2023
2024 // c. Let matcher be ? Construct(C, « R, flags »).
2025 var_matcher = RegExpCreate(context, native_context, source, flags);
2026 CSA_ASSERT(this, IsFastRegExp(context, var_matcher.value()));
2027
2028 // d. Let global be ? ToBoolean(? Get(matcher, "global")).
2029 var_global = UncheckedCast<Int32T>(
2030 FastFlagGetter(var_matcher.value(), JSRegExp::kGlobal));
2031
2032 // e. Let fullUnicode be ? ToBoolean(? Get(matcher, "unicode").
2033 var_unicode = UncheckedCast<Int32T>(
2034 FastFlagGetter(var_matcher.value(), JSRegExp::kUnicode));
2035
2036 // f. Let lastIndex be ? ToLength(? Get(R, "lastIndex")).
2037 // g. Perform ? Set(matcher, "lastIndex", lastIndex, true).
2038 FastStoreLastIndex(var_matcher.value(), FastLoadLastIndex(fast_regexp));
2039 Goto(&create_iterator);
2040 }
2041 BIND(&if_slow_regexp);
2042 {
2043 // a. Let C be ? SpeciesConstructor(R, %RegExp%).
2044 TNode<Object> regexp_fun =
2045 LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX);
2046 TNode<Object> species_constructor =
2047 SpeciesConstructor(native_context, maybe_regexp, regexp_fun);
2048
2049 // b. Let flags be ? ToString(? Get(R, "flags")).
2050 TNode<Object> flags = GetProperty(context, maybe_regexp,
2051 isolate()->factory()->flags_string());
2052 TNode<String> flags_string = ToString_Inline(context, flags);
2053
2054 // c. Let matcher be ? Construct(C, « R, flags »).
2055 var_matcher =
2056 CAST(ConstructJS(CodeFactory::Construct(isolate()), context,
2057 species_constructor, maybe_regexp, flags_string));
2058
2059 // d. Let global be ? ToBoolean(? Get(matcher, "global")).
2060 var_global = UncheckedCast<Int32T>(
2061 SlowFlagGetter(context, var_matcher.value(), JSRegExp::kGlobal));
2062
2063 // e. Let fullUnicode be ? ToBoolean(? Get(matcher, "unicode").
2064 var_unicode = UncheckedCast<Int32T>(
2065 SlowFlagGetter(context, var_matcher.value(), JSRegExp::kUnicode));
2066
2067 // f. Let lastIndex be ? ToLength(? Get(R, "lastIndex")).
2068 TNode<Number> last_index = UncheckedCast<Number>(
2069 ToLength_Inline(context, SlowLoadLastIndex(context, maybe_regexp)));
2070
2071 // g. Perform ? Set(matcher, "lastIndex", lastIndex, true).
2072 SlowStoreLastIndex(context, var_matcher.value(), last_index);
2073
2074 Goto(&create_iterator);
2075 }
2076 // 3. Else,
2077 BIND(&if_not_regexp);
2078 {
2079 // a. Let flags be "g".
2080 // b. Let matcher be ? RegExpCreate(R, flags).
2081 var_matcher = RegExpCreate(context, native_context, maybe_regexp,
2082 StringConstant("g"));
2083
2084 // c. Let global be true.
2085 var_global = Int32Constant(1);
2086
2087 // d. Let fullUnicode be false.
2088 var_unicode = Int32Constant(0);
2089
2090 #ifdef DEBUG
2091 // Assert: ! Get(matcher, "lastIndex") is 0.
2092 TNode<Object> last_index = SlowLoadLastIndex(context, var_matcher.value());
2093 CSA_ASSERT(this, WordEqual(SmiZero(), last_index));
2094 #endif // DEBUG
2095
2096 Goto(&create_iterator);
2097 }
2098 // 4. Return ! CreateRegExpStringIterator(matcher, S, global, fullUnicode).
2099 BIND(&create_iterator);
2100 {
2101 TNode<Map> map = CAST(LoadContextElement(
2102 native_context,
2103 Context::INITIAL_REGEXP_STRING_ITERATOR_PROTOTYPE_MAP_INDEX));
2104
2105 // 4. Let iterator be ObjectCreate(%RegExpStringIteratorPrototype%, «
2106 // [[IteratingRegExp]], [[IteratedString]], [[Global]], [[Unicode]],
2107 // [[Done]] »).
2108 TNode<Object> iterator = CAST(Allocate(JSRegExpStringIterator::kSize));
2109 StoreMapNoWriteBarrier(iterator, map);
2110 StoreObjectFieldRoot(iterator,
2111 JSRegExpStringIterator::kPropertiesOrHashOffset,
2112 Heap::kEmptyFixedArrayRootIndex);
2113 StoreObjectFieldRoot(iterator, JSRegExpStringIterator::kElementsOffset,
2114 Heap::kEmptyFixedArrayRootIndex);
2115
2116 // 5. Set iterator.[[IteratingRegExp]] to R.
2117 StoreObjectFieldNoWriteBarrier(
2118 iterator, JSRegExpStringIterator::kIteratingRegExpOffset,
2119 var_matcher.value());
2120
2121 // 6. Set iterator.[[IteratedString]] to S.
2122 StoreObjectFieldNoWriteBarrier(
2123 iterator, JSRegExpStringIterator::kIteratedStringOffset, string);
2124
2125 #ifdef DEBUG
2126 // Verify global and unicode can be bitwise shifted without masking.
2127 TNode<Int32T> zero = Int32Constant(0);
2128 TNode<Int32T> one = Int32Constant(1);
2129 CSA_ASSERT(this, Word32Or(Word32Equal(var_global.value(), zero),
2130 Word32Equal(var_global.value(), one)));
2131 CSA_ASSERT(this, Word32Or(Word32Equal(var_unicode.value(), zero),
2132 Word32Equal(var_unicode.value(), one)));
2133 #endif // DEBUG
2134
2135 // 7. Set iterator.[[Global]] to global.
2136 // 8. Set iterator.[[Unicode]] to fullUnicode.
2137 // 9. Set iterator.[[Done]] to false.
2138 TNode<Word32T> global_flag = Word32Shl(
2139 var_global.value(), Int32Constant(JSRegExpStringIterator::kGlobalBit));
2140 TNode<Word32T> unicode_flag =
2141 Word32Shl(var_unicode.value(),
2142 Int32Constant(JSRegExpStringIterator::kUnicodeBit));
2143 TNode<Word32T> iterator_flags = Word32Or(global_flag, unicode_flag);
2144 StoreObjectFieldNoWriteBarrier(iterator,
2145 JSRegExpStringIterator::kFlagsOffset,
2146 SmiFromInt32(Signed(iterator_flags)));
2147
2148 return iterator;
2149 }
2150 }
2151
2152 // https://tc39.github.io/proposal-string-matchall/
2153 // RegExp.prototype [ @@matchAll ] ( string )
TF_BUILTIN(RegExpPrototypeMatchAll,RegExpBuiltinsAssembler)2154 TF_BUILTIN(RegExpPrototypeMatchAll, RegExpBuiltinsAssembler) {
2155 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
2156 TNode<Context> native_context = LoadNativeContext(context);
2157 TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
2158 TNode<Object> string = CAST(Parameter(Descriptor::kString));
2159
2160 // 1. Let R be the this value.
2161 // 2. If Type(R) is not Object, throw a TypeError exception.
2162 ThrowIfNotJSReceiver(context, receiver,
2163 MessageTemplate::kIncompatibleMethodReceiver,
2164 "RegExp.prototype.@@matchAll");
2165
2166 // 3. Return ? MatchAllIterator(R, string).
2167 Return(MatchAllIterator(
2168 context, native_context, receiver, ToString_Inline(context, string),
2169 IsFastRegExp(context, receiver), "RegExp.prototype.@@matchAll"));
2170 }
2171
2172 // Helper that skips a few initial checks. and assumes...
2173 // 1) receiver is a "fast" RegExp
2174 // 2) pattern is a string
TF_BUILTIN(RegExpMatchFast,RegExpBuiltinsAssembler)2175 TF_BUILTIN(RegExpMatchFast, RegExpBuiltinsAssembler) {
2176 TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
2177 TNode<String> string = CAST(Parameter(Descriptor::kPattern));
2178 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
2179
2180 RegExpPrototypeMatchBody(context, receiver, string, true);
2181 }
2182
RegExpPrototypeSearchBodyFast(Node * const context,Node * const regexp,Node * const string)2183 void RegExpBuiltinsAssembler::RegExpPrototypeSearchBodyFast(
2184 Node* const context, Node* const regexp, Node* const string) {
2185 CSA_ASSERT(this, IsFastRegExp(context, regexp));
2186 CSA_ASSERT(this, IsString(string));
2187
2188 // Grab the initial value of last index.
2189 Node* const previous_last_index = FastLoadLastIndex(CAST(regexp));
2190
2191 // Ensure last index is 0.
2192 FastStoreLastIndex(regexp, SmiZero());
2193
2194 // Call exec.
2195 Label if_didnotmatch(this);
2196 TNode<RegExpMatchInfo> match_indices = RegExpPrototypeExecBodyWithoutResult(
2197 CAST(context), CAST(regexp), CAST(string), &if_didnotmatch, true);
2198
2199 // Successful match.
2200 {
2201 // Reset last index.
2202 FastStoreLastIndex(regexp, previous_last_index);
2203
2204 // Return the index of the match.
2205 Node* const index = LoadFixedArrayElement(
2206 match_indices, RegExpMatchInfo::kFirstCaptureIndex);
2207 Return(index);
2208 }
2209
2210 BIND(&if_didnotmatch);
2211 {
2212 // Reset last index and return -1.
2213 FastStoreLastIndex(regexp, previous_last_index);
2214 Return(SmiConstant(-1));
2215 }
2216 }
2217
RegExpPrototypeSearchBodySlow(Node * const context,Node * const regexp,Node * const string)2218 void RegExpBuiltinsAssembler::RegExpPrototypeSearchBodySlow(
2219 Node* const context, Node* const regexp, Node* const string) {
2220 CSA_ASSERT(this, IsJSReceiver(regexp));
2221 CSA_ASSERT(this, IsString(string));
2222
2223 Isolate* const isolate = this->isolate();
2224
2225 Node* const smi_zero = SmiZero();
2226
2227 // Grab the initial value of last index.
2228 Node* const previous_last_index =
2229 SlowLoadLastIndex(CAST(context), CAST(regexp));
2230
2231 // Ensure last index is 0.
2232 {
2233 Label next(this), slow(this, Label::kDeferred);
2234 BranchIfSameValue(previous_last_index, smi_zero, &next, &slow);
2235
2236 BIND(&slow);
2237 SlowStoreLastIndex(context, regexp, smi_zero);
2238 Goto(&next);
2239 BIND(&next);
2240 }
2241
2242 // Call exec.
2243 Node* const exec_result = RegExpExec(context, regexp, string);
2244
2245 // Reset last index if necessary.
2246 {
2247 Label next(this), slow(this, Label::kDeferred);
2248 Node* const current_last_index =
2249 SlowLoadLastIndex(CAST(context), CAST(regexp));
2250
2251 BranchIfSameValue(current_last_index, previous_last_index, &next, &slow);
2252
2253 BIND(&slow);
2254 SlowStoreLastIndex(context, regexp, previous_last_index);
2255 Goto(&next);
2256 BIND(&next);
2257 }
2258
2259 // Return -1 if no match was found.
2260 {
2261 Label next(this);
2262 GotoIfNot(IsNull(exec_result), &next);
2263 Return(SmiConstant(-1));
2264 BIND(&next);
2265 }
2266
2267 // Return the index of the match.
2268 {
2269 Label fast_result(this), slow_result(this, Label::kDeferred);
2270 BranchIfFastRegExpResult(context, exec_result, &fast_result, &slow_result);
2271
2272 BIND(&fast_result);
2273 {
2274 Node* const index =
2275 LoadObjectField(exec_result, JSRegExpResult::kIndexOffset);
2276 Return(index);
2277 }
2278
2279 BIND(&slow_result);
2280 {
2281 Return(GetProperty(context, exec_result,
2282 isolate->factory()->index_string()));
2283 }
2284 }
2285 }
2286
2287 // ES#sec-regexp.prototype-@@search
2288 // RegExp.prototype [ @@search ] ( string )
TF_BUILTIN(RegExpPrototypeSearch,RegExpBuiltinsAssembler)2289 TF_BUILTIN(RegExpPrototypeSearch, RegExpBuiltinsAssembler) {
2290 TNode<Object> maybe_receiver = CAST(Parameter(Descriptor::kReceiver));
2291 TNode<Object> maybe_string = CAST(Parameter(Descriptor::kString));
2292 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
2293
2294 // Ensure {maybe_receiver} is a JSReceiver.
2295 ThrowIfNotJSReceiver(context, maybe_receiver,
2296 MessageTemplate::kIncompatibleMethodReceiver,
2297 "RegExp.prototype.@@search");
2298 Node* const receiver = maybe_receiver;
2299
2300 // Convert {maybe_string} to a String.
2301 TNode<String> const string = ToString_Inline(context, maybe_string);
2302
2303 Label fast_path(this), slow_path(this);
2304 BranchIfFastRegExp(context, receiver, &fast_path, &slow_path);
2305
2306 BIND(&fast_path);
2307 // TODO(pwong): Could be optimized to remove the overhead of calling the
2308 // builtin (at the cost of a larger builtin).
2309 Return(CallBuiltin(Builtins::kRegExpSearchFast, context, receiver, string));
2310
2311 BIND(&slow_path);
2312 RegExpPrototypeSearchBodySlow(context, receiver, string);
2313 }
2314
2315 // Helper that skips a few initial checks. and assumes...
2316 // 1) receiver is a "fast" RegExp
2317 // 2) pattern is a string
TF_BUILTIN(RegExpSearchFast,RegExpBuiltinsAssembler)2318 TF_BUILTIN(RegExpSearchFast, RegExpBuiltinsAssembler) {
2319 TNode<JSRegExp> receiver = CAST(Parameter(Descriptor::kReceiver));
2320 TNode<String> string = CAST(Parameter(Descriptor::kPattern));
2321 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
2322
2323 RegExpPrototypeSearchBodyFast(context, receiver, string);
2324 }
2325
2326 // Generates the fast path for @@split. {regexp} is an unmodified, non-sticky
2327 // JSRegExp, {string} is a String, and {limit} is a Smi.
RegExpPrototypeSplitBody(Node * const context,Node * const regexp,TNode<String> string,TNode<Smi> const limit)2328 void RegExpBuiltinsAssembler::RegExpPrototypeSplitBody(Node* const context,
2329 Node* const regexp,
2330 TNode<String> string,
2331 TNode<Smi> const limit) {
2332 CSA_ASSERT(this, IsFastRegExp(context, regexp));
2333 CSA_ASSERT(this, Word32BinaryNot(FastFlagGetter(regexp, JSRegExp::kSticky)));
2334
2335 TNode<IntPtrT> const int_limit = SmiUntag(limit);
2336
2337 const ElementsKind kind = PACKED_ELEMENTS;
2338 const ParameterMode mode = CodeStubAssembler::INTPTR_PARAMETERS;
2339
2340 Node* const allocation_site = nullptr;
2341 Node* const native_context = LoadNativeContext(context);
2342 Node* const array_map = LoadJSArrayElementsMap(kind, native_context);
2343
2344 Label return_empty_array(this, Label::kDeferred);
2345
2346 // If limit is zero, return an empty array.
2347 {
2348 Label next(this), if_limitiszero(this, Label::kDeferred);
2349 Branch(SmiEqual(limit, SmiZero()), &return_empty_array, &next);
2350 BIND(&next);
2351 }
2352
2353 TNode<Smi> const string_length = LoadStringLengthAsSmi(string);
2354
2355 // If passed the empty {string}, return either an empty array or a singleton
2356 // array depending on whether the {regexp} matches.
2357 {
2358 Label next(this), if_stringisempty(this, Label::kDeferred);
2359 Branch(SmiEqual(string_length, SmiZero()), &if_stringisempty, &next);
2360
2361 BIND(&if_stringisempty);
2362 {
2363 Node* const last_match_info = LoadContextElement(
2364 native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX);
2365
2366 Node* const match_indices =
2367 CallBuiltin(Builtins::kRegExpExecInternal, context, regexp, string,
2368 SmiZero(), last_match_info);
2369
2370 Label return_singleton_array(this);
2371 Branch(IsNull(match_indices), &return_singleton_array,
2372 &return_empty_array);
2373
2374 BIND(&return_singleton_array);
2375 {
2376 Node* const length = SmiConstant(1);
2377 Node* const capacity = IntPtrConstant(1);
2378 Node* const result = AllocateJSArray(kind, array_map, capacity, length,
2379 allocation_site, mode);
2380
2381 TNode<FixedArray> const fixed_array = CAST(LoadElements(result));
2382 StoreFixedArrayElement(fixed_array, 0, string);
2383
2384 Return(result);
2385 }
2386 }
2387
2388 BIND(&next);
2389 }
2390
2391 // Loop preparations.
2392
2393 GrowableFixedArray array(state());
2394
2395 TVARIABLE(Smi, var_last_matched_until, SmiZero());
2396 TVARIABLE(Smi, var_next_search_from, SmiZero());
2397
2398 Variable* vars[] = {array.var_array(), array.var_length(),
2399 array.var_capacity(), &var_last_matched_until,
2400 &var_next_search_from};
2401 const int vars_count = sizeof(vars) / sizeof(vars[0]);
2402 Label loop(this, vars_count, vars), push_suffix_and_out(this), out(this);
2403 Goto(&loop);
2404
2405 BIND(&loop);
2406 {
2407 TNode<Smi> const next_search_from = var_next_search_from.value();
2408 TNode<Smi> const last_matched_until = var_last_matched_until.value();
2409
2410 // We're done if we've reached the end of the string.
2411 {
2412 Label next(this);
2413 Branch(SmiEqual(next_search_from, string_length), &push_suffix_and_out,
2414 &next);
2415 BIND(&next);
2416 }
2417
2418 // Search for the given {regexp}.
2419
2420 Node* const last_match_info = LoadContextElement(
2421 native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX);
2422
2423 TNode<HeapObject> const match_indices_ho =
2424 CAST(CallBuiltin(Builtins::kRegExpExecInternal, context, regexp, string,
2425 next_search_from, last_match_info));
2426
2427 // We're done if no match was found.
2428 {
2429 Label next(this);
2430 Branch(IsNull(match_indices_ho), &push_suffix_and_out, &next);
2431 BIND(&next);
2432 }
2433
2434 TNode<FixedArray> match_indices = CAST(match_indices_ho);
2435 TNode<Smi> const match_from = CAST(LoadFixedArrayElement(
2436 match_indices, RegExpMatchInfo::kFirstCaptureIndex));
2437
2438 // We're done if the match starts beyond the string.
2439 {
2440 Label next(this);
2441 Branch(SmiEqual(match_from, string_length), &push_suffix_and_out, &next);
2442 BIND(&next);
2443 }
2444
2445 TNode<Smi> const match_to = CAST(LoadFixedArrayElement(
2446 match_indices, RegExpMatchInfo::kFirstCaptureIndex + 1));
2447
2448 // Advance index and continue if the match is empty.
2449 {
2450 Label next(this);
2451
2452 GotoIfNot(SmiEqual(match_to, next_search_from), &next);
2453 GotoIfNot(SmiEqual(match_to, last_matched_until), &next);
2454
2455 Node* const is_unicode = FastFlagGetter(regexp, JSRegExp::kUnicode);
2456 Node* const new_next_search_from =
2457 AdvanceStringIndex(string, next_search_from, is_unicode, true);
2458 var_next_search_from = CAST(new_next_search_from);
2459 Goto(&loop);
2460
2461 BIND(&next);
2462 }
2463
2464 // A valid match was found, add the new substring to the array.
2465 {
2466 TNode<Smi> const from = last_matched_until;
2467 TNode<Smi> const to = match_from;
2468 array.Push(CallBuiltin(Builtins::kSubString, context, string, from, to));
2469 GotoIf(WordEqual(array.length(), int_limit), &out);
2470 }
2471
2472 // Add all captures to the array.
2473 {
2474 Node* const num_registers = LoadFixedArrayElement(
2475 match_indices, RegExpMatchInfo::kNumberOfCapturesIndex);
2476 Node* const int_num_registers = SmiUntag(num_registers);
2477
2478 VARIABLE(var_reg, MachineType::PointerRepresentation());
2479 var_reg.Bind(IntPtrConstant(2));
2480
2481 Variable* vars[] = {array.var_array(), array.var_length(),
2482 array.var_capacity(), &var_reg};
2483 const int vars_count = sizeof(vars) / sizeof(vars[0]);
2484 Label nested_loop(this, vars_count, vars), nested_loop_out(this);
2485 Branch(IntPtrLessThan(var_reg.value(), int_num_registers), &nested_loop,
2486 &nested_loop_out);
2487
2488 BIND(&nested_loop);
2489 {
2490 Node* const reg = var_reg.value();
2491 Node* const from = LoadFixedArrayElement(
2492 match_indices, reg,
2493 RegExpMatchInfo::kFirstCaptureIndex * kPointerSize, mode);
2494 TNode<Smi> const to = CAST(LoadFixedArrayElement(
2495 match_indices, reg,
2496 (RegExpMatchInfo::kFirstCaptureIndex + 1) * kPointerSize, mode));
2497
2498 Label select_capture(this), select_undefined(this), store_value(this);
2499 VARIABLE(var_value, MachineRepresentation::kTagged);
2500 Branch(SmiEqual(to, SmiConstant(-1)), &select_undefined,
2501 &select_capture);
2502
2503 BIND(&select_capture);
2504 {
2505 var_value.Bind(
2506 CallBuiltin(Builtins::kSubString, context, string, from, to));
2507 Goto(&store_value);
2508 }
2509
2510 BIND(&select_undefined);
2511 {
2512 var_value.Bind(UndefinedConstant());
2513 Goto(&store_value);
2514 }
2515
2516 BIND(&store_value);
2517 {
2518 array.Push(CAST(var_value.value()));
2519 GotoIf(WordEqual(array.length(), int_limit), &out);
2520
2521 Node* const new_reg = IntPtrAdd(reg, IntPtrConstant(2));
2522 var_reg.Bind(new_reg);
2523
2524 Branch(IntPtrLessThan(new_reg, int_num_registers), &nested_loop,
2525 &nested_loop_out);
2526 }
2527 }
2528
2529 BIND(&nested_loop_out);
2530 }
2531
2532 var_last_matched_until = match_to;
2533 var_next_search_from = match_to;
2534 Goto(&loop);
2535 }
2536
2537 BIND(&push_suffix_and_out);
2538 {
2539 Node* const from = var_last_matched_until.value();
2540 Node* const to = string_length;
2541 array.Push(CallBuiltin(Builtins::kSubString, context, string, from, to));
2542 Goto(&out);
2543 }
2544
2545 BIND(&out);
2546 {
2547 Node* const result = array.ToJSArray(CAST(context));
2548 Return(result);
2549 }
2550
2551 BIND(&return_empty_array);
2552 {
2553 Node* const length = SmiZero();
2554 Node* const capacity = IntPtrZero();
2555 Node* const result = AllocateJSArray(kind, array_map, capacity, length,
2556 allocation_site, mode);
2557 Return(result);
2558 }
2559 }
2560
2561 // Helper that skips a few initial checks.
TF_BUILTIN(RegExpSplit,RegExpBuiltinsAssembler)2562 TF_BUILTIN(RegExpSplit, RegExpBuiltinsAssembler) {
2563 TNode<JSRegExp> regexp = CAST(Parameter(Descriptor::kRegExp));
2564 TNode<String> string = CAST(Parameter(Descriptor::kString));
2565 TNode<Object> maybe_limit = CAST(Parameter(Descriptor::kLimit));
2566 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
2567
2568 CSA_ASSERT(this, IsFastRegExp(context, regexp));
2569
2570 // TODO(jgruber): Even if map checks send us to the fast path, we still need
2571 // to verify the constructor property and jump to the slow path if it has
2572 // been changed.
2573
2574 // Verify {maybe_limit}.
2575
2576 VARIABLE(var_limit, MachineRepresentation::kTagged, maybe_limit);
2577 Label if_limitissmimax(this), runtime(this, Label::kDeferred);
2578
2579 {
2580 Label next(this);
2581
2582 GotoIf(IsUndefined(maybe_limit), &if_limitissmimax);
2583 Branch(TaggedIsPositiveSmi(maybe_limit), &next, &runtime);
2584
2585 // We need to be extra-strict and require the given limit to be either
2586 // undefined or a positive smi. We can't call ToUint32(maybe_limit) since
2587 // that might move us onto the slow path, resulting in ordering spec
2588 // violations (see https://crbug.com/801171).
2589
2590 BIND(&if_limitissmimax);
2591 {
2592 // TODO(jgruber): In this case, we can probably avoid generation of limit
2593 // checks in Generate_RegExpPrototypeSplitBody.
2594 var_limit.Bind(SmiConstant(Smi::kMaxValue));
2595 Goto(&next);
2596 }
2597
2598 BIND(&next);
2599 }
2600
2601 // Due to specific shortcuts we take on the fast path (specifically, we don't
2602 // allocate a new regexp instance as specced), we need to ensure that the
2603 // given regexp is non-sticky to avoid invalid results. See crbug.com/v8/6706.
2604
2605 GotoIf(FastFlagGetter(regexp, JSRegExp::kSticky), &runtime);
2606
2607 // We're good to go on the fast path, which is inlined here.
2608
2609 RegExpPrototypeSplitBody(context, regexp, string, CAST(var_limit.value()));
2610
2611 BIND(&runtime);
2612 Return(CallRuntime(Runtime::kRegExpSplit, context, regexp, string,
2613 var_limit.value()));
2614 }
2615
2616 // ES#sec-regexp.prototype-@@split
2617 // RegExp.prototype [ @@split ] ( string, limit )
TF_BUILTIN(RegExpPrototypeSplit,RegExpBuiltinsAssembler)2618 TF_BUILTIN(RegExpPrototypeSplit, RegExpBuiltinsAssembler) {
2619 const int kStringArg = 0;
2620 const int kLimitArg = 1;
2621
2622 TNode<IntPtrT> argc =
2623 ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount));
2624 CodeStubArguments args(this, argc);
2625
2626 TNode<Object> maybe_receiver = args.GetReceiver();
2627 TNode<Object> maybe_string = args.GetOptionalArgumentValue(kStringArg);
2628 TNode<Object> maybe_limit = args.GetOptionalArgumentValue(kLimitArg);
2629 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
2630
2631 // Ensure {maybe_receiver} is a JSReceiver.
2632 ThrowIfNotJSReceiver(context, maybe_receiver,
2633 MessageTemplate::kIncompatibleMethodReceiver,
2634 "RegExp.prototype.@@split");
2635 Node* const receiver = maybe_receiver;
2636
2637 // Convert {maybe_string} to a String.
2638 TNode<String> const string = ToString_Inline(context, maybe_string);
2639
2640 Label stub(this), runtime(this, Label::kDeferred);
2641 BranchIfFastRegExp(context, receiver, &stub, &runtime);
2642
2643 BIND(&stub);
2644 args.PopAndReturn(CallBuiltin(Builtins::kRegExpSplit, context, receiver,
2645 string, maybe_limit));
2646
2647 BIND(&runtime);
2648 args.PopAndReturn(CallRuntime(Runtime::kRegExpSplit, context, receiver,
2649 string, maybe_limit));
2650 }
2651
ReplaceGlobalCallableFastPath(Node * context,Node * regexp,Node * string,Node * replace_callable)2652 Node* RegExpBuiltinsAssembler::ReplaceGlobalCallableFastPath(
2653 Node* context, Node* regexp, Node* string, Node* replace_callable) {
2654 // The fast path is reached only if {receiver} is a global unmodified
2655 // JSRegExp instance and {replace_callable} is callable.
2656
2657 CSA_ASSERT(this, IsFastRegExp(context, regexp));
2658 CSA_ASSERT(this, IsCallable(replace_callable));
2659 CSA_ASSERT(this, IsString(string));
2660
2661 Isolate* const isolate = this->isolate();
2662
2663 Node* const undefined = UndefinedConstant();
2664 TNode<IntPtrT> int_one = IntPtrConstant(1);
2665
2666 Node* const native_context = LoadNativeContext(context);
2667
2668 Label out(this);
2669 VARIABLE(var_result, MachineRepresentation::kTagged);
2670
2671 // Set last index to 0.
2672 FastStoreLastIndex(regexp, SmiZero());
2673
2674 // Allocate {result_array}.
2675 Node* result_array;
2676 {
2677 ElementsKind kind = PACKED_ELEMENTS;
2678 Node* const array_map = LoadJSArrayElementsMap(kind, native_context);
2679 TNode<IntPtrT> capacity = IntPtrConstant(16);
2680 TNode<Smi> length = SmiZero();
2681 Node* const allocation_site = nullptr;
2682 ParameterMode capacity_mode = CodeStubAssembler::INTPTR_PARAMETERS;
2683
2684 result_array = AllocateJSArray(kind, array_map, capacity, length,
2685 allocation_site, capacity_mode);
2686 }
2687
2688 // Call into runtime for RegExpExecMultiple.
2689 TNode<FixedArray> last_match_info = CAST(LoadContextElement(
2690 native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX));
2691 Node* const res = CallRuntime(Runtime::kRegExpExecMultiple, context, regexp,
2692 string, last_match_info, result_array);
2693
2694 // Reset last index to 0.
2695 FastStoreLastIndex(regexp, SmiZero());
2696
2697 // If no matches, return the subject string.
2698 var_result.Bind(string);
2699 GotoIf(IsNull(res), &out);
2700
2701 // Reload last match info since it might have changed.
2702 last_match_info = CAST(LoadContextElement(
2703 native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX));
2704
2705 Node* const res_length = LoadJSArrayLength(res);
2706 TNode<FixedArray> const res_elems = CAST(LoadElements(res));
2707
2708 TNode<Smi> const num_capture_registers = CAST(LoadFixedArrayElement(
2709 last_match_info, RegExpMatchInfo::kNumberOfCapturesIndex));
2710
2711 Label if_hasexplicitcaptures(this), if_noexplicitcaptures(this),
2712 create_result(this);
2713 Branch(SmiEqual(num_capture_registers, SmiConstant(2)),
2714 &if_noexplicitcaptures, &if_hasexplicitcaptures);
2715
2716 BIND(&if_noexplicitcaptures);
2717 {
2718 // If the number of captures is two then there are no explicit captures in
2719 // the regexp, just the implicit capture that captures the whole match. In
2720 // this case we can simplify quite a bit and end up with something faster.
2721 // The builder will consist of some integers that indicate slices of the
2722 // input string and some replacements that were returned from the replace
2723 // function.
2724
2725 TVARIABLE(Smi, var_match_start, SmiZero());
2726
2727 TNode<IntPtrT> const end = SmiUntag(res_length);
2728 TVARIABLE(IntPtrT, var_i, IntPtrZero());
2729
2730 Variable* vars[] = {&var_i, &var_match_start};
2731 Label loop(this, 2, vars);
2732 Goto(&loop);
2733 BIND(&loop);
2734 {
2735 GotoIfNot(IntPtrLessThan(var_i.value(), end), &create_result);
2736
2737 Node* const elem = LoadFixedArrayElement(res_elems, var_i.value());
2738
2739 Label if_issmi(this), if_isstring(this), loop_epilogue(this);
2740 Branch(TaggedIsSmi(elem), &if_issmi, &if_isstring);
2741
2742 BIND(&if_issmi);
2743 {
2744 TNode<Smi> smi_elem = CAST(elem);
2745 // Integers represent slices of the original string.
2746 Label if_isnegativeorzero(this), if_ispositive(this);
2747 BranchIfSmiLessThanOrEqual(smi_elem, SmiZero(), &if_isnegativeorzero,
2748 &if_ispositive);
2749
2750 BIND(&if_ispositive);
2751 {
2752 TNode<IntPtrT> int_elem = SmiUntag(smi_elem);
2753 TNode<IntPtrT> new_match_start =
2754 Signed(IntPtrAdd(WordShr(int_elem, IntPtrConstant(11)),
2755 WordAnd(int_elem, IntPtrConstant(0x7FF))));
2756 var_match_start = SmiTag(new_match_start);
2757 Goto(&loop_epilogue);
2758 }
2759
2760 BIND(&if_isnegativeorzero);
2761 {
2762 var_i = IntPtrAdd(var_i.value(), int_one);
2763
2764 TNode<Smi> const next_elem =
2765 CAST(LoadFixedArrayElement(res_elems, var_i.value()));
2766
2767 var_match_start = SmiSub(next_elem, smi_elem);
2768 Goto(&loop_epilogue);
2769 }
2770 }
2771
2772 BIND(&if_isstring);
2773 {
2774 CSA_ASSERT(this, IsString(elem));
2775
2776 Callable call_callable = CodeFactory::Call(isolate);
2777 TNode<Smi> match_start = var_match_start.value();
2778 Node* const replacement_obj =
2779 CallJS(call_callable, context, replace_callable, undefined, elem,
2780 match_start, string);
2781
2782 TNode<String> const replacement_str =
2783 ToString_Inline(context, replacement_obj);
2784 StoreFixedArrayElement(res_elems, var_i.value(), replacement_str);
2785
2786 TNode<Smi> const elem_length = LoadStringLengthAsSmi(elem);
2787 var_match_start = SmiAdd(match_start, elem_length);
2788
2789 Goto(&loop_epilogue);
2790 }
2791
2792 BIND(&loop_epilogue);
2793 {
2794 var_i = IntPtrAdd(var_i.value(), int_one);
2795 Goto(&loop);
2796 }
2797 }
2798 }
2799
2800 BIND(&if_hasexplicitcaptures);
2801 {
2802 Node* const from = IntPtrZero();
2803 Node* const to = SmiUntag(res_length);
2804 const int increment = 1;
2805
2806 BuildFastLoop(from, to,
2807 [this, res_elems, isolate, native_context, context, undefined,
2808 replace_callable](Node* index) {
2809 Node* const elem = LoadFixedArrayElement(res_elems, index);
2810
2811 Label do_continue(this);
2812 GotoIf(TaggedIsSmi(elem), &do_continue);
2813
2814 // elem must be an Array.
2815 // Use the apply argument as backing for global RegExp
2816 // properties.
2817
2818 CSA_ASSERT(this, HasInstanceType(elem, JS_ARRAY_TYPE));
2819
2820 // TODO(jgruber): Remove indirection through
2821 // Call->ReflectApply.
2822 Callable call_callable = CodeFactory::Call(isolate);
2823 Node* const reflect_apply = LoadContextElement(
2824 native_context, Context::REFLECT_APPLY_INDEX);
2825
2826 Node* const replacement_obj =
2827 CallJS(call_callable, context, reflect_apply, undefined,
2828 replace_callable, undefined, elem);
2829
2830 // Overwrite the i'th element in the results with the string
2831 // we got back from the callback function.
2832
2833 TNode<String> const replacement_str =
2834 ToString_Inline(context, replacement_obj);
2835 StoreFixedArrayElement(res_elems, index, replacement_str);
2836
2837 Goto(&do_continue);
2838 BIND(&do_continue);
2839 },
2840 increment, CodeStubAssembler::INTPTR_PARAMETERS,
2841 CodeStubAssembler::IndexAdvanceMode::kPost);
2842
2843 Goto(&create_result);
2844 }
2845
2846 BIND(&create_result);
2847 {
2848 Node* const result = CallRuntime(Runtime::kStringBuilderConcat, context,
2849 res, res_length, string);
2850 var_result.Bind(result);
2851 Goto(&out);
2852 }
2853
2854 BIND(&out);
2855 return var_result.value();
2856 }
2857
ReplaceSimpleStringFastPath(Node * context,Node * regexp,TNode<String> string,TNode<String> replace_string)2858 Node* RegExpBuiltinsAssembler::ReplaceSimpleStringFastPath(
2859 Node* context, Node* regexp, TNode<String> string,
2860 TNode<String> replace_string) {
2861 // The fast path is reached only if {receiver} is an unmodified
2862 // JSRegExp instance, {replace_value} is non-callable, and
2863 // ToString({replace_value}) does not contain '$', i.e. we're doing a simple
2864 // string replacement.
2865
2866 CSA_ASSERT(this, IsFastRegExp(context, regexp));
2867
2868 const bool kIsFastPath = true;
2869
2870 TVARIABLE(String, var_result, EmptyStringConstant());
2871 VARIABLE(var_last_match_end, MachineRepresentation::kTagged, SmiZero());
2872 VARIABLE(var_is_unicode, MachineRepresentation::kWord32, Int32Constant(0));
2873 Variable* vars[] = {&var_result, &var_last_match_end};
2874 Label out(this), loop(this, 2, vars), loop_end(this),
2875 if_nofurthermatches(this);
2876
2877 // Is {regexp} global?
2878 Node* const is_global = FastFlagGetter(regexp, JSRegExp::kGlobal);
2879 GotoIfNot(is_global, &loop);
2880
2881 var_is_unicode.Bind(FastFlagGetter(regexp, JSRegExp::kUnicode));
2882 FastStoreLastIndex(regexp, SmiZero());
2883 Goto(&loop);
2884
2885 BIND(&loop);
2886 {
2887 TNode<RegExpMatchInfo> var_match_indices =
2888 RegExpPrototypeExecBodyWithoutResult(CAST(context), CAST(regexp),
2889 string, &if_nofurthermatches,
2890 kIsFastPath);
2891
2892 // Successful match.
2893 {
2894 TNode<Smi> const match_start = CAST(LoadFixedArrayElement(
2895 var_match_indices, RegExpMatchInfo::kFirstCaptureIndex));
2896 TNode<Smi> const match_end = CAST(LoadFixedArrayElement(
2897 var_match_indices, RegExpMatchInfo::kFirstCaptureIndex + 1));
2898
2899 TNode<Smi> const replace_length = LoadStringLengthAsSmi(replace_string);
2900
2901 // TODO(jgruber): We could skip many of the checks that using SubString
2902 // here entails.
2903 TNode<String> first_part =
2904 CAST(CallBuiltin(Builtins::kSubString, context, string,
2905 var_last_match_end.value(), match_start));
2906 var_result = CAST(CallBuiltin(Builtins::kStringAdd_CheckNone_NotTenured,
2907 context, var_result.value(), first_part));
2908
2909 GotoIf(SmiEqual(replace_length, SmiZero()), &loop_end);
2910
2911 var_result =
2912 CAST(CallBuiltin(Builtins::kStringAdd_CheckNone_NotTenured, context,
2913 var_result.value(), replace_string));
2914 Goto(&loop_end);
2915
2916 BIND(&loop_end);
2917 {
2918 var_last_match_end.Bind(match_end);
2919 // Non-global case ends here after the first replacement.
2920 GotoIfNot(is_global, &if_nofurthermatches);
2921
2922 GotoIf(SmiNotEqual(match_end, match_start), &loop);
2923 // If match is the empty string, we have to increment lastIndex.
2924 Node* const this_index = FastLoadLastIndex(CAST(regexp));
2925 Node* const next_index = AdvanceStringIndex(
2926 string, this_index, var_is_unicode.value(), kIsFastPath);
2927 FastStoreLastIndex(regexp, next_index);
2928 Goto(&loop);
2929 }
2930 }
2931 }
2932
2933 BIND(&if_nofurthermatches);
2934 {
2935 TNode<Smi> const string_length = LoadStringLengthAsSmi(string);
2936 TNode<String> last_part =
2937 CAST(CallBuiltin(Builtins::kSubString, context, string,
2938 var_last_match_end.value(), string_length));
2939 var_result = CAST(CallBuiltin(Builtins::kStringAdd_CheckNone_NotTenured,
2940 context, var_result.value(), last_part));
2941 Goto(&out);
2942 }
2943
2944 BIND(&out);
2945 return var_result.value();
2946 }
2947
2948 // Helper that skips a few initial checks.
TF_BUILTIN(RegExpReplace,RegExpBuiltinsAssembler)2949 TF_BUILTIN(RegExpReplace, RegExpBuiltinsAssembler) {
2950 TNode<JSRegExp> regexp = CAST(Parameter(Descriptor::kRegExp));
2951 TNode<String> string = CAST(Parameter(Descriptor::kString));
2952 TNode<Object> replace_value = CAST(Parameter(Descriptor::kReplaceValue));
2953 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
2954
2955 CSA_ASSERT(this, IsFastRegExp(context, regexp));
2956
2957 Label checkreplacestring(this), if_iscallable(this),
2958 runtime(this, Label::kDeferred);
2959
2960 // 2. Is {replace_value} callable?
2961 GotoIf(TaggedIsSmi(replace_value), &checkreplacestring);
2962 Branch(IsCallableMap(LoadMap(CAST(replace_value))), &if_iscallable,
2963 &checkreplacestring);
2964
2965 // 3. Does ToString({replace_value}) contain '$'?
2966 BIND(&checkreplacestring);
2967 {
2968 TNode<String> const replace_string =
2969 ToString_Inline(context, replace_value);
2970
2971 // ToString(replaceValue) could potentially change the shape of the RegExp
2972 // object. Recheck that we are still on the fast path and bail to runtime
2973 // otherwise.
2974 {
2975 Label next(this);
2976 BranchIfFastRegExp(context, regexp, &next, &runtime);
2977 BIND(&next);
2978 }
2979
2980 TNode<String> const dollar_string = HeapConstant(
2981 isolate()->factory()->LookupSingleCharacterStringFromCode('$'));
2982 TNode<Smi> const dollar_ix =
2983 CAST(CallBuiltin(Builtins::kStringIndexOf, context, replace_string,
2984 dollar_string, SmiZero()));
2985 GotoIfNot(SmiEqual(dollar_ix, SmiConstant(-1)), &runtime);
2986
2987 Return(
2988 ReplaceSimpleStringFastPath(context, regexp, string, replace_string));
2989 }
2990
2991 // {regexp} is unmodified and {replace_value} is callable.
2992 BIND(&if_iscallable);
2993 {
2994 Node* const replace_fn = replace_value;
2995
2996 // Check if the {regexp} is global.
2997 Label if_isglobal(this), if_isnotglobal(this);
2998
2999 Node* const is_global = FastFlagGetter(regexp, JSRegExp::kGlobal);
3000 Branch(is_global, &if_isglobal, &if_isnotglobal);
3001
3002 BIND(&if_isglobal);
3003 Return(ReplaceGlobalCallableFastPath(context, regexp, string, replace_fn));
3004
3005 BIND(&if_isnotglobal);
3006 Return(CallRuntime(Runtime::kStringReplaceNonGlobalRegExpWithFunction,
3007 context, string, regexp, replace_fn));
3008 }
3009
3010 BIND(&runtime);
3011 Return(CallRuntime(Runtime::kRegExpReplace, context, regexp, string,
3012 replace_value));
3013 }
3014
3015 // ES#sec-regexp.prototype-@@replace
3016 // RegExp.prototype [ @@replace ] ( string, replaceValue )
TF_BUILTIN(RegExpPrototypeReplace,RegExpBuiltinsAssembler)3017 TF_BUILTIN(RegExpPrototypeReplace, RegExpBuiltinsAssembler) {
3018 const int kStringArg = 0;
3019 const int kReplaceValueArg = 1;
3020
3021 TNode<IntPtrT> argc =
3022 ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount));
3023 CodeStubArguments args(this, argc);
3024
3025 TNode<Object> maybe_receiver = args.GetReceiver();
3026 TNode<Object> maybe_string = args.GetOptionalArgumentValue(kStringArg);
3027 TNode<Object> replace_value = args.GetOptionalArgumentValue(kReplaceValueArg);
3028 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
3029
3030 // RegExpPrototypeReplace is a bit of a beast - a summary of dispatch logic:
3031 //
3032 // if (!IsFastRegExp(receiver)) CallRuntime(RegExpReplace)
3033 // if (IsCallable(replace)) {
3034 // if (IsGlobal(receiver)) {
3035 // // Called 'fast-path' but contains several runtime calls.
3036 // ReplaceGlobalCallableFastPath()
3037 // } else {
3038 // CallRuntime(StringReplaceNonGlobalRegExpWithFunction)
3039 // }
3040 // } else {
3041 // if (replace.contains("$")) {
3042 // CallRuntime(RegExpReplace)
3043 // } else {
3044 // ReplaceSimpleStringFastPath()
3045 // }
3046 // }
3047
3048 // Ensure {maybe_receiver} is a JSReceiver.
3049 ThrowIfNotJSReceiver(context, maybe_receiver,
3050 MessageTemplate::kIncompatibleMethodReceiver,
3051 "RegExp.prototype.@@replace");
3052 Node* const receiver = maybe_receiver;
3053
3054 // Convert {maybe_string} to a String.
3055 TNode<String> const string = ToString_Inline(context, maybe_string);
3056
3057 // Fast-path checks: 1. Is the {receiver} an unmodified JSRegExp instance?
3058 Label stub(this), runtime(this, Label::kDeferred);
3059 BranchIfFastRegExp(context, receiver, &stub, &runtime);
3060
3061 BIND(&stub);
3062 args.PopAndReturn(CallBuiltin(Builtins::kRegExpReplace, context, receiver,
3063 string, replace_value));
3064
3065 BIND(&runtime);
3066 args.PopAndReturn(CallRuntime(Runtime::kRegExpReplace, context, receiver,
3067 string, replace_value));
3068 }
3069
3070 // Simple string matching functionality for internal use which does not modify
3071 // the last match info.
TF_BUILTIN(RegExpInternalMatch,RegExpBuiltinsAssembler)3072 TF_BUILTIN(RegExpInternalMatch, RegExpBuiltinsAssembler) {
3073 TNode<JSRegExp> regexp = CAST(Parameter(Descriptor::kRegExp));
3074 TNode<String> string = CAST(Parameter(Descriptor::kString));
3075 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
3076
3077 TNode<Context> native_context = LoadNativeContext(context);
3078 TNode<RegExpMatchInfo> internal_match_info = CAST(LoadContextElement(
3079 native_context, Context::REGEXP_INTERNAL_MATCH_INFO_INDEX));
3080 TNode<HeapObject> maybe_match_indices =
3081 CAST(CallBuiltin(Builtins::kRegExpExecInternal, context, regexp, string,
3082 SmiZero(), internal_match_info));
3083 TNode<Oddball> null = NullConstant();
3084 Label if_matched(this);
3085 GotoIfNot(WordEqual(maybe_match_indices, null), &if_matched);
3086 Return(null);
3087
3088 BIND(&if_matched);
3089 TNode<RegExpMatchInfo> match_indices = CAST(maybe_match_indices);
3090 Return(
3091 ConstructNewResultFromMatchInfo(context, regexp, match_indices, string));
3092 }
3093
3094 class RegExpStringIteratorAssembler : public RegExpBuiltinsAssembler {
3095 public:
RegExpStringIteratorAssembler(compiler::CodeAssemblerState * state)3096 explicit RegExpStringIteratorAssembler(compiler::CodeAssemblerState* state)
3097 : RegExpBuiltinsAssembler(state) {}
3098
3099 protected:
LoadFlags(TNode<HeapObject> iterator)3100 TNode<Smi> LoadFlags(TNode<HeapObject> iterator) {
3101 return LoadObjectField<Smi>(iterator, JSRegExpStringIterator::kFlagsOffset);
3102 }
3103
HasDoneFlag(TNode<Smi> flags)3104 TNode<BoolT> HasDoneFlag(TNode<Smi> flags) {
3105 return UncheckedCast<BoolT>(
3106 IsSetSmi(flags, 1 << JSRegExpStringIterator::kDoneBit));
3107 }
3108
HasGlobalFlag(TNode<Smi> flags)3109 TNode<BoolT> HasGlobalFlag(TNode<Smi> flags) {
3110 return UncheckedCast<BoolT>(
3111 IsSetSmi(flags, 1 << JSRegExpStringIterator::kGlobalBit));
3112 }
3113
HasUnicodeFlag(TNode<Smi> flags)3114 TNode<BoolT> HasUnicodeFlag(TNode<Smi> flags) {
3115 return UncheckedCast<BoolT>(
3116 IsSetSmi(flags, 1 << JSRegExpStringIterator::kUnicodeBit));
3117 }
3118
SetDoneFlag(TNode<HeapObject> iterator,TNode<Smi> flags)3119 void SetDoneFlag(TNode<HeapObject> iterator, TNode<Smi> flags) {
3120 TNode<Smi> new_flags =
3121 SmiOr(flags, SmiConstant(1 << JSRegExpStringIterator::kDoneBit));
3122 StoreObjectFieldNoWriteBarrier(
3123 iterator, JSRegExpStringIterator::kFlagsOffset, new_flags);
3124 }
3125 };
3126
3127 // https://tc39.github.io/proposal-string-matchall/
3128 // %RegExpStringIteratorPrototype%.next ( )
TF_BUILTIN(RegExpStringIteratorPrototypeNext,RegExpStringIteratorAssembler)3129 TF_BUILTIN(RegExpStringIteratorPrototypeNext, RegExpStringIteratorAssembler) {
3130 const char* method_name = "%RegExpStringIterator%.prototype.next";
3131 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
3132 TNode<Object> maybe_receiver = CAST(Parameter(Descriptor::kReceiver));
3133
3134 Label if_match(this), if_no_match(this, Label::kDeferred),
3135 return_empty_done_result(this, Label::kDeferred);
3136
3137 // 1. Let O be the this value.
3138 // 2. If Type(O) is not Object, throw a TypeError exception.
3139 // 3. If O does not have all of the internal slots of a RegExp String Iterator
3140 // Object Instance (see 5.3), throw a TypeError exception.
3141 ThrowIfNotInstanceType(context, maybe_receiver,
3142 JS_REGEXP_STRING_ITERATOR_TYPE, method_name);
3143 TNode<HeapObject> receiver = CAST(maybe_receiver);
3144
3145 // 4. If O.[[Done]] is true, then
3146 // a. Return ! CreateIterResultObject(undefined, true).
3147 TNode<Smi> flags = LoadFlags(receiver);
3148 GotoIf(HasDoneFlag(flags), &return_empty_done_result);
3149
3150 // 5. Let R be O.[[IteratingRegExp]].
3151 TNode<Object> iterating_regexp =
3152 LoadObjectField(receiver, JSRegExpStringIterator::kIteratingRegExpOffset);
3153
3154 // TODO(jgruber): Verify that this is guaranteed.
3155 CSA_CHECK(this, TaggedIsNotSmi(iterating_regexp));
3156 CSA_CHECK(this, IsJSReceiver(CAST(iterating_regexp)));
3157
3158 // 6. Let S be O.[[IteratedString]].
3159 TNode<String> iterating_string = CAST(
3160 LoadObjectField(receiver, JSRegExpStringIterator::kIteratedStringOffset));
3161
3162 // 7. Let global be O.[[Global]].
3163 // See if_match.
3164
3165 // 8. Let fullUnicode be O.[[Unicode]].
3166 // See if_global.
3167
3168 // 9. Let match be ? RegExpExec(R, S).
3169 TVARIABLE(Object, var_match);
3170 TVARIABLE(BoolT, var_is_fast_regexp);
3171 {
3172 Label if_fast(this), if_slow(this, Label::kDeferred);
3173 BranchIfFastRegExp(context, iterating_regexp, &if_fast, &if_slow);
3174
3175 BIND(&if_fast);
3176 {
3177 TNode<RegExpMatchInfo> match_indices =
3178 RegExpPrototypeExecBodyWithoutResult(context, CAST(iterating_regexp),
3179 iterating_string, &if_no_match,
3180 true);
3181 var_match = ConstructNewResultFromMatchInfo(
3182 context, CAST(iterating_regexp), match_indices, iterating_string);
3183 var_is_fast_regexp = Int32TrueConstant();
3184 Goto(&if_match);
3185 }
3186
3187 BIND(&if_slow);
3188 {
3189 var_match = CAST(RegExpExec(context, iterating_regexp, iterating_string));
3190 var_is_fast_regexp = Int32FalseConstant();
3191 Branch(IsNull(var_match.value()), &if_no_match, &if_match);
3192 }
3193 }
3194
3195 // 10. If match is null, then
3196 BIND(&if_no_match);
3197 {
3198 // a. Set O.[[Done]] to true.
3199 SetDoneFlag(receiver, flags);
3200
3201 // b. Return ! CreateIterResultObject(undefined, true).
3202 Goto(&return_empty_done_result);
3203 }
3204 // 11. Else,
3205 BIND(&if_match);
3206 {
3207 Label if_global(this), if_not_global(this, Label::kDeferred),
3208 return_result(this);
3209
3210 // a. If global is true,
3211 Branch(HasGlobalFlag(flags), &if_global, &if_not_global);
3212 BIND(&if_global);
3213 {
3214 Label if_fast(this), if_slow(this, Label::kDeferred);
3215
3216 // ii. If matchStr is the empty string,
3217 Branch(var_is_fast_regexp.value(), &if_fast, &if_slow);
3218 BIND(&if_fast);
3219 {
3220 // i. Let matchStr be ? ToString(? Get(match, "0")).
3221 CSA_ASSERT_BRANCH(this, [&](Label* ok, Label* not_ok) {
3222 BranchIfFastRegExpResult(context, var_match.value(), ok, not_ok);
3223 });
3224 CSA_ASSERT(this,
3225 SmiNotEqual(LoadFastJSArrayLength(CAST(var_match.value())),
3226 SmiZero()));
3227 TNode<FixedArray> result_fixed_array =
3228 CAST(LoadElements(CAST(var_match.value())));
3229 TNode<String> match_str =
3230 CAST(LoadFixedArrayElement(result_fixed_array, 0));
3231
3232 // When iterating_regexp is fast, we assume it stays fast even after
3233 // accessing the first match from the RegExp result.
3234 CSA_ASSERT(this, IsFastRegExp(context, iterating_regexp));
3235 GotoIfNot(IsEmptyString(match_str), &return_result);
3236
3237 // 1. Let thisIndex be ? ToLength(? Get(R, "lastIndex")).
3238 TNode<Smi> this_index = CAST(FastLoadLastIndex(CAST(iterating_regexp)));
3239 CSA_ASSERT(this, TaggedIsSmi(this_index));
3240
3241 // 2. Let nextIndex be ! AdvanceStringIndex(S, thisIndex, fullUnicode).
3242 TNode<Smi> next_index = CAST(AdvanceStringIndex(
3243 iterating_string, this_index, HasUnicodeFlag(flags), true));
3244 CSA_ASSERT(this, TaggedIsSmi(next_index));
3245
3246 // 3. Perform ? Set(R, "lastIndex", nextIndex, true).
3247 FastStoreLastIndex(iterating_regexp, next_index);
3248
3249 // iii. Return ! CreateIterResultObject(match, false).
3250 Goto(&return_result);
3251 }
3252 BIND(&if_slow);
3253 {
3254 // i. Let matchStr be ? ToString(? Get(match, "0")).
3255 TNode<String> match_str = ToString_Inline(
3256 context, GetProperty(context, var_match.value(), SmiZero()));
3257
3258 GotoIfNot(IsEmptyString(match_str), &return_result);
3259
3260 // 1. Let thisIndex be ? ToLength(? Get(R, "lastIndex")).
3261 TNode<Object> last_index = SlowLoadLastIndex(context, iterating_regexp);
3262 TNode<Number> this_index = ToLength_Inline(context, last_index);
3263
3264 // 2. Let nextIndex be ! AdvanceStringIndex(S, thisIndex, fullUnicode).
3265 TNode<Object> next_index = CAST(AdvanceStringIndex(
3266 iterating_string, this_index, HasUnicodeFlag(flags), false));
3267
3268 // 3. Perform ? Set(R, "lastIndex", nextIndex, true).
3269 SlowStoreLastIndex(context, iterating_regexp, next_index);
3270
3271 // iii. Return ! CreateIterResultObject(match, false).
3272 Goto(&return_result);
3273 }
3274 }
3275 // b. Else,
3276 BIND(&if_not_global);
3277 {
3278 // i. Set O.[[Done]] to true.
3279 SetDoneFlag(receiver, flags);
3280
3281 // ii. Return ! CreateIterResultObject(match, false).
3282 Goto(&return_result);
3283 }
3284 BIND(&return_result);
3285 {
3286 Return(AllocateJSIteratorResult(context, var_match.value(),
3287 FalseConstant()));
3288 }
3289 }
3290 BIND(&return_empty_done_result);
3291 Return(
3292 AllocateJSIteratorResult(context, UndefinedConstant(), TrueConstant()));
3293 }
3294
3295 } // namespace internal
3296 } // namespace v8
3297