1// Copyright 2016 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-regexp.h"
6#include "src/builtins/builtins-utils.h"
7#include "src/builtins/builtins.h"
8#include "src/code-factory.h"
9#include "src/code-stub-assembler.h"
10#include "src/conversions.h"
11#include "src/counters.h"
12#include "src/objects-inl.h"
13#include "src/regexp/regexp-utils.h"
14#include "src/string-case.h"
15#include "src/unicode-inl.h"
16#include "src/unicode.h"
17
18namespace v8 {
19namespace internal {
20
21typedef CodeStubAssembler::ResultMode ResultMode;
22typedef CodeStubAssembler::RelationalComparisonMode RelationalComparisonMode;
23
24class StringBuiltinsAssembler : public CodeStubAssembler {
25 public:
26  explicit StringBuiltinsAssembler(compiler::CodeAssemblerState* state)
27      : CodeStubAssembler(state) {}
28
29 protected:
30  Node* DirectStringData(Node* string, Node* string_instance_type) {
31    // Compute the effective offset of the first character.
32    Variable var_data(this, MachineType::PointerRepresentation());
33    Label if_sequential(this), if_external(this), if_join(this);
34    Branch(Word32Equal(Word32And(string_instance_type,
35                                 Int32Constant(kStringRepresentationMask)),
36                       Int32Constant(kSeqStringTag)),
37           &if_sequential, &if_external);
38
39    Bind(&if_sequential);
40    {
41      var_data.Bind(IntPtrAdd(
42          IntPtrConstant(SeqOneByteString::kHeaderSize - kHeapObjectTag),
43          BitcastTaggedToWord(string)));
44      Goto(&if_join);
45    }
46
47    Bind(&if_external);
48    {
49      // This is only valid for ExternalStrings where the resource data
50      // pointer is cached (i.e. no short external strings).
51      CSA_ASSERT(this, Word32NotEqual(
52                           Word32And(string_instance_type,
53                                     Int32Constant(kShortExternalStringMask)),
54                           Int32Constant(kShortExternalStringTag)));
55      var_data.Bind(LoadObjectField(string, ExternalString::kResourceDataOffset,
56                                    MachineType::Pointer()));
57      Goto(&if_join);
58    }
59
60    Bind(&if_join);
61    return var_data.value();
62  }
63
64  Node* LoadOneByteChar(Node* string, Node* index) {
65    return Load(MachineType::Uint8(), string, OneByteCharOffset(index));
66  }
67
68  Node* OneByteCharAddress(Node* string, Node* index) {
69    Node* offset = OneByteCharOffset(index);
70    return IntPtrAdd(string, offset);
71  }
72
73  Node* OneByteCharOffset(Node* index) {
74    return CharOffset(String::ONE_BYTE_ENCODING, index);
75  }
76
77  Node* CharOffset(String::Encoding encoding, Node* index) {
78    const int header = SeqOneByteString::kHeaderSize - kHeapObjectTag;
79    Node* offset = index;
80    if (encoding == String::TWO_BYTE_ENCODING) {
81      offset = IntPtrAdd(offset, offset);
82    }
83    offset = IntPtrAdd(offset, IntPtrConstant(header));
84    return offset;
85  }
86
87  void DispatchOnStringInstanceType(Node* const instance_type,
88                                    Label* if_onebyte_sequential,
89                                    Label* if_onebyte_external,
90                                    Label* if_otherwise) {
91    const int kMask = kStringRepresentationMask | kStringEncodingMask;
92    Node* const encoding_and_representation =
93        Word32And(instance_type, Int32Constant(kMask));
94
95    int32_t values[] = {
96        kOneByteStringTag | kSeqStringTag,
97        kOneByteStringTag | kExternalStringTag,
98    };
99    Label* labels[] = {
100        if_onebyte_sequential, if_onebyte_external,
101    };
102    STATIC_ASSERT(arraysize(values) == arraysize(labels));
103
104    Switch(encoding_and_representation, if_otherwise, values, labels,
105           arraysize(values));
106  }
107
108  void GenerateStringEqual(ResultMode mode);
109  void GenerateStringRelationalComparison(RelationalComparisonMode mode);
110
111  Node* ToSmiBetweenZeroAnd(Node* context, Node* value, Node* limit);
112
113  Node* LoadSurrogatePairAt(Node* string, Node* length, Node* index,
114                            UnicodeEncoding encoding);
115
116  void StringIndexOf(Node* receiver, Node* instance_type, Node* search_string,
117                     Node* search_string_instance_type, Node* position,
118                     std::function<void(Node*)> f_return);
119
120  Node* IsNullOrUndefined(Node* const value);
121  void RequireObjectCoercible(Node* const context, Node* const value,
122                              const char* method_name);
123
124  Node* SmiIsNegative(Node* const value) {
125    return SmiLessThan(value, SmiConstant(0));
126  }
127
128  // Implements boilerplate logic for {match, split, replace, search} of the
129  // form:
130  //
131  //  if (!IS_NULL_OR_UNDEFINED(object)) {
132  //    var maybe_function = object[symbol];
133  //    if (!IS_UNDEFINED(maybe_function)) {
134  //      return %_Call(maybe_function, ...);
135  //    }
136  //  }
137  //
138  // Contains fast paths for Smi and RegExp objects.
139  typedef std::function<Node*()> NodeFunction0;
140  typedef std::function<Node*(Node* fn)> NodeFunction1;
141  void MaybeCallFunctionAtSymbol(Node* const context, Node* const object,
142                                 Handle<Symbol> symbol,
143                                 const NodeFunction0& regexp_call,
144                                 const NodeFunction1& generic_call);
145};
146
147void StringBuiltinsAssembler::GenerateStringEqual(ResultMode mode) {
148  // Here's pseudo-code for the algorithm below in case of kDontNegateResult
149  // mode; for kNegateResult mode we properly negate the result.
150  //
151  // if (lhs == rhs) return true;
152  // if (lhs->length() != rhs->length()) return false;
153  // if (lhs->IsInternalizedString() && rhs->IsInternalizedString()) {
154  //   return false;
155  // }
156  // if (lhs->IsSeqOneByteString() && rhs->IsSeqOneByteString()) {
157  //   for (i = 0; i != lhs->length(); ++i) {
158  //     if (lhs[i] != rhs[i]) return false;
159  //   }
160  //   return true;
161  // }
162  // if (lhs and/or rhs are indirect strings) {
163  //   unwrap them and restart from the beginning;
164  // }
165  // return %StringEqual(lhs, rhs);
166
167  Variable var_left(this, MachineRepresentation::kTagged);
168  Variable var_right(this, MachineRepresentation::kTagged);
169  var_left.Bind(Parameter(0));
170  var_right.Bind(Parameter(1));
171  Node* context = Parameter(2);
172
173  Variable* input_vars[2] = {&var_left, &var_right};
174  Label if_equal(this), if_notequal(this), restart(this, 2, input_vars);
175  Goto(&restart);
176  Bind(&restart);
177  Node* lhs = var_left.value();
178  Node* rhs = var_right.value();
179
180  // Fast check to see if {lhs} and {rhs} refer to the same String object.
181  GotoIf(WordEqual(lhs, rhs), &if_equal);
182
183  // Load the length of {lhs} and {rhs}.
184  Node* lhs_length = LoadStringLength(lhs);
185  Node* rhs_length = LoadStringLength(rhs);
186
187  // Strings with different lengths cannot be equal.
188  GotoIf(WordNotEqual(lhs_length, rhs_length), &if_notequal);
189
190  // Load instance types of {lhs} and {rhs}.
191  Node* lhs_instance_type = LoadInstanceType(lhs);
192  Node* rhs_instance_type = LoadInstanceType(rhs);
193
194  // Combine the instance types into a single 16-bit value, so we can check
195  // both of them at once.
196  Node* both_instance_types = Word32Or(
197      lhs_instance_type, Word32Shl(rhs_instance_type, Int32Constant(8)));
198
199  // Check if both {lhs} and {rhs} are internalized. Since we already know
200  // that they're not the same object, they're not equal in that case.
201  int const kBothInternalizedMask =
202      kIsNotInternalizedMask | (kIsNotInternalizedMask << 8);
203  int const kBothInternalizedTag = kInternalizedTag | (kInternalizedTag << 8);
204  GotoIf(Word32Equal(Word32And(both_instance_types,
205                               Int32Constant(kBothInternalizedMask)),
206                     Int32Constant(kBothInternalizedTag)),
207         &if_notequal);
208
209  // Check that both {lhs} and {rhs} are flat one-byte strings, and that
210  // in case of ExternalStrings the data pointer is cached..
211  STATIC_ASSERT(kShortExternalStringTag != 0);
212  int const kBothDirectOneByteStringMask =
213      kStringEncodingMask | kIsIndirectStringMask | kShortExternalStringMask |
214      ((kStringEncodingMask | kIsIndirectStringMask | kShortExternalStringMask)
215       << 8);
216  int const kBothDirectOneByteStringTag =
217      kOneByteStringTag | (kOneByteStringTag << 8);
218  Label if_bothdirectonebytestrings(this), if_notbothdirectonebytestrings(this);
219  Branch(Word32Equal(Word32And(both_instance_types,
220                               Int32Constant(kBothDirectOneByteStringMask)),
221                     Int32Constant(kBothDirectOneByteStringTag)),
222         &if_bothdirectonebytestrings, &if_notbothdirectonebytestrings);
223
224  Bind(&if_bothdirectonebytestrings);
225  {
226    // Compute the effective offset of the first character.
227    Node* lhs_data = DirectStringData(lhs, lhs_instance_type);
228    Node* rhs_data = DirectStringData(rhs, rhs_instance_type);
229
230    // Compute the first offset after the string from the length.
231    Node* length = SmiUntag(lhs_length);
232
233    // Loop over the {lhs} and {rhs} strings to see if they are equal.
234    Variable var_offset(this, MachineType::PointerRepresentation());
235    Label loop(this, &var_offset);
236    var_offset.Bind(IntPtrConstant(0));
237    Goto(&loop);
238    Bind(&loop);
239    {
240      // If {offset} equals {end}, no difference was found, so the
241      // strings are equal.
242      Node* offset = var_offset.value();
243      GotoIf(WordEqual(offset, length), &if_equal);
244
245      // Load the next characters from {lhs} and {rhs}.
246      Node* lhs_value = Load(MachineType::Uint8(), lhs_data, offset);
247      Node* rhs_value = Load(MachineType::Uint8(), rhs_data, offset);
248
249      // Check if the characters match.
250      GotoIf(Word32NotEqual(lhs_value, rhs_value), &if_notequal);
251
252      // Advance to next character.
253      var_offset.Bind(IntPtrAdd(offset, IntPtrConstant(1)));
254      Goto(&loop);
255    }
256  }
257
258  Bind(&if_notbothdirectonebytestrings);
259  {
260    // Try to unwrap indirect strings, restart the above attempt on success.
261    MaybeDerefIndirectStrings(&var_left, lhs_instance_type, &var_right,
262                              rhs_instance_type, &restart);
263    // TODO(bmeurer): Add support for two byte string equality checks.
264
265    Runtime::FunctionId function_id = (mode == ResultMode::kDontNegateResult)
266                                          ? Runtime::kStringEqual
267                                          : Runtime::kStringNotEqual;
268    TailCallRuntime(function_id, context, lhs, rhs);
269  }
270
271  Bind(&if_equal);
272  Return(BooleanConstant(mode == ResultMode::kDontNegateResult));
273
274  Bind(&if_notequal);
275  Return(BooleanConstant(mode == ResultMode::kNegateResult));
276}
277
278void StringBuiltinsAssembler::GenerateStringRelationalComparison(
279    RelationalComparisonMode mode) {
280  Variable var_left(this, MachineRepresentation::kTagged);
281  Variable var_right(this, MachineRepresentation::kTagged);
282  var_left.Bind(Parameter(0));
283  var_right.Bind(Parameter(1));
284  Node* context = Parameter(2);
285
286  Variable* input_vars[2] = {&var_left, &var_right};
287  Label if_less(this), if_equal(this), if_greater(this);
288  Label restart(this, 2, input_vars);
289  Goto(&restart);
290  Bind(&restart);
291
292  Node* lhs = var_left.value();
293  Node* rhs = var_right.value();
294  // Fast check to see if {lhs} and {rhs} refer to the same String object.
295  GotoIf(WordEqual(lhs, rhs), &if_equal);
296
297  // Load instance types of {lhs} and {rhs}.
298  Node* lhs_instance_type = LoadInstanceType(lhs);
299  Node* rhs_instance_type = LoadInstanceType(rhs);
300
301  // Combine the instance types into a single 16-bit value, so we can check
302  // both of them at once.
303  Node* both_instance_types = Word32Or(
304      lhs_instance_type, Word32Shl(rhs_instance_type, Int32Constant(8)));
305
306  // Check that both {lhs} and {rhs} are flat one-byte strings.
307  int const kBothSeqOneByteStringMask =
308      kStringEncodingMask | kStringRepresentationMask |
309      ((kStringEncodingMask | kStringRepresentationMask) << 8);
310  int const kBothSeqOneByteStringTag =
311      kOneByteStringTag | kSeqStringTag |
312      ((kOneByteStringTag | kSeqStringTag) << 8);
313  Label if_bothonebyteseqstrings(this), if_notbothonebyteseqstrings(this);
314  Branch(Word32Equal(Word32And(both_instance_types,
315                               Int32Constant(kBothSeqOneByteStringMask)),
316                     Int32Constant(kBothSeqOneByteStringTag)),
317         &if_bothonebyteseqstrings, &if_notbothonebyteseqstrings);
318
319  Bind(&if_bothonebyteseqstrings);
320  {
321    // Load the length of {lhs} and {rhs}.
322    Node* lhs_length = LoadStringLength(lhs);
323    Node* rhs_length = LoadStringLength(rhs);
324
325    // Determine the minimum length.
326    Node* length = SmiMin(lhs_length, rhs_length);
327
328    // Compute the effective offset of the first character.
329    Node* begin =
330        IntPtrConstant(SeqOneByteString::kHeaderSize - kHeapObjectTag);
331
332    // Compute the first offset after the string from the length.
333    Node* end = IntPtrAdd(begin, SmiUntag(length));
334
335    // Loop over the {lhs} and {rhs} strings to see if they are equal.
336    Variable var_offset(this, MachineType::PointerRepresentation());
337    Label loop(this, &var_offset);
338    var_offset.Bind(begin);
339    Goto(&loop);
340    Bind(&loop);
341    {
342      // Check if {offset} equals {end}.
343      Node* offset = var_offset.value();
344      Label if_done(this), if_notdone(this);
345      Branch(WordEqual(offset, end), &if_done, &if_notdone);
346
347      Bind(&if_notdone);
348      {
349        // Load the next characters from {lhs} and {rhs}.
350        Node* lhs_value = Load(MachineType::Uint8(), lhs, offset);
351        Node* rhs_value = Load(MachineType::Uint8(), rhs, offset);
352
353        // Check if the characters match.
354        Label if_valueissame(this), if_valueisnotsame(this);
355        Branch(Word32Equal(lhs_value, rhs_value), &if_valueissame,
356               &if_valueisnotsame);
357
358        Bind(&if_valueissame);
359        {
360          // Advance to next character.
361          var_offset.Bind(IntPtrAdd(offset, IntPtrConstant(1)));
362        }
363        Goto(&loop);
364
365        Bind(&if_valueisnotsame);
366        Branch(Uint32LessThan(lhs_value, rhs_value), &if_less, &if_greater);
367      }
368
369      Bind(&if_done);
370      {
371        // All characters up to the min length are equal, decide based on
372        // string length.
373        GotoIf(SmiEqual(lhs_length, rhs_length), &if_equal);
374        BranchIfSmiLessThan(lhs_length, rhs_length, &if_less, &if_greater);
375      }
376    }
377    }
378
379    Bind(&if_notbothonebyteseqstrings);
380    {
381      // Try to unwrap indirect strings, restart the above attempt on success.
382      MaybeDerefIndirectStrings(&var_left, lhs_instance_type, &var_right,
383                                rhs_instance_type, &restart);
384      // TODO(bmeurer): Add support for two byte string relational comparisons.
385      switch (mode) {
386        case RelationalComparisonMode::kLessThan:
387          TailCallRuntime(Runtime::kStringLessThan, context, lhs, rhs);
388          break;
389        case RelationalComparisonMode::kLessThanOrEqual:
390          TailCallRuntime(Runtime::kStringLessThanOrEqual, context, lhs, rhs);
391          break;
392        case RelationalComparisonMode::kGreaterThan:
393          TailCallRuntime(Runtime::kStringGreaterThan, context, lhs, rhs);
394          break;
395        case RelationalComparisonMode::kGreaterThanOrEqual:
396          TailCallRuntime(Runtime::kStringGreaterThanOrEqual, context, lhs,
397                          rhs);
398          break;
399      }
400    }
401
402    Bind(&if_less);
403    switch (mode) {
404      case RelationalComparisonMode::kLessThan:
405      case RelationalComparisonMode::kLessThanOrEqual:
406        Return(BooleanConstant(true));
407        break;
408
409      case RelationalComparisonMode::kGreaterThan:
410      case RelationalComparisonMode::kGreaterThanOrEqual:
411        Return(BooleanConstant(false));
412        break;
413  }
414
415  Bind(&if_equal);
416  switch (mode) {
417    case RelationalComparisonMode::kLessThan:
418    case RelationalComparisonMode::kGreaterThan:
419      Return(BooleanConstant(false));
420      break;
421
422    case RelationalComparisonMode::kLessThanOrEqual:
423    case RelationalComparisonMode::kGreaterThanOrEqual:
424      Return(BooleanConstant(true));
425      break;
426  }
427
428  Bind(&if_greater);
429  switch (mode) {
430    case RelationalComparisonMode::kLessThan:
431    case RelationalComparisonMode::kLessThanOrEqual:
432      Return(BooleanConstant(false));
433      break;
434
435    case RelationalComparisonMode::kGreaterThan:
436    case RelationalComparisonMode::kGreaterThanOrEqual:
437      Return(BooleanConstant(true));
438      break;
439  }
440}
441
442TF_BUILTIN(StringEqual, StringBuiltinsAssembler) {
443  GenerateStringEqual(ResultMode::kDontNegateResult);
444}
445
446TF_BUILTIN(StringNotEqual, StringBuiltinsAssembler) {
447  GenerateStringEqual(ResultMode::kNegateResult);
448}
449
450TF_BUILTIN(StringLessThan, StringBuiltinsAssembler) {
451  GenerateStringRelationalComparison(RelationalComparisonMode::kLessThan);
452}
453
454TF_BUILTIN(StringLessThanOrEqual, StringBuiltinsAssembler) {
455  GenerateStringRelationalComparison(
456      RelationalComparisonMode::kLessThanOrEqual);
457}
458
459TF_BUILTIN(StringGreaterThan, StringBuiltinsAssembler) {
460  GenerateStringRelationalComparison(RelationalComparisonMode::kGreaterThan);
461}
462
463TF_BUILTIN(StringGreaterThanOrEqual, StringBuiltinsAssembler) {
464  GenerateStringRelationalComparison(
465      RelationalComparisonMode::kGreaterThanOrEqual);
466}
467
468TF_BUILTIN(StringCharAt, CodeStubAssembler) {
469  Node* receiver = Parameter(0);
470  Node* position = Parameter(1);
471
472  // Load the character code at the {position} from the {receiver}.
473  Node* code = StringCharCodeAt(receiver, position, INTPTR_PARAMETERS);
474
475  // And return the single character string with only that {code}
476  Node* result = StringFromCharCode(code);
477  Return(result);
478}
479
480TF_BUILTIN(StringCharCodeAt, CodeStubAssembler) {
481  Node* receiver = Parameter(0);
482  Node* position = Parameter(1);
483
484  // Load the character code at the {position} from the {receiver}.
485  Node* code = StringCharCodeAt(receiver, position, INTPTR_PARAMETERS);
486
487  // And return it as TaggedSigned value.
488  // TODO(turbofan): Allow builtins to return values untagged.
489  Node* result = SmiFromWord32(code);
490  Return(result);
491}
492
493// -----------------------------------------------------------------------------
494// ES6 section 21.1 String Objects
495
496// ES6 section 21.1.2.1 String.fromCharCode ( ...codeUnits )
497TF_BUILTIN(StringFromCharCode, CodeStubAssembler) {
498  Node* argc = Parameter(BuiltinDescriptor::kArgumentsCount);
499  Node* context = Parameter(BuiltinDescriptor::kContext);
500
501  CodeStubArguments arguments(this, ChangeInt32ToIntPtr(argc));
502  // From now on use word-size argc value.
503  argc = arguments.GetLength();
504
505  // Check if we have exactly one argument (plus the implicit receiver), i.e.
506  // if the parent frame is not an arguments adaptor frame.
507  Label if_oneargument(this), if_notoneargument(this);
508  Branch(WordEqual(argc, IntPtrConstant(1)), &if_oneargument,
509         &if_notoneargument);
510
511  Bind(&if_oneargument);
512  {
513    // Single argument case, perform fast single character string cache lookup
514    // for one-byte code units, or fall back to creating a single character
515    // string on the fly otherwise.
516    Node* code = arguments.AtIndex(0);
517    Node* code32 = TruncateTaggedToWord32(context, code);
518    Node* code16 = Word32And(code32, Int32Constant(String::kMaxUtf16CodeUnit));
519    Node* result = StringFromCharCode(code16);
520    arguments.PopAndReturn(result);
521  }
522
523  Node* code16 = nullptr;
524  Bind(&if_notoneargument);
525  {
526    Label two_byte(this);
527    // Assume that the resulting string contains only one-byte characters.
528    Node* one_byte_result = AllocateSeqOneByteString(context, argc);
529
530    Variable max_index(this, MachineType::PointerRepresentation());
531    max_index.Bind(IntPtrConstant(0));
532
533    // Iterate over the incoming arguments, converting them to 8-bit character
534    // codes. Stop if any of the conversions generates a code that doesn't fit
535    // in 8 bits.
536    CodeStubAssembler::VariableList vars({&max_index}, zone());
537    arguments.ForEach(vars, [this, context, &two_byte, &max_index, &code16,
538                             one_byte_result](Node* arg) {
539      Node* code32 = TruncateTaggedToWord32(context, arg);
540      code16 = Word32And(code32, Int32Constant(String::kMaxUtf16CodeUnit));
541
542      GotoIf(
543          Int32GreaterThan(code16, Int32Constant(String::kMaxOneByteCharCode)),
544          &two_byte);
545
546      // The {code16} fits into the SeqOneByteString {one_byte_result}.
547      Node* offset = ElementOffsetFromIndex(
548          max_index.value(), UINT8_ELEMENTS,
549          CodeStubAssembler::INTPTR_PARAMETERS,
550          SeqOneByteString::kHeaderSize - kHeapObjectTag);
551      StoreNoWriteBarrier(MachineRepresentation::kWord8, one_byte_result,
552                          offset, code16);
553      max_index.Bind(IntPtrAdd(max_index.value(), IntPtrConstant(1)));
554    });
555    arguments.PopAndReturn(one_byte_result);
556
557    Bind(&two_byte);
558
559    // At least one of the characters in the string requires a 16-bit
560    // representation.  Allocate a SeqTwoByteString to hold the resulting
561    // string.
562    Node* two_byte_result = AllocateSeqTwoByteString(context, argc);
563
564    // Copy the characters that have already been put in the 8-bit string into
565    // their corresponding positions in the new 16-bit string.
566    Node* zero = IntPtrConstant(0);
567    CopyStringCharacters(one_byte_result, two_byte_result, zero, zero,
568                         max_index.value(), String::ONE_BYTE_ENCODING,
569                         String::TWO_BYTE_ENCODING,
570                         CodeStubAssembler::INTPTR_PARAMETERS);
571
572    // Write the character that caused the 8-bit to 16-bit fault.
573    Node* max_index_offset =
574        ElementOffsetFromIndex(max_index.value(), UINT16_ELEMENTS,
575                               CodeStubAssembler::INTPTR_PARAMETERS,
576                               SeqTwoByteString::kHeaderSize - kHeapObjectTag);
577    StoreNoWriteBarrier(MachineRepresentation::kWord16, two_byte_result,
578                        max_index_offset, code16);
579    max_index.Bind(IntPtrAdd(max_index.value(), IntPtrConstant(1)));
580
581    // Resume copying the passed-in arguments from the same place where the
582    // 8-bit copy stopped, but this time copying over all of the characters
583    // using a 16-bit representation.
584    arguments.ForEach(
585        vars,
586        [this, context, two_byte_result, &max_index](Node* arg) {
587          Node* code32 = TruncateTaggedToWord32(context, arg);
588          Node* code16 =
589              Word32And(code32, Int32Constant(String::kMaxUtf16CodeUnit));
590
591          Node* offset = ElementOffsetFromIndex(
592              max_index.value(), UINT16_ELEMENTS,
593              CodeStubAssembler::INTPTR_PARAMETERS,
594              SeqTwoByteString::kHeaderSize - kHeapObjectTag);
595          StoreNoWriteBarrier(MachineRepresentation::kWord16, two_byte_result,
596                              offset, code16);
597          max_index.Bind(IntPtrAdd(max_index.value(), IntPtrConstant(1)));
598        },
599        max_index.value());
600
601    arguments.PopAndReturn(two_byte_result);
602  }
603}
604
605namespace {  // for String.fromCodePoint
606
607bool IsValidCodePoint(Isolate* isolate, Handle<Object> value) {
608  if (!value->IsNumber() && !Object::ToNumber(value).ToHandle(&value)) {
609    return false;
610  }
611
612  if (Object::ToInteger(isolate, value).ToHandleChecked()->Number() !=
613      value->Number()) {
614    return false;
615  }
616
617  if (value->Number() < 0 || value->Number() > 0x10FFFF) {
618    return false;
619  }
620
621  return true;
622}
623
624uc32 NextCodePoint(Isolate* isolate, BuiltinArguments args, int index) {
625  Handle<Object> value = args.at(1 + index);
626  ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, value, Object::ToNumber(value), -1);
627  if (!IsValidCodePoint(isolate, value)) {
628    isolate->Throw(*isolate->factory()->NewRangeError(
629        MessageTemplate::kInvalidCodePoint, value));
630    return -1;
631  }
632  return DoubleToUint32(value->Number());
633}
634
635}  // namespace
636
637// ES6 section 21.1.2.2 String.fromCodePoint ( ...codePoints )
638BUILTIN(StringFromCodePoint) {
639  HandleScope scope(isolate);
640  int const length = args.length() - 1;
641  if (length == 0) return isolate->heap()->empty_string();
642  DCHECK_LT(0, length);
643
644  // Optimistically assume that the resulting String contains only one byte
645  // characters.
646  List<uint8_t> one_byte_buffer(length);
647  uc32 code = 0;
648  int index;
649  for (index = 0; index < length; index++) {
650    code = NextCodePoint(isolate, args, index);
651    if (code < 0) {
652      return isolate->heap()->exception();
653    }
654    if (code > String::kMaxOneByteCharCode) {
655      break;
656    }
657    one_byte_buffer.Add(code);
658  }
659
660  if (index == length) {
661    RETURN_RESULT_OR_FAILURE(isolate, isolate->factory()->NewStringFromOneByte(
662                                          one_byte_buffer.ToConstVector()));
663  }
664
665  List<uc16> two_byte_buffer(length - index);
666
667  while (true) {
668    if (code <= static_cast<uc32>(unibrow::Utf16::kMaxNonSurrogateCharCode)) {
669      two_byte_buffer.Add(code);
670    } else {
671      two_byte_buffer.Add(unibrow::Utf16::LeadSurrogate(code));
672      two_byte_buffer.Add(unibrow::Utf16::TrailSurrogate(code));
673    }
674
675    if (++index == length) {
676      break;
677    }
678    code = NextCodePoint(isolate, args, index);
679    if (code < 0) {
680      return isolate->heap()->exception();
681    }
682  }
683
684  Handle<SeqTwoByteString> result;
685  ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
686      isolate, result,
687      isolate->factory()->NewRawTwoByteString(one_byte_buffer.length() +
688                                              two_byte_buffer.length()));
689
690  CopyChars(result->GetChars(), one_byte_buffer.ToConstVector().start(),
691            one_byte_buffer.length());
692  CopyChars(result->GetChars() + one_byte_buffer.length(),
693            two_byte_buffer.ToConstVector().start(), two_byte_buffer.length());
694
695  return *result;
696}
697
698// ES6 section 21.1.3.1 String.prototype.charAt ( pos )
699TF_BUILTIN(StringPrototypeCharAt, CodeStubAssembler) {
700  Node* receiver = Parameter(0);
701  Node* position = Parameter(1);
702  Node* context = Parameter(4);
703
704  // Check that {receiver} is coercible to Object and convert it to a String.
705  receiver = ToThisString(context, receiver, "String.prototype.charAt");
706
707  // Convert the {position} to a Smi and check that it's in bounds of the
708  // {receiver}.
709  {
710    Label return_emptystring(this, Label::kDeferred);
711    position =
712        ToInteger(context, position, CodeStubAssembler::kTruncateMinusZero);
713    GotoIfNot(TaggedIsSmi(position), &return_emptystring);
714
715    // Determine the actual length of the {receiver} String.
716    Node* receiver_length = LoadObjectField(receiver, String::kLengthOffset);
717
718    // Return "" if the Smi {position} is outside the bounds of the {receiver}.
719    Label if_positioninbounds(this);
720    Branch(SmiAboveOrEqual(position, receiver_length), &return_emptystring,
721           &if_positioninbounds);
722
723    Bind(&return_emptystring);
724    Return(EmptyStringConstant());
725
726    Bind(&if_positioninbounds);
727  }
728
729  // Load the character code at the {position} from the {receiver}.
730  Node* code = StringCharCodeAt(receiver, position);
731
732  // And return the single character string with only that {code}.
733  Node* result = StringFromCharCode(code);
734  Return(result);
735}
736
737// ES6 section 21.1.3.2 String.prototype.charCodeAt ( pos )
738TF_BUILTIN(StringPrototypeCharCodeAt, CodeStubAssembler) {
739  Node* receiver = Parameter(0);
740  Node* position = Parameter(1);
741  Node* context = Parameter(4);
742
743  // Check that {receiver} is coercible to Object and convert it to a String.
744  receiver = ToThisString(context, receiver, "String.prototype.charCodeAt");
745
746  // Convert the {position} to a Smi and check that it's in bounds of the
747  // {receiver}.
748  {
749    Label return_nan(this, Label::kDeferred);
750    position =
751        ToInteger(context, position, CodeStubAssembler::kTruncateMinusZero);
752    GotoIfNot(TaggedIsSmi(position), &return_nan);
753
754    // Determine the actual length of the {receiver} String.
755    Node* receiver_length = LoadObjectField(receiver, String::kLengthOffset);
756
757    // Return NaN if the Smi {position} is outside the bounds of the {receiver}.
758    Label if_positioninbounds(this);
759    Branch(SmiAboveOrEqual(position, receiver_length), &return_nan,
760           &if_positioninbounds);
761
762    Bind(&return_nan);
763    Return(NaNConstant());
764
765    Bind(&if_positioninbounds);
766  }
767
768  // Load the character at the {position} from the {receiver}.
769  Node* value = StringCharCodeAt(receiver, position);
770  Node* result = SmiFromWord32(value);
771  Return(result);
772}
773
774// ES6 section 21.1.3.6
775// String.prototype.endsWith ( searchString [ , endPosition ] )
776BUILTIN(StringPrototypeEndsWith) {
777  HandleScope handle_scope(isolate);
778  TO_THIS_STRING(str, "String.prototype.endsWith");
779
780  // Check if the search string is a regExp and fail if it is.
781  Handle<Object> search = args.atOrUndefined(isolate, 1);
782  Maybe<bool> is_reg_exp = RegExpUtils::IsRegExp(isolate, search);
783  if (is_reg_exp.IsNothing()) {
784    DCHECK(isolate->has_pending_exception());
785    return isolate->heap()->exception();
786  }
787  if (is_reg_exp.FromJust()) {
788    THROW_NEW_ERROR_RETURN_FAILURE(
789        isolate, NewTypeError(MessageTemplate::kFirstArgumentNotRegExp,
790                              isolate->factory()->NewStringFromStaticChars(
791                                  "String.prototype.endsWith")));
792  }
793  Handle<String> search_string;
794  ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, search_string,
795                                     Object::ToString(isolate, search));
796
797  Handle<Object> position = args.atOrUndefined(isolate, 2);
798  int end;
799
800  if (position->IsUndefined(isolate)) {
801    end = str->length();
802  } else {
803    ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, position,
804                                       Object::ToInteger(isolate, position));
805    end = str->ToValidIndex(*position);
806  }
807
808  int start = end - search_string->length();
809  if (start < 0) return isolate->heap()->false_value();
810
811  str = String::Flatten(str);
812  search_string = String::Flatten(search_string);
813
814  DisallowHeapAllocation no_gc;  // ensure vectors stay valid
815  String::FlatContent str_content = str->GetFlatContent();
816  String::FlatContent search_content = search_string->GetFlatContent();
817
818  if (str_content.IsOneByte() && search_content.IsOneByte()) {
819    Vector<const uint8_t> str_vector = str_content.ToOneByteVector();
820    Vector<const uint8_t> search_vector = search_content.ToOneByteVector();
821
822    return isolate->heap()->ToBoolean(memcmp(str_vector.start() + start,
823                                             search_vector.start(),
824                                             search_string->length()) == 0);
825  }
826
827  FlatStringReader str_reader(isolate, str);
828  FlatStringReader search_reader(isolate, search_string);
829
830  for (int i = 0; i < search_string->length(); i++) {
831    if (str_reader.Get(start + i) != search_reader.Get(i)) {
832      return isolate->heap()->false_value();
833    }
834  }
835  return isolate->heap()->true_value();
836}
837
838// ES6 section 21.1.3.7
839// String.prototype.includes ( searchString [ , position ] )
840BUILTIN(StringPrototypeIncludes) {
841  HandleScope handle_scope(isolate);
842  TO_THIS_STRING(str, "String.prototype.includes");
843
844  // Check if the search string is a regExp and fail if it is.
845  Handle<Object> search = args.atOrUndefined(isolate, 1);
846  Maybe<bool> is_reg_exp = RegExpUtils::IsRegExp(isolate, search);
847  if (is_reg_exp.IsNothing()) {
848    DCHECK(isolate->has_pending_exception());
849    return isolate->heap()->exception();
850  }
851  if (is_reg_exp.FromJust()) {
852    THROW_NEW_ERROR_RETURN_FAILURE(
853        isolate, NewTypeError(MessageTemplate::kFirstArgumentNotRegExp,
854                              isolate->factory()->NewStringFromStaticChars(
855                                  "String.prototype.includes")));
856  }
857  Handle<String> search_string;
858  ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, search_string,
859                                     Object::ToString(isolate, search));
860  Handle<Object> position;
861  ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
862      isolate, position,
863      Object::ToInteger(isolate, args.atOrUndefined(isolate, 2)));
864
865  uint32_t index = str->ToValidIndex(*position);
866  int index_in_str = String::IndexOf(isolate, str, search_string, index);
867  return *isolate->factory()->ToBoolean(index_in_str != -1);
868}
869
870void StringBuiltinsAssembler::StringIndexOf(
871    Node* receiver, Node* instance_type, Node* search_string,
872    Node* search_string_instance_type, Node* position,
873    std::function<void(Node*)> f_return) {
874  CSA_ASSERT(this, IsString(receiver));
875  CSA_ASSERT(this, IsString(search_string));
876  CSA_ASSERT(this, TaggedIsSmi(position));
877
878  Label zero_length_needle(this),
879      call_runtime_unchecked(this, Label::kDeferred), return_minus_1(this),
880      check_search_string(this), continue_fast_path(this);
881
882  Node* const int_zero = IntPtrConstant(0);
883  Variable var_needle_byte(this, MachineType::PointerRepresentation(),
884                           int_zero);
885  Variable var_string_addr(this, MachineType::PointerRepresentation(),
886                           int_zero);
887
888  Node* needle_length = SmiUntag(LoadStringLength(search_string));
889  // Use faster/complex runtime fallback for long search strings.
890  GotoIf(IntPtrLessThan(IntPtrConstant(1), needle_length),
891         &call_runtime_unchecked);
892  Node* string_length = SmiUntag(LoadStringLength(receiver));
893  Node* start_position = IntPtrMax(SmiUntag(position), int_zero);
894
895  GotoIf(IntPtrEqual(int_zero, needle_length), &zero_length_needle);
896  // Check that the needle fits in the start position.
897  GotoIfNot(IntPtrLessThanOrEqual(needle_length,
898                                  IntPtrSub(string_length, start_position)),
899            &return_minus_1);
900
901  // Load the string address.
902  {
903    Label if_onebyte_sequential(this);
904    Label if_onebyte_external(this, Label::kDeferred);
905
906    // Only support one-byte strings on the fast path.
907    DispatchOnStringInstanceType(instance_type, &if_onebyte_sequential,
908                                 &if_onebyte_external, &call_runtime_unchecked);
909
910    Bind(&if_onebyte_sequential);
911    {
912      var_string_addr.Bind(
913          OneByteCharAddress(BitcastTaggedToWord(receiver), start_position));
914      Goto(&check_search_string);
915    }
916
917    Bind(&if_onebyte_external);
918    {
919      Node* const unpacked = TryDerefExternalString(receiver, instance_type,
920                                                    &call_runtime_unchecked);
921      var_string_addr.Bind(OneByteCharAddress(unpacked, start_position));
922      Goto(&check_search_string);
923    }
924  }
925
926  // Load the needle character.
927  Bind(&check_search_string);
928  {
929    Label if_onebyte_sequential(this);
930    Label if_onebyte_external(this, Label::kDeferred);
931
932    DispatchOnStringInstanceType(search_string_instance_type,
933                                 &if_onebyte_sequential, &if_onebyte_external,
934                                 &call_runtime_unchecked);
935
936    Bind(&if_onebyte_sequential);
937    {
938      var_needle_byte.Bind(
939          ChangeInt32ToIntPtr(LoadOneByteChar(search_string, int_zero)));
940      Goto(&continue_fast_path);
941    }
942
943    Bind(&if_onebyte_external);
944    {
945      Node* const unpacked = TryDerefExternalString(
946          search_string, search_string_instance_type, &call_runtime_unchecked);
947      var_needle_byte.Bind(
948          ChangeInt32ToIntPtr(LoadOneByteChar(unpacked, int_zero)));
949      Goto(&continue_fast_path);
950    }
951  }
952
953  Bind(&continue_fast_path);
954  {
955    Node* needle_byte = var_needle_byte.value();
956    Node* string_addr = var_string_addr.value();
957    Node* search_length = IntPtrSub(string_length, start_position);
958    // Call out to the highly optimized memchr to perform the actual byte
959    // search.
960    Node* memchr =
961        ExternalConstant(ExternalReference::libc_memchr_function(isolate()));
962    Node* result_address =
963        CallCFunction3(MachineType::Pointer(), MachineType::Pointer(),
964                       MachineType::IntPtr(), MachineType::UintPtr(), memchr,
965                       string_addr, needle_byte, search_length);
966    GotoIf(WordEqual(result_address, int_zero), &return_minus_1);
967    Node* result_index =
968        IntPtrAdd(IntPtrSub(result_address, string_addr), start_position);
969    f_return(SmiTag(result_index));
970  }
971
972  Bind(&return_minus_1);
973  f_return(SmiConstant(-1));
974
975  Bind(&zero_length_needle);
976  {
977    Comment("0-length search_string");
978    f_return(SmiTag(IntPtrMin(string_length, start_position)));
979  }
980
981  Bind(&call_runtime_unchecked);
982  {
983    // Simplified version of the runtime call where the types of the arguments
984    // are already known due to type checks in this stub.
985    Comment("Call Runtime Unchecked");
986    Node* result = CallRuntime(Runtime::kStringIndexOfUnchecked, SmiConstant(0),
987                               receiver, search_string, position);
988    f_return(result);
989  }
990}
991
992// ES6 String.prototype.indexOf(searchString [, position])
993// #sec-string.prototype.indexof
994// Unchecked helper for builtins lowering.
995TF_BUILTIN(StringIndexOf, StringBuiltinsAssembler) {
996  Node* receiver = Parameter(0);
997  Node* search_string = Parameter(1);
998  Node* position = Parameter(2);
999
1000  Node* instance_type = LoadInstanceType(receiver);
1001  Node* search_string_instance_type = LoadInstanceType(search_string);
1002
1003  StringIndexOf(receiver, instance_type, search_string,
1004                search_string_instance_type, position,
1005                [this](Node* result) { this->Return(result); });
1006}
1007
1008// ES6 String.prototype.indexOf(searchString [, position])
1009// #sec-string.prototype.indexof
1010TF_BUILTIN(StringPrototypeIndexOf, StringBuiltinsAssembler) {
1011  Variable search_string(this, MachineRepresentation::kTagged),
1012      position(this, MachineRepresentation::kTagged);
1013  Label call_runtime(this), call_runtime_unchecked(this), argc_0(this),
1014      no_argc_0(this), argc_1(this), no_argc_1(this), argc_2(this),
1015      fast_path(this), return_minus_1(this);
1016
1017  Node* argc = Parameter(BuiltinDescriptor::kArgumentsCount);
1018  Node* context = Parameter(BuiltinDescriptor::kContext);
1019
1020  CodeStubArguments arguments(this, ChangeInt32ToIntPtr(argc));
1021  Node* receiver = arguments.GetReceiver();
1022  // From now on use word-size argc value.
1023  argc = arguments.GetLength();
1024
1025  GotoIf(IntPtrEqual(argc, IntPtrConstant(0)), &argc_0);
1026  GotoIf(IntPtrEqual(argc, IntPtrConstant(1)), &argc_1);
1027  Goto(&argc_2);
1028  Bind(&argc_0);
1029  {
1030    Comment("0 Argument case");
1031    Node* undefined = UndefinedConstant();
1032    search_string.Bind(undefined);
1033    position.Bind(undefined);
1034    Goto(&call_runtime);
1035  }
1036  Bind(&argc_1);
1037  {
1038    Comment("1 Argument case");
1039    search_string.Bind(arguments.AtIndex(0));
1040    position.Bind(SmiConstant(0));
1041    Goto(&fast_path);
1042  }
1043  Bind(&argc_2);
1044  {
1045    Comment("2 Argument case");
1046    search_string.Bind(arguments.AtIndex(0));
1047    position.Bind(arguments.AtIndex(1));
1048    GotoIfNot(TaggedIsSmi(position.value()), &call_runtime);
1049    Goto(&fast_path);
1050  }
1051
1052  Bind(&fast_path);
1053  {
1054    Comment("Fast Path");
1055    GotoIf(TaggedIsSmi(receiver), &call_runtime);
1056    Node* needle = search_string.value();
1057    GotoIf(TaggedIsSmi(needle), &call_runtime);
1058
1059    Node* instance_type = LoadInstanceType(receiver);
1060    GotoIfNot(IsStringInstanceType(instance_type), &call_runtime);
1061
1062    Node* needle_instance_type = LoadInstanceType(needle);
1063    GotoIfNot(IsStringInstanceType(needle_instance_type), &call_runtime);
1064
1065    StringIndexOf(
1066        receiver, instance_type, needle, needle_instance_type, position.value(),
1067        [&arguments](Node* result) { arguments.PopAndReturn(result); });
1068  }
1069
1070  Bind(&call_runtime);
1071  {
1072    Comment("Call Runtime");
1073    Node* result = CallRuntime(Runtime::kStringIndexOf, context, receiver,
1074                               search_string.value(), position.value());
1075    arguments.PopAndReturn(result);
1076  }
1077}
1078
1079// ES6 section 21.1.3.9
1080// String.prototype.lastIndexOf ( searchString [ , position ] )
1081BUILTIN(StringPrototypeLastIndexOf) {
1082  HandleScope handle_scope(isolate);
1083  return String::LastIndexOf(isolate, args.receiver(),
1084                             args.atOrUndefined(isolate, 1),
1085                             args.atOrUndefined(isolate, 2));
1086}
1087
1088// ES6 section 21.1.3.10 String.prototype.localeCompare ( that )
1089//
1090// This function is implementation specific.  For now, we do not
1091// do anything locale specific.
1092// If internationalization is enabled, then i18n.js will override this function
1093// and provide the proper functionality, so this is just a fallback.
1094BUILTIN(StringPrototypeLocaleCompare) {
1095  HandleScope handle_scope(isolate);
1096  DCHECK_EQ(2, args.length());
1097
1098  TO_THIS_STRING(str1, "String.prototype.localeCompare");
1099  Handle<String> str2;
1100  ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, str2,
1101                                     Object::ToString(isolate, args.at(1)));
1102
1103  if (str1.is_identical_to(str2)) return Smi::kZero;  // Equal.
1104  int str1_length = str1->length();
1105  int str2_length = str2->length();
1106
1107  // Decide trivial cases without flattening.
1108  if (str1_length == 0) {
1109    if (str2_length == 0) return Smi::kZero;  // Equal.
1110    return Smi::FromInt(-str2_length);
1111  } else {
1112    if (str2_length == 0) return Smi::FromInt(str1_length);
1113  }
1114
1115  int end = str1_length < str2_length ? str1_length : str2_length;
1116
1117  // No need to flatten if we are going to find the answer on the first
1118  // character. At this point we know there is at least one character
1119  // in each string, due to the trivial case handling above.
1120  int d = str1->Get(0) - str2->Get(0);
1121  if (d != 0) return Smi::FromInt(d);
1122
1123  str1 = String::Flatten(str1);
1124  str2 = String::Flatten(str2);
1125
1126  DisallowHeapAllocation no_gc;
1127  String::FlatContent flat1 = str1->GetFlatContent();
1128  String::FlatContent flat2 = str2->GetFlatContent();
1129
1130  for (int i = 0; i < end; i++) {
1131    if (flat1.Get(i) != flat2.Get(i)) {
1132      return Smi::FromInt(flat1.Get(i) - flat2.Get(i));
1133    }
1134  }
1135
1136  return Smi::FromInt(str1_length - str2_length);
1137}
1138
1139// ES6 section 21.1.3.12 String.prototype.normalize ( [form] )
1140//
1141// Simply checks the argument is valid and returns the string itself.
1142// If internationalization is enabled, then i18n.js will override this function
1143// and provide the proper functionality, so this is just a fallback.
1144BUILTIN(StringPrototypeNormalize) {
1145  HandleScope handle_scope(isolate);
1146  TO_THIS_STRING(string, "String.prototype.normalize");
1147
1148  Handle<Object> form_input = args.atOrUndefined(isolate, 1);
1149  if (form_input->IsUndefined(isolate)) return *string;
1150
1151  Handle<String> form;
1152  ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, form,
1153                                     Object::ToString(isolate, form_input));
1154
1155  if (!(String::Equals(form,
1156                       isolate->factory()->NewStringFromStaticChars("NFC")) ||
1157        String::Equals(form,
1158                       isolate->factory()->NewStringFromStaticChars("NFD")) ||
1159        String::Equals(form,
1160                       isolate->factory()->NewStringFromStaticChars("NFKC")) ||
1161        String::Equals(form,
1162                       isolate->factory()->NewStringFromStaticChars("NFKD")))) {
1163    Handle<String> valid_forms =
1164        isolate->factory()->NewStringFromStaticChars("NFC, NFD, NFKC, NFKD");
1165    THROW_NEW_ERROR_RETURN_FAILURE(
1166        isolate,
1167        NewRangeError(MessageTemplate::kNormalizationForm, valid_forms));
1168  }
1169
1170  return *string;
1171}
1172
1173compiler::Node* StringBuiltinsAssembler::IsNullOrUndefined(Node* const value) {
1174  return Word32Or(IsUndefined(value), IsNull(value));
1175}
1176
1177void StringBuiltinsAssembler::RequireObjectCoercible(Node* const context,
1178                                                     Node* const value,
1179                                                     const char* method_name) {
1180  Label out(this), throw_exception(this, Label::kDeferred);
1181  Branch(IsNullOrUndefined(value), &throw_exception, &out);
1182
1183  Bind(&throw_exception);
1184  TailCallRuntime(
1185      Runtime::kThrowCalledOnNullOrUndefined, context,
1186      HeapConstant(factory()->NewStringFromAsciiChecked(method_name, TENURED)));
1187
1188  Bind(&out);
1189}
1190
1191void StringBuiltinsAssembler::MaybeCallFunctionAtSymbol(
1192    Node* const context, Node* const object, Handle<Symbol> symbol,
1193    const NodeFunction0& regexp_call, const NodeFunction1& generic_call) {
1194  Label out(this);
1195
1196  // Smis definitely don't have an attached symbol.
1197  GotoIf(TaggedIsSmi(object), &out);
1198
1199  Node* const object_map = LoadMap(object);
1200
1201  // Skip the slow lookup for Strings.
1202  {
1203    Label next(this);
1204
1205    GotoIfNot(IsStringInstanceType(LoadMapInstanceType(object_map)), &next);
1206
1207    Node* const native_context = LoadNativeContext(context);
1208    Node* const initial_proto_initial_map = LoadContextElement(
1209        native_context, Context::STRING_FUNCTION_PROTOTYPE_MAP_INDEX);
1210
1211    Node* const string_fun =
1212        LoadContextElement(native_context, Context::STRING_FUNCTION_INDEX);
1213    Node* const initial_map =
1214        LoadObjectField(string_fun, JSFunction::kPrototypeOrInitialMapOffset);
1215    Node* const proto_map = LoadMap(LoadMapPrototype(initial_map));
1216
1217    Branch(WordEqual(proto_map, initial_proto_initial_map), &out, &next);
1218
1219    Bind(&next);
1220  }
1221
1222  // Take the fast path for RegExps.
1223  {
1224    Label stub_call(this), slow_lookup(this);
1225
1226    RegExpBuiltinsAssembler regexp_asm(state());
1227    regexp_asm.BranchIfFastRegExp(context, object, object_map, &stub_call,
1228                                  &slow_lookup);
1229
1230    Bind(&stub_call);
1231    Return(regexp_call());
1232
1233    Bind(&slow_lookup);
1234  }
1235
1236  GotoIf(IsNullOrUndefined(object), &out);
1237
1238  // Fall back to a slow lookup of {object[symbol]}.
1239
1240  Callable getproperty_callable = CodeFactory::GetProperty(isolate());
1241  Node* const key = HeapConstant(symbol);
1242  Node* const maybe_func = CallStub(getproperty_callable, context, object, key);
1243
1244  GotoIf(IsUndefined(maybe_func), &out);
1245
1246  // Attempt to call the function.
1247
1248  Node* const result = generic_call(maybe_func);
1249  Return(result);
1250
1251  Bind(&out);
1252}
1253
1254// ES6 section 21.1.3.16 String.prototype.replace ( search, replace )
1255TF_BUILTIN(StringPrototypeReplace, StringBuiltinsAssembler) {
1256  Label out(this);
1257
1258  Node* const receiver = Parameter(0);
1259  Node* const search = Parameter(1);
1260  Node* const replace = Parameter(2);
1261  Node* const context = Parameter(5);
1262
1263  Node* const smi_zero = SmiConstant(0);
1264
1265  RequireObjectCoercible(context, receiver, "String.prototype.replace");
1266
1267  // Redirect to replacer method if {search[@@replace]} is not undefined.
1268
1269  MaybeCallFunctionAtSymbol(
1270      context, search, isolate()->factory()->replace_symbol(),
1271      [=]() {
1272        Callable tostring_callable = CodeFactory::ToString(isolate());
1273        Node* const subject_string =
1274            CallStub(tostring_callable, context, receiver);
1275
1276        Callable replace_callable = CodeFactory::RegExpReplace(isolate());
1277        return CallStub(replace_callable, context, search, subject_string,
1278                        replace);
1279      },
1280      [=](Node* fn) {
1281        Callable call_callable = CodeFactory::Call(isolate());
1282        return CallJS(call_callable, context, fn, search, receiver, replace);
1283      });
1284
1285  // Convert {receiver} and {search} to strings.
1286
1287  Callable tostring_callable = CodeFactory::ToString(isolate());
1288  Callable indexof_callable = CodeFactory::StringIndexOf(isolate());
1289
1290  Node* const subject_string = CallStub(tostring_callable, context, receiver);
1291  Node* const search_string = CallStub(tostring_callable, context, search);
1292
1293  Node* const subject_length = LoadStringLength(subject_string);
1294  Node* const search_length = LoadStringLength(search_string);
1295
1296  // Fast-path single-char {search}, long {receiver}, and simple string
1297  // {replace}.
1298  {
1299    Label next(this);
1300
1301    GotoIfNot(SmiEqual(search_length, SmiConstant(1)), &next);
1302    GotoIfNot(SmiGreaterThan(subject_length, SmiConstant(0xFF)), &next);
1303    GotoIf(TaggedIsSmi(replace), &next);
1304    GotoIfNot(IsString(replace), &next);
1305
1306    Node* const dollar_string = HeapConstant(
1307        isolate()->factory()->LookupSingleCharacterStringFromCode('$'));
1308    Node* const dollar_ix =
1309        CallStub(indexof_callable, context, replace, dollar_string, smi_zero);
1310    GotoIfNot(SmiIsNegative(dollar_ix), &next);
1311
1312    // Searching by traversing a cons string tree and replace with cons of
1313    // slices works only when the replaced string is a single character, being
1314    // replaced by a simple string and only pays off for long strings.
1315    // TODO(jgruber): Reevaluate if this is still beneficial.
1316    // TODO(jgruber): TailCallRuntime when it correctly handles adapter frames.
1317    Return(CallRuntime(Runtime::kStringReplaceOneCharWithString, context,
1318                       subject_string, search_string, replace));
1319
1320    Bind(&next);
1321  }
1322
1323  // TODO(jgruber): Extend StringIndexOf to handle two-byte strings and
1324  // longer substrings - we can handle up to 8 chars (one-byte) / 4 chars
1325  // (2-byte).
1326
1327  Node* const match_start_index = CallStub(
1328      indexof_callable, context, subject_string, search_string, smi_zero);
1329  CSA_ASSERT(this, TaggedIsSmi(match_start_index));
1330
1331  // Early exit if no match found.
1332  {
1333    Label next(this), return_subject(this);
1334
1335    GotoIfNot(SmiIsNegative(match_start_index), &next);
1336
1337    // The spec requires to perform ToString(replace) if the {replace} is not
1338    // callable even if we are going to exit here.
1339    // Since ToString() being applied to Smi does not have side effects for
1340    // numbers we can skip it.
1341    GotoIf(TaggedIsSmi(replace), &return_subject);
1342    GotoIf(IsCallableMap(LoadMap(replace)), &return_subject);
1343
1344    // TODO(jgruber): Could introduce ToStringSideeffectsStub which only
1345    // performs observable parts of ToString.
1346    CallStub(tostring_callable, context, replace);
1347    Goto(&return_subject);
1348
1349    Bind(&return_subject);
1350    Return(subject_string);
1351
1352    Bind(&next);
1353  }
1354
1355  Node* const match_end_index = SmiAdd(match_start_index, search_length);
1356
1357  Callable substring_callable = CodeFactory::SubString(isolate());
1358  Callable stringadd_callable =
1359      CodeFactory::StringAdd(isolate(), STRING_ADD_CHECK_NONE, NOT_TENURED);
1360
1361  Variable var_result(this, MachineRepresentation::kTagged,
1362                      EmptyStringConstant());
1363
1364  // Compute the prefix.
1365  {
1366    Label next(this);
1367
1368    GotoIf(SmiEqual(match_start_index, smi_zero), &next);
1369    Node* const prefix = CallStub(substring_callable, context, subject_string,
1370                                  smi_zero, match_start_index);
1371    var_result.Bind(prefix);
1372
1373    Goto(&next);
1374    Bind(&next);
1375  }
1376
1377  // Compute the string to replace with.
1378
1379  Label if_iscallablereplace(this), if_notcallablereplace(this);
1380  GotoIf(TaggedIsSmi(replace), &if_notcallablereplace);
1381  Branch(IsCallableMap(LoadMap(replace)), &if_iscallablereplace,
1382         &if_notcallablereplace);
1383
1384  Bind(&if_iscallablereplace);
1385  {
1386    Callable call_callable = CodeFactory::Call(isolate());
1387    Node* const replacement =
1388        CallJS(call_callable, context, replace, UndefinedConstant(),
1389               search_string, match_start_index, subject_string);
1390    Node* const replacement_string =
1391        CallStub(tostring_callable, context, replacement);
1392    var_result.Bind(CallStub(stringadd_callable, context, var_result.value(),
1393                             replacement_string));
1394    Goto(&out);
1395  }
1396
1397  Bind(&if_notcallablereplace);
1398  {
1399    Node* const replace_string = CallStub(tostring_callable, context, replace);
1400
1401    // TODO(jgruber): Simplified GetSubstitution implementation in CSA.
1402    Node* const matched = CallStub(substring_callable, context, subject_string,
1403                                   match_start_index, match_end_index);
1404    Node* const replacement_string =
1405        CallRuntime(Runtime::kGetSubstitution, context, matched, subject_string,
1406                    match_start_index, replace_string);
1407    var_result.Bind(CallStub(stringadd_callable, context, var_result.value(),
1408                             replacement_string));
1409    Goto(&out);
1410  }
1411
1412  Bind(&out);
1413  {
1414    Node* const suffix = CallStub(substring_callable, context, subject_string,
1415                                  match_end_index, subject_length);
1416    Node* const result =
1417        CallStub(stringadd_callable, context, var_result.value(), suffix);
1418    Return(result);
1419  }
1420}
1421
1422// ES6 section 21.1.3.19 String.prototype.split ( separator, limit )
1423TF_BUILTIN(StringPrototypeSplit, StringBuiltinsAssembler) {
1424  Label out(this);
1425
1426  Node* const receiver = Parameter(0);
1427  Node* const separator = Parameter(1);
1428  Node* const limit = Parameter(2);
1429  Node* const context = Parameter(5);
1430
1431  Node* const smi_zero = SmiConstant(0);
1432
1433  RequireObjectCoercible(context, receiver, "String.prototype.split");
1434
1435  // Redirect to splitter method if {separator[@@split]} is not undefined.
1436
1437  MaybeCallFunctionAtSymbol(
1438      context, separator, isolate()->factory()->split_symbol(),
1439      [=]() {
1440        Callable tostring_callable = CodeFactory::ToString(isolate());
1441        Node* const subject_string =
1442            CallStub(tostring_callable, context, receiver);
1443
1444        Callable split_callable = CodeFactory::RegExpSplit(isolate());
1445        return CallStub(split_callable, context, separator, subject_string,
1446                        limit);
1447      },
1448      [=](Node* fn) {
1449        Callable call_callable = CodeFactory::Call(isolate());
1450        return CallJS(call_callable, context, fn, separator, receiver, limit);
1451      });
1452
1453  // String and integer conversions.
1454  // TODO(jgruber): The old implementation used Uint32Max instead of SmiMax -
1455  // but AFAIK there should not be a difference since arrays are capped at Smi
1456  // lengths.
1457
1458  Callable tostring_callable = CodeFactory::ToString(isolate());
1459  Node* const subject_string = CallStub(tostring_callable, context, receiver);
1460  Node* const limit_number =
1461      Select(IsUndefined(limit), [=]() { return SmiConstant(Smi::kMaxValue); },
1462             [=]() { return ToUint32(context, limit); },
1463             MachineRepresentation::kTagged);
1464  Node* const separator_string =
1465      CallStub(tostring_callable, context, separator);
1466
1467  // Shortcut for {limit} == 0.
1468  {
1469    Label next(this);
1470    GotoIfNot(SmiEqual(limit_number, smi_zero), &next);
1471
1472    const ElementsKind kind = FAST_ELEMENTS;
1473    Node* const native_context = LoadNativeContext(context);
1474    Node* const array_map = LoadJSArrayElementsMap(kind, native_context);
1475
1476    Node* const length = smi_zero;
1477    Node* const capacity = IntPtrConstant(0);
1478    Node* const result = AllocateJSArray(kind, array_map, capacity, length);
1479
1480    Return(result);
1481
1482    Bind(&next);
1483  }
1484
1485  // ECMA-262 says that if {separator} is undefined, the result should
1486  // be an array of size 1 containing the entire string.
1487  {
1488    Label next(this);
1489    GotoIfNot(IsUndefined(separator), &next);
1490
1491    const ElementsKind kind = FAST_ELEMENTS;
1492    Node* const native_context = LoadNativeContext(context);
1493    Node* const array_map = LoadJSArrayElementsMap(kind, native_context);
1494
1495    Node* const length = SmiConstant(1);
1496    Node* const capacity = IntPtrConstant(1);
1497    Node* const result = AllocateJSArray(kind, array_map, capacity, length);
1498
1499    Node* const fixed_array = LoadElements(result);
1500    StoreFixedArrayElement(fixed_array, 0, subject_string);
1501
1502    Return(result);
1503
1504    Bind(&next);
1505  }
1506
1507  // If the separator string is empty then return the elements in the subject.
1508  {
1509    Label next(this);
1510    GotoIfNot(SmiEqual(LoadStringLength(separator_string), smi_zero), &next);
1511
1512    Node* const result = CallRuntime(Runtime::kStringToArray, context,
1513                                     subject_string, limit_number);
1514    Return(result);
1515
1516    Bind(&next);
1517  }
1518
1519  Node* const result =
1520      CallRuntime(Runtime::kStringSplit, context, subject_string,
1521                  separator_string, limit_number);
1522  Return(result);
1523}
1524
1525// ES6 section B.2.3.1 String.prototype.substr ( start, length )
1526TF_BUILTIN(StringPrototypeSubstr, CodeStubAssembler) {
1527  Label out(this), handle_length(this);
1528
1529  Variable var_start(this, MachineRepresentation::kTagged);
1530  Variable var_length(this, MachineRepresentation::kTagged);
1531
1532  Node* const receiver = Parameter(0);
1533  Node* const start = Parameter(1);
1534  Node* const length = Parameter(2);
1535  Node* const context = Parameter(5);
1536
1537  Node* const zero = SmiConstant(Smi::kZero);
1538
1539  // Check that {receiver} is coercible to Object and convert it to a String.
1540  Node* const string =
1541      ToThisString(context, receiver, "String.prototype.substr");
1542
1543  Node* const string_length = LoadStringLength(string);
1544
1545  // Conversions and bounds-checks for {start}.
1546  {
1547    Node* const start_int =
1548        ToInteger(context, start, CodeStubAssembler::kTruncateMinusZero);
1549
1550    Label if_issmi(this), if_isheapnumber(this, Label::kDeferred);
1551    Branch(TaggedIsSmi(start_int), &if_issmi, &if_isheapnumber);
1552
1553    Bind(&if_issmi);
1554    {
1555      Node* const length_plus_start = SmiAdd(string_length, start_int);
1556      var_start.Bind(Select(SmiLessThan(start_int, zero),
1557                            [&] { return SmiMax(length_plus_start, zero); },
1558                            [&] { return start_int; },
1559                            MachineRepresentation::kTagged));
1560      Goto(&handle_length);
1561    }
1562
1563    Bind(&if_isheapnumber);
1564    {
1565      // If {start} is a heap number, it is definitely out of bounds. If it is
1566      // negative, {start} = max({string_length} + {start}),0) = 0'. If it is
1567      // positive, set {start} to {string_length} which ultimately results in
1568      // returning an empty string.
1569      Node* const float_zero = Float64Constant(0.);
1570      Node* const start_float = LoadHeapNumberValue(start_int);
1571      var_start.Bind(SelectTaggedConstant(
1572          Float64LessThan(start_float, float_zero), zero, string_length));
1573      Goto(&handle_length);
1574    }
1575  }
1576
1577  // Conversions and bounds-checks for {length}.
1578  Bind(&handle_length);
1579  {
1580    Label if_issmi(this), if_isheapnumber(this, Label::kDeferred);
1581
1582    // Default to {string_length} if {length} is undefined.
1583    {
1584      Label if_isundefined(this, Label::kDeferred), if_isnotundefined(this);
1585      Branch(WordEqual(length, UndefinedConstant()), &if_isundefined,
1586             &if_isnotundefined);
1587
1588      Bind(&if_isundefined);
1589      var_length.Bind(string_length);
1590      Goto(&if_issmi);
1591
1592      Bind(&if_isnotundefined);
1593      var_length.Bind(
1594          ToInteger(context, length, CodeStubAssembler::kTruncateMinusZero));
1595    }
1596
1597    Branch(TaggedIsSmi(var_length.value()), &if_issmi, &if_isheapnumber);
1598
1599    // Set {length} to min(max({length}, 0), {string_length} - {start}
1600    Bind(&if_issmi);
1601    {
1602      Node* const positive_length = SmiMax(var_length.value(), zero);
1603
1604      Node* const minimal_length = SmiSub(string_length, var_start.value());
1605      var_length.Bind(SmiMin(positive_length, minimal_length));
1606
1607      GotoIfNot(SmiLessThanOrEqual(var_length.value(), zero), &out);
1608      Return(EmptyStringConstant());
1609    }
1610
1611    Bind(&if_isheapnumber);
1612    {
1613      // If {length} is a heap number, it is definitely out of bounds. There are
1614      // two cases according to the spec: if it is negative, "" is returned; if
1615      // it is positive, then length is set to {string_length} - {start}.
1616
1617      CSA_ASSERT(this, IsHeapNumberMap(LoadMap(var_length.value())));
1618
1619      Label if_isnegative(this), if_ispositive(this);
1620      Node* const float_zero = Float64Constant(0.);
1621      Node* const length_float = LoadHeapNumberValue(var_length.value());
1622      Branch(Float64LessThan(length_float, float_zero), &if_isnegative,
1623             &if_ispositive);
1624
1625      Bind(&if_isnegative);
1626      Return(EmptyStringConstant());
1627
1628      Bind(&if_ispositive);
1629      {
1630        var_length.Bind(SmiSub(string_length, var_start.value()));
1631        GotoIfNot(SmiLessThanOrEqual(var_length.value(), zero), &out);
1632        Return(EmptyStringConstant());
1633      }
1634    }
1635  }
1636
1637  Bind(&out);
1638  {
1639    Node* const end = SmiAdd(var_start.value(), var_length.value());
1640    Node* const result = SubString(context, string, var_start.value(), end);
1641    Return(result);
1642  }
1643}
1644
1645compiler::Node* StringBuiltinsAssembler::ToSmiBetweenZeroAnd(Node* context,
1646                                                             Node* value,
1647                                                             Node* limit) {
1648  Label out(this);
1649  Variable var_result(this, MachineRepresentation::kTagged);
1650
1651  Node* const value_int =
1652      this->ToInteger(context, value, CodeStubAssembler::kTruncateMinusZero);
1653
1654  Label if_issmi(this), if_isnotsmi(this, Label::kDeferred);
1655  Branch(TaggedIsSmi(value_int), &if_issmi, &if_isnotsmi);
1656
1657  Bind(&if_issmi);
1658  {
1659    Label if_isinbounds(this), if_isoutofbounds(this, Label::kDeferred);
1660    Branch(SmiAbove(value_int, limit), &if_isoutofbounds, &if_isinbounds);
1661
1662    Bind(&if_isinbounds);
1663    {
1664      var_result.Bind(value_int);
1665      Goto(&out);
1666    }
1667
1668    Bind(&if_isoutofbounds);
1669    {
1670      Node* const zero = SmiConstant(Smi::kZero);
1671      var_result.Bind(
1672          SelectTaggedConstant(SmiLessThan(value_int, zero), zero, limit));
1673      Goto(&out);
1674    }
1675  }
1676
1677  Bind(&if_isnotsmi);
1678  {
1679    // {value} is a heap number - in this case, it is definitely out of bounds.
1680    CSA_ASSERT(this, IsHeapNumberMap(LoadMap(value_int)));
1681
1682    Node* const float_zero = Float64Constant(0.);
1683    Node* const smi_zero = SmiConstant(Smi::kZero);
1684    Node* const value_float = LoadHeapNumberValue(value_int);
1685    var_result.Bind(SelectTaggedConstant(
1686        Float64LessThan(value_float, float_zero), smi_zero, limit));
1687    Goto(&out);
1688  }
1689
1690  Bind(&out);
1691  return var_result.value();
1692}
1693
1694// ES6 section 21.1.3.19 String.prototype.substring ( start, end )
1695TF_BUILTIN(StringPrototypeSubstring, StringBuiltinsAssembler) {
1696  Label out(this);
1697
1698  Variable var_start(this, MachineRepresentation::kTagged);
1699  Variable var_end(this, MachineRepresentation::kTagged);
1700
1701  Node* const receiver = Parameter(0);
1702  Node* const start = Parameter(1);
1703  Node* const end = Parameter(2);
1704  Node* const context = Parameter(5);
1705
1706  // Check that {receiver} is coercible to Object and convert it to a String.
1707  Node* const string =
1708      ToThisString(context, receiver, "String.prototype.substring");
1709
1710  Node* const length = LoadStringLength(string);
1711
1712  // Conversion and bounds-checks for {start}.
1713  var_start.Bind(ToSmiBetweenZeroAnd(context, start, length));
1714
1715  // Conversion and bounds-checks for {end}.
1716  {
1717    var_end.Bind(length);
1718    GotoIf(WordEqual(end, UndefinedConstant()), &out);
1719
1720    var_end.Bind(ToSmiBetweenZeroAnd(context, end, length));
1721
1722    Label if_endislessthanstart(this);
1723    Branch(SmiLessThan(var_end.value(), var_start.value()),
1724           &if_endislessthanstart, &out);
1725
1726    Bind(&if_endislessthanstart);
1727    {
1728      Node* const tmp = var_end.value();
1729      var_end.Bind(var_start.value());
1730      var_start.Bind(tmp);
1731      Goto(&out);
1732    }
1733  }
1734
1735  Bind(&out);
1736  {
1737    Node* result =
1738        SubString(context, string, var_start.value(), var_end.value());
1739    Return(result);
1740  }
1741}
1742
1743BUILTIN(StringPrototypeStartsWith) {
1744  HandleScope handle_scope(isolate);
1745  TO_THIS_STRING(str, "String.prototype.startsWith");
1746
1747  // Check if the search string is a regExp and fail if it is.
1748  Handle<Object> search = args.atOrUndefined(isolate, 1);
1749  Maybe<bool> is_reg_exp = RegExpUtils::IsRegExp(isolate, search);
1750  if (is_reg_exp.IsNothing()) {
1751    DCHECK(isolate->has_pending_exception());
1752    return isolate->heap()->exception();
1753  }
1754  if (is_reg_exp.FromJust()) {
1755    THROW_NEW_ERROR_RETURN_FAILURE(
1756        isolate, NewTypeError(MessageTemplate::kFirstArgumentNotRegExp,
1757                              isolate->factory()->NewStringFromStaticChars(
1758                                  "String.prototype.startsWith")));
1759  }
1760  Handle<String> search_string;
1761  ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, search_string,
1762                                     Object::ToString(isolate, search));
1763
1764  Handle<Object> position = args.atOrUndefined(isolate, 2);
1765  int start;
1766
1767  if (position->IsUndefined(isolate)) {
1768    start = 0;
1769  } else {
1770    ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, position,
1771                                       Object::ToInteger(isolate, position));
1772    start = str->ToValidIndex(*position);
1773  }
1774
1775  if (start + search_string->length() > str->length()) {
1776    return isolate->heap()->false_value();
1777  }
1778
1779  FlatStringReader str_reader(isolate, String::Flatten(str));
1780  FlatStringReader search_reader(isolate, String::Flatten(search_string));
1781
1782  for (int i = 0; i < search_string->length(); i++) {
1783    if (str_reader.Get(start + i) != search_reader.Get(i)) {
1784      return isolate->heap()->false_value();
1785    }
1786  }
1787  return isolate->heap()->true_value();
1788}
1789
1790// ES6 section 21.1.3.25 String.prototype.toString ()
1791TF_BUILTIN(StringPrototypeToString, CodeStubAssembler) {
1792  Node* receiver = Parameter(0);
1793  Node* context = Parameter(3);
1794
1795  Node* result = ToThisValue(context, receiver, PrimitiveType::kString,
1796                             "String.prototype.toString");
1797  Return(result);
1798}
1799
1800// ES6 section 21.1.3.27 String.prototype.trim ()
1801BUILTIN(StringPrototypeTrim) {
1802  HandleScope scope(isolate);
1803  TO_THIS_STRING(string, "String.prototype.trim");
1804  return *String::Trim(string, String::kTrim);
1805}
1806
1807// Non-standard WebKit extension
1808BUILTIN(StringPrototypeTrimLeft) {
1809  HandleScope scope(isolate);
1810  TO_THIS_STRING(string, "String.prototype.trimLeft");
1811  return *String::Trim(string, String::kTrimLeft);
1812}
1813
1814// Non-standard WebKit extension
1815BUILTIN(StringPrototypeTrimRight) {
1816  HandleScope scope(isolate);
1817  TO_THIS_STRING(string, "String.prototype.trimRight");
1818  return *String::Trim(string, String::kTrimRight);
1819}
1820
1821// ES6 section 21.1.3.28 String.prototype.valueOf ( )
1822TF_BUILTIN(StringPrototypeValueOf, CodeStubAssembler) {
1823  Node* receiver = Parameter(0);
1824  Node* context = Parameter(3);
1825
1826  Node* result = ToThisValue(context, receiver, PrimitiveType::kString,
1827                             "String.prototype.valueOf");
1828  Return(result);
1829}
1830
1831TF_BUILTIN(StringPrototypeIterator, CodeStubAssembler) {
1832  Node* receiver = Parameter(0);
1833  Node* context = Parameter(3);
1834
1835  Node* string =
1836      ToThisString(context, receiver, "String.prototype[Symbol.iterator]");
1837
1838  Node* native_context = LoadNativeContext(context);
1839  Node* map =
1840      LoadContextElement(native_context, Context::STRING_ITERATOR_MAP_INDEX);
1841  Node* iterator = Allocate(JSStringIterator::kSize);
1842  StoreMapNoWriteBarrier(iterator, map);
1843  StoreObjectFieldRoot(iterator, JSValue::kPropertiesOffset,
1844                       Heap::kEmptyFixedArrayRootIndex);
1845  StoreObjectFieldRoot(iterator, JSObject::kElementsOffset,
1846                       Heap::kEmptyFixedArrayRootIndex);
1847  StoreObjectFieldNoWriteBarrier(iterator, JSStringIterator::kStringOffset,
1848                                 string);
1849  Node* index = SmiConstant(Smi::kZero);
1850  StoreObjectFieldNoWriteBarrier(iterator, JSStringIterator::kNextIndexOffset,
1851                                 index);
1852  Return(iterator);
1853}
1854
1855// Return the |word32| codepoint at {index}. Supports SeqStrings and
1856// ExternalStrings.
1857compiler::Node* StringBuiltinsAssembler::LoadSurrogatePairAt(
1858    compiler::Node* string, compiler::Node* length, compiler::Node* index,
1859    UnicodeEncoding encoding) {
1860  Label handle_surrogate_pair(this), return_result(this);
1861  Variable var_result(this, MachineRepresentation::kWord32);
1862  Variable var_trail(this, MachineRepresentation::kWord32);
1863  var_result.Bind(StringCharCodeAt(string, index));
1864  var_trail.Bind(Int32Constant(0));
1865
1866  GotoIf(Word32NotEqual(Word32And(var_result.value(), Int32Constant(0xFC00)),
1867                        Int32Constant(0xD800)),
1868         &return_result);
1869  Node* next_index = SmiAdd(index, SmiConstant(Smi::FromInt(1)));
1870
1871  GotoIfNot(SmiLessThan(next_index, length), &return_result);
1872  var_trail.Bind(StringCharCodeAt(string, next_index));
1873  Branch(Word32Equal(Word32And(var_trail.value(), Int32Constant(0xFC00)),
1874                     Int32Constant(0xDC00)),
1875         &handle_surrogate_pair, &return_result);
1876
1877  Bind(&handle_surrogate_pair);
1878  {
1879    Node* lead = var_result.value();
1880    Node* trail = var_trail.value();
1881
1882    // Check that this path is only taken if a surrogate pair is found
1883    CSA_SLOW_ASSERT(this,
1884                    Uint32GreaterThanOrEqual(lead, Int32Constant(0xD800)));
1885    CSA_SLOW_ASSERT(this, Uint32LessThan(lead, Int32Constant(0xDC00)));
1886    CSA_SLOW_ASSERT(this,
1887                    Uint32GreaterThanOrEqual(trail, Int32Constant(0xDC00)));
1888    CSA_SLOW_ASSERT(this, Uint32LessThan(trail, Int32Constant(0xE000)));
1889
1890    switch (encoding) {
1891      case UnicodeEncoding::UTF16:
1892        var_result.Bind(Word32Or(
1893// Need to swap the order for big-endian platforms
1894#if V8_TARGET_BIG_ENDIAN
1895            Word32Shl(lead, Int32Constant(16)), trail));
1896#else
1897            Word32Shl(trail, Int32Constant(16)), lead));
1898#endif
1899        break;
1900
1901      case UnicodeEncoding::UTF32: {
1902        // Convert UTF16 surrogate pair into |word32| code point, encoded as
1903        // UTF32.
1904        Node* surrogate_offset =
1905            Int32Constant(0x10000 - (0xD800 << 10) - 0xDC00);
1906
1907        // (lead << 10) + trail + SURROGATE_OFFSET
1908        var_result.Bind(Int32Add(WordShl(lead, Int32Constant(10)),
1909                                 Int32Add(trail, surrogate_offset)));
1910        break;
1911      }
1912    }
1913    Goto(&return_result);
1914  }
1915
1916  Bind(&return_result);
1917  return var_result.value();
1918}
1919
1920TF_BUILTIN(StringIteratorPrototypeNext, StringBuiltinsAssembler) {
1921  Variable var_value(this, MachineRepresentation::kTagged);
1922  Variable var_done(this, MachineRepresentation::kTagged);
1923
1924  var_value.Bind(UndefinedConstant());
1925  var_done.Bind(BooleanConstant(true));
1926
1927  Label throw_bad_receiver(this), next_codepoint(this), return_result(this);
1928
1929  Node* iterator = Parameter(0);
1930  Node* context = Parameter(3);
1931
1932  GotoIf(TaggedIsSmi(iterator), &throw_bad_receiver);
1933  GotoIfNot(Word32Equal(LoadInstanceType(iterator),
1934                        Int32Constant(JS_STRING_ITERATOR_TYPE)),
1935            &throw_bad_receiver);
1936
1937  Node* string = LoadObjectField(iterator, JSStringIterator::kStringOffset);
1938  Node* position =
1939      LoadObjectField(iterator, JSStringIterator::kNextIndexOffset);
1940  Node* length = LoadObjectField(string, String::kLengthOffset);
1941
1942  Branch(SmiLessThan(position, length), &next_codepoint, &return_result);
1943
1944  Bind(&next_codepoint);
1945  {
1946    UnicodeEncoding encoding = UnicodeEncoding::UTF16;
1947    Node* ch = LoadSurrogatePairAt(string, length, position, encoding);
1948    Node* value = StringFromCodePoint(ch, encoding);
1949    var_value.Bind(value);
1950    Node* length = LoadObjectField(value, String::kLengthOffset);
1951    StoreObjectFieldNoWriteBarrier(iterator, JSStringIterator::kNextIndexOffset,
1952                                   SmiAdd(position, length));
1953    var_done.Bind(BooleanConstant(false));
1954    Goto(&return_result);
1955  }
1956
1957  Bind(&return_result);
1958  {
1959    Node* native_context = LoadNativeContext(context);
1960    Node* map =
1961        LoadContextElement(native_context, Context::ITERATOR_RESULT_MAP_INDEX);
1962    Node* result = Allocate(JSIteratorResult::kSize);
1963    StoreMapNoWriteBarrier(result, map);
1964    StoreObjectFieldRoot(result, JSIteratorResult::kPropertiesOffset,
1965                         Heap::kEmptyFixedArrayRootIndex);
1966    StoreObjectFieldRoot(result, JSIteratorResult::kElementsOffset,
1967                         Heap::kEmptyFixedArrayRootIndex);
1968    StoreObjectFieldNoWriteBarrier(result, JSIteratorResult::kValueOffset,
1969                                   var_value.value());
1970    StoreObjectFieldNoWriteBarrier(result, JSIteratorResult::kDoneOffset,
1971                                   var_done.value());
1972    Return(result);
1973  }
1974
1975  Bind(&throw_bad_receiver);
1976  {
1977    // The {receiver} is not a valid JSGeneratorObject.
1978    CallRuntime(Runtime::kThrowIncompatibleMethodReceiver, context,
1979                HeapConstant(factory()->NewStringFromAsciiChecked(
1980                    "String Iterator.prototype.next", TENURED)),
1981                iterator);
1982    Unreachable();
1983  }
1984}
1985
1986namespace {
1987
1988inline bool ToUpperOverflows(uc32 character) {
1989  // y with umlauts and the micro sign are the only characters that stop
1990  // fitting into one-byte when converting to uppercase.
1991  static const uc32 yuml_code = 0xff;
1992  static const uc32 micro_code = 0xb5;
1993  return (character == yuml_code || character == micro_code);
1994}
1995
1996template <class Converter>
1997MUST_USE_RESULT static Object* ConvertCaseHelper(
1998    Isolate* isolate, String* string, SeqString* result, int result_length,
1999    unibrow::Mapping<Converter, 128>* mapping) {
2000  DisallowHeapAllocation no_gc;
2001  // We try this twice, once with the assumption that the result is no longer
2002  // than the input and, if that assumption breaks, again with the exact
2003  // length.  This may not be pretty, but it is nicer than what was here before
2004  // and I hereby claim my vaffel-is.
2005  //
2006  // NOTE: This assumes that the upper/lower case of an ASCII
2007  // character is also ASCII.  This is currently the case, but it
2008  // might break in the future if we implement more context and locale
2009  // dependent upper/lower conversions.
2010  bool has_changed_character = false;
2011
2012  // Convert all characters to upper case, assuming that they will fit
2013  // in the buffer
2014  StringCharacterStream stream(string);
2015  unibrow::uchar chars[Converter::kMaxWidth];
2016  // We can assume that the string is not empty
2017  uc32 current = stream.GetNext();
2018  bool ignore_overflow = Converter::kIsToLower || result->IsSeqTwoByteString();
2019  for (int i = 0; i < result_length;) {
2020    bool has_next = stream.HasMore();
2021    uc32 next = has_next ? stream.GetNext() : 0;
2022    int char_length = mapping->get(current, next, chars);
2023    if (char_length == 0) {
2024      // The case conversion of this character is the character itself.
2025      result->Set(i, current);
2026      i++;
2027    } else if (char_length == 1 &&
2028               (ignore_overflow || !ToUpperOverflows(current))) {
2029      // Common case: converting the letter resulted in one character.
2030      DCHECK(static_cast<uc32>(chars[0]) != current);
2031      result->Set(i, chars[0]);
2032      has_changed_character = true;
2033      i++;
2034    } else if (result_length == string->length()) {
2035      bool overflows = ToUpperOverflows(current);
2036      // We've assumed that the result would be as long as the
2037      // input but here is a character that converts to several
2038      // characters.  No matter, we calculate the exact length
2039      // of the result and try the whole thing again.
2040      //
2041      // Note that this leaves room for optimization.  We could just
2042      // memcpy what we already have to the result string.  Also,
2043      // the result string is the last object allocated we could
2044      // "realloc" it and probably, in the vast majority of cases,
2045      // extend the existing string to be able to hold the full
2046      // result.
2047      int next_length = 0;
2048      if (has_next) {
2049        next_length = mapping->get(next, 0, chars);
2050        if (next_length == 0) next_length = 1;
2051      }
2052      int current_length = i + char_length + next_length;
2053      while (stream.HasMore()) {
2054        current = stream.GetNext();
2055        overflows |= ToUpperOverflows(current);
2056        // NOTE: we use 0 as the next character here because, while
2057        // the next character may affect what a character converts to,
2058        // it does not in any case affect the length of what it convert
2059        // to.
2060        int char_length = mapping->get(current, 0, chars);
2061        if (char_length == 0) char_length = 1;
2062        current_length += char_length;
2063        if (current_length > String::kMaxLength) {
2064          AllowHeapAllocation allocate_error_and_return;
2065          THROW_NEW_ERROR_RETURN_FAILURE(isolate,
2066                                         NewInvalidStringLengthError());
2067        }
2068      }
2069      // Try again with the real length.  Return signed if we need
2070      // to allocate a two-byte string for to uppercase.
2071      return (overflows && !ignore_overflow) ? Smi::FromInt(-current_length)
2072                                             : Smi::FromInt(current_length);
2073    } else {
2074      for (int j = 0; j < char_length; j++) {
2075        result->Set(i, chars[j]);
2076        i++;
2077      }
2078      has_changed_character = true;
2079    }
2080    current = next;
2081  }
2082  if (has_changed_character) {
2083    return result;
2084  } else {
2085    // If we didn't actually change anything in doing the conversion
2086    // we simple return the result and let the converted string
2087    // become garbage; there is no reason to keep two identical strings
2088    // alive.
2089    return string;
2090  }
2091}
2092
2093template <class Converter>
2094MUST_USE_RESULT static Object* ConvertCase(
2095    Handle<String> s, Isolate* isolate,
2096    unibrow::Mapping<Converter, 128>* mapping) {
2097  s = String::Flatten(s);
2098  int length = s->length();
2099  // Assume that the string is not empty; we need this assumption later
2100  if (length == 0) return *s;
2101
2102  // Simpler handling of ASCII strings.
2103  //
2104  // NOTE: This assumes that the upper/lower case of an ASCII
2105  // character is also ASCII.  This is currently the case, but it
2106  // might break in the future if we implement more context and locale
2107  // dependent upper/lower conversions.
2108  if (s->IsOneByteRepresentationUnderneath()) {
2109    // Same length as input.
2110    Handle<SeqOneByteString> result =
2111        isolate->factory()->NewRawOneByteString(length).ToHandleChecked();
2112    DisallowHeapAllocation no_gc;
2113    String::FlatContent flat_content = s->GetFlatContent();
2114    DCHECK(flat_content.IsFlat());
2115    bool has_changed_character = false;
2116    int index_to_first_unprocessed = FastAsciiConvert<Converter::kIsToLower>(
2117        reinterpret_cast<char*>(result->GetChars()),
2118        reinterpret_cast<const char*>(flat_content.ToOneByteVector().start()),
2119        length, &has_changed_character);
2120    // If not ASCII, we discard the result and take the 2 byte path.
2121    if (index_to_first_unprocessed == length)
2122      return has_changed_character ? *result : *s;
2123  }
2124
2125  Handle<SeqString> result;  // Same length as input.
2126  if (s->IsOneByteRepresentation()) {
2127    result = isolate->factory()->NewRawOneByteString(length).ToHandleChecked();
2128  } else {
2129    result = isolate->factory()->NewRawTwoByteString(length).ToHandleChecked();
2130  }
2131
2132  Object* answer = ConvertCaseHelper(isolate, *s, *result, length, mapping);
2133  if (answer->IsException(isolate) || answer->IsString()) return answer;
2134
2135  DCHECK(answer->IsSmi());
2136  length = Smi::cast(answer)->value();
2137  if (s->IsOneByteRepresentation() && length > 0) {
2138    ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
2139        isolate, result, isolate->factory()->NewRawOneByteString(length));
2140  } else {
2141    if (length < 0) length = -length;
2142    ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
2143        isolate, result, isolate->factory()->NewRawTwoByteString(length));
2144  }
2145  return ConvertCaseHelper(isolate, *s, *result, length, mapping);
2146}
2147
2148}  // namespace
2149
2150BUILTIN(StringPrototypeToLocaleLowerCase) {
2151  HandleScope scope(isolate);
2152  TO_THIS_STRING(string, "String.prototype.toLocaleLowerCase");
2153  return ConvertCase(string, isolate,
2154                     isolate->runtime_state()->to_lower_mapping());
2155}
2156
2157BUILTIN(StringPrototypeToLocaleUpperCase) {
2158  HandleScope scope(isolate);
2159  TO_THIS_STRING(string, "String.prototype.toLocaleUpperCase");
2160  return ConvertCase(string, isolate,
2161                     isolate->runtime_state()->to_upper_mapping());
2162}
2163
2164BUILTIN(StringPrototypeToLowerCase) {
2165  HandleScope scope(isolate);
2166  TO_THIS_STRING(string, "String.prototype.toLowerCase");
2167  return ConvertCase(string, isolate,
2168                     isolate->runtime_state()->to_lower_mapping());
2169}
2170
2171BUILTIN(StringPrototypeToUpperCase) {
2172  HandleScope scope(isolate);
2173  TO_THIS_STRING(string, "String.prototype.toUpperCase");
2174  return ConvertCase(string, isolate,
2175                     isolate->runtime_state()->to_upper_mapping());
2176}
2177
2178}  // namespace internal
2179}  // namespace v8
2180