LCOV - code coverage report
Current view: top level - src/builtins - builtins-string.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 107 129 82.9 %
Date: 2017-10-20 Functions: 12 20 60.0 %

          Line data    Source code
       1             : // Copyright 2016 the V8 project authors. All rights reserved.
       2             : // Use of this source code is governed by a BSD-style license that can be
       3             : // found in the LICENSE file.
       4             : 
       5             : #include "src/builtins/builtins-utils.h"
       6             : #include "src/builtins/builtins.h"
       7             : #include "src/conversions.h"
       8             : #include "src/counters.h"
       9             : #include "src/objects-inl.h"
      10             : #include "src/regexp/regexp-utils.h"
      11             : #include "src/string-builder.h"
      12             : #include "src/string-case.h"
      13             : #include "src/unicode-inl.h"
      14             : #include "src/unicode.h"
      15             : 
      16             : namespace v8 {
      17             : namespace internal {
      18             : 
      19             : namespace {  // for String.fromCodePoint
      20             : 
      21    10912007 : bool IsValidCodePoint(Isolate* isolate, Handle<Object> value) {
      22    10912007 :   if (!value->IsNumber() && !Object::ToNumber(value).ToHandle(&value)) {
      23             :     return false;
      24             :   }
      25             : 
      26    10912007 :   if (Object::ToInteger(isolate, value).ToHandleChecked()->Number() !=
      27             :       value->Number()) {
      28             :     return false;
      29             :   }
      30             : 
      31    21823851 :   if (value->Number() < 0 || value->Number() > 0x10FFFF) {
      32             :     return false;
      33             :   }
      34             : 
      35    10911870 :   return true;
      36             : }
      37             : 
      38    10912025 : uc32 NextCodePoint(Isolate* isolate, BuiltinArguments args, int index) {
      39    10912025 :   Handle<Object> value = args.at(1 + index);
      40    21824050 :   ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, value, Object::ToNumber(value), -1);
      41    10912007 :   if (!IsValidCodePoint(isolate, value)) {
      42             :     isolate->Throw(*isolate->factory()->NewRangeError(
      43         274 :         MessageTemplate::kInvalidCodePoint, value));
      44             :     return -1;
      45             :   }
      46             :   return DoubleToUint32(value->Number());
      47             : }
      48             : 
      49             : }  // namespace
      50             : 
      51             : // ES6 section 21.1.2.2 String.fromCodePoint ( ...codePoints )
      52    30081840 : BUILTIN(StringFromCodePoint) {
      53             :   HandleScope scope(isolate);
      54    10027280 :   int const length = args.length() - 1;
      55    10027280 :   if (length == 0) return isolate->heap()->empty_string();
      56             :   DCHECK_LT(0, length);
      57             : 
      58             :   // Optimistically assume that the resulting String contains only one byte
      59             :   // characters.
      60             :   std::vector<uint8_t> one_byte_buffer;
      61    10027271 :   one_byte_buffer.reserve(length);
      62             :   uc32 code = 0;
      63             :   int index;
      64    10472024 :   for (index = 0; index < length; index++) {
      65    10469648 :     code = NextCodePoint(isolate, args, index);
      66    10469648 :     if (code < 0) {
      67         155 :       return isolate->heap()->exception();
      68             :     }
      69    10469493 :     if (code > String::kMaxOneByteCharCode) {
      70             :       break;
      71             :     }
      72      889506 :     one_byte_buffer.push_back(code);
      73             :   }
      74             : 
      75    10027116 :   if (index == length) {
      76        9504 :     RETURN_RESULT_OR_FAILURE(
      77             :         isolate, isolate->factory()->NewStringFromOneByte(Vector<uint8_t>(
      78             :                      one_byte_buffer.data(), one_byte_buffer.size())));
      79             :   }
      80             : 
      81             :   std::vector<uc16> two_byte_buffer;
      82    10024740 :   two_byte_buffer.reserve(length - index);
      83             : 
      84             :   while (true) {
      85    10467117 :     if (code <= static_cast<uc32>(unibrow::Utf16::kMaxNonSurrogateCharCode)) {
      86     1175058 :       two_byte_buffer.push_back(code);
      87             :     } else {
      88    29638764 :       two_byte_buffer.push_back(unibrow::Utf16::LeadSurrogate(code));
      89    19759176 :       two_byte_buffer.push_back(unibrow::Utf16::TrailSurrogate(code));
      90             :     }
      91             : 
      92    10467117 :     if (++index == length) {
      93             :       break;
      94             :     }
      95      442377 :     code = NextCodePoint(isolate, args, index);
      96      442377 :     if (code < 0) {
      97           0 :       return isolate->heap()->exception();
      98             :     }
      99             :   }
     100             : 
     101             :   Handle<SeqTwoByteString> result;
     102    40098960 :   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
     103             :       isolate, result,
     104             :       isolate->factory()->NewRawTwoByteString(
     105             :           static_cast<int>(one_byte_buffer.size() + two_byte_buffer.size())));
     106             : 
     107    20049480 :   CopyChars(result->GetChars(), one_byte_buffer.data(), one_byte_buffer.size());
     108    10024740 :   CopyChars(result->GetChars() + one_byte_buffer.size(), two_byte_buffer.data(),
     109    20049480 :             two_byte_buffer.size());
     110             : 
     111    10024740 :   return *result;
     112             : }
     113             : 
     114             : // ES6 section 21.1.3.6
     115             : // String.prototype.endsWith ( searchString [ , endPosition ] )
     116       85803 : BUILTIN(StringPrototypeEndsWith) {
     117             :   HandleScope handle_scope(isolate);
     118       57526 :   TO_THIS_STRING(str, "String.prototype.endsWith");
     119             : 
     120             :   // Check if the search string is a regExp and fail if it is.
     121       28421 :   Handle<Object> search = args.atOrUndefined(isolate, 1);
     122       28421 :   Maybe<bool> is_reg_exp = RegExpUtils::IsRegExp(isolate, search);
     123       28421 :   if (is_reg_exp.IsNothing()) {
     124             :     DCHECK(isolate->has_pending_exception());
     125           0 :     return isolate->heap()->exception();
     126             :   }
     127       28421 :   if (is_reg_exp.FromJust()) {
     128         135 :     THROW_NEW_ERROR_RETURN_FAILURE(
     129             :         isolate, NewTypeError(MessageTemplate::kFirstArgumentNotRegExp,
     130             :                               isolate->factory()->NewStringFromStaticChars(
     131             :                                   "String.prototype.endsWith")));
     132             :   }
     133             :   Handle<String> search_string;
     134       56752 :   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, search_string,
     135             :                                      Object::ToString(isolate, search));
     136             : 
     137             :   Handle<Object> position = args.atOrUndefined(isolate, 2);
     138             :   int end;
     139             : 
     140       28376 :   if (position->IsUndefined(isolate)) {
     141             :     end = str->length();
     142             :   } else {
     143        1377 :     ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, position,
     144             :                                        Object::ToInteger(isolate, position));
     145        1377 :     end = str->ToValidIndex(*position);
     146             :   }
     147             : 
     148       28376 :   int start = end - search_string->length();
     149       28376 :   if (start < 0) return isolate->heap()->false_value();
     150             : 
     151       27422 :   str = String::Flatten(str);
     152       27422 :   search_string = String::Flatten(search_string);
     153             : 
     154             :   DisallowHeapAllocation no_gc;  // ensure vectors stay valid
     155       27422 :   String::FlatContent str_content = str->GetFlatContent();
     156       27422 :   String::FlatContent search_content = search_string->GetFlatContent();
     157             : 
     158       27422 :   if (str_content.IsOneByte() && search_content.IsOneByte()) {
     159       27260 :     Vector<const uint8_t> str_vector = str_content.ToOneByteVector();
     160       27260 :     Vector<const uint8_t> search_vector = search_content.ToOneByteVector();
     161             : 
     162       27260 :     return isolate->heap()->ToBoolean(memcmp(str_vector.start() + start,
     163             :                                              search_vector.start(),
     164       54520 :                                              search_string->length()) == 0);
     165             :   }
     166             : 
     167         162 :   FlatStringReader str_reader(isolate, str);
     168         162 :   FlatStringReader search_reader(isolate, search_string);
     169             : 
     170        1116 :   for (int i = 0; i < search_string->length(); i++) {
     171         864 :     if (str_reader.Get(start + i) != search_reader.Get(i)) {
     172          36 :       return isolate->heap()->false_value();
     173             :     }
     174             :   }
     175         126 :   return isolate->heap()->true_value();
     176             : }
     177             : 
     178             : // ES6 section 21.1.3.9
     179             : // String.prototype.lastIndexOf ( searchString [ , position ] )
     180        5712 : BUILTIN(StringPrototypeLastIndexOf) {
     181             :   HandleScope handle_scope(isolate);
     182             :   return String::LastIndexOf(isolate, args.receiver(),
     183             :                              args.atOrUndefined(isolate, 1),
     184        3808 :                              args.atOrUndefined(isolate, 2));
     185             : }
     186             : 
     187             : // ES6 section 21.1.3.10 String.prototype.localeCompare ( that )
     188             : //
     189             : // This function is implementation specific.  For now, we do not
     190             : // do anything locale specific.
     191             : // If internationalization is enabled, then intl.js will override this function
     192             : // and provide the proper functionality, so this is just a fallback.
     193           0 : BUILTIN(StringPrototypeLocaleCompare) {
     194             :   HandleScope handle_scope(isolate);
     195             :   DCHECK_EQ(2, args.length());
     196             : 
     197           0 :   TO_THIS_STRING(str1, "String.prototype.localeCompare");
     198             :   Handle<String> str2;
     199           0 :   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, str2,
     200             :                                      Object::ToString(isolate, args.at(1)));
     201             : 
     202           0 :   if (str1.is_identical_to(str2)) return Smi::kZero;  // Equal.
     203             :   int str1_length = str1->length();
     204             :   int str2_length = str2->length();
     205             : 
     206             :   // Decide trivial cases without flattening.
     207           0 :   if (str1_length == 0) {
     208           0 :     if (str2_length == 0) return Smi::kZero;  // Equal.
     209           0 :     return Smi::FromInt(-str2_length);
     210             :   } else {
     211           0 :     if (str2_length == 0) return Smi::FromInt(str1_length);
     212             :   }
     213             : 
     214           0 :   int end = str1_length < str2_length ? str1_length : str2_length;
     215             : 
     216             :   // No need to flatten if we are going to find the answer on the first
     217             :   // character. At this point we know there is at least one character
     218             :   // in each string, due to the trivial case handling above.
     219           0 :   int d = str1->Get(0) - str2->Get(0);
     220           0 :   if (d != 0) return Smi::FromInt(d);
     221             : 
     222           0 :   str1 = String::Flatten(str1);
     223           0 :   str2 = String::Flatten(str2);
     224             : 
     225             :   DisallowHeapAllocation no_gc;
     226           0 :   String::FlatContent flat1 = str1->GetFlatContent();
     227           0 :   String::FlatContent flat2 = str2->GetFlatContent();
     228             : 
     229           0 :   for (int i = 0; i < end; i++) {
     230           0 :     if (flat1.Get(i) != flat2.Get(i)) {
     231           0 :       return Smi::FromInt(flat1.Get(i) - flat2.Get(i));
     232             :     }
     233             :   }
     234             : 
     235           0 :   return Smi::FromInt(str1_length - str2_length);
     236             : }
     237             : 
     238             : #ifndef V8_INTL_SUPPORT
     239             : // ES6 section 21.1.3.12 String.prototype.normalize ( [form] )
     240             : //
     241             : // Simply checks the argument is valid and returns the string itself.
     242             : // If internationalization is enabled, then intl.js will override this function
     243             : // and provide the proper functionality, so this is just a fallback.
     244             : BUILTIN(StringPrototypeNormalize) {
     245             :   HandleScope handle_scope(isolate);
     246             :   TO_THIS_STRING(string, "String.prototype.normalize");
     247             : 
     248             :   Handle<Object> form_input = args.atOrUndefined(isolate, 1);
     249             :   if (form_input->IsUndefined(isolate)) return *string;
     250             : 
     251             :   Handle<String> form;
     252             :   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, form,
     253             :                                      Object::ToString(isolate, form_input));
     254             : 
     255             :   if (!(String::Equals(form,
     256             :                        isolate->factory()->NewStringFromStaticChars("NFC")) ||
     257             :         String::Equals(form,
     258             :                        isolate->factory()->NewStringFromStaticChars("NFD")) ||
     259             :         String::Equals(form,
     260             :                        isolate->factory()->NewStringFromStaticChars("NFKC")) ||
     261             :         String::Equals(form,
     262             :                        isolate->factory()->NewStringFromStaticChars("NFKD")))) {
     263             :     Handle<String> valid_forms =
     264             :         isolate->factory()->NewStringFromStaticChars("NFC, NFD, NFKC, NFKD");
     265             :     THROW_NEW_ERROR_RETURN_FAILURE(
     266             :         isolate,
     267             :         NewRangeError(MessageTemplate::kNormalizationForm, valid_forms));
     268             :   }
     269             : 
     270             :   return *string;
     271             : }
     272             : #endif  // !V8_INTL_SUPPORT
     273             : 
     274       34107 : BUILTIN(StringPrototypeStartsWith) {
     275             :   HandleScope handle_scope(isolate);
     276       23062 :   TO_THIS_STRING(str, "String.prototype.startsWith");
     277             : 
     278             :   // Check if the search string is a regExp and fail if it is.
     279       11189 :   Handle<Object> search = args.atOrUndefined(isolate, 1);
     280       11189 :   Maybe<bool> is_reg_exp = RegExpUtils::IsRegExp(isolate, search);
     281       11189 :   if (is_reg_exp.IsNothing()) {
     282             :     DCHECK(isolate->has_pending_exception());
     283           0 :     return isolate->heap()->exception();
     284             :   }
     285       11189 :   if (is_reg_exp.FromJust()) {
     286         219 :     THROW_NEW_ERROR_RETURN_FAILURE(
     287             :         isolate, NewTypeError(MessageTemplate::kFirstArgumentNotRegExp,
     288             :                               isolate->factory()->NewStringFromStaticChars(
     289             :                                   "String.prototype.startsWith")));
     290             :   }
     291             :   Handle<String> search_string;
     292       22232 :   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, search_string,
     293             :                                      Object::ToString(isolate, search));
     294             : 
     295             :   Handle<Object> position = args.atOrUndefined(isolate, 2);
     296             :   int start;
     297             : 
     298       11116 :   if (position->IsUndefined(isolate)) {
     299             :     start = 0;
     300             :   } else {
     301        1305 :     ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, position,
     302             :                                        Object::ToInteger(isolate, position));
     303        1305 :     start = str->ToValidIndex(*position);
     304             :   }
     305             : 
     306       22232 :   if (start + search_string->length() > str->length()) {
     307         560 :     return isolate->heap()->false_value();
     308             :   }
     309             : 
     310       10556 :   FlatStringReader str_reader(isolate, String::Flatten(str));
     311       10556 :   FlatStringReader search_reader(isolate, String::Flatten(search_string));
     312             : 
     313      131910 :   for (int i = 0; i < search_string->length(); i++) {
     314      125444 :     if (str_reader.Get(start + i) != search_reader.Get(i)) {
     315        7323 :       return isolate->heap()->false_value();
     316             :     }
     317             :   }
     318        3233 :   return isolate->heap()->true_value();
     319             : }
     320             : 
     321             : #ifndef V8_INTL_SUPPORT
     322             : namespace {
     323             : 
     324             : inline bool ToUpperOverflows(uc32 character) {
     325             :   // y with umlauts and the micro sign are the only characters that stop
     326             :   // fitting into one-byte when converting to uppercase.
     327             :   static const uc32 yuml_code = 0xff;
     328             :   static const uc32 micro_code = 0xb5;
     329             :   return (character == yuml_code || character == micro_code);
     330             : }
     331             : 
     332             : template <class Converter>
     333             : MUST_USE_RESULT static Object* ConvertCaseHelper(
     334             :     Isolate* isolate, String* string, SeqString* result, int result_length,
     335             :     unibrow::Mapping<Converter, 128>* mapping) {
     336             :   DisallowHeapAllocation no_gc;
     337             :   // We try this twice, once with the assumption that the result is no longer
     338             :   // than the input and, if that assumption breaks, again with the exact
     339             :   // length.  This may not be pretty, but it is nicer than what was here before
     340             :   // and I hereby claim my vaffel-is.
     341             :   //
     342             :   // NOTE: This assumes that the upper/lower case of an ASCII
     343             :   // character is also ASCII.  This is currently the case, but it
     344             :   // might break in the future if we implement more context and locale
     345             :   // dependent upper/lower conversions.
     346             :   bool has_changed_character = false;
     347             : 
     348             :   // Convert all characters to upper case, assuming that they will fit
     349             :   // in the buffer
     350             :   StringCharacterStream stream(string);
     351             :   unibrow::uchar chars[Converter::kMaxWidth];
     352             :   // We can assume that the string is not empty
     353             :   uc32 current = stream.GetNext();
     354             :   bool ignore_overflow = Converter::kIsToLower || result->IsSeqTwoByteString();
     355             :   for (int i = 0; i < result_length;) {
     356             :     bool has_next = stream.HasMore();
     357             :     uc32 next = has_next ? stream.GetNext() : 0;
     358             :     int char_length = mapping->get(current, next, chars);
     359             :     if (char_length == 0) {
     360             :       // The case conversion of this character is the character itself.
     361             :       result->Set(i, current);
     362             :       i++;
     363             :     } else if (char_length == 1 &&
     364             :                (ignore_overflow || !ToUpperOverflows(current))) {
     365             :       // Common case: converting the letter resulted in one character.
     366             :       DCHECK(static_cast<uc32>(chars[0]) != current);
     367             :       result->Set(i, chars[0]);
     368             :       has_changed_character = true;
     369             :       i++;
     370             :     } else if (result_length == string->length()) {
     371             :       bool overflows = ToUpperOverflows(current);
     372             :       // We've assumed that the result would be as long as the
     373             :       // input but here is a character that converts to several
     374             :       // characters.  No matter, we calculate the exact length
     375             :       // of the result and try the whole thing again.
     376             :       //
     377             :       // Note that this leaves room for optimization.  We could just
     378             :       // memcpy what we already have to the result string.  Also,
     379             :       // the result string is the last object allocated we could
     380             :       // "realloc" it and probably, in the vast majority of cases,
     381             :       // extend the existing string to be able to hold the full
     382             :       // result.
     383             :       int next_length = 0;
     384             :       if (has_next) {
     385             :         next_length = mapping->get(next, 0, chars);
     386             :         if (next_length == 0) next_length = 1;
     387             :       }
     388             :       int current_length = i + char_length + next_length;
     389             :       while (stream.HasMore()) {
     390             :         current = stream.GetNext();
     391             :         overflows |= ToUpperOverflows(current);
     392             :         // NOTE: we use 0 as the next character here because, while
     393             :         // the next character may affect what a character converts to,
     394             :         // it does not in any case affect the length of what it convert
     395             :         // to.
     396             :         int char_length = mapping->get(current, 0, chars);
     397             :         if (char_length == 0) char_length = 1;
     398             :         current_length += char_length;
     399             :         if (current_length > String::kMaxLength) {
     400             :           AllowHeapAllocation allocate_error_and_return;
     401             :           THROW_NEW_ERROR_RETURN_FAILURE(isolate,
     402             :                                          NewInvalidStringLengthError());
     403             :         }
     404             :       }
     405             :       // Try again with the real length.  Return signed if we need
     406             :       // to allocate a two-byte string for to uppercase.
     407             :       return (overflows && !ignore_overflow) ? Smi::FromInt(-current_length)
     408             :                                              : Smi::FromInt(current_length);
     409             :     } else {
     410             :       for (int j = 0; j < char_length; j++) {
     411             :         result->Set(i, chars[j]);
     412             :         i++;
     413             :       }
     414             :       has_changed_character = true;
     415             :     }
     416             :     current = next;
     417             :   }
     418             :   if (has_changed_character) {
     419             :     return result;
     420             :   } else {
     421             :     // If we didn't actually change anything in doing the conversion
     422             :     // we simple return the result and let the converted string
     423             :     // become garbage; there is no reason to keep two identical strings
     424             :     // alive.
     425             :     return string;
     426             :   }
     427             : }
     428             : 
     429             : template <class Converter>
     430             : MUST_USE_RESULT static Object* ConvertCase(
     431             :     Handle<String> s, Isolate* isolate,
     432             :     unibrow::Mapping<Converter, 128>* mapping) {
     433             :   s = String::Flatten(s);
     434             :   int length = s->length();
     435             :   // Assume that the string is not empty; we need this assumption later
     436             :   if (length == 0) return *s;
     437             : 
     438             :   // Simpler handling of ASCII strings.
     439             :   //
     440             :   // NOTE: This assumes that the upper/lower case of an ASCII
     441             :   // character is also ASCII.  This is currently the case, but it
     442             :   // might break in the future if we implement more context and locale
     443             :   // dependent upper/lower conversions.
     444             :   if (s->IsOneByteRepresentationUnderneath()) {
     445             :     // Same length as input.
     446             :     Handle<SeqOneByteString> result =
     447             :         isolate->factory()->NewRawOneByteString(length).ToHandleChecked();
     448             :     DisallowHeapAllocation no_gc;
     449             :     String::FlatContent flat_content = s->GetFlatContent();
     450             :     DCHECK(flat_content.IsFlat());
     451             :     bool has_changed_character = false;
     452             :     int index_to_first_unprocessed = FastAsciiConvert<Converter::kIsToLower>(
     453             :         reinterpret_cast<char*>(result->GetChars()),
     454             :         reinterpret_cast<const char*>(flat_content.ToOneByteVector().start()),
     455             :         length, &has_changed_character);
     456             :     // If not ASCII, we discard the result and take the 2 byte path.
     457             :     if (index_to_first_unprocessed == length)
     458             :       return has_changed_character ? *result : *s;
     459             :   }
     460             : 
     461             :   Handle<SeqString> result;  // Same length as input.
     462             :   if (s->IsOneByteRepresentation()) {
     463             :     result = isolate->factory()->NewRawOneByteString(length).ToHandleChecked();
     464             :   } else {
     465             :     result = isolate->factory()->NewRawTwoByteString(length).ToHandleChecked();
     466             :   }
     467             : 
     468             :   Object* answer = ConvertCaseHelper(isolate, *s, *result, length, mapping);
     469             :   if (answer->IsException(isolate) || answer->IsString()) return answer;
     470             : 
     471             :   DCHECK(answer->IsSmi());
     472             :   length = Smi::ToInt(answer);
     473             :   if (s->IsOneByteRepresentation() && length > 0) {
     474             :     ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
     475             :         isolate, result, isolate->factory()->NewRawOneByteString(length));
     476             :   } else {
     477             :     if (length < 0) length = -length;
     478             :     ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
     479             :         isolate, result, isolate->factory()->NewRawTwoByteString(length));
     480             :   }
     481             :   return ConvertCaseHelper(isolate, *s, *result, length, mapping);
     482             : }
     483             : 
     484             : }  // namespace
     485             : 
     486             : BUILTIN(StringPrototypeToLocaleLowerCase) {
     487             :   HandleScope scope(isolate);
     488             :   TO_THIS_STRING(string, "String.prototype.toLocaleLowerCase");
     489             :   return ConvertCase(string, isolate,
     490             :                      isolate->runtime_state()->to_lower_mapping());
     491             : }
     492             : 
     493             : BUILTIN(StringPrototypeToLocaleUpperCase) {
     494             :   HandleScope scope(isolate);
     495             :   TO_THIS_STRING(string, "String.prototype.toLocaleUpperCase");
     496             :   return ConvertCase(string, isolate,
     497             :                      isolate->runtime_state()->to_upper_mapping());
     498             : }
     499             : 
     500             : BUILTIN(StringPrototypeToLowerCase) {
     501             :   HandleScope scope(isolate);
     502             :   TO_THIS_STRING(string, "String.prototype.toLowerCase");
     503             :   return ConvertCase(string, isolate,
     504             :                      isolate->runtime_state()->to_lower_mapping());
     505             : }
     506             : 
     507             : BUILTIN(StringPrototypeToUpperCase) {
     508             :   HandleScope scope(isolate);
     509             :   TO_THIS_STRING(string, "String.prototype.toUpperCase");
     510             :   return ConvertCase(string, isolate,
     511             :                      isolate->runtime_state()->to_upper_mapping());
     512             : }
     513             : #endif  // !V8_INTL_SUPPORT
     514             : 
     515             : // ES6 #sec-string.prototype.raw
     516        1269 : BUILTIN(StringRaw) {
     517             :   HandleScope scope(isolate);
     518         423 :   Handle<Object> templ = args.atOrUndefined(isolate, 1);
     519         423 :   const uint32_t argc = args.length();
     520             :   Handle<String> raw_string =
     521         423 :       isolate->factory()->NewStringFromAsciiChecked("raw");
     522             : 
     523             :   Handle<Object> cooked;
     524         846 :   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, cooked,
     525             :                                      Object::ToObject(isolate, templ));
     526             : 
     527             :   Handle<Object> raw;
     528         828 :   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, raw,
     529             :                                      Object::GetProperty(cooked, raw_string));
     530         828 :   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, raw,
     531             :                                      Object::ToObject(isolate, raw));
     532             :   Handle<Object> raw_len;
     533         810 :   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
     534             :       isolate, raw_len,
     535             :       Object::GetProperty(raw, isolate->factory()->length_string()));
     536             : 
     537         810 :   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, raw_len,
     538             :                                      Object::ToLength(isolate, raw_len));
     539             : 
     540         405 :   IncrementalStringBuilder result_builder(isolate);
     541         405 :   const uint32_t length = static_cast<uint32_t>(raw_len->Number());
     542         405 :   if (length > 0) {
     543             :     Handle<Object> first_element;
     544         621 :     ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, first_element,
     545             :                                        Object::GetElement(isolate, raw, 0));
     546             : 
     547             :     Handle<String> first_string;
     548         540 :     ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
     549             :         isolate, first_string, Object::ToString(isolate, first_element));
     550         243 :     result_builder.AppendString(first_string);
     551             : 
     552         387 :     for (uint32_t i = 1, arg_i = 2; i < length; i++, arg_i++) {
     553         414 :       if (arg_i < argc) {
     554             :         Handle<String> argument_string;
     555         837 :         ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
     556             :             isolate, argument_string,
     557             :             Object::ToString(isolate, args.at(arg_i)));
     558         252 :         result_builder.AppendString(argument_string);
     559             :       }
     560             : 
     561             :       Handle<Object> element;
     562         774 :       ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, element,
     563             :                                          Object::GetElement(isolate, raw, i));
     564             : 
     565             :       Handle<String> element_string;
     566         774 :       ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, element_string,
     567             :                                          Object::ToString(isolate, element));
     568         387 :       result_builder.AppendString(element_string);
     569             :     }
     570             :   }
     571             : 
     572        1026 :   RETURN_RESULT_OR_FAILURE(isolate, result_builder.Finish());
     573             : }
     574             : 
     575             : }  // namespace internal
     576             : }  // namespace v8

Generated by: LCOV version 1.10