diff --git a/bindgen/callbacks.rs b/bindgen/callbacks.rs index 582446687c..9c29f05642 100644 --- a/bindgen/callbacks.rs +++ b/bindgen/callbacks.rs @@ -135,6 +135,16 @@ pub trait ParseCallbacks: fmt::Debug { fn process_comment(&self, _comment: &str) -> Option { None } + + /// Process a function name that as exactly one `va_list` argument + /// to be wrapped as a variadic function with the wrapped static function + /// feature. + /// + /// The returned string is new function name. + #[cfg(feature = "experimental")] + fn wrap_as_variadic_fn(&self, _name: &str) -> Option { + None + } } /// Relevant information about a type to which new derive attributes will be added using diff --git a/bindgen/codegen/mod.rs b/bindgen/codegen/mod.rs index e7cf1f73a4..fb2edb80e1 100644 --- a/bindgen/codegen/mod.rs +++ b/bindgen/codegen/mod.rs @@ -220,6 +220,12 @@ impl From for Vec<&'static str> { } } +#[derive(Debug)] +struct WrapAsVariadic { + new_name: String, + idx_of_va_list_arg: usize, +} + struct CodegenResult<'a> { items: Vec, dynamic_items: DynamicItems, @@ -268,7 +274,9 @@ struct CodegenResult<'a> { /// that name. This lets us give each overload a unique suffix. overload_counters: HashMap, - items_to_serialize: Vec, + /// List of items to serialize. With optionally the argument for the wrap as + /// variadic transformation to be applied. + items_to_serialize: Vec<(ItemId, Option)>, } impl<'a> CodegenResult<'a> { @@ -4210,9 +4218,6 @@ impl CodeGenerator for Function { result.saw_function(seen_symbol_name); } - let args = utils::fnsig_arguments(ctx, signature); - let ret = utils::fnsig_return_ty(ctx, signature); - let mut attributes = vec![]; if ctx.options().rust_features().must_use_function { @@ -4297,10 +4302,6 @@ impl CodeGenerator for Function { abi => abi, }; - if is_internal && ctx.options().wrap_static_fns { - result.items_to_serialize.push(item.id()); - } - // Handle overloaded functions by giving each overload its own unique // suffix. let times_seen = result.overload_number(&canonical_name); @@ -4334,12 +4335,49 @@ impl CodeGenerator for Function { quote! { #[link(wasm_import_module = #name)] } }); - if is_internal && ctx.options().wrap_static_fns && !has_link_name_attr { + let should_wrap = + is_internal && ctx.options().wrap_static_fns && !has_link_name_attr; + + if should_wrap { let name = canonical_name.clone() + ctx.wrap_static_fns_suffix(); attributes.push(attributes::link_name::(&name)); } - let ident = ctx.rust_ident(canonical_name); + let wrap_as_variadic = if should_wrap && !signature.is_variadic() { + utils::wrap_as_variadic_fn(ctx, signature, name) + } else { + None + }; + + let (ident, args) = if let Some(WrapAsVariadic { + idx_of_va_list_arg, + new_name, + }) = &wrap_as_variadic + { + ( + new_name, + utils::fnsig_arguments_iter( + ctx, + // Prune argument at index (idx_of_va_list_arg) + signature.argument_types().iter().enumerate().filter_map( + |(idx, t)| { + if idx == *idx_of_va_list_arg { + None + } else { + Some(t) + } + }, + ), + // and replace it by a `...` (variadic symbol and the end of the signature) + true, + ), + ) + } else { + (&canonical_name, utils::fnsig_arguments(ctx, signature)) + }; + let ret = utils::fnsig_return_ty(ctx, signature); + + let ident = ctx.rust_ident(ident); let tokens = quote! { #wasm_link_attribute extern #abi { @@ -4348,6 +4386,13 @@ impl CodeGenerator for Function { } }; + // Add the item to the serialization list if necessary + if should_wrap { + result + .items_to_serialize + .push((item.id(), wrap_as_variadic)); + } + // If we're doing dynamic binding generation, add to the dynamic items. if is_dynamic_function { let args_identifiers = @@ -4850,9 +4895,9 @@ pub(crate) mod utils { writeln!(code, "// Static wrappers\n")?; - for &id in &result.items_to_serialize { - let item = context.resolve_item(id); - item.serialize(context, (), &mut vec![], &mut code)?; + for (id, wrap_as_variadic) in &result.items_to_serialize { + let item = context.resolve_item(*id); + item.serialize(context, wrap_as_variadic, &mut vec![], &mut code)?; } std::fs::write(source_path, code)?; @@ -4860,6 +4905,60 @@ pub(crate) mod utils { Ok(()) } + pub(super) fn wrap_as_variadic_fn( + ctx: &BindgenContext, + signature: &FunctionSig, + name: &str, + ) -> Option { + // Fast path, exclude because: + // - with 0 args: no va_list possible, so no point searching for one + // - with 1 args: cannot have a `va_list` and another arg (required by va_start) + if signature.argument_types().len() <= 1 { + return None; + } + + let mut it = signature.argument_types().iter().enumerate().filter_map( + |(idx, (_name, mut type_id))| { + // Hand rolled visitor that checks for the presence of `va_list` + loop { + let ty = ctx.resolve_type(type_id); + if Some("__builtin_va_list") == ty.name() { + return Some(idx); + } + match ty.kind() { + TypeKind::Alias(type_id_alias) => { + type_id = *type_id_alias + } + TypeKind::ResolvedTypeRef(type_id_typedef) => { + type_id = *type_id_typedef + } + _ => break, + } + } + None + }, + ); + + // Return THE idx (by checking that there is no idx after) + // This is done since we cannot handle multiple `va_list` + it.next().filter(|_| it.next().is_none()).and_then(|idx| { + // Call the `wrap_as_variadic_fn` callback + #[cfg(feature = "experimental")] + { + ctx.options() + .last_callback(|c| c.wrap_as_variadic_fn(name)) + .map(|new_name| super::WrapAsVariadic { + new_name, + idx_of_va_list_arg: idx, + }) + } + #[cfg(not(feature = "experimental"))] + { + None + } + }) + } + pub(crate) fn prepend_bitfield_unit_type( ctx: &BindgenContext, result: &mut Vec, diff --git a/bindgen/codegen/serialize.rs b/bindgen/codegen/serialize.rs index 1136234e05..76d5be45f4 100644 --- a/bindgen/codegen/serialize.rs +++ b/bindgen/codegen/serialize.rs @@ -10,7 +10,7 @@ use crate::ir::item::ItemCanonicalName; use crate::ir::item_kind::ItemKind; use crate::ir::ty::{FloatKind, Type, TypeKind}; -use super::CodegenError; +use super::{CodegenError, WrapAsVariadic}; fn get_loc(item: &Item) -> String { item.location() @@ -18,7 +18,7 @@ fn get_loc(item: &Item) -> String { .unwrap_or_else(|| "unknown".to_owned()) } -pub(crate) trait CSerialize<'a> { +pub(super) trait CSerialize<'a> { type Extra; fn serialize( @@ -31,18 +31,18 @@ pub(crate) trait CSerialize<'a> { } impl<'a> CSerialize<'a> for Item { - type Extra = (); + type Extra = &'a Option; fn serialize( &self, ctx: &BindgenContext, - (): Self::Extra, + extra: Self::Extra, stack: &mut Vec, writer: &mut W, ) -> Result<(), CodegenError> { match self.kind() { ItemKind::Function(func) => { - func.serialize(ctx, self, stack, writer) + func.serialize(ctx, (self, extra), stack, writer) } kind => Err(CodegenError::Serialize { msg: format!("Cannot serialize item kind {:?}", kind), @@ -53,12 +53,12 @@ impl<'a> CSerialize<'a> for Item { } impl<'a> CSerialize<'a> for Function { - type Extra = &'a Item; + type Extra = (&'a Item, &'a Option); fn serialize( &self, ctx: &BindgenContext, - item: Self::Extra, + (item, wrap_as_variadic): Self::Extra, stack: &mut Vec, writer: &mut W, ) -> Result<(), CodegenError> { @@ -85,19 +85,30 @@ impl<'a> CSerialize<'a> for Function { let args = { let mut count = 0; + let idx_to_prune = wrap_as_variadic.as_ref().map( + |WrapAsVariadic { + idx_of_va_list_arg, .. + }| *idx_of_va_list_arg, + ); + signature .argument_types() .iter() .cloned() - .map(|(opt_name, type_id)| { - ( - opt_name.unwrap_or_else(|| { - let name = format!("arg_{}", count); - count += 1; - name - }), - type_id, - ) + .enumerate() + .filter_map(|(idx, (opt_name, type_id))| { + if Some(idx) == idx_to_prune { + None + } else { + Some(( + opt_name.unwrap_or_else(|| { + let name = format!("arg_{}", count); + count += 1; + name + }), + type_id, + )) + } }) .collect::>() }; @@ -106,33 +117,72 @@ impl<'a> CSerialize<'a> for Function { let wrap_name = format!("{}{}", name, ctx.wrap_static_fns_suffix()); // The function's return type - let ret_ty = { + let (ret_item, ret_ty) = { let type_id = signature.return_type(); - let item = ctx.resolve_item(type_id); - let ret_ty = item.expect_type(); + let ret_item = ctx.resolve_item(type_id); + let ret_ty = ret_item.expect_type(); // Write `ret_ty`. - ret_ty.serialize(ctx, item, stack, writer)?; + ret_ty.serialize(ctx, ret_item, stack, writer)?; - ret_ty + (ret_item, ret_ty) }; // Write `wrap_name(args`. write!(writer, " {}(", wrap_name)?; serialize_args(&args, ctx, writer)?; - // Write `) { name(` if the function returns void and `) { return name(` if it does not. - if ret_ty.is_void() { - write!(writer, ") {{ {}(", name)?; + if wrap_as_variadic.is_none() { + // Write `) { name(` if the function returns void and `) { return name(` if it does not. + if ret_ty.is_void() { + write!(writer, ") {{ {}(", name)?; + } else { + write!(writer, ") {{ return {}(", name)?; + } } else { - write!(writer, ") {{ return {}(", name)?; + // Write `, ...) {` + writeln!(writer, ", ...) {{")?; + + // Declare the return type `RET_TY ret;` if their is a need to do so + if !ret_ty.is_void() { + ret_ty.serialize(ctx, ret_item, stack, writer)?; + writeln!(writer, " ret;")?; + } + + // Setup va_list + writeln!(writer, "va_list ap;\n")?; + writeln!(writer, "va_start(ap, {});", args.last().unwrap().0)?; + + // Write `ret = name(` or `name(` depending if the function returns something + if !ret_ty.is_void() { + write!(writer, "ret = ")?; + } + write!(writer, "{}(", name)?; + } + + let mut args: Vec<_> = args.into_iter().map(|(name, _)| name).collect(); + if let Some(WrapAsVariadic { + idx_of_va_list_arg, .. + }) = wrap_as_variadic + { + args.insert(*idx_of_va_list_arg, "ap".to_owned()); } - // Write `arg_names); }`. - serialize_sep(", ", args.iter(), ctx, writer, |(name, _), _, buf| { + // Write `arg_names);`. + serialize_sep(", ", args.iter(), ctx, writer, |name, _, buf| { write!(buf, "{}", name).map_err(From::from) })?; - writeln!(writer, "); }}")?; + write!(writer, ");{}", if wrap_as_variadic.is_none() { " " } else { "\n" })?; + + if wrap_as_variadic.is_some() { + // End va_list and return the result if their is one + writeln!(writer, "va_end(ap);")?; + if !ret_ty.is_void() { + writeln!(writer, "return ret;")?; + } + } + + writeln!(writer, "}}")?; Ok(()) }