diff options
-rwxr-xr-x | src/bin/bindgen.rs | 10 | ||||
-rwxr-xr-x | src/clang.rs | 17 | ||||
-rwxr-xr-x | src/codegen/mod.rs | 212 | ||||
-rwxr-xr-x | src/lib.rs | 13 | ||||
-rw-r--r-- | tests/expectations/tests/bitfield-enum-basic.rs | 77 | ||||
-rw-r--r-- | tests/headers/bitfield-enum-basic.h | 27 |
6 files changed, 308 insertions, 48 deletions
diff --git a/src/bin/bindgen.rs b/src/bin/bindgen.rs index 2dbb1690..bdb3b292 100755 --- a/src/bin/bindgen.rs +++ b/src/bin/bindgen.rs @@ -28,6 +28,7 @@ Usage: [--whitelist-type=<type>...] \ [--whitelist-function=<name>...] \ [--whitelist-var=<name>...] \ + [--bitfield-enum=<name>...] \ <input-header> \ [-- <clang-args>...] @@ -91,6 +92,10 @@ Options: matching <regex>. Same behavior on emptyness than the type whitelisting. + --bitfield-enum=<regex> Mark any enum whose name matches <regex> as a + set of bitfield flags instead of an + enumeration. + --dummy-uses=<path> For testing purposes, generate a C/C++ file containing dummy uses of all types defined in the input header. @@ -163,6 +168,11 @@ fn parse_args_or_exit(args: Vec<String>) -> (BindgenOptions, Box<io::Write>) { .expect("--whitelist-var expects a pattern"); options.whitelisted_vars.insert(&var_pat); } + "--bitfield-enum" => { + let enum_pat = iter.next() + .expect("--bitfield-enum expects a pattern"); + options.bitfield_enums.insert(&enum_pat); + } "--" => { while let Some(clang_arg) = iter.next() { options.clang_args.push(clang_arg); diff --git a/src/clang.rs b/src/clang.rs index e6d78123..12397534 100755 --- a/src/clang.rs +++ b/src/clang.rs @@ -102,7 +102,9 @@ impl Cursor { /// lexical parents. pub fn fallible_semantic_parent(&self) -> Option<Cursor> { let sp = unsafe { - Cursor { x: clang_getCursorSemanticParent(self.x) } + Cursor { + x: clang_getCursorSemanticParent(self.x), + } }; if sp == *self || !sp.is_valid() { return None; @@ -161,7 +163,8 @@ impl Cursor { (semantic_parent.unwrap().kind() == CXCursor_Namespace || semantic_parent.unwrap().kind() == CXCursor_NamespaceAlias || semantic_parent.unwrap().kind() == CXCursor_NamespaceRef) { - semantic_parent = semantic_parent.unwrap().fallible_semantic_parent(); + semantic_parent = semantic_parent.unwrap() + .fallible_semantic_parent(); } let tu = self.translation_unit(); @@ -363,9 +366,9 @@ impl Cursor { /// /// Returns None if the cursor's referent is not an enum variant. pub fn enum_val_unsigned(&self) -> Option<u64> { - unsafe { + unsafe { if self.kind() == CXCursor_EnumConstantDecl { - Some(clang_getEnumConstantDeclUnsignedValue(self.x) as u64) + Some(clang_getEnumConstantDeclUnsignedValue(self.x) as u64) } else { None } @@ -691,11 +694,7 @@ impl Type { let rt = Type { x: unsafe { clang_getResultType(self.x) }, }; - if rt.is_valid() { - Some(rt) - } else { - None - } + if rt.is_valid() { Some(rt) } else { None } } /// Given that this type is a function type, get its calling convention. If diff --git a/src/codegen/mod.rs b/src/codegen/mod.rs index 7fa8b19c..20f377e1 100755 --- a/src/codegen/mod.rs +++ b/src/codegen/mod.rs @@ -6,7 +6,7 @@ use aster; use ir::annotations::FieldAccessorKind; use ir::comp::{CompInfo, CompKind, Field, Method}; use ir::context::BindgenContext; -use ir::enum_ty::Enum; +use ir::enum_ty::{Enum, EnumVariant, EnumVariantValue}; use ir::function::{Function, FunctionSig}; use ir::int::IntKind; use ir::item::{Item, ItemCanonicalName, ItemCanonicalPath, ItemId}; @@ -1224,6 +1224,117 @@ impl MethodCodegen for Method { } } +/// A helper type to construct enums, either bitfield ones or rust-style ones. +enum EnumBuilder<'a> { + Rust(aster::item::ItemEnumBuilder<aster::invoke::Identity>), + Bitfield { + canonical_name: &'a str, + aster: P<ast::Item>, + }, +} + +impl<'a> EnumBuilder<'a> { + /// Create a new enum given an item builder, a canonical name, a name for + /// the representation, and whether it should be represented as a rust enum. + fn new(aster: aster::item::ItemBuilder<aster::invoke::Identity>, + name: &'a str, + repr_name: &str, + is_rust: bool) + -> Self { + if is_rust { + EnumBuilder::Rust(aster.enum_(name)) + } else { + EnumBuilder::Bitfield { + canonical_name: name, + aster: aster.tuple_struct(name) + .field() + .pub_() + .ty() + .id(repr_name) + .build(), + } + } + } + + /// Add a variant to this enum. + fn with_variant(self, + ctx: &BindgenContext, + variant: &EnumVariant, + mangling_prefix: Option<&String>, + rust_ty: P<ast::Ty>, + result: &mut CodegenResult) + -> Self { + let variant_name = ctx.rust_mangle(variant.name()); + let expr = aster::AstBuilder::new().expr(); + let expr = match variant.val() { + EnumVariantValue::Signed(v) => expr.int(v), + EnumVariantValue::Unsigned(v) => expr.uint(v), + }; + + match self { + EnumBuilder::Rust(b) => { + EnumBuilder::Rust(b.with_variant_(ast::Variant_ { + name: ctx.rust_ident(&*variant_name), + attrs: vec![], + data: ast::VariantData::Unit(ast::DUMMY_NODE_ID), + disr_expr: Some(expr), + })) + } + EnumBuilder::Bitfield { canonical_name, .. } => { + let constant_name = match mangling_prefix { + Some(prefix) => { + Cow::Owned(format!("{}_{}", prefix, variant_name)) + } + None => variant_name, + }; + + let constant = aster::AstBuilder::new() + .item() + .pub_() + .const_(&*constant_name) + .expr() + .call() + .id(canonical_name) + .arg() + .build(expr) + .build() + .build(rust_ty); + result.push(constant); + self + } + } + } + + fn build(self, + ctx: &BindgenContext, + rust_ty: P<ast::Ty>, + result: &mut CodegenResult) + -> P<ast::Item> { + match self { + EnumBuilder::Rust(b) => b.build(), + EnumBuilder::Bitfield { canonical_name, aster } => { + let rust_ty_name = ctx.rust_ident_raw(canonical_name); + let prefix = ctx.trait_prefix(); + + let impl_ = quote_item!(ctx.ext_cx(), + impl ::$prefix::ops::BitOr<$rust_ty> for $rust_ty { + type Output = Self; + + #[inline] + fn bitor(self, other: Self) -> Self { + $rust_ty_name(self.0 | other.0) + } + } + ) + .unwrap(); + + result.push(impl_); + aster + } + } + } +} + impl CodeGenerator for Enum { type Extra = Item; @@ -1231,10 +1342,9 @@ impl CodeGenerator for Enum { ctx: &BindgenContext, result: &mut CodegenResult, item: &Item) { - use ir::enum_ty::EnumVariantValue; - let name = item.canonical_name(ctx); - let layout = item.expect_type().layout(ctx); + let enum_ty = item.expect_type(); + let layout = enum_ty.layout(ctx); let repr = self.repr().map(|repr| ctx.resolve_type(repr)); let repr = match repr { @@ -1270,10 +1380,24 @@ impl CodeGenerator for Enum { let mut builder = aster::AstBuilder::new().item().pub_(); + let is_bitfield = { + ctx.options().bitfield_enums.matches(&name) || + (enum_ty.name().is_none() && + self.variants() + .iter() + .any(|v| ctx.options().bitfield_enums.matches(&v.name()))) + }; + + let is_rust_enum = !is_bitfield; + // FIXME: Rust forbids repr with empty enums. Remove this condition when // this is allowed. - if !self.variants().is_empty() { - builder = builder.with_attr(attributes::repr(repr_name)); + if is_rust_enum { + if !self.variants().is_empty() { + builder = builder.with_attr(attributes::repr(repr_name)); + } + } else { + builder = builder.with_attr(attributes::repr("C")); } if let Some(comment) = item.comment() { @@ -1289,8 +1413,6 @@ impl CodeGenerator for Enum { builder = builder.with_attr(derives); - let mut builder = builder.enum_(&name); - fn add_constant(enum_: &Type, // Only to avoid recomputing every time. enum_canonical_name: &str, @@ -1318,52 +1440,64 @@ impl CodeGenerator for Enum { result.push(constant); } - // Used to mangle the constants we generate in the unnamed-enum case. - let mut parent_canonical_name = None; + let mut builder = + EnumBuilder::new(builder, &name, repr_name, is_rust_enum); // A map where we keep a value -> variant relation. let mut seen_values = HashMap::<_, String>::new(); - let enum_ty = item.expect_type(); let enum_rust_ty = item.to_rust_ty(ctx); + let is_toplevel = item.is_toplevel(ctx); + + // Used to mangle the constants we generate in the unnamed-enum case. + let parent_canonical_name = if is_toplevel { + None + } else { + Some(item.parent_id().canonical_name(ctx)) + }; + + let constant_mangling_prefix = if enum_ty.name().is_none() { + parent_canonical_name.as_ref().map(|n| &*n) + } else { + Some(&name) + }; + for variant in self.variants().iter() { match seen_values.entry(variant.val()) { Entry::Occupied(ref entry) => { - let existing_variant_name = entry.get(); - let variant_name = ctx.rust_mangle(variant.name()); - add_constant(enum_ty, - &name, - &*variant_name, - existing_variant_name, - enum_rust_ty.clone(), - result); + if is_rust_enum { + let existing_variant_name = entry.get(); + let variant_name = ctx.rust_mangle(variant.name()); + add_constant(enum_ty, + &name, + &*variant_name, + existing_variant_name, + enum_rust_ty.clone(), + result); + } else { + builder = builder.with_variant(ctx, + variant, + constant_mangling_prefix, + enum_rust_ty.clone(), + result); + } } Entry::Vacant(entry) => { - let expr = aster::AstBuilder::new().expr(); - let expr = match variant.val() { - EnumVariantValue::Signed(val) => expr.int(val), - EnumVariantValue::Unsigned(val) => expr.uint(val), - }; + builder = builder.with_variant(ctx, + variant, + constant_mangling_prefix, + enum_rust_ty.clone(), + result); + let variant_name = ctx.rust_mangle(variant.name()); - builder = builder.with_variant_(ast::Variant_ { - name: ctx.rust_ident(&*variant_name), - attrs: vec![], - data: ast::VariantData::Unit(ast::DUMMY_NODE_ID), - disr_expr: Some(expr), - }); // If it's an unnamed enum, we also generate a constant so // it can be properly accessed. - if enum_ty.name().is_none() { + if is_rust_enum && enum_ty.name().is_none() { // NB: if we want to do this for other kind of nested // enums we can probably mangle the name. - let mangled_name = if item.is_toplevel(ctx) { + let mangled_name = if is_toplevel { variant_name.clone() } else { - if parent_canonical_name.is_none() { - parent_canonical_name = Some(item.parent_id() - .canonical_name(ctx)); - } - let parent_name = parent_canonical_name.as_ref() .unwrap(); @@ -1384,8 +1518,8 @@ impl CodeGenerator for Enum { } } - - result.push(builder.build()); + let enum_ = builder.build(ctx, enum_rust_ty, result); + result.push(enum_); } } @@ -166,6 +166,15 @@ impl Builder { self } + /// Mark the given enum (or set of enums, if using a pattern) as being + /// bitfield-like. + /// + /// This makes bindgen generate a type that isn't a rust `enum`. + pub fn bitfield_enum<T: Borrow<str>>(mut self, arg: T) -> Builder { + self.options.bitfield_enums.insert(&arg); + self + } + /// Add a string to prepend to the generated bindings. The string is passed /// through without any modification. pub fn raw_line<T: Into<String>>(mut self, arg: T) -> Builder { @@ -262,6 +271,9 @@ pub struct BindgenOptions { /// Whitelisted variables. See docs for `whitelisted_types` for more. pub whitelisted_vars: RegexSet, + /// The enum patterns to mark an enum as bitfield. + pub bitfield_enums: RegexSet, + /// Whether we should generate builtins or not. pub builtins: bool, @@ -329,6 +341,7 @@ impl Default for BindgenOptions { whitelisted_types: Default::default(), whitelisted_functions: Default::default(), whitelisted_vars: Default::default(), + bitfield_enums: Default::default(), builtins: false, links: vec![], emit_ast: false, diff --git a/tests/expectations/tests/bitfield-enum-basic.rs b/tests/expectations/tests/bitfield-enum-basic.rs new file mode 100644 index 00000000..03e07de6 --- /dev/null +++ b/tests/expectations/tests/bitfield-enum-basic.rs @@ -0,0 +1,77 @@ +/* automatically generated by rust-bindgen */ + + +#![allow(non_snake_case)] + + +pub const Foo_Bar: Foo = Foo(2); +pub const Foo_Baz: Foo = Foo(4); +pub const Foo_Duplicated: Foo = Foo(4); +pub const Foo_Negative: Foo = Foo(-3); +impl ::std::ops::BitOr<Foo> for Foo { + type + Output + = + Self; + #[inline] + fn bitor(self, other: Self) -> Self { Foo(self.0 | other.0) } +} +#[repr(C)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub struct Foo(pub i32); +pub const Buz_Bar: Buz = Buz(2); +pub const Buz_Baz: Buz = Buz(4); +pub const Buz_Duplicated: Buz = Buz(4); +pub const Buz_Negative: Buz = Buz(-3); +impl ::std::ops::BitOr<Buz> for Buz { + type + Output + = + Self; + #[inline] + fn bitor(self, other: Self) -> Self { Buz(self.0 | other.0) } +} +#[repr(C)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub struct Buz(pub i8); +pub const NS_FOO: _bindgen_ty_1 = _bindgen_ty_1(1); +pub const NS_BAR: _bindgen_ty_1 = _bindgen_ty_1(2); +impl ::std::ops::BitOr<_bindgen_ty_1> for _bindgen_ty_1 { + type + Output + = + Self; + #[inline] + fn bitor(self, other: Self) -> Self { _bindgen_ty_1(self.0 | other.0) } +} +#[repr(C)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub struct _bindgen_ty_1(pub u32); +#[repr(C)] +#[derive(Debug, Copy)] +pub struct Dummy { + pub _address: u8, +} +pub const Dummy_DUMMY_FOO: Dummy__bindgen_ty_1 = Dummy__bindgen_ty_1(1); +pub const Dummy_DUMMY_BAR: Dummy__bindgen_ty_1 = Dummy__bindgen_ty_1(2); +impl ::std::ops::BitOr<Dummy__bindgen_ty_1> for Dummy__bindgen_ty_1 { + type + Output + = + Self; + #[inline] + fn bitor(self, other: Self) -> Self { + Dummy__bindgen_ty_1(self.0 | other.0) + } +} +#[repr(C)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub struct Dummy__bindgen_ty_1(pub u32); +#[test] +fn bindgen_test_layout_Dummy() { + assert_eq!(::std::mem::size_of::<Dummy>() , 1usize); + assert_eq!(::std::mem::align_of::<Dummy>() , 1usize); +} +impl Clone for Dummy { + fn clone(&self) -> Self { *self } +} diff --git a/tests/headers/bitfield-enum-basic.h b/tests/headers/bitfield-enum-basic.h new file mode 100644 index 00000000..364bebf2 --- /dev/null +++ b/tests/headers/bitfield-enum-basic.h @@ -0,0 +1,27 @@ +// bindgen-flags: --bitfield-enum "Foo|Buz|NS_.*|DUMMY_.*" -- -std=c++11 + +enum Foo { + Bar = 1 << 1, + Baz = 1 << 2, + Duplicated = 1 << 2, + Negative = -3, +}; + +enum class Buz : signed char { + Bar = 1 << 1, + Baz = 1 << 2, + Duplicated = 1 << 2, + Negative = -3, +}; + +enum { + NS_FOO = 1 << 0, + NS_BAR = 1 << 1, +}; + +class Dummy { + enum { + DUMMY_FOO = 1 << 0, + DUMMY_BAR = 1 << 1, + }; +}; |