diff options
author | Jeff Vander Stoep <jeffv@google.com> | 2023-02-17 13:39:39 +0000 |
---|---|---|
committer | Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com> | 2023-02-17 13:39:39 +0000 |
commit | 727472d3f84b499995ca6b742a09a4fa83103c86 (patch) | |
tree | 7010a4ae0b5e9271943c64f91db035d1057ba750 | |
parent | 4736324f2092c63996fe1a0348bf129d31001907 (diff) | |
parent | 10638ff3e402613f0f02b8760f9e46a3f31ce119 (diff) | |
download | bindgen-727472d3f84b499995ca6b742a09a4fa83103c86.tar.gz |
Upgrade bindgen to 0.64.0 am: 8258a70eed am: fc4b68ac6d am: 10638ff3e4
Original change: https://android-review.googlesource.com/c/platform/external/rust/crates/bindgen/+/2440212
Change-Id: I67a361518984d0d343a22db71a6a6d6c733ef8ee
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
-rw-r--r-- | .cargo_vcs_info.json | 7 | ||||
-rw-r--r-- | Android.bp | 9 | ||||
-rw-r--r-- | Cargo.toml | 8 | ||||
-rw-r--r-- | Cargo.toml.orig | 6 | ||||
-rw-r--r-- | METADATA | 14 | ||||
-rw-r--r-- | README.md | 85 | ||||
-rw-r--r-- | callbacks.rs | 47 | ||||
-rw-r--r-- | cargo2android.json | 2 | ||||
-rw-r--r-- | codegen/dyngen.rs | 2 | ||||
-rw-r--r-- | codegen/mod.rs | 286 | ||||
-rw-r--r-- | codegen/serialize.rs | 356 | ||||
-rw-r--r-- | ir/analysis/template_params.rs | 5 | ||||
-rw-r--r-- | ir/comp.rs | 2 | ||||
-rw-r--r-- | ir/context.rs | 99 | ||||
-rw-r--r-- | ir/enum_ty.rs | 2 | ||||
-rw-r--r-- | ir/function.rs | 46 | ||||
-rw-r--r-- | ir/item.rs | 20 | ||||
-rw-r--r-- | ir/objc.rs | 13 | ||||
-rw-r--r-- | ir/template.rs | 1 | ||||
-rw-r--r-- | ir/ty.rs | 7 | ||||
-rw-r--r-- | ir/var.rs | 21 | ||||
-rw-r--r-- | lib.rs | 158 | ||||
-rw-r--r-- | parse.rs | 64 | ||||
-rw-r--r-- | patches/bindgen_cmd.diff | 90 | ||||
-rw-r--r-- | regex_set.rs | 7 |
25 files changed, 1001 insertions, 356 deletions
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json index f1c8552..32c33e6 100644 --- a/.cargo_vcs_info.json +++ b/.cargo_vcs_info.json @@ -1,5 +1,6 @@ { "git": { - "sha1": "89032649044d875983a851fff6fbde2d4e2ceaeb" - } -} + "sha1": "ae6817256ac557981906e93a1f866349db85053e" + }, + "path_in_vcs": "bindgen" +}
\ No newline at end of file @@ -1,7 +1,6 @@ // This file is generated by cargo2android.py --config cargo2android.json. // Do not modify this file as changes will be overridden on upgrade. - package { default_applicable_licenses: ["external_rust_crates_bindgen_license"], } @@ -43,13 +42,15 @@ rust_library_host { name: "libbindgen", crate_name: "bindgen", cargo_env_compat: true, - cargo_pkg_version: "0.63.0", + cargo_pkg_version: "0.64.0", srcs: [ "lib.rs", ":copy_bindgen_build_out", ], edition: "2018", features: [ + "cli", + "experimental", "runtime", "which", "which-rustfmt", @@ -70,8 +71,4 @@ rust_library_host { "libwhich", ], compile_multilib: "first", - apex_available: [ - "//apex_available:platform", - "//apex_available:anyapex", - ], } @@ -11,9 +11,9 @@ [package] edition = "2018" -rust-version = "1.57.0" +rust-version = "1.60.0" name = "bindgen" -version = "0.63.0" +version = "0.64.0" authors = [ "Jyun-Yan You <jyyou.tw@gmail.com>", "Emilio Cobos Álvarez <emilio@crisal.io>", @@ -24,7 +24,7 @@ build = "build.rs" description = "Automatically generates Rust FFI bindings to C and C++ libraries." homepage = "https://rust-lang.github.io/rust-bindgen/" documentation = "https://docs.rs/bindgen" -readme = "README.md" +readme = "../README.md" keywords = [ "bindings", "ffi", @@ -100,11 +100,13 @@ optional = true default-features = false [features] +cli = [] default = [ "logging", "runtime", "which-rustfmt", ] +experimental = [] logging = ["log"] runtime = ["clang-sys/runtime"] static = ["clang-sys/static"] diff --git a/Cargo.toml.orig b/Cargo.toml.orig index 6522209..bc53be5 100644 --- a/Cargo.toml.orig +++ b/Cargo.toml.orig @@ -14,11 +14,11 @@ readme = "../README.md" repository = "https://github.com/rust-lang/rust-bindgen" documentation = "https://docs.rs/bindgen" homepage = "https://rust-lang.github.io/rust-bindgen/" -version = "0.63.0" +version = "0.64.0" edition = "2018" build = "build.rs" # If you change this, also update README.md and msrv in .github/workflows/bindgen.yml -rust-version = "1.57.0" +rust-version = "1.60.0" [lib] name = "bindgen" @@ -47,6 +47,8 @@ static = ["clang-sys/static"] runtime = ["clang-sys/runtime"] # Dynamically discover a `rustfmt` binary using the `which` crate which-rustfmt = ["which"] +cli = [] +experimental = [] # These features only exist for CI testing -- don't use them if you're not hacking # on bindgen! @@ -1,3 +1,7 @@ +# This project was upgraded with external_updater. +# Usage: tools/external_updater/updater.sh update rust/crates/bindgen +# For more info, check https://cs.android.com/android/platform/superproject/+/master:tools/external_updater/README.md + name: "bindgen" description: "Automatically generates Rust FFI bindings to C and C++ libraries." third_party { @@ -7,13 +11,13 @@ third_party { } url { type: ARCHIVE - value: "https://static.crates.io/crates/bindgen/bindgen-0.63.0.crate" + value: "https://static.crates.io/crates/bindgen/bindgen-0.64.0.crate" } - version: "0.63.0" + version: "0.64.0" license_type: NOTICE last_upgrade_date { - year: 2022 - month: 12 - day: 6 + year: 2023 + month: 2 + day: 16 } } diff --git a/README.md b/README.md deleted file mode 100644 index c78f00d..0000000 --- a/README.md +++ /dev/null @@ -1,85 +0,0 @@ -[![crates.io](https://img.shields.io/crates/v/bindgen.svg)](https://crates.io/crates/bindgen) -[![docs.rs](https://docs.rs/bindgen/badge.svg)](https://docs.rs/bindgen/) - -# `bindgen` - -**`bindgen` automatically generates Rust FFI bindings to C (and some C++) libraries.** - -For example, given the C header `doggo.h`: - -```c -typedef struct Doggo { - int many; - char wow; -} Doggo; - -void eleven_out_of_ten_majestic_af(Doggo* pupper); -``` - -`bindgen` produces Rust FFI code allowing you to call into the `doggo` library's -functions and use its types: - -```rust -/* automatically generated by rust-bindgen 0.99.9 */ - -#[repr(C)] -pub struct Doggo { - pub many: ::std::os::raw::c_int, - pub wow: ::std::os::raw::c_char, -} - -extern "C" { - pub fn eleven_out_of_ten_majestic_af(pupper: *mut Doggo); -} -``` - -## Users Guide - -[📚 Read the `bindgen` users guide here! 📚](https://rust-lang.github.io/rust-bindgen) - -## MSRV - -The minimum supported Rust version is **1.57.0**. - -No MSRV bump policy has been established yet, so MSRV may increase in any release. - -The MSRV is the minimum Rust version that can be used to *compile* `bindgen`. However, `bindgen` can generate bindings that are compatible with Rust versions below the current MSRV. - -## API Reference - -[API reference documentation is on docs.rs](https://docs.rs/bindgen) - -## Environment Variables - -In addition to the [library API](https://docs.rs/bindgen) and [executable command-line API][bindgen-cmdline], -`bindgen` can be controlled through environment variables. - -End-users should set these environment variables to modify `bindgen`'s behavior without modifying the source code of direct consumers of `bindgen`. - -- `BINDGEN_EXTRA_CLANG_ARGS`: extra arguments to pass to `clang` - - Arguments are whitespace-separated - - Use shell-style quoting to pass through whitespace - - Examples: - - Specify alternate sysroot: `--sysroot=/path/to/sysroot` - - Add include search path with spaces: `-I"/path/with spaces"` -- `BINDGEN_EXTRA_CLANG_ARGS_<TARGET>`: similar to `BINDGEN_EXTRA_CLANG_ARGS`, - but used to set per-target arguments to pass to clang. Useful to set system include - directories in a target-specific way in cross-compilation environments with multiple targets. - Has precedence over `BINDGEN_EXTRA_CLANG_ARGS`. - -Additionally, `bindgen` uses `libclang` to parse C and C++ header files. -To modify how `bindgen` searches for `libclang`, see the [`clang-sys` documentation][clang-sys-env]. -For more details on how `bindgen` uses `libclang`, see the [`bindgen` users guide][bindgen-book-clang]. - -## Releases - -We don't follow a specific release calendar, but if you need a release please -file an issue requesting that (ping `@emilio` for increased effectiveness). - -## Contributing - -[See `CONTRIBUTING.md` for hacking on `bindgen`!](./CONTRIBUTING.md) - -[bindgen-cmdline]: https://rust-lang.github.io/rust-bindgen/command-line-usage.html -[clang-sys-env]: https://github.com/KyleMayes/clang-sys#environment-variables -[bindgen-book-clang]: https://rust-lang.github.io/rust-bindgen/requirements.html#clang diff --git a/callbacks.rs b/callbacks.rs index 5e8ac78..dc20e25 100644 --- a/callbacks.rs +++ b/callbacks.rs @@ -25,14 +25,23 @@ impl Default for MacroParsingBehavior { /// A trait to allow configuring different kinds of types in different /// situations. pub trait ParseCallbacks: fmt::Debug { + #[cfg(feature = "cli")] + #[doc(hidden)] + fn cli_args(&self) -> Vec<String> { + vec![] + } + /// This function will be run on every macro that is identified. fn will_parse_macro(&self, _name: &str) -> MacroParsingBehavior { MacroParsingBehavior::Default } - /// This function will run for every function. The returned value determines the name visible - /// in the bindings. - fn generated_name_override(&self, _function_name: &str) -> Option<String> { + /// This function will run for every extern variable and function. The returned value determines + /// the name visible in the bindings. + fn generated_name_override( + &self, + _item_info: ItemInfo<'_>, + ) -> Option<String> { None } @@ -117,8 +126,40 @@ pub trait ParseCallbacks: fmt::Debug { /// Relevant information about a type to which new derive attributes will be added using /// [`ParseCallbacks::add_derives`]. +#[derive(Debug)] #[non_exhaustive] pub struct DeriveInfo<'a> { /// The name of the type. pub name: &'a str, + /// The kind of the type. + pub kind: TypeKind, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +/// The kind of the current type. +pub enum TypeKind { + /// The type is a Rust `struct`. + Struct, + /// The type is a Rust `enum`. + Enum, + /// The type is a Rust `union`. + Union, +} + +/// An struct providing information about the item being passed to `ParseCallbacks::generated_name_override`. +#[non_exhaustive] +pub struct ItemInfo<'a> { + /// The name of the item + pub name: &'a str, + /// The kind of item + pub kind: ItemKind, +} + +/// An enum indicating the kind of item for an ItemInfo. +#[non_exhaustive] +pub enum ItemKind { + /// A Function + Function, + /// A Variable + Var, } diff --git a/cargo2android.json b/cargo2android.json index 655ad69..fd62b50 100644 --- a/cargo2android.json +++ b/cargo2android.json @@ -1,6 +1,6 @@ { "copy-out": true, - "features": "runtime,which-rustfmt", + "features": "cli,experimental,runtime,which-rustfmt", "host-first-multilib": true, "run": true } diff --git a/codegen/dyngen.rs b/codegen/dyngen.rs index 5e734cc..d8ea811 100644 --- a/codegen/dyngen.rs +++ b/codegen/dyngen.rs @@ -170,7 +170,7 @@ impl DynamicItems { if !is_variadic { self.struct_implementation.push(quote! { #(#attributes)* - pub unsafe fn #ident ( &self, #( #args ),* ) -> #ret_ty { + pub unsafe fn #ident ( &self, #( #args ),* ) #ret_ty { #call_body } }); diff --git a/codegen/mod.rs b/codegen/mod.rs index e537242..b6fb70e 100644 --- a/codegen/mod.rs +++ b/codegen/mod.rs @@ -4,6 +4,7 @@ mod helpers; mod impl_debug; mod impl_partialeq; mod postprocessing; +mod serialize; pub mod struct_layout; #[cfg(test)] @@ -18,6 +19,7 @@ use self::struct_layout::StructLayoutTracker; use super::BindgenOptions; +use crate::callbacks::{DeriveInfo, TypeKind as DeriveTypeKind}; use crate::ir::analysis::{HasVtable, Sizedness}; use crate::ir::annotations::FieldAccessorKind; use crate::ir::comp::{ @@ -58,6 +60,29 @@ use std::iter; use std::ops; use std::str::FromStr; +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum CodegenError { + Serialize { msg: String, loc: String }, + Io(String), +} + +impl From<std::io::Error> for CodegenError { + fn from(err: std::io::Error) -> Self { + Self::Io(err.to_string()) + } +} + +impl std::fmt::Display for CodegenError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + CodegenError::Serialize { msg, loc } => { + write!(f, "serialization error at {}: {}", loc, msg) + } + CodegenError::Io(err) => err.fmt(f), + } + } +} + // Name of type defined in constified enum module pub static CONSTIFIED_ENUM_MODULE_REPR_NAME: &str = "Type"; @@ -240,6 +265,8 @@ struct CodegenResult<'a> { /// function name to the number of overloads we have already codegen'd for /// that name. This lets us give each overload a unique suffix. overload_counters: HashMap<String, u32>, + + items_to_serialize: Vec<ItemId>, } impl<'a> CodegenResult<'a> { @@ -257,6 +284,7 @@ impl<'a> CodegenResult<'a> { functions_seen: Default::default(), vars_seen: Default::default(), overload_counters: Default::default(), + items_to_serialize: Default::default(), } } @@ -434,10 +462,10 @@ trait CodeGenerator { /// Extra information returned to the caller. type Return; - fn codegen<'a>( + fn codegen( &self, ctx: &BindgenContext, - result: &mut CodegenResult<'a>, + result: &mut CodegenResult<'_>, extra: &Self::Extra, ) -> Self::Return; } @@ -477,10 +505,10 @@ impl CodeGenerator for Item { type Extra = (); type Return = (); - fn codegen<'a>( + fn codegen( &self, ctx: &BindgenContext, - result: &mut CodegenResult<'a>, + result: &mut CodegenResult<'_>, _extra: &(), ) { debug!("<Item as CodeGenerator>::codegen: self = {:?}", self); @@ -509,10 +537,10 @@ impl CodeGenerator for Module { type Extra = Item; type Return = (); - fn codegen<'a>( + fn codegen( &self, ctx: &BindgenContext, - result: &mut CodegenResult<'a>, + result: &mut CodegenResult<'_>, item: &Item, ) { debug!("<Module as CodeGenerator>::codegen: item = {:?}", item); @@ -601,10 +629,10 @@ impl CodeGenerator for Var { type Extra = Item; type Return = (); - fn codegen<'a>( + fn codegen( &self, ctx: &BindgenContext, - result: &mut CodegenResult<'a>, + result: &mut CodegenResult<'_>, item: &Item, ) { use crate::ir::var::VarType; @@ -748,10 +776,10 @@ impl CodeGenerator for Type { type Extra = Item; type Return = (); - fn codegen<'a>( + fn codegen( &self, ctx: &BindgenContext, - result: &mut CodegenResult<'a>, + result: &mut CodegenResult<'_>, item: &Item, ) { debug!("<Type as CodeGenerator>::codegen: item = {:?}", item); @@ -1069,10 +1097,10 @@ impl<'a> CodeGenerator for Vtable<'a> { type Extra = Item; type Return = (); - fn codegen<'b>( + fn codegen( &self, ctx: &BindgenContext, - result: &mut CodegenResult<'b>, + result: &mut CodegenResult<'_>, item: &Item, ) { assert_eq!(item.id(), self.item_id); @@ -1168,10 +1196,10 @@ impl CodeGenerator for TemplateInstantiation { type Extra = Item; type Return = (); - fn codegen<'a>( + fn codegen( &self, ctx: &BindgenContext, - result: &mut CodegenResult<'a>, + result: &mut CodegenResult<'_>, item: &Item, ) { debug_assert!(item.is_enabled_for_codegen(ctx)); @@ -1796,10 +1824,10 @@ impl CodeGenerator for CompInfo { type Extra = Item; type Return = (); - fn codegen<'a>( + fn codegen( &self, ctx: &BindgenContext, - result: &mut CodegenResult<'a>, + result: &mut CodegenResult<'_>, item: &Item, ) { debug!("<CompInfo as CodeGenerator>::codegen: item = {:?}", item); @@ -2100,11 +2128,18 @@ impl CodeGenerator for CompInfo { let mut derives: Vec<_> = derivable_traits.into(); derives.extend(item.annotations().derives().iter().map(String::as_str)); + let is_rust_union = is_union && struct_layout.is_rust_union(); + // The custom derives callback may return a list of derive attributes; // add them to the end of the list. let custom_derives = ctx.options().all_callbacks(|cb| { - cb.add_derives(&crate::callbacks::DeriveInfo { + cb.add_derives(&DeriveInfo { name: &canonical_name, + kind: if is_rust_union { + DeriveTypeKind::Union + } else { + DeriveTypeKind::Struct + }, }) }); // In most cases this will be a no-op, since custom_derives will be empty. @@ -2118,7 +2153,7 @@ impl CodeGenerator for CompInfo { attributes.push(attributes::must_use()); } - let mut tokens = if is_union && struct_layout.is_rust_union() { + let mut tokens = if is_rust_union { quote! { #( #attributes )* pub union #canonical_ident @@ -2193,16 +2228,7 @@ impl CodeGenerator for CompInfo { }) }; - // FIXME when [issue #465](https://github.com/rust-lang/rust-bindgen/issues/465) ready - let too_many_base_vtables = self - .base_members() - .iter() - .filter(|base| base.ty.has_vtable(ctx)) - .count() > - 1; - - let should_skip_field_offset_checks = - is_opaque || too_many_base_vtables; + let should_skip_field_offset_checks = is_opaque; let check_field_offset = if should_skip_field_offset_checks { @@ -2406,24 +2432,13 @@ impl CodeGenerator for CompInfo { } } -trait MethodCodegen { - fn codegen_method<'a>( +impl Method { + fn codegen_method( &self, ctx: &BindgenContext, methods: &mut Vec<proc_macro2::TokenStream>, method_names: &mut HashSet<String>, - result: &mut CodegenResult<'a>, - parent: &CompInfo, - ); -} - -impl MethodCodegen for Method { - fn codegen_method<'a>( - &self, - ctx: &BindgenContext, - methods: &mut Vec<proc_macro2::TokenStream>, - method_names: &mut HashSet<String>, - result: &mut CodegenResult<'a>, + result: &mut CodegenResult<'_>, _parent: &CompInfo, ) { assert!({ @@ -2720,6 +2735,7 @@ impl<'a> EnumBuilder<'a> { mut attrs: Vec<proc_macro2::TokenStream>, repr: proc_macro2::TokenStream, enum_variation: EnumVariation, + has_typedef: bool, ) -> Self { let ident = Ident::new(name, Span::call_site()); @@ -2752,10 +2768,12 @@ impl<'a> EnumBuilder<'a> { EnumVariation::Consts => { let mut variants = Vec::new(); - variants.push(quote! { - #( #attrs )* - pub type #ident = #repr; - }); + if !has_typedef { + variants.push(quote! { + #( #attrs )* + pub type #ident = #repr; + }); + } EnumBuilder::Consts { variants } } @@ -2779,13 +2797,13 @@ impl<'a> EnumBuilder<'a> { } /// Add a variant to this enum. - fn with_variant<'b>( + fn with_variant( self, ctx: &BindgenContext, variant: &EnumVariant, mangling_prefix: Option<&str>, rust_ty: proc_macro2::TokenStream, - result: &mut CodegenResult<'b>, + result: &mut CodegenResult<'_>, is_ty_named: bool, ) -> Self { let variant_name = ctx.rust_mangle(variant.name()); @@ -2896,11 +2914,11 @@ impl<'a> EnumBuilder<'a> { } } - fn build<'b>( + fn build( self, ctx: &BindgenContext, rust_ty: proc_macro2::TokenStream, - result: &mut CodegenResult<'b>, + result: &mut CodegenResult<'_>, ) -> proc_macro2::TokenStream { match self { EnumBuilder::Rust { @@ -2999,10 +3017,10 @@ impl CodeGenerator for Enum { type Extra = Item; type Return = (); - fn codegen<'a>( + fn codegen( &self, ctx: &BindgenContext, - result: &mut CodegenResult<'a>, + result: &mut CodegenResult<'_>, item: &Item, ) { debug!("<Enum as CodeGenerator>::codegen: item = {:?}", item); @@ -3129,7 +3147,10 @@ impl CodeGenerator for Enum { // The custom derives callback may return a list of derive attributes; // add them to the end of the list. let custom_derives = ctx.options().all_callbacks(|cb| { - cb.add_derives(&crate::callbacks::DeriveInfo { name: &name }) + cb.add_derives(&DeriveInfo { + name: &name, + kind: DeriveTypeKind::Enum, + }) }); // In most cases this will be a no-op, since custom_derives will be empty. derives.extend(custom_derives.iter().map(|s| s.as_str())); @@ -3137,7 +3158,7 @@ impl CodeGenerator for Enum { attrs.push(attributes::derives(&derives)); } - fn add_constant<'a>( + fn add_constant( ctx: &BindgenContext, enum_: &Type, // Only to avoid recomputing every time. @@ -3148,7 +3169,7 @@ impl CodeGenerator for Enum { variant_name: &Ident, referenced_name: &Ident, enum_rust_ty: proc_macro2::TokenStream, - result: &mut CodegenResult<'a>, + result: &mut CodegenResult<'_>, ) { let constant_name = if enum_.name().is_some() { if ctx.options().prepend_enum_name { @@ -3168,8 +3189,10 @@ impl CodeGenerator for Enum { } let repr = repr.to_rust_ty_or_opaque(ctx, item); + let has_typedef = ctx.is_enum_typedef_combo(item.id()); - let mut builder = EnumBuilder::new(&name, attrs, repr, variation); + let mut builder = + EnumBuilder::new(&name, attrs, repr, variation, has_typedef); // A map where we keep a value -> variant relation. let mut seen_values = HashMap::<_, Ident>::default(); @@ -3995,20 +4018,25 @@ impl CodeGenerator for Function { /// it. type Return = Option<u32>; - fn codegen<'a>( + fn codegen( &self, ctx: &BindgenContext, - result: &mut CodegenResult<'a>, + result: &mut CodegenResult<'_>, item: &Item, ) -> Self::Return { debug!("<Function as CodeGenerator>::codegen: item = {:?}", item); debug_assert!(item.is_enabled_for_codegen(ctx)); - // 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 => {} + let is_internal = matches!(self.linkage(), Linkage::Internal); + + if is_internal { + if ctx.options().wrap_static_fns { + result.items_to_serialize.push(item.id()); + } else { + // We can't do anything with Internal functions if we are not wrapping them so just + // avoid generating anything for them. + return None; + } } // Pure virtual methods have no actual symbol, so we can't generate @@ -4118,6 +4146,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( @@ -4127,6 +4156,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 @@ -4137,6 +4167,11 @@ impl CodeGenerator for Function { quote! { #[link(wasm_import_module = #name)] } }); + if is_internal && ctx.options().wrap_static_fns && !has_link_name_attr { + let name = canonical_name.clone() + ctx.wrap_static_fns_suffix(); + attributes.push(attributes::link_name(&name)); + } + let ident = ctx.rust_ident(canonical_name); let tokens = quote! { #wasm_link_attribute @@ -4150,11 +4185,7 @@ impl CodeGenerator for Function { if is_dynamic_function { let args_identifiers = utils::fnsig_argument_identifiers(ctx, signature); - let return_item = ctx.resolve_item(signature.return_type()); - let ret_ty = match *return_item.kind().expect_type().kind() { - TypeKind::Void => quote! {()}, - _ => return_item.to_rust_ty_or_opaque(ctx, &()), - }; + let ret_ty = utils::fnsig_return_ty(ctx, signature); result.dynamic_items().push( ident, abi, @@ -4237,10 +4268,10 @@ impl CodeGenerator for ObjCInterface { type Extra = Item; type Return = (); - fn codegen<'a>( + fn codegen( &self, ctx: &BindgenContext, - result: &mut CodegenResult<'a>, + result: &mut CodegenResult<'_>, item: &Item, ) { debug_assert!(item.is_enabled_for_codegen(ctx)); @@ -4445,7 +4476,8 @@ impl CodeGenerator for ObjCInterface { pub(crate) fn codegen( context: BindgenContext, -) -> (proc_macro2::TokenStream, BindgenOptions, Vec<String>) { +) -> Result<(proc_macro2::TokenStream, BindgenOptions, Vec<String>), CodegenError> +{ context.gen(|context| { let _t = context.timer("codegen"); let counter = Cell::new(0); @@ -4495,21 +4527,73 @@ pub(crate) fn codegen( result.push(dynamic_items_tokens); } - postprocessing::postprocessing(result.items, context.options()) + utils::serialize_items(&result, context)?; + + Ok(postprocessing::postprocessing( + result.items, + context.options(), + )) }) } pub mod utils { - use super::{error, ToRustTyOrOpaque}; + use super::serialize::CSerialize; + use super::{error, CodegenError, CodegenResult, ToRustTyOrOpaque}; use crate::ir::context::BindgenContext; use crate::ir::function::{Abi, ClangAbi, FunctionSig}; use crate::ir::item::{Item, ItemCanonicalPath}; use crate::ir::ty::TypeKind; + use crate::{args_are_cpp, file_is_cpp}; use proc_macro2; use std::borrow::Cow; use std::mem; + use std::path::PathBuf; use std::str::FromStr; + pub(super) fn serialize_items( + result: &CodegenResult, + context: &BindgenContext, + ) -> Result<(), CodegenError> { + if result.items_to_serialize.is_empty() { + return Ok(()); + } + + let path = context + .options() + .wrap_static_fns_path + .as_ref() + .map(PathBuf::from) + .unwrap_or_else(|| { + std::env::temp_dir().join("bindgen").join("extern") + }); + + let dir = path.parent().unwrap(); + + if !dir.exists() { + std::fs::create_dir_all(&dir)?; + } + + let is_cpp = args_are_cpp(&context.options().clang_args) || + context + .options() + .input_headers + .iter() + .any(|h| file_is_cpp(h)); + + let source_path = path.with_extension(if is_cpp { "cpp" } else { "c" }); + + let mut code = Vec::new(); + + for &id in &result.items_to_serialize { + let item = context.resolve_item(id); + item.serialize(context, (), &mut vec![], &mut code)?; + } + + std::fs::write(source_path, code)?; + + Ok(()) + } + pub fn prepend_bitfield_unit_type( ctx: &BindgenContext, result: &mut Vec<proc_macro2::TokenStream>, @@ -4826,25 +4910,52 @@ pub mod utils { }) } - pub fn fnsig_return_ty( + fn fnsig_return_ty_internal( ctx: &BindgenContext, sig: &FunctionSig, + include_arrow: bool, ) -> proc_macro2::TokenStream { if sig.is_divergent() { - return quote! { -> ! }; + return if include_arrow { + quote! { -> ! } + } else { + quote! { ! } + }; } - let return_item = ctx.resolve_item(sig.return_type()); - if let TypeKind::Void = *return_item.kind().expect_type().kind() { - quote! {} + let canonical_type_kind = sig + .return_type() + .into_resolver() + .through_type_refs() + .through_type_aliases() + .resolve(ctx) + .kind() + .expect_type() + .kind(); + + if let TypeKind::Void = canonical_type_kind { + return if include_arrow { + quote! {} + } else { + quote! { () } + }; + } + + let ret_ty = sig.return_type().to_rust_ty_or_opaque(ctx, &()); + if include_arrow { + quote! { -> #ret_ty } } else { - let ret_ty = return_item.to_rust_ty_or_opaque(ctx, &()); - quote! { - -> #ret_ty - } + ret_ty } } + pub fn fnsig_return_ty( + ctx: &BindgenContext, + sig: &FunctionSig, + ) -> proc_macro2::TokenStream { + fnsig_return_ty_internal(ctx, sig, /* include_arrow = */ true) + } + pub fn fnsig_arguments( ctx: &BindgenContext, sig: &FunctionSig, @@ -4957,14 +5068,9 @@ pub mod utils { arg_item.to_rust_ty_or_opaque(ctx, &()) }); - let return_item = ctx.resolve_item(sig.return_type()); - let ret_ty = - if let TypeKind::Void = *return_item.kind().expect_type().kind() { - quote! { () } - } else { - return_item.to_rust_ty_or_opaque(ctx, &()) - }; - + let ret_ty = fnsig_return_ty_internal( + ctx, sig, /* include_arrow = */ false, + ); quote! { *const ::block::Block<(#(#args,)*), #ret_ty> } diff --git a/codegen/serialize.rs b/codegen/serialize.rs new file mode 100644 index 0000000..217098e --- /dev/null +++ b/codegen/serialize.rs @@ -0,0 +1,356 @@ +use std::io::Write; + +use crate::callbacks::IntKind; + +use crate::ir::comp::CompKind; +use crate::ir::context::{BindgenContext, TypeId}; +use crate::ir::function::{Function, FunctionKind}; +use crate::ir::item::Item; +use crate::ir::item::ItemCanonicalName; +use crate::ir::item_kind::ItemKind; +use crate::ir::ty::{FloatKind, Type, TypeKind}; + +use super::CodegenError; + +fn get_loc(item: &Item) -> String { + item.location() + .map(|x| x.to_string()) + .unwrap_or_else(|| "unknown".to_owned()) +} + +pub(crate) trait CSerialize<'a> { + type Extra; + + fn serialize<W: Write>( + &self, + ctx: &BindgenContext, + extra: Self::Extra, + stack: &mut Vec<String>, + writer: &mut W, + ) -> Result<(), CodegenError>; +} + +impl<'a> CSerialize<'a> for Item { + type Extra = (); + + fn serialize<W: Write>( + &self, + ctx: &BindgenContext, + (): Self::Extra, + stack: &mut Vec<String>, + writer: &mut W, + ) -> Result<(), CodegenError> { + match self.kind() { + ItemKind::Function(func) => { + func.serialize(ctx, self, stack, writer) + } + kind => { + return Err(CodegenError::Serialize { + msg: format!("Cannot serialize item kind {:?}", kind), + loc: get_loc(self), + }); + } + } + } +} + +impl<'a> CSerialize<'a> for Function { + type Extra = &'a Item; + + fn serialize<W: Write>( + &self, + ctx: &BindgenContext, + item: Self::Extra, + stack: &mut Vec<String>, + writer: &mut W, + ) -> Result<(), CodegenError> { + if self.kind() != FunctionKind::Function { + return Err(CodegenError::Serialize { + msg: format!( + "Cannot serialize function kind {:?}", + self.kind(), + ), + loc: get_loc(item), + }); + } + + let signature = match ctx.resolve_type(self.signature()).kind() { + TypeKind::Function(signature) => signature, + _ => unreachable!(), + }; + + let name = self.name(); + + // Function argoments stored as `(name, type_id)` tuples. + let args = { + let mut count = 0; + + 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, + ) + }) + .collect::<Vec<_>>() + }; + + // The name used for the wrapper self. + let wrap_name = format!("{}{}", name, ctx.wrap_static_fns_suffix()); + // The function's return type + let ret_ty = signature.return_type(); + + // Write `ret_ty wrap_name(args) asm("wrap_name");` + ret_ty.serialize(ctx, (), stack, writer)?; + write!(writer, " {}(", wrap_name)?; + if args.is_empty() { + write!(writer, "void")?; + } else { + serialize_sep( + ", ", + args.iter(), + ctx, + writer, + |(name, type_id), ctx, buf| { + type_id.serialize(ctx, (), &mut vec![name.clone()], buf) + }, + )?; + } + writeln!(writer, ") asm(\"{}\");", wrap_name)?; + + // Write `ret_ty wrap_name(args) { return name(arg_names)' }` + ret_ty.serialize(ctx, (), stack, writer)?; + write!(writer, " {}(", wrap_name)?; + serialize_sep( + ", ", + args.iter(), + ctx, + writer, + |(name, type_id), _, buf| { + type_id.serialize(ctx, (), &mut vec![name.clone()], buf) + }, + )?; + write!(writer, ") {{ return {}(", name)?; + serialize_sep(", ", args.iter(), ctx, writer, |(name, _), _, buf| { + write!(buf, "{}", name).map_err(From::from) + })?; + writeln!(writer, "); }}")?; + + Ok(()) + } +} + +impl<'a> CSerialize<'a> for TypeId { + type Extra = (); + + fn serialize<W: Write>( + &self, + ctx: &BindgenContext, + (): Self::Extra, + stack: &mut Vec<String>, + writer: &mut W, + ) -> Result<(), CodegenError> { + let item = ctx.resolve_item(*self); + item.expect_type().serialize(ctx, item, stack, writer) + } +} + +impl<'a> CSerialize<'a> for Type { + type Extra = &'a Item; + + fn serialize<W: Write>( + &self, + ctx: &BindgenContext, + item: Self::Extra, + stack: &mut Vec<String>, + writer: &mut W, + ) -> Result<(), CodegenError> { + match self.kind() { + TypeKind::Void => { + if self.is_const() { + write!(writer, "const ")?; + } + write!(writer, "void")? + } + TypeKind::NullPtr => { + if self.is_const() { + write!(writer, "const ")?; + } + write!(writer, "nullptr_t")? + } + TypeKind::Int(int_kind) => { + if self.is_const() { + write!(writer, "const ")?; + } + match int_kind { + IntKind::Bool => write!(writer, "bool")?, + IntKind::SChar => write!(writer, "signed char")?, + IntKind::UChar => write!(writer, "unsigned char")?, + IntKind::WChar => write!(writer, "wchar_t")?, + IntKind::Short => write!(writer, "short")?, + IntKind::UShort => write!(writer, "unsigned short")?, + IntKind::Int => write!(writer, "int")?, + IntKind::UInt => write!(writer, "unsigned int")?, + IntKind::Long => write!(writer, "long")?, + IntKind::ULong => write!(writer, "unsigned long")?, + IntKind::LongLong => write!(writer, "long long")?, + IntKind::ULongLong => write!(writer, "unsigned long long")?, + IntKind::Char { .. } => write!(writer, "char")?, + int_kind => { + return Err(CodegenError::Serialize { + msg: format!( + "Cannot serialize integer kind {:?}", + int_kind + ), + loc: get_loc(item), + }) + } + } + } + TypeKind::Float(float_kind) => { + if self.is_const() { + write!(writer, "const ")?; + } + match float_kind { + FloatKind::Float => write!(writer, "float")?, + FloatKind::Double => write!(writer, "double")?, + FloatKind::LongDouble => write!(writer, "long double")?, + FloatKind::Float128 => write!(writer, "__float128")?, + } + } + TypeKind::Complex(float_kind) => { + if self.is_const() { + write!(writer, "const ")?; + } + match float_kind { + FloatKind::Float => write!(writer, "float complex")?, + FloatKind::Double => write!(writer, "double complex")?, + FloatKind::LongDouble => { + write!(writer, "long double complex")? + } + FloatKind::Float128 => write!(writer, "__complex128")?, + } + } + TypeKind::Alias(type_id) => { + if let Some(name) = self.name() { + if self.is_const() { + write!(writer, "const {}", name)?; + } else { + write!(writer, "{}", name)?; + } + } else { + type_id.serialize(ctx, (), stack, writer)?; + } + } + TypeKind::Array(type_id, length) => { + type_id.serialize(ctx, (), stack, writer)?; + write!(writer, " [{}]", length)? + } + TypeKind::Function(signature) => { + if self.is_const() { + stack.push("const ".to_string()); + } + + signature.return_type().serialize( + ctx, + (), + &mut vec![], + writer, + )?; + + write!(writer, " (")?; + while let Some(item) = stack.pop() { + write!(writer, "{}", item)?; + } + write!(writer, ")")?; + + write!(writer, " (")?; + serialize_sep( + ", ", + signature.argument_types().iter(), + ctx, + writer, + |(name, type_id), ctx, buf| { + let mut stack = vec![]; + if let Some(name) = name { + stack.push(name.clone()); + } + type_id.serialize(ctx, (), &mut stack, buf) + }, + )?; + write!(writer, ")")? + } + TypeKind::ResolvedTypeRef(type_id) => { + if self.is_const() { + write!(writer, "const ")?; + } + type_id.serialize(ctx, (), stack, writer)? + } + TypeKind::Pointer(type_id) => { + if self.is_const() { + stack.push("*const ".to_owned()); + } else { + stack.push("*".to_owned()); + } + type_id.serialize(ctx, (), stack, writer)? + } + TypeKind::Comp(comp_info) => { + if self.is_const() { + write!(writer, "const ")?; + } + + let name = item.canonical_name(ctx); + + match comp_info.kind() { + CompKind::Struct => write!(writer, "struct {}", name)?, + CompKind::Union => write!(writer, "union {}", name)?, + }; + } + ty => { + return Err(CodegenError::Serialize { + msg: format!("Cannot serialize type kind {:?}", ty), + loc: get_loc(item), + }) + } + }; + + if !stack.is_empty() { + write!(writer, " ")?; + while let Some(item) = stack.pop() { + write!(writer, "{}", item)?; + } + } + + Ok(()) + } +} + +fn serialize_sep< + W: Write, + F: FnMut(I::Item, &BindgenContext, &mut W) -> Result<(), CodegenError>, + I: Iterator, +>( + sep: &str, + mut iter: I, + ctx: &BindgenContext, + buf: &mut W, + mut f: F, +) -> Result<(), CodegenError> { + if let Some(item) = iter.next() { + f(item, ctx, buf)?; + let sep = sep.as_bytes(); + for item in iter { + buf.write_all(sep)?; + f(item, ctx, buf)?; + } + } + + Ok(()) +} diff --git a/ir/analysis/template_params.rs b/ir/analysis/template_params.rs index e88b774..f4f0c59 100644 --- a/ir/analysis/template_params.rs +++ b/ir/analysis/template_params.rs @@ -424,8 +424,7 @@ impl<'ctx> MonotoneFramework for UsedTemplateParameters<'ctx> { // generic template parameters are used. let item_kind = ctx.resolve_item(item).as_type().map(|ty| ty.kind()); - if let Some(&TypeKind::TemplateInstantiation(ref inst)) = item_kind - { + if let Some(TypeKind::TemplateInstantiation(inst)) = item_kind { let decl = ctx.resolve_type(inst.template_definition()); let args = inst.template_arguments(); @@ -540,7 +539,7 @@ impl<'ctx> MonotoneFramework for UsedTemplateParameters<'ctx> { } // Template instantiations only use their template arguments if the // template definition uses the corresponding template parameter. - Some(&TypeKind::TemplateInstantiation(ref inst)) => { + Some(TypeKind::TemplateInstantiation(inst)) => { if self .allowlisted_items .contains(&inst.template_definition().into()) @@ -12,7 +12,7 @@ use super::ty::RUST_DERIVE_IN_ARRAY_LIMIT; use crate::clang; use crate::codegen::struct_layout::{align_to, bytes_from_bits_pow2}; use crate::ir::derive::CanDeriveCopy; -use crate::parse::{ClangItemParser, ParseError}; +use crate::parse::ParseError; use crate::HashMap; use crate::NonCopyUnionStyle; use peeking_take_while::PeekableExt; diff --git a/ir/context.rs b/ir/context.rs index 4623b25..b693a70 100644 --- a/ir/context.rs +++ b/ir/context.rs @@ -20,7 +20,7 @@ use super::template::{TemplateInstantiation, TemplateParameters}; use super::traversal::{self, Edge, ItemTraversal}; use super::ty::{FloatKind, Type, TypeKind}; use crate::clang::{self, Cursor}; -use crate::parse::ClangItemParser; +use crate::codegen::CodegenError; use crate::BindgenOptions; use crate::{Entry, HashMap, HashSet}; use cexpr; @@ -399,6 +399,22 @@ pub struct BindgenContext { /// bitfield allocation units computed. Drained in `compute_bitfield_units`. need_bitfield_allocation: Vec<ItemId>, + /// The set of enums that are defined by a pair of `enum` and `typedef`, + /// which is legal in C (but not C++). + /// + /// ```c++ + /// // in either order + /// enum Enum { Variants... }; + /// typedef int16_t Enum; + /// ``` + /// + /// The stored `ItemId` is that of the `TypeKind::Enum`, not of the + /// `TypeKind::Alias`. + /// + /// This is populated when we enter codegen by `compute_enum_typedef_combos` + /// and is always `None` before that and `Some` after. + enum_typedef_combos: Option<HashSet<ItemId>>, + /// The set of (`ItemId`s of) types that can't derive debug. /// /// This is populated when we enter codegen by `compute_cannot_derive_debug` @@ -566,6 +582,7 @@ If you encounter an error missing from this list, please file an issue or a PR!" codegen_items: None, used_template_parameters: None, need_bitfield_allocation: Default::default(), + enum_typedef_combos: None, cannot_derive_debug: None, cannot_derive_default: None, cannot_derive_copy: None, @@ -1130,9 +1147,9 @@ If you encounter an error missing from this list, please file an issue or a PR!" pub(crate) fn gen<F, Out>( mut self, cb: F, - ) -> (Out, BindgenOptions, Vec<String>) + ) -> Result<(Out, BindgenOptions, Vec<String>), CodegenError> where - F: FnOnce(&Self) -> Out, + F: FnOnce(&Self) -> Result<Out, CodegenError>, { self.in_codegen = true; @@ -1158,6 +1175,7 @@ If you encounter an error missing from this list, please file an issue or a PR!" self.compute_sizedness(); self.compute_has_destructor(); self.find_used_template_parameters(); + self.compute_enum_typedef_combos(); self.compute_cannot_derive_debug(); self.compute_cannot_derive_default(); self.compute_cannot_derive_copy(); @@ -1166,8 +1184,8 @@ If you encounter an error missing from this list, please file an issue or a PR!" self.compute_cannot_derive_hash(); self.compute_cannot_derive_partialord_partialeq_or_eq(); - let ret = cb(&self); - (ret, self.options, self.warnings) + let ret = cb(&self)?; + Ok((ret, self.options, self.warnings)) } /// When the `testing_only_extra_assertions` feature is enabled, this @@ -2477,6 +2495,70 @@ If you encounter an error missing from this list, please file an issue or a PR!" self.generated_bindgen_complex.get() } + /// Compute which `enum`s have an associated `typedef` definition. + fn compute_enum_typedef_combos(&mut self) { + let _t = self.timer("compute_enum_typedef_combos"); + assert!(self.enum_typedef_combos.is_none()); + + let mut enum_typedef_combos = HashSet::default(); + for item in &self.items { + if let Some(ItemKind::Module(module)) = + item.as_ref().map(Item::kind) + { + // Find typedefs in this module, and build set of their names. + let mut names_of_typedefs = HashSet::default(); + for child_id in module.children() { + if let Some(ItemKind::Type(ty)) = + self.items[child_id.0].as_ref().map(Item::kind) + { + if let (Some(name), TypeKind::Alias(type_id)) = + (ty.name(), ty.kind()) + { + // We disregard aliases that refer to the enum + // itself, such as in `typedef enum { ... } Enum;`. + if type_id + .into_resolver() + .through_type_refs() + .through_type_aliases() + .resolve(self) + .expect_type() + .is_int() + { + names_of_typedefs.insert(name); + } + } + } + } + + // Find enums in this module, and record the id of each one that + // has a typedef. + for child_id in module.children() { + if let Some(ItemKind::Type(ty)) = + self.items[child_id.0].as_ref().map(Item::kind) + { + if let (Some(name), true) = (ty.name(), ty.is_enum()) { + if names_of_typedefs.contains(name) { + enum_typedef_combos.insert(*child_id); + } + } + } + } + } + } + + self.enum_typedef_combos = Some(enum_typedef_combos); + } + + /// Look up whether `id` refers to an `enum` whose underlying type is + /// defined by a `typedef`. + pub fn is_enum_typedef_combo(&self, id: ItemId) -> bool { + assert!( + self.in_codegen_phase(), + "We only compute enum_typedef_combos when we enter codegen", + ); + self.enum_typedef_combos.as_ref().unwrap().contains(&id) + } + /// Compute whether we can derive debug. fn compute_cannot_derive_debug(&mut self) { let _t = self.timer("compute_cannot_derive_debug"); @@ -2711,6 +2793,13 @@ If you encounter an error missing from this list, please file an issue or a PR!" tokens.into_token_stream() } } + + pub(crate) fn wrap_static_fns_suffix(&self) -> &str { + self.options() + .wrap_static_fns_suffix + .as_deref() + .unwrap_or(crate::DEFAULT_NON_EXTERN_FNS_SUFFIX) + } } /// A builder struct for configuring item resolution options. diff --git a/ir/enum_ty.rs b/ir/enum_ty.rs index 39677e9..63871fd 100644 --- a/ir/enum_ty.rs +++ b/ir/enum_ty.rs @@ -6,7 +6,7 @@ use super::item::Item; use super::ty::{Type, TypeKind}; use crate::clang; use crate::ir::annotations::Annotations; -use crate::parse::{ClangItemParser, ParseError}; +use crate::parse::ParseError; use crate::regex_set::RegexSet; /// An enum representing custom handling that can be given to a variant. diff --git a/ir/function.rs b/ir/function.rs index 7dbbb8f..baa2c36 100644 --- a/ir/function.rs +++ b/ir/function.rs @@ -6,10 +6,9 @@ use super::dot::DotAttributes; use super::item::Item; use super::traversal::{EdgeKind, Trace, Tracer}; use super::ty::TypeKind; +use crate::callbacks::{ItemInfo, ItemKind}; use crate::clang::{self, Attribute}; -use crate::parse::{ - ClangItemParser, ClangSubItemParser, ParseError, ParseResult, -}; +use crate::parse::{ClangSubItemParser, ParseError, ParseResult}; use clang_sys::{self, CXCallingConv}; use proc_macro2; use quote; @@ -665,7 +664,6 @@ impl ClangSubItemParser for Function { }; debug!("Function::parse({:?}, {:?})", cursor, cursor.cur_type()); - let visibility = cursor.visibility(); if visibility != CXVisibility_Default { return Err(ParseError::Continue); @@ -675,25 +673,36 @@ impl ClangSubItemParser for Function { return Err(ParseError::Continue); } + let linkage = cursor.linkage(); + let linkage = match linkage { + CXLinkage_External | CXLinkage_UniqueExternal => Linkage::External, + CXLinkage_Internal => Linkage::Internal, + _ => return Err(ParseError::Continue), + }; + if cursor.is_inlined_function() || cursor .definition() .map_or(false, |x| x.is_inlined_function()) { - if !context.options().generate_inline_functions { + if !context.options().generate_inline_functions && + !context.options().wrap_static_fns + { return Err(ParseError::Continue); } + if cursor.is_deleted_function() { return Err(ParseError::Continue); } - } - let linkage = cursor.linkage(); - let linkage = match linkage { - CXLinkage_External | CXLinkage_UniqueExternal => Linkage::External, - CXLinkage_Internal => Linkage::Internal, - _ => return Err(ParseError::Continue), - }; + // We cannot handle `inline` functions that are not `static`. + if context.options().wrap_static_fns && + cursor.is_inlined_function() && + matches!(linkage, Linkage::External) + { + return Err(ParseError::Continue); + } + } // Grab the signature using Item::from_ty. let sig = Item::from_ty(&cursor.cur_type(), cursor, None, context)?; @@ -714,10 +723,12 @@ impl ClangSubItemParser for Function { // but seems easy enough to handle it here. name.push_str("_destructor"); } - if let Some(nm) = context - .options() - .last_callback(|callbacks| callbacks.generated_name_override(&name)) - { + if let Some(nm) = context.options().last_callback(|callbacks| { + callbacks.generated_name_override(ItemInfo { + name: name.as_str(), + kind: ItemKind::Function, + }) + }) { name = nm; } assert!(!name.is_empty(), "Empty function name."); @@ -726,7 +737,8 @@ impl ClangSubItemParser for Function { let comment = cursor.raw_comment(); let function = - Self::new(name, mangled_name, sig, comment, kind, linkage); + Self::new(name.clone(), mangled_name, sig, comment, kind, linkage); + Ok(ParseResult::New(function, Some(cursor))) } } @@ -18,9 +18,7 @@ use super::template::{AsTemplateParam, TemplateParameters}; use super::traversal::{EdgeKind, Trace, Tracer}; use super::ty::{Type, TypeKind}; use crate::clang; -use crate::parse::{ - ClangItemParser, ClangSubItemParser, ParseError, ParseResult, -}; +use crate::parse::{ClangSubItemParser, ParseError, ParseResult}; use clang_sys; use lazycell::LazyCell; use regex; @@ -1307,8 +1305,8 @@ fn visit_child( } } -impl ClangItemParser for Item { - fn builtin_type( +impl Item { + pub(crate) fn builtin_type( kind: TypeKind, is_const: bool, ctx: &mut BindgenContext, @@ -1333,7 +1331,7 @@ impl ClangItemParser for Item { id.as_type_id_unchecked() } - fn parse( + pub(crate) fn parse( cursor: clang::Cursor, parent_id: Option<ItemId>, ctx: &mut BindgenContext, @@ -1479,7 +1477,7 @@ impl ClangItemParser for Item { } } - fn from_ty_or_ref( + pub(crate) fn from_ty_or_ref( ty: clang::Type, location: clang::Cursor, parent_id: Option<ItemId>, @@ -1499,7 +1497,7 @@ impl ClangItemParser for Item { /// /// Typerefs are resolved once parsing is completely done, see /// `BindgenContext::resolve_typerefs`. - fn from_ty_or_ref_with_id( + pub(crate) fn from_ty_or_ref_with_id( potential_id: ItemId, ty: clang::Type, location: clang::Cursor, @@ -1554,7 +1552,7 @@ impl ClangItemParser for Item { potential_id.as_type_id_unchecked() } - fn from_ty( + pub(crate) fn from_ty( ty: &clang::Type, location: clang::Cursor, parent_id: Option<ItemId>, @@ -1572,7 +1570,7 @@ impl ClangItemParser for Item { /// critical some times to obtain information, an optional parent item id, /// that will, if it's `None`, become the current module id, and the /// context. - fn from_ty_with_id( + pub(crate) fn from_ty_with_id( id: ItemId, ty: &clang::Type, location: clang::Cursor, @@ -1742,7 +1740,7 @@ impl ClangItemParser for Item { /// A named type is a template parameter, e.g., the "T" in Foo<T>. They're /// always local so it's the only exception when there's no declaration for /// a type. - fn type_param( + pub(crate) fn type_param( with_id: Option<ItemId>, location: clang::Cursor, ctx: &mut BindgenContext, @@ -6,7 +6,6 @@ use super::item::Item; use super::traversal::{Trace, Tracer}; use super::ty::TypeKind; use crate::clang; -use crate::parse::ClangItemParser; use clang_sys::CXChildVisit_Continue; use clang_sys::CXCursor_ObjCCategoryDecl; use clang_sys::CXCursor_ObjCClassMethodDecl; @@ -261,7 +260,17 @@ impl ObjCMethod { if name.is_empty() { None } else { - Some(Ident::new(name, Span::call_site())) + // Try to parse the current name as an identifier. This might fail if the + // name is a keyword so we try to prepend "r#" to it and parse again. If + // this also fails, we panic with the first error. + Some( + syn::parse_str::<Ident>(name) + .or_else(|err| { + syn::parse_str::<Ident>(&format!("r#{}", name)) + .map_err(|_| err) + }) + .expect("Invalid identifier"), + ) } }) .collect(); diff --git a/ir/template.rs b/ir/template.rs index 8b06748..e3ef6a9 100644 --- a/ir/template.rs +++ b/ir/template.rs @@ -31,7 +31,6 @@ use super::context::{BindgenContext, ItemId, TypeId}; use super::item::{IsOpaque, Item, ItemAncestors}; use super::traversal::{EdgeKind, Trace, Tracer}; use crate::clang; -use crate::parse::ClangItemParser; /// Template declaration (and such declaration's template parameters) related /// methods. @@ -14,7 +14,7 @@ use super::template::{ }; use super::traversal::{EdgeKind, Trace, Tracer}; use crate::clang::{self, Cursor}; -use crate::parse::{ClangItemParser, ParseError, ParseResult}; +use crate::parse::{ParseError, ParseResult}; use std::borrow::Cow; use std::io; @@ -95,6 +95,11 @@ impl Type { matches!(self.kind, TypeKind::BlockPointer(..)) } + /// Is this an integer type, including `bool` or `char`? + pub fn is_int(&self) -> bool { + matches!(self.kind, TypeKind::Int(_)) + } + /// Is this a compound type? pub fn is_comp(&self) -> bool { matches!(self.kind, TypeKind::Comp(..)) @@ -7,12 +7,10 @@ use super::function::cursor_mangling; use super::int::IntKind; use super::item::Item; use super::ty::{FloatKind, TypeKind}; -use crate::callbacks::MacroParsingBehavior; +use crate::callbacks::{ItemInfo, ItemKind, MacroParsingBehavior}; use crate::clang; use crate::clang::ClangToken; -use crate::parse::{ - ClangItemParser, ClangSubItemParser, ParseError, ParseResult, -}; +use crate::parse::{ClangSubItemParser, ParseError, ParseResult}; use cexpr; use std::io; use std::num::Wrapping; @@ -274,7 +272,20 @@ impl ClangSubItemParser for Var { )) } CXCursor_VarDecl => { - let name = cursor.spelling(); + let mut name = cursor.spelling(); + if cursor.linkage() == CXLinkage_External { + if let Some(nm) = ctx.options().last_callback(|callbacks| { + callbacks.generated_name_override(ItemInfo { + name: name.as_str(), + kind: ItemKind::Var, + }) + }) { + name = nm; + } + } + // No more changes to name + let name = name; + if name.is_empty() { warn!("Empty constant name?"); return Err(ParseError::Continue); @@ -65,7 +65,7 @@ mod clang; mod codegen; mod deps; mod features; -mod ir; +pub mod ir; mod parse; mod regex_set; mod time; @@ -78,6 +78,7 @@ doc_mod!(ir, ir_docs); doc_mod!(parse, parse_docs); doc_mod!(regex_set, regex_set_docs); +use codegen::CodegenError; use ir::comment; pub use crate::codegen::{ @@ -90,8 +91,8 @@ pub use crate::features::{ use crate::ir::context::{BindgenContext, ItemId}; pub use crate::ir::function::Abi; use crate::ir::item::Item; -use crate::parse::{ClangItemParser, ParseError}; -use crate::regex_set::RegexSet; +use crate::parse::ParseError; +pub use crate::regex_set::RegexSet; use std::borrow::Cow; use std::env; @@ -108,6 +109,7 @@ pub(crate) use std::collections::hash_map::Entry; /// Default prefix for the anon fields. pub const DEFAULT_ANON_FIELDS_PREFIX: &str = "__bindgen_anon_"; +const DEFAULT_NON_EXTERN_FNS_SUFFIX: &str = "__extern"; fn file_is_cpp(name_file: &str) -> bool { name_file.ends_with(".hpp") || @@ -244,6 +246,10 @@ impl Default for CodegenConfig { /// regular expressions as arguments. These regular expressions will be parenthesized and wrapped /// in `^` and `$`. So if `<regex>` is passed as argument, the regular expression to be stored will /// be `^(<regex>)$`. +/// +/// Releases of `bindgen` with a version lesser or equal to `0.62.0` used to accept the wildcard +/// pattern `*` as a valid regular expression. This behavior has been deprecated and the `.*` +/// pattern must be used instead. #[derive(Debug, Default, Clone)] pub struct Builder { options: BindgenOptions, @@ -649,6 +655,28 @@ impl Builder { output_vector.push("--wrap-unsafe-ops".into()); } + #[cfg(feature = "cli")] + for callbacks in &self.options.parse_callbacks { + output_vector.extend(callbacks.cli_args()); + } + if self.options.wrap_static_fns { + output_vector.push("--wrap-static-fns".into()) + } + + if let Some(ref path) = self.options.wrap_static_fns_path { + output_vector.push("--wrap-static-fns-path".into()); + output_vector.push(path.display().to_string()); + } + + if let Some(ref suffix) = self.options.wrap_static_fns_suffix { + output_vector.push("--wrap-static-fns-suffix".into()); + output_vector.push(suffix.clone()); + } + + if cfg!(feature = "experimental") { + output_vector.push("--experimental".into()); + } + // Add clang arguments output_vector.push("--".into()); @@ -1544,33 +1572,25 @@ impl Builder { } /// Generate the Rust bindings using the options built up thus far. - pub fn generate(self) -> Result<Bindings, BindgenError> { - let mut options = self.options.clone(); + pub fn generate(mut self) -> Result<Bindings, BindgenError> { // Add any extra arguments from the environment to the clang command line. - options.clang_args.extend(get_extra_clang_args()); + self.options.clang_args.extend(get_extra_clang_args()); // Transform input headers to arguments on the clang command line. - options.clang_args.extend( - options.input_headers - [..options.input_headers.len().saturating_sub(1)] + self.options.clang_args.extend( + self.options.input_headers + [..self.options.input_headers.len().saturating_sub(1)] .iter() .flat_map(|header| ["-include".into(), header.to_string()]), ); let input_unsaved_files = - std::mem::take(&mut options.input_header_contents) + std::mem::take(&mut self.options.input_header_contents) .into_iter() .map(|(name, contents)| clang::UnsavedFile::new(name, contents)) .collect::<Vec<_>>(); - match Bindings::generate(options, input_unsaved_files) { - GenerateResult::Ok(bindings) => Ok(bindings), - GenerateResult::ShouldRestart { header } => self - .header(header) - .generate_inline_functions(false) - .generate(), - GenerateResult::Err(err) => Err(err), - } + Bindings::generate(self.options, input_unsaved_files) } /// Preprocess and dump the input header files to disk. @@ -1605,7 +1625,7 @@ impl Builder { // For each input header content, add a prefix line of `#line 0 "$name"` // followed by the contents. - for &(ref name, ref contents) in &self.options.input_header_contents { + for (name, contents) in &self.options.input_header_contents { is_cpp |= file_is_cpp(name); wrapper_contents.push_str("#line 0 \""); @@ -1787,6 +1807,32 @@ impl Builder { self.options.wrap_unsafe_ops = doit; self } + + #[cfg(feature = "experimental")] + /// Whether to generate extern wrappers for `static` and `static inline` functions. Defaults to + /// false. + pub fn wrap_static_fns(mut self, doit: bool) -> Self { + self.options.wrap_static_fns = doit; + self + } + + #[cfg(feature = "experimental")] + /// Set the path for the source code file that would be created if any wrapper functions must + /// be generated due to the presence of static functions. + /// + /// Bindgen will automatically add the right extension to the header and source code files. + pub fn wrap_static_fns_path<T: AsRef<Path>>(mut self, path: T) -> Self { + self.options.wrap_static_fns_path = Some(path.as_ref().to_owned()); + self + } + + #[cfg(feature = "experimental")] + /// Set the suffix added to the extern wrapper functions generated for `static` and `static + /// inline` functions. + pub fn wrap_static_fns_suffix<T: AsRef<str>>(mut self, suffix: T) -> Self { + self.options.wrap_static_fns_suffix = Some(suffix.as_ref().to_owned()); + self + } } /// Configuration options for generated bindings. @@ -2127,6 +2173,12 @@ struct BindgenOptions { /// Whether to wrap unsafe operations in unsafe blocks or not. wrap_unsafe_ops: bool, + + wrap_static_fns: bool, + + wrap_static_fns_suffix: Option<String>, + + wrap_static_fns_path: Option<PathBuf>, } impl BindgenOptions { @@ -2319,6 +2371,9 @@ impl Default for BindgenOptions { merge_extern_blocks, abi_overrides, wrap_unsafe_ops, + wrap_static_fns, + wrap_static_fns_suffix, + wrap_static_fns_path, } } } @@ -2349,18 +2404,6 @@ fn ensure_libclang_is_loaded() { #[cfg(not(feature = "runtime"))] fn ensure_libclang_is_loaded() {} -#[derive(Debug)] -enum GenerateResult { - Ok(Bindings), - /// Error variant raised when bindgen requires to run again with a newly generated header - /// input. - #[allow(dead_code)] - ShouldRestart { - header: String, - }, - Err(BindgenError), -} - /// Error type for rust-bindgen. #[derive(Debug, Clone, PartialEq, Eq, Hash)] #[non_exhaustive] @@ -2373,6 +2416,8 @@ pub enum BindgenError { NotExist(PathBuf), /// Clang diagnosed an error. ClangDiagnostic(String), + /// Code generation reported an error. + Codegen(CodegenError), } impl std::fmt::Display for BindgenError { @@ -2390,6 +2435,9 @@ impl std::fmt::Display for BindgenError { BindgenError::ClangDiagnostic(message) => { write!(f, "clang diagnosed error: {}", message) } + BindgenError::Codegen(err) => { + write!(f, "codegen error: {}", err) + } } } } @@ -2419,6 +2467,15 @@ fn rust_to_clang_target(rust_target: &str) -> String { let mut clang_target = "riscv64-".to_owned(); clang_target.push_str(rust_target.strip_prefix("riscv64gc-").unwrap()); return clang_target; + } else if rust_target.ends_with("-espidf") { + let mut clang_target = + rust_target.strip_suffix("-espidf").unwrap().to_owned(); + clang_target.push_str("-elf"); + if clang_target.starts_with("riscv32imc-") { + clang_target = "riscv32-".to_owned() + + clang_target.strip_prefix("riscv32imc-").unwrap(); + } + return clang_target; } rust_target.to_owned() } @@ -2454,7 +2511,7 @@ impl Bindings { pub(crate) fn generate( mut options: BindgenOptions, input_unsaved_files: Vec<clang::UnsavedFile>, - ) -> GenerateResult { + ) -> Result<Bindings, BindgenError> { ensure_libclang_is_loaded(); #[cfg(feature = "runtime")] @@ -2575,21 +2632,17 @@ impl Bindings { let path = Path::new(h); if let Ok(md) = std::fs::metadata(path) { if md.is_dir() { - return GenerateResult::Err(BindgenError::FolderAsHeader( - path.into(), - )); + return Err(BindgenError::FolderAsHeader(path.into())); } if !can_read(&md.permissions()) { - return GenerateResult::Err( - BindgenError::InsufficientPermissions(path.into()), - ); + return Err(BindgenError::InsufficientPermissions( + path.into(), + )); } let h = h.clone(); options.clang_args.push(h); } else { - return GenerateResult::Err(BindgenError::NotExist( - path.into(), - )); + return Err(BindgenError::NotExist(path.into())); } } @@ -2617,14 +2670,13 @@ impl Bindings { { let _t = time::Timer::new("parse").with_output(time_phases); - if let Err(err) = parse(&mut context) { - return GenerateResult::Err(err); - } + parse(&mut context)?; } - let (module, options, warnings) = codegen::codegen(context); + let (module, options, warnings) = + codegen::codegen(context).map_err(BindgenError::Codegen)?; - GenerateResult::Ok(Bindings { + Ok(Bindings { options, warnings, module, @@ -2645,7 +2697,7 @@ impl Bindings { /// Write these bindings as source text to the given `Write`able. pub fn write<'a>(&self, mut writer: Box<dyn Write + 'a>) -> io::Result<()> { if !self.options.disable_header_comment { - let version = option_env!("CARGO_PKG_VERSION"); + let version = Some("0.64.0"); let header = format!( "/* automatically generated by rust-bindgen {} */\n\n", version.unwrap_or("(unknown version)") @@ -3002,3 +3054,15 @@ fn test_rust_to_clang_target_riscv() { "riscv64-unknown-linux-gnu" ) } + +#[test] +fn test_rust_to_clang_target_espidf() { + assert_eq!( + rust_to_clang_target("riscv32imc-esp-espidf"), + "riscv32-esp-elf" + ); + assert_eq!( + rust_to_clang_target("xtensa-esp32-espidf"), + "xtensa-esp32-elf" + ); +} @@ -1,8 +1,7 @@ //! Common traits and types related to parsing our IR from Clang cursors. use crate::clang; -use crate::ir::context::{BindgenContext, ItemId, TypeId}; -use crate::ir::ty::TypeKind; +use crate::ir::context::{BindgenContext, ItemId}; /// Not so much an error in the traditional sense, but a control flow message /// when walking over Clang's AST with a cursor. @@ -39,64 +38,3 @@ pub trait ClangSubItemParser: Sized { context: &mut BindgenContext, ) -> Result<ParseResult<Self>, ParseError>; } - -/// An intermediate representation item that can be parsed from a Clang cursor. -pub trait ClangItemParser: Sized { - /// Parse this item from the given Clang cursor. - fn parse( - cursor: clang::Cursor, - parent: Option<ItemId>, - context: &mut BindgenContext, - ) -> Result<ItemId, ParseError>; - - /// Parse this item from the given Clang type. - fn from_ty( - ty: &clang::Type, - location: clang::Cursor, - parent: Option<ItemId>, - ctx: &mut BindgenContext, - ) -> Result<TypeId, ParseError>; - - /// Identical to `from_ty`, but use the given `id` as the `ItemId` for the - /// newly parsed item. - fn from_ty_with_id( - id: ItemId, - ty: &clang::Type, - location: clang::Cursor, - parent: Option<ItemId>, - ctx: &mut BindgenContext, - ) -> Result<TypeId, ParseError>; - - /// Parse this item from the given Clang type, or if we haven't resolved all - /// the other items this one depends on, an unresolved reference. - fn from_ty_or_ref( - ty: clang::Type, - location: clang::Cursor, - parent_id: Option<ItemId>, - context: &mut BindgenContext, - ) -> TypeId; - - /// Identical to `from_ty_or_ref`, but use the given `potential_id` as the - /// `ItemId` for the newly parsed item. - fn from_ty_or_ref_with_id( - potential_id: ItemId, - ty: clang::Type, - location: clang::Cursor, - parent_id: Option<ItemId>, - context: &mut BindgenContext, - ) -> TypeId; - - /// Create a named template type. - fn type_param( - with_id: Option<ItemId>, - location: clang::Cursor, - ctx: &mut BindgenContext, - ) -> Option<TypeId>; - - /// Create a builtin type. - fn builtin_type( - kind: TypeKind, - is_const: bool, - context: &mut BindgenContext, - ) -> TypeId; -} diff --git a/patches/bindgen_cmd.diff b/patches/bindgen_cmd.diff new file mode 100644 index 0000000..9b28ff7 --- /dev/null +++ b/patches/bindgen_cmd.diff @@ -0,0 +1,90 @@ +diff --git a/android/bindgen_cmd/Android.bp b/android/bindgen_cmd/Android.bp +new file mode 100644 +index 0000000..689e7ae +--- /dev/null ++++ b/android/bindgen_cmd/Android.bp +@@ -0,0 +1,28 @@ ++package { ++ // See: http://go/android-license-faq ++ // A large-scale-change added 'default_applicable_licenses' to import ++ // all of the 'license_kinds' from "external_rust_crates_bindgen_license" ++ // to get the below license kinds: ++ // SPDX-license-identifier-Apache-2.0 ++ default_applicable_licenses: ["external_rust_crates_bindgen_license"], ++} ++ ++rust_library_host { ++ name: "libbindgen_cmd", ++ crate_name: "bindgen_cmd", ++ srcs: ["src/lib.rs"], ++ edition: "2018", ++ features: [ ++ "clap", ++ "runtime", ++ "which", ++ "which-rustfmt", ++ ], ++ rustlibs: [ ++ "libbindgen", ++ "libbindgen_cli", ++ "libclap", ++ "libenv_logger", ++ ], ++ compile_multilib: "first", ++} +diff --git a/android/bindgen_cmd/src/lib.rs b/android/bindgen_cmd/src/lib.rs +new file mode 100644 +index 0000000..d33da7f +--- /dev/null ++++ b/android/bindgen_cmd/src/lib.rs +@@ -0,0 +1,50 @@ ++// Copyright 2020, The Android Open Source Project ++// ++// Licensed under the Apache License, Version 2.0 (the "License"); ++// you may not use this file except in compliance with the License. ++// You may obtain a copy of the License at ++// ++// http://www.apache.org/licenses/LICENSE-2.0 ++// ++// Unless required by applicable law or agreed to in writing, software ++// distributed under the License is distributed on an "AS IS" BASIS, ++// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ++// See the License for the specific language governing permissions and ++// limitations under the License. ++ ++//! This is a helper crate for using bindgen as a library from within ++//! Android's build system. Some functionality (such as the type selection ++//! heuristic) is not available on the command line, and so the library ++//! interface may be necessary. Bindgen also needs to receive configuration ++//! from Soong however to find appropriate headers, set global cflags, etc. ++//! ++//! This crate provides the ability to run a hooked version of the command ++//! line bindgen tool, with the ability to call a user-provided transformation ++//! on the the builder before it is used. ++ ++use bindgen; ++use bindgen_cli; ++use std::env; ++ ++/// Takes in a function describing adjustments to make to a builder ++/// initialized by the command line. `build(|x| x)` is equivalent to ++/// running bindgen. When converting a build.rs, you will want to convert the ++/// additional configuration they do into a function, then pass it to `build` ++/// inside your main function. ++pub fn build<C: FnOnce(bindgen::Builder) -> bindgen::Builder>(configure: C) { ++ env_logger::init(); ++ ++ match bindgen_cli::builder_from_flags(env::args()) { ++ Ok((builder, output, _)) => { ++ configure(builder) ++ .generate() ++ .expect("Unable to generate bindings") ++ .write(output) ++ .expect("Unable to write output"); ++ } ++ Err(error) => { ++ eprintln!("{}", error); ++ std::process::exit(1); ++ } ++ }; ++} diff --git a/regex_set.rs b/regex_set.rs index 9f1e225..6246dd2 100644 --- a/regex_set.rs +++ b/regex_set.rs @@ -16,6 +16,13 @@ pub struct RegexSet { } impl RegexSet { + /// Create a new RegexSet + pub fn new() -> RegexSet { + RegexSet { + ..Default::default() + } + } + /// Is this set empty? pub fn is_empty(&self) -> bool { self.items.is_empty() |