5 #include "src/builtins/builtins-string-gen.h" 7 #include "src/builtins/builtins-regexp-gen.h" 8 #include "src/builtins/builtins-utils-gen.h" 9 #include "src/builtins/builtins.h" 10 #include "src/code-factory.h" 11 #include "src/heap/factory-inl.h" 12 #include "src/objects.h" 17 typedef compiler::Node Node;
19 using TNode = compiler::TNode<T>;
21 Node* StringBuiltinsAssembler::DirectStringData(Node*
string,
22 Node* string_instance_type) {
24 VARIABLE(var_data, MachineType::PointerRepresentation());
25 Label if_sequential(
this), if_external(
this), if_join(
this);
26 Branch(Word32Equal(Word32And(string_instance_type,
27 Int32Constant(kStringRepresentationMask)),
28 Int32Constant(kSeqStringTag)),
29 &if_sequential, &if_external);
33 var_data.Bind(IntPtrAdd(
34 IntPtrConstant(SeqOneByteString::kHeaderSize - kHeapObjectTag),
35 BitcastTaggedToWord(
string)));
43 CSA_ASSERT(
this, Word32NotEqual(
44 Word32And(string_instance_type,
45 Int32Constant(kUncachedExternalStringMask)),
46 Int32Constant(kUncachedExternalStringTag)));
47 var_data.Bind(LoadObjectField(
string, ExternalString::kResourceDataOffset,
48 MachineType::Pointer()));
53 return var_data.value();
56 void StringBuiltinsAssembler::DispatchOnStringEncodings(
57 Node*
const lhs_instance_type, Node*
const rhs_instance_type,
58 Label* if_one_one, Label* if_one_two, Label* if_two_one,
60 STATIC_ASSERT(kStringEncodingMask == 0x8);
61 STATIC_ASSERT(kTwoByteStringTag == 0x0);
62 STATIC_ASSERT(kOneByteStringTag == 0x8);
66 Node*
const encoding_mask = Int32Constant(kStringEncodingMask);
67 Node*
const lhs_encoding = Word32And(lhs_instance_type, encoding_mask);
68 Node*
const rhs_encoding = Word32And(rhs_instance_type, encoding_mask);
70 Node*
const combined_encodings =
71 Word32Or(lhs_encoding, Word32Shr(rhs_encoding, 1));
75 Label unreachable(
this, Label::kDeferred);
78 kOneByteStringTag | (kOneByteStringTag >> 1),
79 kOneByteStringTag | (kTwoByteStringTag >> 1),
80 kTwoByteStringTag | (kOneByteStringTag >> 1),
81 kTwoByteStringTag | (kTwoByteStringTag >> 1),
84 if_one_one, if_one_two, if_two_one, if_two_two,
87 STATIC_ASSERT(arraysize(values) == arraysize(labels));
88 Switch(combined_encodings, &unreachable, values, labels, arraysize(values));
94 template <
typename SubjectChar,
typename PatternChar>
95 Node* StringBuiltinsAssembler::CallSearchStringRaw(Node*
const subject_ptr,
96 Node*
const subject_length,
97 Node*
const search_ptr,
98 Node*
const search_length,
99 Node*
const start_position) {
100 Node*
const function_addr = ExternalConstant(
101 ExternalReference::search_string_raw<SubjectChar, PatternChar>());
102 Node*
const isolate_ptr =
103 ExternalConstant(ExternalReference::isolate_address(isolate()));
105 MachineType type_ptr = MachineType::Pointer();
106 MachineType type_intptr = MachineType::IntPtr();
108 Node*
const result = CallCFunction6(
109 type_intptr, type_ptr, type_ptr, type_intptr, type_ptr, type_intptr,
110 type_intptr, function_addr, isolate_ptr, subject_ptr, subject_length,
111 search_ptr, search_length, start_position);
116 Node* StringBuiltinsAssembler::PointerToStringDataAtIndex(
117 Node*
const string_data, Node*
const index, String::Encoding encoding) {
118 const ElementsKind kind = (encoding == String::ONE_BYTE_ENCODING)
121 Node*
const offset_in_bytes =
122 ElementOffsetFromIndex(index, kind, INTPTR_PARAMETERS);
123 return IntPtrAdd(string_data, offset_in_bytes);
126 void StringBuiltinsAssembler::GenerateStringEqual(Node* context, Node* left,
128 VARIABLE(var_left, MachineRepresentation::kTagged, left);
129 VARIABLE(var_right, MachineRepresentation::kTagged, right);
130 Label if_equal(
this), if_notequal(
this), if_indirect(
this, Label::kDeferred),
131 restart(
this, {&var_left, &var_right});
133 TNode<IntPtrT> lhs_length = LoadStringLengthAsWord(left);
134 TNode<IntPtrT> rhs_length = LoadStringLengthAsWord(right);
137 GotoIf(WordNotEqual(lhs_length, rhs_length), &if_notequal);
141 Node* lhs = var_left.value();
142 Node* rhs = var_right.value();
144 Node* lhs_instance_type = LoadInstanceType(lhs);
145 Node* rhs_instance_type = LoadInstanceType(rhs);
147 StringEqual_Core(context, lhs, lhs_instance_type, rhs, rhs_instance_type,
148 lhs_length, &if_equal, &if_notequal, &if_indirect);
153 MaybeDerefIndirectStrings(&var_left, lhs_instance_type, &var_right,
154 rhs_instance_type, &restart);
156 TailCallRuntime(Runtime::kStringEqual, context, lhs, rhs);
160 Return(TrueConstant());
163 Return(FalseConstant());
166 void StringBuiltinsAssembler::StringEqual_Core(
167 Node* context, Node* lhs, Node* lhs_instance_type, Node* rhs,
168 Node* rhs_instance_type, TNode<IntPtrT> length, Label* if_equal,
169 Label* if_not_equal, Label* if_indirect) {
170 CSA_ASSERT(
this, IsString(lhs));
171 CSA_ASSERT(
this, IsString(rhs));
172 CSA_ASSERT(
this, WordEqual(LoadStringLengthAsWord(lhs), length));
173 CSA_ASSERT(
this, WordEqual(LoadStringLengthAsWord(rhs), length));
175 GotoIf(WordEqual(lhs, rhs), if_equal);
179 Node* both_instance_types = Word32Or(
180 lhs_instance_type, Word32Shl(rhs_instance_type, Int32Constant(8)));
184 int const kBothInternalizedMask =
185 kIsNotInternalizedMask | (kIsNotInternalizedMask << 8);
186 int const kBothInternalizedTag = kInternalizedTag | (kInternalizedTag << 8);
187 GotoIf(Word32Equal(Word32And(both_instance_types,
188 Int32Constant(kBothInternalizedMask)),
189 Int32Constant(kBothInternalizedTag)),
194 STATIC_ASSERT(kUncachedExternalStringTag != 0);
195 STATIC_ASSERT(kIsIndirectStringTag != 0);
196 int const kBothDirectStringMask =
197 kIsIndirectStringMask | kUncachedExternalStringMask |
198 ((kIsIndirectStringMask | kUncachedExternalStringMask) << 8);
199 GotoIfNot(Word32Equal(Word32And(both_instance_types,
200 Int32Constant(kBothDirectStringMask)),
205 int const kBothStringEncodingMask =
206 kStringEncodingMask | (kStringEncodingMask << 8);
207 int const kOneOneByteStringTag = kOneByteStringTag | (kOneByteStringTag << 8);
208 int const kTwoTwoByteStringTag = kTwoByteStringTag | (kTwoByteStringTag << 8);
209 int const kOneTwoByteStringTag = kOneByteStringTag | (kTwoByteStringTag << 8);
210 Label if_oneonebytestring(
this), if_twotwobytestring(
this),
211 if_onetwobytestring(
this), if_twoonebytestring(
this);
212 Node* masked_instance_types =
213 Word32And(both_instance_types, Int32Constant(kBothStringEncodingMask));
215 Word32Equal(masked_instance_types, Int32Constant(kOneOneByteStringTag)),
216 &if_oneonebytestring);
218 Word32Equal(masked_instance_types, Int32Constant(kTwoTwoByteStringTag)),
219 &if_twotwobytestring);
221 Word32Equal(masked_instance_types, Int32Constant(kOneTwoByteStringTag)),
222 &if_onetwobytestring, &if_twoonebytestring);
224 BIND(&if_oneonebytestring);
225 StringEqual_Loop(lhs, lhs_instance_type, MachineType::Uint8(), rhs,
226 rhs_instance_type, MachineType::Uint8(), length, if_equal,
229 BIND(&if_twotwobytestring);
230 StringEqual_Loop(lhs, lhs_instance_type, MachineType::Uint16(), rhs,
231 rhs_instance_type, MachineType::Uint16(), length, if_equal,
234 BIND(&if_onetwobytestring);
235 StringEqual_Loop(lhs, lhs_instance_type, MachineType::Uint8(), rhs,
236 rhs_instance_type, MachineType::Uint16(), length, if_equal,
239 BIND(&if_twoonebytestring);
240 StringEqual_Loop(lhs, lhs_instance_type, MachineType::Uint16(), rhs,
241 rhs_instance_type, MachineType::Uint8(), length, if_equal,
245 void StringBuiltinsAssembler::StringEqual_Loop(
246 Node* lhs, Node* lhs_instance_type, MachineType lhs_type, Node* rhs,
247 Node* rhs_instance_type, MachineType rhs_type, TNode<IntPtrT> length,
248 Label* if_equal, Label* if_not_equal) {
249 CSA_ASSERT(
this, IsString(lhs));
250 CSA_ASSERT(
this, IsString(rhs));
251 CSA_ASSERT(
this, WordEqual(LoadStringLengthAsWord(lhs), length));
252 CSA_ASSERT(
this, WordEqual(LoadStringLengthAsWord(rhs), length));
255 Node* lhs_data = DirectStringData(lhs, lhs_instance_type);
256 Node* rhs_data = DirectStringData(rhs, rhs_instance_type);
259 TVARIABLE(IntPtrT, var_offset, IntPtrConstant(0));
260 Label loop(
this, &var_offset);
266 GotoIf(WordEqual(var_offset.value(), length), if_equal);
270 Load(lhs_type, lhs_data,
271 WordShl(var_offset.value(),
272 ElementSizeLog2Of(lhs_type.representation())));
274 Load(rhs_type, rhs_data,
275 WordShl(var_offset.value(),
276 ElementSizeLog2Of(rhs_type.representation())));
279 GotoIf(Word32NotEqual(lhs_value, rhs_value), if_not_equal);
282 var_offset = IntPtrAdd(var_offset.value(), IntPtrConstant(1));
287 TF_BUILTIN(StringAdd_CheckNone, StringBuiltinsAssembler) {
288 TNode<String> left = CAST(Parameter(Descriptor::kLeft));
289 TNode<String> right = CAST(Parameter(Descriptor::kRight));
290 Node* context = Parameter(Descriptor::kContext);
291 Return(StringAdd(context, left, right));
294 TF_BUILTIN(StringAdd_ConvertLeft, StringBuiltinsAssembler) {
295 TNode<Object> left = CAST(Parameter(Descriptor::kLeft));
296 TNode<String> right = CAST(Parameter(Descriptor::kRight));
297 Node* context = Parameter(Descriptor::kContext);
300 left = ToString(context, JSReceiverToPrimitive(context, left));
301 TailCallBuiltin(Builtins::kStringAdd_CheckNone, context, left, right);
304 TF_BUILTIN(StringAdd_ConvertRight, StringBuiltinsAssembler) {
305 TNode<String> left = CAST(Parameter(Descriptor::kLeft));
306 TNode<Object> right = CAST(Parameter(Descriptor::kRight));
307 Node* context = Parameter(Descriptor::kContext);
310 right = ToString(context, JSReceiverToPrimitive(context, right));
311 TailCallBuiltin(Builtins::kStringAdd_CheckNone, context, left, right);
314 TF_BUILTIN(SubString, StringBuiltinsAssembler) {
315 TNode<String>
string = CAST(Parameter(Descriptor::kString));
316 TNode<Smi> from = CAST(Parameter(Descriptor::kFrom));
317 TNode<Smi> to = CAST(Parameter(Descriptor::kTo));
318 Return(SubString(
string, SmiUntag(from), SmiUntag(to)));
321 void StringBuiltinsAssembler::GenerateStringAt(
322 char const* method_name, TNode<Context> context, Node* receiver,
323 TNode<Object> maybe_position, TNode<Object> default_return,
324 const StringAtAccessor& accessor) {
326 TNode<String>
string = ToThisString(context, receiver, method_name);
330 Label if_outofbounds(
this, Label::kDeferred);
331 TNode<Number> position = ToInteger_Inline(
332 context, maybe_position, CodeStubAssembler::kTruncateMinusZero);
333 GotoIfNot(TaggedIsSmi(position), &if_outofbounds);
334 TNode<IntPtrT> index = SmiUntag(CAST(position));
335 TNode<IntPtrT> length = LoadStringLengthAsWord(
string);
336 GotoIfNot(UintPtrLessThan(index, length), &if_outofbounds);
337 TNode<Object> result = accessor(
string, length, index);
340 BIND(&if_outofbounds);
341 Return(default_return);
344 void StringBuiltinsAssembler::GenerateStringRelationalComparison(Node* context,
348 VARIABLE(var_left, MachineRepresentation::kTagged, left);
349 VARIABLE(var_right, MachineRepresentation::kTagged, right);
351 Variable* input_vars[2] = {&var_left, &var_right};
352 Label if_less(
this), if_equal(
this), if_greater(
this);
353 Label restart(
this, 2, input_vars);
357 Node* lhs = var_left.value();
358 Node* rhs = var_right.value();
360 GotoIf(WordEqual(lhs, rhs), &if_equal);
363 Node* lhs_instance_type = LoadInstanceType(lhs);
364 Node* rhs_instance_type = LoadInstanceType(rhs);
368 Node* both_instance_types = Word32Or(
369 lhs_instance_type, Word32Shl(rhs_instance_type, Int32Constant(8)));
372 int const kBothSeqOneByteStringMask =
373 kStringEncodingMask | kStringRepresentationMask |
374 ((kStringEncodingMask | kStringRepresentationMask) << 8);
375 int const kBothSeqOneByteStringTag =
376 kOneByteStringTag | kSeqStringTag |
377 ((kOneByteStringTag | kSeqStringTag) << 8);
378 Label if_bothonebyteseqstrings(
this), if_notbothonebyteseqstrings(
this);
379 Branch(Word32Equal(Word32And(both_instance_types,
380 Int32Constant(kBothSeqOneByteStringMask)),
381 Int32Constant(kBothSeqOneByteStringTag)),
382 &if_bothonebyteseqstrings, &if_notbothonebyteseqstrings);
384 BIND(&if_bothonebyteseqstrings);
387 TNode<IntPtrT> lhs_length = LoadStringLengthAsWord(lhs);
388 TNode<IntPtrT> rhs_length = LoadStringLengthAsWord(rhs);
391 TNode<IntPtrT> length = IntPtrMin(lhs_length, rhs_length);
394 TNode<IntPtrT> begin =
395 IntPtrConstant(SeqOneByteString::kHeaderSize - kHeapObjectTag);
398 TNode<IntPtrT> end = IntPtrAdd(begin, length);
401 TVARIABLE(IntPtrT, var_offset, begin);
402 Label loop(
this, &var_offset);
407 Label if_done(
this), if_notdone(
this);
408 Branch(WordEqual(var_offset.value(), end), &if_done, &if_notdone);
413 Node* lhs_value = Load(MachineType::Uint8(), lhs, var_offset.value());
414 Node* rhs_value = Load(MachineType::Uint8(), rhs, var_offset.value());
417 Label if_valueissame(
this), if_valueisnotsame(
this);
418 Branch(Word32Equal(lhs_value, rhs_value), &if_valueissame,
421 BIND(&if_valueissame);
424 var_offset = IntPtrAdd(var_offset.value(), IntPtrConstant(1));
428 BIND(&if_valueisnotsame);
429 Branch(Uint32LessThan(lhs_value, rhs_value), &if_less, &if_greater);
436 GotoIf(IntPtrEqual(lhs_length, rhs_length), &if_equal);
437 Branch(IntPtrLessThan(lhs_length, rhs_length), &if_less, &if_greater);
442 BIND(&if_notbothonebyteseqstrings);
445 MaybeDerefIndirectStrings(&var_left, lhs_instance_type, &var_right,
446 rhs_instance_type, &restart);
449 case Operation::kLessThan:
450 TailCallRuntime(Runtime::kStringLessThan, context, lhs, rhs);
452 case Operation::kLessThanOrEqual:
453 TailCallRuntime(Runtime::kStringLessThanOrEqual, context, lhs, rhs);
455 case Operation::kGreaterThan:
456 TailCallRuntime(Runtime::kStringGreaterThan, context, lhs, rhs);
458 case Operation::kGreaterThanOrEqual:
459 TailCallRuntime(Runtime::kStringGreaterThanOrEqual, context, lhs, rhs);
468 case Operation::kLessThan:
469 case Operation::kLessThanOrEqual:
470 Return(TrueConstant());
473 case Operation::kGreaterThan:
474 case Operation::kGreaterThanOrEqual:
475 Return(FalseConstant());
483 case Operation::kLessThan:
484 case Operation::kGreaterThan:
485 Return(FalseConstant());
488 case Operation::kLessThanOrEqual:
489 case Operation::kGreaterThanOrEqual:
490 Return(TrueConstant());
498 case Operation::kLessThan:
499 case Operation::kLessThanOrEqual:
500 Return(FalseConstant());
503 case Operation::kGreaterThan:
504 case Operation::kGreaterThanOrEqual:
505 Return(TrueConstant());
512 TF_BUILTIN(StringEqual, StringBuiltinsAssembler) {
513 Node* context = Parameter(Descriptor::kContext);
514 Node* left = Parameter(Descriptor::kLeft);
515 Node* right = Parameter(Descriptor::kRight);
516 GenerateStringEqual(context, left, right);
519 TF_BUILTIN(StringLessThan, StringBuiltinsAssembler) {
520 Node* context = Parameter(Descriptor::kContext);
521 Node* left = Parameter(Descriptor::kLeft);
522 Node* right = Parameter(Descriptor::kRight);
523 GenerateStringRelationalComparison(context, left, right,
524 Operation::kLessThan);
527 TF_BUILTIN(StringLessThanOrEqual, StringBuiltinsAssembler) {
528 Node* context = Parameter(Descriptor::kContext);
529 Node* left = Parameter(Descriptor::kLeft);
530 Node* right = Parameter(Descriptor::kRight);
531 GenerateStringRelationalComparison(context, left, right,
532 Operation::kLessThanOrEqual);
535 TF_BUILTIN(StringGreaterThan, StringBuiltinsAssembler) {
536 Node* context = Parameter(Descriptor::kContext);
537 Node* left = Parameter(Descriptor::kLeft);
538 Node* right = Parameter(Descriptor::kRight);
539 GenerateStringRelationalComparison(context, left, right,
540 Operation::kGreaterThan);
543 TF_BUILTIN(StringGreaterThanOrEqual, StringBuiltinsAssembler) {
544 Node* context = Parameter(Descriptor::kContext);
545 Node* left = Parameter(Descriptor::kLeft);
546 Node* right = Parameter(Descriptor::kRight);
547 GenerateStringRelationalComparison(context, left, right,
548 Operation::kGreaterThanOrEqual);
551 TF_BUILTIN(StringCharAt, StringBuiltinsAssembler) {
552 TNode<String> receiver = CAST(Parameter(Descriptor::kReceiver));
553 TNode<IntPtrT> position =
554 UncheckedCast<IntPtrT>(Parameter(Descriptor::kPosition));
557 TNode<Int32T> code = StringCharCodeAt(receiver, position);
560 TNode<String> result = StringFromSingleCharCode(code);
564 TF_BUILTIN(StringCodePointAtUTF16, StringBuiltinsAssembler) {
565 Node* receiver = Parameter(Descriptor::kReceiver);
566 Node* position = Parameter(Descriptor::kPosition);
568 TNode<IntPtrT> length = LoadStringLengthAsWord(receiver);
571 LoadSurrogatePairAt(receiver, length, position, UnicodeEncoding::UTF16);
574 TNode<Smi> result = SmiFromInt32(code);
578 TF_BUILTIN(StringCodePointAtUTF32, StringBuiltinsAssembler) {
579 Node* receiver = Parameter(Descriptor::kReceiver);
580 Node* position = Parameter(Descriptor::kPosition);
583 TNode<IntPtrT> length = LoadStringLengthAsWord(receiver);
586 LoadSurrogatePairAt(receiver, length, position, UnicodeEncoding::UTF32);
589 TNode<Smi> result = SmiFromInt32(code);
597 TF_BUILTIN(StringFromCharCode, CodeStubAssembler) {
601 UncheckedCast<Int32T>(Parameter(Descriptor::kJSActualArgumentsCount));
602 Node* context = Parameter(Descriptor::kContext);
604 CodeStubArguments arguments(
this, ChangeInt32ToIntPtr(argc));
607 Label if_oneargument(
this), if_notoneargument(
this);
608 Branch(Word32Equal(argc, Int32Constant(1)), &if_oneargument,
611 BIND(&if_oneargument);
616 Node* code = arguments.AtIndex(0);
617 Node* code32 = TruncateTaggedToWord32(context, code);
618 TNode<Int32T> code16 =
619 Signed(Word32And(code32, Int32Constant(String::kMaxUtf16CodeUnit)));
620 Node* result = StringFromSingleCharCode(code16);
621 arguments.PopAndReturn(result);
624 Node* code16 =
nullptr;
625 BIND(&if_notoneargument);
627 Label two_byte(
this);
629 Node* one_byte_result = AllocateSeqOneByteString(context, Unsigned(argc));
631 TVARIABLE(IntPtrT, var_max_index);
632 var_max_index = IntPtrConstant(0);
637 CodeStubAssembler::VariableList vars({&var_max_index}, zone());
638 arguments.ForEach(vars, [
this, context, &two_byte, &var_max_index, &code16,
639 one_byte_result](Node* arg) {
640 Node* code32 = TruncateTaggedToWord32(context, arg);
641 code16 = Word32And(code32, Int32Constant(String::kMaxUtf16CodeUnit));
644 Int32GreaterThan(code16, Int32Constant(String::kMaxOneByteCharCode)),
648 Node* offset = ElementOffsetFromIndex(
649 var_max_index.value(), UINT8_ELEMENTS,
650 CodeStubAssembler::INTPTR_PARAMETERS,
651 SeqOneByteString::kHeaderSize - kHeapObjectTag);
652 StoreNoWriteBarrier(MachineRepresentation::kWord8, one_byte_result,
654 var_max_index = IntPtrAdd(var_max_index.value(), IntPtrConstant(1));
656 arguments.PopAndReturn(one_byte_result);
663 Node* two_byte_result = AllocateSeqTwoByteString(context, Unsigned(argc));
667 TNode<IntPtrT> zero = IntPtrConstant(0);
668 CopyStringCharacters(one_byte_result, two_byte_result, zero, zero,
669 var_max_index.value(), String::ONE_BYTE_ENCODING,
670 String::TWO_BYTE_ENCODING);
673 Node* max_index_offset =
674 ElementOffsetFromIndex(var_max_index.value(), UINT16_ELEMENTS,
675 CodeStubAssembler::INTPTR_PARAMETERS,
676 SeqTwoByteString::kHeaderSize - kHeapObjectTag);
677 StoreNoWriteBarrier(MachineRepresentation::kWord16, two_byte_result,
678 max_index_offset, code16);
679 var_max_index = IntPtrAdd(var_max_index.value(), IntPtrConstant(1));
686 [
this, context, two_byte_result, &var_max_index](Node* arg) {
687 Node* code32 = TruncateTaggedToWord32(context, arg);
689 Word32And(code32, Int32Constant(String::kMaxUtf16CodeUnit));
691 Node* offset = ElementOffsetFromIndex(
692 var_max_index.value(), UINT16_ELEMENTS,
693 CodeStubAssembler::INTPTR_PARAMETERS,
694 SeqTwoByteString::kHeaderSize - kHeapObjectTag);
695 StoreNoWriteBarrier(MachineRepresentation::kWord16, two_byte_result,
697 var_max_index = IntPtrAdd(var_max_index.value(), IntPtrConstant(1));
699 var_max_index.value());
701 arguments.PopAndReturn(two_byte_result);
706 TF_BUILTIN(StringPrototypeCharAt, StringBuiltinsAssembler) {
707 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
708 Node* receiver = Parameter(Descriptor::kReceiver);
709 TNode<Object> maybe_position = CAST(Parameter(Descriptor::kPosition));
711 GenerateStringAt(
"String.prototype.charAt", context, receiver, maybe_position,
712 EmptyStringConstant(),
713 [
this](TNode<String>
string, TNode<IntPtrT> length,
714 TNode<IntPtrT> index) {
715 TNode<Int32T> code = StringCharCodeAt(
string, index);
716 return StringFromSingleCharCode(code);
721 TF_BUILTIN(StringPrototypeCharCodeAt, StringBuiltinsAssembler) {
722 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
723 Node* receiver = Parameter(Descriptor::kReceiver);
724 TNode<Object> maybe_position = CAST(Parameter(Descriptor::kPosition));
726 GenerateStringAt(
"String.prototype.charCodeAt", context, receiver,
727 maybe_position, NanConstant(),
728 [
this](TNode<String> receiver, TNode<IntPtrT> length,
729 TNode<IntPtrT> index) {
730 Node* value = StringCharCodeAt(receiver, index);
731 return SmiFromInt32(value);
736 TF_BUILTIN(StringPrototypeCodePointAt, StringBuiltinsAssembler) {
737 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
738 Node* receiver = Parameter(Descriptor::kReceiver);
739 TNode<Object> maybe_position = CAST(Parameter(Descriptor::kPosition));
741 GenerateStringAt(
"String.prototype.codePointAt", context, receiver,
742 maybe_position, UndefinedConstant(),
743 [
this](TNode<String> receiver, TNode<IntPtrT> length,
744 TNode<IntPtrT> index) {
747 Node* value = LoadSurrogatePairAt(receiver, length, index,
748 UnicodeEncoding::UTF32);
749 return SmiFromInt32(value);
755 TF_BUILTIN(StringPrototypeConcat, CodeStubAssembler) {
758 CodeStubArguments arguments(
760 ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount)));
761 Node* receiver = arguments.GetReceiver();
762 Node* context = Parameter(Descriptor::kContext);
765 receiver = ToThisString(context, receiver,
"String.prototype.concat");
768 VARIABLE(var_result, MachineRepresentation::kTagged);
769 var_result.Bind(receiver);
771 CodeStubAssembler::VariableList({&var_result}, zone()),
772 [
this, context, &var_result](Node* arg) {
773 arg = ToString_Inline(context, arg);
774 var_result.Bind(CallStub(CodeFactory::StringAdd(isolate()), context,
775 var_result.value(), arg));
777 arguments.PopAndReturn(var_result.value());
780 void StringBuiltinsAssembler::StringIndexOf(
781 Node*
const subject_string, Node*
const search_string, Node*
const position,
782 const std::function<
void(Node*)>& f_return) {
783 CSA_ASSERT(
this, IsString(subject_string));
784 CSA_ASSERT(
this, IsString(search_string));
785 CSA_ASSERT(
this, TaggedIsSmi(position));
787 TNode<IntPtrT>
const int_zero = IntPtrConstant(0);
788 TNode<IntPtrT>
const search_length = LoadStringLengthAsWord(search_string);
789 TNode<IntPtrT>
const subject_length = LoadStringLengthAsWord(subject_string);
790 TNode<IntPtrT>
const start_position = IntPtrMax(SmiUntag(position), int_zero);
792 Label zero_length_needle(
this), return_minus_1(
this);
794 GotoIf(IntPtrEqual(int_zero, search_length), &zero_length_needle);
797 GotoIfNot(IntPtrLessThanOrEqual(search_length,
798 IntPtrSub(subject_length, start_position)),
804 Label return_zero(
this);
805 GotoIf(WordEqual(subject_string, search_string), &return_zero);
809 ToDirectStringAssembler subject_to_direct(state(), subject_string);
810 ToDirectStringAssembler search_to_direct(state(), search_string);
812 Label call_runtime_unchecked(
this, Label::kDeferred);
814 subject_to_direct.TryToDirect(&call_runtime_unchecked);
815 search_to_direct.TryToDirect(&call_runtime_unchecked);
818 Node*
const subject_ptr =
819 subject_to_direct.PointerToData(&call_runtime_unchecked);
820 Node*
const search_ptr =
821 search_to_direct.PointerToData(&call_runtime_unchecked);
823 Node*
const subject_offset = subject_to_direct.offset();
824 Node*
const search_offset = search_to_direct.offset();
830 CSA_ASSERT(
this, IntPtrGreaterThan(search_length, int_zero));
831 CSA_ASSERT(
this, IntPtrGreaterThanOrEqual(start_position, int_zero));
832 CSA_ASSERT(
this, IntPtrGreaterThanOrEqual(subject_length, start_position));
834 IntPtrLessThanOrEqual(search_length,
835 IntPtrSub(subject_length, start_position)));
837 Label one_one(
this), one_two(
this), two_one(
this), two_two(
this);
838 DispatchOnStringEncodings(subject_to_direct.instance_type(),
839 search_to_direct.instance_type(), &one_one,
840 &one_two, &two_one, &two_two);
842 typedef const uint8_t onebyte_t;
843 typedef const uc16 twobyte_t;
847 Node*
const adjusted_subject_ptr = PointerToStringDataAtIndex(
848 subject_ptr, subject_offset, String::ONE_BYTE_ENCODING);
849 Node*
const adjusted_search_ptr = PointerToStringDataAtIndex(
850 search_ptr, search_offset, String::ONE_BYTE_ENCODING);
852 Label direct_memchr_call(
this), generic_fast_path(
this);
853 Branch(IntPtrEqual(search_length, IntPtrConstant(1)), &direct_memchr_call,
858 BIND(&direct_memchr_call);
860 Node*
const string_addr = IntPtrAdd(adjusted_subject_ptr, start_position);
861 Node*
const search_length = IntPtrSub(subject_length, start_position);
862 Node*
const search_byte =
863 ChangeInt32ToIntPtr(Load(MachineType::Uint8(), adjusted_search_ptr));
866 ExternalConstant(ExternalReference::libc_memchr_function());
867 Node*
const result_address =
868 CallCFunction3(MachineType::Pointer(), MachineType::Pointer(),
869 MachineType::IntPtr(), MachineType::UintPtr(), memchr,
870 string_addr, search_byte, search_length);
871 GotoIf(WordEqual(result_address, int_zero), &return_minus_1);
872 Node*
const result_index =
873 IntPtrAdd(IntPtrSub(result_address, string_addr), start_position);
874 f_return(SmiTag(result_index));
877 BIND(&generic_fast_path);
879 Node*
const result = CallSearchStringRaw<onebyte_t, onebyte_t>(
880 adjusted_subject_ptr, subject_length, adjusted_search_ptr,
881 search_length, start_position);
882 f_return(SmiTag(result));
888 Node*
const adjusted_subject_ptr = PointerToStringDataAtIndex(
889 subject_ptr, subject_offset, String::ONE_BYTE_ENCODING);
890 Node*
const adjusted_search_ptr = PointerToStringDataAtIndex(
891 search_ptr, search_offset, String::TWO_BYTE_ENCODING);
893 Node*
const result = CallSearchStringRaw<onebyte_t, twobyte_t>(
894 adjusted_subject_ptr, subject_length, adjusted_search_ptr,
895 search_length, start_position);
896 f_return(SmiTag(result));
901 Node*
const adjusted_subject_ptr = PointerToStringDataAtIndex(
902 subject_ptr, subject_offset, String::TWO_BYTE_ENCODING);
903 Node*
const adjusted_search_ptr = PointerToStringDataAtIndex(
904 search_ptr, search_offset, String::ONE_BYTE_ENCODING);
906 Node*
const result = CallSearchStringRaw<twobyte_t, onebyte_t>(
907 adjusted_subject_ptr, subject_length, adjusted_search_ptr,
908 search_length, start_position);
909 f_return(SmiTag(result));
914 Node*
const adjusted_subject_ptr = PointerToStringDataAtIndex(
915 subject_ptr, subject_offset, String::TWO_BYTE_ENCODING);
916 Node*
const adjusted_search_ptr = PointerToStringDataAtIndex(
917 search_ptr, search_offset, String::TWO_BYTE_ENCODING);
919 Node*
const result = CallSearchStringRaw<twobyte_t, twobyte_t>(
920 adjusted_subject_ptr, subject_length, adjusted_search_ptr,
921 search_length, start_position);
922 f_return(SmiTag(result));
925 BIND(&return_minus_1);
926 f_return(SmiConstant(-1));
929 f_return(SmiConstant(0));
931 BIND(&zero_length_needle);
933 Comment(
"0-length search_string");
934 f_return(SmiTag(IntPtrMin(subject_length, start_position)));
937 BIND(&call_runtime_unchecked);
941 Comment(
"Call Runtime Unchecked");
943 CallRuntime(Runtime::kStringIndexOfUnchecked, NoContextConstant(),
944 subject_string, search_string, position);
952 TF_BUILTIN(StringIndexOf, StringBuiltinsAssembler) {
953 Node* receiver = Parameter(Descriptor::kReceiver);
954 Node* search_string = Parameter(Descriptor::kSearchString);
955 Node* position = Parameter(Descriptor::kPosition);
956 StringIndexOf(receiver, search_string, position,
957 [
this](Node* result) { this->Return(result); });
962 TF_BUILTIN(StringPrototypeIncludes, StringIncludesIndexOfAssembler) {
963 TNode<IntPtrT> argc =
964 ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount));
965 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
966 Generate(kIncludes, argc, context);
971 TF_BUILTIN(StringPrototypeIndexOf, StringIncludesIndexOfAssembler) {
972 TNode<IntPtrT> argc =
973 ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount));
974 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
975 Generate(kIndexOf, argc, context);
978 void StringIncludesIndexOfAssembler::Generate(SearchVariant variant,
980 TNode<Context> context) {
981 CodeStubArguments arguments(
this, argc);
982 Node*
const receiver = arguments.GetReceiver();
984 VARIABLE(var_search_string, MachineRepresentation::kTagged);
985 VARIABLE(var_position, MachineRepresentation::kTagged);
986 Label argc_1(
this), argc_2(
this), call_runtime(
this, Label::kDeferred),
989 GotoIf(IntPtrEqual(argc, IntPtrConstant(1)), &argc_1);
990 GotoIf(IntPtrGreaterThan(argc, IntPtrConstant(1)), &argc_2);
992 Comment(
"0 Argument case");
993 CSA_ASSERT(
this, IntPtrEqual(argc, IntPtrConstant(0)));
994 Node*
const undefined = UndefinedConstant();
995 var_search_string.Bind(undefined);
996 var_position.Bind(undefined);
1001 Comment(
"1 Argument case");
1002 var_search_string.Bind(arguments.AtIndex(0));
1003 var_position.Bind(SmiConstant(0));
1008 Comment(
"2 Argument case");
1009 var_search_string.Bind(arguments.AtIndex(0));
1010 var_position.Bind(arguments.AtIndex(1));
1011 GotoIfNot(TaggedIsSmi(var_position.value()), &call_runtime);
1016 Comment(
"Fast Path");
1017 Node*
const search = var_search_string.value();
1018 Node*
const position = var_position.value();
1019 GotoIf(TaggedIsSmi(receiver), &call_runtime);
1020 GotoIf(TaggedIsSmi(search), &call_runtime);
1021 GotoIfNot(IsString(receiver), &call_runtime);
1022 GotoIfNot(IsString(search), &call_runtime);
1024 StringIndexOf(receiver, search, position, [&](Node* result) {
1025 CSA_ASSERT(
this, TaggedIsSmi(result));
1026 arguments.PopAndReturn((variant == kIndexOf)
1028 : SelectBooleanConstant(SmiGreaterThanOrEqual(
1029 CAST(result), SmiConstant(0))));
1032 BIND(&call_runtime);
1034 Comment(
"Call Runtime");
1035 Runtime::FunctionId runtime = variant == kIndexOf
1036 ? Runtime::kStringIndexOf
1037 : Runtime::kStringIncludes;
1038 Node*
const result =
1039 CallRuntime(runtime, context, receiver, var_search_string.value(),
1040 var_position.value());
1041 arguments.PopAndReturn(result);
1045 void StringBuiltinsAssembler::RequireObjectCoercible(Node*
const context,
1047 const char* method_name) {
1048 Label out(
this), throw_exception(
this, Label::kDeferred);
1049 Branch(IsNullOrUndefined(value), &throw_exception, &out);
1051 BIND(&throw_exception);
1052 ThrowTypeError(context, MessageTemplate::kCalledOnNullOrUndefined,
1058 void StringBuiltinsAssembler::MaybeCallFunctionAtSymbol(
1059 Node*
const context, Node*
const object, Node*
const maybe_string,
1060 Handle<Symbol> symbol, DescriptorIndexAndName symbol_index,
1061 const NodeFunction0& regexp_call,
const NodeFunction1& generic_call) {
1065 GotoIf(TaggedIsSmi(
object), &out);
1072 Label stub_call(
this), slow_lookup(
this);
1074 GotoIf(TaggedIsSmi(maybe_string), &slow_lookup);
1075 GotoIfNot(IsString(maybe_string), &slow_lookup);
1077 RegExpBuiltinsAssembler regexp_asm(state());
1078 regexp_asm.BranchIfFastRegExp(context,
object, LoadMap(
object),
1079 symbol_index, &stub_call, &slow_lookup);
1088 GotoIf(IsNullOrUndefined(
object), &out);
1098 Node*
const maybe_func = GetProperty(context,
object, symbol);
1099 GotoIf(IsUndefined(maybe_func), &out);
1100 GotoIf(IsNull(maybe_func), &out);
1103 generic_call(maybe_func);
1108 TNode<Smi> StringBuiltinsAssembler::IndexOfDollarChar(Node*
const context,
1109 Node*
const string) {
1110 CSA_ASSERT(
this, IsString(
string));
1112 TNode<String>
const dollar_string = HeapConstant(
1113 isolate()->factory()->LookupSingleCharacterStringFromCode(
'$'));
1114 TNode<Smi>
const dollar_ix =
1115 CAST(CallBuiltin(Builtins::kStringIndexOf, context,
string, dollar_string,
1120 compiler::Node* StringBuiltinsAssembler::GetSubstitution(
1121 Node* context, Node* subject_string, Node* match_start_index,
1122 Node* match_end_index, Node* replace_string) {
1123 CSA_ASSERT(
this, IsString(subject_string));
1124 CSA_ASSERT(
this, IsString(replace_string));
1125 CSA_ASSERT(
this, TaggedIsPositiveSmi(match_start_index));
1126 CSA_ASSERT(
this, TaggedIsPositiveSmi(match_end_index));
1128 VARIABLE(var_result, MachineRepresentation::kTagged, replace_string);
1129 Label runtime(
this), out(
this);
1139 TNode<Smi>
const dollar_index = IndexOfDollarChar(context, replace_string);
1140 Branch(SmiIsNegative(dollar_index), &out, &runtime);
1144 CSA_ASSERT(
this, TaggedIsPositiveSmi(dollar_index));
1146 Node*
const matched =
1147 CallBuiltin(Builtins::kStringSubstring, context, subject_string,
1148 SmiUntag(match_start_index), SmiUntag(match_end_index));
1149 Node*
const replacement_string =
1150 CallRuntime(Runtime::kGetSubstitution, context, matched, subject_string,
1151 match_start_index, replace_string, dollar_index);
1152 var_result.Bind(replacement_string);
1158 return var_result.value();
1162 TF_BUILTIN(StringPrototypeRepeat, StringBuiltinsAssembler) {
1163 Label invalid_count(
this), invalid_string_length(
this),
1164 return_emptystring(
this);
1166 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1167 Node*
const receiver = Parameter(Descriptor::kReceiver);
1168 TNode<Object> count = CAST(Parameter(Descriptor::kCount));
1169 Node*
const string =
1170 ToThisString(context, receiver,
"String.prototype.repeat");
1173 var_count, MachineRepresentation::kTagged,
1174 ToInteger_Inline(context, count, CodeStubAssembler::kTruncateMinusZero));
1179 Label if_count_isheapnumber(
this, Label::kDeferred);
1181 GotoIfNot(TaggedIsSmi(var_count.value()), &if_count_isheapnumber);
1185 TNode<Smi> smi_count = CAST(var_count.value());
1186 GotoIf(SmiLessThan(smi_count, SmiConstant(0)), &invalid_count);
1187 GotoIf(SmiEqual(smi_count, SmiConstant(0)), &return_emptystring);
1188 GotoIf(Word32Equal(LoadStringLengthAsWord32(
string), Int32Constant(0)),
1189 &return_emptystring);
1190 GotoIf(SmiGreaterThan(smi_count, SmiConstant(String::kMaxLength)),
1191 &invalid_string_length);
1192 Return(CallBuiltin(Builtins::kStringRepeat, context,
string, smi_count));
1199 BIND(&if_count_isheapnumber);
1201 CSA_ASSERT(
this, IsNumberNormalized(var_count.value()));
1202 Node*
const number_value = LoadHeapNumberValue(var_count.value());
1203 GotoIf(Float64Equal(number_value, Float64Constant(V8_INFINITY)),
1205 GotoIf(Float64LessThan(number_value, Float64Constant(0.0)),
1207 Branch(Word32Equal(LoadStringLengthAsWord32(
string), Int32Constant(0)),
1208 &return_emptystring, &invalid_string_length);
1212 BIND(&return_emptystring);
1213 Return(EmptyStringConstant());
1215 BIND(&invalid_count);
1217 ThrowRangeError(context, MessageTemplate::kInvalidCountValue,
1221 BIND(&invalid_string_length);
1223 CallRuntime(Runtime::kThrowInvalidStringLength, context);
1229 TF_BUILTIN(StringRepeat, StringBuiltinsAssembler) {
1230 Node*
const context = Parameter(Descriptor::kContext);
1231 Node*
const string = Parameter(Descriptor::kString);
1232 TNode<Smi>
const count = CAST(Parameter(Descriptor::kCount));
1234 CSA_ASSERT(
this, IsString(
string));
1235 CSA_ASSERT(
this, Word32BinaryNot(IsEmptyString(
string)));
1236 CSA_ASSERT(
this, TaggedIsPositiveSmi(count));
1248 VARIABLE(var_result, MachineRepresentation::kTagged, EmptyStringConstant());
1249 VARIABLE(var_temp, MachineRepresentation::kTagged,
string);
1250 TVARIABLE(Smi, var_count, count);
1252 Label loop(
this, {&var_count, &var_result, &var_temp}), return_result(
this);
1258 GotoIfNot(SmiToInt32(SmiAnd(var_count.value(), SmiConstant(1))), &next);
1259 var_result.Bind(CallBuiltin(Builtins::kStringAdd_CheckNone, context,
1260 var_result.value(), var_temp.value()));
1265 var_count = SmiShr(var_count.value(), 1);
1266 GotoIf(SmiEqual(var_count.value(), SmiConstant(0)), &return_result);
1267 var_temp.Bind(CallBuiltin(Builtins::kStringAdd_CheckNone, context,
1268 var_temp.value(), var_temp.value()));
1272 BIND(&return_result);
1273 Return(var_result.value());
1277 TF_BUILTIN(StringPrototypeReplace, StringBuiltinsAssembler) {
1280 Node*
const receiver = Parameter(Descriptor::kReceiver);
1281 Node*
const search = Parameter(Descriptor::kSearch);
1282 Node*
const replace = Parameter(Descriptor::kReplace);
1283 Node*
const context = Parameter(Descriptor::kContext);
1285 TNode<Smi>
const smi_zero = SmiConstant(0);
1287 RequireObjectCoercible(context, receiver,
"String.prototype.replace");
1291 MaybeCallFunctionAtSymbol(
1292 context, search, receiver, isolate()->factory()->replace_symbol(),
1293 DescriptorIndexAndName{JSRegExp::kSymbolReplaceFunctionDescriptorIndex,
1294 RootIndex::kreplace_symbol},
1296 Return(CallBuiltin(Builtins::kRegExpReplace, context, search, receiver,
1300 Callable call_callable = CodeFactory::Call(isolate());
1301 Return(CallJS(call_callable, context, fn, search, receiver, replace));
1306 TNode<String>
const subject_string = ToString_Inline(context, receiver);
1307 TNode<String>
const search_string = ToString_Inline(context, search);
1309 TNode<IntPtrT>
const subject_length = LoadStringLengthAsWord(subject_string);
1310 TNode<IntPtrT>
const search_length = LoadStringLengthAsWord(search_string);
1317 GotoIfNot(WordEqual(search_length, IntPtrConstant(1)), &next);
1318 GotoIfNot(IntPtrGreaterThan(subject_length, IntPtrConstant(0xFF)), &next);
1319 GotoIf(TaggedIsSmi(replace), &next);
1320 GotoIfNot(IsString(replace), &next);
1322 Node*
const subject_instance_type = LoadInstanceType(subject_string);
1323 GotoIfNot(IsConsStringInstanceType(subject_instance_type), &next);
1325 GotoIf(TaggedIsPositiveSmi(IndexOfDollarChar(context, replace)), &next);
1332 Return(CallRuntime(Runtime::kStringReplaceOneCharWithString, context,
1333 subject_string, search_string, replace));
1342 TNode<Smi>
const match_start_index =
1343 CAST(CallBuiltin(Builtins::kStringIndexOf, context, subject_string,
1344 search_string, smi_zero));
1348 Label next(
this), return_subject(
this);
1350 GotoIfNot(SmiIsNegative(match_start_index), &next);
1356 GotoIf(TaggedIsSmi(replace), &return_subject);
1357 GotoIf(IsCallableMap(LoadMap(replace)), &return_subject);
1361 ToString_Inline(context, replace);
1362 Goto(&return_subject);
1364 BIND(&return_subject);
1365 Return(subject_string);
1370 TNode<Smi>
const match_end_index =
1371 SmiAdd(match_start_index, SmiFromIntPtr(search_length));
1373 VARIABLE(var_result, MachineRepresentation::kTagged, EmptyStringConstant());
1379 GotoIf(SmiEqual(match_start_index, smi_zero), &next);
1380 Node*
const prefix =
1381 CallBuiltin(Builtins::kStringSubstring, context, subject_string,
1382 IntPtrConstant(0), SmiUntag(match_start_index));
1383 var_result.Bind(prefix);
1391 Label if_iscallablereplace(
this), if_notcallablereplace(
this);
1392 GotoIf(TaggedIsSmi(replace), &if_notcallablereplace);
1393 Branch(IsCallableMap(LoadMap(replace)), &if_iscallablereplace,
1394 &if_notcallablereplace);
1396 BIND(&if_iscallablereplace);
1398 Callable call_callable = CodeFactory::Call(isolate());
1399 Node*
const replacement =
1400 CallJS(call_callable, context, replace, UndefinedConstant(),
1401 search_string, match_start_index, subject_string);
1402 Node*
const replacement_string = ToString_Inline(context, replacement);
1403 var_result.Bind(CallBuiltin(Builtins::kStringAdd_CheckNone, context,
1404 var_result.value(), replacement_string));
1408 BIND(&if_notcallablereplace);
1410 Node*
const replace_string = ToString_Inline(context, replace);
1411 Node*
const replacement =
1412 GetSubstitution(context, subject_string, match_start_index,
1413 match_end_index, replace_string);
1414 var_result.Bind(CallBuiltin(Builtins::kStringAdd_CheckNone, context,
1415 var_result.value(), replacement));
1421 Node*
const suffix =
1422 CallBuiltin(Builtins::kStringSubstring, context, subject_string,
1423 SmiUntag(match_end_index), subject_length);
1424 Node*
const result = CallBuiltin(Builtins::kStringAdd_CheckNone, context,
1425 var_result.value(), suffix);
1436 enum Variant { kMatch, kSearch };
1438 void Generate(Variant variant,
const char* method_name,
1441 Label call_regexp_match_search(
this);
1443 Builtins::Name builtin;
1446 if (variant == kMatch) {
1447 builtin = Builtins::kRegExpMatchFast;
1448 symbol = isolate()->factory()->match_symbol();
1451 RootIndex::kmatch_symbol};
1453 builtin = Builtins::kRegExpSearchFast;
1454 symbol = isolate()->factory()->search_symbol();
1457 RootIndex::ksearch_symbol};
1460 RequireObjectCoercible(context, receiver, method_name);
1462 MaybeCallFunctionAtSymbol(
1463 context, maybe_regexp, receiver, symbol, property_to_check,
1464 [=] { Return(CallBuiltin(builtin, context, maybe_regexp, receiver)); },
1466 Callable call_callable = CodeFactory::Call(isolate());
1467 Return(CallJS(call_callable, context, fn, maybe_regexp, receiver));
1474 TNode<String> receiver_string = ToString_Inline(context, receiver);
1477 LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX));
1478 TNode<Map> initial_map = CAST(LoadObjectField(
1479 regexp_function, JSFunction::kPrototypeOrInitialMapOffset));
1481 context, initial_map, maybe_regexp, EmptyStringConstant());
1483 Label fast_path(
this), slow_path(
this);
1484 regexp_asm.BranchIfFastRegExp(context, regexp, initial_map,
1485 property_to_check, &fast_path, &slow_path);
1488 Return(CallBuiltin(builtin, context, regexp, receiver_string));
1492 TNode<Object> maybe_func = GetProperty(context, regexp, symbol);
1493 Callable call_callable = CodeFactory::Call(isolate());
1494 Return(CallJS(call_callable, context, maybe_func, regexp,
1503 TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
1504 TNode<Object> maybe_regexp = CAST(Parameter(Descriptor::kRegexp));
1507 Generate(kMatch,
"String.prototype.match", receiver, maybe_regexp, context);
1511 TF_BUILTIN(StringPrototypeMatchAll, StringBuiltinsAssembler) {
1512 char const* method_name =
"String.prototype.matchAll";
1514 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1515 TNode<Object> maybe_regexp = CAST(Parameter(Descriptor::kRegexp));
1516 TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
1517 TNode<Context> native_context = LoadNativeContext(context);
1520 RequireObjectCoercible(context, receiver, method_name);
1523 Label tostring_and_create_regexp_string_iterator(
this, Label::kDeferred);
1524 TVARIABLE(
String, var_receiver_string);
1525 GotoIf(IsNullOrUndefined(maybe_regexp),
1526 &tostring_and_create_regexp_string_iterator);
1531 auto if_regexp_call = [&] {
1534 var_receiver_string = CAST(receiver);
1535 CSA_ASSERT(
this, IsString(var_receiver_string.value()));
1537 RegExpMatchAllAssembler regexp_asm(state());
1538 regexp_asm.Generate(context, native_context, maybe_regexp,
1539 var_receiver_string.value());
1541 auto if_generic_call = [=](Node* fn) {
1542 Callable call_callable = CodeFactory::Call(isolate());
1543 Return(CallJS(call_callable, context, fn, maybe_regexp, receiver));
1545 MaybeCallFunctionAtSymbol(
1546 context, maybe_regexp, receiver,
1547 isolate()->factory()->match_all_symbol(),
1548 DescriptorIndexAndName{JSRegExp::kSymbolMatchAllFunctionDescriptorIndex,
1549 RootIndex::kmatch_all_symbol},
1550 if_regexp_call, if_generic_call);
1551 Goto(&tostring_and_create_regexp_string_iterator);
1553 BIND(&tostring_and_create_regexp_string_iterator);
1555 RegExpMatchAllAssembler regexp_asm(state());
1558 var_receiver_string = ToString_Inline(context, receiver);
1561 TNode<Object> regexp = regexp_asm.RegExpCreate(
1562 context, native_context, maybe_regexp, StringConstant(
"g"));
1567 TNode<Int32T> global = Int32Constant(1);
1568 TNode<Int32T> full_unicode = Int32Constant(0);
1569 TNode<Object> iterator = regexp_asm.CreateRegExpStringIterator(
1570 native_context, regexp, var_receiver_string.value(), global,
1582 enum Variant { kStart, kEnd };
1584 void Generate(Variant variant,
const char* method_name,
TNode<IntPtrT> argc,
1587 Node*
const receiver = arguments.GetReceiver();
1588 Node*
const receiver_string = ToThisString(context, receiver, method_name);
1589 TNode<Smi> const string_length = LoadStringLengthAsSmi(receiver_string);
1591 TVARIABLE(
String, var_fill_string, StringConstant(
" "));
1592 TVARIABLE(
IntPtrT, var_fill_length, IntPtrConstant(1));
1594 Label check_fill(
this), dont_pad(
this), invalid_string_length(
this),
1598 GotoIf(IntPtrEqual(argc, IntPtrConstant(0)), &dont_pad);
1601 ToLength_Inline(context, arguments.AtIndex(0));
1602 CSA_ASSERT(
this, IsNumberNormalized(max_length));
1605 GotoIfNot(TaggedIsSmi(max_length), &check_fill);
1606 Branch(SmiLessThanOrEqual(CAST(max_length), string_length), &dont_pad,
1611 GotoIf(IntPtrEqual(argc, IntPtrConstant(1)), &pad);
1612 Node*
const fill = arguments.AtIndex(1);
1613 GotoIf(IsUndefined(fill), &pad);
1615 var_fill_string = ToString_Inline(context, fill);
1616 var_fill_length = LoadStringLengthAsWord(var_fill_string.value());
1617 Branch(WordEqual(var_fill_length.value(), IntPtrConstant(0)), &dont_pad,
1624 IntPtrGreaterThan(var_fill_length.value(), IntPtrConstant(0)));
1627 GotoIfNot(TaggedIsSmi(max_length), &invalid_string_length);
1628 TNode<Smi> smi_max_length = CAST(max_length);
1630 SmiLessThanOrEqual(smi_max_length, SmiConstant(String::kMaxLength)),
1631 &invalid_string_length);
1633 CSA_ASSERT(
this, SmiGreaterThan(smi_max_length, string_length));
1634 TNode<Smi> const pad_length = SmiSub(smi_max_length, string_length);
1636 VARIABLE(var_pad, MachineRepresentation::kTagged);
1637 Label single_char_fill(
this), multi_char_fill(
this), return_result(
this);
1638 Branch(IntPtrEqual(var_fill_length.value(), IntPtrConstant(1)),
1639 &single_char_fill, &multi_char_fill);
1643 BIND(&single_char_fill);
1645 var_pad.Bind(CallBuiltin(Builtins::kStringRepeat, context,
1646 static_cast<Node*>(var_fill_string.value()),
1648 Goto(&return_result);
1650 BIND(&multi_char_fill);
1653 TruncateIntPtrToInt32(var_fill_length.value());
1654 TNode<Int32T> const pad_length_word32 = SmiToInt32(pad_length);
1656 Int32Div(pad_length_word32, fill_length_word32);
1658 Int32Mod(pad_length_word32, fill_length_word32);
1660 var_pad.Bind(CallBuiltin(Builtins::kStringRepeat, context,
1661 var_fill_string.value(),
1662 SmiFromInt32(repetitions_word32)));
1664 GotoIfNot(remaining_word32, &return_result);
1666 Node*
const remainder_string = CallBuiltin(
1667 Builtins::kStringSubstring, context, var_fill_string.value(),
1668 IntPtrConstant(0), ChangeInt32ToIntPtr(remaining_word32));
1669 var_pad.Bind(CallBuiltin(Builtins::kStringAdd_CheckNone, context,
1670 var_pad.value(), remainder_string));
1671 Goto(&return_result);
1674 BIND(&return_result);
1676 SmiEqual(pad_length, LoadStringLengthAsSmi(var_pad.value())));
1677 arguments.PopAndReturn(
1679 ? CallBuiltin(Builtins::kStringAdd_CheckNone, context,
1680 var_pad.value(), receiver_string)
1681 : CallBuiltin(Builtins::kStringAdd_CheckNone, context,
1682 receiver_string, var_pad.value()));
1685 arguments.PopAndReturn(receiver_string);
1686 BIND(&invalid_string_length);
1688 CallRuntime(Runtime::kThrowInvalidStringLength, context);
1696 ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount));
1699 Generate(kEnd,
"String.prototype.padEnd", argc, context);
1702 TF_BUILTIN(StringPrototypePadStart, StringPadAssembler) {
1703 TNode<IntPtrT> argc =
1704 ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount));
1705 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1707 Generate(kStart,
"String.prototype.padStart", argc, context);
1711 TF_BUILTIN(StringPrototypeSearch, StringMatchSearchAssembler) {
1712 TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
1713 TNode<Object> maybe_regexp = CAST(Parameter(Descriptor::kRegexp));
1714 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1715 Generate(kSearch,
"String.prototype.search", receiver, maybe_regexp, context);
1719 TF_BUILTIN(StringPrototypeSlice, StringBuiltinsAssembler) {
1721 TVARIABLE(IntPtrT, var_start);
1722 TVARIABLE(IntPtrT, var_end);
1724 const int kStart = 0;
1727 ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount));
1728 CodeStubArguments args(
this, argc);
1729 Node*
const receiver = args.GetReceiver();
1730 TNode<Object> start = args.GetOptionalArgumentValue(kStart);
1731 TNode<Object> end = args.GetOptionalArgumentValue(kEnd);
1732 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1735 RequireObjectCoercible(context, receiver,
"String.prototype.slice");
1738 TNode<String>
const subject_string =
1739 CAST(CallBuiltin(Builtins::kToString, context, receiver));
1742 TNode<IntPtrT>
const length = LoadStringLengthAsWord(subject_string);
1745 var_start = ConvertToRelativeIndex(context, start, length);
1749 GotoIf(IsUndefined(end), &out);
1752 var_end = ConvertToRelativeIndex(context, end, length);
1755 Label return_emptystring(
this);
1758 GotoIf(IntPtrLessThanOrEqual(var_end.value(), var_start.value()),
1759 &return_emptystring);
1760 TNode<String>
const result =
1761 SubString(subject_string, var_start.value(), var_end.value());
1762 args.PopAndReturn(result);
1765 BIND(&return_emptystring);
1766 args.PopAndReturn(EmptyStringConstant());
1769 TNode<JSArray> StringBuiltinsAssembler::StringToArray(
1770 TNode<Context> context, TNode<String> subject_string,
1771 TNode<Smi> subject_length, TNode<Number> limit_number) {
1772 CSA_ASSERT(
this, SmiGreaterThan(subject_length, SmiConstant(0)));
1774 Label done(
this), call_runtime(
this, Label::kDeferred),
1775 fill_thehole_and_call_runtime(
this, Label::kDeferred);
1776 TVARIABLE(JSArray, result_array);
1778 TNode<Int32T> instance_type = LoadInstanceType(subject_string);
1779 GotoIfNot(IsOneByteStringInstanceType(instance_type), &call_runtime);
1783 TNode<Smi> length_smi =
1784 Select<Smi>(TaggedIsSmi(limit_number),
1785 [=] {
return SmiMin(CAST(limit_number), subject_length); },
1786 [=] {
return subject_length; });
1787 TNode<IntPtrT> length = SmiToIntPtr(length_smi);
1789 ToDirectStringAssembler to_direct(state(), subject_string);
1790 to_direct.TryToDirect(&call_runtime);
1791 TNode<FixedArray> elements = CAST(AllocateFixedArray(
1792 PACKED_ELEMENTS, length, AllocationFlag::kAllowLargeObjectAllocation));
1794 TNode<RawPtrT> string_data = UncheckedCast<RawPtrT>(
1795 to_direct.PointerToData(&fill_thehole_and_call_runtime));
1796 TNode<IntPtrT> string_data_offset = to_direct.offset();
1797 TNode<Object> cache = LoadRoot(RootIndex::kSingleCharacterStringCache);
1800 IntPtrConstant(0), length,
1804 CSA_ASSERT(
this, WordEqual(to_direct.PointerToData(&call_runtime),
1806 TNode<Int32T> char_code =
1807 UncheckedCast<Int32T>(Load(MachineType::Uint8(), string_data,
1808 IntPtrAdd(index, string_data_offset)));
1809 Node* code_index = ChangeUint32ToWord(char_code);
1810 TNode<Object> entry = LoadFixedArrayElement(CAST(cache), code_index);
1814 GotoIf(IsUndefined(entry), &fill_thehole_and_call_runtime);
1816 StoreFixedArrayElement(elements, index, entry);
1818 1, ParameterMode::INTPTR_PARAMETERS, IndexAdvanceMode::kPost);
1820 TNode<Map> array_map = LoadJSArrayElementsMap(PACKED_ELEMENTS, context);
1822 AllocateUninitializedJSArrayWithoutElements(array_map, length_smi);
1823 StoreObjectField(result_array.value(), JSObject::kElementsOffset, elements);
1826 BIND(&fill_thehole_and_call_runtime);
1828 FillFixedArrayWithValue(PACKED_ELEMENTS, elements, IntPtrConstant(0),
1829 length, RootIndex::kTheHoleValue);
1830 Goto(&call_runtime);
1834 BIND(&call_runtime);
1836 result_array = CAST(CallRuntime(Runtime::kStringToArray, context,
1837 subject_string, limit_number));
1842 return result_array.value();
1846 TF_BUILTIN(StringPrototypeSplit, StringBuiltinsAssembler) {
1847 const int kSeparatorArg = 0;
1848 const int kLimitArg = 1;
1851 ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount));
1852 CodeStubArguments args(
this, argc);
1854 Node*
const receiver = args.GetReceiver();
1855 Node*
const separator = args.GetOptionalArgumentValue(kSeparatorArg);
1856 Node*
const limit = args.GetOptionalArgumentValue(kLimitArg);
1857 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1859 TNode<Smi> smi_zero = SmiConstant(0);
1861 RequireObjectCoercible(context, receiver,
"String.prototype.split");
1865 MaybeCallFunctionAtSymbol(
1866 context, separator, receiver, isolate()->factory()->split_symbol(),
1867 DescriptorIndexAndName{JSRegExp::kSymbolSplitFunctionDescriptorIndex,
1868 RootIndex::ksplit_symbol},
1870 args.PopAndReturn(CallBuiltin(Builtins::kRegExpSplit, context,
1871 separator, receiver, limit));
1874 Callable call_callable = CodeFactory::Call(isolate());
1876 CallJS(call_callable, context, fn, separator, receiver, limit));
1881 TNode<String> subject_string = ToString_Inline(context, receiver);
1882 TNode<Number> limit_number = Select<Number>(
1883 IsUndefined(limit), [=] {
return NumberConstant(kMaxUInt32); },
1884 [=] {
return ToUint32(context, limit); });
1885 Node*
const separator_string = ToString_Inline(context, separator);
1887 Label return_empty_array(
this);
1890 GotoIf(WordEqual<Object, Object>(limit_number, smi_zero),
1891 &return_empty_array);
1897 GotoIfNot(IsUndefined(separator), &next);
1899 const ElementsKind kind = PACKED_ELEMENTS;
1900 Node*
const native_context = LoadNativeContext(context);
1901 TNode<Map> array_map = LoadJSArrayElementsMap(kind, native_context);
1903 TNode<Smi> length = SmiConstant(1);
1904 TNode<IntPtrT> capacity = IntPtrConstant(1);
1905 TNode<JSArray> result = AllocateJSArray(kind, array_map, capacity, length);
1907 TNode<FixedArray> fixed_array = CAST(LoadElements(result));
1908 StoreFixedArrayElement(fixed_array, 0, subject_string);
1910 args.PopAndReturn(result);
1918 GotoIfNot(SmiEqual(LoadStringLengthAsSmi(separator_string), smi_zero),
1921 TNode<Smi> subject_length = LoadStringLengthAsSmi(subject_string);
1922 GotoIf(SmiEqual(subject_length, smi_zero), &return_empty_array);
1925 StringToArray(context, subject_string, subject_length, limit_number));
1930 Node*
const result =
1931 CallRuntime(Runtime::kStringSplit, context, subject_string,
1932 separator_string, limit_number);
1933 args.PopAndReturn(result);
1935 BIND(&return_empty_array);
1937 const ElementsKind kind = PACKED_ELEMENTS;
1938 Node*
const native_context = LoadNativeContext(context);
1939 TNode<Map> array_map = LoadJSArrayElementsMap(kind, native_context);
1941 TNode<Smi> length = smi_zero;
1942 TNode<IntPtrT> capacity = IntPtrConstant(0);
1943 TNode<JSArray> result = AllocateJSArray(kind, array_map, capacity, length);
1945 args.PopAndReturn(result);
1950 TF_BUILTIN(StringPrototypeSubstr, StringBuiltinsAssembler) {
1951 const int kStartArg = 0;
1952 const int kLengthArg = 1;
1955 ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount));
1956 CodeStubArguments args(
this, argc);
1958 Node*
const receiver = args.GetReceiver();
1959 TNode<Object> start = args.GetOptionalArgumentValue(kStartArg);
1960 TNode<Object> length = args.GetOptionalArgumentValue(kLengthArg);
1961 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1965 TVARIABLE(IntPtrT, var_start);
1966 TVARIABLE(Number, var_length);
1968 TNode<IntPtrT>
const zero = IntPtrConstant(0);
1971 TNode<String>
const string =
1972 ToThisString(context, receiver,
"String.prototype.substr");
1974 TNode<IntPtrT>
const string_length = LoadStringLengthAsWord(
string);
1977 var_start = ConvertToRelativeIndex(context, start, string_length);
1980 Label if_issmi(
this), if_isheapnumber(
this, Label::kDeferred);
1984 Label if_isundefined(
this, Label::kDeferred), if_isnotundefined(
this);
1985 Branch(IsUndefined(length), &if_isundefined, &if_isnotundefined);
1987 BIND(&if_isundefined);
1988 var_length = SmiTag(string_length);
1991 BIND(&if_isnotundefined);
1992 var_length = ToInteger_Inline(context, length,
1993 CodeStubAssembler::kTruncateMinusZero);
1996 TVARIABLE(IntPtrT, var_result_length);
1998 Branch(TaggedIsSmi(var_length.value()), &if_issmi, &if_isheapnumber);
2003 TNode<IntPtrT>
const positive_length =
2004 IntPtrMax(SmiUntag(CAST(var_length.value())), zero);
2005 TNode<IntPtrT>
const minimal_length =
2006 IntPtrSub(string_length, var_start.value());
2007 var_result_length = IntPtrMin(positive_length, minimal_length);
2009 GotoIfNot(IntPtrLessThanOrEqual(var_result_length.value(), zero), &out);
2010 args.PopAndReturn(EmptyStringConstant());
2013 BIND(&if_isheapnumber);
2019 CSA_ASSERT(
this, IsHeapNumber(CAST(var_length.value())));
2021 Label if_isnegative(
this), if_ispositive(
this);
2022 TNode<Float64T>
const float_zero = Float64Constant(0.);
2023 TNode<Float64T>
const length_float =
2024 LoadHeapNumberValue(CAST(var_length.value()));
2025 Branch(Float64LessThan(length_float, float_zero), &if_isnegative,
2028 BIND(&if_isnegative);
2029 args.PopAndReturn(EmptyStringConstant());
2031 BIND(&if_ispositive);
2033 var_result_length = IntPtrSub(string_length, var_start.value());
2034 GotoIfNot(IntPtrLessThanOrEqual(var_result_length.value(), zero), &out);
2035 args.PopAndReturn(EmptyStringConstant());
2041 TNode<IntPtrT>
const end =
2042 IntPtrAdd(var_start.value(), var_result_length.value());
2043 args.PopAndReturn(SubString(
string, var_start.value(), end));
2047 TNode<Smi> StringBuiltinsAssembler::ToSmiBetweenZeroAnd(
2048 SloppyTNode<Context> context, SloppyTNode<Object> value,
2049 SloppyTNode<Smi> limit) {
2051 TVARIABLE(Smi, var_result);
2053 TNode<Number>
const value_int =
2054 ToInteger_Inline(context, value, CodeStubAssembler::kTruncateMinusZero);
2056 Label if_issmi(
this), if_isnotsmi(
this, Label::kDeferred);
2057 Branch(TaggedIsSmi(value_int), &if_issmi, &if_isnotsmi);
2061 TNode<Smi> value_smi = CAST(value_int);
2062 Label if_isinbounds(
this), if_isoutofbounds(
this, Label::kDeferred);
2063 Branch(SmiAbove(value_smi, limit), &if_isoutofbounds, &if_isinbounds);
2065 BIND(&if_isinbounds);
2067 var_result = CAST(value_int);
2071 BIND(&if_isoutofbounds);
2073 TNode<Smi>
const zero = SmiConstant(0);
2075 SelectConstant<Smi>(SmiLessThan(value_smi, zero), zero, limit);
2083 TNode<HeapNumber> value_int_hn = CAST(value_int);
2085 TNode<Float64T>
const float_zero = Float64Constant(0.);
2086 TNode<Smi>
const smi_zero = SmiConstant(0);
2087 TNode<Float64T>
const value_float = LoadHeapNumberValue(value_int_hn);
2088 var_result = SelectConstant<Smi>(Float64LessThan(value_float, float_zero),
2094 return var_result.value();
2097 TF_BUILTIN(StringSubstring, CodeStubAssembler) {
2098 TNode<String>
string = CAST(Parameter(Descriptor::kString));
2099 TNode<IntPtrT> from = UncheckedCast<IntPtrT>(Parameter(Descriptor::kFrom));
2100 TNode<IntPtrT> to = UncheckedCast<IntPtrT>(Parameter(Descriptor::kTo));
2102 Return(SubString(
string, from, to));
2106 TF_BUILTIN(StringPrototypeSubstring, StringBuiltinsAssembler) {
2107 const int kStartArg = 0;
2108 const int kEndArg = 1;
2111 ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount));
2112 CodeStubArguments args(
this, argc);
2114 Node*
const receiver = args.GetReceiver();
2115 Node*
const start = args.GetOptionalArgumentValue(kStartArg);
2116 Node*
const end = args.GetOptionalArgumentValue(kEndArg);
2117 Node*
const context = Parameter(Descriptor::kContext);
2121 TVARIABLE(Smi, var_start);
2122 TVARIABLE(Smi, var_end);
2125 TNode<String>
const string =
2126 ToThisString(context, receiver,
"String.prototype.substring");
2128 TNode<Smi>
const length = LoadStringLengthAsSmi(
string);
2131 var_start = ToSmiBetweenZeroAnd(context, start, length);
2136 GotoIf(IsUndefined(end), &out);
2138 var_end = ToSmiBetweenZeroAnd(context, end, length);
2140 Label if_endislessthanstart(
this);
2141 Branch(SmiLessThan(var_end.value(), var_start.value()),
2142 &if_endislessthanstart, &out);
2144 BIND(&if_endislessthanstart);
2146 TNode<Smi>
const tmp = var_end.value();
2147 var_end = var_start.value();
2155 args.PopAndReturn(SubString(
string, SmiUntag(var_start.value()),
2156 SmiUntag(var_end.value())));
2161 TF_BUILTIN(StringPrototypeTrim, StringTrimAssembler) {
2162 TNode<IntPtrT> argc =
2163 ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount));
2164 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
2166 Generate(String::kTrim,
"String.prototype.trim", argc, context);
2170 TF_BUILTIN(StringPrototypeTrimStart, StringTrimAssembler) {
2171 TNode<IntPtrT> argc =
2172 ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount));
2173 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
2175 Generate(String::kTrimStart,
"String.prototype.trimLeft", argc, context);
2179 TF_BUILTIN(StringPrototypeTrimEnd, StringTrimAssembler) {
2180 TNode<IntPtrT> argc =
2181 ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount));
2182 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
2184 Generate(String::kTrimEnd,
"String.prototype.trimRight", argc, context);
2187 void StringTrimAssembler::Generate(String::TrimMode mode,
2188 const char* method_name, TNode<IntPtrT> argc,
2189 TNode<Context> context) {
2190 Label return_emptystring(
this), if_runtime(
this);
2192 CodeStubArguments arguments(
this, argc);
2193 Node*
const receiver = arguments.GetReceiver();
2196 TNode<String>
const string = ToThisString(context, receiver, method_name);
2197 TNode<IntPtrT>
const string_length = LoadStringLengthAsWord(
string);
2199 ToDirectStringAssembler to_direct(state(),
string);
2200 to_direct.TryToDirect(&if_runtime);
2201 Node*
const string_data = to_direct.PointerToData(&if_runtime);
2202 Node*
const instance_type = to_direct.instance_type();
2203 Node*
const is_stringonebyte = IsOneByteStringInstanceType(instance_type);
2204 Node*
const string_data_offset = to_direct.offset();
2206 TVARIABLE(IntPtrT, var_start, IntPtrConstant(0));
2207 TVARIABLE(IntPtrT, var_end, IntPtrSub(string_length, IntPtrConstant(1)));
2209 if (mode == String::kTrimStart || mode == String::kTrim) {
2210 ScanForNonWhiteSpaceOrLineTerminator(string_data, string_data_offset,
2211 is_stringonebyte, &var_start,
2212 string_length, 1, &return_emptystring);
2214 if (mode == String::kTrimEnd || mode == String::kTrim) {
2215 ScanForNonWhiteSpaceOrLineTerminator(
2216 string_data, string_data_offset, is_stringonebyte, &var_end,
2217 IntPtrConstant(-1), -1, &return_emptystring);
2220 arguments.PopAndReturn(
2221 SubString(
string, var_start.value(),
2222 IntPtrAdd(var_end.value(), IntPtrConstant(1))));
2225 arguments.PopAndReturn(
2226 CallRuntime(Runtime::kStringTrim, context,
string, SmiConstant(mode)));
2228 BIND(&return_emptystring);
2229 arguments.PopAndReturn(EmptyStringConstant());
2232 void StringTrimAssembler::ScanForNonWhiteSpaceOrLineTerminator(
2233 Node*
const string_data, Node*
const string_data_offset,
2234 Node*
const is_stringonebyte, Variable*
const var_index, Node*
const end,
2235 int increment, Label*
const if_none_found) {
2236 Label if_stringisonebyte(
this), out(
this);
2238 GotoIf(is_stringonebyte, &if_stringisonebyte);
2242 var_index, end, increment, if_none_found, &out, [&](Node*
const index) {
2244 MachineType::Uint16(), string_data,
2245 WordShl(IntPtrAdd(index, string_data_offset), IntPtrConstant(1)));
2248 BIND(&if_stringisonebyte);
2249 BuildLoop(var_index, end, increment, if_none_found, &out,
2250 [&](Node*
const index) {
2251 return Load(MachineType::Uint8(), string_data,
2252 IntPtrAdd(index, string_data_offset));
2258 void StringTrimAssembler::BuildLoop(
2259 Variable*
const var_index, Node*
const end,
int increment,
2260 Label*
const if_none_found, Label*
const out,
2261 const std::function<Node*(Node*)>& get_character) {
2262 Label loop(
this, var_index);
2266 Node*
const index = var_index->value();
2267 GotoIf(IntPtrEqual(index, end), if_none_found);
2268 GotoIfNotWhiteSpaceOrLineTerminator(
2269 UncheckedCast<Uint32T>(get_character(index)), out);
2270 Increment(var_index, increment);
2275 void StringTrimAssembler::GotoIfNotWhiteSpaceOrLineTerminator(
2276 Node*
const char_code, Label*
const if_not_whitespace) {
2280 GotoIf(Word32Equal(char_code, Int32Constant(0x0020)), &out);
2283 GotoIf(Uint32LessThan(char_code, Int32Constant(0x0009)), if_not_whitespace);
2288 GotoIf(Uint32LessThanOrEqual(char_code, Int32Constant(0x000D)), &out);
2291 GotoIf(Uint32LessThan(char_code, Int32Constant(0x00A0)), if_not_whitespace);
2294 GotoIf(Word32Equal(char_code, Int32Constant(0x00A0)), &out);
2297 GotoIf(Word32Equal(char_code, Int32Constant(0x1680)), &out);
2300 GotoIf(Uint32LessThan(char_code, Int32Constant(0x2000)), if_not_whitespace);
2311 GotoIf(Uint32LessThanOrEqual(char_code, Int32Constant(0x200A)), &out);
2314 GotoIf(Word32Equal(char_code, Int32Constant(0x2028)), &out);
2316 GotoIf(Word32Equal(char_code, Int32Constant(0x2029)), &out);
2318 GotoIf(Word32Equal(char_code, Int32Constant(0x202F)), &out);
2320 GotoIf(Word32Equal(char_code, Int32Constant(0x205F)), &out);
2322 GotoIf(Word32Equal(char_code, Int32Constant(0xFEFF)), &out);
2324 Branch(Word32Equal(char_code, Int32Constant(0x3000)), &out,
2331 TF_BUILTIN(StringPrototypeToString, CodeStubAssembler) {
2332 Node* context = Parameter(Descriptor::kContext);
2333 Node* receiver = Parameter(Descriptor::kReceiver);
2335 Node* result = ToThisValue(context, receiver, PrimitiveType::kString,
2336 "String.prototype.toString");
2341 TF_BUILTIN(StringPrototypeValueOf, CodeStubAssembler) {
2342 Node* context = Parameter(Descriptor::kContext);
2343 Node* receiver = Parameter(Descriptor::kReceiver);
2345 Node* result = ToThisValue(context, receiver, PrimitiveType::kString,
2346 "String.prototype.valueOf");
2350 TF_BUILTIN(StringPrototypeIterator, CodeStubAssembler) {
2351 Node* context = Parameter(Descriptor::kContext);
2352 Node* receiver = Parameter(Descriptor::kReceiver);
2355 ToThisString(context, receiver,
"String.prototype[Symbol.iterator]");
2357 Node* native_context = LoadNativeContext(context);
2358 Node* map = LoadContextElement(native_context,
2359 Context::INITIAL_STRING_ITERATOR_MAP_INDEX);
2360 Node* iterator = Allocate(JSStringIterator::kSize);
2361 StoreMapNoWriteBarrier(iterator, map);
2362 StoreObjectFieldRoot(iterator, JSValue::kPropertiesOrHashOffset,
2363 RootIndex::kEmptyFixedArray);
2364 StoreObjectFieldRoot(iterator, JSObject::kElementsOffset,
2365 RootIndex::kEmptyFixedArray);
2366 StoreObjectFieldNoWriteBarrier(iterator, JSStringIterator::kStringOffset,
2368 Node* index = SmiConstant(0);
2369 StoreObjectFieldNoWriteBarrier(iterator, JSStringIterator::kNextIndexOffset,
2376 TNode<Int32T> StringBuiltinsAssembler::LoadSurrogatePairAt(
2377 SloppyTNode<String>
string, SloppyTNode<IntPtrT> length,
2378 SloppyTNode<IntPtrT> index, UnicodeEncoding encoding) {
2379 Label handle_surrogate_pair(
this), return_result(
this);
2380 TVARIABLE(Int32T, var_result);
2381 TVARIABLE(Int32T, var_trail);
2382 var_result = StringCharCodeAt(
string, index);
2383 var_trail = Int32Constant(0);
2385 GotoIf(Word32NotEqual(Word32And(var_result.value(), Int32Constant(0xFC00)),
2386 Int32Constant(0xD800)),
2388 TNode<IntPtrT> next_index = IntPtrAdd(index, IntPtrConstant(1));
2390 GotoIfNot(IntPtrLessThan(next_index, length), &return_result);
2391 var_trail = StringCharCodeAt(
string, next_index);
2392 Branch(Word32Equal(Word32And(var_trail.value(), Int32Constant(0xFC00)),
2393 Int32Constant(0xDC00)),
2394 &handle_surrogate_pair, &return_result);
2396 BIND(&handle_surrogate_pair);
2398 TNode<Int32T> lead = var_result.value();
2399 TNode<Int32T> trail = var_trail.value();
2402 CSA_SLOW_ASSERT(
this,
2403 Uint32GreaterThanOrEqual(lead, Int32Constant(0xD800)));
2404 CSA_SLOW_ASSERT(
this, Uint32LessThan(lead, Int32Constant(0xDC00)));
2405 CSA_SLOW_ASSERT(
this,
2406 Uint32GreaterThanOrEqual(trail, Int32Constant(0xDC00)));
2407 CSA_SLOW_ASSERT(
this, Uint32LessThan(trail, Int32Constant(0xE000)));
2410 case UnicodeEncoding::UTF16:
2411 var_result = Signed(Word32Or(
2413 #
if V8_TARGET_BIG_ENDIAN
2414 Word32Shl(lead, Int32Constant(16)), trail));
2416 Word32Shl(trail, Int32Constant(16)), lead));
2420 case UnicodeEncoding::UTF32: {
2423 TNode<Int32T> surrogate_offset =
2424 Int32Constant(0x10000 - (0xD800 << 10) - 0xDC00);
2427 var_result = Signed(Int32Add(Word32Shl(lead, Int32Constant(10)),
2428 Int32Add(trail, surrogate_offset)));
2432 Goto(&return_result);
2435 BIND(&return_result);
2436 return var_result.value();
2440 TF_BUILTIN(StringIteratorPrototypeNext, StringBuiltinsAssembler) {
2441 VARIABLE(var_value, MachineRepresentation::kTagged);
2442 VARIABLE(var_done, MachineRepresentation::kTagged);
2444 var_value.Bind(UndefinedConstant());
2445 var_done.Bind(TrueConstant());
2447 Label throw_bad_receiver(
this), next_codepoint(
this), return_result(
this);
2449 Node* context = Parameter(Descriptor::kContext);
2450 Node* iterator = Parameter(Descriptor::kReceiver);
2452 GotoIf(TaggedIsSmi(iterator), &throw_bad_receiver);
2454 InstanceTypeEqual(LoadInstanceType(iterator), JS_STRING_ITERATOR_TYPE),
2455 &throw_bad_receiver);
2457 Node*
string = LoadObjectField(iterator, JSStringIterator::kStringOffset);
2458 TNode<IntPtrT> position = SmiUntag(
2459 CAST(LoadObjectField(iterator, JSStringIterator::kNextIndexOffset)));
2460 TNode<IntPtrT> length = LoadStringLengthAsWord(
string);
2462 Branch(IntPtrLessThan(position, length), &next_codepoint, &return_result);
2464 BIND(&next_codepoint);
2466 UnicodeEncoding encoding = UnicodeEncoding::UTF16;
2467 TNode<Int32T> ch = LoadSurrogatePairAt(
string, length, position, encoding);
2468 TNode<String> value = StringFromSingleCodePoint(ch, encoding);
2469 var_value.Bind(value);
2470 TNode<IntPtrT> length = LoadStringLengthAsWord(value);
2471 StoreObjectFieldNoWriteBarrier(iterator, JSStringIterator::kNextIndexOffset,
2472 SmiTag(Signed(IntPtrAdd(position, length))));
2473 var_done.Bind(FalseConstant());
2474 Goto(&return_result);
2477 BIND(&return_result);
2480 AllocateJSIteratorResult(context, var_value.value(), var_done.value());
2484 BIND(&throw_bad_receiver);
2487 ThrowTypeError(context, MessageTemplate::kIncompatibleMethodReceiver,
2488 StringConstant(
"String Iterator.prototype.next"), iterator);
2492 void StringBuiltinsAssembler::BranchIfStringPrimitiveWithNoCustomIteration(
2493 TNode<Object>
object, TNode<Context> context, Label* if_true,
2495 GotoIf(TaggedIsSmi(
object), if_false);
2496 GotoIfNot(IsString(CAST(
object)), if_false);
2500 Node* protector_cell = LoadRoot(RootIndex::kStringIteratorProtector);
2501 DCHECK(isolate()->heap()->string_iterator_protector()->IsPropertyCell());
2502 Branch(WordEqual(LoadObjectField(protector_cell, PropertyCell::kValueOffset),
2503 SmiConstant(Isolate::kProtectorValid)),
2508 TNode<JSArray> StringBuiltinsAssembler::StringToList(TNode<Context> context,
2509 TNode<String>
string) {
2510 const ElementsKind kind = PACKED_ELEMENTS;
2511 const TNode<IntPtrT> length = LoadStringLengthAsWord(
string);
2513 TNode<Map> array_map =
2514 LoadJSArrayElementsMap(kind, LoadNativeContext(context));
2515 TNode<JSArray> array =
2516 AllocateJSArray(kind, array_map, length, SmiTag(length),
nullptr,
2517 INTPTR_PARAMETERS, kAllowLargeObjectAllocation);
2518 TNode<FixedArrayBase> elements = LoadElements(array);
2520 const int first_element_offset = FixedArray::kHeaderSize - kHeapObjectTag;
2521 TNode<IntPtrT> first_to_element_offset =
2522 ElementOffsetFromIndex(IntPtrConstant(0), kind, INTPTR_PARAMETERS, 0);
2523 TNode<IntPtrT> first_offset =
2524 IntPtrAdd(first_to_element_offset, IntPtrConstant(first_element_offset));
2525 TVARIABLE(IntPtrT, var_offset, first_offset);
2526 TVARIABLE(IntPtrT, var_position, IntPtrConstant(0));
2527 Label done(
this), next_codepoint(
this, {&var_position, &var_offset});
2529 Goto(&next_codepoint);
2531 BIND(&next_codepoint);
2534 GotoIfNot(IntPtrLessThan(var_position.value(), length), &done);
2535 const UnicodeEncoding encoding = UnicodeEncoding::UTF16;
2537 LoadSurrogatePairAt(
string, length, var_position.value(), encoding);
2538 TNode<String> value = StringFromSingleCodePoint(ch, encoding);
2540 Store(elements, var_offset.value(), value);
2543 TNode<IntPtrT> ch_length = LoadStringLengthAsWord(value);
2544 var_position = IntPtrAdd(var_position.value(), ch_length);
2546 var_offset = IntPtrAdd(var_offset.value(), IntPtrConstant(kPointerSize));
2547 Goto(&next_codepoint);
2551 TNode<IntPtrT> new_length =
2552 IntPtrDiv(IntPtrSub(var_offset.value(), first_offset),
2553 IntPtrConstant(kPointerSize));
2554 CSA_ASSERT(
this, IntPtrGreaterThanOrEqual(new_length, IntPtrConstant(0)));
2555 CSA_ASSERT(
this, IntPtrGreaterThanOrEqual(length, new_length));
2556 StoreObjectFieldNoWriteBarrier(array, JSArray::kLengthOffset,
2557 SmiTag(new_length));
2559 return UncheckedCast<JSArray>(array);
2562 TF_BUILTIN(StringToList, StringBuiltinsAssembler) {
2563 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
2564 TNode<String>
string = CAST(Parameter(Descriptor::kSource));
2565 Return(StringToList(context,
string));
2577 void Generate(
Node*
const context,
Node*
const receiver,
2578 const char* method_name,
const char* tag_name) {
2579 Node*
const string = ToThisString(context, receiver, method_name);
2580 std::string open_tag =
"<" + std::string(tag_name) +
">";
2581 std::string close_tag =
"</" + std::string(tag_name) +
">";
2583 Node* strings[] = {StringConstant(open_tag.c_str()),
string,
2584 StringConstant(close_tag.c_str())};
2585 Return(ConcatStrings(context, strings, arraysize(strings)));
2588 void GenerateWithAttribute(
Node*
const context,
Node*
const receiver,
2589 const char* method_name,
const char* tag_name,
2590 const char* attr,
Node*
const value) {
2591 Node*
const string = ToThisString(context, receiver, method_name);
2592 Node*
const value_string =
2593 EscapeQuotes(context, ToString_Inline(context, value));
2594 std::string open_tag_attr =
2595 "<" + std::string(tag_name) +
" " + std::string(attr) +
"=\"";
2596 std::string close_tag =
"</" + std::string(tag_name) +
">";
2598 Node* strings[] = {StringConstant(open_tag_attr.c_str()), value_string,
2599 StringConstant(
"\">"), string,
2600 StringConstant(close_tag.c_str())};
2601 Return(ConcatStrings(context, strings, arraysize(strings)));
2604 Node* ConcatStrings(
Node*
const context,
Node** strings,
int len) {
2605 VARIABLE(var_result, MachineRepresentation::kTagged, strings[0]);
2606 for (
int i = 1;
i < len;
i++) {
2607 var_result.Bind(CallStub(CodeFactory::StringAdd(isolate()), context,
2608 var_result.value(), strings[
i]));
2610 return var_result.value();
2613 Node* EscapeQuotes(
Node*
const context,
Node*
const string) {
2614 CSA_ASSERT(
this, IsString(
string));
2615 Node*
const regexp_function = LoadContextElement(
2616 LoadNativeContext(context), Context::REGEXP_FUNCTION_INDEX);
2617 Node*
const initial_map = LoadObjectField(
2618 regexp_function, JSFunction::kPrototypeOrInitialMapOffset);
2620 Node*
const regexp =
2621 CallRuntime(Runtime::kRegExpInitializeAndCompile, context,
2622 AllocateJSObjectFromMap(initial_map), StringConstant(
"\""),
2623 StringConstant(
"g"));
2625 return CallRuntime(Runtime::kRegExpInternalReplace, context, regexp,
string,
2626 StringConstant(
"""));
2632 Node*
const context = Parameter(Descriptor::kContext);
2633 Node*
const receiver = Parameter(Descriptor::kReceiver);
2634 Node*
const value = Parameter(Descriptor::kValue);
2635 GenerateWithAttribute(context, receiver,
"String.prototype.anchor",
"a",
2640 TF_BUILTIN(StringPrototypeBig, StringHtmlAssembler) {
2641 Node*
const context = Parameter(Descriptor::kContext);
2642 Node*
const receiver = Parameter(Descriptor::kReceiver);
2643 Generate(context, receiver,
"String.prototype.big",
"big");
2647 TF_BUILTIN(StringPrototypeBlink, StringHtmlAssembler) {
2648 Node*
const context = Parameter(Descriptor::kContext);
2649 Node*
const receiver = Parameter(Descriptor::kReceiver);
2650 Generate(context, receiver,
"String.prototype.blink",
"blink");
2654 TF_BUILTIN(StringPrototypeBold, StringHtmlAssembler) {
2655 Node*
const context = Parameter(Descriptor::kContext);
2656 Node*
const receiver = Parameter(Descriptor::kReceiver);
2657 Generate(context, receiver,
"String.prototype.bold",
"b");
2661 TF_BUILTIN(StringPrototypeFontcolor, StringHtmlAssembler) {
2662 Node*
const context = Parameter(Descriptor::kContext);
2663 Node*
const receiver = Parameter(Descriptor::kReceiver);
2664 Node*
const value = Parameter(Descriptor::kValue);
2665 GenerateWithAttribute(context, receiver,
"String.prototype.fontcolor",
"font",
2670 TF_BUILTIN(StringPrototypeFontsize, StringHtmlAssembler) {
2671 Node*
const context = Parameter(Descriptor::kContext);
2672 Node*
const receiver = Parameter(Descriptor::kReceiver);
2673 Node*
const value = Parameter(Descriptor::kValue);
2674 GenerateWithAttribute(context, receiver,
"String.prototype.fontsize",
"font",
2679 TF_BUILTIN(StringPrototypeFixed, StringHtmlAssembler) {
2680 Node*
const context = Parameter(Descriptor::kContext);
2681 Node*
const receiver = Parameter(Descriptor::kReceiver);
2682 Generate(context, receiver,
"String.prototype.fixed",
"tt");
2686 TF_BUILTIN(StringPrototypeItalics, StringHtmlAssembler) {
2687 Node*
const context = Parameter(Descriptor::kContext);
2688 Node*
const receiver = Parameter(Descriptor::kReceiver);
2689 Generate(context, receiver,
"String.prototype.italics",
"i");
2693 TF_BUILTIN(StringPrototypeLink, StringHtmlAssembler) {
2694 Node*
const context = Parameter(Descriptor::kContext);
2695 Node*
const receiver = Parameter(Descriptor::kReceiver);
2696 Node*
const value = Parameter(Descriptor::kValue);
2697 GenerateWithAttribute(context, receiver,
"String.prototype.link",
"a",
"href",
2702 TF_BUILTIN(StringPrototypeSmall, StringHtmlAssembler) {
2703 Node*
const context = Parameter(Descriptor::kContext);
2704 Node*
const receiver = Parameter(Descriptor::kReceiver);
2705 Generate(context, receiver,
"String.prototype.small",
"small");
2709 TF_BUILTIN(StringPrototypeStrike, StringHtmlAssembler) {
2710 Node*
const context = Parameter(Descriptor::kContext);
2711 Node*
const receiver = Parameter(Descriptor::kReceiver);
2712 Generate(context, receiver,
"String.prototype.strike",
"strike");
2716 TF_BUILTIN(StringPrototypeSub, StringHtmlAssembler) {
2717 Node*
const context = Parameter(Descriptor::kContext);
2718 Node*
const receiver = Parameter(Descriptor::kReceiver);
2719 Generate(context, receiver,
"String.prototype.sub",
"sub");
2723 TF_BUILTIN(StringPrototypeSup, StringHtmlAssembler) {
2724 Node*
const context = Parameter(Descriptor::kContext);
2725 Node*
const receiver = Parameter(Descriptor::kReceiver);
2726 Generate(context, receiver,
"String.prototype.sup",
"sup");