Chromium Code Reviews| Index: src/regexp-delay.js |
| =================================================================== |
| --- src/regexp-delay.js (revision 1489) |
| +++ src/regexp-delay.js (working copy) |
| @@ -1,4 +1,4 @@ |
| -// Copyright 2006-2008 the V8 project authors. All rights reserved. |
| +// Copyright 2006-2009 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: |
| @@ -52,7 +52,7 @@ |
| var multiline = false; |
| for (var i = 0; i < flags.length; i++) { |
| - var c = flags.charAt(i); |
| + var c = StringCharAt.call(flags, i); |
| switch (c) { |
| case 'g': |
| // Allow duplicate flags to be consistent with JSC and others. |
| @@ -117,15 +117,15 @@ |
| // Deprecated RegExp.prototype.compile method. We behave like the constructor |
| // were called again. In SpiderMonkey, this method returns the regexp object. |
| -// In KJS, it returns undefined. For compatibility with KJS, we match their |
| +// In JSC, it returns undefined. For compatibility with JSC, we match their |
| // behavior. |
| function CompileRegExp(pattern, flags) { |
| - // Both KJS and SpiderMonkey treat a missing pattern argument as the |
| + // Both JSC and SpiderMonkey treat a missing pattern argument as the |
| // empty subject string, and an actual undefined value passed as the |
| - // patter as the string 'undefined'. Note that KJS is inconsistent |
| + // pattern as the string 'undefined'. Note that JSC is inconsistent |
| // here, treating undefined values differently in |
| // RegExp.prototype.compile and in the constructor, where they are |
| - // the empty string. For compatibility with KJS, we match their |
| + // the empty string. For compatibility with JSC, we match their |
| // behavior. |
| if (IS_UNDEFINED(pattern) && %_ArgumentsLength() != 0) { |
| DoConstructRegExp(this, 'undefined', flags, false); |
| @@ -135,27 +135,14 @@ |
| } |
| -// DoRegExpExec and DoRegExpExecGlobal are wrappers around the runtime |
| -// %RegExp and %RegExpGlobal functions that ensure that the static |
| -// properties of the RegExp constructor are set. |
| function DoRegExpExec(regexp, string, index) { |
|
Lasse Reichstein
2009/03/11 13:49:41
Is this used any more?
Lasse Reichstein
2009/03/11 13:51:53
Yes it is, just not in this file. It's called from
|
| - var matchIndices = %RegExpExec(regexp, string, index); |
| - if (!IS_NULL(matchIndices)) { |
| - regExpCaptures = matchIndices; |
| - regExpSubject = regExpInput = string; |
| - } |
| - return matchIndices; |
| + return %RegExpExec(regexp, string, index, lastMatchInfo); |
| } |
| function DoRegExpExecGlobal(regexp, string) { |
| - // Here, matchIndices is an array of arrays of substring indices. |
| - var matchIndices = %RegExpExecGlobal(regexp, string); |
| - if (matchIndices.length != 0) { |
| - regExpCaptures = matchIndices[matchIndices.length - 1]; |
| - regExpSubject = regExpInput = string; |
| - } |
| - return matchIndices; |
| + // Returns an array of arrays of substring indices. |
| + return %RegExpExecGlobal(regexp, string, lastMatchInfo); |
| } |
| @@ -164,6 +151,7 @@ |
| throw MakeTypeError('method_called_on_incompatible', ['RegExp.prototype.exec', this]); |
| } |
| if (%_ArgumentsLength() == 0) { |
| + var regExpInput = LAST_INPUT(lastMatchInfo); |
| if (IS_UNDEFINED(regExpInput)) { |
| throw MakeError('no_input_to_regexp', [this]); |
| } |
| @@ -180,23 +168,21 @@ |
| } |
| %_Log('regexp', 'regexp-exec,%0r,%1S,%2i', [this, s, lastIndex]); |
| - // matchIndices is an array of integers with length of captures*2, |
| - // each pair of integers specified the start and the end of index |
| - // in the string. |
| - var matchIndices = DoRegExpExec(this, s, i); |
| + // 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 matchIndices; // no match |
| } |
| - var numResults = matchIndices.length >> 1; |
| + var numResults = NUMBER_OF_CAPTURES(lastMatchInfo) >> 1; |
| var result = new $Array(numResults); |
| for (var i = 0; i < numResults; i++) { |
| - var matchStart = matchIndices[2*i]; |
| - var matchEnd = matchIndices[2*i + 1]; |
| + var matchStart = lastMatchInfo[CAPTURE(i << 1)]; |
| + var matchEnd = lastMatchInfo[CAPTURE((i << 1) + 1)]; |
| if (matchStart != -1 && matchEnd != -1) { |
| - result[i] = s.slice(matchStart, matchEnd); |
| + 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. |
| @@ -205,16 +191,49 @@ |
| } |
| if (this.global) |
| - this.lastIndex = matchIndices[1]; |
| - result.index = matchIndices[0]; |
| + this.lastIndex = lastMatchInfo[CAPTURE1]; |
| + result.index = lastMatchInfo[CAPTURE0]; |
| result.input = s; |
| return result; |
| } |
| +// 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 even if the method is |
| +// called on a non-RegExp object. However, it probably means the original |
| +// value of String.prototype.exec, which is what everybody else implements. |
| function RegExpTest(string) { |
| - var result = (%_ArgumentsLength() == 0) ? this.exec() : this.exec(string); |
| - return result != null; |
| + if (!IS_REGEXP(this)) { |
| + throw MakeTypeError('method_called_on_incompatible', ['RegExp.prototype.test', this]); |
|
Mads Ager (chromium)
2009/03/11 13:49:17
Long line, break it?
Erik Corry
2009/03/11 14:01:06
Yes. It seems none of our linters catch >80 chara
|
| + } |
| + if (%_ArgumentsLength() == 0) { |
| + var regExpInput = LAST_INPUT(lastMatchInfo); |
| + if (IS_UNDEFINED(regExpInput)) { |
| + throw MakeError('no_input_to_regexp', [this]); |
| + } |
| + string = regExpInput; |
| + } |
| + var s = ToString(string); |
| + var length = s.length; |
| + var lastIndex = this.lastIndex; |
| + var i = this.global ? TO_INTEGER(lastIndex) : 0; |
| + |
| + 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); |
| + |
| + if (matchIndices == null) { |
| + if (this.global) this.lastIndex = 0; |
| + return false; |
| + } |
| + |
| + if (this.global) this.lastIndex = lastMatchInfo[CAPTURE1]; |
| + return true; |
| } |
| @@ -239,56 +258,72 @@ |
| // on the captures array of the last successful match and the subject string |
| // of the last successful match. |
| function RegExpGetLastMatch() { |
| - return regExpSubject.slice(regExpCaptures[0], regExpCaptures[1]); |
| + var regExpSubject = LAST_SUBJECT(lastMatchInfo); |
| + return SubString(regExpSubject, |
| + lastMatchInfo[CAPTURE0], |
| + lastMatchInfo[CAPTURE1]); |
| } |
| function RegExpGetLastParen() { |
| - var length = regExpCaptures.length; |
| - if (length <= 2) return ''; // There were no captures. |
| + var length = NUMBER_OF_CAPTURES(lastMatchInfo); |
| + if (length <= 2) return ''; // There were no captures. |
| // We match the SpiderMonkey behavior: return the substring defined by the |
| // last pair (after the first pair) of elements of the capture array even if |
| // it is empty. |
| - return regExpSubject.slice(regExpCaptures[length - 2], |
| - regExpCaptures[length - 1]); |
| + var regExpSubject = LAST_SUBJECT(lastMatchInfo); |
| + var start = lastMatchInfo[CAPTURE(length - 2)]; |
| + var end = lastMatchInfo[CAPTURE(length - 1)]; |
| + if (start != -1 && end != -1) { |
| + return SubString(regExpSubject, start, end); |
| + } |
| + return ""; |
| } |
| function RegExpGetLeftContext() { |
| - return regExpSubject.slice(0, regExpCaptures[0]); |
| + return SubString(LAST_SUBJECT(lastMatchInfo), |
| + 0, |
| + lastMatchInfo[CAPTURE0]); |
| } |
| function RegExpGetRightContext() { |
| - return regExpSubject.slice(regExpCaptures[1], regExpSubject.length); |
| + var subject = LAST_SUBJECT(lastMatchInfo); |
| + return SubString(subject, |
| + lastMatchInfo[CAPTURE1], |
| + subject.length); |
| } |
| // The properties $1..$9 are the first nine capturing substrings of the last |
| // successful match, or ''. The function RegExpMakeCaptureGetter will be |
| -// called with an index greater than or equal to 1 but it actually works for |
| -// any non-negative index. |
| +// called with indeces from 1 to 9. |
| function RegExpMakeCaptureGetter(n) { |
| return function() { |
| var index = n * 2; |
| - if (index >= regExpCaptures.length) return ''; |
| - var matchStart = regExpCaptures[index]; |
| - var matchEnd = regExpCaptures[index + 1]; |
| + if (index >= NUMBER_OF_CAPTURES(lastMatchInfo)) return ''; |
| + var matchStart = lastMatchInfo[CAPTURE(index)]; |
| + var matchEnd = lastMatchInfo[CAPTURE(index + 1)]; |
| if (matchStart == -1 || matchEnd == -1) return ''; |
| - return regExpSubject.slice(matchStart, matchEnd); |
| + return SubString(LAST_SUBJECT(lastMatchInfo), matchStart, matchEnd); |
| }; |
| } |
| -// Properties of the builtins object for recording the result of the last |
| -// regexp match. The property regExpCaptures is the matchIndices array of the |
| -// last successful regexp match (an array of start/end index pairs for the |
| -// match and all the captured substrings), the invariant is that there is at |
| -// least two elements. The property regExpSubject is the subject string for |
| -// the last successful match. |
| -var regExpCaptures = [0, 0]; |
| -var regExpSubject = ''; |
| -var regExpInput; |
| +// Property of the builtins object for recording the result of the last |
| +// regexp match. The property lastMatchInfo includes the matchIndices |
| +// array of the last successful regexp match (an array of start/end index |
| +// pairs for the match and all the captured substrings), the invariant is |
| +// that there are at least two capture indeces. The array also contains |
| +// the subject string for the last successful match. |
| +var lastMatchInfo = [ |
| + 2, // REGEXP_NUMBER_OF_CAPTURES |
| + 0, // REGEXP_FIRST_CAPTURE + 0 |
| + 0, // REGEXP_FIRST_CAPTURE + 1 |
| + "", // Last subject. |
| + void 0, // Last input - settable with RegExpSetInput. |
| +]; |
| // ------------------------------------------------------------------- |
| @@ -312,9 +347,13 @@ |
| // 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); |
| return IS_UNDEFINED(regExpInput) ? "" : regExpInput; |
| } |
| - function RegExpSetInput(string) { regExpInput = ToString(string); } |
| + function RegExpSetInput(string) { |
| + lastMatchInfo[lastMatchInfo[REGEXP_NUMBER_OF_CAPTURES] + 2] = |
| + ToString(string); |
| + }; |
| %DefineAccessor($RegExp, 'input', GETTER, RegExpGetInput, DONT_DELETE); |
| %DefineAccessor($RegExp, 'input', SETTER, RegExpSetInput, DONT_DELETE); |