mirror of
https://github.com/fluencelabs/wasm-bindgen
synced 2025-04-01 18:01:06 +00:00
Add support for optional slice types (#507)
* Shard the `convert.rs` module into sub-modules Hopefully this'll make the organization a little nicer over time! * Start adding support for optional types This commit starts adding support for optional types to wasm-bindgen as arguments/return values to functions. The strategy here is to add two new traits, `OptionIntoWasmAbi` and `OptionFromWasmAbi`. These two traits are used as a blanket impl to implement `IntoWasmAbi` and `FromWasmAbi` for `Option<T>`. Some consequences of this design: * It should be possible to ensure `Option<SomeForeignType>` implements to/from wasm traits. This is because the option-based traits can be implemented for foreign types. * A specialized implementation is possible for all types, so there's no need for `Option<T>` to introduce unnecessary overhead. * Two new traits is a bit unforutnate but I can't currently think of an alternative design that works for the above two constraints, although it doesn't mean one doesn't exist! * The error messages for "can't use this type here" is actually halfway decent because it says these new traits need to be implemented, which provides a good place to document and talk about what's going on here! * Nested references like `Option<&T>` can't implement `FromWasmAbi`. This means that you can't define a function in Rust which takes `Option<&str>`. It may be possible to do this one day but it'll likely require more trait trickery than I'm capable of right now. * Add support for optional slices This commit adds support for optional slice types, things like strings and arrays. The null representation of these has a pointer value of 0, which should never happen in normal Rust. Otherwise the various plumbing is done throughout the tooling to enable these types in all locations. * Fix `takeObject` on global sentinels These don't have a reference count as they're always expected to work, so avoid actually dropping a reference on them. * Remove some no longer needed bindings * Add support for optional anyref types This commit adds support for optional imported class types. Each type imported with `#[wasm_bindgen]` automatically implements the relevant traits and now supports `Option<Foo>` in various argument/return positions. * Fix building without the `std` feature * Actually fix the build... * Add support for optional types to WebIDL Closes #502
This commit is contained in:
parent
6eef5f7b52
commit
cbeb301371
@ -513,6 +513,10 @@ impl ToTokens for ast::ImportType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ::wasm_bindgen::convert::OptionIntoWasmAbi for #name {
|
||||||
|
fn none() -> Self::Abi { 0 }
|
||||||
|
}
|
||||||
|
|
||||||
impl ::wasm_bindgen::convert::FromWasmAbi for #name {
|
impl ::wasm_bindgen::convert::FromWasmAbi for #name {
|
||||||
type Abi = <::wasm_bindgen::JsValue as
|
type Abi = <::wasm_bindgen::JsValue as
|
||||||
::wasm_bindgen::convert::FromWasmAbi>::Abi;
|
::wasm_bindgen::convert::FromWasmAbi>::Abi;
|
||||||
@ -527,6 +531,10 @@ impl ToTokens for ast::ImportType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ::wasm_bindgen::convert::OptionFromWasmAbi for #name {
|
||||||
|
fn is_none(abi: &Self::Abi) -> bool { *abi == 0 }
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a> ::wasm_bindgen::convert::IntoWasmAbi for &'a #name {
|
impl<'a> ::wasm_bindgen::convert::IntoWasmAbi for &'a #name {
|
||||||
type Abi = <&'a ::wasm_bindgen::JsValue as
|
type Abi = <&'a ::wasm_bindgen::JsValue as
|
||||||
::wasm_bindgen::convert::IntoWasmAbi>::Abi;
|
::wasm_bindgen::convert::IntoWasmAbi>::Abi;
|
||||||
|
@ -33,6 +33,7 @@ tys! {
|
|||||||
ENUM
|
ENUM
|
||||||
RUST_STRUCT
|
RUST_STRUCT
|
||||||
CHAR
|
CHAR
|
||||||
|
OPTIONAL
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -59,6 +60,7 @@ pub enum Descriptor {
|
|||||||
Enum,
|
Enum,
|
||||||
RustStruct(String),
|
RustStruct(String),
|
||||||
Char,
|
Char,
|
||||||
|
Option(Box<Descriptor>),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -115,6 +117,7 @@ impl Descriptor {
|
|||||||
REFMUT => Descriptor::RefMut(Box::new(Descriptor::_decode(data))),
|
REFMUT => Descriptor::RefMut(Box::new(Descriptor::_decode(data))),
|
||||||
SLICE => Descriptor::Slice(Box::new(Descriptor::_decode(data))),
|
SLICE => Descriptor::Slice(Box::new(Descriptor::_decode(data))),
|
||||||
VECTOR => Descriptor::Vector(Box::new(Descriptor::_decode(data))),
|
VECTOR => Descriptor::Vector(Box::new(Descriptor::_decode(data))),
|
||||||
|
OPTIONAL => Descriptor::Option(Box::new(Descriptor::_decode(data))),
|
||||||
STRING => Descriptor::String,
|
STRING => Descriptor::String,
|
||||||
ANYREF => Descriptor::Anyref,
|
ANYREF => Descriptor::Anyref,
|
||||||
ENUM => Descriptor::Enum,
|
ENUM => Descriptor::Enum,
|
||||||
|
@ -122,20 +122,31 @@ impl<'a, 'b> Js2Rust<'a, 'b> {
|
|||||||
let i = self.arg_idx;
|
let i = self.arg_idx;
|
||||||
let name = self.abi_arg();
|
let name = self.abi_arg();
|
||||||
|
|
||||||
|
let (arg, optional) = match arg {
|
||||||
|
Descriptor::Option(t) => (&**t, true),
|
||||||
|
_ => (arg, false),
|
||||||
|
};
|
||||||
|
|
||||||
if let Some(kind) = arg.vector_kind() {
|
if let Some(kind) = arg.vector_kind() {
|
||||||
self.js_arguments
|
self.js_arguments
|
||||||
.push((name.clone(), kind.js_ty().to_string()));
|
.push((name.clone(), kind.js_ty().to_string()));
|
||||||
|
|
||||||
let func = self.cx.pass_to_wasm_function(kind)?;
|
let func = self.cx.pass_to_wasm_function(kind)?;
|
||||||
|
let val = if optional {
|
||||||
|
self.cx.expose_is_like_none();
|
||||||
|
format!("isLikeNone({}) ? [0, 0] : {}({})", name, func, name)
|
||||||
|
} else {
|
||||||
|
format!("{}({})", func, name)
|
||||||
|
};
|
||||||
self.prelude(&format!(
|
self.prelude(&format!(
|
||||||
"\
|
"const [ptr{i}, len{i}] = {val};",
|
||||||
const [ptr{i}, len{i}] = {func}({arg});\n\
|
|
||||||
",
|
|
||||||
i = i,
|
i = i,
|
||||||
func = func,
|
val = val,
|
||||||
arg = name
|
|
||||||
));
|
));
|
||||||
if arg.is_by_ref() {
|
if arg.is_by_ref() {
|
||||||
|
if optional {
|
||||||
|
bail!("optional slices aren't currently supported");
|
||||||
|
}
|
||||||
if arg.is_mut_ref() {
|
if arg.is_mut_ref() {
|
||||||
let get = self.cx.memview_function(kind);
|
let get = self.cx.memview_function(kind);
|
||||||
self.finally(&format!(
|
self.finally(&format!(
|
||||||
@ -165,6 +176,25 @@ impl<'a, 'b> Js2Rust<'a, 'b> {
|
|||||||
return Ok(self);
|
return Ok(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if arg.is_anyref() {
|
||||||
|
self.js_arguments.push((name.clone(), "any".to_string()));
|
||||||
|
self.cx.expose_add_heap_object();
|
||||||
|
if optional {
|
||||||
|
self.cx.expose_is_like_none();
|
||||||
|
self.rust_arguments.push(format!(
|
||||||
|
"isLikeNone({0}) ? 0 : addHeapObject({0})",
|
||||||
|
name,
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
self.rust_arguments.push(format!("addHeapObject({})", name));
|
||||||
|
}
|
||||||
|
return Ok(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
if optional {
|
||||||
|
bail!("unsupported optional argument to rust function {:?}", arg);
|
||||||
|
}
|
||||||
|
|
||||||
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()));
|
||||||
|
|
||||||
@ -262,11 +292,6 @@ impl<'a, 'b> Js2Rust<'a, 'b> {
|
|||||||
self.js_arguments.push((name.clone(), "string".to_string()));
|
self.js_arguments.push((name.clone(), "string".to_string()));
|
||||||
self.rust_arguments.push(format!("{}.codePointAt(0)", name))
|
self.rust_arguments.push(format!("{}.codePointAt(0)", name))
|
||||||
}
|
}
|
||||||
Descriptor::Anyref => {
|
|
||||||
self.js_arguments.push((name.clone(), "any".to_string()));
|
|
||||||
self.cx.expose_add_heap_object();
|
|
||||||
self.rust_arguments.push(format!("addHeapObject({})", name));
|
|
||||||
}
|
|
||||||
_ => bail!("unsupported argument to rust function {:?}", arg),
|
_ => bail!("unsupported argument to rust function {:?}", arg),
|
||||||
}
|
}
|
||||||
Ok(self)
|
Ok(self)
|
||||||
@ -282,16 +307,10 @@ impl<'a, 'b> Js2Rust<'a, 'b> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if ty.is_ref_anyref() {
|
let (ty, optional) = match ty {
|
||||||
self.ret_ty = "any".to_string();
|
Descriptor::Option(t) => (&**t, true),
|
||||||
self.cx.expose_get_object();
|
_ => (ty, false),
|
||||||
self.ret_expr = format!("return getObject(RET);");
|
};
|
||||||
return Ok(self);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ty.is_by_ref() {
|
|
||||||
bail!("cannot return references from Rust to JS yet")
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(ty) = ty.vector_kind() {
|
if let Some(ty) = ty.vector_kind() {
|
||||||
self.ret_ty = ty.js_ty().to_string();
|
self.ret_ty = ty.js_ty().to_string();
|
||||||
@ -307,16 +326,42 @@ impl<'a, 'b> Js2Rust<'a, 'b> {
|
|||||||
const mem = getUint32Memory();\n\
|
const mem = getUint32Memory();\n\
|
||||||
const ptr = mem[retptr / 4];\n\
|
const ptr = mem[retptr / 4];\n\
|
||||||
const len = mem[retptr / 4 + 1];\n\
|
const len = mem[retptr / 4 + 1];\n\
|
||||||
|
{guard}
|
||||||
const realRet = {}(ptr, len).slice();\n\
|
const realRet = {}(ptr, len).slice();\n\
|
||||||
wasm.__wbindgen_free(ptr, len * {});\n\
|
wasm.__wbindgen_free(ptr, len * {});\n\
|
||||||
return realRet;\n\
|
return realRet;\n\
|
||||||
",
|
",
|
||||||
f,
|
f,
|
||||||
ty.size()
|
ty.size(),
|
||||||
|
guard = if optional { "if (ptr === 0) return;" } else { "" },
|
||||||
);
|
);
|
||||||
return Ok(self);
|
return Ok(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// No need to worry about `optional` here, the abi representation means
|
||||||
|
// that `takeObject` will naturally pluck out `undefined`.
|
||||||
|
if ty.is_anyref() {
|
||||||
|
self.ret_ty = "any".to_string();
|
||||||
|
self.cx.expose_take_object();
|
||||||
|
self.ret_expr = format!("return takeObject(RET);");
|
||||||
|
return Ok(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
if optional {
|
||||||
|
bail!("unsupported optional argument to rust function {:?}", ty);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ty.is_ref_anyref() {
|
||||||
|
self.ret_ty = "any".to_string();
|
||||||
|
self.cx.expose_get_object();
|
||||||
|
self.ret_expr = format!("return getObject(RET);");
|
||||||
|
return Ok(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ty.is_by_ref() {
|
||||||
|
bail!("cannot return references from Rust to JS yet")
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(name) = ty.rust_struct() {
|
if let Some(name) = ty.rust_struct() {
|
||||||
self.ret_ty = name.to_string();
|
self.ret_ty = name.to_string();
|
||||||
self.ret_expr = format!("return {name}.__construct(RET);", name = name);
|
self.ret_expr = format!("return {name}.__construct(RET);", name = name);
|
||||||
@ -360,11 +405,6 @@ impl<'a, 'b> Js2Rust<'a, 'b> {
|
|||||||
self.ret_ty = "string".to_string();
|
self.ret_ty = "string".to_string();
|
||||||
self.ret_expr = format!("return String.fromCodePoint(RET);")
|
self.ret_expr = format!("return String.fromCodePoint(RET);")
|
||||||
}
|
}
|
||||||
Descriptor::Anyref => {
|
|
||||||
self.ret_ty = "any".to_string();
|
|
||||||
self.cx.expose_take_object();
|
|
||||||
self.ret_expr = format!("return takeObject(RET);");
|
|
||||||
}
|
|
||||||
_ => bail!("unsupported return from Rust to JS {:?}", ty),
|
_ => bail!("unsupported return from Rust to JS {:?}", ty),
|
||||||
}
|
}
|
||||||
Ok(self)
|
Ok(self)
|
||||||
|
@ -53,6 +53,8 @@ pub struct SubContext<'a, 'b: 'a> {
|
|||||||
pub cx: &'a mut Context<'b>,
|
pub cx: &'a mut Context<'b>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const INITIAL_SLAB_VALUES: &[&str] = &["undefined", "null", "true", "false"];
|
||||||
|
|
||||||
impl<'a> Context<'a> {
|
impl<'a> Context<'a> {
|
||||||
fn export(&mut self, name: &str, contents: &str, comments: Option<String>) {
|
fn export(&mut self, name: &str, contents: &str, comments: Option<String>) {
|
||||||
let contents = contents.trim();
|
let contents = contents.trim();
|
||||||
@ -183,28 +185,6 @@ impl<'a> Context<'a> {
|
|||||||
))
|
))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
self.bind("__wbindgen_undefined_new", &|me| {
|
|
||||||
me.expose_add_heap_object();
|
|
||||||
Ok(String::from(
|
|
||||||
"
|
|
||||||
function() {
|
|
||||||
return addHeapObject(undefined);
|
|
||||||
}
|
|
||||||
",
|
|
||||||
))
|
|
||||||
})?;
|
|
||||||
|
|
||||||
self.bind("__wbindgen_null_new", &|me| {
|
|
||||||
me.expose_add_heap_object();
|
|
||||||
Ok(String::from(
|
|
||||||
"
|
|
||||||
function() {
|
|
||||||
return addHeapObject(null);
|
|
||||||
}
|
|
||||||
",
|
|
||||||
))
|
|
||||||
})?;
|
|
||||||
|
|
||||||
self.bind("__wbindgen_is_null", &|me| {
|
self.bind("__wbindgen_is_null", &|me| {
|
||||||
me.expose_get_object();
|
me.expose_get_object();
|
||||||
Ok(String::from(
|
Ok(String::from(
|
||||||
@ -227,17 +207,6 @@ impl<'a> Context<'a> {
|
|||||||
))
|
))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
self.bind("__wbindgen_boolean_new", &|me| {
|
|
||||||
me.expose_add_heap_object();
|
|
||||||
Ok(String::from(
|
|
||||||
"
|
|
||||||
function(v) {
|
|
||||||
return addHeapObject(v === 1);
|
|
||||||
}
|
|
||||||
",
|
|
||||||
))
|
|
||||||
})?;
|
|
||||||
|
|
||||||
self.bind("__wbindgen_boolean_get", &|me| {
|
self.bind("__wbindgen_boolean_get", &|me| {
|
||||||
me.expose_get_object();
|
me.expose_get_object();
|
||||||
Ok(String::from(
|
Ok(String::from(
|
||||||
@ -782,14 +751,16 @@ impl<'a> Context<'a> {
|
|||||||
"
|
"
|
||||||
function dropRef(idx) {{
|
function dropRef(idx) {{
|
||||||
{}
|
{}
|
||||||
let obj = slab[idx >> 1];
|
idx = idx >> 1;
|
||||||
|
if (idx < {}) return;
|
||||||
|
let obj = slab[idx];
|
||||||
{}
|
{}
|
||||||
// If we hit 0 then free up our space in the slab
|
// If we hit 0 then free up our space in the slab
|
||||||
slab[idx >> 1] = slab_next;
|
slab[idx] = slab_next;
|
||||||
slab_next = idx >> 1;
|
slab_next = idx;
|
||||||
}}
|
}}
|
||||||
",
|
",
|
||||||
validate_owned, dec_ref
|
validate_owned, INITIAL_SLAB_VALUES.len(), dec_ref
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -820,12 +791,9 @@ impl<'a> Context<'a> {
|
|||||||
if !self.exposed_globals.insert("slab") {
|
if !self.exposed_globals.insert("slab") {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let initial_values = [
|
let initial_values = INITIAL_SLAB_VALUES.iter()
|
||||||
"{ obj: null }",
|
.map(|s| format!("{{ obj: {} }}", s))
|
||||||
"{ obj: undefined }",
|
.collect::<Vec<_>>();
|
||||||
"{ obj: true }",
|
|
||||||
"{ obj: false }",
|
|
||||||
];
|
|
||||||
self.global(&format!("const slab = [{}];", initial_values.join(", ")));
|
self.global(&format!("const slab = [{}];", initial_values.join(", ")));
|
||||||
if self.config.debug {
|
if self.config.debug {
|
||||||
self.export(
|
self.export(
|
||||||
@ -1575,6 +1543,17 @@ impl<'a> Context<'a> {
|
|||||||
name
|
name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn expose_is_like_none(&mut self) {
|
||||||
|
if !self.exposed_globals.insert("is_like_none") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.global("
|
||||||
|
function isLikeNone(x) {
|
||||||
|
return x === undefined || x === null;
|
||||||
|
}
|
||||||
|
");
|
||||||
|
}
|
||||||
|
|
||||||
fn gc(&mut self) -> Result<(), Error> {
|
fn gc(&mut self) -> Result<(), Error> {
|
||||||
let module = mem::replace(self.module, Module::default());
|
let module = mem::replace(self.module, Module::default());
|
||||||
let wasm_bytes = parity_wasm::serialize(module)?;
|
let wasm_bytes = parity_wasm::serialize(module)?;
|
||||||
|
@ -82,25 +82,36 @@ impl<'a, 'b> Rust2Js<'a, 'b> {
|
|||||||
fn argument(&mut self, arg: &Descriptor) -> Result<(), Error> {
|
fn argument(&mut self, arg: &Descriptor) -> Result<(), Error> {
|
||||||
let abi = self.shim_argument();
|
let abi = self.shim_argument();
|
||||||
|
|
||||||
|
let (arg, optional) = match arg {
|
||||||
|
Descriptor::Option(t) => (&**t, true),
|
||||||
|
_ => (arg, false),
|
||||||
|
};
|
||||||
|
|
||||||
if let Some(ty) = arg.vector_kind() {
|
if let Some(ty) = arg.vector_kind() {
|
||||||
let abi2 = self.shim_argument();
|
let abi2 = self.shim_argument();
|
||||||
let f = self.cx.expose_get_vector_from_wasm(ty);
|
let f = self.cx.expose_get_vector_from_wasm(ty);
|
||||||
self.prelude(&format!(
|
self.prelude(&format!(
|
||||||
"let v{0} = {func}({0}, {1});",
|
"let v{0} = {prefix}{func}({0}, {1});",
|
||||||
abi,
|
abi,
|
||||||
abi2,
|
abi2,
|
||||||
func = f
|
func = f,
|
||||||
|
prefix = if optional { format!("{} == 0 ? undefined : ", abi) } else { String::new() },
|
||||||
));
|
));
|
||||||
|
|
||||||
if !arg.is_by_ref() {
|
if !arg.is_by_ref() {
|
||||||
self.prelude(&format!(
|
self.prelude(&format!(
|
||||||
"\
|
"\
|
||||||
v{0} = v{0}.slice();\n\
|
{start}
|
||||||
wasm.__wbindgen_free({0}, {1} * {size});\
|
v{0} = v{0}.slice();
|
||||||
|
wasm.__wbindgen_free({0}, {1} * {size});
|
||||||
|
{end}\
|
||||||
",
|
",
|
||||||
abi,
|
abi,
|
||||||
abi2,
|
abi2,
|
||||||
size = ty.size()
|
size = ty.size(),
|
||||||
|
start = if optional { format!("if ({} !== 0) {{", abi) } else { String::new() },
|
||||||
|
end = if optional { "}" } else { "" },
|
||||||
|
|
||||||
));
|
));
|
||||||
self.cx.require_internal_export("__wbindgen_free")?;
|
self.cx.require_internal_export("__wbindgen_free")?;
|
||||||
}
|
}
|
||||||
@ -108,6 +119,18 @@ impl<'a, 'b> Rust2Js<'a, 'b> {
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// No need to special case `optional` here because `takeObject` will
|
||||||
|
// naturally work.
|
||||||
|
if arg.is_anyref() {
|
||||||
|
self.cx.expose_take_object();
|
||||||
|
self.js_arguments.push(format!("takeObject({})", abi));
|
||||||
|
return Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
if optional {
|
||||||
|
bail!("unsupported optional argument {:?}", arg);
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(signed) = arg.get_64bit() {
|
if let Some(signed) = arg.get_64bit() {
|
||||||
let f = if signed {
|
let f = if signed {
|
||||||
self.cx.expose_int64_cvt_shim()
|
self.cx.expose_int64_cvt_shim()
|
||||||
@ -233,10 +256,6 @@ impl<'a, 'b> Rust2Js<'a, 'b> {
|
|||||||
ref d if d.is_number() => abi,
|
ref d if d.is_number() => abi,
|
||||||
Descriptor::Boolean => format!("{} !== 0", abi),
|
Descriptor::Boolean => format!("{} !== 0", abi),
|
||||||
Descriptor::Char => format!("String.fromCodePoint({})", abi),
|
Descriptor::Char => format!("String.fromCodePoint({})", abi),
|
||||||
Descriptor::Anyref => {
|
|
||||||
self.cx.expose_take_object();
|
|
||||||
format!("takeObject({})", abi)
|
|
||||||
}
|
|
||||||
ref d if d.is_ref_anyref() => {
|
ref d if d.is_ref_anyref() => {
|
||||||
self.cx.expose_get_object();
|
self.cx.expose_get_object();
|
||||||
format!("getObject({})", abi)
|
format!("getObject({})", abi)
|
||||||
@ -258,6 +277,10 @@ impl<'a, 'b> Rust2Js<'a, 'b> {
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
let (ty, optional) = match ty {
|
||||||
|
Descriptor::Option(t) => (&**t, true),
|
||||||
|
_ => (ty, false),
|
||||||
|
};
|
||||||
if ty.is_by_ref() {
|
if ty.is_by_ref() {
|
||||||
bail!("cannot return a reference from JS to Rust")
|
bail!("cannot return a reference from JS to Rust")
|
||||||
}
|
}
|
||||||
@ -265,17 +288,43 @@ impl<'a, 'b> Rust2Js<'a, 'b> {
|
|||||||
let f = self.cx.pass_to_wasm_function(ty)?;
|
let f = self.cx.pass_to_wasm_function(ty)?;
|
||||||
self.cx.expose_uint32_memory();
|
self.cx.expose_uint32_memory();
|
||||||
self.shim_arguments.insert(0, "ret".to_string());
|
self.shim_arguments.insert(0, "ret".to_string());
|
||||||
|
let mut prelude = String::new();
|
||||||
|
let expr = if optional {
|
||||||
|
prelude.push_str("const val = JS;");
|
||||||
|
self.cx.expose_is_like_none();
|
||||||
|
format!("isLikeNone(val) ? [0, 0] : {}(val)", f)
|
||||||
|
} else {
|
||||||
|
format!("{}(JS)", f)
|
||||||
|
};
|
||||||
self.ret_expr = format!(
|
self.ret_expr = format!(
|
||||||
"\
|
"\
|
||||||
const [retptr, retlen] = {}(JS);\n\
|
{}
|
||||||
|
const [retptr, retlen] = {};
|
||||||
const mem = getUint32Memory();
|
const mem = getUint32Memory();
|
||||||
mem[ret / 4] = retptr;
|
mem[ret / 4] = retptr;
|
||||||
mem[ret / 4 + 1] = retlen;
|
mem[ret / 4 + 1] = retlen;
|
||||||
",
|
",
|
||||||
f
|
prelude,
|
||||||
|
expr
|
||||||
);
|
);
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
if ty.is_anyref() {
|
||||||
|
self.cx.expose_add_heap_object();
|
||||||
|
if optional {
|
||||||
|
self.cx.expose_is_like_none();
|
||||||
|
self.ret_expr = "
|
||||||
|
const val = JS;
|
||||||
|
return isLikeNone(val) ? 0 : addHeapObject(val);
|
||||||
|
".to_string();
|
||||||
|
} else {
|
||||||
|
self.ret_expr = "return addHeapObject(JS);".to_string()
|
||||||
|
}
|
||||||
|
return Ok(())
|
||||||
|
}
|
||||||
|
if optional {
|
||||||
|
bail!("unsupported optional return type {:?}", ty);
|
||||||
|
}
|
||||||
if ty.is_number() {
|
if ty.is_number() {
|
||||||
self.ret_expr = "return JS;".to_string();
|
self.ret_expr = "return JS;".to_string();
|
||||||
return Ok(());
|
return Ok(());
|
||||||
@ -324,10 +373,6 @@ impl<'a, 'b> Rust2Js<'a, 'b> {
|
|||||||
self.ret_expr = match *ty {
|
self.ret_expr = match *ty {
|
||||||
Descriptor::Boolean => "return JS ? 1 : 0;".to_string(),
|
Descriptor::Boolean => "return JS ? 1 : 0;".to_string(),
|
||||||
Descriptor::Char => "return JS.codePointAt(0);".to_string(),
|
Descriptor::Char => "return JS.codePointAt(0);".to_string(),
|
||||||
Descriptor::Anyref => {
|
|
||||||
self.cx.expose_add_heap_object();
|
|
||||||
"return addHeapObject(JS);".to_string()
|
|
||||||
}
|
|
||||||
_ => bail!("unimplemented return from JS to Rust: {:?}", ty),
|
_ => bail!("unimplemented return from JS to Rust: {:?}", ty),
|
||||||
};
|
};
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -6,14 +6,31 @@ fn headers() {
|
|||||||
.file(
|
.file(
|
||||||
"src/lib.rs",
|
"src/lib.rs",
|
||||||
r#"
|
r#"
|
||||||
#![feature(use_extern_macros)]
|
#![feature(use_extern_macros, wasm_import_module)]
|
||||||
extern crate wasm_bindgen;
|
extern crate wasm_bindgen;
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
extern crate web_sys;
|
extern crate web_sys;
|
||||||
|
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
pub fn test_headers(_headers: &web_sys::Headers) {
|
pub fn test_headers(headers: &web_sys::Headers) {
|
||||||
// empty for now...
|
assert_eq!(headers.get("foo").unwrap(), None);
|
||||||
|
assert_eq!(
|
||||||
|
headers.get("content-type").unwrap(),
|
||||||
|
Some("text/plain".to_string()),
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
headers.get("Content-Type").unwrap(),
|
||||||
|
Some("text/plain".to_string()),
|
||||||
|
);
|
||||||
|
assert!(headers.get("").is_err());
|
||||||
|
assert!(headers.set("", "").is_err());
|
||||||
|
assert!(headers.set("x", "").is_ok());
|
||||||
|
assert_eq!(headers.get("x").unwrap(), Some(String::new()));
|
||||||
|
assert!(headers.delete("x").is_ok());
|
||||||
|
assert_eq!(headers.get("x").unwrap(), None);
|
||||||
|
assert!(headers.append("a", "y").is_ok());
|
||||||
|
assert!(headers.append("a", "z").is_ok());
|
||||||
|
assert_eq!(headers.get("a").unwrap(), Some("y, z".to_string()));
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
)
|
)
|
||||||
|
@ -125,10 +125,6 @@ impl<'a> FirstPassRecord<'a> {
|
|||||||
ty: &webidl::ast::Type,
|
ty: &webidl::ast::Type,
|
||||||
pos: TypePosition,
|
pos: TypePosition,
|
||||||
) -> Option<syn::Type> {
|
) -> Option<syn::Type> {
|
||||||
// nullable types are not yet supported (see issue #14)
|
|
||||||
if ty.nullable {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
let array = |base_ty: &str| {
|
let array = |base_ty: &str| {
|
||||||
match pos {
|
match pos {
|
||||||
TypePosition::Argument => {
|
TypePosition::Argument => {
|
||||||
@ -140,7 +136,7 @@ impl<'a> FirstPassRecord<'a> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Some(match ty.kind {
|
let base_ty = match ty.kind {
|
||||||
// `any` becomes `::wasm_bindgen::JsValue`.
|
// `any` becomes `::wasm_bindgen::JsValue`.
|
||||||
webidl::ast::TypeKind::Any => {
|
webidl::ast::TypeKind::Any => {
|
||||||
simple_path_ty(vec![rust_ident("wasm_bindgen"), rust_ident("JsValue")])
|
simple_path_ty(vec![rust_ident("wasm_bindgen"), rust_ident("JsValue")])
|
||||||
@ -223,7 +219,25 @@ impl<'a> FirstPassRecord<'a> {
|
|||||||
| webidl::ast::TypeKind::Union(_) => {
|
| webidl::ast::TypeKind::Union(_) => {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
})
|
};
|
||||||
|
if ty.nullable {
|
||||||
|
let arguments = syn::PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments {
|
||||||
|
colon2_token: None,
|
||||||
|
lt_token: Default::default(),
|
||||||
|
args: FromIterator::from_iter(vec![
|
||||||
|
syn::GenericArgument::Type(base_ty),
|
||||||
|
]),
|
||||||
|
gt_token: Default::default(),
|
||||||
|
});
|
||||||
|
|
||||||
|
let ident = raw_ident("Option");
|
||||||
|
let seg = syn::PathSegment { ident, arguments };
|
||||||
|
let path: syn::Path = seg.into();
|
||||||
|
let ty = syn::TypePath { qself: None, path };
|
||||||
|
Some(ty.into())
|
||||||
|
} else {
|
||||||
|
Some(base_ty)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn webidl_arguments_to_syn_arg_captured<'b, I>(
|
fn webidl_arguments_to_syn_arg_captured<'b, I>(
|
||||||
|
@ -28,6 +28,7 @@ are:
|
|||||||
* Borrowed exported structs (`&Foo` or `&mut Bar`)
|
* Borrowed exported structs (`&Foo` or `&mut Bar`)
|
||||||
* The `JsValue` type and `&JsValue` (not mutable references)
|
* The `JsValue` type and `&JsValue` (not mutable references)
|
||||||
* Vectors and slices of supported integer types and of the `JsValue` type.
|
* Vectors and slices of supported integer types and of the `JsValue` type.
|
||||||
|
* Optional vectors/slices
|
||||||
|
|
||||||
All of the above can also be returned except borrowed references. Passing
|
All of the above can also be returned except borrowed references. Passing
|
||||||
`Vec<JsValue>` as an argument to a function is not currently supported. Strings are
|
`Vec<JsValue>` as an argument to a function is not currently supported. Strings are
|
||||||
|
@ -2,20 +2,16 @@
|
|||||||
|
|
||||||
The table below provides an overview of all the types that wasm-bindgen can send/receive across the wasm ABI boundary.
|
The table below provides an overview of all the types that wasm-bindgen can send/receive across the wasm ABI boundary.
|
||||||
|
|
||||||
| Type | `T` parameter | `&T` parameter | `&mut T` parameter | `T` return value |
|
| Type | `T` parameter | `&T` parameter | `&mut T` parameter | `T` return value | `Option<T>` parameter | `Option<T>` return value |
|
||||||
|:---:|:---:|:---:|:---:|:---:|
|
|:---:|:---:|:---:|:---:|:---:|:---:|
|
||||||
| `str` | No | Yes | No | Yes |
|
| `str` | No | Yes | No | Yes | Yes | No |
|
||||||
| `char` | Yes | No | No | Yes |
|
| `char` | Yes | No | No | Yes | No | No |
|
||||||
| `bool` | Yes | No | No | Yes |
|
| `bool` | Yes | No | No | Yes | No | No |
|
||||||
| `JsValue` | Yes | Yes | Yes | Yes |
|
| `JsValue` | Yes | Yes | Yes | Yes | No | No |
|
||||||
| `Box<[JsValue]>` | Yes | No | No | Yes |
|
| `Box<[JsValue]>` | Yes | No | No | Yes | Yes | yes |
|
||||||
| `*const T` | Yes | No | No | Yes |
|
| `*const T` | Yes | No | No | Yes | No | No |
|
||||||
| `*mut T` | Yes | No | No | Yes |
|
| `*mut T` | Yes | No | No | Yes | No | No |
|
||||||
| `u8` `i8` `u16` `i16` `u64` `i64` `isize` `size` | Yes | No | No | Yes |
|
| `u8` `i8` `u16` `i16` `u64` `i64` `isize` `size` | Yes | No | No | Yes | No | No |
|
||||||
| `u32` `i32` `f32` `f64` | Yes | Yes | Yes | Yes |
|
| `u32` `i32` `f32` `f64` | Yes | Yes | Yes | Yes | No | No |
|
||||||
| `Box<[u8]>` `Box<[i8]>` `Box<[u16]>` `Box<[i16]>` `Box<[u32]>` `Box<[i32]>` `Box<[u64]>` `Box<[i64]>` | Yes | No | No | Yes |
|
| `Box<[u8]>` `Box<[i8]>` `Box<[u16]>` `Box<[i16]>` `Box<[u32]>` `Box<[i32]>` `Box<[u64]>` `Box<[i64]>` `Box<[f32]>` `Box<[f64]`> | Yes | No | No | Yes | Yes | Yes |
|
||||||
| `Box<[f32]>` `Box<[f64]>` | Yes | No | No | Yes |
|
| `[u8]` `[i8]` `[u16]` `[i16]` `[u32]` `[i32]` `[u64]` `[i64]` `[f32]` `[f64]` | No | Yes | Yes | No | Yes | No |
|
||||||
| `[u8]` `[i8]` `[u16]` `[i16]` `[u32]` `[i32]` `[u64]` `[i64]` | No | Yes | Yes | No |
|
|
||||||
| `&[u8]` `&mut[u8]` `&[i8]` `&mut[i8]` `&[u16]` `&mut[u16]` `&[i16]` `&mut[i16]` `&[u32]` `&mut[u32]` `&[i32]` `&mut[i32]` `&[u64]` `&mut[u64]` `&[i64]` `&mut[i64]` | No | No | No | Yes |
|
|
||||||
| `[f32]` `[f64]` | No | Yes | Yes | No |
|
|
||||||
| `&[f32]` `&mut[f32]` `&[f64]` `&mut[f64]` | No | No | No | Yes |
|
|
||||||
|
610
src/convert.rs
610
src/convert.rs
@ -1,610 +0,0 @@
|
|||||||
//! This is mostly an internal module, no stability guarantees are provided. Use
|
|
||||||
//! at your own risk.
|
|
||||||
|
|
||||||
use core::char;
|
|
||||||
use core::mem::{self, ManuallyDrop};
|
|
||||||
use core::ops::{Deref, DerefMut};
|
|
||||||
use core::slice;
|
|
||||||
use core::str;
|
|
||||||
|
|
||||||
use describe::*;
|
|
||||||
use {throw, JsValue};
|
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
|
||||||
use std::prelude::v1::*;
|
|
||||||
|
|
||||||
/// A trait for anything that can be converted into a type that can cross the
|
|
||||||
/// wasm ABI directly, eg `u32` or `f64`.
|
|
||||||
///
|
|
||||||
/// This is the opposite operation as `FromWasmAbi` and `Ref[Mut]FromWasmAbi`.
|
|
||||||
pub trait IntoWasmAbi: WasmDescribe {
|
|
||||||
/// The wasm ABI type that this converts into when crossing the ABI
|
|
||||||
/// boundary.
|
|
||||||
type Abi: WasmAbi;
|
|
||||||
|
|
||||||
/// Convert `self` into `Self::Abi` so that it can be sent across the wasm
|
|
||||||
/// ABI boundary.
|
|
||||||
fn into_abi(self, extra: &mut Stack) -> Self::Abi;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A trait for anything that can be recovered by-value from the wasm ABI
|
|
||||||
/// boundary, eg a Rust `u8` can be recovered from the wasm ABI `u32` type.
|
|
||||||
///
|
|
||||||
/// This is the by-value variant of the opposite operation as `IntoWasmAbi`.
|
|
||||||
pub trait FromWasmAbi: WasmDescribe {
|
|
||||||
/// The wasm ABI type that this converts from when coming back out from the
|
|
||||||
/// ABI boundary.
|
|
||||||
type Abi: WasmAbi;
|
|
||||||
|
|
||||||
/// Recover a `Self` from `Self::Abi`.
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// This is only safe to call when -- and implementations may assume that --
|
|
||||||
/// the supplied `Self::Abi` was previously generated by a call to `<Self as
|
|
||||||
/// IntoWasmAbi>::into_abi()` or the moral equivalent in JS.
|
|
||||||
unsafe fn from_abi(js: Self::Abi, extra: &mut Stack) -> Self;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A trait for anything that can be recovered as some sort of shared reference
|
|
||||||
/// from the wasm ABI boundary.
|
|
||||||
///
|
|
||||||
/// This is the shared reference variant of the opposite operation as
|
|
||||||
/// `IntoWasmAbi`.
|
|
||||||
pub trait RefFromWasmAbi: WasmDescribe {
|
|
||||||
/// The wasm ABI type references to `Self` are recovered from.
|
|
||||||
type Abi: WasmAbi;
|
|
||||||
|
|
||||||
/// The type that holds the reference to `Self` for the duration of the
|
|
||||||
/// invocation of the function that has an `&Self` parameter. This is
|
|
||||||
/// required to ensure that the lifetimes don't persist beyond one function
|
|
||||||
/// call, and so that they remain anonymous.
|
|
||||||
type Anchor: Deref<Target = Self>;
|
|
||||||
|
|
||||||
/// Recover a `Self::Anchor` from `Self::Abi`.
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// Same as `FromWasmAbi::from_abi`.
|
|
||||||
unsafe fn ref_from_abi(js: Self::Abi, extra: &mut Stack) -> Self::Anchor;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait RefMutFromWasmAbi: WasmDescribe {
|
|
||||||
type Abi: WasmAbi;
|
|
||||||
type Anchor: DerefMut<Target = Self>;
|
|
||||||
unsafe fn ref_mut_from_abi(js: Self::Abi, extra: &mut Stack) -> Self::Anchor;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait Stack {
|
|
||||||
fn push(&mut self, bits: u32);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// An unsafe trait which represents types that are ABI-safe to pass via wasm
|
|
||||||
/// arguments.
|
|
||||||
///
|
|
||||||
/// This is an unsafe trait to implement as there's no guarantee the type is
|
|
||||||
/// actually safe to transfer across the was boundary, it's up to you to
|
|
||||||
/// guarantee this so codegen works correctly.
|
|
||||||
pub unsafe trait WasmAbi {}
|
|
||||||
|
|
||||||
unsafe impl WasmAbi for u32 {}
|
|
||||||
unsafe impl WasmAbi for i32 {}
|
|
||||||
unsafe impl WasmAbi for f32 {}
|
|
||||||
unsafe impl WasmAbi for f64 {}
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct WasmSlice {
|
|
||||||
pub ptr: u32,
|
|
||||||
pub len: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl WasmAbi for WasmSlice {}
|
|
||||||
|
|
||||||
macro_rules! simple {
|
|
||||||
($($t:tt)*) => ($(
|
|
||||||
impl IntoWasmAbi for $t {
|
|
||||||
type Abi = $t;
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn into_abi(self, _extra: &mut Stack) -> $t { self }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromWasmAbi for $t {
|
|
||||||
type Abi = $t;
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
unsafe fn from_abi(js: $t, _extra: &mut Stack) -> $t { js }
|
|
||||||
}
|
|
||||||
)*)
|
|
||||||
}
|
|
||||||
|
|
||||||
simple!(u32 i32 f32 f64);
|
|
||||||
|
|
||||||
macro_rules! sixtyfour {
|
|
||||||
($($t:tt)*) => ($(
|
|
||||||
impl IntoWasmAbi for $t {
|
|
||||||
type Abi = WasmSlice;
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn into_abi(self, _extra: &mut Stack) -> WasmSlice {
|
|
||||||
WasmSlice {
|
|
||||||
ptr: self as u32,
|
|
||||||
len: (self >> 32) as u32,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromWasmAbi for $t {
|
|
||||||
type Abi = WasmSlice;
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
unsafe fn from_abi(js: WasmSlice, _extra: &mut Stack) -> $t {
|
|
||||||
(js.ptr as $t) | ((js.len as $t) << 32)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)*)
|
|
||||||
}
|
|
||||||
|
|
||||||
sixtyfour!(i64 u64);
|
|
||||||
|
|
||||||
macro_rules! as_u32 {
|
|
||||||
($($t:tt)*) => ($(
|
|
||||||
impl IntoWasmAbi for $t {
|
|
||||||
type Abi = u32;
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn into_abi(self, _extra: &mut Stack) -> u32 { self as u32 }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromWasmAbi for $t {
|
|
||||||
type Abi = u32;
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
unsafe fn from_abi(js: u32, _extra: &mut Stack) -> $t { js as $t }
|
|
||||||
}
|
|
||||||
)*)
|
|
||||||
}
|
|
||||||
|
|
||||||
as_u32!(i8 u8 i16 u16 isize usize);
|
|
||||||
|
|
||||||
impl IntoWasmAbi for bool {
|
|
||||||
type Abi = u32;
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn into_abi(self, _extra: &mut Stack) -> u32 {
|
|
||||||
self as u32
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromWasmAbi for bool {
|
|
||||||
type Abi = u32;
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
unsafe fn from_abi(js: u32, _extra: &mut Stack) -> bool {
|
|
||||||
js != 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl IntoWasmAbi for char {
|
|
||||||
type Abi = u32;
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn into_abi(self, _extra: &mut Stack) -> u32 {
|
|
||||||
self as u32
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromWasmAbi for char {
|
|
||||||
type Abi = u32;
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
unsafe fn from_abi(js: u32, _extra: &mut Stack) -> char {
|
|
||||||
char::from_u32_unchecked(js)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> IntoWasmAbi for *const T {
|
|
||||||
type Abi = u32;
|
|
||||||
|
|
||||||
fn into_abi(self, _extra: &mut Stack) -> u32 {
|
|
||||||
self as u32
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> FromWasmAbi for *const T {
|
|
||||||
type Abi = u32;
|
|
||||||
|
|
||||||
unsafe fn from_abi(js: u32, _extra: &mut Stack) -> *const T {
|
|
||||||
js as *const T
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> IntoWasmAbi for *mut T {
|
|
||||||
type Abi = u32;
|
|
||||||
|
|
||||||
fn into_abi(self, _extra: &mut Stack) -> u32 {
|
|
||||||
self as u32
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> FromWasmAbi for *mut T {
|
|
||||||
type Abi = u32;
|
|
||||||
|
|
||||||
unsafe fn from_abi(js: u32, _extra: &mut Stack) -> *mut T {
|
|
||||||
js as *mut T
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! vectors {
|
|
||||||
($($t:ident)*) => ($(
|
|
||||||
#[cfg(feature = "std")]
|
|
||||||
impl IntoWasmAbi for Box<[$t]> {
|
|
||||||
type Abi = WasmSlice;
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn into_abi(self, extra: &mut Stack) -> WasmSlice {
|
|
||||||
let ptr = self.as_ptr();
|
|
||||||
let len = self.len();
|
|
||||||
mem::forget(self);
|
|
||||||
WasmSlice {
|
|
||||||
ptr: ptr.into_abi(extra),
|
|
||||||
len: len as u32,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
|
||||||
impl FromWasmAbi for Box<[$t]> {
|
|
||||||
type Abi = WasmSlice;
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
unsafe fn from_abi(js: WasmSlice, extra: &mut Stack) -> Self {
|
|
||||||
let ptr = <*mut $t>::from_abi(js.ptr, extra);
|
|
||||||
let len = js.len as usize;
|
|
||||||
Vec::from_raw_parts(ptr, len, len).into_boxed_slice()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> IntoWasmAbi for &'a [$t] {
|
|
||||||
type Abi = WasmSlice;
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn into_abi(self, extra: &mut Stack) -> WasmSlice {
|
|
||||||
WasmSlice {
|
|
||||||
ptr: self.as_ptr().into_abi(extra),
|
|
||||||
len: self.len() as u32,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> IntoWasmAbi for &'a mut [$t] {
|
|
||||||
type Abi = WasmSlice;
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn into_abi(self, extra: &mut Stack) -> WasmSlice {
|
|
||||||
(&*self).into_abi(extra)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RefFromWasmAbi for [$t] {
|
|
||||||
type Abi = WasmSlice;
|
|
||||||
type Anchor = &'static [$t];
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
unsafe fn ref_from_abi(js: WasmSlice, extra: &mut Stack) -> &'static [$t] {
|
|
||||||
slice::from_raw_parts(
|
|
||||||
<*const $t>::from_abi(js.ptr, extra),
|
|
||||||
js.len as usize,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RefMutFromWasmAbi for [$t] {
|
|
||||||
type Abi = WasmSlice;
|
|
||||||
type Anchor = &'static mut [$t];
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
unsafe fn ref_mut_from_abi(js: WasmSlice, extra: &mut Stack)
|
|
||||||
-> &'static mut [$t]
|
|
||||||
{
|
|
||||||
slice::from_raw_parts_mut(
|
|
||||||
<*mut $t>::from_abi(js.ptr, extra),
|
|
||||||
js.len as usize,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)*)
|
|
||||||
}
|
|
||||||
|
|
||||||
vectors! {
|
|
||||||
u8 i8 u16 i16 u32 i32 u64 i64 f32 f64
|
|
||||||
}
|
|
||||||
|
|
||||||
if_std! {
|
|
||||||
impl<T> IntoWasmAbi for Vec<T> where Box<[T]>: IntoWasmAbi {
|
|
||||||
type Abi = <Box<[T]> as IntoWasmAbi>::Abi;
|
|
||||||
|
|
||||||
fn into_abi(self, extra: &mut Stack) -> Self::Abi {
|
|
||||||
self.into_boxed_slice().into_abi(extra)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> FromWasmAbi for Vec<T> where Box<[T]>: FromWasmAbi {
|
|
||||||
type Abi = <Box<[T]> as FromWasmAbi>::Abi;
|
|
||||||
|
|
||||||
unsafe fn from_abi(js: Self::Abi, extra: &mut Stack) -> Self {
|
|
||||||
<Box<[T]>>::from_abi(js, extra).into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl IntoWasmAbi for String {
|
|
||||||
type Abi = <Vec<u8> as IntoWasmAbi>::Abi;
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn into_abi(self, extra: &mut Stack) -> Self::Abi {
|
|
||||||
self.into_bytes().into_abi(extra)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromWasmAbi for String {
|
|
||||||
type Abi = <Vec<u8> as FromWasmAbi>::Abi;
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
unsafe fn from_abi(js: Self::Abi, extra: &mut Stack) -> Self {
|
|
||||||
String::from_utf8_unchecked(<Vec<u8>>::from_abi(js, extra))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> IntoWasmAbi for &'a str {
|
|
||||||
type Abi = <&'a [u8] as IntoWasmAbi>::Abi;
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn into_abi(self, extra: &mut Stack) -> Self::Abi {
|
|
||||||
self.as_bytes().into_abi(extra)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RefFromWasmAbi for str {
|
|
||||||
type Abi = <[u8] as RefFromWasmAbi>::Abi;
|
|
||||||
type Anchor = &'static str;
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
unsafe fn ref_from_abi(js: Self::Abi, extra: &mut Stack) -> Self::Anchor {
|
|
||||||
str::from_utf8_unchecked(<[u8]>::ref_from_abi(js, extra))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl IntoWasmAbi for JsValue {
|
|
||||||
type Abi = u32;
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn into_abi(self, _extra: &mut Stack) -> u32 {
|
|
||||||
let ret = self.idx;
|
|
||||||
mem::forget(self);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromWasmAbi for JsValue {
|
|
||||||
type Abi = u32;
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
unsafe fn from_abi(js: u32, _extra: &mut Stack) -> JsValue {
|
|
||||||
JsValue { idx: js }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> IntoWasmAbi for &'a JsValue {
|
|
||||||
type Abi = u32;
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn into_abi(self, _extra: &mut Stack) -> u32 {
|
|
||||||
self.idx
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RefFromWasmAbi for JsValue {
|
|
||||||
type Abi = u32;
|
|
||||||
type Anchor = ManuallyDrop<JsValue>;
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
unsafe fn ref_from_abi(js: u32, _extra: &mut Stack) -> Self::Anchor {
|
|
||||||
ManuallyDrop::new(JsValue { idx: js })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if_std! {
|
|
||||||
impl IntoWasmAbi for Box<[JsValue]> {
|
|
||||||
type Abi = WasmSlice;
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn into_abi(self, extra: &mut Stack) -> WasmSlice {
|
|
||||||
let ptr = self.as_ptr();
|
|
||||||
let len = self.len();
|
|
||||||
mem::forget(self);
|
|
||||||
WasmSlice {
|
|
||||||
ptr: ptr.into_abi(extra),
|
|
||||||
len: len as u32,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromWasmAbi for Box<[JsValue]> {
|
|
||||||
type Abi = WasmSlice;
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
unsafe fn from_abi(js: WasmSlice, extra: &mut Stack) -> Self {
|
|
||||||
let ptr = <*mut JsValue>::from_abi(js.ptr, extra);
|
|
||||||
let len = js.len as usize;
|
|
||||||
Vec::from_raw_parts(ptr, len, len).into_boxed_slice()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct GlobalStack {
|
|
||||||
next: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl GlobalStack {
|
|
||||||
#[inline]
|
|
||||||
pub unsafe fn new() -> GlobalStack {
|
|
||||||
GlobalStack { next: 0 }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Stack for GlobalStack {
|
|
||||||
#[inline]
|
|
||||||
fn push(&mut self, val: u32) {
|
|
||||||
use __rt::{
|
|
||||||
__wbindgen_global_argument_ptr as global_ptr,
|
|
||||||
GLOBAL_STACK_CAP,
|
|
||||||
};
|
|
||||||
unsafe {
|
|
||||||
assert!(self.next < GLOBAL_STACK_CAP);
|
|
||||||
*global_ptr().offset(self.next as isize) = val;
|
|
||||||
self.next += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! stack_closures {
|
|
||||||
($( ($($var:ident)*) )*) => ($(
|
|
||||||
impl<'a, 'b, $($var,)* R> IntoWasmAbi for &'a (Fn($($var),*) -> R + 'b)
|
|
||||||
where $($var: FromWasmAbi,)*
|
|
||||||
R: IntoWasmAbi
|
|
||||||
{
|
|
||||||
type Abi = u32;
|
|
||||||
|
|
||||||
fn into_abi(self, extra: &mut Stack) -> u32 {
|
|
||||||
#[allow(non_snake_case)]
|
|
||||||
unsafe extern fn invoke<$($var: FromWasmAbi,)* R: IntoWasmAbi>(
|
|
||||||
a: usize,
|
|
||||||
b: usize,
|
|
||||||
$($var: <$var as FromWasmAbi>::Abi),*
|
|
||||||
) -> <R as IntoWasmAbi>::Abi {
|
|
||||||
if a == 0 {
|
|
||||||
throw("closure invoked recursively or destroyed already");
|
|
||||||
}
|
|
||||||
let f: &Fn($($var),*) -> R = mem::transmute((a, b));
|
|
||||||
let mut _stack = GlobalStack::new();
|
|
||||||
$(
|
|
||||||
let $var = <$var as FromWasmAbi>::from_abi($var, &mut _stack);
|
|
||||||
)*
|
|
||||||
f($($var),*).into_abi(&mut GlobalStack::new())
|
|
||||||
}
|
|
||||||
unsafe {
|
|
||||||
let (a, b): (usize, usize) = mem::transmute(self);
|
|
||||||
extra.push(a as u32);
|
|
||||||
extra.push(b as u32);
|
|
||||||
invoke::<$($var,)* R> as u32
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
where $($var: FromWasmAbi,)*
|
|
||||||
R: IntoWasmAbi
|
|
||||||
{
|
|
||||||
type Abi = u32;
|
|
||||||
|
|
||||||
fn into_abi(self, extra: &mut Stack) -> u32 {
|
|
||||||
#[allow(non_snake_case)]
|
|
||||||
unsafe extern fn invoke<$($var: FromWasmAbi,)* R: IntoWasmAbi>(
|
|
||||||
a: usize,
|
|
||||||
b: usize,
|
|
||||||
$($var: <$var as FromWasmAbi>::Abi),*
|
|
||||||
) -> <R as IntoWasmAbi>::Abi {
|
|
||||||
if a == 0 {
|
|
||||||
throw("closure invoked recursively or destroyed already");
|
|
||||||
}
|
|
||||||
let f: &mut FnMut($($var),*) -> R = mem::transmute((a, b));
|
|
||||||
let mut _stack = GlobalStack::new();
|
|
||||||
$(
|
|
||||||
let $var = <$var as FromWasmAbi>::from_abi($var, &mut _stack);
|
|
||||||
)*
|
|
||||||
f($($var),*).into_abi(&mut GlobalStack::new())
|
|
||||||
}
|
|
||||||
unsafe {
|
|
||||||
let (a, b): (usize, usize) = mem::transmute(self);
|
|
||||||
extra.push(a as u32);
|
|
||||||
extra.push(b as u32);
|
|
||||||
invoke::<$($var,)* R> as u32
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)*)
|
|
||||||
}
|
|
||||||
|
|
||||||
stack_closures! {
|
|
||||||
()
|
|
||||||
(A)
|
|
||||||
(A B)
|
|
||||||
(A B C)
|
|
||||||
(A B C D)
|
|
||||||
(A B C D E)
|
|
||||||
(A B C D E F)
|
|
||||||
(A B C D E F G)
|
|
||||||
}
|
|
146
src/convert/closures.rs
Normal file
146
src/convert/closures.rs
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
use core::mem;
|
||||||
|
|
||||||
|
use convert::{FromWasmAbi, IntoWasmAbi, GlobalStack, Stack};
|
||||||
|
use throw;
|
||||||
|
|
||||||
|
macro_rules! stack_closures {
|
||||||
|
($( ($($var:ident)*) )*) => ($(
|
||||||
|
impl<'a, 'b, $($var,)* R> IntoWasmAbi for &'a (Fn($($var),*) -> R + 'b)
|
||||||
|
where $($var: FromWasmAbi,)*
|
||||||
|
R: IntoWasmAbi
|
||||||
|
{
|
||||||
|
type Abi = u32;
|
||||||
|
|
||||||
|
fn into_abi(self, extra: &mut Stack) -> u32 {
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
unsafe extern fn invoke<$($var: FromWasmAbi,)* R: IntoWasmAbi>(
|
||||||
|
a: usize,
|
||||||
|
b: usize,
|
||||||
|
$($var: <$var as FromWasmAbi>::Abi),*
|
||||||
|
) -> <R as IntoWasmAbi>::Abi {
|
||||||
|
if a == 0 {
|
||||||
|
throw("closure invoked recursively or destroyed already");
|
||||||
|
}
|
||||||
|
let f: &Fn($($var),*) -> R = mem::transmute((a, b));
|
||||||
|
let mut _stack = GlobalStack::new();
|
||||||
|
$(
|
||||||
|
let $var = <$var as FromWasmAbi>::from_abi($var, &mut _stack);
|
||||||
|
)*
|
||||||
|
f($($var),*).into_abi(&mut GlobalStack::new())
|
||||||
|
}
|
||||||
|
unsafe {
|
||||||
|
let (a, b): (usize, usize) = mem::transmute(self);
|
||||||
|
extra.push(a as u32);
|
||||||
|
extra.push(b as u32);
|
||||||
|
invoke::<$($var,)* R> as u32
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
where $($var: FromWasmAbi,)*
|
||||||
|
R: IntoWasmAbi
|
||||||
|
{
|
||||||
|
type Abi = u32;
|
||||||
|
|
||||||
|
fn into_abi(self, extra: &mut Stack) -> u32 {
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
unsafe extern fn invoke<$($var: FromWasmAbi,)* R: IntoWasmAbi>(
|
||||||
|
a: usize,
|
||||||
|
b: usize,
|
||||||
|
$($var: <$var as FromWasmAbi>::Abi),*
|
||||||
|
) -> <R as IntoWasmAbi>::Abi {
|
||||||
|
if a == 0 {
|
||||||
|
throw("closure invoked recursively or destroyed already");
|
||||||
|
}
|
||||||
|
let f: &mut FnMut($($var),*) -> R = mem::transmute((a, b));
|
||||||
|
let mut _stack = GlobalStack::new();
|
||||||
|
$(
|
||||||
|
let $var = <$var as FromWasmAbi>::from_abi($var, &mut _stack);
|
||||||
|
)*
|
||||||
|
f($($var),*).into_abi(&mut GlobalStack::new())
|
||||||
|
}
|
||||||
|
unsafe {
|
||||||
|
let (a, b): (usize, usize) = mem::transmute(self);
|
||||||
|
extra.push(a as u32);
|
||||||
|
extra.push(b as u32);
|
||||||
|
invoke::<$($var,)* R> as u32
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)*)
|
||||||
|
}
|
||||||
|
|
||||||
|
stack_closures! {
|
||||||
|
()
|
||||||
|
(A)
|
||||||
|
(A B)
|
||||||
|
(A B C)
|
||||||
|
(A B C D)
|
||||||
|
(A B C D E)
|
||||||
|
(A B C D E F)
|
||||||
|
(A B C D E F G)
|
||||||
|
}
|
||||||
|
|
204
src/convert/impls.rs
Normal file
204
src/convert/impls.rs
Normal file
@ -0,0 +1,204 @@
|
|||||||
|
use core::char;
|
||||||
|
use core::mem::{self, ManuallyDrop};
|
||||||
|
|
||||||
|
use convert::slices::WasmSlice;
|
||||||
|
use convert::{Stack, FromWasmAbi, IntoWasmAbi, RefFromWasmAbi};
|
||||||
|
use convert::{OptionIntoWasmAbi, OptionFromWasmAbi};
|
||||||
|
use JsValue;
|
||||||
|
|
||||||
|
macro_rules! simple {
|
||||||
|
($($t:tt)*) => ($(
|
||||||
|
impl IntoWasmAbi for $t {
|
||||||
|
type Abi = $t;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn into_abi(self, _extra: &mut Stack) -> $t { self }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromWasmAbi for $t {
|
||||||
|
type Abi = $t;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
unsafe fn from_abi(js: $t, _extra: &mut Stack) -> $t { js }
|
||||||
|
}
|
||||||
|
)*)
|
||||||
|
}
|
||||||
|
|
||||||
|
simple!(u32 i32 f32 f64);
|
||||||
|
|
||||||
|
macro_rules! sixtyfour {
|
||||||
|
($($t:tt)*) => ($(
|
||||||
|
impl IntoWasmAbi for $t {
|
||||||
|
type Abi = WasmSlice;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn into_abi(self, _extra: &mut Stack) -> WasmSlice {
|
||||||
|
WasmSlice {
|
||||||
|
ptr: self as u32,
|
||||||
|
len: (self >> 32) as u32,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromWasmAbi for $t {
|
||||||
|
type Abi = WasmSlice;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
unsafe fn from_abi(js: WasmSlice, _extra: &mut Stack) -> $t {
|
||||||
|
(js.ptr as $t) | ((js.len as $t) << 32)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)*)
|
||||||
|
}
|
||||||
|
|
||||||
|
sixtyfour!(i64 u64);
|
||||||
|
|
||||||
|
macro_rules! as_u32 {
|
||||||
|
($($t:tt)*) => ($(
|
||||||
|
impl IntoWasmAbi for $t {
|
||||||
|
type Abi = u32;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn into_abi(self, _extra: &mut Stack) -> u32 { self as u32 }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromWasmAbi for $t {
|
||||||
|
type Abi = u32;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
unsafe fn from_abi(js: u32, _extra: &mut Stack) -> $t { js as $t }
|
||||||
|
}
|
||||||
|
)*)
|
||||||
|
}
|
||||||
|
|
||||||
|
as_u32!(i8 u8 i16 u16 isize usize);
|
||||||
|
|
||||||
|
impl IntoWasmAbi for bool {
|
||||||
|
type Abi = u32;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn into_abi(self, _extra: &mut Stack) -> u32 {
|
||||||
|
self as u32
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromWasmAbi for bool {
|
||||||
|
type Abi = u32;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
unsafe fn from_abi(js: u32, _extra: &mut Stack) -> bool {
|
||||||
|
js != 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoWasmAbi for char {
|
||||||
|
type Abi = u32;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn into_abi(self, _extra: &mut Stack) -> u32 {
|
||||||
|
self as u32
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromWasmAbi for char {
|
||||||
|
type Abi = u32;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
unsafe fn from_abi(js: u32, _extra: &mut Stack) -> char {
|
||||||
|
char::from_u32_unchecked(js)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> IntoWasmAbi for *const T {
|
||||||
|
type Abi = u32;
|
||||||
|
|
||||||
|
fn into_abi(self, _extra: &mut Stack) -> u32 {
|
||||||
|
self as u32
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> FromWasmAbi for *const T {
|
||||||
|
type Abi = u32;
|
||||||
|
|
||||||
|
unsafe fn from_abi(js: u32, _extra: &mut Stack) -> *const T {
|
||||||
|
js as *const T
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> IntoWasmAbi for *mut T {
|
||||||
|
type Abi = u32;
|
||||||
|
|
||||||
|
fn into_abi(self, _extra: &mut Stack) -> u32 {
|
||||||
|
self as u32
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> FromWasmAbi for *mut T {
|
||||||
|
type Abi = u32;
|
||||||
|
|
||||||
|
unsafe fn from_abi(js: u32, _extra: &mut Stack) -> *mut T {
|
||||||
|
js as *mut T
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoWasmAbi for JsValue {
|
||||||
|
type Abi = u32;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn into_abi(self, _extra: &mut Stack) -> u32 {
|
||||||
|
let ret = self.idx;
|
||||||
|
mem::forget(self);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromWasmAbi for JsValue {
|
||||||
|
type Abi = u32;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
unsafe fn from_abi(js: u32, _extra: &mut Stack) -> JsValue {
|
||||||
|
JsValue { idx: js }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> IntoWasmAbi for &'a JsValue {
|
||||||
|
type Abi = u32;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn into_abi(self, _extra: &mut Stack) -> u32 {
|
||||||
|
self.idx
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RefFromWasmAbi for JsValue {
|
||||||
|
type Abi = u32;
|
||||||
|
type Anchor = ManuallyDrop<JsValue>;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
unsafe fn ref_from_abi(js: u32, _extra: &mut Stack) -> Self::Anchor {
|
||||||
|
ManuallyDrop::new(JsValue { idx: js })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: OptionIntoWasmAbi> IntoWasmAbi for Option<T> {
|
||||||
|
type Abi = T::Abi;
|
||||||
|
|
||||||
|
fn into_abi(self, extra: &mut Stack) -> T::Abi {
|
||||||
|
match self {
|
||||||
|
Some(me) => me.into_abi(extra),
|
||||||
|
None => T::none(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: OptionFromWasmAbi> FromWasmAbi for Option<T> {
|
||||||
|
type Abi = T::Abi;
|
||||||
|
|
||||||
|
unsafe fn from_abi(js: T::Abi, extra: &mut Stack) -> Self {
|
||||||
|
if T::is_none(&js) {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(T::from_abi(js, extra))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
36
src/convert/mod.rs
Normal file
36
src/convert/mod.rs
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
//! This is mostly an internal module, no stability guarantees are provided. Use
|
||||||
|
//! at your own risk.
|
||||||
|
|
||||||
|
mod traits;
|
||||||
|
mod impls;
|
||||||
|
mod slices;
|
||||||
|
mod closures;
|
||||||
|
|
||||||
|
pub use self::slices::WasmSlice;
|
||||||
|
pub use self::traits::*;
|
||||||
|
|
||||||
|
pub struct GlobalStack {
|
||||||
|
next: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GlobalStack {
|
||||||
|
#[inline]
|
||||||
|
pub unsafe fn new() -> GlobalStack {
|
||||||
|
GlobalStack { next: 0 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Stack for GlobalStack {
|
||||||
|
#[inline]
|
||||||
|
fn push(&mut self, val: u32) {
|
||||||
|
use __rt::{
|
||||||
|
__wbindgen_global_argument_ptr as global_ptr,
|
||||||
|
GLOBAL_STACK_CAP,
|
||||||
|
};
|
||||||
|
unsafe {
|
||||||
|
assert!(self.next < GLOBAL_STACK_CAP);
|
||||||
|
*global_ptr().offset(self.next as isize) = val;
|
||||||
|
self.next += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
240
src/convert/slices.rs
Normal file
240
src/convert/slices.rs
Normal file
@ -0,0 +1,240 @@
|
|||||||
|
#[cfg(feature = "std")]
|
||||||
|
use std::prelude::v1::*;
|
||||||
|
|
||||||
|
use core::slice;
|
||||||
|
use core::str;
|
||||||
|
|
||||||
|
use convert::{WasmAbi, IntoWasmAbi, FromWasmAbi, RefFromWasmAbi, RefMutFromWasmAbi};
|
||||||
|
use convert::{Stack, OptionIntoWasmAbi};
|
||||||
|
|
||||||
|
if_std! {
|
||||||
|
use core::mem;
|
||||||
|
use convert::OptionFromWasmAbi;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct WasmSlice {
|
||||||
|
pub ptr: u32,
|
||||||
|
pub len: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl WasmAbi for WasmSlice {}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn null_slice() -> WasmSlice {
|
||||||
|
WasmSlice { ptr: 0, len: 0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! vectors {
|
||||||
|
($($t:ident)*) => ($(
|
||||||
|
if_std! {
|
||||||
|
impl IntoWasmAbi for Box<[$t]> {
|
||||||
|
type Abi = WasmSlice;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn into_abi(self, extra: &mut Stack) -> WasmSlice {
|
||||||
|
let ptr = self.as_ptr();
|
||||||
|
let len = self.len();
|
||||||
|
mem::forget(self);
|
||||||
|
WasmSlice {
|
||||||
|
ptr: ptr.into_abi(extra),
|
||||||
|
len: len as u32,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OptionIntoWasmAbi for Box<[$t]> {
|
||||||
|
fn none() -> WasmSlice { null_slice() }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromWasmAbi for Box<[$t]> {
|
||||||
|
type Abi = WasmSlice;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
unsafe fn from_abi(js: WasmSlice, extra: &mut Stack) -> Self {
|
||||||
|
let ptr = <*mut $t>::from_abi(js.ptr, extra);
|
||||||
|
let len = js.len as usize;
|
||||||
|
Vec::from_raw_parts(ptr, len, len).into_boxed_slice()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OptionFromWasmAbi for Box<[$t]> {
|
||||||
|
fn is_none(slice: &WasmSlice) -> bool { slice.ptr == 0 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> IntoWasmAbi for &'a [$t] {
|
||||||
|
type Abi = WasmSlice;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn into_abi(self, extra: &mut Stack) -> WasmSlice {
|
||||||
|
WasmSlice {
|
||||||
|
ptr: self.as_ptr().into_abi(extra),
|
||||||
|
len: self.len() as u32,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> OptionIntoWasmAbi for &'a [$t] {
|
||||||
|
fn none() -> WasmSlice { null_slice() }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> IntoWasmAbi for &'a mut [$t] {
|
||||||
|
type Abi = WasmSlice;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn into_abi(self, extra: &mut Stack) -> WasmSlice {
|
||||||
|
(&*self).into_abi(extra)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> OptionIntoWasmAbi for &'a mut [$t] {
|
||||||
|
fn none() -> WasmSlice { null_slice() }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RefFromWasmAbi for [$t] {
|
||||||
|
type Abi = WasmSlice;
|
||||||
|
type Anchor = &'static [$t];
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
unsafe fn ref_from_abi(js: WasmSlice, extra: &mut Stack) -> &'static [$t] {
|
||||||
|
slice::from_raw_parts(
|
||||||
|
<*const $t>::from_abi(js.ptr, extra),
|
||||||
|
js.len as usize,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RefMutFromWasmAbi for [$t] {
|
||||||
|
type Abi = WasmSlice;
|
||||||
|
type Anchor = &'static mut [$t];
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
unsafe fn ref_mut_from_abi(js: WasmSlice, extra: &mut Stack)
|
||||||
|
-> &'static mut [$t]
|
||||||
|
{
|
||||||
|
slice::from_raw_parts_mut(
|
||||||
|
<*mut $t>::from_abi(js.ptr, extra),
|
||||||
|
js.len as usize,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)*)
|
||||||
|
}
|
||||||
|
|
||||||
|
vectors! {
|
||||||
|
u8 i8 u16 i16 u32 i32 u64 i64 f32 f64
|
||||||
|
}
|
||||||
|
|
||||||
|
if_std! {
|
||||||
|
impl<T> IntoWasmAbi for Vec<T> where Box<[T]>: IntoWasmAbi<Abi = WasmSlice> {
|
||||||
|
type Abi = <Box<[T]> as IntoWasmAbi>::Abi;
|
||||||
|
|
||||||
|
fn into_abi(self, extra: &mut Stack) -> Self::Abi {
|
||||||
|
self.into_boxed_slice().into_abi(extra)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> OptionIntoWasmAbi for Vec<T> where Box<[T]>: IntoWasmAbi<Abi = WasmSlice> {
|
||||||
|
fn none() -> WasmSlice { null_slice() }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> FromWasmAbi for Vec<T> where Box<[T]>: FromWasmAbi<Abi = WasmSlice> {
|
||||||
|
type Abi = <Box<[T]> as FromWasmAbi>::Abi;
|
||||||
|
|
||||||
|
unsafe fn from_abi(js: Self::Abi, extra: &mut Stack) -> Self {
|
||||||
|
<Box<[T]>>::from_abi(js, extra).into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> OptionFromWasmAbi for Vec<T> where Box<[T]>: FromWasmAbi<Abi = WasmSlice> {
|
||||||
|
fn is_none(abi: &WasmSlice) -> bool { abi.ptr == 0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoWasmAbi for String {
|
||||||
|
type Abi = <Vec<u8> as IntoWasmAbi>::Abi;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn into_abi(self, extra: &mut Stack) -> Self::Abi {
|
||||||
|
self.into_bytes().into_abi(extra)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OptionIntoWasmAbi for String {
|
||||||
|
fn none() -> WasmSlice { null_slice() }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromWasmAbi for String {
|
||||||
|
type Abi = <Vec<u8> as FromWasmAbi>::Abi;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
unsafe fn from_abi(js: Self::Abi, extra: &mut Stack) -> Self {
|
||||||
|
String::from_utf8_unchecked(<Vec<u8>>::from_abi(js, extra))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OptionFromWasmAbi for String {
|
||||||
|
fn is_none(slice: &WasmSlice) -> bool { slice.ptr == 0 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> IntoWasmAbi for &'a str {
|
||||||
|
type Abi = <&'a [u8] as IntoWasmAbi>::Abi;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn into_abi(self, extra: &mut Stack) -> Self::Abi {
|
||||||
|
self.as_bytes().into_abi(extra)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> OptionIntoWasmAbi for &'a str {
|
||||||
|
fn none() -> WasmSlice { null_slice() }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RefFromWasmAbi for str {
|
||||||
|
type Abi = <[u8] as RefFromWasmAbi>::Abi;
|
||||||
|
type Anchor = &'static str;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
unsafe fn ref_from_abi(js: Self::Abi, extra: &mut Stack) -> Self::Anchor {
|
||||||
|
str::from_utf8_unchecked(<[u8]>::ref_from_abi(js, extra))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if_std! {
|
||||||
|
use JsValue;
|
||||||
|
|
||||||
|
impl IntoWasmAbi for Box<[JsValue]> {
|
||||||
|
type Abi = WasmSlice;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn into_abi(self, extra: &mut Stack) -> WasmSlice {
|
||||||
|
let ptr = self.as_ptr();
|
||||||
|
let len = self.len();
|
||||||
|
mem::forget(self);
|
||||||
|
WasmSlice {
|
||||||
|
ptr: ptr.into_abi(extra),
|
||||||
|
len: len as u32,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OptionIntoWasmAbi for Box<[JsValue]> {
|
||||||
|
fn none() -> WasmSlice { null_slice() }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromWasmAbi for Box<[JsValue]> {
|
||||||
|
type Abi = WasmSlice;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
unsafe fn from_abi(js: WasmSlice, extra: &mut Stack) -> Self {
|
||||||
|
let ptr = <*mut JsValue>::from_abi(js.ptr, extra);
|
||||||
|
let len = js.len as usize;
|
||||||
|
Vec::from_raw_parts(ptr, len, len).into_boxed_slice()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OptionFromWasmAbi for Box<[JsValue]> {
|
||||||
|
fn is_none(slice: &WasmSlice) -> bool { slice.ptr == 0 }
|
||||||
|
}
|
||||||
|
}
|
108
src/convert/traits.rs
Normal file
108
src/convert/traits.rs
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
use core::ops::{Deref, DerefMut};
|
||||||
|
|
||||||
|
use describe::*;
|
||||||
|
|
||||||
|
/// A trait for anything that can be converted into a type that can cross the
|
||||||
|
/// wasm ABI directly, eg `u32` or `f64`.
|
||||||
|
///
|
||||||
|
/// This is the opposite operation as `FromWasmAbi` and `Ref[Mut]FromWasmAbi`.
|
||||||
|
pub trait IntoWasmAbi: WasmDescribe {
|
||||||
|
/// The wasm ABI type that this converts into when crossing the ABI
|
||||||
|
/// boundary.
|
||||||
|
type Abi: WasmAbi;
|
||||||
|
|
||||||
|
/// Convert `self` into `Self::Abi` so that it can be sent across the wasm
|
||||||
|
/// ABI boundary.
|
||||||
|
fn into_abi(self, extra: &mut Stack) -> Self::Abi;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A trait for anything that can be recovered by-value from the wasm ABI
|
||||||
|
/// boundary, eg a Rust `u8` can be recovered from the wasm ABI `u32` type.
|
||||||
|
///
|
||||||
|
/// This is the by-value variant of the opposite operation as `IntoWasmAbi`.
|
||||||
|
pub trait FromWasmAbi: WasmDescribe {
|
||||||
|
/// The wasm ABI type that this converts from when coming back out from the
|
||||||
|
/// ABI boundary.
|
||||||
|
type Abi: WasmAbi;
|
||||||
|
|
||||||
|
/// Recover a `Self` from `Self::Abi`.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// This is only safe to call when -- and implementations may assume that --
|
||||||
|
/// the supplied `Self::Abi` was previously generated by a call to `<Self as
|
||||||
|
/// IntoWasmAbi>::into_abi()` or the moral equivalent in JS.
|
||||||
|
unsafe fn from_abi(js: Self::Abi, extra: &mut Stack) -> Self;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A trait for anything that can be recovered as some sort of shared reference
|
||||||
|
/// from the wasm ABI boundary.
|
||||||
|
///
|
||||||
|
/// This is the shared reference variant of the opposite operation as
|
||||||
|
/// `IntoWasmAbi`.
|
||||||
|
pub trait RefFromWasmAbi: WasmDescribe {
|
||||||
|
/// The wasm ABI type references to `Self` are recovered from.
|
||||||
|
type Abi: WasmAbi;
|
||||||
|
|
||||||
|
/// The type that holds the reference to `Self` for the duration of the
|
||||||
|
/// invocation of the function that has an `&Self` parameter. This is
|
||||||
|
/// required to ensure that the lifetimes don't persist beyond one function
|
||||||
|
/// call, and so that they remain anonymous.
|
||||||
|
type Anchor: Deref<Target = Self>;
|
||||||
|
|
||||||
|
/// Recover a `Self::Anchor` from `Self::Abi`.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// Same as `FromWasmAbi::from_abi`.
|
||||||
|
unsafe fn ref_from_abi(js: Self::Abi, extra: &mut Stack) -> Self::Anchor;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Dual of the `RefFromWasmAbi` trait, except for mutable references.
|
||||||
|
pub trait RefMutFromWasmAbi: WasmDescribe {
|
||||||
|
/// Same as `RefFromWasmAbi::Abi`
|
||||||
|
type Abi: WasmAbi;
|
||||||
|
/// Same as `RefFromWasmAbi::Anchor`
|
||||||
|
type Anchor: DerefMut<Target = Self>;
|
||||||
|
/// Same as `RefFromWasmAbi::ref_from_abi`
|
||||||
|
unsafe fn ref_mut_from_abi(js: Self::Abi, extra: &mut Stack) -> Self::Anchor;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Indicates that this type can be passed to JS as `Option<Self>`.
|
||||||
|
///
|
||||||
|
/// This trait is used when implementing `IntoWasmAbi for Option<T>`.
|
||||||
|
pub trait OptionIntoWasmAbi: IntoWasmAbi {
|
||||||
|
/// Returns an ABI instance indicating "none", which JS will interpret as
|
||||||
|
/// the `None` branch of this option.
|
||||||
|
///
|
||||||
|
/// It should be guaranteed that the `IntoWasmAbi` can never produce the ABI
|
||||||
|
/// value returned here.
|
||||||
|
fn none() -> Self::Abi;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Indicates that this type can be received from JS as `Option<Self>`.
|
||||||
|
///
|
||||||
|
/// This trait is used when implementing `FromWasmAbi for Option<T>`.
|
||||||
|
pub trait OptionFromWasmAbi: FromWasmAbi {
|
||||||
|
/// Tests whether the argument is a "none" instance. If so it will be
|
||||||
|
/// deserialized as `None`, and otherwise it will be passed to
|
||||||
|
/// `FromWasmAbi`.
|
||||||
|
fn is_none(abi: &Self::Abi) -> bool;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Stack {
|
||||||
|
fn push(&mut self, bits: u32);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An unsafe trait which represents types that are ABI-safe to pass via wasm
|
||||||
|
/// arguments.
|
||||||
|
///
|
||||||
|
/// This is an unsafe trait to implement as there's no guarantee the type is
|
||||||
|
/// actually safe to transfer across the was boundary, it's up to you to
|
||||||
|
/// guarantee this so codegen works correctly.
|
||||||
|
pub unsafe trait WasmAbi {}
|
||||||
|
|
||||||
|
unsafe impl WasmAbi for u32 {}
|
||||||
|
unsafe impl WasmAbi for i32 {}
|
||||||
|
unsafe impl WasmAbi for f32 {}
|
||||||
|
unsafe impl WasmAbi for f64 {}
|
@ -38,6 +38,7 @@ tys! {
|
|||||||
ENUM
|
ENUM
|
||||||
RUST_STRUCT
|
RUST_STRUCT
|
||||||
CHAR
|
CHAR
|
||||||
|
OPTIONAL
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn inform(a: u32) {
|
pub fn inform(a: u32) {
|
||||||
@ -195,3 +196,10 @@ doit! {
|
|||||||
(A B C D E F)
|
(A B C D E F)
|
||||||
(A B C D E F G)
|
(A B C D E F G)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T: WasmDescribe> WasmDescribe for Option<T> {
|
||||||
|
fn describe() {
|
||||||
|
inform(OPTIONAL);
|
||||||
|
T::describe();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
12
src/lib.rs
12
src/lib.rs
@ -61,8 +61,8 @@ pub struct JsValue {
|
|||||||
idx: u32,
|
idx: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
const JSIDX_NULL: u32 = 0;
|
const JSIDX_UNDEFINED: u32 = 0;
|
||||||
const JSIDX_UNDEFINED: u32 = 2;
|
const JSIDX_NULL: u32 = 2;
|
||||||
const JSIDX_TRUE: u32 = 4;
|
const JSIDX_TRUE: u32 = 4;
|
||||||
const JSIDX_FALSE: u32 = 6;
|
const JSIDX_FALSE: u32 = 6;
|
||||||
const JSIDX_RESERVED: u32 = 8;
|
const JSIDX_RESERVED: u32 = 8;
|
||||||
@ -345,11 +345,8 @@ externs! {
|
|||||||
fn __wbindgen_string_new(ptr: *const u8, len: usize) -> u32;
|
fn __wbindgen_string_new(ptr: *const u8, len: usize) -> u32;
|
||||||
fn __wbindgen_number_new(f: f64) -> u32;
|
fn __wbindgen_number_new(f: f64) -> u32;
|
||||||
fn __wbindgen_number_get(idx: u32, invalid: *mut u8) -> f64;
|
fn __wbindgen_number_get(idx: u32, invalid: *mut u8) -> f64;
|
||||||
fn __wbindgen_null_new() -> u32;
|
|
||||||
fn __wbindgen_undefined_new() -> u32;
|
|
||||||
fn __wbindgen_is_null(idx: u32) -> u32;
|
fn __wbindgen_is_null(idx: u32) -> u32;
|
||||||
fn __wbindgen_is_undefined(idx: u32) -> u32;
|
fn __wbindgen_is_undefined(idx: u32) -> u32;
|
||||||
fn __wbindgen_boolean_new(val: u32) -> u32;
|
|
||||||
fn __wbindgen_boolean_get(idx: u32) -> u32;
|
fn __wbindgen_boolean_get(idx: u32) -> u32;
|
||||||
fn __wbindgen_symbol_new(ptr: *const u8, len: usize) -> u32;
|
fn __wbindgen_symbol_new(ptr: *const u8, len: usize) -> u32;
|
||||||
fn __wbindgen_is_symbol(idx: u32) -> u32;
|
fn __wbindgen_is_symbol(idx: u32) -> u32;
|
||||||
@ -673,6 +670,11 @@ pub mod __rt {
|
|||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern fn __wbindgen_free(ptr: *mut u8, size: usize) {
|
pub unsafe extern fn __wbindgen_free(ptr: *mut u8, size: usize) {
|
||||||
|
// This happens for zero-length slices, and in that case `ptr` is
|
||||||
|
// likely bogus so don't actually send this to the system allocator
|
||||||
|
if size == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
let align = mem::align_of::<usize>();
|
let align = mem::align_of::<usize>();
|
||||||
let layout = Layout::from_size_align_unchecked(size, align);
|
let layout = Layout::from_size_align_unchecked(size, align);
|
||||||
System.dealloc(ptr, layout);
|
System.dealloc(ptr, layout);
|
||||||
|
@ -194,3 +194,40 @@ fn eq_works() {
|
|||||||
)
|
)
|
||||||
.test();
|
.test();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn null_keeps_working() {
|
||||||
|
project()
|
||||||
|
.file(
|
||||||
|
"src/lib.rs",
|
||||||
|
r#"
|
||||||
|
#![feature(use_extern_macros, wasm_import_module)]
|
||||||
|
|
||||||
|
extern crate wasm_bindgen;
|
||||||
|
|
||||||
|
use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
|
#[wasm_bindgen(module = "./foo")]
|
||||||
|
extern {
|
||||||
|
fn take_null(val: JsValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn test() {
|
||||||
|
take_null(JsValue::null());
|
||||||
|
take_null(JsValue::null());
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.file(
|
||||||
|
"foo.js",
|
||||||
|
r#"
|
||||||
|
import { strictEqual } from "assert";
|
||||||
|
|
||||||
|
export function take_null(n) {
|
||||||
|
strictEqual(n, null);
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.test();
|
||||||
|
}
|
||||||
|
@ -505,3 +505,103 @@ fn deny_missing_docs() {
|
|||||||
)
|
)
|
||||||
.test();
|
.test();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn options() {
|
||||||
|
project()
|
||||||
|
.file(
|
||||||
|
"src/lib.rs",
|
||||||
|
r#"
|
||||||
|
#![feature(use_extern_macros, wasm_import_module)]
|
||||||
|
|
||||||
|
extern crate wasm_bindgen;
|
||||||
|
|
||||||
|
use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
|
#[wasm_bindgen(module = "./foo")]
|
||||||
|
extern {
|
||||||
|
pub type Foo;
|
||||||
|
#[wasm_bindgen(constructor)]
|
||||||
|
fn new() -> Foo;
|
||||||
|
|
||||||
|
fn take_none(val: Option<Foo>);
|
||||||
|
fn take_some(val: Option<Foo>);
|
||||||
|
fn return_null() -> Option<Foo>;
|
||||||
|
fn return_undefined() -> Option<Foo>;
|
||||||
|
fn return_some() -> Option<Foo>;
|
||||||
|
fn run_rust_tests();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn test() {
|
||||||
|
take_none(None);
|
||||||
|
take_some(Some(Foo::new()));
|
||||||
|
assert!(return_null().is_none());
|
||||||
|
assert!(return_undefined().is_none());
|
||||||
|
assert!(return_some().is_some());
|
||||||
|
run_rust_tests();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn rust_take_none(a: Option<Foo>) {
|
||||||
|
assert!(a.is_none());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn rust_take_some(a: Option<Foo>) {
|
||||||
|
assert!(a.is_some());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn rust_return_none() -> Option<Foo> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn rust_return_some() -> Option<Foo> {
|
||||||
|
Some(Foo::new())
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.file(
|
||||||
|
"foo.js",
|
||||||
|
r#"
|
||||||
|
import { strictEqual } from "assert";
|
||||||
|
import * as wasm from "./out";
|
||||||
|
|
||||||
|
export class Foo {
|
||||||
|
}
|
||||||
|
|
||||||
|
export function take_none(val) {
|
||||||
|
strictEqual(val, undefined);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function take_some(val) {
|
||||||
|
strictEqual(val === undefined, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function return_null() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function return_undefined() {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function return_some() {
|
||||||
|
return new Foo();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function run_rust_tests() {
|
||||||
|
wasm.rust_take_none();
|
||||||
|
wasm.rust_take_none(null);
|
||||||
|
wasm.rust_take_none(undefined);
|
||||||
|
wasm.rust_take_some(new Foo());
|
||||||
|
strictEqual(wasm.rust_return_none(), undefined);
|
||||||
|
strictEqual(wasm.rust_return_none(), undefined);
|
||||||
|
strictEqual(wasm.rust_return_some() === undefined, false);
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.test();
|
||||||
|
}
|
||||||
|
@ -447,3 +447,118 @@ fn binding_to_unimplemented_apis_doesnt_break_everything() {
|
|||||||
)
|
)
|
||||||
.test();
|
.test();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn optional_slices() {
|
||||||
|
project()
|
||||||
|
.file(
|
||||||
|
"src/lib.rs",
|
||||||
|
r#"
|
||||||
|
#![feature(use_extern_macros, wasm_custom_section, wasm_import_module)]
|
||||||
|
extern crate wasm_bindgen;
|
||||||
|
use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
|
#[wasm_bindgen(module = "./foo")]
|
||||||
|
extern {
|
||||||
|
fn optional_str_none(a: Option<&str>);
|
||||||
|
fn optional_str_some(a: Option<&str>);
|
||||||
|
fn optional_slice_none(a: Option<&[u8]>);
|
||||||
|
fn optional_slice_some(a: Option<&[u8]>);
|
||||||
|
|
||||||
|
fn optional_string_none(a: Option<String>);
|
||||||
|
fn optional_string_some(a: Option<String>);
|
||||||
|
fn optional_string_some_empty(a: Option<String>);
|
||||||
|
|
||||||
|
fn return_string_none() -> Option<String>;
|
||||||
|
fn return_string_some() -> Option<String>;
|
||||||
|
|
||||||
|
fn run_rust_tests();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn test() {
|
||||||
|
optional_str_none(None);
|
||||||
|
optional_str_some(Some("x"));
|
||||||
|
optional_slice_none(None);
|
||||||
|
optional_slice_some(Some(&[1, 2, 3]));
|
||||||
|
optional_string_none(None);
|
||||||
|
optional_string_some_empty(Some(String::new()));
|
||||||
|
optional_string_some(Some("abcd".to_string()));
|
||||||
|
|
||||||
|
assert_eq!(return_string_none(), None);
|
||||||
|
assert_eq!(return_string_some(), Some("foo".to_string()));
|
||||||
|
run_rust_tests();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn take_optional_str_none(x: Option<String>) {
|
||||||
|
assert!(x.is_none())
|
||||||
|
}
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn take_optional_str_some(x: Option<String>) {
|
||||||
|
assert_eq!(x, Some(String::from("hello")));
|
||||||
|
}
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn return_optional_str_none() -> Option<String> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn return_optional_str_some() -> Option<String> {
|
||||||
|
Some("world".to_string())
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.file(
|
||||||
|
"foo.js",
|
||||||
|
r#"
|
||||||
|
import { strictEqual } from "assert";
|
||||||
|
import * as wasm from "./out";
|
||||||
|
|
||||||
|
export function optional_str_none(x) {
|
||||||
|
strictEqual(x, undefined);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function optional_str_some(x) {
|
||||||
|
strictEqual(x, 'x');
|
||||||
|
}
|
||||||
|
|
||||||
|
export function optional_slice_none(x) {
|
||||||
|
strictEqual(x, undefined);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function optional_slice_some(x) {
|
||||||
|
strictEqual(x.length, 3);
|
||||||
|
strictEqual(x[0], 1);
|
||||||
|
strictEqual(x[1], 2);
|
||||||
|
strictEqual(x[2], 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function optional_string_none(x) {
|
||||||
|
strictEqual(x, undefined);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function optional_string_some(x) {
|
||||||
|
strictEqual(x, 'abcd');
|
||||||
|
}
|
||||||
|
|
||||||
|
export function optional_string_some_empty(x) {
|
||||||
|
strictEqual(x, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
export function return_string_none() {}
|
||||||
|
export function return_string_some() {
|
||||||
|
return 'foo';
|
||||||
|
}
|
||||||
|
|
||||||
|
export function run_rust_tests() {
|
||||||
|
wasm.take_optional_str_none();
|
||||||
|
wasm.take_optional_str_none(null);
|
||||||
|
wasm.take_optional_str_none(undefined);
|
||||||
|
wasm.take_optional_str_some('hello');
|
||||||
|
strictEqual(wasm.return_optional_str_none(), undefined);
|
||||||
|
strictEqual(wasm.return_optional_str_some(), 'world');
|
||||||
|
}
|
||||||
|
"#
|
||||||
|
)
|
||||||
|
.test();
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user