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