diff options
-rwxr-xr-x | src/bin/bindgen.rs | 10 | ||||
-rwxr-xr-x | src/codegen/mod.rs | 145 | ||||
-rwxr-xr-x | src/lib.rs | 13 |
3 files changed, 146 insertions, 22 deletions
diff --git a/src/bin/bindgen.rs b/src/bin/bindgen.rs index 2dbb1690..b9e56b08 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 a bitfield as being used as an enum. + This makes bindgen don't generate a rust enum + for it, but a bitfield-like wrapper. + --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/codegen/mod.rs b/src/codegen/mod.rs index 7fa8b19c..02051f27 100755 --- a/src/codegen/mod.rs +++ b/src/codegen/mod.rs @@ -1224,6 +1224,79 @@ impl MethodCodegen for Method { } } +enum EnumBuilder<'a> { + Rust(aster::item::ItemEnumBuilder<aster::invoke::Identity>), + Bitfield { + canonical_name: &'a str, + aster: P<ast::Item>, + }, +} + +impl<'a> EnumBuilder<'a> { + 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(), + } + } + } + + fn with_variant_(self, + ctx: &BindgenContext, + name: &str, + expr: P<ast::Expr>, + rust_ty: P<ast::Ty>, + result: &mut CodegenResult) + -> Self { + match self { + EnumBuilder::Rust(b) => { + EnumBuilder::Rust(b.with_variant_(ast::Variant_ { + name: ctx.rust_ident(name), + attrs: vec![], + data: ast::VariantData::Unit(ast::DUMMY_NODE_ID), + disr_expr: Some(expr), + })) + } + EnumBuilder::Bitfield { canonical_name, .. } => { + // FIXME: Probably if unnamed we should be smarter! Easy to + // improve. + let constant_name = format!("{}_{}", canonical_name, name); + let constant = aster::AstBuilder::new() + .item() + .const_(constant_name) + .expr() + .call() + .id(canonical_name) + .arg() + .build(expr) + .build() + .build(rust_ty); + result.push(constant); + self + } + } + } + + fn build(self) -> P<ast::Item> { + match self { + EnumBuilder::Rust(b) => b.build(), + EnumBuilder::Bitfield { aster, .. } => aster, + } + } +} + impl CodeGenerator for Enum { type Extra = Item; @@ -1234,7 +1307,8 @@ impl CodeGenerator for Enum { 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 +1344,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 +1377,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, @@ -1321,21 +1407,38 @@ impl CodeGenerator for Enum { // 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); 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 { + // FIXME: Don't repeat this block below. + let expr = aster::AstBuilder::new().expr(); + let expr = match variant.val() { + EnumVariantValue::Signed(val) => expr.int(val), + EnumVariantValue::Unsigned(val) => expr.uint(val), + }; + let variant_name = ctx.rust_mangle(variant.name()); + builder = builder.with_variant_(ctx, + &*variant_name, + expr, + enum_rust_ty.clone(), + result); + } } Entry::Vacant(entry) => { let expr = aster::AstBuilder::new().expr(); @@ -1344,16 +1447,15 @@ impl CodeGenerator for Enum { EnumVariantValue::Unsigned(val) => expr.uint(val), }; 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), - }); + builder = builder.with_variant_(ctx, + &*variant_name, + expr, + enum_rust_ty.clone(), + result); // 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) { @@ -1384,7 +1486,6 @@ impl CodeGenerator for Enum { } } - result.push(builder.build()); } } @@ -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 to generate a type for it 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, |