OLD | NEW |
---|---|
1 // Copyright 2016 the V8 project authors. All rights reserved. | 1 // Copyright 2016 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.h" | 5 #include "src/builtins/builtins.h" |
6 #include "src/builtins/builtins-utils.h" | 6 #include "src/builtins/builtins-utils.h" |
7 | 7 |
8 #include "src/code-factory.h" | 8 #include "src/code-factory.h" |
9 #include "src/code-stub-assembler.h" | |
9 #include "src/regexp/regexp-utils.h" | 10 #include "src/regexp/regexp-utils.h" |
10 | 11 |
11 namespace v8 { | 12 namespace v8 { |
12 namespace internal { | 13 namespace internal { |
13 | 14 |
14 typedef CodeStubAssembler::ResultMode ResultMode; | 15 typedef CodeStubAssembler::ResultMode ResultMode; |
15 typedef CodeStubAssembler::RelationalComparisonMode RelationalComparisonMode; | 16 typedef CodeStubAssembler::RelationalComparisonMode RelationalComparisonMode; |
16 | 17 |
18 class StringBuiltinsAssembler : public CodeStubAssembler { | |
19 public: | |
20 explicit StringBuiltinsAssembler(compiler::CodeAssemblerState* state) | |
21 : CodeStubAssembler(state) {} | |
22 | |
23 protected: | |
24 Node* LoadOneByteChar(Node* string, Node* index) { | |
25 return Load(MachineType::Uint8(), string, OneByteCharOffset(index)); | |
26 } | |
27 | |
28 Node* OneByteCharOffset(Node* index) { | |
29 return CharOffset(String::ONE_BYTE_ENCODING, index); | |
30 } | |
31 | |
32 Node* CharOffset(String::Encoding encoding, Node* index) { | |
33 const int header = SeqOneByteString::kHeaderSize - kHeapObjectTag; | |
34 Node* offset = index; | |
35 if (encoding == String::TWO_BYTE_ENCODING) { | |
36 offset = IntPtrAddFoldConstants(offset, offset); | |
37 } | |
38 offset = IntPtrAddFoldConstants(offset, IntPtrConstant(header)); | |
39 return offset; | |
40 } | |
41 | |
42 void BranchIfSimpleOneByteStringInstanceType(Node* instance_type, | |
Jakob Kummerow
2016/12/06 06:58:40
high-level comment: using bit masks would probably
Camillo Bruni
2016/12/12 15:45:20
Replaced with the simpler version to check with th
| |
43 Label* if_true, | |
44 Label* if_false) { | |
45 Label if_not_one_byte_internalize(this); | |
Jakob Kummerow
2016/12/06 06:58:40
nit: s/internalize/internalized/ (or you can call
| |
46 Branch(IntPtrEqual(instance_type, | |
Jakob Kummerow
2016/12/06 06:58:40
This should be either Word32Equal/Int32Constant, o
| |
47 Int32Constant(ONE_BYTE_INTERNALIZED_STRING_TYPE)), | |
48 if_true, &if_not_one_byte_internalize); | |
49 Bind(&if_not_one_byte_internalize); | |
50 Branch(IntPtrEqual(instance_type, Int32Constant(ONE_BYTE_STRING_TYPE)), | |
Jakob Kummerow
2016/12/06 06:58:40
same
| |
51 if_true, if_false); | |
52 } | |
53 }; | |
54 | |
17 namespace { | 55 namespace { |
18 | 56 |
19 void GenerateStringEqual(CodeStubAssembler* assembler, ResultMode mode) { | 57 void GenerateStringEqual(CodeStubAssembler* assembler, ResultMode mode) { |
20 // Here's pseudo-code for the algorithm below in case of kDontNegateResult | 58 // Here's pseudo-code for the algorithm below in case of kDontNegateResult |
21 // mode; for kNegateResult mode we properly negate the result. | 59 // mode; for kNegateResult mode we properly negate the result. |
22 // | 60 // |
23 // if (lhs == rhs) return true; | 61 // if (lhs == rhs) return true; |
24 // if (lhs->length() != rhs->length()) return false; | 62 // if (lhs->length() != rhs->length()) return false; |
25 // if (lhs->IsInternalizedString() && rhs->IsInternalizedString()) { | 63 // if (lhs->IsInternalizedString() && rhs->IsInternalizedString()) { |
26 // return false; | 64 // return false; |
(...skipping 534 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
561 } | 599 } |
562 | 600 |
563 if (value->Number() < 0 || value->Number() > 0x10FFFF) { | 601 if (value->Number() < 0 || value->Number() > 0x10FFFF) { |
564 return false; | 602 return false; |
565 } | 603 } |
566 | 604 |
567 return true; | 605 return true; |
568 } | 606 } |
569 | 607 |
570 uc32 NextCodePoint(Isolate* isolate, BuiltinArguments args, int index) { | 608 uc32 NextCodePoint(Isolate* isolate, BuiltinArguments args, int index) { |
571 Handle<Object> value = args.at<Object>(1 + index); | 609 Handle<Object> value = args.at(1 + index); |
572 ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, value, Object::ToNumber(value), -1); | 610 ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, value, Object::ToNumber(value), -1); |
573 if (!IsValidCodePoint(isolate, value)) { | 611 if (!IsValidCodePoint(isolate, value)) { |
574 isolate->Throw(*isolate->factory()->NewRangeError( | 612 isolate->Throw(*isolate->factory()->NewRangeError( |
575 MessageTemplate::kInvalidCodePoint, value)); | 613 MessageTemplate::kInvalidCodePoint, value)); |
576 return -1; | 614 return -1; |
577 } | 615 } |
578 return DoubleToUint32(value->Number()); | 616 return DoubleToUint32(value->Number()); |
579 } | 617 } |
580 | 618 |
581 } // namespace | 619 } // namespace |
(...skipping 243 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
825 Object::ToInteger(isolate, args.atOrUndefined(isolate, 2))); | 863 Object::ToInteger(isolate, args.atOrUndefined(isolate, 2))); |
826 | 864 |
827 double index = std::max(position->Number(), 0.0); | 865 double index = std::max(position->Number(), 0.0); |
828 index = std::min(index, static_cast<double>(str->length())); | 866 index = std::min(index, static_cast<double>(str->length())); |
829 | 867 |
830 int index_in_str = String::IndexOf(isolate, str, search_string, | 868 int index_in_str = String::IndexOf(isolate, str, search_string, |
831 static_cast<uint32_t>(index)); | 869 static_cast<uint32_t>(index)); |
832 return *isolate->factory()->ToBoolean(index_in_str != -1); | 870 return *isolate->factory()->ToBoolean(index_in_str != -1); |
833 } | 871 } |
834 | 872 |
835 // ES6 section 21.1.3.8 String.prototype.indexOf ( searchString [ , position ] ) | 873 // ES6 #sec-string.prototype.indexof |
836 BUILTIN(StringPrototypeIndexOf) { | 874 TF_BUILTIN(StringPrototypeIndexOf, StringBuiltinsAssembler) { |
837 HandleScope handle_scope(isolate); | 875 Variable search_string(this, MachineRepresentation::kTagged), |
876 position(this, MachineRepresentation::kTagged); | |
877 Label call_runtime(this), argc_0(this), no_argc_0(this), argc_1(this), | |
878 no_argc_1(this), argc_2(this), fast_path(this), return_minus_1(this); | |
838 | 879 |
839 return String::IndexOf(isolate, args.receiver(), | 880 // Parameter(0) = nof arguments. |
Jakob Kummerow
2016/12/06 06:58:40
nit: this comment doesn't add anything (and runs t
Camillo Bruni
2016/12/12 15:45:20
removed.
| |
840 args.atOrUndefined(isolate, 1), | 881 Node* argc = |
841 args.atOrUndefined(isolate, 2)); | 882 ChangeInt32ToIntPtr(Parameter(BuiltinDescriptor::kArgumentsCount)); |
883 Node* context = Parameter(BuiltinDescriptor::kContext); | |
884 | |
885 CodeStubArguments arguments(this, argc); | |
886 Node* const receiver = arguments.GetReceiver(); | |
Jakob Kummerow
2016/12/06 06:58:40
Is there a particular reason for the "const" annot
Camillo Bruni
2016/12/12 15:45:20
mostly copy-paste habit, doubt that they will add
| |
887 | |
888 GotoIf(IntPtrEqual(argc, IntPtrConstant(0)), &argc_0); | |
889 GotoIf(IntPtrEqual(argc, IntPtrConstant(1)), &argc_1); | |
890 Goto(&argc_2); | |
891 Bind(&argc_0); | |
892 { | |
893 Comment("0 Argument case"); | |
894 Node* const undefined = UndefinedConstant(); | |
895 search_string.Bind(undefined); | |
896 position.Bind(undefined); | |
897 Goto(&call_runtime); | |
898 } | |
899 Bind(&argc_1); | |
900 { | |
901 Comment("1 Argument case"); | |
902 search_string.Bind(arguments.AtIndex(0)); | |
903 position.Bind(SmiConstant(0)); | |
904 Goto(&fast_path); | |
905 } | |
906 Bind(&argc_2); | |
907 { | |
908 Comment("2 Argument case"); | |
909 search_string.Bind(arguments.AtIndex(0)); | |
910 position.Bind(arguments.AtIndex(1)); | |
911 Goto(&fast_path); | |
912 } | |
913 | |
914 Bind(&fast_path); | |
915 { | |
916 Label zero_length_needle(this); | |
917 GotoIf(TaggedIsSmi(receiver), &call_runtime); | |
918 Node* const instance_type = LoadInstanceType(receiver); | |
919 GotoUnless(IsStringInstanceType(instance_type), &call_runtime); | |
920 | |
921 Node* const needle = search_string.value(); | |
922 Node* const needle_instance_type = LoadInstanceType(needle); | |
923 GotoUnless(IsStringInstanceType(needle_instance_type), &call_runtime); | |
924 GotoUnless(TaggedIsSmi(position.value()), &call_runtime); | |
925 | |
926 Node* const needle_length = SmiUntag(LoadStringLength(needle)); | |
927 // Use possibly faster runtime fallback for long search strings. | |
928 GotoIf(IntPtrLessThan(IntPtrConstant(1), needle_length), &call_runtime); | |
929 Node* const string_length = SmiUntag(LoadStringLength(receiver)); | |
930 Node* const start_position = SmiUntag(position.value()); | |
931 | |
932 GotoIf(IntPtrEqual(IntPtrConstant(0), needle_length), &zero_length_needle); | |
933 // Check that the needle fits in the start position. | |
934 GotoUnless(IntPtrLessThanOrEqual(needle_length, | |
935 IntPtrSub(string_length, start_position)), | |
936 &return_minus_1); | |
937 // Only support one-byte strings on the fast path. | |
938 Label check_needle(this), continue_fast_path(this); | |
939 BranchIfSimpleOneByteStringInstanceType(instance_type, &check_needle, | |
Jakob Kummerow
2016/12/06 06:58:40
How important is the zero_length_needle case?
You
Camillo Bruni
2016/12/12 15:45:20
Not that important, but it's easy enough to implem
| |
940 &call_runtime); | |
941 Bind(&check_needle); | |
942 BranchIfSimpleOneByteStringInstanceType(needle_instance_type, | |
943 &continue_fast_path, &call_runtime); | |
944 Bind(&continue_fast_path); | |
945 | |
946 // Loops over 1-byte strings longer than 32 characters are implemented | |
947 // faster in C++ making use of faster vector operations. | |
948 GotoIf(IntPtrGreaterThan(string_length, IntPtrConstant(32)), &call_runtime); | |
Camillo Bruni
2016/12/05 16:32:33
I tried unrolling a loop of 8 and 16 iterations re
| |
949 | |
950 // Loop over the 1-byte string comparing against the single character of the | |
951 // needle. | |
952 MachineType type = MachineType::Uint8(); | |
953 Node* const needle_byte = LoadOneByteChar(needle, IntPtrConstant(0)); | |
954 Node* const start_offset = OneByteCharOffset(start_position); | |
955 Node* const end_offset = OneByteCharOffset(string_length); | |
956 | |
957 Variable offset(this, MachineType::PointerRepresentation()); | |
958 | |
959 offset.Bind(start_offset); | |
960 Label loop(this, &offset), found(this); | |
961 | |
962 Branch(WordEqual(offset.value(), end_offset), &return_minus_1, &loop); | |
963 Bind(&loop); | |
Jakob Kummerow
2016/12/06 06:58:40
Consider using BuildFastLoop().
| |
964 { | |
965 Node* const byte = Load(type, receiver, offset.value()); | |
966 GotoIf(IntPtrEqual(byte, needle_byte), &found); | |
Jakob Kummerow
2016/12/06 06:58:40
This should probably be Word32Equal.
| |
967 | |
968 offset.Bind(IntPtrAdd(offset.value(), IntPtrConstant(1))); | |
969 Branch(WordEqual(offset.value(), end_offset), &return_minus_1, &loop); | |
970 } | |
971 Bind(&found); | |
972 Node* const zero_offset = OneByteCharOffset(IntPtrConstant(0)); | |
973 arguments.PopAndReturn(SmiTag(IntPtrSub(offset.value(), zero_offset))); | |
974 | |
975 Bind(&zero_length_needle); | |
976 { | |
977 arguments.PopAndReturn(SmiTag(IntPtrMin(string_length, start_position))); | |
978 } | |
979 } | |
980 | |
981 Bind(&return_minus_1); | |
982 { arguments.PopAndReturn(SmiConstant(-1)); } | |
983 | |
984 Bind(&call_runtime); | |
985 { | |
986 Node* result = | |
987 CallRuntime(Runtime::kStringPrototypeIndexOf, context, receiver, | |
988 search_string.value(), position.value()); | |
989 arguments.PopAndReturn(result); | |
990 } | |
842 } | 991 } |
843 | 992 |
844 // ES6 section 21.1.3.9 | 993 // ES6 section 21.1.3.9 |
845 // String.prototype.lastIndexOf ( searchString [ , position ] ) | 994 // String.prototype.lastIndexOf ( searchString [ , position ] ) |
846 BUILTIN(StringPrototypeLastIndexOf) { | 995 BUILTIN(StringPrototypeLastIndexOf) { |
847 HandleScope handle_scope(isolate); | 996 HandleScope handle_scope(isolate); |
848 return String::LastIndexOf(isolate, args.receiver(), | 997 return String::LastIndexOf(isolate, args.receiver(), |
849 args.atOrUndefined(isolate, 1), | 998 args.atOrUndefined(isolate, 1), |
850 args.atOrUndefined(isolate, 2)); | 999 args.atOrUndefined(isolate, 2)); |
851 } | 1000 } |
852 | 1001 |
853 // ES6 section 21.1.3.10 String.prototype.localeCompare ( that ) | 1002 // ES6 section 21.1.3.10 String.prototype.localeCompare ( that ) |
854 // | 1003 // |
855 // This function is implementation specific. For now, we do not | 1004 // This function is implementation specific. For now, we do not |
856 // do anything locale specific. | 1005 // do anything locale specific. |
857 // If internationalization is enabled, then i18n.js will override this function | 1006 // If internationalization is enabled, then i18n.js will override this function |
858 // and provide the proper functionality, so this is just a fallback. | 1007 // and provide the proper functionality, so this is just a fallback. |
859 BUILTIN(StringPrototypeLocaleCompare) { | 1008 BUILTIN(StringPrototypeLocaleCompare) { |
860 HandleScope handle_scope(isolate); | 1009 HandleScope handle_scope(isolate); |
861 DCHECK_EQ(2, args.length()); | 1010 DCHECK_EQ(2, args.length()); |
862 | 1011 |
863 TO_THIS_STRING(str1, "String.prototype.localeCompare"); | 1012 TO_THIS_STRING(str1, "String.prototype.localeCompare"); |
864 Handle<String> str2; | 1013 Handle<String> str2; |
865 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( | 1014 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, str2, |
866 isolate, str2, Object::ToString(isolate, args.at<Object>(1))); | 1015 Object::ToString(isolate, args.at(1))); |
867 | 1016 |
868 if (str1.is_identical_to(str2)) return Smi::kZero; // Equal. | 1017 if (str1.is_identical_to(str2)) return Smi::kZero; // Equal. |
869 int str1_length = str1->length(); | 1018 int str1_length = str1->length(); |
870 int str2_length = str2->length(); | 1019 int str2_length = str2->length(); |
871 | 1020 |
872 // Decide trivial cases without flattening. | 1021 // Decide trivial cases without flattening. |
873 if (str1_length == 0) { | 1022 if (str1_length == 0) { |
874 if (str2_length == 0) return Smi::kZero; // Equal. | 1023 if (str2_length == 0) return Smi::kZero; // Equal. |
875 return Smi::FromInt(-str2_length); | 1024 return Smi::FromInt(-str2_length); |
876 } else { | 1025 } else { |
(...skipping 589 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1466 Runtime::kThrowIncompatibleMethodReceiver, context, | 1615 Runtime::kThrowIncompatibleMethodReceiver, context, |
1467 assembler.HeapConstant(assembler.factory()->NewStringFromAsciiChecked( | 1616 assembler.HeapConstant(assembler.factory()->NewStringFromAsciiChecked( |
1468 "String Iterator.prototype.next", TENURED)), | 1617 "String Iterator.prototype.next", TENURED)), |
1469 iterator); | 1618 iterator); |
1470 assembler.Return(result); // Never reached. | 1619 assembler.Return(result); // Never reached. |
1471 } | 1620 } |
1472 } | 1621 } |
1473 | 1622 |
1474 } // namespace internal | 1623 } // namespace internal |
1475 } // namespace v8 | 1624 } // namespace v8 |
OLD | NEW |