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