import from UXP: Bug 1317397: Only set lastIndex for global or sticky RegExps in RegExpBuiltinExec per ES2017 (78ce3bf8)

This commit is contained in:
2022-03-11 15:48:29 +08:00
parent 80661feaa0
commit d46a6e25e0
13 changed files with 426 additions and 64 deletions
+64 -30
View File
@@ -614,7 +614,8 @@ function RegExpGlobalReplaceShortOpt(rx, S, lengthS, replaceValue, fullUnicode)
#undef SUBSTITUTION
#undef FUNC_NAME
// ES 2017 draft 6859bb9ccaea9c6ede81d71e5320e3833b92cb3e 21.2.5.9.
// ES2017 draft rev 6390c2f1b34b309895d31d8c0512eac8660a0210
// 21.2.5.9 RegExp.prototype [ @@search ] ( string )
function RegExpSearch(string) {
// Step 1.
var rx = this;
@@ -626,41 +627,69 @@ function RegExpSearch(string) {
// Step 3.
var S = ToString(string);
if (IsRegExpMethodOptimizable(rx) && S.length < 0x7fff) {
// Step 6.
var result = RegExpSearcher(rx, S, 0);
// Step 8.
if (result === -1)
return -1;
// Step 9.
return result & 0x7fff;
}
return RegExpSearchSlowPath(rx, S);
}
// ES 2017 draft 6859bb9ccaea9c6ede81d71e5320e3833b92cb3e 21.2.5.9
// steps 4-9.
function RegExpSearchSlowPath(rx, S) {
// Step 4.
var previousLastIndex = rx.lastIndex;
// Step 5.
rx.lastIndex = 0;
var lastIndexIsZero = SameValue(previousLastIndex, 0);
if (!lastIndexIsZero)
rx.lastIndex = 0;
if (IsRegExpMethodOptimizable(rx) && S.length < 0x7fff) {
// Step 6.
var result = RegExpSearcher(rx, S, 0);
// We need to consider two cases:
//
// 1. Neither global nor sticky is set:
// RegExpBuiltinExec doesn't modify lastIndex for local RegExps, that
// means |SameValue(rx.lastIndex, 0)| is true after calling exec. The
// comparison in steps 7-8 |SameValue(rx.lastIndex, previousLastIndex)|
// is therefore equal to the already computed |lastIndexIsZero| value.
//
// 2. Global or sticky flag is set.
// RegExpBuiltinExec will always update lastIndex and we need to
// restore the property to its original value.
// Steps 7-8.
if (!lastIndexIsZero) {
rx.lastIndex = previousLastIndex;
} else {
var flags = UnsafeGetInt32FromReservedSlot(rx, REGEXP_FLAGS_SLOT);
if (flags & (REGEXP_GLOBAL_FLAG | REGEXP_STICKY_FLAG))
rx.lastIndex = previousLastIndex;
}
// Step 9.
if (result === -1)
return -1;
// Step 10.
return result & 0x7fff;
}
return RegExpSearchSlowPath(rx, S, previousLastIndex);
}
// ES2017 draft rev 6390c2f1b34b309895d31d8c0512eac8660a0210
// 21.2.5.9 RegExp.prototype [ @@search ] ( string )
// Steps 6-10.
function RegExpSearchSlowPath(rx, S, previousLastIndex) {
// Step 6.
var result = RegExpExec(rx, S, false);
// Step 7.
rx.lastIndex = previousLastIndex;
var currentLastIndex = rx.lastIndex;
// Step 8.
if (!SameValue(currentLastIndex, previousLastIndex))
rx.lastIndex = previousLastIndex;
// Step 9.
if (result === null)
return -1;
// Step 9.
// Step 10.
return result.index;
}
@@ -909,15 +938,16 @@ function RegExpExec(R, S, forTest) {
return forTest ? result !== null : result;
}
// ES 2017 draft rev 6a13789aa9e7c6de4e96b7d3e24d9e6eba6584ad 21.2.5.2.2.
// ES2017 draft rev 6390c2f1b34b309895d31d8c0512eac8660a0210
// 21.2.5.2.2 Runtime Semantics: RegExpBuiltinExec ( R, S )
function RegExpBuiltinExec(R, S, forTest) {
// ES6 21.2.5.2.1 step 6.
// 21.2.5.2.1 Runtime Semantics: RegExpExec, step 5.
// This check is here for RegExpTest. RegExp_prototype_Exec does same
// thing already.
if (!IsRegExpObject(R))
return UnwrapAndCallRegExpBuiltinExec(R, S, forTest);
// Steps 1-2 (skipped).
// Steps 1-3 (skipped).
// Step 4.
var lastIndex = ToLength(R.lastIndex);
@@ -932,9 +962,11 @@ function RegExpBuiltinExec(R, S, forTest) {
if (!globalOrSticky) {
lastIndex = 0;
} else {
// Step 12.a.
if (lastIndex > S.length) {
// Steps 12.a.i-ii, 12.c.i.1-2.
R.lastIndex = 0;
// Steps 12.a.i-ii.
if (globalOrSticky)
R.lastIndex = 0;
return forTest ? false : null;
}
}
@@ -944,7 +976,8 @@ function RegExpBuiltinExec(R, S, forTest) {
var endIndex = RegExpTester(R, S, lastIndex);
if (endIndex == -1) {
// Steps 12.a.i-ii, 12.c.i.1-2.
R.lastIndex = 0;
if (globalOrSticky)
R.lastIndex = 0;
return false;
}
@@ -958,8 +991,9 @@ function RegExpBuiltinExec(R, S, forTest) {
// Steps 3, 9-25, except 12.a.i-ii, 12.c.i.1-2, 15.
var result = RegExpMatcher(R, S, lastIndex);
if (result === null) {
// Steps 12.a.i-ii, 12.c.i.1-2.
R.lastIndex = 0;
// Steps 12.a.i, 12.c.i.
if (globalOrSticky)
R.lastIndex = 0;
} else {
// Step 15.
if (globalOrSticky)
+7 -5
View File
@@ -11,7 +11,7 @@
// * FUNCTIONAL -- replaceValue is a function
// * neither of above -- replaceValue is a string without "$"
// ES 2017 draft 03bfda119d060aca4099d2b77cf43f6d4f11cfa2 21.2.5.8
// ES 2017 draft 6390c2f1b34b309895d31d8c0512eac8660a0210 21.2.5.8
// steps 11.a-16.
// Optimized path for @@replace with the following conditions:
// * global flag is false
@@ -36,8 +36,10 @@ function FUNC_NAME(rx, S, lengthS, replaceValue
if (globalOrSticky) {
// 21.2.5.2.2 RegExpBuiltinExec, step 12.a.
if (lastIndex > lengthS) {
// FIXME: Implement changes for bug 1317397.
rx.lastIndex = 0;
if (globalOrSticky)
rx.lastIndex = 0;
// Steps 12-16.
return S;
}
} else {
@@ -51,8 +53,8 @@ function FUNC_NAME(rx, S, lengthS, replaceValue
// Step 11.b.
if (result === null) {
// 21.2.5.2.2 RegExpBuiltinExec, steps 12.a.i, 12.c.i.
// FIXME: Implement changes for bug 1317397.
rx.lastIndex = 0;
if (globalOrSticky)
rx.lastIndex = 0;
// Steps 12-16.
return S;
+11 -1
View File
@@ -106,7 +106,17 @@ function ToLength(v) {
return std_Math_min(v, 0x1fffffffffffff);
}
/* Spec: ECMAScript Draft, 6th edition Oct 14, 2014, 7.2.4 */
// ES2017 draft rev aebf014403a3e641fb1622aec47c40f051943527
// 7.2.9 SameValue ( x, y )
function SameValue(x, y) {
if (x === y) {
return (x !== 0) || (1 / x === 1 / y);
}
return (x !== x && y !== y);
}
// ES2017 draft rev aebf014403a3e641fb1622aec47c40f051943527
// 7.2.10 SameValueZero ( x, y )
function SameValueZero(x, y) {
return x === y || (x !== x && y !== y);
}
@@ -7,7 +7,7 @@ function test() {
pattern.lastIndex = 3;
var result = pattern.exec(string);
assertEq(result, null);
assertEq(pattern.lastIndex, 0);
assertEq(pattern.lastIndex, 3);
}
for (let i = 0; i < 10; i++) {
@@ -18,7 +18,7 @@ function test2() {
pattern.lastIndex = 3;
var result = pattern.test(string);
assertEq(result, false);
assertEq(pattern.lastIndex, 0);
assertEq(pattern.lastIndex, 3);
}
for (let i = 0; i < 10; i++) {
+80
View File
@@ -0,0 +1,80 @@
// RegExp.prototype.exec: Test lastIndex changes for ES2017.
// Test various combinations of:
// - Pattern matches or doesn't match
// - Global and/or sticky flag is set.
// - lastIndex exceeds the input string length
// - lastIndex is +-0
const testCases = [
{ regExp: /a/, lastIndex: 0, input: "a", result: 0 },
{ regExp: /a/g, lastIndex: 0, input: "a", result: 1 },
{ regExp: /a/y, lastIndex: 0, input: "a", result: 1 },
{ regExp: /a/, lastIndex: 0, input: "b", result: 0 },
{ regExp: /a/g, lastIndex: 0, input: "b", result: 0 },
{ regExp: /a/y, lastIndex: 0, input: "b", result: 0 },
{ regExp: /a/, lastIndex: -0, input: "a", result: -0 },
{ regExp: /a/g, lastIndex: -0, input: "a", result: 1 },
{ regExp: /a/y, lastIndex: -0, input: "a", result: 1 },
{ regExp: /a/, lastIndex: -0, input: "b", result: -0 },
{ regExp: /a/g, lastIndex: -0, input: "b", result: 0 },
{ regExp: /a/y, lastIndex: -0, input: "b", result: 0 },
{ regExp: /a/, lastIndex: -1, input: "a", result: -1 },
{ regExp: /a/g, lastIndex: -1, input: "a", result: 1 },
{ regExp: /a/y, lastIndex: -1, input: "a", result: 1 },
{ regExp: /a/, lastIndex: -1, input: "b", result: -1 },
{ regExp: /a/g, lastIndex: -1, input: "b", result: 0 },
{ regExp: /a/y, lastIndex: -1, input: "b", result: 0 },
{ regExp: /a/, lastIndex: 100, input: "a", result: 100 },
{ regExp: /a/g, lastIndex: 100, input: "a", result: 0 },
{ regExp: /a/y, lastIndex: 100, input: "a", result: 0 },
];
// Basic test.
for (let {regExp, lastIndex, input, result} of testCases) {
let re = new RegExp(regExp);
re.lastIndex = lastIndex;
re.exec(input);
assertEq(re.lastIndex, result);
}
// Test when lastIndex is non-writable.
for (let {regExp, lastIndex, input} of testCases) {
let re = new RegExp(regExp);
Object.defineProperty(re, "lastIndex", { value: lastIndex, writable: false });
if (re.global || re.sticky) {
assertThrowsInstanceOf(() => re.exec(input), TypeError);
} else {
re.exec(input);
}
assertEq(re.lastIndex, lastIndex);
}
// Test when lastIndex is changed to non-writable as a side-effect.
for (let {regExp, lastIndex, input} of testCases) {
let re = new RegExp(regExp);
let called = false;
re.lastIndex = {
valueOf() {
assertEq(called, false);
called = true;
Object.defineProperty(re, "lastIndex", { value: 9000, writable: false });
return lastIndex;
}
};
if (re.global || re.sticky) {
assertThrowsInstanceOf(() => re.exec(input), TypeError);
} else {
re.exec(input);
}
assertEq(re.lastIndex, 9000);
assertEq(called, true);
}
if (typeof reportCompare === "function")
reportCompare(true, true);
@@ -0,0 +1,122 @@
// RegExp.prototype[Symbol.match, Symbol.replace]: Test lastIndex changes for ES2017.
// RegExp-like class to test the RegExp method slow paths.
class DuckRegExp extends RegExp {
constructor(pattern, flags) {
return Object.create(DuckRegExp.prototype, {
regExp: {
value: new RegExp(pattern, flags)
},
lastIndex: {
value: 0, writable: true, enumerable: false, configurable: false
}
});
}
exec(...args) {
this.regExp.lastIndex = this.lastIndex;
try {
return this.regExp.exec(...args);
} finally {
if (this.global || this.sticky)
this.lastIndex = this.regExp.lastIndex;
}
}
get source() { return this.regExp.source; }
get global() { return this.regExp.global; }
get ignoreCase() { return this.regExp.ignoreCase; }
get multiline() { return this.regExp.multiline; }
get sticky() { return this.regExp.sticky; }
get unicode() { return this.regExp.unicode; }
}
// Test various combinations of:
// - Pattern matches or doesn't match
// - Global and/or sticky flag is set.
// - lastIndex exceeds the input string length
// - lastIndex is +-0
const testCases = [
{ regExp: /a/, lastIndex: 0, input: "a", result: 0 },
{ regExp: /a/g, lastIndex: 0, input: "a", result: 0 },
{ regExp: /a/y, lastIndex: 0, input: "a", result: 1 },
{ regExp: /a/, lastIndex: 0, input: "b", result: 0 },
{ regExp: /a/g, lastIndex: 0, input: "b", result: 0 },
{ regExp: /a/y, lastIndex: 0, input: "b", result: 0 },
{ regExp: /a/, lastIndex: -0, input: "a", result: -0 },
{ regExp: /a/g, lastIndex: -0, input: "a", result: 0 },
{ regExp: /a/y, lastIndex: -0, input: "a", result: 1 },
{ regExp: /a/, lastIndex: -0, input: "b", result: -0 },
{ regExp: /a/g, lastIndex: -0, input: "b", result: 0 },
{ regExp: /a/y, lastIndex: -0, input: "b", result: 0 },
{ regExp: /a/, lastIndex: -1, input: "a", result: -1 },
{ regExp: /a/g, lastIndex: -1, input: "a", result: 0 },
{ regExp: /a/y, lastIndex: -1, input: "a", result: 1 },
{ regExp: /a/, lastIndex: -1, input: "b", result: -1 },
{ regExp: /a/g, lastIndex: -1, input: "b", result: 0 },
{ regExp: /a/y, lastIndex: -1, input: "b", result: 0 },
{ regExp: /a/, lastIndex: 100, input: "a", result: 100 },
{ regExp: /a/g, lastIndex: 100, input: "a", result: 0 },
{ regExp: /a/y, lastIndex: 100, input: "a", result: 0 },
];
for (let method of [RegExp.prototype[Symbol.match], RegExp.prototype[Symbol.replace]]) {
for (let Constructor of [RegExp, DuckRegExp]) {
// Basic test.
for (let {regExp, lastIndex, input, result} of testCases) {
let re = new Constructor(regExp);
re.lastIndex = lastIndex;
Reflect.apply(method, re, [input]);
assertEq(re.lastIndex, result);
}
// Test when lastIndex is non-writable.
for (let {regExp, lastIndex, input} of testCases) {
let re = new Constructor(regExp);
Object.defineProperty(re, "lastIndex", { value: lastIndex, writable: false });
if (re.global || re.sticky) {
assertThrowsInstanceOf(() => Reflect.apply(method, re, [input]), TypeError);
} else {
Reflect.apply(method, re, [input]);
}
assertEq(re.lastIndex, lastIndex);
}
// Test when lastIndex is changed to non-writable as a side-effect.
for (let {regExp, lastIndex, input, result} of testCases) {
let re = new Constructor(regExp);
let called = false;
re.lastIndex = {
valueOf() {
assertEq(called, false);
called = true;
Object.defineProperty(re, "lastIndex", { value: 9000, writable: false });
return lastIndex;
}
};
if (re.sticky) {
assertThrowsInstanceOf(() => Reflect.apply(method, re, [input]), TypeError);
assertEq(called, true);
assertEq(re.lastIndex, 9000);
} else if (re.global) {
Reflect.apply(method, re, [input]);
assertEq(called, false);
assertEq(re.lastIndex, result);
} else {
Reflect.apply(method, re, [input]);
assertEq(called, true);
assertEq(re.lastIndex, 9000);
}
}
}
}
if (typeof reportCompare === "function")
reportCompare(true, true);
+118
View File
@@ -0,0 +1,118 @@
// RegExp.prototype[Symbol.search]: Test lastIndex changes for ES2017.
// RegExp-like class to test the RegExp method slow paths.
class DuckRegExp extends RegExp {
constructor(pattern, flags) {
return Object.create(DuckRegExp.prototype, {
regExp: {
value: new RegExp(pattern, flags)
},
lastIndex: {
value: 0, writable: true, enumerable: false, configurable: false
}
});
}
exec(...args) {
this.regExp.lastIndex = this.lastIndex;
try {
return this.regExp.exec(...args);
} finally {
if (this.global || this.sticky)
this.lastIndex = this.regExp.lastIndex;
}
}
get source() { return this.regExp.source; }
get global() { return this.regExp.global; }
get ignoreCase() { return this.regExp.ignoreCase; }
get multiline() { return this.regExp.multiline; }
get sticky() { return this.regExp.sticky; }
get unicode() { return this.regExp.unicode; }
}
// Test various combinations of:
// - Pattern matches or doesn't match
// - Global and/or sticky flag is set.
// - lastIndex exceeds the input string length
// - lastIndex is +-0
const testCasesNotPositiveZero = [
{ regExp: /a/, lastIndex: -1, input: "a" },
{ regExp: /a/g, lastIndex: -1, input: "a" },
{ regExp: /a/y, lastIndex: -1, input: "a" },
{ regExp: /a/, lastIndex: 100, input: "a" },
{ regExp: /a/g, lastIndex: 100, input: "a" },
{ regExp: /a/y, lastIndex: 100, input: "a" },
{ regExp: /a/, lastIndex: -1, input: "b" },
{ regExp: /a/g, lastIndex: -1, input: "b" },
{ regExp: /a/y, lastIndex: -1, input: "b" },
{ regExp: /a/, lastIndex: -0, input: "a" },
{ regExp: /a/g, lastIndex: -0, input: "a" },
{ regExp: /a/y, lastIndex: -0, input: "a" },
{ regExp: /a/, lastIndex: -0, input: "b" },
{ regExp: /a/g, lastIndex: -0, input: "b" },
{ regExp: /a/y, lastIndex: -0, input: "b" },
];
const testCasesPositiveZero = [
{ regExp: /a/, lastIndex: 0, input: "a" },
{ regExp: /a/g, lastIndex: 0, input: "a" },
{ regExp: /a/y, lastIndex: 0, input: "a" },
{ regExp: /a/, lastIndex: 0, input: "b" },
{ regExp: /a/g, lastIndex: 0, input: "b" },
{ regExp: /a/y, lastIndex: 0, input: "b" },
];
const testCases = [...testCasesNotPositiveZero, ...testCasesPositiveZero];
for (let Constructor of [RegExp, DuckRegExp]) {
// Basic test.
for (let {regExp, lastIndex, input} of testCases) {
let re = new Constructor(regExp);
re.lastIndex = lastIndex;
re[Symbol.search](input);
assertEq(re.lastIndex, lastIndex);
}
// Test when lastIndex is non-writable and not positive zero.
for (let {regExp, lastIndex, input} of testCasesNotPositiveZero) {
let re = new Constructor(regExp);
Object.defineProperty(re, "lastIndex", { value: lastIndex, writable: false });
assertThrowsInstanceOf(() => re[Symbol.search](input), TypeError);
assertEq(re.lastIndex, lastIndex);
}
// Test when lastIndex is non-writable and positive zero.
for (let {regExp, lastIndex, input} of testCasesPositiveZero) {
let re = new Constructor(regExp);
Object.defineProperty(re, "lastIndex", { value: lastIndex, writable: false });
if (re.global || re.sticky) {
assertThrowsInstanceOf(() => re[Symbol.search](input), TypeError);
} else {
re[Symbol.search](input);
}
assertEq(re.lastIndex, lastIndex);
}
// Test lastIndex isn't converted to a number.
for (let {regExp, lastIndex, input} of testCases) {
let re = new RegExp(regExp);
let badIndex = {
valueOf() {
assertEq(false, true);
}
};
re.lastIndex = badIndex;
re[Symbol.search](input);
assertEq(re.lastIndex, badIndex);
}
}
if (typeof reportCompare === "function")
reportCompare(true, true);
+1 -1
View File
@@ -157,7 +157,7 @@ reportCompare(
rex = /y/, rex.lastIndex = 1;
reportCompare(
"xxx0",
"xxx1",
"xxx".replace(rex, "y") + rex.lastIndex,
"Section 25"
);
+1 -1
View File
@@ -165,7 +165,7 @@ r = /abc/;
r.lastIndex = -17;
res = r.exec("cdefg");
assertEq(res, null);
assertEq(r.lastIndex, 0);
assertEq(r.lastIndex, -17);
r = /abc/g;
r.lastIndex = -42;
+14 -20
View File
@@ -17,15 +17,12 @@ print(BUGNUMBER + ": " + summary);
var regex = /foo/i;
// Aside from making .lastIndex non-writable, this has two incidental effects
// Aside from making .lastIndex non-writable, this has one incidental effect
// ubiquitously tested through the remainder of this test:
//
// * RegExp.prototype.compile will do everything it ordinarily does, BUT it
// will throw a TypeError when attempting to zero .lastIndex immediately
// before succeeding overall.
// * RegExp.prototype.test for a non-global and non-sticky regular expression,
// in case of a match, will return true (as normal). BUT if no match is
// found, it will throw a TypeError when attempting to modify .lastIndex.
//
// Ain't it great?
Object.defineProperty(regex, "lastIndex", { value: 42, writable: false });
@@ -40,8 +37,8 @@ assertEq(regex.lastIndex, 42);
assertEq(regex.test("foo"), true);
assertEq(regex.test("FOO"), true);
assertThrowsInstanceOf(() => regex.test("bar"), TypeError);
assertThrowsInstanceOf(() => regex.test("BAR"), TypeError);
assertEq(regex.test("bar"), false);
assertEq(regex.test("BAR"), false);
assertThrowsInstanceOf(() => regex.compile("bar"), TypeError);
@@ -52,10 +49,10 @@ assertEq(regex.unicode, false);
assertEq(regex.sticky, false);
assertEq(Object.getOwnPropertyDescriptor(regex, "lastIndex").writable, false);
assertEq(regex.lastIndex, 42);
assertThrowsInstanceOf(() => regex.test("foo"), TypeError);
assertThrowsInstanceOf(() => regex.test("FOO"), TypeError);
assertEq(regex.test("foo"), false);
assertEq(regex.test("FOO"), false);
assertEq(regex.test("bar"), true);
assertThrowsInstanceOf(() => regex.test("BAR"), TypeError);
assertEq(regex.test("BAR"), false);
assertThrowsInstanceOf(() => regex.compile("^baz", "m"), TypeError);
@@ -66,19 +63,16 @@ assertEq(regex.unicode, false);
assertEq(regex.sticky, false);
assertEq(Object.getOwnPropertyDescriptor(regex, "lastIndex").writable, false);
assertEq(regex.lastIndex, 42);
assertThrowsInstanceOf(() => regex.test("foo"), TypeError);
assertThrowsInstanceOf(() => regex.test("FOO"), TypeError);
assertThrowsInstanceOf(() => regex.test("bar"), TypeError);
assertThrowsInstanceOf(() => regex.test("BAR"), TypeError);
assertEq(regex.test("foo"), false);
assertEq(regex.test("FOO"), false);
assertEq(regex.test("bar"), false);
assertEq(regex.test("BAR"), false);
assertEq(regex.test("baz"), true);
assertThrowsInstanceOf(() => regex.test("BAZ"), TypeError);
assertThrowsInstanceOf(() => regex.test("012345678901234567890123456789012345678901baz"),
TypeError);
assertEq(regex.test("BAZ"), false);
assertEq(regex.test("012345678901234567890123456789012345678901baz"), false);
assertEq(regex.test("012345678901234567890123456789012345678901\nbaz"), true);
assertThrowsInstanceOf(() => regex.test("012345678901234567890123456789012345678901BAZ"),
TypeError);
assertThrowsInstanceOf(() => regex.test("012345678901234567890123456789012345678901\nBAZ"),
TypeError);
assertEq(regex.test("012345678901234567890123456789012345678901BAZ"), false);
assertEq(regex.test("012345678901234567890123456789012345678901\nBAZ"), false);
/******************************************************************************/
@@ -55,7 +55,7 @@ regExp.lastIndex = {
}
};
regExp[Symbol.match]("a");
assertEq(regExp.lastIndex, 0, "Update the expected value to |9001| after fixing 1317397");
assertEq(regExp.lastIndex, 9001);
// Case 3.b: Removes sticky flag without match, validate by checking lastIndex.
var regExp = new RegExp("a", "y");
@@ -69,7 +69,7 @@ regExp.lastIndex = {
}
};
regExp[Symbol.match]("a");
assertEq(regExp.lastIndex, 0, "Update the expected value to |9002| after fixing 1317397");
assertEq(regExp.lastIndex, 9002);
if (typeof reportCompare === "function")
reportCompare(true, true);
@@ -55,7 +55,7 @@ regExp.lastIndex = {
}
};
regExp[Symbol.replace]("a", "");
assertEq(regExp.lastIndex, 0, "Update the expected value to |9001| after fixing 1317397");
assertEq(regExp.lastIndex, 9001);
// Case 3.b: Removes sticky flag without match, validate by checking lastIndex.
var regExp = new RegExp("a", "y");
@@ -69,7 +69,7 @@ regExp.lastIndex = {
}
};
regExp[Symbol.replace]("a", "");
assertEq(regExp.lastIndex, 0, "Update the expected value to |9002| after fixing 1317397");
assertEq(regExp.lastIndex, 9002);
if (typeof reportCompare === "function")
reportCompare(true, true);
@@ -56,6 +56,7 @@ assertEq(log,
"get:lastIndex," +
"set:lastIndex," +
"get:exec,call:exec," +
"get:lastIndex," +
"set:lastIndex," +
"get:result[index],");
@@ -70,6 +71,7 @@ assertEq(log,
"get:lastIndex," +
"set:lastIndex," +
"get:exec,call:exec," +
"get:lastIndex," +
"set:lastIndex,");
if (typeof reportCompare === "function")