| 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);
|
|
|