runtime-strings.cc revision bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8
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/regexp/jsregexp-inl.h" 9#include "src/string-builder.h" 10#include "src/string-search.h" 11 12namespace v8 { 13namespace internal { 14 15 16// Perform string match of pattern on subject, starting at start index. 17// Caller must ensure that 0 <= start_index <= sub->length(), 18// and should check that pat->length() + start_index <= sub->length(). 19int StringMatch(Isolate* isolate, Handle<String> sub, Handle<String> pat, 20 int start_index) { 21 DCHECK(0 <= start_index); 22 DCHECK(start_index <= sub->length()); 23 24 int pattern_length = pat->length(); 25 if (pattern_length == 0) return start_index; 26 27 int subject_length = sub->length(); 28 if (start_index + pattern_length > subject_length) return -1; 29 30 sub = String::Flatten(sub); 31 pat = String::Flatten(pat); 32 33 DisallowHeapAllocation no_gc; // ensure vectors stay valid 34 // Extract flattened substrings of cons strings before getting encoding. 35 String::FlatContent seq_sub = sub->GetFlatContent(); 36 String::FlatContent seq_pat = pat->GetFlatContent(); 37 38 // dispatch on type of strings 39 if (seq_pat.IsOneByte()) { 40 Vector<const uint8_t> pat_vector = seq_pat.ToOneByteVector(); 41 if (seq_sub.IsOneByte()) { 42 return SearchString(isolate, seq_sub.ToOneByteVector(), pat_vector, 43 start_index); 44 } 45 return SearchString(isolate, seq_sub.ToUC16Vector(), pat_vector, 46 start_index); 47 } 48 Vector<const uc16> pat_vector = seq_pat.ToUC16Vector(); 49 if (seq_sub.IsOneByte()) { 50 return SearchString(isolate, seq_sub.ToOneByteVector(), pat_vector, 51 start_index); 52 } 53 return SearchString(isolate, seq_sub.ToUC16Vector(), pat_vector, start_index); 54} 55 56 57// This may return an empty MaybeHandle if an exception is thrown or 58// we abort due to reaching the recursion limit. 59MaybeHandle<String> StringReplaceOneCharWithString( 60 Isolate* isolate, Handle<String> subject, Handle<String> search, 61 Handle<String> replace, bool* found, int recursion_limit) { 62 StackLimitCheck stackLimitCheck(isolate); 63 if (stackLimitCheck.HasOverflowed() || (recursion_limit == 0)) { 64 return MaybeHandle<String>(); 65 } 66 recursion_limit--; 67 if (subject->IsConsString()) { 68 ConsString* cons = ConsString::cast(*subject); 69 Handle<String> first = Handle<String>(cons->first()); 70 Handle<String> second = Handle<String>(cons->second()); 71 Handle<String> new_first; 72 if (!StringReplaceOneCharWithString(isolate, first, search, replace, found, 73 recursion_limit).ToHandle(&new_first)) { 74 return MaybeHandle<String>(); 75 } 76 if (*found) return isolate->factory()->NewConsString(new_first, second); 77 78 Handle<String> new_second; 79 if (!StringReplaceOneCharWithString(isolate, second, search, replace, found, 80 recursion_limit) 81 .ToHandle(&new_second)) { 82 return MaybeHandle<String>(); 83 } 84 if (*found) return isolate->factory()->NewConsString(first, new_second); 85 86 return subject; 87 } else { 88 int index = StringMatch(isolate, subject, search, 0); 89 if (index == -1) return subject; 90 *found = true; 91 Handle<String> first = isolate->factory()->NewSubString(subject, 0, index); 92 Handle<String> cons1; 93 ASSIGN_RETURN_ON_EXCEPTION( 94 isolate, cons1, isolate->factory()->NewConsString(first, replace), 95 String); 96 Handle<String> second = 97 isolate->factory()->NewSubString(subject, index + 1, subject->length()); 98 return isolate->factory()->NewConsString(cons1, second); 99 } 100} 101 102 103RUNTIME_FUNCTION(Runtime_StringReplaceOneCharWithString) { 104 HandleScope scope(isolate); 105 DCHECK(args.length() == 3); 106 CONVERT_ARG_HANDLE_CHECKED(String, subject, 0); 107 CONVERT_ARG_HANDLE_CHECKED(String, search, 1); 108 CONVERT_ARG_HANDLE_CHECKED(String, replace, 2); 109 110 // If the cons string tree is too deep, we simply abort the recursion and 111 // retry with a flattened subject string. 112 const int kRecursionLimit = 0x1000; 113 bool found = false; 114 Handle<String> result; 115 if (StringReplaceOneCharWithString(isolate, subject, search, replace, &found, 116 kRecursionLimit).ToHandle(&result)) { 117 return *result; 118 } 119 if (isolate->has_pending_exception()) return isolate->heap()->exception(); 120 121 subject = String::Flatten(subject); 122 if (StringReplaceOneCharWithString(isolate, subject, search, replace, &found, 123 kRecursionLimit).ToHandle(&result)) { 124 return *result; 125 } 126 if (isolate->has_pending_exception()) return isolate->heap()->exception(); 127 // In case of empty handle and no pending exception we have stack overflow. 128 return isolate->StackOverflow(); 129} 130 131 132RUNTIME_FUNCTION(Runtime_StringIndexOf) { 133 HandleScope scope(isolate); 134 DCHECK(args.length() == 3); 135 136 CONVERT_ARG_HANDLE_CHECKED(String, sub, 0); 137 CONVERT_ARG_HANDLE_CHECKED(String, pat, 1); 138 CONVERT_ARG_HANDLE_CHECKED(Object, index, 2); 139 140 uint32_t start_index = 0; 141 if (!index->ToArrayIndex(&start_index)) return Smi::FromInt(-1); 142 143 RUNTIME_ASSERT(start_index <= static_cast<uint32_t>(sub->length())); 144 int position = StringMatch(isolate, sub, pat, start_index); 145 return Smi::FromInt(position); 146} 147 148 149template <typename schar, typename pchar> 150static int StringMatchBackwards(Vector<const schar> subject, 151 Vector<const pchar> pattern, int idx) { 152 int pattern_length = pattern.length(); 153 DCHECK(pattern_length >= 1); 154 DCHECK(idx + pattern_length <= subject.length()); 155 156 if (sizeof(schar) == 1 && sizeof(pchar) > 1) { 157 for (int i = 0; i < pattern_length; i++) { 158 uc16 c = pattern[i]; 159 if (c > String::kMaxOneByteCharCode) { 160 return -1; 161 } 162 } 163 } 164 165 pchar pattern_first_char = pattern[0]; 166 for (int i = idx; i >= 0; i--) { 167 if (subject[i] != pattern_first_char) continue; 168 int j = 1; 169 while (j < pattern_length) { 170 if (pattern[j] != subject[i + j]) { 171 break; 172 } 173 j++; 174 } 175 if (j == pattern_length) { 176 return i; 177 } 178 } 179 return -1; 180} 181 182 183RUNTIME_FUNCTION(Runtime_StringLastIndexOf) { 184 HandleScope scope(isolate); 185 DCHECK(args.length() == 3); 186 187 CONVERT_ARG_HANDLE_CHECKED(String, sub, 0); 188 CONVERT_ARG_HANDLE_CHECKED(String, pat, 1); 189 CONVERT_ARG_HANDLE_CHECKED(Object, index, 2); 190 191 uint32_t start_index = 0; 192 if (!index->ToArrayIndex(&start_index)) return Smi::FromInt(-1); 193 194 uint32_t pat_length = pat->length(); 195 uint32_t sub_length = sub->length(); 196 197 if (start_index + pat_length > sub_length) { 198 start_index = sub_length - pat_length; 199 } 200 201 if (pat_length == 0) { 202 return Smi::FromInt(start_index); 203 } 204 205 sub = String::Flatten(sub); 206 pat = String::Flatten(pat); 207 208 int position = -1; 209 DisallowHeapAllocation no_gc; // ensure vectors stay valid 210 211 String::FlatContent sub_content = sub->GetFlatContent(); 212 String::FlatContent pat_content = pat->GetFlatContent(); 213 214 if (pat_content.IsOneByte()) { 215 Vector<const uint8_t> pat_vector = pat_content.ToOneByteVector(); 216 if (sub_content.IsOneByte()) { 217 position = StringMatchBackwards(sub_content.ToOneByteVector(), pat_vector, 218 start_index); 219 } else { 220 position = StringMatchBackwards(sub_content.ToUC16Vector(), pat_vector, 221 start_index); 222 } 223 } else { 224 Vector<const uc16> pat_vector = pat_content.ToUC16Vector(); 225 if (sub_content.IsOneByte()) { 226 position = StringMatchBackwards(sub_content.ToOneByteVector(), pat_vector, 227 start_index); 228 } else { 229 position = StringMatchBackwards(sub_content.ToUC16Vector(), pat_vector, 230 start_index); 231 } 232 } 233 234 return Smi::FromInt(position); 235} 236 237 238RUNTIME_FUNCTION(Runtime_StringLocaleCompare) { 239 HandleScope handle_scope(isolate); 240 DCHECK(args.length() == 2); 241 242 CONVERT_ARG_HANDLE_CHECKED(String, str1, 0); 243 CONVERT_ARG_HANDLE_CHECKED(String, str2, 1); 244 245 if (str1.is_identical_to(str2)) return Smi::FromInt(0); // Equal. 246 int str1_length = str1->length(); 247 int str2_length = str2->length(); 248 249 // Decide trivial cases without flattening. 250 if (str1_length == 0) { 251 if (str2_length == 0) return Smi::FromInt(0); // Equal. 252 return Smi::FromInt(-str2_length); 253 } else { 254 if (str2_length == 0) return Smi::FromInt(str1_length); 255 } 256 257 int end = str1_length < str2_length ? str1_length : str2_length; 258 259 // No need to flatten if we are going to find the answer on the first 260 // character. At this point we know there is at least one character 261 // in each string, due to the trivial case handling above. 262 int d = str1->Get(0) - str2->Get(0); 263 if (d != 0) return Smi::FromInt(d); 264 265 str1 = String::Flatten(str1); 266 str2 = String::Flatten(str2); 267 268 DisallowHeapAllocation no_gc; 269 String::FlatContent flat1 = str1->GetFlatContent(); 270 String::FlatContent flat2 = str2->GetFlatContent(); 271 272 for (int i = 0; i < end; i++) { 273 if (flat1.Get(i) != flat2.Get(i)) { 274 return Smi::FromInt(flat1.Get(i) - flat2.Get(i)); 275 } 276 } 277 278 return Smi::FromInt(str1_length - str2_length); 279} 280 281 282RUNTIME_FUNCTION(Runtime_SubString) { 283 HandleScope scope(isolate); 284 DCHECK(args.length() == 3); 285 286 CONVERT_ARG_HANDLE_CHECKED(String, string, 0); 287 int start, end; 288 // We have a fast integer-only case here to avoid a conversion to double in 289 // the common case where from and to are Smis. 290 if (args[1]->IsSmi() && args[2]->IsSmi()) { 291 CONVERT_SMI_ARG_CHECKED(from_number, 1); 292 CONVERT_SMI_ARG_CHECKED(to_number, 2); 293 start = from_number; 294 end = to_number; 295 } else { 296 CONVERT_DOUBLE_ARG_CHECKED(from_number, 1); 297 CONVERT_DOUBLE_ARG_CHECKED(to_number, 2); 298 start = FastD2IChecked(from_number); 299 end = FastD2IChecked(to_number); 300 } 301 RUNTIME_ASSERT(end >= start); 302 RUNTIME_ASSERT(start >= 0); 303 RUNTIME_ASSERT(end <= string->length()); 304 isolate->counters()->sub_string_runtime()->Increment(); 305 306 return *isolate->factory()->NewSubString(string, start, end); 307} 308 309 310RUNTIME_FUNCTION(Runtime_StringAdd) { 311 HandleScope scope(isolate); 312 DCHECK(args.length() == 2); 313 CONVERT_ARG_HANDLE_CHECKED(String, str1, 0); 314 CONVERT_ARG_HANDLE_CHECKED(String, str2, 1); 315 isolate->counters()->string_add_runtime()->Increment(); 316 Handle<String> result; 317 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( 318 isolate, result, isolate->factory()->NewConsString(str1, str2)); 319 return *result; 320} 321 322 323RUNTIME_FUNCTION(Runtime_InternalizeString) { 324 HandleScope handles(isolate); 325 RUNTIME_ASSERT(args.length() == 1); 326 CONVERT_ARG_HANDLE_CHECKED(String, string, 0); 327 return *isolate->factory()->InternalizeString(string); 328} 329 330 331RUNTIME_FUNCTION(Runtime_StringMatch) { 332 HandleScope handles(isolate); 333 DCHECK(args.length() == 3); 334 335 CONVERT_ARG_HANDLE_CHECKED(String, subject, 0); 336 CONVERT_ARG_HANDLE_CHECKED(JSRegExp, regexp, 1); 337 CONVERT_ARG_HANDLE_CHECKED(JSArray, regexp_info, 2); 338 339 RUNTIME_ASSERT(regexp_info->HasFastObjectElements()); 340 341 RegExpImpl::GlobalCache global_cache(regexp, subject, isolate); 342 if (global_cache.HasException()) return isolate->heap()->exception(); 343 344 int capture_count = regexp->CaptureCount(); 345 346 ZoneScope zone_scope(isolate->runtime_zone()); 347 ZoneList<int> offsets(8, zone_scope.zone()); 348 349 while (true) { 350 int32_t* match = global_cache.FetchNext(); 351 if (match == NULL) break; 352 offsets.Add(match[0], zone_scope.zone()); // start 353 offsets.Add(match[1], zone_scope.zone()); // end 354 } 355 356 if (global_cache.HasException()) return isolate->heap()->exception(); 357 358 if (offsets.length() == 0) { 359 // Not a single match. 360 return isolate->heap()->null_value(); 361 } 362 363 RegExpImpl::SetLastMatchInfo(regexp_info, subject, capture_count, 364 global_cache.LastSuccessfulMatch()); 365 366 int matches = offsets.length() / 2; 367 Handle<FixedArray> elements = isolate->factory()->NewFixedArray(matches); 368 Handle<String> substring = 369 isolate->factory()->NewSubString(subject, offsets.at(0), offsets.at(1)); 370 elements->set(0, *substring); 371 FOR_WITH_HANDLE_SCOPE(isolate, int, i = 1, i, i < matches, i++, { 372 int from = offsets.at(i * 2); 373 int to = offsets.at(i * 2 + 1); 374 Handle<String> substring = 375 isolate->factory()->NewProperSubString(subject, from, to); 376 elements->set(i, *substring); 377 }); 378 Handle<JSArray> result = isolate->factory()->NewJSArrayWithElements(elements); 379 result->set_length(Smi::FromInt(matches)); 380 return *result; 381} 382 383 384RUNTIME_FUNCTION(Runtime_StringCharCodeAtRT) { 385 HandleScope handle_scope(isolate); 386 DCHECK(args.length() == 2); 387 388 CONVERT_ARG_HANDLE_CHECKED(String, subject, 0); 389 CONVERT_NUMBER_CHECKED(uint32_t, i, Uint32, args[1]); 390 391 // Flatten the string. If someone wants to get a char at an index 392 // in a cons string, it is likely that more indices will be 393 // accessed. 394 subject = String::Flatten(subject); 395 396 if (i >= static_cast<uint32_t>(subject->length())) { 397 return isolate->heap()->nan_value(); 398 } 399 400 return Smi::FromInt(subject->Get(i)); 401} 402 403 404RUNTIME_FUNCTION(Runtime_StringCompare) { 405 HandleScope handle_scope(isolate); 406 DCHECK_EQ(2, args.length()); 407 CONVERT_ARG_HANDLE_CHECKED(String, x, 0); 408 CONVERT_ARG_HANDLE_CHECKED(String, y, 1); 409 isolate->counters()->string_compare_runtime()->Increment(); 410 switch (String::Compare(x, y)) { 411 case ComparisonResult::kLessThan: 412 return Smi::FromInt(LESS); 413 case ComparisonResult::kEqual: 414 return Smi::FromInt(EQUAL); 415 case ComparisonResult::kGreaterThan: 416 return Smi::FromInt(GREATER); 417 case ComparisonResult::kUndefined: 418 break; 419 } 420 UNREACHABLE(); 421 return Smi::FromInt(0); 422} 423 424 425RUNTIME_FUNCTION(Runtime_StringBuilderConcat) { 426 HandleScope scope(isolate); 427 DCHECK(args.length() == 3); 428 CONVERT_ARG_HANDLE_CHECKED(JSArray, array, 0); 429 int32_t array_length; 430 if (!args[1]->ToInt32(&array_length)) { 431 THROW_NEW_ERROR_RETURN_FAILURE(isolate, NewInvalidStringLengthError()); 432 } 433 CONVERT_ARG_HANDLE_CHECKED(String, special, 2); 434 435 size_t actual_array_length = 0; 436 RUNTIME_ASSERT( 437 TryNumberToSize(isolate, array->length(), &actual_array_length)); 438 RUNTIME_ASSERT(array_length >= 0); 439 RUNTIME_ASSERT(static_cast<size_t>(array_length) <= actual_array_length); 440 441 // This assumption is used by the slice encoding in one or two smis. 442 DCHECK(Smi::kMaxValue >= String::kMaxLength); 443 444 RUNTIME_ASSERT(array->HasFastElements()); 445 JSObject::EnsureCanContainHeapObjectElements(array); 446 447 int special_length = special->length(); 448 if (!array->HasFastObjectElements()) { 449 return isolate->Throw(isolate->heap()->illegal_argument_string()); 450 } 451 452 int length; 453 bool one_byte = special->HasOnlyOneByteChars(); 454 455 { 456 DisallowHeapAllocation no_gc; 457 FixedArray* fixed_array = FixedArray::cast(array->elements()); 458 if (fixed_array->length() < array_length) { 459 array_length = fixed_array->length(); 460 } 461 462 if (array_length == 0) { 463 return isolate->heap()->empty_string(); 464 } else if (array_length == 1) { 465 Object* first = fixed_array->get(0); 466 if (first->IsString()) return first; 467 } 468 length = StringBuilderConcatLength(special_length, fixed_array, 469 array_length, &one_byte); 470 } 471 472 if (length == -1) { 473 return isolate->Throw(isolate->heap()->illegal_argument_string()); 474 } 475 476 if (one_byte) { 477 Handle<SeqOneByteString> answer; 478 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( 479 isolate, answer, isolate->factory()->NewRawOneByteString(length)); 480 StringBuilderConcatHelper(*special, answer->GetChars(), 481 FixedArray::cast(array->elements()), 482 array_length); 483 return *answer; 484 } else { 485 Handle<SeqTwoByteString> answer; 486 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( 487 isolate, answer, isolate->factory()->NewRawTwoByteString(length)); 488 StringBuilderConcatHelper(*special, answer->GetChars(), 489 FixedArray::cast(array->elements()), 490 array_length); 491 return *answer; 492 } 493} 494 495 496RUNTIME_FUNCTION(Runtime_StringBuilderJoin) { 497 HandleScope scope(isolate); 498 DCHECK(args.length() == 3); 499 CONVERT_ARG_HANDLE_CHECKED(JSArray, array, 0); 500 int32_t array_length; 501 if (!args[1]->ToInt32(&array_length)) { 502 THROW_NEW_ERROR_RETURN_FAILURE(isolate, NewInvalidStringLengthError()); 503 } 504 CONVERT_ARG_HANDLE_CHECKED(String, separator, 2); 505 RUNTIME_ASSERT(array->HasFastObjectElements()); 506 RUNTIME_ASSERT(array_length >= 0); 507 508 Handle<FixedArray> fixed_array(FixedArray::cast(array->elements())); 509 if (fixed_array->length() < array_length) { 510 array_length = fixed_array->length(); 511 } 512 513 if (array_length == 0) { 514 return isolate->heap()->empty_string(); 515 } else if (array_length == 1) { 516 Object* first = fixed_array->get(0); 517 RUNTIME_ASSERT(first->IsString()); 518 return first; 519 } 520 521 int separator_length = separator->length(); 522 RUNTIME_ASSERT(separator_length > 0); 523 int max_nof_separators = 524 (String::kMaxLength + separator_length - 1) / separator_length; 525 if (max_nof_separators < (array_length - 1)) { 526 THROW_NEW_ERROR_RETURN_FAILURE(isolate, NewInvalidStringLengthError()); 527 } 528 int length = (array_length - 1) * separator_length; 529 for (int i = 0; i < array_length; i++) { 530 Object* element_obj = fixed_array->get(i); 531 RUNTIME_ASSERT(element_obj->IsString()); 532 String* element = String::cast(element_obj); 533 int increment = element->length(); 534 if (increment > String::kMaxLength - length) { 535 STATIC_ASSERT(String::kMaxLength < kMaxInt); 536 length = kMaxInt; // Provoke exception; 537 break; 538 } 539 length += increment; 540 } 541 542 Handle<SeqTwoByteString> answer; 543 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( 544 isolate, answer, isolate->factory()->NewRawTwoByteString(length)); 545 546 DisallowHeapAllocation no_gc; 547 548 uc16* sink = answer->GetChars(); 549#ifdef DEBUG 550 uc16* end = sink + length; 551#endif 552 553 RUNTIME_ASSERT(fixed_array->get(0)->IsString()); 554 String* first = String::cast(fixed_array->get(0)); 555 String* separator_raw = *separator; 556 557 int first_length = first->length(); 558 String::WriteToFlat(first, sink, 0, first_length); 559 sink += first_length; 560 561 for (int i = 1; i < array_length; i++) { 562 DCHECK(sink + separator_length <= end); 563 String::WriteToFlat(separator_raw, sink, 0, separator_length); 564 sink += separator_length; 565 566 RUNTIME_ASSERT(fixed_array->get(i)->IsString()); 567 String* element = String::cast(fixed_array->get(i)); 568 int element_length = element->length(); 569 DCHECK(sink + element_length <= end); 570 String::WriteToFlat(element, sink, 0, element_length); 571 sink += element_length; 572 } 573 DCHECK(sink == end); 574 575 // Use %_FastOneByteArrayJoin instead. 576 DCHECK(!answer->IsOneByteRepresentation()); 577 return *answer; 578} 579 580template <typename sinkchar> 581static void WriteRepeatToFlat(String* src, Vector<sinkchar> buffer, int cursor, 582 int repeat, int length) { 583 if (repeat == 0) return; 584 585 sinkchar* start = &buffer[cursor]; 586 String::WriteToFlat<sinkchar>(src, start, 0, length); 587 588 int done = 1; 589 sinkchar* next = start + length; 590 591 while (done < repeat) { 592 int block = Min(done, repeat - done); 593 int block_chars = block * length; 594 CopyChars(next, start, block_chars); 595 next += block_chars; 596 done += block; 597 } 598} 599 600template <typename Char> 601static void JoinSparseArrayWithSeparator(FixedArray* elements, 602 int elements_length, 603 uint32_t array_length, 604 String* separator, 605 Vector<Char> buffer) { 606 DisallowHeapAllocation no_gc; 607 int previous_separator_position = 0; 608 int separator_length = separator->length(); 609 DCHECK_LT(0, separator_length); 610 int cursor = 0; 611 for (int i = 0; i < elements_length; i += 2) { 612 int position = NumberToInt32(elements->get(i)); 613 String* string = String::cast(elements->get(i + 1)); 614 int string_length = string->length(); 615 if (string->length() > 0) { 616 int repeat = position - previous_separator_position; 617 WriteRepeatToFlat<Char>(separator, buffer, cursor, repeat, 618 separator_length); 619 cursor += repeat * separator_length; 620 previous_separator_position = position; 621 String::WriteToFlat<Char>(string, &buffer[cursor], 0, string_length); 622 cursor += string->length(); 623 } 624 } 625 626 int last_array_index = static_cast<int>(array_length - 1); 627 // Array length must be representable as a signed 32-bit number, 628 // otherwise the total string length would have been too large. 629 DCHECK(array_length <= 0x7fffffff); // Is int32_t. 630 int repeat = last_array_index - previous_separator_position; 631 WriteRepeatToFlat<Char>(separator, buffer, cursor, repeat, separator_length); 632 cursor += repeat * separator_length; 633 DCHECK(cursor <= buffer.length()); 634} 635 636 637RUNTIME_FUNCTION(Runtime_SparseJoinWithSeparator) { 638 HandleScope scope(isolate); 639 DCHECK(args.length() == 3); 640 CONVERT_ARG_HANDLE_CHECKED(JSArray, elements_array, 0); 641 CONVERT_NUMBER_CHECKED(uint32_t, array_length, Uint32, args[1]); 642 CONVERT_ARG_HANDLE_CHECKED(String, separator, 2); 643 // elements_array is fast-mode JSarray of alternating positions 644 // (increasing order) and strings. 645 RUNTIME_ASSERT(elements_array->HasFastSmiOrObjectElements()); 646 // array_length is length of original array (used to add separators); 647 // separator is string to put between elements. Assumed to be non-empty. 648 RUNTIME_ASSERT(array_length > 0); 649 650 // Find total length of join result. 651 int string_length = 0; 652 bool is_one_byte = separator->IsOneByteRepresentation(); 653 bool overflow = false; 654 CONVERT_NUMBER_CHECKED(int, elements_length, Int32, elements_array->length()); 655 RUNTIME_ASSERT(elements_length <= elements_array->elements()->length()); 656 RUNTIME_ASSERT((elements_length & 1) == 0); // Even length. 657 FixedArray* elements = FixedArray::cast(elements_array->elements()); 658 { 659 DisallowHeapAllocation no_gc; 660 for (int i = 0; i < elements_length; i += 2) { 661 String* string = String::cast(elements->get(i + 1)); 662 int length = string->length(); 663 if (is_one_byte && !string->IsOneByteRepresentation()) { 664 is_one_byte = false; 665 } 666 if (length > String::kMaxLength || 667 String::kMaxLength - length < string_length) { 668 overflow = true; 669 break; 670 } 671 string_length += length; 672 } 673 } 674 675 int separator_length = separator->length(); 676 if (!overflow && separator_length > 0) { 677 if (array_length <= 0x7fffffffu) { 678 int separator_count = static_cast<int>(array_length) - 1; 679 int remaining_length = String::kMaxLength - string_length; 680 if ((remaining_length / separator_length) >= separator_count) { 681 string_length += separator_length * (array_length - 1); 682 } else { 683 // Not room for the separators within the maximal string length. 684 overflow = true; 685 } 686 } else { 687 // Nonempty separator and at least 2^31-1 separators necessary 688 // means that the string is too large to create. 689 STATIC_ASSERT(String::kMaxLength < 0x7fffffff); 690 overflow = true; 691 } 692 } 693 if (overflow) { 694 // Throw an exception if the resulting string is too large. See 695 // https://code.google.com/p/chromium/issues/detail?id=336820 696 // for details. 697 THROW_NEW_ERROR_RETURN_FAILURE(isolate, NewInvalidStringLengthError()); 698 } 699 700 if (is_one_byte) { 701 Handle<SeqOneByteString> result = isolate->factory() 702 ->NewRawOneByteString(string_length) 703 .ToHandleChecked(); 704 JoinSparseArrayWithSeparator<uint8_t>( 705 FixedArray::cast(elements_array->elements()), elements_length, 706 array_length, *separator, 707 Vector<uint8_t>(result->GetChars(), string_length)); 708 return *result; 709 } else { 710 Handle<SeqTwoByteString> result = isolate->factory() 711 ->NewRawTwoByteString(string_length) 712 .ToHandleChecked(); 713 JoinSparseArrayWithSeparator<uc16>( 714 FixedArray::cast(elements_array->elements()), elements_length, 715 array_length, *separator, 716 Vector<uc16>(result->GetChars(), string_length)); 717 return *result; 718 } 719} 720 721 722// Copies Latin1 characters to the given fixed array looking up 723// one-char strings in the cache. Gives up on the first char that is 724// not in the cache and fills the remainder with smi zeros. Returns 725// the length of the successfully copied prefix. 726static int CopyCachedOneByteCharsToArray(Heap* heap, const uint8_t* chars, 727 FixedArray* elements, int length) { 728 DisallowHeapAllocation no_gc; 729 FixedArray* one_byte_cache = heap->single_character_string_cache(); 730 Object* undefined = heap->undefined_value(); 731 int i; 732 WriteBarrierMode mode = elements->GetWriteBarrierMode(no_gc); 733 for (i = 0; i < length; ++i) { 734 Object* value = one_byte_cache->get(chars[i]); 735 if (value == undefined) break; 736 elements->set(i, value, mode); 737 } 738 if (i < length) { 739 DCHECK(Smi::FromInt(0) == 0); 740 memset(elements->data_start() + i, 0, kPointerSize * (length - i)); 741 } 742#ifdef DEBUG 743 for (int j = 0; j < length; ++j) { 744 Object* element = elements->get(j); 745 DCHECK(element == Smi::FromInt(0) || 746 (element->IsString() && String::cast(element)->LooksValid())); 747 } 748#endif 749 return i; 750} 751 752 753// Converts a String to JSArray. 754// For example, "foo" => ["f", "o", "o"]. 755RUNTIME_FUNCTION(Runtime_StringToArray) { 756 HandleScope scope(isolate); 757 DCHECK(args.length() == 2); 758 CONVERT_ARG_HANDLE_CHECKED(String, s, 0); 759 CONVERT_NUMBER_CHECKED(uint32_t, limit, Uint32, args[1]); 760 761 s = String::Flatten(s); 762 const int length = static_cast<int>(Min<uint32_t>(s->length(), limit)); 763 764 Handle<FixedArray> elements; 765 int position = 0; 766 if (s->IsFlat() && s->IsOneByteRepresentation()) { 767 // Try using cached chars where possible. 768 elements = isolate->factory()->NewUninitializedFixedArray(length); 769 770 DisallowHeapAllocation no_gc; 771 String::FlatContent content = s->GetFlatContent(); 772 if (content.IsOneByte()) { 773 Vector<const uint8_t> chars = content.ToOneByteVector(); 774 // Note, this will initialize all elements (not only the prefix) 775 // to prevent GC from seeing partially initialized array. 776 position = CopyCachedOneByteCharsToArray(isolate->heap(), chars.start(), 777 *elements, length); 778 } else { 779 MemsetPointer(elements->data_start(), isolate->heap()->undefined_value(), 780 length); 781 } 782 } else { 783 elements = isolate->factory()->NewFixedArray(length); 784 } 785 for (int i = position; i < length; ++i) { 786 Handle<Object> str = 787 isolate->factory()->LookupSingleCharacterStringFromCode(s->Get(i)); 788 elements->set(i, *str); 789 } 790 791#ifdef DEBUG 792 for (int i = 0; i < length; ++i) { 793 DCHECK(String::cast(elements->get(i))->length() == 1); 794 } 795#endif 796 797 return *isolate->factory()->NewJSArrayWithElements(elements); 798} 799 800 801static inline bool ToUpperOverflows(uc32 character) { 802 // y with umlauts and the micro sign are the only characters that stop 803 // fitting into one-byte when converting to uppercase. 804 static const uc32 yuml_code = 0xff; 805 static const uc32 micro_code = 0xb5; 806 return (character == yuml_code || character == micro_code); 807} 808 809 810template <class Converter> 811MUST_USE_RESULT static Object* ConvertCaseHelper( 812 Isolate* isolate, String* string, SeqString* result, int result_length, 813 unibrow::Mapping<Converter, 128>* mapping) { 814 DisallowHeapAllocation no_gc; 815 // We try this twice, once with the assumption that the result is no longer 816 // than the input and, if that assumption breaks, again with the exact 817 // length. This may not be pretty, but it is nicer than what was here before 818 // and I hereby claim my vaffel-is. 819 // 820 // NOTE: This assumes that the upper/lower case of an ASCII 821 // character is also ASCII. This is currently the case, but it 822 // might break in the future if we implement more context and locale 823 // dependent upper/lower conversions. 824 bool has_changed_character = false; 825 826 // Convert all characters to upper case, assuming that they will fit 827 // in the buffer 828 StringCharacterStream stream(string); 829 unibrow::uchar chars[Converter::kMaxWidth]; 830 // We can assume that the string is not empty 831 uc32 current = stream.GetNext(); 832 bool ignore_overflow = Converter::kIsToLower || result->IsSeqTwoByteString(); 833 for (int i = 0; i < result_length;) { 834 bool has_next = stream.HasMore(); 835 uc32 next = has_next ? stream.GetNext() : 0; 836 int char_length = mapping->get(current, next, chars); 837 if (char_length == 0) { 838 // The case conversion of this character is the character itself. 839 result->Set(i, current); 840 i++; 841 } else if (char_length == 1 && 842 (ignore_overflow || !ToUpperOverflows(current))) { 843 // Common case: converting the letter resulted in one character. 844 DCHECK(static_cast<uc32>(chars[0]) != current); 845 result->Set(i, chars[0]); 846 has_changed_character = true; 847 i++; 848 } else if (result_length == string->length()) { 849 bool overflows = ToUpperOverflows(current); 850 // We've assumed that the result would be as long as the 851 // input but here is a character that converts to several 852 // characters. No matter, we calculate the exact length 853 // of the result and try the whole thing again. 854 // 855 // Note that this leaves room for optimization. We could just 856 // memcpy what we already have to the result string. Also, 857 // the result string is the last object allocated we could 858 // "realloc" it and probably, in the vast majority of cases, 859 // extend the existing string to be able to hold the full 860 // result. 861 int next_length = 0; 862 if (has_next) { 863 next_length = mapping->get(next, 0, chars); 864 if (next_length == 0) next_length = 1; 865 } 866 int current_length = i + char_length + next_length; 867 while (stream.HasMore()) { 868 current = stream.GetNext(); 869 overflows |= ToUpperOverflows(current); 870 // NOTE: we use 0 as the next character here because, while 871 // the next character may affect what a character converts to, 872 // it does not in any case affect the length of what it convert 873 // to. 874 int char_length = mapping->get(current, 0, chars); 875 if (char_length == 0) char_length = 1; 876 current_length += char_length; 877 if (current_length > String::kMaxLength) { 878 AllowHeapAllocation allocate_error_and_return; 879 THROW_NEW_ERROR_RETURN_FAILURE(isolate, 880 NewInvalidStringLengthError()); 881 } 882 } 883 // Try again with the real length. Return signed if we need 884 // to allocate a two-byte string for to uppercase. 885 return (overflows && !ignore_overflow) ? Smi::FromInt(-current_length) 886 : Smi::FromInt(current_length); 887 } else { 888 for (int j = 0; j < char_length; j++) { 889 result->Set(i, chars[j]); 890 i++; 891 } 892 has_changed_character = true; 893 } 894 current = next; 895 } 896 if (has_changed_character) { 897 return result; 898 } else { 899 // If we didn't actually change anything in doing the conversion 900 // we simple return the result and let the converted string 901 // become garbage; there is no reason to keep two identical strings 902 // alive. 903 return string; 904 } 905} 906 907 908static const uintptr_t kOneInEveryByte = kUintptrAllBitsSet / 0xFF; 909static const uintptr_t kAsciiMask = kOneInEveryByte << 7; 910 911// Given a word and two range boundaries returns a word with high bit 912// set in every byte iff the corresponding input byte was strictly in 913// the range (m, n). All the other bits in the result are cleared. 914// This function is only useful when it can be inlined and the 915// boundaries are statically known. 916// Requires: all bytes in the input word and the boundaries must be 917// ASCII (less than 0x7F). 918static inline uintptr_t AsciiRangeMask(uintptr_t w, char m, char n) { 919 // Use strict inequalities since in edge cases the function could be 920 // further simplified. 921 DCHECK(0 < m && m < n); 922 // Has high bit set in every w byte less than n. 923 uintptr_t tmp1 = kOneInEveryByte * (0x7F + n) - w; 924 // Has high bit set in every w byte greater than m. 925 uintptr_t tmp2 = w + kOneInEveryByte * (0x7F - m); 926 return (tmp1 & tmp2 & (kOneInEveryByte * 0x80)); 927} 928 929 930#ifdef DEBUG 931static bool CheckFastAsciiConvert(char* dst, const char* src, int length, 932 bool changed, bool is_to_lower) { 933 bool expected_changed = false; 934 for (int i = 0; i < length; i++) { 935 if (dst[i] == src[i]) continue; 936 expected_changed = true; 937 if (is_to_lower) { 938 DCHECK('A' <= src[i] && src[i] <= 'Z'); 939 DCHECK(dst[i] == src[i] + ('a' - 'A')); 940 } else { 941 DCHECK('a' <= src[i] && src[i] <= 'z'); 942 DCHECK(dst[i] == src[i] - ('a' - 'A')); 943 } 944 } 945 return (expected_changed == changed); 946} 947#endif 948 949 950template <class Converter> 951static bool FastAsciiConvert(char* dst, const char* src, int length, 952 bool* changed_out) { 953#ifdef DEBUG 954 char* saved_dst = dst; 955 const char* saved_src = src; 956#endif 957 DisallowHeapAllocation no_gc; 958 // We rely on the distance between upper and lower case letters 959 // being a known power of 2. 960 DCHECK('a' - 'A' == (1 << 5)); 961 // Boundaries for the range of input characters than require conversion. 962 static const char lo = Converter::kIsToLower ? 'A' - 1 : 'a' - 1; 963 static const char hi = Converter::kIsToLower ? 'Z' + 1 : 'z' + 1; 964 bool changed = false; 965 uintptr_t or_acc = 0; 966 const char* const limit = src + length; 967 968 // dst is newly allocated and always aligned. 969 DCHECK(IsAligned(reinterpret_cast<intptr_t>(dst), sizeof(uintptr_t))); 970 // Only attempt processing one word at a time if src is also aligned. 971 if (IsAligned(reinterpret_cast<intptr_t>(src), sizeof(uintptr_t))) { 972 // Process the prefix of the input that requires no conversion one aligned 973 // (machine) word at a time. 974 while (src <= limit - sizeof(uintptr_t)) { 975 const uintptr_t w = *reinterpret_cast<const uintptr_t*>(src); 976 or_acc |= w; 977 if (AsciiRangeMask(w, lo, hi) != 0) { 978 changed = true; 979 break; 980 } 981 *reinterpret_cast<uintptr_t*>(dst) = w; 982 src += sizeof(uintptr_t); 983 dst += sizeof(uintptr_t); 984 } 985 // Process the remainder of the input performing conversion when 986 // required one word at a time. 987 while (src <= limit - sizeof(uintptr_t)) { 988 const uintptr_t w = *reinterpret_cast<const uintptr_t*>(src); 989 or_acc |= w; 990 uintptr_t m = AsciiRangeMask(w, lo, hi); 991 // The mask has high (7th) bit set in every byte that needs 992 // conversion and we know that the distance between cases is 993 // 1 << 5. 994 *reinterpret_cast<uintptr_t*>(dst) = w ^ (m >> 2); 995 src += sizeof(uintptr_t); 996 dst += sizeof(uintptr_t); 997 } 998 } 999 // Process the last few bytes of the input (or the whole input if 1000 // unaligned access is not supported). 1001 while (src < limit) { 1002 char c = *src; 1003 or_acc |= c; 1004 if (lo < c && c < hi) { 1005 c ^= (1 << 5); 1006 changed = true; 1007 } 1008 *dst = c; 1009 ++src; 1010 ++dst; 1011 } 1012 1013 if ((or_acc & kAsciiMask) != 0) return false; 1014 1015 DCHECK(CheckFastAsciiConvert(saved_dst, saved_src, length, changed, 1016 Converter::kIsToLower)); 1017 1018 *changed_out = changed; 1019 return true; 1020} 1021 1022 1023template <class Converter> 1024MUST_USE_RESULT static Object* ConvertCase( 1025 Handle<String> s, Isolate* isolate, 1026 unibrow::Mapping<Converter, 128>* mapping) { 1027 s = String::Flatten(s); 1028 int length = s->length(); 1029 // Assume that the string is not empty; we need this assumption later 1030 if (length == 0) return *s; 1031 1032 // Simpler handling of ASCII strings. 1033 // 1034 // NOTE: This assumes that the upper/lower case of an ASCII 1035 // character is also ASCII. This is currently the case, but it 1036 // might break in the future if we implement more context and locale 1037 // dependent upper/lower conversions. 1038 if (s->IsOneByteRepresentationUnderneath()) { 1039 // Same length as input. 1040 Handle<SeqOneByteString> result = 1041 isolate->factory()->NewRawOneByteString(length).ToHandleChecked(); 1042 DisallowHeapAllocation no_gc; 1043 String::FlatContent flat_content = s->GetFlatContent(); 1044 DCHECK(flat_content.IsFlat()); 1045 bool has_changed_character = false; 1046 bool is_ascii = FastAsciiConvert<Converter>( 1047 reinterpret_cast<char*>(result->GetChars()), 1048 reinterpret_cast<const char*>(flat_content.ToOneByteVector().start()), 1049 length, &has_changed_character); 1050 // If not ASCII, we discard the result and take the 2 byte path. 1051 if (is_ascii) return has_changed_character ? *result : *s; 1052 } 1053 1054 Handle<SeqString> result; // Same length as input. 1055 if (s->IsOneByteRepresentation()) { 1056 result = isolate->factory()->NewRawOneByteString(length).ToHandleChecked(); 1057 } else { 1058 result = isolate->factory()->NewRawTwoByteString(length).ToHandleChecked(); 1059 } 1060 1061 Object* answer = ConvertCaseHelper(isolate, *s, *result, length, mapping); 1062 if (answer->IsException() || answer->IsString()) return answer; 1063 1064 DCHECK(answer->IsSmi()); 1065 length = Smi::cast(answer)->value(); 1066 if (s->IsOneByteRepresentation() && length > 0) { 1067 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( 1068 isolate, result, isolate->factory()->NewRawOneByteString(length)); 1069 } else { 1070 if (length < 0) length = -length; 1071 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( 1072 isolate, result, isolate->factory()->NewRawTwoByteString(length)); 1073 } 1074 return ConvertCaseHelper(isolate, *s, *result, length, mapping); 1075} 1076 1077 1078RUNTIME_FUNCTION(Runtime_StringToLowerCase) { 1079 HandleScope scope(isolate); 1080 DCHECK_EQ(args.length(), 1); 1081 CONVERT_ARG_HANDLE_CHECKED(String, s, 0); 1082 return ConvertCase(s, isolate, isolate->runtime_state()->to_lower_mapping()); 1083} 1084 1085 1086RUNTIME_FUNCTION(Runtime_StringToUpperCase) { 1087 HandleScope scope(isolate); 1088 DCHECK_EQ(args.length(), 1); 1089 CONVERT_ARG_HANDLE_CHECKED(String, s, 0); 1090 return ConvertCase(s, isolate, isolate->runtime_state()->to_upper_mapping()); 1091} 1092 1093 1094RUNTIME_FUNCTION(Runtime_StringTrim) { 1095 HandleScope scope(isolate); 1096 DCHECK(args.length() == 3); 1097 1098 CONVERT_ARG_HANDLE_CHECKED(String, string, 0); 1099 CONVERT_BOOLEAN_ARG_CHECKED(trimLeft, 1); 1100 CONVERT_BOOLEAN_ARG_CHECKED(trimRight, 2); 1101 1102 string = String::Flatten(string); 1103 int length = string->length(); 1104 1105 int left = 0; 1106 UnicodeCache* unicode_cache = isolate->unicode_cache(); 1107 if (trimLeft) { 1108 while (left < length && 1109 unicode_cache->IsWhiteSpaceOrLineTerminator(string->Get(left))) { 1110 left++; 1111 } 1112 } 1113 1114 int right = length; 1115 if (trimRight) { 1116 while ( 1117 right > left && 1118 unicode_cache->IsWhiteSpaceOrLineTerminator(string->Get(right - 1))) { 1119 right--; 1120 } 1121 } 1122 1123 return *isolate->factory()->NewSubString(string, left, right); 1124} 1125 1126 1127RUNTIME_FUNCTION(Runtime_TruncateString) { 1128 HandleScope scope(isolate); 1129 DCHECK(args.length() == 2); 1130 CONVERT_ARG_HANDLE_CHECKED(SeqString, string, 0); 1131 CONVERT_INT32_ARG_CHECKED(new_length, 1); 1132 RUNTIME_ASSERT(new_length >= 0); 1133 return *SeqString::Truncate(string, new_length); 1134} 1135 1136 1137RUNTIME_FUNCTION(Runtime_NewString) { 1138 HandleScope scope(isolate); 1139 DCHECK(args.length() == 2); 1140 CONVERT_INT32_ARG_CHECKED(length, 0); 1141 CONVERT_BOOLEAN_ARG_CHECKED(is_one_byte, 1); 1142 if (length == 0) return isolate->heap()->empty_string(); 1143 Handle<String> result; 1144 if (is_one_byte) { 1145 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( 1146 isolate, result, isolate->factory()->NewRawOneByteString(length)); 1147 } else { 1148 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( 1149 isolate, result, isolate->factory()->NewRawTwoByteString(length)); 1150 } 1151 return *result; 1152} 1153 1154 1155RUNTIME_FUNCTION(Runtime_StringLessThan) { 1156 HandleScope handle_scope(isolate); 1157 DCHECK_EQ(2, args.length()); 1158 CONVERT_ARG_HANDLE_CHECKED(String, x, 0); 1159 CONVERT_ARG_HANDLE_CHECKED(String, y, 1); 1160 switch (String::Compare(x, y)) { 1161 case ComparisonResult::kLessThan: 1162 return isolate->heap()->true_value(); 1163 case ComparisonResult::kEqual: 1164 case ComparisonResult::kGreaterThan: 1165 return isolate->heap()->false_value(); 1166 case ComparisonResult::kUndefined: 1167 break; 1168 } 1169 UNREACHABLE(); 1170 return Smi::FromInt(0); 1171} 1172 1173RUNTIME_FUNCTION(Runtime_StringLessThanOrEqual) { 1174 HandleScope handle_scope(isolate); 1175 DCHECK_EQ(2, args.length()); 1176 CONVERT_ARG_HANDLE_CHECKED(String, x, 0); 1177 CONVERT_ARG_HANDLE_CHECKED(String, y, 1); 1178 switch (String::Compare(x, y)) { 1179 case ComparisonResult::kEqual: 1180 case ComparisonResult::kLessThan: 1181 return isolate->heap()->true_value(); 1182 case ComparisonResult::kGreaterThan: 1183 return isolate->heap()->false_value(); 1184 case ComparisonResult::kUndefined: 1185 break; 1186 } 1187 UNREACHABLE(); 1188 return Smi::FromInt(0); 1189} 1190 1191RUNTIME_FUNCTION(Runtime_StringGreaterThan) { 1192 HandleScope handle_scope(isolate); 1193 DCHECK_EQ(2, args.length()); 1194 CONVERT_ARG_HANDLE_CHECKED(String, x, 0); 1195 CONVERT_ARG_HANDLE_CHECKED(String, y, 1); 1196 switch (String::Compare(x, y)) { 1197 case ComparisonResult::kGreaterThan: 1198 return isolate->heap()->true_value(); 1199 case ComparisonResult::kEqual: 1200 case ComparisonResult::kLessThan: 1201 return isolate->heap()->false_value(); 1202 case ComparisonResult::kUndefined: 1203 break; 1204 } 1205 UNREACHABLE(); 1206 return Smi::FromInt(0); 1207} 1208 1209RUNTIME_FUNCTION(Runtime_StringGreaterThanOrEqual) { 1210 HandleScope handle_scope(isolate); 1211 DCHECK_EQ(2, args.length()); 1212 CONVERT_ARG_HANDLE_CHECKED(String, x, 0); 1213 CONVERT_ARG_HANDLE_CHECKED(String, y, 1); 1214 switch (String::Compare(x, y)) { 1215 case ComparisonResult::kEqual: 1216 case ComparisonResult::kGreaterThan: 1217 return isolate->heap()->true_value(); 1218 case ComparisonResult::kLessThan: 1219 return isolate->heap()->false_value(); 1220 case ComparisonResult::kUndefined: 1221 break; 1222 } 1223 UNREACHABLE(); 1224 return Smi::FromInt(0); 1225} 1226 1227RUNTIME_FUNCTION(Runtime_StringEqual) { 1228 HandleScope handle_scope(isolate); 1229 DCHECK_EQ(2, args.length()); 1230 CONVERT_ARG_HANDLE_CHECKED(String, x, 0); 1231 CONVERT_ARG_HANDLE_CHECKED(String, y, 1); 1232 return isolate->heap()->ToBoolean(String::Equals(x, y)); 1233} 1234 1235RUNTIME_FUNCTION(Runtime_StringNotEqual) { 1236 HandleScope handle_scope(isolate); 1237 DCHECK_EQ(2, args.length()); 1238 CONVERT_ARG_HANDLE_CHECKED(String, x, 0); 1239 CONVERT_ARG_HANDLE_CHECKED(String, y, 1); 1240 return isolate->heap()->ToBoolean(!String::Equals(x, y)); 1241} 1242 1243RUNTIME_FUNCTION(Runtime_FlattenString) { 1244 HandleScope scope(isolate); 1245 DCHECK(args.length() == 1); 1246 CONVERT_ARG_HANDLE_CHECKED(String, str, 0); 1247 return *String::Flatten(str); 1248} 1249 1250 1251RUNTIME_FUNCTION(Runtime_StringCharFromCode) { 1252 HandleScope handlescope(isolate); 1253 DCHECK_EQ(1, args.length()); 1254 if (args[0]->IsNumber()) { 1255 CONVERT_NUMBER_CHECKED(uint32_t, code, Uint32, args[0]); 1256 code &= 0xffff; 1257 return *isolate->factory()->LookupSingleCharacterStringFromCode(code); 1258 } 1259 return isolate->heap()->empty_string(); 1260} 1261 1262 1263RUNTIME_FUNCTION(Runtime_StringCharAt) { 1264 SealHandleScope shs(isolate); 1265 DCHECK(args.length() == 2); 1266 if (!args[0]->IsString()) return Smi::FromInt(0); 1267 if (!args[1]->IsNumber()) return Smi::FromInt(0); 1268 if (std::isinf(args.number_at(1))) return isolate->heap()->empty_string(); 1269 Object* code = __RT_impl_Runtime_StringCharCodeAtRT(args, isolate); 1270 if (code->IsNaN()) return isolate->heap()->empty_string(); 1271 return __RT_impl_Runtime_StringCharFromCode(Arguments(1, &code), isolate); 1272} 1273 1274RUNTIME_FUNCTION(Runtime_ExternalStringGetChar) { 1275 SealHandleScope shs(isolate); 1276 DCHECK_EQ(2, args.length()); 1277 CONVERT_ARG_CHECKED(ExternalString, string, 0); 1278 CONVERT_INT32_ARG_CHECKED(index, 1); 1279 return Smi::FromInt(string->Get(index)); 1280} 1281 1282RUNTIME_FUNCTION(Runtime_OneByteSeqStringGetChar) { 1283 SealHandleScope shs(isolate); 1284 DCHECK(args.length() == 2); 1285 CONVERT_ARG_CHECKED(SeqOneByteString, string, 0); 1286 CONVERT_INT32_ARG_CHECKED(index, 1); 1287 return Smi::FromInt(string->SeqOneByteStringGet(index)); 1288} 1289 1290 1291RUNTIME_FUNCTION(Runtime_OneByteSeqStringSetChar) { 1292 SealHandleScope shs(isolate); 1293 DCHECK(args.length() == 3); 1294 CONVERT_INT32_ARG_CHECKED(index, 0); 1295 CONVERT_INT32_ARG_CHECKED(value, 1); 1296 CONVERT_ARG_CHECKED(SeqOneByteString, string, 2); 1297 string->SeqOneByteStringSet(index, value); 1298 return string; 1299} 1300 1301 1302RUNTIME_FUNCTION(Runtime_TwoByteSeqStringGetChar) { 1303 SealHandleScope shs(isolate); 1304 DCHECK(args.length() == 2); 1305 CONVERT_ARG_CHECKED(SeqTwoByteString, string, 0); 1306 CONVERT_INT32_ARG_CHECKED(index, 1); 1307 return Smi::FromInt(string->SeqTwoByteStringGet(index)); 1308} 1309 1310 1311RUNTIME_FUNCTION(Runtime_TwoByteSeqStringSetChar) { 1312 SealHandleScope shs(isolate); 1313 DCHECK(args.length() == 3); 1314 CONVERT_INT32_ARG_CHECKED(index, 0); 1315 CONVERT_INT32_ARG_CHECKED(value, 1); 1316 CONVERT_ARG_CHECKED(SeqTwoByteString, string, 2); 1317 string->SeqTwoByteStringSet(index, value); 1318 return string; 1319} 1320 1321 1322RUNTIME_FUNCTION(Runtime_StringCharCodeAt) { 1323 SealHandleScope shs(isolate); 1324 DCHECK(args.length() == 2); 1325 if (!args[0]->IsString()) return isolate->heap()->undefined_value(); 1326 if (!args[1]->IsNumber()) return isolate->heap()->undefined_value(); 1327 if (std::isinf(args.number_at(1))) return isolate->heap()->nan_value(); 1328 return __RT_impl_Runtime_StringCharCodeAtRT(args, isolate); 1329} 1330 1331} // namespace internal 1332} // namespace v8 1333