Skip to content

Add support for optional slice types #507

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Jul 19, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions crates/backend/src/codegen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
type Abi = <::wasm_bindgen::JsValue as
::wasm_bindgen::convert::FromWasmAbi>::Abi;
Expand All @@ -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 {
type Abi = <&'a ::wasm_bindgen::JsValue as
::wasm_bindgen::convert::IntoWasmAbi>::Abi;
Expand Down
3 changes: 3 additions & 0 deletions crates/cli-support/src/descriptor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ tys! {
ENUM
RUST_STRUCT
CHAR
OPTIONAL
}

#[derive(Debug)]
Expand All @@ -59,6 +60,7 @@ pub enum Descriptor {
Enum,
RustStruct(String),
Char,
Option(Box<Descriptor>),
}

#[derive(Debug)]
Expand Down Expand Up @@ -115,6 +117,7 @@ impl Descriptor {
REFMUT => Descriptor::RefMut(Box::new(Descriptor::_decode(data))),
SLICE => Descriptor::Slice(Box::new(Descriptor::_decode(data))),
VECTOR => Descriptor::Vector(Box::new(Descriptor::_decode(data))),
OPTIONAL => Descriptor::Option(Box::new(Descriptor::_decode(data))),
STRING => Descriptor::String,
ANYREF => Descriptor::Anyref,
ENUM => Descriptor::Enum,
Expand Down
92 changes: 66 additions & 26 deletions crates/cli-support/src/js/js2rust.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,20 +122,31 @@ impl<'a, 'b> Js2Rust<'a, 'b> {
let i = self.arg_idx;
let name = self.abi_arg();

let (arg, optional) = match arg {
Descriptor::Option(t) => (&**t, true),
_ => (arg, false),
};

if let Some(kind) = arg.vector_kind() {
self.js_arguments
.push((name.clone(), kind.js_ty().to_string()));

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!(
"\
const [ptr{i}, len{i}] = {func}({arg});\n\
",
"const [ptr{i}, len{i}] = {val};",
i = i,
func = func,
arg = name
val = val,
));
if arg.is_by_ref() {
if optional {
bail!("optional slices aren't currently supported");
}
if arg.is_mut_ref() {
let get = self.cx.memview_function(kind);
self.finally(&format!(
Expand Down Expand Up @@ -165,6 +176,25 @@ impl<'a, 'b> Js2Rust<'a, 'b> {
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() {
self.js_arguments.push((name.clone(), s.to_string()));

Expand Down Expand Up @@ -262,11 +292,6 @@ impl<'a, 'b> Js2Rust<'a, 'b> {
self.js_arguments.push((name.clone(), "string".to_string()));
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),
}
Ok(self)
Expand All @@ -282,16 +307,10 @@ impl<'a, 'b> Js2Rust<'a, 'b> {
}
};

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")
}
let (ty, optional) = match ty {
Descriptor::Option(t) => (&**t, true),
_ => (ty, false),
};

if let Some(ty) = ty.vector_kind() {
self.ret_ty = ty.js_ty().to_string();
Expand All @@ -307,16 +326,42 @@ impl<'a, 'b> Js2Rust<'a, 'b> {
const mem = getUint32Memory();\n\
const ptr = mem[retptr / 4];\n\
const len = mem[retptr / 4 + 1];\n\
{guard}
const realRet = {}(ptr, len).slice();\n\
wasm.__wbindgen_free(ptr, len * {});\n\
return realRet;\n\
",
f,
ty.size()
ty.size(),
guard = if optional { "if (ptr === 0) return;" } else { "" },
);
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() {
self.ret_ty = name.to_string();
self.ret_expr = format!("return {name}.__construct(RET);", name = name);
Expand Down Expand Up @@ -360,11 +405,6 @@ impl<'a, 'b> Js2Rust<'a, 'b> {
self.ret_ty = "string".to_string();
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),
}
Ok(self)
Expand Down
65 changes: 22 additions & 43 deletions crates/cli-support/src/js/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ pub struct SubContext<'a, 'b: 'a> {
pub cx: &'a mut Context<'b>,
}

const INITIAL_SLAB_VALUES: &[&str] = &["undefined", "null", "true", "false"];

impl<'a> Context<'a> {
fn export(&mut self, name: &str, contents: &str, comments: Option<String>) {
let contents = contents.trim();
Expand Down Expand Up @@ -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| {
me.expose_get_object();
Ok(String::from(
Expand All @@ -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| {
me.expose_get_object();
Ok(String::from(
Expand Down Expand Up @@ -782,14 +751,16 @@ impl<'a> Context<'a> {
"
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
slab[idx >> 1] = slab_next;
slab_next = idx >> 1;
slab[idx] = slab_next;
slab_next = idx;
}}
",
validate_owned, dec_ref
validate_owned, INITIAL_SLAB_VALUES.len(), dec_ref
));
}

Expand Down Expand Up @@ -820,12 +791,9 @@ impl<'a> Context<'a> {
if !self.exposed_globals.insert("slab") {
return;
}
let initial_values = [
"{ obj: null }",
"{ obj: undefined }",
"{ obj: true }",
"{ obj: false }",
];
let initial_values = INITIAL_SLAB_VALUES.iter()
.map(|s| format!("{{ obj: {} }}", s))
.collect::<Vec<_>>();
self.global(&format!("const slab = [{}];", initial_values.join(", ")));
if self.config.debug {
self.export(
Expand Down Expand Up @@ -1575,6 +1543,17 @@ impl<'a> Context<'a> {
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> {
let module = mem::replace(self.module, Module::default());
let wasm_bytes = parity_wasm::serialize(module)?;
Expand Down
Loading