mirror of
https://github.com/fluencelabs/wasm-bindgen
synced 2025-04-02 02:11:06 +00:00
Merge pull request #792 from afdw/master
Add support for variadic arguments in WebIDL
This commit is contained in:
commit
9ca024a812
@ -499,13 +499,26 @@ impl<'a, 'b> Rust2Js<'a, 'b> {
|
|||||||
return Err(failure::err_msg("a function with no arguments cannot be variadic"));
|
return Err(failure::err_msg("a function with no arguments cannot be variadic"));
|
||||||
}
|
}
|
||||||
let last_arg = self.js_arguments.len() - 1; // check implies >= 0
|
let last_arg = self.js_arguments.len() - 1; // check implies >= 0
|
||||||
self.ret_expr.replace(
|
if self.js_arguments.len() != 1 {
|
||||||
"JS",
|
self.ret_expr.replace(
|
||||||
&format!("{}({}, ...{})",
|
"JS",
|
||||||
invoc,
|
&format!(
|
||||||
self.js_arguments[..last_arg].join(", "),
|
"{}({}, ...{})",
|
||||||
self.js_arguments[last_arg])
|
invoc,
|
||||||
)
|
self.js_arguments[..last_arg].join(", "),
|
||||||
|
self.js_arguments[last_arg],
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
self.ret_expr.replace(
|
||||||
|
"JS",
|
||||||
|
&format!(
|
||||||
|
"{}(...{})",
|
||||||
|
invoc,
|
||||||
|
self.js_arguments[last_arg],
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
self.ret_expr.replace(
|
self.ret_expr.replace(
|
||||||
"JS",
|
"JS",
|
||||||
|
@ -110,6 +110,13 @@ global.OptionalAndUnionArguments = class OptionalAndUnionArguments {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
global.Variadic = class Variadic {
|
||||||
|
constructor() {}
|
||||||
|
sum(...values) {
|
||||||
|
return values.reduce((a, b) => a + b, 0);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
global.PartialInterface = class PartialInterface {
|
global.PartialInterface = class PartialInterface {
|
||||||
get un() {
|
get un() {
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -94,6 +94,24 @@ fn optional_and_union_arguments() {
|
|||||||
assert_eq!(f.m_with_b_and_str_and_opt_bool("abc", false, "5", Some(true)), "string, abc, boolean, false, string, 5, boolean, true");
|
assert_eq!(f.m_with_b_and_str_and_opt_bool("abc", false, "5", Some(true)), "string, abc, boolean, false, string, 5, boolean, true");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen_test]
|
||||||
|
fn variadic() {
|
||||||
|
let f = Variadic::new().unwrap();
|
||||||
|
assert_eq!(f.sum_5(1, 2, 3, 4, 5), 15);
|
||||||
|
assert_eq!(
|
||||||
|
f.sum(
|
||||||
|
&::js_sys::Array::of5(
|
||||||
|
&JsValue::from_f64(1f64),
|
||||||
|
&JsValue::from_f64(2f64),
|
||||||
|
&JsValue::from_f64(3f64),
|
||||||
|
&JsValue::from_f64(4f64),
|
||||||
|
&JsValue::from_f64(5f64),
|
||||||
|
)
|
||||||
|
),
|
||||||
|
15
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[wasm_bindgen_test]
|
#[wasm_bindgen_test]
|
||||||
fn unforgeable_is_structural() {
|
fn unforgeable_is_structural() {
|
||||||
let f = Unforgeable::new().unwrap();
|
let f = Unforgeable::new().unwrap();
|
||||||
|
5
crates/webidl-tests/simple.webidl
vendored
5
crates/webidl-tests/simple.webidl
vendored
@ -57,6 +57,11 @@ interface OptionalAndUnionArguments {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
[Constructor()]
|
||||||
|
interface Variadic {
|
||||||
|
short sum(short... values);
|
||||||
|
};
|
||||||
|
|
||||||
[Constructor()]
|
[Constructor()]
|
||||||
interface Unforgeable {
|
interface Unforgeable {
|
||||||
[Unforgeable] readonly attribute short uno;
|
[Unforgeable] readonly attribute short uno;
|
||||||
|
@ -95,18 +95,19 @@ pub(crate) struct OperationData<'src> {
|
|||||||
pub(crate) is_static: bool,
|
pub(crate) is_static: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone, Debug)]
|
||||||
pub(crate) struct Signature<'src> {
|
pub(crate) struct Signature<'src> {
|
||||||
pub(crate) args: Vec<Arg<'src>>,
|
pub(crate) args: Vec<Arg<'src>>,
|
||||||
pub(crate) ret: weedle::types::ReturnType<'src>,
|
pub(crate) ret: weedle::types::ReturnType<'src>,
|
||||||
pub(crate) attrs: &'src Option<ExtendedAttributeList<'src>>,
|
pub(crate) attrs: &'src Option<ExtendedAttributeList<'src>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone, Debug)]
|
||||||
pub(crate) struct Arg<'src> {
|
pub(crate) struct Arg<'src> {
|
||||||
pub(crate) name: &'src str,
|
pub(crate) name: &'src str,
|
||||||
pub(crate) ty: &'src weedle::types::Type<'src>,
|
pub(crate) ty: &'src weedle::types::Type<'src>,
|
||||||
pub(crate) optional: bool,
|
pub(crate) optional: bool,
|
||||||
|
pub(crate) variadic: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Implemented on an AST node to populate the `FirstPassRecord` struct.
|
/// Implemented on an AST node to populate the `FirstPassRecord` struct.
|
||||||
@ -258,19 +259,17 @@ fn first_pass_operation<'src>(
|
|||||||
};
|
};
|
||||||
let mut args = Vec::with_capacity(arguments.len());
|
let mut args = Vec::with_capacity(arguments.len());
|
||||||
for argument in arguments {
|
for argument in arguments {
|
||||||
let arg = match argument {
|
let (name, ty, optional, variadic) = match argument {
|
||||||
Argument::Single(single) => single,
|
Argument::Single(single) =>
|
||||||
Argument::Variadic(v) => {
|
(single.identifier.0, &single.type_.type_, single.optional.is_some(), false),
|
||||||
warn!("Unsupported variadic argument {} in {}",
|
Argument::Variadic(variadic) =>
|
||||||
v.identifier.0,
|
(variadic.identifier.0, &variadic.type_, false, true),
|
||||||
self_name);
|
|
||||||
return
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
args.push(Arg {
|
args.push(Arg {
|
||||||
name: arg.identifier.0,
|
name,
|
||||||
ty: &arg.type_.type_,
|
ty,
|
||||||
optional: arg.optional.is_some(),
|
optional,
|
||||||
|
variadic,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
for id in ids {
|
for id in ids {
|
||||||
|
@ -142,7 +142,7 @@ fn builtin_idents() -> BTreeSet<Ident> {
|
|||||||
vec![
|
vec![
|
||||||
"str", "char", "bool", "JsValue", "u8", "i8", "u16", "i16", "u32", "i32", "u64", "i64",
|
"str", "char", "bool", "JsValue", "u8", "i8", "u16", "i16", "u32", "i32", "u64", "i64",
|
||||||
"usize", "isize", "f32", "f64", "Result", "String", "Vec", "Option",
|
"usize", "isize", "f32", "f64", "Result", "String", "Vec", "Option",
|
||||||
"ArrayBuffer", "Object", "Promise", "Function",
|
"Array", "ArrayBuffer", "Object", "Promise", "Function",
|
||||||
].into_iter()
|
].into_iter()
|
||||||
.map(|id| proc_macro2::Ident::new(id, proc_macro2::Span::call_site())),
|
.map(|id| proc_macro2::Ident::new(id, proc_macro2::Span::call_site())),
|
||||||
)
|
)
|
||||||
|
@ -13,6 +13,12 @@ use weedle::literal::{ConstValue, FloatLit, IntegerLit};
|
|||||||
use first_pass::{FirstPassRecord, OperationId, OperationData, Signature};
|
use first_pass::{FirstPassRecord, OperationId, OperationData, Signature};
|
||||||
use idl_type::{IdlType, ToIdlType};
|
use idl_type::{IdlType, ToIdlType};
|
||||||
|
|
||||||
|
/// For variadic operations an overload with a `js_sys::Array` argument is generated alongside with
|
||||||
|
/// `operation_name_0`, `operation_name_1`, `operation_name_2`, ..., `operation_name_n` overloads
|
||||||
|
/// which have the count of arguments for passing values to the variadic argument
|
||||||
|
/// in their names, where `n` is this constant.
|
||||||
|
const MAX_VARIADIC_ARGUMENTS_COUNT: usize = 7;
|
||||||
|
|
||||||
/// Take a type and create an immutable shared reference to that type.
|
/// Take a type and create an immutable shared reference to that type.
|
||||||
pub(crate) fn shared_ref(ty: syn::Type, mutable: bool) -> syn::Type {
|
pub(crate) fn shared_ref(ty: syn::Type, mutable: bool) -> syn::Type {
|
||||||
syn::TypeReference {
|
syn::TypeReference {
|
||||||
@ -223,6 +229,7 @@ impl<'src> FirstPassRecord<'src> {
|
|||||||
kind: backend::ast::ImportFunctionKind,
|
kind: backend::ast::ImportFunctionKind,
|
||||||
structural: bool,
|
structural: bool,
|
||||||
catch: bool,
|
catch: bool,
|
||||||
|
variadic: bool,
|
||||||
doc_comment: Option<String>,
|
doc_comment: Option<String>,
|
||||||
) -> Option<backend::ast::ImportFunction> where 'src: 'a {
|
) -> Option<backend::ast::ImportFunction> where 'src: 'a {
|
||||||
// Convert all of the arguments from their IDL type to a `syn` type,
|
// Convert all of the arguments from their IDL type to a `syn` type,
|
||||||
@ -245,7 +252,9 @@ impl<'src> FirstPassRecord<'src> {
|
|||||||
} else {
|
} else {
|
||||||
Vec::with_capacity(idl_arguments.size_hint().0)
|
Vec::with_capacity(idl_arguments.size_hint().0)
|
||||||
};
|
};
|
||||||
for (argument_name, idl_type) in idl_arguments {
|
let idl_arguments: Vec<_> = idl_arguments.collect();
|
||||||
|
let arguments_count = idl_arguments.len();
|
||||||
|
for (i, (argument_name, idl_type)) in idl_arguments.into_iter().enumerate() {
|
||||||
let syn_type = match idl_type.to_syn_type(TypePosition::Argument) {
|
let syn_type = match idl_type.to_syn_type(TypePosition::Argument) {
|
||||||
Some(t) => t,
|
Some(t) => t,
|
||||||
None => {
|
None => {
|
||||||
@ -257,6 +266,12 @@ impl<'src> FirstPassRecord<'src> {
|
|||||||
return None
|
return None
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
let syn_type = if variadic && i == arguments_count - 1 {
|
||||||
|
let path = vec![rust_ident("js_sys"), rust_ident("Array")];
|
||||||
|
shared_ref(leading_colon_path_ty(path), false)
|
||||||
|
} else {
|
||||||
|
syn_type
|
||||||
|
};
|
||||||
let argument_name = rust_ident(&argument_name.to_snake_case());
|
let argument_name = rust_ident(&argument_name.to_snake_case());
|
||||||
arguments.push(simple_fn_arg(argument_name, syn_type));
|
arguments.push(simple_fn_arg(argument_name, syn_type));
|
||||||
}
|
}
|
||||||
@ -296,10 +311,10 @@ impl<'src> FirstPassRecord<'src> {
|
|||||||
},
|
},
|
||||||
rust_name: rust_ident(rust_name),
|
rust_name: rust_ident(rust_name),
|
||||||
js_ret: js_ret.clone(),
|
js_ret: js_ret.clone(),
|
||||||
variadic: false,
|
variadic,
|
||||||
catch,
|
catch,
|
||||||
structural,
|
structural,
|
||||||
shim:{
|
shim: {
|
||||||
let ns = match kind {
|
let ns = match kind {
|
||||||
backend::ast::ImportFunctionKind::ScopedMethod { .. } |
|
backend::ast::ImportFunctionKind::ScopedMethod { .. } |
|
||||||
backend::ast::ImportFunctionKind::Normal => "",
|
backend::ast::ImportFunctionKind::Normal => "",
|
||||||
@ -334,7 +349,8 @@ impl<'src> FirstPassRecord<'src> {
|
|||||||
kind,
|
kind,
|
||||||
is_structural(attrs.as_ref(), container_attrs),
|
is_structural(attrs.as_ref(), container_attrs),
|
||||||
throws(attrs),
|
throws(attrs),
|
||||||
Some(format!("The `{}` getter\n\n{}", name, mdn_doc(self_name, Some(name))))
|
false,
|
||||||
|
Some(format!("The `{}` getter\n\n{}", name, mdn_doc(self_name, Some(name)))),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -360,7 +376,8 @@ impl<'src> FirstPassRecord<'src> {
|
|||||||
kind,
|
kind,
|
||||||
is_structural(attrs.as_ref(), container_attrs),
|
is_structural(attrs.as_ref(), container_attrs),
|
||||||
throws(attrs),
|
throws(attrs),
|
||||||
Some(format!("The `{}` setter\n\n{}", name, mdn_doc(self_name, Some(name))))
|
false,
|
||||||
|
Some(format!("The `{}` setter\n\n{}", name, mdn_doc(self_name, Some(name)))),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -413,7 +430,14 @@ impl<'src> FirstPassRecord<'src> {
|
|||||||
let mut idl_args = Vec::with_capacity(signature.args.len());
|
let mut idl_args = Vec::with_capacity(signature.args.len());
|
||||||
for (i, arg) in signature.args.iter().enumerate() {
|
for (i, arg) in signature.args.iter().enumerate() {
|
||||||
if arg.optional {
|
if arg.optional {
|
||||||
assert!(signature.args[i..].iter().all(|a| a.optional));
|
assert!(
|
||||||
|
signature
|
||||||
|
.args[i..]
|
||||||
|
.iter()
|
||||||
|
.all(|arg| arg.optional || arg.variadic),
|
||||||
|
"Not optional or variadic argument after optional argument: {:?}",
|
||||||
|
signature.args,
|
||||||
|
);
|
||||||
signatures.push((signature, idl_args.clone()));
|
signatures.push((signature, idl_args.clone()));
|
||||||
}
|
}
|
||||||
match arg.ty.to_idl_type(self) {
|
match arg.ty.to_idl_type(self) {
|
||||||
@ -559,18 +583,47 @@ impl<'src> FirstPassRecord<'src> {
|
|||||||
rust_name.push_str(&snake_case_ident(arg_name));
|
rust_name.push_str(&snake_case_ident(arg_name));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
let structural = force_structural || is_structural(signature.orig.attrs.as_ref(), container_attrs);
|
||||||
|
let catch = force_throws || throws(&signature.orig.attrs);
|
||||||
|
let variadic = signature.args.len() == signature.orig.args.len()
|
||||||
|
&& signature.orig.args.last().map(|arg| arg.variadic).unwrap_or(false);
|
||||||
ret.extend(self.create_one_function(
|
ret.extend(self.create_one_function(
|
||||||
name,
|
name,
|
||||||
&rust_name,
|
&rust_name,
|
||||||
signature.args.iter()
|
signature.args.iter()
|
||||||
.zip(&signature.orig.args)
|
.zip(&signature.orig.args)
|
||||||
.map(|(ty, orig_arg)| (orig_arg.name, ty)),
|
.map(|(idl_type, orig_arg)| (orig_arg.name, idl_type)),
|
||||||
&ret_ty,
|
&ret_ty,
|
||||||
kind.clone(),
|
kind.clone(),
|
||||||
force_structural || is_structural(signature.orig.attrs.as_ref(), container_attrs),
|
structural,
|
||||||
force_throws || throws(&signature.orig.attrs),
|
catch,
|
||||||
|
variadic,
|
||||||
None,
|
None,
|
||||||
));
|
));
|
||||||
|
if !variadic {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let last_idl_type = &signature.args[signature.args.len() - 1];
|
||||||
|
let last_name = signature.orig.args[signature.args.len() - 1].name;
|
||||||
|
for i in 0..=MAX_VARIADIC_ARGUMENTS_COUNT {
|
||||||
|
ret.extend(self.create_one_function(
|
||||||
|
name,
|
||||||
|
&format!("{}_{}", rust_name, i),
|
||||||
|
signature.args[..signature.args.len() - 1].iter()
|
||||||
|
.zip(&signature.orig.args)
|
||||||
|
.map(|(idl_type, orig_arg)| (orig_arg.name.to_string(), idl_type))
|
||||||
|
.chain((1..=i).map(|j| (format!("{}_{}", last_name, j), last_idl_type)))
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.iter()
|
||||||
|
.map(|(name, idl_type)| (&name[..], idl_type.clone())),
|
||||||
|
&ret_ty,
|
||||||
|
kind.clone(),
|
||||||
|
structural,
|
||||||
|
catch,
|
||||||
|
false,
|
||||||
|
None,
|
||||||
|
));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user