OLD | NEW |
1 // Copyright 2012 the V8 project authors. All rights reserved. | 1 // Copyright 2012 the V8 project authors. All rights reserved. |
2 // Redistribution and use in source and binary forms, with or without | 2 // Redistribution and use in source and binary forms, with or without |
3 // modification, are permitted provided that the following conditions are | 3 // modification, are permitted provided that the following conditions are |
4 // met: | 4 // met: |
5 // | 5 // |
6 // * Redistributions of source code must retain the above copyright | 6 // * Redistributions of source code must retain the above copyright |
7 // notice, this list of conditions and the following disclaimer. | 7 // notice, this list of conditions and the following disclaimer. |
8 // * Redistributions in binary form must reproduce the above | 8 // * Redistributions in binary form must reproduce the above |
9 // copyright notice, this list of conditions and the following | 9 // copyright notice, this list of conditions and the following |
10 // disclaimer in the documentation and/or other materials provided | 10 // disclaimer in the documentation and/or other materials provided |
(...skipping 2870 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2881 limit, | 2881 limit, |
2882 zone); | 2882 zone); |
2883 } | 2883 } |
2884 } | 2884 } |
2885 } | 2885 } |
2886 } | 2886 } |
2887 } | 2887 } |
2888 | 2888 |
2889 | 2889 |
2890 template<typename ResultSeqString> | 2890 template<typename ResultSeqString> |
2891 MUST_USE_RESULT static MaybeObject* StringReplaceAtomRegExpWithString( | 2891 MUST_USE_RESULT static MaybeObject* StringReplaceGlobalAtomRegExpWithString( |
2892 Isolate* isolate, | 2892 Isolate* isolate, |
2893 Handle<String> subject, | 2893 Handle<String> subject, |
2894 Handle<JSRegExp> pattern_regexp, | 2894 Handle<JSRegExp> pattern_regexp, |
2895 Handle<String> replacement, | 2895 Handle<String> replacement, |
2896 Handle<JSArray> last_match_info) { | 2896 Handle<JSArray> last_match_info) { |
2897 ASSERT(subject->IsFlat()); | 2897 ASSERT(subject->IsFlat()); |
2898 ASSERT(replacement->IsFlat()); | 2898 ASSERT(replacement->IsFlat()); |
2899 | 2899 |
2900 Zone* zone = isolate->runtime_zone(); | 2900 Zone* zone = isolate->runtime_zone(); |
2901 ZoneScope zone_space(zone, DELETE_ON_EXIT); | 2901 ZoneScope zone_space(zone, DELETE_ON_EXIT); |
2902 ZoneList<int> indices(8, zone); | 2902 ZoneList<int> indices(8, zone); |
2903 ASSERT_EQ(JSRegExp::ATOM, pattern_regexp->TypeTag()); | 2903 ASSERT_EQ(JSRegExp::ATOM, pattern_regexp->TypeTag()); |
2904 String* pattern = | 2904 String* pattern = |
2905 String::cast(pattern_regexp->DataAt(JSRegExp::kAtomPatternIndex)); | 2905 String::cast(pattern_regexp->DataAt(JSRegExp::kAtomPatternIndex)); |
2906 int subject_len = subject->length(); | 2906 int subject_len = subject->length(); |
2907 int pattern_len = pattern->length(); | 2907 int pattern_len = pattern->length(); |
2908 int replacement_len = replacement->length(); | 2908 int replacement_len = replacement->length(); |
2909 | 2909 |
2910 FindStringIndicesDispatch( | 2910 FindStringIndicesDispatch( |
2911 isolate, *subject, pattern, &indices, 0xffffffff, zone); | 2911 isolate, *subject, pattern, &indices, 0xffffffff, zone); |
2912 | 2912 |
2913 int matches = indices.length(); | 2913 int matches = indices.length(); |
2914 if (matches == 0) { | 2914 if (matches == 0) return *subject; |
2915 return isolate->heap()->undefined_value(); | |
2916 } | |
2917 | 2915 |
2918 // Detect integer overflow. | 2916 // Detect integer overflow. |
2919 int64_t result_len_64 = | 2917 int64_t result_len_64 = |
2920 (static_cast<int64_t>(replacement_len) - | 2918 (static_cast<int64_t>(replacement_len) - |
2921 static_cast<int64_t>(pattern_len)) * | 2919 static_cast<int64_t>(pattern_len)) * |
2922 static_cast<int64_t>(matches) + | 2920 static_cast<int64_t>(matches) + |
2923 static_cast<int64_t>(subject_len); | 2921 static_cast<int64_t>(subject_len); |
2924 if (result_len_64 > INT_MAX) return Failure::OutOfMemoryException(0x11); | 2922 if (result_len_64 > INT_MAX) return Failure::OutOfMemoryException(0x11); |
2925 int result_len = static_cast<int>(result_len_64); | 2923 int result_len = static_cast<int>(result_len_64); |
2926 | 2924 |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2966 } | 2964 } |
2967 | 2965 |
2968 int32_t match_indices[] = { indices.at(matches - 1), | 2966 int32_t match_indices[] = { indices.at(matches - 1), |
2969 indices.at(matches - 1) + pattern_len }; | 2967 indices.at(matches - 1) + pattern_len }; |
2970 RegExpImpl::SetLastMatchInfo(last_match_info, subject, 0, match_indices); | 2968 RegExpImpl::SetLastMatchInfo(last_match_info, subject, 0, match_indices); |
2971 | 2969 |
2972 return *result; | 2970 return *result; |
2973 } | 2971 } |
2974 | 2972 |
2975 | 2973 |
2976 MUST_USE_RESULT static MaybeObject* StringReplaceRegExpWithString( | 2974 MUST_USE_RESULT static MaybeObject* StringReplaceGlobalRegExpWithString( |
2977 Isolate* isolate, | 2975 Isolate* isolate, |
2978 Handle<String> subject, | 2976 Handle<String> subject, |
2979 Handle<JSRegExp> regexp, | 2977 Handle<JSRegExp> regexp, |
2980 Handle<String> replacement, | 2978 Handle<String> replacement, |
2981 Handle<JSArray> last_match_info) { | 2979 Handle<JSArray> last_match_info) { |
2982 ASSERT(subject->IsFlat()); | 2980 ASSERT(subject->IsFlat()); |
2983 ASSERT(replacement->IsFlat()); | 2981 ASSERT(replacement->IsFlat()); |
2984 | 2982 |
2985 bool is_global = regexp->GetFlags().is_global(); | |
2986 int capture_count = regexp->CaptureCount(); | 2983 int capture_count = regexp->CaptureCount(); |
2987 int subject_length = subject->length(); | 2984 int subject_length = subject->length(); |
2988 | 2985 |
2989 // CompiledReplacement uses zone allocation. | 2986 // CompiledReplacement uses zone allocation. |
2990 Zone* zone = isolate->runtime_zone(); | 2987 Zone* zone = isolate->runtime_zone(); |
2991 ZoneScope zonescope(zone, DELETE_ON_EXIT); | 2988 ZoneScope zonescope(zone, DELETE_ON_EXIT); |
2992 CompiledReplacement compiled_replacement(zone); | 2989 CompiledReplacement compiled_replacement(zone); |
2993 bool simple_replace = compiled_replacement.Compile(replacement, | 2990 bool simple_replace = compiled_replacement.Compile(replacement, |
2994 capture_count, | 2991 capture_count, |
2995 subject_length); | 2992 subject_length); |
2996 | 2993 |
2997 // Shortcut for simple non-regexp global replacements | 2994 // Shortcut for simple non-regexp global replacements |
2998 if (is_global && | 2995 if (regexp->TypeTag() == JSRegExp::ATOM && simple_replace) { |
2999 regexp->TypeTag() == JSRegExp::ATOM && | |
3000 simple_replace) { | |
3001 if (subject->IsOneByteConvertible() && | 2996 if (subject->IsOneByteConvertible() && |
3002 replacement->IsOneByteConvertible()) { | 2997 replacement->IsOneByteConvertible()) { |
3003 return StringReplaceAtomRegExpWithString<SeqOneByteString>( | 2998 return StringReplaceGlobalAtomRegExpWithString<SeqOneByteString>( |
3004 isolate, subject, regexp, replacement, last_match_info); | 2999 isolate, subject, regexp, replacement, last_match_info); |
3005 } else { | 3000 } else { |
3006 return StringReplaceAtomRegExpWithString<SeqTwoByteString>( | 3001 return StringReplaceGlobalAtomRegExpWithString<SeqTwoByteString>( |
3007 isolate, subject, regexp, replacement, last_match_info); | 3002 isolate, subject, regexp, replacement, last_match_info); |
3008 } | 3003 } |
3009 } | 3004 } |
3010 | 3005 |
3011 RegExpImpl::GlobalCache global_cache(regexp, subject, is_global, isolate); | 3006 RegExpImpl::GlobalCache global_cache(regexp, subject, true, isolate); |
3012 if (global_cache.HasException()) return Failure::Exception(); | 3007 if (global_cache.HasException()) return Failure::Exception(); |
3013 | 3008 |
3014 int32_t* current_match = global_cache.FetchNext(); | 3009 int32_t* current_match = global_cache.FetchNext(); |
3015 if (current_match == NULL) { | 3010 if (current_match == NULL) { |
3016 if (global_cache.HasException()) return Failure::Exception(); | 3011 if (global_cache.HasException()) return Failure::Exception(); |
3017 return isolate->heap()->undefined_value(); | 3012 return *subject; |
3018 } | 3013 } |
3019 | 3014 |
3020 // Guessing the number of parts that the final result string is built | 3015 // Guessing the number of parts that the final result string is built |
3021 // from. Global regexps can match any number of times, so we guess | 3016 // from. Global regexps can match any number of times, so we guess |
3022 // conservatively. | 3017 // conservatively. |
3023 int expected_parts = | 3018 int expected_parts = (compiled_replacement.parts() + 1) * 4 + 1; |
3024 (compiled_replacement.parts() + 1) * (is_global ? 4 : 1) + 1; | |
3025 ReplacementStringBuilder builder(isolate->heap(), | 3019 ReplacementStringBuilder builder(isolate->heap(), |
3026 subject, | 3020 subject, |
3027 expected_parts); | 3021 expected_parts); |
3028 | 3022 |
3029 // Number of parts added by compiled replacement plus preceeding | 3023 // Number of parts added by compiled replacement plus preceeding |
3030 // string and possibly suffix after last match. It is possible for | 3024 // string and possibly suffix after last match. It is possible for |
3031 // all components to use two elements when encoded as two smis. | 3025 // all components to use two elements when encoded as two smis. |
3032 const int parts_added_per_loop = 2 * (compiled_replacement.parts() + 2); | 3026 const int parts_added_per_loop = 2 * (compiled_replacement.parts() + 2); |
3033 | 3027 |
3034 int prev = 0; | 3028 int prev = 0; |
(...skipping 11 matching lines...) Expand all Loading... |
3046 if (simple_replace) { | 3040 if (simple_replace) { |
3047 builder.AddString(replacement); | 3041 builder.AddString(replacement); |
3048 } else { | 3042 } else { |
3049 compiled_replacement.Apply(&builder, | 3043 compiled_replacement.Apply(&builder, |
3050 start, | 3044 start, |
3051 end, | 3045 end, |
3052 current_match); | 3046 current_match); |
3053 } | 3047 } |
3054 prev = end; | 3048 prev = end; |
3055 | 3049 |
3056 // Only continue checking for global regexps. | |
3057 if (!is_global) break; | |
3058 | |
3059 current_match = global_cache.FetchNext(); | 3050 current_match = global_cache.FetchNext(); |
3060 } while (current_match != NULL); | 3051 } while (current_match != NULL); |
3061 | 3052 |
3062 if (global_cache.HasException()) return Failure::Exception(); | 3053 if (global_cache.HasException()) return Failure::Exception(); |
3063 | 3054 |
3064 if (prev < subject_length) { | 3055 if (prev < subject_length) { |
3065 builder.EnsureCapacity(2); | 3056 builder.EnsureCapacity(2); |
3066 builder.AddSubjectSlice(prev, subject_length); | 3057 builder.AddSubjectSlice(prev, subject_length); |
3067 } | 3058 } |
3068 | 3059 |
3069 RegExpImpl::SetLastMatchInfo(last_match_info, | 3060 RegExpImpl::SetLastMatchInfo(last_match_info, |
3070 subject, | 3061 subject, |
3071 capture_count, | 3062 capture_count, |
3072 global_cache.LastSuccessfulMatch()); | 3063 global_cache.LastSuccessfulMatch()); |
3073 | 3064 |
3074 return *(builder.ToString()); | 3065 return *(builder.ToString()); |
3075 } | 3066 } |
3076 | 3067 |
3077 | 3068 |
3078 template <typename ResultSeqString> | 3069 template <typename ResultSeqString> |
3079 MUST_USE_RESULT static MaybeObject* StringReplaceRegExpWithEmptyString( | 3070 MUST_USE_RESULT static MaybeObject* StringReplaceGlobalRegExpWithEmptyString( |
3080 Isolate* isolate, | 3071 Isolate* isolate, |
3081 Handle<String> subject, | 3072 Handle<String> subject, |
3082 Handle<JSRegExp> regexp, | 3073 Handle<JSRegExp> regexp, |
3083 Handle<JSArray> last_match_info) { | 3074 Handle<JSArray> last_match_info) { |
3084 ASSERT(subject->IsFlat()); | 3075 ASSERT(subject->IsFlat()); |
3085 | 3076 |
3086 bool is_global = regexp->GetFlags().is_global(); | |
3087 | |
3088 // Shortcut for simple non-regexp global replacements | 3077 // Shortcut for simple non-regexp global replacements |
3089 if (is_global && | 3078 if (regexp->TypeTag() == JSRegExp::ATOM) { |
3090 regexp->TypeTag() == JSRegExp::ATOM) { | |
3091 Handle<String> empty_string = isolate->factory()->empty_string(); | 3079 Handle<String> empty_string = isolate->factory()->empty_string(); |
3092 if (subject->IsOneByteRepresentation()) { | 3080 if (subject->IsOneByteRepresentation()) { |
3093 return StringReplaceAtomRegExpWithString<SeqOneByteString>( | 3081 return StringReplaceGlobalAtomRegExpWithString<SeqOneByteString>( |
3094 isolate, | 3082 isolate, subject, regexp, empty_string, last_match_info); |
3095 subject, | |
3096 regexp, | |
3097 empty_string, | |
3098 last_match_info); | |
3099 } else { | 3083 } else { |
3100 return StringReplaceAtomRegExpWithString<SeqTwoByteString>( | 3084 return StringReplaceGlobalAtomRegExpWithString<SeqTwoByteString>( |
3101 isolate, | 3085 isolate, subject, regexp, empty_string, last_match_info); |
3102 subject, | |
3103 regexp, | |
3104 empty_string, | |
3105 last_match_info); | |
3106 } | 3086 } |
3107 } | 3087 } |
3108 | 3088 |
3109 RegExpImpl::GlobalCache global_cache(regexp, subject, is_global, isolate); | 3089 RegExpImpl::GlobalCache global_cache(regexp, subject, true, isolate); |
3110 if (global_cache.HasException()) return Failure::Exception(); | 3090 if (global_cache.HasException()) return Failure::Exception(); |
3111 | 3091 |
3112 int32_t* current_match = global_cache.FetchNext(); | 3092 int32_t* current_match = global_cache.FetchNext(); |
3113 if (current_match == NULL) { | 3093 if (current_match == NULL) { |
3114 if (global_cache.HasException()) return Failure::Exception(); | 3094 if (global_cache.HasException()) return Failure::Exception(); |
3115 return isolate->heap()->undefined_value(); | 3095 return *subject; |
3116 } | 3096 } |
3117 | 3097 |
3118 int start = current_match[0]; | 3098 int start = current_match[0]; |
3119 int end = current_match[1]; | 3099 int end = current_match[1]; |
3120 int capture_count = regexp->CaptureCount(); | 3100 int capture_count = regexp->CaptureCount(); |
3121 int subject_length = subject->length(); | 3101 int subject_length = subject->length(); |
3122 | 3102 |
3123 int new_length = subject_length - (end - start); | 3103 int new_length = subject_length - (end - start); |
3124 if (new_length == 0) return isolate->heap()->empty_string(); | 3104 if (new_length == 0) return isolate->heap()->empty_string(); |
3125 | 3105 |
3126 Handle<ResultSeqString> answer; | 3106 Handle<ResultSeqString> answer; |
3127 if (ResultSeqString::kHasAsciiEncoding) { | 3107 if (ResultSeqString::kHasAsciiEncoding) { |
3128 answer = Handle<ResultSeqString>::cast( | 3108 answer = Handle<ResultSeqString>::cast( |
3129 isolate->factory()->NewRawOneByteString(new_length)); | 3109 isolate->factory()->NewRawOneByteString(new_length)); |
3130 } else { | 3110 } else { |
3131 answer = Handle<ResultSeqString>::cast( | 3111 answer = Handle<ResultSeqString>::cast( |
3132 isolate->factory()->NewRawTwoByteString(new_length)); | 3112 isolate->factory()->NewRawTwoByteString(new_length)); |
3133 } | 3113 } |
3134 | 3114 |
3135 if (!is_global) { | |
3136 RegExpImpl::SetLastMatchInfo( | |
3137 last_match_info, subject, capture_count, current_match); | |
3138 if (start == end) { | |
3139 return *subject; | |
3140 } else { | |
3141 if (start > 0) { | |
3142 String::WriteToFlat(*subject, answer->GetChars(), 0, start); | |
3143 } | |
3144 if (end < subject_length) { | |
3145 String::WriteToFlat( | |
3146 *subject, answer->GetChars() + start, end, subject_length); | |
3147 } | |
3148 return *answer; | |
3149 } | |
3150 } | |
3151 | |
3152 int prev = 0; | 3115 int prev = 0; |
3153 int position = 0; | 3116 int position = 0; |
3154 | 3117 |
3155 do { | 3118 do { |
3156 start = current_match[0]; | 3119 start = current_match[0]; |
3157 end = current_match[1]; | 3120 end = current_match[1]; |
3158 if (prev < start) { | 3121 if (prev < start) { |
3159 // Add substring subject[prev;start] to answer string. | 3122 // Add substring subject[prev;start] to answer string. |
3160 String::WriteToFlat( | 3123 String::WriteToFlat(*subject, answer->GetChars() + position, prev, start); |
3161 *subject, answer->GetChars() + position, prev, start); | |
3162 position += start - prev; | 3124 position += start - prev; |
3163 } | 3125 } |
3164 prev = end; | 3126 prev = end; |
3165 | 3127 |
3166 current_match = global_cache.FetchNext(); | 3128 current_match = global_cache.FetchNext(); |
3167 } while (current_match != NULL); | 3129 } while (current_match != NULL); |
3168 | 3130 |
3169 if (global_cache.HasException()) return Failure::Exception(); | 3131 if (global_cache.HasException()) return Failure::Exception(); |
3170 | 3132 |
3171 RegExpImpl::SetLastMatchInfo(last_match_info, | 3133 RegExpImpl::SetLastMatchInfo(last_match_info, |
(...skipping 21 matching lines...) Expand all Loading... |
3193 Address end_of_string = answer->address() + string_size; | 3155 Address end_of_string = answer->address() + string_size; |
3194 isolate->heap()->CreateFillerObjectAt(end_of_string, delta); | 3156 isolate->heap()->CreateFillerObjectAt(end_of_string, delta); |
3195 if (Marking::IsBlack(Marking::MarkBitFrom(*answer))) { | 3157 if (Marking::IsBlack(Marking::MarkBitFrom(*answer))) { |
3196 MemoryChunk::IncrementLiveBytesFromMutator(answer->address(), -delta); | 3158 MemoryChunk::IncrementLiveBytesFromMutator(answer->address(), -delta); |
3197 } | 3159 } |
3198 | 3160 |
3199 return *answer; | 3161 return *answer; |
3200 } | 3162 } |
3201 | 3163 |
3202 | 3164 |
3203 RUNTIME_FUNCTION(MaybeObject*, Runtime_StringReplaceRegExpWithString) { | 3165 RUNTIME_FUNCTION(MaybeObject*, Runtime_StringReplaceGlobalRegExpWithString) { |
3204 ASSERT(args.length() == 4); | 3166 ASSERT(args.length() == 4); |
3205 | 3167 |
3206 HandleScope scope(isolate); | 3168 HandleScope scope(isolate); |
3207 | 3169 |
3208 CONVERT_ARG_HANDLE_CHECKED(String, subject, 0); | 3170 CONVERT_ARG_HANDLE_CHECKED(String, subject, 0); |
3209 CONVERT_ARG_HANDLE_CHECKED(String, replacement, 2); | 3171 CONVERT_ARG_HANDLE_CHECKED(String, replacement, 2); |
3210 CONVERT_ARG_HANDLE_CHECKED(JSRegExp, regexp, 1); | 3172 CONVERT_ARG_HANDLE_CHECKED(JSRegExp, regexp, 1); |
3211 CONVERT_ARG_HANDLE_CHECKED(JSArray, last_match_info, 3); | 3173 CONVERT_ARG_HANDLE_CHECKED(JSArray, last_match_info, 3); |
3212 | 3174 |
| 3175 ASSERT(regexp->GetFlags().is_global()); |
| 3176 |
3213 if (!subject->IsFlat()) subject = FlattenGetString(subject); | 3177 if (!subject->IsFlat()) subject = FlattenGetString(subject); |
3214 | 3178 |
3215 if (!replacement->IsFlat()) replacement = FlattenGetString(replacement); | 3179 if (!replacement->IsFlat()) replacement = FlattenGetString(replacement); |
3216 | 3180 |
3217 ASSERT(last_match_info->HasFastObjectElements()); | 3181 ASSERT(last_match_info->HasFastObjectElements()); |
3218 | 3182 |
3219 if (replacement->length() == 0) { | 3183 if (replacement->length() == 0) { |
3220 if (subject->IsOneByteConvertible()) { | 3184 if (subject->IsOneByteConvertible()) { |
3221 return StringReplaceRegExpWithEmptyString<SeqOneByteString>( | 3185 return StringReplaceGlobalRegExpWithEmptyString<SeqOneByteString>( |
3222 isolate, subject, regexp, last_match_info); | 3186 isolate, subject, regexp, last_match_info); |
3223 } else { | 3187 } else { |
3224 return StringReplaceRegExpWithEmptyString<SeqTwoByteString>( | 3188 return StringReplaceGlobalRegExpWithEmptyString<SeqTwoByteString>( |
3225 isolate, subject, regexp, last_match_info); | 3189 isolate, subject, regexp, last_match_info); |
3226 } | 3190 } |
3227 } | 3191 } |
3228 | 3192 |
3229 return StringReplaceRegExpWithString( | 3193 return StringReplaceGlobalRegExpWithString( |
3230 isolate, subject, regexp, replacement, last_match_info); | 3194 isolate, subject, regexp, replacement, last_match_info); |
3231 } | 3195 } |
3232 | 3196 |
3233 | 3197 |
3234 Handle<String> StringReplaceOneCharWithString(Isolate* isolate, | 3198 Handle<String> StringReplaceOneCharWithString(Isolate* isolate, |
3235 Handle<String> subject, | 3199 Handle<String> subject, |
3236 Handle<String> search, | 3200 Handle<String> search, |
3237 Handle<String> replace, | 3201 Handle<String> replace, |
3238 bool* found, | 3202 bool* found, |
3239 int recursion_limit) { | 3203 int recursion_limit) { |
(...skipping 10287 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
13527 // Handle last resort GC and make sure to allow future allocations | 13491 // Handle last resort GC and make sure to allow future allocations |
13528 // to grow the heap without causing GCs (if possible). | 13492 // to grow the heap without causing GCs (if possible). |
13529 isolate->counters()->gc_last_resort_from_js()->Increment(); | 13493 isolate->counters()->gc_last_resort_from_js()->Increment(); |
13530 isolate->heap()->CollectAllGarbage(Heap::kNoGCFlags, | 13494 isolate->heap()->CollectAllGarbage(Heap::kNoGCFlags, |
13531 "Runtime::PerformGC"); | 13495 "Runtime::PerformGC"); |
13532 } | 13496 } |
13533 } | 13497 } |
13534 | 13498 |
13535 | 13499 |
13536 } } // namespace v8::internal | 13500 } } // namespace v8::internal |
OLD | NEW |