summaryrefslogtreecommitdiffstats
path: root/js/src/tests/non262/Array/from_async.js
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--js/src/tests/non262/Array/from_async.js302
1 files changed, 302 insertions, 0 deletions
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);