mirror of
https://github.com/rustwasm/wasm-bindgen.git
synced 2024-11-24 06:33:33 +03:00
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:
parent
ccced83b0e
commit
7cf4213283
@ -418,39 +418,28 @@ impl TryToTokens for ast::Export {
|
|||||||
}
|
}
|
||||||
converted_arguments.push(quote! { #ident });
|
converted_arguments.push(quote! { #ident });
|
||||||
}
|
}
|
||||||
let ret_ty;
|
let syn_unit = syn::Type::Tuple(syn::TypeTuple {
|
||||||
let convert_ret;
|
elems: Default::default(),
|
||||||
match &self.function.ret {
|
paren_token: Default::default(),
|
||||||
Some(syn::Type::Reference(_)) => {
|
});
|
||||||
bail_span!(
|
let syn_ret = self.function.ret.as_ref().unwrap_or(&syn_unit);
|
||||||
self.function.ret,
|
if let syn::Type::Reference(_) = syn_ret {
|
||||||
"cannot return a borrowed ref with #[wasm_bindgen]",
|
bail_span!(
|
||||||
)
|
syn_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 describe_ret = match &self.function.ret {
|
let ret_ty = quote! {
|
||||||
Some(ty) => {
|
-> <#syn_ret as ::wasm_bindgen::convert::ReturnWasmAbi>::Abi
|
||||||
quote! {
|
};
|
||||||
inform(1);
|
let convert_ret = quote! {
|
||||||
<#ty as WasmDescribe>::describe();
|
<#syn_ret as ::wasm_bindgen::convert::ReturnWasmAbi>
|
||||||
}
|
::return_abi(#ret, &mut unsafe {
|
||||||
}
|
::wasm_bindgen::convert::GlobalStack::new()
|
||||||
None => quote! { inform(0); },
|
})
|
||||||
|
};
|
||||||
|
let describe_ret = quote! {
|
||||||
|
<#syn_ret as WasmDescribe>::describe();
|
||||||
};
|
};
|
||||||
let nargs = self.function.arguments.len() as u32;
|
let nargs = self.function.arguments.len() as u32;
|
||||||
let argtys = self.function.arguments.iter().map(|arg| &arg.ty);
|
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 {
|
pub extern fn #generated_name(#(#args),*) #ret_ty {
|
||||||
// See definition of `link_mem_intrinsics` for what this is doing
|
// See definition of `link_mem_intrinsics` for what this is doing
|
||||||
::wasm_bindgen::__rt::link_mem_intrinsics();
|
::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 #ret = {
|
||||||
let mut __stack = unsafe {
|
let mut __stack = unsafe {
|
||||||
::wasm_bindgen::convert::GlobalStack::new()
|
::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 argtys = f.function.arguments.iter().map(|arg| &arg.ty);
|
||||||
let nargs = f.function.arguments.len() as u32;
|
let nargs = f.function.arguments.len() as u32;
|
||||||
let inform_ret = match &f.js_ret {
|
let inform_ret = match &f.js_ret {
|
||||||
Some(ref t) => quote! { inform(1); <#t as WasmDescribe>::describe(); },
|
Some(ref t) => quote! { <#t as WasmDescribe>::describe(); },
|
||||||
None => quote! { inform(0); },
|
None => quote! { <() as WasmDescribe>::describe(); },
|
||||||
};
|
};
|
||||||
|
|
||||||
Descriptor(&f.shim, quote! {
|
Descriptor(&f.shim, quote! {
|
||||||
|
@ -34,6 +34,7 @@ tys! {
|
|||||||
RUST_STRUCT
|
RUST_STRUCT
|
||||||
CHAR
|
CHAR
|
||||||
OPTIONAL
|
OPTIONAL
|
||||||
|
UNIT
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -61,12 +62,13 @@ pub enum Descriptor {
|
|||||||
RustStruct(String),
|
RustStruct(String),
|
||||||
Char,
|
Char,
|
||||||
Option(Box<Descriptor>),
|
Option(Box<Descriptor>),
|
||||||
|
Unit,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Function {
|
pub struct Function {
|
||||||
pub arguments: Vec<Descriptor>,
|
pub arguments: Vec<Descriptor>,
|
||||||
pub ret: Option<Descriptor>,
|
pub ret: Descriptor,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -128,6 +130,7 @@ impl Descriptor {
|
|||||||
Descriptor::RustStruct(name)
|
Descriptor::RustStruct(name)
|
||||||
}
|
}
|
||||||
CHAR => Descriptor::Char,
|
CHAR => Descriptor::Char,
|
||||||
|
UNIT => Descriptor::Unit,
|
||||||
other => panic!("unknown descriptor: {}", other),
|
other => panic!("unknown descriptor: {}", other),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -295,12 +298,10 @@ impl Function {
|
|||||||
let arguments = (0..get(data))
|
let arguments = (0..get(data))
|
||||||
.map(|_| Descriptor::_decode(data))
|
.map(|_| Descriptor::_decode(data))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
let ret = if get(data) == 0 {
|
Function {
|
||||||
None
|
arguments,
|
||||||
} else {
|
ret: Descriptor::_decode(data),
|
||||||
Some(Descriptor::_decode(data))
|
}
|
||||||
};
|
|
||||||
Function { arguments, ret }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -390,15 +390,12 @@ impl<'a, 'b> Js2Rust<'a, 'b> {
|
|||||||
Ok(self)
|
Ok(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ret(&mut self, ret: &Option<Descriptor>) -> Result<&mut Self, Error> {
|
pub fn ret(&mut self, ty: &Descriptor) -> Result<&mut Self, Error> {
|
||||||
let ty = match *ret {
|
if let Descriptor::Unit = ty {
|
||||||
Some(ref t) => t,
|
self.ret_ty = "void".to_string();
|
||||||
None => {
|
self.ret_expr = format!("return RET;");
|
||||||
self.ret_ty = "void".to_string();
|
return Ok(self);
|
||||||
self.ret_expr = format!("return RET;");
|
}
|
||||||
return Ok(self);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let (ty, optional) = match ty {
|
let (ty, optional) = match ty {
|
||||||
Descriptor::Option(t) => (&**t, true),
|
Descriptor::Option(t) => (&**t, true),
|
||||||
|
@ -393,6 +393,11 @@ impl<'a> Context<'a> {
|
|||||||
))
|
))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
self.bind("__wbindgen_rethrow", &|me| {
|
||||||
|
me.expose_take_object();
|
||||||
|
Ok(String::from("function(idx) { throw takeObject(idx); }"))
|
||||||
|
})?;
|
||||||
|
|
||||||
self.create_memory_export();
|
self.create_memory_export();
|
||||||
self.unexport_unused_internal_exports();
|
self.unexport_unused_internal_exports();
|
||||||
closures::rewrite(self)?;
|
closures::rewrite(self)?;
|
||||||
@ -626,7 +631,9 @@ impl<'a> Context<'a> {
|
|||||||
|
|
||||||
let set = {
|
let set = {
|
||||||
let mut cx = Js2Rust::new(&field.name, self);
|
let mut cx = Js2Rust::new(&field.name, self);
|
||||||
cx.method(true, false).argument(&descriptor)?.ret(&None)?;
|
cx.method(true, false)
|
||||||
|
.argument(&descriptor)?
|
||||||
|
.ret(&Descriptor::Unit)?;
|
||||||
ts_dst.push_str(&format!(
|
ts_dst.push_str(&format!(
|
||||||
"{}{}: {}\n",
|
"{}{}: {}\n",
|
||||||
if field.readonly { "readonly " } else { "" },
|
if field.readonly { "readonly " } else { "" },
|
||||||
@ -637,7 +644,7 @@ impl<'a> Context<'a> {
|
|||||||
};
|
};
|
||||||
let (get, _ts, js_doc) = Js2Rust::new(&field.name, self)
|
let (get, _ts, js_doc) = Js2Rust::new(&field.name, self)
|
||||||
.method(true, false)
|
.method(true, false)
|
||||||
.ret(&Some(descriptor))?
|
.ret(&descriptor)?
|
||||||
.finish("", &format!("wasm.{}", wasm_getter));
|
.finish("", &format!("wasm.{}", wasm_getter));
|
||||||
if !dst.ends_with("\n") {
|
if !dst.ends_with("\n") {
|
||||||
dst.push_str("\n");
|
dst.push_str("\n");
|
||||||
|
@ -285,14 +285,11 @@ impl<'a, 'b> Rust2Js<'a, 'b> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ret(&mut self, ret: &Option<Descriptor>) -> Result<(), Error> {
|
fn ret(&mut self, ty: &Descriptor) -> Result<(), Error> {
|
||||||
let ty = match *ret {
|
if let Descriptor::Unit = ty {
|
||||||
Some(ref t) => t,
|
self.ret_expr = "JS;".to_string();
|
||||||
None => {
|
return Ok(());
|
||||||
self.ret_expr = "JS;".to_string();
|
}
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let (ty, optional) = match ty {
|
let (ty, optional) = match ty {
|
||||||
Descriptor::Option(t) => (&**t, true),
|
Descriptor::Option(t) => (&**t, true),
|
||||||
_ => (ty, false),
|
_ => (ty, false),
|
||||||
|
@ -726,13 +726,14 @@ impl<'a> MacroParse<(Option<BindgenAttrs>, &'a mut TokenStream)> for syn::Item {
|
|||||||
}
|
}
|
||||||
let comments = extract_doc_comments(&f.attrs);
|
let comments = extract_doc_comments(&f.attrs);
|
||||||
f.to_tokens(tokens);
|
f.to_tokens(tokens);
|
||||||
|
let opts = opts.unwrap_or_default();
|
||||||
program.exports.push(ast::Export {
|
program.exports.push(ast::Export {
|
||||||
class: None,
|
class: None,
|
||||||
method_self: None,
|
method_self: None,
|
||||||
constructor: None,
|
constructor: None,
|
||||||
comments,
|
comments,
|
||||||
rust_name: f.ident.clone(),
|
rust_name: f.ident.clone(),
|
||||||
function: f.convert(opts.unwrap_or_default())?,
|
function: f.convert(opts)?,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
syn::Item::Struct(mut s) => {
|
syn::Item::Struct(mut s) => {
|
||||||
|
@ -30,6 +30,7 @@
|
|||||||
- [`String`](./reference/types/string.md)
|
- [`String`](./reference/types/string.md)
|
||||||
- [Number Slices](./reference/types/number-slices.md)
|
- [Number Slices](./reference/types/number-slices.md)
|
||||||
- [Boxed Number Slices](./reference/types/boxed-number-slices.md)
|
- [Boxed Number Slices](./reference/types/boxed-number-slices.md)
|
||||||
|
- [`Result<T, JsValue>`](./reference/types/result.md)
|
||||||
- [`#[wasm_bindgen]` Attributes](./reference/attributes/index.md)
|
- [`#[wasm_bindgen]` Attributes](./reference/attributes/index.md)
|
||||||
- [On JavaScript Imports](./reference/attributes/on-js-imports/index.md)
|
- [On JavaScript Imports](./reference/attributes/on-js-imports/index.md)
|
||||||
- [`catch`](./reference/attributes/on-js-imports/catch.md)
|
- [`catch`](./reference/attributes/on-js-imports/catch.md)
|
||||||
|
20
guide/src/reference/types/result.md
Normal file
20
guide/src/reference/types/result.md
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
# `Result<T, JsValue>`
|
||||||
|
|
||||||
|
| `T` parameter | `&T` parameter | `&mut T` parameter | `T` return value | `Option<T>` parameter | `Option<T>` return value | JavaScript representation |
|
||||||
|
|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
|
||||||
|
| No | No | No | No | No | Yes | Same as `T`, or an exception |
|
||||||
|
|
||||||
|
The `Result` type can be returned from functions exported to JS as well as
|
||||||
|
closures in Rust. Only `Result<T, JsValue>` is supported where `T` can be
|
||||||
|
converted to JS. Whenever `Ok(val)` is encountered it's converted to JS and
|
||||||
|
handed off, and whenever `Err(error)` is encountered an exception is thrown in
|
||||||
|
JS with `error`.
|
||||||
|
|
||||||
|
You can use `Result` to enable handling of JS exceptions with `?` in Rust,
|
||||||
|
naturally propagating it upwards to the wasm boundary. Furthermore you can also
|
||||||
|
return custom types in Rust so long as they're all convertible to `JsValue`.
|
||||||
|
|
||||||
|
Note that if you import a JS function with `Result` you need
|
||||||
|
`#[wasm_bindgen(catch)]` to be annotated on the import (unlike exported
|
||||||
|
functions, which require no extra annotation). This may not be necessary in the
|
||||||
|
future though and it may work "as is"!.
|
129
src/closure.rs
129
src/closure.rs
@ -14,7 +14,7 @@ use std::rc::Rc;
|
|||||||
use JsValue;
|
use JsValue;
|
||||||
use convert::*;
|
use convert::*;
|
||||||
use describe::*;
|
use describe::*;
|
||||||
use throw;
|
use throw_str;
|
||||||
|
|
||||||
/// A handle to both a closure in Rust as well as JS closure which will invoke
|
/// A handle to both a closure in Rust as well as JS closure which will invoke
|
||||||
/// the Rust closure.
|
/// the Rust closure.
|
||||||
@ -304,41 +304,9 @@ macro_rules! doit {
|
|||||||
($(
|
($(
|
||||||
($($var:ident)*)
|
($($var:ident)*)
|
||||||
)*) => ($(
|
)*) => ($(
|
||||||
// Fn with no return
|
|
||||||
unsafe impl<$($var),*> WasmClosure for Fn($($var),*)
|
|
||||||
where $($var: FromWasmAbi + 'static,)*
|
|
||||||
{
|
|
||||||
fn describe() {
|
|
||||||
<&Self>::describe();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn invoke_fn() -> u32 {
|
|
||||||
#[allow(non_snake_case)]
|
|
||||||
unsafe extern fn invoke<$($var: FromWasmAbi,)*>(
|
|
||||||
a: *const UnsafeCell<Box<Fn($($var),*)>>,
|
|
||||||
$($var: <$var as FromWasmAbi>::Abi),*
|
|
||||||
) {
|
|
||||||
if a.is_null() {
|
|
||||||
throw("closure invoked recursively or destroyed already");
|
|
||||||
}
|
|
||||||
let a = Rc::from_raw(a);
|
|
||||||
let my_handle = a.clone();
|
|
||||||
drop(Rc::into_raw(a));
|
|
||||||
let f: &Fn($($var),*) = &**my_handle.get();
|
|
||||||
let mut _stack = GlobalStack::new();
|
|
||||||
$(
|
|
||||||
let $var = <$var as FromWasmAbi>::from_abi($var, &mut _stack);
|
|
||||||
)*
|
|
||||||
f($($var),*)
|
|
||||||
}
|
|
||||||
invoke::<$($var,)*> as u32
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fn with no return
|
|
||||||
unsafe impl<$($var,)* R> WasmClosure for Fn($($var),*) -> R
|
unsafe impl<$($var,)* R> WasmClosure for Fn($($var),*) -> R
|
||||||
where $($var: FromWasmAbi + 'static,)*
|
where $($var: FromWasmAbi + 'static,)*
|
||||||
R: IntoWasmAbi + 'static,
|
R: ReturnWasmAbi + 'static,
|
||||||
{
|
{
|
||||||
fn describe() {
|
fn describe() {
|
||||||
<&Self>::describe();
|
<&Self>::describe();
|
||||||
@ -346,61 +314,36 @@ macro_rules! doit {
|
|||||||
|
|
||||||
fn invoke_fn() -> u32 {
|
fn invoke_fn() -> u32 {
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
unsafe extern fn invoke<$($var: FromWasmAbi,)* R: IntoWasmAbi>(
|
unsafe extern fn invoke<$($var: FromWasmAbi,)* R: ReturnWasmAbi>(
|
||||||
a: *const UnsafeCell<Box<Fn($($var),*) -> R>>,
|
a: *const UnsafeCell<Box<Fn($($var),*) -> R>>,
|
||||||
$($var: <$var as FromWasmAbi>::Abi),*
|
$($var: <$var as FromWasmAbi>::Abi),*
|
||||||
) -> <R as IntoWasmAbi>::Abi {
|
) -> <R as ReturnWasmAbi>::Abi {
|
||||||
if a.is_null() {
|
if a.is_null() {
|
||||||
throw("closure invoked recursively or destroyed already");
|
throw_str("closure invoked recursively or destroyed already");
|
||||||
}
|
}
|
||||||
let a = Rc::from_raw(a);
|
// Make sure all stack variables are converted before we
|
||||||
let my_handle = a.clone();
|
// convert `ret` as it may throw (for `Result`, for
|
||||||
drop(Rc::into_raw(a));
|
// example)
|
||||||
let f: &Fn($($var),*) -> R = &**my_handle.get();
|
let ret = {
|
||||||
let mut _stack = GlobalStack::new();
|
let a = Rc::from_raw(a);
|
||||||
$(
|
let my_handle = a.clone();
|
||||||
let $var = <$var as FromWasmAbi>::from_abi($var, &mut _stack);
|
drop(Rc::into_raw(a));
|
||||||
)*
|
let f: &Fn($($var),*) -> R = &**my_handle.get();
|
||||||
f($($var),*).into_abi(&mut GlobalStack::new())
|
let mut _stack = GlobalStack::new();
|
||||||
|
$(
|
||||||
|
let $var = <$var as FromWasmAbi>::from_abi($var, &mut _stack);
|
||||||
|
)*
|
||||||
|
f($($var),*)
|
||||||
|
};
|
||||||
|
ret.return_abi(&mut GlobalStack::new())
|
||||||
}
|
}
|
||||||
invoke::<$($var,)* R> as u32
|
invoke::<$($var,)* R> as u32
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// FnMut with no return
|
|
||||||
unsafe impl<$($var),*> WasmClosure for FnMut($($var),*)
|
|
||||||
where $($var: FromWasmAbi + 'static,)*
|
|
||||||
{
|
|
||||||
fn describe() {
|
|
||||||
<&mut Self>::describe();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn invoke_fn() -> u32 {
|
|
||||||
#[allow(non_snake_case)]
|
|
||||||
unsafe extern fn invoke<$($var: FromWasmAbi,)*>(
|
|
||||||
a: *const UnsafeCell<Box<FnMut($($var),*)>>,
|
|
||||||
$($var: <$var as FromWasmAbi>::Abi),*
|
|
||||||
) {
|
|
||||||
if a.is_null() {
|
|
||||||
throw("closure invoked recursively or destroyed already");
|
|
||||||
}
|
|
||||||
let a = Rc::from_raw(a);
|
|
||||||
let my_handle = a.clone();
|
|
||||||
drop(Rc::into_raw(a));
|
|
||||||
let f: &mut FnMut($($var),*) = &mut **my_handle.get();
|
|
||||||
let mut _stack = GlobalStack::new();
|
|
||||||
$(
|
|
||||||
let $var = <$var as FromWasmAbi>::from_abi($var, &mut _stack);
|
|
||||||
)*
|
|
||||||
f($($var),*)
|
|
||||||
}
|
|
||||||
invoke::<$($var,)*> as u32
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fn with no return
|
|
||||||
unsafe impl<$($var,)* R> WasmClosure for FnMut($($var),*) -> R
|
unsafe impl<$($var,)* R> WasmClosure for FnMut($($var),*) -> R
|
||||||
where $($var: FromWasmAbi + 'static,)*
|
where $($var: FromWasmAbi + 'static,)*
|
||||||
R: IntoWasmAbi + 'static,
|
R: ReturnWasmAbi + 'static,
|
||||||
{
|
{
|
||||||
fn describe() {
|
fn describe() {
|
||||||
<&mut Self>::describe();
|
<&mut Self>::describe();
|
||||||
@ -408,22 +351,28 @@ macro_rules! doit {
|
|||||||
|
|
||||||
fn invoke_fn() -> u32 {
|
fn invoke_fn() -> u32 {
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
unsafe extern fn invoke<$($var: FromWasmAbi,)* R: IntoWasmAbi>(
|
unsafe extern fn invoke<$($var: FromWasmAbi,)* R: ReturnWasmAbi>(
|
||||||
a: *const UnsafeCell<Box<FnMut($($var),*) -> R>>,
|
a: *const UnsafeCell<Box<FnMut($($var),*) -> R>>,
|
||||||
$($var: <$var as FromWasmAbi>::Abi),*
|
$($var: <$var as FromWasmAbi>::Abi),*
|
||||||
) -> <R as IntoWasmAbi>::Abi {
|
) -> <R as ReturnWasmAbi>::Abi {
|
||||||
if a.is_null() {
|
if a.is_null() {
|
||||||
throw("closure invoked recursively or destroyed already");
|
throw_str("closure invoked recursively or destroyed already");
|
||||||
}
|
}
|
||||||
let a = Rc::from_raw(a);
|
// Make sure all stack variables are converted before we
|
||||||
let my_handle = a.clone();
|
// convert `ret` as it may throw (for `Result`, for
|
||||||
drop(Rc::into_raw(a));
|
// example)
|
||||||
let f: &mut FnMut($($var),*) -> R = &mut **my_handle.get();
|
let ret = {
|
||||||
let mut _stack = GlobalStack::new();
|
let a = Rc::from_raw(a);
|
||||||
$(
|
let my_handle = a.clone();
|
||||||
let $var = <$var as FromWasmAbi>::from_abi($var, &mut _stack);
|
drop(Rc::into_raw(a));
|
||||||
)*
|
let f: &mut FnMut($($var),*) -> R = &mut **my_handle.get();
|
||||||
f($($var),*).into_abi(&mut GlobalStack::new())
|
let mut _stack = GlobalStack::new();
|
||||||
|
$(
|
||||||
|
let $var = <$var as FromWasmAbi>::from_abi($var, &mut _stack);
|
||||||
|
)*
|
||||||
|
f($($var),*)
|
||||||
|
};
|
||||||
|
ret.return_abi(&mut GlobalStack::new())
|
||||||
}
|
}
|
||||||
invoke::<$($var,)* R> as u32
|
invoke::<$($var,)* R> as u32
|
||||||
}
|
}
|
||||||
|
@ -1,34 +1,37 @@
|
|||||||
#![allow(const_err)] // FIXME(rust-lang/rust#52603)
|
|
||||||
|
|
||||||
use core::mem;
|
use core::mem;
|
||||||
|
|
||||||
use convert::{FromWasmAbi, IntoWasmAbi, GlobalStack, Stack};
|
use convert::{FromWasmAbi, IntoWasmAbi, GlobalStack, Stack, ReturnWasmAbi};
|
||||||
use throw;
|
use throw_str;
|
||||||
|
|
||||||
macro_rules! stack_closures {
|
macro_rules! stack_closures {
|
||||||
($( ($($var:ident)*) )*) => ($(
|
($( ($($var:ident)*) )*) => ($(
|
||||||
impl<'a, 'b, $($var,)* R> IntoWasmAbi for &'a (Fn($($var),*) -> R + 'b)
|
impl<'a, 'b, $($var,)* R> IntoWasmAbi for &'a (Fn($($var),*) -> R + 'b)
|
||||||
where $($var: FromWasmAbi,)*
|
where $($var: FromWasmAbi,)*
|
||||||
R: IntoWasmAbi
|
R: ReturnWasmAbi
|
||||||
{
|
{
|
||||||
type Abi = u32;
|
type Abi = u32;
|
||||||
|
|
||||||
fn into_abi(self, extra: &mut Stack) -> u32 {
|
fn into_abi(self, extra: &mut Stack) -> u32 {
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
unsafe extern fn invoke<$($var: FromWasmAbi,)* R: IntoWasmAbi>(
|
unsafe extern fn invoke<$($var: FromWasmAbi,)* R: ReturnWasmAbi>(
|
||||||
a: usize,
|
a: usize,
|
||||||
b: usize,
|
b: usize,
|
||||||
$($var: <$var as FromWasmAbi>::Abi),*
|
$($var: <$var as FromWasmAbi>::Abi),*
|
||||||
) -> <R as IntoWasmAbi>::Abi {
|
) -> <R as ReturnWasmAbi>::Abi {
|
||||||
if a == 0 {
|
if a == 0 {
|
||||||
throw("closure invoked recursively or destroyed already");
|
throw_str("closure invoked recursively or destroyed already");
|
||||||
}
|
}
|
||||||
let f: &Fn($($var),*) -> R = mem::transmute((a, b));
|
// Scope all local variables before we call `return_abi` to
|
||||||
let mut _stack = GlobalStack::new();
|
// ensure they're all destroyed as `return_abi` may throw
|
||||||
$(
|
let ret = {
|
||||||
let $var = <$var as FromWasmAbi>::from_abi($var, &mut _stack);
|
let f: &Fn($($var),*) -> R = mem::transmute((a, b));
|
||||||
)*
|
let mut _stack = GlobalStack::new();
|
||||||
f($($var),*).into_abi(&mut GlobalStack::new())
|
$(
|
||||||
|
let $var = <$var as FromWasmAbi>::from_abi($var, &mut _stack);
|
||||||
|
)*
|
||||||
|
f($($var),*)
|
||||||
|
};
|
||||||
|
ret.return_abi(&mut GlobalStack::new())
|
||||||
}
|
}
|
||||||
unsafe {
|
unsafe {
|
||||||
let (a, b): (usize, usize) = mem::transmute(self);
|
let (a, b): (usize, usize) = mem::transmute(self);
|
||||||
@ -39,59 +42,33 @@ macro_rules! stack_closures {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'b, $($var,)*> IntoWasmAbi for &'a (Fn($($var),*) + 'b)
|
|
||||||
where $($var: FromWasmAbi,)*
|
|
||||||
{
|
|
||||||
type Abi = u32;
|
|
||||||
|
|
||||||
fn into_abi(self, extra: &mut Stack) -> u32 {
|
|
||||||
#[allow(non_snake_case)]
|
|
||||||
unsafe extern fn invoke<$($var: FromWasmAbi,)* >(
|
|
||||||
a: usize,
|
|
||||||
b: usize,
|
|
||||||
$($var: <$var as FromWasmAbi>::Abi),*
|
|
||||||
) {
|
|
||||||
if a == 0 {
|
|
||||||
throw("closure invoked recursively or destroyed already");
|
|
||||||
}
|
|
||||||
let f: &Fn($($var),*) = mem::transmute((a, b));
|
|
||||||
let mut _stack = GlobalStack::new();
|
|
||||||
$(
|
|
||||||
let $var = <$var as FromWasmAbi>::from_abi($var, &mut _stack);
|
|
||||||
)*
|
|
||||||
f($($var),*)
|
|
||||||
}
|
|
||||||
unsafe {
|
|
||||||
let (a, b): (usize, usize) = mem::transmute(self);
|
|
||||||
extra.push(a as u32);
|
|
||||||
extra.push(b as u32);
|
|
||||||
invoke::<$($var,)*> as u32
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, 'b, $($var,)* R> IntoWasmAbi for &'a mut (FnMut($($var),*) -> R + 'b)
|
impl<'a, 'b, $($var,)* R> IntoWasmAbi for &'a mut (FnMut($($var),*) -> R + 'b)
|
||||||
where $($var: FromWasmAbi,)*
|
where $($var: FromWasmAbi,)*
|
||||||
R: IntoWasmAbi
|
R: ReturnWasmAbi
|
||||||
{
|
{
|
||||||
type Abi = u32;
|
type Abi = u32;
|
||||||
|
|
||||||
fn into_abi(self, extra: &mut Stack) -> u32 {
|
fn into_abi(self, extra: &mut Stack) -> u32 {
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
unsafe extern fn invoke<$($var: FromWasmAbi,)* R: IntoWasmAbi>(
|
unsafe extern fn invoke<$($var: FromWasmAbi,)* R: ReturnWasmAbi>(
|
||||||
a: usize,
|
a: usize,
|
||||||
b: usize,
|
b: usize,
|
||||||
$($var: <$var as FromWasmAbi>::Abi),*
|
$($var: <$var as FromWasmAbi>::Abi),*
|
||||||
) -> <R as IntoWasmAbi>::Abi {
|
) -> <R as ReturnWasmAbi>::Abi {
|
||||||
if a == 0 {
|
if a == 0 {
|
||||||
throw("closure invoked recursively or destroyed already");
|
throw_str("closure invoked recursively or destroyed already");
|
||||||
}
|
}
|
||||||
let f: &mut FnMut($($var),*) -> R = mem::transmute((a, b));
|
// Scope all local variables before we call `return_abi` to
|
||||||
let mut _stack = GlobalStack::new();
|
// ensure they're all destroyed as `return_abi` may throw
|
||||||
$(
|
let ret = {
|
||||||
let $var = <$var as FromWasmAbi>::from_abi($var, &mut _stack);
|
let f: &mut FnMut($($var),*) -> R = mem::transmute((a, b));
|
||||||
)*
|
let mut _stack = GlobalStack::new();
|
||||||
f($($var),*).into_abi(&mut GlobalStack::new())
|
$(
|
||||||
|
let $var = <$var as FromWasmAbi>::from_abi($var, &mut _stack);
|
||||||
|
)*
|
||||||
|
f($($var),*)
|
||||||
|
};
|
||||||
|
ret.return_abi(&mut GlobalStack::new())
|
||||||
}
|
}
|
||||||
unsafe {
|
unsafe {
|
||||||
let (a, b): (usize, usize) = mem::transmute(self);
|
let (a, b): (usize, usize) = mem::transmute(self);
|
||||||
@ -101,37 +78,6 @@ macro_rules! stack_closures {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'b, $($var,)*> IntoWasmAbi for &'a mut (FnMut($($var),*) + 'b)
|
|
||||||
where $($var: FromWasmAbi,)*
|
|
||||||
{
|
|
||||||
type Abi = u32;
|
|
||||||
|
|
||||||
fn into_abi(self, extra: &mut Stack) -> u32 {
|
|
||||||
#[allow(non_snake_case)]
|
|
||||||
unsafe extern fn invoke<$($var: FromWasmAbi,)* >(
|
|
||||||
a: usize,
|
|
||||||
b: usize,
|
|
||||||
$($var: <$var as FromWasmAbi>::Abi),*
|
|
||||||
) {
|
|
||||||
if a == 0 {
|
|
||||||
throw("closure invoked recursively or destroyed already");
|
|
||||||
}
|
|
||||||
let f: &mut FnMut($($var),*) = mem::transmute((a, b));
|
|
||||||
let mut _stack = GlobalStack::new();
|
|
||||||
$(
|
|
||||||
let $var = <$var as FromWasmAbi>::from_abi($var, &mut _stack);
|
|
||||||
)*
|
|
||||||
f($($var),*)
|
|
||||||
}
|
|
||||||
unsafe {
|
|
||||||
let (a, b): (usize, usize) = mem::transmute(self);
|
|
||||||
extra.push(a as u32);
|
|
||||||
extra.push(b as u32);
|
|
||||||
invoke::<$($var,)*> as u32
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)*)
|
)*)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,10 +2,12 @@ use core::char;
|
|||||||
use core::mem::{self, ManuallyDrop};
|
use core::mem::{self, ManuallyDrop};
|
||||||
|
|
||||||
use convert::{Stack, FromWasmAbi, IntoWasmAbi, RefFromWasmAbi};
|
use convert::{Stack, FromWasmAbi, IntoWasmAbi, RefFromWasmAbi};
|
||||||
use convert::{OptionIntoWasmAbi, OptionFromWasmAbi};
|
use convert::{OptionIntoWasmAbi, OptionFromWasmAbi, ReturnWasmAbi};
|
||||||
use convert::traits::WasmAbi;
|
use convert::traits::WasmAbi;
|
||||||
use JsValue;
|
use JsValue;
|
||||||
|
|
||||||
|
unsafe impl WasmAbi for () {}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct WasmOptionalI32 {
|
pub struct WasmOptionalI32 {
|
||||||
pub present: u32,
|
pub present: u32,
|
||||||
@ -370,3 +372,22 @@ impl<T: OptionFromWasmAbi> FromWasmAbi for Option<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl IntoWasmAbi for () {
|
||||||
|
type Abi = ();
|
||||||
|
|
||||||
|
fn into_abi(self, _extra: &mut Stack) -> () {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: IntoWasmAbi> ReturnWasmAbi for Result<T, JsValue> {
|
||||||
|
type Abi = T::Abi;
|
||||||
|
|
||||||
|
fn return_abi(self, extra: &mut Stack) -> Self::Abi {
|
||||||
|
match self {
|
||||||
|
Ok(v) => v.into_abi(extra),
|
||||||
|
Err(e) => ::throw_val(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -106,3 +106,26 @@ unsafe impl WasmAbi for u32 {}
|
|||||||
unsafe impl WasmAbi for i32 {}
|
unsafe impl WasmAbi for i32 {}
|
||||||
unsafe impl WasmAbi for f32 {}
|
unsafe impl WasmAbi for f32 {}
|
||||||
unsafe impl WasmAbi for f64 {}
|
unsafe impl WasmAbi for f64 {}
|
||||||
|
|
||||||
|
/// A trait representing how to interepret the return value of a function for
|
||||||
|
/// the wasm ABI.
|
||||||
|
///
|
||||||
|
/// This is very similar to the `IntoWasmAbi` trait and in fact has a blanket
|
||||||
|
/// implementation for all implementors of the `IntoWasmAbi`. The primary use
|
||||||
|
/// case of this trait is to enable functions to return `Result`, interpreting
|
||||||
|
/// an error as "rethrow this to JS"
|
||||||
|
pub trait ReturnWasmAbi: WasmDescribe {
|
||||||
|
/// Same as `IntoWasmAbi::Abi`
|
||||||
|
type Abi: WasmAbi;
|
||||||
|
|
||||||
|
/// Same as `IntoWasmAbi::into_abi`, except that it may throw and never
|
||||||
|
/// return in the case of `Err`.
|
||||||
|
fn return_abi(self, extra: &mut Stack) -> Self::Abi;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: IntoWasmAbi> ReturnWasmAbi for T {
|
||||||
|
type Abi = T::Abi;
|
||||||
|
fn return_abi(self, extra: &mut Stack) -> Self::Abi {
|
||||||
|
self.into_abi(extra)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -39,6 +39,7 @@ tys! {
|
|||||||
RUST_STRUCT
|
RUST_STRUCT
|
||||||
CHAR
|
CHAR
|
||||||
OPTIONAL
|
OPTIONAL
|
||||||
|
UNIT
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)] // see `interpret.rs` in the the cli-support crate
|
#[inline(always)] // see `interpret.rs` in the the cli-support crate
|
||||||
@ -152,22 +153,10 @@ macro_rules! doit {
|
|||||||
inform(FUNCTION);
|
inform(FUNCTION);
|
||||||
inform(cnt!($($var)*));
|
inform(cnt!($($var)*));
|
||||||
$(<$var as WasmDescribe>::describe();)*
|
$(<$var as WasmDescribe>::describe();)*
|
||||||
inform(1);
|
|
||||||
<R as WasmDescribe>::describe();
|
<R as WasmDescribe>::describe();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, $($var,)* > WasmDescribe for Fn($($var),*) + 'a
|
|
||||||
where $($var: WasmDescribe,)*
|
|
||||||
{
|
|
||||||
fn describe() {
|
|
||||||
inform(FUNCTION);
|
|
||||||
inform(cnt!($($var)*));
|
|
||||||
$(<$var as WasmDescribe>::describe();)*
|
|
||||||
inform(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, $($var,)* R> WasmDescribe for FnMut($($var),*) -> R + 'a
|
impl<'a, $($var,)* R> WasmDescribe for FnMut($($var),*) -> R + 'a
|
||||||
where $($var: WasmDescribe,)*
|
where $($var: WasmDescribe,)*
|
||||||
R: WasmDescribe
|
R: WasmDescribe
|
||||||
@ -176,21 +165,9 @@ macro_rules! doit {
|
|||||||
inform(FUNCTION);
|
inform(FUNCTION);
|
||||||
inform(cnt!($($var)*));
|
inform(cnt!($($var)*));
|
||||||
$(<$var as WasmDescribe>::describe();)*
|
$(<$var as WasmDescribe>::describe();)*
|
||||||
inform(1);
|
|
||||||
<R as WasmDescribe>::describe();
|
<R as WasmDescribe>::describe();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, $($var,)* > WasmDescribe for FnMut($($var),*) + 'a
|
|
||||||
where $($var: WasmDescribe,)*
|
|
||||||
{
|
|
||||||
fn describe() {
|
|
||||||
inform(FUNCTION);
|
|
||||||
inform(cnt!($($var)*));
|
|
||||||
$(<$var as WasmDescribe>::describe();)*
|
|
||||||
inform(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)*)
|
)*)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -211,3 +188,17 @@ impl<T: WasmDescribe> WasmDescribe for Option<T> {
|
|||||||
T::describe();
|
T::describe();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl WasmDescribe for () {
|
||||||
|
fn describe() {
|
||||||
|
inform(UNIT)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note that this is only for `ReturnWasmAbi for Result<T, JsValue>`, which
|
||||||
|
// throws the result, so we only need to inform about the `T`.
|
||||||
|
impl<T: WasmDescribe> WasmDescribe for Result<T, JsValue> {
|
||||||
|
fn describe() {
|
||||||
|
T::describe()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
44
src/lib.rs
44
src/lib.rs
@ -18,6 +18,7 @@ extern crate wasm_bindgen_macro;
|
|||||||
|
|
||||||
use core::cell::UnsafeCell;
|
use core::cell::UnsafeCell;
|
||||||
use core::fmt;
|
use core::fmt;
|
||||||
|
use core::mem;
|
||||||
use core::ops::Deref;
|
use core::ops::Deref;
|
||||||
use core::ptr;
|
use core::ptr;
|
||||||
|
|
||||||
@ -428,6 +429,7 @@ externs! {
|
|||||||
fn __wbindgen_is_string(idx: u32) -> u32;
|
fn __wbindgen_is_string(idx: u32) -> u32;
|
||||||
fn __wbindgen_string_get(idx: u32, len: *mut usize) -> *mut u8;
|
fn __wbindgen_string_get(idx: u32, len: *mut usize) -> *mut u8;
|
||||||
fn __wbindgen_throw(a: *const u8, b: usize) -> !;
|
fn __wbindgen_throw(a: *const u8, b: usize) -> !;
|
||||||
|
fn __wbindgen_rethrow(a: u32) -> !;
|
||||||
|
|
||||||
fn __wbindgen_cb_drop(idx: u32) -> ();
|
fn __wbindgen_cb_drop(idx: u32) -> ();
|
||||||
fn __wbindgen_cb_forget(idx: u32) -> ();
|
fn __wbindgen_cb_forget(idx: u32) -> ();
|
||||||
@ -552,19 +554,53 @@ impl<T: FromWasmAbi + 'static> Deref for JsStatic<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cold]
|
||||||
|
#[inline(never)]
|
||||||
|
#[deprecated(note = "renamed to `throw_str`")]
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub fn throw(s: &str) -> ! {
|
||||||
|
throw_str(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Throws a JS exception.
|
/// Throws a JS exception.
|
||||||
///
|
///
|
||||||
/// This function will throw a JS exception with the message provided. The
|
/// This function will throw a JS exception with the message provided. The
|
||||||
/// function will not return as the wasm stack will be popped when the exception
|
/// function will not return as the wasm stack will be popped when the exception
|
||||||
/// is thrown.
|
/// is thrown.
|
||||||
|
///
|
||||||
|
/// Note that it is very easy to leak memory with this function because this
|
||||||
|
/// function, unlike `panic!` on other platforms, **will not run destructors**.
|
||||||
|
/// It's recommended to return a `Result` where possible to avoid the worry of
|
||||||
|
/// leaks.
|
||||||
#[cold]
|
#[cold]
|
||||||
#[inline(never)]
|
#[inline(never)]
|
||||||
pub fn throw(s: &str) -> ! {
|
pub fn throw_str(s: &str) -> ! {
|
||||||
unsafe {
|
unsafe {
|
||||||
__wbindgen_throw(s.as_ptr(), s.len());
|
__wbindgen_throw(s.as_ptr(), s.len());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Rethrow a JS exception
|
||||||
|
///
|
||||||
|
/// This function will throw a JS exception with the JS value provided. This
|
||||||
|
/// function will not return and the wasm stack will be popped until the point
|
||||||
|
/// of entry of wasm itself.
|
||||||
|
///
|
||||||
|
/// Note that it is very easy to leak memory with this function because this
|
||||||
|
/// function, unlike `panic!` on other platforms, **will not run destructors**.
|
||||||
|
/// It's recommended to return a `Result` where possible to avoid the worry of
|
||||||
|
/// leaks.
|
||||||
|
#[cold]
|
||||||
|
#[inline(never)]
|
||||||
|
pub fn throw_val(s: JsValue) -> ! {
|
||||||
|
unsafe {
|
||||||
|
let idx = s.idx;
|
||||||
|
mem::forget(s);
|
||||||
|
__wbindgen_rethrow(idx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns a handle to this wasm instance's `WebAssembly.Memory`
|
/// Returns a handle to this wasm instance's `WebAssembly.Memory`
|
||||||
pub fn memory() -> JsValue {
|
pub fn memory() -> JsValue {
|
||||||
unsafe {
|
unsafe {
|
||||||
@ -604,7 +640,7 @@ pub mod __rt {
|
|||||||
#[cold]
|
#[cold]
|
||||||
#[inline(never)]
|
#[inline(never)]
|
||||||
fn throw_null() -> ! {
|
fn throw_null() -> ! {
|
||||||
super::throw("null pointer passed to rust");
|
super::throw_str("null pointer passed to rust");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A vendored version of `RefCell` from the standard library.
|
/// A vendored version of `RefCell` from the standard library.
|
||||||
@ -726,7 +762,7 @@ pub mod __rt {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn borrow_fail() -> ! {
|
fn borrow_fail() -> ! {
|
||||||
super::throw(
|
super::throw_str(
|
||||||
"recursive use of an object detected which would lead to \
|
"recursive use of an object detected which would lead to \
|
||||||
unsafe aliasing in rust",
|
unsafe aliasing in rust",
|
||||||
);
|
);
|
||||||
@ -748,7 +784,7 @@ pub mod __rt {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
super::throw("invalid malloc request");
|
super::throw_str("invalid malloc request");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
|
@ -26,6 +26,7 @@ pub mod math;
|
|||||||
pub mod node;
|
pub mod node;
|
||||||
pub mod option;
|
pub mod option;
|
||||||
pub mod optional_primitives;
|
pub mod optional_primitives;
|
||||||
|
pub mod rethrow;
|
||||||
pub mod simple;
|
pub mod simple;
|
||||||
pub mod slice;
|
pub mod slice;
|
||||||
pub mod structural;
|
pub mod structural;
|
||||||
|
14
tests/wasm/rethrow.js
Normal file
14
tests/wasm/rethrow.js
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
const wasm = require('wasm-bindgen-test.js');
|
||||||
|
const assert = require('assert');
|
||||||
|
|
||||||
|
exports.call_throw_one = function() {
|
||||||
|
try {
|
||||||
|
wasm.throw_one();
|
||||||
|
} catch (e) {
|
||||||
|
assert.strictEqual(e, 1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.call_ok = function() {
|
||||||
|
wasm.nothrow();
|
||||||
|
};
|
28
tests/wasm/rethrow.rs
Normal file
28
tests/wasm/rethrow.rs
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
use wasm_bindgen_test::*;
|
||||||
|
use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
|
#[wasm_bindgen(module = "tests/wasm/rethrow.js")]
|
||||||
|
extern {
|
||||||
|
fn call_throw_one();
|
||||||
|
fn call_ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen_test]
|
||||||
|
fn err_works() {
|
||||||
|
call_throw_one();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn throw_one() -> Result<u32, JsValue> {
|
||||||
|
Err(1.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen_test]
|
||||||
|
fn ok_works() {
|
||||||
|
call_ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn nothrow() -> Result<u32, JsValue> {
|
||||||
|
Ok(1)
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user