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