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