1 // Copyright 2017 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "src/builtins/builtins-string-gen.h"
6 
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"
13 
14 namespace v8 {
15 namespace internal {
16 
17 typedef compiler::Node Node;
18 template <class T>
19 using TNode = compiler::TNode<T>;
20 
DirectStringData(Node * string,Node * string_instance_type)21 Node* StringBuiltinsAssembler::DirectStringData(Node* string,
22                                                 Node* string_instance_type) {
23   // Compute the effective offset of the first character.
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);
30 
31   BIND(&if_sequential);
32   {
33     var_data.Bind(IntPtrAdd(
34         IntPtrConstant(SeqOneByteString::kHeaderSize - kHeapObjectTag),
35         BitcastTaggedToWord(string)));
36     Goto(&if_join);
37   }
38 
39   BIND(&if_external);
40   {
41     // This is only valid for ExternalStrings where the resource data
42     // pointer is cached (i.e. no short external strings).
43     CSA_ASSERT(
44         this, Word32NotEqual(Word32And(string_instance_type,
45                                        Int32Constant(kShortExternalStringMask)),
46                              Int32Constant(kShortExternalStringTag)));
47     var_data.Bind(LoadObjectField(string, ExternalString::kResourceDataOffset,
48                                   MachineType::Pointer()));
49     Goto(&if_join);
50   }
51 
52   BIND(&if_join);
53   return var_data.value();
54 }
55 
DispatchOnStringEncodings(Node * const lhs_instance_type,Node * const rhs_instance_type,Label * if_one_one,Label * if_one_two,Label * if_two_one,Label * if_two_two)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,
59     Label* if_two_two) {
60   STATIC_ASSERT(kStringEncodingMask == 0x8);
61   STATIC_ASSERT(kTwoByteStringTag == 0x0);
62   STATIC_ASSERT(kOneByteStringTag == 0x8);
63 
64   // First combine the encodings.
65 
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);
69 
70   Node* const combined_encodings =
71       Word32Or(lhs_encoding, Word32Shr(rhs_encoding, 1));
72 
73   // Then dispatch on the combined encoding.
74 
75   Label unreachable(this, Label::kDeferred);
76 
77   int32_t values[] = {
78       kOneByteStringTag | (kOneByteStringTag >> 1),
79       kOneByteStringTag | (kTwoByteStringTag >> 1),
80       kTwoByteStringTag | (kOneByteStringTag >> 1),
81       kTwoByteStringTag | (kTwoByteStringTag >> 1),
82   };
83   Label* labels[] = {
84       if_one_one, if_one_two, if_two_one, if_two_two,
85   };
86 
87   STATIC_ASSERT(arraysize(values) == arraysize(labels));
88   Switch(combined_encodings, &unreachable, values, labels, arraysize(values));
89 
90   BIND(&unreachable);
91   Unreachable();
92 }
93 
94 template <typename SubjectChar, typename PatternChar>
CallSearchStringRaw(Node * const subject_ptr,Node * const subject_length,Node * const search_ptr,Node * const search_length,Node * const start_position)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()));
104 
105   MachineType type_ptr = MachineType::Pointer();
106   MachineType type_intptr = MachineType::IntPtr();
107 
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);
112 
113   return result;
114 }
115 
PointerToStringDataAtIndex(Node * const string_data,Node * const index,String::Encoding encoding)116 Node* StringBuiltinsAssembler::PointerToStringDataAtIndex(
117     Node* const string_data, Node* const index, String::Encoding encoding) {
118   const ElementsKind kind = (encoding == String::ONE_BYTE_ENCODING)
119                                 ? UINT8_ELEMENTS
120                                 : UINT16_ELEMENTS;
121   Node* const offset_in_bytes =
122       ElementOffsetFromIndex(index, kind, INTPTR_PARAMETERS);
123   return IntPtrAdd(string_data, offset_in_bytes);
124 }
125 
GenerateStringEqual(Node * context,Node * left,Node * right)126 void StringBuiltinsAssembler::GenerateStringEqual(Node* context, Node* left,
127                                                   Node* right) {
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});
132 
133   TNode<IntPtrT> lhs_length = LoadStringLengthAsWord(left);
134   TNode<IntPtrT> rhs_length = LoadStringLengthAsWord(right);
135 
136   // Strings with different lengths cannot be equal.
137   GotoIf(WordNotEqual(lhs_length, rhs_length), &if_notequal);
138 
139   Goto(&restart);
140   BIND(&restart);
141   Node* lhs = var_left.value();
142   Node* rhs = var_right.value();
143 
144   Node* lhs_instance_type = LoadInstanceType(lhs);
145   Node* rhs_instance_type = LoadInstanceType(rhs);
146 
147   StringEqual_Core(context, lhs, lhs_instance_type, rhs, rhs_instance_type,
148                    lhs_length, &if_equal, &if_notequal, &if_indirect);
149 
150   BIND(&if_indirect);
151   {
152     // Try to unwrap indirect strings, restart the above attempt on success.
153     MaybeDerefIndirectStrings(&var_left, lhs_instance_type, &var_right,
154                               rhs_instance_type, &restart);
155 
156     TailCallRuntime(Runtime::kStringEqual, context, lhs, rhs);
157   }
158 
159   BIND(&if_equal);
160   Return(TrueConstant());
161 
162   BIND(&if_notequal);
163   Return(FalseConstant());
164 }
165 
StringEqual_Core(Node * context,Node * lhs,Node * lhs_instance_type,Node * rhs,Node * rhs_instance_type,TNode<IntPtrT> length,Label * if_equal,Label * if_not_equal,Label * if_indirect)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));
174   // Fast check to see if {lhs} and {rhs} refer to the same String object.
175   GotoIf(WordEqual(lhs, rhs), if_equal);
176 
177   // Combine the instance types into a single 16-bit value, so we can check
178   // both of them at once.
179   Node* both_instance_types = Word32Or(
180       lhs_instance_type, Word32Shl(rhs_instance_type, Int32Constant(8)));
181 
182   // Check if both {lhs} and {rhs} are internalized. Since we already know
183   // that they're not the same object, they're not equal in that case.
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)),
190          if_not_equal);
191 
192   // Check if both {lhs} and {rhs} are direct strings, and that in case of
193   // ExternalStrings the data pointer is cached.
194   STATIC_ASSERT(kShortExternalStringTag != 0);
195   STATIC_ASSERT(kIsIndirectStringTag != 0);
196   int const kBothDirectStringMask =
197       kIsIndirectStringMask | kShortExternalStringMask |
198       ((kIsIndirectStringMask | kShortExternalStringMask) << 8);
199   GotoIfNot(Word32Equal(Word32And(both_instance_types,
200                                   Int32Constant(kBothDirectStringMask)),
201                         Int32Constant(0)),
202             if_indirect);
203 
204   // Dispatch based on the {lhs} and {rhs} string encoding.
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));
214   GotoIf(
215       Word32Equal(masked_instance_types, Int32Constant(kOneOneByteStringTag)),
216       &if_oneonebytestring);
217   GotoIf(
218       Word32Equal(masked_instance_types, Int32Constant(kTwoTwoByteStringTag)),
219       &if_twotwobytestring);
220   Branch(
221       Word32Equal(masked_instance_types, Int32Constant(kOneTwoByteStringTag)),
222       &if_onetwobytestring, &if_twoonebytestring);
223 
224   BIND(&if_oneonebytestring);
225   StringEqual_Loop(lhs, lhs_instance_type, MachineType::Uint8(), rhs,
226                    rhs_instance_type, MachineType::Uint8(), length, if_equal,
227                    if_not_equal);
228 
229   BIND(&if_twotwobytestring);
230   StringEqual_Loop(lhs, lhs_instance_type, MachineType::Uint16(), rhs,
231                    rhs_instance_type, MachineType::Uint16(), length, if_equal,
232                    if_not_equal);
233 
234   BIND(&if_onetwobytestring);
235   StringEqual_Loop(lhs, lhs_instance_type, MachineType::Uint8(), rhs,
236                    rhs_instance_type, MachineType::Uint16(), length, if_equal,
237                    if_not_equal);
238 
239   BIND(&if_twoonebytestring);
240   StringEqual_Loop(lhs, lhs_instance_type, MachineType::Uint16(), rhs,
241                    rhs_instance_type, MachineType::Uint8(), length, if_equal,
242                    if_not_equal);
243 }
244 
StringEqual_Loop(Node * lhs,Node * lhs_instance_type,MachineType lhs_type,Node * rhs,Node * rhs_instance_type,MachineType rhs_type,TNode<IntPtrT> length,Label * if_equal,Label * if_not_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));
253 
254   // Compute the effective offset of the first character.
255   Node* lhs_data = DirectStringData(lhs, lhs_instance_type);
256   Node* rhs_data = DirectStringData(rhs, rhs_instance_type);
257 
258   // Loop over the {lhs} and {rhs} strings to see if they are equal.
259   TVARIABLE(IntPtrT, var_offset, IntPtrConstant(0));
260   Label loop(this, &var_offset);
261   Goto(&loop);
262   BIND(&loop);
263   {
264     // If {offset} equals {end}, no difference was found, so the
265     // strings are equal.
266     GotoIf(WordEqual(var_offset.value(), length), if_equal);
267 
268     // Load the next characters from {lhs} and {rhs}.
269     Node* lhs_value =
270         Load(lhs_type, lhs_data,
271              WordShl(var_offset.value(),
272                      ElementSizeLog2Of(lhs_type.representation())));
273     Node* rhs_value =
274         Load(rhs_type, rhs_data,
275              WordShl(var_offset.value(),
276                      ElementSizeLog2Of(rhs_type.representation())));
277 
278     // Check if the characters match.
279     GotoIf(Word32NotEqual(lhs_value, rhs_value), if_not_equal);
280 
281     // Advance to next character.
282     var_offset = IntPtrAdd(var_offset.value(), IntPtrConstant(1));
283     Goto(&loop);
284   }
285 }
286 
Generate_StringAdd(StringAddFlags flags,PretenureFlag pretenure_flag,Node * context,Node * left,Node * right)287 void StringBuiltinsAssembler::Generate_StringAdd(StringAddFlags flags,
288                                                  PretenureFlag pretenure_flag,
289                                                  Node* context, Node* left,
290                                                  Node* right) {
291   switch (flags) {
292     case STRING_ADD_CONVERT_LEFT: {
293       // TODO(danno): The ToString and JSReceiverToPrimitive below could be
294       // combined to avoid duplicate smi and instance type checks.
295       left = ToString(context, JSReceiverToPrimitive(context, left));
296       Callable callable = CodeFactory::StringAdd(
297           isolate(), STRING_ADD_CHECK_NONE, pretenure_flag);
298       TailCallStub(callable, context, left, right);
299       break;
300     }
301     case STRING_ADD_CONVERT_RIGHT: {
302       // TODO(danno): The ToString and JSReceiverToPrimitive below could be
303       // combined to avoid duplicate smi and instance type checks.
304       right = ToString(context, JSReceiverToPrimitive(context, right));
305       Callable callable = CodeFactory::StringAdd(
306           isolate(), STRING_ADD_CHECK_NONE, pretenure_flag);
307       TailCallStub(callable, context, left, right);
308       break;
309     }
310     case STRING_ADD_CHECK_NONE: {
311       CodeStubAssembler::AllocationFlag allocation_flags =
312           (pretenure_flag == TENURED) ? CodeStubAssembler::kPretenured
313                                       : CodeStubAssembler::kNone;
314       Return(StringAdd(context, CAST(left), CAST(right), allocation_flags));
315       break;
316     }
317   }
318 }
319 
TF_BUILTIN(StringAdd_CheckNone_NotTenured,StringBuiltinsAssembler)320 TF_BUILTIN(StringAdd_CheckNone_NotTenured, StringBuiltinsAssembler) {
321   Node* left = Parameter(Descriptor::kLeft);
322   Node* right = Parameter(Descriptor::kRight);
323   Node* context = Parameter(Descriptor::kContext);
324   Generate_StringAdd(STRING_ADD_CHECK_NONE, NOT_TENURED, context, left, right);
325 }
326 
TF_BUILTIN(StringAdd_CheckNone_Tenured,StringBuiltinsAssembler)327 TF_BUILTIN(StringAdd_CheckNone_Tenured, StringBuiltinsAssembler) {
328   Node* left = Parameter(Descriptor::kLeft);
329   Node* right = Parameter(Descriptor::kRight);
330   Node* context = Parameter(Descriptor::kContext);
331   Generate_StringAdd(STRING_ADD_CHECK_NONE, TENURED, context, left, right);
332 }
333 
TF_BUILTIN(StringAdd_ConvertLeft_NotTenured,StringBuiltinsAssembler)334 TF_BUILTIN(StringAdd_ConvertLeft_NotTenured, StringBuiltinsAssembler) {
335   Node* left = Parameter(Descriptor::kLeft);
336   Node* right = Parameter(Descriptor::kRight);
337   Node* context = Parameter(Descriptor::kContext);
338   Generate_StringAdd(STRING_ADD_CONVERT_LEFT, NOT_TENURED, context, left,
339                      right);
340 }
341 
TF_BUILTIN(StringAdd_ConvertRight_NotTenured,StringBuiltinsAssembler)342 TF_BUILTIN(StringAdd_ConvertRight_NotTenured, StringBuiltinsAssembler) {
343   Node* left = Parameter(Descriptor::kLeft);
344   Node* right = Parameter(Descriptor::kRight);
345   Node* context = Parameter(Descriptor::kContext);
346   Generate_StringAdd(STRING_ADD_CONVERT_RIGHT, NOT_TENURED, context, left,
347                      right);
348 }
349 
GenerateStringAt(char const * method_name,TNode<Context> context,Node * receiver,TNode<Object> maybe_position,TNode<Object> default_return,StringAtAccessor accessor)350 void StringBuiltinsAssembler::GenerateStringAt(char const* method_name,
351                                                TNode<Context> context,
352                                                Node* receiver,
353                                                TNode<Object> maybe_position,
354                                                TNode<Object> default_return,
355                                                StringAtAccessor accessor) {
356   // Check that {receiver} is coercible to Object and convert it to a String.
357   TNode<String> string = ToThisString(context, receiver, method_name);
358 
359   // Convert the {position} to a Smi and check that it's in bounds of the
360   // {string}.
361   Label if_outofbounds(this, Label::kDeferred);
362   TNode<Number> position = ToInteger_Inline(
363       context, maybe_position, CodeStubAssembler::kTruncateMinusZero);
364   GotoIfNot(TaggedIsSmi(position), &if_outofbounds);
365   TNode<IntPtrT> index = SmiUntag(CAST(position));
366   TNode<IntPtrT> length = LoadStringLengthAsWord(string);
367   GotoIfNot(UintPtrLessThan(index, length), &if_outofbounds);
368   TNode<Object> result = accessor(string, length, index);
369   Return(result);
370 
371   BIND(&if_outofbounds);
372   Return(default_return);
373 }
374 
GenerateStringRelationalComparison(Node * context,Node * left,Node * right,Operation op)375 void StringBuiltinsAssembler::GenerateStringRelationalComparison(Node* context,
376                                                                  Node* left,
377                                                                  Node* right,
378                                                                  Operation op) {
379   VARIABLE(var_left, MachineRepresentation::kTagged, left);
380   VARIABLE(var_right, MachineRepresentation::kTagged, right);
381 
382   Variable* input_vars[2] = {&var_left, &var_right};
383   Label if_less(this), if_equal(this), if_greater(this);
384   Label restart(this, 2, input_vars);
385   Goto(&restart);
386   BIND(&restart);
387 
388   Node* lhs = var_left.value();
389   Node* rhs = var_right.value();
390   // Fast check to see if {lhs} and {rhs} refer to the same String object.
391   GotoIf(WordEqual(lhs, rhs), &if_equal);
392 
393   // Load instance types of {lhs} and {rhs}.
394   Node* lhs_instance_type = LoadInstanceType(lhs);
395   Node* rhs_instance_type = LoadInstanceType(rhs);
396 
397   // Combine the instance types into a single 16-bit value, so we can check
398   // both of them at once.
399   Node* both_instance_types = Word32Or(
400       lhs_instance_type, Word32Shl(rhs_instance_type, Int32Constant(8)));
401 
402   // Check that both {lhs} and {rhs} are flat one-byte strings.
403   int const kBothSeqOneByteStringMask =
404       kStringEncodingMask | kStringRepresentationMask |
405       ((kStringEncodingMask | kStringRepresentationMask) << 8);
406   int const kBothSeqOneByteStringTag =
407       kOneByteStringTag | kSeqStringTag |
408       ((kOneByteStringTag | kSeqStringTag) << 8);
409   Label if_bothonebyteseqstrings(this), if_notbothonebyteseqstrings(this);
410   Branch(Word32Equal(Word32And(both_instance_types,
411                                Int32Constant(kBothSeqOneByteStringMask)),
412                      Int32Constant(kBothSeqOneByteStringTag)),
413          &if_bothonebyteseqstrings, &if_notbothonebyteseqstrings);
414 
415   BIND(&if_bothonebyteseqstrings);
416   {
417     // Load the length of {lhs} and {rhs}.
418     TNode<IntPtrT> lhs_length = LoadStringLengthAsWord(lhs);
419     TNode<IntPtrT> rhs_length = LoadStringLengthAsWord(rhs);
420 
421     // Determine the minimum length.
422     TNode<IntPtrT> length = IntPtrMin(lhs_length, rhs_length);
423 
424     // Compute the effective offset of the first character.
425     TNode<IntPtrT> begin =
426         IntPtrConstant(SeqOneByteString::kHeaderSize - kHeapObjectTag);
427 
428     // Compute the first offset after the string from the length.
429     TNode<IntPtrT> end = IntPtrAdd(begin, length);
430 
431     // Loop over the {lhs} and {rhs} strings to see if they are equal.
432     TVARIABLE(IntPtrT, var_offset, begin);
433     Label loop(this, &var_offset);
434     Goto(&loop);
435     BIND(&loop);
436     {
437       // Check if {offset} equals {end}.
438       Label if_done(this), if_notdone(this);
439       Branch(WordEqual(var_offset.value(), end), &if_done, &if_notdone);
440 
441       BIND(&if_notdone);
442       {
443         // Load the next characters from {lhs} and {rhs}.
444         Node* lhs_value = Load(MachineType::Uint8(), lhs, var_offset.value());
445         Node* rhs_value = Load(MachineType::Uint8(), rhs, var_offset.value());
446 
447         // Check if the characters match.
448         Label if_valueissame(this), if_valueisnotsame(this);
449         Branch(Word32Equal(lhs_value, rhs_value), &if_valueissame,
450                &if_valueisnotsame);
451 
452         BIND(&if_valueissame);
453         {
454           // Advance to next character.
455           var_offset = IntPtrAdd(var_offset.value(), IntPtrConstant(1));
456         }
457         Goto(&loop);
458 
459         BIND(&if_valueisnotsame);
460         Branch(Uint32LessThan(lhs_value, rhs_value), &if_less, &if_greater);
461       }
462 
463       BIND(&if_done);
464       {
465         // All characters up to the min length are equal, decide based on
466         // string length.
467         GotoIf(IntPtrEqual(lhs_length, rhs_length), &if_equal);
468         Branch(IntPtrLessThan(lhs_length, rhs_length), &if_less, &if_greater);
469       }
470     }
471   }
472 
473   BIND(&if_notbothonebyteseqstrings);
474   {
475     // Try to unwrap indirect strings, restart the above attempt on success.
476     MaybeDerefIndirectStrings(&var_left, lhs_instance_type, &var_right,
477                               rhs_instance_type, &restart);
478     // TODO(bmeurer): Add support for two byte string relational comparisons.
479     switch (op) {
480       case Operation::kLessThan:
481         TailCallRuntime(Runtime::kStringLessThan, context, lhs, rhs);
482         break;
483       case Operation::kLessThanOrEqual:
484         TailCallRuntime(Runtime::kStringLessThanOrEqual, context, lhs, rhs);
485         break;
486       case Operation::kGreaterThan:
487         TailCallRuntime(Runtime::kStringGreaterThan, context, lhs, rhs);
488         break;
489       case Operation::kGreaterThanOrEqual:
490         TailCallRuntime(Runtime::kStringGreaterThanOrEqual, context, lhs, rhs);
491         break;
492       default:
493         UNREACHABLE();
494     }
495   }
496 
497   BIND(&if_less);
498   switch (op) {
499     case Operation::kLessThan:
500     case Operation::kLessThanOrEqual:
501       Return(TrueConstant());
502       break;
503 
504     case Operation::kGreaterThan:
505     case Operation::kGreaterThanOrEqual:
506       Return(FalseConstant());
507       break;
508     default:
509       UNREACHABLE();
510   }
511 
512   BIND(&if_equal);
513   switch (op) {
514     case Operation::kLessThan:
515     case Operation::kGreaterThan:
516       Return(FalseConstant());
517       break;
518 
519     case Operation::kLessThanOrEqual:
520     case Operation::kGreaterThanOrEqual:
521       Return(TrueConstant());
522       break;
523     default:
524       UNREACHABLE();
525   }
526 
527   BIND(&if_greater);
528   switch (op) {
529     case Operation::kLessThan:
530     case Operation::kLessThanOrEqual:
531       Return(FalseConstant());
532       break;
533 
534     case Operation::kGreaterThan:
535     case Operation::kGreaterThanOrEqual:
536       Return(TrueConstant());
537       break;
538     default:
539       UNREACHABLE();
540   }
541 }
542 
TF_BUILTIN(StringEqual,StringBuiltinsAssembler)543 TF_BUILTIN(StringEqual, StringBuiltinsAssembler) {
544   Node* context = Parameter(Descriptor::kContext);
545   Node* left = Parameter(Descriptor::kLeft);
546   Node* right = Parameter(Descriptor::kRight);
547   GenerateStringEqual(context, left, right);
548 }
549 
TF_BUILTIN(StringLessThan,StringBuiltinsAssembler)550 TF_BUILTIN(StringLessThan, StringBuiltinsAssembler) {
551   Node* context = Parameter(Descriptor::kContext);
552   Node* left = Parameter(Descriptor::kLeft);
553   Node* right = Parameter(Descriptor::kRight);
554   GenerateStringRelationalComparison(context, left, right,
555                                      Operation::kLessThan);
556 }
557 
TF_BUILTIN(StringLessThanOrEqual,StringBuiltinsAssembler)558 TF_BUILTIN(StringLessThanOrEqual, StringBuiltinsAssembler) {
559   Node* context = Parameter(Descriptor::kContext);
560   Node* left = Parameter(Descriptor::kLeft);
561   Node* right = Parameter(Descriptor::kRight);
562   GenerateStringRelationalComparison(context, left, right,
563                                      Operation::kLessThanOrEqual);
564 }
565 
TF_BUILTIN(StringGreaterThan,StringBuiltinsAssembler)566 TF_BUILTIN(StringGreaterThan, StringBuiltinsAssembler) {
567   Node* context = Parameter(Descriptor::kContext);
568   Node* left = Parameter(Descriptor::kLeft);
569   Node* right = Parameter(Descriptor::kRight);
570   GenerateStringRelationalComparison(context, left, right,
571                                      Operation::kGreaterThan);
572 }
573 
TF_BUILTIN(StringGreaterThanOrEqual,StringBuiltinsAssembler)574 TF_BUILTIN(StringGreaterThanOrEqual, StringBuiltinsAssembler) {
575   Node* context = Parameter(Descriptor::kContext);
576   Node* left = Parameter(Descriptor::kLeft);
577   Node* right = Parameter(Descriptor::kRight);
578   GenerateStringRelationalComparison(context, left, right,
579                                      Operation::kGreaterThanOrEqual);
580 }
581 
TF_BUILTIN(StringCharAt,StringBuiltinsAssembler)582 TF_BUILTIN(StringCharAt, StringBuiltinsAssembler) {
583   Node* receiver = Parameter(Descriptor::kReceiver);
584   Node* position = Parameter(Descriptor::kPosition);
585 
586   // Load the character code at the {position} from the {receiver}.
587   TNode<Int32T> code = StringCharCodeAt(receiver, position);
588 
589   // And return the single character string with only that {code}
590   TNode<String> result = StringFromSingleCharCode(code);
591   Return(result);
592 }
593 
TF_BUILTIN(StringCodePointAtUTF16,StringBuiltinsAssembler)594 TF_BUILTIN(StringCodePointAtUTF16, StringBuiltinsAssembler) {
595   Node* receiver = Parameter(Descriptor::kReceiver);
596   Node* position = Parameter(Descriptor::kPosition);
597   // TODO(sigurds) Figure out if passing length as argument pays off.
598   TNode<IntPtrT> length = LoadStringLengthAsWord(receiver);
599   // Load the character code at the {position} from the {receiver}.
600   TNode<Int32T> code =
601       LoadSurrogatePairAt(receiver, length, position, UnicodeEncoding::UTF16);
602   // And return it as TaggedSigned value.
603   // TODO(turbofan): Allow builtins to return values untagged.
604   TNode<Smi> result = SmiFromInt32(code);
605   Return(result);
606 }
607 
TF_BUILTIN(StringCodePointAtUTF32,StringBuiltinsAssembler)608 TF_BUILTIN(StringCodePointAtUTF32, StringBuiltinsAssembler) {
609   Node* receiver = Parameter(Descriptor::kReceiver);
610   Node* position = Parameter(Descriptor::kPosition);
611 
612   // TODO(sigurds) Figure out if passing length as argument pays off.
613   TNode<IntPtrT> length = LoadStringLengthAsWord(receiver);
614   // Load the character code at the {position} from the {receiver}.
615   TNode<Int32T> code =
616       LoadSurrogatePairAt(receiver, length, position, UnicodeEncoding::UTF32);
617   // And return it as TaggedSigned value.
618   // TODO(turbofan): Allow builtins to return values untagged.
619   TNode<Smi> result = SmiFromInt32(code);
620   Return(result);
621 }
622 
623 // -----------------------------------------------------------------------------
624 // ES6 section 21.1 String Objects
625 
626 // ES6 #sec-string.fromcharcode
TF_BUILTIN(StringFromCharCode,CodeStubAssembler)627 TF_BUILTIN(StringFromCharCode, CodeStubAssembler) {
628   // TODO(ishell): use constants from Descriptor once the JSFunction linkage
629   // arguments are reordered.
630   TNode<Int32T> argc =
631       UncheckedCast<Int32T>(Parameter(BuiltinDescriptor::kArgumentsCount));
632   Node* context = Parameter(BuiltinDescriptor::kContext);
633 
634   CodeStubArguments arguments(this, ChangeInt32ToIntPtr(argc));
635   TNode<Smi> smi_argc = SmiTag(arguments.GetLength(INTPTR_PARAMETERS));
636   // Check if we have exactly one argument (plus the implicit receiver), i.e.
637   // if the parent frame is not an arguments adaptor frame.
638   Label if_oneargument(this), if_notoneargument(this);
639   Branch(Word32Equal(argc, Int32Constant(1)), &if_oneargument,
640          &if_notoneargument);
641 
642   BIND(&if_oneargument);
643   {
644     // Single argument case, perform fast single character string cache lookup
645     // for one-byte code units, or fall back to creating a single character
646     // string on the fly otherwise.
647     Node* code = arguments.AtIndex(0);
648     Node* code32 = TruncateTaggedToWord32(context, code);
649     TNode<Int32T> code16 =
650         Signed(Word32And(code32, Int32Constant(String::kMaxUtf16CodeUnit)));
651     Node* result = StringFromSingleCharCode(code16);
652     arguments.PopAndReturn(result);
653   }
654 
655   Node* code16 = nullptr;
656   BIND(&if_notoneargument);
657   {
658     Label two_byte(this);
659     // Assume that the resulting string contains only one-byte characters.
660     Node* one_byte_result = AllocateSeqOneByteString(context, smi_argc);
661 
662     TVARIABLE(IntPtrT, var_max_index);
663     var_max_index = IntPtrConstant(0);
664 
665     // Iterate over the incoming arguments, converting them to 8-bit character
666     // codes. Stop if any of the conversions generates a code that doesn't fit
667     // in 8 bits.
668     CodeStubAssembler::VariableList vars({&var_max_index}, zone());
669     arguments.ForEach(vars, [this, context, &two_byte, &var_max_index, &code16,
670                              one_byte_result](Node* arg) {
671       Node* code32 = TruncateTaggedToWord32(context, arg);
672       code16 = Word32And(code32, Int32Constant(String::kMaxUtf16CodeUnit));
673 
674       GotoIf(
675           Int32GreaterThan(code16, Int32Constant(String::kMaxOneByteCharCode)),
676           &two_byte);
677 
678       // The {code16} fits into the SeqOneByteString {one_byte_result}.
679       Node* offset = ElementOffsetFromIndex(
680           var_max_index.value(), UINT8_ELEMENTS,
681           CodeStubAssembler::INTPTR_PARAMETERS,
682           SeqOneByteString::kHeaderSize - kHeapObjectTag);
683       StoreNoWriteBarrier(MachineRepresentation::kWord8, one_byte_result,
684                           offset, code16);
685       var_max_index = IntPtrAdd(var_max_index.value(), IntPtrConstant(1));
686     });
687     arguments.PopAndReturn(one_byte_result);
688 
689     BIND(&two_byte);
690 
691     // At least one of the characters in the string requires a 16-bit
692     // representation.  Allocate a SeqTwoByteString to hold the resulting
693     // string.
694     Node* two_byte_result = AllocateSeqTwoByteString(context, smi_argc);
695 
696     // Copy the characters that have already been put in the 8-bit string into
697     // their corresponding positions in the new 16-bit string.
698     TNode<IntPtrT> zero = IntPtrConstant(0);
699     CopyStringCharacters(one_byte_result, two_byte_result, zero, zero,
700                          var_max_index.value(), String::ONE_BYTE_ENCODING,
701                          String::TWO_BYTE_ENCODING);
702 
703     // Write the character that caused the 8-bit to 16-bit fault.
704     Node* max_index_offset =
705         ElementOffsetFromIndex(var_max_index.value(), UINT16_ELEMENTS,
706                                CodeStubAssembler::INTPTR_PARAMETERS,
707                                SeqTwoByteString::kHeaderSize - kHeapObjectTag);
708     StoreNoWriteBarrier(MachineRepresentation::kWord16, two_byte_result,
709                         max_index_offset, code16);
710     var_max_index = IntPtrAdd(var_max_index.value(), IntPtrConstant(1));
711 
712     // Resume copying the passed-in arguments from the same place where the
713     // 8-bit copy stopped, but this time copying over all of the characters
714     // using a 16-bit representation.
715     arguments.ForEach(
716         vars,
717         [this, context, two_byte_result, &var_max_index](Node* arg) {
718           Node* code32 = TruncateTaggedToWord32(context, arg);
719           Node* code16 =
720               Word32And(code32, Int32Constant(String::kMaxUtf16CodeUnit));
721 
722           Node* offset = ElementOffsetFromIndex(
723               var_max_index.value(), UINT16_ELEMENTS,
724               CodeStubAssembler::INTPTR_PARAMETERS,
725               SeqTwoByteString::kHeaderSize - kHeapObjectTag);
726           StoreNoWriteBarrier(MachineRepresentation::kWord16, two_byte_result,
727                               offset, code16);
728           var_max_index = IntPtrAdd(var_max_index.value(), IntPtrConstant(1));
729         },
730         var_max_index.value());
731 
732     arguments.PopAndReturn(two_byte_result);
733   }
734 }
735 
736 // ES6 #sec-string.prototype.charat
TF_BUILTIN(StringPrototypeCharAt,StringBuiltinsAssembler)737 TF_BUILTIN(StringPrototypeCharAt, StringBuiltinsAssembler) {
738   TNode<Context> context = CAST(Parameter(Descriptor::kContext));
739   Node* receiver = Parameter(Descriptor::kReceiver);
740   TNode<Object> maybe_position = CAST(Parameter(Descriptor::kPosition));
741 
742   GenerateStringAt("String.prototype.charAt", context, receiver, maybe_position,
743                    EmptyStringConstant(),
744                    [this](TNode<String> string, TNode<IntPtrT> length,
745                           TNode<IntPtrT> index) {
746                      TNode<Int32T> code = StringCharCodeAt(string, index);
747                      return StringFromSingleCharCode(code);
748                    });
749 }
750 
751 // ES6 #sec-string.prototype.charcodeat
TF_BUILTIN(StringPrototypeCharCodeAt,StringBuiltinsAssembler)752 TF_BUILTIN(StringPrototypeCharCodeAt, StringBuiltinsAssembler) {
753   TNode<Context> context = CAST(Parameter(Descriptor::kContext));
754   Node* receiver = Parameter(Descriptor::kReceiver);
755   TNode<Object> maybe_position = CAST(Parameter(Descriptor::kPosition));
756 
757   GenerateStringAt("String.prototype.charCodeAt", context, receiver,
758                    maybe_position, NanConstant(),
759                    [this](TNode<String> receiver, TNode<IntPtrT> length,
760                           TNode<IntPtrT> index) {
761                      Node* value = StringCharCodeAt(receiver, index);
762                      return SmiFromInt32(value);
763                    });
764 }
765 
766 // ES6 #sec-string.prototype.codepointat
TF_BUILTIN(StringPrototypeCodePointAt,StringBuiltinsAssembler)767 TF_BUILTIN(StringPrototypeCodePointAt, StringBuiltinsAssembler) {
768   TNode<Context> context = CAST(Parameter(Descriptor::kContext));
769   Node* receiver = Parameter(Descriptor::kReceiver);
770   TNode<Object> maybe_position = CAST(Parameter(Descriptor::kPosition));
771 
772   GenerateStringAt("String.prototype.codePointAt", context, receiver,
773                    maybe_position, UndefinedConstant(),
774                    [this](TNode<String> receiver, TNode<IntPtrT> length,
775                           TNode<IntPtrT> index) {
776                      // This is always a call to a builtin from Javascript,
777                      // so we need to produce UTF32.
778                      Node* value = LoadSurrogatePairAt(receiver, length, index,
779                                                        UnicodeEncoding::UTF32);
780                      return SmiFromInt32(value);
781                    });
782 }
783 
784 // ES6 String.prototype.concat(...args)
785 // ES6 #sec-string.prototype.concat
TF_BUILTIN(StringPrototypeConcat,CodeStubAssembler)786 TF_BUILTIN(StringPrototypeConcat, CodeStubAssembler) {
787   // TODO(ishell): use constants from Descriptor once the JSFunction linkage
788   // arguments are reordered.
789   CodeStubArguments arguments(
790       this, ChangeInt32ToIntPtr(Parameter(BuiltinDescriptor::kArgumentsCount)));
791   Node* receiver = arguments.GetReceiver();
792   Node* context = Parameter(BuiltinDescriptor::kContext);
793 
794   // Check that {receiver} is coercible to Object and convert it to a String.
795   receiver = ToThisString(context, receiver, "String.prototype.concat");
796 
797   // Concatenate all the arguments passed to this builtin.
798   VARIABLE(var_result, MachineRepresentation::kTagged);
799   var_result.Bind(receiver);
800   arguments.ForEach(
801       CodeStubAssembler::VariableList({&var_result}, zone()),
802       [this, context, &var_result](Node* arg) {
803         arg = ToString_Inline(context, arg);
804         var_result.Bind(CallStub(CodeFactory::StringAdd(isolate()), context,
805                                  var_result.value(), arg));
806       });
807   arguments.PopAndReturn(var_result.value());
808 }
809 
StringIndexOf(Node * const subject_string,Node * const search_string,Node * const position,std::function<void (Node *)> f_return)810 void StringBuiltinsAssembler::StringIndexOf(
811     Node* const subject_string, Node* const search_string, Node* const position,
812     std::function<void(Node*)> f_return) {
813   CSA_ASSERT(this, IsString(subject_string));
814   CSA_ASSERT(this, IsString(search_string));
815   CSA_ASSERT(this, TaggedIsSmi(position));
816 
817   TNode<IntPtrT> const int_zero = IntPtrConstant(0);
818   TNode<IntPtrT> const search_length = LoadStringLengthAsWord(search_string);
819   TNode<IntPtrT> const subject_length = LoadStringLengthAsWord(subject_string);
820   TNode<IntPtrT> const start_position = IntPtrMax(SmiUntag(position), int_zero);
821 
822   Label zero_length_needle(this), return_minus_1(this);
823   {
824     GotoIf(IntPtrEqual(int_zero, search_length), &zero_length_needle);
825 
826     // Check that the needle fits in the start position.
827     GotoIfNot(IntPtrLessThanOrEqual(search_length,
828                                     IntPtrSub(subject_length, start_position)),
829               &return_minus_1);
830   }
831 
832   // If the string pointers are identical, we can just return 0. Note that this
833   // implies {start_position} == 0 since we've passed the check above.
834   Label return_zero(this);
835   GotoIf(WordEqual(subject_string, search_string), &return_zero);
836 
837   // Try to unpack subject and search strings. Bail to runtime if either needs
838   // to be flattened.
839   ToDirectStringAssembler subject_to_direct(state(), subject_string);
840   ToDirectStringAssembler search_to_direct(state(), search_string);
841 
842   Label call_runtime_unchecked(this, Label::kDeferred);
843 
844   subject_to_direct.TryToDirect(&call_runtime_unchecked);
845   search_to_direct.TryToDirect(&call_runtime_unchecked);
846 
847   // Load pointers to string data.
848   Node* const subject_ptr =
849       subject_to_direct.PointerToData(&call_runtime_unchecked);
850   Node* const search_ptr =
851       search_to_direct.PointerToData(&call_runtime_unchecked);
852 
853   Node* const subject_offset = subject_to_direct.offset();
854   Node* const search_offset = search_to_direct.offset();
855 
856   // Like String::IndexOf, the actual matching is done by the optimized
857   // SearchString method in string-search.h. Dispatch based on string instance
858   // types, then call straight into C++ for matching.
859 
860   CSA_ASSERT(this, IntPtrGreaterThan(search_length, int_zero));
861   CSA_ASSERT(this, IntPtrGreaterThanOrEqual(start_position, int_zero));
862   CSA_ASSERT(this, IntPtrGreaterThanOrEqual(subject_length, start_position));
863   CSA_ASSERT(this,
864              IntPtrLessThanOrEqual(search_length,
865                                    IntPtrSub(subject_length, start_position)));
866 
867   Label one_one(this), one_two(this), two_one(this), two_two(this);
868   DispatchOnStringEncodings(subject_to_direct.instance_type(),
869                             search_to_direct.instance_type(), &one_one,
870                             &one_two, &two_one, &two_two);
871 
872   typedef const uint8_t onebyte_t;
873   typedef const uc16 twobyte_t;
874 
875   BIND(&one_one);
876   {
877     Node* const adjusted_subject_ptr = PointerToStringDataAtIndex(
878         subject_ptr, subject_offset, String::ONE_BYTE_ENCODING);
879     Node* const adjusted_search_ptr = PointerToStringDataAtIndex(
880         search_ptr, search_offset, String::ONE_BYTE_ENCODING);
881 
882     Label direct_memchr_call(this), generic_fast_path(this);
883     Branch(IntPtrEqual(search_length, IntPtrConstant(1)), &direct_memchr_call,
884            &generic_fast_path);
885 
886     // An additional fast path that calls directly into memchr for 1-length
887     // search strings.
888     BIND(&direct_memchr_call);
889     {
890       Node* const string_addr = IntPtrAdd(adjusted_subject_ptr, start_position);
891       Node* const search_length = IntPtrSub(subject_length, start_position);
892       Node* const search_byte =
893           ChangeInt32ToIntPtr(Load(MachineType::Uint8(), adjusted_search_ptr));
894 
895       Node* const memchr =
896           ExternalConstant(ExternalReference::libc_memchr_function());
897       Node* const result_address =
898           CallCFunction3(MachineType::Pointer(), MachineType::Pointer(),
899                          MachineType::IntPtr(), MachineType::UintPtr(), memchr,
900                          string_addr, search_byte, search_length);
901       GotoIf(WordEqual(result_address, int_zero), &return_minus_1);
902       Node* const result_index =
903           IntPtrAdd(IntPtrSub(result_address, string_addr), start_position);
904       f_return(SmiTag(result_index));
905     }
906 
907     BIND(&generic_fast_path);
908     {
909       Node* const result = CallSearchStringRaw<onebyte_t, onebyte_t>(
910           adjusted_subject_ptr, subject_length, adjusted_search_ptr,
911           search_length, start_position);
912       f_return(SmiTag(result));
913     }
914   }
915 
916   BIND(&one_two);
917   {
918     Node* const adjusted_subject_ptr = PointerToStringDataAtIndex(
919         subject_ptr, subject_offset, String::ONE_BYTE_ENCODING);
920     Node* const adjusted_search_ptr = PointerToStringDataAtIndex(
921         search_ptr, search_offset, String::TWO_BYTE_ENCODING);
922 
923     Node* const result = CallSearchStringRaw<onebyte_t, twobyte_t>(
924         adjusted_subject_ptr, subject_length, adjusted_search_ptr,
925         search_length, start_position);
926     f_return(SmiTag(result));
927   }
928 
929   BIND(&two_one);
930   {
931     Node* const adjusted_subject_ptr = PointerToStringDataAtIndex(
932         subject_ptr, subject_offset, String::TWO_BYTE_ENCODING);
933     Node* const adjusted_search_ptr = PointerToStringDataAtIndex(
934         search_ptr, search_offset, String::ONE_BYTE_ENCODING);
935 
936     Node* const result = CallSearchStringRaw<twobyte_t, onebyte_t>(
937         adjusted_subject_ptr, subject_length, adjusted_search_ptr,
938         search_length, start_position);
939     f_return(SmiTag(result));
940   }
941 
942   BIND(&two_two);
943   {
944     Node* const adjusted_subject_ptr = PointerToStringDataAtIndex(
945         subject_ptr, subject_offset, String::TWO_BYTE_ENCODING);
946     Node* const adjusted_search_ptr = PointerToStringDataAtIndex(
947         search_ptr, search_offset, String::TWO_BYTE_ENCODING);
948 
949     Node* const result = CallSearchStringRaw<twobyte_t, twobyte_t>(
950         adjusted_subject_ptr, subject_length, adjusted_search_ptr,
951         search_length, start_position);
952     f_return(SmiTag(result));
953   }
954 
955   BIND(&return_minus_1);
956   f_return(SmiConstant(-1));
957 
958   BIND(&return_zero);
959   f_return(SmiConstant(0));
960 
961   BIND(&zero_length_needle);
962   {
963     Comment("0-length search_string");
964     f_return(SmiTag(IntPtrMin(subject_length, start_position)));
965   }
966 
967   BIND(&call_runtime_unchecked);
968   {
969     // Simplified version of the runtime call where the types of the arguments
970     // are already known due to type checks in this stub.
971     Comment("Call Runtime Unchecked");
972     Node* result =
973         CallRuntime(Runtime::kStringIndexOfUnchecked, NoContextConstant(),
974                     subject_string, search_string, position);
975     f_return(result);
976   }
977 }
978 
979 // ES6 String.prototype.indexOf(searchString [, position])
980 // #sec-string.prototype.indexof
981 // Unchecked helper for builtins lowering.
TF_BUILTIN(StringIndexOf,StringBuiltinsAssembler)982 TF_BUILTIN(StringIndexOf, StringBuiltinsAssembler) {
983   Node* receiver = Parameter(Descriptor::kReceiver);
984   Node* search_string = Parameter(Descriptor::kSearchString);
985   Node* position = Parameter(Descriptor::kPosition);
986   StringIndexOf(receiver, search_string, position,
987                 [this](Node* result) { this->Return(result); });
988 }
989 
990 // ES6 String.prototype.includes(searchString [, position])
991 // #sec-string.prototype.includes
TF_BUILTIN(StringPrototypeIncludes,StringIncludesIndexOfAssembler)992 TF_BUILTIN(StringPrototypeIncludes, StringIncludesIndexOfAssembler) {
993   Generate(kIncludes);
994 }
995 
996 // ES6 String.prototype.indexOf(searchString [, position])
997 // #sec-string.prototype.indexof
TF_BUILTIN(StringPrototypeIndexOf,StringIncludesIndexOfAssembler)998 TF_BUILTIN(StringPrototypeIndexOf, StringIncludesIndexOfAssembler) {
999   Generate(kIndexOf);
1000 }
1001 
Generate(SearchVariant variant)1002 void StringIncludesIndexOfAssembler::Generate(SearchVariant variant) {
1003   // TODO(ishell): use constants from Descriptor once the JSFunction linkage
1004   // arguments are reordered.
1005   Node* argc = Parameter(BuiltinDescriptor::kArgumentsCount);
1006   Node* const context = Parameter(BuiltinDescriptor::kContext);
1007   CodeStubArguments arguments(this, ChangeInt32ToIntPtr(argc));
1008   Node* const receiver = arguments.GetReceiver();
1009   // From now on use word-size argc value.
1010   argc = arguments.GetLength(INTPTR_PARAMETERS);
1011 
1012   VARIABLE(var_search_string, MachineRepresentation::kTagged);
1013   VARIABLE(var_position, MachineRepresentation::kTagged);
1014   Label argc_1(this), argc_2(this), call_runtime(this, Label::kDeferred),
1015       fast_path(this);
1016 
1017   GotoIf(IntPtrEqual(argc, IntPtrConstant(1)), &argc_1);
1018   GotoIf(IntPtrGreaterThan(argc, IntPtrConstant(1)), &argc_2);
1019   {
1020     Comment("0 Argument case");
1021     CSA_ASSERT(this, IntPtrEqual(argc, IntPtrConstant(0)));
1022     Node* const undefined = UndefinedConstant();
1023     var_search_string.Bind(undefined);
1024     var_position.Bind(undefined);
1025     Goto(&call_runtime);
1026   }
1027   BIND(&argc_1);
1028   {
1029     Comment("1 Argument case");
1030     var_search_string.Bind(arguments.AtIndex(0));
1031     var_position.Bind(SmiConstant(0));
1032     Goto(&fast_path);
1033   }
1034   BIND(&argc_2);
1035   {
1036     Comment("2 Argument case");
1037     var_search_string.Bind(arguments.AtIndex(0));
1038     var_position.Bind(arguments.AtIndex(1));
1039     GotoIfNot(TaggedIsSmi(var_position.value()), &call_runtime);
1040     Goto(&fast_path);
1041   }
1042   BIND(&fast_path);
1043   {
1044     Comment("Fast Path");
1045     Node* const search = var_search_string.value();
1046     Node* const position = var_position.value();
1047     GotoIf(TaggedIsSmi(receiver), &call_runtime);
1048     GotoIf(TaggedIsSmi(search), &call_runtime);
1049     GotoIfNot(IsString(receiver), &call_runtime);
1050     GotoIfNot(IsString(search), &call_runtime);
1051 
1052     StringIndexOf(receiver, search, position, [&](Node* result) {
1053       CSA_ASSERT(this, TaggedIsSmi(result));
1054       arguments.PopAndReturn((variant == kIndexOf)
1055                                  ? result
1056                                  : SelectBooleanConstant(SmiGreaterThanOrEqual(
1057                                        CAST(result), SmiConstant(0))));
1058     });
1059   }
1060   BIND(&call_runtime);
1061   {
1062     Comment("Call Runtime");
1063     Runtime::FunctionId runtime = variant == kIndexOf
1064                                       ? Runtime::kStringIndexOf
1065                                       : Runtime::kStringIncludes;
1066     Node* const result =
1067         CallRuntime(runtime, context, receiver, var_search_string.value(),
1068                     var_position.value());
1069     arguments.PopAndReturn(result);
1070   }
1071 }
1072 
RequireObjectCoercible(Node * const context,Node * const value,const char * method_name)1073 void StringBuiltinsAssembler::RequireObjectCoercible(Node* const context,
1074                                                      Node* const value,
1075                                                      const char* method_name) {
1076   Label out(this), throw_exception(this, Label::kDeferred);
1077   Branch(IsNullOrUndefined(value), &throw_exception, &out);
1078 
1079   BIND(&throw_exception);
1080   ThrowTypeError(context, MessageTemplate::kCalledOnNullOrUndefined,
1081                  method_name);
1082 
1083   BIND(&out);
1084 }
1085 
MaybeCallFunctionAtSymbol(Node * const context,Node * const object,Node * const maybe_string,Handle<Symbol> symbol,const NodeFunction0 & regexp_call,const NodeFunction1 & generic_call)1086 void StringBuiltinsAssembler::MaybeCallFunctionAtSymbol(
1087     Node* const context, Node* const object, Node* const maybe_string,
1088     Handle<Symbol> symbol, const NodeFunction0& regexp_call,
1089     const NodeFunction1& generic_call) {
1090   Label out(this);
1091 
1092   // Smis definitely don't have an attached symbol.
1093   GotoIf(TaggedIsSmi(object), &out);
1094 
1095   Node* const object_map = LoadMap(object);
1096 
1097   // Skip the slow lookup for Strings.
1098   {
1099     Label next(this);
1100 
1101     GotoIfNot(IsStringInstanceType(LoadMapInstanceType(object_map)), &next);
1102 
1103     Node* const native_context = LoadNativeContext(context);
1104     Node* const initial_proto_initial_map = LoadContextElement(
1105         native_context, Context::STRING_FUNCTION_PROTOTYPE_MAP_INDEX);
1106 
1107     Node* const string_fun =
1108         LoadContextElement(native_context, Context::STRING_FUNCTION_INDEX);
1109     Node* const initial_map =
1110         LoadObjectField(string_fun, JSFunction::kPrototypeOrInitialMapOffset);
1111     Node* const proto_map = LoadMap(LoadMapPrototype(initial_map));
1112 
1113     Branch(WordEqual(proto_map, initial_proto_initial_map), &out, &next);
1114 
1115     BIND(&next);
1116   }
1117 
1118   // Take the fast path for RegExps.
1119   // There's two conditions: {object} needs to be a fast regexp, and
1120   // {maybe_string} must be a string (we can't call ToString on the fast path
1121   // since it may mutate {object}).
1122   {
1123     Label stub_call(this), slow_lookup(this);
1124 
1125     GotoIf(TaggedIsSmi(maybe_string), &slow_lookup);
1126     GotoIfNot(IsString(maybe_string), &slow_lookup);
1127 
1128     RegExpBuiltinsAssembler regexp_asm(state());
1129     regexp_asm.BranchIfFastRegExp(context, object, object_map, &stub_call,
1130                                   &slow_lookup);
1131 
1132     BIND(&stub_call);
1133     // TODO(jgruber): Add a no-JS scope once it exists.
1134     regexp_call();
1135 
1136     BIND(&slow_lookup);
1137   }
1138 
1139   GotoIf(IsNullOrUndefined(object), &out);
1140 
1141   // Fall back to a slow lookup of {object[symbol]}.
1142   //
1143   // The spec uses GetMethod({object}, {symbol}), which has a few quirks:
1144   // * null values are turned into undefined, and
1145   // * an exception is thrown if the value is not undefined, null, or callable.
1146   // We handle the former by jumping to {out} for null values as well, while
1147   // the latter is already handled by the Call({maybe_func}) operation.
1148 
1149   Node* const maybe_func = GetProperty(context, object, symbol);
1150   GotoIf(IsUndefined(maybe_func), &out);
1151   GotoIf(IsNull(maybe_func), &out);
1152 
1153   // Attempt to call the function.
1154   generic_call(maybe_func);
1155 
1156   BIND(&out);
1157 }
1158 
IndexOfDollarChar(Node * const context,Node * const string)1159 TNode<Smi> StringBuiltinsAssembler::IndexOfDollarChar(Node* const context,
1160                                                       Node* const string) {
1161   CSA_ASSERT(this, IsString(string));
1162 
1163   TNode<String> const dollar_string = HeapConstant(
1164       isolate()->factory()->LookupSingleCharacterStringFromCode('$'));
1165   TNode<Smi> const dollar_ix =
1166       CAST(CallBuiltin(Builtins::kStringIndexOf, context, string, dollar_string,
1167                        SmiConstant(0)));
1168   return dollar_ix;
1169 }
1170 
GetSubstitution(Node * context,Node * subject_string,Node * match_start_index,Node * match_end_index,Node * replace_string)1171 compiler::Node* StringBuiltinsAssembler::GetSubstitution(
1172     Node* context, Node* subject_string, Node* match_start_index,
1173     Node* match_end_index, Node* replace_string) {
1174   CSA_ASSERT(this, IsString(subject_string));
1175   CSA_ASSERT(this, IsString(replace_string));
1176   CSA_ASSERT(this, TaggedIsPositiveSmi(match_start_index));
1177   CSA_ASSERT(this, TaggedIsPositiveSmi(match_end_index));
1178 
1179   VARIABLE(var_result, MachineRepresentation::kTagged, replace_string);
1180   Label runtime(this), out(this);
1181 
1182   // In this primitive implementation we simply look for the next '$' char in
1183   // {replace_string}. If it doesn't exist, we can simply return
1184   // {replace_string} itself. If it does, then we delegate to
1185   // String::GetSubstitution, passing in the index of the first '$' to avoid
1186   // repeated scanning work.
1187   // TODO(jgruber): Possibly extend this in the future to handle more complex
1188   // cases without runtime calls.
1189 
1190   TNode<Smi> const dollar_index = IndexOfDollarChar(context, replace_string);
1191   Branch(SmiIsNegative(dollar_index), &out, &runtime);
1192 
1193   BIND(&runtime);
1194   {
1195     CSA_ASSERT(this, TaggedIsPositiveSmi(dollar_index));
1196 
1197     Node* const matched =
1198         CallBuiltin(Builtins::kStringSubstring, context, subject_string,
1199                     SmiUntag(match_start_index), SmiUntag(match_end_index));
1200     Node* const replacement_string =
1201         CallRuntime(Runtime::kGetSubstitution, context, matched, subject_string,
1202                     match_start_index, replace_string, dollar_index);
1203     var_result.Bind(replacement_string);
1204 
1205     Goto(&out);
1206   }
1207 
1208   BIND(&out);
1209   return var_result.value();
1210 }
1211 
1212 // ES6 #sec-string.prototype.repeat
TF_BUILTIN(StringPrototypeRepeat,StringBuiltinsAssembler)1213 TF_BUILTIN(StringPrototypeRepeat, StringBuiltinsAssembler) {
1214   Label invalid_count(this), invalid_string_length(this),
1215       return_emptystring(this);
1216 
1217   TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1218   Node* const receiver = Parameter(Descriptor::kReceiver);
1219   TNode<Object> count = CAST(Parameter(Descriptor::kCount));
1220   Node* const string =
1221       ToThisString(context, receiver, "String.prototype.repeat");
1222   Node* const is_stringempty =
1223       SmiEqual(LoadStringLengthAsSmi(string), SmiConstant(0));
1224 
1225   VARIABLE(
1226       var_count, MachineRepresentation::kTagged,
1227       ToInteger_Inline(context, count, CodeStubAssembler::kTruncateMinusZero));
1228 
1229   // Verifies a valid count and takes a fast path when the result will be an
1230   // empty string.
1231   {
1232     Label if_count_isheapnumber(this, Label::kDeferred);
1233 
1234     GotoIfNot(TaggedIsSmi(var_count.value()), &if_count_isheapnumber);
1235     {
1236       // If count is a SMI, throw a RangeError if less than 0 or greater than
1237       // the maximum string length.
1238       TNode<Smi> smi_count = CAST(var_count.value());
1239       GotoIf(SmiLessThan(smi_count, SmiConstant(0)), &invalid_count);
1240       GotoIf(SmiEqual(smi_count, SmiConstant(0)), &return_emptystring);
1241       GotoIf(is_stringempty, &return_emptystring);
1242       GotoIf(SmiGreaterThan(smi_count, SmiConstant(String::kMaxLength)),
1243              &invalid_string_length);
1244       Return(CallBuiltin(Builtins::kStringRepeat, context, string, smi_count));
1245     }
1246 
1247     // If count is a Heap Number...
1248     // 1) If count is Infinity, throw a RangeError exception
1249     // 2) If receiver is an empty string, return an empty string
1250     // 3) Otherwise, throw RangeError exception
1251     BIND(&if_count_isheapnumber);
1252     {
1253       CSA_ASSERT(this, IsNumberNormalized(var_count.value()));
1254       Node* const number_value = LoadHeapNumberValue(var_count.value());
1255       GotoIf(Float64Equal(number_value, Float64Constant(V8_INFINITY)),
1256              &invalid_count);
1257       GotoIf(Float64LessThan(number_value, Float64Constant(0.0)),
1258              &invalid_count);
1259       Branch(is_stringempty, &return_emptystring, &invalid_string_length);
1260     }
1261   }
1262 
1263   BIND(&return_emptystring);
1264   Return(EmptyStringConstant());
1265 
1266   BIND(&invalid_count);
1267   {
1268     ThrowRangeError(context, MessageTemplate::kInvalidCountValue,
1269                     var_count.value());
1270   }
1271 
1272   BIND(&invalid_string_length);
1273   {
1274     CallRuntime(Runtime::kThrowInvalidStringLength, context);
1275     Unreachable();
1276   }
1277 }
1278 
1279 // Helper with less checks
TF_BUILTIN(StringRepeat,StringBuiltinsAssembler)1280 TF_BUILTIN(StringRepeat, StringBuiltinsAssembler) {
1281   Node* const context = Parameter(Descriptor::kContext);
1282   Node* const string = Parameter(Descriptor::kString);
1283   TNode<Smi> const count = CAST(Parameter(Descriptor::kCount));
1284 
1285   CSA_ASSERT(this, IsString(string));
1286   CSA_ASSERT(this, Word32BinaryNot(IsEmptyString(string)));
1287   CSA_ASSERT(this, TaggedIsPositiveSmi(count));
1288   CSA_ASSERT(this, SmiLessThanOrEqual(count, SmiConstant(String::kMaxLength)));
1289 
1290   // The string is repeated with the following algorithm:
1291   //   let n = count;
1292   //   let power_of_two_repeats = string;
1293   //   let result = "";
1294   //   while (true) {
1295   //     if (n & 1) result += s;
1296   //     n >>= 1;
1297   //     if (n === 0) return result;
1298   //     power_of_two_repeats += power_of_two_repeats;
1299   //   }
1300   VARIABLE(var_result, MachineRepresentation::kTagged, EmptyStringConstant());
1301   VARIABLE(var_temp, MachineRepresentation::kTagged, string);
1302   TVARIABLE(Smi, var_count, count);
1303 
1304   Callable stringadd_callable =
1305       CodeFactory::StringAdd(isolate(), STRING_ADD_CHECK_NONE, NOT_TENURED);
1306 
1307   Label loop(this, {&var_count, &var_result, &var_temp}), return_result(this);
1308   Goto(&loop);
1309   BIND(&loop);
1310   {
1311     {
1312       Label next(this);
1313       GotoIfNot(SmiToInt32(SmiAnd(var_count.value(), SmiConstant(1))), &next);
1314       var_result.Bind(CallStub(stringadd_callable, context, var_result.value(),
1315                                var_temp.value()));
1316       Goto(&next);
1317       BIND(&next);
1318     }
1319 
1320     var_count = SmiShr(var_count.value(), 1);
1321     GotoIf(SmiEqual(var_count.value(), SmiConstant(0)), &return_result);
1322     var_temp.Bind(CallStub(stringadd_callable, context, var_temp.value(),
1323                            var_temp.value()));
1324     Goto(&loop);
1325   }
1326 
1327   BIND(&return_result);
1328   Return(var_result.value());
1329 }
1330 
1331 // ES6 #sec-string.prototype.replace
TF_BUILTIN(StringPrototypeReplace,StringBuiltinsAssembler)1332 TF_BUILTIN(StringPrototypeReplace, StringBuiltinsAssembler) {
1333   Label out(this);
1334 
1335   Node* const receiver = Parameter(Descriptor::kReceiver);
1336   Node* const search = Parameter(Descriptor::kSearch);
1337   Node* const replace = Parameter(Descriptor::kReplace);
1338   Node* const context = Parameter(Descriptor::kContext);
1339 
1340   TNode<Smi> const smi_zero = SmiConstant(0);
1341 
1342   RequireObjectCoercible(context, receiver, "String.prototype.replace");
1343 
1344   // Redirect to replacer method if {search[@@replace]} is not undefined.
1345 
1346   MaybeCallFunctionAtSymbol(
1347       context, search, receiver, isolate()->factory()->replace_symbol(),
1348       [=]() {
1349         Return(CallBuiltin(Builtins::kRegExpReplace, context, search, receiver,
1350                            replace));
1351       },
1352       [=](Node* fn) {
1353         Callable call_callable = CodeFactory::Call(isolate());
1354         Return(CallJS(call_callable, context, fn, search, receiver, replace));
1355       });
1356 
1357   // Convert {receiver} and {search} to strings.
1358 
1359   TNode<String> const subject_string = ToString_Inline(context, receiver);
1360   TNode<String> const search_string = ToString_Inline(context, search);
1361 
1362   TNode<Smi> const subject_length = LoadStringLengthAsSmi(subject_string);
1363   TNode<Smi> const search_length = LoadStringLengthAsSmi(search_string);
1364 
1365   // Fast-path single-char {search}, long cons {receiver}, and simple string
1366   // {replace}.
1367   {
1368     Label next(this);
1369 
1370     GotoIfNot(SmiEqual(search_length, SmiConstant(1)), &next);
1371     GotoIfNot(SmiGreaterThan(subject_length, SmiConstant(0xFF)), &next);
1372     GotoIf(TaggedIsSmi(replace), &next);
1373     GotoIfNot(IsString(replace), &next);
1374 
1375     Node* const subject_instance_type = LoadInstanceType(subject_string);
1376     GotoIfNot(IsConsStringInstanceType(subject_instance_type), &next);
1377 
1378     GotoIf(TaggedIsPositiveSmi(IndexOfDollarChar(context, replace)), &next);
1379 
1380     // Searching by traversing a cons string tree and replace with cons of
1381     // slices works only when the replaced string is a single character, being
1382     // replaced by a simple string and only pays off for long strings.
1383     // TODO(jgruber): Reevaluate if this is still beneficial.
1384     // TODO(jgruber): TailCallRuntime when it correctly handles adapter frames.
1385     Return(CallRuntime(Runtime::kStringReplaceOneCharWithString, context,
1386                        subject_string, search_string, replace));
1387 
1388     BIND(&next);
1389   }
1390 
1391   // TODO(jgruber): Extend StringIndexOf to handle two-byte strings and
1392   // longer substrings - we can handle up to 8 chars (one-byte) / 4 chars
1393   // (2-byte).
1394 
1395   TNode<Smi> const match_start_index =
1396       CAST(CallBuiltin(Builtins::kStringIndexOf, context, subject_string,
1397                        search_string, smi_zero));
1398 
1399   // Early exit if no match found.
1400   {
1401     Label next(this), return_subject(this);
1402 
1403     GotoIfNot(SmiIsNegative(match_start_index), &next);
1404 
1405     // The spec requires to perform ToString(replace) if the {replace} is not
1406     // callable even if we are going to exit here.
1407     // Since ToString() being applied to Smi does not have side effects for
1408     // numbers we can skip it.
1409     GotoIf(TaggedIsSmi(replace), &return_subject);
1410     GotoIf(IsCallableMap(LoadMap(replace)), &return_subject);
1411 
1412     // TODO(jgruber): Could introduce ToStringSideeffectsStub which only
1413     // performs observable parts of ToString.
1414     ToString_Inline(context, replace);
1415     Goto(&return_subject);
1416 
1417     BIND(&return_subject);
1418     Return(subject_string);
1419 
1420     BIND(&next);
1421   }
1422 
1423   TNode<Smi> const match_end_index = SmiAdd(match_start_index, search_length);
1424 
1425   Callable stringadd_callable =
1426       CodeFactory::StringAdd(isolate(), STRING_ADD_CHECK_NONE, NOT_TENURED);
1427 
1428   VARIABLE(var_result, MachineRepresentation::kTagged, EmptyStringConstant());
1429 
1430   // Compute the prefix.
1431   {
1432     Label next(this);
1433 
1434     GotoIf(SmiEqual(match_start_index, smi_zero), &next);
1435     Node* const prefix =
1436         CallBuiltin(Builtins::kStringSubstring, context, subject_string,
1437                     IntPtrConstant(0), SmiUntag(match_start_index));
1438     var_result.Bind(prefix);
1439 
1440     Goto(&next);
1441     BIND(&next);
1442   }
1443 
1444   // Compute the string to replace with.
1445 
1446   Label if_iscallablereplace(this), if_notcallablereplace(this);
1447   GotoIf(TaggedIsSmi(replace), &if_notcallablereplace);
1448   Branch(IsCallableMap(LoadMap(replace)), &if_iscallablereplace,
1449          &if_notcallablereplace);
1450 
1451   BIND(&if_iscallablereplace);
1452   {
1453     Callable call_callable = CodeFactory::Call(isolate());
1454     Node* const replacement =
1455         CallJS(call_callable, context, replace, UndefinedConstant(),
1456                search_string, match_start_index, subject_string);
1457     Node* const replacement_string = ToString_Inline(context, replacement);
1458     var_result.Bind(CallStub(stringadd_callable, context, var_result.value(),
1459                              replacement_string));
1460     Goto(&out);
1461   }
1462 
1463   BIND(&if_notcallablereplace);
1464   {
1465     Node* const replace_string = ToString_Inline(context, replace);
1466     Node* const replacement =
1467         GetSubstitution(context, subject_string, match_start_index,
1468                         match_end_index, replace_string);
1469     var_result.Bind(
1470         CallStub(stringadd_callable, context, var_result.value(), replacement));
1471     Goto(&out);
1472   }
1473 
1474   BIND(&out);
1475   {
1476     Node* const suffix =
1477         CallBuiltin(Builtins::kStringSubstring, context, subject_string,
1478                     SmiUntag(match_end_index), SmiUntag(subject_length));
1479     Node* const result =
1480         CallStub(stringadd_callable, context, var_result.value(), suffix);
1481     Return(result);
1482   }
1483 }
1484 
1485 class StringMatchSearchAssembler : public StringBuiltinsAssembler {
1486  public:
StringMatchSearchAssembler(compiler::CodeAssemblerState * state)1487   explicit StringMatchSearchAssembler(compiler::CodeAssemblerState* state)
1488       : StringBuiltinsAssembler(state) {}
1489 
1490  protected:
1491   enum Variant { kMatch, kSearch };
1492 
Generate(Variant variant,const char * method_name,TNode<Object> receiver,TNode<Object> maybe_regexp,TNode<Context> context)1493   void Generate(Variant variant, const char* method_name,
1494                 TNode<Object> receiver, TNode<Object> maybe_regexp,
1495                 TNode<Context> context) {
1496     Label call_regexp_match_search(this);
1497 
1498     Builtins::Name builtin;
1499     Handle<Symbol> symbol;
1500     if (variant == kMatch) {
1501       builtin = Builtins::kRegExpMatchFast;
1502       symbol = isolate()->factory()->match_symbol();
1503     } else {
1504       builtin = Builtins::kRegExpSearchFast;
1505       symbol = isolate()->factory()->search_symbol();
1506     }
1507 
1508     RequireObjectCoercible(context, receiver, method_name);
1509 
1510     MaybeCallFunctionAtSymbol(
1511         context, maybe_regexp, receiver, symbol,
1512         [=] { Return(CallBuiltin(builtin, context, maybe_regexp, receiver)); },
1513         [=](Node* fn) {
1514           Callable call_callable = CodeFactory::Call(isolate());
1515           Return(CallJS(call_callable, context, fn, maybe_regexp, receiver));
1516         });
1517 
1518     // maybe_regexp is not a RegExp nor has [@@match / @@search] property.
1519     {
1520       RegExpBuiltinsAssembler regexp_asm(state());
1521 
1522       TNode<String> receiver_string = ToString_Inline(context, receiver);
1523       TNode<Context> native_context = LoadNativeContext(context);
1524       TNode<HeapObject> regexp_function = CAST(
1525           LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX));
1526       TNode<Map> initial_map = CAST(LoadObjectField(
1527           regexp_function, JSFunction::kPrototypeOrInitialMapOffset));
1528       TNode<Object> regexp = regexp_asm.RegExpCreate(
1529           context, initial_map, maybe_regexp, EmptyStringConstant());
1530 
1531       Label fast_path(this), slow_path(this);
1532       regexp_asm.BranchIfFastRegExp(context, regexp, initial_map, &fast_path,
1533                                     &slow_path);
1534 
1535       BIND(&fast_path);
1536       Return(CallBuiltin(builtin, context, regexp, receiver_string));
1537 
1538       BIND(&slow_path);
1539       {
1540         TNode<Object> maybe_func = GetProperty(context, regexp, symbol);
1541         Callable call_callable = CodeFactory::Call(isolate());
1542         Return(CallJS(call_callable, context, maybe_func, regexp,
1543                       receiver_string));
1544       }
1545     }
1546   }
1547 };
1548 
1549 // ES6 #sec-string.prototype.match
TF_BUILTIN(StringPrototypeMatch,StringMatchSearchAssembler)1550 TF_BUILTIN(StringPrototypeMatch, StringMatchSearchAssembler) {
1551   TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
1552   TNode<Object> maybe_regexp = CAST(Parameter(Descriptor::kRegexp));
1553   TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1554 
1555   Generate(kMatch, "String.prototype.match", receiver, maybe_regexp, context);
1556 }
1557 
1558 // ES #sec-string.prototype.matchAll
TF_BUILTIN(StringPrototypeMatchAll,StringBuiltinsAssembler)1559 TF_BUILTIN(StringPrototypeMatchAll, StringBuiltinsAssembler) {
1560   char const* method_name = "String.prototype.matchAll";
1561 
1562   TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1563   TNode<Object> maybe_regexp = CAST(Parameter(Descriptor::kRegexp));
1564   TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
1565   TNode<Context> native_context = LoadNativeContext(context);
1566 
1567   // 1. Let O be ? RequireObjectCoercible(this value).
1568   RequireObjectCoercible(context, receiver, method_name);
1569 
1570   // 2. If regexp is neither undefined nor null, then
1571   Label return_match_all_iterator(this),
1572       tostring_and_return_match_all_iterator(this, Label::kDeferred);
1573   TVARIABLE(BoolT, var_is_fast_regexp);
1574   TVARIABLE(String, var_receiver_string);
1575   GotoIf(IsNullOrUndefined(maybe_regexp),
1576          &tostring_and_return_match_all_iterator);
1577   {
1578     // a. Let matcher be ? GetMethod(regexp, @@matchAll).
1579     // b. If matcher is not undefined, then
1580     //   i. Return ? Call(matcher, regexp, « O »).
1581     auto if_regexp_call = [&] {
1582       // MaybeCallFunctionAtSymbol guarantees fast path is chosen only if
1583       // maybe_regexp is a fast regexp and receiver is a string.
1584       var_receiver_string = CAST(receiver);
1585       CSA_ASSERT(this, IsString(var_receiver_string.value()));
1586       var_is_fast_regexp = Int32TrueConstant();
1587       Goto(&return_match_all_iterator);
1588     };
1589     auto if_generic_call = [=](Node* fn) {
1590       Callable call_callable = CodeFactory::Call(isolate());
1591       Return(CallJS(call_callable, context, fn, maybe_regexp, receiver));
1592     };
1593     MaybeCallFunctionAtSymbol(context, maybe_regexp, receiver,
1594                               isolate()->factory()->match_all_symbol(),
1595                               if_regexp_call, if_generic_call);
1596     Goto(&tostring_and_return_match_all_iterator);
1597   }
1598   BIND(&tostring_and_return_match_all_iterator);
1599   {
1600     var_receiver_string = ToString_Inline(context, receiver);
1601     var_is_fast_regexp = Int32FalseConstant();
1602     Goto(&return_match_all_iterator);
1603   }
1604   BIND(&return_match_all_iterator);
1605   {
1606     // 3. Return ? MatchAllIterator(regexp, O).
1607     RegExpBuiltinsAssembler regexp_asm(state());
1608     TNode<Object> iterator = regexp_asm.MatchAllIterator(
1609         context, native_context, maybe_regexp, var_receiver_string.value(),
1610         var_is_fast_regexp.value(), method_name);
1611     Return(iterator);
1612   }
1613 }
1614 
1615 class StringPadAssembler : public StringBuiltinsAssembler {
1616  public:
StringPadAssembler(compiler::CodeAssemblerState * state)1617   explicit StringPadAssembler(compiler::CodeAssemblerState* state)
1618       : StringBuiltinsAssembler(state) {}
1619 
1620  protected:
1621   enum Variant { kStart, kEnd };
1622 
Generate(Variant variant,const char * method_name)1623   void Generate(Variant variant, const char* method_name) {
1624     Node* const context = Parameter(BuiltinDescriptor::kContext);
1625     Node* argc =
1626         ChangeInt32ToIntPtr(Parameter(BuiltinDescriptor::kArgumentsCount));
1627     CodeStubArguments arguments(this, argc);
1628     Node* const receiver = arguments.GetReceiver();
1629     Node* const receiver_string = ToThisString(context, receiver, method_name);
1630     TNode<Smi> const string_length = LoadStringLengthAsSmi(receiver_string);
1631 
1632     TVARIABLE(String, var_fill_string, StringConstant(" "));
1633     TVARIABLE(IntPtrT, var_fill_length, IntPtrConstant(1));
1634 
1635     Label argc_2(this), dont_pad(this), invalid_string_length(this), pad(this);
1636 
1637     // If no max_length was provided, return the string.
1638     GotoIf(IntPtrEqual(argc, IntPtrConstant(0)), &dont_pad);
1639 
1640     TNode<Number> const max_length =
1641         ToLength_Inline(context, arguments.AtIndex(0));
1642     CSA_ASSERT(this, IsNumberNormalized(max_length));
1643 
1644     // Throw if max_length is not a smi or greater than the max string length.
1645     GotoIfNot(TaggedIsSmi(max_length), &invalid_string_length);
1646     TNode<Smi> smi_max_length = CAST(max_length);
1647     GotoIfNot(
1648         SmiLessThanOrEqual(smi_max_length, SmiConstant(String::kMaxLength)),
1649         &invalid_string_length);
1650 
1651     // If the max_length is less than length of the string, return the string.
1652     CSA_ASSERT(this, TaggedIsPositiveSmi(smi_max_length));
1653     GotoIf(SmiLessThanOrEqual(smi_max_length, string_length), &dont_pad);
1654 
1655     Branch(IntPtrEqual(argc, IntPtrConstant(1)), &pad, &argc_2);
1656     BIND(&argc_2);
1657     {
1658       Node* const fill = arguments.AtIndex(1);
1659       GotoIf(IsUndefined(fill), &pad);
1660 
1661       var_fill_string = ToString_Inline(context, fill);
1662       var_fill_length = LoadStringLengthAsWord(var_fill_string.value());
1663 
1664       Branch(IntPtrGreaterThan(var_fill_length.value(), IntPtrConstant(0)),
1665              &pad, &dont_pad);
1666     }
1667     BIND(&pad);
1668     {
1669       CSA_ASSERT(this,
1670                  IntPtrGreaterThan(var_fill_length.value(), IntPtrConstant(0)));
1671       CSA_ASSERT(this, SmiGreaterThan(smi_max_length, string_length));
1672 
1673       Callable stringadd_callable =
1674           CodeFactory::StringAdd(isolate(), STRING_ADD_CHECK_NONE, NOT_TENURED);
1675       TNode<Smi> const pad_length = SmiSub(smi_max_length, string_length);
1676 
1677       VARIABLE(var_pad, MachineRepresentation::kTagged);
1678 
1679       Label single_char_fill(this), multi_char_fill(this), return_result(this);
1680       Branch(IntPtrEqual(var_fill_length.value(), IntPtrConstant(1)),
1681              &single_char_fill, &multi_char_fill);
1682 
1683       // Fast path for a single character fill.  No need to calculate number of
1684       // repetitions or remainder.
1685       BIND(&single_char_fill);
1686       {
1687         var_pad.Bind(CallBuiltin(Builtins::kStringRepeat, context,
1688                                  static_cast<Node*>(var_fill_string.value()),
1689                                  pad_length));
1690         Goto(&return_result);
1691       }
1692       BIND(&multi_char_fill);
1693       {
1694         TNode<Int32T> const fill_length_word32 =
1695             TruncateIntPtrToInt32(var_fill_length.value());
1696         TNode<Int32T> const pad_length_word32 = SmiToInt32(pad_length);
1697         TNode<Int32T> const repetitions_word32 =
1698             Int32Div(pad_length_word32, fill_length_word32);
1699         TNode<Int32T> const remaining_word32 =
1700             Int32Mod(pad_length_word32, fill_length_word32);
1701 
1702         var_pad.Bind(CallBuiltin(Builtins::kStringRepeat, context,
1703                                  var_fill_string.value(),
1704                                  SmiFromInt32(repetitions_word32)));
1705 
1706         GotoIfNot(remaining_word32, &return_result);
1707         {
1708           Node* const remainder_string = CallBuiltin(
1709               Builtins::kStringSubstring, context, var_fill_string.value(),
1710               IntPtrConstant(0), ChangeInt32ToIntPtr(remaining_word32));
1711           var_pad.Bind(CallStub(stringadd_callable, context, var_pad.value(),
1712                                 remainder_string));
1713           Goto(&return_result);
1714         }
1715       }
1716       BIND(&return_result);
1717       CSA_ASSERT(this,
1718                  SmiEqual(pad_length, LoadStringLengthAsSmi(var_pad.value())));
1719       arguments.PopAndReturn(variant == kStart
1720                                  ? CallStub(stringadd_callable, context,
1721                                             var_pad.value(), receiver_string)
1722                                  : CallStub(stringadd_callable, context,
1723                                             receiver_string, var_pad.value()));
1724     }
1725     BIND(&dont_pad);
1726     arguments.PopAndReturn(receiver_string);
1727     BIND(&invalid_string_length);
1728     {
1729       CallRuntime(Runtime::kThrowInvalidStringLength, context);
1730       Unreachable();
1731     }
1732   }
1733 };
1734 
TF_BUILTIN(StringPrototypePadEnd,StringPadAssembler)1735 TF_BUILTIN(StringPrototypePadEnd, StringPadAssembler) {
1736   Generate(kEnd, "String.prototype.padEnd");
1737 }
1738 
TF_BUILTIN(StringPrototypePadStart,StringPadAssembler)1739 TF_BUILTIN(StringPrototypePadStart, StringPadAssembler) {
1740   Generate(kStart, "String.prototype.padStart");
1741 }
1742 
1743 // ES6 #sec-string.prototype.search
TF_BUILTIN(StringPrototypeSearch,StringMatchSearchAssembler)1744 TF_BUILTIN(StringPrototypeSearch, StringMatchSearchAssembler) {
1745   TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
1746   TNode<Object> maybe_regexp = CAST(Parameter(Descriptor::kRegexp));
1747   TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1748   Generate(kSearch, "String.prototype.search", receiver, maybe_regexp, context);
1749 }
1750 
1751 // ES6 section 21.1.3.18 String.prototype.slice ( start, end )
TF_BUILTIN(StringPrototypeSlice,StringBuiltinsAssembler)1752 TF_BUILTIN(StringPrototypeSlice, StringBuiltinsAssembler) {
1753   Label out(this);
1754   TVARIABLE(IntPtrT, var_start);
1755   TVARIABLE(IntPtrT, var_end);
1756 
1757   const int kStart = 0;
1758   const int kEnd = 1;
1759   Node* argc =
1760       ChangeInt32ToIntPtr(Parameter(BuiltinDescriptor::kArgumentsCount));
1761   CodeStubArguments args(this, argc);
1762   Node* const receiver = args.GetReceiver();
1763   TNode<Object> start = args.GetOptionalArgumentValue(kStart);
1764   TNode<Object> end = args.GetOptionalArgumentValue(kEnd);
1765   TNode<Context> context = CAST(Parameter(BuiltinDescriptor::kContext));
1766 
1767   // 1. Let O be ? RequireObjectCoercible(this value).
1768   RequireObjectCoercible(context, receiver, "String.prototype.slice");
1769 
1770   // 2. Let S be ? ToString(O).
1771   TNode<String> const subject_string =
1772       CAST(CallBuiltin(Builtins::kToString, context, receiver));
1773 
1774   // 3. Let len be the number of elements in S.
1775   TNode<IntPtrT> const length = LoadStringLengthAsWord(subject_string);
1776 
1777   // Convert {start} to a relative index.
1778   var_start = ConvertToRelativeIndex(context, start, length);
1779 
1780   // 5. If end is undefined, let intEnd be len;
1781   var_end = length;
1782   GotoIf(IsUndefined(end), &out);
1783 
1784   // Convert {end} to a relative index.
1785   var_end = ConvertToRelativeIndex(context, end, length);
1786   Goto(&out);
1787 
1788   Label return_emptystring(this);
1789   BIND(&out);
1790   {
1791     GotoIf(IntPtrLessThanOrEqual(var_end.value(), var_start.value()),
1792            &return_emptystring);
1793     TNode<String> const result =
1794         SubString(subject_string, var_start.value(), var_end.value());
1795     args.PopAndReturn(result);
1796   }
1797 
1798   BIND(&return_emptystring);
1799   args.PopAndReturn(EmptyStringConstant());
1800 }
1801 
StringToArray(TNode<Context> context,TNode<String> subject_string,TNode<Smi> subject_length,TNode<Number> limit_number)1802 TNode<JSArray> StringBuiltinsAssembler::StringToArray(
1803     TNode<Context> context, TNode<String> subject_string,
1804     TNode<Smi> subject_length, TNode<Number> limit_number) {
1805   CSA_ASSERT(this, SmiGreaterThan(subject_length, SmiConstant(0)));
1806 
1807   Label done(this), call_runtime(this, Label::kDeferred),
1808       fill_thehole_and_call_runtime(this, Label::kDeferred);
1809   TVARIABLE(JSArray, result_array);
1810 
1811   TNode<Int32T> instance_type = LoadInstanceType(subject_string);
1812   GotoIfNot(IsOneByteStringInstanceType(instance_type), &call_runtime);
1813 
1814   // Try to use cached one byte characters.
1815   {
1816     TNode<Smi> length_smi =
1817         Select<Smi>(TaggedIsSmi(limit_number),
1818                     [=] { return SmiMin(CAST(limit_number), subject_length); },
1819                     [=] { return subject_length; });
1820     TNode<IntPtrT> length = SmiToIntPtr(length_smi);
1821 
1822     ToDirectStringAssembler to_direct(state(), subject_string);
1823     to_direct.TryToDirect(&call_runtime);
1824     TNode<FixedArray> elements =
1825         AllocateFixedArray(PACKED_ELEMENTS, length_smi,
1826                            AllocationFlag::kAllowLargeObjectAllocation);
1827     // Don't allocate anything while {string_data} is live!
1828     TNode<RawPtrT> string_data = UncheckedCast<RawPtrT>(
1829         to_direct.PointerToData(&fill_thehole_and_call_runtime));
1830     TNode<IntPtrT> string_data_offset = to_direct.offset();
1831     TNode<Object> cache = LoadRoot(Heap::kSingleCharacterStringCacheRootIndex);
1832 
1833     BuildFastLoop(
1834         IntPtrConstant(0), length,
1835         [&](Node* index) {
1836           // TODO(jkummerow): Implement a CSA version of DisallowHeapAllocation
1837           // and use that to guard ToDirectStringAssembler.PointerToData().
1838           CSA_ASSERT(this, WordEqual(to_direct.PointerToData(&call_runtime),
1839                                      string_data));
1840           TNode<Int32T> char_code =
1841               UncheckedCast<Int32T>(Load(MachineType::Uint8(), string_data,
1842                                          IntPtrAdd(index, string_data_offset)));
1843           Node* code_index = ChangeUint32ToWord(char_code);
1844           TNode<Object> entry = LoadFixedArrayElement(CAST(cache), code_index);
1845 
1846           // If we cannot find a char in the cache, fill the hole for the fixed
1847           // array, and call runtime.
1848           GotoIf(IsUndefined(entry), &fill_thehole_and_call_runtime);
1849 
1850           StoreFixedArrayElement(elements, index, entry);
1851         },
1852         1, ParameterMode::INTPTR_PARAMETERS, IndexAdvanceMode::kPost);
1853 
1854     TNode<Map> array_map = LoadJSArrayElementsMap(PACKED_ELEMENTS, context);
1855     result_array = CAST(
1856         AllocateUninitializedJSArrayWithoutElements(array_map, length_smi));
1857     StoreObjectField(result_array.value(), JSObject::kElementsOffset, elements);
1858     Goto(&done);
1859 
1860     BIND(&fill_thehole_and_call_runtime);
1861     {
1862       FillFixedArrayWithValue(PACKED_ELEMENTS, elements, IntPtrConstant(0),
1863                               length, Heap::kTheHoleValueRootIndex);
1864       Goto(&call_runtime);
1865     }
1866   }
1867 
1868   BIND(&call_runtime);
1869   {
1870     result_array = CAST(CallRuntime(Runtime::kStringToArray, context,
1871                                     subject_string, limit_number));
1872     Goto(&done);
1873   }
1874 
1875   BIND(&done);
1876   return result_array.value();
1877 }
1878 
1879 // ES6 section 21.1.3.19 String.prototype.split ( separator, limit )
TF_BUILTIN(StringPrototypeSplit,StringBuiltinsAssembler)1880 TF_BUILTIN(StringPrototypeSplit, StringBuiltinsAssembler) {
1881   const int kSeparatorArg = 0;
1882   const int kLimitArg = 1;
1883 
1884   Node* const argc =
1885       ChangeInt32ToIntPtr(Parameter(BuiltinDescriptor::kArgumentsCount));
1886   CodeStubArguments args(this, argc);
1887 
1888   Node* const receiver = args.GetReceiver();
1889   Node* const separator = args.GetOptionalArgumentValue(kSeparatorArg);
1890   Node* const limit = args.GetOptionalArgumentValue(kLimitArg);
1891   TNode<Context> context = CAST(Parameter(BuiltinDescriptor::kContext));
1892 
1893   TNode<Smi> smi_zero = SmiConstant(0);
1894 
1895   RequireObjectCoercible(context, receiver, "String.prototype.split");
1896 
1897   // Redirect to splitter method if {separator[@@split]} is not undefined.
1898 
1899   MaybeCallFunctionAtSymbol(
1900       context, separator, receiver, isolate()->factory()->split_symbol(),
1901       [&]() {
1902         args.PopAndReturn(CallBuiltin(Builtins::kRegExpSplit, context,
1903                                       separator, receiver, limit));
1904       },
1905       [&](Node* fn) {
1906         Callable call_callable = CodeFactory::Call(isolate());
1907         args.PopAndReturn(
1908             CallJS(call_callable, context, fn, separator, receiver, limit));
1909       });
1910 
1911   // String and integer conversions.
1912 
1913   TNode<String> subject_string = ToString_Inline(context, receiver);
1914   TNode<Number> limit_number = Select<Number>(
1915       IsUndefined(limit), [=] { return NumberConstant(kMaxUInt32); },
1916       [=] { return ToUint32(context, limit); });
1917   Node* const separator_string = ToString_Inline(context, separator);
1918 
1919   Label return_empty_array(this);
1920 
1921   // Shortcut for {limit} == 0.
1922   GotoIf(WordEqual<Object, Object>(limit_number, smi_zero),
1923          &return_empty_array);
1924 
1925   // ECMA-262 says that if {separator} is undefined, the result should
1926   // be an array of size 1 containing the entire string.
1927   {
1928     Label next(this);
1929     GotoIfNot(IsUndefined(separator), &next);
1930 
1931     const ElementsKind kind = PACKED_ELEMENTS;
1932     Node* const native_context = LoadNativeContext(context);
1933     Node* const array_map = LoadJSArrayElementsMap(kind, native_context);
1934 
1935     Node* const length = SmiConstant(1);
1936     Node* const capacity = IntPtrConstant(1);
1937     Node* const result = AllocateJSArray(kind, array_map, capacity, length);
1938 
1939     Node* const fixed_array = LoadElements(result);
1940     StoreFixedArrayElement(fixed_array, 0, subject_string);
1941 
1942     args.PopAndReturn(result);
1943 
1944     BIND(&next);
1945   }
1946 
1947   // If the separator string is empty then return the elements in the subject.
1948   {
1949     Label next(this);
1950     GotoIfNot(SmiEqual(LoadStringLengthAsSmi(separator_string), smi_zero),
1951               &next);
1952 
1953     TNode<Smi> subject_length = LoadStringLengthAsSmi(subject_string);
1954     GotoIf(SmiEqual(subject_length, smi_zero), &return_empty_array);
1955 
1956     args.PopAndReturn(
1957         StringToArray(context, subject_string, subject_length, limit_number));
1958 
1959     BIND(&next);
1960   }
1961 
1962   Node* const result =
1963       CallRuntime(Runtime::kStringSplit, context, subject_string,
1964                   separator_string, limit_number);
1965   args.PopAndReturn(result);
1966 
1967   BIND(&return_empty_array);
1968   {
1969     const ElementsKind kind = PACKED_ELEMENTS;
1970     Node* const native_context = LoadNativeContext(context);
1971     Node* const array_map = LoadJSArrayElementsMap(kind, native_context);
1972 
1973     Node* const length = smi_zero;
1974     Node* const capacity = IntPtrConstant(0);
1975     Node* const result = AllocateJSArray(kind, array_map, capacity, length);
1976 
1977     args.PopAndReturn(result);
1978   }
1979 }
1980 
1981 // ES6 #sec-string.prototype.substr
TF_BUILTIN(StringPrototypeSubstr,StringBuiltinsAssembler)1982 TF_BUILTIN(StringPrototypeSubstr, StringBuiltinsAssembler) {
1983   const int kStartArg = 0;
1984   const int kLengthArg = 1;
1985 
1986   Node* const argc =
1987       ChangeInt32ToIntPtr(Parameter(BuiltinDescriptor::kArgumentsCount));
1988   CodeStubArguments args(this, argc);
1989 
1990   Node* const receiver = args.GetReceiver();
1991   TNode<Object> start = args.GetOptionalArgumentValue(kStartArg);
1992   TNode<Object> length = args.GetOptionalArgumentValue(kLengthArg);
1993   TNode<Context> context = CAST(Parameter(BuiltinDescriptor::kContext));
1994 
1995   Label out(this);
1996 
1997   TVARIABLE(IntPtrT, var_start);
1998   TVARIABLE(Number, var_length);
1999 
2000   TNode<IntPtrT> const zero = IntPtrConstant(0);
2001 
2002   // Check that {receiver} is coercible to Object and convert it to a String.
2003   TNode<String> const string =
2004       ToThisString(context, receiver, "String.prototype.substr");
2005 
2006   TNode<IntPtrT> const string_length = LoadStringLengthAsWord(string);
2007 
2008   // Convert {start} to a relative index.
2009   var_start = ConvertToRelativeIndex(context, start, string_length);
2010 
2011   // Conversions and bounds-checks for {length}.
2012   Label if_issmi(this), if_isheapnumber(this, Label::kDeferred);
2013 
2014   // Default to {string_length} if {length} is undefined.
2015   {
2016     Label if_isundefined(this, Label::kDeferred), if_isnotundefined(this);
2017     Branch(IsUndefined(length), &if_isundefined, &if_isnotundefined);
2018 
2019     BIND(&if_isundefined);
2020     var_length = SmiTag(string_length);
2021     Goto(&if_issmi);
2022 
2023     BIND(&if_isnotundefined);
2024     var_length = ToInteger_Inline(context, length,
2025                                   CodeStubAssembler::kTruncateMinusZero);
2026   }
2027 
2028   TVARIABLE(IntPtrT, var_result_length);
2029 
2030   Branch(TaggedIsSmi(var_length.value()), &if_issmi, &if_isheapnumber);
2031 
2032   // Set {length} to min(max({length}, 0), {string_length} - {start}
2033   BIND(&if_issmi);
2034   {
2035     TNode<IntPtrT> const positive_length =
2036         IntPtrMax(SmiUntag(CAST(var_length.value())), zero);
2037     TNode<IntPtrT> const minimal_length =
2038         IntPtrSub(string_length, var_start.value());
2039     var_result_length = IntPtrMin(positive_length, minimal_length);
2040 
2041     GotoIfNot(IntPtrLessThanOrEqual(var_result_length.value(), zero), &out);
2042     args.PopAndReturn(EmptyStringConstant());
2043   }
2044 
2045   BIND(&if_isheapnumber);
2046   {
2047     // If {length} is a heap number, it is definitely out of bounds. There are
2048     // two cases according to the spec: if it is negative, "" is returned; if
2049     // it is positive, then length is set to {string_length} - {start}.
2050 
2051     CSA_ASSERT(this, IsHeapNumber(CAST(var_length.value())));
2052 
2053     Label if_isnegative(this), if_ispositive(this);
2054     TNode<Float64T> const float_zero = Float64Constant(0.);
2055     TNode<Float64T> const length_float =
2056         LoadHeapNumberValue(CAST(var_length.value()));
2057     Branch(Float64LessThan(length_float, float_zero), &if_isnegative,
2058            &if_ispositive);
2059 
2060     BIND(&if_isnegative);
2061     args.PopAndReturn(EmptyStringConstant());
2062 
2063     BIND(&if_ispositive);
2064     {
2065       var_result_length = IntPtrSub(string_length, var_start.value());
2066       GotoIfNot(IntPtrLessThanOrEqual(var_result_length.value(), zero), &out);
2067       args.PopAndReturn(EmptyStringConstant());
2068     }
2069   }
2070 
2071   BIND(&out);
2072   {
2073     TNode<IntPtrT> const end =
2074         IntPtrAdd(var_start.value(), var_result_length.value());
2075     args.PopAndReturn(SubString(string, var_start.value(), end));
2076   }
2077 }
2078 
ToSmiBetweenZeroAnd(SloppyTNode<Context> context,SloppyTNode<Object> value,SloppyTNode<Smi> limit)2079 TNode<Smi> StringBuiltinsAssembler::ToSmiBetweenZeroAnd(
2080     SloppyTNode<Context> context, SloppyTNode<Object> value,
2081     SloppyTNode<Smi> limit) {
2082   Label out(this);
2083   TVARIABLE(Smi, var_result);
2084 
2085   TNode<Number> const value_int =
2086       ToInteger_Inline(context, value, CodeStubAssembler::kTruncateMinusZero);
2087 
2088   Label if_issmi(this), if_isnotsmi(this, Label::kDeferred);
2089   Branch(TaggedIsSmi(value_int), &if_issmi, &if_isnotsmi);
2090 
2091   BIND(&if_issmi);
2092   {
2093     TNode<Smi> value_smi = CAST(value_int);
2094     Label if_isinbounds(this), if_isoutofbounds(this, Label::kDeferred);
2095     Branch(SmiAbove(value_smi, limit), &if_isoutofbounds, &if_isinbounds);
2096 
2097     BIND(&if_isinbounds);
2098     {
2099       var_result = CAST(value_int);
2100       Goto(&out);
2101     }
2102 
2103     BIND(&if_isoutofbounds);
2104     {
2105       TNode<Smi> const zero = SmiConstant(0);
2106       var_result =
2107           SelectConstant<Smi>(SmiLessThan(value_smi, zero), zero, limit);
2108       Goto(&out);
2109     }
2110   }
2111 
2112   BIND(&if_isnotsmi);
2113   {
2114     // {value} is a heap number - in this case, it is definitely out of bounds.
2115     TNode<HeapNumber> value_int_hn = CAST(value_int);
2116 
2117     TNode<Float64T> const float_zero = Float64Constant(0.);
2118     TNode<Smi> const smi_zero = SmiConstant(0);
2119     TNode<Float64T> const value_float = LoadHeapNumberValue(value_int_hn);
2120     var_result = SelectConstant<Smi>(Float64LessThan(value_float, float_zero),
2121                                      smi_zero, limit);
2122     Goto(&out);
2123   }
2124 
2125   BIND(&out);
2126   return var_result.value();
2127 }
2128 
TF_BUILTIN(StringSubstring,CodeStubAssembler)2129 TF_BUILTIN(StringSubstring, CodeStubAssembler) {
2130   TNode<String> string = CAST(Parameter(Descriptor::kString));
2131   TNode<IntPtrT> from = UncheckedCast<IntPtrT>(Parameter(Descriptor::kFrom));
2132   TNode<IntPtrT> to = UncheckedCast<IntPtrT>(Parameter(Descriptor::kTo));
2133 
2134   Return(SubString(string, from, to));
2135 }
2136 
2137 // ES6 #sec-string.prototype.substring
TF_BUILTIN(StringPrototypeSubstring,StringBuiltinsAssembler)2138 TF_BUILTIN(StringPrototypeSubstring, StringBuiltinsAssembler) {
2139   const int kStartArg = 0;
2140   const int kEndArg = 1;
2141 
2142   Node* const argc =
2143       ChangeInt32ToIntPtr(Parameter(BuiltinDescriptor::kArgumentsCount));
2144   CodeStubArguments args(this, argc);
2145 
2146   Node* const receiver = args.GetReceiver();
2147   Node* const start = args.GetOptionalArgumentValue(kStartArg);
2148   Node* const end = args.GetOptionalArgumentValue(kEndArg);
2149   Node* const context = Parameter(BuiltinDescriptor::kContext);
2150 
2151   Label out(this);
2152 
2153   TVARIABLE(Smi, var_start);
2154   TVARIABLE(Smi, var_end);
2155 
2156   // Check that {receiver} is coercible to Object and convert it to a String.
2157   TNode<String> const string =
2158       ToThisString(context, receiver, "String.prototype.substring");
2159 
2160   TNode<Smi> const length = LoadStringLengthAsSmi(string);
2161 
2162   // Conversion and bounds-checks for {start}.
2163   var_start = ToSmiBetweenZeroAnd(context, start, length);
2164 
2165   // Conversion and bounds-checks for {end}.
2166   {
2167     var_end = length;
2168     GotoIf(IsUndefined(end), &out);
2169 
2170     var_end = ToSmiBetweenZeroAnd(context, end, length);
2171 
2172     Label if_endislessthanstart(this);
2173     Branch(SmiLessThan(var_end.value(), var_start.value()),
2174            &if_endislessthanstart, &out);
2175 
2176     BIND(&if_endislessthanstart);
2177     {
2178       TNode<Smi> const tmp = var_end.value();
2179       var_end = var_start.value();
2180       var_start = tmp;
2181       Goto(&out);
2182     }
2183   }
2184 
2185   BIND(&out);
2186   {
2187     args.PopAndReturn(SubString(string, SmiUntag(var_start.value()),
2188                                 SmiUntag(var_end.value())));
2189   }
2190 }
2191 
2192 // ES6 #sec-string.prototype.trim
TF_BUILTIN(StringPrototypeTrim,StringTrimAssembler)2193 TF_BUILTIN(StringPrototypeTrim, StringTrimAssembler) {
2194   Generate(String::kTrim, "String.prototype.trim");
2195 }
2196 
2197 // https://github.com/tc39/proposal-string-left-right-trim
TF_BUILTIN(StringPrototypeTrimStart,StringTrimAssembler)2198 TF_BUILTIN(StringPrototypeTrimStart, StringTrimAssembler) {
2199   Generate(String::kTrimStart, "String.prototype.trimLeft");
2200 }
2201 
2202 // https://github.com/tc39/proposal-string-left-right-trim
TF_BUILTIN(StringPrototypeTrimEnd,StringTrimAssembler)2203 TF_BUILTIN(StringPrototypeTrimEnd, StringTrimAssembler) {
2204   Generate(String::kTrimEnd, "String.prototype.trimRight");
2205 }
2206 
Generate(String::TrimMode mode,const char * method_name)2207 void StringTrimAssembler::Generate(String::TrimMode mode,
2208                                    const char* method_name) {
2209   Label return_emptystring(this), if_runtime(this);
2210 
2211   Node* const argc = Parameter(BuiltinDescriptor::kArgumentsCount);
2212   Node* const context = Parameter(BuiltinDescriptor::kContext);
2213   CodeStubArguments arguments(this, ChangeInt32ToIntPtr(argc));
2214   Node* const receiver = arguments.GetReceiver();
2215 
2216   // Check that {receiver} is coercible to Object and convert it to a String.
2217   TNode<String> const string = ToThisString(context, receiver, method_name);
2218   TNode<IntPtrT> const string_length = LoadStringLengthAsWord(string);
2219 
2220   ToDirectStringAssembler to_direct(state(), string);
2221   to_direct.TryToDirect(&if_runtime);
2222   Node* const string_data = to_direct.PointerToData(&if_runtime);
2223   Node* const instance_type = to_direct.instance_type();
2224   Node* const is_stringonebyte = IsOneByteStringInstanceType(instance_type);
2225   Node* const string_data_offset = to_direct.offset();
2226 
2227   TVARIABLE(IntPtrT, var_start, IntPtrConstant(0));
2228   TVARIABLE(IntPtrT, var_end, IntPtrSub(string_length, IntPtrConstant(1)));
2229 
2230   if (mode == String::kTrimStart || mode == String::kTrim) {
2231     ScanForNonWhiteSpaceOrLineTerminator(string_data, string_data_offset,
2232                                          is_stringonebyte, &var_start,
2233                                          string_length, 1, &return_emptystring);
2234   }
2235   if (mode == String::kTrimEnd || mode == String::kTrim) {
2236     ScanForNonWhiteSpaceOrLineTerminator(
2237         string_data, string_data_offset, is_stringonebyte, &var_end,
2238         IntPtrConstant(-1), -1, &return_emptystring);
2239   }
2240 
2241   arguments.PopAndReturn(
2242       SubString(string, var_start.value(),
2243                 IntPtrAdd(var_end.value(), IntPtrConstant(1))));
2244 
2245   BIND(&if_runtime);
2246   arguments.PopAndReturn(
2247       CallRuntime(Runtime::kStringTrim, context, string, SmiConstant(mode)));
2248 
2249   BIND(&return_emptystring);
2250   arguments.PopAndReturn(EmptyStringConstant());
2251 }
2252 
ScanForNonWhiteSpaceOrLineTerminator(Node * const string_data,Node * const string_data_offset,Node * const is_stringonebyte,Variable * const var_index,Node * const end,int increment,Label * const if_none_found)2253 void StringTrimAssembler::ScanForNonWhiteSpaceOrLineTerminator(
2254     Node* const string_data, Node* const string_data_offset,
2255     Node* const is_stringonebyte, Variable* const var_index, Node* const end,
2256     int increment, Label* const if_none_found) {
2257   Label if_stringisonebyte(this), out(this);
2258 
2259   GotoIf(is_stringonebyte, &if_stringisonebyte);
2260 
2261   // Two Byte String
2262   BuildLoop(
2263       var_index, end, increment, if_none_found, &out, [&](Node* const index) {
2264         return Load(
2265             MachineType::Uint16(), string_data,
2266             WordShl(IntPtrAdd(index, string_data_offset), IntPtrConstant(1)));
2267       });
2268 
2269   BIND(&if_stringisonebyte);
2270   BuildLoop(var_index, end, increment, if_none_found, &out,
2271             [&](Node* const index) {
2272               return Load(MachineType::Uint8(), string_data,
2273                           IntPtrAdd(index, string_data_offset));
2274             });
2275 
2276   BIND(&out);
2277 }
2278 
BuildLoop(Variable * const var_index,Node * const end,int increment,Label * const if_none_found,Label * const out,std::function<Node * (Node *)> get_character)2279 void StringTrimAssembler::BuildLoop(Variable* const var_index, Node* const end,
2280                                     int increment, Label* const if_none_found,
2281                                     Label* const out,
2282                                     std::function<Node*(Node*)> get_character) {
2283   Label loop(this, var_index);
2284   Goto(&loop);
2285   BIND(&loop);
2286   {
2287     Node* const index = var_index->value();
2288     GotoIf(IntPtrEqual(index, end), if_none_found);
2289     GotoIfNotWhiteSpaceOrLineTerminator(
2290         UncheckedCast<Uint32T>(get_character(index)), out);
2291     Increment(var_index, increment);
2292     Goto(&loop);
2293   }
2294 }
2295 
GotoIfNotWhiteSpaceOrLineTerminator(Node * const char_code,Label * const if_not_whitespace)2296 void StringTrimAssembler::GotoIfNotWhiteSpaceOrLineTerminator(
2297     Node* const char_code, Label* const if_not_whitespace) {
2298   Label out(this);
2299 
2300   // 0x0020 - SPACE (Intentionally out of order to fast path a commmon case)
2301   GotoIf(Word32Equal(char_code, Int32Constant(0x0020)), &out);
2302 
2303   // 0x0009 - HORIZONTAL TAB
2304   GotoIf(Uint32LessThan(char_code, Int32Constant(0x0009)), if_not_whitespace);
2305   // 0x000A - LINE FEED OR NEW LINE
2306   // 0x000B - VERTICAL TAB
2307   // 0x000C - FORMFEED
2308   // 0x000D - HORIZONTAL TAB
2309   GotoIf(Uint32LessThanOrEqual(char_code, Int32Constant(0x000D)), &out);
2310 
2311   // Common Non-whitespace characters
2312   GotoIf(Uint32LessThan(char_code, Int32Constant(0x00A0)), if_not_whitespace);
2313 
2314   // 0x00A0 - NO-BREAK SPACE
2315   GotoIf(Word32Equal(char_code, Int32Constant(0x00A0)), &out);
2316 
2317   // 0x1680 - Ogham Space Mark
2318   GotoIf(Word32Equal(char_code, Int32Constant(0x1680)), &out);
2319 
2320   // 0x2000 - EN QUAD
2321   GotoIf(Uint32LessThan(char_code, Int32Constant(0x2000)), if_not_whitespace);
2322   // 0x2001 - EM QUAD
2323   // 0x2002 - EN SPACE
2324   // 0x2003 - EM SPACE
2325   // 0x2004 - THREE-PER-EM SPACE
2326   // 0x2005 - FOUR-PER-EM SPACE
2327   // 0x2006 - SIX-PER-EM SPACE
2328   // 0x2007 - FIGURE SPACE
2329   // 0x2008 - PUNCTUATION SPACE
2330   // 0x2009 - THIN SPACE
2331   // 0x200A - HAIR SPACE
2332   GotoIf(Uint32LessThanOrEqual(char_code, Int32Constant(0x200A)), &out);
2333 
2334   // 0x2028 - LINE SEPARATOR
2335   GotoIf(Word32Equal(char_code, Int32Constant(0x2028)), &out);
2336   // 0x2029 - PARAGRAPH SEPARATOR
2337   GotoIf(Word32Equal(char_code, Int32Constant(0x2029)), &out);
2338   // 0x202F - NARROW NO-BREAK SPACE
2339   GotoIf(Word32Equal(char_code, Int32Constant(0x202F)), &out);
2340   // 0x205F - MEDIUM MATHEMATICAL SPACE
2341   GotoIf(Word32Equal(char_code, Int32Constant(0x205F)), &out);
2342   // 0xFEFF - BYTE ORDER MARK
2343   GotoIf(Word32Equal(char_code, Int32Constant(0xFEFF)), &out);
2344   // 0x3000 - IDEOGRAPHIC SPACE
2345   Branch(Word32Equal(char_code, Int32Constant(0x3000)), &out,
2346          if_not_whitespace);
2347 
2348   BIND(&out);
2349 }
2350 
2351 // ES6 #sec-string.prototype.tostring
TF_BUILTIN(StringPrototypeToString,CodeStubAssembler)2352 TF_BUILTIN(StringPrototypeToString, CodeStubAssembler) {
2353   Node* context = Parameter(Descriptor::kContext);
2354   Node* receiver = Parameter(Descriptor::kReceiver);
2355 
2356   Node* result = ToThisValue(context, receiver, PrimitiveType::kString,
2357                              "String.prototype.toString");
2358   Return(result);
2359 }
2360 
2361 // ES6 #sec-string.prototype.valueof
TF_BUILTIN(StringPrototypeValueOf,CodeStubAssembler)2362 TF_BUILTIN(StringPrototypeValueOf, CodeStubAssembler) {
2363   Node* context = Parameter(Descriptor::kContext);
2364   Node* receiver = Parameter(Descriptor::kReceiver);
2365 
2366   Node* result = ToThisValue(context, receiver, PrimitiveType::kString,
2367                              "String.prototype.valueOf");
2368   Return(result);
2369 }
2370 
TF_BUILTIN(StringPrototypeIterator,CodeStubAssembler)2371 TF_BUILTIN(StringPrototypeIterator, CodeStubAssembler) {
2372   Node* context = Parameter(Descriptor::kContext);
2373   Node* receiver = Parameter(Descriptor::kReceiver);
2374 
2375   Node* string =
2376       ToThisString(context, receiver, "String.prototype[Symbol.iterator]");
2377 
2378   Node* native_context = LoadNativeContext(context);
2379   Node* map =
2380       LoadContextElement(native_context, Context::STRING_ITERATOR_MAP_INDEX);
2381   Node* iterator = Allocate(JSStringIterator::kSize);
2382   StoreMapNoWriteBarrier(iterator, map);
2383   StoreObjectFieldRoot(iterator, JSValue::kPropertiesOrHashOffset,
2384                        Heap::kEmptyFixedArrayRootIndex);
2385   StoreObjectFieldRoot(iterator, JSObject::kElementsOffset,
2386                        Heap::kEmptyFixedArrayRootIndex);
2387   StoreObjectFieldNoWriteBarrier(iterator, JSStringIterator::kStringOffset,
2388                                  string);
2389   Node* index = SmiConstant(0);
2390   StoreObjectFieldNoWriteBarrier(iterator, JSStringIterator::kNextIndexOffset,
2391                                  index);
2392   Return(iterator);
2393 }
2394 
2395 // Return the |word32| codepoint at {index}. Supports SeqStrings and
2396 // ExternalStrings.
LoadSurrogatePairAt(SloppyTNode<String> string,SloppyTNode<IntPtrT> length,SloppyTNode<IntPtrT> index,UnicodeEncoding encoding)2397 TNode<Int32T> StringBuiltinsAssembler::LoadSurrogatePairAt(
2398     SloppyTNode<String> string, SloppyTNode<IntPtrT> length,
2399     SloppyTNode<IntPtrT> index, UnicodeEncoding encoding) {
2400   Label handle_surrogate_pair(this), return_result(this);
2401   TVARIABLE(Int32T, var_result);
2402   TVARIABLE(Int32T, var_trail);
2403   var_result = StringCharCodeAt(string, index);
2404   var_trail = Int32Constant(0);
2405 
2406   GotoIf(Word32NotEqual(Word32And(var_result.value(), Int32Constant(0xFC00)),
2407                         Int32Constant(0xD800)),
2408          &return_result);
2409   TNode<IntPtrT> next_index = IntPtrAdd(index, IntPtrConstant(1));
2410 
2411   GotoIfNot(IntPtrLessThan(next_index, length), &return_result);
2412   var_trail = StringCharCodeAt(string, next_index);
2413   Branch(Word32Equal(Word32And(var_trail.value(), Int32Constant(0xFC00)),
2414                      Int32Constant(0xDC00)),
2415          &handle_surrogate_pair, &return_result);
2416 
2417   BIND(&handle_surrogate_pair);
2418   {
2419     TNode<Int32T> lead = var_result.value();
2420     TNode<Int32T> trail = var_trail.value();
2421 
2422     // Check that this path is only taken if a surrogate pair is found
2423     CSA_SLOW_ASSERT(this,
2424                     Uint32GreaterThanOrEqual(lead, Int32Constant(0xD800)));
2425     CSA_SLOW_ASSERT(this, Uint32LessThan(lead, Int32Constant(0xDC00)));
2426     CSA_SLOW_ASSERT(this,
2427                     Uint32GreaterThanOrEqual(trail, Int32Constant(0xDC00)));
2428     CSA_SLOW_ASSERT(this, Uint32LessThan(trail, Int32Constant(0xE000)));
2429 
2430     switch (encoding) {
2431       case UnicodeEncoding::UTF16:
2432         var_result = Signed(Word32Or(
2433 // Need to swap the order for big-endian platforms
2434 #if V8_TARGET_BIG_ENDIAN
2435             Word32Shl(lead, Int32Constant(16)), trail));
2436 #else
2437             Word32Shl(trail, Int32Constant(16)), lead));
2438 #endif
2439         break;
2440 
2441       case UnicodeEncoding::UTF32: {
2442         // Convert UTF16 surrogate pair into |word32| code point, encoded as
2443         // UTF32.
2444         TNode<Int32T> surrogate_offset =
2445             Int32Constant(0x10000 - (0xD800 << 10) - 0xDC00);
2446 
2447         // (lead << 10) + trail + SURROGATE_OFFSET
2448         var_result = Signed(Int32Add(Word32Shl(lead, Int32Constant(10)),
2449                                      Int32Add(trail, surrogate_offset)));
2450         break;
2451       }
2452     }
2453     Goto(&return_result);
2454   }
2455 
2456   BIND(&return_result);
2457   return var_result.value();
2458 }
2459 
2460 // ES6 #sec-%stringiteratorprototype%.next
TF_BUILTIN(StringIteratorPrototypeNext,StringBuiltinsAssembler)2461 TF_BUILTIN(StringIteratorPrototypeNext, StringBuiltinsAssembler) {
2462   VARIABLE(var_value, MachineRepresentation::kTagged);
2463   VARIABLE(var_done, MachineRepresentation::kTagged);
2464 
2465   var_value.Bind(UndefinedConstant());
2466   var_done.Bind(TrueConstant());
2467 
2468   Label throw_bad_receiver(this), next_codepoint(this), return_result(this);
2469 
2470   Node* context = Parameter(Descriptor::kContext);
2471   Node* iterator = Parameter(Descriptor::kReceiver);
2472 
2473   GotoIf(TaggedIsSmi(iterator), &throw_bad_receiver);
2474   GotoIfNot(
2475       InstanceTypeEqual(LoadInstanceType(iterator), JS_STRING_ITERATOR_TYPE),
2476       &throw_bad_receiver);
2477 
2478   Node* string = LoadObjectField(iterator, JSStringIterator::kStringOffset);
2479   TNode<IntPtrT> position = SmiUntag(
2480       CAST(LoadObjectField(iterator, JSStringIterator::kNextIndexOffset)));
2481   TNode<IntPtrT> length = LoadStringLengthAsWord(string);
2482 
2483   Branch(IntPtrLessThan(position, length), &next_codepoint, &return_result);
2484 
2485   BIND(&next_codepoint);
2486   {
2487     UnicodeEncoding encoding = UnicodeEncoding::UTF16;
2488     TNode<Int32T> ch = LoadSurrogatePairAt(string, length, position, encoding);
2489     TNode<String> value = StringFromSingleCodePoint(ch, encoding);
2490     var_value.Bind(value);
2491     TNode<IntPtrT> length = LoadStringLengthAsWord(value);
2492     StoreObjectFieldNoWriteBarrier(iterator, JSStringIterator::kNextIndexOffset,
2493                                    SmiTag(Signed(IntPtrAdd(position, length))));
2494     var_done.Bind(FalseConstant());
2495     Goto(&return_result);
2496   }
2497 
2498   BIND(&return_result);
2499   {
2500     Node* result =
2501         AllocateJSIteratorResult(context, var_value.value(), var_done.value());
2502     Return(result);
2503   }
2504 
2505   BIND(&throw_bad_receiver);
2506   {
2507     // The {receiver} is not a valid JSGeneratorObject.
2508     ThrowTypeError(context, MessageTemplate::kIncompatibleMethodReceiver,
2509                    StringConstant("String Iterator.prototype.next"), iterator);
2510   }
2511 }
2512 
2513 // -----------------------------------------------------------------------------
2514 // ES6 section B.2.3 Additional Properties of the String.prototype object
2515 
2516 class StringHtmlAssembler : public StringBuiltinsAssembler {
2517  public:
StringHtmlAssembler(compiler::CodeAssemblerState * state)2518   explicit StringHtmlAssembler(compiler::CodeAssemblerState* state)
2519       : StringBuiltinsAssembler(state) {}
2520 
2521  protected:
Generate(Node * const context,Node * const receiver,const char * method_name,const char * tag_name)2522   void Generate(Node* const context, Node* const receiver,
2523                 const char* method_name, const char* tag_name) {
2524     Node* const string = ToThisString(context, receiver, method_name);
2525     std::string open_tag = "<" + std::string(tag_name) + ">";
2526     std::string close_tag = "</" + std::string(tag_name) + ">";
2527 
2528     Node* strings[] = {StringConstant(open_tag.c_str()), string,
2529                        StringConstant(close_tag.c_str())};
2530     Return(ConcatStrings(context, strings, arraysize(strings)));
2531   }
2532 
GenerateWithAttribute(Node * const context,Node * const receiver,const char * method_name,const char * tag_name,const char * attr,Node * const value)2533   void GenerateWithAttribute(Node* const context, Node* const receiver,
2534                              const char* method_name, const char* tag_name,
2535                              const char* attr, Node* const value) {
2536     Node* const string = ToThisString(context, receiver, method_name);
2537     Node* const value_string =
2538         EscapeQuotes(context, ToString_Inline(context, value));
2539     std::string open_tag_attr =
2540         "<" + std::string(tag_name) + " " + std::string(attr) + "=\"";
2541     std::string close_tag = "</" + std::string(tag_name) + ">";
2542 
2543     Node* strings[] = {StringConstant(open_tag_attr.c_str()), value_string,
2544                        StringConstant("\">"), string,
2545                        StringConstant(close_tag.c_str())};
2546     Return(ConcatStrings(context, strings, arraysize(strings)));
2547   }
2548 
ConcatStrings(Node * const context,Node ** strings,int len)2549   Node* ConcatStrings(Node* const context, Node** strings, int len) {
2550     VARIABLE(var_result, MachineRepresentation::kTagged, strings[0]);
2551     for (int i = 1; i < len; i++) {
2552       var_result.Bind(CallStub(CodeFactory::StringAdd(isolate()), context,
2553                                var_result.value(), strings[i]));
2554     }
2555     return var_result.value();
2556   }
2557 
EscapeQuotes(Node * const context,Node * const string)2558   Node* EscapeQuotes(Node* const context, Node* const string) {
2559     CSA_ASSERT(this, IsString(string));
2560     Node* const regexp_function = LoadContextElement(
2561         LoadNativeContext(context), Context::REGEXP_FUNCTION_INDEX);
2562     Node* const initial_map = LoadObjectField(
2563         regexp_function, JSFunction::kPrototypeOrInitialMapOffset);
2564     // TODO(pwong): Refactor to not allocate RegExp
2565     Node* const regexp =
2566         CallRuntime(Runtime::kRegExpInitializeAndCompile, context,
2567                     AllocateJSObjectFromMap(initial_map), StringConstant("\""),
2568                     StringConstant("g"));
2569 
2570     return CallRuntime(Runtime::kRegExpInternalReplace, context, regexp, string,
2571                        StringConstant("&quot;"));
2572   }
2573 };
2574 
2575 // ES6 #sec-string.prototype.anchor
TF_BUILTIN(StringPrototypeAnchor,StringHtmlAssembler)2576 TF_BUILTIN(StringPrototypeAnchor, StringHtmlAssembler) {
2577   Node* const context = Parameter(Descriptor::kContext);
2578   Node* const receiver = Parameter(Descriptor::kReceiver);
2579   Node* const value = Parameter(Descriptor::kValue);
2580   GenerateWithAttribute(context, receiver, "String.prototype.anchor", "a",
2581                         "name", value);
2582 }
2583 
2584 // ES6 #sec-string.prototype.big
TF_BUILTIN(StringPrototypeBig,StringHtmlAssembler)2585 TF_BUILTIN(StringPrototypeBig, StringHtmlAssembler) {
2586   Node* const context = Parameter(Descriptor::kContext);
2587   Node* const receiver = Parameter(Descriptor::kReceiver);
2588   Generate(context, receiver, "String.prototype.big", "big");
2589 }
2590 
2591 // ES6 #sec-string.prototype.blink
TF_BUILTIN(StringPrototypeBlink,StringHtmlAssembler)2592 TF_BUILTIN(StringPrototypeBlink, StringHtmlAssembler) {
2593   Node* const context = Parameter(Descriptor::kContext);
2594   Node* const receiver = Parameter(Descriptor::kReceiver);
2595   Generate(context, receiver, "String.prototype.blink", "blink");
2596 }
2597 
2598 // ES6 #sec-string.prototype.bold
TF_BUILTIN(StringPrototypeBold,StringHtmlAssembler)2599 TF_BUILTIN(StringPrototypeBold, StringHtmlAssembler) {
2600   Node* const context = Parameter(Descriptor::kContext);
2601   Node* const receiver = Parameter(Descriptor::kReceiver);
2602   Generate(context, receiver, "String.prototype.bold", "b");
2603 }
2604 
2605 // ES6 #sec-string.prototype.fontcolor
TF_BUILTIN(StringPrototypeFontcolor,StringHtmlAssembler)2606 TF_BUILTIN(StringPrototypeFontcolor, StringHtmlAssembler) {
2607   Node* const context = Parameter(Descriptor::kContext);
2608   Node* const receiver = Parameter(Descriptor::kReceiver);
2609   Node* const value = Parameter(Descriptor::kValue);
2610   GenerateWithAttribute(context, receiver, "String.prototype.fontcolor", "font",
2611                         "color", value);
2612 }
2613 
2614 // ES6 #sec-string.prototype.fontsize
TF_BUILTIN(StringPrototypeFontsize,StringHtmlAssembler)2615 TF_BUILTIN(StringPrototypeFontsize, StringHtmlAssembler) {
2616   Node* const context = Parameter(Descriptor::kContext);
2617   Node* const receiver = Parameter(Descriptor::kReceiver);
2618   Node* const value = Parameter(Descriptor::kValue);
2619   GenerateWithAttribute(context, receiver, "String.prototype.fontsize", "font",
2620                         "size", value);
2621 }
2622 
2623 // ES6 #sec-string.prototype.fixed
TF_BUILTIN(StringPrototypeFixed,StringHtmlAssembler)2624 TF_BUILTIN(StringPrototypeFixed, StringHtmlAssembler) {
2625   Node* const context = Parameter(Descriptor::kContext);
2626   Node* const receiver = Parameter(Descriptor::kReceiver);
2627   Generate(context, receiver, "String.prototype.fixed", "tt");
2628 }
2629 
2630 // ES6 #sec-string.prototype.italics
TF_BUILTIN(StringPrototypeItalics,StringHtmlAssembler)2631 TF_BUILTIN(StringPrototypeItalics, StringHtmlAssembler) {
2632   Node* const context = Parameter(Descriptor::kContext);
2633   Node* const receiver = Parameter(Descriptor::kReceiver);
2634   Generate(context, receiver, "String.prototype.italics", "i");
2635 }
2636 
2637 // ES6 #sec-string.prototype.link
TF_BUILTIN(StringPrototypeLink,StringHtmlAssembler)2638 TF_BUILTIN(StringPrototypeLink, StringHtmlAssembler) {
2639   Node* const context = Parameter(Descriptor::kContext);
2640   Node* const receiver = Parameter(Descriptor::kReceiver);
2641   Node* const value = Parameter(Descriptor::kValue);
2642   GenerateWithAttribute(context, receiver, "String.prototype.link", "a", "href",
2643                         value);
2644 }
2645 
2646 // ES6 #sec-string.prototype.small
TF_BUILTIN(StringPrototypeSmall,StringHtmlAssembler)2647 TF_BUILTIN(StringPrototypeSmall, StringHtmlAssembler) {
2648   Node* const context = Parameter(Descriptor::kContext);
2649   Node* const receiver = Parameter(Descriptor::kReceiver);
2650   Generate(context, receiver, "String.prototype.small", "small");
2651 }
2652 
2653 // ES6 #sec-string.prototype.strike
TF_BUILTIN(StringPrototypeStrike,StringHtmlAssembler)2654 TF_BUILTIN(StringPrototypeStrike, StringHtmlAssembler) {
2655   Node* const context = Parameter(Descriptor::kContext);
2656   Node* const receiver = Parameter(Descriptor::kReceiver);
2657   Generate(context, receiver, "String.prototype.strike", "strike");
2658 }
2659 
2660 // ES6 #sec-string.prototype.sub
TF_BUILTIN(StringPrototypeSub,StringHtmlAssembler)2661 TF_BUILTIN(StringPrototypeSub, StringHtmlAssembler) {
2662   Node* const context = Parameter(Descriptor::kContext);
2663   Node* const receiver = Parameter(Descriptor::kReceiver);
2664   Generate(context, receiver, "String.prototype.sub", "sub");
2665 }
2666 
2667 // ES6 #sec-string.prototype.sup
TF_BUILTIN(StringPrototypeSup,StringHtmlAssembler)2668 TF_BUILTIN(StringPrototypeSup, StringHtmlAssembler) {
2669   Node* const context = Parameter(Descriptor::kContext);
2670   Node* const receiver = Parameter(Descriptor::kReceiver);
2671   Generate(context, receiver, "String.prototype.sup", "sup");
2672 }
2673 
2674 }  // namespace internal
2675 }  // namespace v8
2676