From 75fc5e1ba68539e22ee34d8c6404051bdcf8c912 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Ribaudo?= Date: Wed, 17 May 2023 16:36:54 +0300 Subject: [PATCH 01/22] Implement transform support for `using` declarations --- .../babel-helpers/src/helpers-generated.ts | 8 ++ packages/babel-helpers/src/helpers/dispose.js | 31 +++++++ packages/babel-helpers/src/helpers/using.js | 13 +++ .../.npmignore | 3 + .../README.md | 19 +++++ .../package.json | 47 ++++++++++ .../src/index.ts | 85 +++++++++++++++++++ .../test/fixtures/options.json | 3 + .../transform-sync/bare-block/input.js | 4 + .../transform-sync/bare-block/output.js | 10 +++ .../transform-sync/for-await-head/input.js | 3 + .../for-await-head/options.json | 3 + .../transform-sync/for-await-head/output.mjs | 10 +++ .../fixtures/transform-sync/for-head/input.js | 3 + .../transform-sync/for-head/output.js | 10 +++ .../transform-sync/function-body/input.js | 4 + .../transform-sync/function-body/output.js | 10 +++ .../fixtures/transform-sync/if-body/input.js | 4 + .../fixtures/transform-sync/if-body/output.js | 12 +++ .../transform-sync/multiple-nested/input.js | 11 +++ .../transform-sync/multiple-nested/output.js | 31 +++++++ .../multiple-same-level/input.js | 9 ++ .../multiple-same-level/output.js | 16 ++++ .../test/index.js | 3 + .../test/package.json | 1 + packages/babel-runtime-corejs2/package.json | 18 ++++ packages/babel-runtime-corejs3/package.json | 18 ++++ packages/babel-runtime/package.json | 18 ++++ tsconfig.json | 4 + yarn.lock | 13 +++ 30 files changed, 424 insertions(+) create mode 100644 packages/babel-helpers/src/helpers/dispose.js create mode 100644 packages/babel-helpers/src/helpers/using.js create mode 100644 packages/babel-plugin-proposal-explicit-resource-management/.npmignore create mode 100644 packages/babel-plugin-proposal-explicit-resource-management/README.md create mode 100644 packages/babel-plugin-proposal-explicit-resource-management/package.json create mode 100644 packages/babel-plugin-proposal-explicit-resource-management/src/index.ts create mode 100644 packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/options.json create mode 100644 packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-sync/bare-block/input.js create mode 100644 packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-sync/bare-block/output.js create mode 100644 packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-sync/for-await-head/input.js create mode 100644 packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-sync/for-await-head/options.json create mode 100644 packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-sync/for-await-head/output.mjs create mode 100644 packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-sync/for-head/input.js create mode 100644 packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-sync/for-head/output.js create mode 100644 packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-sync/function-body/input.js create mode 100644 packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-sync/function-body/output.js create mode 100644 packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-sync/if-body/input.js create mode 100644 packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-sync/if-body/output.js create mode 100644 packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-sync/multiple-nested/input.js create mode 100644 packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-sync/multiple-nested/output.js create mode 100644 packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-sync/multiple-same-level/input.js create mode 100644 packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-sync/multiple-same-level/output.js create mode 100644 packages/babel-plugin-proposal-explicit-resource-management/test/index.js create mode 100644 packages/babel-plugin-proposal-explicit-resource-management/test/package.json diff --git a/packages/babel-helpers/src/helpers-generated.ts b/packages/babel-helpers/src/helpers-generated.ts index 85b5c056487d..3b875e2aad5b 100644 --- a/packages/babel-helpers/src/helpers-generated.ts +++ b/packages/babel-helpers/src/helpers-generated.ts @@ -61,6 +61,10 @@ export default Object.freeze({ "7.20.7", "export default function _defineAccessor(type,obj,key,fn){var desc={configurable:!0,enumerable:!0};return desc[type]=fn,Object.defineProperty(obj,key,desc)}", ), + dispose: helper( + "7.0.0-beta.0", + "function dispose_SuppressedError(suppressed,error){return dispose_SuppressedError=function(suppressed,error){this.suppressed=suppressed,this.error=error,this.stack=(new Error).stack},dispose_SuppressedError.prototype=Object.create(Error.prototype,{constructor:{value:dispose_SuppressedError,writable:!0,configurable:!0}}),new dispose_SuppressedError(suppressed,error)}export default function _dispose(stack,error,hasError,SuppressedError){for(;stack.length>0;){const r=stack.pop();try{r.d.call(r.v)}catch(e){error=hasError?new(SuppressedError||dispose_SuppressedError)(e,error):e,hasError=!0}}if(hasError)throw error}", + ), iterableToArrayLimit: helper( "7.0.0-beta.0", 'export default function _iterableToArrayLimit(arr,i){var _i=null==arr?null:"undefined"!=typeof Symbol&&arr[Symbol.iterator]||arr["@@iterator"];if(null!=_i){var _s,_e,_x,_r,_arr=[],_n=!0,_d=!1;try{if(_x=(_i=_i.call(arr)).next,0===i){if(Object(_i)!==_i)return;_n=!1}else for(;!(_n=(_s=_x.call(_i)).done)&&(_arr.push(_s.value),_arr.length!==i);_n=!0);}catch(err){_d=!0,_e=err}finally{try{if(!_n&&null!=_i.return&&(_r=_i.return(),Object(_r)!==_r))return}finally{if(_d)throw _e}}return _arr}}', @@ -85,6 +89,10 @@ export default Object.freeze({ "7.0.0-beta.0", 'export default function _typeof(obj){"@babel/helpers - typeof";return _typeof="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(obj){return typeof obj}:function(obj){return obj&&"function"==typeof Symbol&&obj.constructor===Symbol&&obj!==Symbol.prototype?"symbol":typeof obj},_typeof(obj)}', ), + using: helper( + "7.0.0-beta.0", + 'export default function _using(stack,value){if(null!=value){var dispose=value[Symbol.dispose||Symbol.for("Symbol.dispose")];if("function"!=typeof dispose)throw new TypeError("Property [Symbol.dispose] is not a function.");stack.push({v:value,d:dispose})}return value}', + ), wrapRegExp: helper( "7.19.0", 'import setPrototypeOf from"setPrototypeOf";import inherits from"inherits";export default function _wrapRegExp(){_wrapRegExp=function(re,groups){return new BabelRegExp(re,void 0,groups)};var _super=RegExp.prototype,_groups=new WeakMap;function BabelRegExp(re,flags,groups){var _this=new RegExp(re,flags);return _groups.set(_this,groups||_groups.get(re)),setPrototypeOf(_this,BabelRegExp.prototype)}function buildGroups(result,re){var g=_groups.get(re);return Object.keys(g).reduce((function(groups,name){var i=g[name];if("number"==typeof i)groups[name]=result[i];else{for(var k=0;void 0===result[i[k]]&&k+1]+)>/g,(function(_,name){var group=groups[name];return"$"+(Array.isArray(group)?group.join("$"):group)})))}if("function"==typeof substitution){var _this=this;return _super[Symbol.replace].call(this,str,(function(){var args=arguments;return"object"!=typeof args[args.length-1]&&(args=[].slice.call(args)).push(buildGroups(args,_this)),substitution.apply(this,args)}))}return _super[Symbol.replace].call(this,str,substitution)},_wrapRegExp.apply(this,arguments)}', diff --git a/packages/babel-helpers/src/helpers/dispose.js b/packages/babel-helpers/src/helpers/dispose.js new file mode 100644 index 000000000000..bfe9485769ab --- /dev/null +++ b/packages/babel-helpers/src/helpers/dispose.js @@ -0,0 +1,31 @@ +/* @minVersion 7.0.0-beta.0 */ +function dispose_SuppressedError(suppressed, error) { + dispose_SuppressedError = function SuppressedError(suppressed, error) { + this.suppressed = suppressed; + this.error = error; + this.stack = new Error().stack; + }; + dispose_SuppressedError.prototype = Object.create(Error.prototype, { + constructor: { + value: dispose_SuppressedError, + writable: true, + configurable: true, + }, + }); + return new dispose_SuppressedError(suppressed, error); +} + +export default function _dispose(stack, error, hasError, SuppressedError) { + while (stack.length > 0) { + const r = stack.pop(); + try { + r.d.call(r.v); + } catch (e) { + error = hasError + ? new (SuppressedError || dispose_SuppressedError)(e, error) + : e; + hasError = true; + } + } + if (hasError) throw error; +} diff --git a/packages/babel-helpers/src/helpers/using.js b/packages/babel-helpers/src/helpers/using.js new file mode 100644 index 000000000000..7521f9259e3c --- /dev/null +++ b/packages/babel-helpers/src/helpers/using.js @@ -0,0 +1,13 @@ +/* @minVersion 7.0.0-beta.0 */ + +export default function _using(stack, value) { + if (value !== null && value !== void 0) { + // core-js-pure uses Symbol.for("Symbol.dispose"). + var dispose = value[Symbol.dispose || Symbol.for("Symbol.dispose")]; + if (typeof dispose !== "function") { + throw new TypeError(`Property [Symbol.dispose] is not a function.`); + } + stack.push({ v: value, d: dispose }); + } + return value; +} diff --git a/packages/babel-plugin-proposal-explicit-resource-management/.npmignore b/packages/babel-plugin-proposal-explicit-resource-management/.npmignore new file mode 100644 index 000000000000..f9806945836e --- /dev/null +++ b/packages/babel-plugin-proposal-explicit-resource-management/.npmignore @@ -0,0 +1,3 @@ +src +test +*.log diff --git a/packages/babel-plugin-proposal-explicit-resource-management/README.md b/packages/babel-plugin-proposal-explicit-resource-management/README.md new file mode 100644 index 000000000000..62d8356f5369 --- /dev/null +++ b/packages/babel-plugin-proposal-explicit-resource-management/README.md @@ -0,0 +1,19 @@ +# @babel/plugin-proposal-explicit-resource-managements + +> Compile `using` declarations to ES2015 + +See our website [@babel/plugin-proposal-explicit-resource-managements](https://babeljs.io/docs/en/babel-plugin-proposal-explicit-resource-managements) for more information. + +## Install + +Using npm: + +```sh +npm install --save-dev @babel/plugin-proposal-explicit-resource-managements +``` + +or using yarn: + +```sh +yarn add @babel/plugin-proposal-explicit-resource-managements --dev +``` diff --git a/packages/babel-plugin-proposal-explicit-resource-management/package.json b/packages/babel-plugin-proposal-explicit-resource-management/package.json new file mode 100644 index 000000000000..e5a360237bd8 --- /dev/null +++ b/packages/babel-plugin-proposal-explicit-resource-management/package.json @@ -0,0 +1,47 @@ +{ + "name": "@babel/plugin-proposal-explicit-resource-management", + "version": "7.18.6", + "description": "Compile `using` declarations to ES2015", + "repository": { + "type": "git", + "url": "https://github.com/babel/babel.git", + "directory": "packages/babel-plugin-proposal-explicit-resource-management" + }, + "homepage": "https://babel.dev/docs/en/next/babel-plugin-proposal-explicit-resource-management", + "license": "MIT", + "publishConfig": { + "access": "public" + }, + "main": "./lib/index.js", + "keywords": [ + "babel-plugin" + ], + "dependencies": { + "@babel/helper-plugin-utils": "workspace:^", + "@babel/plugin-syntax-explicit-resource-management": "workspace:^" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + }, + "devDependencies": { + "@babel/core": "workspace:^", + "@babel/helper-plugin-test-runner": "workspace:^" + }, + "engines": { + "node": ">=6.9.0" + }, + "author": "The Babel Team (https://babel.dev/team)", + "conditions": { + "USE_ESM": [ + { + "type": "module" + }, + null + ] + }, + "exports": { + ".": "./lib/index.js", + "./package.json": "./package.json" + }, + "type": "commonjs" +} diff --git a/packages/babel-plugin-proposal-explicit-resource-management/src/index.ts b/packages/babel-plugin-proposal-explicit-resource-management/src/index.ts new file mode 100644 index 000000000000..563ae7df21e3 --- /dev/null +++ b/packages/babel-plugin-proposal-explicit-resource-management/src/index.ts @@ -0,0 +1,85 @@ +import { declare } from "@babel/helper-plugin-utils"; +import syntacExplicitResourceManagement from "@babel/plugin-syntax-explicit-resource-management"; +import { types as t, template } from "@babel/core"; +import type { NodePath } from "@babel/traverse"; + +export default declare(api => { + // TOOD: assert version 7.22.0 + api.assertVersion(7); + + return { + name: "proposal-explicit-resource-management", + inherits: syntacExplicitResourceManagement, + + visitor: { + VariableDeclaration(path) { + if (path.node.kind === "using") { + throw path.buildCodeFrameError( + `"using" declaration at the top-level of modules is not supported yet.`, + ); + } + }, + ForOfStatement(path: NodePath) { + const { left } = path.node; + if (!t.isVariableDeclaration(left, { kind: "using" })) return; + + const { id } = left.declarations[0]; + const tmpId = path.scope.generateUidIdentifierBasedOnNode(id); + left.declarations[0].id = tmpId; + left.kind = "const"; + + path.ensureBlock(); + path.node.body.body.unshift( + t.variableDeclaration("using", [ + t.variableDeclarator(id, t.cloneNode(tmpId)), + ]), + ); + }, + BlockStatement(path, state) { + let stackId: t.Identifier | null = null; + for (const node of path.node.body) { + if (!t.isVariableDeclaration(node, { kind: "using" })) continue; + node.kind = "const"; + stackId ??= path.scope.generateUidIdentifier("stack"); + + node.declarations.forEach(decl => { + decl.init = t.callExpression(state.addHelper("using"), [ + t.cloneNode(stackId), + decl.init, + ]); + }); + } + if (!stackId) return; + + const errorId = path.scope.generateUidIdentifier("error"); + const hasErrorId = path.scope.generateUidIdentifier("hasError"); + + const replacement = template.statement.ast` + try { + var ${stackId} = []; + ${path.node.body} + } catch (_) { + var ${errorId} = _; + var ${hasErrorId} = true; + } finally { + ${state.addHelper("dispose")}( + ${t.cloneNode(stackId)}, + ${t.cloneNode(errorId)}, + ${t.cloneNode(hasErrorId)}, + // Pass SuppressedError so that it can be used with "pure" + // polyfills that do not compile the contents of runtime + // helpers. + typeof SuppressedError !== undefined && SuppressedError + ); + } + `; + + if (path.parentPath.isFunction()) { + path.replaceWith(t.blockStatement([replacement])); + } else { + path.replaceWith(replacement); + } + }, + }, + }; +}); diff --git a/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/options.json b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/options.json new file mode 100644 index 000000000000..2bc3ea3845be --- /dev/null +++ b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/options.json @@ -0,0 +1,3 @@ +{ + "plugins": ["proposal-explicit-resource-management"] +} diff --git a/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-sync/bare-block/input.js b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-sync/bare-block/input.js new file mode 100644 index 000000000000..0c1336f3178e --- /dev/null +++ b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-sync/bare-block/input.js @@ -0,0 +1,4 @@ +{ + using x = obj; + doSomethingWith(x); +} diff --git a/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-sync/bare-block/output.js b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-sync/bare-block/output.js new file mode 100644 index 000000000000..6d27feb531db --- /dev/null +++ b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-sync/bare-block/output.js @@ -0,0 +1,10 @@ +try { + var _stack = []; + const x = babelHelpers.using(_stack, obj); + doSomethingWith(x); +} catch (_) { + var _error = _; + var _hasError = true; +} finally { + babelHelpers.dispose(_stack, _error, _hasError, typeof SuppressedError !== undefined && SuppressedError); +} diff --git a/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-sync/for-await-head/input.js b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-sync/for-await-head/input.js new file mode 100644 index 000000000000..865040caae71 --- /dev/null +++ b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-sync/for-await-head/input.js @@ -0,0 +1,3 @@ +for await (using x of y) { + doSomethingWith(x); +} diff --git a/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-sync/for-await-head/options.json b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-sync/for-await-head/options.json new file mode 100644 index 000000000000..2104ca43283f --- /dev/null +++ b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-sync/for-await-head/options.json @@ -0,0 +1,3 @@ +{ + "sourceType": "module" +} diff --git a/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-sync/for-await-head/output.mjs b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-sync/for-await-head/output.mjs new file mode 100644 index 000000000000..df664bab0847 --- /dev/null +++ b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-sync/for-await-head/output.mjs @@ -0,0 +1,10 @@ +for await (const _x of y) try { + var _stack = []; + const x = babelHelpers.using(_stack, _x); + doSomethingWith(x); +} catch (_) { + var _error = _; + var _hasError = true; +} finally { + babelHelpers.dispose(_stack, _error, _hasError, typeof SuppressedError !== undefined && SuppressedError); +} diff --git a/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-sync/for-head/input.js b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-sync/for-head/input.js new file mode 100644 index 000000000000..cfb987659b1f --- /dev/null +++ b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-sync/for-head/input.js @@ -0,0 +1,3 @@ +for (using x of y) { + doSomethingWith(x); +} diff --git a/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-sync/for-head/output.js b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-sync/for-head/output.js new file mode 100644 index 000000000000..b247bddea82b --- /dev/null +++ b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-sync/for-head/output.js @@ -0,0 +1,10 @@ +for (const _x of y) try { + var _stack = []; + const x = babelHelpers.using(_stack, _x); + doSomethingWith(x); +} catch (_) { + var _error = _; + var _hasError = true; +} finally { + babelHelpers.dispose(_stack, _error, _hasError, typeof SuppressedError !== undefined && SuppressedError); +} diff --git a/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-sync/function-body/input.js b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-sync/function-body/input.js new file mode 100644 index 000000000000..326085daa898 --- /dev/null +++ b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-sync/function-body/input.js @@ -0,0 +1,4 @@ +if (test) { + using x = obj; + doSomethingWith(x); +} diff --git a/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-sync/function-body/output.js b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-sync/function-body/output.js new file mode 100644 index 000000000000..c1a28b0248b6 --- /dev/null +++ b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-sync/function-body/output.js @@ -0,0 +1,10 @@ +if (test) try { + var _stack = []; + const x = babelHelpers.using(_stack, obj); + doSomethingWith(x); +} catch (_) { + var _error = _; + var _hasError = true; +} finally { + babelHelpers.dispose(_stack, _error, _hasError, typeof SuppressedError !== undefined && SuppressedError); +} diff --git a/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-sync/if-body/input.js b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-sync/if-body/input.js new file mode 100644 index 000000000000..0ed05eeaeae2 --- /dev/null +++ b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-sync/if-body/input.js @@ -0,0 +1,4 @@ +function fn() { + using x = obj; + return doSomethingWith(x); +} diff --git a/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-sync/if-body/output.js b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-sync/if-body/output.js new file mode 100644 index 000000000000..817b433ec023 --- /dev/null +++ b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-sync/if-body/output.js @@ -0,0 +1,12 @@ +function fn() { + try { + var _stack = []; + const x = babelHelpers.using(_stack, obj); + return doSomethingWith(x); + } catch (_) { + var _error = _; + var _hasError = true; + } finally { + babelHelpers.dispose(_stack, _error, _hasError, typeof SuppressedError !== undefined && SuppressedError); + } +} diff --git a/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-sync/multiple-nested/input.js b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-sync/multiple-nested/input.js new file mode 100644 index 000000000000..69584d3c4f9e --- /dev/null +++ b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-sync/multiple-nested/input.js @@ -0,0 +1,11 @@ +{ + using x = obj; + { + using y = call(() => { + using z = obj; + return z; + }); + stmt; + } + stmt; +} \ No newline at end of file diff --git a/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-sync/multiple-nested/output.js b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-sync/multiple-nested/output.js new file mode 100644 index 000000000000..ec58a08dc6c7 --- /dev/null +++ b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-sync/multiple-nested/output.js @@ -0,0 +1,31 @@ +try { + var _stack = []; + const x = babelHelpers.using(_stack, obj); + try { + var _stack2 = []; + const y = babelHelpers.using(_stack2, call(() => { + try { + var _stack3 = []; + const z = babelHelpers.using(_stack3, obj); + return z; + } catch (_) { + var _error3 = _; + var _hasError3 = true; + } finally { + babelHelpers.dispose(_stack3, _error3, _hasError3, typeof SuppressedError !== undefined && SuppressedError); + } + })); + stmt; + } catch (_) { + var _error2 = _; + var _hasError2 = true; + } finally { + babelHelpers.dispose(_stack2, _error2, _hasError2, typeof SuppressedError !== undefined && SuppressedError); + } + stmt; +} catch (_) { + var _error = _; + var _hasError = true; +} finally { + babelHelpers.dispose(_stack, _error, _hasError, typeof SuppressedError !== undefined && SuppressedError); +} diff --git a/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-sync/multiple-same-level/input.js b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-sync/multiple-same-level/input.js new file mode 100644 index 000000000000..477b10c839e1 --- /dev/null +++ b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-sync/multiple-same-level/input.js @@ -0,0 +1,9 @@ +{ + stmt; + using x = obj; + stmt; + using y = obj, z = obj; + stmt; + using w = obj; + doSomethingWith(x, z); +} diff --git a/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-sync/multiple-same-level/output.js b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-sync/multiple-same-level/output.js new file mode 100644 index 000000000000..22f3ee042b0e --- /dev/null +++ b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-sync/multiple-same-level/output.js @@ -0,0 +1,16 @@ +try { + var _stack = []; + stmt; + const x = babelHelpers.using(_stack, obj); + stmt; + const y = babelHelpers.using(_stack, obj), + z = babelHelpers.using(_stack, obj); + stmt; + const w = babelHelpers.using(_stack, obj); + doSomethingWith(x, z); +} catch (_) { + var _error = _; + var _hasError = true; +} finally { + babelHelpers.dispose(_stack, _error, _hasError, typeof SuppressedError !== undefined && SuppressedError); +} diff --git a/packages/babel-plugin-proposal-explicit-resource-management/test/index.js b/packages/babel-plugin-proposal-explicit-resource-management/test/index.js new file mode 100644 index 000000000000..21a55ce6b5e7 --- /dev/null +++ b/packages/babel-plugin-proposal-explicit-resource-management/test/index.js @@ -0,0 +1,3 @@ +import runner from "@babel/helper-plugin-test-runner"; + +runner(import.meta.url); diff --git a/packages/babel-plugin-proposal-explicit-resource-management/test/package.json b/packages/babel-plugin-proposal-explicit-resource-management/test/package.json new file mode 100644 index 000000000000..5ffd9800b97c --- /dev/null +++ b/packages/babel-plugin-proposal-explicit-resource-management/test/package.json @@ -0,0 +1 @@ +{ "type": "module" } diff --git a/packages/babel-runtime-corejs2/package.json b/packages/babel-runtime-corejs2/package.json index 724ff204671a..573b0a722206 100644 --- a/packages/babel-runtime-corejs2/package.json +++ b/packages/babel-runtime-corejs2/package.json @@ -126,6 +126,15 @@ "./helpers/defineAccessor.js" ], "./helpers/esm/defineAccessor": "./helpers/esm/defineAccessor.js", + "./helpers/dispose": [ + { + "node": "./helpers/dispose.js", + "import": "./helpers/esm/dispose.js", + "default": "./helpers/dispose.js" + }, + "./helpers/dispose.js" + ], + "./helpers/esm/dispose": "./helpers/esm/dispose.js", "./helpers/iterableToArrayLimit": [ { "node": "./helpers/iterableToArrayLimit.js", @@ -180,6 +189,15 @@ "./helpers/typeof.js" ], "./helpers/esm/typeof": "./helpers/esm/typeof.js", + "./helpers/using": [ + { + "node": "./helpers/using.js", + "import": "./helpers/esm/using.js", + "default": "./helpers/using.js" + }, + "./helpers/using.js" + ], + "./helpers/esm/using": "./helpers/esm/using.js", "./helpers/wrapRegExp": [ { "node": "./helpers/wrapRegExp.js", diff --git a/packages/babel-runtime-corejs3/package.json b/packages/babel-runtime-corejs3/package.json index bd750c443f08..39548234e7b3 100644 --- a/packages/babel-runtime-corejs3/package.json +++ b/packages/babel-runtime-corejs3/package.json @@ -125,6 +125,15 @@ "./helpers/defineAccessor.js" ], "./helpers/esm/defineAccessor": "./helpers/esm/defineAccessor.js", + "./helpers/dispose": [ + { + "node": "./helpers/dispose.js", + "import": "./helpers/esm/dispose.js", + "default": "./helpers/dispose.js" + }, + "./helpers/dispose.js" + ], + "./helpers/esm/dispose": "./helpers/esm/dispose.js", "./helpers/iterableToArrayLimit": [ { "node": "./helpers/iterableToArrayLimit.js", @@ -179,6 +188,15 @@ "./helpers/typeof.js" ], "./helpers/esm/typeof": "./helpers/esm/typeof.js", + "./helpers/using": [ + { + "node": "./helpers/using.js", + "import": "./helpers/esm/using.js", + "default": "./helpers/using.js" + }, + "./helpers/using.js" + ], + "./helpers/esm/using": "./helpers/esm/using.js", "./helpers/wrapRegExp": [ { "node": "./helpers/wrapRegExp.js", diff --git a/packages/babel-runtime/package.json b/packages/babel-runtime/package.json index 5d5cad35bee3..fd3cdbac6af4 100644 --- a/packages/babel-runtime/package.json +++ b/packages/babel-runtime/package.json @@ -125,6 +125,15 @@ "./helpers/defineAccessor.js" ], "./helpers/esm/defineAccessor": "./helpers/esm/defineAccessor.js", + "./helpers/dispose": [ + { + "node": "./helpers/dispose.js", + "import": "./helpers/esm/dispose.js", + "default": "./helpers/dispose.js" + }, + "./helpers/dispose.js" + ], + "./helpers/esm/dispose": "./helpers/esm/dispose.js", "./helpers/iterableToArrayLimit": [ { "node": "./helpers/iterableToArrayLimit.js", @@ -179,6 +188,15 @@ "./helpers/typeof.js" ], "./helpers/esm/typeof": "./helpers/esm/typeof.js", + "./helpers/using": [ + { + "node": "./helpers/using.js", + "import": "./helpers/esm/using.js", + "default": "./helpers/using.js" + }, + "./helpers/using.js" + ], + "./helpers/esm/using": "./helpers/esm/using.js", "./helpers/wrapRegExp": [ { "node": "./helpers/wrapRegExp.js", diff --git a/tsconfig.json b/tsconfig.json index fa142c229a25..4d1cd6446472 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -46,6 +46,7 @@ "./packages/babel-plugin-proposal-destructuring-private/src/**/*.ts", "./packages/babel-plugin-proposal-do-expressions/src/**/*.ts", "./packages/babel-plugin-proposal-duplicate-named-capturing-groups-regex/src/**/*.ts", + "./packages/babel-plugin-proposal-explicit-resource-management/src/**/*.ts", "./packages/babel-plugin-proposal-export-default-from/src/**/*.ts", "./packages/babel-plugin-proposal-function-bind/src/**/*.ts", "./packages/babel-plugin-proposal-function-sent/src/**/*.ts", @@ -305,6 +306,9 @@ "@babel/plugin-proposal-duplicate-named-capturing-groups-regex": [ "./packages/babel-plugin-proposal-duplicate-named-capturing-groups-regex/src" ], + "@babel/plugin-proposal-explicit-resource-management": [ + "./packages/babel-plugin-proposal-explicit-resource-management/src" + ], "@babel/plugin-proposal-export-default-from": [ "./packages/babel-plugin-proposal-export-default-from/src" ], diff --git a/yarn.lock b/yarn.lock index f0cedf09218f..dcae0334b2cd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1365,6 +1365,19 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-proposal-explicit-resource-management@workspace:packages/babel-plugin-proposal-explicit-resource-management": + version: 0.0.0-use.local + resolution: "@babel/plugin-proposal-explicit-resource-management@workspace:packages/babel-plugin-proposal-explicit-resource-management" + dependencies: + "@babel/core": "workspace:^" + "@babel/helper-plugin-test-runner": "workspace:^" + "@babel/helper-plugin-utils": "workspace:^" + "@babel/plugin-syntax-explicit-resource-management": "workspace:^" + peerDependencies: + "@babel/core": ^7.0.0-0 + languageName: unknown + linkType: soft + "@babel/plugin-proposal-export-default-from@workspace:^, @babel/plugin-proposal-export-default-from@workspace:packages/babel-plugin-proposal-export-default-from": version: 0.0.0-use.local resolution: "@babel/plugin-proposal-export-default-from@workspace:packages/babel-plugin-proposal-export-default-from" From 412683d8c788b2b069b15e234d1868d65462e3e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Ribaudo?= Date: Wed, 17 May 2023 17:11:50 +0300 Subject: [PATCH 02/22] Support for `await using` --- .../babel-helpers/src/helpers-generated.ts | 4 +- packages/babel-helpers/src/helpers/dispose.js | 37 ++++++++++---- packages/babel-helpers/src/helpers/using.js | 14 ++++-- .../src/index.ts | 50 ++++++++++++------- .../fixtures/transform-await/mixed/input.js | 5 ++ .../fixtures/transform-await/mixed/output.mjs | 11 ++++ .../transform-await/only-using-await/input.js | 6 +++ .../only-using-await/output.mjs | 13 +++++ .../fixtures/transform-await/options.json | 4 ++ 9 files changed, 111 insertions(+), 33 deletions(-) create mode 100644 packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-await/mixed/input.js create mode 100644 packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-await/mixed/output.mjs create mode 100644 packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-await/only-using-await/input.js create mode 100644 packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-await/only-using-await/output.mjs create mode 100644 packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-await/options.json diff --git a/packages/babel-helpers/src/helpers-generated.ts b/packages/babel-helpers/src/helpers-generated.ts index 3b875e2aad5b..8805890d890a 100644 --- a/packages/babel-helpers/src/helpers-generated.ts +++ b/packages/babel-helpers/src/helpers-generated.ts @@ -63,7 +63,7 @@ export default Object.freeze({ ), dispose: helper( "7.0.0-beta.0", - "function dispose_SuppressedError(suppressed,error){return dispose_SuppressedError=function(suppressed,error){this.suppressed=suppressed,this.error=error,this.stack=(new Error).stack},dispose_SuppressedError.prototype=Object.create(Error.prototype,{constructor:{value:dispose_SuppressedError,writable:!0,configurable:!0}}),new dispose_SuppressedError(suppressed,error)}export default function _dispose(stack,error,hasError,SuppressedError){for(;stack.length>0;){const r=stack.pop();try{r.d.call(r.v)}catch(e){error=hasError?new(SuppressedError||dispose_SuppressedError)(e,error):e,hasError=!0}}if(hasError)throw error}", + "function dispose_SuppressedError(suppressed,error){return dispose_SuppressedError=function(suppressed,error){this.suppressed=suppressed,this.error=error,this.stack=(new Error).stack},dispose_SuppressedError.prototype=Object.create(Error.prototype,{constructor:{value:dispose_SuppressedError,writable:!0,configurable:!0}}),new dispose_SuppressedError(suppressed,error)}export default function _dispose(stack,error,hasError,SuppressedError){function next(){if(0!==stack.length){var r=stack.pop();if(r.a)return r.d.call(r.v).then(next,err);try{r.d.call(r.v)}catch(e){return err(e)}return next()}if(hasError)throw error}function err(e){return error=hasError?new(SuppressedError||dispose_SuppressedError)(e,error):e,hasError=!0,next()}return next()}", ), iterableToArrayLimit: helper( "7.0.0-beta.0", @@ -91,7 +91,7 @@ export default Object.freeze({ ), using: helper( "7.0.0-beta.0", - 'export default function _using(stack,value){if(null!=value){var dispose=value[Symbol.dispose||Symbol.for("Symbol.dispose")];if("function"!=typeof dispose)throw new TypeError("Property [Symbol.dispose] is not a function.");stack.push({v:value,d:dispose})}return value}', + 'export default function _using(stack,value,isAwait){if(null!=value){if(isAwait)var dispose=value[Symbol.asyncDispose||Symbol.for("Symbol.asyncDispose")];if(null==dispose&&(dispose=value[Symbol.dispose||Symbol.for("Symbol.dispose")]),"function"!=typeof dispose)throw new TypeError("Property [Symbol.dispose] is not a function.");stack.push({v:value,d:dispose,a:isAwait})}return value}', ), wrapRegExp: helper( "7.19.0", diff --git a/packages/babel-helpers/src/helpers/dispose.js b/packages/babel-helpers/src/helpers/dispose.js index bfe9485769ab..caff586257fe 100644 --- a/packages/babel-helpers/src/helpers/dispose.js +++ b/packages/babel-helpers/src/helpers/dispose.js @@ -16,16 +16,33 @@ function dispose_SuppressedError(suppressed, error) { } export default function _dispose(stack, error, hasError, SuppressedError) { - while (stack.length > 0) { - const r = stack.pop(); - try { - r.d.call(r.v); - } catch (e) { - error = hasError - ? new (SuppressedError || dispose_SuppressedError)(e, error) - : e; - hasError = true; + function next() { + if (stack.length === 0) { + if (hasError) throw error; + return; } + + var r = stack.pop(); + if (r.a) { + return r.d.call(r.v).then(next, err); + } else { + try { + r.d.call(r.v); + } catch (e) { + return err(e); + } + return next(); + } + } + + function err(e) { + error = hasError + ? new (SuppressedError || dispose_SuppressedError)(e, error) + : e; + hasError = true; + + return next(); } - if (hasError) throw error; + + return next(); } diff --git a/packages/babel-helpers/src/helpers/using.js b/packages/babel-helpers/src/helpers/using.js index 7521f9259e3c..05047f14804b 100644 --- a/packages/babel-helpers/src/helpers/using.js +++ b/packages/babel-helpers/src/helpers/using.js @@ -1,13 +1,19 @@ /* @minVersion 7.0.0-beta.0 */ -export default function _using(stack, value) { +export default function _using(stack, value, isAwait) { if (value !== null && value !== void 0) { - // core-js-pure uses Symbol.for("Symbol.dispose"). - var dispose = value[Symbol.dispose || Symbol.for("Symbol.dispose")]; + // core-js-pure uses Symbol.for for polyfilling well-known symbols + if (isAwait) { + var dispose = + value[Symbol.asyncDispose || Symbol.for("Symbol.asyncDispose")]; + } + if (dispose === null || dispose === void 0) { + dispose = value[Symbol.dispose || Symbol.for("Symbol.dispose")]; + } if (typeof dispose !== "function") { throw new TypeError(`Property [Symbol.dispose] is not a function.`); } - stack.push({ v: value, d: dispose }); + stack.push({ v: value, d: dispose, a: isAwait }); } return value; } diff --git a/packages/babel-plugin-proposal-explicit-resource-management/src/index.ts b/packages/babel-plugin-proposal-explicit-resource-management/src/index.ts index 563ae7df21e3..4ec8da964284 100644 --- a/packages/babel-plugin-proposal-explicit-resource-management/src/index.ts +++ b/packages/babel-plugin-proposal-explicit-resource-management/src/index.ts @@ -3,6 +3,11 @@ import syntacExplicitResourceManagement from "@babel/plugin-syntax-explicit-reso import { types as t, template } from "@babel/core"; import type { NodePath } from "@babel/traverse"; +function isUsingDeclaration(node: t.Node): node is t.VariableDeclaration { + if (!t.isVariableDeclaration(node)) return false; + return node.kind === "using" || node.kind === "await using"; +} + export default declare(api => { // TOOD: assert version 7.22.0 api.assertVersion(7); @@ -13,7 +18,7 @@ export default declare(api => { visitor: { VariableDeclaration(path) { - if (path.node.kind === "using") { + if (path.node.kind === "using" || path.node.kind === "await using") { throw path.buildCodeFrameError( `"using" declaration at the top-level of modules is not supported yet.`, ); @@ -21,7 +26,7 @@ export default declare(api => { }, ForOfStatement(path: NodePath) { const { left } = path.node; - if (!t.isVariableDeclaration(left, { kind: "using" })) return; + if (!isUsingDeclaration(left)) return; const { id } = left.declarations[0]; const tmpId = path.scope.generateUidIdentifierBasedOnNode(id); @@ -37,16 +42,19 @@ export default declare(api => { }, BlockStatement(path, state) { let stackId: t.Identifier | null = null; + let needsAwait = false; + for (const node of path.node.body) { - if (!t.isVariableDeclaration(node, { kind: "using" })) continue; - node.kind = "const"; + if (!isUsingDeclaration(node)) continue; stackId ??= path.scope.generateUidIdentifier("stack"); + const isAwaitUsing = node.kind === "await using"; + needsAwait ||= isAwaitUsing; + node.kind = "const"; node.declarations.forEach(decl => { - decl.init = t.callExpression(state.addHelper("using"), [ - t.cloneNode(stackId), - decl.init, - ]); + const args = [t.cloneNode(stackId), decl.init]; + if (isAwaitUsing) args.push(t.booleanLiteral(true)); + decl.init = t.callExpression(state.addHelper("using"), args); }); } if (!stackId) return; @@ -54,6 +62,22 @@ export default declare(api => { const errorId = path.scope.generateUidIdentifier("error"); const hasErrorId = path.scope.generateUidIdentifier("hasError"); + let disposeCall: t.Expression = t.callExpression( + state.addHelper("dispose"), + [ + t.cloneNode(stackId), + t.cloneNode(errorId), + t.cloneNode(hasErrorId), + // Pass SuppressedError so that it can be used with "pure" + // polyfills that do not compile the contents of runtime + // helpers. + template.expression.ast` + typeof SuppressedError !== undefined && SuppressedError + `, + ], + ); + if (needsAwait) disposeCall = t.awaitExpression(disposeCall); + const replacement = template.statement.ast` try { var ${stackId} = []; @@ -62,15 +86,7 @@ export default declare(api => { var ${errorId} = _; var ${hasErrorId} = true; } finally { - ${state.addHelper("dispose")}( - ${t.cloneNode(stackId)}, - ${t.cloneNode(errorId)}, - ${t.cloneNode(hasErrorId)}, - // Pass SuppressedError so that it can be used with "pure" - // polyfills that do not compile the contents of runtime - // helpers. - typeof SuppressedError !== undefined && SuppressedError - ); + ${disposeCall} } `; diff --git a/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-await/mixed/input.js b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-await/mixed/input.js new file mode 100644 index 000000000000..2e6f9a0b8d68 --- /dev/null +++ b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-await/mixed/input.js @@ -0,0 +1,5 @@ +{ + using a = 1; + await using b = 2; + using c = 3; +} \ No newline at end of file diff --git a/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-await/mixed/output.mjs b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-await/mixed/output.mjs new file mode 100644 index 000000000000..212e57123aa9 --- /dev/null +++ b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-await/mixed/output.mjs @@ -0,0 +1,11 @@ +try { + var _stack = []; + const a = babelHelpers.using(_stack, 1); + const b = babelHelpers.using(_stack, 2, true); + const c = babelHelpers.using(_stack, 3); +} catch (_) { + var _error = _; + var _hasError = true; +} finally { + await babelHelpers.dispose(_stack, _error, _hasError, typeof SuppressedError !== undefined && SuppressedError); +} diff --git a/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-await/only-using-await/input.js b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-await/only-using-await/input.js new file mode 100644 index 000000000000..578ef4f231eb --- /dev/null +++ b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-await/only-using-await/input.js @@ -0,0 +1,6 @@ +{ + await using x = obj; + stmt; + await using y = obj, z = obj; + doSomethingWith(x, y); +} diff --git a/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-await/only-using-await/output.mjs b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-await/only-using-await/output.mjs new file mode 100644 index 000000000000..1349e31e395e --- /dev/null +++ b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-await/only-using-await/output.mjs @@ -0,0 +1,13 @@ +try { + var _stack = []; + const x = babelHelpers.using(_stack, obj, true); + stmt; + const y = babelHelpers.using(_stack, obj, true), + z = babelHelpers.using(_stack, obj, true); + doSomethingWith(x, y); +} catch (_) { + var _error = _; + var _hasError = true; +} finally { + await babelHelpers.dispose(_stack, _error, _hasError, typeof SuppressedError !== undefined && SuppressedError); +} diff --git a/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-await/options.json b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-await/options.json new file mode 100644 index 000000000000..53d449e808f5 --- /dev/null +++ b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-await/options.json @@ -0,0 +1,4 @@ +{ + "sourceType": "module", + "plugins": ["proposal-explicit-resource-management"] +} From cfe39ca8fa770169579d508ee88b7e8b7ec355dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Ribaudo?= Date: Thu, 18 May 2023 13:47:43 +0300 Subject: [PATCH 03/22] Fix bug --- packages/babel-helpers/src/helpers-generated.ts | 2 +- packages/babel-helpers/src/helpers/dispose.js | 15 +++++++-------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/packages/babel-helpers/src/helpers-generated.ts b/packages/babel-helpers/src/helpers-generated.ts index 8805890d890a..3d5287e1ea78 100644 --- a/packages/babel-helpers/src/helpers-generated.ts +++ b/packages/babel-helpers/src/helpers-generated.ts @@ -63,7 +63,7 @@ export default Object.freeze({ ), dispose: helper( "7.0.0-beta.0", - "function dispose_SuppressedError(suppressed,error){return dispose_SuppressedError=function(suppressed,error){this.suppressed=suppressed,this.error=error,this.stack=(new Error).stack},dispose_SuppressedError.prototype=Object.create(Error.prototype,{constructor:{value:dispose_SuppressedError,writable:!0,configurable:!0}}),new dispose_SuppressedError(suppressed,error)}export default function _dispose(stack,error,hasError,SuppressedError){function next(){if(0!==stack.length){var r=stack.pop();if(r.a)return r.d.call(r.v).then(next,err);try{r.d.call(r.v)}catch(e){return err(e)}return next()}if(hasError)throw error}function err(e){return error=hasError?new(SuppressedError||dispose_SuppressedError)(e,error):e,hasError=!0,next()}return next()}", + "function dispose_SuppressedError(suppressed,error){return dispose_SuppressedError=function(suppressed,error){this.suppressed=suppressed,this.error=error,this.stack=(new Error).stack},dispose_SuppressedError.prototype=Object.create(Error.prototype,{constructor:{value:dispose_SuppressedError,writable:!0,configurable:!0}}),new dispose_SuppressedError(suppressed,error)}export default function _dispose(stack,error,hasError,SuppressedError){function next(){if(0!==stack.length){var r=stack.pop();if(r.a)return Promise.resolve(r.d.call(r.v)).then(next,err);try{r.d.call(r.v)}catch(e){return err(e)}return next()}if(hasError)throw error}function err(e){return error=hasError?new(SuppressedError||dispose_SuppressedError)(e,error):e,hasError=!0,next()}return next()}", ), iterableToArrayLimit: helper( "7.0.0-beta.0", diff --git a/packages/babel-helpers/src/helpers/dispose.js b/packages/babel-helpers/src/helpers/dispose.js index caff586257fe..13d126c7d593 100644 --- a/packages/babel-helpers/src/helpers/dispose.js +++ b/packages/babel-helpers/src/helpers/dispose.js @@ -24,15 +24,14 @@ export default function _dispose(stack, error, hasError, SuppressedError) { var r = stack.pop(); if (r.a) { - return r.d.call(r.v).then(next, err); - } else { - try { - r.d.call(r.v); - } catch (e) { - return err(e); - } - return next(); + return Promise.resolve(r.d.call(r.v)).then(next, err); } + try { + r.d.call(r.v); + } catch (e) { + return err(e); + } + return next(); } function err(e) { From 21d34edc6ba165b745ff90336ed391f908136bef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Ribaudo?= Date: Fri, 19 May 2023 18:20:57 +0300 Subject: [PATCH 04/22] Fix bugs, add evaluation tests --- .../src/index.ts | 10 ++++- .../fixtures/exec-async/completion-break.js | 17 ++++++++ .../fixtures/exec-async/completion-normal.js | 16 ++++++++ .../fixtures/exec-async/completion-return.js | 17 ++++++++ .../fixtures/exec-async/completion-throw.js | 22 ++++++++++ .../exec-async/invalid-not-a-function.js | 3 ++ .../test/fixtures/exec-async/multiple.js | 20 +++++++++ .../test/fixtures/exec-async/options.json | 4 ++ .../fixtures/exec-async/sync-async-mixed.js | 41 +++++++++++++++++++ .../test/fixtures/exec-async/sync-fallback.js | 20 +++++++++ .../test/fixtures/exec-async/using-null.js | 3 ++ .../fixtures/exec-sync/completion-break.js | 15 +++++++ .../fixtures/exec-sync/completion-normal.js | 14 +++++++ .../fixtures/exec-sync/completion-return.js | 15 +++++++ .../fixtures/exec-sync/completion-throw.js | 20 +++++++++ .../exec-sync/invalid-not-a-function.js | 3 ++ .../test/fixtures/exec-sync/multiple.js | 18 ++++++++ .../test/fixtures/exec-sync/using-null.js | 3 ++ .../fixtures/transform-await/mixed/output.mjs | 2 +- .../only-using-await/output.mjs | 2 +- .../transform-sync/bare-block/output.js | 2 +- .../transform-sync/for-await-head/output.mjs | 2 +- .../transform-sync/for-head/output.js | 2 +- .../transform-sync/function-body/output.js | 2 +- .../fixtures/transform-sync/if-body/output.js | 2 +- .../transform-sync/multiple-nested/output.js | 6 +-- .../multiple-same-level/output.js | 2 +- 27 files changed, 270 insertions(+), 13 deletions(-) create mode 100644 packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/exec-async/completion-break.js create mode 100644 packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/exec-async/completion-normal.js create mode 100644 packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/exec-async/completion-return.js create mode 100644 packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/exec-async/completion-throw.js create mode 100644 packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/exec-async/invalid-not-a-function.js create mode 100644 packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/exec-async/multiple.js create mode 100644 packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/exec-async/options.json create mode 100644 packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/exec-async/sync-async-mixed.js create mode 100644 packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/exec-async/sync-fallback.js create mode 100644 packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/exec-async/using-null.js create mode 100644 packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/exec-sync/completion-break.js create mode 100644 packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/exec-sync/completion-normal.js create mode 100644 packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/exec-sync/completion-return.js create mode 100644 packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/exec-sync/completion-throw.js create mode 100644 packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/exec-sync/invalid-not-a-function.js create mode 100644 packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/exec-sync/multiple.js create mode 100644 packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/exec-sync/using-null.js diff --git a/packages/babel-plugin-proposal-explicit-resource-management/src/index.ts b/packages/babel-plugin-proposal-explicit-resource-management/src/index.ts index 4ec8da964284..ef905173cb86 100644 --- a/packages/babel-plugin-proposal-explicit-resource-management/src/index.ts +++ b/packages/babel-plugin-proposal-explicit-resource-management/src/index.ts @@ -72,7 +72,7 @@ export default declare(api => { // polyfills that do not compile the contents of runtime // helpers. template.expression.ast` - typeof SuppressedError !== undefined && SuppressedError + typeof SuppressedError !== "undefined" && SuppressedError `, ], ); @@ -90,7 +90,13 @@ export default declare(api => { } `; - if (path.parentPath.isFunction()) { + const { parentPath } = path; + if ( + parentPath.isFunction() || + parentPath.isTryStatement() || + parentPath.isCatchClause() || + parentPath.isStaticBlock() + ) { path.replaceWith(t.blockStatement([replacement])); } else { path.replaceWith(replacement); diff --git a/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/exec-async/completion-break.js b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/exec-async/completion-break.js new file mode 100644 index 000000000000..ef632efc02b1 --- /dev/null +++ b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/exec-async/completion-break.js @@ -0,0 +1,17 @@ +return async function () { + let disposed = false; + let beforeBreak; + + while (true) { + await using x = { + async [Symbol.asyncDispose || Symbol.for("Symbol.asyncDispose")]() { + disposed = true; + } + }; + beforeBreak = disposed; + break; + } + + expect(beforeBreak).toBe(false); + expect(disposed).toBe(true); +}(); \ No newline at end of file diff --git a/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/exec-async/completion-normal.js b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/exec-async/completion-normal.js new file mode 100644 index 000000000000..af00c107aaf5 --- /dev/null +++ b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/exec-async/completion-normal.js @@ -0,0 +1,16 @@ +return async function () { + let disposed = false; + let beforeEnd; + + { + await using x = { + async [Symbol.asyncDispose || Symbol.for("Symbol.asyncDispose")]() { + disposed = true; + } + }; + beforeEnd = disposed; + } + + expect(beforeEnd).toBe(false); + expect(disposed).toBe(true); +}(); \ No newline at end of file diff --git a/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/exec-async/completion-return.js b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/exec-async/completion-return.js new file mode 100644 index 000000000000..c2ac56bb06a6 --- /dev/null +++ b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/exec-async/completion-return.js @@ -0,0 +1,17 @@ +return async function () { + let disposed = false; + let beforeReturn; + + await async function () { + await using x = { + async [Symbol.asyncDispose || Symbol.for("Symbol.asyncDispose")]() { + disposed = true; + } + }; + beforeReturn = disposed; + return 0; + }(); + + expect(beforeReturn).toBe(false); + expect(disposed).toBe(true); +}(); \ No newline at end of file diff --git a/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/exec-async/completion-throw.js b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/exec-async/completion-throw.js new file mode 100644 index 000000000000..358bd54730d5 --- /dev/null +++ b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/exec-async/completion-throw.js @@ -0,0 +1,22 @@ +return async function () { + let disposed = false; + let beforeReturn; + let inCatch; + let inFinally; + + try { + await using x = { + async [Symbol.asyncDispose || Symbol.for("Symbol.asyncDispose")]() { + disposed = true; + } + }; + beforeReturn = disposed; + throw 0; + } catch { + inCatch = disposed; + } + + expect(beforeReturn).toBe(false); + expect(inCatch).toBe(true); + expect(disposed).toBe(true); +}(); \ No newline at end of file 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 new file mode 100644 index 000000000000..0877dd37be3f --- /dev/null +++ b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/exec-async/invalid-not-a-function.js @@ -0,0 +1,3 @@ +return expect(async function () { + await using foo = { [Symbol.asyncDispose || Symbol.for("Symbol.asyncDispose")]: 3 }; +}()).rejects.toThrow(TypeError); diff --git a/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/exec-async/multiple.js b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/exec-async/multiple.js new file mode 100644 index 000000000000..b6abd4b909a9 --- /dev/null +++ b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/exec-async/multiple.js @@ -0,0 +1,20 @@ +return async function () { + let log = []; + + function disposable(name){ + return { + async [Symbol.asyncDispose || Symbol.for("Symbol.asyncDispose")]() { + log.push(name); + } + }; + } + + { + await using x = disposable("x"); + await using y = disposable("y"), z = disposable("z"); + await using w = disposable("w"); + log.push("body"); + } + + expect(log).toEqual(["body", "w", "z", "y", "x"]); +}(); \ No newline at end of file diff --git a/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/exec-async/options.json b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/exec-async/options.json new file mode 100644 index 000000000000..2fb3b2ef88d0 --- /dev/null +++ b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/exec-async/options.json @@ -0,0 +1,4 @@ +{ + "plugins": ["proposal-explicit-resource-management"], + "parserOpts": { "allowReturnOutsideFunction": true } +} diff --git a/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/exec-async/sync-async-mixed.js b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/exec-async/sync-async-mixed.js new file mode 100644 index 000000000000..7c6e5babded9 --- /dev/null +++ b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/exec-async/sync-async-mixed.js @@ -0,0 +1,41 @@ +return (async function () { + let log = []; + + function disposable(name, symbolName, cb){ + return { + async [Symbol[symbolName] || Symbol.for("Symbol." + symbolName)]() { + log.push(name); + cb?.(); + } + }; + } + + let inBmicrotick = false; + let inBmicrotickWhileC = false; + let inBmicrotickWhileD = false; + let inBmicrotickWhileE = false; + { + using e = disposable("e", "dispose", () => { + inBmicrotickWhileE = inBmicrotick; + }); + await using d = disposable("d", "asyncDispose", () => { + inBmicrotickWhileD = inBmicrotick; + }); + using c = disposable("c", "dispose", () => { + inBmicrotickWhileC = inBmicrotick; + }); + using b = disposable("b", "dispose", () => { + inBmicrotick = true; + Promise.resolve().then(() => { + inBmicrotick = false; + }); + }); + await using a = disposable("a", "asyncDispose"); + } + + expect(log).toEqual(["a", "b", "c", "d", "e"]); + expect(inBmicrotick).toBe(false); + expect(inBmicrotickWhileC).toBe(true); + expect(inBmicrotickWhileD).toBe(true); + expect(inBmicrotickWhileE).toBe(false); +})(); diff --git a/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/exec-async/sync-fallback.js b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/exec-async/sync-fallback.js new file mode 100644 index 000000000000..7c2b4e552e04 --- /dev/null +++ b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/exec-async/sync-fallback.js @@ -0,0 +1,20 @@ +return (async function () { + let disposed = false; + let awaited = false; + let beforeEnd; + { + await using x = { + [Symbol.dispose || Symbol.for("Symbol.dispose")]() { + disposed = true; + } + }; + beforeEnd = disposed; + Promise.resolve().then(() => { + awaited = true; + }); + } + + expect(beforeEnd).toBe(false); + expect(disposed).toBe(true); + expect(awaited).toBe(true); +})(); diff --git a/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/exec-async/using-null.js b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/exec-async/using-null.js new file mode 100644 index 000000000000..c690e5893205 --- /dev/null +++ b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/exec-async/using-null.js @@ -0,0 +1,3 @@ +return expect(async function () { + await using x = null; +}()).resolves.toBeUndefined(); diff --git a/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/exec-sync/completion-break.js b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/exec-sync/completion-break.js new file mode 100644 index 000000000000..eb284fb60a64 --- /dev/null +++ b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/exec-sync/completion-break.js @@ -0,0 +1,15 @@ +let disposed = false; +let beforeBreak; + +while (true) { + using x = { + [Symbol.dispose || Symbol.for("Symbol.dispose")]() { + disposed = true; + } + }; + beforeBreak = disposed; + break; +} + +expect(beforeBreak).toBe(false); +expect(disposed).toBe(true); diff --git a/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/exec-sync/completion-normal.js b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/exec-sync/completion-normal.js new file mode 100644 index 000000000000..309c14d652ca --- /dev/null +++ b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/exec-sync/completion-normal.js @@ -0,0 +1,14 @@ +let disposed = false; +let beforeEnd; + +{ + using x = { + [Symbol.dispose || Symbol.for("Symbol.dispose")]() { + disposed = true; + } + }; + beforeEnd = disposed; +} + +expect(beforeEnd).toBe(false); +expect(disposed).toBe(true); diff --git a/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/exec-sync/completion-return.js b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/exec-sync/completion-return.js new file mode 100644 index 000000000000..2d245176d446 --- /dev/null +++ b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/exec-sync/completion-return.js @@ -0,0 +1,15 @@ +let disposed = false; +let beforeReturn; + +(function () { + using x = { + [Symbol.dispose || Symbol.for("Symbol.dispose")]() { + disposed = true; + } + }; + beforeReturn = disposed; + return 0; +})(); + +expect(beforeReturn).toBe(false); +expect(disposed).toBe(true); diff --git a/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/exec-sync/completion-throw.js b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/exec-sync/completion-throw.js new file mode 100644 index 000000000000..659c82703310 --- /dev/null +++ b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/exec-sync/completion-throw.js @@ -0,0 +1,20 @@ +let disposed = false; +let beforeReturn; +let inCatch; +let inFinally; + +try { + using x = { + [Symbol.dispose || Symbol.for("Symbol.dispose")]() { + disposed = true; + } + }; + beforeReturn = disposed; + throw 0; +} catch { + inCatch = disposed; +} + +expect(beforeReturn).toBe(false); +expect(inCatch).toBe(true); +expect(disposed).toBe(true); diff --git a/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/exec-sync/invalid-not-a-function.js b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/exec-sync/invalid-not-a-function.js new file mode 100644 index 000000000000..008047ca6228 --- /dev/null +++ b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/exec-sync/invalid-not-a-function.js @@ -0,0 +1,3 @@ +expect(() => { + using foo = { [Symbol.dispose || Symbol.for("Symbol.dispose")]: 3 }; +}).toThrow(TypeError); diff --git a/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/exec-sync/multiple.js b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/exec-sync/multiple.js new file mode 100644 index 000000000000..f4e38974f9d0 --- /dev/null +++ b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/exec-sync/multiple.js @@ -0,0 +1,18 @@ +let log = []; + +function disposable(name){ + return { + [Symbol.dispose || Symbol.for("Symbol.dispose")]() { + log.push(name); + } + }; +} + +{ + using x = disposable("x"); + using y = disposable("y"), z = disposable("z"); + using w = disposable("w"); + log.push("body"); +} + +expect(log).toEqual(["body", "w", "z", "y", "x"]); diff --git a/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/exec-sync/using-null.js b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/exec-sync/using-null.js new file mode 100644 index 000000000000..452b59bfb335 --- /dev/null +++ b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/exec-sync/using-null.js @@ -0,0 +1,3 @@ +expect(() => { + using x = null; +}).not.toThrow(); diff --git a/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-await/mixed/output.mjs b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-await/mixed/output.mjs index 212e57123aa9..03f4665e87b8 100644 --- a/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-await/mixed/output.mjs +++ b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-await/mixed/output.mjs @@ -7,5 +7,5 @@ try { var _error = _; var _hasError = true; } finally { - await babelHelpers.dispose(_stack, _error, _hasError, typeof SuppressedError !== undefined && SuppressedError); + await babelHelpers.dispose(_stack, _error, _hasError, typeof SuppressedError !== "undefined" && SuppressedError); } diff --git a/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-await/only-using-await/output.mjs b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-await/only-using-await/output.mjs index 1349e31e395e..4e59efd7dfd5 100644 --- a/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-await/only-using-await/output.mjs +++ b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-await/only-using-await/output.mjs @@ -9,5 +9,5 @@ try { var _error = _; var _hasError = true; } finally { - await babelHelpers.dispose(_stack, _error, _hasError, typeof SuppressedError !== undefined && SuppressedError); + await babelHelpers.dispose(_stack, _error, _hasError, typeof SuppressedError !== "undefined" && SuppressedError); } diff --git a/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-sync/bare-block/output.js b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-sync/bare-block/output.js index 6d27feb531db..3f473ea9e3bb 100644 --- a/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-sync/bare-block/output.js +++ b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-sync/bare-block/output.js @@ -6,5 +6,5 @@ try { var _error = _; var _hasError = true; } finally { - babelHelpers.dispose(_stack, _error, _hasError, typeof SuppressedError !== undefined && SuppressedError); + babelHelpers.dispose(_stack, _error, _hasError, typeof SuppressedError !== "undefined" && SuppressedError); } diff --git a/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-sync/for-await-head/output.mjs b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-sync/for-await-head/output.mjs index df664bab0847..abde998f5972 100644 --- a/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-sync/for-await-head/output.mjs +++ b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-sync/for-await-head/output.mjs @@ -6,5 +6,5 @@ for await (const _x of y) try { var _error = _; var _hasError = true; } finally { - babelHelpers.dispose(_stack, _error, _hasError, typeof SuppressedError !== undefined && SuppressedError); + babelHelpers.dispose(_stack, _error, _hasError, typeof SuppressedError !== "undefined" && SuppressedError); } diff --git a/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-sync/for-head/output.js b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-sync/for-head/output.js index b247bddea82b..87afde5cfe12 100644 --- a/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-sync/for-head/output.js +++ b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-sync/for-head/output.js @@ -6,5 +6,5 @@ for (const _x of y) try { var _error = _; var _hasError = true; } finally { - babelHelpers.dispose(_stack, _error, _hasError, typeof SuppressedError !== undefined && SuppressedError); + babelHelpers.dispose(_stack, _error, _hasError, typeof SuppressedError !== "undefined" && SuppressedError); } diff --git a/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-sync/function-body/output.js b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-sync/function-body/output.js index c1a28b0248b6..1893ea294ba5 100644 --- a/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-sync/function-body/output.js +++ b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-sync/function-body/output.js @@ -6,5 +6,5 @@ if (test) try { var _error = _; var _hasError = true; } finally { - babelHelpers.dispose(_stack, _error, _hasError, typeof SuppressedError !== undefined && SuppressedError); + babelHelpers.dispose(_stack, _error, _hasError, typeof SuppressedError !== "undefined" && SuppressedError); } diff --git a/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-sync/if-body/output.js b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-sync/if-body/output.js index 817b433ec023..70932ce58965 100644 --- a/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-sync/if-body/output.js +++ b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-sync/if-body/output.js @@ -7,6 +7,6 @@ function fn() { var _error = _; var _hasError = true; } finally { - babelHelpers.dispose(_stack, _error, _hasError, typeof SuppressedError !== undefined && SuppressedError); + babelHelpers.dispose(_stack, _error, _hasError, typeof SuppressedError !== "undefined" && SuppressedError); } } diff --git a/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-sync/multiple-nested/output.js b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-sync/multiple-nested/output.js index ec58a08dc6c7..400910f9733b 100644 --- a/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-sync/multiple-nested/output.js +++ b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-sync/multiple-nested/output.js @@ -12,7 +12,7 @@ try { var _error3 = _; var _hasError3 = true; } finally { - babelHelpers.dispose(_stack3, _error3, _hasError3, typeof SuppressedError !== undefined && SuppressedError); + babelHelpers.dispose(_stack3, _error3, _hasError3, typeof SuppressedError !== "undefined" && SuppressedError); } })); stmt; @@ -20,12 +20,12 @@ try { var _error2 = _; var _hasError2 = true; } finally { - babelHelpers.dispose(_stack2, _error2, _hasError2, typeof SuppressedError !== undefined && SuppressedError); + babelHelpers.dispose(_stack2, _error2, _hasError2, typeof SuppressedError !== "undefined" && SuppressedError); } stmt; } catch (_) { var _error = _; var _hasError = true; } finally { - babelHelpers.dispose(_stack, _error, _hasError, typeof SuppressedError !== undefined && SuppressedError); + babelHelpers.dispose(_stack, _error, _hasError, typeof SuppressedError !== "undefined" && SuppressedError); } diff --git a/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-sync/multiple-same-level/output.js b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-sync/multiple-same-level/output.js index 22f3ee042b0e..ee2755059986 100644 --- a/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-sync/multiple-same-level/output.js +++ b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-sync/multiple-same-level/output.js @@ -12,5 +12,5 @@ try { var _error = _; var _hasError = true; } finally { - babelHelpers.dispose(_stack, _error, _hasError, typeof SuppressedError !== undefined && SuppressedError); + babelHelpers.dispose(_stack, _error, _hasError, typeof SuppressedError !== "undefined" && SuppressedError); } From abea92e9ae43afb70424d8129f65862773c95f73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Ribaudo?= Date: Sun, 21 May 2023 20:07:50 +0200 Subject: [PATCH 05/22] Add support for top-level using in modules --- .../src/index.ts | 117 +++++++++++++++--- .../await-or-not-preserved/input.mjs | 4 + .../await-or-not-preserved/output.mjs | 11 ++ .../hoisting-default-clas-anon/input.mjs | 2 + .../hoisting-default-clas-anon/output.mjs | 11 ++ .../hoisting-default-class/input.mjs | 2 + .../hoisting-default-class/output.mjs | 11 ++ .../hoisting-default-expr/input.mjs | 2 + .../hoisting-default-expr/output.mjs | 11 ++ .../hoisting-default-fn-anon/input.mjs | 2 + .../hoisting-default-fn-anon/output.mjs | 10 ++ .../hoisting-default-fn/input.mjs | 2 + .../hoisting-default-fn/output.mjs | 10 ++ .../transform-top-level/hoisting/input.mjs | 18 +++ .../transform-top-level/hoisting/output.mjs | 33 +++++ 15 files changed, 232 insertions(+), 14 deletions(-) create mode 100644 packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-top-level/await-or-not-preserved/input.mjs create mode 100644 packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-top-level/await-or-not-preserved/output.mjs create mode 100644 packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-top-level/hoisting-default-clas-anon/input.mjs create mode 100644 packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-top-level/hoisting-default-clas-anon/output.mjs create mode 100644 packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-top-level/hoisting-default-class/input.mjs create mode 100644 packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-top-level/hoisting-default-class/output.mjs create mode 100644 packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-top-level/hoisting-default-expr/input.mjs create mode 100644 packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-top-level/hoisting-default-expr/output.mjs create mode 100644 packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-top-level/hoisting-default-fn-anon/input.mjs create mode 100644 packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-top-level/hoisting-default-fn-anon/output.mjs create mode 100644 packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-top-level/hoisting-default-fn/input.mjs create mode 100644 packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-top-level/hoisting-default-fn/output.mjs create mode 100644 packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-top-level/hoisting/input.mjs create mode 100644 packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-top-level/hoisting/output.mjs diff --git a/packages/babel-plugin-proposal-explicit-resource-management/src/index.ts b/packages/babel-plugin-proposal-explicit-resource-management/src/index.ts index ef905173cb86..72357dcc89f3 100644 --- a/packages/babel-plugin-proposal-explicit-resource-management/src/index.ts +++ b/packages/babel-plugin-proposal-explicit-resource-management/src/index.ts @@ -3,27 +3,28 @@ import syntacExplicitResourceManagement from "@babel/plugin-syntax-explicit-reso import { types as t, template } from "@babel/core"; import type { NodePath } from "@babel/traverse"; -function isUsingDeclaration(node: t.Node): node is t.VariableDeclaration { - if (!t.isVariableDeclaration(node)) return false; - return node.kind === "using" || node.kind === "await using"; -} - export default declare(api => { // TOOD: assert version 7.22.0 api.assertVersion(7); + const TOP_LEVEL_USING = new WeakSet(); + const TOP_LEVEL_AWAIT_USING = new WeakSet(); + + function isUsingDeclaration(node: t.Node): node is t.VariableDeclaration { + if (!t.isVariableDeclaration(node)) return false; + return ( + node.kind === "using" || + node.kind === "await using" || + TOP_LEVEL_USING.has(node) || + TOP_LEVEL_AWAIT_USING.has(node) + ); + } + return { name: "proposal-explicit-resource-management", inherits: syntacExplicitResourceManagement, visitor: { - VariableDeclaration(path) { - if (path.node.kind === "using" || path.node.kind === "await using") { - throw path.buildCodeFrameError( - `"using" declaration at the top-level of modules is not supported yet.`, - ); - } - }, ForOfStatement(path: NodePath) { const { left } = path.node; if (!isUsingDeclaration(left)) return; @@ -47,10 +48,16 @@ export default declare(api => { for (const node of path.node.body) { if (!isUsingDeclaration(node)) continue; stackId ??= path.scope.generateUidIdentifier("stack"); - const isAwaitUsing = node.kind === "await using"; + const isAwaitUsing = + node.kind === "await using" || TOP_LEVEL_AWAIT_USING.has(node); needsAwait ||= isAwaitUsing; - node.kind = "const"; + if ( + !TOP_LEVEL_AWAIT_USING.delete(node) && + !TOP_LEVEL_USING.delete(node) + ) { + node.kind = "const"; + } node.declarations.forEach(decl => { const args = [t.cloneNode(stackId), decl.init]; if (isAwaitUsing) args.push(t.booleanLiteral(true)); @@ -102,6 +109,88 @@ export default declare(api => { path.replaceWith(replacement); } }, + // To transform top-level using declarations, we must wrap the + // module body in a block after hoisting all the exports and imports. + // This might cause some variables to be `undefined` rather than TDZ. + Program(path) { + if (path.node.sourceType !== "module") return; + if (!path.node.body.some(isUsingDeclaration)) return; + + const innerBlockBody = []; + for (const stmt of path.get("body")) { + if (stmt.isFunctionDeclaration() || stmt.isImportDeclaration()) { + continue; + } + + let { node } = stmt; + let shouldRemove = true; + + if (stmt.isExportDefaultDeclaration()) { + let { declaration } = stmt.node; + let varId; + if (t.isClassDeclaration(declaration)) { + varId = declaration.id; + declaration.id = null; + declaration = t.toExpression(declaration); + } else if (!t.isExpression(declaration)) { + continue; + } + + varId ??= path.scope.generateUidIdentifier("_default"); + innerBlockBody.push( + t.variableDeclaration("var", [ + t.variableDeclarator(varId, declaration), + ]), + ); + stmt.replaceWith( + t.exportNamedDeclaration(null, [ + t.exportSpecifier(t.cloneNode(varId), t.identifier("default")), + ]), + ); + continue; + } + + if (stmt.isExportNamedDeclaration()) { + node = stmt.node.declaration; + if (!node || t.isFunction(node)) continue; + stmt.replaceWith( + t.exportNamedDeclaration( + null, + Object.values(t.getOuterBindingIdentifiers(node, false)).map( + id => t.exportSpecifier(t.cloneNode(id), t.cloneNode(id)), + ), + ), + ); + shouldRemove = false; + } else if (stmt.isExportDeclaration()) { + continue; + } + + if (t.isClassDeclaration(node)) { + const { id } = node; + node.id = null; + innerBlockBody.push( + t.variableDeclaration("var", [ + t.variableDeclarator(id, t.toExpression(node)), + ]), + ); + } else if (t.isVariableDeclaration(node)) { + if (node.kind === "using") { + TOP_LEVEL_USING.add(stmt.node); + } else if (node.kind === "await using") { + TOP_LEVEL_AWAIT_USING.add(stmt.node); + } + node.kind = "var"; + innerBlockBody.push(node); + } else { + innerBlockBody.push(stmt.node); + } + + if (shouldRemove) stmt.remove(); + } + + path.pushContainer("body", t.blockStatement(innerBlockBody)); + }, }, }; }); diff --git a/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-top-level/await-or-not-preserved/input.mjs b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-top-level/await-or-not-preserved/input.mjs new file mode 100644 index 000000000000..5301dc35ce5b --- /dev/null +++ b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-top-level/await-or-not-preserved/input.mjs @@ -0,0 +1,4 @@ +using x = A; +await using y = B; + +export { x, y }; diff --git a/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-top-level/await-or-not-preserved/output.mjs b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-top-level/await-or-not-preserved/output.mjs new file mode 100644 index 000000000000..8af13d3cf1bd --- /dev/null +++ b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-top-level/await-or-not-preserved/output.mjs @@ -0,0 +1,11 @@ +export { x, y }; +try { + var _stack = []; + var x = babelHelpers.using(_stack, A); + var y = babelHelpers.using(_stack, B, true); +} catch (_) { + var _error = _; + var _hasError = true; +} finally { + await babelHelpers.dispose(_stack, _error, _hasError, typeof SuppressedError !== "undefined" && SuppressedError); +} diff --git a/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-top-level/hoisting-default-clas-anon/input.mjs b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-top-level/hoisting-default-clas-anon/input.mjs new file mode 100644 index 000000000000..195496d86a06 --- /dev/null +++ b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-top-level/hoisting-default-clas-anon/input.mjs @@ -0,0 +1,2 @@ +using x = null; +export default class {} diff --git a/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-top-level/hoisting-default-clas-anon/output.mjs b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-top-level/hoisting-default-clas-anon/output.mjs new file mode 100644 index 000000000000..ad4d0ee8a395 --- /dev/null +++ b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-top-level/hoisting-default-clas-anon/output.mjs @@ -0,0 +1,11 @@ +export { _default as default }; +try { + var _stack = []; + var x = babelHelpers.using(_stack, null); + var _default = class {}; +} catch (_) { + var _error = _; + var _hasError = true; +} finally { + babelHelpers.dispose(_stack, _error, _hasError, typeof SuppressedError !== "undefined" && SuppressedError); +} diff --git a/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-top-level/hoisting-default-class/input.mjs b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-top-level/hoisting-default-class/input.mjs new file mode 100644 index 000000000000..39a0ffa10821 --- /dev/null +++ b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-top-level/hoisting-default-class/input.mjs @@ -0,0 +1,2 @@ +using x = null; +export default class C {} diff --git a/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-top-level/hoisting-default-class/output.mjs b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-top-level/hoisting-default-class/output.mjs new file mode 100644 index 000000000000..7f73d97dda49 --- /dev/null +++ b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-top-level/hoisting-default-class/output.mjs @@ -0,0 +1,11 @@ +export { C as default }; +try { + var _stack = []; + var x = babelHelpers.using(_stack, null); + var C = class {}; +} catch (_) { + var _error = _; + var _hasError = true; +} finally { + babelHelpers.dispose(_stack, _error, _hasError, typeof SuppressedError !== "undefined" && SuppressedError); +} diff --git a/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-top-level/hoisting-default-expr/input.mjs b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-top-level/hoisting-default-expr/input.mjs new file mode 100644 index 000000000000..da55c82598ad --- /dev/null +++ b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-top-level/hoisting-default-expr/input.mjs @@ -0,0 +1,2 @@ +using x = null; +export default doSomething(); diff --git a/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-top-level/hoisting-default-expr/output.mjs b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-top-level/hoisting-default-expr/output.mjs new file mode 100644 index 000000000000..779ed9d9252d --- /dev/null +++ b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-top-level/hoisting-default-expr/output.mjs @@ -0,0 +1,11 @@ +export { _default as default }; +try { + var _stack = []; + var x = babelHelpers.using(_stack, null); + var _default = doSomething(); +} catch (_) { + var _error = _; + var _hasError = true; +} finally { + babelHelpers.dispose(_stack, _error, _hasError, typeof SuppressedError !== "undefined" && SuppressedError); +} diff --git a/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-top-level/hoisting-default-fn-anon/input.mjs b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-top-level/hoisting-default-fn-anon/input.mjs new file mode 100644 index 000000000000..659df073897e --- /dev/null +++ b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-top-level/hoisting-default-fn-anon/input.mjs @@ -0,0 +1,2 @@ +using x = null; +export default function fn() {} diff --git a/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-top-level/hoisting-default-fn-anon/output.mjs b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-top-level/hoisting-default-fn-anon/output.mjs new file mode 100644 index 000000000000..e026c959172b --- /dev/null +++ b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-top-level/hoisting-default-fn-anon/output.mjs @@ -0,0 +1,10 @@ +export default function fn() {} +try { + var _stack = []; + var x = babelHelpers.using(_stack, null); +} catch (_) { + var _error = _; + var _hasError = true; +} finally { + babelHelpers.dispose(_stack, _error, _hasError, typeof SuppressedError !== "undefined" && SuppressedError); +} diff --git a/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-top-level/hoisting-default-fn/input.mjs b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-top-level/hoisting-default-fn/input.mjs new file mode 100644 index 000000000000..659df073897e --- /dev/null +++ b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-top-level/hoisting-default-fn/input.mjs @@ -0,0 +1,2 @@ +using x = null; +export default function fn() {} diff --git a/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-top-level/hoisting-default-fn/output.mjs b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-top-level/hoisting-default-fn/output.mjs new file mode 100644 index 000000000000..e026c959172b --- /dev/null +++ b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-top-level/hoisting-default-fn/output.mjs @@ -0,0 +1,10 @@ +export default function fn() {} +try { + var _stack = []; + var x = babelHelpers.using(_stack, null); +} catch (_) { + var _error = _; + var _hasError = true; +} finally { + babelHelpers.dispose(_stack, _error, _hasError, typeof SuppressedError !== "undefined" && SuppressedError); +} diff --git a/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-top-level/hoisting/input.mjs b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-top-level/hoisting/input.mjs new file mode 100644 index 000000000000..4299b20ca7f8 --- /dev/null +++ b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-top-level/hoisting/input.mjs @@ -0,0 +1,18 @@ +import { doSomething } from "somewhere"; +export * from "somewhere else"; +export * as ns from "somewhere else"; + +function f() { a; B; } +function h() { b; A; } +export function g() { c; } + +doSomething(); + +export { f }; +export let { b } = {}; + +let c = 2; +class A {} +export class B {} + +using x = null; diff --git a/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-top-level/hoisting/output.mjs b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-top-level/hoisting/output.mjs new file mode 100644 index 000000000000..55399e38e396 --- /dev/null +++ b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/transform-top-level/hoisting/output.mjs @@ -0,0 +1,33 @@ +import { doSomething } from "somewhere"; +export * from "somewhere else"; +export * as ns from "somewhere else"; +function f() { + a; + B; +} +function h() { + b; + A; +} +export function g() { + c; +} +export { f }; +export { b }; +export { B }; +try { + var _stack = []; + doSomething(); + var { + b + } = {}; + var c = 2; + var A = class {}; + var B = class {}; + var x = babelHelpers.using(_stack, null); +} catch (_) { + var _error = _; + var _hasError = true; +} finally { + babelHelpers.dispose(_stack, _error, _hasError, typeof SuppressedError !== "undefined" && SuppressedError); +} From 5915cab7a2a39f8bc7864b2b49abdb7bd0c4d965 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Ribaudo?= Date: Sun, 21 May 2023 20:09:58 +0200 Subject: [PATCH 06/22] Fix test old node --- .../test/fixtures/exec-async/sync-async-mixed.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/exec-async/sync-async-mixed.js b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/exec-async/sync-async-mixed.js index 7c6e5babded9..0f542a21ce96 100644 --- a/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/exec-async/sync-async-mixed.js +++ b/packages/babel-plugin-proposal-explicit-resource-management/test/fixtures/exec-async/sync-async-mixed.js @@ -5,7 +5,7 @@ return (async function () { return { async [Symbol[symbolName] || Symbol.for("Symbol." + symbolName)]() { log.push(name); - cb?.(); + if (cb) cb(); } }; } From e557bb56349650bd74a177d729567b00825e671f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Ribaudo?= Date: Mon, 22 May 2023 10:41:53 +0200 Subject: [PATCH 07/22] new-version-checklist --- Makefile.js | 2 +- Makefile.source.mjs | 11 +++++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/Makefile.js b/Makefile.js index 7d7edba00f73..82045ed6a3ab 100644 --- a/Makefile.js +++ b/Makefile.js @@ -1,3 +1,3 @@ /* eslint-disable */ // prettier-ignore -"use strict";var e=require("os"),r=require("fs"),t=require("path"),n=require("events"),i=require("assert"),o=require("util"),s=require("child_process");function c(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}var a=c(e),u=c(r),l=c(t),f=c(n),p=c(i),h=c(o),d=c(s),v="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{};function g(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}var y=function(e){return e&&e.Math==Math&&e},m=y("object"==typeof globalThis&&globalThis)||y("object"==typeof window&&window)||y("object"==typeof self&&self)||y("object"==typeof v&&v)||function(){return this}()||v||Function("return this")(),E={},b=function(e){try{return!!e()}catch(e){return!0}},w=!b((function(){return 7!=Object.defineProperty({},1,{get:function(){return 7}})[1]})),O=!b((function(){var e=function(){}.bind();return"function"!=typeof e||e.hasOwnProperty("prototype")})),S=O,R=Function.prototype.call,I=S?R.bind(R):function(){return R.apply(R,arguments)},A={},j={}.propertyIsEnumerable,x=Object.getOwnPropertyDescriptor,k=x&&!j.call({1:2},1);A.f=k?function(e){var r=x(this,e);return!!r&&r.enumerable}:j;var L,_,T=function(e,r){return{enumerable:!(1&e),configurable:!(2&e),writable:!(4&e),value:r}},N=O,P=Function.prototype,F=P.call,D=N&&P.bind.bind(F,F),C=N?D:function(e){return function(){return F.apply(e,arguments)}},M=C,G=M({}.toString),$=M("".slice),U=function(e){return $(G(e),8,-1)},B=b,X=U,q=Object,W=C("".split),V=B((function(){return!q("z").propertyIsEnumerable(0)}))?function(e){return"String"==X(e)?W(e,""):q(e)}:q,H=function(e){return null==e},z=H,Y=TypeError,K=function(e){if(z(e))throw Y("Can't call method on "+e);return e},J=V,Q=K,Z=function(e){return J(Q(e))},ee="object"==typeof document&&document.all,re={all:ee,IS_HTMLDDA:void 0===ee&&void 0!==ee},te=re.all,ne=re.IS_HTMLDDA?function(e){return"function"==typeof e||e===te}:function(e){return"function"==typeof e},ie=ne,oe=re.all,se=re.IS_HTMLDDA?function(e){return"object"==typeof e?null!==e:ie(e)||e===oe}:function(e){return"object"==typeof e?null!==e:ie(e)},ce=m,ae=ne,ue=function(e){return ae(e)?e:void 0},le=function(e,r){return arguments.length<2?ue(ce[e]):ce[e]&&ce[e][r]},fe=C({}.isPrototypeOf),pe="undefined"!=typeof navigator&&String(navigator.userAgent)||"",he=m,de=pe,ve=he.process,ge=he.Deno,ye=ve&&ve.versions||ge&&ge.version,me=ye&&ye.v8;me&&(_=(L=me.split("."))[0]>0&&L[0]<4?1:+(L[0]+L[1])),!_&&de&&(!(L=de.match(/Edge\/(\d+)/))||L[1]>=74)&&(L=de.match(/Chrome\/(\d+)/))&&(_=+L[1]);var Ee=_,be=Ee,we=b,Oe=m.String,Se=!!Object.getOwnPropertySymbols&&!we((function(){var e=Symbol();return!Oe(e)||!(Object(e)instanceof Symbol)||!Symbol.sham&&be&&be<41})),Re=Se&&!Symbol.sham&&"symbol"==typeof Symbol.iterator,Ie=le,Ae=ne,je=fe,xe=Object,ke=Re?function(e){return"symbol"==typeof e}:function(e){var r=Ie("Symbol");return Ae(r)&&je(r.prototype,xe(e))},Le=String,_e=function(e){try{return Le(e)}catch(e){return"Object"}},Te=ne,Ne=_e,Pe=TypeError,Fe=function(e){if(Te(e))return e;throw Pe(Ne(e)+" is not a function")},De=Fe,Ce=H,Me=function(e,r){var t=e[r];return Ce(t)?void 0:De(t)},Ge=I,$e=ne,Ue=se,Be=TypeError,Xe={exports:{}},qe=m,We=Object.defineProperty,Ve=function(e,r){try{We(qe,e,{value:r,configurable:!0,writable:!0})}catch(t){qe[e]=r}return r},He=Ve,ze=m["__core-js_shared__"]||He("__core-js_shared__",{}),Ye=ze;(Xe.exports=function(e,r){return Ye[e]||(Ye[e]=void 0!==r?r:{})})("versions",[]).push({version:"3.30.2",mode:"global",copyright:"© 2014-2023 Denis Pushkarev (zloirock.ru)",license:"https://github.com/zloirock/core-js/blob/v3.30.2/LICENSE",source:"https://github.com/zloirock/core-js"});var Ke=Xe.exports,Je=K,Qe=Object,Ze=function(e){return Qe(Je(e))},er=Ze,rr=C({}.hasOwnProperty),tr=Object.hasOwn||function(e,r){return rr(er(e),r)},nr=C,ir=0,or=Math.random(),sr=nr(1..toString),cr=function(e){return"Symbol("+(void 0===e?"":e)+")_"+sr(++ir+or,36)},ar=Ke,ur=tr,lr=cr,fr=Se,pr=Re,hr=m.Symbol,dr=ar("wks"),vr=pr?hr.for||hr:hr&&hr.withoutSetter||lr,gr=function(e){return ur(dr,e)||(dr[e]=fr&&ur(hr,e)?hr[e]:vr("Symbol."+e)),dr[e]},yr=I,mr=se,Er=ke,br=Me,wr=function(e,r){var t,n;if("string"===r&&$e(t=e.toString)&&!Ue(n=Ge(t,e)))return n;if($e(t=e.valueOf)&&!Ue(n=Ge(t,e)))return n;if("string"!==r&&$e(t=e.toString)&&!Ue(n=Ge(t,e)))return n;throw Be("Can't convert object to primitive value")},Or=TypeError,Sr=gr("toPrimitive"),Rr=function(e,r){if(!mr(e)||Er(e))return e;var t,n=br(e,Sr);if(n){if(void 0===r&&(r="default"),t=yr(n,e,r),!mr(t)||Er(t))return t;throw Or("Can't convert object to primitive value")}return void 0===r&&(r="number"),wr(e,r)},Ir=ke,Ar=function(e){var r=Rr(e,"string");return Ir(r)?r:r+""},jr=se,xr=m.document,kr=jr(xr)&&jr(xr.createElement),Lr=function(e){return kr?xr.createElement(e):{}},_r=Lr,Tr=!w&&!b((function(){return 7!=Object.defineProperty(_r("div"),"a",{get:function(){return 7}}).a})),Nr=w,Pr=I,Fr=A,Dr=T,Cr=Z,Mr=Ar,Gr=tr,$r=Tr,Ur=Object.getOwnPropertyDescriptor;E.f=Nr?Ur:function(e,r){if(e=Cr(e),r=Mr(r),$r)try{return Ur(e,r)}catch(e){}if(Gr(e,r))return Dr(!Pr(Fr.f,e,r),e[r])};var Br={},Xr=w&&b((function(){return 42!=Object.defineProperty((function(){}),"prototype",{value:42,writable:!1}).prototype})),qr=se,Wr=String,Vr=TypeError,Hr=function(e){if(qr(e))return e;throw Vr(Wr(e)+" is not an object")},zr=w,Yr=Tr,Kr=Xr,Jr=Hr,Qr=Ar,Zr=TypeError,et=Object.defineProperty,rt=Object.getOwnPropertyDescriptor;Br.f=zr?Kr?function(e,r,t){if(Jr(e),r=Qr(r),Jr(t),"function"==typeof e&&"prototype"===r&&"value"in t&&"writable"in t&&!t.writable){var n=rt(e,r);n&&n.writable&&(e[r]=t.value,t={configurable:"configurable"in t?t.configurable:n.configurable,enumerable:"enumerable"in t?t.enumerable:n.enumerable,writable:!1})}return et(e,r,t)}:et:function(e,r,t){if(Jr(e),r=Qr(r),Jr(t),Yr)try{return et(e,r,t)}catch(e){}if("get"in t||"set"in t)throw Zr("Accessors not supported");return"value"in t&&(e[r]=t.value),e};var tt=Br,nt=T,it=w?function(e,r,t){return tt.f(e,r,nt(1,t))}:function(e,r,t){return e[r]=t,e},ot={exports:{}},st=w,ct=tr,at=Function.prototype,ut=st&&Object.getOwnPropertyDescriptor,lt=ct(at,"name"),ft={EXISTS:lt,PROPER:lt&&"something"===function(){}.name,CONFIGURABLE:lt&&(!st||st&&ut(at,"name").configurable)},pt=ne,ht=ze,dt=C(Function.toString);pt(ht.inspectSource)||(ht.inspectSource=function(e){return dt(e)});var vt,gt,yt,mt=ht.inspectSource,Et=ne,bt=m.WeakMap,wt=Et(bt)&&/native code/.test(String(bt)),Ot=cr,St=Ke("keys"),Rt=function(e){return St[e]||(St[e]=Ot(e))},It={},At=wt,jt=m,xt=se,kt=it,Lt=tr,_t=ze,Tt=Rt,Nt=It,Pt=jt.TypeError,Ft=jt.WeakMap;if(At||_t.state){var Dt=_t.state||(_t.state=new Ft);Dt.get=Dt.get,Dt.has=Dt.has,Dt.set=Dt.set,vt=function(e,r){if(Dt.has(e))throw Pt("Object already initialized");return r.facade=e,Dt.set(e,r),r},gt=function(e){return Dt.get(e)||{}},yt=function(e){return Dt.has(e)}}else{var Ct=Tt("state");Nt[Ct]=!0,vt=function(e,r){if(Lt(e,Ct))throw Pt("Object already initialized");return r.facade=e,kt(e,Ct,r),r},gt=function(e){return Lt(e,Ct)?e[Ct]:{}},yt=function(e){return Lt(e,Ct)}}var Mt={set:vt,get:gt,has:yt,enforce:function(e){return yt(e)?gt(e):vt(e,{})},getterFor:function(e){return function(r){var t;if(!xt(r)||(t=gt(r)).type!==e)throw Pt("Incompatible receiver, "+e+" required");return t}}},Gt=C,$t=b,Ut=ne,Bt=tr,Xt=w,qt=ft.CONFIGURABLE,Wt=mt,Vt=Mt.enforce,Ht=Mt.get,zt=String,Yt=Object.defineProperty,Kt=Gt("".slice),Jt=Gt("".replace),Qt=Gt([].join),Zt=Xt&&!$t((function(){return 8!==Yt((function(){}),"length",{value:8}).length})),en=String(String).split("String"),rn=ot.exports=function(e,r,t){"Symbol("===Kt(zt(r),0,7)&&(r="["+Jt(zt(r),/^Symbol\(([^)]*)\)/,"$1")+"]"),t&&t.getter&&(r="get "+r),t&&t.setter&&(r="set "+r),(!Bt(e,"name")||qt&&e.name!==r)&&(Xt?Yt(e,"name",{value:r,configurable:!0}):e.name=r),Zt&&t&&Bt(t,"arity")&&e.length!==t.arity&&Yt(e,"length",{value:t.arity});try{t&&Bt(t,"constructor")&&t.constructor?Xt&&Yt(e,"prototype",{writable:!1}):e.prototype&&(e.prototype=void 0)}catch(e){}var n=Vt(e);return Bt(n,"source")||(n.source=Qt(en,"string"==typeof r?r:"")),e};Function.prototype.toString=rn((function(){return Ut(this)&&Ht(this).source||Wt(this)}),"toString");var tn=ot.exports,nn=ne,on=Br,sn=tn,cn=Ve,an=function(e,r,t,n){n||(n={});var i=n.enumerable,o=void 0!==n.name?n.name:r;if(nn(t)&&sn(t,o,n),n.global)i?e[r]=t:cn(r,t);else{try{n.unsafe?e[r]&&(i=!0):delete e[r]}catch(e){}i?e[r]=t:on.f(e,r,{value:t,enumerable:!1,configurable:!n.nonConfigurable,writable:!n.nonWritable})}return e},un={},ln=Math.ceil,fn=Math.floor,pn=Math.trunc||function(e){var r=+e;return(r>0?fn:ln)(r)},hn=function(e){var r=+e;return r!=r||0===r?0:pn(r)},dn=hn,vn=Math.max,gn=Math.min,yn=function(e,r){var t=dn(e);return t<0?vn(t+r,0):gn(t,r)},mn=hn,En=Math.min,bn=function(e){return e>0?En(mn(e),9007199254740991):0},wn=bn,On=function(e){return wn(e.length)},Sn=Z,Rn=yn,In=On,An=function(e){return function(r,t,n){var i,o=Sn(r),s=In(o),c=Rn(n,s);if(e&&t!=t){for(;s>c;)if((i=o[c++])!=i)return!0}else for(;s>c;c++)if((e||c in o)&&o[c]===t)return e||c||0;return!e&&-1}},jn={includes:An(!0),indexOf:An(!1)},xn=tr,kn=Z,Ln=jn.indexOf,_n=It,Tn=C([].push),Nn=function(e,r){var t,n=kn(e),i=0,o=[];for(t in n)!xn(_n,t)&&xn(n,t)&&Tn(o,t);for(;r.length>i;)xn(n,t=r[i++])&&(~Ln(o,t)||Tn(o,t));return o},Pn=["constructor","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","toLocaleString","toString","valueOf"],Fn=Nn,Dn=Pn.concat("length","prototype");un.f=Object.getOwnPropertyNames||function(e){return Fn(e,Dn)};var Cn={};Cn.f=Object.getOwnPropertySymbols;var Mn=le,Gn=un,$n=Cn,Un=Hr,Bn=C([].concat),Xn=Mn("Reflect","ownKeys")||function(e){var r=Gn.f(Un(e)),t=$n.f;return t?Bn(r,t(e)):r},qn=tr,Wn=Xn,Vn=E,Hn=Br,zn=function(e,r,t){for(var n=Wn(r),i=Hn.f,o=Vn.f,s=0;ss;)Ri.f(e,t=i[s++],n[t]);return e};var xi,ki=le("document","documentElement"),Li=Hr,_i=mi,Ti=Pn,Ni=It,Pi=ki,Fi=Lr,Di=Rt("IE_PROTO"),Ci=function(){},Mi=function(e){return"