Add support for variadic arguments in WebIDL

This commit is contained in:
Anton Danilkin 2018-09-06 20:00:38 +03:00
parent 3c41d39b16
commit 1c0a34ff8e
7 changed files with 103 additions and 27 deletions

View File

@ -547,13 +547,26 @@ impl<'a, 'b> Rust2Js<'a, 'b> {
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(
"JS",
&format!("{}({}, ...{})",
invoc,
self.js_arguments[..last_arg].join(", "),
self.js_arguments[last_arg])
)
if self.js_arguments.len() != 1 {
self.ret_expr.replace(
"JS",
&format!(
"{}({}, ...{})",
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 {
self.ret_expr.replace(
"JS",

View File

@ -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 {
get un() {
return 1;

View File

@ -90,6 +90,12 @@ 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");
}
#[wasm_bindgen_test]
fn variadic() {
let f = Variadic::new().unwrap();
assert_eq!(f.sum(Box::new([1, 2, 3, 4, 5])), 15);
}
#[wasm_bindgen_test]
fn unforgeable_is_structural() {
let f = Unforgeable::new().unwrap();

View File

@ -57,6 +57,11 @@ interface OptionalAndUnionArguments {
);
};
[Constructor()]
interface Variadic {
short sum(short... values);
};
[Constructor()]
interface Unforgeable {
[Unforgeable] readonly attribute short uno;

View File

@ -89,18 +89,19 @@ pub(crate) struct OperationData<'src> {
pub(crate) is_static: bool,
}
#[derive(Clone)]
#[derive(Clone, Debug)]
pub(crate) struct Signature<'src> {
pub(crate) args: Vec<Arg<'src>>,
pub(crate) ret: weedle::types::ReturnType<'src>,
pub(crate) attrs: &'src Option<ExtendedAttributeList<'src>>,
}
#[derive(Clone)]
#[derive(Clone, Debug)]
pub(crate) struct Arg<'src> {
pub(crate) name: &'src str,
pub(crate) ty: &'src weedle::types::Type<'src>,
pub(crate) optional: bool,
pub(crate) variadic: bool,
}
/// Implemented on an AST node to populate the `FirstPassRecord` struct.
@ -259,19 +260,17 @@ fn first_pass_operation<'src>(
};
let mut args = Vec::with_capacity(arguments.len());
for argument in arguments {
let arg = match argument {
Argument::Single(single) => single,
Argument::Variadic(v) => {
warn!("Unsupported variadic argument {} in {}",
v.identifier.0,
self_name);
return
}
let (name, ty, optional, variadic) = match argument {
Argument::Single(single) =>
(single.identifier.0, &single.type_.type_, single.optional.is_some(), false),
Argument::Variadic(variadic) =>
(variadic.identifier.0, &variadic.type_, false, true),
};
args.push(Arg {
name: arg.identifier.0,
ty: &arg.type_.type_,
optional: arg.optional.is_some(),
name,
ty,
optional,
variadic,
});
}
for id in ids {

View File

@ -137,7 +137,7 @@ fn builtin_idents() -> BTreeSet<Ident> {
BTreeSet::from_iter(
vec![
"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", "Box", "Vec", "Option",
"ArrayBuffer", "Object", "Promise",
].into_iter()
.map(|id| proc_macro2::Ident::new(id, proc_macro2::Span::call_site())),

View File

@ -172,6 +172,24 @@ pub(crate) fn slice_ty(t: syn::Type) -> syn::Type {
}.into()
}
/// From `T` create `Box<T>`.
pub(crate) fn box_ty(t: syn::Type) -> syn::Type {
let arguments = syn::PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments {
colon2_token: None,
lt_token: Default::default(),
args: FromIterator::from_iter(vec![
syn::GenericArgument::Type(t),
]),
gt_token: Default::default(),
});
let ident = raw_ident("Box");
let seg = syn::PathSegment { ident, arguments };
let path: syn::Path = seg.into();
let ty = syn::TypePath { qself: None, path };
ty.into()
}
/// From `T` create `Vec<T>`.
pub(crate) fn vec_ty(t: syn::Type) -> syn::Type {
let arguments = syn::PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments {
@ -223,6 +241,7 @@ impl<'src> FirstPassRecord<'src> {
kind: backend::ast::ImportFunctionKind,
structural: bool,
catch: bool,
variadic: bool,
doc_comment: Option<String>,
) -> Option<backend::ast::ImportFunction> where 'src: 'a {
// Convert all of the arguments from their IDL type to a `syn` type,
@ -245,7 +264,9 @@ impl<'src> FirstPassRecord<'src> {
} else {
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) {
Some(t) => t,
None => {
@ -257,6 +278,20 @@ impl<'src> FirstPassRecord<'src> {
return None
}
};
let syn_type = if variadic && i == arguments_count - 1 {
// Blacklist unsupported slice types
match idl_type {
| IdlType::DomString
| IdlType::ByteString
| IdlType::UsvString => return None,
IdlType::Interface(..) => return None,
_ => box_ty(slice_ty(syn_type))
}
} else {
syn_type
};
let argument_name = rust_ident(&argument_name.to_snake_case());
arguments.push(simple_fn_arg(argument_name, syn_type));
}
@ -296,10 +331,10 @@ impl<'src> FirstPassRecord<'src> {
},
rust_name: rust_ident(rust_name),
js_ret: js_ret.clone(),
variadic: false,
variadic,
catch,
structural,
shim:{
shim: {
let ns = match kind {
backend::ast::ImportFunctionKind::ScopedMethod { .. } |
backend::ast::ImportFunctionKind::Normal => "",
@ -334,7 +369,8 @@ impl<'src> FirstPassRecord<'src> {
kind,
is_structural(attrs.as_ref(), container_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 +396,8 @@ impl<'src> FirstPassRecord<'src> {
kind,
is_structural(attrs.as_ref(), container_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 +450,14 @@ impl<'src> FirstPassRecord<'src> {
let mut idl_args = Vec::with_capacity(signature.args.len());
for (i, arg) in signature.args.iter().enumerate() {
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()));
}
match arg.ty.to_idl_type(self) {
@ -569,6 +613,8 @@ impl<'src> FirstPassRecord<'src> {
kind.clone(),
force_structural || is_structural(signature.orig.attrs.as_ref(), container_attrs),
force_throws || throws(&signature.orig.attrs),
signature.args.len() == signature.orig.args.len()
&& signature.orig.args.last().map(|arg| arg.variadic).unwrap_or(false),
None,
));
}