| OLD | NEW |
| 1 // Copyright 2016 the V8 project authors. All rights reserved. | 1 // Copyright 2017 the V8 project authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "src/builtins/builtins-regexp.h" | 5 #include "src/builtins/builtins-regexp-gen.h" |
| 6 #include "src/builtins/builtins-utils.h" | 6 #include "src/builtins/builtins-utils-gen.h" |
| 7 #include "src/builtins/builtins.h" | 7 #include "src/builtins/builtins.h" |
| 8 #include "src/code-factory.h" | 8 #include "src/code-factory.h" |
| 9 #include "src/code-stub-assembler.h" | 9 #include "src/code-stub-assembler.h" |
| 10 #include "src/conversions.h" | 10 #include "src/objects.h" |
| 11 #include "src/counters.h" | |
| 12 #include "src/objects-inl.h" | |
| 13 #include "src/regexp/regexp-utils.h" | |
| 14 #include "src/string-case.h" | |
| 15 #include "src/unicode-inl.h" | |
| 16 #include "src/unicode.h" | |
| 17 | 11 |
| 18 namespace v8 { | 12 namespace v8 { |
| 19 namespace internal { | 13 namespace internal { |
| 20 | 14 |
| 21 typedef CodeStubAssembler::RelationalComparisonMode RelationalComparisonMode; | 15 typedef CodeStubAssembler::RelationalComparisonMode RelationalComparisonMode; |
| 22 | 16 |
| 23 class StringBuiltinsAssembler : public CodeStubAssembler { | 17 class StringBuiltinsAssembler : public CodeStubAssembler { |
| 24 public: | 18 public: |
| 25 explicit StringBuiltinsAssembler(compiler::CodeAssemblerState* state) | 19 explicit StringBuiltinsAssembler(compiler::CodeAssemblerState* state) |
| 26 : CodeStubAssembler(state) {} | 20 : CodeStubAssembler(state) {} |
| (...skipping 335 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 362 } | 356 } |
| 363 | 357 |
| 364 Bind(&if_done); | 358 Bind(&if_done); |
| 365 { | 359 { |
| 366 // All characters up to the min length are equal, decide based on | 360 // All characters up to the min length are equal, decide based on |
| 367 // string length. | 361 // string length. |
| 368 GotoIf(SmiEqual(lhs_length, rhs_length), &if_equal); | 362 GotoIf(SmiEqual(lhs_length, rhs_length), &if_equal); |
| 369 BranchIfSmiLessThan(lhs_length, rhs_length, &if_less, &if_greater); | 363 BranchIfSmiLessThan(lhs_length, rhs_length, &if_less, &if_greater); |
| 370 } | 364 } |
| 371 } | 365 } |
| 372 } | 366 } |
| 373 | 367 |
| 374 Bind(&if_notbothonebyteseqstrings); | 368 Bind(&if_notbothonebyteseqstrings); |
| 375 { | 369 { |
| 376 // Try to unwrap indirect strings, restart the above attempt on success. | 370 // Try to unwrap indirect strings, restart the above attempt on success. |
| 377 MaybeDerefIndirectStrings(&var_left, lhs_instance_type, &var_right, | 371 MaybeDerefIndirectStrings(&var_left, lhs_instance_type, &var_right, |
| 378 rhs_instance_type, &restart); | 372 rhs_instance_type, &restart); |
| 379 // TODO(bmeurer): Add support for two byte string relational comparisons. | 373 // TODO(bmeurer): Add support for two byte string relational comparisons. |
| 380 switch (mode) { | |
| 381 case RelationalComparisonMode::kLessThan: | |
| 382 TailCallRuntime(Runtime::kStringLessThan, context, lhs, rhs); | |
| 383 break; | |
| 384 case RelationalComparisonMode::kLessThanOrEqual: | |
| 385 TailCallRuntime(Runtime::kStringLessThanOrEqual, context, lhs, rhs); | |
| 386 break; | |
| 387 case RelationalComparisonMode::kGreaterThan: | |
| 388 TailCallRuntime(Runtime::kStringGreaterThan, context, lhs, rhs); | |
| 389 break; | |
| 390 case RelationalComparisonMode::kGreaterThanOrEqual: | |
| 391 TailCallRuntime(Runtime::kStringGreaterThanOrEqual, context, lhs, | |
| 392 rhs); | |
| 393 break; | |
| 394 } | |
| 395 } | |
| 396 | |
| 397 Bind(&if_less); | |
| 398 switch (mode) { | 374 switch (mode) { |
| 399 case RelationalComparisonMode::kLessThan: | 375 case RelationalComparisonMode::kLessThan: |
| 376 TailCallRuntime(Runtime::kStringLessThan, context, lhs, rhs); |
| 377 break; |
| 400 case RelationalComparisonMode::kLessThanOrEqual: | 378 case RelationalComparisonMode::kLessThanOrEqual: |
| 401 Return(BooleanConstant(true)); | 379 TailCallRuntime(Runtime::kStringLessThanOrEqual, context, lhs, rhs); |
| 402 break; | 380 break; |
| 381 case RelationalComparisonMode::kGreaterThan: |
| 382 TailCallRuntime(Runtime::kStringGreaterThan, context, lhs, rhs); |
| 383 break; |
| 384 case RelationalComparisonMode::kGreaterThanOrEqual: |
| 385 TailCallRuntime(Runtime::kStringGreaterThanOrEqual, context, lhs, rhs); |
| 386 break; |
| 387 } |
| 388 } |
| 403 | 389 |
| 404 case RelationalComparisonMode::kGreaterThan: | 390 Bind(&if_less); |
| 405 case RelationalComparisonMode::kGreaterThanOrEqual: | 391 switch (mode) { |
| 406 Return(BooleanConstant(false)); | 392 case RelationalComparisonMode::kLessThan: |
| 407 break; | 393 case RelationalComparisonMode::kLessThanOrEqual: |
| 394 Return(BooleanConstant(true)); |
| 395 break; |
| 396 |
| 397 case RelationalComparisonMode::kGreaterThan: |
| 398 case RelationalComparisonMode::kGreaterThanOrEqual: |
| 399 Return(BooleanConstant(false)); |
| 400 break; |
| 408 } | 401 } |
| 409 | 402 |
| 410 Bind(&if_equal); | 403 Bind(&if_equal); |
| 411 switch (mode) { | 404 switch (mode) { |
| 412 case RelationalComparisonMode::kLessThan: | 405 case RelationalComparisonMode::kLessThan: |
| 413 case RelationalComparisonMode::kGreaterThan: | 406 case RelationalComparisonMode::kGreaterThan: |
| 414 Return(BooleanConstant(false)); | 407 Return(BooleanConstant(false)); |
| 415 break; | 408 break; |
| 416 | 409 |
| 417 case RelationalComparisonMode::kLessThanOrEqual: | 410 case RelationalComparisonMode::kLessThanOrEqual: |
| (...skipping 166 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 584 StoreNoWriteBarrier(MachineRepresentation::kWord16, two_byte_result, | 577 StoreNoWriteBarrier(MachineRepresentation::kWord16, two_byte_result, |
| 585 offset, code16); | 578 offset, code16); |
| 586 max_index.Bind(IntPtrAdd(max_index.value(), IntPtrConstant(1))); | 579 max_index.Bind(IntPtrAdd(max_index.value(), IntPtrConstant(1))); |
| 587 }, | 580 }, |
| 588 max_index.value()); | 581 max_index.value()); |
| 589 | 582 |
| 590 arguments.PopAndReturn(two_byte_result); | 583 arguments.PopAndReturn(two_byte_result); |
| 591 } | 584 } |
| 592 } | 585 } |
| 593 | 586 |
| 594 namespace { // for String.fromCodePoint | |
| 595 | |
| 596 bool IsValidCodePoint(Isolate* isolate, Handle<Object> value) { | |
| 597 if (!value->IsNumber() && !Object::ToNumber(value).ToHandle(&value)) { | |
| 598 return false; | |
| 599 } | |
| 600 | |
| 601 if (Object::ToInteger(isolate, value).ToHandleChecked()->Number() != | |
| 602 value->Number()) { | |
| 603 return false; | |
| 604 } | |
| 605 | |
| 606 if (value->Number() < 0 || value->Number() > 0x10FFFF) { | |
| 607 return false; | |
| 608 } | |
| 609 | |
| 610 return true; | |
| 611 } | |
| 612 | |
| 613 uc32 NextCodePoint(Isolate* isolate, BuiltinArguments args, int index) { | |
| 614 Handle<Object> value = args.at(1 + index); | |
| 615 ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, value, Object::ToNumber(value), -1); | |
| 616 if (!IsValidCodePoint(isolate, value)) { | |
| 617 isolate->Throw(*isolate->factory()->NewRangeError( | |
| 618 MessageTemplate::kInvalidCodePoint, value)); | |
| 619 return -1; | |
| 620 } | |
| 621 return DoubleToUint32(value->Number()); | |
| 622 } | |
| 623 | |
| 624 } // namespace | |
| 625 | |
| 626 // ES6 section 21.1.2.2 String.fromCodePoint ( ...codePoints ) | |
| 627 BUILTIN(StringFromCodePoint) { | |
| 628 HandleScope scope(isolate); | |
| 629 int const length = args.length() - 1; | |
| 630 if (length == 0) return isolate->heap()->empty_string(); | |
| 631 DCHECK_LT(0, length); | |
| 632 | |
| 633 // Optimistically assume that the resulting String contains only one byte | |
| 634 // characters. | |
| 635 List<uint8_t> one_byte_buffer(length); | |
| 636 uc32 code = 0; | |
| 637 int index; | |
| 638 for (index = 0; index < length; index++) { | |
| 639 code = NextCodePoint(isolate, args, index); | |
| 640 if (code < 0) { | |
| 641 return isolate->heap()->exception(); | |
| 642 } | |
| 643 if (code > String::kMaxOneByteCharCode) { | |
| 644 break; | |
| 645 } | |
| 646 one_byte_buffer.Add(code); | |
| 647 } | |
| 648 | |
| 649 if (index == length) { | |
| 650 RETURN_RESULT_OR_FAILURE(isolate, isolate->factory()->NewStringFromOneByte( | |
| 651 one_byte_buffer.ToConstVector())); | |
| 652 } | |
| 653 | |
| 654 List<uc16> two_byte_buffer(length - index); | |
| 655 | |
| 656 while (true) { | |
| 657 if (code <= static_cast<uc32>(unibrow::Utf16::kMaxNonSurrogateCharCode)) { | |
| 658 two_byte_buffer.Add(code); | |
| 659 } else { | |
| 660 two_byte_buffer.Add(unibrow::Utf16::LeadSurrogate(code)); | |
| 661 two_byte_buffer.Add(unibrow::Utf16::TrailSurrogate(code)); | |
| 662 } | |
| 663 | |
| 664 if (++index == length) { | |
| 665 break; | |
| 666 } | |
| 667 code = NextCodePoint(isolate, args, index); | |
| 668 if (code < 0) { | |
| 669 return isolate->heap()->exception(); | |
| 670 } | |
| 671 } | |
| 672 | |
| 673 Handle<SeqTwoByteString> result; | |
| 674 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( | |
| 675 isolate, result, | |
| 676 isolate->factory()->NewRawTwoByteString(one_byte_buffer.length() + | |
| 677 two_byte_buffer.length())); | |
| 678 | |
| 679 CopyChars(result->GetChars(), one_byte_buffer.ToConstVector().start(), | |
| 680 one_byte_buffer.length()); | |
| 681 CopyChars(result->GetChars() + one_byte_buffer.length(), | |
| 682 two_byte_buffer.ToConstVector().start(), two_byte_buffer.length()); | |
| 683 | |
| 684 return *result; | |
| 685 } | |
| 686 | |
| 687 // ES6 section 21.1.3.1 String.prototype.charAt ( pos ) | 587 // ES6 section 21.1.3.1 String.prototype.charAt ( pos ) |
| 688 TF_BUILTIN(StringPrototypeCharAt, CodeStubAssembler) { | 588 TF_BUILTIN(StringPrototypeCharAt, CodeStubAssembler) { |
| 689 Node* receiver = Parameter(0); | 589 Node* receiver = Parameter(0); |
| 690 Node* position = Parameter(1); | 590 Node* position = Parameter(1); |
| 691 Node* context = Parameter(4); | 591 Node* context = Parameter(4); |
| 692 | 592 |
| 693 // Check that {receiver} is coercible to Object and convert it to a String. | 593 // Check that {receiver} is coercible to Object and convert it to a String. |
| 694 receiver = ToThisString(context, receiver, "String.prototype.charAt"); | 594 receiver = ToThisString(context, receiver, "String.prototype.charAt"); |
| 695 | 595 |
| 696 // Convert the {position} to a Smi and check that it's in bounds of the | 596 // Convert the {position} to a Smi and check that it's in bounds of the |
| (...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 753 | 653 |
| 754 Bind(&if_positioninbounds); | 654 Bind(&if_positioninbounds); |
| 755 } | 655 } |
| 756 | 656 |
| 757 // Load the character at the {position} from the {receiver}. | 657 // Load the character at the {position} from the {receiver}. |
| 758 Node* value = StringCharCodeAt(receiver, position); | 658 Node* value = StringCharCodeAt(receiver, position); |
| 759 Node* result = SmiFromWord32(value); | 659 Node* result = SmiFromWord32(value); |
| 760 Return(result); | 660 Return(result); |
| 761 } | 661 } |
| 762 | 662 |
| 763 // ES6 section 21.1.3.6 | |
| 764 // String.prototype.endsWith ( searchString [ , endPosition ] ) | |
| 765 BUILTIN(StringPrototypeEndsWith) { | |
| 766 HandleScope handle_scope(isolate); | |
| 767 TO_THIS_STRING(str, "String.prototype.endsWith"); | |
| 768 | |
| 769 // Check if the search string is a regExp and fail if it is. | |
| 770 Handle<Object> search = args.atOrUndefined(isolate, 1); | |
| 771 Maybe<bool> is_reg_exp = RegExpUtils::IsRegExp(isolate, search); | |
| 772 if (is_reg_exp.IsNothing()) { | |
| 773 DCHECK(isolate->has_pending_exception()); | |
| 774 return isolate->heap()->exception(); | |
| 775 } | |
| 776 if (is_reg_exp.FromJust()) { | |
| 777 THROW_NEW_ERROR_RETURN_FAILURE( | |
| 778 isolate, NewTypeError(MessageTemplate::kFirstArgumentNotRegExp, | |
| 779 isolate->factory()->NewStringFromStaticChars( | |
| 780 "String.prototype.endsWith"))); | |
| 781 } | |
| 782 Handle<String> search_string; | |
| 783 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, search_string, | |
| 784 Object::ToString(isolate, search)); | |
| 785 | |
| 786 Handle<Object> position = args.atOrUndefined(isolate, 2); | |
| 787 int end; | |
| 788 | |
| 789 if (position->IsUndefined(isolate)) { | |
| 790 end = str->length(); | |
| 791 } else { | |
| 792 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, position, | |
| 793 Object::ToInteger(isolate, position)); | |
| 794 end = str->ToValidIndex(*position); | |
| 795 } | |
| 796 | |
| 797 int start = end - search_string->length(); | |
| 798 if (start < 0) return isolate->heap()->false_value(); | |
| 799 | |
| 800 str = String::Flatten(str); | |
| 801 search_string = String::Flatten(search_string); | |
| 802 | |
| 803 DisallowHeapAllocation no_gc; // ensure vectors stay valid | |
| 804 String::FlatContent str_content = str->GetFlatContent(); | |
| 805 String::FlatContent search_content = search_string->GetFlatContent(); | |
| 806 | |
| 807 if (str_content.IsOneByte() && search_content.IsOneByte()) { | |
| 808 Vector<const uint8_t> str_vector = str_content.ToOneByteVector(); | |
| 809 Vector<const uint8_t> search_vector = search_content.ToOneByteVector(); | |
| 810 | |
| 811 return isolate->heap()->ToBoolean(memcmp(str_vector.start() + start, | |
| 812 search_vector.start(), | |
| 813 search_string->length()) == 0); | |
| 814 } | |
| 815 | |
| 816 FlatStringReader str_reader(isolate, str); | |
| 817 FlatStringReader search_reader(isolate, search_string); | |
| 818 | |
| 819 for (int i = 0; i < search_string->length(); i++) { | |
| 820 if (str_reader.Get(start + i) != search_reader.Get(i)) { | |
| 821 return isolate->heap()->false_value(); | |
| 822 } | |
| 823 } | |
| 824 return isolate->heap()->true_value(); | |
| 825 } | |
| 826 | |
| 827 // ES6 section 21.1.3.7 | |
| 828 // String.prototype.includes ( searchString [ , position ] ) | |
| 829 BUILTIN(StringPrototypeIncludes) { | |
| 830 HandleScope handle_scope(isolate); | |
| 831 TO_THIS_STRING(str, "String.prototype.includes"); | |
| 832 | |
| 833 // Check if the search string is a regExp and fail if it is. | |
| 834 Handle<Object> search = args.atOrUndefined(isolate, 1); | |
| 835 Maybe<bool> is_reg_exp = RegExpUtils::IsRegExp(isolate, search); | |
| 836 if (is_reg_exp.IsNothing()) { | |
| 837 DCHECK(isolate->has_pending_exception()); | |
| 838 return isolate->heap()->exception(); | |
| 839 } | |
| 840 if (is_reg_exp.FromJust()) { | |
| 841 THROW_NEW_ERROR_RETURN_FAILURE( | |
| 842 isolate, NewTypeError(MessageTemplate::kFirstArgumentNotRegExp, | |
| 843 isolate->factory()->NewStringFromStaticChars( | |
| 844 "String.prototype.includes"))); | |
| 845 } | |
| 846 Handle<String> search_string; | |
| 847 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, search_string, | |
| 848 Object::ToString(isolate, search)); | |
| 849 Handle<Object> position; | |
| 850 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( | |
| 851 isolate, position, | |
| 852 Object::ToInteger(isolate, args.atOrUndefined(isolate, 2))); | |
| 853 | |
| 854 uint32_t index = str->ToValidIndex(*position); | |
| 855 int index_in_str = String::IndexOf(isolate, str, search_string, index); | |
| 856 return *isolate->factory()->ToBoolean(index_in_str != -1); | |
| 857 } | |
| 858 | |
| 859 void StringBuiltinsAssembler::StringIndexOf( | 663 void StringBuiltinsAssembler::StringIndexOf( |
| 860 Node* receiver, Node* instance_type, Node* search_string, | 664 Node* receiver, Node* instance_type, Node* search_string, |
| 861 Node* search_string_instance_type, Node* position, | 665 Node* search_string_instance_type, Node* position, |
| 862 std::function<void(Node*)> f_return) { | 666 std::function<void(Node*)> f_return) { |
| 863 CSA_ASSERT(this, IsString(receiver)); | 667 CSA_ASSERT(this, IsString(receiver)); |
| 864 CSA_ASSERT(this, IsString(search_string)); | 668 CSA_ASSERT(this, IsString(search_string)); |
| 865 CSA_ASSERT(this, TaggedIsSmi(position)); | 669 CSA_ASSERT(this, TaggedIsSmi(position)); |
| 866 | 670 |
| 867 Label zero_length_needle(this), | 671 Label zero_length_needle(this), |
| 868 call_runtime_unchecked(this, Label::kDeferred), return_minus_1(this), | 672 call_runtime_unchecked(this, Label::kDeferred), return_minus_1(this), |
| (...skipping 189 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1058 | 862 |
| 1059 Bind(&call_runtime); | 863 Bind(&call_runtime); |
| 1060 { | 864 { |
| 1061 Comment("Call Runtime"); | 865 Comment("Call Runtime"); |
| 1062 Node* result = CallRuntime(Runtime::kStringIndexOf, context, receiver, | 866 Node* result = CallRuntime(Runtime::kStringIndexOf, context, receiver, |
| 1063 search_string.value(), position.value()); | 867 search_string.value(), position.value()); |
| 1064 arguments.PopAndReturn(result); | 868 arguments.PopAndReturn(result); |
| 1065 } | 869 } |
| 1066 } | 870 } |
| 1067 | 871 |
| 1068 // ES6 section 21.1.3.9 | |
| 1069 // String.prototype.lastIndexOf ( searchString [ , position ] ) | |
| 1070 BUILTIN(StringPrototypeLastIndexOf) { | |
| 1071 HandleScope handle_scope(isolate); | |
| 1072 return String::LastIndexOf(isolate, args.receiver(), | |
| 1073 args.atOrUndefined(isolate, 1), | |
| 1074 args.atOrUndefined(isolate, 2)); | |
| 1075 } | |
| 1076 | |
| 1077 // ES6 section 21.1.3.10 String.prototype.localeCompare ( that ) | |
| 1078 // | |
| 1079 // This function is implementation specific. For now, we do not | |
| 1080 // do anything locale specific. | |
| 1081 // If internationalization is enabled, then i18n.js will override this function | |
| 1082 // and provide the proper functionality, so this is just a fallback. | |
| 1083 BUILTIN(StringPrototypeLocaleCompare) { | |
| 1084 HandleScope handle_scope(isolate); | |
| 1085 DCHECK_EQ(2, args.length()); | |
| 1086 | |
| 1087 TO_THIS_STRING(str1, "String.prototype.localeCompare"); | |
| 1088 Handle<String> str2; | |
| 1089 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, str2, | |
| 1090 Object::ToString(isolate, args.at(1))); | |
| 1091 | |
| 1092 if (str1.is_identical_to(str2)) return Smi::kZero; // Equal. | |
| 1093 int str1_length = str1->length(); | |
| 1094 int str2_length = str2->length(); | |
| 1095 | |
| 1096 // Decide trivial cases without flattening. | |
| 1097 if (str1_length == 0) { | |
| 1098 if (str2_length == 0) return Smi::kZero; // Equal. | |
| 1099 return Smi::FromInt(-str2_length); | |
| 1100 } else { | |
| 1101 if (str2_length == 0) return Smi::FromInt(str1_length); | |
| 1102 } | |
| 1103 | |
| 1104 int end = str1_length < str2_length ? str1_length : str2_length; | |
| 1105 | |
| 1106 // No need to flatten if we are going to find the answer on the first | |
| 1107 // character. At this point we know there is at least one character | |
| 1108 // in each string, due to the trivial case handling above. | |
| 1109 int d = str1->Get(0) - str2->Get(0); | |
| 1110 if (d != 0) return Smi::FromInt(d); | |
| 1111 | |
| 1112 str1 = String::Flatten(str1); | |
| 1113 str2 = String::Flatten(str2); | |
| 1114 | |
| 1115 DisallowHeapAllocation no_gc; | |
| 1116 String::FlatContent flat1 = str1->GetFlatContent(); | |
| 1117 String::FlatContent flat2 = str2->GetFlatContent(); | |
| 1118 | |
| 1119 for (int i = 0; i < end; i++) { | |
| 1120 if (flat1.Get(i) != flat2.Get(i)) { | |
| 1121 return Smi::FromInt(flat1.Get(i) - flat2.Get(i)); | |
| 1122 } | |
| 1123 } | |
| 1124 | |
| 1125 return Smi::FromInt(str1_length - str2_length); | |
| 1126 } | |
| 1127 | |
| 1128 // ES6 section 21.1.3.12 String.prototype.normalize ( [form] ) | |
| 1129 // | |
| 1130 // Simply checks the argument is valid and returns the string itself. | |
| 1131 // If internationalization is enabled, then i18n.js will override this function | |
| 1132 // and provide the proper functionality, so this is just a fallback. | |
| 1133 BUILTIN(StringPrototypeNormalize) { | |
| 1134 HandleScope handle_scope(isolate); | |
| 1135 TO_THIS_STRING(string, "String.prototype.normalize"); | |
| 1136 | |
| 1137 Handle<Object> form_input = args.atOrUndefined(isolate, 1); | |
| 1138 if (form_input->IsUndefined(isolate)) return *string; | |
| 1139 | |
| 1140 Handle<String> form; | |
| 1141 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, form, | |
| 1142 Object::ToString(isolate, form_input)); | |
| 1143 | |
| 1144 if (!(String::Equals(form, | |
| 1145 isolate->factory()->NewStringFromStaticChars("NFC")) || | |
| 1146 String::Equals(form, | |
| 1147 isolate->factory()->NewStringFromStaticChars("NFD")) || | |
| 1148 String::Equals(form, | |
| 1149 isolate->factory()->NewStringFromStaticChars("NFKC")) || | |
| 1150 String::Equals(form, | |
| 1151 isolate->factory()->NewStringFromStaticChars("NFKD")))) { | |
| 1152 Handle<String> valid_forms = | |
| 1153 isolate->factory()->NewStringFromStaticChars("NFC, NFD, NFKC, NFKD"); | |
| 1154 THROW_NEW_ERROR_RETURN_FAILURE( | |
| 1155 isolate, | |
| 1156 NewRangeError(MessageTemplate::kNormalizationForm, valid_forms)); | |
| 1157 } | |
| 1158 | |
| 1159 return *string; | |
| 1160 } | |
| 1161 | |
| 1162 compiler::Node* StringBuiltinsAssembler::IsNullOrUndefined(Node* const value) { | 872 compiler::Node* StringBuiltinsAssembler::IsNullOrUndefined(Node* const value) { |
| 1163 return Word32Or(IsUndefined(value), IsNull(value)); | 873 return Word32Or(IsUndefined(value), IsNull(value)); |
| 1164 } | 874 } |
| 1165 | 875 |
| 1166 void StringBuiltinsAssembler::RequireObjectCoercible(Node* const context, | 876 void StringBuiltinsAssembler::RequireObjectCoercible(Node* const context, |
| 1167 Node* const value, | 877 Node* const value, |
| 1168 const char* method_name) { | 878 const char* method_name) { |
| 1169 Label out(this), throw_exception(this, Label::kDeferred); | 879 Label out(this), throw_exception(this, Label::kDeferred); |
| 1170 Branch(IsNullOrUndefined(value), &throw_exception, &out); | 880 Branch(IsNullOrUndefined(value), &throw_exception, &out); |
| 1171 | 881 |
| (...skipping 547 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1719 } | 1429 } |
| 1720 | 1430 |
| 1721 Bind(&out); | 1431 Bind(&out); |
| 1722 { | 1432 { |
| 1723 Node* result = | 1433 Node* result = |
| 1724 SubString(context, string, var_start.value(), var_end.value()); | 1434 SubString(context, string, var_start.value(), var_end.value()); |
| 1725 Return(result); | 1435 Return(result); |
| 1726 } | 1436 } |
| 1727 } | 1437 } |
| 1728 | 1438 |
| 1729 BUILTIN(StringPrototypeStartsWith) { | |
| 1730 HandleScope handle_scope(isolate); | |
| 1731 TO_THIS_STRING(str, "String.prototype.startsWith"); | |
| 1732 | |
| 1733 // Check if the search string is a regExp and fail if it is. | |
| 1734 Handle<Object> search = args.atOrUndefined(isolate, 1); | |
| 1735 Maybe<bool> is_reg_exp = RegExpUtils::IsRegExp(isolate, search); | |
| 1736 if (is_reg_exp.IsNothing()) { | |
| 1737 DCHECK(isolate->has_pending_exception()); | |
| 1738 return isolate->heap()->exception(); | |
| 1739 } | |
| 1740 if (is_reg_exp.FromJust()) { | |
| 1741 THROW_NEW_ERROR_RETURN_FAILURE( | |
| 1742 isolate, NewTypeError(MessageTemplate::kFirstArgumentNotRegExp, | |
| 1743 isolate->factory()->NewStringFromStaticChars( | |
| 1744 "String.prototype.startsWith"))); | |
| 1745 } | |
| 1746 Handle<String> search_string; | |
| 1747 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, search_string, | |
| 1748 Object::ToString(isolate, search)); | |
| 1749 | |
| 1750 Handle<Object> position = args.atOrUndefined(isolate, 2); | |
| 1751 int start; | |
| 1752 | |
| 1753 if (position->IsUndefined(isolate)) { | |
| 1754 start = 0; | |
| 1755 } else { | |
| 1756 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, position, | |
| 1757 Object::ToInteger(isolate, position)); | |
| 1758 start = str->ToValidIndex(*position); | |
| 1759 } | |
| 1760 | |
| 1761 if (start + search_string->length() > str->length()) { | |
| 1762 return isolate->heap()->false_value(); | |
| 1763 } | |
| 1764 | |
| 1765 FlatStringReader str_reader(isolate, String::Flatten(str)); | |
| 1766 FlatStringReader search_reader(isolate, String::Flatten(search_string)); | |
| 1767 | |
| 1768 for (int i = 0; i < search_string->length(); i++) { | |
| 1769 if (str_reader.Get(start + i) != search_reader.Get(i)) { | |
| 1770 return isolate->heap()->false_value(); | |
| 1771 } | |
| 1772 } | |
| 1773 return isolate->heap()->true_value(); | |
| 1774 } | |
| 1775 | |
| 1776 // ES6 section 21.1.3.25 String.prototype.toString () | 1439 // ES6 section 21.1.3.25 String.prototype.toString () |
| 1777 TF_BUILTIN(StringPrototypeToString, CodeStubAssembler) { | 1440 TF_BUILTIN(StringPrototypeToString, CodeStubAssembler) { |
| 1778 Node* receiver = Parameter(0); | 1441 Node* receiver = Parameter(0); |
| 1779 Node* context = Parameter(3); | 1442 Node* context = Parameter(3); |
| 1780 | 1443 |
| 1781 Node* result = ToThisValue(context, receiver, PrimitiveType::kString, | 1444 Node* result = ToThisValue(context, receiver, PrimitiveType::kString, |
| 1782 "String.prototype.toString"); | 1445 "String.prototype.toString"); |
| 1783 Return(result); | 1446 Return(result); |
| 1784 } | 1447 } |
| 1785 | 1448 |
| 1786 // ES6 section 21.1.3.27 String.prototype.trim () | |
| 1787 BUILTIN(StringPrototypeTrim) { | |
| 1788 HandleScope scope(isolate); | |
| 1789 TO_THIS_STRING(string, "String.prototype.trim"); | |
| 1790 return *String::Trim(string, String::kTrim); | |
| 1791 } | |
| 1792 | |
| 1793 // Non-standard WebKit extension | |
| 1794 BUILTIN(StringPrototypeTrimLeft) { | |
| 1795 HandleScope scope(isolate); | |
| 1796 TO_THIS_STRING(string, "String.prototype.trimLeft"); | |
| 1797 return *String::Trim(string, String::kTrimLeft); | |
| 1798 } | |
| 1799 | |
| 1800 // Non-standard WebKit extension | |
| 1801 BUILTIN(StringPrototypeTrimRight) { | |
| 1802 HandleScope scope(isolate); | |
| 1803 TO_THIS_STRING(string, "String.prototype.trimRight"); | |
| 1804 return *String::Trim(string, String::kTrimRight); | |
| 1805 } | |
| 1806 | |
| 1807 // ES6 section 21.1.3.28 String.prototype.valueOf ( ) | 1449 // ES6 section 21.1.3.28 String.prototype.valueOf ( ) |
| 1808 TF_BUILTIN(StringPrototypeValueOf, CodeStubAssembler) { | 1450 TF_BUILTIN(StringPrototypeValueOf, CodeStubAssembler) { |
| 1809 Node* receiver = Parameter(0); | 1451 Node* receiver = Parameter(0); |
| 1810 Node* context = Parameter(3); | 1452 Node* context = Parameter(3); |
| 1811 | 1453 |
| 1812 Node* result = ToThisValue(context, receiver, PrimitiveType::kString, | 1454 Node* result = ToThisValue(context, receiver, PrimitiveType::kString, |
| 1813 "String.prototype.valueOf"); | 1455 "String.prototype.valueOf"); |
| 1814 Return(result); | 1456 Return(result); |
| 1815 } | 1457 } |
| 1816 | 1458 |
| (...skipping 145 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1962 { | 1604 { |
| 1963 // The {receiver} is not a valid JSGeneratorObject. | 1605 // The {receiver} is not a valid JSGeneratorObject. |
| 1964 CallRuntime(Runtime::kThrowIncompatibleMethodReceiver, context, | 1606 CallRuntime(Runtime::kThrowIncompatibleMethodReceiver, context, |
| 1965 HeapConstant(factory()->NewStringFromAsciiChecked( | 1607 HeapConstant(factory()->NewStringFromAsciiChecked( |
| 1966 "String Iterator.prototype.next", TENURED)), | 1608 "String Iterator.prototype.next", TENURED)), |
| 1967 iterator); | 1609 iterator); |
| 1968 Unreachable(); | 1610 Unreachable(); |
| 1969 } | 1611 } |
| 1970 } | 1612 } |
| 1971 | 1613 |
| 1972 namespace { | |
| 1973 | |
| 1974 inline bool ToUpperOverflows(uc32 character) { | |
| 1975 // y with umlauts and the micro sign are the only characters that stop | |
| 1976 // fitting into one-byte when converting to uppercase. | |
| 1977 static const uc32 yuml_code = 0xff; | |
| 1978 static const uc32 micro_code = 0xb5; | |
| 1979 return (character == yuml_code || character == micro_code); | |
| 1980 } | |
| 1981 | |
| 1982 template <class Converter> | |
| 1983 MUST_USE_RESULT static Object* ConvertCaseHelper( | |
| 1984 Isolate* isolate, String* string, SeqString* result, int result_length, | |
| 1985 unibrow::Mapping<Converter, 128>* mapping) { | |
| 1986 DisallowHeapAllocation no_gc; | |
| 1987 // We try this twice, once with the assumption that the result is no longer | |
| 1988 // than the input and, if that assumption breaks, again with the exact | |
| 1989 // length. This may not be pretty, but it is nicer than what was here before | |
| 1990 // and I hereby claim my vaffel-is. | |
| 1991 // | |
| 1992 // NOTE: This assumes that the upper/lower case of an ASCII | |
| 1993 // character is also ASCII. This is currently the case, but it | |
| 1994 // might break in the future if we implement more context and locale | |
| 1995 // dependent upper/lower conversions. | |
| 1996 bool has_changed_character = false; | |
| 1997 | |
| 1998 // Convert all characters to upper case, assuming that they will fit | |
| 1999 // in the buffer | |
| 2000 StringCharacterStream stream(string); | |
| 2001 unibrow::uchar chars[Converter::kMaxWidth]; | |
| 2002 // We can assume that the string is not empty | |
| 2003 uc32 current = stream.GetNext(); | |
| 2004 bool ignore_overflow = Converter::kIsToLower || result->IsSeqTwoByteString(); | |
| 2005 for (int i = 0; i < result_length;) { | |
| 2006 bool has_next = stream.HasMore(); | |
| 2007 uc32 next = has_next ? stream.GetNext() : 0; | |
| 2008 int char_length = mapping->get(current, next, chars); | |
| 2009 if (char_length == 0) { | |
| 2010 // The case conversion of this character is the character itself. | |
| 2011 result->Set(i, current); | |
| 2012 i++; | |
| 2013 } else if (char_length == 1 && | |
| 2014 (ignore_overflow || !ToUpperOverflows(current))) { | |
| 2015 // Common case: converting the letter resulted in one character. | |
| 2016 DCHECK(static_cast<uc32>(chars[0]) != current); | |
| 2017 result->Set(i, chars[0]); | |
| 2018 has_changed_character = true; | |
| 2019 i++; | |
| 2020 } else if (result_length == string->length()) { | |
| 2021 bool overflows = ToUpperOverflows(current); | |
| 2022 // We've assumed that the result would be as long as the | |
| 2023 // input but here is a character that converts to several | |
| 2024 // characters. No matter, we calculate the exact length | |
| 2025 // of the result and try the whole thing again. | |
| 2026 // | |
| 2027 // Note that this leaves room for optimization. We could just | |
| 2028 // memcpy what we already have to the result string. Also, | |
| 2029 // the result string is the last object allocated we could | |
| 2030 // "realloc" it and probably, in the vast majority of cases, | |
| 2031 // extend the existing string to be able to hold the full | |
| 2032 // result. | |
| 2033 int next_length = 0; | |
| 2034 if (has_next) { | |
| 2035 next_length = mapping->get(next, 0, chars); | |
| 2036 if (next_length == 0) next_length = 1; | |
| 2037 } | |
| 2038 int current_length = i + char_length + next_length; | |
| 2039 while (stream.HasMore()) { | |
| 2040 current = stream.GetNext(); | |
| 2041 overflows |= ToUpperOverflows(current); | |
| 2042 // NOTE: we use 0 as the next character here because, while | |
| 2043 // the next character may affect what a character converts to, | |
| 2044 // it does not in any case affect the length of what it convert | |
| 2045 // to. | |
| 2046 int char_length = mapping->get(current, 0, chars); | |
| 2047 if (char_length == 0) char_length = 1; | |
| 2048 current_length += char_length; | |
| 2049 if (current_length > String::kMaxLength) { | |
| 2050 AllowHeapAllocation allocate_error_and_return; | |
| 2051 THROW_NEW_ERROR_RETURN_FAILURE(isolate, | |
| 2052 NewInvalidStringLengthError()); | |
| 2053 } | |
| 2054 } | |
| 2055 // Try again with the real length. Return signed if we need | |
| 2056 // to allocate a two-byte string for to uppercase. | |
| 2057 return (overflows && !ignore_overflow) ? Smi::FromInt(-current_length) | |
| 2058 : Smi::FromInt(current_length); | |
| 2059 } else { | |
| 2060 for (int j = 0; j < char_length; j++) { | |
| 2061 result->Set(i, chars[j]); | |
| 2062 i++; | |
| 2063 } | |
| 2064 has_changed_character = true; | |
| 2065 } | |
| 2066 current = next; | |
| 2067 } | |
| 2068 if (has_changed_character) { | |
| 2069 return result; | |
| 2070 } else { | |
| 2071 // If we didn't actually change anything in doing the conversion | |
| 2072 // we simple return the result and let the converted string | |
| 2073 // become garbage; there is no reason to keep two identical strings | |
| 2074 // alive. | |
| 2075 return string; | |
| 2076 } | |
| 2077 } | |
| 2078 | |
| 2079 template <class Converter> | |
| 2080 MUST_USE_RESULT static Object* ConvertCase( | |
| 2081 Handle<String> s, Isolate* isolate, | |
| 2082 unibrow::Mapping<Converter, 128>* mapping) { | |
| 2083 s = String::Flatten(s); | |
| 2084 int length = s->length(); | |
| 2085 // Assume that the string is not empty; we need this assumption later | |
| 2086 if (length == 0) return *s; | |
| 2087 | |
| 2088 // Simpler handling of ASCII strings. | |
| 2089 // | |
| 2090 // NOTE: This assumes that the upper/lower case of an ASCII | |
| 2091 // character is also ASCII. This is currently the case, but it | |
| 2092 // might break in the future if we implement more context and locale | |
| 2093 // dependent upper/lower conversions. | |
| 2094 if (s->IsOneByteRepresentationUnderneath()) { | |
| 2095 // Same length as input. | |
| 2096 Handle<SeqOneByteString> result = | |
| 2097 isolate->factory()->NewRawOneByteString(length).ToHandleChecked(); | |
| 2098 DisallowHeapAllocation no_gc; | |
| 2099 String::FlatContent flat_content = s->GetFlatContent(); | |
| 2100 DCHECK(flat_content.IsFlat()); | |
| 2101 bool has_changed_character = false; | |
| 2102 int index_to_first_unprocessed = FastAsciiConvert<Converter::kIsToLower>( | |
| 2103 reinterpret_cast<char*>(result->GetChars()), | |
| 2104 reinterpret_cast<const char*>(flat_content.ToOneByteVector().start()), | |
| 2105 length, &has_changed_character); | |
| 2106 // If not ASCII, we discard the result and take the 2 byte path. | |
| 2107 if (index_to_first_unprocessed == length) | |
| 2108 return has_changed_character ? *result : *s; | |
| 2109 } | |
| 2110 | |
| 2111 Handle<SeqString> result; // Same length as input. | |
| 2112 if (s->IsOneByteRepresentation()) { | |
| 2113 result = isolate->factory()->NewRawOneByteString(length).ToHandleChecked(); | |
| 2114 } else { | |
| 2115 result = isolate->factory()->NewRawTwoByteString(length).ToHandleChecked(); | |
| 2116 } | |
| 2117 | |
| 2118 Object* answer = ConvertCaseHelper(isolate, *s, *result, length, mapping); | |
| 2119 if (answer->IsException(isolate) || answer->IsString()) return answer; | |
| 2120 | |
| 2121 DCHECK(answer->IsSmi()); | |
| 2122 length = Smi::cast(answer)->value(); | |
| 2123 if (s->IsOneByteRepresentation() && length > 0) { | |
| 2124 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( | |
| 2125 isolate, result, isolate->factory()->NewRawOneByteString(length)); | |
| 2126 } else { | |
| 2127 if (length < 0) length = -length; | |
| 2128 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( | |
| 2129 isolate, result, isolate->factory()->NewRawTwoByteString(length)); | |
| 2130 } | |
| 2131 return ConvertCaseHelper(isolate, *s, *result, length, mapping); | |
| 2132 } | |
| 2133 | |
| 2134 } // namespace | |
| 2135 | |
| 2136 BUILTIN(StringPrototypeToLocaleLowerCase) { | |
| 2137 HandleScope scope(isolate); | |
| 2138 TO_THIS_STRING(string, "String.prototype.toLocaleLowerCase"); | |
| 2139 return ConvertCase(string, isolate, | |
| 2140 isolate->runtime_state()->to_lower_mapping()); | |
| 2141 } | |
| 2142 | |
| 2143 BUILTIN(StringPrototypeToLocaleUpperCase) { | |
| 2144 HandleScope scope(isolate); | |
| 2145 TO_THIS_STRING(string, "String.prototype.toLocaleUpperCase"); | |
| 2146 return ConvertCase(string, isolate, | |
| 2147 isolate->runtime_state()->to_upper_mapping()); | |
| 2148 } | |
| 2149 | |
| 2150 BUILTIN(StringPrototypeToLowerCase) { | |
| 2151 HandleScope scope(isolate); | |
| 2152 TO_THIS_STRING(string, "String.prototype.toLowerCase"); | |
| 2153 return ConvertCase(string, isolate, | |
| 2154 isolate->runtime_state()->to_lower_mapping()); | |
| 2155 } | |
| 2156 | |
| 2157 BUILTIN(StringPrototypeToUpperCase) { | |
| 2158 HandleScope scope(isolate); | |
| 2159 TO_THIS_STRING(string, "String.prototype.toUpperCase"); | |
| 2160 return ConvertCase(string, isolate, | |
| 2161 isolate->runtime_state()->to_upper_mapping()); | |
| 2162 } | |
| 2163 | |
| 2164 } // namespace internal | 1614 } // namespace internal |
| 2165 } // namespace v8 | 1615 } // namespace v8 |
| OLD | NEW |