Index: src/js/string.js |
diff --git a/src/js/string.js b/src/js/string.js |
index b220038b74ce1283f0575943533d8fde8e9180ae..e906a563652782ace88005b3aa11fbbbef1d9503 100644 |
--- a/src/js/string.js |
+++ b/src/js/string.js |
@@ -20,9 +20,8 @@ var MakeTypeError; |
var MathMax; |
var MathMin; |
var matchSymbol = utils.ImportNow("match_symbol"); |
-var RegExpExec; |
var RegExpExecNoTests; |
-var RegExpLastMatchInfo; |
+var replaceSymbol = utils.ImportNow("replace_symbol"); |
var searchSymbol = utils.ImportNow("search_symbol"); |
var splitSymbol = utils.ImportNow("split_symbol"); |
@@ -33,9 +32,7 @@ utils.Import(function(from) { |
MakeTypeError = from.MakeTypeError; |
MathMax = from.MathMax; |
MathMin = from.MathMin; |
- RegExpExec = from.RegExpExec; |
RegExpExecNoTests = from.RegExpExecNoTests; |
- RegExpLastMatchInfo = from.RegExpLastMatchInfo; |
}); |
//------------------------------------------------------------------- |
@@ -206,14 +203,12 @@ function StringNormalizeJS() { |
var reusableMatchInfo = [2, "", "", -1, -1]; |
-// ECMA-262, section 15.5.4.11 |
+// ES6, section 21.1.3.14 |
function StringReplace(search, replace) { |
CHECK_OBJECT_COERCIBLE(this, "String.prototype.replace"); |
- var subject = TO_STRING(this); |
- |
// Decision tree for dispatch |
- // .. regexp search |
+ // .. regexp search (in src/js/regexp.js, RegExpReplace) |
// .... string replace |
// ...... non-global search |
// ........ empty string replace |
@@ -229,40 +224,15 @@ function StringReplace(search, replace) { |
// ...... function replace |
// ...... string replace (with $-expansion) |
- if (IS_REGEXP(search)) { |
- if (!IS_CALLABLE(replace)) { |
- replace = TO_STRING(replace); |
- |
- if (!REGEXP_GLOBAL(search)) { |
- // Non-global regexp search, string replace. |
- var match = RegExpExec(search, subject, 0); |
- if (match == null) { |
- search.lastIndex = 0 |
- return subject; |
- } |
- if (replace.length == 0) { |
- return %_SubString(subject, 0, match[CAPTURE0]) + |
- %_SubString(subject, match[CAPTURE1], subject.length) |
- } |
- return ExpandReplacement(replace, subject, RegExpLastMatchInfo, |
- %_SubString(subject, 0, match[CAPTURE0])) + |
- %_SubString(subject, match[CAPTURE1], subject.length); |
- } |
- |
- // Global regexp search, string replace. |
- search.lastIndex = 0; |
- return %StringReplaceGlobalRegExpWithString( |
- subject, search, replace, RegExpLastMatchInfo); |
+ if (!IS_NULL_OR_UNDEFINED(search)) { |
+ var replacer = search[replaceSymbol]; |
+ if (!IS_UNDEFINED(replacer)) { |
+ return %_Call(replacer, search, this, replace); |
} |
- |
- if (REGEXP_GLOBAL(search)) { |
- // Global regexp search, function replace. |
- return StringReplaceGlobalRegExpWithFunction(subject, search, replace); |
- } |
- // Non-global regexp search, function replace. |
- return StringReplaceNonGlobalRegExpWithFunction(subject, search, replace); |
} |
+ var subject = TO_STRING(this); |
+ |
search = TO_STRING(search); |
if (search.length == 1 && |
@@ -379,130 +349,6 @@ function ExpandReplacement(string, subject, matchInfo, result) { |
} |
-// Compute the string of a given regular expression capture. |
-function CaptureString(string, lastCaptureInfo, index) { |
- // Scale the index. |
- var scaled = index << 1; |
- // Compute start and end. |
- var start = lastCaptureInfo[CAPTURE(scaled)]; |
- // If start isn't valid, return undefined. |
- if (start < 0) return; |
- var end = lastCaptureInfo[CAPTURE(scaled + 1)]; |
- return %_SubString(string, start, end); |
-} |
- |
- |
-// TODO(lrn): This array will survive indefinitely if replace is never |
-// called again. However, it will be empty, since the contents are cleared |
-// in the finally block. |
-var reusableReplaceArray = new InternalArray(4); |
- |
-// Helper function for replacing regular expressions with the result of a |
-// function application in String.prototype.replace. |
-function StringReplaceGlobalRegExpWithFunction(subject, regexp, replace) { |
- var resultArray = reusableReplaceArray; |
- if (resultArray) { |
- reusableReplaceArray = null; |
- } else { |
- // Inside a nested replace (replace called from the replacement function |
- // of another replace) or we have failed to set the reusable array |
- // back due to an exception in a replacement function. Create a new |
- // array to use in the future, or until the original is written back. |
- resultArray = new InternalArray(16); |
- } |
- var res = %RegExpExecMultiple(regexp, |
- subject, |
- RegExpLastMatchInfo, |
- resultArray); |
- regexp.lastIndex = 0; |
- if (IS_NULL(res)) { |
- // No matches at all. |
- reusableReplaceArray = resultArray; |
- return subject; |
- } |
- var len = res.length; |
- if (NUMBER_OF_CAPTURES(RegExpLastMatchInfo) == 2) { |
- // If the number of captures is two then there are no explicit captures in |
- // the regexp, just the implicit capture that captures the whole match. In |
- // this case we can simplify quite a bit and end up with something faster. |
- // The builder will consist of some integers that indicate slices of the |
- // input string and some replacements that were returned from the replace |
- // function. |
- var match_start = 0; |
- for (var i = 0; i < len; i++) { |
- var elem = res[i]; |
- if (%_IsSmi(elem)) { |
- // Integers represent slices of the original string. |
- if (elem > 0) { |
- match_start = (elem >> 11) + (elem & 0x7ff); |
- } else { |
- match_start = res[++i] - elem; |
- } |
- } else { |
- var func_result = replace(elem, match_start, subject); |
- // Overwrite the i'th element in the results with the string we got |
- // back from the callback function. |
- res[i] = TO_STRING(func_result); |
- match_start += elem.length; |
- } |
- } |
- } else { |
- for (var i = 0; i < len; i++) { |
- var elem = res[i]; |
- if (!%_IsSmi(elem)) { |
- // elem must be an Array. |
- // Use the apply argument as backing for global RegExp properties. |
- var func_result = %Apply(replace, UNDEFINED, elem, 0, elem.length); |
- // Overwrite the i'th element in the results with the string we got |
- // back from the callback function. |
- res[i] = TO_STRING(func_result); |
- } |
- } |
- } |
- var result = %StringBuilderConcat(res, len, subject); |
- resultArray.length = 0; |
- reusableReplaceArray = resultArray; |
- return result; |
-} |
- |
- |
-function StringReplaceNonGlobalRegExpWithFunction(subject, regexp, replace) { |
- var matchInfo = RegExpExec(regexp, subject, 0); |
- if (IS_NULL(matchInfo)) { |
- regexp.lastIndex = 0; |
- return subject; |
- } |
- var index = matchInfo[CAPTURE0]; |
- var result = %_SubString(subject, 0, index); |
- var endOfMatch = matchInfo[CAPTURE1]; |
- // Compute the parameter list consisting of the match, captures, index, |
- // and subject for the replace function invocation. |
- // The number of captures plus one for the match. |
- var m = NUMBER_OF_CAPTURES(matchInfo) >> 1; |
- var replacement; |
- if (m == 1) { |
- // No captures, only the match, which is always valid. |
- var s = %_SubString(subject, index, endOfMatch); |
- // Don't call directly to avoid exposing the built-in global object. |
- replacement = replace(s, index, subject); |
- } else { |
- var parameters = new InternalArray(m + 2); |
- for (var j = 0; j < m; j++) { |
- parameters[j] = CaptureString(subject, matchInfo, j); |
- } |
- parameters[j] = index; |
- parameters[j + 1] = subject; |
- |
- replacement = %Apply(replace, UNDEFINED, parameters, 0, j + 2); |
- } |
- |
- result += replacement; // The add method converts to string if necessary. |
- // Can't use matchInfo any more from here, since the function could |
- // overwrite it. |
- return result + %_SubString(subject, endOfMatch, subject.length); |
-} |
- |
- |
// ES6 21.1.3.15. |
function StringSearch(pattern) { |
CHECK_OBJECT_COERCIBLE(this, "String.prototype.search"); |
@@ -1098,6 +944,7 @@ utils.InstallFunctions(GlobalString.prototype, DONT_ENUM, [ |
// Exports |
utils.Export(function(to) { |
+ to.ExpandReplacement = ExpandReplacement; |
to.StringCharAt = StringCharAtJS; |
to.StringIndexOf = StringIndexOfJS; |
to.StringLastIndexOf = StringLastIndexOfJS; |