From 2e44b8f13f8ce061fc9ccd6b507659f6602b65b8 Mon Sep 17 00:00:00 2001 From: "Ioanna M. Dimitriou H" Date: Fri, 12 Jan 2024 16:44:40 +0100 Subject: [PATCH] Test that the sync iterator called via for-await-of closes when a rejected promise is returned normally by the iterator. These tests correspond to this normative changes example: https://docs.google.com/presentation/d/1W9EJoWOvi6jU1A2bwyixzOceoP_ASAeaOYER3Zy9P00/edit#slide=id.g10c9207737d_0_31 --- ...ator-next-result-rejected-promise-close.js | 65 +++++++++++++++++ .../next-result-rejected-promise-close.js | 70 +++++++++++++++++++ 2 files changed, 135 insertions(+) create mode 100644 test/built-ins/AsyncFromSyncIteratorPrototype/next/iterator-next-result-rejected-promise-close.js create mode 100644 test/built-ins/AsyncFromSyncIteratorPrototype/next/next-result-rejected-promise-close.js diff --git a/test/built-ins/AsyncFromSyncIteratorPrototype/next/iterator-next-result-rejected-promise-close.js b/test/built-ins/AsyncFromSyncIteratorPrototype/next/iterator-next-result-rejected-promise-close.js new file mode 100644 index 0000000000..2f736d00d9 --- /dev/null +++ b/test/built-ins/AsyncFromSyncIteratorPrototype/next/iterator-next-result-rejected-promise-close.js @@ -0,0 +1,65 @@ +// Copyright (C) 2024 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-%asyncfromsynciteratorprototype%.next +description: next() will reject promise if resolving result promise abrupt completes. +info: | + %AsyncFromSyncIteratorPrototype%.next ( value ) + ... + 3. Let promiseCapability be ! NewPromiseCapability(%Promise%). + 4. Let syncIteratorRecord be O.[[SyncIteratorRecord]]. + 5. If value is present, then + ... + 6. Else, + a. Let result be IteratorNext(syncIteratorRecord). + 7. IfAbruptRejectPromise(result, promiseCapability). + 8. Return ! AsyncFromSyncIteratorContinuation(result, promiseCapability, syncIteratorRecord, true). + + AsyncFromSyncIteratorContinuation ( result, promiseCapability, syncIteratorRecord, closeOnRejection ) + 1. Let done be IteratorComplete(result). + 2. IfAbruptRejectPromise(done, promiseCapability). + 3. Let value be IteratorValue(result). + 4. IfAbruptRejectPromise(value, promiseCapability). + 5. Let valueWrapper be PromiseResolve(%Promise%, value). + 6. If valueWrapper is an abrupt completion, done is false, and closeOnRejection is true, then + a. Set valueWrapper to IteratorClose(syncIteratorRecord, valueWrapper). + ... + 12. If done is true, or if closeOnRejection is false, then + ... + 13. Else, + a. Let closeIterator be a new Abstract Closure with parameters (error) that captures syncIteratorRecord and performs the following steps when called: + i. Return ? IteratorClose(syncIteratorRecord, ThrowCompletion(error)). + b. Let onRejected be CreateBuiltinFunction(closeIterator, 1, "", « »). + c. NOTE: onRejected is used to close the Iterator when the "value" property of an IteratorResult object it yields is a rejected promise. + 14. Perform PerformPromiseThen(valueWrapper, onFulfilled, onRejected, promiseCapability). + 15. Return promiseCapability.[[Promise]]. + + +flags: [async] +features: [async-iteration] +includes: [asyncHelpers.js] +---*/ + +var finallyCount = 0; +var caught = false; + +function* iterator() { + try { + yield Promise.reject("reject"); + } finally { + finallyCount += 1; + } +} + +asyncTest(async () => { + try { + for await (const x of iterator()); + } catch (e) { + caught = true; + assert.sameValue(e, "reject"); + } + + assert.sameValue(finallyCount, 1); + assert(caught); +}); diff --git a/test/built-ins/AsyncFromSyncIteratorPrototype/next/next-result-rejected-promise-close.js b/test/built-ins/AsyncFromSyncIteratorPrototype/next/next-result-rejected-promise-close.js new file mode 100644 index 0000000000..183359112f --- /dev/null +++ b/test/built-ins/AsyncFromSyncIteratorPrototype/next/next-result-rejected-promise-close.js @@ -0,0 +1,70 @@ +// Copyright (C) 2024 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-%asyncfromsynciteratorprototype%.next +description: next() will reject promise if resolving result promise abrupt completes. +info: | + %AsyncFromSyncIteratorPrototype%.next ( value ) + ... + 3. Let promiseCapability be ! NewPromiseCapability(%Promise%). + 4. Let syncIteratorRecord be O.[[SyncIteratorRecord]]. + 5. If value is present, then + ... + 6. Else, + a. Let result be IteratorNext(syncIteratorRecord). + 7. IfAbruptRejectPromise(result, promiseCapability). + 8. Return ! AsyncFromSyncIteratorContinuation(result, promiseCapability, syncIteratorRecord, true). + + AsyncFromSyncIteratorContinuation ( result, promiseCapability, syncIteratorRecord, closeOnRejection ) + 1. Let done be IteratorComplete(result). + 2. IfAbruptRejectPromise(done, promiseCapability). + 3. Let value be IteratorValue(result). + 4. IfAbruptRejectPromise(value, promiseCapability). + 5. Let valueWrapper be PromiseResolve(%Promise%, value). + 6. If valueWrapper is an abrupt completion, done is false, and closeOnRejection is true, then + a. Set valueWrapper to IteratorClose(syncIteratorRecord, valueWrapper). + ... + 12. If done is true, or if closeOnRejection is false, then + ... + 13. Else, + a. Let closeIterator be a new Abstract Closure with parameters (error) that captures syncIteratorRecord and performs the following steps when called: + i. Return ? IteratorClose(syncIteratorRecord, ThrowCompletion(error)). + b. Let onRejected be CreateBuiltinFunction(closeIterator, 1, "", « »). + c. NOTE: onRejected is used to close the Iterator when the "value" property of an IteratorResult object it yields is a rejected promise. + 14. Perform PerformPromiseThen(valueWrapper, onFulfilled, onRejected, promiseCapability). + 15. Return promiseCapability.[[Promise]]. + + +flags: [async] +features: [async-iteration] +includes: [asyncHelpers.js] +---*/ + +var returnCount = 0; +var caught = false; + +const syncIterator = { + [Symbol.iterator]() { + return { + next() { + return { value: Promise.reject("reject"), done: false }; + }, + return() { + returnCount += 1; + } + }; + } +} + +asyncTest(async () => { + try { + for await (let _ of syncIterator); + } catch (e) { + caught = true; + assert.sameValue(e, "reject"); + } + + assert.sameValue(returnCount, 1); + assert(caught); +});