Index: src/objects.cc |
diff --git a/src/objects.cc b/src/objects.cc |
index b7fa0b2c5e577cdb6988edb8934eb98f6c49f4b8..551e4a35dbb17be555bed0591d8bccb384f9d066 100644 |
--- a/src/objects.cc |
+++ b/src/objects.cc |
@@ -11652,6 +11652,101 @@ int String::IndexOf(Isolate* isolate, Handle<String> receiver, |
start_index); |
} |
+MaybeHandle<String> String::GetSubstitution(Isolate* isolate, Match* match, |
+ Handle<String> replacement) { |
+ Factory* factory = isolate->factory(); |
+ |
+ const int replacement_length = replacement->length(); |
+ const int captures_length = match->CaptureCount(); |
+ |
+ replacement = String::Flatten(replacement); |
+ |
+ Handle<String> dollar_string = |
+ factory->LookupSingleCharacterStringFromCode('$'); |
+ int next = String::IndexOf(isolate, replacement, dollar_string, 0); |
+ if (next < 0) { |
+ return replacement; |
+ } |
+ |
+ IncrementalStringBuilder builder(isolate); |
+ |
+ if (next > 0) { |
+ builder.AppendString(factory->NewSubString(replacement, 0, next)); |
+ } |
+ |
+ while (true) { |
+ int pos = next + 1; |
+ if (pos < replacement_length) { |
+ const uint16_t peek = replacement->Get(pos); |
+ if (peek == '$') { // $$ |
+ pos++; |
+ builder.AppendCharacter('$'); |
+ } else if (peek == '&') { // $& - match |
+ pos++; |
+ builder.AppendString(match->GetMatch()); |
+ } else if (peek == '`') { // $` - prefix |
+ pos++; |
+ builder.AppendString(match->GetPrefix()); |
+ } else if (peek == '\'') { // $' - suffix |
+ pos++; |
+ builder.AppendString(match->GetSuffix()); |
+ } else if (peek >= '0' && peek <= '9') { |
+ // Valid indices are $1 .. $9, $01 .. $09 and $10 .. $99 |
+ int scaled_index = (peek - '0'); |
+ int advance = 1; |
+ |
+ if (pos + 1 < replacement_length) { |
+ const uint16_t next_peek = replacement->Get(pos + 1); |
+ if (next_peek >= '0' && next_peek <= '9') { |
+ const int new_scaled_index = scaled_index * 10 + (next_peek - '0'); |
+ if (new_scaled_index < captures_length) { |
+ scaled_index = new_scaled_index; |
+ advance = 2; |
+ } |
+ } |
+ } |
+ |
+ if (scaled_index != 0 && scaled_index < captures_length) { |
+ bool capture_exists; |
+ Handle<String> capture; |
+ ASSIGN_RETURN_ON_EXCEPTION( |
+ isolate, capture, |
+ match->GetCapture(scaled_index, &capture_exists), String); |
+ if (capture_exists) builder.AppendString(capture); |
+ pos += advance; |
+ } else { |
+ builder.AppendCharacter('$'); |
+ } |
+ } else { |
+ builder.AppendCharacter('$'); |
+ } |
+ } else { |
+ builder.AppendCharacter('$'); |
+ } |
+ |
+ // Go the the next $ in the replacement. |
+ next = String::IndexOf(isolate, replacement, dollar_string, pos); |
+ |
+ // Return if there are no more $ characters in the replacement. If we |
+ // haven't reached the end, we need to append the suffix. |
+ if (next < 0) { |
+ if (pos < replacement_length) { |
+ builder.AppendString( |
+ factory->NewSubString(replacement, pos, replacement_length)); |
+ } |
+ return builder.Finish(); |
+ } |
+ |
+ // Append substring between the previous and the next $ character. |
+ if (next > pos) { |
+ builder.AppendString(factory->NewSubString(replacement, pos, next)); |
+ } |
+ } |
+ |
+ UNREACHABLE(); |
+ return MaybeHandle<String>(); |
+} |
+ |
namespace { // for String.Prototype.lastIndexOf |
template <typename schar, typename pchar> |