| OLD | NEW | 
|---|
| 1 // Copyright 2016 the V8 project authors. All rights reserved. | 1 // Copyright 2016 the V8 project authors. All rights reserved. | 
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be | 
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. | 
| 4 | 4 | 
| 5 #include "src/builtins/builtins-regexp.h" |  | 
| 6 #include "src/builtins/builtins-utils.h" | 5 #include "src/builtins/builtins-utils.h" | 
| 7 #include "src/builtins/builtins.h" | 6 #include "src/builtins/builtins.h" | 
| 8 #include "src/code-factory.h" |  | 
| 9 #include "src/code-stub-assembler.h" |  | 
| 10 #include "src/conversions.h" | 7 #include "src/conversions.h" | 
| 11 #include "src/counters.h" | 8 #include "src/counters.h" | 
| 12 #include "src/objects-inl.h" | 9 #include "src/objects-inl.h" | 
| 13 #include "src/regexp/regexp-utils.h" | 10 #include "src/regexp/regexp-utils.h" | 
| 14 #include "src/string-case.h" | 11 #include "src/string-case.h" | 
| 15 #include "src/unicode-inl.h" | 12 #include "src/unicode-inl.h" | 
| 16 #include "src/unicode.h" | 13 #include "src/unicode.h" | 
| 17 | 14 | 
| 18 namespace v8 { | 15 namespace v8 { | 
| 19 namespace internal { | 16 namespace internal { | 
| 20 | 17 | 
| 21 typedef CodeStubAssembler::RelationalComparisonMode RelationalComparisonMode; |  | 
| 22 |  | 
| 23 class StringBuiltinsAssembler : public CodeStubAssembler { |  | 
| 24  public: |  | 
| 25   explicit StringBuiltinsAssembler(compiler::CodeAssemblerState* state) |  | 
| 26       : CodeStubAssembler(state) {} |  | 
| 27 |  | 
| 28  protected: |  | 
| 29   Node* DirectStringData(Node* string, Node* string_instance_type) { |  | 
| 30     // Compute the effective offset of the first character. |  | 
| 31     Variable var_data(this, MachineType::PointerRepresentation()); |  | 
| 32     Label if_sequential(this), if_external(this), if_join(this); |  | 
| 33     Branch(Word32Equal(Word32And(string_instance_type, |  | 
| 34                                  Int32Constant(kStringRepresentationMask)), |  | 
| 35                        Int32Constant(kSeqStringTag)), |  | 
| 36            &if_sequential, &if_external); |  | 
| 37 |  | 
| 38     Bind(&if_sequential); |  | 
| 39     { |  | 
| 40       var_data.Bind(IntPtrAdd( |  | 
| 41           IntPtrConstant(SeqOneByteString::kHeaderSize - kHeapObjectTag), |  | 
| 42           BitcastTaggedToWord(string))); |  | 
| 43       Goto(&if_join); |  | 
| 44     } |  | 
| 45 |  | 
| 46     Bind(&if_external); |  | 
| 47     { |  | 
| 48       // This is only valid for ExternalStrings where the resource data |  | 
| 49       // pointer is cached (i.e. no short external strings). |  | 
| 50       CSA_ASSERT(this, Word32NotEqual( |  | 
| 51                            Word32And(string_instance_type, |  | 
| 52                                      Int32Constant(kShortExternalStringMask)), |  | 
| 53                            Int32Constant(kShortExternalStringTag))); |  | 
| 54       var_data.Bind(LoadObjectField(string, ExternalString::kResourceDataOffset, |  | 
| 55                                     MachineType::Pointer())); |  | 
| 56       Goto(&if_join); |  | 
| 57     } |  | 
| 58 |  | 
| 59     Bind(&if_join); |  | 
| 60     return var_data.value(); |  | 
| 61   } |  | 
| 62 |  | 
| 63   Node* LoadOneByteChar(Node* string, Node* index) { |  | 
| 64     return Load(MachineType::Uint8(), string, OneByteCharOffset(index)); |  | 
| 65   } |  | 
| 66 |  | 
| 67   Node* OneByteCharAddress(Node* string, Node* index) { |  | 
| 68     Node* offset = OneByteCharOffset(index); |  | 
| 69     return IntPtrAdd(string, offset); |  | 
| 70   } |  | 
| 71 |  | 
| 72   Node* OneByteCharOffset(Node* index) { |  | 
| 73     return CharOffset(String::ONE_BYTE_ENCODING, index); |  | 
| 74   } |  | 
| 75 |  | 
| 76   Node* CharOffset(String::Encoding encoding, Node* index) { |  | 
| 77     const int header = SeqOneByteString::kHeaderSize - kHeapObjectTag; |  | 
| 78     Node* offset = index; |  | 
| 79     if (encoding == String::TWO_BYTE_ENCODING) { |  | 
| 80       offset = IntPtrAdd(offset, offset); |  | 
| 81     } |  | 
| 82     offset = IntPtrAdd(offset, IntPtrConstant(header)); |  | 
| 83     return offset; |  | 
| 84   } |  | 
| 85 |  | 
| 86   void DispatchOnStringInstanceType(Node* const instance_type, |  | 
| 87                                     Label* if_onebyte_sequential, |  | 
| 88                                     Label* if_onebyte_external, |  | 
| 89                                     Label* if_otherwise) { |  | 
| 90     const int kMask = kStringRepresentationMask | kStringEncodingMask; |  | 
| 91     Node* const encoding_and_representation = |  | 
| 92         Word32And(instance_type, Int32Constant(kMask)); |  | 
| 93 |  | 
| 94     int32_t values[] = { |  | 
| 95         kOneByteStringTag | kSeqStringTag, |  | 
| 96         kOneByteStringTag | kExternalStringTag, |  | 
| 97     }; |  | 
| 98     Label* labels[] = { |  | 
| 99         if_onebyte_sequential, if_onebyte_external, |  | 
| 100     }; |  | 
| 101     STATIC_ASSERT(arraysize(values) == arraysize(labels)); |  | 
| 102 |  | 
| 103     Switch(encoding_and_representation, if_otherwise, values, labels, |  | 
| 104            arraysize(values)); |  | 
| 105   } |  | 
| 106 |  | 
| 107   void GenerateStringEqual(); |  | 
| 108   void GenerateStringRelationalComparison(RelationalComparisonMode mode); |  | 
| 109 |  | 
| 110   Node* ToSmiBetweenZeroAnd(Node* context, Node* value, Node* limit); |  | 
| 111 |  | 
| 112   Node* LoadSurrogatePairAt(Node* string, Node* length, Node* index, |  | 
| 113                             UnicodeEncoding encoding); |  | 
| 114 |  | 
| 115   void StringIndexOf(Node* receiver, Node* instance_type, Node* search_string, |  | 
| 116                      Node* search_string_instance_type, Node* position, |  | 
| 117                      std::function<void(Node*)> f_return); |  | 
| 118 |  | 
| 119   Node* IsNullOrUndefined(Node* const value); |  | 
| 120   void RequireObjectCoercible(Node* const context, Node* const value, |  | 
| 121                               const char* method_name); |  | 
| 122 |  | 
| 123   Node* SmiIsNegative(Node* const value) { |  | 
| 124     return SmiLessThan(value, SmiConstant(0)); |  | 
| 125   } |  | 
| 126 |  | 
| 127   // Implements boilerplate logic for {match, split, replace, search} of the |  | 
| 128   // form: |  | 
| 129   // |  | 
| 130   //  if (!IS_NULL_OR_UNDEFINED(object)) { |  | 
| 131   //    var maybe_function = object[symbol]; |  | 
| 132   //    if (!IS_UNDEFINED(maybe_function)) { |  | 
| 133   //      return %_Call(maybe_function, ...); |  | 
| 134   //    } |  | 
| 135   //  } |  | 
| 136   // |  | 
| 137   // Contains fast paths for Smi and RegExp objects. |  | 
| 138   typedef std::function<Node*()> NodeFunction0; |  | 
| 139   typedef std::function<Node*(Node* fn)> NodeFunction1; |  | 
| 140   void MaybeCallFunctionAtSymbol(Node* const context, Node* const object, |  | 
| 141                                  Handle<Symbol> symbol, |  | 
| 142                                  const NodeFunction0& regexp_call, |  | 
| 143                                  const NodeFunction1& generic_call); |  | 
| 144 }; |  | 
| 145 |  | 
| 146 void StringBuiltinsAssembler::GenerateStringEqual() { |  | 
| 147   // Here's pseudo-code for the algorithm below: |  | 
| 148   // |  | 
| 149   // if (lhs == rhs) return true; |  | 
| 150   // if (lhs->length() != rhs->length()) return false; |  | 
| 151   // if (lhs->IsInternalizedString() && rhs->IsInternalizedString()) { |  | 
| 152   //   return false; |  | 
| 153   // } |  | 
| 154   // if (lhs->IsSeqOneByteString() && rhs->IsSeqOneByteString()) { |  | 
| 155   //   for (i = 0; i != lhs->length(); ++i) { |  | 
| 156   //     if (lhs[i] != rhs[i]) return false; |  | 
| 157   //   } |  | 
| 158   //   return true; |  | 
| 159   // } |  | 
| 160   // if (lhs and/or rhs are indirect strings) { |  | 
| 161   //   unwrap them and restart from the beginning; |  | 
| 162   // } |  | 
| 163   // return %StringEqual(lhs, rhs); |  | 
| 164 |  | 
| 165   Variable var_left(this, MachineRepresentation::kTagged); |  | 
| 166   Variable var_right(this, MachineRepresentation::kTagged); |  | 
| 167   var_left.Bind(Parameter(0)); |  | 
| 168   var_right.Bind(Parameter(1)); |  | 
| 169   Node* context = Parameter(2); |  | 
| 170 |  | 
| 171   Variable* input_vars[2] = {&var_left, &var_right}; |  | 
| 172   Label if_equal(this), if_notequal(this), restart(this, 2, input_vars); |  | 
| 173   Goto(&restart); |  | 
| 174   Bind(&restart); |  | 
| 175   Node* lhs = var_left.value(); |  | 
| 176   Node* rhs = var_right.value(); |  | 
| 177 |  | 
| 178   // Fast check to see if {lhs} and {rhs} refer to the same String object. |  | 
| 179   GotoIf(WordEqual(lhs, rhs), &if_equal); |  | 
| 180 |  | 
| 181   // Load the length of {lhs} and {rhs}. |  | 
| 182   Node* lhs_length = LoadStringLength(lhs); |  | 
| 183   Node* rhs_length = LoadStringLength(rhs); |  | 
| 184 |  | 
| 185   // Strings with different lengths cannot be equal. |  | 
| 186   GotoIf(WordNotEqual(lhs_length, rhs_length), &if_notequal); |  | 
| 187 |  | 
| 188   // Load instance types of {lhs} and {rhs}. |  | 
| 189   Node* lhs_instance_type = LoadInstanceType(lhs); |  | 
| 190   Node* rhs_instance_type = LoadInstanceType(rhs); |  | 
| 191 |  | 
| 192   // Combine the instance types into a single 16-bit value, so we can check |  | 
| 193   // both of them at once. |  | 
| 194   Node* both_instance_types = Word32Or( |  | 
| 195       lhs_instance_type, Word32Shl(rhs_instance_type, Int32Constant(8))); |  | 
| 196 |  | 
| 197   // Check if both {lhs} and {rhs} are internalized. Since we already know |  | 
| 198   // that they're not the same object, they're not equal in that case. |  | 
| 199   int const kBothInternalizedMask = |  | 
| 200       kIsNotInternalizedMask | (kIsNotInternalizedMask << 8); |  | 
| 201   int const kBothInternalizedTag = kInternalizedTag | (kInternalizedTag << 8); |  | 
| 202   GotoIf(Word32Equal(Word32And(both_instance_types, |  | 
| 203                                Int32Constant(kBothInternalizedMask)), |  | 
| 204                      Int32Constant(kBothInternalizedTag)), |  | 
| 205          &if_notequal); |  | 
| 206 |  | 
| 207   // Check that both {lhs} and {rhs} are flat one-byte strings, and that |  | 
| 208   // in case of ExternalStrings the data pointer is cached.. |  | 
| 209   STATIC_ASSERT(kShortExternalStringTag != 0); |  | 
| 210   int const kBothDirectOneByteStringMask = |  | 
| 211       kStringEncodingMask | kIsIndirectStringMask | kShortExternalStringMask | |  | 
| 212       ((kStringEncodingMask | kIsIndirectStringMask | kShortExternalStringMask) |  | 
| 213        << 8); |  | 
| 214   int const kBothDirectOneByteStringTag = |  | 
| 215       kOneByteStringTag | (kOneByteStringTag << 8); |  | 
| 216   Label if_bothdirectonebytestrings(this), if_notbothdirectonebytestrings(this); |  | 
| 217   Branch(Word32Equal(Word32And(both_instance_types, |  | 
| 218                                Int32Constant(kBothDirectOneByteStringMask)), |  | 
| 219                      Int32Constant(kBothDirectOneByteStringTag)), |  | 
| 220          &if_bothdirectonebytestrings, &if_notbothdirectonebytestrings); |  | 
| 221 |  | 
| 222   Bind(&if_bothdirectonebytestrings); |  | 
| 223   { |  | 
| 224     // Compute the effective offset of the first character. |  | 
| 225     Node* lhs_data = DirectStringData(lhs, lhs_instance_type); |  | 
| 226     Node* rhs_data = DirectStringData(rhs, rhs_instance_type); |  | 
| 227 |  | 
| 228     // Compute the first offset after the string from the length. |  | 
| 229     Node* length = SmiUntag(lhs_length); |  | 
| 230 |  | 
| 231     // Loop over the {lhs} and {rhs} strings to see if they are equal. |  | 
| 232     Variable var_offset(this, MachineType::PointerRepresentation()); |  | 
| 233     Label loop(this, &var_offset); |  | 
| 234     var_offset.Bind(IntPtrConstant(0)); |  | 
| 235     Goto(&loop); |  | 
| 236     Bind(&loop); |  | 
| 237     { |  | 
| 238       // If {offset} equals {end}, no difference was found, so the |  | 
| 239       // strings are equal. |  | 
| 240       Node* offset = var_offset.value(); |  | 
| 241       GotoIf(WordEqual(offset, length), &if_equal); |  | 
| 242 |  | 
| 243       // Load the next characters from {lhs} and {rhs}. |  | 
| 244       Node* lhs_value = Load(MachineType::Uint8(), lhs_data, offset); |  | 
| 245       Node* rhs_value = Load(MachineType::Uint8(), rhs_data, offset); |  | 
| 246 |  | 
| 247       // Check if the characters match. |  | 
| 248       GotoIf(Word32NotEqual(lhs_value, rhs_value), &if_notequal); |  | 
| 249 |  | 
| 250       // Advance to next character. |  | 
| 251       var_offset.Bind(IntPtrAdd(offset, IntPtrConstant(1))); |  | 
| 252       Goto(&loop); |  | 
| 253     } |  | 
| 254   } |  | 
| 255 |  | 
| 256   Bind(&if_notbothdirectonebytestrings); |  | 
| 257   { |  | 
| 258     // Try to unwrap indirect strings, restart the above attempt on success. |  | 
| 259     MaybeDerefIndirectStrings(&var_left, lhs_instance_type, &var_right, |  | 
| 260                               rhs_instance_type, &restart); |  | 
| 261     // TODO(bmeurer): Add support for two byte string equality checks. |  | 
| 262 |  | 
| 263     TailCallRuntime(Runtime::kStringEqual, context, lhs, rhs); |  | 
| 264   } |  | 
| 265 |  | 
| 266   Bind(&if_equal); |  | 
| 267   Return(TrueConstant()); |  | 
| 268 |  | 
| 269   Bind(&if_notequal); |  | 
| 270   Return(FalseConstant()); |  | 
| 271 } |  | 
| 272 |  | 
| 273 void StringBuiltinsAssembler::GenerateStringRelationalComparison( |  | 
| 274     RelationalComparisonMode mode) { |  | 
| 275   Variable var_left(this, MachineRepresentation::kTagged); |  | 
| 276   Variable var_right(this, MachineRepresentation::kTagged); |  | 
| 277   var_left.Bind(Parameter(0)); |  | 
| 278   var_right.Bind(Parameter(1)); |  | 
| 279   Node* context = Parameter(2); |  | 
| 280 |  | 
| 281   Variable* input_vars[2] = {&var_left, &var_right}; |  | 
| 282   Label if_less(this), if_equal(this), if_greater(this); |  | 
| 283   Label restart(this, 2, input_vars); |  | 
| 284   Goto(&restart); |  | 
| 285   Bind(&restart); |  | 
| 286 |  | 
| 287   Node* lhs = var_left.value(); |  | 
| 288   Node* rhs = var_right.value(); |  | 
| 289   // Fast check to see if {lhs} and {rhs} refer to the same String object. |  | 
| 290   GotoIf(WordEqual(lhs, rhs), &if_equal); |  | 
| 291 |  | 
| 292   // Load instance types of {lhs} and {rhs}. |  | 
| 293   Node* lhs_instance_type = LoadInstanceType(lhs); |  | 
| 294   Node* rhs_instance_type = LoadInstanceType(rhs); |  | 
| 295 |  | 
| 296   // Combine the instance types into a single 16-bit value, so we can check |  | 
| 297   // both of them at once. |  | 
| 298   Node* both_instance_types = Word32Or( |  | 
| 299       lhs_instance_type, Word32Shl(rhs_instance_type, Int32Constant(8))); |  | 
| 300 |  | 
| 301   // Check that both {lhs} and {rhs} are flat one-byte strings. |  | 
| 302   int const kBothSeqOneByteStringMask = |  | 
| 303       kStringEncodingMask | kStringRepresentationMask | |  | 
| 304       ((kStringEncodingMask | kStringRepresentationMask) << 8); |  | 
| 305   int const kBothSeqOneByteStringTag = |  | 
| 306       kOneByteStringTag | kSeqStringTag | |  | 
| 307       ((kOneByteStringTag | kSeqStringTag) << 8); |  | 
| 308   Label if_bothonebyteseqstrings(this), if_notbothonebyteseqstrings(this); |  | 
| 309   Branch(Word32Equal(Word32And(both_instance_types, |  | 
| 310                                Int32Constant(kBothSeqOneByteStringMask)), |  | 
| 311                      Int32Constant(kBothSeqOneByteStringTag)), |  | 
| 312          &if_bothonebyteseqstrings, &if_notbothonebyteseqstrings); |  | 
| 313 |  | 
| 314   Bind(&if_bothonebyteseqstrings); |  | 
| 315   { |  | 
| 316     // Load the length of {lhs} and {rhs}. |  | 
| 317     Node* lhs_length = LoadStringLength(lhs); |  | 
| 318     Node* rhs_length = LoadStringLength(rhs); |  | 
| 319 |  | 
| 320     // Determine the minimum length. |  | 
| 321     Node* length = SmiMin(lhs_length, rhs_length); |  | 
| 322 |  | 
| 323     // Compute the effective offset of the first character. |  | 
| 324     Node* begin = |  | 
| 325         IntPtrConstant(SeqOneByteString::kHeaderSize - kHeapObjectTag); |  | 
| 326 |  | 
| 327     // Compute the first offset after the string from the length. |  | 
| 328     Node* end = IntPtrAdd(begin, SmiUntag(length)); |  | 
| 329 |  | 
| 330     // Loop over the {lhs} and {rhs} strings to see if they are equal. |  | 
| 331     Variable var_offset(this, MachineType::PointerRepresentation()); |  | 
| 332     Label loop(this, &var_offset); |  | 
| 333     var_offset.Bind(begin); |  | 
| 334     Goto(&loop); |  | 
| 335     Bind(&loop); |  | 
| 336     { |  | 
| 337       // Check if {offset} equals {end}. |  | 
| 338       Node* offset = var_offset.value(); |  | 
| 339       Label if_done(this), if_notdone(this); |  | 
| 340       Branch(WordEqual(offset, end), &if_done, &if_notdone); |  | 
| 341 |  | 
| 342       Bind(&if_notdone); |  | 
| 343       { |  | 
| 344         // Load the next characters from {lhs} and {rhs}. |  | 
| 345         Node* lhs_value = Load(MachineType::Uint8(), lhs, offset); |  | 
| 346         Node* rhs_value = Load(MachineType::Uint8(), rhs, offset); |  | 
| 347 |  | 
| 348         // Check if the characters match. |  | 
| 349         Label if_valueissame(this), if_valueisnotsame(this); |  | 
| 350         Branch(Word32Equal(lhs_value, rhs_value), &if_valueissame, |  | 
| 351                &if_valueisnotsame); |  | 
| 352 |  | 
| 353         Bind(&if_valueissame); |  | 
| 354         { |  | 
| 355           // Advance to next character. |  | 
| 356           var_offset.Bind(IntPtrAdd(offset, IntPtrConstant(1))); |  | 
| 357         } |  | 
| 358         Goto(&loop); |  | 
| 359 |  | 
| 360         Bind(&if_valueisnotsame); |  | 
| 361         Branch(Uint32LessThan(lhs_value, rhs_value), &if_less, &if_greater); |  | 
| 362       } |  | 
| 363 |  | 
| 364       Bind(&if_done); |  | 
| 365       { |  | 
| 366         // All characters up to the min length are equal, decide based on |  | 
| 367         // string length. |  | 
| 368         GotoIf(SmiEqual(lhs_length, rhs_length), &if_equal); |  | 
| 369         BranchIfSmiLessThan(lhs_length, rhs_length, &if_less, &if_greater); |  | 
| 370       } |  | 
| 371     } |  | 
| 372     } |  | 
| 373 |  | 
| 374     Bind(&if_notbothonebyteseqstrings); |  | 
| 375     { |  | 
| 376       // Try to unwrap indirect strings, restart the above attempt on success. |  | 
| 377       MaybeDerefIndirectStrings(&var_left, lhs_instance_type, &var_right, |  | 
| 378                                 rhs_instance_type, &restart); |  | 
| 379       // TODO(bmeurer): Add support for two byte string relational comparisons. |  | 
| 380       switch (mode) { |  | 
| 381         case RelationalComparisonMode::kLessThan: |  | 
| 382           TailCallRuntime(Runtime::kStringLessThan, context, lhs, rhs); |  | 
| 383           break; |  | 
| 384         case RelationalComparisonMode::kLessThanOrEqual: |  | 
| 385           TailCallRuntime(Runtime::kStringLessThanOrEqual, context, lhs, rhs); |  | 
| 386           break; |  | 
| 387         case RelationalComparisonMode::kGreaterThan: |  | 
| 388           TailCallRuntime(Runtime::kStringGreaterThan, context, lhs, rhs); |  | 
| 389           break; |  | 
| 390         case RelationalComparisonMode::kGreaterThanOrEqual: |  | 
| 391           TailCallRuntime(Runtime::kStringGreaterThanOrEqual, context, lhs, |  | 
| 392                           rhs); |  | 
| 393           break; |  | 
| 394       } |  | 
| 395     } |  | 
| 396 |  | 
| 397     Bind(&if_less); |  | 
| 398     switch (mode) { |  | 
| 399       case RelationalComparisonMode::kLessThan: |  | 
| 400       case RelationalComparisonMode::kLessThanOrEqual: |  | 
| 401         Return(BooleanConstant(true)); |  | 
| 402         break; |  | 
| 403 |  | 
| 404       case RelationalComparisonMode::kGreaterThan: |  | 
| 405       case RelationalComparisonMode::kGreaterThanOrEqual: |  | 
| 406         Return(BooleanConstant(false)); |  | 
| 407         break; |  | 
| 408   } |  | 
| 409 |  | 
| 410   Bind(&if_equal); |  | 
| 411   switch (mode) { |  | 
| 412     case RelationalComparisonMode::kLessThan: |  | 
| 413     case RelationalComparisonMode::kGreaterThan: |  | 
| 414       Return(BooleanConstant(false)); |  | 
| 415       break; |  | 
| 416 |  | 
| 417     case RelationalComparisonMode::kLessThanOrEqual: |  | 
| 418     case RelationalComparisonMode::kGreaterThanOrEqual: |  | 
| 419       Return(BooleanConstant(true)); |  | 
| 420       break; |  | 
| 421   } |  | 
| 422 |  | 
| 423   Bind(&if_greater); |  | 
| 424   switch (mode) { |  | 
| 425     case RelationalComparisonMode::kLessThan: |  | 
| 426     case RelationalComparisonMode::kLessThanOrEqual: |  | 
| 427       Return(BooleanConstant(false)); |  | 
| 428       break; |  | 
| 429 |  | 
| 430     case RelationalComparisonMode::kGreaterThan: |  | 
| 431     case RelationalComparisonMode::kGreaterThanOrEqual: |  | 
| 432       Return(BooleanConstant(true)); |  | 
| 433       break; |  | 
| 434   } |  | 
| 435 } |  | 
| 436 |  | 
| 437 TF_BUILTIN(StringEqual, StringBuiltinsAssembler) { GenerateStringEqual(); } |  | 
| 438 |  | 
| 439 TF_BUILTIN(StringLessThan, StringBuiltinsAssembler) { |  | 
| 440   GenerateStringRelationalComparison(RelationalComparisonMode::kLessThan); |  | 
| 441 } |  | 
| 442 |  | 
| 443 TF_BUILTIN(StringLessThanOrEqual, StringBuiltinsAssembler) { |  | 
| 444   GenerateStringRelationalComparison( |  | 
| 445       RelationalComparisonMode::kLessThanOrEqual); |  | 
| 446 } |  | 
| 447 |  | 
| 448 TF_BUILTIN(StringGreaterThan, StringBuiltinsAssembler) { |  | 
| 449   GenerateStringRelationalComparison(RelationalComparisonMode::kGreaterThan); |  | 
| 450 } |  | 
| 451 |  | 
| 452 TF_BUILTIN(StringGreaterThanOrEqual, StringBuiltinsAssembler) { |  | 
| 453   GenerateStringRelationalComparison( |  | 
| 454       RelationalComparisonMode::kGreaterThanOrEqual); |  | 
| 455 } |  | 
| 456 |  | 
| 457 TF_BUILTIN(StringCharAt, CodeStubAssembler) { |  | 
| 458   Node* receiver = Parameter(0); |  | 
| 459   Node* position = Parameter(1); |  | 
| 460 |  | 
| 461   // Load the character code at the {position} from the {receiver}. |  | 
| 462   Node* code = StringCharCodeAt(receiver, position, INTPTR_PARAMETERS); |  | 
| 463 |  | 
| 464   // And return the single character string with only that {code} |  | 
| 465   Node* result = StringFromCharCode(code); |  | 
| 466   Return(result); |  | 
| 467 } |  | 
| 468 |  | 
| 469 TF_BUILTIN(StringCharCodeAt, CodeStubAssembler) { |  | 
| 470   Node* receiver = Parameter(0); |  | 
| 471   Node* position = Parameter(1); |  | 
| 472 |  | 
| 473   // Load the character code at the {position} from the {receiver}. |  | 
| 474   Node* code = StringCharCodeAt(receiver, position, INTPTR_PARAMETERS); |  | 
| 475 |  | 
| 476   // And return it as TaggedSigned value. |  | 
| 477   // TODO(turbofan): Allow builtins to return values untagged. |  | 
| 478   Node* result = SmiFromWord32(code); |  | 
| 479   Return(result); |  | 
| 480 } |  | 
| 481 |  | 
| 482 // ----------------------------------------------------------------------------- |  | 
| 483 // ES6 section 21.1 String Objects |  | 
| 484 |  | 
| 485 // ES6 section 21.1.2.1 String.fromCharCode ( ...codeUnits ) |  | 
| 486 TF_BUILTIN(StringFromCharCode, CodeStubAssembler) { |  | 
| 487   Node* argc = Parameter(BuiltinDescriptor::kArgumentsCount); |  | 
| 488   Node* context = Parameter(BuiltinDescriptor::kContext); |  | 
| 489 |  | 
| 490   CodeStubArguments arguments(this, ChangeInt32ToIntPtr(argc)); |  | 
| 491   // From now on use word-size argc value. |  | 
| 492   argc = arguments.GetLength(); |  | 
| 493 |  | 
| 494   // Check if we have exactly one argument (plus the implicit receiver), i.e. |  | 
| 495   // if the parent frame is not an arguments adaptor frame. |  | 
| 496   Label if_oneargument(this), if_notoneargument(this); |  | 
| 497   Branch(WordEqual(argc, IntPtrConstant(1)), &if_oneargument, |  | 
| 498          &if_notoneargument); |  | 
| 499 |  | 
| 500   Bind(&if_oneargument); |  | 
| 501   { |  | 
| 502     // Single argument case, perform fast single character string cache lookup |  | 
| 503     // for one-byte code units, or fall back to creating a single character |  | 
| 504     // string on the fly otherwise. |  | 
| 505     Node* code = arguments.AtIndex(0); |  | 
| 506     Node* code32 = TruncateTaggedToWord32(context, code); |  | 
| 507     Node* code16 = Word32And(code32, Int32Constant(String::kMaxUtf16CodeUnit)); |  | 
| 508     Node* result = StringFromCharCode(code16); |  | 
| 509     arguments.PopAndReturn(result); |  | 
| 510   } |  | 
| 511 |  | 
| 512   Node* code16 = nullptr; |  | 
| 513   Bind(&if_notoneargument); |  | 
| 514   { |  | 
| 515     Label two_byte(this); |  | 
| 516     // Assume that the resulting string contains only one-byte characters. |  | 
| 517     Node* one_byte_result = AllocateSeqOneByteString(context, argc); |  | 
| 518 |  | 
| 519     Variable max_index(this, MachineType::PointerRepresentation()); |  | 
| 520     max_index.Bind(IntPtrConstant(0)); |  | 
| 521 |  | 
| 522     // Iterate over the incoming arguments, converting them to 8-bit character |  | 
| 523     // codes. Stop if any of the conversions generates a code that doesn't fit |  | 
| 524     // in 8 bits. |  | 
| 525     CodeStubAssembler::VariableList vars({&max_index}, zone()); |  | 
| 526     arguments.ForEach(vars, [this, context, &two_byte, &max_index, &code16, |  | 
| 527                              one_byte_result](Node* arg) { |  | 
| 528       Node* code32 = TruncateTaggedToWord32(context, arg); |  | 
| 529       code16 = Word32And(code32, Int32Constant(String::kMaxUtf16CodeUnit)); |  | 
| 530 |  | 
| 531       GotoIf( |  | 
| 532           Int32GreaterThan(code16, Int32Constant(String::kMaxOneByteCharCode)), |  | 
| 533           &two_byte); |  | 
| 534 |  | 
| 535       // The {code16} fits into the SeqOneByteString {one_byte_result}. |  | 
| 536       Node* offset = ElementOffsetFromIndex( |  | 
| 537           max_index.value(), UINT8_ELEMENTS, |  | 
| 538           CodeStubAssembler::INTPTR_PARAMETERS, |  | 
| 539           SeqOneByteString::kHeaderSize - kHeapObjectTag); |  | 
| 540       StoreNoWriteBarrier(MachineRepresentation::kWord8, one_byte_result, |  | 
| 541                           offset, code16); |  | 
| 542       max_index.Bind(IntPtrAdd(max_index.value(), IntPtrConstant(1))); |  | 
| 543     }); |  | 
| 544     arguments.PopAndReturn(one_byte_result); |  | 
| 545 |  | 
| 546     Bind(&two_byte); |  | 
| 547 |  | 
| 548     // At least one of the characters in the string requires a 16-bit |  | 
| 549     // representation.  Allocate a SeqTwoByteString to hold the resulting |  | 
| 550     // string. |  | 
| 551     Node* two_byte_result = AllocateSeqTwoByteString(context, argc); |  | 
| 552 |  | 
| 553     // Copy the characters that have already been put in the 8-bit string into |  | 
| 554     // their corresponding positions in the new 16-bit string. |  | 
| 555     Node* zero = IntPtrConstant(0); |  | 
| 556     CopyStringCharacters(one_byte_result, two_byte_result, zero, zero, |  | 
| 557                          max_index.value(), String::ONE_BYTE_ENCODING, |  | 
| 558                          String::TWO_BYTE_ENCODING, |  | 
| 559                          CodeStubAssembler::INTPTR_PARAMETERS); |  | 
| 560 |  | 
| 561     // Write the character that caused the 8-bit to 16-bit fault. |  | 
| 562     Node* max_index_offset = |  | 
| 563         ElementOffsetFromIndex(max_index.value(), UINT16_ELEMENTS, |  | 
| 564                                CodeStubAssembler::INTPTR_PARAMETERS, |  | 
| 565                                SeqTwoByteString::kHeaderSize - kHeapObjectTag); |  | 
| 566     StoreNoWriteBarrier(MachineRepresentation::kWord16, two_byte_result, |  | 
| 567                         max_index_offset, code16); |  | 
| 568     max_index.Bind(IntPtrAdd(max_index.value(), IntPtrConstant(1))); |  | 
| 569 |  | 
| 570     // Resume copying the passed-in arguments from the same place where the |  | 
| 571     // 8-bit copy stopped, but this time copying over all of the characters |  | 
| 572     // using a 16-bit representation. |  | 
| 573     arguments.ForEach( |  | 
| 574         vars, |  | 
| 575         [this, context, two_byte_result, &max_index](Node* arg) { |  | 
| 576           Node* code32 = TruncateTaggedToWord32(context, arg); |  | 
| 577           Node* code16 = |  | 
| 578               Word32And(code32, Int32Constant(String::kMaxUtf16CodeUnit)); |  | 
| 579 |  | 
| 580           Node* offset = ElementOffsetFromIndex( |  | 
| 581               max_index.value(), UINT16_ELEMENTS, |  | 
| 582               CodeStubAssembler::INTPTR_PARAMETERS, |  | 
| 583               SeqTwoByteString::kHeaderSize - kHeapObjectTag); |  | 
| 584           StoreNoWriteBarrier(MachineRepresentation::kWord16, two_byte_result, |  | 
| 585                               offset, code16); |  | 
| 586           max_index.Bind(IntPtrAdd(max_index.value(), IntPtrConstant(1))); |  | 
| 587         }, |  | 
| 588         max_index.value()); |  | 
| 589 |  | 
| 590     arguments.PopAndReturn(two_byte_result); |  | 
| 591   } |  | 
| 592 } |  | 
| 593 |  | 
| 594 namespace {  // for String.fromCodePoint | 18 namespace {  // for String.fromCodePoint | 
| 595 | 19 | 
| 596 bool IsValidCodePoint(Isolate* isolate, Handle<Object> value) { | 20 bool IsValidCodePoint(Isolate* isolate, Handle<Object> value) { | 
| 597   if (!value->IsNumber() && !Object::ToNumber(value).ToHandle(&value)) { | 21   if (!value->IsNumber() && !Object::ToNumber(value).ToHandle(&value)) { | 
| 598     return false; | 22     return false; | 
| 599   } | 23   } | 
| 600 | 24 | 
| 601   if (Object::ToInteger(isolate, value).ToHandleChecked()->Number() != | 25   if (Object::ToInteger(isolate, value).ToHandleChecked()->Number() != | 
| 602       value->Number()) { | 26       value->Number()) { | 
| 603     return false; | 27     return false; | 
| (...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 677                                               two_byte_buffer.length())); | 101                                               two_byte_buffer.length())); | 
| 678 | 102 | 
| 679   CopyChars(result->GetChars(), one_byte_buffer.ToConstVector().start(), | 103   CopyChars(result->GetChars(), one_byte_buffer.ToConstVector().start(), | 
| 680             one_byte_buffer.length()); | 104             one_byte_buffer.length()); | 
| 681   CopyChars(result->GetChars() + one_byte_buffer.length(), | 105   CopyChars(result->GetChars() + one_byte_buffer.length(), | 
| 682             two_byte_buffer.ToConstVector().start(), two_byte_buffer.length()); | 106             two_byte_buffer.ToConstVector().start(), two_byte_buffer.length()); | 
| 683 | 107 | 
| 684   return *result; | 108   return *result; | 
| 685 } | 109 } | 
| 686 | 110 | 
| 687 // ES6 section 21.1.3.1 String.prototype.charAt ( pos ) |  | 
| 688 TF_BUILTIN(StringPrototypeCharAt, CodeStubAssembler) { |  | 
| 689   Node* receiver = Parameter(0); |  | 
| 690   Node* position = Parameter(1); |  | 
| 691   Node* context = Parameter(4); |  | 
| 692 |  | 
| 693   // Check that {receiver} is coercible to Object and convert it to a String. |  | 
| 694   receiver = ToThisString(context, receiver, "String.prototype.charAt"); |  | 
| 695 |  | 
| 696   // Convert the {position} to a Smi and check that it's in bounds of the |  | 
| 697   // {receiver}. |  | 
| 698   { |  | 
| 699     Label return_emptystring(this, Label::kDeferred); |  | 
| 700     position = |  | 
| 701         ToInteger(context, position, CodeStubAssembler::kTruncateMinusZero); |  | 
| 702     GotoIfNot(TaggedIsSmi(position), &return_emptystring); |  | 
| 703 |  | 
| 704     // Determine the actual length of the {receiver} String. |  | 
| 705     Node* receiver_length = LoadObjectField(receiver, String::kLengthOffset); |  | 
| 706 |  | 
| 707     // Return "" if the Smi {position} is outside the bounds of the {receiver}. |  | 
| 708     Label if_positioninbounds(this); |  | 
| 709     Branch(SmiAboveOrEqual(position, receiver_length), &return_emptystring, |  | 
| 710            &if_positioninbounds); |  | 
| 711 |  | 
| 712     Bind(&return_emptystring); |  | 
| 713     Return(EmptyStringConstant()); |  | 
| 714 |  | 
| 715     Bind(&if_positioninbounds); |  | 
| 716   } |  | 
| 717 |  | 
| 718   // Load the character code at the {position} from the {receiver}. |  | 
| 719   Node* code = StringCharCodeAt(receiver, position); |  | 
| 720 |  | 
| 721   // And return the single character string with only that {code}. |  | 
| 722   Node* result = StringFromCharCode(code); |  | 
| 723   Return(result); |  | 
| 724 } |  | 
| 725 |  | 
| 726 // ES6 section 21.1.3.2 String.prototype.charCodeAt ( pos ) |  | 
| 727 TF_BUILTIN(StringPrototypeCharCodeAt, CodeStubAssembler) { |  | 
| 728   Node* receiver = Parameter(0); |  | 
| 729   Node* position = Parameter(1); |  | 
| 730   Node* context = Parameter(4); |  | 
| 731 |  | 
| 732   // Check that {receiver} is coercible to Object and convert it to a String. |  | 
| 733   receiver = ToThisString(context, receiver, "String.prototype.charCodeAt"); |  | 
| 734 |  | 
| 735   // Convert the {position} to a Smi and check that it's in bounds of the |  | 
| 736   // {receiver}. |  | 
| 737   { |  | 
| 738     Label return_nan(this, Label::kDeferred); |  | 
| 739     position = |  | 
| 740         ToInteger(context, position, CodeStubAssembler::kTruncateMinusZero); |  | 
| 741     GotoIfNot(TaggedIsSmi(position), &return_nan); |  | 
| 742 |  | 
| 743     // Determine the actual length of the {receiver} String. |  | 
| 744     Node* receiver_length = LoadObjectField(receiver, String::kLengthOffset); |  | 
| 745 |  | 
| 746     // Return NaN if the Smi {position} is outside the bounds of the {receiver}. |  | 
| 747     Label if_positioninbounds(this); |  | 
| 748     Branch(SmiAboveOrEqual(position, receiver_length), &return_nan, |  | 
| 749            &if_positioninbounds); |  | 
| 750 |  | 
| 751     Bind(&return_nan); |  | 
| 752     Return(NaNConstant()); |  | 
| 753 |  | 
| 754     Bind(&if_positioninbounds); |  | 
| 755   } |  | 
| 756 |  | 
| 757   // Load the character at the {position} from the {receiver}. |  | 
| 758   Node* value = StringCharCodeAt(receiver, position); |  | 
| 759   Node* result = SmiFromWord32(value); |  | 
| 760   Return(result); |  | 
| 761 } |  | 
| 762 |  | 
| 763 // ES6 section 21.1.3.6 | 111 // ES6 section 21.1.3.6 | 
| 764 // String.prototype.endsWith ( searchString [ , endPosition ] ) | 112 // String.prototype.endsWith ( searchString [ , endPosition ] ) | 
| 765 BUILTIN(StringPrototypeEndsWith) { | 113 BUILTIN(StringPrototypeEndsWith) { | 
| 766   HandleScope handle_scope(isolate); | 114   HandleScope handle_scope(isolate); | 
| 767   TO_THIS_STRING(str, "String.prototype.endsWith"); | 115   TO_THIS_STRING(str, "String.prototype.endsWith"); | 
| 768 | 116 | 
| 769   // Check if the search string is a regExp and fail if it is. | 117   // Check if the search string is a regExp and fail if it is. | 
| 770   Handle<Object> search = args.atOrUndefined(isolate, 1); | 118   Handle<Object> search = args.atOrUndefined(isolate, 1); | 
| 771   Maybe<bool> is_reg_exp = RegExpUtils::IsRegExp(isolate, search); | 119   Maybe<bool> is_reg_exp = RegExpUtils::IsRegExp(isolate, search); | 
| 772   if (is_reg_exp.IsNothing()) { | 120   if (is_reg_exp.IsNothing()) { | 
| (...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 849   Handle<Object> position; | 197   Handle<Object> position; | 
| 850   ASSIGN_RETURN_FAILURE_ON_EXCEPTION( | 198   ASSIGN_RETURN_FAILURE_ON_EXCEPTION( | 
| 851       isolate, position, | 199       isolate, position, | 
| 852       Object::ToInteger(isolate, args.atOrUndefined(isolate, 2))); | 200       Object::ToInteger(isolate, args.atOrUndefined(isolate, 2))); | 
| 853 | 201 | 
| 854   uint32_t index = str->ToValidIndex(*position); | 202   uint32_t index = str->ToValidIndex(*position); | 
| 855   int index_in_str = String::IndexOf(isolate, str, search_string, index); | 203   int index_in_str = String::IndexOf(isolate, str, search_string, index); | 
| 856   return *isolate->factory()->ToBoolean(index_in_str != -1); | 204   return *isolate->factory()->ToBoolean(index_in_str != -1); | 
| 857 } | 205 } | 
| 858 | 206 | 
| 859 void StringBuiltinsAssembler::StringIndexOf( |  | 
| 860     Node* receiver, Node* instance_type, Node* search_string, |  | 
| 861     Node* search_string_instance_type, Node* position, |  | 
| 862     std::function<void(Node*)> f_return) { |  | 
| 863   CSA_ASSERT(this, IsString(receiver)); |  | 
| 864   CSA_ASSERT(this, IsString(search_string)); |  | 
| 865   CSA_ASSERT(this, TaggedIsSmi(position)); |  | 
| 866 |  | 
| 867   Label zero_length_needle(this), |  | 
| 868       call_runtime_unchecked(this, Label::kDeferred), return_minus_1(this), |  | 
| 869       check_search_string(this), continue_fast_path(this); |  | 
| 870 |  | 
| 871   Node* const int_zero = IntPtrConstant(0); |  | 
| 872   Variable var_needle_byte(this, MachineType::PointerRepresentation(), |  | 
| 873                            int_zero); |  | 
| 874   Variable var_string_addr(this, MachineType::PointerRepresentation(), |  | 
| 875                            int_zero); |  | 
| 876 |  | 
| 877   Node* needle_length = SmiUntag(LoadStringLength(search_string)); |  | 
| 878   // Use faster/complex runtime fallback for long search strings. |  | 
| 879   GotoIf(IntPtrLessThan(IntPtrConstant(1), needle_length), |  | 
| 880          &call_runtime_unchecked); |  | 
| 881   Node* string_length = SmiUntag(LoadStringLength(receiver)); |  | 
| 882   Node* start_position = IntPtrMax(SmiUntag(position), int_zero); |  | 
| 883 |  | 
| 884   GotoIf(IntPtrEqual(int_zero, needle_length), &zero_length_needle); |  | 
| 885   // Check that the needle fits in the start position. |  | 
| 886   GotoIfNot(IntPtrLessThanOrEqual(needle_length, |  | 
| 887                                   IntPtrSub(string_length, start_position)), |  | 
| 888             &return_minus_1); |  | 
| 889 |  | 
| 890   // Load the string address. |  | 
| 891   { |  | 
| 892     Label if_onebyte_sequential(this); |  | 
| 893     Label if_onebyte_external(this, Label::kDeferred); |  | 
| 894 |  | 
| 895     // Only support one-byte strings on the fast path. |  | 
| 896     DispatchOnStringInstanceType(instance_type, &if_onebyte_sequential, |  | 
| 897                                  &if_onebyte_external, &call_runtime_unchecked); |  | 
| 898 |  | 
| 899     Bind(&if_onebyte_sequential); |  | 
| 900     { |  | 
| 901       var_string_addr.Bind( |  | 
| 902           OneByteCharAddress(BitcastTaggedToWord(receiver), start_position)); |  | 
| 903       Goto(&check_search_string); |  | 
| 904     } |  | 
| 905 |  | 
| 906     Bind(&if_onebyte_external); |  | 
| 907     { |  | 
| 908       Node* const unpacked = TryDerefExternalString(receiver, instance_type, |  | 
| 909                                                     &call_runtime_unchecked); |  | 
| 910       var_string_addr.Bind(OneByteCharAddress(unpacked, start_position)); |  | 
| 911       Goto(&check_search_string); |  | 
| 912     } |  | 
| 913   } |  | 
| 914 |  | 
| 915   // Load the needle character. |  | 
| 916   Bind(&check_search_string); |  | 
| 917   { |  | 
| 918     Label if_onebyte_sequential(this); |  | 
| 919     Label if_onebyte_external(this, Label::kDeferred); |  | 
| 920 |  | 
| 921     DispatchOnStringInstanceType(search_string_instance_type, |  | 
| 922                                  &if_onebyte_sequential, &if_onebyte_external, |  | 
| 923                                  &call_runtime_unchecked); |  | 
| 924 |  | 
| 925     Bind(&if_onebyte_sequential); |  | 
| 926     { |  | 
| 927       var_needle_byte.Bind( |  | 
| 928           ChangeInt32ToIntPtr(LoadOneByteChar(search_string, int_zero))); |  | 
| 929       Goto(&continue_fast_path); |  | 
| 930     } |  | 
| 931 |  | 
| 932     Bind(&if_onebyte_external); |  | 
| 933     { |  | 
| 934       Node* const unpacked = TryDerefExternalString( |  | 
| 935           search_string, search_string_instance_type, &call_runtime_unchecked); |  | 
| 936       var_needle_byte.Bind( |  | 
| 937           ChangeInt32ToIntPtr(LoadOneByteChar(unpacked, int_zero))); |  | 
| 938       Goto(&continue_fast_path); |  | 
| 939     } |  | 
| 940   } |  | 
| 941 |  | 
| 942   Bind(&continue_fast_path); |  | 
| 943   { |  | 
| 944     Node* needle_byte = var_needle_byte.value(); |  | 
| 945     Node* string_addr = var_string_addr.value(); |  | 
| 946     Node* search_length = IntPtrSub(string_length, start_position); |  | 
| 947     // Call out to the highly optimized memchr to perform the actual byte |  | 
| 948     // search. |  | 
| 949     Node* memchr = |  | 
| 950         ExternalConstant(ExternalReference::libc_memchr_function(isolate())); |  | 
| 951     Node* result_address = |  | 
| 952         CallCFunction3(MachineType::Pointer(), MachineType::Pointer(), |  | 
| 953                        MachineType::IntPtr(), MachineType::UintPtr(), memchr, |  | 
| 954                        string_addr, needle_byte, search_length); |  | 
| 955     GotoIf(WordEqual(result_address, int_zero), &return_minus_1); |  | 
| 956     Node* result_index = |  | 
| 957         IntPtrAdd(IntPtrSub(result_address, string_addr), start_position); |  | 
| 958     f_return(SmiTag(result_index)); |  | 
| 959   } |  | 
| 960 |  | 
| 961   Bind(&return_minus_1); |  | 
| 962   f_return(SmiConstant(-1)); |  | 
| 963 |  | 
| 964   Bind(&zero_length_needle); |  | 
| 965   { |  | 
| 966     Comment("0-length search_string"); |  | 
| 967     f_return(SmiTag(IntPtrMin(string_length, start_position))); |  | 
| 968   } |  | 
| 969 |  | 
| 970   Bind(&call_runtime_unchecked); |  | 
| 971   { |  | 
| 972     // Simplified version of the runtime call where the types of the arguments |  | 
| 973     // are already known due to type checks in this stub. |  | 
| 974     Comment("Call Runtime Unchecked"); |  | 
| 975     Node* result = CallRuntime(Runtime::kStringIndexOfUnchecked, SmiConstant(0), |  | 
| 976                                receiver, search_string, position); |  | 
| 977     f_return(result); |  | 
| 978   } |  | 
| 979 } |  | 
| 980 |  | 
| 981 // ES6 String.prototype.indexOf(searchString [, position]) |  | 
| 982 // #sec-string.prototype.indexof |  | 
| 983 // Unchecked helper for builtins lowering. |  | 
| 984 TF_BUILTIN(StringIndexOf, StringBuiltinsAssembler) { |  | 
| 985   Node* receiver = Parameter(0); |  | 
| 986   Node* search_string = Parameter(1); |  | 
| 987   Node* position = Parameter(2); |  | 
| 988 |  | 
| 989   Node* instance_type = LoadInstanceType(receiver); |  | 
| 990   Node* search_string_instance_type = LoadInstanceType(search_string); |  | 
| 991 |  | 
| 992   StringIndexOf(receiver, instance_type, search_string, |  | 
| 993                 search_string_instance_type, position, |  | 
| 994                 [this](Node* result) { this->Return(result); }); |  | 
| 995 } |  | 
| 996 |  | 
| 997 // ES6 String.prototype.indexOf(searchString [, position]) |  | 
| 998 // #sec-string.prototype.indexof |  | 
| 999 TF_BUILTIN(StringPrototypeIndexOf, StringBuiltinsAssembler) { |  | 
| 1000   Variable search_string(this, MachineRepresentation::kTagged), |  | 
| 1001       position(this, MachineRepresentation::kTagged); |  | 
| 1002   Label call_runtime(this), call_runtime_unchecked(this), argc_0(this), |  | 
| 1003       no_argc_0(this), argc_1(this), no_argc_1(this), argc_2(this), |  | 
| 1004       fast_path(this), return_minus_1(this); |  | 
| 1005 |  | 
| 1006   Node* argc = Parameter(BuiltinDescriptor::kArgumentsCount); |  | 
| 1007   Node* context = Parameter(BuiltinDescriptor::kContext); |  | 
| 1008 |  | 
| 1009   CodeStubArguments arguments(this, ChangeInt32ToIntPtr(argc)); |  | 
| 1010   Node* receiver = arguments.GetReceiver(); |  | 
| 1011   // From now on use word-size argc value. |  | 
| 1012   argc = arguments.GetLength(); |  | 
| 1013 |  | 
| 1014   GotoIf(IntPtrEqual(argc, IntPtrConstant(0)), &argc_0); |  | 
| 1015   GotoIf(IntPtrEqual(argc, IntPtrConstant(1)), &argc_1); |  | 
| 1016   Goto(&argc_2); |  | 
| 1017   Bind(&argc_0); |  | 
| 1018   { |  | 
| 1019     Comment("0 Argument case"); |  | 
| 1020     Node* undefined = UndefinedConstant(); |  | 
| 1021     search_string.Bind(undefined); |  | 
| 1022     position.Bind(undefined); |  | 
| 1023     Goto(&call_runtime); |  | 
| 1024   } |  | 
| 1025   Bind(&argc_1); |  | 
| 1026   { |  | 
| 1027     Comment("1 Argument case"); |  | 
| 1028     search_string.Bind(arguments.AtIndex(0)); |  | 
| 1029     position.Bind(SmiConstant(0)); |  | 
| 1030     Goto(&fast_path); |  | 
| 1031   } |  | 
| 1032   Bind(&argc_2); |  | 
| 1033   { |  | 
| 1034     Comment("2 Argument case"); |  | 
| 1035     search_string.Bind(arguments.AtIndex(0)); |  | 
| 1036     position.Bind(arguments.AtIndex(1)); |  | 
| 1037     GotoIfNot(TaggedIsSmi(position.value()), &call_runtime); |  | 
| 1038     Goto(&fast_path); |  | 
| 1039   } |  | 
| 1040 |  | 
| 1041   Bind(&fast_path); |  | 
| 1042   { |  | 
| 1043     Comment("Fast Path"); |  | 
| 1044     GotoIf(TaggedIsSmi(receiver), &call_runtime); |  | 
| 1045     Node* needle = search_string.value(); |  | 
| 1046     GotoIf(TaggedIsSmi(needle), &call_runtime); |  | 
| 1047 |  | 
| 1048     Node* instance_type = LoadInstanceType(receiver); |  | 
| 1049     GotoIfNot(IsStringInstanceType(instance_type), &call_runtime); |  | 
| 1050 |  | 
| 1051     Node* needle_instance_type = LoadInstanceType(needle); |  | 
| 1052     GotoIfNot(IsStringInstanceType(needle_instance_type), &call_runtime); |  | 
| 1053 |  | 
| 1054     StringIndexOf( |  | 
| 1055         receiver, instance_type, needle, needle_instance_type, position.value(), |  | 
| 1056         [&arguments](Node* result) { arguments.PopAndReturn(result); }); |  | 
| 1057   } |  | 
| 1058 |  | 
| 1059   Bind(&call_runtime); |  | 
| 1060   { |  | 
| 1061     Comment("Call Runtime"); |  | 
| 1062     Node* result = CallRuntime(Runtime::kStringIndexOf, context, receiver, |  | 
| 1063                                search_string.value(), position.value()); |  | 
| 1064     arguments.PopAndReturn(result); |  | 
| 1065   } |  | 
| 1066 } |  | 
| 1067 |  | 
| 1068 // ES6 section 21.1.3.9 | 207 // ES6 section 21.1.3.9 | 
| 1069 // String.prototype.lastIndexOf ( searchString [ , position ] ) | 208 // String.prototype.lastIndexOf ( searchString [ , position ] ) | 
| 1070 BUILTIN(StringPrototypeLastIndexOf) { | 209 BUILTIN(StringPrototypeLastIndexOf) { | 
| 1071   HandleScope handle_scope(isolate); | 210   HandleScope handle_scope(isolate); | 
| 1072   return String::LastIndexOf(isolate, args.receiver(), | 211   return String::LastIndexOf(isolate, args.receiver(), | 
| 1073                              args.atOrUndefined(isolate, 1), | 212                              args.atOrUndefined(isolate, 1), | 
| 1074                              args.atOrUndefined(isolate, 2)); | 213                              args.atOrUndefined(isolate, 2)); | 
| 1075 } | 214 } | 
| 1076 | 215 | 
| 1077 // ES6 section 21.1.3.10 String.prototype.localeCompare ( that ) | 216 // ES6 section 21.1.3.10 String.prototype.localeCompare ( that ) | 
| (...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 1152     Handle<String> valid_forms = | 291     Handle<String> valid_forms = | 
| 1153         isolate->factory()->NewStringFromStaticChars("NFC, NFD, NFKC, NFKD"); | 292         isolate->factory()->NewStringFromStaticChars("NFC, NFD, NFKC, NFKD"); | 
| 1154     THROW_NEW_ERROR_RETURN_FAILURE( | 293     THROW_NEW_ERROR_RETURN_FAILURE( | 
| 1155         isolate, | 294         isolate, | 
| 1156         NewRangeError(MessageTemplate::kNormalizationForm, valid_forms)); | 295         NewRangeError(MessageTemplate::kNormalizationForm, valid_forms)); | 
| 1157   } | 296   } | 
| 1158 | 297 | 
| 1159   return *string; | 298   return *string; | 
| 1160 } | 299 } | 
| 1161 | 300 | 
| 1162 compiler::Node* StringBuiltinsAssembler::IsNullOrUndefined(Node* const value) { |  | 
| 1163   return Word32Or(IsUndefined(value), IsNull(value)); |  | 
| 1164 } |  | 
| 1165 |  | 
| 1166 void StringBuiltinsAssembler::RequireObjectCoercible(Node* const context, |  | 
| 1167                                                      Node* const value, |  | 
| 1168                                                      const char* method_name) { |  | 
| 1169   Label out(this), throw_exception(this, Label::kDeferred); |  | 
| 1170   Branch(IsNullOrUndefined(value), &throw_exception, &out); |  | 
| 1171 |  | 
| 1172   Bind(&throw_exception); |  | 
| 1173   TailCallRuntime( |  | 
| 1174       Runtime::kThrowCalledOnNullOrUndefined, context, |  | 
| 1175       HeapConstant(factory()->NewStringFromAsciiChecked(method_name, TENURED))); |  | 
| 1176 |  | 
| 1177   Bind(&out); |  | 
| 1178 } |  | 
| 1179 |  | 
| 1180 void StringBuiltinsAssembler::MaybeCallFunctionAtSymbol( |  | 
| 1181     Node* const context, Node* const object, Handle<Symbol> symbol, |  | 
| 1182     const NodeFunction0& regexp_call, const NodeFunction1& generic_call) { |  | 
| 1183   Label out(this); |  | 
| 1184 |  | 
| 1185   // Smis definitely don't have an attached symbol. |  | 
| 1186   GotoIf(TaggedIsSmi(object), &out); |  | 
| 1187 |  | 
| 1188   Node* const object_map = LoadMap(object); |  | 
| 1189 |  | 
| 1190   // Skip the slow lookup for Strings. |  | 
| 1191   { |  | 
| 1192     Label next(this); |  | 
| 1193 |  | 
| 1194     GotoIfNot(IsStringInstanceType(LoadMapInstanceType(object_map)), &next); |  | 
| 1195 |  | 
| 1196     Node* const native_context = LoadNativeContext(context); |  | 
| 1197     Node* const initial_proto_initial_map = LoadContextElement( |  | 
| 1198         native_context, Context::STRING_FUNCTION_PROTOTYPE_MAP_INDEX); |  | 
| 1199 |  | 
| 1200     Node* const string_fun = |  | 
| 1201         LoadContextElement(native_context, Context::STRING_FUNCTION_INDEX); |  | 
| 1202     Node* const initial_map = |  | 
| 1203         LoadObjectField(string_fun, JSFunction::kPrototypeOrInitialMapOffset); |  | 
| 1204     Node* const proto_map = LoadMap(LoadMapPrototype(initial_map)); |  | 
| 1205 |  | 
| 1206     Branch(WordEqual(proto_map, initial_proto_initial_map), &out, &next); |  | 
| 1207 |  | 
| 1208     Bind(&next); |  | 
| 1209   } |  | 
| 1210 |  | 
| 1211   // Take the fast path for RegExps. |  | 
| 1212   { |  | 
| 1213     Label stub_call(this), slow_lookup(this); |  | 
| 1214 |  | 
| 1215     RegExpBuiltinsAssembler regexp_asm(state()); |  | 
| 1216     regexp_asm.BranchIfFastRegExp(context, object_map, &stub_call, |  | 
| 1217                                   &slow_lookup); |  | 
| 1218 |  | 
| 1219     Bind(&stub_call); |  | 
| 1220     Return(regexp_call()); |  | 
| 1221 |  | 
| 1222     Bind(&slow_lookup); |  | 
| 1223   } |  | 
| 1224 |  | 
| 1225   GotoIf(IsNullOrUndefined(object), &out); |  | 
| 1226 |  | 
| 1227   // Fall back to a slow lookup of {object[symbol]}. |  | 
| 1228 |  | 
| 1229   Node* const maybe_func = GetProperty(context, object, symbol); |  | 
| 1230   GotoIf(IsUndefined(maybe_func), &out); |  | 
| 1231 |  | 
| 1232   // Attempt to call the function. |  | 
| 1233 |  | 
| 1234   Node* const result = generic_call(maybe_func); |  | 
| 1235   Return(result); |  | 
| 1236 |  | 
| 1237   Bind(&out); |  | 
| 1238 } |  | 
| 1239 |  | 
| 1240 // ES6 section 21.1.3.16 String.prototype.replace ( search, replace ) |  | 
| 1241 TF_BUILTIN(StringPrototypeReplace, StringBuiltinsAssembler) { |  | 
| 1242   Label out(this); |  | 
| 1243 |  | 
| 1244   Node* const receiver = Parameter(0); |  | 
| 1245   Node* const search = Parameter(1); |  | 
| 1246   Node* const replace = Parameter(2); |  | 
| 1247   Node* const context = Parameter(5); |  | 
| 1248 |  | 
| 1249   Node* const smi_zero = SmiConstant(0); |  | 
| 1250 |  | 
| 1251   RequireObjectCoercible(context, receiver, "String.prototype.replace"); |  | 
| 1252 |  | 
| 1253   // Redirect to replacer method if {search[@@replace]} is not undefined. |  | 
| 1254 |  | 
| 1255   MaybeCallFunctionAtSymbol( |  | 
| 1256       context, search, isolate()->factory()->replace_symbol(), |  | 
| 1257       [=]() { |  | 
| 1258         Callable tostring_callable = CodeFactory::ToString(isolate()); |  | 
| 1259         Node* const subject_string = |  | 
| 1260             CallStub(tostring_callable, context, receiver); |  | 
| 1261 |  | 
| 1262         Callable replace_callable = CodeFactory::RegExpReplace(isolate()); |  | 
| 1263         return CallStub(replace_callable, context, search, subject_string, |  | 
| 1264                         replace); |  | 
| 1265       }, |  | 
| 1266       [=](Node* fn) { |  | 
| 1267         Callable call_callable = CodeFactory::Call(isolate()); |  | 
| 1268         return CallJS(call_callable, context, fn, search, receiver, replace); |  | 
| 1269       }); |  | 
| 1270 |  | 
| 1271   // Convert {receiver} and {search} to strings. |  | 
| 1272 |  | 
| 1273   Callable tostring_callable = CodeFactory::ToString(isolate()); |  | 
| 1274   Callable indexof_callable = CodeFactory::StringIndexOf(isolate()); |  | 
| 1275 |  | 
| 1276   Node* const subject_string = CallStub(tostring_callable, context, receiver); |  | 
| 1277   Node* const search_string = CallStub(tostring_callable, context, search); |  | 
| 1278 |  | 
| 1279   Node* const subject_length = LoadStringLength(subject_string); |  | 
| 1280   Node* const search_length = LoadStringLength(search_string); |  | 
| 1281 |  | 
| 1282   // Fast-path single-char {search}, long {receiver}, and simple string |  | 
| 1283   // {replace}. |  | 
| 1284   { |  | 
| 1285     Label next(this); |  | 
| 1286 |  | 
| 1287     GotoIfNot(SmiEqual(search_length, SmiConstant(1)), &next); |  | 
| 1288     GotoIfNot(SmiGreaterThan(subject_length, SmiConstant(0xFF)), &next); |  | 
| 1289     GotoIf(TaggedIsSmi(replace), &next); |  | 
| 1290     GotoIfNot(IsString(replace), &next); |  | 
| 1291 |  | 
| 1292     Node* const dollar_string = HeapConstant( |  | 
| 1293         isolate()->factory()->LookupSingleCharacterStringFromCode('$')); |  | 
| 1294     Node* const dollar_ix = |  | 
| 1295         CallStub(indexof_callable, context, replace, dollar_string, smi_zero); |  | 
| 1296     GotoIfNot(SmiIsNegative(dollar_ix), &next); |  | 
| 1297 |  | 
| 1298     // Searching by traversing a cons string tree and replace with cons of |  | 
| 1299     // slices works only when the replaced string is a single character, being |  | 
| 1300     // replaced by a simple string and only pays off for long strings. |  | 
| 1301     // TODO(jgruber): Reevaluate if this is still beneficial. |  | 
| 1302     // TODO(jgruber): TailCallRuntime when it correctly handles adapter frames. |  | 
| 1303     Return(CallRuntime(Runtime::kStringReplaceOneCharWithString, context, |  | 
| 1304                        subject_string, search_string, replace)); |  | 
| 1305 |  | 
| 1306     Bind(&next); |  | 
| 1307   } |  | 
| 1308 |  | 
| 1309   // TODO(jgruber): Extend StringIndexOf to handle two-byte strings and |  | 
| 1310   // longer substrings - we can handle up to 8 chars (one-byte) / 4 chars |  | 
| 1311   // (2-byte). |  | 
| 1312 |  | 
| 1313   Node* const match_start_index = CallStub( |  | 
| 1314       indexof_callable, context, subject_string, search_string, smi_zero); |  | 
| 1315   CSA_ASSERT(this, TaggedIsSmi(match_start_index)); |  | 
| 1316 |  | 
| 1317   // Early exit if no match found. |  | 
| 1318   { |  | 
| 1319     Label next(this), return_subject(this); |  | 
| 1320 |  | 
| 1321     GotoIfNot(SmiIsNegative(match_start_index), &next); |  | 
| 1322 |  | 
| 1323     // The spec requires to perform ToString(replace) if the {replace} is not |  | 
| 1324     // callable even if we are going to exit here. |  | 
| 1325     // Since ToString() being applied to Smi does not have side effects for |  | 
| 1326     // numbers we can skip it. |  | 
| 1327     GotoIf(TaggedIsSmi(replace), &return_subject); |  | 
| 1328     GotoIf(IsCallableMap(LoadMap(replace)), &return_subject); |  | 
| 1329 |  | 
| 1330     // TODO(jgruber): Could introduce ToStringSideeffectsStub which only |  | 
| 1331     // performs observable parts of ToString. |  | 
| 1332     CallStub(tostring_callable, context, replace); |  | 
| 1333     Goto(&return_subject); |  | 
| 1334 |  | 
| 1335     Bind(&return_subject); |  | 
| 1336     Return(subject_string); |  | 
| 1337 |  | 
| 1338     Bind(&next); |  | 
| 1339   } |  | 
| 1340 |  | 
| 1341   Node* const match_end_index = SmiAdd(match_start_index, search_length); |  | 
| 1342 |  | 
| 1343   Callable substring_callable = CodeFactory::SubString(isolate()); |  | 
| 1344   Callable stringadd_callable = |  | 
| 1345       CodeFactory::StringAdd(isolate(), STRING_ADD_CHECK_NONE, NOT_TENURED); |  | 
| 1346 |  | 
| 1347   Variable var_result(this, MachineRepresentation::kTagged, |  | 
| 1348                       EmptyStringConstant()); |  | 
| 1349 |  | 
| 1350   // Compute the prefix. |  | 
| 1351   { |  | 
| 1352     Label next(this); |  | 
| 1353 |  | 
| 1354     GotoIf(SmiEqual(match_start_index, smi_zero), &next); |  | 
| 1355     Node* const prefix = CallStub(substring_callable, context, subject_string, |  | 
| 1356                                   smi_zero, match_start_index); |  | 
| 1357     var_result.Bind(prefix); |  | 
| 1358 |  | 
| 1359     Goto(&next); |  | 
| 1360     Bind(&next); |  | 
| 1361   } |  | 
| 1362 |  | 
| 1363   // Compute the string to replace with. |  | 
| 1364 |  | 
| 1365   Label if_iscallablereplace(this), if_notcallablereplace(this); |  | 
| 1366   GotoIf(TaggedIsSmi(replace), &if_notcallablereplace); |  | 
| 1367   Branch(IsCallableMap(LoadMap(replace)), &if_iscallablereplace, |  | 
| 1368          &if_notcallablereplace); |  | 
| 1369 |  | 
| 1370   Bind(&if_iscallablereplace); |  | 
| 1371   { |  | 
| 1372     Callable call_callable = CodeFactory::Call(isolate()); |  | 
| 1373     Node* const replacement = |  | 
| 1374         CallJS(call_callable, context, replace, UndefinedConstant(), |  | 
| 1375                search_string, match_start_index, subject_string); |  | 
| 1376     Node* const replacement_string = |  | 
| 1377         CallStub(tostring_callable, context, replacement); |  | 
| 1378     var_result.Bind(CallStub(stringadd_callable, context, var_result.value(), |  | 
| 1379                              replacement_string)); |  | 
| 1380     Goto(&out); |  | 
| 1381   } |  | 
| 1382 |  | 
| 1383   Bind(&if_notcallablereplace); |  | 
| 1384   { |  | 
| 1385     Node* const replace_string = CallStub(tostring_callable, context, replace); |  | 
| 1386 |  | 
| 1387     // TODO(jgruber): Simplified GetSubstitution implementation in CSA. |  | 
| 1388     Node* const matched = CallStub(substring_callable, context, subject_string, |  | 
| 1389                                    match_start_index, match_end_index); |  | 
| 1390     Node* const replacement_string = |  | 
| 1391         CallRuntime(Runtime::kGetSubstitution, context, matched, subject_string, |  | 
| 1392                     match_start_index, replace_string); |  | 
| 1393     var_result.Bind(CallStub(stringadd_callable, context, var_result.value(), |  | 
| 1394                              replacement_string)); |  | 
| 1395     Goto(&out); |  | 
| 1396   } |  | 
| 1397 |  | 
| 1398   Bind(&out); |  | 
| 1399   { |  | 
| 1400     Node* const suffix = CallStub(substring_callable, context, subject_string, |  | 
| 1401                                   match_end_index, subject_length); |  | 
| 1402     Node* const result = |  | 
| 1403         CallStub(stringadd_callable, context, var_result.value(), suffix); |  | 
| 1404     Return(result); |  | 
| 1405   } |  | 
| 1406 } |  | 
| 1407 |  | 
| 1408 // ES6 section 21.1.3.19 String.prototype.split ( separator, limit ) |  | 
| 1409 TF_BUILTIN(StringPrototypeSplit, StringBuiltinsAssembler) { |  | 
| 1410   Label out(this); |  | 
| 1411 |  | 
| 1412   Node* const receiver = Parameter(0); |  | 
| 1413   Node* const separator = Parameter(1); |  | 
| 1414   Node* const limit = Parameter(2); |  | 
| 1415   Node* const context = Parameter(5); |  | 
| 1416 |  | 
| 1417   Node* const smi_zero = SmiConstant(0); |  | 
| 1418 |  | 
| 1419   RequireObjectCoercible(context, receiver, "String.prototype.split"); |  | 
| 1420 |  | 
| 1421   // Redirect to splitter method if {separator[@@split]} is not undefined. |  | 
| 1422 |  | 
| 1423   MaybeCallFunctionAtSymbol( |  | 
| 1424       context, separator, isolate()->factory()->split_symbol(), |  | 
| 1425       [=]() { |  | 
| 1426         Callable tostring_callable = CodeFactory::ToString(isolate()); |  | 
| 1427         Node* const subject_string = |  | 
| 1428             CallStub(tostring_callable, context, receiver); |  | 
| 1429 |  | 
| 1430         Callable split_callable = CodeFactory::RegExpSplit(isolate()); |  | 
| 1431         return CallStub(split_callable, context, separator, subject_string, |  | 
| 1432                         limit); |  | 
| 1433       }, |  | 
| 1434       [=](Node* fn) { |  | 
| 1435         Callable call_callable = CodeFactory::Call(isolate()); |  | 
| 1436         return CallJS(call_callable, context, fn, separator, receiver, limit); |  | 
| 1437       }); |  | 
| 1438 |  | 
| 1439   // String and integer conversions. |  | 
| 1440   // TODO(jgruber): The old implementation used Uint32Max instead of SmiMax - |  | 
| 1441   // but AFAIK there should not be a difference since arrays are capped at Smi |  | 
| 1442   // lengths. |  | 
| 1443 |  | 
| 1444   Callable tostring_callable = CodeFactory::ToString(isolate()); |  | 
| 1445   Node* const subject_string = CallStub(tostring_callable, context, receiver); |  | 
| 1446   Node* const limit_number = |  | 
| 1447       Select(IsUndefined(limit), [=]() { return SmiConstant(Smi::kMaxValue); }, |  | 
| 1448              [=]() { return ToUint32(context, limit); }, |  | 
| 1449              MachineRepresentation::kTagged); |  | 
| 1450   Node* const separator_string = |  | 
| 1451       CallStub(tostring_callable, context, separator); |  | 
| 1452 |  | 
| 1453   // Shortcut for {limit} == 0. |  | 
| 1454   { |  | 
| 1455     Label next(this); |  | 
| 1456     GotoIfNot(SmiEqual(limit_number, smi_zero), &next); |  | 
| 1457 |  | 
| 1458     const ElementsKind kind = FAST_ELEMENTS; |  | 
| 1459     Node* const native_context = LoadNativeContext(context); |  | 
| 1460     Node* const array_map = LoadJSArrayElementsMap(kind, native_context); |  | 
| 1461 |  | 
| 1462     Node* const length = smi_zero; |  | 
| 1463     Node* const capacity = IntPtrConstant(0); |  | 
| 1464     Node* const result = AllocateJSArray(kind, array_map, capacity, length); |  | 
| 1465 |  | 
| 1466     Return(result); |  | 
| 1467 |  | 
| 1468     Bind(&next); |  | 
| 1469   } |  | 
| 1470 |  | 
| 1471   // ECMA-262 says that if {separator} is undefined, the result should |  | 
| 1472   // be an array of size 1 containing the entire string. |  | 
| 1473   { |  | 
| 1474     Label next(this); |  | 
| 1475     GotoIfNot(IsUndefined(separator), &next); |  | 
| 1476 |  | 
| 1477     const ElementsKind kind = FAST_ELEMENTS; |  | 
| 1478     Node* const native_context = LoadNativeContext(context); |  | 
| 1479     Node* const array_map = LoadJSArrayElementsMap(kind, native_context); |  | 
| 1480 |  | 
| 1481     Node* const length = SmiConstant(1); |  | 
| 1482     Node* const capacity = IntPtrConstant(1); |  | 
| 1483     Node* const result = AllocateJSArray(kind, array_map, capacity, length); |  | 
| 1484 |  | 
| 1485     Node* const fixed_array = LoadElements(result); |  | 
| 1486     StoreFixedArrayElement(fixed_array, 0, subject_string); |  | 
| 1487 |  | 
| 1488     Return(result); |  | 
| 1489 |  | 
| 1490     Bind(&next); |  | 
| 1491   } |  | 
| 1492 |  | 
| 1493   // If the separator string is empty then return the elements in the subject. |  | 
| 1494   { |  | 
| 1495     Label next(this); |  | 
| 1496     GotoIfNot(SmiEqual(LoadStringLength(separator_string), smi_zero), &next); |  | 
| 1497 |  | 
| 1498     Node* const result = CallRuntime(Runtime::kStringToArray, context, |  | 
| 1499                                      subject_string, limit_number); |  | 
| 1500     Return(result); |  | 
| 1501 |  | 
| 1502     Bind(&next); |  | 
| 1503   } |  | 
| 1504 |  | 
| 1505   Node* const result = |  | 
| 1506       CallRuntime(Runtime::kStringSplit, context, subject_string, |  | 
| 1507                   separator_string, limit_number); |  | 
| 1508   Return(result); |  | 
| 1509 } |  | 
| 1510 |  | 
| 1511 // ES6 section B.2.3.1 String.prototype.substr ( start, length ) |  | 
| 1512 TF_BUILTIN(StringPrototypeSubstr, CodeStubAssembler) { |  | 
| 1513   Label out(this), handle_length(this); |  | 
| 1514 |  | 
| 1515   Variable var_start(this, MachineRepresentation::kTagged); |  | 
| 1516   Variable var_length(this, MachineRepresentation::kTagged); |  | 
| 1517 |  | 
| 1518   Node* const receiver = Parameter(0); |  | 
| 1519   Node* const start = Parameter(1); |  | 
| 1520   Node* const length = Parameter(2); |  | 
| 1521   Node* const context = Parameter(5); |  | 
| 1522 |  | 
| 1523   Node* const zero = SmiConstant(Smi::kZero); |  | 
| 1524 |  | 
| 1525   // Check that {receiver} is coercible to Object and convert it to a String. |  | 
| 1526   Node* const string = |  | 
| 1527       ToThisString(context, receiver, "String.prototype.substr"); |  | 
| 1528 |  | 
| 1529   Node* const string_length = LoadStringLength(string); |  | 
| 1530 |  | 
| 1531   // Conversions and bounds-checks for {start}. |  | 
| 1532   { |  | 
| 1533     Node* const start_int = |  | 
| 1534         ToInteger(context, start, CodeStubAssembler::kTruncateMinusZero); |  | 
| 1535 |  | 
| 1536     Label if_issmi(this), if_isheapnumber(this, Label::kDeferred); |  | 
| 1537     Branch(TaggedIsSmi(start_int), &if_issmi, &if_isheapnumber); |  | 
| 1538 |  | 
| 1539     Bind(&if_issmi); |  | 
| 1540     { |  | 
| 1541       Node* const length_plus_start = SmiAdd(string_length, start_int); |  | 
| 1542       var_start.Bind(Select(SmiLessThan(start_int, zero), |  | 
| 1543                             [&] { return SmiMax(length_plus_start, zero); }, |  | 
| 1544                             [&] { return start_int; }, |  | 
| 1545                             MachineRepresentation::kTagged)); |  | 
| 1546       Goto(&handle_length); |  | 
| 1547     } |  | 
| 1548 |  | 
| 1549     Bind(&if_isheapnumber); |  | 
| 1550     { |  | 
| 1551       // If {start} is a heap number, it is definitely out of bounds. If it is |  | 
| 1552       // negative, {start} = max({string_length} + {start}),0) = 0'. If it is |  | 
| 1553       // positive, set {start} to {string_length} which ultimately results in |  | 
| 1554       // returning an empty string. |  | 
| 1555       Node* const float_zero = Float64Constant(0.); |  | 
| 1556       Node* const start_float = LoadHeapNumberValue(start_int); |  | 
| 1557       var_start.Bind(SelectTaggedConstant( |  | 
| 1558           Float64LessThan(start_float, float_zero), zero, string_length)); |  | 
| 1559       Goto(&handle_length); |  | 
| 1560     } |  | 
| 1561   } |  | 
| 1562 |  | 
| 1563   // Conversions and bounds-checks for {length}. |  | 
| 1564   Bind(&handle_length); |  | 
| 1565   { |  | 
| 1566     Label if_issmi(this), if_isheapnumber(this, Label::kDeferred); |  | 
| 1567 |  | 
| 1568     // Default to {string_length} if {length} is undefined. |  | 
| 1569     { |  | 
| 1570       Label if_isundefined(this, Label::kDeferred), if_isnotundefined(this); |  | 
| 1571       Branch(WordEqual(length, UndefinedConstant()), &if_isundefined, |  | 
| 1572              &if_isnotundefined); |  | 
| 1573 |  | 
| 1574       Bind(&if_isundefined); |  | 
| 1575       var_length.Bind(string_length); |  | 
| 1576       Goto(&if_issmi); |  | 
| 1577 |  | 
| 1578       Bind(&if_isnotundefined); |  | 
| 1579       var_length.Bind( |  | 
| 1580           ToInteger(context, length, CodeStubAssembler::kTruncateMinusZero)); |  | 
| 1581     } |  | 
| 1582 |  | 
| 1583     Branch(TaggedIsSmi(var_length.value()), &if_issmi, &if_isheapnumber); |  | 
| 1584 |  | 
| 1585     // Set {length} to min(max({length}, 0), {string_length} - {start} |  | 
| 1586     Bind(&if_issmi); |  | 
| 1587     { |  | 
| 1588       Node* const positive_length = SmiMax(var_length.value(), zero); |  | 
| 1589 |  | 
| 1590       Node* const minimal_length = SmiSub(string_length, var_start.value()); |  | 
| 1591       var_length.Bind(SmiMin(positive_length, minimal_length)); |  | 
| 1592 |  | 
| 1593       GotoIfNot(SmiLessThanOrEqual(var_length.value(), zero), &out); |  | 
| 1594       Return(EmptyStringConstant()); |  | 
| 1595     } |  | 
| 1596 |  | 
| 1597     Bind(&if_isheapnumber); |  | 
| 1598     { |  | 
| 1599       // If {length} is a heap number, it is definitely out of bounds. There are |  | 
| 1600       // two cases according to the spec: if it is negative, "" is returned; if |  | 
| 1601       // it is positive, then length is set to {string_length} - {start}. |  | 
| 1602 |  | 
| 1603       CSA_ASSERT(this, IsHeapNumberMap(LoadMap(var_length.value()))); |  | 
| 1604 |  | 
| 1605       Label if_isnegative(this), if_ispositive(this); |  | 
| 1606       Node* const float_zero = Float64Constant(0.); |  | 
| 1607       Node* const length_float = LoadHeapNumberValue(var_length.value()); |  | 
| 1608       Branch(Float64LessThan(length_float, float_zero), &if_isnegative, |  | 
| 1609              &if_ispositive); |  | 
| 1610 |  | 
| 1611       Bind(&if_isnegative); |  | 
| 1612       Return(EmptyStringConstant()); |  | 
| 1613 |  | 
| 1614       Bind(&if_ispositive); |  | 
| 1615       { |  | 
| 1616         var_length.Bind(SmiSub(string_length, var_start.value())); |  | 
| 1617         GotoIfNot(SmiLessThanOrEqual(var_length.value(), zero), &out); |  | 
| 1618         Return(EmptyStringConstant()); |  | 
| 1619       } |  | 
| 1620     } |  | 
| 1621   } |  | 
| 1622 |  | 
| 1623   Bind(&out); |  | 
| 1624   { |  | 
| 1625     Node* const end = SmiAdd(var_start.value(), var_length.value()); |  | 
| 1626     Node* const result = SubString(context, string, var_start.value(), end); |  | 
| 1627     Return(result); |  | 
| 1628   } |  | 
| 1629 } |  | 
| 1630 |  | 
| 1631 compiler::Node* StringBuiltinsAssembler::ToSmiBetweenZeroAnd(Node* context, |  | 
| 1632                                                              Node* value, |  | 
| 1633                                                              Node* limit) { |  | 
| 1634   Label out(this); |  | 
| 1635   Variable var_result(this, MachineRepresentation::kTagged); |  | 
| 1636 |  | 
| 1637   Node* const value_int = |  | 
| 1638       this->ToInteger(context, value, CodeStubAssembler::kTruncateMinusZero); |  | 
| 1639 |  | 
| 1640   Label if_issmi(this), if_isnotsmi(this, Label::kDeferred); |  | 
| 1641   Branch(TaggedIsSmi(value_int), &if_issmi, &if_isnotsmi); |  | 
| 1642 |  | 
| 1643   Bind(&if_issmi); |  | 
| 1644   { |  | 
| 1645     Label if_isinbounds(this), if_isoutofbounds(this, Label::kDeferred); |  | 
| 1646     Branch(SmiAbove(value_int, limit), &if_isoutofbounds, &if_isinbounds); |  | 
| 1647 |  | 
| 1648     Bind(&if_isinbounds); |  | 
| 1649     { |  | 
| 1650       var_result.Bind(value_int); |  | 
| 1651       Goto(&out); |  | 
| 1652     } |  | 
| 1653 |  | 
| 1654     Bind(&if_isoutofbounds); |  | 
| 1655     { |  | 
| 1656       Node* const zero = SmiConstant(Smi::kZero); |  | 
| 1657       var_result.Bind( |  | 
| 1658           SelectTaggedConstant(SmiLessThan(value_int, zero), zero, limit)); |  | 
| 1659       Goto(&out); |  | 
| 1660     } |  | 
| 1661   } |  | 
| 1662 |  | 
| 1663   Bind(&if_isnotsmi); |  | 
| 1664   { |  | 
| 1665     // {value} is a heap number - in this case, it is definitely out of bounds. |  | 
| 1666     CSA_ASSERT(this, IsHeapNumberMap(LoadMap(value_int))); |  | 
| 1667 |  | 
| 1668     Node* const float_zero = Float64Constant(0.); |  | 
| 1669     Node* const smi_zero = SmiConstant(Smi::kZero); |  | 
| 1670     Node* const value_float = LoadHeapNumberValue(value_int); |  | 
| 1671     var_result.Bind(SelectTaggedConstant( |  | 
| 1672         Float64LessThan(value_float, float_zero), smi_zero, limit)); |  | 
| 1673     Goto(&out); |  | 
| 1674   } |  | 
| 1675 |  | 
| 1676   Bind(&out); |  | 
| 1677   return var_result.value(); |  | 
| 1678 } |  | 
| 1679 |  | 
| 1680 // ES6 section 21.1.3.19 String.prototype.substring ( start, end ) |  | 
| 1681 TF_BUILTIN(StringPrototypeSubstring, StringBuiltinsAssembler) { |  | 
| 1682   Label out(this); |  | 
| 1683 |  | 
| 1684   Variable var_start(this, MachineRepresentation::kTagged); |  | 
| 1685   Variable var_end(this, MachineRepresentation::kTagged); |  | 
| 1686 |  | 
| 1687   Node* const receiver = Parameter(0); |  | 
| 1688   Node* const start = Parameter(1); |  | 
| 1689   Node* const end = Parameter(2); |  | 
| 1690   Node* const context = Parameter(5); |  | 
| 1691 |  | 
| 1692   // Check that {receiver} is coercible to Object and convert it to a String. |  | 
| 1693   Node* const string = |  | 
| 1694       ToThisString(context, receiver, "String.prototype.substring"); |  | 
| 1695 |  | 
| 1696   Node* const length = LoadStringLength(string); |  | 
| 1697 |  | 
| 1698   // Conversion and bounds-checks for {start}. |  | 
| 1699   var_start.Bind(ToSmiBetweenZeroAnd(context, start, length)); |  | 
| 1700 |  | 
| 1701   // Conversion and bounds-checks for {end}. |  | 
| 1702   { |  | 
| 1703     var_end.Bind(length); |  | 
| 1704     GotoIf(WordEqual(end, UndefinedConstant()), &out); |  | 
| 1705 |  | 
| 1706     var_end.Bind(ToSmiBetweenZeroAnd(context, end, length)); |  | 
| 1707 |  | 
| 1708     Label if_endislessthanstart(this); |  | 
| 1709     Branch(SmiLessThan(var_end.value(), var_start.value()), |  | 
| 1710            &if_endislessthanstart, &out); |  | 
| 1711 |  | 
| 1712     Bind(&if_endislessthanstart); |  | 
| 1713     { |  | 
| 1714       Node* const tmp = var_end.value(); |  | 
| 1715       var_end.Bind(var_start.value()); |  | 
| 1716       var_start.Bind(tmp); |  | 
| 1717       Goto(&out); |  | 
| 1718     } |  | 
| 1719   } |  | 
| 1720 |  | 
| 1721   Bind(&out); |  | 
| 1722   { |  | 
| 1723     Node* result = |  | 
| 1724         SubString(context, string, var_start.value(), var_end.value()); |  | 
| 1725     Return(result); |  | 
| 1726   } |  | 
| 1727 } |  | 
| 1728 |  | 
| 1729 BUILTIN(StringPrototypeStartsWith) { | 301 BUILTIN(StringPrototypeStartsWith) { | 
| 1730   HandleScope handle_scope(isolate); | 302   HandleScope handle_scope(isolate); | 
| 1731   TO_THIS_STRING(str, "String.prototype.startsWith"); | 303   TO_THIS_STRING(str, "String.prototype.startsWith"); | 
| 1732 | 304 | 
| 1733   // Check if the search string is a regExp and fail if it is. | 305   // Check if the search string is a regExp and fail if it is. | 
| 1734   Handle<Object> search = args.atOrUndefined(isolate, 1); | 306   Handle<Object> search = args.atOrUndefined(isolate, 1); | 
| 1735   Maybe<bool> is_reg_exp = RegExpUtils::IsRegExp(isolate, search); | 307   Maybe<bool> is_reg_exp = RegExpUtils::IsRegExp(isolate, search); | 
| 1736   if (is_reg_exp.IsNothing()) { | 308   if (is_reg_exp.IsNothing()) { | 
| 1737     DCHECK(isolate->has_pending_exception()); | 309     DCHECK(isolate->has_pending_exception()); | 
| 1738     return isolate->heap()->exception(); | 310     return isolate->heap()->exception(); | 
| (...skipping 27 matching lines...) Expand all  Loading... | 
| 1766   FlatStringReader search_reader(isolate, String::Flatten(search_string)); | 338   FlatStringReader search_reader(isolate, String::Flatten(search_string)); | 
| 1767 | 339 | 
| 1768   for (int i = 0; i < search_string->length(); i++) { | 340   for (int i = 0; i < search_string->length(); i++) { | 
| 1769     if (str_reader.Get(start + i) != search_reader.Get(i)) { | 341     if (str_reader.Get(start + i) != search_reader.Get(i)) { | 
| 1770       return isolate->heap()->false_value(); | 342       return isolate->heap()->false_value(); | 
| 1771     } | 343     } | 
| 1772   } | 344   } | 
| 1773   return isolate->heap()->true_value(); | 345   return isolate->heap()->true_value(); | 
| 1774 } | 346 } | 
| 1775 | 347 | 
| 1776 // ES6 section 21.1.3.25 String.prototype.toString () |  | 
| 1777 TF_BUILTIN(StringPrototypeToString, CodeStubAssembler) { |  | 
| 1778   Node* receiver = Parameter(0); |  | 
| 1779   Node* context = Parameter(3); |  | 
| 1780 |  | 
| 1781   Node* result = ToThisValue(context, receiver, PrimitiveType::kString, |  | 
| 1782                              "String.prototype.toString"); |  | 
| 1783   Return(result); |  | 
| 1784 } |  | 
| 1785 |  | 
| 1786 // ES6 section 21.1.3.27 String.prototype.trim () | 348 // ES6 section 21.1.3.27 String.prototype.trim () | 
| 1787 BUILTIN(StringPrototypeTrim) { | 349 BUILTIN(StringPrototypeTrim) { | 
| 1788   HandleScope scope(isolate); | 350   HandleScope scope(isolate); | 
| 1789   TO_THIS_STRING(string, "String.prototype.trim"); | 351   TO_THIS_STRING(string, "String.prototype.trim"); | 
| 1790   return *String::Trim(string, String::kTrim); | 352   return *String::Trim(string, String::kTrim); | 
| 1791 } | 353 } | 
| 1792 | 354 | 
| 1793 // Non-standard WebKit extension | 355 // Non-standard WebKit extension | 
| 1794 BUILTIN(StringPrototypeTrimLeft) { | 356 BUILTIN(StringPrototypeTrimLeft) { | 
| 1795   HandleScope scope(isolate); | 357   HandleScope scope(isolate); | 
| 1796   TO_THIS_STRING(string, "String.prototype.trimLeft"); | 358   TO_THIS_STRING(string, "String.prototype.trimLeft"); | 
| 1797   return *String::Trim(string, String::kTrimLeft); | 359   return *String::Trim(string, String::kTrimLeft); | 
| 1798 } | 360 } | 
| 1799 | 361 | 
| 1800 // Non-standard WebKit extension | 362 // Non-standard WebKit extension | 
| 1801 BUILTIN(StringPrototypeTrimRight) { | 363 BUILTIN(StringPrototypeTrimRight) { | 
| 1802   HandleScope scope(isolate); | 364   HandleScope scope(isolate); | 
| 1803   TO_THIS_STRING(string, "String.prototype.trimRight"); | 365   TO_THIS_STRING(string, "String.prototype.trimRight"); | 
| 1804   return *String::Trim(string, String::kTrimRight); | 366   return *String::Trim(string, String::kTrimRight); | 
| 1805 } | 367 } | 
| 1806 | 368 | 
| 1807 // ES6 section 21.1.3.28 String.prototype.valueOf ( ) |  | 
| 1808 TF_BUILTIN(StringPrototypeValueOf, CodeStubAssembler) { |  | 
| 1809   Node* receiver = Parameter(0); |  | 
| 1810   Node* context = Parameter(3); |  | 
| 1811 |  | 
| 1812   Node* result = ToThisValue(context, receiver, PrimitiveType::kString, |  | 
| 1813                              "String.prototype.valueOf"); |  | 
| 1814   Return(result); |  | 
| 1815 } |  | 
| 1816 |  | 
| 1817 TF_BUILTIN(StringPrototypeIterator, CodeStubAssembler) { |  | 
| 1818   Node* receiver = Parameter(0); |  | 
| 1819   Node* context = Parameter(3); |  | 
| 1820 |  | 
| 1821   Node* string = |  | 
| 1822       ToThisString(context, receiver, "String.prototype[Symbol.iterator]"); |  | 
| 1823 |  | 
| 1824   Node* native_context = LoadNativeContext(context); |  | 
| 1825   Node* map = |  | 
| 1826       LoadContextElement(native_context, Context::STRING_ITERATOR_MAP_INDEX); |  | 
| 1827   Node* iterator = Allocate(JSStringIterator::kSize); |  | 
| 1828   StoreMapNoWriteBarrier(iterator, map); |  | 
| 1829   StoreObjectFieldRoot(iterator, JSValue::kPropertiesOffset, |  | 
| 1830                        Heap::kEmptyFixedArrayRootIndex); |  | 
| 1831   StoreObjectFieldRoot(iterator, JSObject::kElementsOffset, |  | 
| 1832                        Heap::kEmptyFixedArrayRootIndex); |  | 
| 1833   StoreObjectFieldNoWriteBarrier(iterator, JSStringIterator::kStringOffset, |  | 
| 1834                                  string); |  | 
| 1835   Node* index = SmiConstant(Smi::kZero); |  | 
| 1836   StoreObjectFieldNoWriteBarrier(iterator, JSStringIterator::kNextIndexOffset, |  | 
| 1837                                  index); |  | 
| 1838   Return(iterator); |  | 
| 1839 } |  | 
| 1840 |  | 
| 1841 // Return the |word32| codepoint at {index}. Supports SeqStrings and |  | 
| 1842 // ExternalStrings. |  | 
| 1843 compiler::Node* StringBuiltinsAssembler::LoadSurrogatePairAt( |  | 
| 1844     compiler::Node* string, compiler::Node* length, compiler::Node* index, |  | 
| 1845     UnicodeEncoding encoding) { |  | 
| 1846   Label handle_surrogate_pair(this), return_result(this); |  | 
| 1847   Variable var_result(this, MachineRepresentation::kWord32); |  | 
| 1848   Variable var_trail(this, MachineRepresentation::kWord32); |  | 
| 1849   var_result.Bind(StringCharCodeAt(string, index)); |  | 
| 1850   var_trail.Bind(Int32Constant(0)); |  | 
| 1851 |  | 
| 1852   GotoIf(Word32NotEqual(Word32And(var_result.value(), Int32Constant(0xFC00)), |  | 
| 1853                         Int32Constant(0xD800)), |  | 
| 1854          &return_result); |  | 
| 1855   Node* next_index = SmiAdd(index, SmiConstant(Smi::FromInt(1))); |  | 
| 1856 |  | 
| 1857   GotoIfNot(SmiLessThan(next_index, length), &return_result); |  | 
| 1858   var_trail.Bind(StringCharCodeAt(string, next_index)); |  | 
| 1859   Branch(Word32Equal(Word32And(var_trail.value(), Int32Constant(0xFC00)), |  | 
| 1860                      Int32Constant(0xDC00)), |  | 
| 1861          &handle_surrogate_pair, &return_result); |  | 
| 1862 |  | 
| 1863   Bind(&handle_surrogate_pair); |  | 
| 1864   { |  | 
| 1865     Node* lead = var_result.value(); |  | 
| 1866     Node* trail = var_trail.value(); |  | 
| 1867 |  | 
| 1868     // Check that this path is only taken if a surrogate pair is found |  | 
| 1869     CSA_SLOW_ASSERT(this, |  | 
| 1870                     Uint32GreaterThanOrEqual(lead, Int32Constant(0xD800))); |  | 
| 1871     CSA_SLOW_ASSERT(this, Uint32LessThan(lead, Int32Constant(0xDC00))); |  | 
| 1872     CSA_SLOW_ASSERT(this, |  | 
| 1873                     Uint32GreaterThanOrEqual(trail, Int32Constant(0xDC00))); |  | 
| 1874     CSA_SLOW_ASSERT(this, Uint32LessThan(trail, Int32Constant(0xE000))); |  | 
| 1875 |  | 
| 1876     switch (encoding) { |  | 
| 1877       case UnicodeEncoding::UTF16: |  | 
| 1878         var_result.Bind(Word32Or( |  | 
| 1879 // Need to swap the order for big-endian platforms |  | 
| 1880 #if V8_TARGET_BIG_ENDIAN |  | 
| 1881             Word32Shl(lead, Int32Constant(16)), trail)); |  | 
| 1882 #else |  | 
| 1883             Word32Shl(trail, Int32Constant(16)), lead)); |  | 
| 1884 #endif |  | 
| 1885         break; |  | 
| 1886 |  | 
| 1887       case UnicodeEncoding::UTF32: { |  | 
| 1888         // Convert UTF16 surrogate pair into |word32| code point, encoded as |  | 
| 1889         // UTF32. |  | 
| 1890         Node* surrogate_offset = |  | 
| 1891             Int32Constant(0x10000 - (0xD800 << 10) - 0xDC00); |  | 
| 1892 |  | 
| 1893         // (lead << 10) + trail + SURROGATE_OFFSET |  | 
| 1894         var_result.Bind(Int32Add(WordShl(lead, Int32Constant(10)), |  | 
| 1895                                  Int32Add(trail, surrogate_offset))); |  | 
| 1896         break; |  | 
| 1897       } |  | 
| 1898     } |  | 
| 1899     Goto(&return_result); |  | 
| 1900   } |  | 
| 1901 |  | 
| 1902   Bind(&return_result); |  | 
| 1903   return var_result.value(); |  | 
| 1904 } |  | 
| 1905 |  | 
| 1906 TF_BUILTIN(StringIteratorPrototypeNext, StringBuiltinsAssembler) { |  | 
| 1907   Variable var_value(this, MachineRepresentation::kTagged); |  | 
| 1908   Variable var_done(this, MachineRepresentation::kTagged); |  | 
| 1909 |  | 
| 1910   var_value.Bind(UndefinedConstant()); |  | 
| 1911   var_done.Bind(BooleanConstant(true)); |  | 
| 1912 |  | 
| 1913   Label throw_bad_receiver(this), next_codepoint(this), return_result(this); |  | 
| 1914 |  | 
| 1915   Node* iterator = Parameter(0); |  | 
| 1916   Node* context = Parameter(3); |  | 
| 1917 |  | 
| 1918   GotoIf(TaggedIsSmi(iterator), &throw_bad_receiver); |  | 
| 1919   GotoIfNot(Word32Equal(LoadInstanceType(iterator), |  | 
| 1920                         Int32Constant(JS_STRING_ITERATOR_TYPE)), |  | 
| 1921             &throw_bad_receiver); |  | 
| 1922 |  | 
| 1923   Node* string = LoadObjectField(iterator, JSStringIterator::kStringOffset); |  | 
| 1924   Node* position = |  | 
| 1925       LoadObjectField(iterator, JSStringIterator::kNextIndexOffset); |  | 
| 1926   Node* length = LoadObjectField(string, String::kLengthOffset); |  | 
| 1927 |  | 
| 1928   Branch(SmiLessThan(position, length), &next_codepoint, &return_result); |  | 
| 1929 |  | 
| 1930   Bind(&next_codepoint); |  | 
| 1931   { |  | 
| 1932     UnicodeEncoding encoding = UnicodeEncoding::UTF16; |  | 
| 1933     Node* ch = LoadSurrogatePairAt(string, length, position, encoding); |  | 
| 1934     Node* value = StringFromCodePoint(ch, encoding); |  | 
| 1935     var_value.Bind(value); |  | 
| 1936     Node* length = LoadObjectField(value, String::kLengthOffset); |  | 
| 1937     StoreObjectFieldNoWriteBarrier(iterator, JSStringIterator::kNextIndexOffset, |  | 
| 1938                                    SmiAdd(position, length)); |  | 
| 1939     var_done.Bind(BooleanConstant(false)); |  | 
| 1940     Goto(&return_result); |  | 
| 1941   } |  | 
| 1942 |  | 
| 1943   Bind(&return_result); |  | 
| 1944   { |  | 
| 1945     Node* native_context = LoadNativeContext(context); |  | 
| 1946     Node* map = |  | 
| 1947         LoadContextElement(native_context, Context::ITERATOR_RESULT_MAP_INDEX); |  | 
| 1948     Node* result = Allocate(JSIteratorResult::kSize); |  | 
| 1949     StoreMapNoWriteBarrier(result, map); |  | 
| 1950     StoreObjectFieldRoot(result, JSIteratorResult::kPropertiesOffset, |  | 
| 1951                          Heap::kEmptyFixedArrayRootIndex); |  | 
| 1952     StoreObjectFieldRoot(result, JSIteratorResult::kElementsOffset, |  | 
| 1953                          Heap::kEmptyFixedArrayRootIndex); |  | 
| 1954     StoreObjectFieldNoWriteBarrier(result, JSIteratorResult::kValueOffset, |  | 
| 1955                                    var_value.value()); |  | 
| 1956     StoreObjectFieldNoWriteBarrier(result, JSIteratorResult::kDoneOffset, |  | 
| 1957                                    var_done.value()); |  | 
| 1958     Return(result); |  | 
| 1959   } |  | 
| 1960 |  | 
| 1961   Bind(&throw_bad_receiver); |  | 
| 1962   { |  | 
| 1963     // The {receiver} is not a valid JSGeneratorObject. |  | 
| 1964     CallRuntime(Runtime::kThrowIncompatibleMethodReceiver, context, |  | 
| 1965                 HeapConstant(factory()->NewStringFromAsciiChecked( |  | 
| 1966                     "String Iterator.prototype.next", TENURED)), |  | 
| 1967                 iterator); |  | 
| 1968     Unreachable(); |  | 
| 1969   } |  | 
| 1970 } |  | 
| 1971 |  | 
| 1972 namespace { | 369 namespace { | 
| 1973 | 370 | 
| 1974 inline bool ToUpperOverflows(uc32 character) { | 371 inline bool ToUpperOverflows(uc32 character) { | 
| 1975   // y with umlauts and the micro sign are the only characters that stop | 372   // y with umlauts and the micro sign are the only characters that stop | 
| 1976   // fitting into one-byte when converting to uppercase. | 373   // fitting into one-byte when converting to uppercase. | 
| 1977   static const uc32 yuml_code = 0xff; | 374   static const uc32 yuml_code = 0xff; | 
| 1978   static const uc32 micro_code = 0xb5; | 375   static const uc32 micro_code = 0xb5; | 
| 1979   return (character == yuml_code || character == micro_code); | 376   return (character == yuml_code || character == micro_code); | 
| 1980 } | 377 } | 
| 1981 | 378 | 
| (...skipping 174 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 2156 | 553 | 
| 2157 BUILTIN(StringPrototypeToUpperCase) { | 554 BUILTIN(StringPrototypeToUpperCase) { | 
| 2158   HandleScope scope(isolate); | 555   HandleScope scope(isolate); | 
| 2159   TO_THIS_STRING(string, "String.prototype.toUpperCase"); | 556   TO_THIS_STRING(string, "String.prototype.toUpperCase"); | 
| 2160   return ConvertCase(string, isolate, | 557   return ConvertCase(string, isolate, | 
| 2161                      isolate->runtime_state()->to_upper_mapping()); | 558                      isolate->runtime_state()->to_upper_mapping()); | 
| 2162 } | 559 } | 
| 2163 | 560 | 
| 2164 }  // namespace internal | 561 }  // namespace internal | 
| 2165 }  // namespace v8 | 562 }  // namespace v8 | 
| OLD | NEW | 
|---|