Chromium Code Reviews| 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 |