From 509363bcfd24b3476dc106eabc0ac856ed5eb51d Mon Sep 17 00:00:00 2001 From: Chengzhong Wu Date: Fri, 13 May 2022 02:28:33 +0800 Subject: [PATCH] Add test on handling broken promises in AsyncGenerator.prototype.return (#3472) * Add test on handling broken promises in AsyncGenerator.prototype.return * fixup! * fixup! --- .../return-state-completed-broken-promise.js | 62 ++++++++++++++++++ .../return-suspendedStart-broken-promise.js | 54 ++++++++++++++++ ...suspendedYield-broken-promise-try-catch.js | 64 +++++++++++++++++++ 3 files changed, 180 insertions(+) create mode 100644 test/built-ins/AsyncGeneratorPrototype/return/return-state-completed-broken-promise.js create mode 100644 test/built-ins/AsyncGeneratorPrototype/return/return-suspendedStart-broken-promise.js create mode 100644 test/built-ins/AsyncGeneratorPrototype/return/return-suspendedYield-broken-promise-try-catch.js diff --git a/test/built-ins/AsyncGeneratorPrototype/return/return-state-completed-broken-promise.js b/test/built-ins/AsyncGeneratorPrototype/return/return-state-completed-broken-promise.js new file mode 100644 index 0000000000..ca3f605185 --- /dev/null +++ b/test/built-ins/AsyncGeneratorPrototype/return/return-state-completed-broken-promise.js @@ -0,0 +1,62 @@ +// Copyright (C) 2022 Chengzhong Wu. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asyncgenerator-prototype-return +description: > + A broken promise should reject the returned promise of + AsyncGenerator.prototype.return when the generator is completed. +info: | + AsyncGenerator.prototype.return ( value ) + ... + 8. If state is either suspendedStart or completed, then + a. Set generator.[[AsyncGeneratorState]] to awaiting-return. + b. Perform ! AsyncGeneratorAwaitReturn(generator). + ... + + AsyncGeneratorAwaitReturn ( generator ) + ... + 6. Let promise be Completion(PromiseResolve(%Promise%, completion.[[Value]])). + 7. If promiseCompletion is an abrupt completion, then + a. Set generator.[[AsyncGeneratorState]] to completed. + b. Perform AsyncGeneratorCompleteStep(generator, promiseCompletion, true). + c. Perform AsyncGeneratorDrainQueue(generator). + d. Return unused. + 8. Assert: promiseCompletion.[[Type]] is normal. + 9. Let promise be promiseCompletion.[[Value]]. + ... + +flags: [async] +features: [async-iteration] +---*/ + +let unblock; +let blocking = new Promise(resolve => { unblock = resolve; }); +let unblocked = false; +var g = async function*() { + await blocking; + unblocked = true; +}; + +var it = g(); +let brokenPromise = Promise.resolve(42); +Object.defineProperty(brokenPromise, 'constructor', { + get: function () { + throw new Error('broken promise'); + } +}); + +it.next(); +it.return(brokenPromise) + .then( + () => { + throw new Test262Error("Expected rejection"); + }, + err => { + assert(unblocked, false, 'return should be rejected before generator is resumed'); + assert.sameValue(err.message, 'broken promise'); + } + ) + .then($DONE, $DONE); + +unblock(); diff --git a/test/built-ins/AsyncGeneratorPrototype/return/return-suspendedStart-broken-promise.js b/test/built-ins/AsyncGeneratorPrototype/return/return-suspendedStart-broken-promise.js new file mode 100644 index 0000000000..993ca8146f --- /dev/null +++ b/test/built-ins/AsyncGeneratorPrototype/return/return-suspendedStart-broken-promise.js @@ -0,0 +1,54 @@ +// Copyright (C) 2022 Chengzhong Wu. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asyncgenerator-prototype-return +description: > + A broken promise should reject the returned promise of + AsyncGenerator.prototype.return when the generator's state is suspendedStart. +info: | + AsyncGenerator.prototype.return ( value ) + ... + 8. If state is either suspendedStart or completed, then + a. Set generator.[[AsyncGeneratorState]] to awaiting-return. + b. Perform AsyncGeneratorAwaitReturn(_generator_). + ... + + AsyncGeneratorAwaitReturn ( generator ) + ... + 6. Let promise be Completion(PromiseResolve(%Promise%, completion.[[Value]])). + 7. If promiseCompletion is an abrupt completion, then + a. Set generator.[[AsyncGeneratorState]] to completed. + b. Perform AsyncGeneratorCompleteStep(generator, promiseCompletion, true). + c. Perform AsyncGeneratorDrainQueue(generator). + d. Return unused. + 8. Assert: promiseCompletion.[[Type]] is normal. + 9. Let promise be promiseCompletion.[[Value]]. + ... + +flags: [async] +features: [async-iteration] +---*/ + +var g = async function*() { + throw new Test262Error('Generator must not be resumed.'); +}; + +var it = g(); +let brokenPromise = Promise.resolve(42); +Object.defineProperty(brokenPromise, 'constructor', { + get: function () { + throw new Error('broken promise'); + } +}); + +it.return(brokenPromise) + .then( + () => { + throw new Test262Error("Expected rejection"); + }, + err => { + assert.sameValue(err.message, 'broken promise'); + } + ) + .then($DONE, $DONE); diff --git a/test/built-ins/AsyncGeneratorPrototype/return/return-suspendedYield-broken-promise-try-catch.js b/test/built-ins/AsyncGeneratorPrototype/return/return-suspendedYield-broken-promise-try-catch.js new file mode 100644 index 0000000000..7fc3312392 --- /dev/null +++ b/test/built-ins/AsyncGeneratorPrototype/return/return-suspendedYield-broken-promise-try-catch.js @@ -0,0 +1,64 @@ +// Copyright (C) 2022 Chengzhong Wu. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asyncgenerator-prototype-return +description: > + A broken promise should resume the generator and reject with + the exception when the generator's state is suspendedYield. +info: | + AsyncGenerator.prototype.return ( value ) + ... + 9. Else if state is suspendedYield, then + a. Perform AsyncGeneratorResume(generator, completion). + ... + + AsyncGeneratorCompleteStep ( generator, completion, done [ , realm ] ) + Resumes the steps defined at AsyncGeneratorStart ( generator, generatorBody ) + ... + 4. Set the code evaluation state of genContext such that when evaluation is resumed for that execution context the following steps will be performed: + ... + i. Perform AsyncGeneratorDrainQueue(generator). + j. Return undefined. + + AsyncGeneratorDrainQueue ( generator ) + ... + 5. Repeat, while done is false, + a. Let next be the first element of queue. + b. Let completion be Completion(next.[[Completion]]). + c. If completion.[[Type]] is return, then + i. Set generator.[[AsyncGeneratorState]] to awaiting-return. + ii. Perform AsyncGeneratorAwaitReturn(generator). + iii. Set done to true. + ... + +flags: [async] +features: [async-iteration] +---*/ + +let caughtErr; +var g = async function*() { + try { + yield; + return 'this is never returned'; + } catch (err) { + caughtErr = err; + return 1; + } +}; + +let brokenPromise = Promise.resolve(42); +Object.defineProperty(brokenPromise, 'constructor', { + get: function () { + throw new Error('broken promise'); + } +}); + +var it = g(); +it.next().then(() => { + return it.return(brokenPromise); +}).then(ret => { + assert.sameValue(caughtErr.message, 'broken promise'); + assert.sameValue(ret.value, 1, 'returned value'); + assert.sameValue(ret.done, true, 'iterator is closed'); +}).then($DONE, $DONE);