Allow returning Result from functions

This commit adds support for exporting a function defined in Rust that returns a
`Result`, translating the `Ok` variant to the actual return value and the `Err`
variant to an exception that's thrown in JS.

The support for return types and descriptors was rejiggered a bit to be a bit
more abstract and more well suited for this purpose. We no longer distinguish
between functions with a return value and those without a return value.
Additionally a new trait, `ReturnWasmAbi`, is used for converting return values.
This trait is an internal implementation detail, however, and shouldn't surface
itself to users much (if at all).

Closes #841
This commit is contained in:
Alex Crichton
2018-09-17 18:26:45 -07:00
parent ccced83b0e
commit 7cf4213283
17 changed files with 292 additions and 266 deletions

View File

@ -418,39 +418,28 @@ impl TryToTokens for ast::Export {
}
converted_arguments.push(quote! { #ident });
}
let ret_ty;
let convert_ret;
match &self.function.ret {
Some(syn::Type::Reference(_)) => {
bail_span!(
self.function.ret,
"cannot return a borrowed ref with #[wasm_bindgen]",
)
}
Some(ty) => {
ret_ty = quote! {
-> <#ty as ::wasm_bindgen::convert::IntoWasmAbi>::Abi
};
convert_ret = quote! {
<#ty as ::wasm_bindgen::convert::IntoWasmAbi>
::into_abi(#ret, &mut unsafe {
::wasm_bindgen::convert::GlobalStack::new()
})
};
}
None => {
ret_ty = quote!();
convert_ret = quote!();
}
let syn_unit = syn::Type::Tuple(syn::TypeTuple {
elems: Default::default(),
paren_token: Default::default(),
});
let syn_ret = self.function.ret.as_ref().unwrap_or(&syn_unit);
if let syn::Type::Reference(_) = syn_ret {
bail_span!(
syn_ret,
"cannot return a borrowed ref with #[wasm_bindgen]",
)
}
let describe_ret = match &self.function.ret {
Some(ty) => {
quote! {
inform(1);
<#ty as WasmDescribe>::describe();
}
}
None => quote! { inform(0); },
let ret_ty = quote! {
-> <#syn_ret as ::wasm_bindgen::convert::ReturnWasmAbi>::Abi
};
let convert_ret = quote! {
<#syn_ret as ::wasm_bindgen::convert::ReturnWasmAbi>
::return_abi(#ret, &mut unsafe {
::wasm_bindgen::convert::GlobalStack::new()
})
};
let describe_ret = quote! {
<#syn_ret as WasmDescribe>::describe();
};
let nargs = self.function.arguments.len() as u32;
let argtys = self.function.arguments.iter().map(|arg| &arg.ty);
@ -464,6 +453,10 @@ impl TryToTokens for ast::Export {
pub extern fn #generated_name(#(#args),*) #ret_ty {
// See definition of `link_mem_intrinsics` for what this is doing
::wasm_bindgen::__rt::link_mem_intrinsics();
// Scope all local variables to be destroyed after we call the
// function to ensure that `#convert_ret`, if it panics, doesn't
// leak anything.
let #ret = {
let mut __stack = unsafe {
::wasm_bindgen::convert::GlobalStack::new()
@ -954,8 +947,8 @@ impl<'a> ToTokens for DescribeImport<'a> {
let argtys = f.function.arguments.iter().map(|arg| &arg.ty);
let nargs = f.function.arguments.len() as u32;
let inform_ret = match &f.js_ret {
Some(ref t) => quote! { inform(1); <#t as WasmDescribe>::describe(); },
None => quote! { inform(0); },
Some(ref t) => quote! { <#t as WasmDescribe>::describe(); },
None => quote! { <() as WasmDescribe>::describe(); },
};
Descriptor(&f.shim, quote! {