summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xsrc/codegen/mod.rs126
-rw-r--r--src/ir/context.rs116
-rw-r--r--tests/expectations/type_alias_empty.rs2
-rwxr-xr-xtests/tools/run-bindgen.py1
4 files changed, 151 insertions, 94 deletions
diff --git a/src/codegen/mod.rs b/src/codegen/mod.rs
index 57173904..e7fa62ee 100755
--- a/src/codegen/mod.rs
+++ b/src/codegen/mod.rs
@@ -14,7 +14,7 @@ use ir::item_kind::ItemKind;
use ir::layout::Layout;
use ir::module::Module;
use ir::ty::{Type, TypeKind};
-use ir::type_collector::{ItemSet, TypeCollector};
+use ir::type_collector::ItemSet;
use ir::var::Var;
use self::helpers::{BlobTyBuilder, attributes};
@@ -1692,108 +1692,48 @@ impl CodeGenerator for Function {
}
}
+// Return true if any of the ancestors of `id` are in the whitelisted items set,
+// false otherwise.
+fn ancestor_is_whitelisted(ctx: &BindgenContext,
+ whitelisted_items: &ItemSet,
+ id: ItemId)
+ -> bool {
+ let item = ctx.resolve_item(id);
+ let mut last = id;
+ let mut current = item.parent_id();
+
+ while last != current {
+ if whitelisted_items.contains(&current) {
+ return true;
+ }
+ last = current;
+ current = ctx.resolve_item(current).parent_id();
+ }
+
+ false
+}
+
pub fn codegen(context: &mut BindgenContext) -> Vec<P<ast::Item>> {
context.gen(|context| {
let mut result = CodegenResult::new();
debug!("codegen: {:?}", context.options());
- // If the whitelisted types and functions sets are empty, just generate
- // everything.
- if context.options().whitelisted_types.is_empty() &&
- context.options().whitelisted_functions.is_empty() &&
- context.options().whitelisted_vars.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 items = ItemSet::new();
- for (_item_id, item) in context.items() {
- // FIXME(emilio): This probably should look only at whether the
- // parent is a module.
- if !item.is_toplevel(context) {
- continue;
- }
-
- let name = item.canonical_name(context);
- match *item.kind() {
- ItemKind::Type(ref ty) => {
- if context.options().whitelisted_types.matches(&name) {
- item.collect_types(context, &mut items, &());
- }
- // Unnamed top-level enums are special and we whitelist
- // them via the whitelisted_vars filter, since they're
- // effectively top-level constants, and there's no way
- // for them to be referenced consistently.
- if let TypeKind::Enum(ref enum_) = *ty.kind() {
- if ty.name().is_none() {
- if enum_.variants().iter().any(|variant| {
- context.options()
- .whitelisted_vars
- .matches(&variant.name())
- }) {
- item.collect_types(context,
- &mut items,
- &());
- }
- }
- }
- }
- ItemKind::Function(ref fun) => {
- if context.options()
- .whitelisted_functions
- .matches(&name) {
- items.insert(item.id());
- fun.signature()
- .collect_types(context, &mut items, &());
- }
- }
- ItemKind::Var(ref var) => {
- if context.options().whitelisted_vars.matches(&name) {
- items.insert(item.id());
- var.ty().collect_types(context, &mut items, &());
- }
- }
- ItemKind::Module(..) => {}
- }
- }
-
- fn contains_parent(ctx: &BindgenContext,
- types: &ItemSet,
- id: ItemId)
- -> bool {
- let item = ctx.resolve_item(id);
- let mut last = id;
- let mut current = item.parent_id();
-
- while last != current {
- if types.contains(&current) {
- return true;
- }
- last = current;
- current = ctx.resolve_item(current).parent_id();
- }
+ let whitelisted_items: ItemSet = context.whitelisted_items().collect();
- false
- }
+ for &id in whitelisted_items.iter() {
+ let item = context.resolve_item(id);
- for item_id in items.iter() {
- let item = context.resolve_item(*item_id);
- if item.is_toplevel(context) ||
- !contains_parent(context, &items, *item_id) {
- item.codegen(context, &mut result, &());
- }
+ // Non-toplevel items' parents are responsible one for generating
+ // their children. However, if we find an orphaned reference to a
+ // non-toplevel item whose parent is not in our whitelisted set, we
+ // need to take responsibility for generating it.
+ if item.is_toplevel(context) ||
+ !ancestor_is_whitelisted(context, &whitelisted_items, id) {
+ item.codegen(context, &mut result, &());
}
}
+
let saw_union = result.saw_union;
let mut result = result.items;
if saw_union && !context.options().unstable_rust {
diff --git a/src/ir/context.rs b/src/ir/context.rs
index 273dc3e3..7e824be1 100644
--- a/src/ir/context.rs
+++ b/src/ir/context.rs
@@ -12,6 +12,7 @@ use super::item::{Item, ItemCanonicalName, ItemId};
use super::item_kind::ItemKind;
use super::module::Module;
use super::ty::{FloatKind, Type, TypeKind};
+use super::type_collector::{ItemSet, TypeCollector};
use syntax::ast::Ident;
use syntax::codemap::{DUMMY_SP, Span};
use syntax::ext::base::ExtCtxt;
@@ -814,4 +815,119 @@ impl<'ctx> BindgenContext<'ctx> {
self.current_module = previous_id;
}
+
+ /// Iterate over all (explicitly or transitively) whitelisted items.
+ ///
+ /// If no items are explicitly whitelisted, then all items are considered
+ /// whitelisted.
+ pub fn whitelisted_items<'me>(&'me self)
+ -> WhitelistedItemsIter<'me, 'ctx> {
+ assert!(self.in_codegen_phase());
+ assert!(self.current_module == self.root_module);
+
+ let roots = self.items()
+ .filter(|&(_, item)| {
+ // If nothing is explicitly whitelisted, then everything is fair
+ // game.
+ if self.options().whitelisted_types.is_empty() &&
+ self.options().whitelisted_functions.is_empty() &&
+ self.options().whitelisted_vars.is_empty() {
+ return true;
+ }
+
+ let name = item.canonical_name(self);
+ match *item.kind() {
+ ItemKind::Module(..) => false,
+ ItemKind::Function(_) => {
+ self.options().whitelisted_functions.matches(&name)
+ }
+ ItemKind::Var(_) => {
+ self.options().whitelisted_vars.matches(&name)
+ }
+ ItemKind::Type(ref ty) => {
+ if self.options().whitelisted_types.matches(&name) {
+ return true;
+ }
+
+ // Unnamed top-level enums are special and we whitelist
+ // them via the `whitelisted_vars` filter, since they're
+ // effectively top-level constants, and there's no way
+ // for them to be referenced consistently.
+ if let TypeKind::Enum(ref enum_) = *ty.kind() {
+ if ty.name().is_none() &&
+ enum_.variants().iter().any(|variant| {
+ self.options()
+ .whitelisted_vars
+ .matches(&variant.name())
+ }) {
+ return true;
+ }
+ }
+
+ false
+ }
+ }
+ })
+ .map(|(&id, _)| id);
+
+ let seen: ItemSet = roots.collect();
+
+ // The .rev() preserves the expected ordering traversal, resulting in
+ // more stable-ish bindgen-generated names for anonymous types (like
+ // unions).
+ let to_iterate = seen.iter().cloned().rev().collect();
+
+ WhitelistedItemsIter {
+ ctx: self,
+ seen: seen,
+ to_iterate: to_iterate,
+ }
+ }
+}
+
+/// An iterator over whitelisted items.
+///
+/// See `BindgenContext::whitelisted_items` for more information.
+pub struct WhitelistedItemsIter<'ctx, 'gen>
+ where 'gen: 'ctx,
+{
+ ctx: &'ctx BindgenContext<'gen>,
+
+ // The set of whitelisted items we have seen. If you think of traversing
+ // whitelisted items like GC tracing, this is the mark bits, and contains
+ // both black and gray items.
+ seen: ItemSet,
+
+ // The set of whitelisted items that we have seen but have yet to iterate
+ // over and collect transitive references from. To return to the GC analogy,
+ // this is the mark stack, containing the set of gray items which we have
+ // not finished tracing yet.
+ to_iterate: Vec<ItemId>,
+}
+
+impl<'ctx, 'gen> Iterator for WhitelistedItemsIter<'ctx, 'gen>
+ where 'gen: 'ctx,
+{
+ type Item = ItemId;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ let id = match self.to_iterate.pop() {
+ None => return None,
+ Some(id) => id,
+ };
+
+ debug_assert!(self.seen.contains(&id));
+
+ let mut sub_types = ItemSet::new();
+ id.collect_types(self.ctx, &mut sub_types, &());
+
+ for id in sub_types {
+ if !self.seen.contains(&id) {
+ self.to_iterate.push(id);
+ self.seen.insert(id);
+ }
+ }
+
+ Some(id)
+ }
}
diff --git a/tests/expectations/type_alias_empty.rs b/tests/expectations/type_alias_empty.rs
index b4b7b2bc..47e36006 100644
--- a/tests/expectations/type_alias_empty.rs
+++ b/tests/expectations/type_alias_empty.rs
@@ -4,4 +4,4 @@
#![allow(non_snake_case)]
-
+pub type bool_constant = ();
diff --git a/tests/tools/run-bindgen.py b/tests/tools/run-bindgen.py
index 258a60e5..bc8b567b 100755
--- a/tests/tools/run-bindgen.py
+++ b/tests/tools/run-bindgen.py
@@ -69,6 +69,7 @@ def parse_args():
def make_bindgen_env():
"""Build the environment to run bindgen in."""
env = os.environ.copy()
+ env["RUST_BACKTRACE"] = "1"
# El Capitan likes to unset dyld variables
# https://forums.developer.apple.com/thread/9233