Chromium Code Reviews| Index: src/string.js |
| diff --git a/src/string.js b/src/string.js |
| index b39976c51ed8a15332eccabecc2d1c34e26cf962..7d81abbcfda4ecb32f2663a09f5e1ea149d7fe2f 100644 |
| --- a/src/string.js |
| +++ b/src/string.js |
| @@ -229,60 +229,78 @@ function StringReplace(search, replace) { |
| } |
| var subject = TO_STRING_INLINE(this); |
| - // Delegate to one of the regular expression variants if necessary. |
| + // Decision tree for dispatch |
| + // .. regexp search |
| + // .... string replace |
| + // ...... non-global search |
| + // ........ empty string replace |
| + // ........ non-empty string replace (with $-expansion) |
| + // ...... global search |
| + // ........ no need to circumvent last match info override |
| + // ........ need to circument last match info override |
| + // .... function replace |
| + // ...... global search |
| + // ...... non-global search |
| + // .. string search |
| + // .... special case that replaces with one single character |
| + // ...... function replace |
| + // ...... string replace (with $-expansion) |
| + |
| if (IS_REGEXP(search)) { |
| - // Emulate RegExp.prototype.exec's side effect in step 5, even though |
| + // Emulate RegExp.prototype.exec's side effect in step 5, even if |
| // value is discarded. |
| ToInteger(search.lastIndex); |
| %_Log('regexp', 'regexp-replace,%0r,%1S', [search, subject]); |
| - if (IS_SPEC_FUNCTION(replace)) { |
| - if (search.global) { |
| - return StringReplaceGlobalRegExpWithFunction(subject, search, replace); |
| - } else { |
| - return StringReplaceNonGlobalRegExpWithFunction(subject, |
| - search, |
| - replace); |
| - } |
| - } else { |
| - if (lastMatchInfoOverride == null) { |
| - var answer = %StringReplaceRegExpWithString(subject, |
| - search, |
| - TO_STRING_INLINE(replace), |
| - lastMatchInfo); |
| - if (IS_UNDEFINED(answer)) { // No match. Return subject string. |
| - search.lastIndex = 0; |
| + |
| + if (!IS_SPEC_FUNCTION(replace)) { |
| + if (!search.global) { |
| + // Non-global regexp search, string replace. |
| + var match = DoRegExpExec(search, subject, 0); |
| + if (match == null) { |
| + search.lastIndex = 0 |
| return subject; |
| } |
| - if (search.global) search.lastIndex = 0; |
| - return answer; |
| + if (replace.length == 0) { |
| + return SubString(subject, 0, match[CAPTURE0]) + |
| + SubString(subject, match[CAPTURE1], subject.length) |
| + } |
| + return ExpandReplacement(TO_STRING_INLINE(replace), |
| + subject, |
| + lastMatchInfo, |
| + SubString(subject, 0, match[CAPTURE0])) + |
| + SubString(subject, match[CAPTURE1], subject.length); |
|
ulan
2013/02/05 09:30:58
I would reformat by putting the first argument of
Yang
2013/02/05 10:47:32
Not entirely sure what you mean here.
|
| + } |
| + |
| + // Global regexp search, string replace. |
| + search.lastIndex = 0; |
| + if (lastMatchInfoOverride == null) { |
| + return %StringReplaceGlobalRegExpWithString( |
| + subject, search, replace, lastMatchInfo); |
| } else { |
| // We use this hack to detect whether StringReplaceRegExpWithString |
| // found at least one hit. In that case we need to remove any |
| // override. |
| var saved_subject = lastMatchInfo[LAST_SUBJECT_INDEX]; |
| lastMatchInfo[LAST_SUBJECT_INDEX] = 0; |
| - var answer = %StringReplaceRegExpWithString(subject, |
| - search, |
| - TO_STRING_INLINE(replace), |
| - lastMatchInfo); |
| - if (IS_UNDEFINED(answer)) { // No match. Return subject string. |
| - search.lastIndex = 0; |
| - lastMatchInfo[LAST_SUBJECT_INDEX] = saved_subject; |
| - return subject; |
| - } |
| + var answer = %StringReplaceGlobalRegExpWithString( |
| + subject, search, replace, lastMatchInfo); |
| if (%_IsSmi(lastMatchInfo[LAST_SUBJECT_INDEX])) { |
| lastMatchInfo[LAST_SUBJECT_INDEX] = saved_subject; |
| } else { |
| lastMatchInfoOverride = null; |
| } |
| - if (search.global) search.lastIndex = 0; |
| return answer; |
| } |
| } |
| + |
| + if (search.global) { |
| + // Global regexp search, function replace. |
| + return StringReplaceGlobalRegExpWithFunction(subject, search, replace); |
| + } |
| + // Non-global regexp search, function replace. |
| + return StringReplaceNonGlobalRegExpWithFunction(subject, search, replace); |
| } |
| - // Convert the search argument to a string and search for it. |
| - search = TO_STRING_INLINE(search); |
|
ulan
2013/02/05 09:30:58
Looks like it was remove accidentally.
|
| if (search.length == 1 && |
| subject.length > 0xFF && |
| IS_STRING(replace) && |
| @@ -342,6 +360,38 @@ function ExpandReplacement(string, subject, matchInfo, result) { |
| } else if (peek == 39) { // $' - suffix |
| ++position; |
| result += SubString(subject, matchInfo[CAPTURE1], subject.length); |
| + } else if (peek >= 48 && peek <= 57) { // between '0' and '9' |
| + // Convert to digit by subtracting '0'. |
| + var scaled_index; |
| + var advance; |
| + if (peek == 48) { // $0... |
| + if (position + 1 == string.length || // ends with '$0', invalid. |
| + %_StringCharCodeAt(string, position + 1) != 49) { // not '$01' |
|
ulan
2013/02/05 09:30:58
As discussed offline, we should check $02, ... $nn
|
| + scaled_index = 1 << 18; |
| + } else { |
| + scaled_index = 2; // 1 * 2 |
| + advance = 2; |
| + } |
| + } else if (peek == 49 && |
| + position + 1 < string.length && |
| + %_StringCharCodeAt(string, position + 1) == 48) { // $10 |
| + scaled_index = 20; // 10 * 2 |
| + advance = 2; |
| + } else { |
| + scaled_index = (peek - 48) << 1; // index * 2 |
| + advance = 1; |
| + } |
| + |
| + if (NUMBER_OF_CAPTURES(matchInfo) > scaled_index) { |
| + var start = matchInfo[CAPTURE(scaled_index)]; |
| + if (start >= 0) { |
| + result += |
| + SubString(subject, start, matchInfo[CAPTURE(scaled_index + 1)]); |
| + } |
| + position += advance; |
| + } else { |
| + result += '$'; |
| + } |
| } else { |
| result += '$'; |
| } |