mirror of
https://github.com/roytam1/basilisk55.git
synced 2026-05-26 15:02:46 +00:00
import from UXP: Bug 1317397: Only set lastIndex for global or sticky RegExps in RegExpBuiltinExec per ES2017 (78ce3bf8)
This commit is contained in:
+64
-30
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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++) {
|
||||
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -157,7 +157,7 @@ reportCompare(
|
||||
|
||||
rex = /y/, rex.lastIndex = 1;
|
||||
reportCompare(
|
||||
"xxx0",
|
||||
"xxx1",
|
||||
"xxx".replace(rex, "y") + rex.lastIndex,
|
||||
"Section 25"
|
||||
);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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")
|
||||
|
||||
Reference in New Issue
Block a user