diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /js/src/tests/non262/AsyncIterator | |
parent | Initial commit. (diff) | |
download | firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'js/src/tests/non262/AsyncIterator')
165 files changed, 4672 insertions, 0 deletions
diff --git a/js/src/tests/non262/AsyncIterator/asynciterator.js b/js/src/tests/non262/AsyncIterator/asynciterator.js new file mode 100644 index 0000000000..048c3b4392 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/asynciterator.js @@ -0,0 +1,13 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) -- AsyncIterator is not enabled unconditionally +/*--- + Property descriptor of AsyncIterator. +---*/ + +const propDesc = Reflect.getOwnPropertyDescriptor(this, 'AsyncIterator'); +assertEq(propDesc.value, AsyncIterator); +assertEq(propDesc.writable, true); +assertEq(propDesc.enumerable, false); +assertEq(propDesc.configurable, true); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/constructor-subclassable.js b/js/src/tests/non262/AsyncIterator/constructor-subclassable.js new file mode 100644 index 0000000000..4e8061cfe7 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/constructor-subclassable.js @@ -0,0 +1,12 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) +/*--- + AsyncIterator constructor can be subclassed. +---*/ + +class TestIterator extends AsyncIterator { +} + +assertEq(new TestIterator() instanceof AsyncIterator, true); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/constructor-throw-when-called-directly.js b/js/src/tests/non262/AsyncIterator/constructor-throw-when-called-directly.js new file mode 100644 index 0000000000..8abc132282 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/constructor-throw-when-called-directly.js @@ -0,0 +1,9 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) +/*--- + AsyncIterator constructor throws when called directly. +---*/ + +assertThrowsInstanceOf(() => new AsyncIterator(), TypeError); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/constructor-throw-without-new.js b/js/src/tests/non262/AsyncIterator/constructor-throw-without-new.js new file mode 100644 index 0000000000..699bbed007 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/constructor-throw-without-new.js @@ -0,0 +1,9 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) -- AsyncIterator is not enabled unconditionally +/*--- + AsyncIterator constructor throws when called without new. +---*/ + +assertThrowsInstanceOf(() => AsyncIterator(), TypeError); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/constructor.js b/js/src/tests/non262/AsyncIterator/constructor.js new file mode 100644 index 0000000000..e421c4e98b --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/constructor.js @@ -0,0 +1,9 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) -- AsyncIterator is not enabled unconditionally +/*--- + The AsyncIterator constructor is a built-in function. +---*/ + +assertEq(typeof AsyncIterator, 'function'); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/length.js b/js/src/tests/non262/AsyncIterator/length.js new file mode 100644 index 0000000000..26803fc44e --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/length.js @@ -0,0 +1,13 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) -- AsyncIterator is not enabled unconditionally +/*--- + The "length" property of AsyncIterator +---*/ + +const propDesc = Reflect.getOwnPropertyDescriptor(AsyncIterator, 'length'); +assertEq(propDesc.value, 0); +assertEq(propDesc.writable, false); +assertEq(propDesc.enumerable, false); +assertEq(propDesc.configurable, true); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/name.js b/js/src/tests/non262/AsyncIterator/name.js new file mode 100644 index 0000000000..d8944341b5 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/name.js @@ -0,0 +1,13 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) -- AsyncIterator is not enabled unconditionally +/*--- + The "name" property of AsyncIterator +---*/ + +const propDesc = Reflect.getOwnPropertyDescriptor(AsyncIterator, 'name'); +assertEq(propDesc.value, 'AsyncIterator'); +assertEq(propDesc.writable, false); +assertEq(propDesc.enumerable, false); +assertEq(propDesc.configurable, true); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/proto.js b/js/src/tests/non262/AsyncIterator/proto.js new file mode 100644 index 0000000000..3f19f099c7 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/proto.js @@ -0,0 +1,14 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) -- AsyncIterator is not enabled unconditionally +/*--- + The prototype of the AsyncIterator constructor is the intrinsic object %FunctionPrototype%. +---*/ + +assertEq(Object.getPrototypeOf(AsyncIterator), Function.prototype); + +const propDesc = Reflect.getOwnPropertyDescriptor(AsyncIterator, 'prototype'); +assertEq(propDesc.writable, false); +assertEq(propDesc.enumerable, false); +assertEq(propDesc.configurable, false); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/asIndexedPairs/asIndexedPairs.js b/js/src/tests/non262/AsyncIterator/prototype/asIndexedPairs/asIndexedPairs.js new file mode 100644 index 0000000000..71ae91d602 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/asIndexedPairs/asIndexedPairs.js @@ -0,0 +1,25 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +async function* gen() { + yield 1; + yield 2; + yield 3; +} + +let iter = gen().asIndexedPairs(); + +for (const v of [[0, 1], [1, 2], [2, 3]]) { + iter.next().then( + result => { + console.log(result); + assertEq(result.done, false); + assertEq(result.value[0], v[0]); + assertEq(result.value[1], v[1]); + } + ); +} + +iter.next().then(({done}) => assertEq(done, true)); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/asIndexedPairs/length.js b/js/src/tests/non262/AsyncIterator/prototype/asIndexedPairs/length.js new file mode 100644 index 0000000000..6854cd7dd2 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/asIndexedPairs/length.js @@ -0,0 +1,21 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) +// + +/*--- +esid: pending +description: %AsyncIterator.prototype%.asIndexedPairs length value and descriptor. +info: > + 17 ECMAScript Standard Built-in Objects +features: [iterator-helpers] +---*/ + +assertEq(AsyncIterator.prototype.asIndexedPairs.length, 0); + +const propertyDescriptor = Reflect.getOwnPropertyDescriptor(AsyncIterator.prototype.asIndexedPairs, 'length'); +assertEq(propertyDescriptor.value, 0); +assertEq(propertyDescriptor.enumerable, false); +assertEq(propertyDescriptor.writable, false); +assertEq(propertyDescriptor.configurable, true); + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/asIndexedPairs/name.js b/js/src/tests/non262/AsyncIterator/prototype/asIndexedPairs/name.js new file mode 100644 index 0000000000..c19ac4105e --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/asIndexedPairs/name.js @@ -0,0 +1,20 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +/*--- +esid: pending +description: %AsyncIterator.prototype%.asIndexedPairs.name value and descriptor. +info: > + 17 ECMAScript Standard Built-in Objects +features: [iterator-helpers] +---*/ + +assertEq(AsyncIterator.prototype.asIndexedPairs.name, 'asIndexedPairs'); + +const propertyDescriptor = Reflect.getOwnPropertyDescriptor(AsyncIterator.prototype.asIndexedPairs, 'name'); +assertEq(propertyDescriptor.value, 'asIndexedPairs'); +assertEq(propertyDescriptor.enumerable, false); +assertEq(propertyDescriptor.writable, false); +assertEq(propertyDescriptor.configurable, true); + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/async-iterator-helpers-from-other-global.js b/js/src/tests/non262/AsyncIterator/prototype/async-iterator-helpers-from-other-global.js new file mode 100644 index 0000000000..f8b8d719cd --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/async-iterator-helpers-from-other-global.js @@ -0,0 +1,65 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +class TestError extends Error {} + +class TestIterator extends AsyncIterator { + async next() { + return {done: false, value: 'value'}; + } + + closed = false; + async return(value) { + this.closed = true; + return {done: true, value}; + } +} + +function checkIterResult({done, value}, expectedDone, expectedValue) { + assertEq(done, expectedDone); + assertEq(Array.isArray(value) ? value[1] : value, expectedValue); +} + +const otherGlobal = newGlobal({newCompartment: true}); + +const methods = [ + ["map", x => x], + ["filter", x => true], + ["take", Infinity], + ["drop", 0], + ["asIndexedPairs", undefined], + ["flatMap", async function*(x) { yield x; }], +]; + +const {next: otherNext, return: otherReturn, throw: otherThrow} = + Object.getPrototypeOf(otherGlobal.eval("(async function*() {})().map(x => x)")); + +(async () => { + for (const [method, arg] of methods) { + const iterator = new TestIterator(); + const helper = iterator[method](arg); + checkIterResult(await otherNext.call(helper), false, 'value'); + } + + for (const [method, arg] of methods) { + const iterator = new TestIterator(); + const helper = iterator[method](arg); + assertEq(iterator.closed, false); + checkIterResult(await otherReturn.call(helper), true, undefined); + assertEq(iterator.closed, true); + } + + for (const [method, arg] of methods) { + const iterator = new TestIterator(); + const helper = iterator[method](arg); + try { + await otherThrow.call(helper, new TestError()); + assertEq(true, false, 'Expected exception'); + } catch (exc) { + assertEq(exc instanceof TestError, true); + } + checkIterResult(await helper.next(), true, undefined); + } +})(); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/clobber-symbol.js b/js/src/tests/non262/AsyncIterator/prototype/clobber-symbol.js new file mode 100644 index 0000000000..5db6b2276f --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/clobber-symbol.js @@ -0,0 +1,50 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) +// +// + +/*--- +esid: pending +description: %AsyncIterator.prototype% methods work even if the global Symbol has been clobbered. +info: > + Iterator Helpers proposal 2.1.6 +features: [iterator-helpers, Symbol.asyncIterator] +---*/ + +Symbol = undefined; +assertThrowsInstanceOf(() => Symbol.asyncIterator, TypeError); + +async function* gen(value) { + yield value; +} + +const lazyMethods = [ + iter => iter.map(x => x), + iter => iter.filter(x => x), + iter => iter.take(1), + iter => iter.drop(0), + iter => iter.asIndexedPairs(), + iter => iter.flatMap(gen), +]; + +const strictMethods = [ + iter => iter.reduce((_, x) => x, undefined), + iter => iter.toArray(), + iter => iter.forEach(() => undefined), + iter => iter.some(x => true), + iter => iter.every(x => true), + iter => iter.find(x => true), +]; + +(async () => { + for (const method of lazyMethods) { + const {value} = await method(gen('value')).next(); + assertEq(Array.isArray(value) ? value[1] : value, 'value'); + } + + for (const method of strictMethods) { + await method(gen('value')); + } +})(); + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/drop/drop-more-than-available.js b/js/src/tests/non262/AsyncIterator/prototype/drop/drop-more-than-available.js new file mode 100644 index 0000000000..45e0f91559 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/drop/drop-more-than-available.js @@ -0,0 +1,39 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +// +// +/*--- +esid: pending +description: %AsyncIterator.prototype%.drop returns if the iterator is done. +info: > + Iterator Helpers proposal 2.1.6.5 + 1. Repeat, while remaining > 0, + ... + b. Let next be ? Await(? IteratorStep(iterated)). + c. If ? IteratorComplete(next) is true, return undefined. +features: [iterator-helpers] +---*/ + +class TestIterator extends AsyncIterator { + counter = 0; + async next() { + return {done: ++this.counter >= 2, value: undefined}; + } +} + +(async () => { + let iter = [1, 2].values().drop(3); + let result = await iter.next(); + assertEq(result.value, undefined); + assertEq(result.done, true); + + iter = new TestIterator(); + let dropped = iter.drop(10); + result = await dropped.next(); + assertEq(result.value, undefined); + assertEq(result.done, true); + assertEq(iter.counter, 2); +})(); + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/drop/drop.js b/js/src/tests/non262/AsyncIterator/prototype/drop/drop.js new file mode 100644 index 0000000000..e380d7b7ed --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/drop/drop.js @@ -0,0 +1,23 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +async function* gen() { + yield 1; + yield 2; + yield 3; +} + +let iter = gen().drop(1); + +for (const v of [2, 3]) { + iter.next().then( + ({done, value}) => { + assertEq(done, false); + assertEq(value, v); + } + ); +} + +iter.next().then(({done}) => assertEq(done, true)); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/drop/length.js b/js/src/tests/non262/AsyncIterator/prototype/drop/length.js new file mode 100644 index 0000000000..6176622f17 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/drop/length.js @@ -0,0 +1,20 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) +// + +/*--- +esid: pending +description: %AsyncIterator.prototype%.drop length value and descriptor. +info: > +features: [iterator-helpers] +---*/ + +assertEq(AsyncIterator.prototype.drop.length, 1); + +const propertyDescriptor = Reflect.getOwnPropertyDescriptor(AsyncIterator.prototype.drop, 'length'); +assertEq(propertyDescriptor.value, 1); +assertEq(propertyDescriptor.enumerable, false); +assertEq(propertyDescriptor.writable, false); +assertEq(propertyDescriptor.configurable, true); + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/drop/name.js b/js/src/tests/non262/AsyncIterator/prototype/drop/name.js new file mode 100644 index 0000000000..ec0a73416a --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/drop/name.js @@ -0,0 +1,20 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +/*--- +esid: pending +description: %AsyncIterator.prototype%.drop.name value and descriptor. +info: > + 17 ECMAScript Standard Built-in Objects +features: [iterator-helpers] +---*/ + +assertEq(AsyncIterator.prototype.drop.name, 'drop'); + +const propertyDescriptor = Reflect.getOwnPropertyDescriptor(AsyncIterator.prototype.drop, 'name'); +assertEq(propertyDescriptor.value, 'drop'); +assertEq(propertyDescriptor.enumerable, false); +assertEq(propertyDescriptor.writable, false); +assertEq(propertyDescriptor.configurable, true); + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/every/async-writes.js b/js/src/tests/non262/AsyncIterator/prototype/every/async-writes.js new file mode 100644 index 0000000000..655dfd2624 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/every/async-writes.js @@ -0,0 +1,20 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +let x = {a: () => true}; + +async function* gen() { + yield x.a(); + yield x.a(); +} + +gen().every(() => true).then( + () => assertEq(true, false, 'expected error'), + err => assertEq(err instanceof Error, true), +); + +x.a = () => { + throw Error(); +}; + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/every/check-fn-after-getting-iterator.js b/js/src/tests/non262/AsyncIterator/prototype/every/check-fn-after-getting-iterator.js new file mode 100644 index 0000000000..ee147ab14b --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/every/check-fn-after-getting-iterator.js @@ -0,0 +1,30 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) +const log = []; +const handlerProxy = new Proxy({}, { + get: (target, key, receiver) => (...args) => { + log.push(`${key}: ${args[1]?.toString()}`); + return Reflect[key](...args); + }, +}); + +class TestIterator extends AsyncIterator { + next() { + return Promise.resolve({done: true}); + } +} + +async function* gen() { + yield 1; +} + +const iter = new Proxy(new TestIterator(), handlerProxy); +iter.every(1).then(() => assertEq(true, false, 'expected error'), err => assertEq(err instanceof TypeError, true)); + +assertEq( + log.join('\n'), + `get: every +get: next` +); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/every/coerce-result-to-boolean.js b/js/src/tests/non262/AsyncIterator/prototype/every/coerce-result-to-boolean.js new file mode 100644 index 0000000000..35c40d235e --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/every/coerce-result-to-boolean.js @@ -0,0 +1,29 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +async function* gen(value) { + yield value; +} +const fn = x => x; +function check(value, expected) { + gen(value).every(fn).then(result => assertEq(result, expected)); +} + +check(true, true); +check(1, true); +check([], true); +check({}, true); +check('test', true); + +check(false, false); +check(0, false); +check('', false); +check(null, false); +check(undefined, false); +check(NaN, false); +check(-0, false); +check(0n, false); +check(createIsHTMLDDA(), false); +check(Promise.resolve(false), false); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/every/descriptor.js b/js/src/tests/non262/AsyncIterator/prototype/every/descriptor.js new file mode 100644 index 0000000000..180085cefe --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/every/descriptor.js @@ -0,0 +1,10 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +const propDesc = Reflect.getOwnPropertyDescriptor(AsyncIterator.prototype, 'every'); +assertEq(typeof propDesc.value, 'function'); +assertEq(propDesc.writable, true); +assertEq(propDesc.enumerable, false); +assertEq(propDesc.configurable, true); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/every/error-from-correct-realm.js b/js/src/tests/non262/AsyncIterator/prototype/every/error-from-correct-realm.js new file mode 100644 index 0000000000..2ff93254ce --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/every/error-from-correct-realm.js @@ -0,0 +1,21 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +const otherGlobal = newGlobal({newCompartment: true}); +assertEq(TypeError !== otherGlobal.TypeError, true); + +async function *gen() {} + +gen().every().then(() => assertEq(true, false, 'expected error'), err => { + assertEq(err instanceof TypeError, true); +}); + +otherGlobal.AsyncIterator.prototype.every.call(gen()).then(() => assertEq(true, false, 'expected error'), err => { + assertEq( + err instanceof otherGlobal.TypeError, + true, + 'TypeError comes from the realm of the method.', + ); +}); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/every/fn-not-callable-throws.js b/js/src/tests/non262/AsyncIterator/prototype/every/fn-not-callable-throws.js new file mode 100644 index 0000000000..025c46dfdf --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/every/fn-not-callable-throws.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +async function *gen() { + yield 1; +} + +function check(fn) { + gen().every(fn).then(() => { + throw new Error('every should have thrown'); + }, + (err) => { + assertEq(err instanceof TypeError, true); + }); +} + +check(); +check(undefined); +check(null); +check(0); +check(false); +check(''); +check(Symbol('')); +check({}); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/every/fn-throws-close-iterator.js b/js/src/tests/non262/AsyncIterator/prototype/every/fn-throws-close-iterator.js new file mode 100644 index 0000000000..2a18d2ab45 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/every/fn-throws-close-iterator.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +class TestIterator extends AsyncIterator { + next() { + return Promise.resolve({ done: this.closed }); + } + + closed = false; + return() { + this.closed = true; + } +} + +const fn = () => { throw new Error(); }; +const iter = new TestIterator(); + +assertEq(iter.closed, false); +iter.every(fn).then(() => assertEq(true, false, 'expected error'), err => { + assertEq(err instanceof Error, true); + assertEq(iter.closed, true); +}); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/every/interleaving-calls.js b/js/src/tests/non262/AsyncIterator/prototype/every/interleaving-calls.js new file mode 100644 index 0000000000..8aacdb4316 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/every/interleaving-calls.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +const log = []; +async function* gen(n) { + log.push(`${n}`); + yield 1; + log.push(`${n}`); + yield 2; +} + +Promise.all([gen(1).every(() => true), gen(2).every(() => true)]).then( + () => { + assertEq( + log.join(' '), + '1 2 1 2', + ); + }, + err => { + throw err; + } +); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/every/length.js b/js/src/tests/non262/AsyncIterator/prototype/every/length.js new file mode 100644 index 0000000000..58d7942018 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/every/length.js @@ -0,0 +1,17 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) +/*--- + The `length` property of AsyncIterator.prototype.every. +info: | + ES7 section 17: Unless otherwise specified, the length property of a built-in + Function object has the attributes { [[Writable]]: false, [[Enumerable]]: + false, [[Configurable]]: true }. +---*/ + +const propDesc = Reflect.getOwnPropertyDescriptor(AsyncIterator.prototype.every, 'length'); +assertEq(propDesc.value, 1); +assertEq(propDesc.writable, false); +assertEq(propDesc.enumerable, false); +assertEq(propDesc.configurable, true); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/every/name.js b/js/src/tests/non262/AsyncIterator/prototype/every/name.js new file mode 100644 index 0000000000..086bfb13a6 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/every/name.js @@ -0,0 +1,13 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) +/*--- + `name` property of AsyncIterator.prototype.every. +---*/ + +const propDesc = Reflect.getOwnPropertyDescriptor(AsyncIterator.prototype.every, 'name'); +assertEq(propDesc.value, 'every'); +assertEq(propDesc.writable, false); +assertEq(propDesc.enumerable, false); +assertEq(propDesc.configurable, true); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/every/next-throws-iterator-not-closed.js b/js/src/tests/non262/AsyncIterator/prototype/every/next-throws-iterator-not-closed.js new file mode 100644 index 0000000000..98cc850a07 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/every/next-throws-iterator-not-closed.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +class TestIterator extends AsyncIterator { + next() { + throw new Error(); + } + + closed = false; + return() { + this.closed = true; + } +} + +const fn = () => {}; +const iter = new TestIterator(); + +assertEq(iter.closed, false); +iter.every(fn).then(() => assertEq(true, false, 'expected error'), err => { + assertEq(err instanceof Error, true); + assertEq(iter.closed, false); +}); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/every/proxy.js b/js/src/tests/non262/AsyncIterator/prototype/every/proxy.js new file mode 100644 index 0000000000..628f143e27 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/every/proxy.js @@ -0,0 +1,46 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) +// +// This test checks that %AsyncIterator.prototype%.every only gets the `next` method off of the +// iterator once, and never accesses the @@asyncIterator property. +const log = []; +const handlerProxy = new Proxy({}, { + get: (target, key, receiver) => (...args) => { + log.push(`${key}: ${args[1]?.toString()}`); + return Reflect[key](...args); + }, +}); + +class Counter extends AsyncIterator { + value = 0; + next() { + const value = this.value; + if (value < 2) { + this.value = value + 1; + return Promise.resolve({done: false, value}); + } + return Promise.resolve({done: true}); + } +} + +const iter = new Proxy(new Counter(), handlerProxy); +iter.every(x => x % 2 == 0).then(value => { + assertEq(value, false) + + assertEq( + log.join('\n'), + `get: every +get: next +get: value +set: value +getOwnPropertyDescriptor: value +defineProperty: value +get: value +set: value +getOwnPropertyDescriptor: value +defineProperty: value +get: return` + ); +}); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/every/return-true-if-all-match.js b/js/src/tests/non262/AsyncIterator/prototype/every/return-true-if-all-match.js new file mode 100644 index 0000000000..01d0f75d58 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/every/return-true-if-all-match.js @@ -0,0 +1,16 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +async function* gen() { + yield 1; + yield 3; + yield 5; +} +const fn = x => x % 2 == 1; + +gen().every(fn).then(result => assertEq(result, true)); + +async function* empty() {} +empty().every(x => x).then(result => assertEq(result, true)); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/every/short-circuit-on-false.js b/js/src/tests/non262/AsyncIterator/prototype/every/short-circuit-on-false.js new file mode 100644 index 0000000000..7b2c74d513 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/every/short-circuit-on-false.js @@ -0,0 +1,21 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +async function* gen() { + yield 1; + yield 2; + yield 3; +} + +const log = []; +const fn = value => { + log.push(value.toString()); + return value % 2 == 1; +}; + +gen().every(fn).then(result => { + assertEq(result, false); + assertEq(log.join(','), '1,2'); +}); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/every/this-not-iterator-throws.js b/js/src/tests/non262/AsyncIterator/prototype/every/this-not-iterator-throws.js new file mode 100644 index 0000000000..ed576c32ec --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/every/this-not-iterator-throws.js @@ -0,0 +1,22 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +const fn = x => x; + +function check(x) { + AsyncIterator.prototype.every.call(x, fn).then( + () => { + throw new Error('check should have been rejected'); + }, + err => { + assertEq(err instanceof TypeError, true); + } + ); +} + +check(); +check(undefined); +check({}); +check({next: 0}); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/every/value-throws-iterator-not-closed.js b/js/src/tests/non262/AsyncIterator/prototype/every/value-throws-iterator-not-closed.js new file mode 100644 index 0000000000..423a85ca7b --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/every/value-throws-iterator-not-closed.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +class TestError extends Error {} +class TestIterator extends AsyncIterator { + next() { + return Promise.resolve({ + done: false, + get value() { + throw new TestError(); + } + }); + } + + closed = false; + return() { + closed = true; + } +} + +const iterator = new TestIterator(); +assertEq(iterator.closed, false, 'iterator starts unclosed'); +iterator.every(x => x).then(() => assertEq(true, false, 'expected error'), err => { + assertEq(err instanceof TestError, true); + assertEq(iterator.closed, false, 'iterator remains unclosed'); +}); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/filter/coerce-result-to-boolean.js b/js/src/tests/non262/AsyncIterator/prototype/filter/coerce-result-to-boolean.js new file mode 100644 index 0000000000..23e3a317eb --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/filter/coerce-result-to-boolean.js @@ -0,0 +1,25 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +async function* gen(iterable) { + yield* iterable; +} + +// All truthy values are kept. +const truthyValues = [true, 1, [], {}, 'test']; +(async () => { + for await (const value of gen([...truthyValues]).filter(x => x)) { + assertEq(truthyValues.shift(), value); + } +})(); + +// All falsy values are filtered out. +const falsyValues = [false, 0, '', null, undefined, NaN, -0, 0n, createIsHTMLDDA()]; +gen(falsyValues).filter(x => x).next().then( + ({done, value}) => { + assertEq(done, true); + assertEq(value, undefined); + } +); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/filter/filter.js b/js/src/tests/non262/AsyncIterator/prototype/filter/filter.js new file mode 100644 index 0000000000..060e9853f2 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/filter/filter.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +async function* gen() { + yield 1; + yield 2; + yield 3; +} + +let iter = gen().filter(x => x % 2); + +for (const v of [1, 3]) { + console.log(iter); + iter.next().then( + ({done, value}) => { + assertEq(done, false); + assertEq(value, v); + } + ); +} + +iter.next().then(({done}) => assertEq(done, true)); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/filter/length.js b/js/src/tests/non262/AsyncIterator/prototype/filter/length.js new file mode 100644 index 0000000000..7ca14621ff --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/filter/length.js @@ -0,0 +1,21 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) +// + +/*--- +esid: pending +description: %AsyncIterator.prototype%.filter length value and descriptor. +info: > + 17 ECMAScript Standard Built-in Objects +features: [iterator-helpers] +---*/ + +assertEq(AsyncIterator.prototype.filter.length, 1); + +const propertyDescriptor = Reflect.getOwnPropertyDescriptor(AsyncIterator.prototype.filter, 'length'); +assertEq(propertyDescriptor.value, 1); +assertEq(propertyDescriptor.enumerable, false); +assertEq(propertyDescriptor.writable, false); +assertEq(propertyDescriptor.configurable, true); + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/filter/name.js b/js/src/tests/non262/AsyncIterator/prototype/filter/name.js new file mode 100644 index 0000000000..1a8e030e9a --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/filter/name.js @@ -0,0 +1,19 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) +/*--- +esid: pending +description: %AsyncIterator.prototype%.filter.name value and descriptor. +info: > + 17 ECMAScript Standard Built-in Objects +features: [iterator-helpers] +---*/ + +assertEq(AsyncIterator.prototype.filter.name, 'filter'); + +const propertyDescriptor = Reflect.getOwnPropertyDescriptor(AsyncIterator.prototype.filter, 'name'); +assertEq(propertyDescriptor.value, 'filter'); +assertEq(propertyDescriptor.enumerable, false); +assertEq(propertyDescriptor.writable, false); +assertEq(propertyDescriptor.configurable, true); + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/find/async-writes.js b/js/src/tests/non262/AsyncIterator/prototype/find/async-writes.js new file mode 100644 index 0000000000..6d1707e2fe --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/find/async-writes.js @@ -0,0 +1,20 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +let x = {a: () => true}; + +async function* gen() { + yield x.a(); + yield x.a(); +} + +gen().find(() => {}).then( + () => assertEq(true, false, 'expected error'), + err => assertEq(err instanceof Error, true), +); + +x.a = () => { + throw Error(); +}; + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/find/check-fn-after-getting-iterator.js b/js/src/tests/non262/AsyncIterator/prototype/find/check-fn-after-getting-iterator.js new file mode 100644 index 0000000000..579e4f589a --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/find/check-fn-after-getting-iterator.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) +const log = []; +const handlerProxy = new Proxy({}, { + get: (target, key, receiver) => (...args) => { + log.push(`${key}: ${args[1]?.toString()}`); + return Reflect[key](...args); + }, +}); + +class TestIterator extends AsyncIterator { + next() { + return Promise.resolve({done: true}); + } +} + +const iter = new Proxy(new TestIterator(), handlerProxy); +iter.find(1).then(() => assertEq(true, false, 'expected error'), err => { + assertEq(err instanceof TypeError, true); + assertEq( + log.join('\n'), + `get: find +get: next` + ); +}); + + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/find/coerce-result-to-boolean.js b/js/src/tests/non262/AsyncIterator/prototype/find/coerce-result-to-boolean.js new file mode 100644 index 0000000000..eb89eca840 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/find/coerce-result-to-boolean.js @@ -0,0 +1,35 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +async function* gen(value) { + yield value; +} +const fn = x => x; +function check(value, expected) { + gen(value).find(fn).then(result => assertEq(result, expected)); +} + +check(true, true); +check(1, 1); +check('test', 'test'); + +check(false, undefined); +check(0, undefined); +check('', undefined); +check(null, undefined); +check(undefined, undefined); +check(NaN, undefined); +check(-0, undefined); +check(0n, undefined); +check(Promise.resolve(false), undefined); + +let array = []; +check(array, array); + +let object = {}; +check(object, object); + +const htmlDDA = createIsHTMLDDA(); +check(htmlDDA, undefined); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/find/descriptor.js b/js/src/tests/non262/AsyncIterator/prototype/find/descriptor.js new file mode 100644 index 0000000000..7b93147d81 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/find/descriptor.js @@ -0,0 +1,13 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) +/*--- + Descriptor property of AsyncIterator.prototype.find +---*/ + +const propDesc = Reflect.getOwnPropertyDescriptor(AsyncIterator.prototype, 'find'); +assertEq(typeof propDesc.value, 'function'); +assertEq(propDesc.writable, true); +assertEq(propDesc.enumerable, false); +assertEq(propDesc.configurable, true); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/find/error-from-correct-realm.js b/js/src/tests/non262/AsyncIterator/prototype/find/error-from-correct-realm.js new file mode 100644 index 0000000000..37c6b4b593 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/find/error-from-correct-realm.js @@ -0,0 +1,21 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +const otherGlobal = newGlobal({newCompartment: true}); +assertEq(TypeError !== otherGlobal.TypeError, true); + +async function *gen() {} + +gen().find().then(() => assertEq(true, false, 'expected error'), err => { + assertEq(err instanceof TypeError, true); +}); + +otherGlobal.AsyncIterator.prototype.find.call(gen()).then(() => assertEq(true, false, 'expected error'), err => { + assertEq( + err instanceof otherGlobal.TypeError, + true, + 'TypeError comes from the realm of the method.', + ); +}); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/find/fn-not-callable-throws.js b/js/src/tests/non262/AsyncIterator/prototype/find/fn-not-callable-throws.js new file mode 100644 index 0000000000..c858b812d3 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/find/fn-not-callable-throws.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +async function *gen() { + yield 1; +} + +function check(fn) { + gen().every(fn).then(() => { + throw new Error('every should have thrown'); + }, + (err) => { + assertEq(err instanceof TypeError, true); + }); +} + +check(); +check(undefined); +check(null); +check(0); +check(false); +check(''); +check(Symbol('')); +check({}); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/find/fn-throws-close-iterator.js b/js/src/tests/non262/AsyncIterator/prototype/find/fn-throws-close-iterator.js new file mode 100644 index 0000000000..9544c57d4a --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/find/fn-throws-close-iterator.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +class TestIterator extends AsyncIterator { + next() { + return Promise.resolve({done: this.closed}); + } + + closed = false; + return() { + this.closed = true; + } +} + +const fn = () => { throw new Error(); }; +const iter = new TestIterator(); + +assertEq(iter.closed, false); +iter.find(fn).then(() => { + throw new Error('promise should be rejected'); +}, err => { + assertEq(err instanceof Error, true); + assertEq(iter.closed, true); +}); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/find/interleaving-calls.js b/js/src/tests/non262/AsyncIterator/prototype/find/interleaving-calls.js new file mode 100644 index 0000000000..d973e8b030 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/find/interleaving-calls.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +const log = []; +async function* gen(n) { + log.push(`${n}`); + yield 1; + log.push(`${n}`); + yield 2; +} + +Promise.all([gen(1).find(() => {}), gen(2).find(() => {})]).then( + () => { + assertEq( + log.join(' '), + '1 2 1 2', + ); + }, + err => { + throw err; + } +); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/find/length.js b/js/src/tests/non262/AsyncIterator/prototype/find/length.js new file mode 100644 index 0000000000..e207a52898 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/find/length.js @@ -0,0 +1,17 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) +/*--- + The `length` property of AsyncIterator.prototype.find. +info: | + ES7 section 17: Unless otherwise specified, the length property of a built-in + Function object has the attributes { [[Writable]]: false, [[Enumerable]]: + false, [[Configurable]]: true }. +---*/ + +const propDesc = Reflect.getOwnPropertyDescriptor(AsyncIterator.prototype.find, 'length'); +assertEq(propDesc.value, 1); +assertEq(propDesc.writable, false); +assertEq(propDesc.enumerable, false); +assertEq(propDesc.configurable, true); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/find/name.js b/js/src/tests/non262/AsyncIterator/prototype/find/name.js new file mode 100644 index 0000000000..21e8fa90f8 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/find/name.js @@ -0,0 +1,13 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) +/*--- + `name` property of AsyncIterator.prototype.find. +---*/ + +const propDesc = Reflect.getOwnPropertyDescriptor(AsyncIterator.prototype.find, 'name'); +assertEq(propDesc.value, 'find'); +assertEq(propDesc.writable, false); +assertEq(propDesc.enumerable, false); +assertEq(propDesc.configurable, true); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/find/next-throws-iterator-not-closed.js b/js/src/tests/non262/AsyncIterator/prototype/find/next-throws-iterator-not-closed.js new file mode 100644 index 0000000000..4c5cd3ddbd --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/find/next-throws-iterator-not-closed.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +class TestIterator extends AsyncIterator { + next() { + throw new Error(); + } + + closed = false; + return() { + this.closed = true; + } +} + +const fn = x => x; +const iter = new TestIterator(); + +assertEq(iter.closed, false); +iter.find(fn).then(() => assertEq(true, false, 'expected error'), err => { + assertEq(err instanceof Error, true); + assertEq(iter.closed, false); +}); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/find/proxy.js b/js/src/tests/non262/AsyncIterator/prototype/find/proxy.js new file mode 100644 index 0000000000..4f8a210b45 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/find/proxy.js @@ -0,0 +1,46 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) +// +// This test checks that %Iterator.prototype%.find only gets the `next` method off of the +// iterator once, and never accesses the @@iterator property. +const log = []; +const handlerProxy = new Proxy({}, { + get: (target, key, receiver) => (...args) => { + log.push(`${key}: ${args[1]?.toString()}`); + return Reflect[key](...args); + }, +}); + +class Counter extends AsyncIterator { + value = 0; + next() { + const value = this.value; + if (value < 2) { + this.value = value + 1; + return Promise.resolve({done: false, value}); + } + return Promise.resolve({done: true}); + } +} + +const iter = new Proxy(new Counter(), handlerProxy); +iter.find(x => x % 2 == 1).then(value => { + assertEq(value, 1); + + assertEq( + log.join('\n'), + `get: find +get: next +get: value +set: value +getOwnPropertyDescriptor: value +defineProperty: value +get: value +set: value +getOwnPropertyDescriptor: value +defineProperty: value +get: return` + ); +}); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/find/return-undefined-if-none-match.js b/js/src/tests/non262/AsyncIterator/prototype/find/return-undefined-if-none-match.js new file mode 100644 index 0000000000..b7dc847036 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/find/return-undefined-if-none-match.js @@ -0,0 +1,16 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +async function* gen() { + yield 1; + yield 3; + yield 5; +} +const fn = x => x % 2 == 0; + +gen().find(fn).then(result => assertEq(result, undefined)); + +async function* empty() {} +empty().find(x => x).then(result => assertEq(result, undefined)); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/find/short-circuit-on-match.js b/js/src/tests/non262/AsyncIterator/prototype/find/short-circuit-on-match.js new file mode 100644 index 0000000000..4404c53ac1 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/find/short-circuit-on-match.js @@ -0,0 +1,21 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +async function* gen() { + yield 1; + yield 2; + yield 3; +} + +const log = []; +const fn = (value) => { + log.push(value.toString()); + return value % 2 == 0; +}; + +gen().find(fn).then(result => { + assertEq(result, 2); + assertEq(log.join(','), '1,2'); +}); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/find/this-not-iterator-throws.js b/js/src/tests/non262/AsyncIterator/prototype/find/this-not-iterator-throws.js new file mode 100644 index 0000000000..97c8b142a2 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/find/this-not-iterator-throws.js @@ -0,0 +1,20 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +const fn = x => x; + +function check(x) { + AsyncIterator.prototype.find.call(x, fn).then( + () => assertEq(true, false, 'expected error'), + err => { + assertEq(err instanceof TypeError, true); + } + ); +} + +check(); +check(undefined); +check({}); +check({next: 0}); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/find/value-throws-iterator-not-closed.js b/js/src/tests/non262/AsyncIterator/prototype/find/value-throws-iterator-not-closed.js new file mode 100644 index 0000000000..c639eea5a4 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/find/value-throws-iterator-not-closed.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +class TestError extends Error {} +class TestIterator extends AsyncIterator { + next() { + return Promise.resolve({ + done: false, + get value() { + throw new TestError(); + } + }); + } + + closed = false; + return() { + closed = true; + } +} + +const iterator = new TestIterator(); +assertEq(iterator.closed, false, 'iterator starts unclosed'); +iterator.find(x => x).then(() => assertEq(true, false, 'expected error'), err => { + assertEq(err instanceof TestError, true); + assertEq(iterator.closed, false, 'iterator remains unclosed'); +}); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/flatMap/close-iterator-when-inner-complete-throws.js b/js/src/tests/non262/AsyncIterator/prototype/flatMap/close-iterator-when-inner-complete-throws.js new file mode 100644 index 0000000000..bca8f7c316 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/flatMap/close-iterator-when-inner-complete-throws.js @@ -0,0 +1,55 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +// +// +/*--- +esid: pending +description: %AsyncIterator.prototype%.flatMap closes the iterator when innerComplete throws. +info: > + Iterator Helpers proposal 2.1.6.7 + 1. Repeat, + ... + k. Repeat, while innerAlive is true, + ... + v. Let innerComplete be IteratorComplete(innerNext). + vi. IfAbruptCloseAsyncIterator(innerComplete, iterated). +features: [iterator-helpers] +---*/ + +class TestIterator extends AsyncIterator { + async next() { + return {done: false, value: 0}; + } + + closed = false; + async return() { + this.closed = true; + return {done: true}; + } +} + +class TestError extends Error {} +class InnerIterator extends AsyncIterator { + async next() { + return { + get done() { + throw new TestError(); + } + }; + } +} + +const iter = new TestIterator(); +const mapped = iter.flatMap(x => new InnerIterator()); + +assertEq(iter.closed, false); +mapped.next().then( + _ => assertEq(true, false, 'Expected reject.'), + err => { + assertEq(err instanceof TestError, true); + assertEq(iter.closed, true); + } +); + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/flatMap/close-iterator-when-inner-next-throws.js b/js/src/tests/non262/AsyncIterator/prototype/flatMap/close-iterator-when-inner-next-throws.js new file mode 100644 index 0000000000..d92732e9fd --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/flatMap/close-iterator-when-inner-next-throws.js @@ -0,0 +1,50 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +// +// +/*--- +esid: pending +description: %AsyncIterator.prototype%.flatMap closes the iterator when IteratorNext throws. +info: > + Iterator Helpers proposal 2.1.6.7 + 1. Repeat, + ... + k. Repeat, while innerAlive is true, + i. Let innerNextPromise be IteratorNext(innerIterator). + ii. IfAbruptCloseAsyncIterator(innerNextPromise, iterated). +features: [iterator-helpers] +---*/ + +class TestIterator extends AsyncIterator { + async next() { + return {done: false, value: 0}; + } + + closed = false; + async return(value) { + this.closed = true; + return {done: true, value}; + } +} + +class TestError extends Error {} +class InnerIterator extends AsyncIterator { + async next() { + throw new TestError(); + } +} + +const iter = new TestIterator(); +const mapped = iter.flatMap(x => new InnerIterator()); + +assertEq(iter.closed, false); +mapped.next().then( + _ => assertEq(true, false, 'Expected reject.'), + err => { + assertEq(err instanceof TestError, true); + assertEq(iter.closed, true); + } +); + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/flatMap/close-iterator-when-inner-value-throws.js b/js/src/tests/non262/AsyncIterator/prototype/flatMap/close-iterator-when-inner-value-throws.js new file mode 100644 index 0000000000..97a82ec0bb --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/flatMap/close-iterator-when-inner-value-throws.js @@ -0,0 +1,57 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +// +// +/*--- +esid: pending +description: %AsyncIterator.prototype%.flatMap closes the iterator when innerValue throws. +info: > + Iterator Helpers proposal 2.1.6.7 + 1. Repeat, + ... + k. Repeat, while innerAlive is true, + ... + viii. Else, + 1. Let innerValue be IteratorValue(innerNext). + 2. IfAbruptCloseAsyncIterator(innerValue, iterated). +features: [iterator-helpers] +---*/ + +class TestIterator extends AsyncIterator { + async next() { + return {done: false, value: 0}; + } + + closed = false; + async return() { + this.closed = true; + return {done: true}; + } +} + +class TestError extends Error {} +class InnerIterator extends AsyncIterator { + async next() { + return { + done: false, + get value() { + throw new TestError(); + }, + }; + } +} + +const iter = new TestIterator(); +const mapped = iter.flatMap(x => new InnerIterator()); + +assertEq(iter.closed, false); +mapped.next().then( + _ => assertEq(true, false, 'Expected reject.'), + err => { + assertEq(err instanceof TestError, true); + assertEq(iter.closed, true); + } +); + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/flatMap/flatMap.js b/js/src/tests/non262/AsyncIterator/prototype/flatMap/flatMap.js new file mode 100644 index 0000000000..dbd17cb33b --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/flatMap/flatMap.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +async function* gen() { + yield 1; + yield 2; + yield 3; +} + +async function* inner(x) { + yield x; + yield x + 1; +} + +let iter = gen().flatMap(x => inner(x)); + +for (const v of [1, 2, 2, 3, 3, 4]) { + iter.next().then( + ({done, value}) => { + assertEq(done, false); + assertEq(value, v); + } + ); +} + +iter.next().then(({done}) => assertEq(done, true)); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/flatMap/inner-empty-iterable.js b/js/src/tests/non262/AsyncIterator/prototype/flatMap/inner-empty-iterable.js new file mode 100644 index 0000000000..3da588f444 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/flatMap/inner-empty-iterable.js @@ -0,0 +1,44 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +// +// +/*--- +esid: pending +description: %AsyncIterator.prototype%.flatMap skips empty inner iterables. +info: > + Iterator Helpers proposal 2.1.6.7 + 1. Repeat, + ... + k. Repeat, while innerAlive is true, + ... + v. Let innerComplete be IteratorComplete(innerNext). + ... + vii. If innerComplete is true, set innerAlive to false. +features: [iterator-helpers] +---*/ + +async function* gen(values) { + yield* values; +} + +(async () => { + let iter = gen([0, 1, 2, 3]).flatMap(x => x % 2 ? gen([]) : gen([x])); + + for (const expected of [0, 2]) { + const result = await iter.next(); + assertEq(result.value, expected); + assertEq(result.done, false); + } + + let result = await iter.next(); + assertEq(result.value, undefined); + assertEq(result.done, true); + + iter = gen([0, 1, 2, 3]).flatMap(x => gen([])); + result = await iter.next(); + assertEq(result.value, undefined); + assertEq(result.done, true); +})(); + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/flatMap/inner-generator.js b/js/src/tests/non262/AsyncIterator/prototype/flatMap/inner-generator.js new file mode 100644 index 0000000000..08c0dfbba7 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/flatMap/inner-generator.js @@ -0,0 +1,36 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +// +// +/*--- +esid: pending +description: %AsyncIterator.prototype%.flatMap innerIterator can be a generator. +info: > + Iterator Helpers proposal 2.1.6.7 +features: [iterator-helpers] +---*/ + +async function* gen() { + yield 1; + yield 2; +} + +(async () => { + const iter = gen().flatMap(async function*(x) { + yield x; + yield* [x + 1, x + 2]; + }); + + for (const expected of [1, 2, 3, 2, 3, 4]) { + const result = await iter.next(); + assertEq(result.value, expected); + assertEq(result.done, false); + } + + const result = await iter.next(); + assertEq(result.value, undefined); + assertEq(result.done, true); +})(); + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/flatMap/length.js b/js/src/tests/non262/AsyncIterator/prototype/flatMap/length.js new file mode 100644 index 0000000000..4e6862cd41 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/flatMap/length.js @@ -0,0 +1,21 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) +// + +/*--- +esid: pending +description: %AsyncIterator.prototype%.flatMap length value and descriptor. +info: > + 17 ECMAScript Standard Built-in Objects +features: [iterator-helpers] +---*/ + +assertEq(AsyncIterator.prototype.flatMap.length, 1); + +const propertyDescriptor = Reflect.getOwnPropertyDescriptor(AsyncIterator.prototype.flatMap, 'length'); +assertEq(propertyDescriptor.value, 1); +assertEq(propertyDescriptor.enumerable, false); +assertEq(propertyDescriptor.writable, false); +assertEq(propertyDescriptor.configurable, true); + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/flatMap/name.js b/js/src/tests/non262/AsyncIterator/prototype/flatMap/name.js new file mode 100644 index 0000000000..2aeebe0f85 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/flatMap/name.js @@ -0,0 +1,19 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) +/*--- +esid: pending +description: %AsyncIterator.prototype%.flatMap.name value and descriptor. +info: > + 17 ECMAScript Standard Built-in Objects +features: [iterator-helpers] +---*/ + +assertEq(AsyncIterator.prototype.flatMap.name, 'flatMap'); + +const propertyDescriptor = Reflect.getOwnPropertyDescriptor(AsyncIterator.prototype.flatMap, 'name'); +assertEq(propertyDescriptor.value, 'flatMap'); +assertEq(propertyDescriptor.enumerable, false); +assertEq(propertyDescriptor.writable, false); +assertEq(propertyDescriptor.configurable, true); + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/flatMap/throw-when-inner-not-iterable.js b/js/src/tests/non262/AsyncIterator/prototype/flatMap/throw-when-inner-not-iterable.js new file mode 100644 index 0000000000..e854a8c8ae --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/flatMap/throw-when-inner-not-iterable.js @@ -0,0 +1,71 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +// +// +/*--- +esid: pending +description: %AsyncIterator.prototype%.flatMap closes the iterator and throws when mapped isn't iterable. +info: > + Iterator Helpers proposal 2.1.6.7 + 1. Repeat, + ... + h. Let innerIterator be GetIterator(mapped, async). + i. IfAbruptCloseAsyncIterator(innerIterator, iterated). +features: [iterator-helpers] +---*/ + +class NotIterable { + async next() { + return {done: true}; + } +} + +class InvalidIterable { + [Symbol.asyncIterator]() { + return {}; + } +} + +class TestIterator extends AsyncIterator { + async next() { + return {done: false, value: 0}; + } + + closed = false; + async return(value) { + this.closed = true; + return {done: true, value}; + } +} + +const nonIterables = [ + new NotIterable(), + new InvalidIterable(), + undefined, + null, + 0, + false, + Symbol(''), + 0n, + {}, +]; + +(async () => { + for (const value of nonIterables) { + const iter = new TestIterator(); + const mapped = iter.flatMap(x => value); + + assertEq(iter.closed, false); + console.log("here!"); + try { + await mapped.next(); + assertEq(true, false, 'Expected reject'); + } catch (exc) { + assertEq(exc instanceof TypeError, true); + } + assertEq(iter.closed, true); + } +})(); + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/forEach/async-writes.js b/js/src/tests/non262/AsyncIterator/prototype/forEach/async-writes.js new file mode 100644 index 0000000000..25c4302ade --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/forEach/async-writes.js @@ -0,0 +1,20 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +let x = {a: () => true}; + +async function* gen() { + yield x.a(); + yield x.a(); +} + +gen().forEach(() => {}).then( + () => assertEq(true, false, 'expected error'), + err => assertEq(err instanceof Error, true), +); + +x.a = () => { + throw Error(); +}; + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/forEach/check-fn-after-getting-iterator.js b/js/src/tests/non262/AsyncIterator/prototype/forEach/check-fn-after-getting-iterator.js new file mode 100644 index 0000000000..bcf77e56d1 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/forEach/check-fn-after-getting-iterator.js @@ -0,0 +1,27 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) +const log = []; +const handlerProxy = new Proxy({}, { + get: (target, key, receiver) => (...args) => { + log.push(`${key}: ${args[1]?.toString()}`); + return Reflect[key](...args); + }, +}); + +class TestIterator extends AsyncIterator { + next() { + return Promise.resolve({done: true}); + } +} + +const iter = new Proxy(new TestIterator(), handlerProxy); +iter.forEach(1).then(() => assertEq(true, false, 'expected error'), err => { + assertEq(err instanceof TypeError, true); + assertEq( + log.join('\n'), + `get: forEach +get: next` + ); +}); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/forEach/descriptor.js b/js/src/tests/non262/AsyncIterator/prototype/forEach/descriptor.js new file mode 100644 index 0000000000..8825422a28 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/forEach/descriptor.js @@ -0,0 +1,13 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) +/*--- + Descriptor property of AsyncIterator.prototype.forEach +---*/ + +const propDesc = Reflect.getOwnPropertyDescriptor(AsyncIterator.prototype, 'forEach'); +assertEq(typeof propDesc.value, 'function'); +assertEq(propDesc.writable, true); +assertEq(propDesc.enumerable, false); +assertEq(propDesc.configurable, true); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/forEach/error-from-correct-realm.js b/js/src/tests/non262/AsyncIterator/prototype/forEach/error-from-correct-realm.js new file mode 100644 index 0000000000..32797fe750 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/forEach/error-from-correct-realm.js @@ -0,0 +1,21 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +const otherGlobal = newGlobal({newCompartment: true}); +assertEq(TypeError !== otherGlobal.TypeError, true); + +async function *gen() {} + +gen().forEach().then(() => assertEq(true, false, 'expected error'), err => { + assertEq(err instanceof TypeError, true); +}); + +otherGlobal.AsyncIterator.prototype.forEach.call(gen()).then(() => assertEq(true, false, 'expected error'), err => { + assertEq( + err instanceof otherGlobal.TypeError, + true, + 'TypeError comes from the realm of the method.', + ); +}); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/forEach/fn-not-callable-throws.js b/js/src/tests/non262/AsyncIterator/prototype/forEach/fn-not-callable-throws.js new file mode 100644 index 0000000000..c70e336bff --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/forEach/fn-not-callable-throws.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +async function *gen() {} + +function check(fn) { + gen().forEach(fn).then(() => { + throw new Error('every should have thrown'); + }, + (err) => { + assertEq(err instanceof TypeError, true); + }); +} + +check(); +check(undefined); +check(null); +check(0); +check(false); +check(''); +check(Symbol('')); +check({}); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/forEach/fn-throws-close-iterator.js b/js/src/tests/non262/AsyncIterator/prototype/forEach/fn-throws-close-iterator.js new file mode 100644 index 0000000000..4e7d60412e --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/forEach/fn-throws-close-iterator.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +class TestIterator extends AsyncIterator { + next() { + return Promise.resolve({ done: this.closed }); + } + + closed = false; + return() { + this.closed = true; + } +} + +const fn = () => { throw new Error(); }; +const iter = new TestIterator(); + +assertEq(iter.closed, false); +iter.forEach(fn).then(() => assertEq(true, false, 'expected error'), err => { + assertEq(err instanceof Error, true); + assertEq(iter.closed, true); +}); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/forEach/forEach.js b/js/src/tests/non262/AsyncIterator/prototype/forEach/forEach.js new file mode 100644 index 0000000000..2109be5fd6 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/forEach/forEach.js @@ -0,0 +1,11 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +const log = []; +const fn = (value) => log.push(value); +const iter = [1, 2, 3].values(); + +assertEq(iter.forEach(fn), undefined); +assertEq(log.join(','), '1,2,3'); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/forEach/interleaving-calls.js b/js/src/tests/non262/AsyncIterator/prototype/forEach/interleaving-calls.js new file mode 100644 index 0000000000..c8c47bde45 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/forEach/interleaving-calls.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +const log = []; +async function* gen(n) { + log.push(`${n}`); + yield 1; + log.push(`${n}`); + yield 2; +} + +Promise.all([gen(1).forEach(() => {}), gen(2).forEach(() => {})]).then( + () => { + assertEq( + log.join(' '), + '1 2 1 2', + ); + }, + err => { + throw err; + } +); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/forEach/length.js b/js/src/tests/non262/AsyncIterator/prototype/forEach/length.js new file mode 100644 index 0000000000..5e59af9fea --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/forEach/length.js @@ -0,0 +1,17 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) +/*--- + The `length` property of AsyncIterator.prototype.forEach. +info: | + ES7 section 17: Unless otherwise specified, the length property of a built-in + Function object has the attributes { [[Writable]]: false, [[Enumerable]]: + false, [[Configurable]]: true }. +---*/ + +const propDesc = Reflect.getOwnPropertyDescriptor(AsyncIterator.prototype.forEach, 'length'); +assertEq(propDesc.value, 1); +assertEq(propDesc.writable, false); +assertEq(propDesc.enumerable, false); +assertEq(propDesc.configurable, true); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/forEach/name.js b/js/src/tests/non262/AsyncIterator/prototype/forEach/name.js new file mode 100644 index 0000000000..3053c3967f --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/forEach/name.js @@ -0,0 +1,13 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) +/*--- + `name` property of AsyncIterator.prototype.forEach. +---*/ + +const propDesc = Reflect.getOwnPropertyDescriptor(AsyncIterator.prototype.forEach, 'name'); +assertEq(propDesc.value, 'forEach'); +assertEq(propDesc.writable, false); +assertEq(propDesc.enumerable, false); +assertEq(propDesc.configurable, true); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/forEach/next-throws-iterator-not-closed.js b/js/src/tests/non262/AsyncIterator/prototype/forEach/next-throws-iterator-not-closed.js new file mode 100644 index 0000000000..0c72c700e2 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/forEach/next-throws-iterator-not-closed.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +class TestIterator extends AsyncIterator { + next() { + throw new Error(); + } + + closed = false; + return() { + this.closed = true; + } +} + +const fn = () => {}; +const iter = new TestIterator(); + +assertEq(iter.closed, false); +iter.forEach(fn).then(() => assertEq(true, false, 'expected error'), err => { + assertEq(err instanceof Error, true); + assertEq(iter.closed, false); +}); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/forEach/proxy.js b/js/src/tests/non262/AsyncIterator/prototype/forEach/proxy.js new file mode 100644 index 0000000000..bab7a2e864 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/forEach/proxy.js @@ -0,0 +1,44 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) +// +// This test checks that %Iterator.prototype%.forEach only gets the `next` method off of the +// iterator once, and never accesses the @@iterator property. +const log = []; +const handlerProxy = new Proxy({}, { + get: (target, key, receiver) => (...args) => { + log.push(`${key}: ${args[1]?.toString()}`); + return Reflect[key](...args); + }, +}); + +class Counter extends AsyncIterator { + value = 0; + next() { + const value = this.value; + if (value < 2) { + this.value = value + 1; + return Promise.resolve({done: false, value}); + } + return Promise.resolve({done: true}); + } +} + +const iter = new Proxy(new Counter(), handlerProxy); +iter.forEach(x => x).then(() => { + assertEq( + log.join('\n'), + `get: forEach +get: next +get: value +set: value +getOwnPropertyDescriptor: value +defineProperty: value +get: value +set: value +getOwnPropertyDescriptor: value +defineProperty: value +get: value` + ); +}); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/forEach/this-not-iterator-throws.js b/js/src/tests/non262/AsyncIterator/prototype/forEach/this-not-iterator-throws.js new file mode 100644 index 0000000000..c92536fbe6 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/forEach/this-not-iterator-throws.js @@ -0,0 +1,19 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +const fn = x => x; +function check(x) { + AsyncIterator.prototype.forEach.call(x, fn).then( + () => assertEq(true, false, 'expected error'), + err => { + assertEq(err instanceof TypeError, true); + } + ); +} + +check(); +check(undefined); +check({}); +check({next: 0}); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/forEach/value-throws-iterator-not-closed.js b/js/src/tests/non262/AsyncIterator/prototype/forEach/value-throws-iterator-not-closed.js new file mode 100644 index 0000000000..8611a0100f --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/forEach/value-throws-iterator-not-closed.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +class TestError extends Error {} +class TestIterator extends AsyncIterator { + next() { + return Promise.resolve({ + done: false, + get value() { + throw new TestError(); + } + }); + } + + closed = false; + return() { + closed = true; + } +} + +const iterator = new TestIterator(); +assertEq(iterator.closed, false, 'iterator starts unclosed'); +iterator.forEach(x => x).then(() => assertEq(true, false, 'expected error'), err => { + assertEq(err instanceof TestError, true); + assertEq(iterator.closed, false, 'iterator remains unclosed'); +}); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/generator-methods-throw-on-iterator-helpers.js b/js/src/tests/non262/AsyncIterator/prototype/generator-methods-throw-on-iterator-helpers.js new file mode 100644 index 0000000000..0e5b6ac0b5 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/generator-methods-throw-on-iterator-helpers.js @@ -0,0 +1,35 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +const asyncGeneratorProto = Object.getPrototypeOf( + Object.getPrototypeOf( + (async function *() {})() + ) +); + +const methods = [ + iter => iter.map(x => x), + iter => iter.filter(x => x), + iter => iter.take(1), + iter => iter.drop(0), + iter => iter.asIndexedPairs(), + iter => iter.flatMap(x => (async function*() {})()), +]; + +for (const method of methods) { + const iteratorHelper = method((async function*() {})()); + asyncGeneratorProto.next.call(iteratorHelper).then( + _ => assertEq(true, false, 'Expected reject'), + err => assertEq(err instanceof TypeError, true), + ); + asyncGeneratorProto.return.call(iteratorHelper).then( + _ => assertEq(true, false, 'Expected reject'), + err => assertEq(err instanceof TypeError, true), + ); + asyncGeneratorProto.throw.call(iteratorHelper).then( + _ => assertEq(true, false, 'Expected reject'), + err => assertEq(err instanceof TypeError, true), + ); +} + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/iterator-helper-methods-throw-on-generators.js b/js/src/tests/non262/AsyncIterator/prototype/iterator-helper-methods-throw-on-generators.js new file mode 100644 index 0000000000..f79fb7d842 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/iterator-helper-methods-throw-on-generators.js @@ -0,0 +1,12 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +async function *gen() { yield 'value'; } + +const asyncIteratorHelperProto = Object.getPrototypeOf(gen().map(x => x)); + +assertThrowsInstanceOf(() => asyncIteratorHelperProto.next.call(gen()), TypeError); +assertThrowsInstanceOf(() => asyncIteratorHelperProto.return.call(gen()), TypeError); +assertThrowsInstanceOf(() => asyncIteratorHelperProto.throw.call(gen()), TypeError); + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/lazy-methods-from-other-global.js b/js/src/tests/non262/AsyncIterator/prototype/lazy-methods-from-other-global.js new file mode 100644 index 0000000000..8b7a945fd5 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/lazy-methods-from-other-global.js @@ -0,0 +1,36 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +async function* gen(values) { + yield* values; +} + +const otherAsyncIteratorProto = newGlobal({newCompartment: true}).AsyncIterator.prototype; + +const methods = [ + ["map", x => x], + ["filter", x => true], + ["take", Infinity], + ["drop", 0], + ["asIndexedPairs", undefined], + ["flatMap", x => gen([x])], +]; + +(async () => { + for (const [method, arg] of methods) { + const iterator = gen([1, 2, 3]); + const helper = otherAsyncIteratorProto[method].call(iterator, arg); + + for (const expected of [1, 2, 3]) { + const {done, value} = await helper.next(); + assertEq(done, false); + assertEq(Array.isArray(value) ? value[1] : value, expected); + } + + const {done, value} = await helper.next(); + assertEq(done, true); + assertEq(value, undefined); + } +})(); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/lazy-methods-handle-empty-iterators.js b/js/src/tests/non262/AsyncIterator/prototype/lazy-methods-handle-empty-iterators.js new file mode 100644 index 0000000000..b1bd426dd8 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/lazy-methods-handle-empty-iterators.js @@ -0,0 +1,42 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +// +// +/*--- +esid: pending +description: Lazy %AsyncIterator.prototype% methods handle empty iterators. +info: > + Iterator Helpers proposal 2.1.6 +features: [iterator-helpers] +---*/ + +class EmptyIterator extends AsyncIterator { + async next() { + return {done: true}; + } +} + +async function* gen() {} + +const methods = [ + iter => iter.map(x => x), + iter => iter.filter(x => x), + iter => iter.take(1), + iter => iter.drop(0), + iter => iter.asIndexedPairs(), + iter => iter.flatMap(x => gen()), +]; + +for (const method of methods) { + for (const iterator of [new EmptyIterator(), gen()]) { + method(iterator).next().then( + ({done, value}) => { + assertEq(done, true); + assertEq(value, undefined); + } + ); + } +} + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/lazy-methods-interleaved.js b/js/src/tests/non262/AsyncIterator/prototype/lazy-methods-interleaved.js new file mode 100644 index 0000000000..98c2a2b680 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/lazy-methods-interleaved.js @@ -0,0 +1,59 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +// +// +/*--- +esid: pending +description: Lazy %AsyncIterator.prototype% method calls can be interleaved. +info: > + Iterator Helpers proposal 2.1.6 +features: [iterator-helpers] +---*/ + +class TestIterator extends AsyncIterator { + value = 0; + async next() { + return {done: false, value: this.value++}; + } +} + +function unwrapResult(value) { + // Unwrap the asIndexedPair return values. + while (Array.isArray(value)) { + value = value[1]; + } + return value; +} + +function check({done, value}, expectedDone, expectedValue) { + assertEq(done, expectedDone); + assertEq(unwrapResult(value), expectedValue); +} + +const methods = [ + ['map', x => x], + ['filter', x => true], + ['take', Infinity], + ['drop', 0], + ['asIndexedPairs', undefined], + ['flatMap', async function*(x) { yield x; }], +]; + +(async () => { + for (const [firstMethod, firstArg] of methods) { + for (const [secondMethod, secondArg] of methods) { + const iterator = new TestIterator(); + + const firstHelper = iterator[firstMethod](firstArg); + const secondHelper = iterator[secondMethod](secondArg); + + check(await firstHelper.next(), false, 0); + check(await secondHelper.next(), false, 1); + check(await firstHelper.next(), false, 2); + check(await secondHelper.next(), false, 3); + } + } +})(); + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/lazy-methods-iterator-closed-on-call-throws.js b/js/src/tests/non262/AsyncIterator/prototype/lazy-methods-iterator-closed-on-call-throws.js new file mode 100644 index 0000000000..1a52d113f9 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/lazy-methods-iterator-closed-on-call-throws.js @@ -0,0 +1,49 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +// +// +/*--- +esid: pending +description: Lazy %AsyncIterator.prototype% methods close the iterator if callback throws. +info: > + AsyncIterator Helpers proposal 2.1.6 +features: [iterator-helpers] +---*/ + +class TestError extends Error {} +class TestAsyncIterator extends AsyncIterator { + async next() { + return {done: false, value: 1}; + } + + closed = false; + async return() { + this.closed = true; + return {done: true}; + } +} + +function fn() { + throw new TestError(); +} +const methods = [ + iter => iter.map(fn), + iter => iter.filter(fn), + iter => iter.flatMap(fn), +]; + +for (const method of methods) { + const iter = new TestAsyncIterator(); + assertEq(iter.closed, false); + method(iter).next().then( + _ => assertEq(true, false, 'Expected reject'), + err => { + assertEq(err instanceof TestError, true); + assertEq(iter.closed, true); + }, + ); +} + +if (typeof reportCompare == 'function') + reportCompare(0, 0); + diff --git a/js/src/tests/non262/AsyncIterator/prototype/lazy-methods-iterator-closed-on-yield-throws.js b/js/src/tests/non262/AsyncIterator/prototype/lazy-methods-iterator-closed-on-yield-throws.js new file mode 100644 index 0000000000..bec857eb33 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/lazy-methods-iterator-closed-on-yield-throws.js @@ -0,0 +1,53 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +// +// +/*--- +esid: pending +description: Lazy %AsyncIterator.prototype% methods close the iterator if `yield` throws. +info: > + AsyncIterator Helpers proposal 2.1.6 +features: [iterator-helpers] +---*/ + +class TestError extends Error {} +class TestAsyncIterator extends AsyncIterator { + async next() { + return {done: false, value: 1}; + } + + closed = false; + async return(value) { + this.closed = true; + return {done: true, value}; + } +} + +async function* gen(x) { yield x; } +const methods = [ + iter => iter.map(x => x), + iter => iter.filter(x => true), + iter => iter.take(1), + iter => iter.drop(0), + iter => iter.asIndexedPairs(), + iter => iter.flatMap(gen), +]; + +for (const method of methods) { + const iterator = new TestAsyncIterator(); + const iteratorHelper = method(iterator); + + assertEq(iterator.closed, false); + iteratorHelper.next().then( + _ => iteratorHelper.throw(new TestError()).then( + _ => assertEq(true, false, 'Expected reject'), + err => { + assertEq(err instanceof TestError, true); + assertEq(iterator.closed, true); + }, + ), + ); +} + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/lazy-methods-iterator-not-closed-on-next-get-then-throws.js b/js/src/tests/non262/AsyncIterator/prototype/lazy-methods-iterator-not-closed-on-next-get-then-throws.js new file mode 100644 index 0000000000..a9c8a6ebca --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/lazy-methods-iterator-not-closed-on-next-get-then-throws.js @@ -0,0 +1,57 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +// +// +/*--- +esid: pending +description: Lazy %AsyncIterator.prototype% methods don't close the iterator if getting `then` throws. +info: > + AsyncIterator Helpers proposal 2.1.6 +features: [iterator-helpers] +---*/ + +class TestError extends Error {} + +class TestIterator extends AsyncIterator { + next() { + return { + get then() { + throw new TestError(); + } + }; + } + + closed = false; + async return(value) { + this.closed = true; + return {done: true, value}; + } +} + +const methods = [ + ["map", x => x], + ["filter", x => true], + ["take", Infinity], + ["drop", 0], + ["asIndexedPairs", undefined], + ["flatMap", async function*(x) { yield x; }], +]; + +(async () => { + for (const [method, arg] of methods) { + const iterator = new TestIterator(); + assertEq(iterator.closed, false); + + try { + await iterator[method](arg).next(); + assertEq(true, false, 'Expected exception'); + } catch(err) { + console.log(err); + assertEq(err instanceof TestError, true); + } + assertEq(iterator.closed, false); + } +})(); + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/lazy-methods-iterator-not-closed-on-next-promise-executor-throws.js b/js/src/tests/non262/AsyncIterator/prototype/lazy-methods-iterator-not-closed-on-next-promise-executor-throws.js new file mode 100644 index 0000000000..838c381b17 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/lazy-methods-iterator-not-closed-on-next-promise-executor-throws.js @@ -0,0 +1,53 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +// +// +/*--- +esid: pending +description: Lazy %AsyncIterator.prototype% methods don't close the iterator if executor of Promise returned by `.next` throws. +info: > + AsyncIterator Helpers proposal 2.1.6 +features: [iterator-helpers] +---*/ + +class TestError extends Error {} +class TestIterator extends AsyncIterator { + next() { + return new Promise((resolve, reject) => { + throw new TestError(); + }); + } + + closed = false; + async return(value) { + this.closed = true; + return {done: true, value}; + } +} + +const methods = [ + ["map", x => x], + ["filter", x => true], + ["take", Infinity], + ["drop", 0], + ["asIndexedPairs", undefined], + ["flatMap", async function*(x) { yield x; }], +]; + +(async () => { + for (const [method, arg] of methods) { + const iterator = new TestIterator(); + assertEq(iterator.closed, false); + + try { + await iterator[method](arg).next(); + assertEq(true, false, 'Expected exception'); + } catch(err) { + assertEq(err instanceof TestError, true); + } + assertEq(iterator.closed, false); + } +})(); + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/lazy-methods-iterator-not-closed-on-next-returns-reject.js b/js/src/tests/non262/AsyncIterator/prototype/lazy-methods-iterator-not-closed-on-next-returns-reject.js new file mode 100644 index 0000000000..65eede2a6b --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/lazy-methods-iterator-not-closed-on-next-returns-reject.js @@ -0,0 +1,51 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +// +// +/*--- +esid: pending +description: Lazy %AsyncIterator.prototype% methods don't close the iterator if `.next` returns a rejected Promise. +info: > + AsyncIterator Helpers proposal 2.1.6 +features: [iterator-helpers] +---*/ + +class TestError extends Error {} +class TestIterator extends AsyncIterator { + next() { + return Promise.reject(new TestError()); + } + + closed = false; + async return(value) { + this.closed = true; + return {done: true, value}; + } +} + +const methods = [ + ["map", x => x], + ["filter", x => true], + ["take", Infinity], + ["drop", 0], + ["asIndexedPairs", undefined], + ["flatMap", async function*(x) { yield x; }], +]; + +(async () => { + for (const [method, arg] of methods) { + const iterator = new TestIterator(); + assertEq(iterator.closed, false); + + try { + await iterator[method](arg).next(); + assertEq(true, false, 'Expected exception'); + } catch(err) { + assertEq(err instanceof TestError, true); + } + assertEq(iterator.closed, false); + } +})(); + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/lazy-methods-iterator-not-closed-on-next-then-throws.js b/js/src/tests/non262/AsyncIterator/prototype/lazy-methods-iterator-not-closed-on-next-then-throws.js new file mode 100644 index 0000000000..609a49c3c7 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/lazy-methods-iterator-not-closed-on-next-then-throws.js @@ -0,0 +1,57 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +// +// +/*--- +esid: pending +description: Lazy %AsyncIterator.prototype% methods don't close the iterator if `then` returned by `next` throws. +info: > + AsyncIterator Helpers proposal 2.1.6 +features: [iterator-helpers] +---*/ + +class TestError extends Error {} + +class TestIterator extends AsyncIterator { + next() { + return { + then() { + throw new TestError(); + } + }; + } + + closed = false; + async return(value) { + this.closed = true; + return {done: true, value}; + } +} + +const methods = [ + ["map", x => x], + ["filter", x => true], + ["take", Infinity], + ["drop", 0], + ["asIndexedPairs", undefined], + ["flatMap", async function*(x) { yield x; }], +]; + +(async () => { + for (const [method, arg] of methods) { + const iterator = new TestIterator(); + assertEq(iterator.closed, false); + + try { + await iterator[method](arg).next(); + assertEq(true, false, 'Expected exception'); + } catch(err) { + console.log(err); + assertEq(err instanceof TestError, true); + } + assertEq(iterator.closed, false); + } +})(); + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/lazy-methods-iterator-not-closed-on-next-throws.js b/js/src/tests/non262/AsyncIterator/prototype/lazy-methods-iterator-not-closed-on-next-throws.js new file mode 100644 index 0000000000..ff42a3f7b6 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/lazy-methods-iterator-not-closed-on-next-throws.js @@ -0,0 +1,49 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +// +// +/*--- +esid: pending +description: Lazy %AsyncIterator.prototype% methods don't close the iterator if `.next` call throws. +info: > + AsyncIterator Helpers proposal 2.1.6 +features: [iterator-helpers] +---*/ + +class TestError extends Error {} +class TestAsyncIterator extends AsyncIterator { + next() { + throw new TestError(); + } + + closed = false; + async return(value) { + this.closed = true; + return {done: true, value}; + } +} + +async function* gen(x) { yield x; } +const methods = [ + iter => iter.map(x => x), + iter => iter.filter(x => x), + iter => iter.take(1), + iter => iter.drop(0), + iter => iter.asIndexedPairs(), + iter => iter.flatMap(gen), +]; + +for (const method of methods) { + const iterator = new TestAsyncIterator(); + assertEq(iterator.closed, false); + method(iterator).next().then( + _ => assertEq(true, false, 'Expected reject'), + err => { + assertEq(err instanceof TestError, true); + assertEq(iterator.closed, false); + }, + ); +} + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/lazy-methods-iterator-not-closed-on-value-throws.js b/js/src/tests/non262/AsyncIterator/prototype/lazy-methods-iterator-not-closed-on-value-throws.js new file mode 100644 index 0000000000..1578070553 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/lazy-methods-iterator-not-closed-on-value-throws.js @@ -0,0 +1,54 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +// +// +/*--- +esid: pending +description: Lazy %AsyncIterator.prototype% methods don't close the iterator if `value` throws. +info: > + AsyncIterator Helpers proposal 2.1.6 +features: [iterator-helpers] +---*/ + +class TestError extends Error {} +class TestAsyncIterator extends AsyncIterator { + async next() { + return { + get value() { + throw new TestError(); + } + }; + } + + closed = false; + async return(value) { + this.closed = true; + return {done: true, value}; + } +} + +const iterator = new TestAsyncIterator(); + +async function* gen(x) { yield x; } +const methods = [ + iter => iter.map(x => x), + iter => iter.filter(x => x), + iter => iter.take(1), + iter => iter.drop(0), + iter => iter.asIndexedPairs(), + iter => iter.flatMap(gen), +]; + +for (const method of methods) { + assertEq(iterator.closed, false); + method(iterator).next().then( + _ => assertEq(true, false, 'Expected reject'), + err => { + assertEq(err instanceof TestError, true); + assertEq(iterator.closed, false); + }, + ); +} + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/lazy-methods-iterator-returns-done-generator-finishes.js b/js/src/tests/non262/AsyncIterator/prototype/lazy-methods-iterator-returns-done-generator-finishes.js new file mode 100644 index 0000000000..d7e2be8f5d --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/lazy-methods-iterator-returns-done-generator-finishes.js @@ -0,0 +1,30 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +class TestIterator extends AsyncIterator { + async next() { + return {done: true, value: 'value'}; + } +} + +async function* gen(x) { yield x; } + +const methods = [ + iter => iter.map(x => x), + iter => iter.filter(x => true), + iter => iter.take(1), + iter => iter.drop(0), + iter => iter.asIndexedPairs(), + iter => iter.flatMap(gen), +]; + +(async () => { + for (const method of methods) { + const iterator = method(new TestIterator()); + const {done, value} = await iterator.next(); + assertEq(done, true); + assertEq(value, undefined); + } +})(); + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/lazy-methods-multiple-return-close-iterator-once.js b/js/src/tests/non262/AsyncIterator/prototype/lazy-methods-multiple-return-close-iterator-once.js new file mode 100644 index 0000000000..4ccb6fd4b1 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/lazy-methods-multiple-return-close-iterator-once.js @@ -0,0 +1,64 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +// +// +/*--- +esid: pending +description: Calling `.return()` on a lazy %AsyncIterator.prototype% method multiple times closes the source iterator once. +info: > + Iterator Helpers proposal 2.1.6 +features: [iterator-helpers] +---*/ + +class TestIterator extends AsyncIterator { + async next() { + return {done: false, value: 1}; + } + + closeCount = 0; + async return(value) { + this.closeCount++; + return {done: true, value}; + } +} + +async function* gen(x) { yield x; } + +const methods = [ + iter => iter.map(x => x), + iter => iter.filter(x => x), + iter => iter.take(1), + iter => iter.drop(0), + iter => iter.asIndexedPairs(), + iter => iter.flatMap(gen), +]; + +(async () => { + // Call `return` after stepping the iterator once: + for (const method of methods) { + const iter = new TestIterator(); + const iterHelper = method(iter); + + await iterHelper.next(); + assertEq(iter.closeCount, 0); + await iterHelper.return(); + assertEq(iter.closeCount, 1); + await iterHelper.return(); + assertEq(iter.closeCount, 1); + } + + // Call `return` before stepping the iterator: + for (const method of methods) { + const iter = new TestIterator(); + const iterHelper = method(iter); + + assertEq(iter.closeCount, 0); + await iterHelper.return(); + assertEq(iter.closeCount, 1); + await iterHelper.return(); + assertEq(iter.closeCount, 1); + } +})(); + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/lazy-methods-multiple-throw-close-iterator-once.js b/js/src/tests/non262/AsyncIterator/prototype/lazy-methods-multiple-throw-close-iterator-once.js new file mode 100644 index 0000000000..45e88be26a --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/lazy-methods-multiple-throw-close-iterator-once.js @@ -0,0 +1,90 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +// +// +/*--- +esid: pending +description: Calling `throw` on a lazy %AsyncIterator.prototype% method multiple times closes the source iterator once. +info: > + Iterator Helpers proposal 2.1.6 +features: [iterator-helpers] +---*/ + +class TestError extends Error {} + +class TestIterator extends AsyncIterator { + async next() { + return {done: false, value: 1}; + } + + closeCount = 0; + async return(value) { + this.closeCount++; + return {done: true, value}; + } +} + +async function* gen(x) { yield x; } + +const methods = [ + iter => iter.map(x => x), + iter => iter.filter(x => true), + iter => iter.take(Infinity), + iter => iter.drop(0), + iter => iter.asIndexedPairs(), + iter => iter.flatMap(gen), +]; + +(async () => { + // Call `throw` after stepping the iterator once: + for (const method of methods) { + const iter = new TestIterator(); + const iterHelper = method(iter); + + await iterHelper.next() + assertEq(iter.closeCount, 0); + + try { + await iterHelper.throw(new TestError()); + assertEq(true, false, 'Expected reject'); + } catch (exc) { + assertEq(exc instanceof TestError, true); + } + assertEq(iter.closeCount, 1); + + try { + await iterHelper.throw(new TestError()); + assertEq(true, false, 'Expected reject'); + } catch (exc) { + assertEq(exc instanceof TestError, true); + } + assertEq(iter.closeCount, 1); + } + + // Call `throw` before stepping the iterator: + for (const method of methods) { + const iter = new TestIterator(); + const iterHelper = method(iter); + + assertEq(iter.closeCount, 0); + + try { + await iterHelper.throw(new TestError()); + assertEq(true, false, 'Expected reject'); + } catch (exc) { + assertEq(exc instanceof TestError, true); + } + assertEq(iter.closeCount, 1); + + try { + await iterHelper.throw(new TestError()); + assertEq(true, false, 'Expected reject'); + } catch (exc) { + assertEq(exc instanceof TestError, true); + } + assertEq(iter.closeCount, 1); + } +})(); + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/lazy-methods-mutate-iterator-after-done.js b/js/src/tests/non262/AsyncIterator/prototype/lazy-methods-mutate-iterator-after-done.js new file mode 100644 index 0000000000..b9a37234a8 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/lazy-methods-mutate-iterator-after-done.js @@ -0,0 +1,49 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) +// +// + +/*--- +esid: pending +description: %AsyncIterator.prototype% methods ignore iterator mutation if already done. +info: > + Iterator Helpers proposal 2.1.6 +features: [iterator-helpers] +---*/ + +class TestIterator extends AsyncIterator { + values = [1, 2]; + async next() { + if (this.values.length == 0) + return {done: true}; + return {done: false, value: this.values.shift()}; + } +} + +function check({done, value}, expectedDone, expectedValue) { + assertEq(done, expectedDone); + assertEq(Array.isArray(value) ? value[1] : value, expectedValue); +} + +const methods = [ + ['map', x => x], + ['filter', x => true], + ['take', Infinity], + ['drop', 0], + ['asIndexedPairs', undefined], + ['flatMap', async function*(x) { yield x; }], +]; + +for (const [method, arg] of methods) { + (async () => { + const iter = new TestIterator(); + const iterHelper = iter[method](arg); + check(await iterHelper.next(), false, 1); + check(await iterHelper.next(), false, 2); + check(await iterHelper.next(), true, undefined); + iter.values.push(3); + check(await iterHelper.next(), true, undefined); + })(); +} + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/lazy-methods-mutate-iterator.js b/js/src/tests/non262/AsyncIterator/prototype/lazy-methods-mutate-iterator.js new file mode 100644 index 0000000000..876990d945 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/lazy-methods-mutate-iterator.js @@ -0,0 +1,49 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) +// +// + +/*--- +esid: pending +description: %AsyncIterator.prototype% methods work properly if the iterator has been mutated. +info: > + Iterator Helpers proposal 2.1.6 +features: [iterator-helpers] +---*/ + +class TestIterator extends AsyncIterator { + values = [1, 2]; + async next() { + if (this.values.length == 0) + return {done: true}; + return {done: false, value: this.values.shift()}; + } +} + +function check({done, value}, expectedDone, expectedValue) { + assertEq(done, expectedDone); + assertEq(Array.isArray(value) ? value[1] : value, expectedValue); +} + +const methods = [ + ['map', x => x], + ['filter', x => true], + ['take', Infinity], + ['drop', 0], + ['asIndexedPairs', undefined], + ['flatMap', async function*(x) { yield x; }], +]; + +for (const [method, arg] of methods) { + (async () => { + const iter = new TestIterator(); + const iterHelper = iter[method](arg); + check(await iterHelper.next(), false, 1); + check(await iterHelper.next(), false, 2); + iter.values.push(3); + check(await iterHelper.next(), false, 3); + check(await iterHelper.next(), true, undefined); + })(); +} + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/lazy-methods-not-close-iterator-next-reject.js b/js/src/tests/non262/AsyncIterator/prototype/lazy-methods-not-close-iterator-next-reject.js new file mode 100644 index 0000000000..43d84ae17d --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/lazy-methods-not-close-iterator-next-reject.js @@ -0,0 +1,48 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +// +// +/*--- +esid: pending +description: Lazy %AsyncIterator.prototype% methods don't close the iterator if `next` returns rejected promise. +info: > + AsyncIterator Helpers proposal 2.1.6 +features: [iterator-helpers] +---*/ + +class TestIterator extends AsyncIterator { + next() { + return Promise.reject('rejection'); + } + + closed = false; + async return(value) { + this.closed = true; + return {done: true, value}; + } +} + +async function* gen(x) { yield x; } +const methods = [ + iter => iter.map(x => x), + iter => iter.filter(x => x), + iter => iter.take(1), + iter => iter.drop(0), + iter => iter.asIndexedPairs(), + iter => iter.flatMap(gen), +]; + +for (const method of methods) { + const iterator = new TestIterator(); + assertEq(iterator.closed, false); + method(iterator).next().then( + _ => assertEq(true, false, 'Expected reject'), + err => { + assertEq(err, 'rejection'); + assertEq(iterator.closed, false); + }, + ); +} + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/lazy-methods-pass-through-lastValue.js b/js/src/tests/non262/AsyncIterator/prototype/lazy-methods-pass-through-lastValue.js new file mode 100644 index 0000000000..40da229ca6 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/lazy-methods-pass-through-lastValue.js @@ -0,0 +1,33 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +class TestAsyncIterator extends AsyncIterator { + async next(value) { + return {done: false, value}; + } +} + +const methods = [ + iter => iter.map(x => x), + iter => iter.filter(x => true), + iter => iter.take(2), + iter => iter.drop(0), + iter => iter.asIndexedPairs(), +]; + +for (const method of methods) { + const iterator = new TestAsyncIterator(); + const iteratorHelper = method(iterator); + iteratorHelper.next().then( + _ => iteratorHelper.next('last value').then( + ({done, value}) => { + assertEq(done, false); + // Unwrap the return value from asIndexedPairs. + assertEq(Array.isArray(value) ? value[1] : value, 'last value'); + } + ), + ); +} + +if (typeof reportCompare == 'function') + reportCompare(0, 0); + diff --git a/js/src/tests/non262/AsyncIterator/prototype/lazy-methods-pass-value-through-chain.js b/js/src/tests/non262/AsyncIterator/prototype/lazy-methods-pass-value-through-chain.js new file mode 100644 index 0000000000..8b5506ee97 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/lazy-methods-pass-value-through-chain.js @@ -0,0 +1,38 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +class TestAsyncIterator extends AsyncIterator { + async next(value) { + return {done: false, value}; + } +} + +const methods = [ + iter => iter.map(x => x), + iter => iter.filter(x => true), + iter => iter.take(2), + iter => iter.drop(0), + iter => iter.asIndexedPairs(), +]; + +for (const outerMethod of methods) { + for (const innerMethod of methods) { + const iterator = new TestAsyncIterator(); + const iteratorChain = outerMethod(innerMethod(iterator)); + iteratorChain.next().then( + _ => iteratorChain.next('last value').then( + ({done, value}) => { + assertEq(done, false); + // Unwrap the asIndexedPair return values. + while (Array.isArray(value)) { + value = value[1]; + } + assertEq(value, 'last value'); + } + ), + ); + } +} + +if (typeof reportCompare == 'function') + reportCompare(0, 0); + diff --git a/js/src/tests/non262/AsyncIterator/prototype/lazy-methods-proxy-accesses.js b/js/src/tests/non262/AsyncIterator/prototype/lazy-methods-proxy-accesses.js new file mode 100644 index 0000000000..5892a41e5b --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/lazy-methods-proxy-accesses.js @@ -0,0 +1,68 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) +// + +/*--- +esid: pending +description: Lazy %AsyncIterator.prototype% methods access specified properties only. +info: > + Iterator Helpers proposal 2.1.6 +features: [iterator-helpers] +---*/ + +let log; +const handlerProxy = new Proxy({}, { + get: (target, key, receiver) => (...args) => { + log.push(`${key}: ${args.filter(x => typeof x != 'object').map(x => x.toString())}`); + return Reflect[key](...args); + }, +}); + +class TestIterator extends AsyncIterator { + value = 0; + async next() { + if (this.value < 1) + return new Proxy({done: false, value: this.value++}, handlerProxy); + return new Proxy({done: true, value: undefined}, handlerProxy); + } +} + +const methods = [ + ['map', x => x], + ['filter', x => true], + ['take', 4], + ['drop', 0], + ['asIndexedPairs', undefined], + ['flatMap', async function*(x) { yield x; }], +]; + +(async () => { + for (const [method, argument] of methods) { + log = []; + const iteratorProxy = new Proxy(new TestIterator(), handlerProxy); + const iteratorHelper = iteratorProxy[method](argument); + + await iteratorHelper.next(); + await iteratorHelper.next(); + const {done} = await iteratorHelper.next(); + assertEq(done, true); + assertEq( + log.join('\n'), + `get: ${method} +get: next +get: value +get: value +set: value,1 +getOwnPropertyDescriptor: value +defineProperty: value +get: then +get: done +get: value +get: value +get: then +get: done` + ); + } +})(); + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/lazy-methods-return-closes-iterator.js b/js/src/tests/non262/AsyncIterator/prototype/lazy-methods-return-closes-iterator.js new file mode 100644 index 0000000000..98a0902b75 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/lazy-methods-return-closes-iterator.js @@ -0,0 +1,63 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +// +// +/*--- +esid: pending +description: Calling `return` on a lazy %AsyncIterator.prototype% method closes the source iterator. +info: > + Iterator Helpers proposal 2.1.6 +features: [iterator-helpers] +---*/ + +class TestIterator extends AsyncIterator { + async next() { + return {done: false, value: 1}; + } + + closed = false; + async return(value) { + this.closed = true; + return {done: true, value}; + } +} + +async function* gen(x) { yield x; } + +const methods = [ + iter => iter.map(x => x), + iter => iter.filter(x => x), + iter => iter.take(1), + iter => iter.drop(0), + iter => iter.asIndexedPairs(), + iter => iter.flatMap(gen), +]; + +for (const method of methods) { + const iter = new TestIterator(); + const iterHelper = method(iter); + + iterHelper.next().then(() => { + assertEq(iter.closed, false); + iterHelper.return(0).then(({done, value}) => { + assertEq(iter.closed, true); + assertEq(done, true); + assertEq(value, 0); + }); + }); +} + +for (const method of methods) { + const iter = new TestIterator(); + const iterHelper = method(iter); + + assertEq(iter.closed, false); + iterHelper.return(0).then(({done, value}) => { + assertEq(iter.closed, true); + assertEq(done, true); + assertEq(value, 0); + }); +} + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/lazy-methods-return-new-iterator-result.js b/js/src/tests/non262/AsyncIterator/prototype/lazy-methods-return-new-iterator-result.js new file mode 100644 index 0000000000..02cae2740d --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/lazy-methods-return-new-iterator-result.js @@ -0,0 +1,42 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +// +// +/*--- +esid: pending +description: Lazy Iterator Helper methods return new iterator result objects. +info: > + Iterator Helpers proposal 2.1.6 +features: [iterator-helpers] +---*/ + +const iterResult = {done: false, value: 1, testProperty: 'test'}; +class TestIterator extends AsyncIterator { + async next() { + return iterResult; + } +} + +async function* gen(x) { yield x; } + +const methods = [ + iter => iter.map(x => x), + iter => iter.filter(x => true), + iter => iter.take(1), + iter => iter.drop(0), + iter => iter.asIndexedPairs(), + iter => iter.flatMap(gen), +]; + +// Call `return` before stepping the iterator: +for (const method of methods) { + const iter = new TestIterator(); + const iterHelper = method(iter); + iterHelper.next().then(result => { + assertEq(result == iterResult, false); + assertEq(result.testProperty, undefined); + }); +} + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/lazy-methods-throw-closes-iterator-before-next.js b/js/src/tests/non262/AsyncIterator/prototype/lazy-methods-throw-closes-iterator-before-next.js new file mode 100644 index 0000000000..4e9d9f6421 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/lazy-methods-throw-closes-iterator-before-next.js @@ -0,0 +1,58 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +// +// +/*--- +esid: pending +description: Calling `throw` on a lazy %AsyncIterator.prototype% method closes the source iterator. +info: > + Iterator Helpers proposal 2.1.6 +features: [iterator-helpers] +---*/ + +class TestError extends Error {} + +class TestIterator extends AsyncIterator { + async next() { + return {done: false, value: 1}; + } + + closed = false; + async return(value) { + this.closed = true; + return {done: true, value}; + } +} + +async function* gen(x) { yield x; } + +const methods = [ + iter => iter.map(x => x), + iter => iter.filter(x => x), + iter => iter.take(1), + iter => iter.drop(0), + iter => iter.asIndexedPairs(), + iter => iter.flatMap(gen), +]; + +for (const method of methods) { + const iter = new TestIterator(); + const iterHelper = method(iter); + + assertEq(iter.closed, false); + iterHelper.throw(new TestError()).then( + _ => assertEq(true, false, 'Expected reject.'), + err => { + assertEq(err instanceof TestError, true); + assertEq(iter.closed, true); + + iterHelper.next().then(({done, value}) => { + assertEq(done, true); + assertEq(value, undefined); + }); + }, + ); +} + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/lazy-methods-throw-eagerly-on-next-non-callable.js b/js/src/tests/non262/AsyncIterator/prototype/lazy-methods-throw-eagerly-on-next-non-callable.js new file mode 100644 index 0000000000..489887d020 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/lazy-methods-throw-eagerly-on-next-non-callable.js @@ -0,0 +1,35 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +// +// +/*--- +esid: pending +description: Lazy %AsyncIterator.prototype% methods throw eagerly when `next` is non-callable. +info: > + Iterator Helpers proposal 1.1.1 +features: [iterator-helpers] +---*/ + +async function* gen(x) { yield x; } +const methods = [ + next => AsyncIterator.prototype.map.bind({next}, x => x), + next => AsyncIterator.prototype.filter.bind({next}, x => x), + next => AsyncIterator.prototype.take.bind({next}, 1), + next => AsyncIterator.prototype.drop.bind({next}, 0), + next => AsyncIterator.prototype.asIndexedPairs.bind({next}), + next => AsyncIterator.prototype.flatMap.bind({next}, gen), +]; + +for (const method of methods) { + assertThrowsInstanceOf(method(0), TypeError); + assertThrowsInstanceOf(method(false), TypeError); + assertThrowsInstanceOf(method(undefined), TypeError); + assertThrowsInstanceOf(method(null), TypeError); + assertThrowsInstanceOf(method(''), TypeError); + assertThrowsInstanceOf(method(Symbol('')), TypeError); + assertThrowsInstanceOf(method({}), TypeError); + assertThrowsInstanceOf(method([]), TypeError); +} + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/lazy-methods-throw-eagerly-on-non-callable.js b/js/src/tests/non262/AsyncIterator/prototype/lazy-methods-throw-eagerly-on-non-callable.js new file mode 100644 index 0000000000..d8a9b390fa --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/lazy-methods-throw-eagerly-on-non-callable.js @@ -0,0 +1,40 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +// +// +/*--- +esid: pending +description: Lazy %AsyncIterator.prototype% methods throw eagerly when passed non-callables. +info: > + Iterator Helpers proposal 2.1.6 +features: [iterator-helpers] +---*/ + +async function* gen() {} + +const methods = [ + (iter, fn) => iter.map(fn), + (iter, fn) => iter.filter(fn), + (iter, fn) => iter.flatMap(fn), +]; + +for (const method of methods) { + assertThrowsInstanceOf(() => method(AsyncIterator.prototype, 0), TypeError); + assertThrowsInstanceOf(() => method(AsyncIterator.prototype, false), TypeError); + assertThrowsInstanceOf(() => method(AsyncIterator.prototype, undefined), TypeError); + assertThrowsInstanceOf(() => method(AsyncIterator.prototype, null), TypeError); + assertThrowsInstanceOf(() => method(AsyncIterator.prototype, ''), TypeError); + assertThrowsInstanceOf(() => method(AsyncIterator.prototype, Symbol('')), TypeError); + assertThrowsInstanceOf(() => method(AsyncIterator.prototype, {}), TypeError); + + assertThrowsInstanceOf(() => method(gen(), 0), TypeError); + assertThrowsInstanceOf(() => method(gen(), false), TypeError); + assertThrowsInstanceOf(() => method(gen(), undefined), TypeError); + assertThrowsInstanceOf(() => method(gen(), null), TypeError); + assertThrowsInstanceOf(() => method(gen(), ''), TypeError); + assertThrowsInstanceOf(() => method(gen(), Symbol('')), TypeError); + assertThrowsInstanceOf(() => method(gen(), {}), TypeError); +} + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/lazy-methods-throw-eagerly-on-non-iterator.js b/js/src/tests/non262/AsyncIterator/prototype/lazy-methods-throw-eagerly-on-non-iterator.js new file mode 100644 index 0000000000..1a61f5001a --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/lazy-methods-throw-eagerly-on-non-iterator.js @@ -0,0 +1,31 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +// +// +/*--- +esid: pending +description: Lazy %AsyncIterator.prototype% methods throw eagerly when called on non-iterators. +info: > + AsyncIterator Helpers proposal 1.1.1 +features: [iterator-helpers] +---*/ + +async function* gen(x) { yield x; } + +const methods = [ + iter => AsyncIterator.prototype.map.bind(iter, x => x), + iter => AsyncIterator.prototype.filter.bind(iter, x => x), + iter => AsyncIterator.prototype.take.bind(iter, 1), + iter => AsyncIterator.prototype.drop.bind(iter, 0), + iter => AsyncIterator.prototype.asIndexedPairs.bind(iter), + iter => AsyncIterator.prototype.flatMap.bind(iter, gen), +]; + +for (const method of methods) { + for (const value of [undefined, null, 0, false, '', Symbol(''), {}, []]) { + assertThrowsInstanceOf(method(value), TypeError); + } +} + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/lazy-methods-throw-next-done-throws.js b/js/src/tests/non262/AsyncIterator/prototype/lazy-methods-throw-next-done-throws.js new file mode 100644 index 0000000000..276e59fc86 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/lazy-methods-throw-next-done-throws.js @@ -0,0 +1,53 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +// +// +/*--- +esid: pending +description: Lazy %AsyncIterator.prototype% methods throw if `next.done` throws. +info: > + AsyncIterator Helpers proposal 2.1.6 +features: [iterator-helpers] +---*/ + +class TestError extends Error {} +class TestAsyncIterator extends AsyncIterator { + async next() { + return { + get done() { + throw new TestError(); + } + }; + } + + closed = false; + async return() { + this.closed = true; + return {done: true}; + } +} + +async function* gen(x) { yield x; } +const methods = [ + iter => iter.map(x => x), + iter => iter.filter(x => x), + iter => iter.take(1), + iter => iter.drop(0), + iter => iter.asIndexedPairs(), + iter => iter.flatMap(gen), +]; + +for (const method of methods) { + const iterator = new TestAsyncIterator(); + assertEq(iterator.closed, false); + method(iterator).next().then( + _ => assertEq(true, false, 'Expected reject'), + err => { + assertEq(err instanceof TestError, true); + assertEq(iterator.closed, false); + }, + ); +} + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/lazy-methods-throw-next-not-object.js b/js/src/tests/non262/AsyncIterator/prototype/lazy-methods-throw-next-not-object.js new file mode 100644 index 0000000000..bb38a67000 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/lazy-methods-throw-next-not-object.js @@ -0,0 +1,50 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +// +// +/*--- +esid: pending +description: Lazy %AsyncIterator.prototype% methods throw if `next` call returns a non-object. +info: > + Iterator Helpers proposal 2.1.6 +features: [iterator-helpers] +---*/ + +class TestAsyncIterator extends AsyncIterator { + async next(value) { + return value; + } + + closed = false; + async return() { + this.closed = true; + return {done: true}; + } +} + +async function* gen(x) { yield x; } +const methods = [ + iter => iter.map(x => x), + iter => iter.filter(x => x), + iter => iter.take(1), + iter => iter.drop(0), + iter => iter.asIndexedPairs(), + iter => iter.flatMap(gen), +]; + +for (const method of methods) { + for (const value of [undefined, null, 0, false, '', Symbol('')]) { + const iterator = new TestAsyncIterator(); + assertEq(iterator.closed, false); + method(iterator).next(value).then( + _ => assertEq(true, false, 'Expected reject'), + err => { + assertEq(err instanceof TypeError, true); + assertEq(iterator.closed, false); + }, + ); + } +} + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/lazy-methods-throw-on-reentry.js b/js/src/tests/non262/AsyncIterator/prototype/lazy-methods-throw-on-reentry.js new file mode 100644 index 0000000000..d7a582c6c1 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/lazy-methods-throw-on-reentry.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +async function* gen(x) { yield x; } + +const methods = ['map', 'filter', 'flatMap']; + +for (const method of methods) { + const iter = gen('value'); + const iterHelper = iter[method](x => iterHelper.next()); + iterHelper.next().then( + _ => assertEq(true, false, 'Expected reject.'), + err => assertEq(err instanceof TypeError, true), + ); +} + +if (typeof reportCompare == 'function') + reportCompare(0, 0); + diff --git a/js/src/tests/non262/AsyncIterator/prototype/map/length.js b/js/src/tests/non262/AsyncIterator/prototype/map/length.js new file mode 100644 index 0000000000..4bd55f92e0 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/map/length.js @@ -0,0 +1,21 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) +// + +/*--- +esid: pending +description: %AsyncIterator.prototype%.map length value and descriptor. +info: > + 17 ECMAScript Standard Built-in Objects +features: [iterator-helpers] +---*/ + +assertEq(AsyncIterator.prototype.map.length, 1); + +const propertyDescriptor = Reflect.getOwnPropertyDescriptor(AsyncIterator.prototype.map, 'length'); +assertEq(propertyDescriptor.value, 1); +assertEq(propertyDescriptor.enumerable, false); +assertEq(propertyDescriptor.writable, false); +assertEq(propertyDescriptor.configurable, true); + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/map/map.js b/js/src/tests/non262/AsyncIterator/prototype/map/map.js new file mode 100644 index 0000000000..b3adb5cd6c --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/map/map.js @@ -0,0 +1,23 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +async function* gen() { + yield 1; + yield 2; + yield 3; +} + +const iter = gen().map(x => x**2); + +for (const v of [1, 4, 9]) { + iter.next().then( + ({done, value}) => { + assertEq(done, false); + assertEq(value, v); + } + ); +} + +iter.next().then(({done}) => assertEq(done, true)); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/map/name.js b/js/src/tests/non262/AsyncIterator/prototype/map/name.js new file mode 100644 index 0000000000..efb7181966 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/map/name.js @@ -0,0 +1,19 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) +/*--- +esid: pending +description: %AsyncIterator.prototype%.map.name value and descriptor. +info: > + 17 ECMAScript Standard Built-in Objects +features: [iterator-helpers] +---*/ + +assertEq(AsyncIterator.prototype.map.name, 'map'); + +const propertyDescriptor = Reflect.getOwnPropertyDescriptor(AsyncIterator.prototype.map, 'name'); +assertEq(propertyDescriptor.value, 'map'); +assertEq(propertyDescriptor.enumerable, false); +assertEq(propertyDescriptor.writable, false); +assertEq(propertyDescriptor.configurable, true); + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/reduce/accumulator-set-to-initial-value.js b/js/src/tests/non262/AsyncIterator/prototype/reduce/accumulator-set-to-initial-value.js new file mode 100644 index 0000000000..943094aabc --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/reduce/accumulator-set-to-initial-value.js @@ -0,0 +1,13 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +const reducer = (acc, _) => acc; +async function* gen() { + yield 1; + yield 2; + yield 3; +} + +gen().reduce(reducer, 0).then(value => assertEq(value, 0)); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/reduce/async-writes.js b/js/src/tests/non262/AsyncIterator/prototype/reduce/async-writes.js new file mode 100644 index 0000000000..2328cc3521 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/reduce/async-writes.js @@ -0,0 +1,20 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +let x = {a: () => true}; + +async function* gen() { + yield x.a(); + yield x.a(); +} + +gen().reduce(() => {}, 0).then( + () => assertEq(true, false, 'expected error'), + err => assertEq(err instanceof Error, true), +); + +x.a = () => { + throw Error(); +}; + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/reduce/check-fn-after-getting-iterator.js b/js/src/tests/non262/AsyncIterator/prototype/reduce/check-fn-after-getting-iterator.js new file mode 100644 index 0000000000..4a5484f786 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/reduce/check-fn-after-getting-iterator.js @@ -0,0 +1,27 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) +const log = []; +const handlerProxy = new Proxy({}, { + get: (target, key, receiver) => (...args) => { + log.push(`${key}: ${args[1]?.toString()}`); + return Reflect[key](...args); + }, +}); + +class TestIterator extends AsyncIterator { + next() { + return Promise.resolve({ done: this.closed }); + } +} + +const iter = new Proxy(new TestIterator(), handlerProxy); +iter.reduce(1).then(() => assertEq(true, false, 'expected error'), err => { + assertEq(err instanceof TypeError, true); + assertEq( + log.join('\n'), + `get: reduce +get: next` + ); +}); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/reduce/descriptor.js b/js/src/tests/non262/AsyncIterator/prototype/reduce/descriptor.js new file mode 100644 index 0000000000..6472c1a14d --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/reduce/descriptor.js @@ -0,0 +1,13 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) +/*--- + Descriptor property of AsyncIterator.prototype.reduce +---*/ + +const propDesc = Reflect.getOwnPropertyDescriptor(AsyncIterator.prototype, 'reduce'); +assertEq(typeof propDesc.value, 'function'); +assertEq(propDesc.writable, true); +assertEq(propDesc.enumerable, false); +assertEq(propDesc.configurable, true); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/reduce/empty-iterator-without-initial-value-throws.js b/js/src/tests/non262/AsyncIterator/prototype/reduce/empty-iterator-without-initial-value-throws.js new file mode 100644 index 0000000000..c54fccba20 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/reduce/empty-iterator-without-initial-value-throws.js @@ -0,0 +1,9 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +async function* gen() {} +gen().reduce((x, y) => x + y).then(() => assertEq(true, false, 'expected error'), err => { + assertEq(err instanceof TypeError, true); +}); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/reduce/error-from-correct-realm.js b/js/src/tests/non262/AsyncIterator/prototype/reduce/error-from-correct-realm.js new file mode 100644 index 0000000000..16c0ac51a9 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/reduce/error-from-correct-realm.js @@ -0,0 +1,21 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +const otherGlobal = newGlobal({newCompartment: true}); +assertEq(TypeError !== otherGlobal.TypeError, true); + +async function *gen() {} + +gen().reduce().then(() => assertEq(true, false, 'expected error'), err => { + assertEq(err instanceof TypeError, true); +}); + +otherGlobal.AsyncIterator.prototype.reduce.call(gen()).then(() => assertEq(true, false, 'expected error'), err => { + assertEq( + err instanceof otherGlobal.TypeError, + true, + 'TypeError comes from the realm of the method.', + ); +}); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/reduce/interleaving-calls.js b/js/src/tests/non262/AsyncIterator/prototype/reduce/interleaving-calls.js new file mode 100644 index 0000000000..8961c69903 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/reduce/interleaving-calls.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +const log = []; +async function* gen(n) { + log.push(`${n}`); + yield 1; + log.push(`${n}`); + yield 2; +} + +Promise.all([gen(1).reduce(() => {}, 0), gen(2).reduce(() => {}, 0)]).then( + () => { + assertEq( + log.join(' '), + '1 2 1 2', + ); + }, + err => { + throw err; + } +); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/reduce/iterator-empty-return-initial-value.js b/js/src/tests/non262/AsyncIterator/prototype/reduce/iterator-empty-return-initial-value.js new file mode 100644 index 0000000000..b664d25729 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/reduce/iterator-empty-return-initial-value.js @@ -0,0 +1,9 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +const reducer = (x, y) => 0; +async function *gen() {} + +gen().reduce(reducer, 1).then(result => assertEq(result, 1)); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/reduce/iterator-next-return-non-object-throws.js b/js/src/tests/non262/AsyncIterator/prototype/reduce/iterator-next-return-non-object-throws.js new file mode 100644 index 0000000000..57c244d603 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/reduce/iterator-next-return-non-object-throws.js @@ -0,0 +1,31 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +class TestIterator extends AsyncIterator { + constructor(value) { + super(); + this.value = value; + } + + next() { + return Promise.resolve(this.value); + } +} + +const sum = (x, y) => x + y; +function check(value) { + const iter = new TestIterator(value); + iter.reduce(sum).then(() => assertEq(true, false, 'expected error'), err => { + assertEq(err instanceof TypeError, true); + }); +} + +check(); +check(undefined); +check(null); +check(0); +check(false); +check(''); +check(Symbol('')); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/reduce/left-associative.js b/js/src/tests/non262/AsyncIterator/prototype/reduce/left-associative.js new file mode 100644 index 0000000000..91147a1fba --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/reduce/left-associative.js @@ -0,0 +1,15 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +async function* gen() { + yield 1; + yield 2; + yield 3; +} + +gen().reduce((x, y) => `(${x}+${y})`, 0) + .then(result => assertEq(result, '(((0+1)+2)+3)')); +gen().reduce((x, y) => `(${x}+${y})`) + .then(result => assertEq(result, '((1+2)+3)')); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/reduce/length.js b/js/src/tests/non262/AsyncIterator/prototype/reduce/length.js new file mode 100644 index 0000000000..022df65164 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/reduce/length.js @@ -0,0 +1,17 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) +/*--- + The `length` property of AsyncIterator.prototype.reduce. +info: | + ES7 section 17: Unless otherwise specified, the length property of a built-in + Function object has the attributes { [[Writable]]: false, [[Enumerable]]: + false, [[Configurable]]: true }. +---*/ + +const propDesc = Reflect.getOwnPropertyDescriptor(AsyncIterator.prototype.reduce, 'length'); +assertEq(propDesc.value, 1); +assertEq(propDesc.writable, false); +assertEq(propDesc.enumerable, false); +assertEq(propDesc.configurable, true); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/reduce/name.js b/js/src/tests/non262/AsyncIterator/prototype/reduce/name.js new file mode 100644 index 0000000000..aada19b46d --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/reduce/name.js @@ -0,0 +1,13 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) +/*--- + `name` property of AsyncIterator.prototype.reduce. +---*/ + +const propDesc = Reflect.getOwnPropertyDescriptor(AsyncIterator.prototype.reduce, 'name'); +assertEq(propDesc.value, 'reduce'); +assertEq(propDesc.writable, false); +assertEq(propDesc.enumerable, false); +assertEq(propDesc.configurable, true); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/reduce/next-throws-iterator-not-closed.js b/js/src/tests/non262/AsyncIterator/prototype/reduce/next-throws-iterator-not-closed.js new file mode 100644 index 0000000000..796cdb1895 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/reduce/next-throws-iterator-not-closed.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +class TestIterator extends AsyncIterator { + next() { + throw new Error(); + } + + closed = false; + return() { + this.closed = true; + } +} + +const sum = (x, y) => x + y; +const iter = new TestIterator(); + +assertEq(iter.closed, false); +iter.reduce(sum).then(() => assertEq(true, false, 'expected error'), err => { + assertEq(err instanceof Error, true); + assertEq(iter.closed, false); +}); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/reduce/no-initial-value-set-accumulator-to-first-value.js b/js/src/tests/non262/AsyncIterator/prototype/reduce/no-initial-value-set-accumulator-to-first-value.js new file mode 100644 index 0000000000..0e480dbe4a --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/reduce/no-initial-value-set-accumulator-to-first-value.js @@ -0,0 +1,13 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +const reducer = (acc, _) => acc; +async function* gen() { + yield 1; + yield 2; + yield 3; +} + +gen().reduce(reducer).then(result => assertEq(result, 1)); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/reduce/proxy.js b/js/src/tests/non262/AsyncIterator/prototype/reduce/proxy.js new file mode 100644 index 0000000000..b1e6d52495 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/reduce/proxy.js @@ -0,0 +1,44 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) +// +// This test checks that %Iterator.prototype%.reduce only gets the `next` method off of the +// iterator once, and never accesses the @@iterator property. +const log = []; +const handlerProxy = new Proxy({}, { + get: (target, key, receiver) => (...args) => { + log.push(`${key}: ${args[1]?.toString()}`); + return Reflect[key](...args); + }, +}); + +class Counter extends AsyncIterator { + value = 0; + next() { + const value = this.value; + if (value < 2) { + this.value = value + 1; + return Promise.resolve({done: false, value}); + } + return Promise.resolve({done: true}); + } +} + +const iter = new Proxy(new Counter(), handlerProxy); +iter.reduce((x, y) => x + y).then(() => { + assertEq( + log.join('\n'), + `get: reduce +get: next +get: value +set: value +getOwnPropertyDescriptor: value +defineProperty: value +get: value +set: value +getOwnPropertyDescriptor: value +defineProperty: value +get: value` + ); +}); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/reduce/reduce.js b/js/src/tests/non262/AsyncIterator/prototype/reduce/reduce.js new file mode 100644 index 0000000000..d2382e4d53 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/reduce/reduce.js @@ -0,0 +1,13 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +const reducer = (acc, value) => acc + value; +async function* gen() { + yield 1; + yield 2; + yield 3; +} + +gen().reduce(reducer, 0).then(result => assertEq(result, 6)); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/reduce/reducer-not-callable-throws.js b/js/src/tests/non262/AsyncIterator/prototype/reduce/reducer-not-callable-throws.js new file mode 100644 index 0000000000..c7fb2005d8 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/reduce/reducer-not-callable-throws.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +async function *gen() {} + +function check(fn) { + gen().reduce(fn).then(() => { + throw new Error('every should have thrown'); + }, + err => { + assertEq(err instanceof TypeError, true); + }); +} + +check(); +check(undefined); +check(null); +check(0); +check(false); +check(''); +check(Symbol('')); +check({}); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/reduce/reducer-throws-iterator-closed.js b/js/src/tests/non262/AsyncIterator/prototype/reduce/reducer-throws-iterator-closed.js new file mode 100644 index 0000000000..2a55a89850 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/reduce/reducer-throws-iterator-closed.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +class TestIterator extends AsyncIterator { + next() { + return Promise.resolve({ done: this.closed, value: undefined }); + } + + closed = false; + return() { + this.closed = true; + } +} + +const reducer = (x, y) => { throw new Error(); }; +const iter = new TestIterator(); + +assertEq(iter.closed, false); +iter.reduce(reducer).then(() => assertEq(true, false, 'expected error'), err => { + assertEq(err instanceof Error, true); + assertEq(iter.closed, true); +}); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/reduce/this-not-iterator-throws.js b/js/src/tests/non262/AsyncIterator/prototype/reduce/this-not-iterator-throws.js new file mode 100644 index 0000000000..7f95920fbd --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/reduce/this-not-iterator-throws.js @@ -0,0 +1,19 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +const sum = (x, y) => x + y; +function check(x) { + AsyncIterator.prototype.reduce.call(x, sum).then( + () => assertEq(true, false, 'expected error'), + err => { + assertEq(err instanceof TypeError, true); + } + ); +} + +check(); +check(undefined); +check({}); +check({next: 0}); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/reduce/value-throws-iterator-not-closed.js b/js/src/tests/non262/AsyncIterator/prototype/reduce/value-throws-iterator-not-closed.js new file mode 100644 index 0000000000..bfe4a9213f --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/reduce/value-throws-iterator-not-closed.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +class TestError extends Error {} +class TestIterator extends AsyncIterator { + next() { + return Promise.resolve({ + done: false, + get value() { + throw new TestError(); + } + }); + } + + closed = false; + return() { + closed = true; + } +} + +const iterator = new TestIterator(); +assertEq(iterator.closed, false, 'iterator starts unclosed'); +iterator.reduce((x, y) => x + y, 0).then(() => assertEq(true, false, 'expected error'), err => { + assertEq(err instanceof TestError, true); + assertEq(iterator.closed, false, 'iterator remains unclosed'); +}); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/some/async-writes.js b/js/src/tests/non262/AsyncIterator/prototype/some/async-writes.js new file mode 100644 index 0000000000..384bf2e115 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/some/async-writes.js @@ -0,0 +1,20 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +let x = {a: () => true}; + +async function* gen() { + yield x.a(); + yield x.a(); +} + +gen().some(() => {}).then( + () => assertEq(true, false, 'expected error'), + err => assertEq(err instanceof Error, true), +); + +x.a = () => { + throw Error(); +}; + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/some/check-fn-after-getting-iterator.js b/js/src/tests/non262/AsyncIterator/prototype/some/check-fn-after-getting-iterator.js new file mode 100644 index 0000000000..41dd30bee0 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/some/check-fn-after-getting-iterator.js @@ -0,0 +1,31 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) +const log = []; +const handlerProxy = new Proxy({}, { + get: (target, key, receiver) => (...args) => { + log.push(`${key}: ${args[1]?.toString()}`); + return Reflect[key](...args); + }, +}); + +class TestIterator extends AsyncIterator { + next() { + return Promise.resolve({done: true}); + } +} + +async function* gen() { + yield 1; +} + +const iter = new Proxy(new TestIterator(), handlerProxy); +iter.some(1).then(() => assertEq(true, false, 'expected error'), err => assertEq(err instanceof TypeError, true)); + +assertEq( + log.join('\n'), + `get: some +get: next` +); + + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/some/coerce-result-to-boolean.js b/js/src/tests/non262/AsyncIterator/prototype/some/coerce-result-to-boolean.js new file mode 100644 index 0000000000..8b56511040 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/some/coerce-result-to-boolean.js @@ -0,0 +1,29 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +async function* gen(value) { + yield value; +} +const fn = x => x; +function check(value, expected) { + gen(value).some(fn).then(result => assertEq(result, expected)); +} + +check(true, true); +check(1, true); +check([], true); +check({}, true); +check('test', true); + +check(false, false); +check(0, false); +check('', false); +check(null, false); +check(undefined, false); +check(NaN, false); +check(-0, false); +check(0n, false); +check(createIsHTMLDDA(), false); +check(Promise.resolve(false), false); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/some/descriptor.js b/js/src/tests/non262/AsyncIterator/prototype/some/descriptor.js new file mode 100644 index 0000000000..d9f5ef8fb7 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/some/descriptor.js @@ -0,0 +1,13 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) +/*--- + Descriptor property of AsyncIterator.prototype.some +---*/ + +const propDesc = Reflect.getOwnPropertyDescriptor(AsyncIterator.prototype, 'some'); +assertEq(typeof propDesc.value, 'function'); +assertEq(propDesc.writable, true); +assertEq(propDesc.enumerable, false); +assertEq(propDesc.configurable, true); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/some/error-from-correct-realm.js b/js/src/tests/non262/AsyncIterator/prototype/some/error-from-correct-realm.js new file mode 100644 index 0000000000..bb4b637c1a --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/some/error-from-correct-realm.js @@ -0,0 +1,21 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +const otherGlobal = newGlobal({newCompartment: true}); +assertEq(TypeError !== otherGlobal.TypeError, true); + +async function *gen() {} + +gen().some().then(() => assertEq(true, false, 'expected error'), err => { + assertEq(err instanceof TypeError, true); +}); + +otherGlobal.AsyncIterator.prototype.some.call(gen()).then(() => assertEq(true, false, 'expected error'), err => { + assertEq( + err instanceof otherGlobal.TypeError, + true, + 'TypeError comes from the realm of the method.', + ); +}); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/some/fn-not-callable-throws.js b/js/src/tests/non262/AsyncIterator/prototype/some/fn-not-callable-throws.js new file mode 100644 index 0000000000..74a11ceebf --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/some/fn-not-callable-throws.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +async function *gen() { + yield 1; +} + +function check(fn) { + gen().some(fn).then( + () => { + throw new Error('every should have thrown'); + }, + err => { + assertEq(err instanceof TypeError, true); + } + ); +} + +check(); +check(undefined); +check(null); +check(0); +check(false); +check(''); +check(Symbol('')); +check({}); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/some/fn-throws-close-iterator.js b/js/src/tests/non262/AsyncIterator/prototype/some/fn-throws-close-iterator.js new file mode 100644 index 0000000000..bdf5119f57 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/some/fn-throws-close-iterator.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +class TestIterator extends AsyncIterator { + next() { + return Promise.resolve({ done: this.closed }); + } + + closed = false; + return() { + this.closed = true; + } +} + +const fn = () => { throw new Error(); }; +const iter = new TestIterator(); + +assertEq(iter.closed, false); +iter.some(fn).then(() => assertEq(true, false, 'expected error'), err => { + assertEq(err instanceof Error, true); + assertEq(iter.closed, true); +}); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/some/interleaving-calls.js b/js/src/tests/non262/AsyncIterator/prototype/some/interleaving-calls.js new file mode 100644 index 0000000000..d5882b9532 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/some/interleaving-calls.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +const log = []; +async function* gen(n) { + log.push(`${n}`); + yield 1; + log.push(`${n}`); + yield 2; +} + +Promise.all([gen(1).some(() => {}), gen(2).some(() => {})]).then( + () => { + assertEq( + log.join(' '), + '1 2 1 2', + ); + }, + err => { + throw err; + } +); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/some/length.js b/js/src/tests/non262/AsyncIterator/prototype/some/length.js new file mode 100644 index 0000000000..ce86dc8b2f --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/some/length.js @@ -0,0 +1,17 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) +/*--- + The `length` property of AsyncIterator.prototype.some. +info: | + ES7 section 17: Unless otherwise specified, the length property of a built-in + Function object has the attributes { [[Writable]]: false, [[Enumerable]]: + false, [[Configurable]]: true }. +---*/ + +const propDesc = Reflect.getOwnPropertyDescriptor(AsyncIterator.prototype.some, 'length'); +assertEq(propDesc.value, 1); +assertEq(propDesc.writable, false); +assertEq(propDesc.enumerable, false); +assertEq(propDesc.configurable, true); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/some/name.js b/js/src/tests/non262/AsyncIterator/prototype/some/name.js new file mode 100644 index 0000000000..f2e84a139c --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/some/name.js @@ -0,0 +1,13 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) +/*--- + `name` property of AsyncIterator.prototype.some. +---*/ + +const propDesc = Reflect.getOwnPropertyDescriptor(AsyncIterator.prototype.some, 'name'); +assertEq(propDesc.value, 'some'); +assertEq(propDesc.writable, false); +assertEq(propDesc.enumerable, false); +assertEq(propDesc.configurable, true); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/some/next-throws-iterator-not-closed.js b/js/src/tests/non262/AsyncIterator/prototype/some/next-throws-iterator-not-closed.js new file mode 100644 index 0000000000..89574e0f8e --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/some/next-throws-iterator-not-closed.js @@ -0,0 +1,23 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +class TestIterator extends AsyncIterator { + next() { + throw new Error(); + } + + closed = false; + return() { + this.closed = true; + } +} + +const fn = () => {}; +const iter = new TestIterator(); + +assertEq(iter.closed, false); +iter.some(fn).then(() => assertEq(true, false, 'expected error'), err => { + assertEq(err instanceof Error, true); + assertEq(iter.closed, false); +}); +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/some/proxy.js b/js/src/tests/non262/AsyncIterator/prototype/some/proxy.js new file mode 100644 index 0000000000..6ae089ae38 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/some/proxy.js @@ -0,0 +1,46 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) +// +// This test checks that %Iterator.prototype%.some only gets the `next` method off of the +// iterator once, and never accesses the @@iterator property. +const log = []; +const handlerProxy = new Proxy({}, { + get: (target, key, receiver) => (...args) => { + log.push(`${key}: ${args[1]?.toString()}`); + return Reflect[key](...args); + }, +}); + +class Counter extends AsyncIterator { + value = 0; + next() { + const value = this.value; + if (value < 2) { + this.value = value + 1; + return Promise.resolve({done: false, value}); + } + return Promise.resolve({done: true}); + } +} + +const iter = new Proxy(new Counter(), handlerProxy); +iter.some(x => x % 2 == 1).then(value => { + assertEq(value, true); + + assertEq( + log.join('\n'), + `get: some +get: next +get: value +set: value +getOwnPropertyDescriptor: value +defineProperty: value +get: value +set: value +getOwnPropertyDescriptor: value +defineProperty: value +get: return` + ); +}); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/some/return-false-if-none-match.js b/js/src/tests/non262/AsyncIterator/prototype/some/return-false-if-none-match.js new file mode 100644 index 0000000000..118436d70b --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/some/return-false-if-none-match.js @@ -0,0 +1,16 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +async function* gen() { + yield 1; + yield 3; + yield 5; +} +const fn = x => x % 2 == 0; + +gen().some(fn).then(result => assertEq(result, false)); + +async function* empty() {} +empty().some(x => x).then(result => assertEq(result, false)); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/some/short-circuit-on-true.js b/js/src/tests/non262/AsyncIterator/prototype/some/short-circuit-on-true.js new file mode 100644 index 0000000000..3d7250b9a2 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/some/short-circuit-on-true.js @@ -0,0 +1,20 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +async function* gen() { + yield 1; + yield 2; + yield 3; +} +const log = []; +const fn = (value) => { + log.push(value.toString()); + return value % 2 == 0; +}; + +gen().some(fn).then(result => { + assertEq(result, true); + assertEq(log.join(','), '1,2'); +}); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/some/this-not-iterator-throws.js b/js/src/tests/non262/AsyncIterator/prototype/some/this-not-iterator-throws.js new file mode 100644 index 0000000000..1233fc371c --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/some/this-not-iterator-throws.js @@ -0,0 +1,19 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +const fn = x => x; +function check(x) { + AsyncIterator.prototype.some.call(x, fn).then( + () => assertEq(true, false, 'expected error'), + err => { + assertEq(err instanceof TypeError, true); + } + ); +} + +check(); +check(undefined); +check({}); +check({next: 0}); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/some/value-throws-iterator-not-closed.js b/js/src/tests/non262/AsyncIterator/prototype/some/value-throws-iterator-not-closed.js new file mode 100644 index 0000000000..f7a344eea4 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/some/value-throws-iterator-not-closed.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +class TestError extends Error {} +class TestIterator extends AsyncIterator { + next() { + return Promise.resolve({ + done: false, + get value() { + throw new TestError(); + } + }); + } + + closed = false; + return() { + closed = true; + } +} + +const iterator = new TestIterator(); +assertEq(iterator.closed, false, 'iterator starts unclosed'); +iterator.some(x => x).then(() => assertEq(true, false, 'expected error'), err => { + assertEq(err instanceof TestError, true); + assertEq(iterator.closed, false, 'iterator remains unclosed'); +}); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/take-drop-throw-eagerly-on-negative.js b/js/src/tests/non262/AsyncIterator/prototype/take-drop-throw-eagerly-on-negative.js new file mode 100644 index 0000000000..f21c78e650 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/take-drop-throw-eagerly-on-negative.js @@ -0,0 +1,30 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +// +// +/*--- +esid: pending +description: `take` and `drop` throw eagerly when passed negative numbers, after rounding towards 0. +info: > + Iterator Helpers proposal 2.1.6.4 and 2.1.6.5 +features: [iterator-helpers] +---*/ + +async function* gen() {} +const iter = gen(); +const methods = [ + value => iter.take(value), + value => iter.drop(value), +]; + +for (const method of methods) { + assertThrowsInstanceOf(() => method(-1), RangeError); + assertThrowsInstanceOf(() => method(-Infinity), RangeError); + + // Both -0 and -0.9 round towards 0. + method(-0); + method(-0.9); +} + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/take-drop-throw-eagerly-on-non-integer.js b/js/src/tests/non262/AsyncIterator/prototype/take-drop-throw-eagerly-on-non-integer.js new file mode 100644 index 0000000000..a81b407afc --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/take-drop-throw-eagerly-on-non-integer.js @@ -0,0 +1,33 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +// +// +/*--- +esid: pending +description: `take` and `drop` throw eagerly when passed values that can't be converted to numbers. +info: > + Iterator Helpers proposal 2.1.6.4 and 2.1.6.5 +features: [iterator-helpers] +---*/ + +async function* gen() {} +const iter = gen(); +const methods = [ + value => iter.take(value), + value => iter.drop(value), +]; + +const objectWithToPrimitive = { + [Symbol.toPrimitive]() { + return {}; + } +}; + +for (const method of methods) { + assertThrowsInstanceOf(() => method(0n), TypeError); + assertThrowsInstanceOf(() => method(Symbol('')), TypeError); + assertThrowsInstanceOf(() => method(objectWithToPrimitive), TypeError); +} + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/take/close-iterator-when-none-remaining.js b/js/src/tests/non262/AsyncIterator/prototype/take/close-iterator-when-none-remaining.js new file mode 100644 index 0000000000..6ae3e34b5d --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/take/close-iterator-when-none-remaining.js @@ -0,0 +1,45 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +// +// +/*--- +esid: pending +description: %AsyncIterator.prototype%.take closes the iterator when remaining is 0. +info: > + Iterator Helpers proposal 2.1.6.4 +features: [iterator-helpers] +---*/ + +class TestIterator extends AsyncIterator { + async next() { + return {done: false, value: 'value'}; + } + + closed = false; + async return() { + this.closed = true; + return {done: true}; + } +} + +const iter = new TestIterator(); +const iterTake = iter.take(1); + +iterTake.next().then( + ({done, value}) => { + assertEq(done, false); + assertEq(value, 'value'); + assertEq(iter.closed, false); + + iterTake.next().then( + ({done, value}) => { + assertEq(done, true); + assertEq(value, undefined); + assertEq(iter.closed, true); + } + ); + } +); + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/take/length.js b/js/src/tests/non262/AsyncIterator/prototype/take/length.js new file mode 100644 index 0000000000..0daf649a58 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/take/length.js @@ -0,0 +1,21 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) +// + +/*--- +esid: pending +description: %AsyncIterator.prototype%.take length value and descriptor. +info: > + 17 ECMAScript Standard Built-in Objects +features: [iterator-helpers] +---*/ + +assertEq(AsyncIterator.prototype.take.length, 1); + +const propertyDescriptor = Reflect.getOwnPropertyDescriptor(AsyncIterator.prototype.take, 'length'); +assertEq(propertyDescriptor.value, 1); +assertEq(propertyDescriptor.enumerable, false); +assertEq(propertyDescriptor.writable, false); +assertEq(propertyDescriptor.configurable, true); + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/take/name.js b/js/src/tests/non262/AsyncIterator/prototype/take/name.js new file mode 100644 index 0000000000..6d126390fb --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/take/name.js @@ -0,0 +1,19 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) +/*--- +esid: pending +description: %AsyncIterator.prototype%.take.name value and descriptor. +info: > + 17 ECMAScript Standard Built-in Objects +features: [iterator-helpers] +---*/ + +assertEq(AsyncIterator.prototype.take.name, 'take'); + +const propertyDescriptor = Reflect.getOwnPropertyDescriptor(AsyncIterator.prototype.take, 'name'); +assertEq(propertyDescriptor.value, 'take'); +assertEq(propertyDescriptor.enumerable, false); +assertEq(propertyDescriptor.writable, false); +assertEq(propertyDescriptor.configurable, true); + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/take/take-more-than-available.js b/js/src/tests/non262/AsyncIterator/prototype/take/take-more-than-available.js new file mode 100644 index 0000000000..65355704ae --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/take/take-more-than-available.js @@ -0,0 +1,34 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +// +// +/*--- +esid: pending +description: %AsyncIterator.prototype%.take returns if the iterator is done. +info: > + Iterator Helpers proposal 2.1.6.4 + 2. Repeat, + ... + c. Let next be ? Await(? IteratorNext(iterated, lastValue)). + d. If ? IteratorComplete(next) is false, return undefined. +features: [iterator-helpers] +---*/ + +async function* gen(values) { + yield* values; +} + +(async () => { + const iter = gen([1, 2]).take(10); + for (const expected of [1, 2]) { + const result = await iter.next(); + assertEq(result.value, expected); + assertEq(result.done, false); + } + const result = await iter.next(); + assertEq(result.value, undefined); + assertEq(result.done, true); +})(); + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/take/take.js b/js/src/tests/non262/AsyncIterator/prototype/take/take.js new file mode 100644 index 0000000000..a734da8c2c --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/take/take.js @@ -0,0 +1,23 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +async function* gen() { + yield 1; + yield 2; + yield 3; +} + +let iter = gen().take(2); + +for (const v of [1, 2]) { + iter.next().then( + ({done, value}) => { + assertEq(done, false); + assertEq(value, v); + } + ); +} + +iter.next().then(({done}) => assertEq(done, true)); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/toArray/async-writes.js b/js/src/tests/non262/AsyncIterator/prototype/toArray/async-writes.js new file mode 100644 index 0000000000..8246d8d4ed --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/toArray/async-writes.js @@ -0,0 +1,20 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +let x = {a: () => true}; + +async function* gen() { + yield x.a(); + yield x.a(); +} + +gen().toArray().then( + () => assertEq(true, false, 'expected error'), + err => assertEq(err instanceof Error, true), +); + +x.a = () => { + throw Error(); +}; + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/toArray/create-in-current-realm.js b/js/src/tests/non262/AsyncIterator/prototype/toArray/create-in-current-realm.js new file mode 100644 index 0000000000..6f0b69f8ad --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/toArray/create-in-current-realm.js @@ -0,0 +1,22 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +const otherGlobal = newGlobal({newCompartment: true}); + +async function* gen() { + yield 1; + yield 2; + yield 3; +} + +gen().toArray().then(array => { + assertEq(array instanceof Array, true); + assertEq(array instanceof otherGlobal.Array, false); +}); + +otherGlobal.AsyncIterator.prototype.toArray.call(gen()).then(array => { + assertEq(array instanceof Array, false); + assertEq(array instanceof otherGlobal.Array, true); +}); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/toArray/descriptor.js b/js/src/tests/non262/AsyncIterator/prototype/toArray/descriptor.js new file mode 100644 index 0000000000..58c7a13872 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/toArray/descriptor.js @@ -0,0 +1,13 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) +/*--- + Descriptor property of AsyncIterator.prototype.toArray +---*/ + +const propDesc = Reflect.getOwnPropertyDescriptor(AsyncIterator.prototype, 'toArray'); +assertEq(typeof propDesc.value, 'function'); +assertEq(propDesc.writable, true); +assertEq(propDesc.enumerable, false); +assertEq(propDesc.configurable, true); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/toArray/interleaving-calls.js b/js/src/tests/non262/AsyncIterator/prototype/toArray/interleaving-calls.js new file mode 100644 index 0000000000..88e3fcd5e8 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/toArray/interleaving-calls.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +const log = []; +async function* gen(n) { + log.push(`${n}`); + yield 1; + log.push(`${n}`); + yield 2; +} + +Promise.all([gen(1).toArray(), gen(2).toArray()]).then( + () => { + assertEq( + log.join(' '), + '1 2 1 2', + ); + }, + err => { + throw err; + } +); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/toArray/iterator-empty.js b/js/src/tests/non262/AsyncIterator/prototype/toArray/iterator-empty.js new file mode 100644 index 0000000000..d8eec0454d --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/toArray/iterator-empty.js @@ -0,0 +1,10 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +async function* gen() {} +gen().toArray().then(array => { + assertEq(Array.isArray(array), true); + assertEq(array.length, 0); +}); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/toArray/length.js b/js/src/tests/non262/AsyncIterator/prototype/toArray/length.js new file mode 100644 index 0000000000..f0467d5fc3 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/toArray/length.js @@ -0,0 +1,17 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) +/*--- + The `length` property of AsyncIterator.prototype.toArray. +info: | + ES7 section 17: Unless otherwise specified, the length property of a built-in + Function object has the attributes { [[Writable]]: false, [[Enumerable]]: + false, [[Configurable]]: true }. +---*/ + +const propDesc = Reflect.getOwnPropertyDescriptor(AsyncIterator.prototype.toArray, 'length'); +assertEq(propDesc.value, 0); +assertEq(propDesc.writable, false); +assertEq(propDesc.enumerable, false); +assertEq(propDesc.configurable, true); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/toArray/name.js b/js/src/tests/non262/AsyncIterator/prototype/toArray/name.js new file mode 100644 index 0000000000..63cdf4c201 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/toArray/name.js @@ -0,0 +1,13 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) +/*--- + `name` property of AsyncIterator.prototype.toArray. +---*/ + +const propDesc = Reflect.getOwnPropertyDescriptor(AsyncIterator.prototype.toArray, 'name'); +assertEq(propDesc.value, 'toArray'); +assertEq(propDesc.writable, false); +assertEq(propDesc.enumerable, false); +assertEq(propDesc.configurable, true); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/toArray/next-throws.js b/js/src/tests/non262/AsyncIterator/prototype/toArray/next-throws.js new file mode 100644 index 0000000000..d998f83e83 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/toArray/next-throws.js @@ -0,0 +1,23 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +class TestIterator extends AsyncIterator { + next() { + throw new Error(); + } + + closed = false; + return() { + this.closed = true; + } +} + +const iter = new TestIterator(); + +assertEq(iter.closed, false); +iter.toArray().then(() => assertEq(true, false, 'expected error'), err => { + assertEq(err instanceof Error, true); + assertEq(iter.closed, false); +}); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/toArray/proxy.js b/js/src/tests/non262/AsyncIterator/prototype/toArray/proxy.js new file mode 100644 index 0000000000..b82af6628e --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/toArray/proxy.js @@ -0,0 +1,44 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) +// +// This test checks that %Iterator.prototype%.toArray only gets the `next` method off of the +// iterator once, and never accesses the @@iterator property. +const log = []; +const handlerProxy = new Proxy({}, { + get: (target, key, receiver) => (...args) => { + log.push(`${key}: ${args[1]?.toString()}`); + return Reflect[key](...args); + }, +}); + +class Counter extends AsyncIterator { + value = 0; + next() { + const value = this.value; + if (value < 2) { + this.value = value + 1; + return Promise.resolve({done: false, value}); + } + return Promise.resolve({done: true}); + } +} + +const iter = new Proxy(new Counter(), handlerProxy); +iter.toArray().then(() => { + assertEq( + log.join('\n'), + `get: toArray +get: next +get: value +set: value +getOwnPropertyDescriptor: value +defineProperty: value +get: value +set: value +getOwnPropertyDescriptor: value +defineProperty: value +get: value` + ); +}); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/toArray/this-not-iterator-throws.js b/js/src/tests/non262/AsyncIterator/prototype/toArray/this-not-iterator-throws.js new file mode 100644 index 0000000000..9846c33ee5 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/toArray/this-not-iterator-throws.js @@ -0,0 +1,21 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + + +function check(x) { + AsyncIterator.prototype.toArray.call(x).then( + () => { + throw new Error('check should have been rejected'); + }, + err => { + assertEq(err instanceof TypeError, true); + } + ); +} + +check(); +check(undefined); +check({}); +check({next: 0}); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/toArray/toArray.js b/js/src/tests/non262/AsyncIterator/prototype/toArray/toArray.js new file mode 100644 index 0000000000..0a91e4d95d --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/toArray/toArray.js @@ -0,0 +1,22 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +async function* gen() { + yield 1; + yield 2; + yield 3; +} +assertEq(Array.isArray(gen()), false); + +gen().toArray().then(array => { + assertEq(Array.isArray(array), true); + assertEq(array.length, 3); + + const expected = [1, 2, 3]; + for (const item of array) { + const expect = expected.shift(); + assertEq(item, expect); + } +}); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/AsyncIterator/prototype/toArray/value-throws-iterator-not-closed.js b/js/src/tests/non262/AsyncIterator/prototype/toArray/value-throws-iterator-not-closed.js new file mode 100644 index 0000000000..a00f780185 --- /dev/null +++ b/js/src/tests/non262/AsyncIterator/prototype/toArray/value-throws-iterator-not-closed.js @@ -0,0 +1,33 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +class TestError extends Error {} +class TestIterator extends AsyncIterator { + next() { + return Promise.resolve({ + done: false, + get value() { + throw new TestError(); + } + }); + } + + closed = false; + return() { + closed = true; + } +} + +const iterator = new TestIterator(); +assertEq(iterator.closed, false, 'iterator starts unclosed'); +iterator.toArray().then( + () => { + throw new Error('toArray should have thrown'); + }, + err => { + assertEq(err instanceof TestError, true); + assertEq(iterator.closed, false, 'iterator remains unclosed'); + } +); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); |