LCOV - code coverage report
Current view: top level - src/runtime - runtime-strings.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 320 364 87.9 %
Date: 2017-10-20 Functions: 33 66 50.0 %

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

Generated by: LCOV version 1.10