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

@ -34,6 +34,7 @@ tys! {
RUST_STRUCT
CHAR
OPTIONAL
UNIT
}
#[derive(Debug)]
@ -61,12 +62,13 @@ pub enum Descriptor {
RustStruct(String),
Char,
Option(Box<Descriptor>),
Unit,
}
#[derive(Debug)]
pub struct Function {
pub arguments: Vec<Descriptor>,
pub ret: Option<Descriptor>,
pub ret: Descriptor,
}
#[derive(Debug)]
@ -128,6 +130,7 @@ impl Descriptor {
Descriptor::RustStruct(name)
}
CHAR => Descriptor::Char,
UNIT => Descriptor::Unit,
other => panic!("unknown descriptor: {}", other),
}
}
@ -295,12 +298,10 @@ impl Function {
let arguments = (0..get(data))
.map(|_| Descriptor::_decode(data))
.collect::<Vec<_>>();
let ret = if get(data) == 0 {
None
} else {
Some(Descriptor::_decode(data))
};
Function { arguments, ret }
Function {
arguments,
ret: Descriptor::_decode(data),
}
}
}

View File

@ -390,15 +390,12 @@ impl<'a, 'b> Js2Rust<'a, 'b> {
Ok(self)
}
pub fn ret(&mut self, ret: &Option<Descriptor>) -> Result<&mut Self, Error> {
let ty = match *ret {
Some(ref t) => t,
None => {
self.ret_ty = "void".to_string();
self.ret_expr = format!("return RET;");
return Ok(self);
}
};
pub fn ret(&mut self, ty: &Descriptor) -> Result<&mut Self, Error> {
if let Descriptor::Unit = ty {
self.ret_ty = "void".to_string();
self.ret_expr = format!("return RET;");
return Ok(self);
}
let (ty, optional) = match ty {
Descriptor::Option(t) => (&**t, true),

View File

@ -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.unexport_unused_internal_exports();
closures::rewrite(self)?;
@ -626,7 +631,9 @@ impl<'a> Context<'a> {
let set = {
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!(
"{}{}: {}\n",
if field.readonly { "readonly " } else { "" },
@ -637,7 +644,7 @@ impl<'a> Context<'a> {
};
let (get, _ts, js_doc) = Js2Rust::new(&field.name, self)
.method(true, false)
.ret(&Some(descriptor))?
.ret(&descriptor)?
.finish("", &format!("wasm.{}", wasm_getter));
if !dst.ends_with("\n") {
dst.push_str("\n");

View File

@ -285,14 +285,11 @@ impl<'a, 'b> Rust2Js<'a, 'b> {
Ok(())
}
fn ret(&mut self, ret: &Option<Descriptor>) -> Result<(), Error> {
let ty = match *ret {
Some(ref t) => t,
None => {
self.ret_expr = "JS;".to_string();
return Ok(());
}
};
fn ret(&mut self, ty: &Descriptor) -> Result<(), Error> {
if let Descriptor::Unit = ty {
self.ret_expr = "JS;".to_string();
return Ok(());
}
let (ty, optional) = match ty {
Descriptor::Option(t) => (&**t, true),
_ => (ty, false),