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-extern-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 additional 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 24, 2022
1 parent 95fd17b commit 0736ef4
Show file tree
Hide file tree
Showing 9 changed files with 487 additions and 9 deletions.
31 changes: 31 additions & 0 deletions bindgen-cli/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -565,6 +565,21 @@ where
Arg::new("wrap-unsafe-ops")
.long("wrap-unsafe-ops")
.help("Wrap unsafe operations in unsafe blocks."),
Arg::new("generate-extern-functions")
.long("generate-extern-functions")
.help("Generate extern wrappers for inlined functions"),
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 @@ -1092,5 +1107,21 @@ where
builder = builder.wrap_unsafe_ops(true);
}

if matches.is_present("generate-extern-functions") {
builder = builder.generate_extern_functions(true);
}

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))
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 11 additions & 0 deletions bindgen-tests/tests/headers/generate-extern-functions.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// bindgen-flags: --generate-extern-functions

static inline int foo() {
return 0;
}
static int bar() {
return 1;
}
inline int baz() {
return 2;
}
21 changes: 18 additions & 3 deletions bindgen/codegen/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3995,9 +3995,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 matches!(self.linkage(), Linkage::Internal) {
return None;
}

// Pure virtual methods have no actual symbol, so we can't generate
Expand Down Expand Up @@ -4107,6 +4106,7 @@ impl CodeGenerator for Function {
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 @@ -4116,6 +4116,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 @@ -4126,6 +4127,20 @@ impl CodeGenerator for Function {
quote! { #[link(wasm_import_module = #name)] }
});

if ctx.options().generate_extern_functions.is_second_run() {
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
16 changes: 15 additions & 1 deletion 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::{ClangSubItemParser, ParseError, ParseResult};
use clang_sys::{self, CXCallingConv};
use proc_macro2;
Expand Down Expand Up @@ -678,9 +679,12 @@ impl ClangSubItemParser for Function {
.definition()
.map_or(false, |x| x.is_inlined_function())
{
if !context.options().generate_inline_functions {
if !(context.options().generate_inline_functions ||
context.options().generate_extern_functions.is_true())
{
return Err(ParseError::Continue);
}

if cursor.is_deleted_function() {
return Err(ParseError::Continue);
}
Expand Down Expand Up @@ -725,6 +729,16 @@ impl ClangSubItemParser for Function {

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

if matches!(linkage, Linkage::Internal) &&
context.options().generate_extern_functions.is_true()
{
match CItem::from_function(&function, context) {
Ok(c_item) => context.c_items.push(c_item),
Err(err) => warn!("Serialization failed: {:?}", err),
}
}

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 0736ef4

Please sign in to comment.