Home | History | Annotate | Download | only in runtime
      1 // Copyright 2014 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/arguments-inl.h"
      6 #include "src/conversions.h"
      7 #include "src/counters.h"
      8 #include "src/objects-inl.h"
      9 #include "src/objects/js-array-inl.h"
     10 #include "src/regexp/jsregexp-inl.h"
     11 #include "src/regexp/regexp-utils.h"
     12 #include "src/runtime/runtime-utils.h"
     13 #include "src/string-builder-inl.h"
     14 #include "src/string-search.h"
     15 
     16 namespace v8 {
     17 namespace internal {
     18 
     19 RUNTIME_FUNCTION(Runtime_GetSubstitution) {
     20   HandleScope scope(isolate);
     21   DCHECK_EQ(5, args.length());
     22   CONVERT_ARG_HANDLE_CHECKED(String, matched, 0);
     23   CONVERT_ARG_HANDLE_CHECKED(String, subject, 1);
     24   CONVERT_SMI_ARG_CHECKED(position, 2);
     25   CONVERT_ARG_HANDLE_CHECKED(String, replacement, 3);
     26   CONVERT_SMI_ARG_CHECKED(start_index, 4);
     27 
     28   // A simple match without captures.
     29   class SimpleMatch : public String::Match {
     30    public:
     31     SimpleMatch(Handle<String> match, Handle<String> prefix,
     32                 Handle<String> suffix)
     33         : match_(match), prefix_(prefix), suffix_(suffix) {}
     34 
     35     Handle<String> GetMatch() override { return match_; }
     36     Handle<String> GetPrefix() override { return prefix_; }
     37     Handle<String> GetSuffix() override { return suffix_; }
     38 
     39     int CaptureCount() override { return 0; }
     40     bool HasNamedCaptures() override { return false; }
     41     MaybeHandle<String> GetCapture(int i, bool* capture_exists) override {
     42       *capture_exists = false;
     43       return match_;  // Return arbitrary string handle.
     44     }
     45     MaybeHandle<String> GetNamedCapture(Handle<String> name,
     46                                         CaptureState* state) override {
     47       UNREACHABLE();
     48     }
     49 
     50    private:
     51     Handle<String> match_, prefix_, suffix_;
     52   };
     53 
     54   Handle<String> prefix =
     55       isolate->factory()->NewSubString(subject, 0, position);
     56   Handle<String> suffix = isolate->factory()->NewSubString(
     57       subject, position + matched->length(), subject->length());
     58   SimpleMatch match(matched, prefix, suffix);
     59 
     60   RETURN_RESULT_OR_FAILURE(
     61       isolate,
     62       String::GetSubstitution(isolate, &match, replacement, start_index));
     63 }
     64 
     65 // This may return an empty MaybeHandle if an exception is thrown or
     66 // we abort due to reaching the recursion limit.
     67 MaybeHandle<String> StringReplaceOneCharWithString(
     68     Isolate* isolate, Handle<String> subject, Handle<String> search,
     69     Handle<String> replace, bool* found, int recursion_limit) {
     70   StackLimitCheck stackLimitCheck(isolate);
     71   if (stackLimitCheck.HasOverflowed() || (recursion_limit == 0)) {
     72     return MaybeHandle<String>();
     73   }
     74   recursion_limit--;
     75   if (subject->IsConsString()) {
     76     ConsString* cons = ConsString::cast(*subject);
     77     Handle<String> first = handle(cons->first(), isolate);
     78     Handle<String> second = handle(cons->second(), isolate);
     79     Handle<String> new_first;
     80     if (!StringReplaceOneCharWithString(isolate, first, search, replace, found,
     81                                         recursion_limit).ToHandle(&new_first)) {
     82       return MaybeHandle<String>();
     83     }
     84     if (*found) return isolate->factory()->NewConsString(new_first, second);
     85 
     86     Handle<String> new_second;
     87     if (!StringReplaceOneCharWithString(isolate, second, search, replace, found,
     88                                         recursion_limit)
     89              .ToHandle(&new_second)) {
     90       return MaybeHandle<String>();
     91     }
     92     if (*found) return isolate->factory()->NewConsString(first, new_second);
     93 
     94     return subject;
     95   } else {
     96     int index = String::IndexOf(isolate, subject, search, 0);
     97     if (index == -1) return subject;
     98     *found = true;
     99     Handle<String> first = isolate->factory()->NewSubString(subject, 0, index);
    100     Handle<String> cons1;
    101     ASSIGN_RETURN_ON_EXCEPTION(
    102         isolate, cons1, isolate->factory()->NewConsString(first, replace),
    103         String);
    104     Handle<String> second =
    105         isolate->factory()->NewSubString(subject, index + 1, subject->length());
    106     return isolate->factory()->NewConsString(cons1, second);
    107   }
    108 }
    109 
    110 RUNTIME_FUNCTION(Runtime_StringReplaceOneCharWithString) {
    111   HandleScope scope(isolate);
    112   DCHECK_EQ(3, args.length());
    113   CONVERT_ARG_HANDLE_CHECKED(String, subject, 0);
    114   CONVERT_ARG_HANDLE_CHECKED(String, search, 1);
    115   CONVERT_ARG_HANDLE_CHECKED(String, replace, 2);
    116 
    117   // If the cons string tree is too deep, we simply abort the recursion and
    118   // retry with a flattened subject string.
    119   const int kRecursionLimit = 0x1000;
    120   bool found = false;
    121   Handle<String> result;
    122   if (StringReplaceOneCharWithString(isolate, subject, search, replace, &found,
    123                                      kRecursionLimit).ToHandle(&result)) {
    124     return *result;
    125   }
    126   if (isolate->has_pending_exception())
    127     return ReadOnlyRoots(isolate).exception();
    128 
    129   subject = String::Flatten(isolate, subject);
    130   if (StringReplaceOneCharWithString(isolate, subject, search, replace, &found,
    131                                      kRecursionLimit).ToHandle(&result)) {
    132     return *result;
    133   }
    134   if (isolate->has_pending_exception())
    135     return ReadOnlyRoots(isolate).exception();
    136   // In case of empty handle and no pending exception we have stack overflow.
    137   return isolate->StackOverflow();
    138 }
    139 
    140 RUNTIME_FUNCTION(Runtime_StringTrim) {
    141   HandleScope scope(isolate);
    142   DCHECK_EQ(2, args.length());
    143   Handle<String> string = args.at<String>(0);
    144   CONVERT_SMI_ARG_CHECKED(mode, 1);
    145   String::TrimMode trim_mode = static_cast<String::TrimMode>(mode);
    146   return *String::Trim(isolate, string, trim_mode);
    147 }
    148 
    149 // ES6 #sec-string.prototype.includes
    150 // String.prototype.includes(searchString [, position])
    151 RUNTIME_FUNCTION(Runtime_StringIncludes) {
    152   HandleScope scope(isolate);
    153   DCHECK_EQ(3, args.length());
    154 
    155   Handle<Object> receiver = args.at(0);
    156   if (receiver->IsNullOrUndefined(isolate)) {
    157     THROW_NEW_ERROR_RETURN_FAILURE(
    158         isolate, NewTypeError(MessageTemplate::kCalledOnNullOrUndefined,
    159                               isolate->factory()->NewStringFromAsciiChecked(
    160                                   "String.prototype.includes")));
    161   }
    162   Handle<String> receiver_string;
    163   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, receiver_string,
    164                                      Object::ToString(isolate, receiver));
    165 
    166   // Check if the search string is a regExp and fail if it is.
    167   Handle<Object> search = args.at(1);
    168   Maybe<bool> is_reg_exp = RegExpUtils::IsRegExp(isolate, search);
    169   if (is_reg_exp.IsNothing()) {
    170     DCHECK(isolate->has_pending_exception());
    171     return ReadOnlyRoots(isolate).exception();
    172   }
    173   if (is_reg_exp.FromJust()) {
    174     THROW_NEW_ERROR_RETURN_FAILURE(
    175         isolate, NewTypeError(MessageTemplate::kFirstArgumentNotRegExp,
    176                               isolate->factory()->NewStringFromStaticChars(
    177                                   "String.prototype.includes")));
    178   }
    179   Handle<String> search_string;
    180   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, search_string,
    181                                      Object::ToString(isolate, args.at(1)));
    182   Handle<Object> position;
    183   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, position,
    184                                      Object::ToInteger(isolate, args.at(2)));
    185 
    186   uint32_t index = receiver_string->ToValidIndex(*position);
    187   int index_in_str =
    188       String::IndexOf(isolate, receiver_string, search_string, index);
    189   return *isolate->factory()->ToBoolean(index_in_str != -1);
    190 }
    191 
    192 // ES6 #sec-string.prototype.indexof
    193 // String.prototype.indexOf(searchString [, position])
    194 RUNTIME_FUNCTION(Runtime_StringIndexOf) {
    195   HandleScope scope(isolate);
    196   DCHECK_EQ(3, args.length());
    197   return String::IndexOf(isolate, args.at(0), args.at(1), args.at(2));
    198 }
    199 
    200 // ES6 #sec-string.prototype.indexof
    201 // String.prototype.indexOf(searchString, position)
    202 // Fast version that assumes that does not perform conversions of the incoming
    203 // arguments.
    204 RUNTIME_FUNCTION(Runtime_StringIndexOfUnchecked) {
    205   HandleScope scope(isolate);
    206   DCHECK_EQ(3, args.length());
    207   Handle<String> receiver_string = args.at<String>(0);
    208   Handle<String> search_string = args.at<String>(1);
    209   int index = std::min(std::max(args.smi_at(2), 0), receiver_string->length());
    210 
    211   return Smi::FromInt(String::IndexOf(isolate, receiver_string, search_string,
    212                                       static_cast<uint32_t>(index)));
    213 }
    214 
    215 RUNTIME_FUNCTION(Runtime_StringLastIndexOf) {
    216   HandleScope handle_scope(isolate);
    217   return String::LastIndexOf(isolate, args.at(0), args.at(1),
    218                              isolate->factory()->undefined_value());
    219 }
    220 
    221 RUNTIME_FUNCTION(Runtime_StringSubstring) {
    222   HandleScope scope(isolate);
    223   DCHECK_EQ(3, args.length());
    224   CONVERT_ARG_HANDLE_CHECKED(String, string, 0);
    225   CONVERT_INT32_ARG_CHECKED(start, 1);
    226   CONVERT_INT32_ARG_CHECKED(end, 2);
    227   DCHECK_LE(0, start);
    228   DCHECK_LE(start, end);
    229   DCHECK_LE(end, string->length());
    230   isolate->counters()->sub_string_runtime()->Increment();
    231   return *isolate->factory()->NewSubString(string, start, end);
    232 }
    233 
    234 RUNTIME_FUNCTION(Runtime_StringAdd) {
    235   HandleScope scope(isolate);
    236   DCHECK_EQ(2, args.length());
    237   CONVERT_ARG_HANDLE_CHECKED(String, str1, 0);
    238   CONVERT_ARG_HANDLE_CHECKED(String, str2, 1);
    239   isolate->counters()->string_add_runtime()->Increment();
    240   RETURN_RESULT_OR_FAILURE(isolate,
    241                            isolate->factory()->NewConsString(str1, str2));
    242 }
    243 
    244 
    245 RUNTIME_FUNCTION(Runtime_InternalizeString) {
    246   HandleScope handles(isolate);
    247   DCHECK_EQ(1, args.length());
    248   CONVERT_ARG_HANDLE_CHECKED(String, string, 0);
    249   return *isolate->factory()->InternalizeString(string);
    250 }
    251 
    252 RUNTIME_FUNCTION(Runtime_StringCharCodeAt) {
    253   HandleScope handle_scope(isolate);
    254   DCHECK_EQ(2, args.length());
    255 
    256   CONVERT_ARG_HANDLE_CHECKED(String, subject, 0);
    257   CONVERT_NUMBER_CHECKED(uint32_t, i, Uint32, args[1]);
    258 
    259   // Flatten the string.  If someone wants to get a char at an index
    260   // in a cons string, it is likely that more indices will be
    261   // accessed.
    262   subject = String::Flatten(isolate, subject);
    263 
    264   if (i >= static_cast<uint32_t>(subject->length())) {
    265     return ReadOnlyRoots(isolate).nan_value();
    266   }
    267 
    268   return Smi::FromInt(subject->Get(i));
    269 }
    270 
    271 RUNTIME_FUNCTION(Runtime_StringBuilderConcat) {
    272   HandleScope scope(isolate);
    273   DCHECK_EQ(3, args.length());
    274   CONVERT_ARG_HANDLE_CHECKED(JSArray, array, 0);
    275   int32_t array_length;
    276   if (!args[1]->ToInt32(&array_length)) {
    277     THROW_NEW_ERROR_RETURN_FAILURE(isolate, NewInvalidStringLengthError());
    278   }
    279   CONVERT_ARG_HANDLE_CHECKED(String, special, 2);
    280 
    281   size_t actual_array_length = 0;
    282   CHECK(TryNumberToSize(array->length(), &actual_array_length));
    283   CHECK_GE(array_length, 0);
    284   CHECK(static_cast<size_t>(array_length) <= actual_array_length);
    285 
    286   // This assumption is used by the slice encoding in one or two smis.
    287   DCHECK_GE(Smi::kMaxValue, String::kMaxLength);
    288 
    289   CHECK(array->HasFastElements());
    290   JSObject::EnsureCanContainHeapObjectElements(array);
    291 
    292   int special_length = special->length();
    293   if (!array->HasObjectElements()) {
    294     return isolate->Throw(ReadOnlyRoots(isolate).illegal_argument_string());
    295   }
    296 
    297   int length;
    298   bool one_byte = special->HasOnlyOneByteChars();
    299 
    300   {
    301     DisallowHeapAllocation no_gc;
    302     FixedArray* fixed_array = FixedArray::cast(array->elements());
    303     if (fixed_array->length() < array_length) {
    304       array_length = fixed_array->length();
    305     }
    306 
    307     if (array_length == 0) {
    308       return ReadOnlyRoots(isolate).empty_string();
    309     } else if (array_length == 1) {
    310       Object* first = fixed_array->get(0);
    311       if (first->IsString()) return first;
    312     }
    313     length = StringBuilderConcatLength(special_length, fixed_array,
    314                                        array_length, &one_byte);
    315   }
    316 
    317   if (length == -1) {
    318     return isolate->Throw(ReadOnlyRoots(isolate).illegal_argument_string());
    319   }
    320   if (length == 0) {
    321     return ReadOnlyRoots(isolate).empty_string();
    322   }
    323 
    324   if (one_byte) {
    325     Handle<SeqOneByteString> answer;
    326     ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
    327         isolate, answer, isolate->factory()->NewRawOneByteString(length));
    328     StringBuilderConcatHelper(*special, answer->GetChars(),
    329                               FixedArray::cast(array->elements()),
    330                               array_length);
    331     return *answer;
    332   } else {
    333     Handle<SeqTwoByteString> answer;
    334     ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
    335         isolate, answer, isolate->factory()->NewRawTwoByteString(length));
    336     StringBuilderConcatHelper(*special, answer->GetChars(),
    337                               FixedArray::cast(array->elements()),
    338                               array_length);
    339     return *answer;
    340   }
    341 }
    342 
    343 RUNTIME_FUNCTION(Runtime_StringBuilderJoin) {
    344   HandleScope scope(isolate);
    345   DCHECK_EQ(3, args.length());
    346   CONVERT_ARG_HANDLE_CHECKED(JSArray, array, 0);
    347   int32_t array_length;
    348   if (!args[1]->ToInt32(&array_length)) {
    349     THROW_NEW_ERROR_RETURN_FAILURE(isolate, NewInvalidStringLengthError());
    350   }
    351   CONVERT_ARG_HANDLE_CHECKED(String, separator, 2);
    352   CHECK(array->HasObjectElements());
    353   CHECK_GE(array_length, 0);
    354 
    355   Handle<FixedArray> fixed_array(FixedArray::cast(array->elements()), isolate);
    356   if (fixed_array->length() < array_length) {
    357     array_length = fixed_array->length();
    358   }
    359 
    360   if (array_length == 0) {
    361     return ReadOnlyRoots(isolate).empty_string();
    362   } else if (array_length == 1) {
    363     Object* first = fixed_array->get(0);
    364     CHECK(first->IsString());
    365     return first;
    366   }
    367 
    368   int separator_length = separator->length();
    369   CHECK_GT(separator_length, 0);
    370   int max_nof_separators =
    371       (String::kMaxLength + separator_length - 1) / separator_length;
    372   if (max_nof_separators < (array_length - 1)) {
    373     THROW_NEW_ERROR_RETURN_FAILURE(isolate, NewInvalidStringLengthError());
    374   }
    375   int length = (array_length - 1) * separator_length;
    376   for (int i = 0; i < array_length; i++) {
    377     Object* element_obj = fixed_array->get(i);
    378     CHECK(element_obj->IsString());
    379     String* element = String::cast(element_obj);
    380     int increment = element->length();
    381     if (increment > String::kMaxLength - length) {
    382       STATIC_ASSERT(String::kMaxLength < kMaxInt);
    383       length = kMaxInt;  // Provoke exception;
    384       break;
    385     }
    386     length += increment;
    387   }
    388 
    389   Handle<SeqTwoByteString> answer;
    390   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
    391       isolate, answer, isolate->factory()->NewRawTwoByteString(length));
    392 
    393   DisallowHeapAllocation no_gc;
    394 
    395   uc16* sink = answer->GetChars();
    396 #ifdef DEBUG
    397   uc16* end = sink + length;
    398 #endif
    399 
    400   CHECK(fixed_array->get(0)->IsString());
    401   String* first = String::cast(fixed_array->get(0));
    402   String* separator_raw = *separator;
    403 
    404   int first_length = first->length();
    405   String::WriteToFlat(first, sink, 0, first_length);
    406   sink += first_length;
    407 
    408   for (int i = 1; i < array_length; i++) {
    409     DCHECK(sink + separator_length <= end);
    410     String::WriteToFlat(separator_raw, sink, 0, separator_length);
    411     sink += separator_length;
    412 
    413     CHECK(fixed_array->get(i)->IsString());
    414     String* element = String::cast(fixed_array->get(i));
    415     int element_length = element->length();
    416     DCHECK(sink + element_length <= end);
    417     String::WriteToFlat(element, sink, 0, element_length);
    418     sink += element_length;
    419   }
    420   DCHECK(sink == end);
    421 
    422   // Use %_FastOneByteArrayJoin instead.
    423   DCHECK(!answer->IsOneByteRepresentation());
    424   return *answer;
    425 }
    426 
    427 template <typename sinkchar>
    428 static void WriteRepeatToFlat(String* src, Vector<sinkchar> buffer, int cursor,
    429                               int repeat, int length) {
    430   if (repeat == 0) return;
    431 
    432   sinkchar* start = &buffer[cursor];
    433   String::WriteToFlat<sinkchar>(src, start, 0, length);
    434 
    435   int done = 1;
    436   sinkchar* next = start + length;
    437 
    438   while (done < repeat) {
    439     int block = Min(done, repeat - done);
    440     int block_chars = block * length;
    441     CopyChars(next, start, block_chars);
    442     next += block_chars;
    443     done += block;
    444   }
    445 }
    446 
    447 template <typename Char>
    448 static void JoinSparseArrayWithSeparator(FixedArray* elements,
    449                                          int elements_length,
    450                                          uint32_t array_length,
    451                                          String* separator,
    452                                          Vector<Char> buffer) {
    453   DisallowHeapAllocation no_gc;
    454   int previous_separator_position = 0;
    455   int separator_length = separator->length();
    456   DCHECK_LT(0, separator_length);
    457   int cursor = 0;
    458   for (int i = 0; i < elements_length; i += 2) {
    459     int position = NumberToInt32(elements->get(i));
    460     String* string = String::cast(elements->get(i + 1));
    461     int string_length = string->length();
    462     if (string->length() > 0) {
    463       int repeat = position - previous_separator_position;
    464       WriteRepeatToFlat<Char>(separator, buffer, cursor, repeat,
    465                               separator_length);
    466       cursor += repeat * separator_length;
    467       previous_separator_position = position;
    468       String::WriteToFlat<Char>(string, &buffer[cursor], 0, string_length);
    469       cursor += string->length();
    470     }
    471   }
    472 
    473   int last_array_index = static_cast<int>(array_length - 1);
    474   // Array length must be representable as a signed 32-bit number,
    475   // otherwise the total string length would have been too large.
    476   DCHECK_LE(array_length, 0x7FFFFFFF);  // Is int32_t.
    477   int repeat = last_array_index - previous_separator_position;
    478   WriteRepeatToFlat<Char>(separator, buffer, cursor, repeat, separator_length);
    479   cursor += repeat * separator_length;
    480   DCHECK(cursor <= buffer.length());
    481 }
    482 
    483 RUNTIME_FUNCTION(Runtime_SparseJoinWithSeparator) {
    484   HandleScope scope(isolate);
    485   DCHECK_EQ(3, args.length());
    486   CONVERT_ARG_HANDLE_CHECKED(JSArray, elements_array, 0);
    487   CONVERT_NUMBER_CHECKED(uint32_t, array_length, Uint32, args[1]);
    488   CONVERT_ARG_HANDLE_CHECKED(String, separator, 2);
    489   // elements_array is fast-mode JSarray of alternating positions
    490   // (increasing order) and strings.
    491   CHECK(elements_array->HasSmiOrObjectElements());
    492   // array_length is length of original array (used to add separators);
    493   // separator is string to put between elements. Assumed to be non-empty.
    494   CHECK_GT(array_length, 0);
    495 
    496   // Find total length of join result.
    497   int string_length = 0;
    498   bool is_one_byte = separator->IsOneByteRepresentation();
    499   bool overflow = false;
    500   CONVERT_NUMBER_CHECKED(int, elements_length, Int32, elements_array->length());
    501   CHECK(elements_length <= elements_array->elements()->length());
    502   CHECK_EQ(elements_length & 1, 0);  // Even length.
    503   FixedArray* elements = FixedArray::cast(elements_array->elements());
    504   {
    505     DisallowHeapAllocation no_gc;
    506     for (int i = 0; i < elements_length; i += 2) {
    507       String* string = String::cast(elements->get(i + 1));
    508       int length = string->length();
    509       if (is_one_byte && !string->IsOneByteRepresentation()) {
    510         is_one_byte = false;
    511       }
    512       if (length > String::kMaxLength ||
    513           String::kMaxLength - length < string_length) {
    514         overflow = true;
    515         break;
    516       }
    517       string_length += length;
    518     }
    519   }
    520 
    521   int separator_length = separator->length();
    522   if (!overflow && separator_length > 0) {
    523     if (array_length <= 0x7FFFFFFFu) {
    524       int separator_count = static_cast<int>(array_length) - 1;
    525       int remaining_length = String::kMaxLength - string_length;
    526       if ((remaining_length / separator_length) >= separator_count) {
    527         string_length += separator_length * (array_length - 1);
    528       } else {
    529         // Not room for the separators within the maximal string length.
    530         overflow = true;
    531       }
    532     } else {
    533       // Nonempty separator and at least 2^31-1 separators necessary
    534       // means that the string is too large to create.
    535       STATIC_ASSERT(String::kMaxLength < 0x7FFFFFFF);
    536       overflow = true;
    537     }
    538   }
    539   if (overflow) {
    540     // Throw an exception if the resulting string is too large. See
    541     // https://code.google.com/p/chromium/issues/detail?id=336820
    542     // for details.
    543     THROW_NEW_ERROR_RETURN_FAILURE(isolate, NewInvalidStringLengthError());
    544   }
    545 
    546   if (is_one_byte) {
    547     Handle<SeqOneByteString> result = isolate->factory()
    548                                           ->NewRawOneByteString(string_length)
    549                                           .ToHandleChecked();
    550     JoinSparseArrayWithSeparator<uint8_t>(
    551         FixedArray::cast(elements_array->elements()), elements_length,
    552         array_length, *separator,
    553         Vector<uint8_t>(result->GetChars(), string_length));
    554     return *result;
    555   } else {
    556     Handle<SeqTwoByteString> result = isolate->factory()
    557                                           ->NewRawTwoByteString(string_length)
    558                                           .ToHandleChecked();
    559     JoinSparseArrayWithSeparator<uc16>(
    560         FixedArray::cast(elements_array->elements()), elements_length,
    561         array_length, *separator,
    562         Vector<uc16>(result->GetChars(), string_length));
    563     return *result;
    564   }
    565 }
    566 
    567 // Copies Latin1 characters to the given fixed array looking up
    568 // one-char strings in the cache. Gives up on the first char that is
    569 // not in the cache and fills the remainder with smi zeros. Returns
    570 // the length of the successfully copied prefix.
    571 static int CopyCachedOneByteCharsToArray(Heap* heap, const uint8_t* chars,
    572                                          FixedArray* elements, int length) {
    573   DisallowHeapAllocation no_gc;
    574   FixedArray* one_byte_cache = heap->single_character_string_cache();
    575   Object* undefined = ReadOnlyRoots(heap).undefined_value();
    576   int i;
    577   WriteBarrierMode mode = elements->GetWriteBarrierMode(no_gc);
    578   for (i = 0; i < length; ++i) {
    579     Object* value = one_byte_cache->get(chars[i]);
    580     if (value == undefined) break;
    581     elements->set(i, value, mode);
    582   }
    583   if (i < length) {
    584     static_assert(Smi::kZero == 0, "Can use memset since Smi::kZero is 0");
    585     memset(elements->data_start() + i, 0, kPointerSize * (length - i));
    586   }
    587 #ifdef DEBUG
    588   for (int j = 0; j < length; ++j) {
    589     Object* element = elements->get(j);
    590     DCHECK(element == Smi::kZero ||
    591            (element->IsString() && String::cast(element)->LooksValid()));
    592   }
    593 #endif
    594   return i;
    595 }
    596 
    597 // Converts a String to JSArray.
    598 // For example, "foo" => ["f", "o", "o"].
    599 RUNTIME_FUNCTION(Runtime_StringToArray) {
    600   HandleScope scope(isolate);
    601   DCHECK_EQ(2, args.length());
    602   CONVERT_ARG_HANDLE_CHECKED(String, s, 0);
    603   CONVERT_NUMBER_CHECKED(uint32_t, limit, Uint32, args[1]);
    604 
    605   s = String::Flatten(isolate, s);
    606   const int length = static_cast<int>(Min<uint32_t>(s->length(), limit));
    607 
    608   Handle<FixedArray> elements;
    609   int position = 0;
    610   if (s->IsFlat() && s->IsOneByteRepresentation()) {
    611     // Try using cached chars where possible.
    612     elements = isolate->factory()->NewUninitializedFixedArray(length);
    613 
    614     DisallowHeapAllocation no_gc;
    615     String::FlatContent content = s->GetFlatContent();
    616     if (content.IsOneByte()) {
    617       Vector<const uint8_t> chars = content.ToOneByteVector();
    618       // Note, this will initialize all elements (not only the prefix)
    619       // to prevent GC from seeing partially initialized array.
    620       position = CopyCachedOneByteCharsToArray(isolate->heap(), chars.start(),
    621                                                *elements, length);
    622     } else {
    623       MemsetPointer(elements->data_start(),
    624                     ReadOnlyRoots(isolate).undefined_value(), length);
    625     }
    626   } else {
    627     elements = isolate->factory()->NewFixedArray(length);
    628   }
    629   for (int i = position; i < length; ++i) {
    630     Handle<Object> str =
    631         isolate->factory()->LookupSingleCharacterStringFromCode(s->Get(i));
    632     elements->set(i, *str);
    633   }
    634 
    635 #ifdef DEBUG
    636   for (int i = 0; i < length; ++i) {
    637     DCHECK_EQ(String::cast(elements->get(i))->length(), 1);
    638   }
    639 #endif
    640 
    641   return *isolate->factory()->NewJSArrayWithElements(elements);
    642 }
    643 
    644 RUNTIME_FUNCTION(Runtime_StringLessThan) {
    645   HandleScope handle_scope(isolate);
    646   DCHECK_EQ(2, args.length());
    647   CONVERT_ARG_HANDLE_CHECKED(String, x, 0);
    648   CONVERT_ARG_HANDLE_CHECKED(String, y, 1);
    649   ComparisonResult result = String::Compare(isolate, x, y);
    650   DCHECK_NE(result, ComparisonResult::kUndefined);
    651   return isolate->heap()->ToBoolean(
    652       ComparisonResultToBool(Operation::kLessThan, result));
    653 }
    654 
    655 RUNTIME_FUNCTION(Runtime_StringLessThanOrEqual) {
    656   HandleScope handle_scope(isolate);
    657   DCHECK_EQ(2, args.length());
    658   CONVERT_ARG_HANDLE_CHECKED(String, x, 0);
    659   CONVERT_ARG_HANDLE_CHECKED(String, y, 1);
    660   ComparisonResult result = String::Compare(isolate, x, y);
    661   DCHECK_NE(result, ComparisonResult::kUndefined);
    662   return isolate->heap()->ToBoolean(
    663       ComparisonResultToBool(Operation::kLessThanOrEqual, result));
    664 }
    665 
    666 RUNTIME_FUNCTION(Runtime_StringGreaterThan) {
    667   HandleScope handle_scope(isolate);
    668   DCHECK_EQ(2, args.length());
    669   CONVERT_ARG_HANDLE_CHECKED(String, x, 0);
    670   CONVERT_ARG_HANDLE_CHECKED(String, y, 1);
    671   ComparisonResult result = String::Compare(isolate, x, y);
    672   DCHECK_NE(result, ComparisonResult::kUndefined);
    673   return isolate->heap()->ToBoolean(
    674       ComparisonResultToBool(Operation::kGreaterThan, result));
    675 }
    676 
    677 RUNTIME_FUNCTION(Runtime_StringGreaterThanOrEqual) {
    678   HandleScope handle_scope(isolate);
    679   DCHECK_EQ(2, args.length());
    680   CONVERT_ARG_HANDLE_CHECKED(String, x, 0);
    681   CONVERT_ARG_HANDLE_CHECKED(String, y, 1);
    682   ComparisonResult result = String::Compare(isolate, x, y);
    683   DCHECK_NE(result, ComparisonResult::kUndefined);
    684   return isolate->heap()->ToBoolean(
    685       ComparisonResultToBool(Operation::kGreaterThanOrEqual, result));
    686 }
    687 
    688 RUNTIME_FUNCTION(Runtime_StringEqual) {
    689   HandleScope handle_scope(isolate);
    690   DCHECK_EQ(2, args.length());
    691   CONVERT_ARG_HANDLE_CHECKED(String, x, 0);
    692   CONVERT_ARG_HANDLE_CHECKED(String, y, 1);
    693   return isolate->heap()->ToBoolean(String::Equals(isolate, x, y));
    694 }
    695 
    696 RUNTIME_FUNCTION(Runtime_StringNotEqual) {
    697   HandleScope handle_scope(isolate);
    698   DCHECK_EQ(2, args.length());
    699   CONVERT_ARG_HANDLE_CHECKED(String, x, 0);
    700   CONVERT_ARG_HANDLE_CHECKED(String, y, 1);
    701   return isolate->heap()->ToBoolean(!String::Equals(isolate, x, y));
    702 }
    703 
    704 RUNTIME_FUNCTION(Runtime_FlattenString) {
    705   HandleScope scope(isolate);
    706   DCHECK_EQ(1, args.length());
    707   CONVERT_ARG_HANDLE_CHECKED(String, str, 0);
    708   return *String::Flatten(isolate, str);
    709 }
    710 
    711 RUNTIME_FUNCTION(Runtime_StringCharFromCode) {
    712   HandleScope handlescope(isolate);
    713   DCHECK_EQ(1, args.length());
    714   if (args[0]->IsNumber()) {
    715     CONVERT_NUMBER_CHECKED(uint32_t, code, Uint32, args[0]);
    716     code &= 0xFFFF;
    717     return *isolate->factory()->LookupSingleCharacterStringFromCode(code);
    718   }
    719   return ReadOnlyRoots(isolate).empty_string();
    720 }
    721 
    722 RUNTIME_FUNCTION(Runtime_StringMaxLength) {
    723   SealHandleScope shs(isolate);
    724   return Smi::FromInt(String::kMaxLength);
    725 }
    726 
    727 }  // namespace internal
    728 }  // namespace v8
    729