OLD | NEW |
---|---|
1 // Copyright 2014 the V8 project authors. All rights reserved. | 1 // Copyright 2014 the V8 project authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 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/runtime/runtime-utils.h" | 5 #include "src/runtime/runtime-utils.h" |
6 | 6 |
7 #include "src/arguments.h" | 7 #include "src/arguments.h" |
8 #include "src/conversions-inl.h" | 8 #include "src/conversions-inl.h" |
9 #include "src/isolate-inl.h" | 9 #include "src/isolate-inl.h" |
10 #include "src/messages.h" | 10 #include "src/messages.h" |
11 #include "src/regexp/jsregexp-inl.h" | 11 #include "src/regexp/jsregexp-inl.h" |
12 #include "src/regexp/jsregexp.h" | 12 #include "src/regexp/jsregexp.h" |
13 #include "src/regexp/regexp-utils.h" | |
13 #include "src/string-builder.h" | 14 #include "src/string-builder.h" |
14 #include "src/string-search.h" | 15 #include "src/string-search.h" |
15 | 16 |
16 namespace v8 { | 17 namespace v8 { |
17 namespace internal { | 18 namespace internal { |
18 | 19 |
19 class CompiledReplacement { | 20 class CompiledReplacement { |
20 public: | 21 public: |
21 explicit CompiledReplacement(Zone* zone) | 22 explicit CompiledReplacement(Zone* zone) |
22 : parts_(1, zone), replacement_substrings_(0, zone), zone_(zone) {} | 23 : parts_(1, zone), replacement_substrings_(0, zone), zone_(zone) {} |
(...skipping 763 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
786 } | 787 } |
787 | 788 |
788 | 789 |
789 RUNTIME_FUNCTION(Runtime_RegExpFlags) { | 790 RUNTIME_FUNCTION(Runtime_RegExpFlags) { |
790 SealHandleScope shs(isolate); | 791 SealHandleScope shs(isolate); |
791 DCHECK(args.length() == 1); | 792 DCHECK(args.length() == 1); |
792 CONVERT_ARG_CHECKED(JSRegExp, regexp, 0); | 793 CONVERT_ARG_CHECKED(JSRegExp, regexp, 0); |
793 return regexp->flags(); | 794 return regexp->flags(); |
794 } | 795 } |
795 | 796 |
796 | |
797 RUNTIME_FUNCTION(Runtime_RegExpSource) { | 797 RUNTIME_FUNCTION(Runtime_RegExpSource) { |
798 SealHandleScope shs(isolate); | 798 SealHandleScope shs(isolate); |
799 DCHECK(args.length() == 1); | 799 DCHECK(args.length() == 1); |
800 CONVERT_ARG_CHECKED(JSRegExp, regexp, 0); | 800 CONVERT_ARG_CHECKED(JSRegExp, regexp, 0); |
801 return regexp->source(); | 801 return regexp->source(); |
802 } | 802 } |
803 | 803 |
804 // TODO(jgruber): Remove this once all uses in regexp.js have been removed. | 804 // TODO(jgruber): Remove this once all uses in regexp.js have been removed. |
805 RUNTIME_FUNCTION(Runtime_RegExpConstructResult) { | 805 RUNTIME_FUNCTION(Runtime_RegExpConstructResult) { |
806 HandleScope handle_scope(isolate); | 806 HandleScope handle_scope(isolate); |
807 DCHECK(args.length() == 3); | 807 DCHECK(args.length() == 3); |
808 CONVERT_SMI_ARG_CHECKED(size, 0); | 808 CONVERT_SMI_ARG_CHECKED(size, 0); |
809 CHECK(size >= 0 && size <= FixedArray::kMaxLength); | 809 CHECK(size >= 0 && size <= FixedArray::kMaxLength); |
810 CONVERT_ARG_HANDLE_CHECKED(Object, index, 1); | 810 CONVERT_ARG_HANDLE_CHECKED(Object, index, 1); |
811 CONVERT_ARG_HANDLE_CHECKED(Object, input, 2); | 811 CONVERT_ARG_HANDLE_CHECKED(Object, input, 2); |
812 Handle<FixedArray> elements = isolate->factory()->NewFixedArray(size); | 812 Handle<FixedArray> elements = isolate->factory()->NewFixedArray(size); |
813 Handle<Map> regexp_map(isolate->native_context()->regexp_result_map()); | 813 Handle<Map> regexp_map(isolate->native_context()->regexp_result_map()); |
814 Handle<JSObject> object = | 814 Handle<JSObject> object = |
815 isolate->factory()->NewJSObjectFromMap(regexp_map, NOT_TENURED); | 815 isolate->factory()->NewJSObjectFromMap(regexp_map, NOT_TENURED); |
816 Handle<JSArray> array = Handle<JSArray>::cast(object); | 816 Handle<JSArray> array = Handle<JSArray>::cast(object); |
817 array->set_elements(*elements); | 817 array->set_elements(*elements); |
818 array->set_length(Smi::FromInt(size)); | 818 array->set_length(Smi::FromInt(size)); |
819 // Write in-object properties after the length of the array. | 819 // Write in-object properties after the length of the array. |
820 array->InObjectPropertyAtPut(JSRegExpResult::kIndexIndex, *index); | 820 array->InObjectPropertyAtPut(JSRegExpResult::kIndexIndex, *index); |
821 array->InObjectPropertyAtPut(JSRegExpResult::kInputIndex, *input); | 821 array->InObjectPropertyAtPut(JSRegExpResult::kInputIndex, *input); |
822 return *array; | 822 return *array; |
823 } | 823 } |
824 | 824 |
825 | |
826 RUNTIME_FUNCTION(Runtime_RegExpInitializeAndCompile) { | 825 RUNTIME_FUNCTION(Runtime_RegExpInitializeAndCompile) { |
827 HandleScope scope(isolate); | 826 HandleScope scope(isolate); |
828 DCHECK(args.length() == 3); | 827 DCHECK(args.length() == 3); |
829 CONVERT_ARG_HANDLE_CHECKED(JSRegExp, regexp, 0); | 828 CONVERT_ARG_HANDLE_CHECKED(JSRegExp, regexp, 0); |
830 CONVERT_ARG_HANDLE_CHECKED(String, source, 1); | 829 CONVERT_ARG_HANDLE_CHECKED(String, source, 1); |
831 CONVERT_ARG_HANDLE_CHECKED(String, flags, 2); | 830 CONVERT_ARG_HANDLE_CHECKED(String, flags, 2); |
832 | 831 |
833 RETURN_FAILURE_ON_EXCEPTION(isolate, | 832 RETURN_FAILURE_ON_EXCEPTION(isolate, |
834 JSRegExp::Initialize(regexp, source, flags)); | 833 JSRegExp::Initialize(regexp, source, flags)); |
835 | 834 |
836 return *regexp; | 835 return *regexp; |
837 } | 836 } |
838 | 837 |
838 namespace { | |
839 | 839 |
840 // Only called from Runtime_RegExpExecMultiple so it doesn't need to maintain | 840 class MatchInfoBackedMatch : public String::Match { |
841 public: | |
842 MatchInfoBackedMatch(Isolate* isolate, Handle<String> subject, | |
843 Handle<JSObject> match_info) | |
844 : isolate_(isolate), match_info_(match_info) { | |
845 subject_ = String::Flatten(subject); | |
846 } | |
847 | |
848 Handle<String> GetMatch() override { | |
849 return RegExpUtils::GenericCaptureGetter(isolate_, match_info_, 0, nullptr); | |
850 } | |
851 | |
852 MaybeHandle<String> GetCapture(int i, bool* capture_exists) override { | |
853 Handle<Object> capture_obj = | |
854 RegExpUtils::GenericCaptureGetter(isolate_, match_info_, i); | |
855 if (capture_obj->IsUndefined(isolate_)) { | |
856 *capture_exists = false; | |
857 return isolate_->factory()->empty_string(); | |
858 } | |
859 *capture_exists = true; | |
860 return Object::ToString(isolate_, capture_obj); | |
861 } | |
862 | |
863 Handle<String> GetPrefix() override { | |
864 const int match_start = | |
865 RegExpUtils::GetLastMatchCapture(isolate_, match_info_, 0); | |
866 return isolate_->factory()->NewSubString(subject_, 0, match_start); | |
867 } | |
868 | |
869 Handle<String> GetSuffix() override { | |
870 const int match_end = | |
871 RegExpUtils::GetLastMatchCapture(isolate_, match_info_, 1); | |
872 return isolate_->factory()->NewSubString(subject_, match_end, | |
873 subject_->length()); | |
874 } | |
875 | |
876 int CaptureCount() override { | |
877 return RegExpUtils::GetLastMatchNumberOfCaptures(isolate_, match_info_) / 2; | |
878 } | |
879 | |
880 virtual ~MatchInfoBackedMatch() {} | |
881 | |
882 private: | |
883 Isolate* isolate_; | |
884 Handle<String> subject_; | |
885 Handle<JSObject> match_info_; | |
886 }; | |
887 | |
888 class VectorBackedMatch : public String::Match { | |
889 public: | |
890 VectorBackedMatch(Isolate* isolate, Handle<String> subject, | |
891 Handle<String> match, int match_position, | |
892 ZoneVector<Handle<Object>>* captures) | |
893 : isolate_(isolate), | |
894 match_(match), | |
895 match_position_(match_position), | |
896 captures_(captures) { | |
897 subject_ = String::Flatten(subject); | |
898 } | |
899 | |
900 Handle<String> GetMatch() override { return match_; } | |
901 | |
902 MaybeHandle<String> GetCapture(int i, bool* capture_exists) override { | |
903 Handle<Object> capture_obj = captures_->at(i); | |
904 if (capture_obj->IsUndefined(isolate_)) { | |
905 *capture_exists = false; | |
906 return isolate_->factory()->empty_string(); | |
907 } | |
908 *capture_exists = true; | |
909 return Object::ToString(isolate_, capture_obj); | |
910 } | |
911 | |
912 Handle<String> GetPrefix() override { | |
913 return isolate_->factory()->NewSubString(subject_, 0, match_position_); | |
914 } | |
915 | |
916 Handle<String> GetSuffix() override { | |
917 const int match_end_position = match_position_ + match_->length(); | |
918 return isolate_->factory()->NewSubString(subject_, match_end_position, | |
919 subject_->length()); | |
920 } | |
921 | |
922 int CaptureCount() override { return static_cast<int>(captures_->size()); } | |
923 | |
924 virtual ~VectorBackedMatch() {} | |
925 | |
926 private: | |
927 Isolate* isolate_; | |
928 Handle<String> subject_; | |
929 Handle<String> match_; | |
930 const int match_position_; | |
931 ZoneVector<Handle<Object>>* captures_; | |
932 }; | |
933 | |
934 // Only called from RegExpExecMultiple so it doesn't need to maintain | |
841 // separate last match info. See comment on that function. | 935 // separate last match info. See comment on that function. |
842 template <bool has_capture> | 936 template <bool has_capture> |
843 static Object* SearchRegExpMultiple(Isolate* isolate, Handle<String> subject, | 937 MaybeHandle<Object> SearchRegExpMultiple(Isolate* isolate, |
844 Handle<JSRegExp> regexp, | 938 Handle<String> subject, |
845 Handle<JSObject> last_match_array, | 939 Handle<JSRegExp> regexp, |
846 Handle<JSArray> result_array) { | 940 Handle<JSObject> last_match_array, |
941 Handle<FixedArray> result_elements) { | |
847 DCHECK(subject->IsFlat()); | 942 DCHECK(subject->IsFlat()); |
848 DCHECK_NE(has_capture, regexp->CaptureCount() == 0); | 943 DCHECK_NE(has_capture, regexp->CaptureCount() == 0); |
849 | 944 |
850 int capture_count = regexp->CaptureCount(); | 945 int capture_count = regexp->CaptureCount(); |
851 int subject_length = subject->length(); | 946 int subject_length = subject->length(); |
852 | 947 |
853 static const int kMinLengthToCache = 0x1000; | 948 static const int kMinLengthToCache = 0x1000; |
854 | 949 |
855 if (subject_length > kMinLengthToCache) { | 950 if (subject_length > kMinLengthToCache) { |
856 FixedArray* last_match_cache; | 951 FixedArray* last_match_cache; |
857 Object* cached_answer = RegExpResultsCache::Lookup( | 952 Object* cached_answer = RegExpResultsCache::Lookup( |
858 isolate->heap(), *subject, regexp->data(), &last_match_cache, | 953 isolate->heap(), *subject, regexp->data(), &last_match_cache, |
859 RegExpResultsCache::REGEXP_MULTIPLE_INDICES); | 954 RegExpResultsCache::REGEXP_MULTIPLE_INDICES); |
860 if (cached_answer->IsFixedArray()) { | 955 if (cached_answer->IsFixedArray()) { |
861 int capture_registers = (capture_count + 1) * 2; | 956 int capture_registers = (capture_count + 1) * 2; |
862 int32_t* last_match = NewArray<int32_t>(capture_registers); | 957 int32_t* last_match = NewArray<int32_t>(capture_registers); |
863 for (int i = 0; i < capture_registers; i++) { | 958 for (int i = 0; i < capture_registers; i++) { |
864 last_match[i] = Smi::cast(last_match_cache->get(i))->value(); | 959 last_match[i] = Smi::cast(last_match_cache->get(i))->value(); |
865 } | 960 } |
866 Handle<FixedArray> cached_fixed_array = | 961 Handle<FixedArray> cached_fixed_array(FixedArray::cast(cached_answer)); |
867 Handle<FixedArray>(FixedArray::cast(cached_answer)); | |
868 // The cache FixedArray is a COW-array and can therefore be reused. | |
869 JSArray::SetContent(result_array, cached_fixed_array); | |
870 RegExpImpl::SetLastMatchInfo(last_match_array, subject, capture_count, | 962 RegExpImpl::SetLastMatchInfo(last_match_array, subject, capture_count, |
871 last_match); | 963 last_match); |
872 DeleteArray(last_match); | 964 DeleteArray(last_match); |
873 return *result_array; | 965 // The cache FixedArray is a COW-array and we need to return a copy. |
966 return isolate->factory()->CopyFixedArrayWithMap( | |
967 cached_fixed_array, isolate->factory()->fixed_array_map()); | |
874 } | 968 } |
875 } | 969 } |
876 | 970 |
877 RegExpImpl::GlobalCache global_cache(regexp, subject, isolate); | 971 RegExpImpl::GlobalCache global_cache(regexp, subject, isolate); |
878 if (global_cache.HasException()) return isolate->heap()->exception(); | 972 if (global_cache.HasException()) return MaybeHandle<Object>(); |
879 | 973 |
880 // Ensured in Runtime_RegExpExecMultiple. | 974 // Ensured in Runtime_RegExpExecMultiple. |
881 DCHECK(result_array->HasFastObjectElements()); | |
882 Handle<FixedArray> result_elements( | |
883 FixedArray::cast(result_array->elements())); | |
884 if (result_elements->length() < 16) { | 975 if (result_elements->length() < 16) { |
885 result_elements = isolate->factory()->NewFixedArrayWithHoles(16); | 976 result_elements = isolate->factory()->NewFixedArrayWithHoles(16); |
886 } | 977 } |
887 | 978 |
888 FixedArrayBuilder builder(result_elements); | 979 FixedArrayBuilder builder(result_elements); |
889 | 980 |
890 // Position to search from. | 981 // Position to search from. |
891 int match_start = -1; | 982 int match_start = -1; |
892 int match_end = 0; | 983 int match_end = 0; |
893 bool first = true; | 984 bool first = true; |
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
940 } | 1031 } |
941 elements->set(capture_count + 1, Smi::FromInt(match_start)); | 1032 elements->set(capture_count + 1, Smi::FromInt(match_start)); |
942 elements->set(capture_count + 2, *subject); | 1033 elements->set(capture_count + 2, *subject); |
943 builder.Add(*isolate->factory()->NewJSArrayWithElements(elements)); | 1034 builder.Add(*isolate->factory()->NewJSArrayWithElements(elements)); |
944 } else { | 1035 } else { |
945 builder.Add(*match); | 1036 builder.Add(*match); |
946 } | 1037 } |
947 } | 1038 } |
948 } | 1039 } |
949 | 1040 |
950 if (global_cache.HasException()) return isolate->heap()->exception(); | 1041 if (global_cache.HasException()) return MaybeHandle<Object>(); |
951 | 1042 |
952 if (match_start >= 0) { | 1043 if (match_start >= 0) { |
953 // Finished matching, with at least one match. | 1044 // Finished matching, with at least one match. |
954 if (match_end < subject_length) { | 1045 if (match_end < subject_length) { |
955 ReplacementStringBuilder::AddSubjectSlice(&builder, match_end, | 1046 ReplacementStringBuilder::AddSubjectSlice(&builder, match_end, |
956 subject_length); | 1047 subject_length); |
957 } | 1048 } |
958 | 1049 |
959 RegExpImpl::SetLastMatchInfo(last_match_array, subject, capture_count, | 1050 RegExpImpl::SetLastMatchInfo(last_match_array, subject, capture_count, |
960 global_cache.LastSuccessfulMatch()); | 1051 global_cache.LastSuccessfulMatch()); |
961 | 1052 |
1053 Handle<FixedArray> result_fixed_array = builder.array(); | |
1054 result_fixed_array->Shrink(builder.length()); | |
1055 | |
962 if (subject_length > kMinLengthToCache) { | 1056 if (subject_length > kMinLengthToCache) { |
963 // Store the last successful match into the array for caching. | 1057 // Store the last successful match into the array for caching. |
964 // TODO(yangguo): do not expose last match to JS and simplify caching. | 1058 // TODO(yangguo): do not expose last match to JS and simplify caching. |
965 int capture_registers = (capture_count + 1) * 2; | 1059 int capture_registers = (capture_count + 1) * 2; |
966 Handle<FixedArray> last_match_cache = | 1060 Handle<FixedArray> last_match_cache = |
967 isolate->factory()->NewFixedArray(capture_registers); | 1061 isolate->factory()->NewFixedArray(capture_registers); |
968 int32_t* last_match = global_cache.LastSuccessfulMatch(); | 1062 int32_t* last_match = global_cache.LastSuccessfulMatch(); |
969 for (int i = 0; i < capture_registers; i++) { | 1063 for (int i = 0; i < capture_registers; i++) { |
970 last_match_cache->set(i, Smi::FromInt(last_match[i])); | 1064 last_match_cache->set(i, Smi::FromInt(last_match[i])); |
971 } | 1065 } |
972 Handle<FixedArray> result_fixed_array = builder.array(); | |
973 result_fixed_array->Shrink(builder.length()); | |
974 // Cache the result and turn the FixedArray into a COW array. | 1066 // Cache the result and turn the FixedArray into a COW array. |
975 RegExpResultsCache::Enter( | 1067 RegExpResultsCache::Enter( |
976 isolate, subject, handle(regexp->data(), isolate), result_fixed_array, | 1068 isolate, subject, handle(regexp->data(), isolate), result_fixed_array, |
977 last_match_cache, RegExpResultsCache::REGEXP_MULTIPLE_INDICES); | 1069 last_match_cache, RegExpResultsCache::REGEXP_MULTIPLE_INDICES); |
978 } | 1070 } |
979 return *builder.ToJSArray(result_array); | 1071 // The cache FixedArray is a COW-array and we need to return a copy. |
1072 return isolate->factory()->CopyFixedArrayWithMap( | |
1073 result_fixed_array, isolate->factory()->fixed_array_map()); | |
980 } else { | 1074 } else { |
981 return isolate->heap()->null_value(); // No matches at all. | 1075 return isolate->factory()->null_value(); // No matches at all. |
982 } | 1076 } |
983 } | 1077 } |
984 | 1078 |
985 | |
986 // This is only called for StringReplaceGlobalRegExpWithFunction. This sets | 1079 // This is only called for StringReplaceGlobalRegExpWithFunction. This sets |
987 // lastMatchInfoOverride to maintain the last match info, so we don't need to | 1080 // lastMatchInfoOverride to maintain the last match info, so we don't need to |
988 // set any other last match array info. | 1081 // set any other last match array info. |
989 RUNTIME_FUNCTION(Runtime_RegExpExecMultiple) { | 1082 MaybeHandle<Object> RegExpExecMultiple(Isolate* isolate, |
990 HandleScope handles(isolate); | 1083 Handle<JSRegExp> regexp, |
991 DCHECK(args.length() == 4); | 1084 Handle<String> subject, |
992 | 1085 Handle<JSObject> last_match_info, |
993 CONVERT_ARG_HANDLE_CHECKED(JSRegExp, regexp, 0); | 1086 Handle<FixedArray> result_array) { |
994 CONVERT_ARG_HANDLE_CHECKED(String, subject, 1); | |
995 CONVERT_ARG_HANDLE_CHECKED(JSObject, last_match_info, 2); | |
996 CONVERT_ARG_HANDLE_CHECKED(JSArray, result_array, 3); | |
997 CHECK(last_match_info->HasFastObjectElements()); | 1087 CHECK(last_match_info->HasFastObjectElements()); |
998 CHECK(result_array->HasFastObjectElements()); | |
999 | 1088 |
1000 subject = String::Flatten(subject); | 1089 subject = String::Flatten(subject); |
1001 CHECK(regexp->GetFlags() & JSRegExp::kGlobal); | 1090 CHECK(regexp->GetFlags() & JSRegExp::kGlobal); |
1002 | 1091 |
1003 if (regexp->CaptureCount() == 0) { | 1092 if (regexp->CaptureCount() == 0) { |
1004 return SearchRegExpMultiple<false>(isolate, subject, regexp, | 1093 return SearchRegExpMultiple<false>(isolate, subject, regexp, |
1005 last_match_info, result_array); | 1094 last_match_info, result_array); |
1006 } else { | 1095 } else { |
1007 return SearchRegExpMultiple<true>(isolate, subject, regexp, last_match_info, | 1096 return SearchRegExpMultiple<true>(isolate, subject, regexp, last_match_info, |
1008 result_array); | 1097 result_array); |
1009 } | 1098 } |
1010 } | 1099 } |
1011 | 1100 |
1101 // Helper function for replacing regular expressions with the result of a | |
1102 // function application in String.prototype.replace. | |
1103 MaybeHandle<String> StringReplaceGlobalRegExpWithFunction( | |
1104 Isolate* isolate, Handle<String> subject, Handle<JSRegExp> regexp, | |
1105 Handle<Object> replace_obj) { | |
1106 Factory* factory = isolate->factory(); | |
1107 | |
1108 // TODO(jgruber): The result_array used to be reused for efficiency. We might | |
1109 // want to revisit that at some point. | |
1110 Handle<JSObject> last_match_info = isolate->regexp_last_match_info(); | |
1111 Handle<FixedArray> result_array = factory->NewFixedArrayWithHoles(16); | |
Yang
2016/10/11 13:26:35
I wonder whether we can't simply use a List<Handle
jgruber
2016/10/13 07:39:44
Yes, plenty of potential for optimization in these
| |
1112 | |
1113 Handle<Object> res; | |
1114 ASSIGN_RETURN_ON_EXCEPTION(isolate, res, | |
1115 RegExpExecMultiple(isolate, regexp, subject, | |
1116 last_match_info, result_array), | |
1117 String); | |
1118 | |
1119 if (res->IsNull(isolate)) return subject; // No matches at all. | |
1120 | |
1121 result_array = Handle<FixedArray>::cast(res); | |
1122 const int result_length = result_array->length(); | |
1123 | |
1124 const int num_captures = | |
1125 RegExpUtils::GetLastMatchNumberOfCaptures(isolate, last_match_info) / 2; | |
1126 if (num_captures == 1) { | |
1127 // If the number of captures is one then there are no explicit captures in | |
1128 // the regexp, just the implicit capture that captures the whole match. In | |
1129 // this case we can simplify quite a bit and end up with something faster. | |
1130 // The builder will consist of some integers that indicate slices of the | |
1131 // input string and some replacements that were returned from the replace | |
1132 // function. | |
1133 int match_start = 0; | |
1134 for (int i = 0; i < result_length; i++) { | |
1135 Handle<Object> elem = FixedArray::get(*result_array, i, isolate); | |
1136 if (elem->IsSmi()) { | |
Yang
2016/10/11 13:26:35
Maybe we don't need this weird encoding anymore (i
jgruber
2016/10/13 07:39:44
Yes, it'd be great to make this more readable. Add
| |
1137 // Integers represent slices of the original string. | |
1138 const int elem_value = Handle<Smi>::cast(elem)->value(); | |
1139 if (elem_value > 0) { | |
1140 match_start = (elem_value >> 11) + (elem_value & 0x7ff); | |
1141 } else { | |
1142 Handle<Object> next_elem = | |
1143 FixedArray::get(*result_array, ++i, isolate); | |
1144 const int next_elem_value = Handle<Smi>::cast(next_elem)->value(); | |
1145 match_start = next_elem_value - elem_value; | |
1146 } | |
1147 } else { | |
1148 DCHECK(elem->IsString()); | |
1149 Handle<String> elem_string = Handle<String>::cast(elem); | |
1150 | |
1151 // Overwrite the i'th element in the results with the string we got | |
1152 // back from the callback function. | |
1153 const int argc = 3; | |
1154 ScopedVector<Handle<Object>> argv(argc); | |
1155 | |
1156 argv[0] = elem_string; | |
1157 argv[1] = handle(Smi::FromInt(match_start), isolate); | |
1158 argv[2] = subject; | |
1159 | |
1160 Handle<Object> replacement_obj; | |
1161 ASSIGN_RETURN_ON_EXCEPTION( | |
1162 isolate, replacement_obj, | |
1163 Execution::Call(isolate, replace_obj, factory->undefined_value(), | |
1164 argc, argv.start()), | |
1165 String); | |
1166 | |
1167 Handle<String> replacement; | |
1168 ASSIGN_RETURN_ON_EXCEPTION(isolate, replacement, | |
1169 Object::ToString(isolate, replacement_obj), | |
1170 String); | |
1171 | |
1172 result_array->set(i, *replacement); | |
1173 match_start += elem_string->length(); | |
1174 } | |
1175 } | |
1176 } else { | |
1177 DCHECK(num_captures > 1); | |
1178 for (int i = 0; i < result_length; i++) { | |
1179 Handle<Object> elem = FixedArray::get(*result_array, i, isolate); | |
1180 if (elem->IsSmi()) continue; | |
1181 | |
1182 // TODO(jgruber): Can we skip this whole round-trip through a JS array | |
1183 // for result_array? | |
Yang
2016/10/11 13:26:35
Yes. We could either merge the loop in SearchRegEx
jgruber
2016/10/13 07:39:44
Likewise, updated the TODO.
| |
1184 Handle<JSArray> elem_array = Handle<JSArray>::cast(elem); | |
1185 Handle<FixedArray> elem_array_elems( | |
1186 FixedArray::cast(elem_array->elements()), isolate); | |
1187 | |
1188 const int argc = elem_array_elems->length(); | |
1189 ScopedVector<Handle<Object>> argv(argc); | |
1190 | |
1191 for (int j = 0; j < argc; j++) { | |
1192 argv[j] = FixedArray::get(*elem_array_elems, j, isolate); | |
1193 } | |
1194 | |
1195 // TODO(jgruber): This call is another pattern we could refactor. | |
1196 Handle<Object> replacement_obj; | |
1197 ASSIGN_RETURN_ON_EXCEPTION( | |
1198 isolate, replacement_obj, | |
1199 Execution::Call(isolate, replace_obj, factory->undefined_value(), | |
1200 argc, argv.start()), | |
1201 String); | |
1202 | |
1203 Handle<String> replacement; | |
1204 ASSIGN_RETURN_ON_EXCEPTION(isolate, replacement, | |
1205 Object::ToString(isolate, replacement_obj), | |
1206 String); | |
1207 | |
1208 result_array->set(i, *replacement); | |
1209 } | |
1210 } | |
1211 | |
1212 if (result_length == 0) { | |
1213 return factory->empty_string(); | |
1214 } else if (result_length == 1) { | |
1215 Handle<Object> first = FixedArray::get(*result_array, 0, isolate); | |
1216 if (first->IsString()) return Handle<String>::cast(first); | |
1217 } | |
1218 | |
1219 bool one_byte = subject->HasOnlyOneByteChars(); | |
1220 const int length = StringBuilderConcatLength(subject->length(), *result_array, | |
1221 result_length, &one_byte); | |
1222 | |
1223 if (length == -1) { | |
1224 isolate->Throw(isolate->heap()->illegal_argument_string()); | |
1225 return MaybeHandle<String>(); | |
1226 } | |
1227 | |
1228 if (one_byte) { | |
1229 Handle<SeqOneByteString> answer; | |
1230 ASSIGN_RETURN_ON_EXCEPTION(isolate, answer, | |
1231 isolate->factory()->NewRawOneByteString(length), | |
1232 String); | |
1233 StringBuilderConcatHelper(*subject, answer->GetChars(), *result_array, | |
1234 result_length); | |
1235 return answer; | |
1236 } else { | |
1237 DCHECK(!one_byte); | |
1238 Handle<SeqTwoByteString> answer; | |
1239 ASSIGN_RETURN_ON_EXCEPTION(isolate, answer, | |
1240 isolate->factory()->NewRawTwoByteString(length), | |
1241 String); | |
1242 StringBuilderConcatHelper(*subject, answer->GetChars(), *result_array, | |
1243 result_length); | |
1244 return answer; | |
1245 } | |
1246 | |
1247 UNREACHABLE(); | |
1248 return MaybeHandle<String>(); | |
1249 } | |
1250 | |
1251 MaybeHandle<String> StringReplaceNonGlobalRegExpWithFunction( | |
1252 Isolate* isolate, Handle<String> subject, Handle<JSRegExp> regexp, | |
1253 Handle<Object> replace_obj) { | |
1254 Factory* factory = isolate->factory(); | |
1255 Handle<JSObject> last_match_info = isolate->regexp_last_match_info(); | |
1256 | |
1257 // TODO(jgruber): This is a pattern we could refactor. | |
1258 Handle<Object> match_indices_obj; | |
1259 ASSIGN_RETURN_ON_EXCEPTION( | |
1260 isolate, match_indices_obj, | |
1261 RegExpImpl::Exec(regexp, subject, 0, last_match_info), String); | |
1262 | |
1263 if (match_indices_obj->IsNull(isolate)) { | |
1264 RETURN_ON_EXCEPTION(isolate, RegExpUtils::SetLastIndex(isolate, regexp, 0), | |
1265 String); | |
1266 return subject; | |
1267 } | |
1268 | |
1269 Handle<JSObject> match_indices = Handle<JSObject>::cast(match_indices_obj); | |
1270 | |
1271 const int index = RegExpUtils::GetLastMatchCapture(isolate, match_indices, 0); | |
1272 const int end_of_match = | |
1273 RegExpUtils::GetLastMatchCapture(isolate, match_indices, 1); | |
1274 | |
1275 IncrementalStringBuilder builder(isolate); | |
1276 builder.AppendString(factory->NewSubString(subject, 0, index)); | |
1277 | |
1278 // Compute the parameter list consisting of the match, captures, index, | |
1279 // and subject for the replace function invocation. | |
1280 // The number of captures plus one for the match. | |
1281 const int m = | |
1282 RegExpUtils::GetLastMatchNumberOfCaptures(isolate, match_indices) / 2; | |
1283 | |
1284 const int argc = m + 2; | |
1285 ScopedVector<Handle<Object>> argv(argc); | |
1286 | |
1287 for (int j = 0; j < m; j++) { | |
1288 bool ok; | |
1289 Handle<String> capture = | |
1290 RegExpUtils::GenericCaptureGetter(isolate, match_indices, j, &ok); | |
1291 if (ok) { | |
1292 argv[j] = capture; | |
1293 } else { | |
1294 argv[j] = factory->undefined_value(); | |
1295 } | |
1296 } | |
1297 | |
1298 argv[m] = handle(Smi::FromInt(index), isolate); | |
1299 argv[m + 1] = subject; | |
1300 | |
1301 Handle<Object> replacement_obj; | |
1302 ASSIGN_RETURN_ON_EXCEPTION( | |
1303 isolate, replacement_obj, | |
1304 Execution::Call(isolate, replace_obj, factory->undefined_value(), argc, | |
1305 argv.start()), | |
1306 String); | |
1307 | |
1308 Handle<String> replacement; | |
1309 ASSIGN_RETURN_ON_EXCEPTION( | |
1310 isolate, replacement, Object::ToString(isolate, replacement_obj), String); | |
1311 | |
1312 builder.AppendString(replacement); | |
1313 builder.AppendString( | |
1314 factory->NewSubString(subject, end_of_match, subject->length())); | |
1315 | |
1316 return builder.Finish(); | |
1317 } | |
1318 | |
1319 // Legacy implementation of RegExp.prototype[Symbol.replace] which | |
1320 // doesn't properly call the underlying exec method. | |
1321 MaybeHandle<String> RegExpReplace(Isolate* isolate, Handle<JSRegExp> regexp, | |
1322 Handle<String> string, | |
1323 Handle<Object> replace_obj) { | |
1324 Factory* factory = isolate->factory(); | |
1325 | |
1326 // TODO(jgruber): We need the even stricter guarantee of an unmodified | |
1327 // JSRegExp map here for access to GetFlags to be legal. | |
1328 const int flags = regexp->GetFlags(); | |
1329 const bool global = (flags & JSRegExp::kGlobal) != 0; | |
1330 | |
1331 const bool functional_replace = replace_obj->IsCallable(); | |
1332 if (!functional_replace) { | |
1333 Handle<String> replace; | |
1334 ASSIGN_RETURN_ON_EXCEPTION(isolate, replace, | |
1335 Object::ToString(isolate, replace_obj), String); | |
1336 replace = String::Flatten(replace); | |
1337 | |
1338 Handle<JSObject> last_match_info = isolate->regexp_last_match_info(); | |
1339 | |
1340 if (!global) { | |
1341 // Non-global regexp search, string replace. | |
1342 | |
1343 Handle<Object> match_indices_obj; | |
1344 ASSIGN_RETURN_ON_EXCEPTION( | |
1345 isolate, match_indices_obj, | |
1346 RegExpImpl::Exec(regexp, string, 0, last_match_info), String); | |
1347 | |
1348 if (match_indices_obj->IsNull(isolate)) { | |
1349 RETURN_ON_EXCEPTION( | |
1350 isolate, RegExpUtils::SetLastIndex(isolate, regexp, 0), String); | |
1351 return string; | |
1352 } | |
1353 | |
1354 auto match_indices = Handle<JSReceiver>::cast(match_indices_obj); | |
1355 | |
1356 Handle<Object> start_index_obj = | |
1357 JSReceiver::GetElement(isolate, match_indices, | |
1358 RegExpImpl::kFirstCapture) | |
1359 .ToHandleChecked(); | |
1360 const int start_index = Handle<Smi>::cast(start_index_obj)->value(); | |
1361 | |
1362 Handle<Object> end_index_obj = | |
1363 JSReceiver::GetElement(isolate, match_indices, | |
1364 RegExpImpl::kFirstCapture + 1) | |
1365 .ToHandleChecked(); | |
1366 const int end_index = Handle<Smi>::cast(end_index_obj)->value(); | |
1367 | |
1368 IncrementalStringBuilder builder(isolate); | |
1369 builder.AppendString(factory->NewSubString(string, 0, start_index)); | |
1370 | |
1371 if (replace->length() > 0) { | |
1372 MatchInfoBackedMatch m(isolate, string, last_match_info); | |
1373 Handle<String> replacement; | |
1374 ASSIGN_RETURN_ON_EXCEPTION( | |
1375 isolate, replacement, String::GetSubstitution(isolate, &m, replace), | |
1376 String); | |
1377 builder.AppendString(replacement); | |
1378 } | |
1379 | |
1380 builder.AppendString( | |
1381 factory->NewSubString(string, end_index, string->length())); | |
1382 return builder.Finish(); | |
1383 } else { | |
1384 // Global regexp search, string replace. | |
1385 DCHECK(global); | |
1386 RETURN_ON_EXCEPTION( | |
1387 isolate, RegExpUtils::SetLastIndex(isolate, regexp, 0), String); | |
1388 | |
1389 if (replace->length() == 0) { | |
1390 if (string->HasOnlyOneByteChars()) { | |
1391 Object* result = | |
1392 StringReplaceGlobalRegExpWithEmptyString<SeqOneByteString>( | |
1393 isolate, string, regexp, last_match_info); | |
1394 return handle(String::cast(result), isolate); | |
1395 } else { | |
1396 Object* result = | |
1397 StringReplaceGlobalRegExpWithEmptyString<SeqTwoByteString>( | |
1398 isolate, string, regexp, last_match_info); | |
1399 return handle(String::cast(result), isolate); | |
1400 } | |
1401 } | |
1402 | |
1403 Object* result = StringReplaceGlobalRegExpWithString( | |
1404 isolate, string, regexp, replace, last_match_info); | |
1405 if (result->IsString()) { | |
1406 return handle(String::cast(result), isolate); | |
1407 } else { | |
1408 return MaybeHandle<String>(); | |
1409 } | |
1410 } | |
1411 } else { | |
1412 DCHECK(functional_replace); | |
1413 if (global) { | |
1414 // Global regexp search, function replace. | |
1415 return StringReplaceGlobalRegExpWithFunction(isolate, string, regexp, | |
1416 replace_obj); | |
1417 } else { | |
1418 // Non-global regexp search, function replace. | |
1419 return StringReplaceNonGlobalRegExpWithFunction(isolate, string, regexp, | |
1420 replace_obj); | |
1421 } | |
1422 } | |
1423 | |
1424 UNREACHABLE(); | |
1425 return MaybeHandle<String>(); | |
1426 } | |
1427 | |
1428 } // namespace | |
1429 | |
1430 // Slow path for: | |
1431 // ES#sec-regexp.prototype-@@replace | |
1432 // RegExp.prototype [ @@replace ] ( string, replaceValue ) | |
1433 RUNTIME_FUNCTION(Runtime_RegExpReplace) { | |
1434 HandleScope scope(isolate); | |
1435 DCHECK(args.length() == 3); | |
1436 | |
1437 CONVERT_ARG_HANDLE_CHECKED(JSReceiver, recv, 0); | |
1438 CONVERT_ARG_HANDLE_CHECKED(String, string, 1); | |
1439 Handle<Object> replace_obj = args.at<Object>(2); | |
1440 | |
1441 Factory* factory = isolate->factory(); | |
1442 | |
1443 string = String::Flatten(string); | |
1444 | |
1445 const int length = string->length(); | |
1446 const bool functional_replace = replace_obj->IsCallable(); | |
1447 | |
1448 Handle<String> replace; | |
1449 if (!functional_replace) { | |
1450 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, replace, | |
1451 Object::ToString(isolate, replace_obj)); | |
1452 } | |
1453 | |
1454 Handle<Object> global_obj; | |
1455 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( | |
1456 isolate, global_obj, | |
1457 JSReceiver::GetProperty(recv, factory->global_string())); | |
1458 const bool global = global_obj->BooleanValue(); | |
1459 | |
1460 bool unicode = false; | |
1461 if (global) { | |
1462 Handle<Object> unicode_obj; | |
1463 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( | |
1464 isolate, unicode_obj, | |
1465 JSReceiver::GetProperty(recv, factory->unicode_string())); | |
1466 unicode = unicode_obj->BooleanValue(); | |
1467 | |
1468 RETURN_FAILURE_ON_EXCEPTION(isolate, | |
1469 RegExpUtils::SetLastIndex(isolate, recv, 0)); | |
1470 } | |
1471 | |
1472 // TODO(adamk): this fast path is wrong as we doesn't ensure that 'exec' | |
1473 // is actually a data property on RegExp.prototype. | |
1474 Handle<Object> exec = factory->undefined_value(); | |
1475 if (recv->IsJSRegExp()) { | |
1476 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( | |
1477 isolate, exec, JSObject::GetProperty( | |
1478 recv, factory->NewStringFromAsciiChecked("exec"))); | |
1479 if (RegExpUtils::IsBuiltinExec(exec)) { | |
1480 RETURN_RESULT_OR_FAILURE( | |
1481 isolate, RegExpReplace(isolate, Handle<JSRegExp>::cast(recv), string, | |
1482 replace_obj)); | |
1483 } | |
1484 } | |
1485 | |
1486 Zone zone(isolate->allocator()); | |
1487 ZoneVector<Handle<Object>> results(&zone); | |
1488 | |
1489 while (true) { | |
1490 Handle<Object> result; | |
1491 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( | |
1492 isolate, result, RegExpUtils::RegExpExec(isolate, recv, string, exec)); | |
1493 | |
1494 // Ensure exec will be read again on the next loop through. | |
1495 exec = factory->undefined_value(); | |
1496 | |
1497 if (result->IsNull(isolate)) break; | |
1498 | |
1499 results.push_back(result); | |
1500 if (!global) break; | |
1501 | |
1502 Handle<Object> match_obj; | |
1503 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, match_obj, | |
1504 Object::GetElement(isolate, result, 0)); | |
1505 | |
1506 Handle<String> match; | |
1507 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, match, | |
1508 Object::ToString(isolate, match_obj)); | |
1509 | |
1510 if (match->length() == 0) { | |
1511 RETURN_FAILURE_ON_EXCEPTION(isolate, RegExpUtils::SetAdvancedStringIndex( | |
1512 isolate, recv, string, unicode)); | |
1513 } | |
1514 } | |
1515 | |
1516 // TODO(jgruber): Look into ReplacementStringBuilder instead. | |
1517 IncrementalStringBuilder builder(isolate); | |
1518 int next_source_position = 0; | |
1519 | |
1520 for (const auto& result : results) { | |
1521 Handle<Object> captures_length_obj; | |
1522 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( | |
1523 isolate, captures_length_obj, | |
1524 Object::GetProperty(result, factory->length_string())); | |
1525 | |
1526 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( | |
1527 isolate, captures_length_obj, | |
1528 Object::ToLength(isolate, captures_length_obj)); | |
1529 const int captures_length = | |
1530 std::max(Handle<Smi>::cast(captures_length_obj)->value(), 0); | |
1531 | |
1532 Handle<Object> match_obj; | |
1533 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, match_obj, | |
1534 Object::GetElement(isolate, result, 0)); | |
1535 | |
1536 Handle<String> match; | |
1537 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, match, | |
1538 Object::ToString(isolate, match_obj)); | |
1539 | |
1540 const int match_length = match->length(); | |
1541 | |
1542 Handle<Object> position_obj; | |
1543 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( | |
1544 isolate, position_obj, | |
1545 Object::GetProperty(result, factory->index_string())); | |
1546 | |
1547 // TODO(jgruber): Extract and correct error handling. Since we can go up to | |
1548 // 2^53 - 1 (at least for ToLength), we might actually need uint64_t here? | |
1549 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( | |
1550 isolate, position_obj, Object::ToInteger(isolate, position_obj)); | |
1551 const int position = | |
1552 std::max(std::min(Handle<Smi>::cast(position_obj)->value(), length), 0); | |
1553 | |
1554 ZoneVector<Handle<Object>> captures(&zone); | |
1555 for (int n = 0; n < captures_length; n++) { | |
1556 Handle<Object> capture; | |
1557 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( | |
1558 isolate, capture, Object::GetElement(isolate, result, n)); | |
1559 | |
1560 if (!capture->IsUndefined(isolate)) { | |
1561 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, capture, | |
1562 Object::ToString(isolate, capture)); | |
1563 } | |
1564 captures.push_back(capture); | |
1565 } | |
1566 | |
1567 Handle<String> replacement; | |
1568 if (functional_replace) { | |
1569 const int argc = captures_length + 2; | |
1570 ScopedVector<Handle<Object>> argv(argc); | |
1571 | |
1572 for (int j = 0; j < captures_length; j++) { | |
1573 argv[j] = captures[j]; | |
1574 } | |
1575 | |
1576 argv[captures_length] = handle(Smi::FromInt(position), isolate); | |
1577 argv[captures_length + 1] = string; | |
1578 | |
1579 Handle<Object> replacement_obj; | |
1580 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( | |
1581 isolate, replacement_obj, | |
1582 Execution::Call(isolate, replace_obj, factory->undefined_value(), | |
1583 argc, argv.start())); | |
1584 | |
1585 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( | |
1586 isolate, replacement, Object::ToString(isolate, replacement_obj)); | |
1587 } else { | |
1588 VectorBackedMatch m(isolate, string, match, position, &captures); | |
1589 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( | |
1590 isolate, replacement, String::GetSubstitution(isolate, &m, replace)); | |
1591 } | |
1592 | |
1593 if (position >= next_source_position) { | |
1594 builder.AppendString( | |
1595 factory->NewSubString(string, next_source_position, position)); | |
1596 builder.AppendString(replacement); | |
1597 | |
1598 next_source_position = position + match_length; | |
1599 } | |
1600 } | |
1601 | |
1602 if (next_source_position < length) { | |
1603 builder.AppendString( | |
1604 factory->NewSubString(string, next_source_position, length)); | |
1605 } | |
1606 | |
1607 RETURN_RESULT_OR_FAILURE(isolate, builder.Finish()); | |
1608 } | |
1012 | 1609 |
1013 RUNTIME_FUNCTION(Runtime_RegExpExecReThrow) { | 1610 RUNTIME_FUNCTION(Runtime_RegExpExecReThrow) { |
1014 SealHandleScope shs(isolate); | 1611 SealHandleScope shs(isolate); |
1015 DCHECK(args.length() == 4); | 1612 DCHECK(args.length() == 4); |
1016 Object* exception = isolate->pending_exception(); | 1613 Object* exception = isolate->pending_exception(); |
1017 isolate->clear_pending_exception(); | 1614 isolate->clear_pending_exception(); |
1018 return isolate->ReThrow(exception); | 1615 return isolate->ReThrow(exception); |
1019 } | 1616 } |
1020 | 1617 |
1021 | 1618 |
1022 RUNTIME_FUNCTION(Runtime_IsRegExp) { | 1619 RUNTIME_FUNCTION(Runtime_IsRegExp) { |
1023 SealHandleScope shs(isolate); | 1620 SealHandleScope shs(isolate); |
1024 DCHECK(args.length() == 1); | 1621 DCHECK(args.length() == 1); |
1025 CONVERT_ARG_CHECKED(Object, obj, 0); | 1622 CONVERT_ARG_CHECKED(Object, obj, 0); |
1026 return isolate->heap()->ToBoolean(obj->IsJSRegExp()); | 1623 return isolate->heap()->ToBoolean(obj->IsJSRegExp()); |
1027 } | 1624 } |
1625 | |
1028 } // namespace internal | 1626 } // namespace internal |
1029 } // namespace v8 | 1627 } // namespace v8 |
OLD | NEW |