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

Fix a bug in the regexp caching. Also add a few more places to

cache.  We now cache most of the places where Opera cache and
one or two where they do not cache for some reason.  Since
these optimizations aren't necessarily useful on real code we
may remove them if and when the Dromaeo website makes the
benchmarks harder to game.
Review URL: http://codereview.chromium.org/995005

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@4157 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 38d6470f
...@@ -95,16 +95,7 @@ function DoConstructRegExp(object, pattern, flags, isConstructorCall) { ...@@ -95,16 +95,7 @@ function DoConstructRegExp(object, pattern, flags, isConstructorCall) {
%IgnoreAttributesAndSetProperty(object, 'ignoreCase', ignoreCase); %IgnoreAttributesAndSetProperty(object, 'ignoreCase', ignoreCase);
%IgnoreAttributesAndSetProperty(object, 'multiline', multiline); %IgnoreAttributesAndSetProperty(object, 'multiline', multiline);
%IgnoreAttributesAndSetProperty(object, 'lastIndex', 0); %IgnoreAttributesAndSetProperty(object, 'lastIndex', 0);
// Clear the regexp result cache. regExpCache.type = 'none';
cachedRegexp = 0;
cachedSubject = 0;
cachedLastIndex = 0;
cachedAnswer = 0;
// These are from string.js.
cachedReplaceSubject = 0;
cachedReplaceRegexp = 0;
cachedReplaceReplacement = 0;
cachedReplaceAnswer = 0;
} }
// Call internal function to compile the pattern. // Call internal function to compile the pattern.
...@@ -150,10 +141,17 @@ function DoRegExpExec(regexp, string, index) { ...@@ -150,10 +141,17 @@ function DoRegExpExec(regexp, string, index) {
} }
var cachedRegexp; function RegExpCache() {
var cachedSubject; this.type = 'none';
var cachedLastIndex; this.regExp = 0;
var cachedAnswer; this.subject = 0;
this.replaceString = 0;
this.lastIndex = 0;
this.answer = 0;
}
var regExpCache = new RegExpCache();
function CloneRegexpAnswer(array) { function CloneRegexpAnswer(array) {
...@@ -169,10 +167,18 @@ function CloneRegexpAnswer(array) { ...@@ -169,10 +167,18 @@ function CloneRegexpAnswer(array) {
function RegExpExec(string) { function RegExpExec(string) {
if (%_ObjectEquals(cachedLastIndex, this.lastIndex) && if (!IS_REGEXP(this)) {
%_ObjectEquals(cachedRegexp, this) && throw MakeTypeError('incompatible_method_receiver',
%_ObjectEquals(cachedSubject, string)) { ['RegExp.prototype.exec', this]);
var last = cachedAnswer; }
var cache = regExpCache;
if (%_ObjectEquals(cache.type, 'exec') &&
%_ObjectEquals(cache.lastIndex, this.lastIndex) &&
%_ObjectEquals(cache.regExp, this) &&
%_ObjectEquals(cache.subject, string)) {
var last = cache.answer;
if (last == null) { if (last == null) {
return last; return last;
} else { } else {
...@@ -180,10 +186,6 @@ function RegExpExec(string) { ...@@ -180,10 +186,6 @@ function RegExpExec(string) {
} }
} }
if (!IS_REGEXP(this)) {
throw MakeTypeError('incompatible_method_receiver',
['RegExp.prototype.exec', this]);
}
if (%_ArgumentsLength() == 0) { if (%_ArgumentsLength() == 0) {
var regExpInput = LAST_INPUT(lastMatchInfo); var regExpInput = LAST_INPUT(lastMatchInfo);
if (IS_UNDEFINED(regExpInput)) { if (IS_UNDEFINED(regExpInput)) {
...@@ -212,10 +214,11 @@ function RegExpExec(string) { ...@@ -212,10 +214,11 @@ function RegExpExec(string) {
if (matchIndices == null) { if (matchIndices == null) {
if (this.global) this.lastIndex = 0; if (this.global) this.lastIndex = 0;
cachedLastIndex = lastIndex; cache.lastIndex = lastIndex;
cachedRegexp = this; cache.regExp = this;
cachedSubject = s; cache.subject = s;
cachedAnswer = matchIndices; // Null. cache.answer = matchIndices; // Null.
cache.type = 'exec';
return matchIndices; // No match. return matchIndices; // No match.
} }
...@@ -246,10 +249,11 @@ function RegExpExec(string) { ...@@ -246,10 +249,11 @@ function RegExpExec(string) {
this.lastIndex = lastMatchInfo[CAPTURE1]; this.lastIndex = lastMatchInfo[CAPTURE1];
return result; return result;
} else { } else {
cachedRegexp = this; cache.regExp = this;
cachedSubject = s; cache.subject = s;
cachedLastIndex = lastIndex; cache.lastIndex = lastIndex;
cachedAnswer = result; cache.answer = result;
cache.type = 'exec';
return CloneRegexpAnswer(result); return CloneRegexpAnswer(result);
} }
} }
...@@ -271,13 +275,35 @@ function RegExpTest(string) { ...@@ -271,13 +275,35 @@ function RegExpTest(string) {
} }
string = regExpInput; string = regExpInput;
} }
var s = ToString(string); var s;
var length = s.length; if (IS_STRING(string)) {
s = string;
} else {
s = ToString(string);
}
var lastIndex = this.lastIndex; var lastIndex = this.lastIndex;
var cache = regExpCache;
if (%_ObjectEquals(cache.type, 'test') &&
%_ObjectEquals(cache.regExp, this) &&
%_ObjectEquals(cache.subject, string) &&
%_ObjectEquals(cache.lastIndex, lastIndex)) {
return cache.answer;
}
var length = s.length;
var i = this.global ? TO_INTEGER(lastIndex) : 0; var i = this.global ? TO_INTEGER(lastIndex) : 0;
cache.type = 'test';
cache.regExp = this;
cache.subject = s;
cache.lastIndex = i;
if (i < 0 || i > s.length) { if (i < 0 || i > s.length) {
this.lastIndex = 0; this.lastIndex = 0;
cache.answer = false;
return false; return false;
} }
...@@ -287,10 +313,12 @@ function RegExpTest(string) { ...@@ -287,10 +313,12 @@ function RegExpTest(string) {
if (matchIndices == null) { if (matchIndices == null) {
if (this.global) this.lastIndex = 0; if (this.global) this.lastIndex = 0;
cache.answer = false;
return false; return false;
} }
if (this.global) this.lastIndex = lastMatchInfo[CAPTURE1]; if (this.global) this.lastIndex = lastMatchInfo[CAPTURE1];
cache.answer = true;
return true; return true;
} }
...@@ -409,6 +437,7 @@ function SetupRegExp() { ...@@ -409,6 +437,7 @@ function SetupRegExp() {
return IS_UNDEFINED(regExpInput) ? "" : regExpInput; return IS_UNDEFINED(regExpInput) ? "" : regExpInput;
} }
function RegExpSetInput(string) { function RegExpSetInput(string) {
regExpCache.type = 'none';
LAST_INPUT(lastMatchInfo) = ToString(string); LAST_INPUT(lastMatchInfo) = ToString(string);
}; };
......
...@@ -168,9 +168,32 @@ function StringMatch(regexp) { ...@@ -168,9 +168,32 @@ function StringMatch(regexp) {
var subject = TO_STRING_INLINE(this); var subject = TO_STRING_INLINE(this);
if (!regexp.global) return regexp.exec(subject); if (!regexp.global) return regexp.exec(subject);
var cache = regExpCache;
if (%_ObjectEquals(cache.type, 'match') &&
%_ObjectEquals(cache.regExp, regexp) &&
%_ObjectEquals(cache.subject, subject)) {
var last = cache.answer;
if (last == null) {
return last;
} else {
return CloneRegexpAnswer(last);
}
}
%_Log('regexp', 'regexp-match,%0S,%1r', [subject, regexp]); %_Log('regexp', 'regexp-match,%0S,%1r', [subject, regexp]);
// lastMatchInfo is defined in regexp-delay.js. // lastMatchInfo is defined in regexp-delay.js.
return %StringMatch(subject, regexp, lastMatchInfo); var result = %StringMatch(subject, regexp, lastMatchInfo);
cache.type = 'match';
cache.regExp = regexp;
cache.subject = subject;
cache.answer = result;
if (result == null) {
return result;
} else {
return CloneRegexpAnswer(result);
}
} }
...@@ -206,6 +229,7 @@ function StringReplace(search, replace) { ...@@ -206,6 +229,7 @@ function StringReplace(search, replace) {
if (IS_REGEXP(search)) { if (IS_REGEXP(search)) {
%_Log('regexp', 'regexp-replace,%0r,%1S', [search, subject]); %_Log('regexp', 'regexp-replace,%0r,%1S', [search, subject]);
if (IS_FUNCTION(replace)) { if (IS_FUNCTION(replace)) {
regExpCache.type = 'none';
return StringReplaceRegExpWithFunction(subject, search, replace); return StringReplaceRegExpWithFunction(subject, search, replace);
} else { } else {
return StringReplaceRegExp(subject, search, replace); return StringReplaceRegExp(subject, search, replace);
...@@ -239,27 +263,25 @@ function StringReplace(search, replace) { ...@@ -239,27 +263,25 @@ function StringReplace(search, replace) {
} }
var cachedReplaceSubject;
var cachedReplaceRegexp;
var cachedReplaceReplacement;
var cachedReplaceAnswer;
// Helper function for regular expressions in String.prototype.replace. // Helper function for regular expressions in String.prototype.replace.
function StringReplaceRegExp(subject, regexp, replace) { function StringReplaceRegExp(subject, regexp, replace) {
if (%_ObjectEquals(replace, cachedReplaceReplacement) && var cache = regExpCache;
%_ObjectEquals(subject, cachedReplaceSubject) && if (%_ObjectEquals(cache.regExp, regexp) &&
%_ObjectEquals(regexp, cachedReplaceRegexp)) { %_ObjectEquals(cache.type, 'replace') &&
return cachedReplaceAnswer; %_ObjectEquals(cache.replaceString, replace) &&
%_ObjectEquals(cache.subject, subject)) {
return cache.answer;
} }
replace = TO_STRING_INLINE(replace); replace = TO_STRING_INLINE(replace);
var answer = %StringReplaceRegExpWithString(subject, var answer = %StringReplaceRegExpWithString(subject,
regexp, regexp,
replace, replace,
lastMatchInfo); lastMatchInfo);
cachedReplaceSubject = subject; cache.subject = subject;
cachedReplaceRegexp = regexp; cache.regExp = regexp;
cachedReplaceReplacement = replace; cache.replaceString = replace;
cachedReplaceAnswer = answer; cache.answer = answer;
cache.type = 'replace';
return answer; return answer;
} }
...@@ -577,10 +599,26 @@ function StringSplit(separator, limit) { ...@@ -577,10 +599,26 @@ function StringSplit(separator, limit) {
return result; return result;
} }
var cache = regExpCache;
if (%_ObjectEquals(cache.type, 'split') &&
%_ObjectEquals(cache.regExp, separator) &&
%_ObjectEquals(cache.subject, subject)) {
return CloneRegexpAnswer(cache.answer);
}
cache.type = 'split';
cache.regExp = separator;
cache.subject = subject;
%_Log('regexp', 'regexp-split,%0S,%1r', [subject, separator]); %_Log('regexp', 'regexp-split,%0S,%1r', [subject, separator]);
if (length === 0) { if (length === 0) {
if (splitMatch(separator, subject, 0, 0) != null) return []; if (splitMatch(separator, subject, 0, 0) != null) {
cache.answer = [];
return [];
}
cache.answer = [subject];
return [subject]; return [subject];
} }
...@@ -592,14 +630,16 @@ function StringSplit(separator, limit) { ...@@ -592,14 +630,16 @@ function StringSplit(separator, limit) {
if (startIndex === length) { if (startIndex === length) {
result[result.length] = subject.slice(currentIndex, length); result[result.length] = subject.slice(currentIndex, length);
return result; cache.answer = result;
return CloneRegexpAnswer(result);
} }
var matchInfo = splitMatch(separator, subject, currentIndex, startIndex); var matchInfo = splitMatch(separator, subject, currentIndex, startIndex);
if (IS_NULL(matchInfo)) { if (IS_NULL(matchInfo)) {
result[result.length] = subject.slice(currentIndex, length); result[result.length] = subject.slice(currentIndex, length);
return result; cache.answer = result;
return CloneRegexpAnswer(result);
} }
var endIndex = matchInfo[CAPTURE1]; var endIndex = matchInfo[CAPTURE1];
...@@ -611,7 +651,10 @@ function StringSplit(separator, limit) { ...@@ -611,7 +651,10 @@ function StringSplit(separator, limit) {
} }
result[result.length] = SubString(subject, currentIndex, matchInfo[CAPTURE0]); result[result.length] = SubString(subject, currentIndex, matchInfo[CAPTURE0]);
if (result.length === limit) return result; if (result.length === limit) {
cache.answer = result;
return CloneRegexpAnswer(result);
}
var num_captures = NUMBER_OF_CAPTURES(matchInfo); var num_captures = NUMBER_OF_CAPTURES(matchInfo);
for (var i = 2; i < num_captures; i += 2) { for (var i = 2; i < num_captures; i += 2) {
...@@ -622,7 +665,10 @@ function StringSplit(separator, limit) { ...@@ -622,7 +665,10 @@ function StringSplit(separator, limit) {
} else { } else {
result[result.length] = void 0; result[result.length] = void 0;
} }
if (result.length === limit) return result; if (result.length === limit) {
cache.answer = result;
return CloneRegexpAnswer(result);
}
} }
startIndex = currentIndex = endIndex; startIndex = currentIndex = endIndex;
......
// Copyright 2010 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Tests that regexp caching isn't messing things up.
var re1 = /(o)/g;
assertEquals("FxxBar", "FooBar".replace(re1, "x"));
assertEquals("o", RegExp.$1);
assertTrue(/(x)/.test("abcxdef"));
assertEquals("x", RegExp.$1);
assertEquals("FxxBar", "FooBar".replace(re1, "x"));
assertEquals("o", RegExp.$1);
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