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