diff --git a/packages/babel-helpers/src/helpers-generated.ts b/packages/babel-helpers/src/helpers-generated.ts index a00184c18a93..9e9ae6c7eb92 100644 --- a/packages/babel-helpers/src/helpers-generated.ts +++ b/packages/babel-helpers/src/helpers-generated.ts @@ -188,10 +188,10 @@ export default Object.freeze({ "7.22.0", 'export default function _using(o,n,e){if(null==n)return n;if(Object(n)!==n)throw new TypeError("using declarations can only be used with objects, functions, null, or undefined.");if(e)var r=n[Symbol.asyncDispose||Symbol.for("Symbol.asyncDispose")];if(null==r&&(r=n[Symbol.dispose||Symbol.for("Symbol.dispose")]),"function"!=typeof r)throw new TypeError("Property [Symbol.dispose] is not a function.");return o.push({v:n,d:r,a:e}),n}', ), - // size: 922, gzip size: 481 + // size: 922, gzip size: 496 usingCtx: helper( "7.23.9", - 'export default function _usingCtx(){var r="function"==typeof SuppressedError?SuppressedError:function(r,n){var e=Error();return e.name="SuppressedError",e.error=r,e.suppressed=n,e},n={},e=[];function using(r,n){if(null!=n){if(Object(n)!==n)throw new TypeError("using declarations can only be used with objects, functions, null, or undefined.");if(r)var o=n[Symbol.asyncDispose||Symbol.for("Symbol.asyncDispose")];if(null==o&&(o=n[Symbol.dispose||Symbol.for("Symbol.dispose")]),"function"!=typeof o)throw new TypeError("Property [Symbol.dispose] is not a function.");e.push({v:n,d:o,a:r})}else r&&e.push({d:n,a:r});return n}return{e:n,u:using.bind(null,!1),a:using.bind(null,!0),d:function(){var o=this.e;function next(){for(;r=e.pop();)try{var r,t=r.d&&r.d.call(r.v);if(r.a)return Promise.resolve(t).then(next,err)}catch(r){return err(r)}if(o!==n)throw o}function err(e){return o=o!==n?new r(e,o):e,next()}return next()}}}', + 'export default function _usingCtx(){var r="function"==typeof SuppressedError?SuppressedError:function(r,e){var n=Error();return n.name="SuppressedError",n.error=r,n.suppressed=e,n},e={},n=[];function using(r,e){if(null!=e){if(Object(e)!==e)throw new TypeError("using declarations can only be used with objects, functions, null, or undefined.");if(r)var o=e[Symbol.asyncDispose||Symbol.for("Symbol.asyncDispose")],t=3;if(void 0===o&&(o=e[Symbol.dispose||Symbol.for("Symbol.dispose")],t&=2),"function"!=typeof o)throw new TypeError("Object is not disposable.");n.push({v:e,d:o,k:t})}else r&&n.push({d:e,k:2});return e}return{e:e,u:using.bind(null,!1),a:using.bind(null,!0),d:function(){var o=this.e;function next(){for(;r=n.pop();)try{var r,t=r.d&&r.d.call(r.v);if(r.k)return Promise.resolve(1&r.k&&t).then(next,err)}catch(r){return err(r)}if(o!==e)throw o}function err(n){return o=o!==e?new r(n,o):n,next()}return next()}}}', ), // size: 1252, gzip size: 572 wrapRegExp: helper( diff --git a/packages/babel-helpers/src/helpers/usingCtx.ts b/packages/babel-helpers/src/helpers/usingCtx.ts index 9ca800ae9787..068851943f85 100644 --- a/packages/babel-helpers/src/helpers/usingCtx.ts +++ b/packages/babel-helpers/src/helpers/usingCtx.ts @@ -1,9 +1,24 @@ /* @minVersion 7.23.9 */ +const enum DisposeFlag { + // set when the dispose function is a [Symbol.asyncDispose] method + ASYNC_DISPOSE = 1, + // alias of AWAIT_USING: set when the resource is an `await using` disposable + ASYNC_DISPOSE_MASK = 2, +} + +const enum DisposeKind { + USING_DISPOSE = 0, + // AWAIT_FLAG + AWAIT_USING_DISPOSE = 2, + // Flag.AWAIT_USING | Flag.ASYNC_DISPOSE + AWAIT_USING_ASYNC_DISPOSE = 3, +} + type Stack = { v?: any; d: null | undefined | (() => any); - a: boolean; + k: DisposeKind; }; export default function _usingCtx() { @@ -30,18 +45,20 @@ export default function _usingCtx() { // core-js-pure uses Symbol.for for polyfilling well-known symbols if (isAwait) { var dispose = - value[Symbol.asyncDispose || Symbol.for("Symbol.asyncDispose")]; + value[Symbol.asyncDispose || Symbol.for("Symbol.asyncDispose")], + kind = DisposeKind.AWAIT_USING_ASYNC_DISPOSE; } - if (dispose == null) { + if (dispose === undefined) { dispose = value[Symbol.dispose || Symbol.for("Symbol.dispose")]; + kind &= DisposeFlag.ASYNC_DISPOSE_MASK; } if (typeof dispose !== "function") { - throw new TypeError(`Property [Symbol.dispose] is not a function.`); + throw new TypeError("Object is not disposable."); } - stack.push({ v: value, d: dispose, a: isAwait }); + stack.push({ v: value, d: dispose, k: kind }); } else if (isAwait) { // provide the nullish `value` as `d` for minification gain - stack.push({ d: value, a: isAwait }); + stack.push({ d: value, k: DisposeKind.AWAIT_USING_DISPOSE }); } return value; } @@ -62,8 +79,11 @@ export default function _usingCtx() { try { var resource, disposalResult = resource.d && resource.d.call(resource.v); - if (resource.a) { - return Promise.resolve(disposalResult).then(next, err); + if (resource.k) { + return Promise.resolve( + // do not await the promise returned from the sync @@dispose + resource.k & DisposeFlag.ASYNC_DISPOSE && disposalResult, + ).then(next, err); } } catch (e) { return err(e); diff --git a/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/exec-async/invalid-not-a-function.js b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/exec-async/invalid-not-a-function.js index 0877dd37be3f..bff4ca8c2968 100644 --- a/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/exec-async/invalid-not-a-function.js +++ b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/exec-async/invalid-not-a-function.js @@ -1,3 +1,24 @@ -return expect(async function () { - await using foo = { [Symbol.asyncDispose || Symbol.for("Symbol.asyncDispose")]: 3 }; -}()).rejects.toThrow(TypeError); +expect( + (async function () { + await using foo = { + [Symbol.asyncDispose || Symbol.for("Symbol.asyncDispose")]: 3, + }; + })() +).rejects.toThrow(TypeError); + +expect( + (async function () { + await using foo = { + [Symbol.asyncDispose || Symbol.for("Symbol.asyncDispose")]: 3, + }; + })() +).rejects.toThrow("Object is not disposable."); + +expect( + (async function () { + await using foo = { + [Symbol.asyncDispose || Symbol.for("Symbol.asyncDispose")]: null, + [Symbol.dispose || Symbol.for("Symbol.dispose")]() {}, + }; + })() +).rejects.toThrow("Object is not disposable."); diff --git a/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/exec-async/sync-fallback-promise.js b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/exec-async/sync-fallback-promise.js new file mode 100644 index 000000000000..fac7a9dcaf6e --- /dev/null +++ b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/exec-async/sync-fallback-promise.js @@ -0,0 +1,35 @@ +return (async () => { + const log = []; + + const promiseDispose = new Promise((resolve, _) => { + setTimeout(() => { + log.push("y dispose promise body"); + resolve(); + }, 0); + }); + + { + await using x = { + [Symbol.asyncDispose || Symbol.for("Symbol.asyncDispose")]() { + log.push("x asyncDispose body"); + }, + }; + await using y = { + [Symbol.dispose || Symbol.for("Symbol.dispose")]() { + log.push("y dispose body"); + return promiseDispose; + }, + }; + } + + log.push("body"); + + await promiseDispose; + + expect(log).toEqual([ + "y dispose body", + "x asyncDispose body", + "body", + "y dispose promise body", + ]); +})(); diff --git a/packages/babel-runtime-corejs3/helpers/esm/usingCtx.js b/packages/babel-runtime-corejs3/helpers/esm/usingCtx.js index 331cfe7cd7e9..af1a0b3163df 100644 --- a/packages/babel-runtime-corejs3/helpers/esm/usingCtx.js +++ b/packages/babel-runtime-corejs3/helpers/esm/usingCtx.js @@ -6,46 +6,47 @@ import _pushInstanceProperty from "core-js-pure/features/instance/push.js"; import _bindInstanceProperty from "core-js-pure/features/instance/bind.js"; import _Promise from "core-js-pure/features/promise/index.js"; export default function _usingCtx() { - var r = "function" == typeof _SuppressedError ? _SuppressedError : function (r, n) { - var e = Error(); - return e.name = "SuppressedError", e.error = r, e.suppressed = n, e; + var r = "function" == typeof _SuppressedError ? _SuppressedError : function (r, e) { + var n = Error(); + return n.name = "SuppressedError", n.error = r, n.suppressed = e, n; }, - n = {}, - e = []; - function using(r, n) { - if (null != n) { - if (Object(n) !== n) throw new TypeError("using declarations can only be used with objects, functions, null, or undefined."); - if (r) var o = n[_Symbol$asyncDispose || _Symbol$for("Symbol.asyncDispose")]; - if (null == o && (o = n[_Symbol$dispose || _Symbol$for("Symbol.dispose")]), "function" != typeof o) throw new TypeError("Property [Symbol.dispose] is not a function."); - _pushInstanceProperty(e).call(e, { - v: n, + e = {}, + n = []; + function using(r, e) { + if (null != e) { + if (Object(e) !== e) throw new TypeError("using declarations can only be used with objects, functions, null, or undefined."); + if (r) var o = e[_Symbol$asyncDispose || _Symbol$for("Symbol.asyncDispose")], + t = 3; + if (void 0 === o && (o = e[_Symbol$dispose || _Symbol$for("Symbol.dispose")], t &= 2), "function" != typeof o) throw new TypeError("Object is not disposable."); + _pushInstanceProperty(n).call(n, { + v: e, d: o, - a: r + k: t }); - } else r && _pushInstanceProperty(e).call(e, { - d: n, - a: r + } else r && _pushInstanceProperty(n).call(n, { + d: e, + k: 2 }); - return n; + return e; } return { - e: n, + e: e, u: _bindInstanceProperty(using).call(using, null, !1), a: _bindInstanceProperty(using).call(using, null, !0), d: function d() { var o = this.e; function next() { - for (; r = e.pop();) try { + for (; r = n.pop();) try { var r, t = r.d && r.d.call(r.v); - if (r.a) return _Promise.resolve(t).then(next, err); + if (r.k) return _Promise.resolve(1 & r.k && t).then(next, err); } catch (r) { return err(r); } - if (o !== n) throw o; + if (o !== e) throw o; } - function err(e) { - return o = o !== n ? new r(e, o) : e, next(); + function err(n) { + return o = o !== e ? new r(n, o) : n, next(); } return next(); }