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