mirror of
https://github.com/fluencelabs/wasm-bindgen
synced 2025-06-03 08:01:23 +00:00
[wip] support variadic javascript function parameters
This commit is contained in:
parent
57693ee11a
commit
d9fd2147a0
@ -85,6 +85,7 @@ pub struct ImportFunction {
|
|||||||
pub rust_name: Ident,
|
pub rust_name: Ident,
|
||||||
pub js_ret: Option<syn::Type>,
|
pub js_ret: Option<syn::Type>,
|
||||||
pub catch: bool,
|
pub catch: bool,
|
||||||
|
pub variadic: bool,
|
||||||
pub structural: bool,
|
pub structural: bool,
|
||||||
pub kind: ImportFunctionKind,
|
pub kind: ImportFunctionKind,
|
||||||
pub shim: Ident,
|
pub shim: Ident,
|
||||||
@ -449,6 +450,7 @@ impl ImportFunction {
|
|||||||
shared::ImportFunction {
|
shared::ImportFunction {
|
||||||
shim: self.shim.to_string(),
|
shim: self.shim.to_string(),
|
||||||
catch: self.catch,
|
catch: self.catch,
|
||||||
|
variadic: self.variadic,
|
||||||
method,
|
method,
|
||||||
structural: self.structural,
|
structural: self.structural,
|
||||||
function: self.function.shared(),
|
function: self.function.shared(),
|
||||||
|
@ -183,6 +183,14 @@ impl BindgenAttrs {
|
|||||||
_ => None,
|
_ => 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 {
|
impl syn::synom::Synom for BindgenAttrs {
|
||||||
@ -219,6 +227,7 @@ pub enum BindgenAttr {
|
|||||||
JsName(String),
|
JsName(String),
|
||||||
JsClass(String),
|
JsClass(String),
|
||||||
Extends(Ident),
|
Extends(Ident),
|
||||||
|
Variadic,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl syn::synom::Synom for BindgenAttr {
|
impl syn::synom::Synom for BindgenAttr {
|
||||||
@ -304,6 +313,8 @@ impl syn::synom::Synom for BindgenAttr {
|
|||||||
ns: call!(term2ident) >>
|
ns: call!(term2ident) >>
|
||||||
(ns)
|
(ns)
|
||||||
)=> { BindgenAttr::Extends }
|
)=> { 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 getter = shared::struct_field_get(&ident, &name_str);
|
||||||
let setter = shared::struct_field_set(&ident, &name_str);
|
let setter = shared::struct_field_set(&ident, &name_str);
|
||||||
let opts = BindgenAttrs::find(&mut field.attrs)?;
|
let opts = BindgenAttrs::find(&mut field.attrs)?;
|
||||||
|
assert_not_variadic(&opts)?;
|
||||||
let comments = extract_doc_comments(&field.attrs);
|
let comments = extract_doc_comments(&field.attrs);
|
||||||
fields.push(ast::StructField {
|
fields.push(ast::StructField {
|
||||||
name: name.clone(),
|
name: name.clone(),
|
||||||
@ -395,6 +407,7 @@ impl<'a> ConvertToAst<(BindgenAttrs, &'a Option<String>)> for syn::ForeignItemFn
|
|||||||
) -> Result<Self::Target, Diagnostic> {
|
) -> Result<Self::Target, Diagnostic> {
|
||||||
let default_name = self.ident.to_string();
|
let default_name = self.ident.to_string();
|
||||||
let js_name = opts.js_name().unwrap_or(&default_name);
|
let js_name = opts.js_name().unwrap_or(&default_name);
|
||||||
|
|
||||||
let wasm = function_from_decl(
|
let wasm = function_from_decl(
|
||||||
js_name,
|
js_name,
|
||||||
self.decl.clone(),
|
self.decl.clone(),
|
||||||
@ -404,6 +417,10 @@ impl<'a> ConvertToAst<(BindgenAttrs, &'a Option<String>)> for syn::ForeignItemFn
|
|||||||
None,
|
None,
|
||||||
)?.0;
|
)?.0;
|
||||||
let catch = opts.catch();
|
let catch = opts.catch();
|
||||||
|
let variadic = opts.variadic();
|
||||||
|
if variadic {
|
||||||
|
assert_last_param_is_slice(&self.decl)?;
|
||||||
|
}
|
||||||
let js_ret = if catch {
|
let js_ret = if catch {
|
||||||
// TODO: this assumes a whole bunch:
|
// TODO: this assumes a whole bunch:
|
||||||
//
|
//
|
||||||
@ -532,6 +549,7 @@ impl<'a> ConvertToAst<(BindgenAttrs, &'a Option<String>)> for syn::ForeignItemFn
|
|||||||
kind,
|
kind,
|
||||||
js_ret,
|
js_ret,
|
||||||
catch,
|
catch,
|
||||||
|
variadic,
|
||||||
structural: opts.structural(),
|
structural: opts.structural(),
|
||||||
rust_name: self.ident.clone(),
|
rust_name: self.ident.clone(),
|
||||||
shim: Ident::new(&shim, Span::call_site()),
|
shim: Ident::new(&shim, Span::call_site()),
|
||||||
@ -544,6 +562,7 @@ impl ConvertToAst<BindgenAttrs> for syn::ForeignItemType {
|
|||||||
type Target = ast::ImportKind;
|
type Target = ast::ImportKind;
|
||||||
|
|
||||||
fn convert(self, attrs: BindgenAttrs) -> Result<Self::Target, Diagnostic> {
|
fn convert(self, attrs: BindgenAttrs) -> Result<Self::Target, Diagnostic> {
|
||||||
|
assert_not_variadic(&attrs)?;
|
||||||
let js_name = attrs
|
let js_name = attrs
|
||||||
.js_name()
|
.js_name()
|
||||||
.map_or_else(|| self.ident.to_string(), |s| s.to_string());
|
.map_or_else(|| self.ident.to_string(), |s| s.to_string());
|
||||||
@ -567,6 +586,7 @@ impl ConvertToAst<BindgenAttrs> for syn::ForeignItemStatic {
|
|||||||
if self.mutability.is_some() {
|
if self.mutability.is_some() {
|
||||||
bail_span!(self.mutability, "cannot import mutable globals yet")
|
bail_span!(self.mutability, "cannot import mutable globals yet")
|
||||||
}
|
}
|
||||||
|
assert_not_variadic(&opts)?;
|
||||||
let default_name = self.ident.to_string();
|
let default_name = self.ident.to_string();
|
||||||
let js_name = opts.js_name().unwrap_or(&default_name);
|
let js_name = opts.js_name().unwrap_or(&default_name);
|
||||||
let shim = format!(
|
let shim = format!(
|
||||||
@ -604,6 +624,7 @@ impl ConvertToAst<BindgenAttrs> for syn::ItemFn {
|
|||||||
if self.unsafety.is_some() {
|
if self.unsafety.is_some() {
|
||||||
bail_span!(self.unsafety, "can only #[wasm_bindgen] safe functions");
|
bail_span!(self.unsafety, "can only #[wasm_bindgen] safe functions");
|
||||||
}
|
}
|
||||||
|
assert_not_variadic(&attrs)?;
|
||||||
|
|
||||||
let default_name = self.ident.to_string();
|
let default_name = self.ident.to_string();
|
||||||
let name = attrs.js_name().unwrap_or(&default_name);
|
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)
|
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.
|
/// If the path is a single ident, return it.
|
||||||
fn extract_path_ident(path: &syn::Path) -> Result<Ident, Diagnostic> {
|
fn extract_path_ident(path: &syn::Path) -> Result<Ident, Diagnostic> {
|
||||||
if path.leading_colon.is_some() {
|
if path.leading_colon.is_some() {
|
||||||
|
@ -41,6 +41,7 @@ pub enum ImportKind {
|
|||||||
pub struct ImportFunction {
|
pub struct ImportFunction {
|
||||||
pub shim: String,
|
pub shim: String,
|
||||||
pub catch: bool,
|
pub catch: bool,
|
||||||
|
pub variadic: bool,
|
||||||
pub method: Option<MethodData>,
|
pub method: Option<MethodData>,
|
||||||
pub structural: bool,
|
pub structural: bool,
|
||||||
pub function: Function,
|
pub function: Function,
|
||||||
|
@ -31,3 +31,4 @@ pub mod slice;
|
|||||||
pub mod structural;
|
pub mod structural;
|
||||||
pub mod u64;
|
pub mod u64;
|
||||||
pub mod validate_prt;
|
pub mod validate_prt;
|
||||||
|
pub mod variadic;
|
||||||
|
6
tests/wasm/variadic.js
Normal file
6
tests/wasm/variadic.js
Normal file
@ -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);
|
||||||
|
|
16
tests/wasm/variadic.rs
Normal file
16
tests/wasm/variadic.rs
Normal file
@ -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.);
|
||||||
|
}
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user