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

Unified Diff: src/string.js

Issue 1014005: Attempt at pre-calculating replace regexp matches. (Closed)
Patch Set: Reuse result array. Avoid using apply. Created 10 years, 9 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 | « src/runtime.cc ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: src/string.js
diff --git a/src/string.js b/src/string.js
index 2fe17f8e5ed814680f00f2d79df55a5cccc9a686..0acfc16e7d5eb4f51476cfd7ee66756332adbdf7 100644
--- a/src/string.js
+++ b/src/string.js
@@ -28,6 +28,7 @@
// This file relies on the fact that the following declaration has been made
// in runtime.js:
+// const $Function = global.Function
// const $String = global.String;
// const $NaN = 0/0;
@@ -384,96 +385,87 @@ function addCaptureString(builder, matchInfo, index) {
};
+var nestedReplace = 0;
+var staticReplaceResult = $Array(64);
+var replaceAppliers = { __proto__: null }; // Really empty object.
+
+function makeReplaceApplier(n) {
+ var source = "return func.call(null, arr[idx+1],";
+ for (var i = 0; i < n; i++) {
+ source += " arr[idx+" + (i + 2) +"],";
+ }
+ source += " arr[idx], subject);";
+ return $Function("func, arr, idx, subject", source);
+}
+
// Helper function for replacing regular expressions with the result of a
-// function application in String.prototype.replace. The function application
-// must be interleaved with the regexp matching (contrary to ECMA-262
-// 15.5.4.11) to mimic SpiderMonkey and KJS behavior when the function uses
-// the static properties of the RegExp constructor. Example:
-// 'abcd'.replace(/(.)/g, function() { return RegExp.$1; }
-// should be 'abcd' and not 'dddd' (or anything else).
+// function application in String.prototype.replace.
function StringReplaceRegExpWithFunction(subject, regexp, replace) {
- var matchInfo = DoRegExpExec(regexp, subject, 0);
- if (IS_NULL(matchInfo)) return subject;
-
- var result = new ReplaceResultBuilder(subject);
- // There's at least one match. If the regexp is global, we have to loop
- // over all matches. The loop is not in C++ code here like the one in
- // RegExp.prototype.exec, because of the interleaved function application.
- // Unfortunately, that means this code is nearly duplicated, here and in
- // jsregexp.cc.
if (regexp.global) {
- var previous = 0;
- var startOfMatch;
- if (NUMBER_OF_CAPTURES(matchInfo) == 2) {
- // Both branches contain essentially the same loop except for the call
- // to the replace function. The branch is put outside of the loop for
- // speed
- do {
- startOfMatch = matchInfo[CAPTURE0];
- result.addSpecialSlice(previous, startOfMatch);
- previous = matchInfo[CAPTURE1];
- var match = SubString(subject, startOfMatch, previous);
- // Don't call directly to avoid exposing the built-in global object.
- result.add(replace.call(null, match, startOfMatch, subject));
- // Can't use matchInfo any more from here, since the function could
- // overwrite it.
- // Continue with the next match.
- // Increment previous if we matched an empty string, as per ECMA-262
- // 15.5.4.10.
- if (previous == startOfMatch) {
- // Add the skipped character to the output, if any.
- if (previous < subject.length) {
- result.addSpecialSlice(previous, previous + 1);
- }
- previous++;
- // Per ECMA-262 15.10.6.2, if the previous index is greater than the
- // string length, there is no match
- if (previous > subject.length) {
- return result.generate();
+ var matches = (nestedReplace > 0) ? $Array(0) : staticReplaceResult;
+ var match_count = %RegExpExecMultiple(regexp, subject, lastMatchInfo, matches);
+ regexp.lastIndex = 0;
+ if (match_count == 0) return subject; // No matches at all.
+ // Capture count, not including actual match.
+ var capture_count = matches[0];
+ var result = new ReplaceResultBuilder(subject);
+ var match_index = 1;
+ var index = 0;
+ nestedReplace++;
+ try {
+ if (capture_count == 0) {
+ while (match_index < matches.length) {
+ var match_start = matches[match_index];
+ if (index < match_start) {
+ result.addSpecialSlice(index, match_start);
}
+ var match = matches[match_index + 1];
+ var func_result = replace.call(null, match, match_start, subject);
+ if (!IS_STRING(func_result)) func_result = TO_STRING(func_result);
+ result.add(func_result);
+ index = match_start + match.length;
+ match_index += 2;
}
- matchInfo = DoRegExpExec(regexp, subject, previous);
- } while (!IS_NULL(matchInfo));
- } else {
- do {
- startOfMatch = matchInfo[CAPTURE0];
- result.addSpecialSlice(previous, startOfMatch);
- previous = matchInfo[CAPTURE1];
- result.add(ApplyReplacementFunction(replace, matchInfo, subject));
- // Can't use matchInfo any more from here, since the function could
- // overwrite it.
- // Continue with the next match.
- // Increment previous if we matched an empty string, as per ECMA-262
- // 15.5.4.10.
- if (previous == startOfMatch) {
- // Add the skipped character to the output, if any.
- if (previous < subject.length) {
- result.addSpecialSlice(previous, previous + 1);
- }
- previous++;
- // Per ECMA-262 15.10.6.2, if the previous index is greater than the
- // string length, there is no match
- if (previous > subject.length) {
- return result.generate();
+ } else {
+ var applier = replaceAppliers[capture_count];
+ if (!applier) {
+ applier = replaceAppliers[capture_count] =
+ makeReplaceApplier(capture_count);
+ }
+ while (match_index < matches.length) {
+ var match_start = matches[match_index];
+ if (index < match_start) {
+ result.addSpecialSlice(index, match_start);
}
+ var match = matches[match_index + 1];
+ var func_result = applier(replace, matches, match_index, subject);
+ if (!IS_STRING(func_result)) func_result = TO_STRING(func_result);
+ result.add(func_result);
+ index = match_start + match.length;
+ match_index += capture_count + 2;
}
- matchInfo = DoRegExpExec(regexp, subject, previous);
- } while (!IS_NULL(matchInfo));
+ }
+ if (index < subject.length) {
+ result.addSpecialSlice(index, subject.length);
+ }
+ } finally {
+ nestedReplace--;
}
-
- // Tack on the final right substring after the last match.
- result.addSpecialSlice(previous, subject.length);
-
+ return result.generate();
} else { // Not a global regexp, no need to loop.
+ var matchInfo = DoRegExpExec(regexp, subject, 0);
+ if (IS_NULL(matchInfo)) return subject;
+
+ var result = new ReplaceResultBuilder(subject);
result.addSpecialSlice(0, matchInfo[CAPTURE0]);
var endOfMatch = matchInfo[CAPTURE1];
result.add(ApplyReplacementFunction(replace, matchInfo, subject));
// Can't use matchInfo any more from here, since the function could
// overwrite it.
result.addSpecialSlice(endOfMatch, subject.length);
+ return result.generate();
}
- return result.generate();
}
« no previous file with comments | « src/runtime.cc ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698