Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(2)

Side by Side Diff: src/runtime/runtime-regexp.cc

Issue 2398423002: [regexp] Port RegExp.prototype[@@replace] (Closed)
Patch Set: Tweaks in string code-stub-assembler methods Created 4 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698