LCOV - code coverage report
Current view: top level - src/builtins - builtins-string-gen.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 1069 1076 99.3 %
Date: 2017-10-20 Functions: 133 140 95.0 %

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

Generated by: LCOV version 1.10