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 |