diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
commit | 26a029d407be480d791972afb5975cf62c9360a6 (patch) | |
tree | f435a8308119effd964b339f76abb83a57c29483 /js/src/jit-test/tests/generators | |
parent | Initial commit. (diff) | |
download | firefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz firefox-26a029d407be480d791972afb5975cf62c9360a6.zip |
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'js/src/jit-test/tests/generators')
28 files changed, 1080 insertions, 0 deletions
diff --git a/js/src/jit-test/tests/generators/bug1098947.js b/js/src/jit-test/tests/generators/bug1098947.js new file mode 100644 index 0000000000..2a1d5af774 --- /dev/null +++ b/js/src/jit-test/tests/generators/bug1098947.js @@ -0,0 +1,11 @@ +function* f() { + try { + let foo = 3; + for (var i=0; i<50; i++) + yield i + foo; + } catch(e) {} +} +var it = f(); +for (var i=0; i<40; i++) + it.next(); +it.return(); diff --git a/js/src/jit-test/tests/generators/bug1462353.js b/js/src/jit-test/tests/generators/bug1462353.js new file mode 100644 index 0000000000..9140a38339 --- /dev/null +++ b/js/src/jit-test/tests/generators/bug1462353.js @@ -0,0 +1,11 @@ +class Base {} +class Derived extends Base { + constructor() { + var fun = async() => { + for (var i = 0; i < 20; i++) {} // Trigger OSR. + super(); + }; + fun(); + } +} +d = new Derived(); diff --git a/js/src/jit-test/tests/generators/bug1491331.js b/js/src/jit-test/tests/generators/bug1491331.js new file mode 100644 index 0000000000..2fe3770234 --- /dev/null +++ b/js/src/jit-test/tests/generators/bug1491331.js @@ -0,0 +1,11 @@ +let g = newGlobal({newCompartment: true}); +g.eval('function* f() { yield 123; }'); + +let dbg = Debugger(g); +dbg.onEnterFrame = frame => { + dbg.removeDebuggee(g); + dbg.addDebuggee(g); +}; + +let genObj = g.f(); +genObj.return(); diff --git a/js/src/jit-test/tests/generators/bug1501722.js b/js/src/jit-test/tests/generators/bug1501722.js new file mode 100644 index 0000000000..9ff2724a9f --- /dev/null +++ b/js/src/jit-test/tests/generators/bug1501722.js @@ -0,0 +1,11 @@ +// |jit-test| skip-if: !('oomTest' in this) + +ignoreUnhandledRejections(); + +(function () { + g = newGlobal({newCompartment: true}); + g.parent = this; + g.eval("(function() { var dbg = Debugger(parent); dbg.onEnterFrame = function() {} } )") + ``; + oomTest(async function() {}, {expectExceptionOnFailure: false}); +})() diff --git a/js/src/jit-test/tests/generators/bug1542660-2.js b/js/src/jit-test/tests/generators/bug1542660-2.js new file mode 100644 index 0000000000..60fc3edcf6 --- /dev/null +++ b/js/src/jit-test/tests/generators/bug1542660-2.js @@ -0,0 +1,111 @@ +// |jit-test| skip-if: !('gc' in this) || !('clearKeptObjects' in this) +// In generators, when we exit a lexical scope, its non-aliased bindings go away; +// they don't keep their last values gc-alive. + +let cases = [ + function* onNormalExitFromFunction_VarBinding() { + var tmp = yield 1; + consumeValue(tmp); + }, + + function* onNormalExitFromFunction_LetBinding() { + let tmp = yield 1; + consumeValue(tmp); + }, + + function* onNormalExitFromBlock() { + if (typeof onNormalExitFromBlock === 'function') { + let tmp = yield 1; + consumeValue(tmp); + } + yield 2; + }, + + function* onNormalExitFromCStyleForLet() { + for (let tmp = yield 1; tmp !== null; tmp = null) { + consumeValue(tmp); + } + yield 2; + }, + + function* onNormalExitFromForLetOf() { + for (let tmp of [yield 1]) { + consumeValue(tmp); + } + yield 2; + }, + + function* onNormalExitFromForConstOf() { + for (const tmp of [yield 1]) { + consumeValue(tmp); + } + yield 2; + }, + + function* onNormalExitFromForConstDestructuringOf() { + for (const {a, b, c, d} of [yield 1]) { + consumeValue(a); + } + yield 2; + }, + + function* onNormalExitFromForConstArrayDestructuringOf() { + for (const [x] of [[yield 1]]) { + consumeValue(x); + } + yield 2; + }, + + function* onNormalExitFromBlockInLoop() { + for (var i = 0; i < 2; i++) { + if (i == 0) { + let tmp = yield 1; + consumeValue(tmp); + } else { + yield 2; + } + } + }, + + function* onBreakFromBlock() { + x: { + let tmp = yield 1; + consumeValue(tmp); + break x; + } + yield 2; + }, + + function* onExitFromCatchBlock() { + try { + throw yield 1; + } catch (exc) { + consumeValue(exc); + } + yield 2; + }, +]; + +var consumeValue; + +function runTest(g) { + consumeValue = eval("_ => {}"); + + let generator = g(); + let result = generator.next(); + assertEq(result.done, false); + assertEq(result.value, 1); + let object = {}; + let weakRef = new WeakRef(object); + result = generator.next(object); + assertEq(result.value, result.done ? undefined : 2); + + object = null; + clearKeptObjects(); + gc(); + assertEq(weakRef.deref(), undefined); +} + +for (let g of cases) { + runTest(g); +} diff --git a/js/src/jit-test/tests/generators/bug1542660.js b/js/src/jit-test/tests/generators/bug1542660.js new file mode 100644 index 0000000000..b3f3ea4554 --- /dev/null +++ b/js/src/jit-test/tests/generators/bug1542660.js @@ -0,0 +1,53 @@ +// |jit-test| skip-if: !('gc' in this) || !('clearKeptObjects' in this) +// Locals in async functions should not keep objects alive after going out of scope. +// Test by Mathieu Hofman. + +let nextId = 0; + +let weakRef; +let savedCallback; + +const tests = [ + function() { + let object = { id: ++nextId }; + console.log(`created object ${object.id}`); + savedCallback = () => {}; + weakRef = new WeakRef(object); + }, + async function() { + let object = { id: ++nextId }; + console.log(`created object ${object.id}`); + savedCallback = () => {}; + weakRef = new WeakRef(object); + }, + async function() { + function* gen() { + { + let object = { id: ++nextId }; + console.log(`created object ${object.id}`); + // Yielding here stores the local variable `object` in the generator + // object. + yield 1; + weakRef = new WeakRef(object); + } + // Yielding here should clear it. + yield 2; + } + let iter = gen(); + assertEq(iter.next().value, 1); + assertEq(iter.next().value, 2); + savedCallback = iter; // Keep the generator alive for GC. + } +]; + +(async () => { + for (const test of tests) { + await test(); + assertEq(!!weakRef.deref(), true); + clearKeptObjects(); + gc(); + if (weakRef.deref()) { + throw new Error(`object ${nextId} was not collected`); + } + } +})(); diff --git a/js/src/jit-test/tests/generators/bug1664463.js b/js/src/jit-test/tests/generators/bug1664463.js new file mode 100644 index 0000000000..a6a45b4907 --- /dev/null +++ b/js/src/jit-test/tests/generators/bug1664463.js @@ -0,0 +1,31 @@ +// |jit-test| skip-if: !('gc' in this) || !('drainJobQueue' in this) +// Test that an unused local in an async function is not kept alive by a closure +// (bug 1412202). Based on a test case by Andy Wingo in bug 1664463. + +let nfinalized = 0; +let finalizers = new FinalizationRegistry(f => { nfinalized++; }); + +class A { + constructor(callback) { + this.b = {callback}; + finalizers.register(this, this.b, this); + } +} + +const LOOP_COUNT = 200; +const FINALIZER_COUNT = 200; + +async function main() { + for (let j = 0; j < LOOP_COUNT; j++) { + for (let i = 0; i < FINALIZER_COUNT; i++) { + let console = globalThis.console; + let obj = new A(() => console.log("hello")); + } + drainJobQueue(); + } + + gc(); + drainJobQueue(); + assertEq(nfinalized, LOOP_COUNT * FINALIZER_COUNT, "all objects should be finalized"); +} +main(); diff --git a/js/src/jit-test/tests/generators/bug1673080.js b/js/src/jit-test/tests/generators/bug1673080.js new file mode 100644 index 0000000000..a2b590c748 --- /dev/null +++ b/js/src/jit-test/tests/generators/bug1673080.js @@ -0,0 +1,23 @@ +// |jit-test| --code-coverage + +"use strict"; + +let s = `\ +(function* () { +yield 0; +try { v1; } catch (exc) { yield [1, exc]; } +try { v255; } catch (exc) { yield [2, exc]; } +let v255, v254, v253, v252, v251, v250, v249, v248, v247, v246, v245, v244, v243, v242, v241, v240, v239, v238, v237, v236, v235, v234, v233, v232, v231, v230, v229, v228, v227, v226, v225, v224, v223, v222, v221, v220, v219, v218, v217, v216, v215, v214, v213, v212, v211, v210, v209, v208, v207, v206, v205, v204, v203, v202, v201, v200, v199, v198, v197, v196, v195, v194, v193, v192; +let v191, v190, v189, v188, v187, v186, v185, v184, v183, v182, v181, v180, v179, v178, v177, v176, v175, v174, v173, v172, v171, v170, v169, v168, v167, v166, v165, v164, v163, v162, v161, v160, v159, v158, v157, v156, v155, v154, v153, v152, v151, v150, v149, v148, v147, v146, v145, v144, v143, v142, v141, v140, v139, v138, v137, v136, v135, v134, v133, v132, v131, v130, v129, v128; +let v127, v126, v125, v124, v123, v122, v121, v120, v119, v118, v117, v116, v115, v114, v113, v112, v111, v110, v109, v108, v107, v106, v105, v104, v103, v102, v101, v100, v99, v98, v97, v96, v95, v94, v93, v92, v91, v90, v89, v88, v87, v86, v85, v84, v83, v82, v81, v80, v79, v78, v77, v76, v75, v74, v73, v72, v71, v70, v69, v68, v67, v66, v65, v64; +let v63, v62, v61, v60, v59, v58, v57, v56, v55, v54, v53, v52, v51, v50, v49, v48, v47, v46, v45, v44, v43, v42, v41, v40, v39, v38, v37, v36, v35, v34, v33, v32, v31, v30, v29, v28, v27, v26, v25, v24, v23, v22, v21, v20, v19, v18, v17, v16, v15, v14, v13, v12, v11, v10, v9, v8, v7, v6, v5, v4, v3, v2, v1, v0; + +let u255, u254, u253, u252, u251, u250, u249, u248, u247, u246, u245, u244, u243, u242, u241, u240, u239, u238, u237, u236, u235, u234, u233, u232, u231, u230, u229, u228, u227, u226, u225, u224, u223, u222, u221, u220, u219, u218, u217, u216, u215, u214, u213, u212, u211, u210, u209, u208, u207, u206, u205, u204, u203, u202, u201, u200, u199, u198, u197, u196, u195, u194, u193, u192; +let u191, u190, u189, u188, u187, u186, u185, u184, u183, u182, u181, u180, u179, u178, u177, u176, u175, u174, u173, u172, u171, u170, u169, u168, u167, u166, u165, u164, u163, u162, u161, u160, u159, u158, u157, u156, u155, u154, u153, u152, u151, u150, u149, u148, u147, u146, u145, u144, u143, u142, u141, u140, u139, u138, u137, u136, u135, u134, u133, u132, u131, u130, u129, u128; +let u127, u126, u125, u124, u123, u122, u121, u120, u119, u118, u117, u116, u115, u114, u113, u112, u111, u110, u109, u108, u107, u106, u105, u104, u103, u102, u101, u100, u99, u98, u97, u96, u95, u94, u93, u92, u91, u90, u89, u88, u87, u86, u85, u84, u83, u82, u81, u80, u79, u78, u77, u76, u75, u74, u73, u72, u71, u70, u69, u68, u67, u66, u65, u64; +let u63, u62, u61, u60, u59, u58, u57, u56, u55, u54, u53, u52, u51, u50, u49, u48, u47, u46, u45, u44, u43, u42, u41, u40, u39, u38, u37, u36, u35, u34, u33, u32, u31, u30, u29, u28, u27, u26, u25, u24, u23, u22, u21, u20, u19, u18, u17, u16, u15, u14, u13, u12, u11, u10, u9, u8, u7, u6, u5, u4, u3, u2, u1, u0; + +}) +`; +let gen = eval(s); +gen().next(); // don't assert diff --git a/js/src/jit-test/tests/generators/bug1767181.js b/js/src/jit-test/tests/generators/bug1767181.js new file mode 100644 index 0000000000..54892be81f --- /dev/null +++ b/js/src/jit-test/tests/generators/bug1767181.js @@ -0,0 +1,14 @@ +var caught = false; +function* a() { + try { + try { + yield; + } finally { + for (b = 0; b < 20; b++) {} + } + } catch (e) { caught = true; } +} +c = a(); +c.next(); +c.return(); +assertEq(caught, false); diff --git a/js/src/jit-test/tests/generators/bug1773628.js b/js/src/jit-test/tests/generators/bug1773628.js new file mode 100644 index 0000000000..8fe578d71e --- /dev/null +++ b/js/src/jit-test/tests/generators/bug1773628.js @@ -0,0 +1,13 @@ +// |jit-test| --fast-warmup +function* f() { + try { + yield; + } finally { + for (let b = 0; b < 10; b++) {} + } +} +for (var i = 0; i < 50; i++) { + let c = f(); + c.next(); + c.return(); +} diff --git a/js/src/jit-test/tests/generators/bug1791968.js b/js/src/jit-test/tests/generators/bug1791968.js new file mode 100644 index 0000000000..8438981ddf --- /dev/null +++ b/js/src/jit-test/tests/generators/bug1791968.js @@ -0,0 +1,33 @@ +function opaqueThrow() { with ({}) {} throw 3; } + +function* foo(n) { + try { + // Get into the catch block. + opaqueThrow(); + } catch(v12) { + // Yield a value. + yield 1; + } finally { + // OSR into Warp with JS_GENERATOR_CLOSING + // as the pushed exception. + for (let i = 0; i < 100; i++) { } + + // Create an RPow. + var x = Math.pow(1,n); + + // When the finally block terminates, we re-throw + // JS_GENERATOR_CLOSING, and rematerialize the frame + // for HandleClosingGeneratorReturn, triggering + // recovery of the RPow. + } +} + +for (let i = 0; i < 30; i++) { + let gen = foo(1); + + // Advance to the yield in the catch block. + gen.next(); + + // Close the generator. + gen.return(); +} diff --git a/js/src/jit-test/tests/generators/bug1811171.js b/js/src/jit-test/tests/generators/bug1811171.js new file mode 100644 index 0000000000..4f6bbb50fa --- /dev/null +++ b/js/src/jit-test/tests/generators/bug1811171.js @@ -0,0 +1,14 @@ +function* gen(z) { + try { + yield 1; + } finally { + for (let i = 0; i < 10; i++) {} + var x = z + 1; + } +} + +for (let i = 0; i < 20; i++) { + let g = gen(1); + g.next(); + g.return(); +} diff --git a/js/src/jit-test/tests/generators/bug908920.js b/js/src/jit-test/tests/generators/bug908920.js new file mode 100644 index 0000000000..5de57c546e --- /dev/null +++ b/js/src/jit-test/tests/generators/bug908920.js @@ -0,0 +1,9 @@ +if (typeof schedulegc != 'undefined') { + Function("\ + x = (function*() { yield })();\ + new Set(x);\ + schedulegc(1);\ + print( /x/ );\ + for (p in x) {}\ + ")(); +} diff --git a/js/src/jit-test/tests/generators/bug931414.js b/js/src/jit-test/tests/generators/bug931414.js new file mode 100644 index 0000000000..df1f24d094 --- /dev/null +++ b/js/src/jit-test/tests/generators/bug931414.js @@ -0,0 +1,11 @@ +// |jit-test| error: TypeError + +load(libdir + "iteration.js"); + +function iterable() { + var iterable = {}; + iterable[Symbol.iterator] = () => ({next: () => void 0}); + return iterable; +} + +(function*(){yield*iterable()}()).next(); diff --git a/js/src/jit-test/tests/generators/closing-osr.js b/js/src/jit-test/tests/generators/closing-osr.js new file mode 100644 index 0000000000..0e0fcfe68d --- /dev/null +++ b/js/src/jit-test/tests/generators/closing-osr.js @@ -0,0 +1,24 @@ +// OSR into a |finally| block while closing a legacy generator should work. +var log = ""; +function* f() { + try { + try { + log += "a"; + yield 2; + log += "b"; + yield 3; + } finally { + log += "c"; + for (var i=0; i<20; i++) {}; + log += "d"; + } + } catch(e) { + log += "e"; + } + log += "f"; +} + +var it = f(); +assertEq(it.next().value, 2); +it.return(); +assertEq(log, "acd"); diff --git a/js/src/jit-test/tests/generators/es6-syntax.js b/js/src/jit-test/tests/generators/es6-syntax.js new file mode 100644 index 0000000000..e805a0b62b --- /dev/null +++ b/js/src/jit-test/tests/generators/es6-syntax.js @@ -0,0 +1,34 @@ +// Test interactions between ES6 generators and not-yet-standard +// features. + +function assertSyntaxError(str) { + var msg; + var evil = eval; + try { + // Non-direct eval. + evil(str); + } catch (exc) { + if (exc instanceof SyntaxError) + return; + msg = "Assertion failed: expected SyntaxError, got " + exc; + } + if (msg === undefined) + msg = "Assertion failed: expected SyntaxError, but no exception thrown"; + throw new Error(msg + " - " + str); +} + +// Destructuring binding. +assertSyntaxError("function* f(x = yield) {}"); +assertSyntaxError("function* f(x = yield 17) {}"); +assertSyntaxError("function* f([yield]) {}"); +assertSyntaxError("function* f({ yield }) {}"); +assertSyntaxError("function* f(...yield) {}"); + +// For each. +assertSyntaxError("for yield"); +assertSyntaxError("for yield (;;) {}"); +assertSyntaxError("for yield (x of y) {}"); +assertSyntaxError("for yield (var i in o) {}"); + +// Expression bodies. +assertSyntaxError("function* f() yield 7"); diff --git a/js/src/jit-test/tests/generators/limits.js b/js/src/jit-test/tests/generators/limits.js new file mode 100644 index 0000000000..0e44284d90 --- /dev/null +++ b/js/src/jit-test/tests/generators/limits.js @@ -0,0 +1,90 @@ +// |jit-test| skip-if: getBuildConfiguration("wasi") +// +// Tests aimed at AbstractGeneratorObject::FixedSlotLimit. + +"use strict"; + +function test(n) { + const iterate = (start, f) => { + let value = start; + for (let i = n; i-- > 0; ) { + value = f(value, i); + } + return value; + }; + + const generate = (start, f) => { + let s = iterate(start, f); + let gen = eval('(function* () {\n' + s + '})'); + return gen(); + }; + + // Test 1: many vars in the function scope + { + let it = generate( + "yield 0;", + (s, i) => ` + var v${i} = ${i}; + ${s} + assertEq(v${i}, ${i}); + ` + ); + assertEq(it.next().done, false); + assertEq(it.next().done, true); + } + + // Test 2: many let-bindings in nested lexical scopes + { + let it = generate( + "yield a => v174;", + (s, i) => { + let block = ` + let v${i} = ${i}; + ${s} + assertEq(v${i}, ${i}); + `; + if (i % 17 == 0) { + block = '{\n' + block + '}\n'; + } + return block; + } + ); + assertEq(it.next().done, false); + assertEq(it.next().done, true); + } + + // Test 3: TDZ is preserved across yield + { + let it = generate( + 'yield 0;\n' + + 'try { v1; } catch (exc) { yield [1, exc]; }\n' + + `try { v${n - 1}; } catch (exc) { yield [2, exc]; }\n`, + (s, i) => { + let block = ` + ${s} + let v${i}; + `; + if (i % 256 == 0) { + block = '{\n' + block + '}\n'; + } + return block; + } + ); + let {value, done} = it.next(); + assertEq(value, 0); + ({value, done} = it.next()); + assertEq(value[0], 1); + assertEq(value[1].name, "ReferenceError"); + ({value, done} = it.next()); + assertEq(value[0], 2); + assertEq(value[1].name, "ReferenceError"); + ({value, done} = it.next()); + assertEq(done, true); + } +} + +for (let exp = 8; exp < 12; exp++) { + const n = 2 ** exp; + test(n - 2); + test(n + 1); +} diff --git a/js/src/jit-test/tests/generators/next-on-finished.js b/js/src/jit-test/tests/generators/next-on-finished.js new file mode 100644 index 0000000000..66c25a13f7 --- /dev/null +++ b/js/src/jit-test/tests/generators/next-on-finished.js @@ -0,0 +1,6 @@ +function*g(){ }; +o = g(); +o.next(); +result = o.next(); +assertEq(result.done, true); +assertEq(o.value, undefined); diff --git a/js/src/jit-test/tests/generators/relazify-arguments-usage.js b/js/src/jit-test/tests/generators/relazify-arguments-usage.js new file mode 100644 index 0000000000..0c37d9b56c --- /dev/null +++ b/js/src/jit-test/tests/generators/relazify-arguments-usage.js @@ -0,0 +1,10 @@ + +function * f() { + yield arguments; + yield arguments; + yield arguments; +} + +for (x of f()) { + relazifyFunctions(); +} diff --git a/js/src/jit-test/tests/generators/return-break-continue.js b/js/src/jit-test/tests/generators/return-break-continue.js new file mode 100644 index 0000000000..1d3070e28a --- /dev/null +++ b/js/src/jit-test/tests/generators/return-break-continue.js @@ -0,0 +1,66 @@ +load(libdir + "iteration.js"); + +// break in finally. +function *f1() { + L: try { + yield 1; + } finally { + break L; + } + return 2; +} +it = f1(); +assertIteratorNext(it, 1); +assertIteratorResult(it.return(4), 2, true); +assertIteratorDone(it); + +// continue in finally, followed by return. +function *f2() { + do try { + yield 1; + } catch (e) { + assertEq(0, 1); + } finally { + continue; + } while (0); + return 2; +} +it = f2(); +assertIteratorNext(it, 1); +assertIteratorResult(it.return(4), 2, true); +assertIteratorDone(it); + +// continue in finally, followed by yield. +function *f3() { + do try { + yield 1; + } catch (e) { + assertEq(0, 1); + } finally { + continue; + } while (0); + yield 2; +} +it = f3(); +assertIteratorNext(it, 1); +assertIteratorResult(it.return(4), 2, false); +assertIteratorDone(it); + +// continue in finally. +function *f4() { + var i = 0; + while (true) { + try { + yield i++; + } finally { + if (i < 3) + continue; + } + } +} +it = f4(); +assertIteratorNext(it, 0); +assertIteratorResult(it.return(-1), 1, false); +assertIteratorResult(it.return(-2), 2, false); +assertIteratorResult(it.return(-3), -3, true); +assertIteratorDone(it); diff --git a/js/src/jit-test/tests/generators/return.js b/js/src/jit-test/tests/generators/return.js new file mode 100644 index 0000000000..b71b38921e --- /dev/null +++ b/js/src/jit-test/tests/generators/return.js @@ -0,0 +1,181 @@ +// |jit-test| error:done + +load(libdir + "iteration.js"); + +function *f1() { + yield 1; + yield 2; +} + +// Return after initial yield. +var it = f1(); +assertIteratorResult(it.return(3), 3, true); +assertIteratorResult(it.return(Math), Math, true); +assertIteratorResult(it.return(), undefined, true); +assertIteratorDone(it); + +// Return after other yield. +it = f1(); +assertIteratorNext(it, 1); +assertIteratorResult(it.return(null), null, true); +assertIteratorDone(it); + +// Finally blocks should run and can override the return value. +function *f2() { + try { + yield 1; + yield 2; + } finally { + return 9; + } +} +it = f2(); +assertIteratorNext(it, 1); +assertIteratorResult(it.return(3), 9, true); +assertIteratorDone(it); + +// Yield in finally block can override the return, but we should still +// return the correct value after that. +function *f3() { + try { + try { + yield 1; + yield 2; + } finally { + yield 3; + } + } finally { + yield 4; + } +} +it = f3(); +assertIteratorNext(it, 1); +assertIteratorResult(it.return(9), 3, false); +assertIteratorNext(it, 4); +assertIteratorDone(it, 9); +assertIteratorDone(it, undefined); + +// Finally block can throw. +function *f4() { + try { + yield 1; + yield 2; + } finally { + throw 3; + } +} +it = f4(); +assertIteratorNext(it, 1); +assertThrowsValue(() => it.return(8), 3); +assertIteratorDone(it); + +function *f5() {} +it = f5(); +assertIteratorDone(it); +assertIteratorResult(it.return(3), 3, true); +assertIteratorDone(it); + +function *f6() { + try { + yield 1; + yield 2; + } finally { + try { + return 9; + } finally { + yield 3; + } + } +} +it = f6(); +assertIteratorNext(it, 1); +assertIteratorResult(it.return(5), 3, false); +assertIteratorDone(it, 9); +assertIteratorDone(it); + +// If we yield in a finally block, a second .return() can override +// the first one. +function *f7() { + try { + yield 1; + yield 2; + } finally { + try { + yield 3; + } finally { + yield 4; + } + } +} +it = f7(); +assertIteratorNext(it, 1); +assertIteratorResult(it.return(5), 3, false); +assertIteratorResult(it.return(6), 4, false); +assertIteratorDone(it, 6); +assertIteratorDone(it); + +// If we yield in a finally block, .throw() should work. +function *f8() { + try { + yield 1; + yield 2; + } finally { + yield 3; + } +} +it = f8(); +assertIteratorNext(it, 1); +assertIteratorResult(it.return(5), 3, false); +assertThrowsValue(() => it.throw(4), 4); +assertIteratorDone(it); + +// If the generator is already running, we should throw a TypeError. +function *f9() { + try { + yield 1; + yield 2; + } finally { + it.return(4); + yield 3; + } +} +it = f9(); +assertIteratorNext(it, 1); +assertThrowsInstanceOf(() => it.return(5), TypeError); +assertIteratorDone(it); +assertIteratorDone(it); + +// Second return overrides first one and closes the generator. +function *f10() { + try { + yield 1; + } finally { + yield 2; + } +} +it = f10(); +assertIteratorNext(it, 1); +assertIteratorResult(it.return(-1), 2, false); +assertIteratorResult(it.return(-2), -2, true); +assertIteratorDone(it); + +function *f11() { + try { + try { + yield 1; + } finally { + throw 2; + } + } catch(e) { + yield e; + } finally { + yield 3; + } +} +it = f11(); +assertIteratorNext(it, 1); +assertIteratorResult(it.return(9), 2, false); +assertIteratorNext(it, 3); +assertIteratorDone(it); + +throw "done"; diff --git a/js/src/jit-test/tests/generators/throw-closes.js b/js/src/jit-test/tests/generators/throw-closes.js new file mode 100644 index 0000000000..c190ccdef5 --- /dev/null +++ b/js/src/jit-test/tests/generators/throw-closes.js @@ -0,0 +1,42 @@ +// When a generator function throws, the generator is closed. + +load(libdir + "asserts.js"); +load(libdir + "iteration.js"); + +// Star generator, next() throws. +function *g() { + yield 1; + yield 2; + throw 3; + yield 4; +} +var i = g(); +assertIteratorNext(i, 1); +assertIteratorNext(i, 2); +assertThrowsValue(() => i.next(), 3); +assertIteratorDone(i); +assertIteratorDone(i); + +// Star generator, throw() throws. +function *h() { + yield 1; + yield 2; +} +var i = h(); +assertIteratorNext(i, 1); +assertThrowsValue(() => i.throw(4), 4); +assertIteratorDone(i); + +// Star generator, return() throws. +function *h2() { + try { + yield 1; + yield 2; + } finally { + throw 6; + } +} +var i = h2(); +assertIteratorNext(i, 1); +assertThrowsValue(() => i.return(4), 6); +assertIteratorDone(i); diff --git a/js/src/jit-test/tests/generators/throw-on-finished.js b/js/src/jit-test/tests/generators/throw-on-finished.js new file mode 100644 index 0000000000..541e48f1f0 --- /dev/null +++ b/js/src/jit-test/tests/generators/throw-on-finished.js @@ -0,0 +1,7 @@ +load(libdir + "asserts.js"); + +function*g(){ }; +o = g(); +o.next(); +function TestException() {}; +assertThrowsInstanceOf(() => o.throw(new TestException()), TestException); diff --git a/js/src/jit-test/tests/generators/wrappers.js b/js/src/jit-test/tests/generators/wrappers.js new file mode 100644 index 0000000000..40328f3183 --- /dev/null +++ b/js/src/jit-test/tests/generators/wrappers.js @@ -0,0 +1,27 @@ +// Generator methods work transparently on CrossCompartmentWrappers. + +load(libdir + "asserts.js"); +load(libdir + "iteration.js"); + +var g = newGlobal(); + +function *gen3() { yield 1; yield 2; } +it = gen3(); +g.eval("function *gen4() { yield 5; yield 6; }; var it4 = gen4();"); + +// StarGenerator.next +assertIteratorResult(it.next.call(g.it4), 5, false) + +// StarGenerator.throw +assertThrowsValue(() => it.throw.call(g.it4, 8), 8); + +// StarGenerator.return +assertIteratorResult(it.return.call(g.it4, 8), 8, true); + +// Other objects should throw. +try { + it.next.call([]); + assertEq(0, 1); +} catch (e) { + assertEq(e.toString().includes("called on incompatible Array"), true); +} diff --git a/js/src/jit-test/tests/generators/yield-exception-stack-in-finally.js b/js/src/jit-test/tests/generators/yield-exception-stack-in-finally.js new file mode 100644 index 0000000000..6a1a0ef006 --- /dev/null +++ b/js/src/jit-test/tests/generators/yield-exception-stack-in-finally.js @@ -0,0 +1,33 @@ +load(libdir + "asserts.js"); + +var g = newGlobal({newCompartment: true}) + +// Throw an exception from a different compartment. +var thrower = g.Function(` + throw 0; +`); + +function* throwAndYield() { + try { + thrower(); + } finally { + // The |finally| block is entered with the exception stack on the function + // stack. The function stack is saved in the generator object when executing + // |yield|, so the exception stack, which was created in a different + // compartment, has to be wrapped into the current compartment. + yield; + } +} +assertThrowsValue(() => [...throwAndYield()], 0); + +function* throwAndNuke() { + try { + thrower(); + } finally { + // The function stack contains the wrapped exception stack when entering + // the |finally| block. When nuking all CCWs it becomes a dead wrapper. + nukeAllCCWs(); + yield; + } +} +assertThrowsValue(() => [...throwAndNuke()], 0); diff --git a/js/src/jit-test/tests/generators/yield-in-finally.js b/js/src/jit-test/tests/generators/yield-in-finally.js new file mode 100644 index 0000000000..9d73721823 --- /dev/null +++ b/js/src/jit-test/tests/generators/yield-in-finally.js @@ -0,0 +1,164 @@ +// return value in try block should not be overridden by yield in finally block. + +load(libdir + "asserts.js"); + +// simple +function* g1() { + try { + return 42; + } finally { + yield 43; + } +} +var o = g1(); +var v = o.next(); +assertEq(v.done, false); +assertEq(v.value, 43); +v = o.next(); +assertEq(v.done, true); +assertEq(v.value, 42); +v = o.next(); +assertEq(v.done, true); +assertEq(v.value, undefined); + +// without return value +function* g2() { + try { + return; + } finally { + yield 43; + } +} +o = g2(); +v = o.next(); +assertEq(v.done, false); +assertEq(v.value, 43); +v = o.next(); +assertEq(v.done, true); +assertEq(v.value, undefined); +v = o.next(); +assertEq(v.done, true); +assertEq(v.value, undefined); + +// nested try-finally +function* g3() { + try { + try { + return 42; + } finally { + try { + return 43; + } finally { + yield 44; + } + } + } finally { + yield 45; + } +} +o = g3(); +v = o.next(); +assertEq(v.done, false); +assertEq(v.value, 44); +v = o.next(); +assertEq(v.done, false); +assertEq(v.value, 45); +v = o.next(); +assertEq(v.done, true); +assertEq(v.value, 43); +v = o.next(); +assertEq(v.done, true); +assertEq(v.value, undefined); + +// yield* +function* g4() { + try { + return 42; + } finally { + try { + return 43; + } finally { + yield* g5(); + } + } +} +function* g5() { + yield 44; + return 45; +} +o = g4(); +v = o.next(); +assertEq(v.done, false); +assertEq(v.value, 44); +v = o.next(); +assertEq(v.done, true); +assertEq(v.value, 43); +v = o.next(); +assertEq(v.done, true); +assertEq(v.value, undefined); + +// return in block scope +function* g6() { + let a = 10; + { + let a = 20; + try { + let a = 30; + { + let a = 40; + return 42; + } + } finally { + yield 43; + } + } +} +o = g6(); +v = o.next(); +assertEq(v.done, false); +assertEq(v.value, 43); +v = o.next(); +assertEq(v.done, true); +assertEq(v.value, 42); +v = o.next(); +assertEq(v.done, true); +assertEq(v.value, undefined); + +// no finally +function* g7() { + try { + return 42; + } catch (e) { + yield 1; + } +} +o = g7(); +v = o.next(); +assertEq(v.done, true); +assertEq(v.value, 42); +v = o.next(); +assertEq(v.done, true); +assertEq(v.value, undefined); + +// in "with" statement +eval(` +function* g9() { + with ({ ".genrval": { value: 44, done: false } }) { + try { + return 42; + } finally { + yield 43; + } + } +} +o = g9(); +v = o.next(); +assertEq(v.done, false); +assertEq(v.value, 43); +v = o.next(); +assertEq(v.done, true); +assertEq(v.value, 42); +v = o.next(); +assertEq(v.done, true); +assertEq(v.value, undefined); +`); diff --git a/js/src/jit-test/tests/generators/yield-regexp.js b/js/src/jit-test/tests/generators/yield-regexp.js new file mode 100644 index 0000000000..30f6a02982 --- /dev/null +++ b/js/src/jit-test/tests/generators/yield-regexp.js @@ -0,0 +1,28 @@ +// Bug 1099956 + +load(libdir + "asserts.js"); + +// Parses as IDENT(yield) DIV IDENT(abc) DIV IDENT(g). +eval(`function f1() { yield /abc/g; }`); + +// Throws a ReferenceError because no global "yield" variable is defined. +var ex; +try { + f1(); +} catch(e) { + ex = e; +} +assertEq(ex instanceof ReferenceError, true); + +// Parses as YIELD REGEXP(/abc/g). +function* f2() { + yield /abc/g; +} + +g = f2(); +v = g.next(); +assertEq(v.done, false); +assertEq(v.value instanceof RegExp, true); +assertEq(v.value.toString(), "/abc/g"); +v = g.next(); +assertEq(v.done, true); diff --git a/js/src/jit-test/tests/generators/yield-yield.js b/js/src/jit-test/tests/generators/yield-yield.js new file mode 100644 index 0000000000..4c468ba5af --- /dev/null +++ b/js/src/jit-test/tests/generators/yield-yield.js @@ -0,0 +1,12 @@ +// Bug 880447 + +load(libdir + "asserts.js"); + +function* f() { + yield yield 1; +} + +var g = f(); +assertEq(g.next().value, 1); +assertEq(g.return("hello").value, "hello"); +assertEq(g.next().value, undefined); |