diff --git a/aos/AddDisposableResource.js b/aos/AddDisposableResource.js index 33919f8..541f326 100644 --- a/aos/AddDisposableResource.js +++ b/aos/AddDisposableResource.js @@ -29,7 +29,7 @@ module.exports = function AddDisposableResource(disposeCapability, V, hint) { var resource; if (arguments.length < 4) { // step 1 - if (V == null && hint === 'SYNC_DISPOSE') { + if (V == null && hint === 'SYNC-DISPOSE') { return 'UNUSED'; // step 1.a } resource = CreateDisposableResource(V, hint); // step 1.c diff --git a/aos/DisposeResources.js b/aos/DisposeResources.js index 1a1cbf8..9a342cb 100644 --- a/aos/DisposeResources.js +++ b/aos/DisposeResources.js @@ -11,7 +11,6 @@ var $then = callBound('Promise.prototype.then', true); var CompletionRecord = require('es-abstract/2025/CompletionRecord'); var Dispose = require('./Dispose'); -var NormalCompletion = require('es-abstract/2025/NormalCompletion'); var PromiseResolve = require('es-abstract/2025/PromiseResolve'); var ThrowCompletion = require('es-abstract/2025/ThrowCompletion'); @@ -56,9 +55,8 @@ module.exports = function DisposeResources(disposeCapability, completion) { }; var getPromise = actualHint === 'ASYNC-DISPOSE' && function getPromise(resource) { - return $then( - promise, - function () { + var runDispose = function () { + try { var result = Dispose( // step 2.a resource['[[ResourceValue]]'], resource['[[Hint]]'], @@ -67,9 +65,19 @@ module.exports = function DisposeResources(disposeCapability, completion) { if (!result) { throw new $SyntaxError('Assertion failed: non-`~ASYNC-DISPOSE~` resource returned a promise from Dispose'); } - return $then(result, NormalCompletion); - }, - rejecter + return $then(result, void undefined, rejecter); + } catch (e) { + rejecter(e); + } + return void undefined; + }; + return $then( + promise, + runDispose, + function (e) { + rejecter(e); + return runDispose(); + } ); }; @@ -96,5 +104,10 @@ module.exports = function DisposeResources(disposeCapability, completion) { // eslint-disable-next-line no-param-reassign disposeCapability['[[DisposableResourceStack]]'] = null; // step 3 - return actualHint === 'ASYNC-DISPOSE' ? promise : completion; // step 4 + if (actualHint === 'ASYNC-DISPOSE') { // step 4 + return $then(promise, function () { + return completion; + }); + } + return completion; }; diff --git a/test/tests.js b/test/tests.js index a4dce47..e823268 100644 --- a/test/tests.js +++ b/test/tests.js @@ -13,6 +13,7 @@ var semver = require('semver'); var gOPD = require('gopd'); var defineAccessorProperty = require('define-accessor-property'); var SuppressedError = require('suppressed-error/polyfill')(); +var SLOT = require('internal-slot'); var brokenNodePolyfill = semver.satisfies(process.version, '^18.18 || >= 20.4'); @@ -99,6 +100,12 @@ module.exports = { instance.use(); instance.use(disposable); + // AddDisposableResource step 1.a: `use(null)` and `use(undefined)` on a sync stack must return without pushing a resource. + if (SLOT.has(instance, '[[DisposeCapability]]')) { + var cap = SLOT.get(instance, '[[DisposeCapability]]'); + st.equal(cap['[[DisposableResourceStack]]'].length, 2, '`use(null)` and `use(undefined)` do not add a resource'); + } + forEach(v.nonNullPrimitives, function (nonNullishObject) { st['throws']( function () { instance.use(nonNullishObject); }, @@ -614,7 +621,35 @@ module.exports = { function (e) { st.equal(e, throwSentinel, 'throws `throwSentinel`'); } - ); + ).then(function () { + // https://github.com/es-shims/DisposableStack/issues/9 + var instance3 = new AsyncDisposableStack(); + var ran = 0; + instance3.defer(function () { ran += 1; }); + instance3.defer(throwsSentinel); + + return instance3.disposeAsync().then( + function () { + st.fail('dispose with a throwing later disposable failed to throw'); + }, + function (e) { + st.equal(e, throwSentinel, 'throws `throwSentinel` when an earlier-pushed defer throws'); + st.equal(ran, 1, 'later-pushed non-throwing defer still ran'); + } + ); + }).then(function () { + var instance4 = new AsyncDisposableStack(); + instance4.defer(function () { return Promise.reject(throwSentinel); }); + + return instance4.disposeAsync().then( + function () { + st.fail('dispose with a rejecting disposable failed to throw'); + }, + function (e) { + st.equal(e, throwSentinel, 'rejecting defer surfaces `throwSentinel`'); + } + ); + }); }); });