Skip to content

Commit 58f529e

Browse files
fix(bindgen): fix error passing for initial export call errors
1 parent 3d8aa58 commit 58f529e

2 files changed

Lines changed: 94 additions & 37 deletions

File tree

crates/js-component-bindgen/src/function_bindgen.rs

Lines changed: 87 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,9 @@ impl FunctionBindgen<'_> {
277277
/// let ret;
278278
/// ```
279279
///
280+
/// This function returns as a first tuple parameter a list of
281+
/// variables that should be created via let statements beforehand.
282+
///
280283
/// # Arguments
281284
///
282285
/// * `amt` - number of results
@@ -287,35 +290,37 @@ impl FunctionBindgen<'_> {
287290
amt: usize,
288291
results: &mut Vec<String>,
289292
is_async: bool,
290-
) -> String {
293+
) -> (String, String) {
291294
let mut s = String::new();
295+
let mut vars_init = String::new();
292296
match amt {
293297
0 => {
294298
// Async functions with no returns still return async code,
295299
// which will be used as the initial callback result going into the async driver
296300
if is_async {
297-
uwrite!(s, "let ret = ")
298-
} else {
299-
uwrite!(s, "let ret;")
301+
uwrite!(s, "ret = ")
300302
}
303+
uwriteln!(vars_init, "let ret;");
301304
}
302305
1 => {
303-
uwrite!(s, "let ret = ");
306+
uwrite!(s, "ret = ");
304307
results.push("ret".to_string());
308+
uwriteln!(vars_init, "let ret;");
305309
}
306310
n => {
307-
uwrite!(s, "var [");
311+
uwrite!(s, "[");
308312
for i in 0..n {
309313
if i > 0 {
310314
uwrite!(s, ", ");
311315
}
312-
uwrite!(s, "ret{}", i);
316+
uwrite!(s, "ret{i}");
313317
results.push(format!("ret{i}"));
318+
uwriteln!(vars_init, "let ret;");
314319
}
315320
uwrite!(s, "] = ");
316321
}
317322
}
318-
s
323+
(vars_init, s)
319324
}
320325

321326
fn bitcast(&mut self, cast: &Bitcast, op: &str) -> String {
@@ -1482,20 +1487,44 @@ impl Bindgen for FunctionBindgen<'_> {
14821487
// Output result binding preamble (e.g. 'var ret =', 'var [ ret0, ret1] = exports...() ')
14831488
// along with the code to perofrm the call
14841489
let sig_results_length = sig.results.len();
1485-
let s = self.generate_result_assignment_lhs(sig_results_length, results, is_async);
1486-
1487-
let (call_prefix, call_wrapper) = if self.requires_async_porcelain | self.is_async {
1488-
("await ", Intrinsic::WithGlobalCurrentTaskMetaFnAsync.name())
1489-
} else {
1490-
("", Intrinsic::WithGlobalCurrentTaskMetaFn.name())
1491-
};
1490+
let (vars_init, assignment_lhs) =
1491+
self.generate_result_assignment_lhs(sig_results_length, results, is_async);
1492+
1493+
let (call_prefix, call_wrapper, call_err_cleanup) =
1494+
if self.requires_async_porcelain | self.is_async {
1495+
(
1496+
"await ",
1497+
Intrinsic::WithGlobalCurrentTaskMetaFnAsync.name(),
1498+
r#"
1499+
task.reject(err);
1500+
task.exit();
1501+
return task.completionPromise();
1502+
"#,
1503+
)
1504+
} else {
1505+
(
1506+
"",
1507+
Intrinsic::WithGlobalCurrentTaskMetaFn.name(),
1508+
r#"
1509+
task.reject(err);
1510+
task.exit();
1511+
throw err;
1512+
"#,
1513+
)
1514+
};
14921515
uwriteln!(
14931516
self.src,
1494-
r#"{s} {call_prefix} {call_wrapper}({{
1495-
taskID: task.id(),
1496-
componentIdx: task.componentIdx(),
1497-
fn: () => {callee}({args}),
1498-
}});
1517+
r#"
1518+
{vars_init}
1519+
try {{
1520+
{assignment_lhs} {call_prefix} {call_wrapper}({{
1521+
taskID: task.id(),
1522+
componentIdx: task.componentIdx(),
1523+
fn: () => {callee}({args}),
1524+
}});
1525+
}} catch (err) {{
1526+
{call_err_cleanup}
1527+
}}
14991528
"#,
15001529
callee = self.callee,
15011530
args = operands.join(", "),
@@ -1644,30 +1673,57 @@ impl Bindgen for FunctionBindgen<'_> {
16441673
}
16451674

16461675
// Build the JS expression that calls the callee
1647-
let (call_prefix, call_wrapper) = if is_async || self.requires_async_porcelain {
1648-
("await ", Intrinsic::WithGlobalCurrentTaskMetaFnAsync.name())
1649-
} else {
1650-
("", Intrinsic::WithGlobalCurrentTaskMetaFn.name())
1651-
};
1676+
let (call_prefix, call_wrapper, call_err_cleanup) =
1677+
if is_async || self.requires_async_porcelain {
1678+
(
1679+
"await ",
1680+
Intrinsic::WithGlobalCurrentTaskMetaFnAsync.name(),
1681+
r#"
1682+
task.reject(err);
1683+
task.exit();
1684+
return task.completionPromise();
1685+
"#,
1686+
)
1687+
} else {
1688+
(
1689+
"",
1690+
Intrinsic::WithGlobalCurrentTaskMetaFn.name(),
1691+
r#"
1692+
task.reject(err);
1693+
task.exit();
1694+
throw err;
1695+
"#,
1696+
)
1697+
};
16521698
let call = format!(
16531699
r#"{call_prefix} {call_wrapper}({{
1654-
componentIdx: task.componentIdx(),
1655-
taskID: task.id(),
1656-
fn: () => {callee_fn_js}({callee_args_js})
1657-
}})
1700+
componentIdx: task.componentIdx(),
1701+
taskID: task.id(),
1702+
fn: () => {callee_fn_js}({callee_args_js}),
1703+
}})
16581704
"#,
16591705
);
16601706

16611707
match self.err {
16621708
// If configured to do *no* error handling at all or throw
16631709
// error objects directly, we can simply perform the call
16641710
ErrHandling::None | ErrHandling::ThrowResultErr => {
1665-
let s = self.generate_result_assignment_lhs(
1711+
let (vars_init, assignment_lhs) = self.generate_result_assignment_lhs(
16661712
fn_wasm_result_count,
16671713
results,
16681714
is_async,
16691715
);
1670-
uwriteln!(self.src, "{s}{call};");
1716+
uwriteln!(
1717+
self.src,
1718+
r#"
1719+
{vars_init}
1720+
try {{
1721+
{assignment_lhs}{call};
1722+
}} catch (err) {{
1723+
{call_err_cleanup}
1724+
}}
1725+
"#
1726+
);
16711727
}
16721728
// If configured to force all thrown errors into result objects,
16731729
// then we add a try/catch around the call

crates/js-component-bindgen/src/intrinsics/p3/async_task.rs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -992,7 +992,7 @@ impl AsyncTaskIntrinsic {
992992
}}
993993
994994
async runCallbackFn(...args) {{
995-
if (!this.#callbackFn) {{ throw new Error('on callback function has been set for task'); }}
995+
if (!this.#callbackFn) {{ throw new Error('no callback function has been set for task'); }}
996996
return {with_global_current_task_meta_async_fn}({{
997997
taskID: this.#id,
998998
componentIdx: this.#componentIdx,
@@ -1348,7 +1348,7 @@ impl AsyncTaskIntrinsic {
13481348
}}
13491349
}}
13501350
1351-
exit() {{
1351+
exit(args) {{
13521352
{debug_log_fn}('[{task_class}#exit()]', {{
13531353
componentIdx: this.#componentIdx,
13541354
taskID: this.#id,
@@ -1383,8 +1383,10 @@ impl AsyncTaskIntrinsic {
13831383
if (!state) {{ throw new Error('missing async state for component [' + this.#componentIdx + ']'); }}
13841384
13851385
// Exempt the host from exclusive lock check
1386-
if (this.#componentIdx !== -1 && this.needsExclusiveLock() && !state.isExclusivelyLocked()) {{
1387-
throw new Error(`task [${{this.#id}}] exit: component [${{this.#componentIdx}}] should have been exclusively locked`);
1386+
if (this.#componentIdx !== -1 && !args?.skipExclusiveLockCheck) {{
1387+
if (this.needsExclusiveLock() && !state.isExclusivelyLocked()) {{
1388+
throw new Error(`task [${{this.#id}}] exit: component [${{this.#componentIdx}}] should have been exclusively locked`);
1389+
}}
13881390
}}
13891391
13901392
state.exclusiveRelease();
@@ -1906,8 +1908,7 @@ impl AsyncTaskIntrinsic {
19061908
callbackFnName,
19071909
taskID: task.id()
19081910
}});
1909-
// NOTE: if we've exited
1910-
task.exit();
1911+
task.exit({{ skipExclusiveLockCheck: true }});
19111912
return;
19121913
19131914
case 1: // YIELD

0 commit comments

Comments
 (0)