From 36d22d82aa202bb199967e9512281e9a53db42c9 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 21:33:14 +0200 Subject: Adding upstream version 115.7.0esr. Signed-off-by: Daniel Baumann --- js/src/tests/non262/Array/from_async.js | 302 ++++++++++++++++++++++++++++++++ 1 file changed, 302 insertions(+) create mode 100644 js/src/tests/non262/Array/from_async.js (limited to 'js/src/tests/non262/Array/from_async.js') diff --git a/js/src/tests/non262/Array/from_async.js b/js/src/tests/non262/Array/from_async.js new file mode 100644 index 0000000000..c104c26f7d --- /dev/null +++ b/js/src/tests/non262/Array/from_async.js @@ -0,0 +1,302 @@ +// |reftest| shell-option(--enable-array-from-async) skip-if(!xulRuntime.shell||!Array.fromAsync) -- needs drainJobQueue + +// Basic Smoke Test +async function* asyncGen(n) { + for (let i = 0; i < n; i++) { + yield i * 2; + } +} + +let done = false; +Array.fromAsync(asyncGen(4)).then((x) => { + assertEq(Array.isArray(x), true); + assertEq(x.length, 4); + assertEq(x[0], 0); + assertEq(x[1], 2); + assertEq(x[2], 4); + assertEq(x[3], 6); + done = true; +} +); + +drainJobQueue(); +assertEq(done, true); + +(async function () { + class InterruptableAsyncIterator { + count = 0 + closed = false + throwAfter = NaN + constructor(n, throwAfter = NaN) { + this.count = n; + this.throwAfter = throwAfter; + } + [Symbol.asyncIterator] = function () { + return { + iter: this, + i: 0, + async next() { + if (this.i > this.iter.throwAfter) { + throw "Exception" + } + if (this.i++ < this.iter.count) { + return Promise.resolve({ done: false, value: this.i - 1 }); + } + return Promise.resolve({ done: true, value: undefined }); + }, + async return(x) { + this.iter.closed = true; + return { value: x, done: true }; + } + } + } + } + + var one = await Array.fromAsync(new InterruptableAsyncIterator(2)); + assertEq(one.length, 2) + assertEq(one[0], 0); + assertEq(one[1], 1); + + var two = new InterruptableAsyncIterator(10, 2); + var threw = false; + try { + var res = await Array.fromAsync(two); + } catch (e) { + threw = true; + assertEq(e, "Exception"); + } + assertEq(threw, true); + // The iterator is not closed unless we have an abrupt completion while mapping. + assertEq(two.closed, false); + + // Test throwing while mapping: Iterator should be closed. + var three = new InterruptableAsyncIterator(10, 9); + threw = false; + try { + var res = await Array.fromAsync(three, (x) => { + if (x > 3) { + throw "Range" + } + return x; + }); + } catch (e) { + assertEq(e, "Range"); + threw = true; + } + assertEq(threw, true); + assertEq(three.closed, true); + + var sync = await Array.fromAsync([1, 2, 3]); + assertEq(sync.length, 3); + assertEq(sync[0], 1) + assertEq(sync[1], 2) + assertEq(sync[2], 3) + + let closed_frozen = false; + class Frozen { + constructor(x) { + this.count = x; + Object.freeze(this); + } + [Symbol.asyncIterator] = function () { + return { + iter: this, + i: 0, + async next() { + if (this.i++ < this.iter.count) { + return Promise.resolve({ done: false, value: this.i - 1 }); + } + return Promise.resolve({ done: true, value: undefined }); + }, + async return(x) { + // Can't use Frozen instance, becuse frozen is frozen. + closed_frozen = true; + return { value: x, done: true }; + } + } + } + } + + // We should close the iterator when define property throws. + // Test by defining into a frozen object. + var frozen = new Frozen(10); + threw = false; + try { + var result = await Array.fromAsync.call(Frozen, frozen); + } catch (e) { + threw = true; + } + + assertEq(threw, true); + assertEq(closed_frozen, true); + +})(); + +drainJobQueue(); + +(async function () { + var badSyncIterator = { + [Symbol.iterator]() { + return null; + } + }; + + var badAsyncIterator = { + [Symbol.asyncIterator]() { + return null; + } + }; + + async function errorMessage(fn) { + try { + await fn(); + } catch (e) { + return e.message; + } + throw new Error("missing error"); + } + + // Ensure Array.from and Array.fromAsync use consistent error reporting. + var expected = await errorMessage(() => Array.from(badSyncIterator)); + var actual = await errorMessage(() => Array.fromAsync(badSyncIterator)); + assertEq(actual, expected); + + // Ensure for-of iteration and Array.fromAsync use consistent error reporting. + var expected = await errorMessage(() => { for (var _ of badSyncIterator); }); + var actual = await errorMessage(() => Array.fromAsync(badSyncIterator)); + assertEq(actual, expected); + + // Ensure await for-of iteration and Array.fromAsync use consistent error reporting. + var expected = await errorMessage(async () => { for await (var _ of badAsyncIterator); }); + var actual = await errorMessage(() => Array.fromAsync(badAsyncIterator)); + assertEq(actual, expected); +})(); + +drainJobQueue(); + +(async function () { + function* gen() { + for (let i = 0; i < 4; ++i) { + yield Promise.resolve(i); + } + }; + + var array = await Array.fromAsync(gen()); + + // Promise values are unwrapped via AsyncFromSyncIterator. + assertEqArray(array, [0, 1, 2, 3]); +})(); + +drainJobQueue(); + +(async function () { + var badSyncIterator = { + [Symbol.iterator]: 123, + }; + + var badAsyncIterator = { + [Symbol.asyncIterator]: 123, + }; + + async function errorMessage(fn) { + try { + await fn(); + } catch (e) { + return e.message; + } + throw new Error("missing error"); + } + + // Ensure Array.from and Array.fromAsync use consistent error reporting. + var expected = await errorMessage(() => Array.from(badSyncIterator)); + var actual = await errorMessage(() => Array.fromAsync(badSyncIterator)); + assertEq(actual.endsWith("is not iterable"), expected.endsWith("is not iterable")); + + // Ensure for-of iteration and Array.fromAsync use consistent error reporting. + var expected = await errorMessage(() => { for (var _ of badSyncIterator); }); + var actual = await errorMessage(() => Array.fromAsync(badSyncIterator)); + assertEq(actual.endsWith("is not iterable"), expected.endsWith("is not iterable")); + + // Ensure await for-of iteration and Array.fromAsync use consistent error reporting. + var expected = await errorMessage(async () => { for await (var _ of badAsyncIterator); }); + var actual = await errorMessage(() => Array.fromAsync(badAsyncIterator)); + assertEq(actual.endsWith("is not iterable"), expected.endsWith("is not iterable")); +})(); + +drainJobQueue(); + + +var g = newGlobal(); +g.asyncGen = asyncGen; +var p = g.evaluate(` +Array.fromAsync(asyncGen(4)) +`) + +p.then((x) => { + assertEq(x instanceof Array, false); // Should use the other global's Array. + assertEq(x instanceof g.Array, true); +}) + +drainJobQueue(); + + +var g2 = newGlobal({ newCompartment: true }); +g2.asyncGen = asyncGen; +var p = g2.evaluate(` +Array.fromAsync(asyncGen(4)) +`) + +p.then((x) => { + assertEq(x instanceof Array, false); // Should use the other global's Array. + assertEq(x instanceof g2.Array, true); + nukeCCW(x); // this will throw if x happens to not be a CCW (it should be!) +}) +drainJobQueue(); + +// Test having a CCW 'this' value. +g2.obj = {}; +var p2 = g2.evaluate(` +Array.fromAsync.call(obj, asyncGen(4)) +`) + +p2.then((x) => { + assertEq(x instanceof Array, false); // Should use the other global's Array. + assertEq(x instanceof g2.Array, true); + nukeCCW(x); +}) + +drainJobQueue(); + +// Verify user promise resolution behaviour. +var myThenCalled = false; +var obj = { then: () => { myThenCalled = true; } } +function* genO() { + yield obj; + return; +} + +var res = Array.fromAsync(genO()); +res.then((x) => { + assertEq(x[0], obj); + assertEq(myThenCalled, true); +}); + +drainJobQueue(); + +function* thrower() { + throw new Error(); +} + +g2.thrower = thrower; +var p = g2.evaluate(`Array.fromAsync(thrower())`) +p.catch((e) => { + assertEq(e instanceof Error, true, "Should throw an error from the current global"); +}) +drainJobQueue(); + +p = g2.evaluate(`Array.fromAsync(thrower, 1)`); +p.catch((e) => assertEq(e instanceof g2.Error, true, "Should throw error from g2")) +drainJobQueue(); + +if (typeof reportCompare === 'function') + reportCompare(true, true); -- cgit v1.2.3