Commit c436c70f authored by erik.corry@gmail.com's avatar erik.corry@gmail.com

Fix some bugs in accessing details of the lastest regexp

match.  Sometimes were were not updating it when we should
and sometimes we were leaving the lastMatchInfoOverride in
place when we should be using the updated regular last match
info.  Small optimization for zero length match in
String.prototype.replace.
Review URL: https://chromiumcodereview.appspot.com/10184004

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@11422 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 09657d94
......@@ -196,6 +196,7 @@ macro SET_UTC_DATE_VALUE(arg, value) = (%DateSetValue(arg, value, 1));
macro SET_LOCAL_DATE_VALUE(arg, value) = (%DateSetValue(arg, value, 0));
# Last input and last subject of regexp matches.
const LAST_SUBJECT_INDEX = 1;
macro LAST_SUBJECT(array) = ((array)[1]);
macro LAST_INPUT(array) = ((array)[2]);
......
This diff is collapsed.
......@@ -237,10 +237,28 @@ function StringReplace(search, replace) {
replace);
}
} else {
return %StringReplaceRegExpWithString(subject,
search,
TO_STRING_INLINE(replace),
lastMatchInfo);
if (lastMatchInfoOverride == null) {
return %StringReplaceRegExpWithString(subject,
search,
TO_STRING_INLINE(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 (%_IsSmi(lastMatchInfo[LAST_SUBJECT_INDEX])) {
lastMatchInfo[LAST_SUBJECT_INDEX] = saved_subject;
} else {
lastMatchInfoOverride = null;
}
return answer;
}
}
}
......@@ -429,14 +447,22 @@ function StringReplaceGlobalRegExpWithFunction(subject, regexp, replace) {
return subject;
}
var len = res.length;
var i = 0;
if (NUMBER_OF_CAPTURES(lastMatchInfo) == 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;
var override = new InternalArray(null, 0, subject);
var receiver = %GetDefaultReceiver(replace);
while (i < len) {
for (var i = 0; i < len; i++) {
var elem = res[i];
if (%_IsSmi(elem)) {
// Integers represent slices of the original string. Use these to
// get the offsets we need for the override array (so things like
// RegExp.leftContext work during the callback function.
if (elem > 0) {
match_start = (elem >> 11) + (elem & 0x7ff);
} else {
......@@ -448,23 +474,25 @@ function StringReplaceGlobalRegExpWithFunction(subject, regexp, replace) {
lastMatchInfoOverride = override;
var func_result =
%_CallFunction(receiver, elem, match_start, subject, replace);
// Overwrite the i'th element in the results with the string we got
// back from the callback function.
res[i] = TO_STRING_INLINE(func_result);
match_start += elem.length;
}
i++;
}
} else {
var receiver = %GetDefaultReceiver(replace);
while (i < len) {
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.
lastMatchInfoOverride = elem;
var func_result = %Apply(replace, receiver, 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_INLINE(func_result);
}
i++;
}
}
var resultBuilder = new ReplaceResultBuilder(subject, res);
......
......@@ -86,3 +86,71 @@ assertEquals('c', RegExp.$2);
for (var i = 3; i < 10; i++) {
assertEquals("", RegExp['$' + i]);
}
function Override() {
// Set the internal lastMatchInfoOverride. After calling this we do a normal
// match and verify the override was cleared and that we record the new
// captures.
"abcdabcd".replace(/(b)(c)/g, function() { });
}
function TestOverride(input, expect, property, re_src) {
var re = new RegExp(re_src);
var re_g = new RegExp(re_src, "g");
function OverrideCase(fn) {
Override();
fn();
assertEquals(expect, RegExp[property]);
}
OverrideCase(function() { return input.replace(re, "x"); });
OverrideCase(function() { return input.replace(re_g, "x"); });
OverrideCase(function() { return input.replace(re, ""); });
OverrideCase(function() { return input.replace(re_g, ""); });
OverrideCase(function() { return input.match(re); });
OverrideCase(function() { return input.match(re_g); });
OverrideCase(function() { return re.test(input); });
OverrideCase(function() { return re_g.test(input); });
}
var input = "bar.foo baz......";
var re_str = "(ba.).*?f";
TestOverride(input, "bar", "$1", re_str);
input = "foo bar baz";
var re_str = "bar";
TestOverride(input, "bar", "$&", re_str);
function no_last_match(fn) {
fn();
assertEquals("hestfisk", RegExp.$1);
}
/(hestfisk)/.test("There's no such thing as a hestfisk!");
no_last_match(function() { "foo".replace("f", ""); });
no_last_match(function() { "foo".replace("f", "f"); });
no_last_match(function() { "foo".split("o"); });
var base = "In the music. In the music. ";
var cons = base + base + base + base;
no_last_match(function() { cons.replace("x", "y"); });
no_last_match(function() { cons.replace("e", "E"); });
// Here's one that matches once, then tries to match again, but fails.
// Verify that the last match info is from the last match, not from the
// failure that came after.
"bar.foo baz......".replace(/(ba.).*?f/g, function() { return "x";});
assertEquals("bar", RegExp.$1);
var a = "foo bar baz".replace(/^|bar/g, "");
assertEquals("foo baz", a);
a = "foo bar baz".replace(/^|bar/g, "*");
assertEquals("*foo * baz", a);
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment