Index: src/regexp-delay.js |
diff --git a/src/regexp-delay.js b/src/regexp-delay.js |
index 7bec455d37fedd46702d36f02db20ed6d6396d71..606ca17ffd9e6d5d4739667b4dc4f09029d19f6e 100644 |
--- a/src/regexp-delay.js |
+++ b/src/regexp-delay.js |
@@ -140,22 +140,43 @@ function DoRegExpExec(regexp, string, index) { |
} |
+var regExpExecCacheResult; |
+var regExpExecCacheInput; |
+var regExpExecCacheId; |
+var regExpExecCacheIndex; |
+ |
function RegExpExec(string) { |
if (!IS_REGEXP(this)) { |
throw MakeTypeError('method_called_on_incompatible', |
['RegExp.prototype.exec', this]); |
} |
if (%_ArgumentsLength() == 0) { |
- var regExpInput = LAST_INPUT(lastMatchInfo); |
- if (IS_UNDEFINED(regExpInput)) { |
+ string = LAST_INPUT(lastMatchInfo); |
+ if (IS_UNDEFINED(string)) { |
throw MakeError('no_input_to_regexp', [this]); |
} |
- string = regExpInput; |
} |
- var s = ToString(string); |
- var length = s.length; |
+ |
var lastIndex = this.lastIndex; |
+ if (%_IsIdentical(regExpExecCacheInput, string) && |
+ %_IsIdentical(regExpExecCacheId, %_RegExpId(this)) && |
+ %_IsIdentical(regExpExecCacheIndex, lastIndex)) { |
+ var cachedResult = regExpExecCacheResult; // Cached result. |
+ if (cachedResult) { |
+ // If non-null, don't return the same array as last time. |
+ var newArray = ArraySlice.call(cachedResult, 0, cachedResult.length); |
+ newArray.input = cachedResult.input; |
+ newArray.index = cachedResult.index; |
+ cachedResult = newArray; |
+ } |
+ %_Log("regexp", "regexp-exec-cache-hit", []); |
+ return cachedResult; |
+ } |
+ %_Log("regexp", "regexp-exec-cache-miss", []); |
+ |
var i = this.global ? TO_INTEGER(lastIndex) : 0; |
+ var s = ToString(string); |
+ var length = s.length; |
if (i < 0 || i > s.length) { |
this.lastIndex = 0; |
@@ -166,33 +187,41 @@ function RegExpExec(string) { |
// matchIndices is either null or the lastMatchInfo array. |
var matchIndices = %_RegExpExec(this, s, i, lastMatchInfo); |
+ var result; |
if (matchIndices == null) { |
if (this.global) this.lastIndex = 0; |
- return matchIndices; // no match |
- } |
- |
- var numResults = NUMBER_OF_CAPTURES(lastMatchInfo) >> 1; |
- var result = new $Array(numResults); |
- for (var i = 0; i < numResults; i++) { |
- var matchStart = lastMatchInfo[CAPTURE(i << 1)]; |
- var matchEnd = lastMatchInfo[CAPTURE((i << 1) + 1)]; |
- if (matchStart != -1 && matchEnd != -1) { |
- result[i] = SubString(s, matchStart, matchEnd); |
- } else { |
- // Make sure the element is present. Avoid reading the undefined |
- // property from the global object since this may change. |
- result[i] = void 0; |
+ result = matchIndices; // no match |
+ } else { |
+ var numResults = NUMBER_OF_CAPTURES(lastMatchInfo) >> 1; |
+ result = new $Array(numResults); |
+ for (var i = 0; i < numResults; i++) { |
+ var matchStart = lastMatchInfo[CAPTURE(i << 1)]; |
+ var matchEnd = lastMatchInfo[CAPTURE((i << 1) + 1)]; |
+ if (matchStart != -1 && matchEnd != -1) { |
+ result[i] = SubString(s, matchStart, matchEnd); |
+ } else { |
+ // Make sure the element is present. Avoid reading the undefined |
+ // property from the global object since this may change. |
+ result[i] = void 0; |
+ } |
} |
+ if (this.global) this.lastIndex = lastMatchInfo[CAPTURE1]; |
+ result.index = lastMatchInfo[CAPTURE0]; |
+ result.input = s; |
} |
- |
- if (this.global) |
- this.lastIndex = lastMatchInfo[CAPTURE1]; |
- result.index = lastMatchInfo[CAPTURE0]; |
- result.input = s; |
+ regExpExecCacheResult = result; |
+ regExpExecCacheInput = string; |
+ regExpExecCacheId = %_RegExpId(this); // May have changed. |
+ regExpExecCacheIndex = lastIndex; |
return result; |
} |
+var regExpTestCacheResult; |
+var regExpTestCacheId; |
+var regExpTestCacheIndex; |
+var regExpTestCacheInput; |
+ |
// Section 15.10.6.3 doesn't actually make sense, but the intention seems to be |
// that test is defined in terms of String.prototype.exec. However, it probably |
// means the original value of String.prototype.exec, which is what everybody |
@@ -203,33 +232,47 @@ function RegExpTest(string) { |
['RegExp.prototype.test', this]); |
} |
if (%_ArgumentsLength() == 0) { |
- var regExpInput = LAST_INPUT(lastMatchInfo); |
- if (IS_UNDEFINED(regExpInput)) { |
+ string = LAST_INPUT(lastMatchInfo); |
+ if (IS_UNDEFINED(string)) { |
throw MakeError('no_input_to_regexp', [this]); |
} |
- string = regExpInput; |
} |
- var s = ToString(string); |
- var length = s.length; |
+ |
var lastIndex = this.lastIndex; |
+ if (%_IsIdentical(regExpTestCacheInput, string) && |
+ %_IsIdentical(regExpTestCacheId, %_RegExpId(this)) && |
+ %_IsIdentical(regExpTestCacheIndex, lastIndex)) { |
+ %_Log("regexp", "regexp-test-cache-hit", []); |
+ return regExpTestCacheResult; // Always true or false. |
+ } |
+ %_Log("regexp", "regexp-test-cache-miss", []); |
+ |
var i = this.global ? TO_INTEGER(lastIndex) : 0; |
+ var s = ToString(string); |
+ var length = s.length; |
+ var result; |
if (i < 0 || i > s.length) { |
this.lastIndex = 0; |
- 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); |
+ result = false; |
+ } else { |
+ %_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; |
- return false; |
+ if (matchIndices == null) { |
+ if (this.global) this.lastIndex = 0; |
+ result = false; |
+ } else { |
+ if (this.global) this.lastIndex = lastMatchInfo[CAPTURE1]; |
+ result = true; |
+ } |
} |
- |
- if (this.global) this.lastIndex = lastMatchInfo[CAPTURE1]; |
- return true; |
+ regExpTestCacheResult = result; |
+ regExpTestCacheInput = string; |
+ regExpTestCacheId = %_RegExpId(this); |
+ regExpTestCacheIndex = lastIndex; |
+ return result; |
} |
@@ -340,7 +383,7 @@ function SetupRegExp() { |
%FunctionSetLength($RegExp.prototype.compile, 1); |
// The properties input, $input, and $_ are aliases for each other. When this |
- // value is set the value it is set to is coerced to a string. |
+ // value is set the value it is set to is coerced to a string. |
// Getter and setter for the input. |
function RegExpGetInput() { |
var regExpInput = LAST_INPUT(lastMatchInfo); |