Skip to content

Commit

Permalink
fix(es/proposal): Update explicit resource management to match spec (#…
Browse files Browse the repository at this point in the history
…8860)

**Related issue:**

 - Closes #8853
  • Loading branch information
kdy1 authored Apr 15, 2024
1 parent 3de8253 commit 6d24076
Show file tree
Hide file tree
Showing 26 changed files with 404 additions and 160 deletions.
76 changes: 76 additions & 0 deletions crates/swc_ecma_transforms_base/src/helpers/_using_ctx.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
function _using_ctx() {
var _disposeSuppressedError =
typeof SuppressedError === "function"
? // eslint-disable-next-line no-undef
SuppressedError
: (function (error, suppressed) {
var err = new Error();
err.name = "SuppressedError";
err.suppressed = suppressed;
err.error = error;
return err;
}),
empty = {},
stack = [];
function using(isAwait, value) {
if (value != null) {
if (Object(value) !== value) {
throw new TypeError(
"using declarations can only be used with objects, functions, null, or undefined.",
);
}
// 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 = 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, a: isAwait });
} else if (isAwait) {
// provide the nullish `value` as `d` for minification gain
stack.push({ d: value, a: isAwait });
}
return value;
}
return {
// error
e: empty,
// using
u: using.bind(null, false),
// await using
a: using.bind(null, true),
// dispose
d: function () {
var error = this.e;

function next() {
// eslint-disable-next-line @typescript-eslint/no-use-before-define
while ((resource = stack.pop())) {
try {
var resource,
disposalResult = resource.d && resource.d.call(resource.v);
if (resource.a) {
return Promise.resolve(disposalResult).then(next, err);
}
} catch (e) {
return err(e);
}
}
if (error !== empty) throw error;
}

function err(e) {
error = error !== empty ? new _disposeSuppressedError(error, e) : e;

return next();
}

return next();
},
};
}
1 change: 1 addition & 0 deletions crates/swc_ecma_transforms_base/src/helpers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,7 @@ define_helpers!(Helpers {
identity: (),
dispose: (),
using: (),
using_ctx: (),
});

pub fn inject_helpers(global_mark: Mark) -> impl Fold + VisitMut {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
use std::iter::once;

use swc_common::{util::take::Take, DUMMY_SP};
use swc_ecma_ast::*;
use swc_ecma_transforms_base::helper;
use swc_ecma_utils::{find_pat_ids, private_ident, ExprFactory, ModuleItemLike, StmtLike};
use swc_ecma_utils::{
find_pat_ids, private_ident, quote_ident, ExprFactory, ModuleItemLike, StmtLike,
};
use swc_ecma_visit::{as_folder, noop_visit_mut_type, Fold, VisitMut, VisitMutWith};

pub fn explicit_resource_management() -> impl Fold + VisitMut {
Expand All @@ -18,9 +18,7 @@ struct ExplicitResourceManagement {
}

struct State {
stack: Ident,
has_error: Ident,
error_var: Ident,
using_ctx: Ident,
catch_var: Ident,

has_await: bool,
Expand All @@ -29,9 +27,7 @@ struct State {
impl Default for State {
fn default() -> Self {
Self {
stack: private_ident!("_stack"),
has_error: private_ident!("_hasError"),
error_var: private_ident!("_error"),
using_ctx: private_ident!("_usingCtx"),
catch_var: private_ident!("_"),
has_await: false,
}
Expand Down Expand Up @@ -63,13 +59,15 @@ impl ExplicitResourceManagement {
let mut extras = vec![];
let mut try_body = vec![];

let stack_var_decl = VarDeclarator {
let using_ctx_var = VarDeclarator {
span: DUMMY_SP,
name: state.stack.clone().into(),
name: state.using_ctx.clone().into(),
init: Some(
ArrayLit {
CallExpr {
callee: helper!(using_ctx),
span: DUMMY_SP,
elems: vec![],
args: Default::default(),
type_args: Default::default(),
}
.into(),
),
Expand All @@ -80,16 +78,16 @@ impl ExplicitResourceManagement {
span: DUMMY_SP,
kind: VarDeclKind::Var,
declare: false,
decls: vec![stack_var_decl],
decls: vec![using_ctx_var],
}))));

for stmt in stmts.take() {
match stmt.try_into_stmt() {
Ok(stmt @ Stmt::Decl(Decl::Fn(..))) => {
new.push(T::from_stmt(stmt));
}
Ok(Stmt::Decl(Decl::Var(mut var))) => {
var.kind = VarDeclKind::Var;
Ok(Stmt::Decl(Decl::Var(var))) => {
// var.kind = VarDeclKind::Var;
try_body.push(Stmt::Decl(Decl::Var(var)));
}
Ok(stmt) => try_body.push(stmt),
Expand Down Expand Up @@ -310,39 +308,28 @@ impl ExplicitResourceManagement {
// Drop `;`
try_body.retain(|stmt| !matches!(stmt, Stmt::Empty(..)));

// var error = $catch_var
let error_catch_var = Stmt::Decl(Decl::Var(Box::new(VarDecl {
span: DUMMY_SP,
kind: VarDeclKind::Var,
declare: false,
decls: vec![VarDeclarator {
span: DUMMY_SP,
name: state.error_var.clone().into(),
init: Some(state.catch_var.clone().into()),
definite: false,
}],
})));

// var has_error = true
let has_error_true = Stmt::Decl(Decl::Var(Box::new(VarDecl {
// usingCtx.e = $catch_var
let assign_error = AssignExpr {
span: DUMMY_SP,
kind: VarDeclKind::Var,
declare: false,
decls: vec![VarDeclarator {
span: DUMMY_SP,
name: state.has_error.clone().into(),
init: Some(true.into()),
definite: false,
}],
})));
op: op!("="),
left: state
.using_ctx
.clone()
.make_member(quote_ident!("e"))
.into(),
right: state.catch_var.clone().into(),
}
.into_stmt();

// _usingCtx.d()
let dispose_expr = CallExpr {
span: DUMMY_SP,
callee: helper!(dispose),
args: vec![
state.stack.as_arg(),
state.error_var.as_arg(),
state.has_error.as_arg(),
],
callee: state
.using_ctx
.clone()
.make_member(quote_ident!("d"))
.as_callee(),
args: vec![],
type_args: Default::default(),
};
let dispose_stmt = if state.has_await {
Expand All @@ -366,7 +353,7 @@ impl ExplicitResourceManagement {
param: Some(state.catch_var.into()),
body: BlockStmt {
span: DUMMY_SP,
stmts: vec![error_catch_var, has_error_true],
stmts: vec![assign_error],
},
}),
finalizer: Some(BlockStmt {
Expand Down Expand Up @@ -434,15 +421,16 @@ impl VisitMut for ExplicitResourceManagement {
.map(|d| {
let init = CallExpr {
span: decl.span,
callee: helper!(using),
args: once(state.stack.clone().as_arg())
.chain(once(d.init.unwrap().as_arg()))
.chain(if decl.is_await {
Some(true.as_arg())
callee: state
.using_ctx
.clone()
.make_member(if decl.is_await {
quote_ident!("a")
} else {
None
quote_ident!("u")
})
.collect(),
.as_callee(),
args: vec![d.init.unwrap().as_arg()],
type_args: Default::default(),
};

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
const { deepStrictEqual } = require('node:assert')

let i = 0
let err
try {
await using _x1 = {
async [Symbol.asyncDispose || Symbol.for("Symbol.asyncDispose")]() {
throw [1, ++i]
}
}

await using _x2 = {
async [Symbol.asyncDispose || Symbol.for("Symbol.asyncDispose")]() {
throw [2, ++i]
}
}

await using _x3 = {
async [Symbol.asyncDispose || Symbol.for("Symbol.asyncDispose")]() {
throw [3, ++i]
}
}

await using _x4 = {
async [Symbol.asyncDispose || Symbol.for("Symbol.asyncDispose")]() {
throw [4, ++i]
}
}

throw [5, ++i]
} catch (e) {
err = e
}

console.log(err)
deepStrictEqual(err.suppressed, [1, 5])
deepStrictEqual(err.error.suppressed, [2, 4])
deepStrictEqual(err.error.error.suppressed, [3, 3])
deepStrictEqual(err.error.error.error.suppressed, [4, 2])
deepStrictEqual(err.error.error.error.error, [5, 1])
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@

let i = 0
let err
try {
await using _x1 = {
async [Symbol.asyncDispose]() {
throw [1, ++i]
}
}

await using _x2 = {
async [Symbol.asyncDispose]() {
throw [2, ++i]
}
}

await using _x3 = {
async [Symbol.asyncDispose]() {
throw [3, ++i]
}
}

await using _x4 = {
async [Symbol.asyncDispose]() {
throw [4, ++i]
}
}

throw [5, ++i]
} catch (e) {
err = e
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
let i = 0;
let err;
try {
try {
var _usingCtx = _using_ctx();
const _x1 = _usingCtx.a({
async [Symbol.asyncDispose] () {
throw [
1,
++i
];
}
});
const _x2 = _usingCtx.a({
async [Symbol.asyncDispose] () {
throw [
2,
++i
];
}
});
const _x3 = _usingCtx.a({
async [Symbol.asyncDispose] () {
throw [
3,
++i
];
}
});
const _x4 = _usingCtx.a({
async [Symbol.asyncDispose] () {
throw [
4,
++i
];
}
});
throw [
5,
++i
];
} catch (_) {
_usingCtx.e = _;
} finally{
await _usingCtx.d();
}
} catch (e) {
err = e;
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
{
try {
var _stack = [];
var a = _using(_stack, 1);
var b = _using(_stack, 2, true);
var c = _using(_stack, 3);
var _usingCtx = _using_ctx();
const a = _usingCtx.u(1);
const b = _usingCtx.a(2);
const c = _usingCtx.u(3);
} catch (_) {
var _error = _;
var _hasError = true;
_usingCtx.e = _;
} finally{
await _dispose(_stack, _error, _hasError);
await _usingCtx.d();
}
}
Loading

0 comments on commit 6d24076

Please sign in to comment.