Home | History | Annotate | Download | only in builtins
      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 
     18 namespace v8 {
     19 namespace internal {
     20 
     21 typedef CodeStubAssembler::ResultMode ResultMode;
     22 typedef CodeStubAssembler::RelationalComparisonMode RelationalComparisonMode;
     23 
     24 class 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 
    147 void 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 
    278 void 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 
    442 TF_BUILTIN(StringEqual, StringBuiltinsAssembler) {
    443   GenerateStringEqual(ResultMode::kDontNegateResult);
    444 }
    445 
    446 TF_BUILTIN(StringNotEqual, StringBuiltinsAssembler) {
    447   GenerateStringEqual(ResultMode::kNegateResult);
    448 }
    449 
    450 TF_BUILTIN(StringLessThan, StringBuiltinsAssembler) {
    451   GenerateStringRelationalComparison(RelationalComparisonMode::kLessThan);
    452 }
    453 
    454 TF_BUILTIN(StringLessThanOrEqual, StringBuiltinsAssembler) {
    455   GenerateStringRelationalComparison(
    456       RelationalComparisonMode::kLessThanOrEqual);
    457 }
    458 
    459 TF_BUILTIN(StringGreaterThan, StringBuiltinsAssembler) {
    460   GenerateStringRelationalComparison(RelationalComparisonMode::kGreaterThan);
    461 }
    462 
    463 TF_BUILTIN(StringGreaterThanOrEqual, StringBuiltinsAssembler) {
    464   GenerateStringRelationalComparison(
    465       RelationalComparisonMode::kGreaterThanOrEqual);
    466 }
    467 
    468 TF_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 
    480 TF_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 )
    497 TF_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 
    605 namespace {  // for String.fromCodePoint
    606 
    607 bool 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 
    624 uc32 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 )
    638 BUILTIN(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 )
    699 TF_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 )
    738 TF_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 ] )
    776 BUILTIN(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 ] )
    840 BUILTIN(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 
    870 void 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.
    995 TF_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
   1010 TF_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 ] )
   1081 BUILTIN(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.
   1094 BUILTIN(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.
   1144 BUILTIN(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 
   1173 compiler::Node* StringBuiltinsAssembler::IsNullOrUndefined(Node* const value) {
   1174   return Word32Or(IsUndefined(value), IsNull(value));
   1175 }
   1176 
   1177 void 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 
   1191 void 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 )
   1255 TF_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 )
   1423 TF_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 )
   1526 TF_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 
   1645 compiler::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 )
   1695 TF_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 
   1743 BUILTIN(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 ()
   1791 TF_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 ()
   1801 BUILTIN(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
   1808 BUILTIN(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
   1815 BUILTIN(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 ( )
   1822 TF_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 
   1831 TF_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.
   1857 compiler::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 
   1920 TF_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 
   1986 namespace {
   1987 
   1988 inline 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 
   1996 template <class Converter>
   1997 MUST_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 
   2093 template <class Converter>
   2094 MUST_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 
   2150 BUILTIN(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 
   2157 BUILTIN(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 
   2164 BUILTIN(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 
   2171 BUILTIN(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