mirror of
https://github.com/fluencelabs/wasm-bindgen
synced 2025-05-07 11:32:16 +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()
|
(*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);
|
.to_tokens(tokens);
|
||||||
|
|
||||||
|
@ -313,6 +313,18 @@ impl<'a, 'b> Js2Rust<'a, 'b> {
|
|||||||
.push(format!("isLikeNone({0}) ? {1} : {0}", name, hole));
|
.push(format!("isLikeNone({0}) ? {1} : {0}", name, hole));
|
||||||
return Ok(self);
|
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!(
|
_ => bail!(
|
||||||
"unsupported optional argument type for calling Rust function from JS: {:?}",
|
"unsupported optional argument type for calling Rust function from JS: {:?}",
|
||||||
arg
|
arg
|
||||||
@ -322,44 +334,13 @@ impl<'a, 'b> Js2Rust<'a, 'b> {
|
|||||||
|
|
||||||
if let Some(s) = arg.rust_struct() {
|
if let Some(s) = arg.rust_struct() {
|
||||||
self.js_arguments.push((name.clone(), s.to_string()));
|
self.js_arguments.push((name.clone(), s.to_string()));
|
||||||
|
self.assert_class(&name, s);
|
||||||
if self.cx.config.debug {
|
self.assert_not_moved(&name);
|
||||||
self.cx.expose_assert_class();
|
|
||||||
self.prelude(&format!(
|
|
||||||
"\
|
|
||||||
_assertClass({arg}, {struct_});\n\
|
|
||||||
",
|
|
||||||
arg = name,
|
|
||||||
struct_ = s
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
if arg.is_by_ref() {
|
if arg.is_by_ref() {
|
||||||
self.rust_arguments.push(format!("{}.ptr", name));
|
self.rust_arguments.push(format!("{}.ptr", name));
|
||||||
} else {
|
} else {
|
||||||
self.prelude(&format!(
|
self.prelude(&format!("const ptr{} = {}.ptr;", i, name));
|
||||||
"\
|
self.prelude(&format!("{}.ptr = 0;", name));
|
||||||
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.rust_arguments.push(format!("ptr{}", i));
|
self.rust_arguments.push(format!("ptr{}", i));
|
||||||
}
|
}
|
||||||
return Ok(self);
|
return Ok(self);
|
||||||
@ -627,6 +608,17 @@ impl<'a, 'b> Js2Rust<'a, 'b> {
|
|||||||
);
|
);
|
||||||
return Ok(self);
|
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!(
|
_ => bail!(
|
||||||
"unsupported optional return type for calling Rust function from JS: {:?}",
|
"unsupported optional return type for calling Rust function from JS: {:?}",
|
||||||
ty
|
ty
|
||||||
@ -764,4 +756,26 @@ impl<'a, 'b> Js2Rust<'a, 'b> {
|
|||||||
ts.push(';');
|
ts.push(';');
|
||||||
(js, ts, self.js_doc_comments())
|
(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(());
|
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!(
|
_ => bail!(
|
||||||
"unsupported optional argument type for calling JS function from Rust: {:?}",
|
"unsupported optional argument type for calling JS function from Rust: {:?}",
|
||||||
arg
|
arg
|
||||||
@ -456,6 +463,24 @@ impl<'a, 'b> Rust2Js<'a, 'b> {
|
|||||||
);
|
);
|
||||||
return Ok(());
|
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!(
|
_ => bail!(
|
||||||
"unsupported optional return type for calling JS function from Rust: {:?}",
|
"unsupported optional return type for calling JS function from Rust: {:?}",
|
||||||
ty
|
ty
|
||||||
|
@ -148,3 +148,22 @@ exports.js_conditional_bindings = () => {
|
|||||||
const x = new wasm.ConditionalBindings();
|
const x = new wasm.ConditionalBindings();
|
||||||
x.free();
|
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_access_fields();
|
||||||
fn js_renamed_export();
|
fn js_renamed_export();
|
||||||
fn js_conditional_bindings();
|
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]
|
#[wasm_bindgen_test]
|
||||||
@ -414,7 +421,41 @@ impl ConditionalBindings {
|
|||||||
ConditionalBindings {}
|
ConditionalBindings {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[wasm_bindgen_test]
|
#[wasm_bindgen_test]
|
||||||
fn conditional_bindings() {
|
fn conditional_bindings() {
|
||||||
js_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