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