diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/codegen/mod.rs | 37 | ||||
-rw-r--r-- | src/codegen/postprocessing/merge_extern_blocks.rs | 46 | ||||
-rw-r--r-- | src/codegen/postprocessing/mod.rs | 66 | ||||
-rw-r--r-- | src/codegen/postprocessing/sort_semantically.rs | 24 | ||||
-rw-r--r-- | src/ir/context.rs | 34 | ||||
-rw-r--r-- | src/ir/ty.rs | 7 | ||||
-rw-r--r-- | src/lib.rs | 113 |
7 files changed, 192 insertions, 135 deletions
diff --git a/src/codegen/mod.rs b/src/codegen/mod.rs index 8eb7b013..a8a7b076 100644 --- a/src/codegen/mod.rs +++ b/src/codegen/mod.rs @@ -3,6 +3,7 @@ mod error; mod helpers; mod impl_debug; mod impl_partialeq; +mod postprocessing; pub mod struct_layout; #[cfg(test)] @@ -4190,9 +4191,19 @@ impl CodeGenerator for Function { fn objc_method_codegen( ctx: &BindgenContext, method: &ObjCMethod, + methods: &mut Vec<proc_macro2::TokenStream>, class_name: Option<&str>, + rust_class_name: &str, prefix: &str, -) -> proc_macro2::TokenStream { +) { + // This would ideally resolve the method into an Item, and use + // Item::process_before_codegen; however, ObjC methods are not currently + // made into function items. + let name = format!("{}::{}{}", rust_class_name, prefix, method.rust_name()); + if ctx.options().blocklisted_items.matches(name) { + return; + } + let signature = method.signature(); let fn_args = utils::fnsig_arguments(ctx, signature); let fn_ret = utils::fnsig_return_ty(ctx, signature); @@ -4228,11 +4239,11 @@ fn objc_method_codegen( let method_name = ctx.rust_ident(format!("{}{}", prefix, method.rust_name())); - quote! { + methods.push(quote! { unsafe fn #method_name #sig where <Self as std::ops::Deref>::Target: objc::Message + Sized { #body } - } + }); } impl CodeGenerator for ObjCInterface { @@ -4248,10 +4259,17 @@ impl CodeGenerator for ObjCInterface { debug_assert!(item.is_enabled_for_codegen(ctx)); let mut impl_items = vec![]; + let rust_class_name = item.path_for_allowlisting(ctx)[1..].join("::"); for method in self.methods() { - let impl_item = objc_method_codegen(ctx, method, None, ""); - impl_items.push(impl_item); + objc_method_codegen( + ctx, + method, + &mut impl_items, + None, + &rust_class_name, + "", + ); } for class_method in self.class_methods() { @@ -4261,13 +4279,14 @@ impl CodeGenerator for ObjCInterface { .map(|m| m.rust_name()) .any(|x| x == class_method.rust_name()); let prefix = if ambiquity { "class_" } else { "" }; - let impl_item = objc_method_codegen( + objc_method_codegen( ctx, class_method, + &mut impl_items, Some(self.name()), + &rust_class_name, prefix, ); - impl_items.push(impl_item); } let trait_name = ctx.rust_ident(self.rust_name()); @@ -4439,7 +4458,7 @@ impl CodeGenerator for ObjCInterface { pub(crate) fn codegen( context: BindgenContext, -) -> (Vec<proc_macro2::TokenStream>, BindgenOptions, Vec<String>) { +) -> (proc_macro2::TokenStream, BindgenOptions, Vec<String>) { context.gen(|context| { let _t = context.timer("codegen"); let counter = Cell::new(0); @@ -4489,7 +4508,7 @@ pub(crate) fn codegen( result.push(dynamic_items_tokens); } - result.items + postprocessing::postprocessing(result.items, context.options()) }) } diff --git a/src/codegen/postprocessing/merge_extern_blocks.rs b/src/codegen/postprocessing/merge_extern_blocks.rs new file mode 100644 index 00000000..2b761494 --- /dev/null +++ b/src/codegen/postprocessing/merge_extern_blocks.rs @@ -0,0 +1,46 @@ +use syn::{Item, ItemForeignMod}; + +pub(super) fn merge_extern_blocks(items: &mut Vec<Item>) { + // Keep all the extern blocks in a different `Vec` for faster search. + let mut foreign_mods = Vec::<ItemForeignMod>::new(); + + for item in std::mem::take(items) { + match item { + Item::ForeignMod(ItemForeignMod { + attrs, + abi, + brace_token, + items: foreign_items, + }) => { + let mut exists = false; + for foreign_mod in &mut foreign_mods { + // Check if there is a extern block with the same ABI and + // attributes. + if foreign_mod.attrs == attrs && foreign_mod.abi == abi { + // Merge the items of the two blocks. + foreign_mod.items.extend_from_slice(&foreign_items); + exists = true; + break; + } + } + // If no existing extern block had the same ABI and attributes, store + // it. + if !exists { + foreign_mods.push(ItemForeignMod { + attrs, + abi, + brace_token, + items: foreign_items, + }); + } + } + // If the item is not an extern block, we don't have to do anything. + _ => items.push(item), + } + } + + // Move all the extern blocks alongside the rest of the items. + for foreign_mod in foreign_mods { + items.push(Item::ForeignMod(foreign_mod)); + } +} diff --git a/src/codegen/postprocessing/mod.rs b/src/codegen/postprocessing/mod.rs new file mode 100644 index 00000000..c6612f2b --- /dev/null +++ b/src/codegen/postprocessing/mod.rs @@ -0,0 +1,66 @@ +use proc_macro2::TokenStream; +use quote::ToTokens; +use syn::Item; + +use crate::BindgenOptions; + +mod merge_extern_blocks; +mod sort_semantically; + +use merge_extern_blocks::merge_extern_blocks; +use sort_semantically::sort_semantically; + +struct PostProcessingPass { + should_run: fn(&BindgenOptions) -> bool, + run: fn(&mut Vec<Item>), +} + +// TODO: This can be a const fn when mutable references are allowed in const +// context. +macro_rules! pass { + ($pass:ident) => { + PostProcessingPass { + should_run: |options| options.$pass, + run: |items| $pass(items), + } + }; +} + +const PASSES: &[PostProcessingPass] = + &[pass!(merge_extern_blocks), pass!(sort_semantically)]; + +pub(crate) fn postprocessing( + items: Vec<TokenStream>, + options: &BindgenOptions, +) -> TokenStream { + let require_syn = PASSES.iter().any(|pass| (pass.should_run)(options)); + if !require_syn { + return items.into_iter().collect(); + } + let module_wrapped_tokens = + quote!(mod wrapper_for_sorting_hack { #( #items )* }); + + // This syn business is a hack, for now. This means that we are re-parsing already + // generated code using `syn` (as opposed to `quote`) because `syn` provides us more + // control over the elements. + // One caveat is that some of the items coming from `quote`d output might have + // multiple items within them. Hence, we have to wrap the incoming in a `mod`. + // The two `unwrap`s here are deliberate because + // The first one won't panic because we build the `mod` and know it is there + // The second one won't panic because we know original output has something in + // it already. + let (_, mut items) = syn::parse2::<syn::ItemMod>(module_wrapped_tokens) + .unwrap() + .content + .unwrap(); + + for pass in PASSES { + if (pass.should_run)(options) { + (pass.run)(&mut items); + } + } + + let synful_items = items.into_iter().map(|item| item.into_token_stream()); + + quote! { #( #synful_items )* } +} diff --git a/src/codegen/postprocessing/sort_semantically.rs b/src/codegen/postprocessing/sort_semantically.rs new file mode 100644 index 00000000..96596cb0 --- /dev/null +++ b/src/codegen/postprocessing/sort_semantically.rs @@ -0,0 +1,24 @@ +use syn::Item; + +pub(super) fn sort_semantically(items: &mut [Item]) { + items.sort_by_key(|item| match item { + Item::Type(_) => 0, + Item::Struct(_) => 1, + Item::Const(_) => 2, + Item::Fn(_) => 3, + Item::Enum(_) => 4, + Item::Union(_) => 5, + Item::Static(_) => 6, + Item::Trait(_) => 7, + Item::TraitAlias(_) => 8, + Item::Impl(_) => 9, + Item::Mod(_) => 10, + Item::Use(_) => 11, + Item::Verbatim(_) => 12, + Item::ExternCrate(_) => 13, + Item::ForeignMod(_) => 14, + Item::Macro(_) => 15, + Item::Macro2(_) => 16, + _ => 18, + }); +} diff --git a/src/ir/context.rs b/src/ir/context.rs index 6f16e192..12373952 100644 --- a/src/ir/context.rs +++ b/src/ir/context.rs @@ -2253,24 +2253,27 @@ If you encounter an error missing from this list, please file an issue or a PR!" // Sized integer types from <stdint.h> get mapped to Rust primitive // types regardless of whether they are blocklisted, so ensure that // standard traits are considered derivable for them too. - None => match name { - "int8_t" | "uint8_t" | "int16_t" | "uint16_t" | - "int32_t" | "uint32_t" | "int64_t" | - "uint64_t" | "uintptr_t" | "intptr_t" | - "ptrdiff_t" => Some(CanDerive::Yes), - "size_t" if self.options.size_t_is_usize => { - Some(CanDerive::Yes) - } - "ssize_t" if self.options.size_t_is_usize => { - Some(CanDerive::Yes) - } - _ => Some(CanDerive::No), - }, + None => Some(if self.is_stdint_type(name) { + CanDerive::Yes + } else { + CanDerive::No + }), }) .unwrap_or(CanDerive::No) }) } + /// Is the given type a type from <stdint.h> that corresponds to a Rust primitive type? + pub fn is_stdint_type(&self, name: &str) -> bool { + match name { + "int8_t" | "uint8_t" | "int16_t" | "uint16_t" | "int32_t" | + "uint32_t" | "int64_t" | "uint64_t" | "uintptr_t" | + "intptr_t" | "ptrdiff_t" => true, + "size_t" | "ssize_t" => self.options.size_t_is_usize, + _ => false, + } + } + /// Get a reference to the set of items we should generate. pub fn codegen_items(&self) -> &ItemSet { assert!(self.in_codegen_phase()); @@ -2358,7 +2361,10 @@ If you encounter an error missing from this list, please file an issue or a PR!" TypeKind::Opaque | TypeKind::TypeParam => return true, _ => {} - }; + } + if self.is_stdint_type(&name) { + return true; + } } // Unnamed top-level enums are special and we diff --git a/src/ir/ty.rs b/src/ir/ty.rs index c85bc687..6a3fd0e8 100644 --- a/src/ir/ty.rs +++ b/src/ir/ty.rs @@ -1206,6 +1206,13 @@ impl Trace for Type { where T: Tracer, { + if self + .name() + .map_or(false, |name| context.is_stdint_type(name)) + { + // These types are special-cased in codegen and don't need to be traversed. + return; + } match *self.kind() { TypeKind::Pointer(inner) | TypeKind::Reference(inner) | @@ -89,7 +89,6 @@ use std::{env, iter}; // Some convenient typedefs for a fast hash map and hash set. type HashMap<K, V> = ::rustc_hash::FxHashMap<K, V>; type HashSet<K> = ::rustc_hash::FxHashSet<K>; -use quote::ToTokens; pub(crate) use std::collections::hash_map::Entry; /// Default prefix for the anon fields. @@ -2114,11 +2113,6 @@ struct BindgenOptions { impl ::std::panic::UnwindSafe for BindgenOptions {} impl BindgenOptions { - /// Whether any of the enabled options requires `syn`. - fn require_syn(&self) -> bool { - self.sort_semantically || self.merge_extern_blocks - } - fn build(&mut self) { let mut regex_sets = [ &mut self.allowlisted_vars, @@ -2551,112 +2545,7 @@ impl Bindings { parse(&mut context)?; } - let (items, options, warnings) = codegen::codegen(context); - - let module = if options.require_syn() { - let module_wrapped_tokens = - quote!(mod wrapper_for_sorting_hack { #( #items )* }); - - // This syn business is a hack, for now. This means that we are re-parsing already - // generated code using `syn` (as opposed to `quote`) because `syn` provides us more - // control over the elements. - // One caveat is that some of the items coming from `quote`d output might have - // multiple items within them. Hence, we have to wrap the incoming in a `mod`. - // The two `unwrap`s here are deliberate because - // The first one won't panic because we build the `mod` and know it is there - // The second one won't panic because we know original output has something in - // it already. - let mut syn_parsed_items = - syn::parse2::<syn::ItemMod>(module_wrapped_tokens) - .unwrap() - .content - .unwrap() - .1; - - if options.merge_extern_blocks { - // Here we will store all the items after deduplication. - let mut items = Vec::new(); - - // Keep all the extern blocks in a different `Vec` for faster search. - let mut foreign_mods = Vec::<syn::ItemForeignMod>::new(); - for item in syn_parsed_items { - match item { - syn::Item::ForeignMod(syn::ItemForeignMod { - attrs, - abi, - brace_token, - items: foreign_items, - }) => { - let mut exists = false; - for foreign_mod in &mut foreign_mods { - // Check if there is a extern block with the same ABI and - // attributes. - if foreign_mod.attrs == attrs && - foreign_mod.abi == abi - { - // Merge the items of the two blocks. - foreign_mod - .items - .extend_from_slice(&foreign_items); - exists = true; - break; - } - } - // If no existing extern block had the same ABI and attributes, store - // it. - if !exists { - foreign_mods.push(syn::ItemForeignMod { - attrs, - abi, - brace_token, - items: foreign_items, - }); - } - } - // If the item is not an extern block, we don't have to do anything. - _ => items.push(item), - } - } - - // Move all the extern blocks alongiside the rest of the items. - for foreign_mod in foreign_mods { - items.push(syn::Item::ForeignMod(foreign_mod)); - } - - syn_parsed_items = items; - } - - if options.sort_semantically { - syn_parsed_items.sort_by_key(|item| match item { - syn::Item::Type(_) => 0, - syn::Item::Struct(_) => 1, - syn::Item::Const(_) => 2, - syn::Item::Fn(_) => 3, - syn::Item::Enum(_) => 4, - syn::Item::Union(_) => 5, - syn::Item::Static(_) => 6, - syn::Item::Trait(_) => 7, - syn::Item::TraitAlias(_) => 8, - syn::Item::Impl(_) => 9, - syn::Item::Mod(_) => 10, - syn::Item::Use(_) => 11, - syn::Item::Verbatim(_) => 12, - syn::Item::ExternCrate(_) => 13, - syn::Item::ForeignMod(_) => 14, - syn::Item::Macro(_) => 15, - syn::Item::Macro2(_) => 16, - _ => 18, - }); - } - - let synful_items = syn_parsed_items - .into_iter() - .map(|item| item.into_token_stream()); - - quote! { #( #synful_items )* } - } else { - quote! { #( #items )* } - }; + let (module, options, warnings) = codegen::codegen(context); Ok(Bindings { options, |