From d9fd2147a03760574e30d630720096cfd0afab1e Mon Sep 17 00:00:00 2001 From: Richard Dodd Date: Sat, 18 Aug 2018 22:15:29 +0100 Subject: [PATCH 01/13] [wip] support variadic javascript function parameters --- crates/backend/src/ast.rs | 2 ++ crates/macro-support/src/parser.rs | 55 ++++++++++++++++++++++++++++++ crates/shared/src/lib.rs | 1 + tests/wasm/main.rs | 1 + tests/wasm/variadic.js | 6 ++++ tests/wasm/variadic.rs | 16 +++++++++ 6 files changed, 81 insertions(+) create mode 100644 tests/wasm/variadic.js create mode 100644 tests/wasm/variadic.rs diff --git a/crates/backend/src/ast.rs b/crates/backend/src/ast.rs index d8989e35..e229131a 100644 --- a/crates/backend/src/ast.rs +++ b/crates/backend/src/ast.rs @@ -85,6 +85,7 @@ pub struct ImportFunction { pub rust_name: Ident, pub js_ret: Option, pub catch: bool, + pub variadic: bool, pub structural: bool, pub kind: ImportFunctionKind, pub shim: Ident, @@ -449,6 +450,7 @@ impl ImportFunction { shared::ImportFunction { shim: self.shim.to_string(), catch: self.catch, + variadic: self.variadic, method, structural: self.structural, function: self.function.shared(), diff --git a/crates/macro-support/src/parser.rs b/crates/macro-support/src/parser.rs index 18b198a6..824ee147 100644 --- a/crates/macro-support/src/parser.rs +++ b/crates/macro-support/src/parser.rs @@ -183,6 +183,14 @@ impl BindgenAttrs { _ => None, }) } + + /// Whether the variadic attributes is present + fn variadic(&self) -> bool { + self.attrs.iter().any(|a| match *a { + BindgenAttr::Variadic => true, + _ => false, + }) + } } impl syn::synom::Synom for BindgenAttrs { @@ -219,6 +227,7 @@ pub enum BindgenAttr { JsName(String), JsClass(String), Extends(Ident), + Variadic, } impl syn::synom::Synom for BindgenAttr { @@ -304,6 +313,8 @@ impl syn::synom::Synom for BindgenAttr { ns: call!(term2ident) >> (ns) )=> { BindgenAttr::Extends } + | + call!(term, "variadic") => { |_| BindgenAttr::Variadic } )); } @@ -365,6 +376,7 @@ impl<'a> ConvertToAst<()> for &'a mut syn::ItemStruct { let getter = shared::struct_field_get(&ident, &name_str); let setter = shared::struct_field_set(&ident, &name_str); let opts = BindgenAttrs::find(&mut field.attrs)?; + assert_not_variadic(&opts)?; let comments = extract_doc_comments(&field.attrs); fields.push(ast::StructField { name: name.clone(), @@ -395,6 +407,7 @@ impl<'a> ConvertToAst<(BindgenAttrs, &'a Option)> for syn::ForeignItemFn ) -> Result { let default_name = self.ident.to_string(); let js_name = opts.js_name().unwrap_or(&default_name); + let wasm = function_from_decl( js_name, self.decl.clone(), @@ -404,6 +417,10 @@ impl<'a> ConvertToAst<(BindgenAttrs, &'a Option)> for syn::ForeignItemFn None, )?.0; let catch = opts.catch(); + let variadic = opts.variadic(); + if variadic { + assert_last_param_is_slice(&self.decl)?; + } let js_ret = if catch { // TODO: this assumes a whole bunch: // @@ -532,6 +549,7 @@ impl<'a> ConvertToAst<(BindgenAttrs, &'a Option)> for syn::ForeignItemFn kind, js_ret, catch, + variadic, structural: opts.structural(), rust_name: self.ident.clone(), shim: Ident::new(&shim, Span::call_site()), @@ -544,6 +562,7 @@ impl ConvertToAst for syn::ForeignItemType { type Target = ast::ImportKind; fn convert(self, attrs: BindgenAttrs) -> Result { + assert_not_variadic(&attrs)?; let js_name = attrs .js_name() .map_or_else(|| self.ident.to_string(), |s| s.to_string()); @@ -567,6 +586,7 @@ impl ConvertToAst for syn::ForeignItemStatic { if self.mutability.is_some() { bail_span!(self.mutability, "cannot import mutable globals yet") } + assert_not_variadic(&opts)?; let default_name = self.ident.to_string(); let js_name = opts.js_name().unwrap_or(&default_name); let shim = format!( @@ -604,6 +624,7 @@ impl ConvertToAst for syn::ItemFn { if self.unsafety.is_some() { bail_span!(self.unsafety, "can only #[wasm_bindgen] safe functions"); } + assert_not_variadic(&attrs)?; let default_name = self.ident.to_string(); let name = attrs.js_name().unwrap_or(&default_name); @@ -1074,6 +1095,40 @@ fn assert_no_lifetimes(decl: &syn::FnDecl) -> Result<(), Diagnostic> { Diagnostic::from_vec(walk.diagnostics) } +/// This method always fails if the BindgenAttrs contain variadic +fn assert_not_variadic(attrs: &BindgenAttrs) -> Result<(), Diagnostic> { + match attrs.variadic() { + true => Err(Diagnostic::error("the `variadic` attribute can only be applied to imported \ + (`extern`) functions")), + false => Ok(()) + } +} + +/// Checks the function signature to ensure it finishes with a slice +/// +/// Example of a valid signature: `fn my_func(arg1: u64, res: &[u64])`. +fn assert_last_param_is_slice(decl: &syn::FnDecl) -> Result<(), Diagnostic> { + #[inline] + fn not_slice_error(tok: &dyn ToTokens) -> Diagnostic { + Diagnostic::span_error(tok, "for variadic extern functions, the last argument must be a \ + slice, to hold the arguments of unknown length") + } + + let arg = decl.inputs.last().ok_or_else(|| not_slice_error(&decl.inputs))?; + let ty = match arg.value() { + syn::FnArg::Captured(ref arg_cap) => &arg_cap.ty, + _ => return Err(not_slice_error(&arg)) + }; + match ty { + syn::Type::Reference(ref ref_ty) => match &*ref_ty.elem { + syn::Type::Slice(_) => Ok(()), + _ => Err(not_slice_error(ty)) + }, + _ => Err(not_slice_error(ty)) + } + +} + /// If the path is a single ident, return it. fn extract_path_ident(path: &syn::Path) -> Result { if path.leading_colon.is_some() { diff --git a/crates/shared/src/lib.rs b/crates/shared/src/lib.rs index 20d78db7..af9205ef 100644 --- a/crates/shared/src/lib.rs +++ b/crates/shared/src/lib.rs @@ -41,6 +41,7 @@ pub enum ImportKind { pub struct ImportFunction { pub shim: String, pub catch: bool, + pub variadic: bool, pub method: Option, pub structural: bool, pub function: Function, diff --git a/tests/wasm/main.rs b/tests/wasm/main.rs index 6b783f93..07cc2c5d 100644 --- a/tests/wasm/main.rs +++ b/tests/wasm/main.rs @@ -31,3 +31,4 @@ pub mod slice; pub mod structural; pub mod u64; pub mod validate_prt; +pub mod variadic; diff --git a/tests/wasm/variadic.js b/tests/wasm/variadic.js new file mode 100644 index 00000000..71a722b6 --- /dev/null +++ b/tests/wasm/variadic.js @@ -0,0 +1,6 @@ +const wasm = require('wasm-bindgen-test.js'); +const assert = require('assert'); + +exports.variadic_sum = () => + Array.from(arguments).reduce((acc, val) => acc + val, 0); + diff --git a/tests/wasm/variadic.rs b/tests/wasm/variadic.rs new file mode 100644 index 00000000..5fc56e80 --- /dev/null +++ b/tests/wasm/variadic.rs @@ -0,0 +1,16 @@ +use wasm_bindgen_test::*; +use wasm_bindgen::prelude::*; + +#[wasm_bindgen(module = "tests/wasm/variadic.js")] +extern { + #[wasm_bindgen(variadic)] + fn variadic_sum(first: f64, second: f64, rest: &[f64]) -> f64; +} + +#[wasm_bindgen_test] +fn variadic_simple() { + assert_eq!(variadic_sum(1., 2., &[]), 3.); + assert_eq!(variadic_sum(1., 2., &[3.]), 6.); + assert_eq!(variadic_sum(1., 2., &[3., 4.]), 10.); +} + From a4835304eb473b4fba982fe78f2b799b516aab87 Mon Sep 17 00:00:00 2001 From: Richard Dodd Date: Sun, 19 Aug 2018 13:39:16 +0100 Subject: [PATCH 02/13] Add codegen to make test work. --- crates/cli-support/src/js/mod.rs | 2 +- crates/cli-support/src/js/rust2js.rs | 44 +++++++++++++++++++++++++--- tests/wasm/variadic.js | 11 +++++-- 3 files changed, 50 insertions(+), 7 deletions(-) diff --git a/crates/cli-support/src/js/mod.rs b/crates/cli-support/src/js/mod.rs index 220f6e70..b52f0e4d 100644 --- a/crates/cli-support/src/js/mod.rs +++ b/crates/cli-support/src/js/mod.rs @@ -1614,7 +1614,6 @@ impl<'a> Context<'a> { } fn global(&mut self, s: &str) { - let s = s; let s = s.trim(); // Ensure a blank line between adjacent items, and ensure everything is @@ -1941,6 +1940,7 @@ impl<'a, 'b> SubContext<'a, 'b> { let js = Rust2Js::new(self.cx) .catch(import.catch) + .variadic(import.variadic) .process(descriptor.unwrap_function())? .finish(&target); self.cx.export(&import.shim, &js, None); diff --git a/crates/cli-support/src/js/rust2js.rs b/crates/cli-support/src/js/rust2js.rs index cb607f7c..27755ae5 100644 --- a/crates/cli-support/src/js/rust2js.rs +++ b/crates/cli-support/src/js/rust2js.rs @@ -36,6 +36,9 @@ pub struct Rust2Js<'a, 'b: 'a> { /// Whether or not we're catching JS exceptions catch: bool, + + /// Whether or not the last argument is a slice representing variadic arguments. + variadic: bool, } impl<'a, 'b> Rust2Js<'a, 'b> { @@ -50,6 +53,7 @@ impl<'a, 'b> Rust2Js<'a, 'b> { arg_idx: 0, ret_expr: String::new(), catch: false, + variadic: false, } } @@ -62,6 +66,15 @@ impl<'a, 'b> Rust2Js<'a, 'b> { self } + pub fn variadic(&mut self, variadic: bool) -> &mut Self { + if variadic { + self.cx.expose_uint32_memory(); + self.cx.expose_add_heap_object(); + } + self.variadic = variadic; + self + } + /// Generates all bindings necessary for the signature in `Function`, /// creating necessary argument conversions and return value processing. pub fn process(&mut self, function: &Function) -> Result<&mut Self, Error> { @@ -72,6 +85,7 @@ impl<'a, 'b> Rust2Js<'a, 'b> { Ok(self) } + /// Get a generated name for an argument. fn shim_argument(&mut self) -> String { let s = format!("arg{}", self.arg_idx); self.arg_idx += 1; @@ -516,6 +530,7 @@ impl<'a, 'b> Rust2Js<'a, 'b> { } pub fn finish(&self, invoc: &str) -> String { + use std::fmt::Write; let mut ret = String::new(); ret.push_str("function("); ret.push_str(&self.shim_arguments.join(", ")); @@ -528,10 +543,31 @@ impl<'a, 'b> Rust2Js<'a, 'b> { ret.push_str(") {\n"); ret.push_str(&self.prelude); - let mut invoc = self.ret_expr.replace( - "JS", - &format!("{}({})", invoc, self.js_arguments.join(", ")), - ); + let mut invoc = if self.variadic { + if self.js_arguments.is_empty() { + unreachable!("the last argument of a variadic must be a slice"); + } + let last_arg = self.js_arguments.len() - 1; // check implies > 0 + // The variadic part of the arguments could be passed as as a typed array in + // javascript, so we cannot just do `.concat(..)`, as this seems to convert the + // contents to strings (javascript is very strange). + write!(ret, "\ + let args = [{}];\n\ + {}.forEach(extra_arg => args.push(extra_arg));\n\ + ", + self.js_arguments[..last_arg].join(", "), + self.js_arguments[last_arg] + ).unwrap(); + self.ret_expr.replace( + "JS", + &format!("{}.apply(this, args)", invoc), + ) + } else { + self.ret_expr.replace( + "JS", + &format!("{}({})", invoc, self.js_arguments.join(", ")), + ) + }; if self.catch { let catch = "\ const view = getUint32Memory();\n\ diff --git a/tests/wasm/variadic.js b/tests/wasm/variadic.js index 71a722b6..ed850df9 100644 --- a/tests/wasm/variadic.js +++ b/tests/wasm/variadic.js @@ -1,6 +1,13 @@ const wasm = require('wasm-bindgen-test.js'); const assert = require('assert'); -exports.variadic_sum = () => - Array.from(arguments).reduce((acc, val) => acc + val, 0); +function variadic_sum() { + let answer = 0; + for(var i=0; i Date: Sun, 19 Aug 2018 13:41:23 +0100 Subject: [PATCH 03/13] Comment typo --- crates/cli-support/src/js/rust2js.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/cli-support/src/js/rust2js.rs b/crates/cli-support/src/js/rust2js.rs index 27755ae5..91045015 100644 --- a/crates/cli-support/src/js/rust2js.rs +++ b/crates/cli-support/src/js/rust2js.rs @@ -547,7 +547,7 @@ impl<'a, 'b> Rust2Js<'a, 'b> { if self.js_arguments.is_empty() { unreachable!("the last argument of a variadic must be a slice"); } - let last_arg = self.js_arguments.len() - 1; // check implies > 0 + let last_arg = self.js_arguments.len() - 1; // check implies >= 0 // The variadic part of the arguments could be passed as as a typed array in // javascript, so we cannot just do `.concat(..)`, as this seems to convert the // contents to strings (javascript is very strange). From 003cf102f15ec4d23a430363345faec8a408c7a7 Mon Sep 17 00:00:00 2001 From: Richard Dodd Date: Sun, 19 Aug 2018 14:28:45 +0100 Subject: [PATCH 04/13] Fix webidl --- crates/webidl/src/util.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/webidl/src/util.rs b/crates/webidl/src/util.rs index aa80f93c..d4450d41 100644 --- a/crates/webidl/src/util.rs +++ b/crates/webidl/src/util.rs @@ -341,6 +341,7 @@ impl<'src> FirstPassRecord<'src> { rust_name, js_ret: js_ret.clone(), catch, + variadic: false, structural, kind: kind.clone(), shim, From 385e80550915518b76a828a404bd568b4f15b1ea Mon Sep 17 00:00:00 2001 From: Richard Dodd Date: Tue, 21 Aug 2018 12:55:09 +0100 Subject: [PATCH 05/13] Work on review comments --- crates/cli-support/src/js/rust2js.rs | 15 ++-- crates/macro-support/src/parser.rs | 18 ++--- tests/wasm/variadic.js | 39 ++++++++-- tests/wasm/variadic.rs | 102 +++++++++++++++++++++++++-- 4 files changed, 145 insertions(+), 29 deletions(-) diff --git a/crates/cli-support/src/js/rust2js.rs b/crates/cli-support/src/js/rust2js.rs index 91045015..e0f1ebb4 100644 --- a/crates/cli-support/src/js/rust2js.rs +++ b/crates/cli-support/src/js/rust2js.rs @@ -548,19 +548,12 @@ impl<'a, 'b> Rust2Js<'a, 'b> { unreachable!("the last argument of a variadic must be a slice"); } let last_arg = self.js_arguments.len() - 1; // check implies >= 0 - // The variadic part of the arguments could be passed as as a typed array in - // javascript, so we cannot just do `.concat(..)`, as this seems to convert the - // contents to strings (javascript is very strange). - write!(ret, "\ - let args = [{}];\n\ - {}.forEach(extra_arg => args.push(extra_arg));\n\ - ", - self.js_arguments[..last_arg].join(", "), - self.js_arguments[last_arg] - ).unwrap(); self.ret_expr.replace( "JS", - &format!("{}.apply(this, args)", invoc), + &format!("{}({}, ...{})", + invoc, + self.js_arguments[..last_arg].join(", "), + self.js_arguments[last_arg]) ) } else { self.ret_expr.replace( diff --git a/crates/macro-support/src/parser.rs b/crates/macro-support/src/parser.rs index 824ee147..9dd61eba 100644 --- a/crates/macro-support/src/parser.rs +++ b/crates/macro-support/src/parser.rs @@ -376,7 +376,7 @@ impl<'a> ConvertToAst<()> for &'a mut syn::ItemStruct { let getter = shared::struct_field_get(&ident, &name_str); let setter = shared::struct_field_set(&ident, &name_str); let opts = BindgenAttrs::find(&mut field.attrs)?; - assert_not_variadic(&opts)?; + assert_not_variadic(&opts, &field)?; let comments = extract_doc_comments(&field.attrs); fields.push(ast::StructField { name: name.clone(), @@ -562,7 +562,7 @@ impl ConvertToAst for syn::ForeignItemType { type Target = ast::ImportKind; fn convert(self, attrs: BindgenAttrs) -> Result { - assert_not_variadic(&attrs)?; + assert_not_variadic(&attrs, &self)?; let js_name = attrs .js_name() .map_or_else(|| self.ident.to_string(), |s| s.to_string()); @@ -586,7 +586,7 @@ impl ConvertToAst for syn::ForeignItemStatic { if self.mutability.is_some() { bail_span!(self.mutability, "cannot import mutable globals yet") } - assert_not_variadic(&opts)?; + assert_not_variadic(&opts, &self)?; let default_name = self.ident.to_string(); let js_name = opts.js_name().unwrap_or(&default_name); let shim = format!( @@ -624,7 +624,7 @@ impl ConvertToAst for syn::ItemFn { if self.unsafety.is_some() { bail_span!(self.unsafety, "can only #[wasm_bindgen] safe functions"); } - assert_not_variadic(&attrs)?; + assert_not_variadic(&attrs, &self)?; let default_name = self.ident.to_string(); let name = attrs.js_name().unwrap_or(&default_name); @@ -1096,12 +1096,12 @@ fn assert_no_lifetimes(decl: &syn::FnDecl) -> Result<(), Diagnostic> { } /// This method always fails if the BindgenAttrs contain variadic -fn assert_not_variadic(attrs: &BindgenAttrs) -> Result<(), Diagnostic> { - match attrs.variadic() { - true => Err(Diagnostic::error("the `variadic` attribute can only be applied to imported \ - (`extern`) functions")), - false => Ok(()) +fn assert_not_variadic(attrs: &BindgenAttrs, span: &dyn ToTokens) -> Result<(), Diagnostic> { + if attrs.variadic() { + bail_span!(span, "the `variadic` attribute can only be applied to imported \ + (`extern`) functions") } + Ok(()) } /// Checks the function signature to ensure it finishes with a slice diff --git a/tests/wasm/variadic.js b/tests/wasm/variadic.js index ed850df9..ccd64d4d 100644 --- a/tests/wasm/variadic.js +++ b/tests/wasm/variadic.js @@ -1,13 +1,44 @@ const wasm = require('wasm-bindgen-test.js'); const assert = require('assert'); -function variadic_sum() { +function variadic_sum(...args) { let answer = 0; - for(var i=0; i f64; + fn variadic_sum_u8(first: u8, second: u8, rest: &[u8]) -> u8; + #[wasm_bindgen(variadic)] + fn variadic_sum_u16(first: u16, second: u16, rest: &[u16]) -> u16; + #[wasm_bindgen(variadic)] + fn variadic_sum_u32(first: u32, second: u32, rest: &[u32]) -> u32; + #[wasm_bindgen(variadic)] + fn variadic_sum_u64(first: u64, second: u64, rest: &[u64]) -> u64; + #[wasm_bindgen(variadic)] + fn variadic_sum_usize(first: usize, second: usize, rest: &[usize]) -> usize; + #[wasm_bindgen(variadic)] + fn variadic_sum_i8(first: i8, second: i8, rest: &[i8]) -> i8; + #[wasm_bindgen(variadic)] + fn variadic_sum_i16(first: i16, second: i16, rest: &[i16]) -> i16; + #[wasm_bindgen(variadic)] + fn variadic_sum_i32(first: i32, second: i32, rest: &[i32]) -> i32; + #[wasm_bindgen(variadic)] + fn variadic_sum_i64(first: i64, second: i64, rest: &[i64]) -> i64; + #[wasm_bindgen(variadic)] + fn variadic_sum_isize(first: isize, second: isize, rest: &[isize]) -> isize; + #[wasm_bindgen(variadic)] + fn variadic_sum_f32(first: f32, second: f32, rest: &[f32]) -> f32; + #[wasm_bindgen(variadic)] + fn variadic_sum_f64(first: f64, second: f64, rest: &[f64]) -> f64; + #[wasm_bindgen(variadic)] + fn variadic_sum_opt(first: Option, second: Option, rest: &[Option]) -> u32; + #[wasm_bindgen(variadic)] + fn variadic_concat_str(first: &str, second: &str, rest: &[&str]) -> String; + #[wasm_bindgen(variadic)] + fn variadic_concat_string(first: String, second: String, rest: Vec) -> String; +} + +// ints + +macro_rules! variadic_test_int { + ($fn_name:ident, $extern_name:ident) => { + #[wasm_bindgen_test] + fn $fn_name() { + assert_eq!($extern_name(1, 2, &[]), 3); + assert_eq!($extern_name(1, 2, &[3]), 6); + assert_eq!($extern_name(1, 2, &[3, 4]), 10); + } + } +} + +// The 64 tests throw js `Cannot mix BigInt and other types, use explicit conversions` +variadic_test_int!(variadic_simple_u8, variadic_sum_u8); +variadic_test_int!(variadic_simple_u16, variadic_sum_u16); +variadic_test_int!(variadic_simple_u32, variadic_sum_u32); +//variadic_test_int!(variadic_simple_u64, variadic_sum_u64); +variadic_test_int!(variadic_simple_usize, variadic_sum_usize); +variadic_test_int!(variadic_simple_i8, variadic_sum_i8); +variadic_test_int!(variadic_simple_i16, variadic_sum_i16); +variadic_test_int!(variadic_simple_i32, variadic_sum_i32); +//variadic_test_int!(variadic_simple_i64, variadic_sum_i64); +variadic_test_int!(variadic_simple_isize, variadic_sum_isize); + +// floats + +macro_rules! variadic_test_float { + ($fn_name:ident, $extern_name:ident) => { + #[wasm_bindgen_test] + fn $fn_name() { + assert_eq!($extern_name(1., 2., &[]), 3.); + assert_eq!($extern_name(1., 2., &[3.]), 6.); + assert_eq!($extern_name(1., 2., &[3., 4.]), 10.); + } + } +} + +variadic_test_float!(variadic_simple_f32, variadic_sum_f32); +variadic_test_float!(variadic_simple_f64, variadic_sum_f64); + +// strings + +// `the trait `wasm_bindgen::convert::IntoWasmAbi` is not implemented for `&[&str]` +#[wasm_bindgen_test] +fn variadic_simple_str() { + assert_eq!(variadic_concat_str("a ", "test", &[]), "a test"); + assert_eq!(variadic_concat_str("a", "nother ", &["test"]), "another test"); + assert_eq!(variadic_concat_str("yet ", "a", &["nother ", "test"]), "yet another test"); } #[wasm_bindgen_test] -fn variadic_simple() { - assert_eq!(variadic_sum(1., 2., &[]), 3.); - assert_eq!(variadic_sum(1., 2., &[3.]), 6.); - assert_eq!(variadic_sum(1., 2., &[3., 4.]), 10.); +fn variadic_simple_string() { + assert_eq!(variadic_concat_string("a ".into(), "test".into(), vec![]), "a test"); + assert_eq!(variadic_concat_string("a".into(), "nother ".into(), vec!["test".into()]), + "another test"); + assert_eq!(variadic_concat_string("yet ".into(), + "a".into(), + vec!["nother ".into(), "test".into()]), + "yet another test"); +} + +// options + +#[wasm_bindgen_test] +fn variadic_simple_opt() { + assert_eq!(variadic_sum_opt(Some(1), None, &[]), 1); + assert_eq!(variadic_sum_opt(Some(1), None, &[Some(2)]), 3); + assert_eq!(variadic_sum_opt(Some(1), None, &[None, Some(2)]), 3); } From 5342a26fd014ef95a0f72f5016682b8a05254e95 Mon Sep 17 00:00:00 2001 From: Richard Dodd Date: Tue, 21 Aug 2018 13:11:53 +0100 Subject: [PATCH 06/13] Clean up checking code a bit --- crates/macro-support/src/parser.rs | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/crates/macro-support/src/parser.rs b/crates/macro-support/src/parser.rs index 9dd61eba..5fc0c05f 100644 --- a/crates/macro-support/src/parser.rs +++ b/crates/macro-support/src/parser.rs @@ -1115,18 +1115,14 @@ fn assert_last_param_is_slice(decl: &syn::FnDecl) -> Result<(), Diagnostic> { } let arg = decl.inputs.last().ok_or_else(|| not_slice_error(&decl.inputs))?; - let ty = match arg.value() { - syn::FnArg::Captured(ref arg_cap) => &arg_cap.ty, - _ => return Err(not_slice_error(&arg)) - }; - match ty { - syn::Type::Reference(ref ref_ty) => match &*ref_ty.elem { - syn::Type::Slice(_) => Ok(()), - _ => Err(not_slice_error(ty)) - }, - _ => Err(not_slice_error(ty)) + if let syn::FnArg::Captured(ref arg_cap) = arg.value() { + if let syn::Type::Reference(ref ref_ty) = arg_cap.ty { + if let syn::Type::Slice(_) = *ref_ty.elem { + return Ok(()) + } + } } - + Err(not_slice_error(&arg)) } /// If the path is a single ident, return it. From 496d8cadd87d7f38a7ea2f05753982dc867815c6 Mon Sep 17 00:00:00 2001 From: Richard Dodd Date: Tue, 21 Aug 2018 13:12:25 +0100 Subject: [PATCH 07/13] Actually span makes more sense on whole function --- crates/macro-support/src/parser.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/macro-support/src/parser.rs b/crates/macro-support/src/parser.rs index 5fc0c05f..dd52bc3c 100644 --- a/crates/macro-support/src/parser.rs +++ b/crates/macro-support/src/parser.rs @@ -1114,7 +1114,7 @@ fn assert_last_param_is_slice(decl: &syn::FnDecl) -> Result<(), Diagnostic> { slice, to hold the arguments of unknown length") } - let arg = decl.inputs.last().ok_or_else(|| not_slice_error(&decl.inputs))?; + let arg = decl.inputs.last().ok_or_else(|| not_slice_error(&decl))?; if let syn::FnArg::Captured(ref arg_cap) = arg.value() { if let syn::Type::Reference(ref ref_ty) = arg_cap.ty { if let syn::Type::Slice(_) = *ref_ty.elem { From e92536f300f4ece151bcab62bf3dc1fbd36c8c41 Mon Sep 17 00:00:00 2001 From: Richard Dodd Date: Tue, 21 Aug 2018 13:47:00 +0100 Subject: [PATCH 08/13] Allow Vec as well as slice --- crates/macro-support/src/parser.rs | 39 +++++++++++++++++++++++++++--- tests/wasm/variadic.rs | 2 +- 2 files changed, 37 insertions(+), 4 deletions(-) diff --git a/crates/macro-support/src/parser.rs b/crates/macro-support/src/parser.rs index dd52bc3c..97957f8f 100644 --- a/crates/macro-support/src/parser.rs +++ b/crates/macro-support/src/parser.rs @@ -1111,14 +1111,47 @@ fn assert_last_param_is_slice(decl: &syn::FnDecl) -> Result<(), Diagnostic> { #[inline] fn not_slice_error(tok: &dyn ToTokens) -> Diagnostic { Diagnostic::span_error(tok, "for variadic extern functions, the last argument must be a \ - slice, to hold the arguments of unknown length") + slice or `::std::vec::Vec`, to hold the arguments of unknown length") } - let arg = decl.inputs.last().ok_or_else(|| not_slice_error(&decl))?; + /// Is this path `::std::vec::Vec`. + /// + /// I could add `Vec`, but this would break in wierd ways if the user does + /// `use SomethingElse as Vec;`. + #[inline] + fn path_is_vec(path: &syn::Path) -> bool { + #[inline] + fn is_path_segment(path: Option<&syn::PathSegment>, + name: Option<&str>, + plain: bool) -> bool { + match (path, name) { + (Some(ref path), Some(ref name)) => + (path.arguments.is_empty() || ! plain) && path.ident == name, + (None, None) => true, + _ => false + } + } + + let mut iter = (&path.segments).into_iter(); + path.leading_colon.is_some() + && is_path_segment(iter.next(), Some("std"), true) + && is_path_segment(iter.next(), Some("vec"), true) + && is_path_segment(iter.next(), Some("Vec"), false) + && is_path_segment(iter.next(), None, true) + } + + let arg = decl.inputs.last().ok_or_else(|| not_slice_error(&decl.inputs))?; if let syn::FnArg::Captured(ref arg_cap) = arg.value() { + // check for slice reference if let syn::Type::Reference(ref ref_ty) = arg_cap.ty { if let syn::Type::Slice(_) = *ref_ty.elem { - return Ok(()) + return Ok(()); + } + } + // check for `Vec` + if let syn::Type::Path(ref path) = arg_cap.ty { + if path_is_vec(&path.path) { + return Ok(()); } } } diff --git a/tests/wasm/variadic.rs b/tests/wasm/variadic.rs index b2219078..a26c0fdb 100644 --- a/tests/wasm/variadic.rs +++ b/tests/wasm/variadic.rs @@ -32,7 +32,7 @@ extern { #[wasm_bindgen(variadic)] fn variadic_concat_str(first: &str, second: &str, rest: &[&str]) -> String; #[wasm_bindgen(variadic)] - fn variadic_concat_string(first: String, second: String, rest: Vec) -> String; + fn variadic_concat_string(first: String, second: String, rest: ::std::vec::Vec) -> String; } // ints From b8c1f72dabb6ed2781b3bdac09d6eb2e9597e3f4 Mon Sep 17 00:00:00 2001 From: Richard Dodd Date: Tue, 21 Aug 2018 13:55:14 +0100 Subject: [PATCH 09/13] Comment out failing code --- tests/wasm/variadic.js | 1 + tests/wasm/variadic.rs | 37 ++++++++++++++++++++++++++----------- 2 files changed, 27 insertions(+), 11 deletions(-) diff --git a/tests/wasm/variadic.js b/tests/wasm/variadic.js index ccd64d4d..63a0acad 100644 --- a/tests/wasm/variadic.js +++ b/tests/wasm/variadic.js @@ -19,6 +19,7 @@ exports.variadic_sum_i32 = variadic_sum; exports.variadic_sum_i64 = variadic_sum; exports.variadic_sum_f32 = variadic_sum; exports.variadic_sum_f64 = variadic_sum; +exports.variadic_sum_rest_vec = variadic_sum; function variadic_sum_opt(...args) { let answer = 0; diff --git a/tests/wasm/variadic.rs b/tests/wasm/variadic.rs index a26c0fdb..14bbaadb 100644 --- a/tests/wasm/variadic.rs +++ b/tests/wasm/variadic.rs @@ -11,8 +11,8 @@ extern { fn variadic_sum_u32(first: u32, second: u32, rest: &[u32]) -> u32; #[wasm_bindgen(variadic)] fn variadic_sum_u64(first: u64, second: u64, rest: &[u64]) -> u64; - #[wasm_bindgen(variadic)] - fn variadic_sum_usize(first: usize, second: usize, rest: &[usize]) -> usize; + //#[wasm_bindgen(variadic)] + //fn variadic_sum_usize(first: usize, second: usize, rest: &[usize]) -> usize; #[wasm_bindgen(variadic)] fn variadic_sum_i8(first: i8, second: i8, rest: &[i8]) -> i8; #[wasm_bindgen(variadic)] @@ -21,18 +21,20 @@ extern { fn variadic_sum_i32(first: i32, second: i32, rest: &[i32]) -> i32; #[wasm_bindgen(variadic)] fn variadic_sum_i64(first: i64, second: i64, rest: &[i64]) -> i64; - #[wasm_bindgen(variadic)] - fn variadic_sum_isize(first: isize, second: isize, rest: &[isize]) -> isize; + //#[wasm_bindgen(variadic)] + //fn variadic_sum_isize(first: isize, second: isize, rest: &[isize]) -> isize; #[wasm_bindgen(variadic)] fn variadic_sum_f32(first: f32, second: f32, rest: &[f32]) -> f32; #[wasm_bindgen(variadic)] fn variadic_sum_f64(first: f64, second: f64, rest: &[f64]) -> f64; + //#[wasm_bindgen(variadic)] + //fn variadic_sum_opt(first: Option, second: Option, rest: &[Option]) -> u32; + //#[wasm_bindgen(variadic)] + //fn variadic_concat_str(first: &str, second: &str, rest: &[&str]) -> String; + //#[wasm_bindgen(variadic)] + //fn variadic_concat_string(first: String, second: String, rest: ::std::vec::Vec) -> String; #[wasm_bindgen(variadic)] - fn variadic_sum_opt(first: Option, second: Option, rest: &[Option]) -> u32; - #[wasm_bindgen(variadic)] - fn variadic_concat_str(first: &str, second: &str, rest: &[&str]) -> String; - #[wasm_bindgen(variadic)] - fn variadic_concat_string(first: String, second: String, rest: ::std::vec::Vec) -> String; + fn variadic_sum_rest_vec(first: u8, second: u8, rest: ::std::vec::Vec) -> u8; } // ints @@ -53,12 +55,12 @@ variadic_test_int!(variadic_simple_u8, variadic_sum_u8); variadic_test_int!(variadic_simple_u16, variadic_sum_u16); variadic_test_int!(variadic_simple_u32, variadic_sum_u32); //variadic_test_int!(variadic_simple_u64, variadic_sum_u64); -variadic_test_int!(variadic_simple_usize, variadic_sum_usize); +//variadic_test_int!(variadic_simple_usize, variadic_sum_usize); variadic_test_int!(variadic_simple_i8, variadic_sum_i8); variadic_test_int!(variadic_simple_i16, variadic_sum_i16); variadic_test_int!(variadic_simple_i32, variadic_sum_i32); //variadic_test_int!(variadic_simple_i64, variadic_sum_i64); -variadic_test_int!(variadic_simple_isize, variadic_sum_isize); +//variadic_test_int!(variadic_simple_isize, variadic_sum_isize); // floats @@ -79,6 +81,7 @@ variadic_test_float!(variadic_simple_f64, variadic_sum_f64); // strings // `the trait `wasm_bindgen::convert::IntoWasmAbi` is not implemented for `&[&str]` +/* #[wasm_bindgen_test] fn variadic_simple_str() { assert_eq!(variadic_concat_str("a ", "test", &[]), "a test"); @@ -96,13 +99,25 @@ fn variadic_simple_string() { vec!["nother ".into(), "test".into()]), "yet another test"); } +*/ // options +/* #[wasm_bindgen_test] fn variadic_simple_opt() { assert_eq!(variadic_sum_opt(Some(1), None, &[]), 1); assert_eq!(variadic_sum_opt(Some(1), None, &[Some(2)]), 3); assert_eq!(variadic_sum_opt(Some(1), None, &[None, Some(2)]), 3); } +*/ + +// vec not slice + +#[wasm_bindgen_test] +fn variadic_simple_rest_vec() { + assert_eq!(variadic_sum_rest_vec(1, 2, vec![]), 3); + assert_eq!(variadic_sum_rest_vec(1, 2, vec![3]), 6); + assert_eq!(variadic_sum_rest_vec(1, 2, vec![3, 4]), 10); +} From 8ff0da6f853f10ae3dc9ac346dd3fd4c94a81f50 Mon Sep 17 00:00:00 2001 From: Richard Dodd Date: Tue, 21 Aug 2018 14:07:29 +0100 Subject: [PATCH 10/13] Add more tests then comment them out --- tests/wasm/variadic.js | 15 ++++++++++++++ tests/wasm/variadic.rs | 46 ++++++++++++++++++++++++++---------------- 2 files changed, 44 insertions(+), 17 deletions(-) diff --git a/tests/wasm/variadic.js b/tests/wasm/variadic.js index 63a0acad..c4b5a741 100644 --- a/tests/wasm/variadic.js +++ b/tests/wasm/variadic.js @@ -1,6 +1,7 @@ const wasm = require('wasm-bindgen-test.js'); const assert = require('assert'); +// a function for testing numbers function variadic_sum(...args) { let answer = 0; for(var i=0; i String; //#[wasm_bindgen(variadic)] - //fn variadic_concat_string(first: String, second: String, rest: ::std::vec::Vec) -> String; + //fn variadic_concat_string(first: String, + // second: String, + // rest: ::std::vec::Vec) -> String; #[wasm_bindgen(variadic)] fn variadic_sum_rest_vec(first: u8, second: u8, rest: ::std::vec::Vec) -> u8; + //#[wasm_bindgen(variadic)] + //fn variadic_compare_pairs(first: JsValue, second: JsValue, rest: &[JsValue]); + //TODO imported type } // ints @@ -51,16 +56,16 @@ macro_rules! variadic_test_int { } // The 64 tests throw js `Cannot mix BigInt and other types, use explicit conversions` -variadic_test_int!(variadic_simple_u8, variadic_sum_u8); -variadic_test_int!(variadic_simple_u16, variadic_sum_u16); -variadic_test_int!(variadic_simple_u32, variadic_sum_u32); -//variadic_test_int!(variadic_simple_u64, variadic_sum_u64); -//variadic_test_int!(variadic_simple_usize, variadic_sum_usize); -variadic_test_int!(variadic_simple_i8, variadic_sum_i8); -variadic_test_int!(variadic_simple_i16, variadic_sum_i16); -variadic_test_int!(variadic_simple_i32, variadic_sum_i32); -//variadic_test_int!(variadic_simple_i64, variadic_sum_i64); -//variadic_test_int!(variadic_simple_isize, variadic_sum_isize); +variadic_test_int!(u8, variadic_sum_u8); +variadic_test_int!(u16, variadic_sum_u16); +variadic_test_int!(u32, variadic_sum_u32); +//variadic_test_int!(u64, variadic_sum_u64); +//variadic_test_int!(usize, variadic_sum_usize); +variadic_test_int!(i8, variadic_sum_i8); +variadic_test_int!(i16, variadic_sum_i16); +variadic_test_int!(i32, variadic_sum_i32); +//variadic_test_int!(i64, variadic_sum_i64); +//variadic_test_int!(isize, variadic_sum_isize); // floats @@ -75,22 +80,22 @@ macro_rules! variadic_test_float { } } -variadic_test_float!(variadic_simple_f32, variadic_sum_f32); -variadic_test_float!(variadic_simple_f64, variadic_sum_f64); +variadic_test_float!(f32, variadic_sum_f32); +variadic_test_float!(f64, variadic_sum_f64); // strings // `the trait `wasm_bindgen::convert::IntoWasmAbi` is not implemented for `&[&str]` /* #[wasm_bindgen_test] -fn variadic_simple_str() { +fn str() { assert_eq!(variadic_concat_str("a ", "test", &[]), "a test"); assert_eq!(variadic_concat_str("a", "nother ", &["test"]), "another test"); assert_eq!(variadic_concat_str("yet ", "a", &["nother ", "test"]), "yet another test"); } #[wasm_bindgen_test] -fn variadic_simple_string() { +fn string() { assert_eq!(variadic_concat_string("a ".into(), "test".into(), vec![]), "a test"); assert_eq!(variadic_concat_string("a".into(), "nother ".into(), vec!["test".into()]), "another test"); @@ -105,7 +110,7 @@ fn variadic_simple_string() { /* #[wasm_bindgen_test] -fn variadic_simple_opt() { +fn opt() { assert_eq!(variadic_sum_opt(Some(1), None, &[]), 1); assert_eq!(variadic_sum_opt(Some(1), None, &[Some(2)]), 3); assert_eq!(variadic_sum_opt(Some(1), None, &[None, Some(2)]), 3); @@ -115,9 +120,16 @@ fn variadic_simple_opt() { // vec not slice #[wasm_bindgen_test] -fn variadic_simple_rest_vec() { +fn rest_vec() { assert_eq!(variadic_sum_rest_vec(1, 2, vec![]), 3); assert_eq!(variadic_sum_rest_vec(1, 2, vec![3]), 6); assert_eq!(variadic_sum_rest_vec(1, 2, vec![3, 4]), 10); } +// JsValue +//#[wasm_bindgen_test] +//fn jsvalue() { +// variadic_compare_pairs_jsvalue(true, true, vec![]); +// variadic_compare_pairs_jsvalue(false, false, vec![3, 3]); +//} + From 958e557c867b0d140c5564be2069582c8c382104 Mon Sep 17 00:00:00 2001 From: Richard Dodd Date: Fri, 31 Aug 2018 09:59:11 +0100 Subject: [PATCH 11/13] try removing typecheck --- crates/macro-support/src/parser.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/crates/macro-support/src/parser.rs b/crates/macro-support/src/parser.rs index 97957f8f..951dd33d 100644 --- a/crates/macro-support/src/parser.rs +++ b/crates/macro-support/src/parser.rs @@ -418,9 +418,6 @@ impl<'a> ConvertToAst<(BindgenAttrs, &'a Option)> for syn::ForeignItemFn )?.0; let catch = opts.catch(); let variadic = opts.variadic(); - if variadic { - assert_last_param_is_slice(&self.decl)?; - } let js_ret = if catch { // TODO: this assumes a whole bunch: // From 7d5d845608c3819ce9fb34ac8738c603639a9edd Mon Sep 17 00:00:00 2001 From: Richard Dodd Date: Sat, 1 Sep 2018 13:55:35 +0100 Subject: [PATCH 12/13] Add docs and remove typecheck from variadic attribute --- crates/cli-support/src/js/mod.rs | 1 - crates/macro-support/src/parser.rs | 54 ------------------- guide/src/SUMMARY.md | 1 + .../attributes/on-js-imports/variadic.md | 38 +++++++++++++ .../src/whirlwind-tour/what-else-can-we-do.md | 12 +++++ tests/wasm/variadic.js | 1 - tests/wasm/variadic.rs | 4 +- 7 files changed, 53 insertions(+), 58 deletions(-) create mode 100644 guide/src/reference/attributes/on-js-imports/variadic.md diff --git a/crates/cli-support/src/js/mod.rs b/crates/cli-support/src/js/mod.rs index c085abd4..09aeaf09 100644 --- a/crates/cli-support/src/js/mod.rs +++ b/crates/cli-support/src/js/mod.rs @@ -2128,7 +2128,6 @@ impl<'a, 'b> SubContext<'a, 'b> { import.shim, if op.is_static { "" } else { ".call" } )) ->>>>>>> master } fn generate_import_type( diff --git a/crates/macro-support/src/parser.rs b/crates/macro-support/src/parser.rs index 942bca0a..91f7f7ad 100644 --- a/crates/macro-support/src/parser.rs +++ b/crates/macro-support/src/parser.rs @@ -1101,60 +1101,6 @@ fn assert_not_variadic(attrs: &BindgenAttrs, span: &dyn ToTokens) -> Result<(), Ok(()) } -/// Checks the function signature to ensure it finishes with a slice -/// -/// Example of a valid signature: `fn my_func(arg1: u64, res: &[u64])`. -fn assert_last_param_is_slice(decl: &syn::FnDecl) -> Result<(), Diagnostic> { - #[inline] - fn not_slice_error(tok: &dyn ToTokens) -> Diagnostic { - Diagnostic::span_error(tok, "for variadic extern functions, the last argument must be a \ - slice or `::std::vec::Vec`, to hold the arguments of unknown length") - } - - /// Is this path `::std::vec::Vec`. - /// - /// I could add `Vec`, but this would break in wierd ways if the user does - /// `use SomethingElse as Vec;`. - #[inline] - fn path_is_vec(path: &syn::Path) -> bool { - #[inline] - fn is_path_segment(path: Option<&syn::PathSegment>, - name: Option<&str>, - plain: bool) -> bool { - match (path, name) { - (Some(ref path), Some(ref name)) => - (path.arguments.is_empty() || ! plain) && path.ident == name, - (None, None) => true, - _ => false - } - } - - let mut iter = (&path.segments).into_iter(); - path.leading_colon.is_some() - && is_path_segment(iter.next(), Some("std"), true) - && is_path_segment(iter.next(), Some("vec"), true) - && is_path_segment(iter.next(), Some("Vec"), false) - && is_path_segment(iter.next(), None, true) - } - - let arg = decl.inputs.last().ok_or_else(|| not_slice_error(&decl.inputs))?; - if let syn::FnArg::Captured(ref arg_cap) = arg.value() { - // check for slice reference - if let syn::Type::Reference(ref ref_ty) = arg_cap.ty { - if let syn::Type::Slice(_) = *ref_ty.elem { - return Ok(()); - } - } - // check for `Vec` - if let syn::Type::Path(ref path) = arg_cap.ty { - if path_is_vec(&path.path) { - return Ok(()); - } - } - } - Err(not_slice_error(&arg)) -} - /// If the path is a single ident, return it. fn extract_path_ident(path: &syn::Path) -> Result { if path.leading_colon.is_some() { diff --git a/guide/src/SUMMARY.md b/guide/src/SUMMARY.md index 720e4b84..89dfa890 100644 --- a/guide/src/SUMMARY.md +++ b/guide/src/SUMMARY.md @@ -42,6 +42,7 @@ - [`module = "blah"`](./reference/attributes/on-js-imports/module.md) - [`static_method_of = Blah`](./reference/attributes/on-js-imports/static_method_of.md) - [`structural`](./reference/attributes/on-js-imports/structural.md) + - [variadic](./reference/attributes/on-js-imports/variadic.md) - [On Rust Exports](./reference/attributes/on-rust-exports/index.md) - [`constructor`](./reference/attributes/on-rust-exports/constructor.md) - [`js_name = Blah`](./reference/attributes/on-rust-exports/js_name.md) diff --git a/guide/src/reference/attributes/on-js-imports/variadic.md b/guide/src/reference/attributes/on-js-imports/variadic.md new file mode 100644 index 00000000..a9ad97b4 --- /dev/null +++ b/guide/src/reference/attributes/on-js-imports/variadic.md @@ -0,0 +1,38 @@ +# Variadic Parameters + +In javascript, both the types of function arguments, and the number of function arguments are +dynamic. For example + +```js +function sum(...rest) { + let i; + // the old way + let old_way = 0; + for (i=0; i i32; +} +``` + +when we call this function, the last argument will be expanded as the javascript expects. + diff --git a/guide/src/whirlwind-tour/what-else-can-we-do.md b/guide/src/whirlwind-tour/what-else-can-we-do.md index 80bc19b0..7d9ec2b0 100644 --- a/guide/src/whirlwind-tour/what-else-can-we-do.md +++ b/guide/src/whirlwind-tour/what-else-can-we-do.md @@ -64,6 +64,10 @@ extern { fn new() -> Awesome; #[wasm_bindgen(method)] fn get_internal(this: &Awesome) -> u32; + // We can call javascript functions that have a dynamic number of arguments, + // e.g. rust `sum(&[1, 2, 3])` will be called like `sum(1, 2, 3)` + #[wasm_bindgen(variadic)] + fn sum(vals: &[u32]) -> u32; } #[wasm_bindgen] @@ -143,5 +147,13 @@ export class Awesome { } } +export function sum(...args) { + let answer = 0; + for(var i=0; i) -> String; + // rest: Vec) -> String; #[wasm_bindgen(variadic)] - fn variadic_sum_rest_vec(first: u8, second: u8, rest: ::std::vec::Vec) -> u8; + fn variadic_sum_rest_vec(first: u8, second: u8, rest: Vec) -> u8; //#[wasm_bindgen(variadic)] //fn variadic_compare_pairs(first: JsValue, second: JsValue, rest: &[JsValue]); //TODO imported type From 5c7e638b8cd612714e4ba6ccf9a895c1451b8190 Mon Sep 17 00:00:00 2001 From: Richard Dodd Date: Mon, 3 Sep 2018 09:50:26 +0100 Subject: [PATCH 13/13] Handle variadic no args more gracefully. --- crates/cli-support/src/js/mod.rs | 2 +- crates/cli-support/src/js/rust2js.rs | 9 ++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/crates/cli-support/src/js/mod.rs b/crates/cli-support/src/js/mod.rs index 09aeaf09..8017338c 100644 --- a/crates/cli-support/src/js/mod.rs +++ b/crates/cli-support/src/js/mod.rs @@ -1964,7 +1964,7 @@ impl<'a, 'b> SubContext<'a, 'b> { .catch(import.catch) .variadic(import.variadic) .process(descriptor.unwrap_function())? - .finish(&target); + .finish(&target)?; self.cx.export(&import.shim, &js, None); Ok(()) } diff --git a/crates/cli-support/src/js/rust2js.rs b/crates/cli-support/src/js/rust2js.rs index e0f1ebb4..a0fcd20a 100644 --- a/crates/cli-support/src/js/rust2js.rs +++ b/crates/cli-support/src/js/rust2js.rs @@ -1,4 +1,4 @@ -use failure::Error; +use failure::{self, Error}; use super::{Context, Js2Rust}; use descriptor::{Descriptor, Function}; @@ -529,8 +529,7 @@ impl<'a, 'b> Rust2Js<'a, 'b> { Ok(()) } - pub fn finish(&self, invoc: &str) -> String { - use std::fmt::Write; + pub fn finish(&self, invoc: &str) -> Result { let mut ret = String::new(); ret.push_str("function("); ret.push_str(&self.shim_arguments.join(", ")); @@ -545,7 +544,7 @@ impl<'a, 'b> Rust2Js<'a, 'b> { let mut invoc = if self.variadic { if self.js_arguments.is_empty() { - unreachable!("the last argument of a variadic must be a slice"); + return Err(failure::err_msg("a function with no arguments cannot be variadic")); } let last_arg = self.js_arguments.len() - 1; // check implies >= 0 self.ret_expr.replace( @@ -595,7 +594,7 @@ impl<'a, 'b> Rust2Js<'a, 'b> { ret.push_str(&invoc); ret.push_str("\n}\n"); - return ret; + Ok(ret) } fn global_idx(&mut self) -> usize {