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

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

Issue 2398423002: [regexp] Port RegExp.prototype[@@replace] (Closed)
Patch Set: Smi::kZero 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
« no previous file with comments | « src/runtime/runtime.h ('k') | src/string-builder.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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): Convert result_array into a List<Handle<Object>> (or
1109 // similar) and adapt / remove FixedArrayBuilder.
1110 Handle<JSObject> last_match_info = isolate->regexp_last_match_info();
1111 Handle<FixedArray> result_array = factory->NewFixedArrayWithHoles(16);
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 // Reload the last match info since it might have changed in the meantime.
1120 last_match_info = isolate->regexp_last_match_info();
1121
1122 if (res->IsNull(isolate)) return subject; // No matches at all.
1123
1124 result_array = Handle<FixedArray>::cast(res);
1125 const int result_length = result_array->length();
1126
1127 const int num_captures =
1128 RegExpUtils::GetLastMatchNumberOfCaptures(isolate, last_match_info) / 2;
1129 if (num_captures == 1) {
1130 // If the number of captures is one then there are no explicit captures in
1131 // the regexp, just the implicit capture that captures the whole match. In
1132 // this case we can simplify quite a bit and end up with something faster.
1133 // The builder will consist of some integers that indicate slices of the
1134 // input string and some replacements that were returned from the replace
1135 // function.
1136 int match_start = 0;
1137 for (int i = 0; i < result_length; i++) {
1138 Handle<Object> elem = FixedArray::get(*result_array, i, isolate);
1139 if (elem->IsSmi()) {
1140 // Integers represent slices of the original string.
1141 // TODO(jgruber): Maybe we don't need this weird encoding anymore (in
1142 // preparation to invoking StringBuilderConcat), but can just copy into
1143 // the result string with the IncrementalStringBuilder as we go?
1144 const int elem_value = Handle<Smi>::cast(elem)->value();
1145 if (elem_value > 0) {
1146 match_start = (elem_value >> 11) + (elem_value & 0x7ff);
1147 } else {
1148 Handle<Object> next_elem =
1149 FixedArray::get(*result_array, ++i, isolate);
1150 const int next_elem_value = Handle<Smi>::cast(next_elem)->value();
1151 match_start = next_elem_value - elem_value;
1152 }
1153 } else {
1154 DCHECK(elem->IsString());
1155 Handle<String> elem_string = Handle<String>::cast(elem);
1156
1157 // Overwrite the i'th element in the results with the string we got
1158 // back from the callback function.
1159 const int argc = 3;
1160 ScopedVector<Handle<Object>> argv(argc);
1161
1162 argv[0] = elem_string;
1163 argv[1] = handle(Smi::FromInt(match_start), isolate);
1164 argv[2] = subject;
1165
1166 Handle<Object> replacement_obj;
1167 ASSIGN_RETURN_ON_EXCEPTION(
1168 isolate, replacement_obj,
1169 Execution::Call(isolate, replace_obj, factory->undefined_value(),
1170 argc, argv.start()),
1171 String);
1172
1173 Handle<String> replacement;
1174 ASSIGN_RETURN_ON_EXCEPTION(isolate, replacement,
1175 Object::ToString(isolate, replacement_obj),
1176 String);
1177
1178 result_array->set(i, *replacement);
1179 match_start += elem_string->length();
1180 }
1181 }
1182 } else {
1183 DCHECK(num_captures > 1);
1184 for (int i = 0; i < result_length; i++) {
1185 Handle<Object> elem = FixedArray::get(*result_array, i, isolate);
1186 if (elem->IsSmi()) continue;
1187
1188 // TODO(jgruber): We can skip this whole round-trip through a JS array
1189 // for result_array.
1190 Handle<JSArray> elem_array = Handle<JSArray>::cast(elem);
1191 Handle<FixedArray> elem_array_elems(
1192 FixedArray::cast(elem_array->elements()), isolate);
1193
1194 const int argc = elem_array_elems->length();
1195 ScopedVector<Handle<Object>> argv(argc);
1196
1197 for (int j = 0; j < argc; j++) {
1198 argv[j] = FixedArray::get(*elem_array_elems, j, isolate);
1199 }
1200
1201 // TODO(jgruber): This call is another pattern we could refactor.
1202 Handle<Object> replacement_obj;
1203 ASSIGN_RETURN_ON_EXCEPTION(
1204 isolate, replacement_obj,
1205 Execution::Call(isolate, replace_obj, factory->undefined_value(),
1206 argc, argv.start()),
1207 String);
1208
1209 Handle<String> replacement;
1210 ASSIGN_RETURN_ON_EXCEPTION(isolate, replacement,
1211 Object::ToString(isolate, replacement_obj),
1212 String);
1213
1214 result_array->set(i, *replacement);
1215 }
1216 }
1217
1218 if (result_length == 0) {
1219 return factory->empty_string();
1220 } else if (result_length == 1) {
1221 Handle<Object> first = FixedArray::get(*result_array, 0, isolate);
1222 if (first->IsString()) return Handle<String>::cast(first);
1223 }
1224
1225 bool one_byte = subject->HasOnlyOneByteChars();
1226 const int length = StringBuilderConcatLength(subject->length(), *result_array,
1227 result_length, &one_byte);
1228
1229 if (length == -1) {
1230 isolate->Throw(isolate->heap()->illegal_argument_string());
1231 return MaybeHandle<String>();
1232 }
1233
1234 if (one_byte) {
1235 Handle<SeqOneByteString> answer;
1236 ASSIGN_RETURN_ON_EXCEPTION(isolate, answer,
1237 isolate->factory()->NewRawOneByteString(length),
1238 String);
1239 StringBuilderConcatHelper(*subject, answer->GetChars(), *result_array,
1240 result_length);
1241 return answer;
1242 } else {
1243 DCHECK(!one_byte);
1244 Handle<SeqTwoByteString> answer;
1245 ASSIGN_RETURN_ON_EXCEPTION(isolate, answer,
1246 isolate->factory()->NewRawTwoByteString(length),
1247 String);
1248 StringBuilderConcatHelper(*subject, answer->GetChars(), *result_array,
1249 result_length);
1250 return answer;
1251 }
1252
1253 UNREACHABLE();
1254 return MaybeHandle<String>();
1255 }
1256
1257 MaybeHandle<String> StringReplaceNonGlobalRegExpWithFunction(
1258 Isolate* isolate, Handle<String> subject, Handle<JSRegExp> regexp,
1259 Handle<Object> replace_obj) {
1260 Factory* factory = isolate->factory();
1261 Handle<JSObject> last_match_info = isolate->regexp_last_match_info();
1262
1263 // TODO(jgruber): This is a pattern we could refactor.
1264 Handle<Object> match_indices_obj;
1265 ASSIGN_RETURN_ON_EXCEPTION(
1266 isolate, match_indices_obj,
1267 RegExpImpl::Exec(regexp, subject, 0, last_match_info), String);
1268
1269 if (match_indices_obj->IsNull(isolate)) {
1270 RETURN_ON_EXCEPTION(isolate, RegExpUtils::SetLastIndex(isolate, regexp, 0),
1271 String);
1272 return subject;
1273 }
1274
1275 Handle<JSObject> match_indices = Handle<JSObject>::cast(match_indices_obj);
1276
1277 const int index = RegExpUtils::GetLastMatchCapture(isolate, match_indices, 0);
1278 const int end_of_match =
1279 RegExpUtils::GetLastMatchCapture(isolate, match_indices, 1);
1280
1281 IncrementalStringBuilder builder(isolate);
1282 builder.AppendString(factory->NewSubString(subject, 0, index));
1283
1284 // Compute the parameter list consisting of the match, captures, index,
1285 // and subject for the replace function invocation.
1286 // The number of captures plus one for the match.
1287 const int m =
1288 RegExpUtils::GetLastMatchNumberOfCaptures(isolate, match_indices) / 2;
1289
1290 const int argc = m + 2;
1291 ScopedVector<Handle<Object>> argv(argc);
1292
1293 for (int j = 0; j < m; j++) {
1294 bool ok;
1295 Handle<String> capture =
1296 RegExpUtils::GenericCaptureGetter(isolate, match_indices, j, &ok);
1297 if (ok) {
1298 argv[j] = capture;
1299 } else {
1300 argv[j] = factory->undefined_value();
1301 }
1302 }
1303
1304 argv[m] = handle(Smi::FromInt(index), isolate);
1305 argv[m + 1] = subject;
1306
1307 Handle<Object> replacement_obj;
1308 ASSIGN_RETURN_ON_EXCEPTION(
1309 isolate, replacement_obj,
1310 Execution::Call(isolate, replace_obj, factory->undefined_value(), argc,
1311 argv.start()),
1312 String);
1313
1314 Handle<String> replacement;
1315 ASSIGN_RETURN_ON_EXCEPTION(
1316 isolate, replacement, Object::ToString(isolate, replacement_obj), String);
1317
1318 builder.AppendString(replacement);
1319 builder.AppendString(
1320 factory->NewSubString(subject, end_of_match, subject->length()));
1321
1322 return builder.Finish();
1323 }
1324
1325 // Legacy implementation of RegExp.prototype[Symbol.replace] which
1326 // doesn't properly call the underlying exec method.
1327 MaybeHandle<String> RegExpReplace(Isolate* isolate, Handle<JSRegExp> regexp,
1328 Handle<String> string,
1329 Handle<Object> replace_obj) {
1330 Factory* factory = isolate->factory();
1331
1332 // TODO(jgruber): We need the even stricter guarantee of an unmodified
1333 // JSRegExp map here for access to GetFlags to be legal.
1334 const int flags = regexp->GetFlags();
1335 const bool global = (flags & JSRegExp::kGlobal) != 0;
1336
1337 const bool functional_replace = replace_obj->IsCallable();
1338 if (!functional_replace) {
1339 Handle<String> replace;
1340 ASSIGN_RETURN_ON_EXCEPTION(isolate, replace,
1341 Object::ToString(isolate, replace_obj), String);
1342 replace = String::Flatten(replace);
1343
1344 Handle<JSObject> last_match_info = isolate->regexp_last_match_info();
1345
1346 if (!global) {
1347 // Non-global regexp search, string replace.
1348
1349 Handle<Object> match_indices_obj;
1350 ASSIGN_RETURN_ON_EXCEPTION(
1351 isolate, match_indices_obj,
1352 RegExpImpl::Exec(regexp, string, 0, last_match_info), String);
1353
1354 if (match_indices_obj->IsNull(isolate)) {
1355 RETURN_ON_EXCEPTION(
1356 isolate, RegExpUtils::SetLastIndex(isolate, regexp, 0), String);
1357 return string;
1358 }
1359
1360 auto match_indices = Handle<JSReceiver>::cast(match_indices_obj);
1361
1362 Handle<Object> start_index_obj =
1363 JSReceiver::GetElement(isolate, match_indices,
1364 RegExpImpl::kFirstCapture)
1365 .ToHandleChecked();
1366 const int start_index = Handle<Smi>::cast(start_index_obj)->value();
1367
1368 Handle<Object> end_index_obj =
1369 JSReceiver::GetElement(isolate, match_indices,
1370 RegExpImpl::kFirstCapture + 1)
1371 .ToHandleChecked();
1372 const int end_index = Handle<Smi>::cast(end_index_obj)->value();
1373
1374 IncrementalStringBuilder builder(isolate);
1375 builder.AppendString(factory->NewSubString(string, 0, start_index));
1376
1377 if (replace->length() > 0) {
1378 MatchInfoBackedMatch m(isolate, string, last_match_info);
1379 Handle<String> replacement;
1380 ASSIGN_RETURN_ON_EXCEPTION(
1381 isolate, replacement, String::GetSubstitution(isolate, &m, replace),
1382 String);
1383 builder.AppendString(replacement);
1384 }
1385
1386 builder.AppendString(
1387 factory->NewSubString(string, end_index, string->length()));
1388 return builder.Finish();
1389 } else {
1390 // Global regexp search, string replace.
1391 DCHECK(global);
1392 RETURN_ON_EXCEPTION(
1393 isolate, RegExpUtils::SetLastIndex(isolate, regexp, 0), String);
1394
1395 if (replace->length() == 0) {
1396 if (string->HasOnlyOneByteChars()) {
1397 Object* result =
1398 StringReplaceGlobalRegExpWithEmptyString<SeqOneByteString>(
1399 isolate, string, regexp, last_match_info);
1400 return handle(String::cast(result), isolate);
1401 } else {
1402 Object* result =
1403 StringReplaceGlobalRegExpWithEmptyString<SeqTwoByteString>(
1404 isolate, string, regexp, last_match_info);
1405 return handle(String::cast(result), isolate);
1406 }
1407 }
1408
1409 Object* result = StringReplaceGlobalRegExpWithString(
1410 isolate, string, regexp, replace, last_match_info);
1411 if (result->IsString()) {
1412 return handle(String::cast(result), isolate);
1413 } else {
1414 return MaybeHandle<String>();
1415 }
1416 }
1417 } else {
1418 DCHECK(functional_replace);
1419 if (global) {
1420 // Global regexp search, function replace.
1421 return StringReplaceGlobalRegExpWithFunction(isolate, string, regexp,
1422 replace_obj);
1423 } else {
1424 // Non-global regexp search, function replace.
1425 return StringReplaceNonGlobalRegExpWithFunction(isolate, string, regexp,
1426 replace_obj);
1427 }
1428 }
1429
1430 UNREACHABLE();
1431 return MaybeHandle<String>();
1432 }
1433
1434 } // namespace
1435
1436 // Slow path for:
1437 // ES#sec-regexp.prototype-@@replace
1438 // RegExp.prototype [ @@replace ] ( string, replaceValue )
1439 RUNTIME_FUNCTION(Runtime_RegExpReplace) {
1440 HandleScope scope(isolate);
1441 DCHECK(args.length() == 3);
1442
1443 CONVERT_ARG_HANDLE_CHECKED(JSReceiver, recv, 0);
1444 CONVERT_ARG_HANDLE_CHECKED(String, string, 1);
1445 Handle<Object> replace_obj = args.at<Object>(2);
1446
1447 Factory* factory = isolate->factory();
1448
1449 string = String::Flatten(string);
1450
1451 const int length = string->length();
1452 const bool functional_replace = replace_obj->IsCallable();
1453
1454 Handle<String> replace;
1455 if (!functional_replace) {
1456 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, replace,
1457 Object::ToString(isolate, replace_obj));
1458 }
1459
1460 Handle<Object> global_obj;
1461 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
1462 isolate, global_obj,
1463 JSReceiver::GetProperty(recv, factory->global_string()));
1464 const bool global = global_obj->BooleanValue();
1465
1466 bool unicode = false;
1467 if (global) {
1468 Handle<Object> unicode_obj;
1469 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
1470 isolate, unicode_obj,
1471 JSReceiver::GetProperty(recv, factory->unicode_string()));
1472 unicode = unicode_obj->BooleanValue();
1473
1474 RETURN_FAILURE_ON_EXCEPTION(isolate,
1475 RegExpUtils::SetLastIndex(isolate, recv, 0));
1476 }
1477
1478 // TODO(adamk): this fast path is wrong as we doesn't ensure that 'exec'
1479 // is actually a data property on RegExp.prototype.
1480 Handle<Object> exec = factory->undefined_value();
1481 if (recv->IsJSRegExp()) {
1482 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
1483 isolate, exec, JSObject::GetProperty(
1484 recv, factory->NewStringFromAsciiChecked("exec")));
1485 if (RegExpUtils::IsBuiltinExec(exec)) {
1486 RETURN_RESULT_OR_FAILURE(
1487 isolate, RegExpReplace(isolate, Handle<JSRegExp>::cast(recv), string,
1488 replace_obj));
1489 }
1490 }
1491
1492 Zone zone(isolate->allocator());
1493 ZoneVector<Handle<Object>> results(&zone);
1494
1495 while (true) {
1496 Handle<Object> result;
1497 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
1498 isolate, result, RegExpUtils::RegExpExec(isolate, recv, string, exec));
1499
1500 // Ensure exec will be read again on the next loop through.
1501 exec = factory->undefined_value();
1502
1503 if (result->IsNull(isolate)) break;
1504
1505 results.push_back(result);
1506 if (!global) break;
1507
1508 Handle<Object> match_obj;
1509 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, match_obj,
1510 Object::GetElement(isolate, result, 0));
1511
1512 Handle<String> match;
1513 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, match,
1514 Object::ToString(isolate, match_obj));
1515
1516 if (match->length() == 0) {
1517 RETURN_FAILURE_ON_EXCEPTION(isolate, RegExpUtils::SetAdvancedStringIndex(
1518 isolate, recv, string, unicode));
1519 }
1520 }
1521
1522 // TODO(jgruber): Look into ReplacementStringBuilder instead.
1523 IncrementalStringBuilder builder(isolate);
1524 int next_source_position = 0;
1525
1526 for (const auto& result : results) {
1527 Handle<Object> captures_length_obj;
1528 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
1529 isolate, captures_length_obj,
1530 Object::GetProperty(result, factory->length_string()));
1531
1532 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
1533 isolate, captures_length_obj,
1534 Object::ToLength(isolate, captures_length_obj));
1535 const int captures_length =
1536 std::max(Handle<Smi>::cast(captures_length_obj)->value(), 0);
1537
1538 Handle<Object> match_obj;
1539 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, match_obj,
1540 Object::GetElement(isolate, result, 0));
1541
1542 Handle<String> match;
1543 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, match,
1544 Object::ToString(isolate, match_obj));
1545
1546 const int match_length = match->length();
1547
1548 Handle<Object> position_obj;
1549 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
1550 isolate, position_obj,
1551 Object::GetProperty(result, factory->index_string()));
1552
1553 // TODO(jgruber): Extract and correct error handling. Since we can go up to
1554 // 2^53 - 1 (at least for ToLength), we might actually need uint64_t here?
1555 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
1556 isolate, position_obj, Object::ToInteger(isolate, position_obj));
1557 const int position =
1558 std::max(std::min(Handle<Smi>::cast(position_obj)->value(), length), 0);
1559
1560 ZoneVector<Handle<Object>> captures(&zone);
1561 for (int n = 0; n < captures_length; n++) {
1562 Handle<Object> capture;
1563 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
1564 isolate, capture, Object::GetElement(isolate, result, n));
1565
1566 if (!capture->IsUndefined(isolate)) {
1567 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, capture,
1568 Object::ToString(isolate, capture));
1569 }
1570 captures.push_back(capture);
1571 }
1572
1573 Handle<String> replacement;
1574 if (functional_replace) {
1575 const int argc = captures_length + 2;
1576 ScopedVector<Handle<Object>> argv(argc);
1577
1578 for (int j = 0; j < captures_length; j++) {
1579 argv[j] = captures[j];
1580 }
1581
1582 argv[captures_length] = handle(Smi::FromInt(position), isolate);
1583 argv[captures_length + 1] = string;
1584
1585 Handle<Object> replacement_obj;
1586 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
1587 isolate, replacement_obj,
1588 Execution::Call(isolate, replace_obj, factory->undefined_value(),
1589 argc, argv.start()));
1590
1591 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
1592 isolate, replacement, Object::ToString(isolate, replacement_obj));
1593 } else {
1594 VectorBackedMatch m(isolate, string, match, position, &captures);
1595 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
1596 isolate, replacement, String::GetSubstitution(isolate, &m, replace));
1597 }
1598
1599 if (position >= next_source_position) {
1600 builder.AppendString(
1601 factory->NewSubString(string, next_source_position, position));
1602 builder.AppendString(replacement);
1603
1604 next_source_position = position + match_length;
1605 }
1606 }
1607
1608 if (next_source_position < length) {
1609 builder.AppendString(
1610 factory->NewSubString(string, next_source_position, length));
1611 }
1612
1613 RETURN_RESULT_OR_FAILURE(isolate, builder.Finish());
1614 }
1012 1615
1013 RUNTIME_FUNCTION(Runtime_RegExpExecReThrow) { 1616 RUNTIME_FUNCTION(Runtime_RegExpExecReThrow) {
1014 SealHandleScope shs(isolate); 1617 SealHandleScope shs(isolate);
1015 DCHECK(args.length() == 4); 1618 DCHECK(args.length() == 4);
1016 Object* exception = isolate->pending_exception(); 1619 Object* exception = isolate->pending_exception();
1017 isolate->clear_pending_exception(); 1620 isolate->clear_pending_exception();
1018 return isolate->ReThrow(exception); 1621 return isolate->ReThrow(exception);
1019 } 1622 }
1020 1623
1021 1624
1022 RUNTIME_FUNCTION(Runtime_IsRegExp) { 1625 RUNTIME_FUNCTION(Runtime_IsRegExp) {
1023 SealHandleScope shs(isolate); 1626 SealHandleScope shs(isolate);
1024 DCHECK(args.length() == 1); 1627 DCHECK(args.length() == 1);
1025 CONVERT_ARG_CHECKED(Object, obj, 0); 1628 CONVERT_ARG_CHECKED(Object, obj, 0);
1026 return isolate->heap()->ToBoolean(obj->IsJSRegExp()); 1629 return isolate->heap()->ToBoolean(obj->IsJSRegExp());
1027 } 1630 }
1631
1028 } // namespace internal 1632 } // namespace internal
1029 } // namespace v8 1633 } // namespace v8
OLDNEW
« no previous file with comments | « src/runtime/runtime.h ('k') | src/string-builder.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698