Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(99)

Unified Diff: src/js/regexp.js

Issue 1590673002: Separate String.prototype.replace into RegExp.prototype[Symbol.replace] (Closed) Base URL: https://chromium.googlesource.com/v8/v8.git@master
Patch Set: Remove extra newline from test Created 4 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | src/js/string.js » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: src/js/regexp.js
diff --git a/src/js/regexp.js b/src/js/regexp.js
index a163952451f082ea50e12678a7a94fffa3e4052e..3ca5c39811ea869449295a715298642ac0f0f790 100644
--- a/src/js/regexp.js
+++ b/src/js/regexp.js
@@ -9,6 +9,7 @@
// -------------------------------------------------------------------
// Imports
+var ExpandReplacement;
var FLAG_harmony_tolength;
var GlobalObject = global.Object;
var GlobalRegExp = global.RegExp;
@@ -17,6 +18,7 @@ var InternalArray = utils.InternalArray;
var InternalPackedArray = utils.InternalPackedArray;
var MakeTypeError;
var matchSymbol = utils.ImportNow("match_symbol");
+var replaceSymbol = utils.ImportNow("replace_symbol");
var searchSymbol = utils.ImportNow("search_symbol");
var splitSymbol = utils.ImportNow("split_symbol");
@@ -25,6 +27,7 @@ utils.ImportFromExperimental(function(from) {
});
utils.Import(function(from) {
+ ExpandReplacement = from.ExpandReplacement;
MakeTypeError = from.MakeTypeError;
});
@@ -379,6 +382,175 @@ function RegExpMatch(string) {
}
+// ES6 21.2.5.8.
+
+// 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;
+}
+
+
+// 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);
+}
+
+
+function StringReplaceNonGlobalRegExpWithFunction(subject, regexp, replace) {
+ var matchInfo = DoRegExpExec(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);
+}
+
+
+function RegExpReplace(string, replace) {
+ // TODO(littledan): allow non-regexp receivers.
+ if (!IS_REGEXP(this)) {
+ throw MakeTypeError(kIncompatibleMethodReceiver,
+ "RegExp.prototype.@@replace", this);
+ }
+ var subject = TO_STRING(string);
+ var search = this;
+
+ if (!IS_CALLABLE(replace)) {
+ replace = TO_STRING(replace);
+
+ if (!REGEXP_GLOBAL(search)) {
+ // Non-global regexp search, string replace.
+ var match = DoRegExpExec(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 (REGEXP_GLOBAL(search)) {
+ // Global regexp search, function replace.
+ return StringReplaceGlobalRegExpWithFunction(subject, search, replace);
+ }
+ // Non-global regexp search, function replace.
+ return StringReplaceNonGlobalRegExpWithFunction(subject, search, replace);
+}
+
+
// ES6 21.2.5.9.
function RegExpSearch(string) {
// TODO(yangguo): allow non-regexp receivers.
@@ -511,6 +683,7 @@ utils.InstallFunctions(GlobalRegExp.prototype, DONT_ENUM, [
"toString", RegExpToString,
"compile", RegExpCompileJS,
matchSymbol, RegExpMatch,
+ replaceSymbol, RegExpReplace,
searchSymbol, RegExpSearch,
splitSymbol, RegExpSplit,
]);
« no previous file with comments | « no previous file | src/js/string.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698