mirror of
https://github.com/fluencelabs/wasm-bindgen
synced 2025-03-16 02:00:51 +00:00
Merge pull request #1275 from alexcrichton/option-classes
Support `Option<RustStruct>` in arguments/returns
This commit is contained in:
commit
32b72a3ae0
@ -248,6 +248,17 @@ impl ToTokens for ast::Struct {
|
||||
(*js).borrow_mut()
|
||||
}
|
||||
}
|
||||
|
||||
impl ::wasm_bindgen::convert::OptionIntoWasmAbi for #name {
|
||||
#[inline]
|
||||
fn none() -> Self::Abi { 0 }
|
||||
}
|
||||
|
||||
impl ::wasm_bindgen::convert::OptionFromWasmAbi for #name {
|
||||
#[inline]
|
||||
fn is_none(abi: &Self::Abi) -> bool { *abi == 0 }
|
||||
}
|
||||
|
||||
})
|
||||
.to_tokens(tokens);
|
||||
|
||||
|
@ -313,6 +313,18 @@ impl<'a, 'b> Js2Rust<'a, 'b> {
|
||||
.push(format!("isLikeNone({0}) ? {1} : {0}", name, hole));
|
||||
return Ok(self);
|
||||
}
|
||||
Descriptor::RustStruct(ref s) => {
|
||||
self.js_arguments.push((name.clone(), format!("{} | undefined", s)));
|
||||
self.prelude(&format!("let ptr{} = 0;", i));
|
||||
self.prelude(&format!("if ({0} !== null && {0} !== undefined) {{", name));
|
||||
self.assert_class(&name, s);
|
||||
self.assert_not_moved(&name);
|
||||
self.prelude(&format!("ptr{} = {}.ptr;", i, name));
|
||||
self.prelude(&format!("{}.ptr = 0;", name));
|
||||
self.prelude("}");
|
||||
self.rust_arguments.push(format!("ptr{}", i));
|
||||
return Ok(self);
|
||||
}
|
||||
_ => bail!(
|
||||
"unsupported optional argument type for calling Rust function from JS: {:?}",
|
||||
arg
|
||||
@ -322,44 +334,13 @@ impl<'a, 'b> Js2Rust<'a, 'b> {
|
||||
|
||||
if let Some(s) = arg.rust_struct() {
|
||||
self.js_arguments.push((name.clone(), s.to_string()));
|
||||
|
||||
if self.cx.config.debug {
|
||||
self.cx.expose_assert_class();
|
||||
self.prelude(&format!(
|
||||
"\
|
||||
_assertClass({arg}, {struct_});\n\
|
||||
",
|
||||
arg = name,
|
||||
struct_ = s
|
||||
));
|
||||
}
|
||||
|
||||
self.assert_class(&name, s);
|
||||
self.assert_not_moved(&name);
|
||||
if arg.is_by_ref() {
|
||||
self.rust_arguments.push(format!("{}.ptr", name));
|
||||
} else {
|
||||
self.prelude(&format!(
|
||||
"\
|
||||
const ptr{i} = {arg}.ptr;\n\
|
||||
",
|
||||
i = i,
|
||||
arg = name
|
||||
));
|
||||
if self.cx.config.debug {
|
||||
self.prelude(&format!(
|
||||
"\
|
||||
if (ptr{i} === 0) {{
|
||||
throw new Error('Attempt to use a moved value');
|
||||
}}
|
||||
",
|
||||
i = i,
|
||||
));
|
||||
}
|
||||
self.prelude(&format!(
|
||||
"\
|
||||
{arg}.ptr = 0;\n\
|
||||
",
|
||||
arg = name
|
||||
));
|
||||
self.prelude(&format!("const ptr{} = {}.ptr;", i, name));
|
||||
self.prelude(&format!("{}.ptr = 0;", name));
|
||||
self.rust_arguments.push(format!("ptr{}", i));
|
||||
}
|
||||
return Ok(self);
|
||||
@ -627,6 +608,17 @@ impl<'a, 'b> Js2Rust<'a, 'b> {
|
||||
);
|
||||
return Ok(self);
|
||||
}
|
||||
Descriptor::RustStruct(ref name) => {
|
||||
self.ret_ty = format!("{} | undefined", name);
|
||||
self.cx.require_class_wrap(name);
|
||||
self.ret_expr = format!("
|
||||
const ptr = RET;
|
||||
return ptr === 0 ? undefined : {}.__wrap(ptr);
|
||||
",
|
||||
name,
|
||||
);
|
||||
return Ok(self);
|
||||
}
|
||||
_ => bail!(
|
||||
"unsupported optional return type for calling Rust function from JS: {:?}",
|
||||
ty
|
||||
@ -764,4 +756,26 @@ impl<'a, 'b> Js2Rust<'a, 'b> {
|
||||
ts.push(';');
|
||||
(js, ts, self.js_doc_comments())
|
||||
}
|
||||
|
||||
fn assert_class(&mut self, arg: &str, class: &str) {
|
||||
if !self.cx.config.debug {
|
||||
return
|
||||
}
|
||||
self.cx.expose_assert_class();
|
||||
self.prelude(&format!("_assertClass({}, {});", arg, class));
|
||||
}
|
||||
|
||||
fn assert_not_moved(&mut self, arg: &str) {
|
||||
if !self.cx.config.debug {
|
||||
return
|
||||
}
|
||||
self.prelude(&format!(
|
||||
"\
|
||||
if ({0}.ptr === 0) {{
|
||||
throw new Error('Attempt to use a moved value');
|
||||
}}
|
||||
",
|
||||
arg,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
@ -213,6 +213,13 @@ impl<'a, 'b> Rust2Js<'a, 'b> {
|
||||
));
|
||||
return Ok(());
|
||||
}
|
||||
Descriptor::RustStruct(ref class) => {
|
||||
self.cx.require_class_wrap(class);
|
||||
let assign = format!("let c{0} = {0} === 0 ? undefined : {1}.__wrap({0});", abi, class);
|
||||
self.prelude(&assign);
|
||||
self.js_arguments.push(format!("c{}", abi));
|
||||
return Ok(());
|
||||
}
|
||||
_ => bail!(
|
||||
"unsupported optional argument type for calling JS function from Rust: {:?}",
|
||||
arg
|
||||
@ -456,6 +463,24 @@ impl<'a, 'b> Rust2Js<'a, 'b> {
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
Descriptor::RustStruct(ref class) => {
|
||||
// Like below, assert the type
|
||||
self.ret_expr = format!(
|
||||
"\
|
||||
const val = JS;
|
||||
if (val === undefined || val === null)
|
||||
return 0;
|
||||
if (!(val instanceof {0})) {{
|
||||
throw new Error('expected value of type {0}');
|
||||
}}
|
||||
const ret = val.ptr;
|
||||
val.ptr = 0;
|
||||
return ret;\
|
||||
",
|
||||
class
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
_ => bail!(
|
||||
"unsupported optional return type for calling JS function from Rust: {:?}",
|
||||
ty
|
||||
|
@ -148,3 +148,22 @@ exports.js_conditional_bindings = () => {
|
||||
const x = new wasm.ConditionalBindings();
|
||||
x.free();
|
||||
};
|
||||
|
||||
exports.js_assert_none = x => {
|
||||
assert.strictEqual(x, undefined);
|
||||
};
|
||||
exports.js_assert_some = x => {
|
||||
assert.ok(x instanceof wasm.OptionClass);
|
||||
};
|
||||
exports.js_return_none1 = () => null;
|
||||
exports.js_return_none2 = () => undefined;
|
||||
exports.js_return_some = x => x;
|
||||
|
||||
exports.js_test_option_classes = () => {
|
||||
assert.strictEqual(wasm.option_class_none(), undefined);
|
||||
wasm.option_class_assert_none(undefined);
|
||||
wasm.option_class_assert_none(null);
|
||||
const c = wasm.option_class_some();
|
||||
assert.ok(c instanceof wasm.OptionClass);
|
||||
wasm.option_class_assert_some(c);
|
||||
};
|
||||
|
@ -23,6 +23,13 @@ extern "C" {
|
||||
fn js_access_fields();
|
||||
fn js_renamed_export();
|
||||
fn js_conditional_bindings();
|
||||
|
||||
fn js_assert_none(a: Option<OptionClass>);
|
||||
fn js_assert_some(a: Option<OptionClass>);
|
||||
fn js_return_none1() -> Option<OptionClass>;
|
||||
fn js_return_none2() -> Option<OptionClass>;
|
||||
fn js_return_some(a: OptionClass) -> Option<OptionClass>;
|
||||
fn js_test_option_classes();
|
||||
}
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
@ -414,7 +421,41 @@ impl ConditionalBindings {
|
||||
ConditionalBindings {}
|
||||
}
|
||||
}
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn conditional_bindings() {
|
||||
js_conditional_bindings();
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub struct OptionClass(u32);
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn option_class() {
|
||||
js_assert_none(None);
|
||||
js_assert_some(Some(OptionClass(1)));
|
||||
assert!(js_return_none1().is_none());
|
||||
assert!(js_return_none2().is_none());
|
||||
assert_eq!(js_return_some(OptionClass(2)).unwrap().0, 2);
|
||||
js_test_option_classes();
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn option_class_none() -> Option<OptionClass> {
|
||||
None
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn option_class_some() -> Option<OptionClass> {
|
||||
Some(OptionClass(3))
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn option_class_assert_none(x: Option<OptionClass>) {
|
||||
assert!(x.is_none());
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn option_class_assert_some(x: Option<OptionClass>) {
|
||||
assert_eq!(x.unwrap().0, 3);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user