Fix incorrect handling of global RegExp properties for nested replace-regexp-with-function.

Review URL: http://codereview.chromium.org/1695002

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@4455 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
lrn@chromium.org 2010-04-21 08:33:04 +00:00
parent 5c7ffe9f1c
commit 0adfe842a5
3 changed files with 88 additions and 69 deletions

View File

@ -115,7 +115,9 @@ function CompileRegExp(pattern, flags) {
function DoRegExpExec(regexp, string, index) {
return %_RegExpExec(regexp, string, index, lastMatchInfo);
var result = %_RegExpExec(regexp, string, index, lastMatchInfo);
if (result !== null) lastMatchInfoOverride = null;
return result;
}
@ -136,7 +138,7 @@ var regExpCache = new RegExpCache();
function CloneRegExpResult(array) {
if (array == null) return null;
if (array == null) return null;
var length = array.length;
var answer = %_RegExpConstructResult(length, array.index, array.input);
for (var i = 0; i < length; i++) {
@ -237,7 +239,7 @@ function RegExpExec(string) {
cache.type = 'exec';
return matchIndices; // No match.
}
lastMatchInfoOverride = null;
var result = BuildResultFromMatchInfo(matchIndices, s);
if (this.global) {
@ -312,7 +314,7 @@ function RegExpTest(string) {
cache.answer = false;
return false;
}
lastMatchInfoOverride = null;
if (this.global) this.lastIndex = lastMatchInfo[CAPTURE1];
cache.answer = true;
return true;
@ -340,7 +342,9 @@ function RegExpToString() {
// on the captures array of the last successful match and the subject string
// of the last successful match.
function RegExpGetLastMatch() {
if (lastMatchInfoOverride) { return lastMatchInfoOverride[0]; }
if (lastMatchInfoOverride !== null) {
return lastMatchInfoOverride[0];
}
var regExpSubject = LAST_SUBJECT(lastMatchInfo);
return SubString(regExpSubject,
lastMatchInfo[CAPTURE0],

View File

@ -175,9 +175,9 @@ function StringLocaleCompare(other) {
// ECMA-262 section 15.5.4.10
function StringMatch(regexp) {
var subject = TO_STRING_INLINE(this);
if (IS_REGEXP(regexp)) {
if (!regexp.global) return regexp.exec(subject);
if (IS_REGEXP(regexp)) {
if (!regexp.global) return regexp.exec(subject);
var cache = regExpCache;
var saveAnswer = false;
@ -435,63 +435,63 @@ function StringReplaceRegExpWithFunction(subject, regexp, replace) {
// array to use in the future, or until the original is written back.
resultArray = $Array(16);
}
try {
// Must handle exceptions thrown by the replace functions correctly,
// including unregistering global regexps.
var res = %RegExpExecMultiple(regexp,
subject,
lastMatchInfo,
resultArray);
regexp.lastIndex = 0;
if (IS_NULL(res)) {
// No matches at all.
return subject;
}
var len = res.length;
var i = 0;
if (NUMBER_OF_CAPTURES(lastMatchInfo) == 2) {
var match_start = 0;
while (i < len) {
var elem = res[i];
if (%_IsSmi(elem)) {
if (elem > 0) {
match_start = (elem >> 11) + (elem & 0x7ff);
} else {
match_start = res[++i] - elem;
}
} else {
var func_result = replace.call(null, elem, match_start, subject);
if (!IS_STRING(func_result)) {
func_result = NonStringToString(func_result);
}
res[i] = func_result;
match_start += elem.length;
}
i++;
}
} else {
while (i < len) {
var elem = res[i];
if (!%_IsSmi(elem)) {
// elem must be an Array.
// Use the apply argument as backing for global RegExp properties.
lastMatchInfoOverride = elem;
var func_result = replace.apply(null, elem);
if (!IS_STRING(func_result)) {
func_result = NonStringToString(func_result);
}
res[i] = func_result;
}
i++;
}
}
var result = new ReplaceResultBuilder(subject, res);
return result.generate();
} finally {
lastMatchInfoOverride = null;
resultArray.length = 0;
reusableReplaceArray = resultArray;
var res = %RegExpExecMultiple(regexp,
subject,
lastMatchInfo,
resultArray);
regexp.lastIndex = 0;
if (IS_NULL(res)) {
// No matches at all.
return subject;
}
var len = res.length;
var i = 0;
if (NUMBER_OF_CAPTURES(lastMatchInfo) == 2) {
var match_start = 0;
var override = [null, 0, subject];
while (i < len) {
var elem = res[i];
if (%_IsSmi(elem)) {
if (elem > 0) {
match_start = (elem >> 11) + (elem & 0x7ff);
} else {
match_start = res[++i] - elem;
}
} else {
override[0] = elem;
override[1] = match_start;
lastMatchInfoOverride = override;
var func_result = replace.call(null, elem, match_start, subject);
if (!IS_STRING(func_result)) {
func_result = NonStringToString(func_result);
}
res[i] = func_result;
match_start += elem.length;
}
i++;
}
} else {
while (i < len) {
var elem = res[i];
if (!%_IsSmi(elem)) {
// elem must be an Array.
// Use the apply argument as backing for global RegExp properties.
lastMatchInfoOverride = elem;
var func_result = replace.apply(null, elem);
if (!IS_STRING(func_result)) {
func_result = NonStringToString(func_result);
}
res[i] = func_result;
}
i++;
}
}
var resultBuilder = new ReplaceResultBuilder(subject, res);
var result = resultBuilder.generate();
resultArray.length = 0;
reusableReplaceArray = resultArray;
return result;
} else { // Not a global regexp, no need to loop.
var matchInfo = DoRegExpExec(regexp, subject, 0);
if (IS_NULL(matchInfo)) return subject;
@ -542,7 +542,6 @@ function StringSearch(re) {
var s = TO_STRING_INLINE(this);
var match = DoRegExpExec(regexp, s, 0);
if (match) {
lastMatchInfo = match;
return match[CAPTURE0];
}
return -1;

View File

@ -30,7 +30,7 @@
*/
function replaceTest(result, subject, pattern, replacement) {
var name =
var name =
"\"" + subject + "\".replace(" + pattern + ", " + replacement + ")";
assertEquals(result, subject.replace(pattern, replacement), name);
}
@ -114,8 +114,8 @@ replaceTest("xaxe$xcx", short, /b/, "e$");
replaceTest("xaxe$xcx", short, /b/g, "e$");
replaceTest("[$$$1$$a1abb1bb0$002$3$03][$$$1$$b1bcc1cc0$002$3$03]c",
"abc", /(.)(?=(.))/g, "[$$$$$$1$$$$$11$01$2$21$02$020$002$3$03]");
replaceTest("[$$$1$$a1abb1bb0$002$3$03][$$$1$$b1bcc1cc0$002$3$03]c",
"abc", /(.)(?=(.))/g, "[$$$$$$1$$$$$11$01$2$21$02$020$002$3$03]");
// Replace with functions.
@ -189,5 +189,21 @@ replaceTest("string true", "string x", /x/g, function() { return true; });
replaceTest("string null", "string x", /x/g, function() { return null; });
replaceTest("string undefined", "string x", /x/g, function() { return undefined; });
replaceTest("aundefinedbundefinedcundefined",
replaceTest("aundefinedbundefinedcundefined",
"abc", /(.)|(.)/g, function(m, m1, m2, i, s) { return m1+m2; });
// Test nested calls to replace, including that it sets RegExp.$& correctly.
function replacer(m,i,s) {
assertEquals(m,RegExp['$&']);
return "[" + RegExp['$&'] + "-"
+ m.replace(/./g,"$&$&") + "-"
+ m.replace(/./g,function() { return RegExp['$&']; })
+ "-" + RegExp['$&'] + "]";
}
replaceTest("[ab-aabb-ab-b][az-aazz-az-z]",
"abaz", /a./g, replacer);
replaceTest("[ab-aabb-ab-b][az-aazz-az-z]",
"abaz", /a(.)/g, replacer);