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