Skip to content

Commit

Permalink
Generate extern wrappers for inlined functions
Browse files Browse the repository at this point in the history
If bindgen finds an inlined function and the
`--generate-inline-functions` options is enabled, then:

- It will generate two new source and header files with external
  functions that wrap the inlined functions.
- Rerun `Bindings::generate` using the new header file to include these
  wrappers in the generated bindings.

The following options were added:
- `--extern-function-suffix=<suffix>`: Adds <suffix> to the name of each
  external wrapper function (`__extern` is used by default).
- `--extern-functions-file-name=<name>`: Uses <name> as the file name
  for the header and source files (`extern` is used by default).
- `--extern-function-directory=<dir>`: Creates the source and header
  files inside <dir> (`/tmp/bindgen` is used by default).

The C code serialization is experimental and only supports a very
limited set of C functions.

Fixes #1090.
  • Loading branch information
pvdrz committed Nov 4, 2022
1 parent e8ffb42 commit 039d7d0
Show file tree
Hide file tree
Showing 7 changed files with 379 additions and 10 deletions.
24 changes: 24 additions & 0 deletions bindgen-cli/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -574,6 +574,18 @@ where
.value_name("override")
.multiple_occurrences(true)
.number_of_values(1),
Arg::new("extern-functions-file-name")
.long("extern-functions-file-name")
.help("Sets the name of the header and source code files that would be created if any extern wrapper functions must be generated due to the presence of inlined functions.")
.takes_value(true),
Arg::new("extern-functions-directory")
.long("extern-functions-directory")
.help("Sets the directory path where any extra files must be created due to the presence of inlined functions.")
.takes_value(true),
Arg::new("extern-function-suffix")
.long("extern-function-suffix")
.help("Sets the suffix added to the extern wrapper functions generated for inlined functions.")
.takes_value(true),
Arg::new("V")
.long("version")
.help("Prints the version, and exits"),
Expand Down Expand Up @@ -1106,5 +1118,17 @@ where
}
}

if let Some(file_name) = matches.value_of("extern-functions-file-name") {
builder = builder.extern_functions_file_name(file_name);
}

if let Some(directory) = matches.value_of("extern-functions-directory") {
builder = builder.extern_functions_directory(directory);
}

if let Some(suffix) = matches.value_of("extern-function-suffix") {
builder = builder.extern_function_suffix(suffix);
}

Ok((builder, output, verbose))
}
25 changes: 18 additions & 7 deletions bindgen/codegen/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,7 @@ use crate::ir::derive::{
};
use crate::ir::dot;
use crate::ir::enum_ty::{Enum, EnumVariant, EnumVariantValue};
use crate::ir::function::{
Abi, ClangAbi, Function, FunctionKind, FunctionSig, Linkage,
};
use crate::ir::function::{Abi, ClangAbi, Function, FunctionKind, FunctionSig};
use crate::ir::int::IntKind;
use crate::ir::item::{IsOpaque, Item, ItemCanonicalName, ItemCanonicalPath};
use crate::ir::item_kind::ItemKind;
Expand Down Expand Up @@ -4045,9 +4043,8 @@ impl CodeGenerator for Function {

// We can't currently do anything with Internal functions so just
// avoid generating anything for them.
match self.linkage() {
Linkage::Internal => return None,
Linkage::External => {}
if self.is_inlined() {
return None;
}

// Pure virtual methods have no actual symbol, so we can't generate
Expand Down Expand Up @@ -4150,13 +4147,14 @@ impl CodeGenerator for Function {
abi => abi,
};

// Handle overloaded functions by giving each overload its own unique
// Handle overloaded functions by giving each overload its own uniq_ue
// suffix.
let times_seen = result.overload_number(&canonical_name);
if times_seen > 0 {
write!(&mut canonical_name, "{}", times_seen).unwrap();
}

let mut has_link_name_attr = false;
let link_name = mangled_name.unwrap_or(name);
if !is_dynamic_function &&
!utils::names_will_be_identical_after_mangling(
Expand All @@ -4166,6 +4164,7 @@ impl CodeGenerator for Function {
)
{
attributes.push(attributes::link_name(link_name));
has_link_name_attr = true;
}

// Unfortunately this can't piggyback on the `attributes` list because
Expand All @@ -4176,6 +4175,18 @@ impl CodeGenerator for Function {
quote! { #[link(wasm_import_module = #name)] }
});

if let Some(name) = canonical_name.strip_suffix(
ctx.options()
.extern_function_suffix
.as_deref()
.unwrap_or(crate::DEFAULT_EXTERN_FUNCTION_SUFFIX),
) {
if !has_link_name_attr {
attributes.push(attributes::link_name(&canonical_name));
}
canonical_name = name.to_owned();
}

let ident = ctx.rust_ident(canonical_name);
let tokens = quote! {
#wasm_link_attribute
Expand Down
5 changes: 5 additions & 0 deletions bindgen/ir/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use super::int::IntKind;
use super::item::{IsOpaque, Item, ItemAncestors, ItemSet};
use super::item_kind::ItemKind;
use super::module::{Module, ModuleKind};
use super::serialize::CItem;
use super::template::{TemplateInstantiation, TemplateParameters};
use super::traversal::{self, Edge, ItemTraversal};
use super::ty::{FloatKind, Type, TypeKind};
Expand Down Expand Up @@ -461,6 +462,9 @@ pub struct BindgenContext {

/// The set of warnings raised during binding generation.
warnings: Vec<String>,

/// C items that need to be serialized to an extra header file.
pub(crate) c_items: Vec<CItem>,
}

/// A traversal of allowlisted items.
Expand Down Expand Up @@ -576,6 +580,7 @@ If you encounter an error missing from this list, please file an issue or a PR!"
has_type_param_in_array: None,
has_float: None,
warnings: Vec::new(),
c_items: Vec::new(),
}
}

Expand Down
33 changes: 30 additions & 3 deletions bindgen/ir/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use super::item::Item;
use super::traversal::{EdgeKind, Trace, Tracer};
use super::ty::TypeKind;
use crate::clang::{self, Attribute};
use crate::ir::serialize::CItem;
use crate::parse::{
ClangItemParser, ClangSubItemParser, ParseError, ParseResult,
};
Expand Down Expand Up @@ -95,6 +96,9 @@ pub struct Function {

/// The linkage of the function.
linkage: Linkage,

/// Whether this function is inlined or not.
is_inlined: bool,
}

impl Function {
Expand All @@ -106,6 +110,7 @@ impl Function {
comment: Option<String>,
kind: FunctionKind,
linkage: Linkage,
is_inlined: bool,
) -> Self {
Function {
name,
Expand All @@ -114,6 +119,7 @@ impl Function {
comment,
kind,
linkage,
is_inlined,
}
}

Expand Down Expand Up @@ -146,6 +152,11 @@ impl Function {
pub fn linkage(&self) -> Linkage {
self.linkage
}

/// Whether this function is inlined or not.
pub fn is_inlined(&self) -> bool {
self.is_inlined
}
}

impl DotAttributes for Function {
Expand Down Expand Up @@ -675,7 +686,9 @@ impl ClangSubItemParser for Function {
return Err(ParseError::Continue);
}

if cursor.is_inlined_function() {
let is_inlined = cursor.is_inlined_function();

if is_inlined {
if !context.options().generate_inline_functions {
return Err(ParseError::Continue);
}
Expand Down Expand Up @@ -721,8 +734,22 @@ impl ClangSubItemParser for Function {
let mangled_name = cursor_mangling(context, &cursor);
let comment = cursor.raw_comment();

let function =
Self::new(name, mangled_name, sig, comment, kind, linkage);
let function = Self::new(
name,
mangled_name,
sig,
comment,
kind,
linkage,
is_inlined,
);

if is_inlined {
context
.c_items
.push(CItem::from_function(&function, context));
}

Ok(ParseResult::New(function, Some(cursor)))
}
}
Expand Down
1 change: 1 addition & 0 deletions bindgen/ir/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ pub mod item_kind;
pub mod layout;
pub mod module;
pub mod objc;
pub(crate) mod serialize;
pub mod template;
pub mod traversal;
pub mod ty;
Expand Down
Loading

0 comments on commit 039d7d0

Please sign in to comment.