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 <functional> | 7 #include <functional> |
8 | 8 |
9 #include "src/arguments.h" | 9 #include "src/arguments.h" |
10 #include "src/conversions-inl.h" | 10 #include "src/conversions-inl.h" |
11 #include "src/isolate-inl.h" | 11 #include "src/isolate-inl.h" |
12 #include "src/messages.h" | 12 #include "src/messages.h" |
13 #include "src/regexp/jsregexp-inl.h" | 13 #include "src/regexp/jsregexp-inl.h" |
14 #include "src/regexp/jsregexp.h" | 14 #include "src/regexp/jsregexp.h" |
15 #include "src/regexp/regexp-utils.h" | 15 #include "src/regexp/regexp-utils.h" |
16 #include "src/string-builder.h" | 16 #include "src/string-builder.h" |
17 #include "src/string-search.h" | 17 #include "src/string-search.h" |
18 | 18 |
19 namespace v8 { | 19 namespace v8 { |
20 namespace internal { | 20 namespace internal { |
21 | 21 |
22 namespace { | |
23 | |
24 // Looks up the capture of the given name. Returns the (1-based) numbered | |
25 // capture index or -1 on failure. | |
26 int LookupNamedCapture(std::function<bool(String*)> name_matches, | |
27 FixedArray* capture_name_map) { | |
28 // TODO(jgruber): Sort capture_name_map and do binary search via | |
29 // internalized strings. | |
30 | |
31 int maybe_capture_index = -1; | |
32 const int named_capture_count = capture_name_map->length() >> 1; | |
33 for (int j = 0; j < named_capture_count; j++) { | |
34 // The format of {capture_name_map} is documented at | |
35 // JSRegExp::kIrregexpCaptureNameMapIndex. | |
36 const int name_ix = j * 2; | |
37 const int index_ix = j * 2 + 1; | |
38 | |
39 String* capture_name = String::cast(capture_name_map->get(name_ix)); | |
40 if (!name_matches(capture_name)) continue; | |
41 | |
42 maybe_capture_index = Smi::cast(capture_name_map->get(index_ix))->value(); | |
43 break; | |
44 } | |
45 | |
46 return maybe_capture_index; | |
47 } | |
48 | |
49 } // namespace | |
50 | |
51 class CompiledReplacement { | 22 class CompiledReplacement { |
52 public: | 23 public: |
53 explicit CompiledReplacement(Zone* zone) | 24 explicit CompiledReplacement(Zone* zone) |
54 : parts_(1, zone), replacement_substrings_(0, zone), zone_(zone) {} | 25 : parts_(1, zone), replacement_substrings_(0, zone), zone_(zone) {} |
55 | 26 |
56 // Return whether the replacement is simple. Can also fail and return Nothing | 27 // Return whether the replacement is simple. |
57 // if the given replacement string is invalid (and requires throwing a | 28 bool Compile(Handle<String> replacement, int capture_count, |
58 // SyntaxError). | 29 int subject_length); |
59 Maybe<bool> Compile(Handle<JSRegExp> regexp, Handle<String> replacement, | |
60 int capture_count, int subject_length); | |
61 | 30 |
62 // Use Apply only if Compile returned false. | 31 // Use Apply only if Compile returned false. |
63 void Apply(ReplacementStringBuilder* builder, int match_from, int match_to, | 32 void Apply(ReplacementStringBuilder* builder, int match_from, int match_to, |
64 int32_t* match); | 33 int32_t* match); |
65 | 34 |
66 // Number of distinct parts of the replacement pattern. | 35 // Number of distinct parts of the replacement pattern. |
67 int parts() { return parts_.length(); } | 36 int parts() { return parts_.length(); } |
68 | 37 |
69 Zone* zone() const { return zone_; } | 38 Zone* zone() const { return zone_; } |
70 | 39 |
71 private: | 40 private: |
72 enum PartType { | 41 enum PartType { |
73 SUBJECT_PREFIX = 1, | 42 SUBJECT_PREFIX = 1, |
74 SUBJECT_SUFFIX, | 43 SUBJECT_SUFFIX, |
75 SUBJECT_CAPTURE, | 44 SUBJECT_CAPTURE, |
76 REPLACEMENT_SUBSTRING, | 45 REPLACEMENT_SUBSTRING, |
77 REPLACEMENT_STRING, | 46 REPLACEMENT_STRING, |
78 EMPTY, | |
79 NUMBER_OF_PART_TYPES | 47 NUMBER_OF_PART_TYPES |
80 }; | 48 }; |
81 | 49 |
82 struct ReplacementPart { | 50 struct ReplacementPart { |
83 static inline ReplacementPart SubjectMatch() { | 51 static inline ReplacementPart SubjectMatch() { |
84 return ReplacementPart(SUBJECT_CAPTURE, 0); | 52 return ReplacementPart(SUBJECT_CAPTURE, 0); |
85 } | 53 } |
86 static inline ReplacementPart SubjectCapture(int capture_index) { | 54 static inline ReplacementPart SubjectCapture(int capture_index) { |
87 return ReplacementPart(SUBJECT_CAPTURE, capture_index); | 55 return ReplacementPart(SUBJECT_CAPTURE, capture_index); |
88 } | 56 } |
89 static inline ReplacementPart SubjectPrefix() { | 57 static inline ReplacementPart SubjectPrefix() { |
90 return ReplacementPart(SUBJECT_PREFIX, 0); | 58 return ReplacementPart(SUBJECT_PREFIX, 0); |
91 } | 59 } |
92 static inline ReplacementPart SubjectSuffix(int subject_length) { | 60 static inline ReplacementPart SubjectSuffix(int subject_length) { |
93 return ReplacementPart(SUBJECT_SUFFIX, subject_length); | 61 return ReplacementPart(SUBJECT_SUFFIX, subject_length); |
94 } | 62 } |
95 static inline ReplacementPart ReplacementString() { | 63 static inline ReplacementPart ReplacementString() { |
96 return ReplacementPart(REPLACEMENT_STRING, 0); | 64 return ReplacementPart(REPLACEMENT_STRING, 0); |
97 } | 65 } |
98 static inline ReplacementPart ReplacementSubString(int from, int to) { | 66 static inline ReplacementPart ReplacementSubString(int from, int to) { |
99 DCHECK(from >= 0); | 67 DCHECK(from >= 0); |
100 DCHECK(to > from); | 68 DCHECK(to > from); |
101 return ReplacementPart(-from, to); | 69 return ReplacementPart(-from, to); |
102 } | 70 } |
103 static inline ReplacementPart Empty() { return ReplacementPart(EMPTY, 0); } | |
104 | 71 |
105 // If tag <= 0 then it is the negation of a start index of a substring of | 72 // If tag <= 0 then it is the negation of a start index of a substring of |
106 // the replacement pattern, otherwise it's a value from PartType. | 73 // the replacement pattern, otherwise it's a value from PartType. |
107 ReplacementPart(int tag, int data) : tag(tag), data(data) { | 74 ReplacementPart(int tag, int data) : tag(tag), data(data) { |
108 // Must be non-positive or a PartType value. | 75 // Must be non-positive or a PartType value. |
109 DCHECK(tag < NUMBER_OF_PART_TYPES); | 76 DCHECK(tag < NUMBER_OF_PART_TYPES); |
110 } | 77 } |
111 // Either a value of PartType or a non-positive number that is | 78 // Either a value of PartType or a non-positive number that is |
112 // the negation of an index into the replacement string. | 79 // the negation of an index into the replacement string. |
113 int tag; | 80 int tag; |
114 // The data value's interpretation depends on the value of tag: | 81 // The data value's interpretation depends on the value of tag: |
115 // tag == SUBJECT_PREFIX || | 82 // tag == SUBJECT_PREFIX || |
116 // tag == SUBJECT_SUFFIX || | 83 // tag == SUBJECT_SUFFIX: data is unused. |
117 // tag == EMPTY: data is unused. | |
118 // tag == SUBJECT_CAPTURE: data is the number of the capture. | 84 // tag == SUBJECT_CAPTURE: data is the number of the capture. |
119 // tag == REPLACEMENT_SUBSTRING || | 85 // tag == REPLACEMENT_SUBSTRING || |
120 // tag == REPLACEMENT_STRING: data is index into array of substrings | 86 // tag == REPLACEMENT_STRING: data is index into array of substrings |
121 // of the replacement string. | 87 // of the replacement string. |
122 // tag <= 0: Temporary representation of the substring of the replacement | 88 // tag <= 0: Temporary representation of the substring of the replacement |
123 // string ranging over -tag .. data. | 89 // string ranging over -tag .. data. |
124 // Is replaced by REPLACEMENT_{SUB,}STRING when we create the | 90 // Is replaced by REPLACEMENT_{SUB,}STRING when we create the |
125 // substring objects. | 91 // substring objects. |
126 int data; | 92 int data; |
127 }; | 93 }; |
128 | 94 |
129 template <typename Char> | 95 template <typename Char> |
130 Maybe<bool> ParseReplacementPattern(ZoneList<ReplacementPart>* parts, | 96 bool ParseReplacementPattern(ZoneList<ReplacementPart>* parts, |
131 Vector<Char> characters, | 97 Vector<Char> characters, int capture_count, |
132 FixedArray* capture_name_map, | 98 int subject_length, Zone* zone) { |
133 int capture_count, int subject_length, | |
134 Zone* zone) { | |
135 // Equivalent to String::GetSubstitution, except that this method converts | |
136 // the replacement string into an internal representation that avoids | |
137 // repeated parsing when used repeatedly. | |
138 DCHECK_IMPLIES(capture_name_map != nullptr, | |
139 FLAG_harmony_regexp_named_captures); | |
140 | |
141 int length = characters.length(); | 99 int length = characters.length(); |
142 int last = 0; | 100 int last = 0; |
143 for (int i = 0; i < length; i++) { | 101 for (int i = 0; i < length; i++) { |
144 Char c = characters[i]; | 102 Char c = characters[i]; |
145 if (c == '$') { | 103 if (c == '$') { |
146 int next_index = i + 1; | 104 int next_index = i + 1; |
147 if (next_index == length) { // No next character! | 105 if (next_index == length) { // No next character! |
148 break; | 106 break; |
149 } | 107 } |
150 Char c2 = characters[next_index]; | 108 Char c2 = characters[next_index]; |
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
218 parts->Add(ReplacementPart::ReplacementSubString(last, i), | 176 parts->Add(ReplacementPart::ReplacementSubString(last, i), |
219 zone); | 177 zone); |
220 } | 178 } |
221 DCHECK(capture_ref <= capture_count); | 179 DCHECK(capture_ref <= capture_count); |
222 parts->Add(ReplacementPart::SubjectCapture(capture_ref), zone); | 180 parts->Add(ReplacementPart::SubjectCapture(capture_ref), zone); |
223 last = next_index + 1; | 181 last = next_index + 1; |
224 } | 182 } |
225 i = next_index; | 183 i = next_index; |
226 break; | 184 break; |
227 } | 185 } |
228 case '<': { | |
229 if (capture_name_map == nullptr) { | |
230 i = next_index; | |
231 break; | |
232 } | |
233 | |
234 // Scan until the next '>', throwing a SyntaxError exception if one | |
235 // is not found, and let the enclosed substring be groupName. | |
236 | |
237 const int name_start_index = next_index + 1; | |
238 int closing_bracket_index = -1; | |
239 for (int j = name_start_index; j < length; j++) { | |
240 if (characters[j] == '>') { | |
241 closing_bracket_index = j; | |
242 break; | |
243 } | |
244 } | |
245 | |
246 // Throw a SyntaxError for invalid replacement strings. | |
247 if (closing_bracket_index == -1) return Nothing<bool>(); | |
248 | |
249 Vector<Char> requested_name = | |
250 characters.SubVector(name_start_index, closing_bracket_index); | |
251 | |
252 // Let capture be ? Get(namedCaptures, groupName). | |
253 | |
254 int capture_index = LookupNamedCapture( | |
255 [=](String* capture_name) { | |
256 return capture_name->IsEqualTo(requested_name); | |
257 }, | |
258 capture_name_map); | |
259 | |
260 // If capture is undefined, replace the text through the following | |
261 // '>' with the empty string. | |
262 // Otherwise, replace the text through the following '>' with | |
263 // ? ToString(capture). | |
264 | |
265 DCHECK_IMPLIES( | |
266 capture_index != -1, | |
267 1 <= capture_index && capture_index <= capture_count); | |
268 | |
269 ReplacementPart replacement = | |
270 (capture_index == -1) | |
271 ? ReplacementPart::Empty() | |
272 : ReplacementPart::SubjectCapture(capture_index); | |
273 | |
274 if (i > last) { | |
275 parts->Add(ReplacementPart::ReplacementSubString(last, i), zone); | |
276 } | |
277 parts->Add(replacement, zone); | |
278 last = closing_bracket_index + 1; | |
279 i = closing_bracket_index; | |
280 break; | |
281 } | |
282 default: | 186 default: |
283 i = next_index; | 187 i = next_index; |
284 break; | 188 break; |
285 } | 189 } |
286 } | 190 } |
287 } | 191 } |
288 if (length > last) { | 192 if (length > last) { |
289 if (last == 0) { | 193 if (last == 0) { |
290 // Replacement is simple. Do not use Apply to do the replacement. | 194 // Replacement is simple. Do not use Apply to do the replacement. |
291 return Just(true); | 195 return true; |
292 } else { | 196 } else { |
293 parts->Add(ReplacementPart::ReplacementSubString(last, length), zone); | 197 parts->Add(ReplacementPart::ReplacementSubString(last, length), zone); |
294 } | 198 } |
295 } | 199 } |
296 return Just(false); | 200 return false; |
297 } | 201 } |
298 | 202 |
299 ZoneList<ReplacementPart> parts_; | 203 ZoneList<ReplacementPart> parts_; |
300 ZoneList<Handle<String> > replacement_substrings_; | 204 ZoneList<Handle<String> > replacement_substrings_; |
301 Zone* zone_; | 205 Zone* zone_; |
302 }; | 206 }; |
303 | 207 |
304 Maybe<bool> CompiledReplacement::Compile(Handle<JSRegExp> regexp, | 208 |
305 Handle<String> replacement, | 209 bool CompiledReplacement::Compile(Handle<String> replacement, int capture_count, |
306 int capture_count, | 210 int subject_length) { |
307 int subject_length) { | |
308 { | 211 { |
309 DisallowHeapAllocation no_gc; | 212 DisallowHeapAllocation no_gc; |
310 String::FlatContent content = replacement->GetFlatContent(); | 213 String::FlatContent content = replacement->GetFlatContent(); |
311 DCHECK(content.IsFlat()); | 214 DCHECK(content.IsFlat()); |
312 | 215 bool simple = false; |
313 FixedArray* capture_name_map = nullptr; | |
314 if (capture_count > 0) { | |
315 DCHECK_EQ(regexp->TypeTag(), JSRegExp::IRREGEXP); | |
316 Object* maybe_capture_name_map = regexp->CaptureNameMap(); | |
317 if (maybe_capture_name_map->IsFixedArray()) { | |
318 DCHECK(FLAG_harmony_regexp_named_captures); | |
319 capture_name_map = FixedArray::cast(maybe_capture_name_map); | |
320 } | |
321 } | |
322 | |
323 Maybe<bool> simple = Nothing<bool>(); | |
324 if (content.IsOneByte()) { | 216 if (content.IsOneByte()) { |
325 simple = ParseReplacementPattern(&parts_, content.ToOneByteVector(), | 217 simple = ParseReplacementPattern(&parts_, content.ToOneByteVector(), |
326 capture_name_map, capture_count, | 218 capture_count, subject_length, zone()); |
327 subject_length, zone()); | |
328 } else { | 219 } else { |
329 DCHECK(content.IsTwoByte()); | 220 DCHECK(content.IsTwoByte()); |
330 simple = ParseReplacementPattern(&parts_, content.ToUC16Vector(), | 221 simple = ParseReplacementPattern(&parts_, content.ToUC16Vector(), |
331 capture_name_map, capture_count, | 222 capture_count, subject_length, zone()); |
332 subject_length, zone()); | |
333 } | 223 } |
334 if (simple.IsNothing() || simple.FromJust()) return simple; | 224 if (simple) return true; |
335 } | 225 } |
336 | 226 |
337 Isolate* isolate = replacement->GetIsolate(); | 227 Isolate* isolate = replacement->GetIsolate(); |
338 // Find substrings of replacement string and create them as String objects. | 228 // Find substrings of replacement string and create them as String objects. |
339 int substring_index = 0; | 229 int substring_index = 0; |
340 for (int i = 0, n = parts_.length(); i < n; i++) { | 230 for (int i = 0, n = parts_.length(); i < n; i++) { |
341 int tag = parts_[i].tag; | 231 int tag = parts_[i].tag; |
342 if (tag <= 0) { // A replacement string slice. | 232 if (tag <= 0) { // A replacement string slice. |
343 int from = -tag; | 233 int from = -tag; |
344 int to = parts_[i].data; | 234 int to = parts_[i].data; |
345 replacement_substrings_.Add( | 235 replacement_substrings_.Add( |
346 isolate->factory()->NewSubString(replacement, from, to), zone()); | 236 isolate->factory()->NewSubString(replacement, from, to), zone()); |
347 parts_[i].tag = REPLACEMENT_SUBSTRING; | 237 parts_[i].tag = REPLACEMENT_SUBSTRING; |
348 parts_[i].data = substring_index; | 238 parts_[i].data = substring_index; |
349 substring_index++; | 239 substring_index++; |
350 } else if (tag == REPLACEMENT_STRING) { | 240 } else if (tag == REPLACEMENT_STRING) { |
351 replacement_substrings_.Add(replacement, zone()); | 241 replacement_substrings_.Add(replacement, zone()); |
352 parts_[i].data = substring_index; | 242 parts_[i].data = substring_index; |
353 substring_index++; | 243 substring_index++; |
354 } | 244 } |
355 } | 245 } |
356 return Just(false); | 246 return false; |
357 } | 247 } |
358 | 248 |
359 | 249 |
360 void CompiledReplacement::Apply(ReplacementStringBuilder* builder, | 250 void CompiledReplacement::Apply(ReplacementStringBuilder* builder, |
361 int match_from, int match_to, int32_t* match) { | 251 int match_from, int match_to, int32_t* match) { |
362 DCHECK_LT(0, parts_.length()); | 252 DCHECK_LT(0, parts_.length()); |
363 for (int i = 0, n = parts_.length(); i < n; i++) { | 253 for (int i = 0, n = parts_.length(); i < n; i++) { |
364 ReplacementPart part = parts_[i]; | 254 ReplacementPart part = parts_[i]; |
365 switch (part.tag) { | 255 switch (part.tag) { |
366 case SUBJECT_PREFIX: | 256 case SUBJECT_PREFIX: |
(...skipping 12 matching lines...) Expand all Loading... |
379 int to = match[capture * 2 + 1]; | 269 int to = match[capture * 2 + 1]; |
380 if (from >= 0 && to > from) { | 270 if (from >= 0 && to > from) { |
381 builder->AddSubjectSlice(from, to); | 271 builder->AddSubjectSlice(from, to); |
382 } | 272 } |
383 break; | 273 break; |
384 } | 274 } |
385 case REPLACEMENT_SUBSTRING: | 275 case REPLACEMENT_SUBSTRING: |
386 case REPLACEMENT_STRING: | 276 case REPLACEMENT_STRING: |
387 builder->AddString(replacement_substrings_[part.data]); | 277 builder->AddString(replacement_substrings_[part.data]); |
388 break; | 278 break; |
389 case EMPTY: | |
390 break; | |
391 default: | 279 default: |
392 UNREACHABLE(); | 280 UNREACHABLE(); |
393 } | 281 } |
394 } | 282 } |
395 } | 283 } |
396 | 284 |
397 void FindOneByteStringIndices(Vector<const uint8_t> subject, uint8_t pattern, | 285 void FindOneByteStringIndices(Vector<const uint8_t> subject, uint8_t pattern, |
398 List<int>* indices, unsigned int limit) { | 286 List<int>* indices, unsigned int limit) { |
399 DCHECK(limit > 0); | 287 DCHECK(limit > 0); |
400 // Collect indices of pattern in subject using memchr. | 288 // Collect indices of pattern in subject using memchr. |
(...skipping 195 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
596 | 484 |
597 MUST_USE_RESULT static Object* StringReplaceGlobalRegExpWithString( | 485 MUST_USE_RESULT static Object* StringReplaceGlobalRegExpWithString( |
598 Isolate* isolate, Handle<String> subject, Handle<JSRegExp> regexp, | 486 Isolate* isolate, Handle<String> subject, Handle<JSRegExp> regexp, |
599 Handle<String> replacement, Handle<RegExpMatchInfo> last_match_info) { | 487 Handle<String> replacement, Handle<RegExpMatchInfo> last_match_info) { |
600 DCHECK(subject->IsFlat()); | 488 DCHECK(subject->IsFlat()); |
601 DCHECK(replacement->IsFlat()); | 489 DCHECK(replacement->IsFlat()); |
602 | 490 |
603 int capture_count = regexp->CaptureCount(); | 491 int capture_count = regexp->CaptureCount(); |
604 int subject_length = subject->length(); | 492 int subject_length = subject->length(); |
605 | 493 |
606 JSRegExp::Type typeTag = regexp->TypeTag(); | |
607 if (typeTag == JSRegExp::IRREGEXP) { | |
608 // Ensure the RegExp is compiled so we can access the capture-name map. | |
609 RegExpImpl::IrregexpPrepare(regexp, subject); | |
610 } | |
611 | |
612 // CompiledReplacement uses zone allocation. | 494 // CompiledReplacement uses zone allocation. |
613 Zone zone(isolate->allocator(), ZONE_NAME); | 495 Zone zone(isolate->allocator(), ZONE_NAME); |
614 CompiledReplacement compiled_replacement(&zone); | 496 CompiledReplacement compiled_replacement(&zone); |
615 Maybe<bool> maybe_simple_replace = compiled_replacement.Compile( | 497 bool simple_replace = |
616 regexp, replacement, capture_count, subject_length); | 498 compiled_replacement.Compile(replacement, capture_count, subject_length); |
617 if (maybe_simple_replace.IsNothing()) { | |
618 THROW_NEW_ERROR_RETURN_FAILURE( | |
619 isolate, NewSyntaxError(MessageTemplate::kRegExpInvalidReplaceString, | |
620 replacement)); | |
621 } | |
622 | |
623 const bool simple_replace = maybe_simple_replace.FromJust(); | |
624 | 499 |
625 // Shortcut for simple non-regexp global replacements | 500 // Shortcut for simple non-regexp global replacements |
626 if (typeTag == JSRegExp::ATOM && simple_replace) { | 501 if (regexp->TypeTag() == JSRegExp::ATOM && simple_replace) { |
627 if (subject->HasOnlyOneByteChars() && replacement->HasOnlyOneByteChars()) { | 502 if (subject->HasOnlyOneByteChars() && replacement->HasOnlyOneByteChars()) { |
628 return StringReplaceGlobalAtomRegExpWithString<SeqOneByteString>( | 503 return StringReplaceGlobalAtomRegExpWithString<SeqOneByteString>( |
629 isolate, subject, regexp, replacement, last_match_info); | 504 isolate, subject, regexp, replacement, last_match_info); |
630 } else { | 505 } else { |
631 return StringReplaceGlobalAtomRegExpWithString<SeqTwoByteString>( | 506 return StringReplaceGlobalAtomRegExpWithString<SeqTwoByteString>( |
632 isolate, subject, regexp, replacement, last_match_info); | 507 isolate, subject, regexp, replacement, last_match_info); |
633 } | 508 } |
634 } | 509 } |
635 | 510 |
636 RegExpImpl::GlobalCache global_cache(regexp, subject, isolate); | 511 RegExpImpl::GlobalCache global_cache(regexp, subject, isolate); |
(...skipping 130 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
767 int allocated_string_size = ResultSeqString::SizeFor(new_length); | 642 int allocated_string_size = ResultSeqString::SizeFor(new_length); |
768 int delta = allocated_string_size - string_size; | 643 int delta = allocated_string_size - string_size; |
769 | 644 |
770 answer->set_length(position); | 645 answer->set_length(position); |
771 if (delta == 0) return *answer; | 646 if (delta == 0) return *answer; |
772 | 647 |
773 Address end_of_string = answer->address() + string_size; | 648 Address end_of_string = answer->address() + string_size; |
774 Heap* heap = isolate->heap(); | 649 Heap* heap = isolate->heap(); |
775 | 650 |
776 // The trimming is performed on a newly allocated object, which is on a | 651 // The trimming is performed on a newly allocated object, which is on a |
777 // freshly allocated page or on an already swept page. Hence, the sweeper | 652 // fresly allocated page or on an already swept page. Hence, the sweeper |
778 // thread can not get confused with the filler creation. No synchronization | 653 // thread can not get confused with the filler creation. No synchronization |
779 // needed. | 654 // needed. |
780 // TODO(hpayer): We should shrink the large object page if the size | 655 // TODO(hpayer): We should shrink the large object page if the size |
781 // of the object changed significantly. | 656 // of the object changed significantly. |
782 if (!heap->lo_space()->Contains(*answer)) { | 657 if (!heap->lo_space()->Contains(*answer)) { |
783 heap->CreateFillerObjectAt(end_of_string, delta, ClearRecordedSlots::kNo); | 658 heap->CreateFillerObjectAt(end_of_string, delta, ClearRecordedSlots::kNo); |
784 } | 659 } |
785 heap->AdjustLiveBytes(*answer, -delta); | 660 heap->AdjustLiveBytes(*answer, -delta); |
786 return *answer; | 661 return *answer; |
787 } | 662 } |
(...skipping 173 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
961 isolate->regexp_internal_match_info(); | 836 isolate->regexp_internal_match_info(); |
962 | 837 |
963 return StringReplaceGlobalRegExpWithStringHelper( | 838 return StringReplaceGlobalRegExpWithStringHelper( |
964 isolate, regexp, subject, replacement, internal_match_info); | 839 isolate, regexp, subject, replacement, internal_match_info); |
965 } | 840 } |
966 | 841 |
967 namespace { | 842 namespace { |
968 | 843 |
969 class MatchInfoBackedMatch : public String::Match { | 844 class MatchInfoBackedMatch : public String::Match { |
970 public: | 845 public: |
971 MatchInfoBackedMatch(Isolate* isolate, Handle<JSRegExp> regexp, | 846 MatchInfoBackedMatch(Isolate* isolate, Handle<String> subject, |
972 Handle<String> subject, | |
973 Handle<RegExpMatchInfo> match_info) | 847 Handle<RegExpMatchInfo> match_info) |
974 : isolate_(isolate), match_info_(match_info) { | 848 : isolate_(isolate), match_info_(match_info) { |
975 subject_ = String::Flatten(subject); | 849 subject_ = String::Flatten(subject); |
976 | |
977 if (regexp->TypeTag() == JSRegExp::IRREGEXP) { | |
978 Object* o = regexp->CaptureNameMap(); | |
979 has_named_captures_ = o->IsFixedArray(); | |
980 if (has_named_captures_) { | |
981 DCHECK(FLAG_harmony_regexp_named_captures); | |
982 capture_name_map_ = handle(FixedArray::cast(o)); | |
983 } | |
984 } else { | |
985 has_named_captures_ = false; | |
986 } | |
987 } | 850 } |
988 | 851 |
989 Handle<String> GetMatch() override { | 852 Handle<String> GetMatch() override { |
990 return RegExpUtils::GenericCaptureGetter(isolate_, match_info_, 0, nullptr); | 853 return RegExpUtils::GenericCaptureGetter(isolate_, match_info_, 0, nullptr); |
991 } | 854 } |
992 | 855 |
993 Handle<String> GetPrefix() override { | |
994 const int match_start = match_info_->Capture(0); | |
995 return isolate_->factory()->NewSubString(subject_, 0, match_start); | |
996 } | |
997 | |
998 Handle<String> GetSuffix() override { | |
999 const int match_end = match_info_->Capture(1); | |
1000 return isolate_->factory()->NewSubString(subject_, match_end, | |
1001 subject_->length()); | |
1002 } | |
1003 | |
1004 bool HasNamedCaptures() override { return has_named_captures_; } | |
1005 | |
1006 int CaptureCount() override { | |
1007 return match_info_->NumberOfCaptureRegisters() / 2; | |
1008 } | |
1009 | |
1010 MaybeHandle<String> GetCapture(int i, bool* capture_exists) override { | 856 MaybeHandle<String> GetCapture(int i, bool* capture_exists) override { |
1011 Handle<Object> capture_obj = RegExpUtils::GenericCaptureGetter( | 857 Handle<Object> capture_obj = RegExpUtils::GenericCaptureGetter( |
1012 isolate_, match_info_, i, capture_exists); | 858 isolate_, match_info_, i, capture_exists); |
1013 return (*capture_exists) ? Object::ToString(isolate_, capture_obj) | 859 return (*capture_exists) ? Object::ToString(isolate_, capture_obj) |
1014 : isolate_->factory()->empty_string(); | 860 : isolate_->factory()->empty_string(); |
1015 } | 861 } |
1016 | 862 |
1017 MaybeHandle<String> GetNamedCapture(Handle<String> name, | 863 Handle<String> GetPrefix() override { |
1018 bool* capture_exists) override { | 864 const int match_start = match_info_->Capture(0); |
1019 DCHECK(has_named_captures_); | 865 return isolate_->factory()->NewSubString(subject_, 0, match_start); |
1020 const int capture_index = LookupNamedCapture( | 866 } |
1021 [=](String* capture_name) { return capture_name->Equals(*name); }, | |
1022 *capture_name_map_); | |
1023 | 867 |
1024 if (capture_index == -1) { | 868 Handle<String> GetSuffix() override { |
1025 *capture_exists = false; | 869 const int match_end = match_info_->Capture(1); |
1026 return name; // Arbitrary string handle. | 870 return isolate_->factory()->NewSubString(subject_, match_end, |
1027 } | 871 subject_->length()); |
| 872 } |
1028 | 873 |
1029 DCHECK(1 <= capture_index && capture_index <= CaptureCount()); | 874 int CaptureCount() override { |
1030 return GetCapture(capture_index, capture_exists); | 875 return match_info_->NumberOfCaptureRegisters() / 2; |
1031 } | 876 } |
1032 | 877 |
| 878 virtual ~MatchInfoBackedMatch() {} |
| 879 |
1033 private: | 880 private: |
1034 Isolate* isolate_; | 881 Isolate* isolate_; |
1035 Handle<String> subject_; | 882 Handle<String> subject_; |
1036 Handle<RegExpMatchInfo> match_info_; | 883 Handle<RegExpMatchInfo> match_info_; |
1037 | |
1038 bool has_named_captures_; | |
1039 Handle<FixedArray> capture_name_map_; | |
1040 }; | 884 }; |
1041 | 885 |
1042 class VectorBackedMatch : public String::Match { | 886 class VectorBackedMatch : public String::Match { |
1043 public: | 887 public: |
1044 VectorBackedMatch(Isolate* isolate, Handle<String> subject, | 888 VectorBackedMatch(Isolate* isolate, Handle<String> subject, |
1045 Handle<String> match, int match_position, | 889 Handle<String> match, int match_position, |
1046 std::vector<Handle<Object>>* captures, | 890 std::vector<Handle<Object>>* captures) |
1047 Handle<Object> groups_obj) | |
1048 : isolate_(isolate), | 891 : isolate_(isolate), |
1049 match_(match), | 892 match_(match), |
1050 match_position_(match_position), | 893 match_position_(match_position), |
1051 captures_(captures) { | 894 captures_(captures) { |
1052 subject_ = String::Flatten(subject); | 895 subject_ = String::Flatten(subject); |
1053 | |
1054 DCHECK(groups_obj->IsUndefined(isolate) || groups_obj->IsJSReceiver()); | |
1055 has_named_captures_ = !groups_obj->IsUndefined(isolate); | |
1056 if (has_named_captures_) groups_obj_ = Handle<JSReceiver>::cast(groups_obj); | |
1057 } | 896 } |
1058 | 897 |
1059 Handle<String> GetMatch() override { return match_; } | 898 Handle<String> GetMatch() override { return match_; } |
1060 | 899 |
1061 Handle<String> GetPrefix() override { | |
1062 return isolate_->factory()->NewSubString(subject_, 0, match_position_); | |
1063 } | |
1064 | |
1065 Handle<String> GetSuffix() override { | |
1066 const int match_end_position = match_position_ + match_->length(); | |
1067 return isolate_->factory()->NewSubString(subject_, match_end_position, | |
1068 subject_->length()); | |
1069 } | |
1070 | |
1071 bool HasNamedCaptures() override { return has_named_captures_; } | |
1072 | |
1073 int CaptureCount() override { return static_cast<int>(captures_->size()); } | |
1074 | |
1075 MaybeHandle<String> GetCapture(int i, bool* capture_exists) override { | 900 MaybeHandle<String> GetCapture(int i, bool* capture_exists) override { |
1076 Handle<Object> capture_obj = captures_->at(i); | 901 Handle<Object> capture_obj = captures_->at(i); |
1077 if (capture_obj->IsUndefined(isolate_)) { | 902 if (capture_obj->IsUndefined(isolate_)) { |
1078 *capture_exists = false; | 903 *capture_exists = false; |
1079 return isolate_->factory()->empty_string(); | 904 return isolate_->factory()->empty_string(); |
1080 } | 905 } |
1081 *capture_exists = true; | 906 *capture_exists = true; |
1082 return Object::ToString(isolate_, capture_obj); | 907 return Object::ToString(isolate_, capture_obj); |
1083 } | 908 } |
1084 | 909 |
1085 MaybeHandle<String> GetNamedCapture(Handle<String> name, | 910 Handle<String> GetPrefix() override { |
1086 bool* capture_exists) override { | 911 return isolate_->factory()->NewSubString(subject_, 0, match_position_); |
1087 DCHECK(has_named_captures_); | |
1088 Handle<Object> capture_obj; | |
1089 ASSIGN_RETURN_ON_EXCEPTION(isolate_, capture_obj, | |
1090 Object::GetProperty(groups_obj_, name), String); | |
1091 if (capture_obj->IsUndefined(isolate_)) { | |
1092 *capture_exists = false; | |
1093 return name; | |
1094 } else { | |
1095 *capture_exists = true; | |
1096 return Object::ToString(isolate_, capture_obj); | |
1097 } | |
1098 } | 912 } |
1099 | 913 |
| 914 Handle<String> GetSuffix() override { |
| 915 const int match_end_position = match_position_ + match_->length(); |
| 916 return isolate_->factory()->NewSubString(subject_, match_end_position, |
| 917 subject_->length()); |
| 918 } |
| 919 |
| 920 int CaptureCount() override { return static_cast<int>(captures_->size()); } |
| 921 |
| 922 virtual ~VectorBackedMatch() {} |
| 923 |
1100 private: | 924 private: |
1101 Isolate* isolate_; | 925 Isolate* isolate_; |
1102 Handle<String> subject_; | 926 Handle<String> subject_; |
1103 Handle<String> match_; | 927 Handle<String> match_; |
1104 const int match_position_; | 928 const int match_position_; |
1105 std::vector<Handle<Object>>* captures_; | 929 std::vector<Handle<Object>>* captures_; |
1106 | |
1107 bool has_named_captures_; | |
1108 Handle<JSReceiver> groups_obj_; | |
1109 }; | 930 }; |
1110 | 931 |
1111 // Create the groups object (see also the RegExp result creation in | 932 // Create the groups object (see also the RegExp result creation in |
1112 // RegExpBuiltinsAssembler::ConstructNewResultFromMatchInfo). | 933 // RegExpBuiltinsAssembler::ConstructNewResultFromMatchInfo). |
1113 Handle<JSObject> ConstructNamedCaptureGroupsObject( | 934 Handle<JSObject> ConstructNamedCaptureGroupsObject( |
1114 Isolate* isolate, Handle<FixedArray> capture_map, | 935 Isolate* isolate, Handle<FixedArray> capture_map, |
1115 std::function<Object*(int)> f_get_capture) { | 936 std::function<Object*(int)> f_get_capture) { |
1116 Handle<JSObject> groups = isolate->factory()->NewJSObjectWithNullProto(); | 937 Handle<JSObject> groups = isolate->factory()->NewJSObjectWithNullProto(); |
1117 | 938 |
1118 const int capture_count = capture_map->length() >> 1; | 939 const int capture_count = capture_map->length() >> 1; |
(...skipping 125 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1244 } else { | 1065 } else { |
1245 DCHECK(current_match[i * 2 + 1] < 0); | 1066 DCHECK(current_match[i * 2 + 1] < 0); |
1246 elements->set(cursor++, isolate->heap()->undefined_value()); | 1067 elements->set(cursor++, isolate->heap()->undefined_value()); |
1247 } | 1068 } |
1248 } | 1069 } |
1249 | 1070 |
1250 elements->set(cursor++, Smi::FromInt(match_start)); | 1071 elements->set(cursor++, Smi::FromInt(match_start)); |
1251 elements->set(cursor++, *subject); | 1072 elements->set(cursor++, *subject); |
1252 | 1073 |
1253 if (has_named_captures) { | 1074 if (has_named_captures) { |
1254 DCHECK(FLAG_harmony_regexp_named_captures); | |
1255 Handle<FixedArray> capture_map = | 1075 Handle<FixedArray> capture_map = |
1256 Handle<FixedArray>::cast(maybe_capture_map); | 1076 Handle<FixedArray>::cast(maybe_capture_map); |
1257 Handle<JSObject> groups = ConstructNamedCaptureGroupsObject( | 1077 Handle<JSObject> groups = ConstructNamedCaptureGroupsObject( |
1258 isolate, capture_map, [=](int ix) { return elements->get(ix); }); | 1078 isolate, capture_map, [=](int ix) { return elements->get(ix); }); |
1259 elements->set(cursor++, *groups); | 1079 elements->set(cursor++, *groups); |
1260 } | 1080 } |
1261 | 1081 |
1262 DCHECK_EQ(cursor, argc); | 1082 DCHECK_EQ(cursor, argc); |
1263 builder.Add(*isolate->factory()->NewJSArrayWithElements(elements)); | 1083 builder.Add(*isolate->factory()->NewJSArrayWithElements(elements)); |
1264 } else { | 1084 } else { |
(...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1356 | 1176 |
1357 const int start_index = match_indices->Capture(0); | 1177 const int start_index = match_indices->Capture(0); |
1358 const int end_index = match_indices->Capture(1); | 1178 const int end_index = match_indices->Capture(1); |
1359 | 1179 |
1360 if (sticky) regexp->SetLastIndex(end_index); | 1180 if (sticky) regexp->SetLastIndex(end_index); |
1361 | 1181 |
1362 IncrementalStringBuilder builder(isolate); | 1182 IncrementalStringBuilder builder(isolate); |
1363 builder.AppendString(factory->NewSubString(string, 0, start_index)); | 1183 builder.AppendString(factory->NewSubString(string, 0, start_index)); |
1364 | 1184 |
1365 if (replace->length() > 0) { | 1185 if (replace->length() > 0) { |
1366 MatchInfoBackedMatch m(isolate, regexp, string, match_indices); | 1186 MatchInfoBackedMatch m(isolate, string, match_indices); |
1367 Handle<String> replacement; | 1187 Handle<String> replacement; |
1368 ASSIGN_RETURN_ON_EXCEPTION(isolate, replacement, | 1188 ASSIGN_RETURN_ON_EXCEPTION(isolate, replacement, |
1369 String::GetSubstitution(isolate, &m, replace), | 1189 String::GetSubstitution(isolate, &m, replace), |
1370 String); | 1190 String); |
1371 builder.AppendString(replacement); | 1191 builder.AppendString(replacement); |
1372 } | 1192 } |
1373 | 1193 |
1374 builder.AppendString( | 1194 builder.AppendString( |
1375 factory->NewSubString(string, end_index, string->length())); | 1195 factory->NewSubString(string, end_index, string->length())); |
1376 return builder.Finish(); | 1196 return builder.Finish(); |
(...skipping 112 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1489 const int m = match_indices->NumberOfCaptureRegisters() / 2; | 1309 const int m = match_indices->NumberOfCaptureRegisters() / 2; |
1490 | 1310 |
1491 bool has_named_captures = false; | 1311 bool has_named_captures = false; |
1492 Handle<FixedArray> capture_map; | 1312 Handle<FixedArray> capture_map; |
1493 if (m > 1) { | 1313 if (m > 1) { |
1494 // The existence of capture groups implies IRREGEXP kind. | 1314 // The existence of capture groups implies IRREGEXP kind. |
1495 DCHECK_EQ(regexp->TypeTag(), JSRegExp::IRREGEXP); | 1315 DCHECK_EQ(regexp->TypeTag(), JSRegExp::IRREGEXP); |
1496 | 1316 |
1497 Object* maybe_capture_map = regexp->CaptureNameMap(); | 1317 Object* maybe_capture_map = regexp->CaptureNameMap(); |
1498 if (maybe_capture_map->IsFixedArray()) { | 1318 if (maybe_capture_map->IsFixedArray()) { |
1499 DCHECK(FLAG_harmony_regexp_named_captures); | |
1500 has_named_captures = true; | 1319 has_named_captures = true; |
1501 capture_map = handle(FixedArray::cast(maybe_capture_map)); | 1320 capture_map = handle(FixedArray::cast(maybe_capture_map)); |
1502 } | 1321 } |
1503 } | 1322 } |
1504 | 1323 |
1505 const int argc = has_named_captures ? m + 3 : m + 2; | 1324 const int argc = has_named_captures ? m + 3 : m + 2; |
1506 ScopedVector<Handle<Object>> argv(argc); | 1325 ScopedVector<Handle<Object>> argv(argc); |
1507 | 1326 |
1508 int cursor = 0; | 1327 int cursor = 0; |
1509 for (int j = 0; j < m; j++) { | 1328 for (int j = 0; j < m; j++) { |
(...skipping 367 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1877 Handle<Object> replacement_obj; | 1696 Handle<Object> replacement_obj; |
1878 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( | 1697 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( |
1879 isolate, replacement_obj, | 1698 isolate, replacement_obj, |
1880 Execution::Call(isolate, replace_obj, factory->undefined_value(), | 1699 Execution::Call(isolate, replace_obj, factory->undefined_value(), |
1881 argc, argv.start())); | 1700 argc, argv.start())); |
1882 | 1701 |
1883 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( | 1702 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( |
1884 isolate, replacement, Object::ToString(isolate, replacement_obj)); | 1703 isolate, replacement, Object::ToString(isolate, replacement_obj)); |
1885 } else { | 1704 } else { |
1886 DCHECK(!functional_replace); | 1705 DCHECK(!functional_replace); |
1887 if (!groups_obj->IsUndefined(isolate)) { | 1706 VectorBackedMatch m(isolate, string, match, position, &captures); |
1888 // TODO(jgruber): Behavior in this case is not yet specced. | |
1889 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( | |
1890 isolate, groups_obj, JSReceiver::ToObject(isolate, groups_obj)); | |
1891 } | |
1892 VectorBackedMatch m(isolate, string, match, position, &captures, | |
1893 groups_obj); | |
1894 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( | 1707 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( |
1895 isolate, replacement, String::GetSubstitution(isolate, &m, replace)); | 1708 isolate, replacement, String::GetSubstitution(isolate, &m, replace)); |
1896 } | 1709 } |
1897 | 1710 |
1898 if (position >= next_source_position) { | 1711 if (position >= next_source_position) { |
1899 builder.AppendString( | 1712 builder.AppendString( |
1900 factory->NewSubString(string, next_source_position, position)); | 1713 factory->NewSubString(string, next_source_position, position)); |
1901 builder.AppendString(replacement); | 1714 builder.AppendString(replacement); |
1902 | 1715 |
1903 next_source_position = position + match_length; | 1716 next_source_position = position + match_length; |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1935 | 1748 |
1936 RUNTIME_FUNCTION(Runtime_IsRegExp) { | 1749 RUNTIME_FUNCTION(Runtime_IsRegExp) { |
1937 SealHandleScope shs(isolate); | 1750 SealHandleScope shs(isolate); |
1938 DCHECK_EQ(1, args.length()); | 1751 DCHECK_EQ(1, args.length()); |
1939 CONVERT_ARG_CHECKED(Object, obj, 0); | 1752 CONVERT_ARG_CHECKED(Object, obj, 0); |
1940 return isolate->heap()->ToBoolean(obj->IsJSRegExp()); | 1753 return isolate->heap()->ToBoolean(obj->IsJSRegExp()); |
1941 } | 1754 } |
1942 | 1755 |
1943 } // namespace internal | 1756 } // namespace internal |
1944 } // namespace v8 | 1757 } // namespace v8 |
OLD | NEW |