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