Skip to content

Commit b2f9e12

Browse files
authored
Move async function arguments into spawned futures (#3043)
Fixes #3042 Previously, any exported async functions which borrowed their arguments would fail to compile, because the values being referenced are stored on the stack of the initial synchronous call and then dropped, leaving nothing for the spawned future to reference. This moves the referenced values into the spawned future, fixing that.
1 parent f292973 commit b2f9e12

File tree

3 files changed

+52
-14
lines changed

3 files changed

+52
-14
lines changed

crates/backend/src/codegen.rs

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -470,19 +470,15 @@ impl TryToTokens for ast::Export {
470470
quote! { () },
471471
quote! { () },
472472
quote! {
473-
wasm_bindgen_futures::spawn_local(async move {
474-
<#syn_ret as wasm_bindgen::__rt::Start>::start(#ret.await);
475-
})
473+
<#syn_ret as wasm_bindgen::__rt::Start>::start(#ret.await)
476474
},
477475
)
478476
} else {
479477
(
480478
quote! { wasm_bindgen::JsValue },
481479
quote! { #syn_ret },
482480
quote! {
483-
wasm_bindgen_futures::future_to_promise(async move {
484-
<#syn_ret as wasm_bindgen::__rt::IntoJsResult>::into_js_result(#ret.await)
485-
}).into()
481+
<#syn_ret as wasm_bindgen::__rt::IntoJsResult>::into_js_result(#ret.await)
486482
},
487483
)
488484
}
@@ -496,8 +492,32 @@ impl TryToTokens for ast::Export {
496492
(quote! { #syn_ret }, quote! { #syn_ret }, quote! { #ret })
497493
};
498494

495+
let mut call = quote! {
496+
{
497+
#(#arg_conversions)*
498+
let #ret = #receiver(#(#converted_arguments),*);
499+
#ret_expr
500+
}
501+
};
502+
503+
if self.function.r#async {
504+
if self.start {
505+
call = quote! {
506+
wasm_bindgen_futures::spawn_local(async move {
507+
#call
508+
})
509+
}
510+
} else {
511+
call = quote! {
512+
wasm_bindgen_futures::future_to_promise(async move {
513+
#call
514+
}).into()
515+
}
516+
}
517+
}
518+
499519
let projection = quote! { <#ret_ty as wasm_bindgen::convert::ReturnWasmAbi> };
500-
let convert_ret = quote! { #projection::return_abi(#ret_expr) };
520+
let convert_ret = quote! { #projection::return_abi(#ret) };
501521
let describe_ret = quote! {
502522
<#ret_ty as WasmDescribe>::describe();
503523
<#inner_ret_ty as WasmDescribe>::describe();
@@ -523,13 +543,8 @@ impl TryToTokens for ast::Export {
523543
)]
524544
pub unsafe extern "C" fn #generated_name(#(#args),*) -> #projection::Abi {
525545
#start_check
526-
// Scope all local variables to be destroyed after we call
527-
// the function to ensure that `#convert_ret`, if it panics,
528-
// doesn't leak anything.
529-
let #ret = {
530-
#(#arg_conversions)*
531-
#receiver(#(#converted_arguments),*)
532-
};
546+
547+
let #ret = #call;
533548
#convert_ret
534549
}
535550
};

tests/wasm/futures.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ exports.call_exports = async function() {
1515
await assert.rejects(wasm.async_throw_message(), /async message/);
1616
await assert.rejects(wasm.async_throw_jserror(), /async message/);
1717
await assert.rejects(wasm.async_throw_custom_error(), /custom error/);
18+
assert.strictEqual("Hi, Jim!", await wasm.async_take_reference("Jim"));
19+
const foo = await new wasm.AsyncStruct();
20+
assert.strictEqual(42, await foo.method());
1821
};
1922

2023
exports.call_promise = async function() {

tests/wasm/futures.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,26 @@ pub async fn async_throw_custom_error() -> Result<AsyncCustomReturn, AsyncCustom
106106
})
107107
}
108108

109+
#[wasm_bindgen]
110+
pub async fn async_take_reference(x: &str) -> String {
111+
format!("Hi, {x}!")
112+
}
113+
114+
#[wasm_bindgen]
115+
pub struct AsyncStruct;
116+
117+
#[wasm_bindgen]
118+
impl AsyncStruct {
119+
#[wasm_bindgen(constructor)]
120+
pub async fn new() -> AsyncStruct {
121+
AsyncStruct
122+
}
123+
124+
pub async fn method(&self) -> u32 {
125+
42
126+
}
127+
}
128+
109129
#[wasm_bindgen_test]
110130
async fn test_promise() {
111131
assert_eq!(call_promise().await.as_string(), Some(String::from("ok")))

0 commit comments

Comments
 (0)