Skip to content

Commit

Permalink
Whitelisting codegen, v1
Browse files Browse the repository at this point in the history
  • Loading branch information
emilio committed Sep 12, 2016
1 parent b07ea62 commit f7adf26
Show file tree
Hide file tree
Showing 5 changed files with 166 additions and 25 deletions.
6 changes: 6 additions & 0 deletions src/bin/bindgen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ Usage:
[--dtor-attr=<attr>...] \
[--opaque-type=<type>...] \
[--blacklist-type=<type>...] \
[--whitelist-type=<type>...] \
<input-header> \
[-- <clang-args>...]
Expand Down Expand Up @@ -92,6 +93,9 @@ Options:
--ignore-methods Avoid generating all kind of methods.
--opaque-type=<type> Mark a type as opaque.
--blacklist-type=<type> Mark a type as hidden.
--whitelist-type=<type> Whitelist the type. If this set is not empty,
then all the non-whitelisted types (or
dependant) won't be generated.
<clang-args> Options other than stated above are passed
directly through to clang.
Expand Down Expand Up @@ -122,6 +126,7 @@ struct Args {
flag_ignore_methods: bool,
flag_opaque_type: Vec<String>,
flag_blacklist_type: Vec<String>,
flag_whitelist_type: Vec<String>,
arg_clang_args: Vec<String>,
}

Expand Down Expand Up @@ -171,6 +176,7 @@ impl Into<ParseResult<(BindgenOptions, Box<io::Write>)>> for Args {
options.ignore_methods = self.flag_ignore_methods;
options.opaque_types.extend(self.flag_opaque_type.drain(..));
options.hidden_types.extend(self.flag_blacklist_type.drain(..));
options.whitelisted_types.extend(self.flag_whitelist_type.drain(..));
options.clang_args.extend(self.arg_clang_args.drain(..));
options.clang_args.push(self.arg_input_header);

Expand Down
147 changes: 122 additions & 25 deletions src/codegen/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use ir::layout::Layout;

use std::ops;
use std::mem;
use std::collections::BTreeSet;
use std::collections::hash_map::{HashMap, Entry};

use syntax::abi::Abi;
Expand Down Expand Up @@ -489,26 +490,6 @@ impl<'a> Bitfield<'a> {
}
}

fn type_for_bitfield_width(ctx: &BindgenContext, width: u32, is_arg: bool) -> P<ast::Ty> {
let input_type = if width > 16 {
"u32"
} else if width > 8 {
"u16"
} else if width > 1 {
"u8"
} else {
if is_arg {
"bool"
} else {
"u8"
}
};

let ident = ctx.rust_ident_raw(input_type);
quote_ty!(ctx.ext_cx(), $ident)
}


fn codegen_fields(self,
ctx: &BindgenContext,
fields: &mut Vec<ast::StructField>,
Expand Down Expand Up @@ -1400,14 +1381,130 @@ impl CodeGenerator for Function {
}
}

type ItemSet = BTreeSet<ItemId>;

trait TypeCollector {
type Extra;

fn collect_types(&self,
context: &BindgenContext,
types: &mut ItemSet,
extra: &Self::Extra);
}

impl TypeCollector for ItemId {
type Extra = ();

fn collect_types(&self,
context: &BindgenContext,
types: &mut ItemSet,
extra: &()) {
context.resolve_item(*self).collect_types(context, types, extra);
}
}

impl TypeCollector for Item {
type Extra = ();

fn collect_types(&self,
context: &BindgenContext,
types: &mut ItemSet,
_extra: &()) {
// NB: non-toplevel items are generated by their parents.
if self.hidden(context) || !self.is_toplevel(context) || types.contains(&self.id()) {
return;
}

match *self.kind() {
ItemKind::Type(ref ty) => {
types.insert(self.id());
if !self.opaque(context) {
ty.collect_types(context, types, self);
}
}
_ => {}, // FIXME.
}
}
}

impl TypeCollector for Type {
type Extra = Item;

fn collect_types(&self,
context: &BindgenContext,
types: &mut ItemSet,
item: &Item) {
match *self.kind() {
TypeKind::Comp(ref ci) => ci.collect_types(context, types, item),
TypeKind::Alias(_, ref inner) => inner.collect_types(context, types, &()),
_ => {},
}
}
}

impl TypeCollector for CompInfo {
type Extra = Item;

fn collect_types(&self,
context: &BindgenContext,
types: &mut ItemSet,
item: &Item) {
let applicable_template_args = item.applicable_template_args(context);
for arg in applicable_template_args {
arg.collect_types(context, types, &());
}

for base in self.base_members() {
base.collect_types(context, types, &());
}

for field in self.fields() {
field.ty().collect_types(context, types, &());
}

// FIXME(emilio): Methods, VTable?
}
}

pub fn codegen(context: &mut BindgenContext) -> Vec<P<ast::Item>> {
context.gen(|context| {
let mut result = CodegenResult::new();
for (_item_id, item) in context.items() {
// Non-toplevel item parents are the responsible one for generating
// them.
if item.is_toplevel(context) {
item.codegen(context, &mut result, &());

debug!("codegen: {:?}", context.options());

// If the whitelisted types set is empty, just generate everything.
if context.options().whitelisted_types.is_empty() {
for (_item_id, item) in context.items() {
// Non-toplevel item parents are the responsible one for generating
// them.
if item.is_toplevel(context) {
item.codegen(context, &mut result, &());
}
}
} else {
// Recursively collect all the types dependent on the whitelisted
// types, then generate them.
//
// FIXME(emilio): This pass is probably slow, but it can't be faster
// than docopt anyway :)
let mut types = ItemSet::new();
for (_item_id, item) in context.items() {
// FIXME(emilio): This only works for types, but we need to
// eventually come with a coherent history for functions...
//
// Whitelisting any type touched by a function seems overkill,
// maybe another pass whitelisting functions that only contain
// whitelisted types?
if item.kind().is_type() && item.is_toplevel(context) {
let name = item.canonical_name(context);
if context.options().whitelisted_types.contains(&name) {
item.collect_types(context, &mut types, &());
}
}
}

for item_id in types.iter() {
context.resolve_item(*item_id).codegen(context, &mut result, &());
}
}
let saw_union = result.saw_union;
Expand Down
7 changes: 7 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,11 @@ impl<'a> Builder<'a> {
self
}

pub fn whitelisted_type<T: Into<String>>(&mut self, arg: T) -> &mut Self {
self.options.whitelisted_types.insert(arg.into());
self
}

pub fn raw_line<T: Into<String>>(&mut self, arg: T) -> &mut Self {
self.options.raw_lines.push(arg.into());
self
Expand Down Expand Up @@ -155,6 +160,7 @@ pub struct BindgenOptions {
pub match_pat: HashSet<String>,
pub hidden_types: HashSet<String>,
pub opaque_types: HashSet<String>,
pub whitelisted_types: HashSet<String>,
pub builtins: bool,
pub rust_enums: bool,
pub links: Vec<(String, LinkType)>,
Expand Down Expand Up @@ -187,6 +193,7 @@ impl Default for BindgenOptions {
match_pat: Default::default(),
hidden_types: Default::default(),
opaque_types: Default::default(),
whitelisted_types: Default::default(),
builtins: false,
rust_enums: true,
links: vec![],
Expand Down
16 changes: 16 additions & 0 deletions tests/expectations/whitelist_basic.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/* automatically generated by rust-bindgen */


#![allow(non_snake_case)]


#[repr(C)]
pub struct WhitelistMe<T> {
pub foo: ::std::os::raw::c_int,
pub bar: WhitelistMe_Inner<T>,
pub _phantom_0: ::std::marker::PhantomData<T>,
}
#[repr(C)]
pub struct WhitelistMe_Inner<T> {
pub bar: T,
}
15 changes: 15 additions & 0 deletions tests/headers/whitelist_basic.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// bindgen-flags: --whitelist-type WhitelistMe

template<typename T>
class WhitelistMe {
class Inner {
T bar;
};

int foo;
Inner bar;
};

struct DontWhitelistMe {
void* foo;
};

0 comments on commit f7adf26

Please sign in to comment.