| 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 |