5 #include "src/builtins/builtins-regexp-gen.h" 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" 25 using TNode = compiler::TNode<T>;
27 TNode<Smi> RegExpBuiltinsAssembler::SmiZero() {
return SmiConstant(0); }
29 TNode<IntPtrT> RegExpBuiltinsAssembler::IntPtrZero() {
30 return IntPtrConstant(0);
36 TNode<JSRegExpResult> RegExpBuiltinsAssembler::AllocateRegExpResult(
37 TNode<Context> context, TNode<Smi> length, TNode<Smi> index,
38 TNode<String> input) {
40 TNode<Smi> max_length = SmiConstant(JSArray::kInitialMaxFastElementArray);
41 CSA_ASSERT(
this, SmiLessThanOrEqual(length, max_length));
47 TNode<IntPtrT> length_intptr = SmiUntag(length);
48 const ElementsKind elements_kind = PACKED_ELEMENTS;
50 TNode<IntPtrT> elements_size = GetFixedArrayAllocationSize(
51 length_intptr, elements_kind, INTPTR_PARAMETERS);
52 TNode<IntPtrT> total_size =
53 IntPtrAdd(elements_size, IntPtrConstant(JSRegExpResult::kSize));
55 static const int kRegExpResultOffset = 0;
56 static const int kElementsOffset =
57 kRegExpResultOffset + JSRegExpResult::kSize;
61 TNode<HeapObject> result = Allocate(total_size);
62 TNode<HeapObject> elements = InnerAllocate(result, kElementsOffset);
66 TNode<Context> native_context = LoadNativeContext(context);
67 TNode<Map> map = CAST(
68 LoadContextElement(native_context, Context::REGEXP_RESULT_MAP_INDEX));
69 StoreMapNoWriteBarrier(result, map);
71 StoreObjectFieldNoWriteBarrier(result, JSArray::kPropertiesOrHashOffset,
72 EmptyFixedArrayConstant());
73 StoreObjectFieldNoWriteBarrier(result, JSArray::kElementsOffset, elements);
74 StoreObjectFieldNoWriteBarrier(result, JSArray::kLengthOffset, length);
76 StoreObjectFieldNoWriteBarrier(result, JSRegExpResult::kIndexOffset, index);
77 StoreObjectFieldNoWriteBarrier(result, JSRegExpResult::kInputOffset, input);
78 StoreObjectFieldNoWriteBarrier(result, JSRegExpResult::kGroupsOffset,
83 DCHECK(!IsDoubleElementsKind(elements_kind));
84 const RootIndex map_index = RootIndex::kFixedArrayMap;
85 DCHECK(RootsTable::IsImmortalImmovable(map_index));
86 StoreMapNoWriteBarrier(elements, map_index);
87 StoreObjectFieldNoWriteBarrier(elements, FixedArray::kLengthOffset, length);
89 FillFixedArrayWithValue(elements_kind, elements, IntPtrZero(), length_intptr,
90 RootIndex::kUndefinedValue);
95 TNode<Object> RegExpBuiltinsAssembler::RegExpCreate(
96 TNode<Context> context, TNode<Context> native_context,
97 TNode<Object> maybe_string, TNode<String> flags) {
98 TNode<JSFunction> regexp_function =
99 CAST(LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX));
100 TNode<Map> initial_map = CAST(LoadObjectField(
101 regexp_function, JSFunction::kPrototypeOrInitialMapOffset));
102 return RegExpCreate(context, initial_map, maybe_string, flags);
105 TNode<Object> RegExpBuiltinsAssembler::RegExpCreate(TNode<Context> context,
106 TNode<Map> initial_map,
107 TNode<Object> maybe_string,
108 TNode<String> flags) {
109 TNode<String> pattern = Select<String>(
110 IsUndefined(maybe_string), [=] {
return EmptyStringConstant(); },
111 [=] {
return ToString_Inline(context, maybe_string); });
112 TNode<Object> regexp = CAST(AllocateJSObjectFromMap(initial_map));
113 return CallRuntime(Runtime::kRegExpInitializeAndCompile, context, regexp,
117 TNode<Object> RegExpBuiltinsAssembler::FastLoadLastIndex(
118 TNode<JSRegExp> regexp) {
120 static const int field_offset =
121 JSRegExp::kSize + JSRegExp::kLastIndexFieldIndex * kPointerSize;
122 return LoadObjectField(regexp, field_offset);
125 TNode<Object> RegExpBuiltinsAssembler::SlowLoadLastIndex(TNode<Context> context,
126 TNode<Object> regexp) {
127 return GetProperty(context, regexp, isolate()->factory()->lastIndex_string());
130 TNode<Object> RegExpBuiltinsAssembler::LoadLastIndex(TNode<Context> context,
131 TNode<Object> regexp,
133 return is_fastpath ? FastLoadLastIndex(CAST(regexp))
134 : SlowLoadLastIndex(context, regexp);
139 void RegExpBuiltinsAssembler::FastStoreLastIndex(Node* regexp, Node* value) {
141 static const int field_offset =
142 JSRegExp::kSize + JSRegExp::kLastIndexFieldIndex * kPointerSize;
143 StoreObjectField(regexp, field_offset, value);
146 void RegExpBuiltinsAssembler::SlowStoreLastIndex(Node* context, Node* regexp,
148 Node*
const name = HeapConstant(isolate()->factory()->lastIndex_string());
149 SetPropertyStrict(CAST(context), CAST(regexp), CAST(name), CAST(value));
152 void RegExpBuiltinsAssembler::StoreLastIndex(Node* context, Node* regexp,
153 Node* value,
bool is_fastpath) {
155 FastStoreLastIndex(regexp, value);
157 SlowStoreLastIndex(context, regexp, value);
161 TNode<JSRegExpResult> RegExpBuiltinsAssembler::ConstructNewResultFromMatchInfo(
162 TNode<Context> context, TNode<JSReceiver> maybe_regexp,
163 TNode<RegExpMatchInfo> match_info, TNode<String>
string) {
164 Label named_captures(
this), out(
this);
166 TNode<IntPtrT> num_indices = SmiUntag(CAST(LoadFixedArrayElement(
167 match_info, RegExpMatchInfo::kNumberOfCapturesIndex)));
168 TNode<Smi> num_results = SmiTag(WordShr(num_indices, 1));
169 TNode<Smi> start = CAST(
170 LoadFixedArrayElement(match_info, RegExpMatchInfo::kFirstCaptureIndex));
171 TNode<Smi> end = CAST(LoadFixedArrayElement(
172 match_info, RegExpMatchInfo::kFirstCaptureIndex + 1));
177 TNode<String> first =
178 CAST(CallBuiltin(Builtins::kSubString, context,
string, start, end));
180 TNode<JSRegExpResult> result =
181 AllocateRegExpResult(context, num_results, start,
string);
182 TNode<FixedArray> result_elements = CAST(LoadElements(result));
184 StoreFixedArrayElement(result_elements, 0, first, SKIP_WRITE_BARRIER);
187 GotoIf(SmiEqual(num_results, SmiConstant(1)), &out);
190 TNode<IntPtrT> limit = IntPtrAdd(
191 IntPtrConstant(RegExpMatchInfo::kFirstCaptureIndex), num_indices);
193 TVARIABLE(IntPtrT, var_from_cursor,
194 IntPtrConstant(RegExpMatchInfo::kFirstCaptureIndex + 2));
195 TVARIABLE(IntPtrT, var_to_cursor, IntPtrConstant(1));
197 Variable* vars[] = {&var_from_cursor, &var_to_cursor};
198 Label loop(
this, 2, vars);
203 TNode<IntPtrT> from_cursor = var_from_cursor.value();
204 TNode<IntPtrT> to_cursor = var_to_cursor.value();
205 TNode<Smi> start = CAST(LoadFixedArrayElement(match_info, from_cursor));
207 Label next_iter(
this);
208 GotoIf(SmiEqual(start, SmiConstant(-1)), &next_iter);
210 TNode<IntPtrT> from_cursor_plus1 =
211 IntPtrAdd(from_cursor, IntPtrConstant(1));
212 TNode<Smi> end = CAST(LoadFixedArrayElement(match_info, from_cursor_plus1));
214 TNode<String> capture =
215 CAST(CallBuiltin(Builtins::kSubString, context,
string, start, end));
216 StoreFixedArrayElement(result_elements, to_cursor, capture);
220 var_from_cursor = IntPtrAdd(from_cursor, IntPtrConstant(2));
221 var_to_cursor = IntPtrAdd(to_cursor, IntPtrConstant(1));
222 Branch(UintPtrLessThan(var_from_cursor.value(), limit), &loop,
226 BIND(&named_captures);
228 CSA_ASSERT(
this, SmiGreaterThan(num_results, SmiConstant(1)));
233 TNode<JSRegExp> regexp = CAST(maybe_regexp);
238 TNode<FixedArray> data =
239 CAST(LoadObjectField(regexp, JSRegExp::kDataOffset));
241 SmiEqual(CAST(LoadFixedArrayElement(data, JSRegExp::kTagIndex)),
242 SmiConstant(JSRegExp::IRREGEXP)));
246 TNode<Object> maybe_names =
247 LoadFixedArrayElement(data, JSRegExp::kIrregexpCaptureNameMapIndex);
248 GotoIf(WordEqual(maybe_names, SmiZero()), &out);
254 TNode<Context> native_context = LoadNativeContext(context);
255 TNode<Map> map = CAST(LoadContextElement(
256 native_context, Context::SLOW_OBJECT_WITH_NULL_PROTOTYPE_MAP));
257 TNode<NameDictionary> properties =
258 AllocateNameDictionary(NameDictionary::kInitialCapacity);
260 TNode<JSObject> group_object =
261 CAST(AllocateJSObjectFromMap(map, properties));
262 StoreObjectField(result, JSRegExpResult::kGroupsOffset, group_object);
266 TNode<FixedArray> names = CAST(maybe_names);
267 TNode<IntPtrT> names_length = LoadAndUntagFixedArrayBaseLength(names);
268 CSA_ASSERT(
this, IntPtrGreaterThan(names_length, IntPtrZero()));
270 TVARIABLE(IntPtrT, var_i, IntPtrZero());
272 Variable* vars[] = {&var_i};
273 const int vars_count =
sizeof(vars) /
sizeof(vars[0]);
274 Label loop(
this, vars_count, vars);
279 TNode<IntPtrT>
i = var_i.value();
280 TNode<IntPtrT> i_plus_1 = IntPtrAdd(
i, IntPtrConstant(1));
281 TNode<IntPtrT> i_plus_2 = IntPtrAdd(i_plus_1, IntPtrConstant(1));
283 TNode<String> name = CAST(LoadFixedArrayElement(names,
i));
284 TNode<Smi> index = CAST(LoadFixedArrayElement(names, i_plus_1));
285 TNode<HeapObject> capture =
286 CAST(LoadFixedArrayElement(result_elements, SmiUntag(index)));
291 CallRuntime(Runtime::kCreateDataProperty, context, group_object, name,
295 Branch(IntPtrGreaterThanOrEqual(var_i.value(), names_length), &out,
304 void RegExpBuiltinsAssembler::GetStringPointers(
305 Node*
const string_data, Node*
const offset, Node*
const last_index,
306 Node*
const string_length, String::Encoding encoding,
307 Variable* var_string_start, Variable* var_string_end) {
308 DCHECK_EQ(var_string_start->rep(), MachineType::PointerRepresentation());
309 DCHECK_EQ(var_string_end->rep(), MachineType::PointerRepresentation());
311 const ElementsKind kind = (encoding == String::ONE_BYTE_ENCODING)
315 Node*
const from_offset = ElementOffsetFromIndex(
316 IntPtrAdd(offset, last_index), kind, INTPTR_PARAMETERS);
317 var_string_start->Bind(IntPtrAdd(string_data, from_offset));
319 Node*
const to_offset = ElementOffsetFromIndex(
320 IntPtrAdd(offset, string_length), kind, INTPTR_PARAMETERS);
321 var_string_end->Bind(IntPtrAdd(string_data, to_offset));
324 TNode<HeapObject> RegExpBuiltinsAssembler::RegExpExecInternal(
325 TNode<Context> context, TNode<JSRegExp> regexp, TNode<String>
string,
326 TNode<Number> last_index, TNode<RegExpMatchInfo> match_info) {
330 #ifdef V8_INTERPRETED_REGEXP 331 return CAST(CallRuntime(Runtime::kRegExpExec, context, regexp,
string,
332 last_index, match_info));
333 #else // V8_INTERPRETED_REGEXP 334 ToDirectStringAssembler to_direct(state(),
string);
336 TVARIABLE(HeapObject, var_result);
337 Label out(
this), atom(
this), runtime(
this, Label::kDeferred);
340 TNode<ExternalReference> isolate_address =
341 ExternalConstant(ExternalReference::isolate_address(isolate()));
342 TNode<ExternalReference> regexp_stack_memory_address_address =
344 ExternalReference::address_of_regexp_stack_memory_address(isolate()));
345 TNode<ExternalReference> regexp_stack_memory_size_address = ExternalConstant(
346 ExternalReference::address_of_regexp_stack_memory_size(isolate()));
347 TNode<ExternalReference> static_offsets_vector_address = ExternalConstant(
348 ExternalReference::address_of_static_offsets_vector(isolate()));
355 Label if_failure(
this);
357 CSA_ASSERT(
this, IsNumberNormalized(last_index));
358 CSA_ASSERT(
this, IsNumberPositive(last_index));
359 GotoIf(TaggedIsNotSmi(last_index), &if_failure);
361 TNode<IntPtrT> int_string_length = LoadStringLengthAsWord(
string);
362 TNode<IntPtrT> int_last_index = SmiUntag(CAST(last_index));
364 GotoIf(UintPtrGreaterThan(int_last_index, int_string_length), &if_failure);
367 TNode<FixedArray> data = CAST(LoadObjectField(regexp, JSRegExp::kDataOffset));
371 Label next(
this), unreachable(
this, Label::kDeferred);
372 TNode<Int32T> tag = LoadAndUntagToWord32FixedArrayElement(
373 data, IntPtrConstant(JSRegExp::kTagIndex));
376 JSRegExp::IRREGEXP, JSRegExp::ATOM, JSRegExp::NOT_COMPILED,
378 Label* labels[] = {&next, &atom, &runtime};
380 STATIC_ASSERT(arraysize(values) == arraysize(labels));
381 Switch(tag, &unreachable, values, labels, arraysize(values));
391 TNode<Smi> capture_count =
392 CAST(LoadFixedArrayElement(data, JSRegExp::kIrregexpCaptureCountIndex));
394 const int kOffsetsSize = Isolate::kJSRegexpStaticOffsetsVectorSize;
395 STATIC_ASSERT(kOffsetsSize >= 2);
396 GotoIf(SmiAbove(capture_count, SmiConstant(kOffsetsSize / 2 - 1)),
403 TNode<IntPtrT> stack_size = UncheckedCast<IntPtrT>(
404 Load(MachineType::IntPtr(), regexp_stack_memory_size_address));
405 GotoIf(IntPtrEqual(stack_size, IntPtrZero()), &runtime);
410 to_direct.TryToDirect(&runtime);
415 TVARIABLE(RawPtrT, var_string_start);
416 TVARIABLE(RawPtrT, var_string_end);
417 TVARIABLE(Object, var_code);
420 TNode<RawPtrT> direct_string_data = to_direct.PointerToData(&runtime);
422 Label next(
this), if_isonebyte(
this), if_istwobyte(
this, Label::kDeferred);
423 Branch(IsOneByteStringInstanceType(to_direct.instance_type()),
424 &if_isonebyte, &if_istwobyte);
428 GetStringPointers(direct_string_data, to_direct.offset(), int_last_index,
429 int_string_length, String::ONE_BYTE_ENCODING,
430 &var_string_start, &var_string_end);
432 LoadFixedArrayElement(data, JSRegExp::kIrregexpLatin1CodeIndex);
438 GetStringPointers(direct_string_data, to_direct.offset(), int_last_index,
439 int_string_length, String::TWO_BYTE_ENCODING,
440 &var_string_start, &var_string_end);
441 var_code = LoadFixedArrayElement(data, JSRegExp::kIrregexpUC16CodeIndex);
454 GotoIfNot(TaggedIsSmi(var_code.value()), &next);
455 CSA_ASSERT(
this, SmiEqual(CAST(var_code.value()),
456 SmiConstant(JSRegExp::kUninitializedValue)));
462 GotoIf(TaggedIsSmi(var_code.value()), &runtime);
463 TNode<Code> code = CAST(var_code.value());
465 Label if_success(
this), if_exception(
this, Label::kDeferred);
467 IncrementCounter(isolate()->counters()->regexp_entry_native(), 1);
471 MachineType type_int32 = MachineType::Int32();
472 MachineType type_tagged = MachineType::AnyTagged();
473 MachineType type_ptr = MachineType::Pointer();
476 MachineType retval_type = type_int32;
479 MachineType arg0_type = type_tagged;
480 TNode<String> arg0 = string;
483 MachineType arg1_type = type_int32;
484 TNode<Int32T> arg1 = TruncateIntPtrToInt32(int_last_index);
487 MachineType arg2_type = type_ptr;
488 TNode<RawPtrT> arg2 = var_string_start.value();
491 MachineType arg3_type = type_ptr;
492 TNode<RawPtrT> arg3 = var_string_end.value();
495 MachineType arg4_type = type_ptr;
496 TNode<ExternalReference> arg4 = static_offsets_vector_address;
501 MachineType arg5_type = type_int32;
502 TNode<Int32T> arg5 = Int32Constant(0);
505 TNode<RawPtrT> stack_start = UncheckedCast<RawPtrT>(
506 Load(MachineType::Pointer(), regexp_stack_memory_address_address));
507 TNode<IntPtrT> stack_size = UncheckedCast<IntPtrT>(
508 Load(MachineType::IntPtr(), regexp_stack_memory_size_address));
509 TNode<RawPtrT> stack_end =
510 ReinterpretCast<RawPtrT>(IntPtrAdd(stack_start, stack_size));
512 MachineType arg6_type = type_ptr;
513 TNode<RawPtrT> arg6 = stack_end;
516 MachineType arg7_type = type_int32;
517 TNode<Int32T> arg7 = Int32Constant(1);
520 MachineType arg8_type = type_ptr;
521 TNode<ExternalReference> arg8 = isolate_address;
523 TNode<RawPtrT> code_entry = ReinterpretCast<RawPtrT>(
524 IntPtrAdd(BitcastTaggedToWord(code),
525 IntPtrConstant(Code::kHeaderSize - kHeapObjectTag)));
527 TNode<Int32T> result = UncheckedCast<Int32T>(CallCFunction9(
528 retval_type, arg0_type, arg1_type, arg2_type, arg3_type, arg4_type,
529 arg5_type, arg6_type, arg7_type, arg8_type, code_entry, arg0, arg1,
530 arg2, arg3, arg4, arg5, arg6, arg7, arg8));
535 TNode<IntPtrT> int_result = ChangeInt32ToIntPtr(result);
536 GotoIf(IntPtrEqual(int_result,
537 IntPtrConstant(NativeRegExpMacroAssembler::SUCCESS)),
539 GotoIf(IntPtrEqual(int_result,
540 IntPtrConstant(NativeRegExpMacroAssembler::FAILURE)),
542 GotoIf(IntPtrEqual(int_result,
543 IntPtrConstant(NativeRegExpMacroAssembler::EXCEPTION)),
547 IntPtrEqual(int_result,
548 IntPtrConstant(NativeRegExpMacroAssembler::RETRY)));
556 STATIC_ASSERT(FixedArray::kMaxLength < kMaxInt - FixedArray::kLengthOffset);
557 TNode<Smi> available_slots =
558 SmiSub(LoadFixedArrayBaseLength(match_info),
559 SmiConstant(RegExpMatchInfo::kLastMatchOverhead));
560 TNode<Smi> capture_count =
561 CAST(LoadFixedArrayElement(data, JSRegExp::kIrregexpCaptureCountIndex));
563 TNode<Smi> register_count =
564 SmiShl(SmiAdd(capture_count, SmiConstant(1)), 1);
565 GotoIf(SmiGreaterThan(register_count, available_slots), &runtime);
569 StoreFixedArrayElement(match_info, RegExpMatchInfo::kNumberOfCapturesIndex,
570 register_count, SKIP_WRITE_BARRIER);
571 StoreFixedArrayElement(match_info, RegExpMatchInfo::kLastSubjectIndex,
573 StoreFixedArrayElement(match_info, RegExpMatchInfo::kLastInputIndex,
578 TNode<IntPtrT> limit_offset = ElementOffsetFromIndex(
579 register_count, INT32_ELEMENTS, SMI_PARAMETERS, 0);
581 TNode<IntPtrT> to_offset = ElementOffsetFromIndex(
582 IntPtrConstant(RegExpMatchInfo::kFirstCaptureIndex), PACKED_ELEMENTS,
583 INTPTR_PARAMETERS, RegExpMatchInfo::kHeaderSize - kHeapObjectTag);
584 TVARIABLE(IntPtrT, var_to_offset, to_offset);
586 VariableList vars({&var_to_offset}, zone());
588 vars, IntPtrZero(), limit_offset,
589 [=, &var_to_offset](Node* offset) {
590 TNode<Int32T> value = UncheckedCast<Int32T>(Load(
591 MachineType::Int32(), static_offsets_vector_address, offset));
592 TNode<Smi> smi_value = SmiFromInt32(value);
593 StoreNoWriteBarrier(MachineRepresentation::kTagged, match_info,
594 var_to_offset.value(), smi_value);
595 Increment(&var_to_offset, kPointerSize);
597 kInt32Size, INTPTR_PARAMETERS, IndexAdvanceMode::kPost);
600 var_result = match_info;
606 var_result = NullConstant();
614 TNode<ExternalReference> pending_exception_address =
615 ExternalConstant(ExternalReference::Create(
616 IsolateAddressId::kPendingExceptionAddress, isolate()));
617 CSA_ASSERT(
this, IsTheHole(Load(MachineType::AnyTagged(),
618 pending_exception_address)));
620 CallRuntime(Runtime::kThrowStackOverflow, context);
626 var_result = CAST(CallRuntime(Runtime::kRegExpExec, context, regexp,
string,
627 last_index, match_info));
635 var_result = CAST(CallBuiltin(Builtins::kRegExpExecAtom, context, regexp,
636 string, last_index, match_info));
641 return var_result.value();
642 #endif // V8_INTERPRETED_REGEXP 651 TNode<RegExpMatchInfo>
652 RegExpBuiltinsAssembler::RegExpPrototypeExecBodyWithoutResult(
653 TNode<Context> context, TNode<JSReceiver> maybe_regexp,
654 TNode<String>
string, Label* if_didnotmatch,
const bool is_fastpath) {
656 ThrowIfNotInstanceType(context, maybe_regexp, JS_REGEXP_TYPE,
657 "RegExp.prototype.exec");
660 TNode<JSRegExp> regexp = CAST(maybe_regexp);
662 TVARIABLE(HeapObject, var_result);
666 TVARIABLE(Number, var_lastindex);
668 TNode<Object> regexp_lastindex =
669 LoadLastIndex(context, regexp, is_fastpath);
673 CSA_ASSERT(
this, TaggedIsPositiveSmi(regexp_lastindex));
674 var_lastindex = CAST(regexp_lastindex);
677 Label call_tolength(
this, Label::kDeferred), is_smi(
this), next(
this);
678 Branch(TaggedIsPositiveSmi(regexp_lastindex), &is_smi, &call_tolength);
680 BIND(&call_tolength);
681 var_lastindex = ToLength_Inline(context, regexp_lastindex);
685 var_lastindex = CAST(regexp_lastindex);
694 TNode<Smi> flags = CAST(LoadObjectField(regexp, JSRegExp::kFlagsOffset));
695 TNode<IntPtrT> is_global_or_sticky = WordAnd(
696 SmiUntag(flags), IntPtrConstant(JSRegExp::kGlobal | JSRegExp::kSticky));
697 TNode<BoolT> should_update_last_index =
698 WordNotEqual(is_global_or_sticky, IntPtrZero());
701 Label run_exec(
this);
703 Label if_doupdate(
this), if_dontupdate(
this);
704 Branch(should_update_last_index, &if_doupdate, &if_dontupdate);
708 Label if_isoob(
this, Label::kDeferred);
709 GotoIfNot(TaggedIsSmi(var_lastindex.value()), &if_isoob);
710 TNode<Smi> string_length = LoadStringLengthAsSmi(
string);
711 GotoIfNot(SmiLessThanOrEqual(CAST(var_lastindex.value()), string_length),
717 StoreLastIndex(context, regexp, SmiZero(), is_fastpath);
718 Goto(if_didnotmatch);
722 BIND(&if_dontupdate);
724 var_lastindex = SmiZero();
729 TNode<HeapObject> match_indices;
730 Label successful_match(
this);
734 TNode<Context> native_context = LoadNativeContext(context);
735 TNode<RegExpMatchInfo> last_match_info = CAST(LoadContextElement(
736 native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX));
739 match_indices = RegExpExecInternal(context, regexp,
string,
740 var_lastindex.value(), last_match_info);
741 var_result = match_indices;
745 GotoIfNot(IsNull(match_indices), &successful_match);
747 GotoIfNot(should_update_last_index, if_didnotmatch);
749 StoreLastIndex(context, regexp, SmiZero(), is_fastpath);
750 Goto(if_didnotmatch);
753 BIND(&successful_match);
755 GotoIfNot(should_update_last_index, &out);
758 TNode<Number> new_lastindex = CAST(LoadFixedArrayElement(
759 CAST(match_indices), RegExpMatchInfo::kFirstCaptureIndex + 1));
761 StoreLastIndex(context, regexp, new_lastindex, is_fastpath);
766 return CAST(var_result.value());
771 TNode<HeapObject> RegExpBuiltinsAssembler::RegExpPrototypeExecBody(
772 TNode<Context> context, TNode<JSReceiver> maybe_regexp,
773 TNode<String>
string,
const bool is_fastpath) {
774 TVARIABLE(HeapObject, var_result);
776 Label if_didnotmatch(
this), out(
this);
777 TNode<RegExpMatchInfo> match_indices = RegExpPrototypeExecBodyWithoutResult(
778 context, maybe_regexp,
string, &if_didnotmatch, is_fastpath);
782 var_result = ConstructNewResultFromMatchInfo(context, maybe_regexp,
783 match_indices,
string);
787 BIND(&if_didnotmatch);
789 var_result = NullConstant();
794 return var_result.value();
797 Node* RegExpBuiltinsAssembler::ThrowIfNotJSReceiver(
798 Node* context, Node* maybe_receiver, MessageTemplate msg_template,
799 char const* method_name) {
800 Label out(
this), throw_exception(
this, Label::kDeferred);
801 VARIABLE(var_value_map, MachineRepresentation::kTagged);
803 GotoIf(TaggedIsSmi(maybe_receiver), &throw_exception);
806 var_value_map.Bind(LoadMap(maybe_receiver));
807 Node*
const value_instance_type = LoadMapInstanceType(var_value_map.value());
809 Branch(IsJSReceiverInstanceType(value_instance_type), &out, &throw_exception);
812 BIND(&throw_exception);
814 Node*
const value_str =
815 CallBuiltin(Builtins::kToString, context, maybe_receiver);
816 ThrowTypeError(context, msg_template, StringConstant(method_name),
821 return var_value_map.value();
824 Node* RegExpBuiltinsAssembler::IsFastRegExpNoPrototype(Node*
const context,
828 VARIABLE(var_result, MachineRepresentation::kWord32);
830 #ifdef V8_ENABLE_FORCE_SLOW_PATH 831 var_result.Bind(Int32Constant(0));
832 GotoIfForceSlowPath(&out);
835 Node*
const native_context = LoadNativeContext(context);
836 Node*
const regexp_fun =
837 LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX);
838 Node*
const initial_map =
839 LoadObjectField(regexp_fun, JSFunction::kPrototypeOrInitialMapOffset);
840 Node*
const has_initialmap = WordEqual(map, initial_map);
842 var_result.Bind(has_initialmap);
843 GotoIfNot(has_initialmap, &out);
847 Node*
const last_index = FastLoadLastIndex(CAST(
object));
848 var_result.Bind(TaggedIsPositiveSmi(last_index));
852 return var_result.value();
857 TNode<BoolT> RegExpBuiltinsAssembler::IsFastRegExpWithOriginalExec(
858 TNode<Context> context, TNode<JSRegExp>
object) {
859 CSA_ASSERT(
this, TaggedIsNotSmi(
object));
861 Label check_last_index(
this);
862 TVARIABLE(BoolT, var_result);
864 #ifdef V8_ENABLE_FORCE_SLOW_PATH 865 var_result = BoolConstant(
false);
866 GotoIfForceSlowPath(&out);
869 TNode<BoolT> is_regexp = HasInstanceType(
object, JS_REGEXP_TYPE);
871 var_result = is_regexp;
872 GotoIfNot(is_regexp, &out);
874 TNode<Context> native_context = LoadNativeContext(context);
875 TNode<Object> original_exec =
876 LoadContextElement(native_context, Context::REGEXP_EXEC_FUNCTION_INDEX);
878 TNode<Object> regexp_exec =
879 GetProperty(context,
object, isolate()->factory()->exec_string());
881 TNode<BoolT> has_initialexec = WordEqual(regexp_exec, original_exec);
882 var_result = has_initialexec;
883 GotoIf(has_initialexec, &check_last_index);
884 TNode<BoolT> is_undefined = IsUndefined(regexp_exec);
885 var_result = is_undefined;
886 GotoIfNot(is_undefined, &out);
887 Goto(&check_last_index);
889 BIND(&check_last_index);
892 TNode<Object> last_index = FastLoadLastIndex(
object);
893 var_result = TaggedIsPositiveSmi(last_index);
897 return var_result.value();
900 Node* RegExpBuiltinsAssembler::IsFastRegExpNoPrototype(Node*
const context,
901 Node*
const object) {
902 CSA_ASSERT(
this, TaggedIsNotSmi(
object));
903 return IsFastRegExpNoPrototype(context,
object, LoadMap(
object));
910 void RegExpBuiltinsAssembler::BranchIfFastRegExp(
911 Node*
const context, Node*
const object, Node*
const map,
912 base::Optional<DescriptorIndexAndName> additional_property_to_check,
913 Label*
const if_isunmodified, Label*
const if_ismodified) {
914 CSA_ASSERT(
this, WordEqual(LoadMap(
object), map));
916 GotoIfForceSlowPath(if_ismodified);
920 GotoIf(IsRegExpSpeciesProtectorCellInvalid(), if_ismodified);
922 Node*
const native_context = LoadNativeContext(context);
923 Node*
const regexp_fun =
924 LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX);
925 Node*
const initial_map =
926 LoadObjectField(regexp_fun, JSFunction::kPrototypeOrInitialMapOffset);
927 Node*
const has_initialmap = WordEqual(map, initial_map);
929 GotoIfNot(has_initialmap, if_ismodified);
931 Node*
const initial_proto_initial_map =
932 LoadContextElement(native_context, Context::REGEXP_PROTOTYPE_MAP_INDEX);
934 DescriptorIndexAndName properties_to_check[2];
935 int property_count = 0;
936 properties_to_check[property_count++] = DescriptorIndexAndName{
937 JSRegExp::kExecFunctionDescriptorIndex, RootIndex::kexec_string};
938 if (additional_property_to_check) {
939 properties_to_check[property_count++] = *additional_property_to_check;
942 GotoIfInitialPrototypePropertiesModified(
943 CAST(map), CAST(initial_proto_initial_map),
944 Vector<DescriptorIndexAndName>(properties_to_check, property_count),
949 Node*
const last_index = FastLoadLastIndex(CAST(
object));
950 Branch(TaggedIsPositiveSmi(last_index), if_isunmodified, if_ismodified);
953 void RegExpBuiltinsAssembler::BranchIfFastRegExp(Node*
const context,
955 Label*
const if_isunmodified,
956 Label*
const if_ismodified) {
957 CSA_ASSERT(
this, TaggedIsNotSmi(
object));
958 BranchIfFastRegExp(context,
object, LoadMap(
object), base::nullopt,
959 if_isunmodified, if_ismodified);
962 TNode<BoolT> RegExpBuiltinsAssembler::IsFastRegExp(SloppyTNode<Context> context,
963 SloppyTNode<Object>
object) {
964 Label yup(
this), nope(
this), out(
this);
965 TVARIABLE(BoolT, var_result);
967 BranchIfFastRegExp(context,
object, &yup, &nope);
970 var_result = Int32TrueConstant();
974 var_result = Int32FalseConstant();
978 return var_result.value();
981 void RegExpBuiltinsAssembler::BranchIfFastRegExpResult(Node*
const context,
983 Label* if_isunmodified,
984 Label* if_ismodified) {
986 Node*
const map = LoadReceiverMap(
object);
988 Node*
const native_context = LoadNativeContext(context);
989 Node*
const initial_regexp_result_map =
990 LoadContextElement(native_context, Context::REGEXP_RESULT_MAP_INDEX);
992 Branch(WordEqual(map, initial_regexp_result_map), if_isunmodified,
997 TF_BUILTIN(RegExpPrototypeExecSlow, RegExpBuiltinsAssembler) {
998 TNode<JSRegExp> regexp = CAST(Parameter(Descriptor::kReceiver));
999 TNode<String>
string = CAST(Parameter(Descriptor::kString));
1000 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1002 Return(RegExpPrototypeExecBody(context, regexp,
string,
false));
1008 TF_BUILTIN(RegExpExecAtom, RegExpBuiltinsAssembler) {
1009 TNode<JSRegExp> regexp = CAST(Parameter(Descriptor::kRegExp));
1010 TNode<String> subject_string = CAST(Parameter(Descriptor::kString));
1011 TNode<Smi> last_index = CAST(Parameter(Descriptor::kLastIndex));
1012 TNode<FixedArray> match_info = CAST(Parameter(Descriptor::kMatchInfo));
1013 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1015 CSA_ASSERT(
this, TaggedIsPositiveSmi(last_index));
1017 TNode<FixedArray> data = CAST(LoadObjectField(regexp, JSRegExp::kDataOffset));
1019 SmiEqual(CAST(LoadFixedArrayElement(data, JSRegExp::kTagIndex)),
1020 SmiConstant(JSRegExp::ATOM)));
1024 UintPtrLessThanOrEqual(SmiUntag(last_index),
1025 LoadStringLengthAsWord(subject_string)));
1027 Node*
const needle_string =
1028 LoadFixedArrayElement(data, JSRegExp::kAtomPatternIndex);
1029 CSA_ASSERT(
this, IsString(needle_string));
1031 TNode<Smi>
const match_from =
1032 CAST(CallBuiltin(Builtins::kStringIndexOf, context, subject_string,
1033 needle_string, last_index));
1035 Label if_failure(
this), if_success(
this);
1036 Branch(SmiEqual(match_from, SmiConstant(-1)), &if_failure, &if_success);
1040 CSA_ASSERT(
this, TaggedIsPositiveSmi(match_from));
1041 CSA_ASSERT(
this, UintPtrLessThan(SmiUntag(match_from),
1042 LoadStringLengthAsWord(subject_string)));
1044 const int kNumRegisters = 2;
1045 STATIC_ASSERT(RegExpMatchInfo::kInitialCaptureIndices >= kNumRegisters);
1047 TNode<Smi>
const match_to =
1048 SmiAdd(match_from, LoadStringLengthAsSmi(needle_string));
1050 StoreFixedArrayElement(match_info, RegExpMatchInfo::kNumberOfCapturesIndex,
1051 SmiConstant(kNumRegisters), SKIP_WRITE_BARRIER);
1052 StoreFixedArrayElement(match_info, RegExpMatchInfo::kLastSubjectIndex,
1054 StoreFixedArrayElement(match_info, RegExpMatchInfo::kLastInputIndex,
1056 StoreFixedArrayElement(match_info, RegExpMatchInfo::kFirstCaptureIndex,
1057 match_from, SKIP_WRITE_BARRIER);
1058 StoreFixedArrayElement(match_info, RegExpMatchInfo::kFirstCaptureIndex + 1,
1059 match_to, SKIP_WRITE_BARRIER);
1065 Return(NullConstant());
1068 TF_BUILTIN(RegExpExecInternal, RegExpBuiltinsAssembler) {
1069 TNode<JSRegExp> regexp = CAST(Parameter(Descriptor::kRegExp));
1070 TNode<String>
string = CAST(Parameter(Descriptor::kString));
1071 TNode<Number> last_index = CAST(Parameter(Descriptor::kLastIndex));
1072 TNode<RegExpMatchInfo> match_info = CAST(Parameter(Descriptor::kMatchInfo));
1073 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1075 CSA_ASSERT(
this, IsNumberNormalized(last_index));
1076 CSA_ASSERT(
this, IsNumberPositive(last_index));
1078 Return(RegExpExecInternal(context, regexp,
string, last_index, match_info));
1083 TF_BUILTIN(RegExpPrototypeExec, RegExpBuiltinsAssembler) {
1084 TNode<Object> maybe_receiver = CAST(Parameter(Descriptor::kReceiver));
1085 TNode<Object> maybe_string = CAST(Parameter(Descriptor::kString));
1086 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1089 ThrowIfNotInstanceType(context, maybe_receiver, JS_REGEXP_TYPE,
1090 "RegExp.prototype.exec");
1091 TNode<JSRegExp> receiver = CAST(maybe_receiver);
1094 TNode<String>
string = ToString_Inline(context, maybe_string);
1096 Label if_isfastpath(
this), if_isslowpath(
this);
1097 Branch(IsFastRegExpNoPrototype(context, receiver), &if_isfastpath,
1100 BIND(&if_isfastpath);
1101 Return(RegExpPrototypeExecBody(context, receiver,
string,
true));
1103 BIND(&if_isslowpath);
1104 Return(CallBuiltin(Builtins::kRegExpPrototypeExecSlow, context, receiver,
1108 Node* RegExpBuiltinsAssembler::FlagsGetter(Node*
const context,
1111 Isolate* isolate = this->isolate();
1113 TNode<IntPtrT>
const int_one = IntPtrConstant(1);
1114 TVARIABLE(Uint32T, var_length, Uint32Constant(0));
1115 TVARIABLE(IntPtrT, var_flags);
1122 CSA_ASSERT(
this, IsJSRegExp(regexp));
1123 Node*
const flags_smi = LoadObjectField(regexp, JSRegExp::kFlagsOffset);
1124 var_flags = SmiUntag(flags_smi);
1126 #define CASE_FOR_FLAG(FLAG) \ 1129 GotoIfNot(IsSetWord(var_flags.value(), FLAG), &next); \ 1130 var_length = Uint32Add(var_length.value(), Uint32Constant(1)); \ 1135 CASE_FOR_FLAG(JSRegExp::kGlobal);
1136 CASE_FOR_FLAG(JSRegExp::kIgnoreCase);
1137 CASE_FOR_FLAG(JSRegExp::kMultiline);
1138 CASE_FOR_FLAG(JSRegExp::kDotAll);
1139 CASE_FOR_FLAG(JSRegExp::kUnicode);
1140 CASE_FOR_FLAG(JSRegExp::kSticky);
1141 #undef CASE_FOR_FLAG 1143 DCHECK(!is_fastpath);
1146 var_flags = IntPtrZero();
1148 #define CASE_FOR_FLAG(NAME, FLAG) \ 1151 Node* const flag = GetProperty( \ 1152 context, regexp, isolate->factory()->InternalizeUtf8String(NAME)); \ 1153 Label if_isflagset(this); \ 1154 BranchIfToBooleanIsTrue(flag, &if_isflagset, &next); \ 1155 BIND(&if_isflagset); \ 1156 var_length = Uint32Add(var_length.value(), Uint32Constant(1)); \ 1157 var_flags = Signed(WordOr(var_flags.value(), IntPtrConstant(FLAG))); \ 1162 CASE_FOR_FLAG(
"global", JSRegExp::kGlobal);
1163 CASE_FOR_FLAG(
"ignoreCase", JSRegExp::kIgnoreCase);
1164 CASE_FOR_FLAG(
"multiline", JSRegExp::kMultiline);
1165 CASE_FOR_FLAG(
"dotAll", JSRegExp::kDotAll);
1166 CASE_FOR_FLAG(
"unicode", JSRegExp::kUnicode);
1167 CASE_FOR_FLAG(
"sticky", JSRegExp::kSticky);
1168 #undef CASE_FOR_FLAG 1175 Node*
const result = AllocateSeqOneByteString(context, var_length.value());
1177 VARIABLE(var_offset, MachineType::PointerRepresentation(),
1178 IntPtrConstant(SeqOneByteString::kHeaderSize - kHeapObjectTag));
1180 #define CASE_FOR_FLAG(FLAG, CHAR) \ 1183 GotoIfNot(IsSetWord(var_flags.value(), FLAG), &next); \ 1184 Node* const value = Int32Constant(CHAR); \ 1185 StoreNoWriteBarrier(MachineRepresentation::kWord8, result, \ 1186 var_offset.value(), value); \ 1187 var_offset.Bind(IntPtrAdd(var_offset.value(), int_one)); \ 1192 CASE_FOR_FLAG(JSRegExp::kGlobal,
'g');
1193 CASE_FOR_FLAG(JSRegExp::kIgnoreCase,
'i');
1194 CASE_FOR_FLAG(JSRegExp::kMultiline,
'm');
1195 CASE_FOR_FLAG(JSRegExp::kDotAll,
's');
1196 CASE_FOR_FLAG(JSRegExp::kUnicode,
'u');
1197 CASE_FOR_FLAG(JSRegExp::kSticky,
'y');
1198 #undef CASE_FOR_FLAG 1205 Node* RegExpBuiltinsAssembler::IsRegExp(Node*
const context,
1206 Node*
const maybe_receiver) {
1207 Label out(
this), if_isregexp(
this);
1209 VARIABLE(var_result, MachineRepresentation::kWord32, Int32Constant(0));
1211 GotoIf(TaggedIsSmi(maybe_receiver), &out);
1212 GotoIfNot(IsJSReceiver(maybe_receiver), &out);
1214 Node*
const receiver = maybe_receiver;
1219 GetProperty(context, receiver, isolate()->factory()->match_symbol());
1221 Label match_isundefined(
this), match_isnotundefined(
this);
1222 Branch(IsUndefined(value), &match_isundefined, &match_isnotundefined);
1224 BIND(&match_isundefined);
1225 Branch(IsJSRegExp(receiver), &if_isregexp, &out);
1227 BIND(&match_isnotundefined);
1228 BranchIfToBooleanIsTrue(value, &if_isregexp, &out);
1232 var_result.Bind(Int32Constant(1));
1236 return var_result.value();
1241 Node* RegExpBuiltinsAssembler::RegExpInitialize(Node*
const context,
1243 Node*
const maybe_pattern,
1244 Node*
const maybe_flags) {
1245 CSA_ASSERT(
this, IsJSRegExp(regexp));
1248 TNode<Object>
const pattern = Select<Object>(
1249 IsUndefined(maybe_pattern), [=] {
return EmptyStringConstant(); },
1250 [=] {
return ToString_Inline(context, maybe_pattern); });
1253 TNode<Object>
const flags = Select<Object>(
1254 IsUndefined(maybe_flags), [=] {
return EmptyStringConstant(); },
1255 [=] {
return ToString_Inline(context, maybe_flags); });
1259 return CallRuntime(Runtime::kRegExpInitializeAndCompile, context, regexp,
1264 TF_BUILTIN(RegExpPrototypeFlagsGetter, RegExpBuiltinsAssembler) {
1265 TNode<Object> maybe_receiver = CAST(Parameter(Descriptor::kReceiver));
1266 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1268 TNode<Map> map = CAST(ThrowIfNotJSReceiver(context, maybe_receiver,
1269 MessageTemplate::kRegExpNonObject,
1270 "RegExp.prototype.flags"));
1271 TNode<JSReceiver> receiver = CAST(maybe_receiver);
1273 Label if_isfastpath(
this), if_isslowpath(
this, Label::kDeferred);
1274 BranchIfFastRegExp(context, receiver, map, base::nullopt, &if_isfastpath,
1277 BIND(&if_isfastpath);
1278 Return(FlagsGetter(context, receiver,
true));
1280 BIND(&if_isslowpath);
1281 Return(FlagsGetter(context, receiver,
false));
1286 TF_BUILTIN(RegExpConstructor, RegExpBuiltinsAssembler) {
1287 TNode<Object> pattern = CAST(Parameter(Descriptor::kPattern));
1288 TNode<Object> flags = CAST(Parameter(Descriptor::kFlags));
1289 TNode<Object> new_target = CAST(Parameter(Descriptor::kJSNewTarget));
1290 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1292 Isolate* isolate = this->isolate();
1294 VARIABLE(var_flags, MachineRepresentation::kTagged, flags);
1295 VARIABLE(var_pattern, MachineRepresentation::kTagged, pattern);
1296 VARIABLE(var_new_target, MachineRepresentation::kTagged, new_target);
1298 Node*
const native_context = LoadNativeContext(context);
1299 Node*
const regexp_function =
1300 LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX);
1302 Node*
const pattern_is_regexp = IsRegExp(context, pattern);
1307 GotoIfNot(IsUndefined(new_target), &next);
1308 var_new_target.Bind(regexp_function);
1310 GotoIfNot(pattern_is_regexp, &next);
1311 GotoIfNot(IsUndefined(flags), &next);
1314 GetProperty(context, pattern, isolate->factory()->constructor_string());
1316 GotoIfNot(WordEqual(value, regexp_function), &next);
1323 Label next(
this), if_patternisfastregexp(
this),
1324 if_patternisslowregexp(
this);
1325 GotoIf(TaggedIsSmi(pattern), &next);
1327 GotoIf(IsJSRegExp(CAST(pattern)), &if_patternisfastregexp);
1329 Branch(pattern_is_regexp, &if_patternisslowregexp, &next);
1331 BIND(&if_patternisfastregexp);
1333 Node*
const source =
1334 LoadObjectField(CAST(pattern), JSRegExp::kSourceOffset);
1335 var_pattern.Bind(source);
1338 Label inner_next(
this);
1339 GotoIfNot(IsUndefined(flags), &inner_next);
1341 Node*
const value = FlagsGetter(context, pattern,
true);
1342 var_flags.Bind(value);
1351 BIND(&if_patternisslowregexp);
1355 GetProperty(context, pattern, isolate->factory()->source_string());
1356 var_pattern.Bind(value);
1360 Label inner_next(
this);
1361 GotoIfNot(IsUndefined(flags), &inner_next);
1364 GetProperty(context, pattern, isolate->factory()->flags_string());
1365 var_flags.Bind(value);
1379 VARIABLE(var_regexp, MachineRepresentation::kTagged);
1381 Label allocate_jsregexp(
this), allocate_generic(
this, Label::kDeferred),
1383 Branch(WordEqual(var_new_target.value(), regexp_function),
1384 &allocate_jsregexp, &allocate_generic);
1386 BIND(&allocate_jsregexp);
1388 Node*
const initial_map = LoadObjectField(
1389 regexp_function, JSFunction::kPrototypeOrInitialMapOffset);
1390 Node*
const regexp = AllocateJSObjectFromMap(initial_map);
1391 var_regexp.Bind(regexp);
1395 BIND(&allocate_generic);
1397 ConstructorBuiltinsAssembler constructor_assembler(this->state());
1398 Node*
const regexp = constructor_assembler.EmitFastNewObject(
1399 context, regexp_function, var_new_target.value());
1400 var_regexp.Bind(regexp);
1407 Node*
const result = RegExpInitialize(context, var_regexp.value(),
1408 var_pattern.value(), var_flags.value());
1414 TF_BUILTIN(RegExpPrototypeCompile, RegExpBuiltinsAssembler) {
1415 TNode<Object> maybe_receiver = CAST(Parameter(Descriptor::kReceiver));
1416 TNode<Object> maybe_pattern = CAST(Parameter(Descriptor::kPattern));
1417 TNode<Object> maybe_flags = CAST(Parameter(Descriptor::kFlags));
1418 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1420 ThrowIfNotInstanceType(context, maybe_receiver, JS_REGEXP_TYPE,
1421 "RegExp.prototype.compile");
1422 Node*
const receiver = maybe_receiver;
1424 VARIABLE(var_flags, MachineRepresentation::kTagged, maybe_flags);
1425 VARIABLE(var_pattern, MachineRepresentation::kTagged, maybe_pattern);
1431 GotoIf(TaggedIsSmi(maybe_pattern), &next);
1432 GotoIfNot(IsJSRegExp(CAST(maybe_pattern)), &next);
1434 Node*
const pattern = maybe_pattern;
1439 GotoIf(IsUndefined(maybe_flags), &next);
1441 ThrowTypeError(context, MessageTemplate::kRegExpFlags);
1446 Node*
const new_flags = FlagsGetter(context, pattern,
true);
1447 Node*
const new_pattern = LoadObjectField(pattern, JSRegExp::kSourceOffset);
1449 var_flags.Bind(new_flags);
1450 var_pattern.Bind(new_pattern);
1456 Node*
const result = RegExpInitialize(context, receiver, var_pattern.value(),
1463 TF_BUILTIN(RegExpPrototypeSourceGetter, RegExpBuiltinsAssembler) {
1464 TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
1465 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1468 Label if_isjsregexp(
this), if_isnotjsregexp(
this, Label::kDeferred);
1470 GotoIf(TaggedIsSmi(receiver), &if_isnotjsregexp);
1471 Branch(IsJSRegExp(CAST(receiver)), &if_isjsregexp, &if_isnotjsregexp);
1473 BIND(&if_isjsregexp);
1474 Return(LoadObjectField(CAST(receiver), JSRegExp::kSourceOffset));
1476 BIND(&if_isnotjsregexp);
1478 Isolate* isolate = this->isolate();
1479 Node*
const native_context = LoadNativeContext(context);
1480 Node*
const regexp_fun =
1481 LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX);
1482 Node*
const initial_map =
1483 LoadObjectField(regexp_fun, JSFunction::kPrototypeOrInitialMapOffset);
1484 Node*
const initial_prototype = LoadMapPrototype(initial_map);
1486 Label if_isprototype(
this), if_isnotprototype(
this);
1487 Branch(WordEqual(receiver, initial_prototype), &if_isprototype,
1488 &if_isnotprototype);
1490 BIND(&if_isprototype);
1492 const int counter = v8::Isolate::kRegExpPrototypeSourceGetter;
1493 Node*
const counter_smi = SmiConstant(counter);
1494 CallRuntime(Runtime::kIncrementUseCounter, context, counter_smi);
1496 Node*
const result =
1497 HeapConstant(isolate->factory()->NewStringFromAsciiChecked(
"(?:)"));
1501 BIND(&if_isnotprototype);
1503 ThrowTypeError(context, MessageTemplate::kRegExpNonRegExp,
1504 "RegExp.prototype.source");
1510 TNode<Int32T> RegExpBuiltinsAssembler::FastFlagGetter(TNode<JSRegExp> regexp,
1511 JSRegExp::Flag flag) {
1512 TNode<Smi> flags = CAST(LoadObjectField(regexp, JSRegExp::kFlagsOffset));
1513 TNode<Smi> mask = SmiConstant(flag);
1514 return SmiToInt32(SmiShr(SmiAnd(flags, mask), JSRegExp::FlagShiftBits(flag)));
1518 TNode<Int32T> RegExpBuiltinsAssembler::SlowFlagGetter(TNode<Context> context,
1519 TNode<Object> regexp,
1520 JSRegExp::Flag flag) {
1522 TVARIABLE(Int32T, var_result);
1524 Handle<String> name;
1526 case JSRegExp::kGlobal:
1527 name = isolate()->factory()->global_string();
1529 case JSRegExp::kIgnoreCase:
1530 name = isolate()->factory()->ignoreCase_string();
1532 case JSRegExp::kMultiline:
1533 name = isolate()->factory()->multiline_string();
1535 case JSRegExp::kDotAll:
1538 case JSRegExp::kSticky:
1539 name = isolate()->factory()->sticky_string();
1541 case JSRegExp::kUnicode:
1542 name = isolate()->factory()->unicode_string();
1548 TNode<Object> value = GetProperty(context, regexp, name);
1550 Label if_true(
this), if_false(
this);
1551 BranchIfToBooleanIsTrue(value, &if_true, &if_false);
1554 var_result = Int32Constant(1);
1558 var_result = Int32Constant(0);
1562 return var_result.value();
1565 TNode<Int32T> RegExpBuiltinsAssembler::FlagGetter(TNode<Context> context,
1566 TNode<Object> regexp,
1567 JSRegExp::Flag flag,
1569 return is_fastpath ? FastFlagGetter(CAST(regexp), flag)
1570 : SlowFlagGetter(context, regexp, flag);
1573 void RegExpBuiltinsAssembler::FlagGetter(Node* context, Node* receiver,
1574 JSRegExp::Flag flag,
int counter,
1575 const char* method_name) {
1577 Label if_isunmodifiedjsregexp(
this),
1578 if_isnotunmodifiedjsregexp(
this, Label::kDeferred);
1580 GotoIf(TaggedIsSmi(receiver), &if_isnotunmodifiedjsregexp);
1581 Branch(IsJSRegExp(receiver), &if_isunmodifiedjsregexp,
1582 &if_isnotunmodifiedjsregexp);
1584 BIND(&if_isunmodifiedjsregexp);
1587 Node*
const is_flag_set = FastFlagGetter(CAST(receiver), flag);
1588 Return(SelectBooleanConstant(is_flag_set));
1591 BIND(&if_isnotunmodifiedjsregexp);
1593 Node*
const native_context = LoadNativeContext(context);
1594 Node*
const regexp_fun =
1595 LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX);
1596 Node*
const initial_map =
1597 LoadObjectField(regexp_fun, JSFunction::kPrototypeOrInitialMapOffset);
1598 Node*
const initial_prototype = LoadMapPrototype(initial_map);
1600 Label if_isprototype(
this), if_isnotprototype(
this);
1601 Branch(WordEqual(receiver, initial_prototype), &if_isprototype,
1602 &if_isnotprototype);
1604 BIND(&if_isprototype);
1606 if (counter != -1) {
1607 Node*
const counter_smi = SmiConstant(counter);
1608 CallRuntime(Runtime::kIncrementUseCounter, context, counter_smi);
1610 Return(UndefinedConstant());
1613 BIND(&if_isnotprototype);
1614 { ThrowTypeError(context, MessageTemplate::kRegExpNonRegExp, method_name); }
1620 TF_BUILTIN(RegExpPrototypeGlobalGetter, RegExpBuiltinsAssembler) {
1621 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1622 TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
1623 FlagGetter(context, receiver, JSRegExp::kGlobal,
1624 v8::Isolate::kRegExpPrototypeOldFlagGetter,
1625 "RegExp.prototype.global");
1630 TF_BUILTIN(RegExpPrototypeIgnoreCaseGetter, RegExpBuiltinsAssembler) {
1631 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1632 TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
1633 FlagGetter(context, receiver, JSRegExp::kIgnoreCase,
1634 v8::Isolate::kRegExpPrototypeOldFlagGetter,
1635 "RegExp.prototype.ignoreCase");
1640 TF_BUILTIN(RegExpPrototypeMultilineGetter, RegExpBuiltinsAssembler) {
1641 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1642 TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
1643 FlagGetter(context, receiver, JSRegExp::kMultiline,
1644 v8::Isolate::kRegExpPrototypeOldFlagGetter,
1645 "RegExp.prototype.multiline");
1649 TF_BUILTIN(RegExpPrototypeDotAllGetter, RegExpBuiltinsAssembler) {
1650 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1651 TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
1652 static const int kNoCounter = -1;
1653 FlagGetter(context, receiver, JSRegExp::kDotAll, kNoCounter,
1654 "RegExp.prototype.dotAll");
1659 TF_BUILTIN(RegExpPrototypeStickyGetter, RegExpBuiltinsAssembler) {
1660 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1661 TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
1662 FlagGetter(context, receiver, JSRegExp::kSticky,
1663 v8::Isolate::kRegExpPrototypeStickyGetter,
1664 "RegExp.prototype.sticky");
1669 TF_BUILTIN(RegExpPrototypeUnicodeGetter, RegExpBuiltinsAssembler) {
1670 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1671 TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
1672 FlagGetter(context, receiver, JSRegExp::kUnicode,
1673 v8::Isolate::kRegExpPrototypeUnicodeGetter,
1674 "RegExp.prototype.unicode");
1678 Node* RegExpBuiltinsAssembler::RegExpExec(Node* context, Node* regexp,
1680 VARIABLE(var_result, MachineRepresentation::kTagged);
1688 GetProperty(context, regexp, isolate()->factory()->exec_string());
1691 Label if_iscallable(
this), if_isnotcallable(
this);
1693 GotoIf(TaggedIsSmi(exec), &if_isnotcallable);
1695 Node*
const exec_map = LoadMap(exec);
1696 Branch(IsCallableMap(exec_map), &if_iscallable, &if_isnotcallable);
1698 BIND(&if_iscallable);
1700 Callable call_callable = CodeFactory::Call(isolate());
1701 Node*
const result = CallJS(call_callable, context, exec, regexp,
string);
1703 var_result.Bind(result);
1704 GotoIf(IsNull(result), &out);
1706 ThrowIfNotJSReceiver(context, result,
1707 MessageTemplate::kInvalidRegExpExecResult,
"");
1712 BIND(&if_isnotcallable);
1714 ThrowIfNotInstanceType(context, regexp, JS_REGEXP_TYPE,
1715 "RegExp.prototype.exec");
1717 Node*
const result = CallBuiltin(Builtins::kRegExpPrototypeExecSlow,
1718 context, regexp,
string);
1719 var_result.Bind(result);
1724 return var_result.value();
1729 TF_BUILTIN(RegExpPrototypeTest, RegExpBuiltinsAssembler) {
1730 TNode<Object> maybe_receiver = CAST(Parameter(Descriptor::kReceiver));
1731 TNode<Object> maybe_string = CAST(Parameter(Descriptor::kString));
1732 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1735 ThrowIfNotJSReceiver(context, maybe_receiver,
1736 MessageTemplate::kIncompatibleMethodReceiver,
1737 "RegExp.prototype.test");
1738 TNode<JSReceiver> receiver = CAST(maybe_receiver);
1741 TNode<String>
string = ToString_Inline(context, maybe_string);
1743 Label fast_path(
this), slow_path(
this);
1744 BranchIfFastRegExp(context, receiver, &fast_path, &slow_path);
1748 Label if_didnotmatch(
this);
1749 RegExpPrototypeExecBodyWithoutResult(context, receiver,
string,
1750 &if_didnotmatch,
true);
1751 Return(TrueConstant());
1753 BIND(&if_didnotmatch);
1754 Return(FalseConstant());
1760 TNode<HeapObject> match_indices =
1761 CAST(RegExpExec(context, receiver,
string));
1764 Return(SelectBooleanConstant(IsNotNull(match_indices)));
1768 TF_BUILTIN(RegExpPrototypeTestFast, RegExpBuiltinsAssembler) {
1769 TNode<JSRegExp> regexp = CAST(Parameter(Descriptor::kReceiver));
1770 TNode<String>
string = CAST(Parameter(Descriptor::kString));
1771 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1773 Label if_didnotmatch(
this);
1774 CSA_ASSERT(
this, IsFastRegExpWithOriginalExec(context, regexp));
1775 RegExpPrototypeExecBodyWithoutResult(context, regexp,
string, &if_didnotmatch,
1777 Return(TrueConstant());
1779 BIND(&if_didnotmatch);
1780 Return(FalseConstant());
1783 Node* RegExpBuiltinsAssembler::AdvanceStringIndex(Node*
const string,
1785 Node*
const is_unicode,
1787 CSA_ASSERT(
this, IsString(
string));
1788 CSA_ASSERT(
this, IsNumberNormalized(index));
1789 if (is_fastpath) CSA_ASSERT(
this, TaggedIsPositiveSmi(index));
1792 Node*
const index_plus_one = NumberInc(index);
1793 VARIABLE(var_result, MachineRepresentation::kTagged, index_plus_one);
1807 STATIC_ASSERT(String::kMaxLength + 2 < Smi::kMaxValue);
1808 CSA_ASSERT(
this, TaggedIsPositiveSmi(index_plus_one));
1811 Label if_isunicode(
this), out(
this);
1812 GotoIfNot(is_unicode, &out);
1815 Branch(TaggedIsPositiveSmi(index_plus_one), &if_isunicode, &out);
1817 BIND(&if_isunicode);
1819 TNode<IntPtrT>
const string_length = LoadStringLengthAsWord(
string);
1820 TNode<IntPtrT> untagged_plus_one = SmiUntag(index_plus_one);
1821 GotoIfNot(IntPtrLessThan(untagged_plus_one, string_length), &out);
1823 Node*
const lead = StringCharCodeAt(
string, SmiUntag(index));
1824 GotoIfNot(Word32Equal(Word32And(lead, Int32Constant(0xFC00)),
1825 Int32Constant(0xD800)),
1828 Node*
const trail = StringCharCodeAt(
string, untagged_plus_one);
1829 GotoIfNot(Word32Equal(Word32And(trail, Int32Constant(0xFC00)),
1830 Int32Constant(0xDC00)),
1834 Node*
const index_plus_two = NumberInc(index_plus_one);
1835 var_result.Bind(index_plus_two);
1841 return var_result.value();
1844 void RegExpBuiltinsAssembler::RegExpPrototypeMatchBody(Node*
const context,
1846 TNode<String>
string,
1847 const bool is_fastpath) {
1848 if (is_fastpath) CSA_ASSERT(
this, IsFastRegExp(context, regexp));
1850 Node*
const is_global =
1851 FlagGetter(CAST(context), CAST(regexp), JSRegExp::kGlobal, is_fastpath);
1853 Label if_isglobal(
this), if_isnotglobal(
this);
1854 Branch(is_global, &if_isglobal, &if_isnotglobal);
1856 BIND(&if_isnotglobal);
1858 Node*
const result =
1860 ? RegExpPrototypeExecBody(CAST(context), CAST(regexp),
string,
true)
1861 : RegExpExec(context, regexp, string);
1867 Node*
const is_unicode = FlagGetter(CAST(context), CAST(regexp),
1868 JSRegExp::kUnicode, is_fastpath);
1870 StoreLastIndex(context, regexp, SmiZero(), is_fastpath);
1874 GrowableFixedArray array(state());
1879 Variable* vars[] = {array.var_array(), array.var_length(),
1880 array.var_capacity()};
1881 Label loop(
this, 3, vars), out(
this);
1886 VARIABLE(var_match, MachineRepresentation::kTagged);
1888 Label if_didmatch(
this), if_didnotmatch(
this);
1892 TNode<RegExpMatchInfo> match_indices =
1893 RegExpPrototypeExecBodyWithoutResult(CAST(context), CAST(regexp),
1894 string, &if_didnotmatch,
true);
1896 Node*
const match_from = LoadFixedArrayElement(
1897 match_indices, RegExpMatchInfo::kFirstCaptureIndex);
1898 Node*
const match_to = LoadFixedArrayElement(
1899 match_indices, RegExpMatchInfo::kFirstCaptureIndex + 1);
1901 var_match.Bind(CallBuiltin(Builtins::kSubString, context,
string,
1902 match_from, match_to));
1905 DCHECK(!is_fastpath);
1906 Node*
const result = RegExpExec(context, regexp,
string);
1908 Label load_match(
this);
1909 Branch(IsNull(result), &if_didnotmatch, &load_match);
1913 ToString_Inline(context, GetProperty(context, result, SmiZero())));
1917 BIND(&if_didnotmatch);
1920 GotoIfNot(IntPtrEqual(array.length(), IntPtrZero()), &out);
1921 Return(NullConstant());
1926 Node* match = var_match.value();
1930 array.Push(CAST(match));
1934 TNode<Smi>
const match_length = LoadStringLengthAsSmi(match);
1935 GotoIfNot(SmiEqual(match_length, SmiZero()), &loop);
1938 LoadLastIndex(CAST(context), CAST(regexp), is_fastpath);
1940 CSA_ASSERT(
this, TaggedIsPositiveSmi(last_index));
1942 last_index = ToLength_Inline(context, last_index);
1945 Node*
const new_last_index =
1946 AdvanceStringIndex(
string, last_index, is_unicode, is_fastpath);
1952 STATIC_ASSERT(String::kMaxLength < Smi::kMaxValue);
1953 CSA_ASSERT(
this, TaggedIsPositiveSmi(new_last_index));
1956 StoreLastIndex(context, regexp, new_last_index, is_fastpath);
1966 Node*
const result = array.ToJSArray(CAST(context));
1974 TF_BUILTIN(RegExpPrototypeMatch, RegExpBuiltinsAssembler) {
1975 TNode<Object> maybe_receiver = CAST(Parameter(Descriptor::kReceiver));
1976 TNode<Object> maybe_string = CAST(Parameter(Descriptor::kString));
1977 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1980 ThrowIfNotJSReceiver(context, maybe_receiver,
1981 MessageTemplate::kIncompatibleMethodReceiver,
1982 "RegExp.prototype.@@match");
1983 Node*
const receiver = maybe_receiver;
1986 TNode<String>
const string = ToString_Inline(context, maybe_string);
1988 Label fast_path(
this), slow_path(
this);
1989 BranchIfFastRegExp(context, receiver, &fast_path, &slow_path);
1994 Return(CallBuiltin(Builtins::kRegExpMatchFast, context, receiver,
string));
1997 RegExpPrototypeMatchBody(context, receiver,
string,
false);
2000 void RegExpMatchAllAssembler::Generate(TNode<Context> context,
2001 TNode<Context> native_context,
2002 TNode<Object> receiver,
2003 TNode<Object> maybe_string) {
2006 ThrowIfNotJSReceiver(context, receiver,
2007 MessageTemplate::kIncompatibleMethodReceiver,
2008 "RegExp.prototype.@@matchAll");
2011 TNode<String>
string = ToString_Inline(context, maybe_string);
2013 TVARIABLE(Object, var_matcher);
2014 TVARIABLE(Int32T, var_global);
2015 TVARIABLE(Int32T, var_unicode);
2016 Label create_iterator(
this), if_fast_regexp(
this),
2017 if_slow_regexp(
this, Label::kDeferred);
2019 BranchIfFastRegExp(context, receiver, &if_fast_regexp, &if_slow_regexp);
2020 BIND(&if_fast_regexp);
2022 TNode<JSRegExp> fast_regexp = CAST(receiver);
2023 TNode<Object> source =
2024 LoadObjectField(fast_regexp, JSRegExp::kSourceOffset);
2029 TNode<String> flags = CAST(FlagsGetter(context, fast_regexp,
true));
2030 var_matcher = RegExpCreate(context, native_context, source, flags);
2031 CSA_ASSERT(
this, IsFastRegExp(context, var_matcher.value()));
2035 FastStoreLastIndex(var_matcher.value(), FastLoadLastIndex(fast_regexp));
2039 var_global = FastFlagGetter(CAST(var_matcher.value()), JSRegExp::kGlobal);
2043 var_unicode = FastFlagGetter(CAST(var_matcher.value()), JSRegExp::kUnicode);
2044 Goto(&create_iterator);
2047 BIND(&if_slow_regexp);
2050 TNode<JSFunction> regexp_fun = CAST(
2051 LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX));
2052 TNode<JSReceiver> species_constructor =
2053 SpeciesConstructor(native_context, receiver, regexp_fun);
2056 TNode<Object> flags =
2057 GetProperty(context, receiver, isolate()->factory()->flags_string());
2058 TNode<String> flags_string = ToString_Inline(context, flags);
2062 Construct(context, species_constructor, receiver, flags_string);
2065 TNode<Number> last_index =
2066 ToLength_Inline(context, SlowLoadLastIndex(context, receiver));
2069 SlowStoreLastIndex(context, var_matcher.value(), last_index);
2073 TNode<String> global_char_string = StringConstant(
"g");
2074 TNode<Smi> global_ix =
2075 CAST(CallBuiltin(Builtins::kStringIndexOf, context, flags_string,
2076 global_char_string, SmiZero()));
2078 SelectInt32Constant(SmiEqual(global_ix, SmiConstant(-1)), 0, 1);
2082 TNode<String> unicode_char_string = StringConstant(
"u");
2083 TNode<Smi> unicode_ix =
2084 CAST(CallBuiltin(Builtins::kStringIndexOf, context, flags_string,
2085 unicode_char_string, SmiZero()));
2087 SelectInt32Constant(SmiEqual(unicode_ix, SmiConstant(-1)), 0, 1);
2088 Goto(&create_iterator);
2091 BIND(&create_iterator);
2094 TNode<Object> iterator =
2095 CreateRegExpStringIterator(native_context, var_matcher.value(), string,
2096 var_global.value(), var_unicode.value());
2103 TNode<Object> RegExpMatchAllAssembler::CreateRegExpStringIterator(
2104 TNode<Context> native_context, TNode<Object> regexp, TNode<String>
string,
2105 TNode<Int32T> global, TNode<Int32T> full_unicode) {
2106 TNode<Map> map = CAST(LoadContextElement(
2108 Context::INITIAL_REGEXP_STRING_ITERATOR_PROTOTYPE_MAP_INDEX));
2113 TNode<Object> iterator = Allocate(JSRegExpStringIterator::kSize);
2114 StoreMapNoWriteBarrier(iterator, map);
2115 StoreObjectFieldRoot(iterator,
2116 JSRegExpStringIterator::kPropertiesOrHashOffset,
2117 RootIndex::kEmptyFixedArray);
2118 StoreObjectFieldRoot(iterator, JSRegExpStringIterator::kElementsOffset,
2119 RootIndex::kEmptyFixedArray);
2122 StoreObjectFieldNoWriteBarrier(
2123 iterator, JSRegExpStringIterator::kIteratingRegExpOffset, regexp);
2126 StoreObjectFieldNoWriteBarrier(
2127 iterator, JSRegExpStringIterator::kIteratedStringOffset,
string);
2131 TNode<Int32T> zero = Int32Constant(0);
2132 TNode<Int32T> one = Int32Constant(1);
2134 Word32Or(Word32Equal(global, zero), Word32Equal(global, one)));
2135 CSA_ASSERT(
this, Word32Or(Word32Equal(full_unicode, zero),
2136 Word32Equal(full_unicode, one)));
2142 TNode<Word32T> global_flag =
2143 Word32Shl(global, Int32Constant(JSRegExpStringIterator::kGlobalBit));
2144 TNode<Word32T> unicode_flag = Word32Shl(
2145 full_unicode, Int32Constant(JSRegExpStringIterator::kUnicodeBit));
2146 TNode<Word32T> iterator_flags = Word32Or(global_flag, unicode_flag);
2147 StoreObjectFieldNoWriteBarrier(iterator, JSRegExpStringIterator::kFlagsOffset,
2148 SmiFromInt32(Signed(iterator_flags)));
2155 TF_BUILTIN(RegExpPrototypeMatchAll, RegExpMatchAllAssembler) {
2156 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
2157 TNode<Context> native_context = LoadNativeContext(context);
2158 TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
2159 TNode<Object> maybe_string = CAST(Parameter(Descriptor::kString));
2160 Generate(context, native_context, receiver, maybe_string);
2166 TF_BUILTIN(RegExpMatchFast, RegExpBuiltinsAssembler) {
2167 TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
2168 TNode<String>
string = CAST(Parameter(Descriptor::kPattern));
2169 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
2171 RegExpPrototypeMatchBody(context, receiver,
string,
true);
2174 void RegExpBuiltinsAssembler::RegExpPrototypeSearchBodyFast(
2175 Node*
const context, Node*
const regexp, Node*
const string) {
2176 CSA_ASSERT(
this, IsFastRegExp(context, regexp));
2177 CSA_ASSERT(
this, IsString(
string));
2180 Node*
const previous_last_index = FastLoadLastIndex(CAST(regexp));
2183 FastStoreLastIndex(regexp, SmiZero());
2186 Label if_didnotmatch(
this);
2187 TNode<RegExpMatchInfo> match_indices = RegExpPrototypeExecBodyWithoutResult(
2188 CAST(context), CAST(regexp), CAST(
string), &if_didnotmatch,
true);
2193 FastStoreLastIndex(regexp, previous_last_index);
2196 Node*
const index = LoadFixedArrayElement(
2197 match_indices, RegExpMatchInfo::kFirstCaptureIndex);
2201 BIND(&if_didnotmatch);
2204 FastStoreLastIndex(regexp, previous_last_index);
2205 Return(SmiConstant(-1));
2209 void RegExpBuiltinsAssembler::RegExpPrototypeSearchBodySlow(
2210 Node*
const context, Node*
const regexp, Node*
const string) {
2211 CSA_ASSERT(
this, IsJSReceiver(regexp));
2212 CSA_ASSERT(
this, IsString(
string));
2214 Isolate*
const isolate = this->isolate();
2216 Node*
const smi_zero = SmiZero();
2219 Node*
const previous_last_index =
2220 SlowLoadLastIndex(CAST(context), CAST(regexp));
2224 Label next(
this), slow(
this, Label::kDeferred);
2225 BranchIfSameValue(previous_last_index, smi_zero, &next, &slow);
2228 SlowStoreLastIndex(context, regexp, smi_zero);
2234 Node*
const exec_result = RegExpExec(context, regexp,
string);
2238 Label next(
this), slow(
this, Label::kDeferred);
2239 Node*
const current_last_index =
2240 SlowLoadLastIndex(CAST(context), CAST(regexp));
2242 BranchIfSameValue(current_last_index, previous_last_index, &next, &slow);
2245 SlowStoreLastIndex(context, regexp, previous_last_index);
2253 GotoIfNot(IsNull(exec_result), &next);
2254 Return(SmiConstant(-1));
2260 Label fast_result(
this), slow_result(
this, Label::kDeferred);
2261 BranchIfFastRegExpResult(context, exec_result, &fast_result, &slow_result);
2266 LoadObjectField(exec_result, JSRegExpResult::kIndexOffset);
2272 Return(GetProperty(context, exec_result,
2273 isolate->factory()->index_string()));
2280 TF_BUILTIN(RegExpPrototypeSearch, RegExpBuiltinsAssembler) {
2281 TNode<Object> maybe_receiver = CAST(Parameter(Descriptor::kReceiver));
2282 TNode<Object> maybe_string = CAST(Parameter(Descriptor::kString));
2283 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
2286 ThrowIfNotJSReceiver(context, maybe_receiver,
2287 MessageTemplate::kIncompatibleMethodReceiver,
2288 "RegExp.prototype.@@search");
2289 Node*
const receiver = maybe_receiver;
2292 TNode<String>
const string = ToString_Inline(context, maybe_string);
2294 Label fast_path(
this), slow_path(
this);
2295 BranchIfFastRegExp(context, receiver, &fast_path, &slow_path);
2300 Return(CallBuiltin(Builtins::kRegExpSearchFast, context, receiver,
string));
2303 RegExpPrototypeSearchBodySlow(context, receiver,
string);
2309 TF_BUILTIN(RegExpSearchFast, RegExpBuiltinsAssembler) {
2310 TNode<JSRegExp> receiver = CAST(Parameter(Descriptor::kReceiver));
2311 TNode<String>
string = CAST(Parameter(Descriptor::kPattern));
2312 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
2314 RegExpPrototypeSearchBodyFast(context, receiver,
string);
2319 void RegExpBuiltinsAssembler::RegExpPrototypeSplitBody(Node*
const context,
2321 TNode<String>
string,
2322 TNode<Smi>
const limit) {
2323 CSA_ASSERT(
this, IsFastRegExp(context, regexp));
2325 Word32BinaryNot(FastFlagGetter(CAST(regexp), JSRegExp::kSticky)));
2327 TNode<IntPtrT>
const int_limit = SmiUntag(limit);
2329 const ElementsKind kind = PACKED_ELEMENTS;
2330 const ParameterMode mode = CodeStubAssembler::INTPTR_PARAMETERS;
2332 Node*
const allocation_site =
nullptr;
2333 Node*
const native_context = LoadNativeContext(context);
2334 TNode<Map> array_map = LoadJSArrayElementsMap(kind, native_context);
2336 Label return_empty_array(
this, Label::kDeferred);
2340 Label next(
this), if_limitiszero(
this, Label::kDeferred);
2341 Branch(SmiEqual(limit, SmiZero()), &return_empty_array, &next);
2345 TNode<Smi>
const string_length = LoadStringLengthAsSmi(
string);
2350 Label next(
this), if_stringisempty(
this, Label::kDeferred);
2351 Branch(SmiEqual(string_length, SmiZero()), &if_stringisempty, &next);
2353 BIND(&if_stringisempty);
2355 Node*
const last_match_info = LoadContextElement(
2356 native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX);
2358 Node*
const match_indices =
2359 CallBuiltin(Builtins::kRegExpExecInternal, context, regexp,
string,
2360 SmiZero(), last_match_info);
2362 Label return_singleton_array(
this);
2363 Branch(IsNull(match_indices), &return_singleton_array,
2364 &return_empty_array);
2366 BIND(&return_singleton_array);
2368 TNode<Smi> length = SmiConstant(1);
2369 TNode<IntPtrT> capacity = IntPtrConstant(1);
2370 TNode<JSArray> result = AllocateJSArray(kind, array_map, capacity,
2371 length, allocation_site, mode);
2373 TNode<FixedArray> fixed_array = CAST(LoadElements(result));
2374 StoreFixedArrayElement(fixed_array, 0,
string);
2385 GrowableFixedArray array(state());
2387 TVARIABLE(Smi, var_last_matched_until, SmiZero());
2388 TVARIABLE(Smi, var_next_search_from, SmiZero());
2390 Variable* vars[] = {array.var_array(), array.var_length(),
2391 array.var_capacity(), &var_last_matched_until,
2392 &var_next_search_from};
2393 const int vars_count =
sizeof(vars) /
sizeof(vars[0]);
2394 Label loop(
this, vars_count, vars), push_suffix_and_out(
this), out(
this);
2399 TNode<Smi>
const next_search_from = var_next_search_from.value();
2400 TNode<Smi>
const last_matched_until = var_last_matched_until.value();
2405 Branch(SmiEqual(next_search_from, string_length), &push_suffix_and_out,
2412 Node*
const last_match_info = LoadContextElement(
2413 native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX);
2415 TNode<HeapObject>
const match_indices_ho =
2416 CAST(CallBuiltin(Builtins::kRegExpExecInternal, context, regexp,
string,
2417 next_search_from, last_match_info));
2422 Branch(IsNull(match_indices_ho), &push_suffix_and_out, &next);
2426 TNode<FixedArray> match_indices = CAST(match_indices_ho);
2427 TNode<Smi>
const match_from = CAST(LoadFixedArrayElement(
2428 match_indices, RegExpMatchInfo::kFirstCaptureIndex));
2433 Branch(SmiEqual(match_from, string_length), &push_suffix_and_out, &next);
2437 TNode<Smi>
const match_to = CAST(LoadFixedArrayElement(
2438 match_indices, RegExpMatchInfo::kFirstCaptureIndex + 1));
2444 GotoIfNot(SmiEqual(match_to, next_search_from), &next);
2445 GotoIfNot(SmiEqual(match_to, last_matched_until), &next);
2447 Node*
const is_unicode = FastFlagGetter(CAST(regexp), JSRegExp::kUnicode);
2448 Node*
const new_next_search_from =
2449 AdvanceStringIndex(
string, next_search_from, is_unicode,
true);
2450 var_next_search_from = CAST(new_next_search_from);
2458 TNode<Smi>
const from = last_matched_until;
2459 TNode<Smi>
const to = match_from;
2460 array.Push(CallBuiltin(Builtins::kSubString, context,
string, from, to));
2461 GotoIf(WordEqual(array.length(), int_limit), &out);
2466 Node*
const num_registers = LoadFixedArrayElement(
2467 match_indices, RegExpMatchInfo::kNumberOfCapturesIndex);
2468 Node*
const int_num_registers = SmiUntag(num_registers);
2470 VARIABLE(var_reg, MachineType::PointerRepresentation());
2471 var_reg.Bind(IntPtrConstant(2));
2473 Variable* vars[] = {array.var_array(), array.var_length(),
2474 array.var_capacity(), &var_reg};
2475 const int vars_count =
sizeof(vars) /
sizeof(vars[0]);
2476 Label nested_loop(
this, vars_count, vars), nested_loop_out(
this);
2477 Branch(IntPtrLessThan(var_reg.value(), int_num_registers), &nested_loop,
2482 Node*
const reg = var_reg.value();
2483 Node*
const from = LoadFixedArrayElement(
2485 RegExpMatchInfo::kFirstCaptureIndex * kPointerSize, mode);
2486 TNode<Smi>
const to = CAST(LoadFixedArrayElement(
2488 (RegExpMatchInfo::kFirstCaptureIndex + 1) * kPointerSize, mode));
2490 Label select_capture(
this), select_undefined(
this), store_value(
this);
2491 VARIABLE(var_value, MachineRepresentation::kTagged);
2492 Branch(SmiEqual(to, SmiConstant(-1)), &select_undefined,
2495 BIND(&select_capture);
2498 CallBuiltin(Builtins::kSubString, context,
string, from, to));
2502 BIND(&select_undefined);
2504 var_value.Bind(UndefinedConstant());
2510 array.Push(CAST(var_value.value()));
2511 GotoIf(WordEqual(array.length(), int_limit), &out);
2513 Node*
const new_reg = IntPtrAdd(reg, IntPtrConstant(2));
2514 var_reg.Bind(new_reg);
2516 Branch(IntPtrLessThan(new_reg, int_num_registers), &nested_loop,
2521 BIND(&nested_loop_out);
2524 var_last_matched_until = match_to;
2525 var_next_search_from = match_to;
2529 BIND(&push_suffix_and_out);
2531 Node*
const from = var_last_matched_until.value();
2532 Node*
const to = string_length;
2533 array.Push(CallBuiltin(Builtins::kSubString, context,
string, from, to));
2539 Node*
const result = array.ToJSArray(CAST(context));
2543 BIND(&return_empty_array);
2545 TNode<Smi> length = SmiZero();
2546 TNode<IntPtrT> capacity = IntPtrZero();
2547 TNode<JSArray> result = AllocateJSArray(kind, array_map, capacity, length,
2548 allocation_site, mode);
2554 TF_BUILTIN(RegExpSplit, RegExpBuiltinsAssembler) {
2555 TNode<JSRegExp> regexp = CAST(Parameter(Descriptor::kRegExp));
2556 TNode<String>
string = CAST(Parameter(Descriptor::kString));
2557 TNode<Object> maybe_limit = CAST(Parameter(Descriptor::kLimit));
2558 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
2560 CSA_ASSERT(
this, IsFastRegExp(context, regexp));
2568 VARIABLE(var_limit, MachineRepresentation::kTagged, maybe_limit);
2569 Label if_limitissmimax(
this), runtime(
this, Label::kDeferred);
2574 GotoIf(IsUndefined(maybe_limit), &if_limitissmimax);
2575 Branch(TaggedIsPositiveSmi(maybe_limit), &next, &runtime);
2582 BIND(&if_limitissmimax);
2586 var_limit.Bind(SmiConstant(Smi::kMaxValue));
2597 GotoIf(FastFlagGetter(regexp, JSRegExp::kSticky), &runtime);
2601 RegExpPrototypeSplitBody(context, regexp,
string, CAST(var_limit.value()));
2604 Return(CallRuntime(Runtime::kRegExpSplit, context, regexp,
string,
2605 var_limit.value()));
2610 TF_BUILTIN(RegExpPrototypeSplit, RegExpBuiltinsAssembler) {
2611 const int kStringArg = 0;
2612 const int kLimitArg = 1;
2614 TNode<IntPtrT> argc =
2615 ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount));
2616 CodeStubArguments args(
this, argc);
2618 TNode<Object> maybe_receiver = args.GetReceiver();
2619 TNode<Object> maybe_string = args.GetOptionalArgumentValue(kStringArg);
2620 TNode<Object> maybe_limit = args.GetOptionalArgumentValue(kLimitArg);
2621 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
2624 ThrowIfNotJSReceiver(context, maybe_receiver,
2625 MessageTemplate::kIncompatibleMethodReceiver,
2626 "RegExp.prototype.@@split");
2627 Node*
const receiver = maybe_receiver;
2630 TNode<String>
const string = ToString_Inline(context, maybe_string);
2632 Label stub(
this), runtime(
this, Label::kDeferred);
2633 BranchIfFastRegExp(context, receiver, &stub, &runtime);
2636 args.PopAndReturn(CallBuiltin(Builtins::kRegExpSplit, context, receiver,
2637 string, maybe_limit));
2640 args.PopAndReturn(CallRuntime(Runtime::kRegExpSplit, context, receiver,
2641 string, maybe_limit));
2644 Node* RegExpBuiltinsAssembler::ReplaceGlobalCallableFastPath(
2645 Node* context, Node* regexp, Node*
string, Node* replace_callable) {
2649 CSA_ASSERT(
this, IsFastRegExp(context, regexp));
2650 CSA_ASSERT(
this, IsCallable(replace_callable));
2651 CSA_ASSERT(
this, IsString(
string));
2653 Isolate*
const isolate = this->isolate();
2655 Node*
const undefined = UndefinedConstant();
2656 TNode<IntPtrT> int_one = IntPtrConstant(1);
2658 Node*
const native_context = LoadNativeContext(context);
2661 VARIABLE(var_result, MachineRepresentation::kTagged);
2664 FastStoreLastIndex(regexp, SmiZero());
2669 ElementsKind kind = PACKED_ELEMENTS;
2670 TNode<Map> array_map = LoadJSArrayElementsMap(kind, native_context);
2671 TNode<IntPtrT> capacity = IntPtrConstant(16);
2672 TNode<Smi> length = SmiZero();
2673 Node*
const allocation_site =
nullptr;
2674 ParameterMode capacity_mode = CodeStubAssembler::INTPTR_PARAMETERS;
2676 result_array = AllocateJSArray(kind, array_map, capacity, length,
2677 allocation_site, capacity_mode);
2681 TNode<FixedArray> last_match_info = CAST(LoadContextElement(
2682 native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX));
2683 Node*
const res = CallRuntime(Runtime::kRegExpExecMultiple, context, regexp,
2684 string, last_match_info, result_array);
2687 FastStoreLastIndex(regexp, SmiZero());
2690 var_result.Bind(
string);
2691 GotoIf(IsNull(res), &out);
2694 last_match_info = CAST(LoadContextElement(
2695 native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX));
2697 Node*
const res_length = LoadJSArrayLength(res);
2698 TNode<FixedArray>
const res_elems = CAST(LoadElements(res));
2700 TNode<Smi>
const num_capture_registers = CAST(LoadFixedArrayElement(
2701 last_match_info, RegExpMatchInfo::kNumberOfCapturesIndex));
2703 Label if_hasexplicitcaptures(
this), if_noexplicitcaptures(
this),
2704 create_result(
this);
2705 Branch(SmiEqual(num_capture_registers, SmiConstant(2)),
2706 &if_noexplicitcaptures, &if_hasexplicitcaptures);
2708 BIND(&if_noexplicitcaptures);
2717 TVARIABLE(Smi, var_match_start, SmiZero());
2719 TNode<IntPtrT>
const end = SmiUntag(res_length);
2720 TVARIABLE(IntPtrT, var_i, IntPtrZero());
2722 Variable* vars[] = {&var_i, &var_match_start};
2723 Label loop(
this, 2, vars);
2727 GotoIfNot(IntPtrLessThan(var_i.value(), end), &create_result);
2729 Node*
const elem = LoadFixedArrayElement(res_elems, var_i.value());
2731 Label if_issmi(
this), if_isstring(
this), loop_epilogue(
this);
2732 Branch(TaggedIsSmi(elem), &if_issmi, &if_isstring);
2736 TNode<Smi> smi_elem = CAST(elem);
2738 Label if_isnegativeorzero(
this), if_ispositive(
this);
2739 BranchIfSmiLessThanOrEqual(smi_elem, SmiZero(), &if_isnegativeorzero,
2742 BIND(&if_ispositive);
2744 TNode<IntPtrT> int_elem = SmiUntag(smi_elem);
2745 TNode<IntPtrT> new_match_start =
2746 Signed(IntPtrAdd(WordShr(int_elem, IntPtrConstant(11)),
2747 WordAnd(int_elem, IntPtrConstant(0x7FF))));
2748 var_match_start = SmiTag(new_match_start);
2749 Goto(&loop_epilogue);
2752 BIND(&if_isnegativeorzero);
2754 var_i = IntPtrAdd(var_i.value(), int_one);
2756 TNode<Smi>
const next_elem =
2757 CAST(LoadFixedArrayElement(res_elems, var_i.value()));
2759 var_match_start = SmiSub(next_elem, smi_elem);
2760 Goto(&loop_epilogue);
2766 CSA_ASSERT(
this, IsString(elem));
2768 Callable call_callable = CodeFactory::Call(isolate);
2769 TNode<Smi> match_start = var_match_start.value();
2770 Node*
const replacement_obj =
2771 CallJS(call_callable, context, replace_callable, undefined, elem,
2772 match_start,
string);
2774 TNode<String>
const replacement_str =
2775 ToString_Inline(context, replacement_obj);
2776 StoreFixedArrayElement(res_elems, var_i.value(), replacement_str);
2778 TNode<Smi>
const elem_length = LoadStringLengthAsSmi(elem);
2779 var_match_start = SmiAdd(match_start, elem_length);
2781 Goto(&loop_epilogue);
2784 BIND(&loop_epilogue);
2786 var_i = IntPtrAdd(var_i.value(), int_one);
2792 BIND(&if_hasexplicitcaptures);
2794 Node*
const from = IntPtrZero();
2795 Node*
const to = SmiUntag(res_length);
2796 const int increment = 1;
2798 BuildFastLoop(from, to,
2799 [
this, res_elems, isolate, native_context, context, undefined,
2800 replace_callable](Node* index) {
2801 Node*
const elem = LoadFixedArrayElement(res_elems, index);
2803 Label do_continue(
this);
2804 GotoIf(TaggedIsSmi(elem), &do_continue);
2810 CSA_ASSERT(
this, HasInstanceType(elem, JS_ARRAY_TYPE));
2814 Callable call_callable = CodeFactory::Call(isolate);
2815 Node*
const reflect_apply = LoadContextElement(
2816 native_context, Context::REFLECT_APPLY_INDEX);
2818 Node*
const replacement_obj =
2819 CallJS(call_callable, context, reflect_apply, undefined,
2820 replace_callable, undefined, elem);
2825 TNode<String>
const replacement_str =
2826 ToString_Inline(context, replacement_obj);
2827 StoreFixedArrayElement(res_elems, index, replacement_str);
2832 increment, CodeStubAssembler::INTPTR_PARAMETERS,
2833 CodeStubAssembler::IndexAdvanceMode::kPost);
2835 Goto(&create_result);
2838 BIND(&create_result);
2840 Node*
const result = CallRuntime(Runtime::kStringBuilderConcat, context,
2841 res, res_length,
string);
2842 var_result.Bind(result);
2847 return var_result.value();
2850 Node* RegExpBuiltinsAssembler::ReplaceSimpleStringFastPath(
2851 Node* context, Node* regexp, TNode<String>
string,
2852 TNode<String> replace_string) {
2858 CSA_ASSERT(
this, IsFastRegExp(context, regexp));
2860 const bool kIsFastPath =
true;
2862 TVARIABLE(String, var_result, EmptyStringConstant());
2863 VARIABLE(var_last_match_end, MachineRepresentation::kTagged, SmiZero());
2864 VARIABLE(var_is_unicode, MachineRepresentation::kWord32, Int32Constant(0));
2865 Variable* vars[] = {&var_result, &var_last_match_end};
2866 Label out(
this), loop(
this, 2, vars), loop_end(
this),
2867 if_nofurthermatches(
this);
2870 Node*
const is_global = FastFlagGetter(CAST(regexp), JSRegExp::kGlobal);
2871 GotoIfNot(is_global, &loop);
2873 var_is_unicode.Bind(FastFlagGetter(CAST(regexp), JSRegExp::kUnicode));
2874 FastStoreLastIndex(regexp, SmiZero());
2879 TNode<RegExpMatchInfo> var_match_indices =
2880 RegExpPrototypeExecBodyWithoutResult(CAST(context), CAST(regexp),
2881 string, &if_nofurthermatches,
2886 TNode<Smi>
const match_start = CAST(LoadFixedArrayElement(
2887 var_match_indices, RegExpMatchInfo::kFirstCaptureIndex));
2888 TNode<Smi>
const match_end = CAST(LoadFixedArrayElement(
2889 var_match_indices, RegExpMatchInfo::kFirstCaptureIndex + 1));
2891 TNode<Smi>
const replace_length = LoadStringLengthAsSmi(replace_string);
2895 TNode<String> first_part =
2896 CAST(CallBuiltin(Builtins::kSubString, context,
string,
2897 var_last_match_end.value(), match_start));
2898 var_result = CAST(CallBuiltin(Builtins::kStringAdd_CheckNone, context,
2899 var_result.value(), first_part));
2901 GotoIf(SmiEqual(replace_length, SmiZero()), &loop_end);
2903 var_result = CAST(CallBuiltin(Builtins::kStringAdd_CheckNone, context,
2904 var_result.value(), replace_string));
2909 var_last_match_end.Bind(match_end);
2911 GotoIfNot(is_global, &if_nofurthermatches);
2913 GotoIf(SmiNotEqual(match_end, match_start), &loop);
2915 Node*
const this_index = FastLoadLastIndex(CAST(regexp));
2916 Node*
const next_index = AdvanceStringIndex(
2917 string, this_index, var_is_unicode.value(), kIsFastPath);
2918 FastStoreLastIndex(regexp, next_index);
2924 BIND(&if_nofurthermatches);
2926 TNode<Smi>
const string_length = LoadStringLengthAsSmi(
string);
2927 TNode<String> last_part =
2928 CAST(CallBuiltin(Builtins::kSubString, context,
string,
2929 var_last_match_end.value(), string_length));
2930 var_result = CAST(CallBuiltin(Builtins::kStringAdd_CheckNone, context,
2931 var_result.value(), last_part));
2936 return var_result.value();
2940 TF_BUILTIN(RegExpReplace, RegExpBuiltinsAssembler) {
2941 TNode<JSRegExp> regexp = CAST(Parameter(Descriptor::kRegExp));
2942 TNode<String>
string = CAST(Parameter(Descriptor::kString));
2943 TNode<Object> replace_value = CAST(Parameter(Descriptor::kReplaceValue));
2944 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
2946 CSA_ASSERT(
this, IsFastRegExp(context, regexp));
2948 Label checkreplacestring(
this), if_iscallable(
this),
2949 runtime(
this, Label::kDeferred);
2952 GotoIf(TaggedIsSmi(replace_value), &checkreplacestring);
2953 Branch(IsCallableMap(LoadMap(CAST(replace_value))), &if_iscallable,
2954 &checkreplacestring);
2957 BIND(&checkreplacestring);
2959 TNode<String>
const replace_string =
2960 ToString_Inline(context, replace_value);
2967 BranchIfFastRegExp(context, regexp, &next, &runtime);
2971 TNode<String>
const dollar_string = HeapConstant(
2972 isolate()->factory()->LookupSingleCharacterStringFromCode(
'$'));
2973 TNode<Smi>
const dollar_ix =
2974 CAST(CallBuiltin(Builtins::kStringIndexOf, context, replace_string,
2975 dollar_string, SmiZero()));
2976 GotoIfNot(SmiEqual(dollar_ix, SmiConstant(-1)), &runtime);
2979 ReplaceSimpleStringFastPath(context, regexp,
string, replace_string));
2983 BIND(&if_iscallable);
2985 Node*
const replace_fn = replace_value;
2988 Label if_isglobal(
this), if_isnotglobal(
this);
2990 Node*
const is_global = FastFlagGetter(regexp, JSRegExp::kGlobal);
2991 Branch(is_global, &if_isglobal, &if_isnotglobal);
2994 Return(ReplaceGlobalCallableFastPath(context, regexp,
string, replace_fn));
2996 BIND(&if_isnotglobal);
2997 Return(CallRuntime(Runtime::kStringReplaceNonGlobalRegExpWithFunction,
2998 context,
string, regexp, replace_fn));
3002 Return(CallRuntime(Runtime::kRegExpReplace, context, regexp,
string,
3008 TF_BUILTIN(RegExpPrototypeReplace, RegExpBuiltinsAssembler) {
3009 const int kStringArg = 0;
3010 const int kReplaceValueArg = 1;
3012 TNode<IntPtrT> argc =
3013 ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount));
3014 CodeStubArguments args(
this, argc);
3016 TNode<Object> maybe_receiver = args.GetReceiver();
3017 TNode<Object> maybe_string = args.GetOptionalArgumentValue(kStringArg);
3018 TNode<Object> replace_value = args.GetOptionalArgumentValue(kReplaceValueArg);
3019 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
3040 ThrowIfNotJSReceiver(context, maybe_receiver,
3041 MessageTemplate::kIncompatibleMethodReceiver,
3042 "RegExp.prototype.@@replace");
3043 Node*
const receiver = maybe_receiver;
3046 TNode<String>
const string = ToString_Inline(context, maybe_string);
3049 Label stub(
this), runtime(
this, Label::kDeferred);
3050 BranchIfFastRegExp(context, receiver, &stub, &runtime);
3053 args.PopAndReturn(CallBuiltin(Builtins::kRegExpReplace, context, receiver,
3054 string, replace_value));
3057 args.PopAndReturn(CallRuntime(Runtime::kRegExpReplace, context, receiver,
3058 string, replace_value));
3063 TF_BUILTIN(RegExpInternalMatch, RegExpBuiltinsAssembler) {
3064 TNode<JSRegExp> regexp = CAST(Parameter(Descriptor::kRegExp));
3065 TNode<String>
string = CAST(Parameter(Descriptor::kString));
3066 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
3068 TNode<Context> native_context = LoadNativeContext(context);
3069 TNode<RegExpMatchInfo> internal_match_info = CAST(LoadContextElement(
3070 native_context, Context::REGEXP_INTERNAL_MATCH_INFO_INDEX));
3071 TNode<HeapObject> maybe_match_indices =
3072 CAST(CallBuiltin(Builtins::kRegExpExecInternal, context, regexp,
string,
3073 SmiZero(), internal_match_info));
3074 TNode<Oddball> null = NullConstant();
3075 Label if_matched(
this);
3076 GotoIfNot(WordEqual(maybe_match_indices, null), &if_matched);
3080 TNode<RegExpMatchInfo> match_indices = CAST(maybe_match_indices);
3082 ConstructNewResultFromMatchInfo(context, regexp, match_indices,
string));
3092 return LoadObjectField<Smi>(iterator, JSRegExpStringIterator::kFlagsOffset);
3096 return UncheckedCast<BoolT>(
3097 IsSetSmi(flags, 1 << JSRegExpStringIterator::kDoneBit));
3101 return UncheckedCast<BoolT>(
3102 IsSetSmi(flags, 1 << JSRegExpStringIterator::kGlobalBit));
3106 return UncheckedCast<BoolT>(
3107 IsSetSmi(flags, 1 << JSRegExpStringIterator::kUnicodeBit));
3112 SmiOr(flags, SmiConstant(1 << JSRegExpStringIterator::kDoneBit));
3113 StoreObjectFieldNoWriteBarrier(
3114 iterator, JSRegExpStringIterator::kFlagsOffset, new_flags);
3121 const char* method_name =
"%RegExpStringIterator%.prototype.next";
3123 TNode<Object> maybe_receiver = CAST(Parameter(Descriptor::kReceiver));
3125 Label if_match(
this), if_no_match(
this, Label::kDeferred),
3126 return_empty_done_result(
this, Label::kDeferred);
3132 ThrowIfNotInstanceType(context, maybe_receiver,
3133 JS_REGEXP_STRING_ITERATOR_TYPE, method_name);
3139 GotoIf(HasDoneFlag(flags), &return_empty_done_result);
3143 LoadObjectField(receiver, JSRegExpStringIterator::kIteratingRegExpOffset);
3146 CSA_CHECK(
this, TaggedIsNotSmi(iterating_regexp));
3147 CSA_CHECK(
this, IsJSReceiver(CAST(iterating_regexp)));
3151 LoadObjectField(receiver, JSRegExpStringIterator::kIteratedStringOffset));
3160 TVARIABLE(
Object, var_match);
3161 TVARIABLE(
BoolT, var_is_fast_regexp);
3163 Label if_fast(
this), if_slow(
this, Label::kDeferred);
3164 BranchIfFastRegExp(context, iterating_regexp, &if_fast, &if_slow);
3169 RegExpPrototypeExecBodyWithoutResult(context, CAST(iterating_regexp),
3170 iterating_string, &if_no_match,
3172 var_match = ConstructNewResultFromMatchInfo(
3173 context, CAST(iterating_regexp), match_indices, iterating_string);
3174 var_is_fast_regexp = Int32TrueConstant();
3180 var_match = CAST(RegExpExec(context, iterating_regexp, iterating_string));
3181 var_is_fast_regexp = Int32FalseConstant();
3182 Branch(IsNull(var_match.value()), &if_no_match, &if_match);
3190 SetDoneFlag(receiver, flags);
3193 Goto(&return_empty_done_result);
3198 Label if_global(
this), if_not_global(
this, Label::kDeferred),
3199 return_result(
this);
3202 Branch(HasGlobalFlag(flags), &if_global, &if_not_global);
3205 Label if_fast(
this), if_slow(
this, Label::kDeferred);
3208 Branch(var_is_fast_regexp.value(), &if_fast, &if_slow);
3212 CSA_ASSERT_BRANCH(
this, [&](Label* ok, Label* not_ok) {
3213 BranchIfFastRegExpResult(context, var_match.value(), ok, not_ok);
3216 SmiNotEqual(LoadFastJSArrayLength(CAST(var_match.value())),
3218 TNode<FixedArray> result_fixed_array =
3219 CAST(LoadElements(CAST(var_match.value())));
3220 TNode<String> match_str =
3221 CAST(LoadFixedArrayElement(result_fixed_array, 0));
3225 CSA_ASSERT(
this, IsFastRegExp(context, iterating_regexp));
3226 GotoIfNot(IsEmptyString(match_str), &return_result);
3229 TNode<Smi> this_index = CAST(FastLoadLastIndex(CAST(iterating_regexp)));
3230 CSA_ASSERT(
this, TaggedIsSmi(this_index));
3233 TNode<Smi> next_index = CAST(AdvanceStringIndex(
3234 iterating_string, this_index, HasUnicodeFlag(flags),
true));
3235 CSA_ASSERT(
this, TaggedIsSmi(next_index));
3238 FastStoreLastIndex(iterating_regexp, next_index);
3241 Goto(&return_result);
3246 TNode<String> match_str = ToString_Inline(
3247 context, GetProperty(context, var_match.value(), SmiZero()));
3249 GotoIfNot(IsEmptyString(match_str), &return_result);
3252 TNode<Object> last_index = SlowLoadLastIndex(context, iterating_regexp);
3253 TNode<Number> this_index = ToLength_Inline(context, last_index);
3256 TNode<Object> next_index = CAST(AdvanceStringIndex(
3257 iterating_string, this_index, HasUnicodeFlag(flags),
false));
3260 SlowStoreLastIndex(context, iterating_regexp, next_index);
3263 Goto(&return_result);
3267 BIND(&if_not_global);
3270 SetDoneFlag(receiver, flags);
3273 Goto(&return_result);
3275 BIND(&return_result);
3277 Return(AllocateJSIteratorResult(context, var_match.value(),
3281 BIND(&return_empty_done_result);
3283 AllocateJSIteratorResult(context, UndefinedConstant(), TrueConstant()));