diff options
Diffstat (limited to 'js/src/jit-test/tests/regexp')
34 files changed, 585 insertions, 0 deletions
diff --git a/js/src/jit-test/tests/regexp/RegExpExec-errors.js b/js/src/jit-test/tests/regexp/RegExpExec-errors.js new file mode 100644 index 0000000000..bee579859f --- /dev/null +++ b/js/src/jit-test/tests/regexp/RegExpExec-errors.js @@ -0,0 +1,6 @@ +load(libdir + "asserts.js"); + +assertErrorMessage(() => RegExp.prototype.test.call({}), TypeError, + /test method called on incompatible Object/); +assertErrorMessage(() => RegExp.prototype[Symbol.match].call([]), TypeError, + /\[Symbol\.match\] method called on incompatible Array/); diff --git a/js/src/jit-test/tests/regexp/bug1419785.js b/js/src/jit-test/tests/regexp/bug1419785.js new file mode 100644 index 0000000000..b40eca9f8c --- /dev/null +++ b/js/src/jit-test/tests/regexp/bug1419785.js @@ -0,0 +1,3 @@ +// |jit-test| error:character class +Object.defineProperty(RegExp.prototype, Symbol.search, {get: () => { throw "wrong"; }}); +"abc".search("[["); diff --git a/js/src/jit-test/tests/regexp/bug1445907.js b/js/src/jit-test/tests/regexp/bug1445907.js new file mode 100644 index 0000000000..a0e0dca57b --- /dev/null +++ b/js/src/jit-test/tests/regexp/bug1445907.js @@ -0,0 +1,14 @@ +// |jit-test| skip-if: !wasmDebuggingEnabled() + +// On ARM64, we failed to save x28 properly when generating code for the regexp +// matcher. +// +// There's wasm and Debugger code here because the combination forces the use of +// x28 and exposes the bug when running on the simulator. + +var g = newGlobal({newCompartment: true}); +var dbg = new Debugger(g); +g.eval(`var m = new WebAssembly.Instance(new WebAssembly.Module(wasmTextToBinary('(module (func (export "test")))')))`); +var re = /./; +dbg.onEnterFrame = function(frame) { re.exec("x") }; +result = g.eval("m.exports.test()"); diff --git a/js/src/jit-test/tests/regexp/bug1600272.js b/js/src/jit-test/tests/regexp/bug1600272.js new file mode 100644 index 0000000000..08f154f771 --- /dev/null +++ b/js/src/jit-test/tests/regexp/bug1600272.js @@ -0,0 +1,6 @@ +// |jit-test| skip-if: helperThreadCount() === 0 +evalInWorker(` + Array.prototype[1] = 1; + var source = Array(50000).join("(") + "a" + Array(50000).join(")"); + var r70 = RegExp(source); +`); diff --git a/js/src/jit-test/tests/regexp/bug1640473.js b/js/src/jit-test/tests/regexp/bug1640473.js new file mode 100644 index 0000000000..710ece3fc2 --- /dev/null +++ b/js/src/jit-test/tests/regexp/bug1640473.js @@ -0,0 +1,16 @@ +// |jit-test| skip-if: getBuildConfiguration()['wasi'] +var s = ""; +var input = ""; +for (var i = 0; i < 500; ++i) { + s += "(?<a" + i + ">a)"; + s += "(?<b" + i + ">b)?"; + input += "a"; +} + +var r = RegExp(s); +var e = r.exec(input); + +for (var i = 0; i < 500; i++) { + assertEq(e.groups["a" + i], "a"); + assertEq(e.groups["b" + i], undefined); +} diff --git a/js/src/jit-test/tests/regexp/bug1640475.js b/js/src/jit-test/tests/regexp/bug1640475.js new file mode 100644 index 0000000000..58c092ec1d --- /dev/null +++ b/js/src/jit-test/tests/regexp/bug1640475.js @@ -0,0 +1,9 @@ +// |jit-test| skip-if: !('oomTest' in this) + +var i = 0; +oomTest(function() { + for (var j = 0; j < 10; ++j) { + var r = RegExp(`(?<_${(i++).toString(32)}>a)`); + r.exec("a"); + } +}); diff --git a/js/src/jit-test/tests/regexp/bug1640479.js b/js/src/jit-test/tests/regexp/bug1640479.js new file mode 100644 index 0000000000..ff166d6451 --- /dev/null +++ b/js/src/jit-test/tests/regexp/bug1640479.js @@ -0,0 +1,15 @@ +// |jit-test| skip-if: !('oomTest' in this) + +var failures = 0; +var i = 0; + +function foo() { + var e; + var r = RegExp("(?<_" + (i++) + "a>)"); + try { e = r.exec("a"); } catch {} + e = r.exec("a"); + if (e.groups === undefined) failures++; +} + +oomTest(foo); +assertEq(failures, 0); diff --git a/js/src/jit-test/tests/regexp/bug1640487.js b/js/src/jit-test/tests/regexp/bug1640487.js new file mode 100644 index 0000000000..7ee3756075 --- /dev/null +++ b/js/src/jit-test/tests/regexp/bug1640487.js @@ -0,0 +1,8 @@ +var re = /(?<x>a)|b/; + +function f(j) { + var s = String.fromCharCode(0x61 + (j === 1)); + var e = re.exec(s); + if (e.groups.x !== "a") print(i,j); +} +for (var i = 0; i < 2; ++i) f(i); diff --git a/js/src/jit-test/tests/regexp/bug1640592.js b/js/src/jit-test/tests/regexp/bug1640592.js new file mode 100644 index 0000000000..e00fd81e99 --- /dev/null +++ b/js/src/jit-test/tests/regexp/bug1640592.js @@ -0,0 +1,2 @@ +gczeal(14, 16); +"a".match(/(?<a>a)/); diff --git a/js/src/jit-test/tests/regexp/bug1697077.js b/js/src/jit-test/tests/regexp/bug1697077.js new file mode 100644 index 0000000000..3d43ae60a2 --- /dev/null +++ b/js/src/jit-test/tests/regexp/bug1697077.js @@ -0,0 +1,11 @@ +// |jit-test| skip-if: !('interruptRegexp' in this) +var s0 = "A".repeat(10*1024); +var interrupted = false; +gczeal(0); +setInterruptCallback(() => { + interrupted = true; + startgc(7,'shrinking'); + return true; +}); +assertEq(interruptRegexp(/a(bc|bd)/, s0), null); +assertEq(interrupted, true);
\ No newline at end of file diff --git a/js/src/jit-test/tests/regexp/bug1703750.js b/js/src/jit-test/tests/regexp/bug1703750.js new file mode 100644 index 0000000000..4be2d7748a --- /dev/null +++ b/js/src/jit-test/tests/regexp/bug1703750.js @@ -0,0 +1,7 @@ +function foo() { + const b = "".match(); + try { + foo(); + } catch {} +} +foo() diff --git a/js/src/jit-test/tests/regexp/bug1718842-1.js b/js/src/jit-test/tests/regexp/bug1718842-1.js new file mode 100644 index 0000000000..35b557b6b4 --- /dev/null +++ b/js/src/jit-test/tests/regexp/bug1718842-1.js @@ -0,0 +1,7 @@ +var g = newGlobal({sameZoneAs: this}); + +var re1 = RegExp("(?<a>a)"); +var re2 = g.RegExp("(?<a>a)"); + +re1.exec("a"); +re2.exec("a"); diff --git a/js/src/jit-test/tests/regexp/bug1718842-2.js b/js/src/jit-test/tests/regexp/bug1718842-2.js new file mode 100644 index 0000000000..88f76046f7 --- /dev/null +++ b/js/src/jit-test/tests/regexp/bug1718842-2.js @@ -0,0 +1,9 @@ +var g = newGlobal(); + +g.enableTrackAllocations(); + +var re1 = RegExp("(?<a>a)"); +var re2 = g.RegExp("(?<a>a)"); + +re1.exec("a"); +re2.exec("a"); diff --git a/js/src/jit-test/tests/regexp/bug1783555.js b/js/src/jit-test/tests/regexp/bug1783555.js new file mode 100644 index 0000000000..60978910a2 --- /dev/null +++ b/js/src/jit-test/tests/regexp/bug1783555.js @@ -0,0 +1,4 @@ +var src = "(.?)".repeat(65536); +try { + "".match(src); +} catch {} diff --git a/js/src/jit-test/tests/regexp/bug1783830.js b/js/src/jit-test/tests/regexp/bug1783830.js new file mode 100644 index 0000000000..cd41be7f66 --- /dev/null +++ b/js/src/jit-test/tests/regexp/bug1783830.js @@ -0,0 +1,7 @@ +assertEq(/[^!]/u.exec('\u{1F4A9}')[0], "\u{1F4A9}"); +assertEq(/^[^!]/u.exec('\u{1F4A9}')[0], "\u{1F4A9}"); +assertEq(/[^!]$/u.exec('\u{1F4A9}')[0], "\u{1F4A9}"); +assertEq(/![^!]/u.exec('!\u{1F4A9}')[0], "!\u{1F4A9}"); +assertEq(/[^!]!/u.exec('\u{1F4A9}!')[0], "\u{1F4A9}!"); +assertEq(/![^!]/ui.exec('!\u{1F4A9}')[0], "!\u{1F4A9}"); +assertEq(/[^!]!/ui.exec('\u{1F4A9}!')[0], "\u{1F4A9}!"); diff --git a/js/src/jit-test/tests/regexp/bug1786012.js b/js/src/jit-test/tests/regexp/bug1786012.js new file mode 100644 index 0000000000..f806fd22bb --- /dev/null +++ b/js/src/jit-test/tests/regexp/bug1786012.js @@ -0,0 +1,8 @@ +var repeated = "A".repeat(65536); +var src = "^(?:" + repeated + ")\$"; + +for (var i = 0; i < 100; i++) { + try { + RegExp(src).test(); + } catch {} +} diff --git a/js/src/jit-test/tests/regexp/bug1794317.js b/js/src/jit-test/tests/regexp/bug1794317.js new file mode 100644 index 0000000000..1ecb21eb64 --- /dev/null +++ b/js/src/jit-test/tests/regexp/bug1794317.js @@ -0,0 +1,7 @@ +// |jit-test| skip-if: !('oomTest' in this) + +for (let i = 0; i < 2; i++) { + oomTest(function () { + RegExp("(?<name" + i + ">)").exec(); + }) +} diff --git a/js/src/jit-test/tests/regexp/builtin-exec-wrapper.js b/js/src/jit-test/tests/regexp/builtin-exec-wrapper.js new file mode 100644 index 0000000000..63660de643 --- /dev/null +++ b/js/src/jit-test/tests/regexp/builtin-exec-wrapper.js @@ -0,0 +1,7 @@ +var g = newGlobal({newCompartment: true}); +g.evaluate(`RegExp.prototype.exec = {};`); +var wrapper = g.evaluate(`/abc.+def/`); +assertEq(RegExp.prototype.test.call(wrapper, "abc"), false); +assertEq(RegExp.prototype.test.call(wrapper, "abcXdef"), true); +assertEq(RegExp.prototype[Symbol.match].call(wrapper, "abc"), null); +assertEq(RegExp.prototype[Symbol.match].call(wrapper, "abcXdef")[0], "abcXdef"); diff --git a/js/src/jit-test/tests/regexp/clone-statics.js b/js/src/jit-test/tests/regexp/clone-statics.js new file mode 100644 index 0000000000..d2f0ce1cac --- /dev/null +++ b/js/src/jit-test/tests/regexp/clone-statics.js @@ -0,0 +1,21 @@ +// |jit-test| skip-if: helperThreadCount() === 0 + +offThreadCompileToStencil(` + function foo(x, {}) { + do { + re = /erwe/; + if (x === 1) + re.x = 1; + else + re.x = "a"; + assertEq(re.x.length, (x === 1) ? undefined : 1); + } while (!inIon()); + } + + foo(0, 0); + RegExp.multiline = 1; + foo(1, 0); +`); + +var stencil = finishOffThreadStencil(); +evalStencil(stencil); diff --git a/js/src/jit-test/tests/regexp/flag-getters.js b/js/src/jit-test/tests/regexp/flag-getters.js new file mode 100644 index 0000000000..fbc1ad7274 --- /dev/null +++ b/js/src/jit-test/tests/regexp/flag-getters.js @@ -0,0 +1,61 @@ +// Test inlining for RegExp flag getters. + +function testGlobal() { + const xs = [/a/, /b/g]; + + for (let i = 0; i < 200; ++i) { + let x = xs[i & 1]; + assertEq(x.global, !!(i & 1)); + } +} +for (let i = 0; i < 2; ++i) testGlobal(); + +function testIgnoreCase() { + const xs = [/a/, /b/i]; + + for (let i = 0; i < 200; ++i) { + let x = xs[i & 1]; + assertEq(x.ignoreCase, !!(i & 1)); + } +} +for (let i = 0; i < 2; ++i) testIgnoreCase(); + +function testMultiline() { + const xs = [/a/, /b/m]; + + for (let i = 0; i < 200; ++i) { + let x = xs[i & 1]; + assertEq(x.multiline, !!(i & 1)); + } +} +for (let i = 0; i < 2; ++i) testMultiline(); + +function testDotAll() { + const xs = [/a/, /b/s]; + + for (let i = 0; i < 200; ++i) { + let x = xs[i & 1]; + assertEq(x.dotAll, !!(i & 1)); + } +} +for (let i = 0; i < 2; ++i) testDotAll(); + +function testUnicode() { + const xs = [/a/, /b/u]; + + for (let i = 0; i < 200; ++i) { + let x = xs[i & 1]; + assertEq(x.unicode, !!(i & 1)); + } +} +for (let i = 0; i < 2; ++i) testUnicode(); + +function testSticky() { + const xs = [/a/, /b/y]; + + for (let i = 0; i < 200; ++i) { + let x = xs[i & 1]; + assertEq(x.sticky, !!(i & 1)); + } +} +for (let i = 0; i < 2; ++i) testSticky(); diff --git a/js/src/jit-test/tests/regexp/huge-01.js b/js/src/jit-test/tests/regexp/huge-01.js new file mode 100644 index 0000000000..bc1529f0c6 --- /dev/null +++ b/js/src/jit-test/tests/regexp/huge-01.js @@ -0,0 +1,21 @@ + +function g(N, p) { + var prefix = p.repeat(N); + var str = prefix + "[AB]"; + + try { + var re = new RegExp(str); + re.exec(prefix + "A"); + } catch(e) { + // 1. Regexp too big is raised by the lack of the 64k virtual registers + // reserved for the regexp evaluation. + // 2. The stack overflow can occur during the analysis of the regexp + assertEq(e.message.includes("regexp too big") || + e.message.includes("Stack overflow") || + e.message.includes("too much recursion"), true); + } +} + +var prefix = "/(?=k)ok/"; +for (var i = 0; i < 18; i++) + g(Math.pow(2, i), prefix) diff --git a/js/src/jit-test/tests/regexp/huge-02.js b/js/src/jit-test/tests/regexp/huge-02.js new file mode 100644 index 0000000000..1d0d94e10c --- /dev/null +++ b/js/src/jit-test/tests/regexp/huge-02.js @@ -0,0 +1,13 @@ +var interestingCaptureNums = [(1 << 14), + (1 << 15) - 1, + (1 << 15), + (1 << 15) + 1, + (1 << 16)] + +for (let i of interestingCaptureNums) { + print(i); + try { + var source = Array(i).join("(") + "a" + Array(i).join(")"); + RegExp(source).exec("a"); + } catch {} +} diff --git a/js/src/jit-test/tests/regexp/lastIndex-negative.js b/js/src/jit-test/tests/regexp/lastIndex-negative.js new file mode 100644 index 0000000000..536b3d2233 --- /dev/null +++ b/js/src/jit-test/tests/regexp/lastIndex-negative.js @@ -0,0 +1,14 @@ +// A negative lastIndex value must be treated as 0. +function test() { + var re = /abc.+de/g; + for (var i = 0; i < 100; i++) { + re.lastIndex = (i > 60) ? -1 : 0; + assertEq(typeof re.exec("abcXdef"), "object"); + assertEq(re.lastIndex, 6); + + re.lastIndex = (i > 60) ? -1 : 0; + assertEq(re.test("abcXdef"), true); + assertEq(re.lastIndex, 6); + } +} +test(); diff --git a/js/src/jit-test/tests/regexp/lastIndex-non-writable.js b/js/src/jit-test/tests/regexp/lastIndex-non-writable.js new file mode 100644 index 0000000000..74839762f5 --- /dev/null +++ b/js/src/jit-test/tests/regexp/lastIndex-non-writable.js @@ -0,0 +1,71 @@ +function testGlobalExec() { + var re = /abc.+de/g; + var c = 0; + for (var i = 0; i < 100; i++) { + re.lastIndex = 0; + if (i === 60) { + Object.freeze(re); + } + try { + re.exec("abcXdef"); + } catch (e) { + assertEq(e.toString().includes("lastIndex"), true); + c++; + } + assertEq(re.lastIndex, i >= 60 ? 0 : 6); + } + assertEq(c, 40); +} +testGlobalExec(); + +function testStickyTest() { + var re = /abc.+de/y; + var c = 0; + for (var i = 0; i < 100; i++) { + re.lastIndex = 0; + if (i === 60) { + Object.freeze(re); + } + try { + re.test("abcXdef"); + } catch (e) { + assertEq(e.toString().includes("lastIndex"), true); + c++; + } + assertEq(re.lastIndex, i >= 60 ? 0 : 6); + } + assertEq(c, 40); +} +testStickyTest(); + +// Must not reset too-large .lastIndex to 0 if non-writable. +function testLastIndexOutOfRange() { + var re = /abc.+de/g; + re.lastIndex = 123; + Object.freeze(re); + for (var i = 0; i < 100; i++) { + var ex = null; + try { + re.exec("abcXdef"); + } catch (e) { + ex = e; + } + assertEq(ex.toString().includes("lastIndex"), true); + assertEq(re.lastIndex, 123); + } +} +testLastIndexOutOfRange(); + +// .lastIndex is ignored for non-global/non-sticky regexps. +function testPlainExec() { + var re = /abc.+de/; + re.lastIndex = 1234; + for (var i = 0; i < 100; i++) { + if (i === 60) { + Object.freeze(re); + } + assertEq(re.exec("abcXdef")[0], "abcXde"); + assertEq(re.lastIndex, 1234); + } +} +testPlainExec(); diff --git a/js/src/jit-test/tests/regexp/lastIndex-too-large.js b/js/src/jit-test/tests/regexp/lastIndex-too-large.js new file mode 100644 index 0000000000..3e04042dd0 --- /dev/null +++ b/js/src/jit-test/tests/regexp/lastIndex-too-large.js @@ -0,0 +1,13 @@ +function test() { + var re = /abc.+de/y; + for (var i = 0; i < 100; i++) { + re.lastIndex = 10; + assertEq(re.exec("abcXdef"), null); + assertEq(re.lastIndex, 0); + + re.lastIndex = 10; + assertEq(re.test("abcXdef"), false); + assertEq(re.lastIndex, 0); + } +} +test(); diff --git a/js/src/jit-test/tests/regexp/lastIndex-valueOf.js b/js/src/jit-test/tests/regexp/lastIndex-valueOf.js new file mode 100644 index 0000000000..3794d20c2e --- /dev/null +++ b/js/src/jit-test/tests/regexp/lastIndex-valueOf.js @@ -0,0 +1,16 @@ +function test() { + var re = /abc.+de/g; + var c = 0; + var weird = {valueOf() { c++; return 0; }}; + for (var i = 0; i < 100; i++) { + re.lastIndex = (i > 60) ? weird : 0; + assertEq(typeof re.exec("abcXdef"), "object"); + assertEq(re.lastIndex, 6); + + re.lastIndex = (i > 60) ? weird : 0; + assertEq(re.test("abcXdef"), true); + assertEq(re.lastIndex, 6); + } + assertEq(c, 78); +} +test(); diff --git a/js/src/jit-test/tests/regexp/match-indices-dictionary.js b/js/src/jit-test/tests/regexp/match-indices-dictionary.js new file mode 100644 index 0000000000..e0cb5993f5 --- /dev/null +++ b/js/src/jit-test/tests/regexp/match-indices-dictionary.js @@ -0,0 +1,20 @@ +// |jit-test| skip-if: getBuildConfiguration()['wasi'] +var s = ""; +var input = ""; +for (var i = 0; i < 500; ++i) { + s += "(?<a" + i + ">a)"; + s += "(?<b" + i + ">b)?"; + input += "a"; +} + +var r = RegExp(s, "d"); +var e = r.exec(input); + +for (var i = 0; i < 500; i++) { + assertEq(e.groups["a" + i], "a"); + assertEq(e.groups["b" + i], undefined); + + assertEq(e.indices.groups["a" + i][0], i) + assertEq(e.indices.groups["a" + i][1], i + 1) + assertEq(e.indices.groups["b" + i], undefined) +} diff --git a/js/src/jit-test/tests/regexp/match-indices-warp.js b/js/src/jit-test/tests/regexp/match-indices-warp.js new file mode 100644 index 0000000000..634d368aee --- /dev/null +++ b/js/src/jit-test/tests/regexp/match-indices-warp.js @@ -0,0 +1,8 @@ +var re = /A*(B)A*/d; +for (var i = 0; i < 100; i++) { + var match = re.exec("xxxxxAAABAxxxxx"); + assertEq(match.indices[0][0], 5); + assertEq(match.indices[0][1], 10); + assertEq(match.indices[1][0], 8); + assertEq(match.indices[1][1], 9); +} diff --git a/js/src/jit-test/tests/regexp/named-capture-proxy.js b/js/src/jit-test/tests/regexp/named-capture-proxy.js new file mode 100644 index 0000000000..e7cc933850 --- /dev/null +++ b/js/src/jit-test/tests/regexp/named-capture-proxy.js @@ -0,0 +1,21 @@ +var access_log = ""; +const handler = { + get: function(obj, prop) { + access_log += prop + ","; + return prop in obj ? obj[prop] : "<filled by proxy>"; + } +}; + +class ProxiedGroupRegExp extends RegExp { + exec(s) { + var result = super.exec(s); + if (result) { + result.groups = new Proxy(result.groups, handler); + } + return result; + } +} + +let re = new ProxiedGroupRegExp("(?<x>.)"); +assertEq("a".replace(re, "$<x> $<y>"), "a <filled by proxy>"); +assertEq(access_log, "x,y,") diff --git a/js/src/jit-test/tests/regexp/non-unicode-case-folding-backreference.js b/js/src/jit-test/tests/regexp/non-unicode-case-folding-backreference.js new file mode 100644 index 0000000000..bfea5f89a9 --- /dev/null +++ b/js/src/jit-test/tests/regexp/non-unicode-case-folding-backreference.js @@ -0,0 +1,67 @@ +// |jit-test| skip-if: typeof Intl === 'undefined' + +// See https://tc39.es/ecma262/#sec-runtime-semantics-canonicalize-ch +function Canonicalize(ch) { + var u = ch.toUpperCase(); + if (u.length > 1) return ch; + var cu = u.charCodeAt(0); + if (ch.charCodeAt(0) >= 128 && cu < 128) return ch; + return cu; +} + +function TestEquivalenceClass(eclass) { + var backref = /(.)\1/i; + + for (var i = 0; i < eclass.length; i++) { + for (var j = 0; j < eclass.length; j++) { + if (i == j) continue; + var c1 = eclass[i]; + var c2 = eclass[j]; + var cc = c1 + c2; + var shouldMatch = Canonicalize(c1) === Canonicalize(c2); + + assertEq(backref.test(cc), shouldMatch); + } + } +} + +function TestAll() { + for (var eclass of equivalence_classes) { + TestEquivalenceClass(eclass); + } +} + +// Interesting case-folding equivalence classes (as determined by +// ICU's UnicodeSet::closeOver). A class is interesting if it contains +// more than two characters, or if it contains any characters in +// IgnoreSet or SpecialAddSet as defined in new-regexp/special-case.h. +var equivalence_classes = [ + '\u0041\u0061', // Aa (sanity check) + '\u004b\u006b\u212a', // KkK + '\u0053\u0073\u017f', // Ssſ + '\u00b5\u039c\u03bc', // µΜμ + '\u00c5\u00e5\u212b', // ÅåÅ + '\u00df\u1e9e', // ßẞ + '\u03a9\u03c9\u2126', // ΩωΩ + '\u0390\u1fd3', // ΐΐ + '\u0398\u03b8\u03d1\u03f4', // Θθϑϴ + '\u03b0\u1fe3', // ΰΰ + '\u1f80\u1f88', // ᾀᾈ + '\u1fb3\u1fbc', // ᾳᾼ + '\u1fc3\u1fcc', // ῃῌ + '\u1ff3\u1ffc', // ῳῼ + '\ufb05\ufb06', // ſtst + + // Everything below this line is a well-behaved case-folding + // equivalence class with more than two characters but only one + // canonical case-folded character + '\u01c4\u01c5\u01c6', '\u01c7\u01c8\u01c9', '\u01ca\u01cb\u01cc', + '\u01f1\u01f2\u01f3', '\u0345\u0399\u03b9\u1fbe', '\u0392\u03b2\u03d0', + '\u0395\u03b5\u03f5', '\u039a\u03ba\u03f0', '\u03a0\u03c0\u03d6', + '\u03a1\u03c1\u03f1', '\u03a3\u03c2\u03c3', '\u03a6\u03c6\u03d5', + '\u0412\u0432\u1c80', '\u0414\u0434\u1c81', '\u041e\u043e\u1c82', + '\u0421\u0441\u1c83', '\u0422\u0442\u1c84\u1c85', '\u042a\u044a\u1c86', + '\u0462\u0463\u1c87', '\u1c88\ua64a\ua64b', '\u1e60\u1e61\u1e9b' +]; + +TestAll(); diff --git a/js/src/jit-test/tests/regexp/non-unicode-case-folding.js b/js/src/jit-test/tests/regexp/non-unicode-case-folding.js new file mode 100644 index 0000000000..52a799238e --- /dev/null +++ b/js/src/jit-test/tests/regexp/non-unicode-case-folding.js @@ -0,0 +1,68 @@ +// |jit-test| skip-if: typeof Intl === 'undefined' + +// See https://tc39.es/ecma262/#sec-runtime-semantics-canonicalize-ch +function Canonicalize(ch) { + var u = ch.toUpperCase(); + if (u.length > 1) return ch; + var cu = u.charCodeAt(0); + if (ch.charCodeAt(0) >= 128 && cu < 128) return ch; + return cu; +} + +function TestEquivalenceClass(eclass) { + for (var i = 0; i < eclass.length; i++) { + for (var j = 0; j < eclass.length; j++) { + if (i == j) continue; + var c1 = eclass[i]; + var c2 = eclass[j]; + var shouldMatch = Canonicalize(c1) === Canonicalize(c2); + + var re1 = new RegExp(c1, 'i'); + var re2 = new RegExp('[' + c1 + ']', 'i'); + + assertEq(re1.test(c2), shouldMatch); + assertEq(re2.test(c2), shouldMatch); + } + } +} + +function TestAll() { + for (var eclass of equivalence_classes) { + TestEquivalenceClass(eclass); + } +} + +// Interesting case-folding equivalence classes (as determined by +// ICU's UnicodeSet::closeOver). A class is interesting if it contains +// more than two characters, or if it contains any characters in +// IgnoreSet or SpecialAddSet as defined in new-regexp/special-case.h. +var equivalence_classes = [ + '\u0041\u0061', // Aa (sanity check) + '\u004b\u006b\u212a', // KkK + '\u0053\u0073\u017f', // Ssſ + '\u00b5\u039c\u03bc', // µΜμ + '\u00c5\u00e5\u212b', // ÅåÅ + '\u00df\u1e9e', // ßẞ + '\u03a9\u03c9\u2126', // ΩωΩ + '\u0390\u1fd3', // ΐΐ + '\u0398\u03b8\u03d1\u03f4', // Θθϑϴ + '\u03b0\u1fe3', // ΰΰ + '\u1f80\u1f88', // ᾀᾈ + '\u1fb3\u1fbc', // ᾳᾼ + '\u1fc3\u1fcc', // ῃῌ + '\u1ff3\u1ffc', // ῳῼ + '\ufb05\ufb06', // ſtst + + // Everything below this line is a well-behaved case-folding + // equivalence class with more than two characters but only one + // canonical case-folded character + '\u01c4\u01c5\u01c6', '\u01c7\u01c8\u01c9', '\u01ca\u01cb\u01cc', + '\u01f1\u01f2\u01f3', '\u0345\u0399\u03b9\u1fbe', '\u0392\u03b2\u03d0', + '\u0395\u03b5\u03f5', '\u039a\u03ba\u03f0', '\u03a0\u03c0\u03d6', + '\u03a1\u03c1\u03f1', '\u03a3\u03c2\u03c3', '\u03a6\u03c6\u03d5', + '\u0412\u0432\u1c80', '\u0414\u0434\u1c81', '\u041e\u043e\u1c82', + '\u0421\u0441\u1c83', '\u0422\u0442\u1c84\u1c85', '\u042a\u044a\u1c86', + '\u0462\u0463\u1c87', '\u1c88\ua64a\ua64b', '\u1e60\u1e61\u1e9b' +]; + +TestAll(); diff --git a/js/src/jit-test/tests/regexp/replace-exec.js b/js/src/jit-test/tests/regexp/replace-exec.js new file mode 100644 index 0000000000..b6488ec678 --- /dev/null +++ b/js/src/jit-test/tests/regexp/replace-exec.js @@ -0,0 +1,12 @@ +function test() { + var re = /abc.+de/; + var c = 0; + for (var i = 0; i < 100; i++) { + assertEq(re.test("abcXdef"), true); + if (i === 60) { + RegExp.prototype.exec = () => { c++; return {}; }; + } + } + assertEq(c, 39); +} +test(); diff --git a/js/src/jit-test/tests/regexp/rope-inputs.js b/js/src/jit-test/tests/regexp/rope-inputs.js new file mode 100644 index 0000000000..d8f4b4de94 --- /dev/null +++ b/js/src/jit-test/tests/regexp/rope-inputs.js @@ -0,0 +1,10 @@ +function test() { + var re = /abc(.+)z/; + for (var i = 0; i < 100; i++) { + assertEq(re.exec(newRope("abcd", "xyz"))[1], "dxy"); + assertEq(re.test(newRope("abcd", "xyz")), true); + assertEq(re[Symbol.search](newRope(".abcd", "xyz")), 1); + assertEq(re[Symbol.match](newRope("abcd", "xyz"))[1], "dxy"); + } +} +test(); diff --git a/js/src/jit-test/tests/regexp/unicode-back-reference.js b/js/src/jit-test/tests/regexp/unicode-back-reference.js new file mode 100644 index 0000000000..90eef410a5 --- /dev/null +++ b/js/src/jit-test/tests/regexp/unicode-back-reference.js @@ -0,0 +1,3 @@ +for (var t = 0; t < 10000; t++) { + (t + "\u1234").split(/(.)\1/i); +} |