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

Unified Diff: src/regexp.js

Issue 3778004: Restructure RegExp exec cache code. (Closed)
Patch Set: Addressed review comments. Created 10 years, 2 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/string.js » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: src/regexp.js
diff --git a/src/regexp.js b/src/regexp.js
index 59c40407fbc20ddc4406d0ef2661d7d9bcf39099..f87a5ee18ff8c9000dc4bed15e6d1db4bded574a 100644
--- a/src/regexp.js
+++ b/src/regexp.js
@@ -126,11 +126,11 @@ function RegExpCache() {
this.regExp = 0;
this.subject = 0;
this.replaceString = 0;
- this.lastIndex = 0; // Also used for splitLimit when type is "split"
this.answer = 0;
// answerSaved marks whether the contents of answer is valid for a cache
// hit in RegExpExec, StringMatch and StringSplit.
this.answerSaved = false;
+ this.splitLimit = 0; // Used only when type is "split".
}
@@ -181,22 +181,30 @@ function RegExpExec(string) {
var cache = regExpCache;
var saveAnswer = false;
+ var lastIndex = this.lastIndex;
+
+ // Since cache.subject is always a string, a matching input can not
+ // cause visible side-effects when converted to a string, so we can omit
+ // the conversion required by the specification.
+ // Likewise, the regexp.lastIndex and regexp.global properties are value
+ // properties that are not configurable, so reading them can also not cause
+ // any side effects (converting lastIndex to a number can, though).
if (%_ObjectEquals(cache.type, 'exec') &&
- %_ObjectEquals(cache.lastIndex, this.lastIndex) &&
+ %_ObjectEquals(0, lastIndex) &&
%_IsRegExpEquivalent(cache.regExp, this) &&
%_ObjectEquals(cache.subject, string)) {
if (cache.answerSaved) {
- // If this regexp is global, cache.lastIndex is zero, so we only get
- // here if this.lastIndex is zero, and resulting this.lastIndex
- // must be zero too, so no change is necessary.
- if (!this.global) this.lastIndex = lastMatchInfo[CAPTURE1];
+ // The regexp.lastIndex value must be 0 for non-global RegExps, and for
+ // global RegExps we only cache negative results, which gives a lastIndex
+ // of zero as well.
+ this.lastIndex = 0;
return %_RegExpCloneResult(cache.answer);
} else {
saveAnswer = true;
}
}
- if (%_ArgumentsLength() == 0) {
+ if (%_ArgumentsLength() === 0) {
var regExpInput = LAST_INPUT(lastMatchInfo);
if (IS_UNDEFINED(regExpInput)) {
throw MakeError('no_input_to_regexp', [this]);
@@ -209,41 +217,48 @@ function RegExpExec(string) {
} else {
s = ToString(string);
}
- var lastIndex = this.lastIndex;
-
- var i = this.global ? TO_INTEGER(lastIndex) : 0;
+ var global = this.global;
- if (i < 0 || i > s.length) {
- this.lastIndex = 0;
- return null;
+ // Conversion is required by the ES5 specification (RegExp.prototype.exec
+ // algorithm, step 5) even if the value is discarded for non-global RegExps.
+ var i = TO_INTEGER(lastIndex);
+ if (global) {
+ if (i < 0 || i > s.length) {
+ this.lastIndex = 0;
+ return null;
+ }
+ } else {
+ i = 0;
}
%_Log('regexp', 'regexp-exec,%0r,%1S,%2i', [this, s, lastIndex]);
// matchIndices is either null or the lastMatchInfo array.
var matchIndices = %_RegExpExec(this, s, i, lastMatchInfo);
- if (matchIndices == null) {
- if (this.global) {
+ if (matchIndices === null) {
+ if (global) {
+ // Cache negative result only if initial lastIndex was zero.
this.lastIndex = 0;
- if (lastIndex != 0) return matchIndices;
+ if (lastIndex !== 0) return matchIndices;
}
- cache.lastIndex = lastIndex;
cache.regExp = this;
- cache.subject = s;
- cache.answer = matchIndices; // Null.
+ cache.subject = s; // Always a string.
+ cache.answer = null;
cache.answerSaved = true; // Safe since no cloning is needed.
cache.type = 'exec';
return matchIndices; // No match.
}
+
+ // Successful match.
lastMatchInfoOverride = null;
var result = BuildResultFromMatchInfo(matchIndices, s);
- if (this.global) {
+ if (global) {
+ // Don't cache positive results for global regexps.
this.lastIndex = lastMatchInfo[CAPTURE1];
} else {
cache.regExp = this;
cache.subject = s;
- cache.lastIndex = lastIndex;
if (saveAnswer) cache.answer = %_RegExpCloneResult(result);
cache.answerSaved = saveAnswer;
cache.type = 'exec';
@@ -273,32 +288,49 @@ function RegExpTest(string) {
}
string = regExpInput;
}
- var s;
- if (IS_STRING(string)) {
- s = string;
- } else {
- s = ToString(string);
- }
var lastIndex = this.lastIndex;
+
var cache = regExpCache;
if (%_ObjectEquals(cache.type, 'test') &&
%_IsRegExpEquivalent(cache.regExp, this) &&
%_ObjectEquals(cache.subject, string) &&
- %_ObjectEquals(cache.lastIndex, lastIndex)) {
- // If this regexp is not global, cache.lastIndex is zero, so we only get
- // here if this.lastIndex is zero, and resulting this.lastIndex
- // must be zero too, so no change is necessary.
- if (this.global) this.lastIndex = lastMatchInfo[CAPTURE1];
+ %_ObjectEquals(0, lastIndex)) {
+ // The regexp.lastIndex value must be 0 for non-global RegExps, and for
+ // global RegExps we only cache negative results, which gives a resulting
+ // lastIndex of zero as well.
+ if (global) this.lastIndex = 0;
return cache.answer;
}
+ var s;
+ if (IS_STRING(string)) {
+ s = string;
+ } else {
+ s = ToString(string);
+ }
+ var length = s.length;
+
+ // Conversion is required by the ES5 specification (RegExp.prototype.exec
+ // algorithm, step 5) even if the value is discarded for non-global RegExps.
+ var i = TO_INTEGER(lastIndex);
+ if (global) {
+ if (i < 0 || i > length) {
+ this.lastIndex = 0;
+ return false;
+ }
+ } else {
+ i = 0;
+ }
+
+ var global = this.global;
+
// Remove irrelevant preceeding '.*' in a test regexp. The expression
// checks whether this.source starts with '.*' and that the third
// char is not a '?'
- if (%_StringCharCodeAt(this.source,0) == 46 && // '.'
- %_StringCharCodeAt(this.source,1) == 42 && // '*'
- %_StringCharCodeAt(this.source,2) != 63) { // '?'
+ if (%_StringCharCodeAt(this.source, 0) == 46 && // '.'
+ %_StringCharCodeAt(this.source, 1) == 42 && // '*'
+ %_StringCharCodeAt(this.source, 2) != 63) { // '?'
if (!%_ObjectEquals(regexp_key, this)) {
regexp_key = this;
regexp_val = new $RegExp(this.source.substring(2, this.source.length),
@@ -309,33 +341,28 @@ function RegExpTest(string) {
if (!regexp_val.test(s)) return false;
}
- var length = s.length;
- var i = this.global ? TO_INTEGER(lastIndex) : 0;
-
- cache.type = 'test';
- cache.regExp = this;
- cache.subject = s;
- cache.lastIndex = i;
-
- if (i < 0 || i > length) {
- this.lastIndex = 0;
- cache.answer = false;
- return false;
- }
-
%_Log('regexp', 'regexp-exec,%0r,%1S,%2i', [this, s, lastIndex]);
// matchIndices is either null or the lastMatchInfo array.
var matchIndices = %_RegExpExec(this, s, i, lastMatchInfo);
- if (matchIndices == null) {
- if (this.global) this.lastIndex = 0;
- cache.answer = false;
- return false;
+ var result = (matchIndices !== null);
+ if (result) {
+ lastMatchInfoOverride = null;
}
- lastMatchInfoOverride = null;
- if (this.global) this.lastIndex = lastMatchInfo[CAPTURE1];
- cache.answer = true;
- return true;
+ if (global) {
+ if (result) {
+ this.lastIndex = lastMatchInfo[CAPTURE1];
+ return true;
+ } else {
+ this.lastIndex = 0;
+ if (lastIndex !== 0) return false;
+ }
+ }
+ cache.type = 'test';
+ cache.regExp = this;
+ cache.subject = s;
+ cache.answer = result;
+ return result;
}
@@ -345,12 +372,9 @@ function RegExpToString() {
// ecma_2/RegExp/properties-001.js.
var src = this.source ? this.source : '(?:)';
var result = '/' + src + '/';
- if (this.global)
- result += 'g';
- if (this.ignoreCase)
- result += 'i';
- if (this.multiline)
- result += 'm';
+ if (this.global) result += 'g';
+ if (this.ignoreCase) result += 'i';
+ if (this.multiline) result += 'm';
return result;
}
« no previous file with comments | « no previous file | src/string.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698