| Index: src/js/regexp.js
|
| diff --git a/src/js/regexp.js b/src/js/regexp.js
|
| index b9d0f50b244345c52e73e1cb33fdbe09d7c3c6f6..584d4cdf41bdbe58c76cb3bd9ee22249f34f494a 100644
|
| --- a/src/js/regexp.js
|
| +++ b/src/js/regexp.js
|
| @@ -162,6 +162,46 @@
|
| %FunctionRemovePrototype(RegExpSubclassExecJS);
|
|
|
|
|
| +// Legacy implementation of RegExp.prototype.exec
|
| +function RegExpExecJS(string) {
|
| + if (!IS_REGEXP(this)) {
|
| + throw %make_type_error(kIncompatibleMethodReceiver,
|
| + 'RegExp.prototype.exec', this);
|
| + }
|
| +
|
| + string = TO_STRING(string);
|
| + var lastIndex = this.lastIndex;
|
| +
|
| + // Conversion is required by the ES2015 specification (RegExpBuiltinExec
|
| + // algorithm, step 4) even if the value is discarded for non-global RegExps.
|
| + var i = TO_LENGTH(lastIndex);
|
| +
|
| + var updateLastIndex = REGEXP_GLOBAL(this) || REGEXP_STICKY(this);
|
| + if (updateLastIndex) {
|
| + if (i < 0 || i > string.length) {
|
| + this.lastIndex = 0;
|
| + return null;
|
| + }
|
| + } else {
|
| + i = 0;
|
| + }
|
| +
|
| + // matchIndices is either null or the RegExpLastMatchInfo array.
|
| + var matchIndices = %_RegExpExec(this, string, i, RegExpLastMatchInfo);
|
| +
|
| + if (IS_NULL(matchIndices)) {
|
| + this.lastIndex = 0;
|
| + return null;
|
| + }
|
| +
|
| + // Successful match.
|
| + if (updateLastIndex) {
|
| + this.lastIndex = RegExpLastMatchInfo[CAPTURE1];
|
| + }
|
| + RETURN_NEW_RESULT_FROM_MATCH_INFO(matchIndices, string);
|
| +}
|
| +
|
| +
|
| // ES#sec-regexpexec Runtime Semantics: RegExpExec ( R, S )
|
| // Also takes an optional exec method in case our caller
|
| // has already fetched exec.
|
| @@ -176,9 +216,68 @@
|
| }
|
| return result;
|
| }
|
| - return %_Call(RegExpSubclassExecJS, regexp, string);
|
| + return %_Call(RegExpExecJS, regexp, string);
|
| }
|
| %SetForceInlineFlag(RegExpSubclassExec);
|
| +
|
| +
|
| +// One-element cache for the simplified test regexp.
|
| +var regexp_key;
|
| +var regexp_val;
|
| +
|
| +// Legacy implementation of RegExp.prototype.test
|
| +// 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
|
| +// else implements.
|
| +function RegExpTest(string) {
|
| + if (!IS_REGEXP(this)) {
|
| + throw %make_type_error(kIncompatibleMethodReceiver,
|
| + 'RegExp.prototype.test', this);
|
| + }
|
| + string = TO_STRING(string);
|
| +
|
| + var lastIndex = this.lastIndex;
|
| +
|
| + // Conversion is required by the ES2015 specification (RegExpBuiltinExec
|
| + // algorithm, step 4) even if the value is discarded for non-global RegExps.
|
| + var i = TO_LENGTH(lastIndex);
|
| +
|
| + if (REGEXP_GLOBAL(this) || REGEXP_STICKY(this)) {
|
| + if (i < 0 || i > string.length) {
|
| + this.lastIndex = 0;
|
| + return false;
|
| + }
|
| + // matchIndices is either null or the RegExpLastMatchInfo array.
|
| + var matchIndices = %_RegExpExec(this, string, i, RegExpLastMatchInfo);
|
| + if (IS_NULL(matchIndices)) {
|
| + this.lastIndex = 0;
|
| + return false;
|
| + }
|
| + this.lastIndex = RegExpLastMatchInfo[CAPTURE1];
|
| + return true;
|
| + } else {
|
| + // Non-global, non-sticky regexp.
|
| + // Remove irrelevant preceeding '.*' in a test regexp. The expression
|
| + // checks whether this.source starts with '.*' and that the third char is
|
| + // not a '?'. But see https://code.google.com/p/v8/issues/detail?id=3560
|
| + var regexp = this;
|
| + var source = REGEXP_SOURCE(regexp);
|
| + if (source.length >= 3 &&
|
| + %_StringCharCodeAt(source, 0) == 46 && // '.'
|
| + %_StringCharCodeAt(source, 1) == 42 && // '*'
|
| + %_StringCharCodeAt(source, 2) != 63) { // '?'
|
| + regexp = TrimRegExp(regexp);
|
| + }
|
| + // matchIndices is either null or the RegExpLastMatchInfo array.
|
| + var matchIndices = %_RegExpExec(regexp, string, 0, RegExpLastMatchInfo);
|
| + if (IS_NULL(matchIndices)) {
|
| + this.lastIndex = 0;
|
| + return false;
|
| + }
|
| + return true;
|
| + }
|
| +}
|
|
|
|
|
| // ES#sec-regexp.prototype.test RegExp.prototype.test ( S )
|
| @@ -192,6 +291,18 @@
|
| return !IS_NULL(match);
|
| }
|
| %FunctionRemovePrototype(RegExpSubclassTest);
|
| +
|
| +function TrimRegExp(regexp) {
|
| + if (regexp_key !== regexp) {
|
| + regexp_key = regexp;
|
| + regexp_val =
|
| + new GlobalRegExp(
|
| + %_SubString(REGEXP_SOURCE(regexp), 2, REGEXP_SOURCE(regexp).length),
|
| + (REGEXP_IGNORE_CASE(regexp) ? REGEXP_MULTILINE(regexp) ? "im" : "i"
|
| + : REGEXP_MULTILINE(regexp) ? "m" : ""));
|
| + }
|
| + return regexp_val;
|
| +}
|
|
|
|
|
| function AtSurrogatePair(subject, index) {
|
| @@ -816,7 +927,10 @@
|
| to.InternalRegExpMatch = InternalRegExpMatch;
|
| to.InternalRegExpReplace = InternalRegExpReplace;
|
| to.IsRegExp = IsRegExp;
|
| + to.RegExpExec = DoRegExpExec;
|
| to.RegExpInitialize = RegExpInitialize;
|
| + to.RegExpLastMatchInfo = RegExpLastMatchInfo;
|
| + to.RegExpTest = RegExpTest;
|
| });
|
|
|
| })
|
|
|