Skip to content

Commit b097751

Browse files
author
bors-servo
authored
Auto merge of rust-lang#184 - fitzgen:whitelisted-items, r=emilio
Factor out the collection and traversal of the transitive closure of whitelisted items I'm going to re-use this stuff, plus I think it is way more understandable when pulled out of `codegen`. This did have the unfortunate consequence of changing iteration order of types, resulting in different anonymous bindgen names for things like unions and such. We *could* sort the whitelisted item set before codegen to alleviate this kind of thing in the future, but I'm not sure it is worth the extra overhead... r? @emilio
2 parents 7966566 + 5691cab commit b097751

File tree

4 files changed

+151
-94
lines changed

4 files changed

+151
-94
lines changed

src/codegen/mod.rs

+33-93
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use ir::item_kind::ItemKind;
1414
use ir::layout::Layout;
1515
use ir::module::Module;
1616
use ir::ty::{Type, TypeKind};
17-
use ir::type_collector::{ItemSet, TypeCollector};
17+
use ir::type_collector::ItemSet;
1818
use ir::var::Var;
1919
use self::helpers::{BlobTyBuilder, attributes};
2020

@@ -1692,108 +1692,48 @@ impl CodeGenerator for Function {
16921692
}
16931693
}
16941694

1695+
// Return true if any of the ancestors of `id` are in the whitelisted items set,
1696+
// false otherwise.
1697+
fn ancestor_is_whitelisted(ctx: &BindgenContext,
1698+
whitelisted_items: &ItemSet,
1699+
id: ItemId)
1700+
-> bool {
1701+
let item = ctx.resolve_item(id);
1702+
let mut last = id;
1703+
let mut current = item.parent_id();
1704+
1705+
while last != current {
1706+
if whitelisted_items.contains(&current) {
1707+
return true;
1708+
}
1709+
last = current;
1710+
current = ctx.resolve_item(current).parent_id();
1711+
}
1712+
1713+
false
1714+
}
1715+
16951716
pub fn codegen(context: &mut BindgenContext) -> Vec<P<ast::Item>> {
16961717
context.gen(|context| {
16971718
let mut result = CodegenResult::new();
16981719

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

1701-
// If the whitelisted types and functions sets are empty, just generate
1702-
// everything.
1703-
if context.options().whitelisted_types.is_empty() &&
1704-
context.options().whitelisted_functions.is_empty() &&
1705-
context.options().whitelisted_vars.is_empty() {
1706-
for (_item_id, item) in context.items() {
1707-
// Non-toplevel item parents are the responsible one for
1708-
// generating them.
1709-
if item.is_toplevel(context) {
1710-
item.codegen(context, &mut result, &());
1711-
}
1712-
}
1713-
} else {
1714-
// Recursively collect all the types dependent on the whitelisted
1715-
// types, then generate them.
1716-
//
1717-
// FIXME(emilio): This pass is probably slow, but it can't be faster
1718-
// than docopt anyway :)
1719-
let mut items = ItemSet::new();
1720-
for (_item_id, item) in context.items() {
1721-
// FIXME(emilio): This probably should look only at whether the
1722-
// parent is a module.
1723-
if !item.is_toplevel(context) {
1724-
continue;
1725-
}
1726-
1727-
let name = item.canonical_name(context);
1728-
match *item.kind() {
1729-
ItemKind::Type(ref ty) => {
1730-
if context.options().whitelisted_types.matches(&name) {
1731-
item.collect_types(context, &mut items, &());
1732-
}
1733-
// Unnamed top-level enums are special and we whitelist
1734-
// them via the whitelisted_vars filter, since they're
1735-
// effectively top-level constants, and there's no way
1736-
// for them to be referenced consistently.
1737-
if let TypeKind::Enum(ref enum_) = *ty.kind() {
1738-
if ty.name().is_none() {
1739-
if enum_.variants().iter().any(|variant| {
1740-
context.options()
1741-
.whitelisted_vars
1742-
.matches(&variant.name())
1743-
}) {
1744-
item.collect_types(context,
1745-
&mut items,
1746-
&());
1747-
}
1748-
}
1749-
}
1750-
}
1751-
ItemKind::Function(ref fun) => {
1752-
if context.options()
1753-
.whitelisted_functions
1754-
.matches(&name) {
1755-
items.insert(item.id());
1756-
fun.signature()
1757-
.collect_types(context, &mut items, &());
1758-
}
1759-
}
1760-
ItemKind::Var(ref var) => {
1761-
if context.options().whitelisted_vars.matches(&name) {
1762-
items.insert(item.id());
1763-
var.ty().collect_types(context, &mut items, &());
1764-
}
1765-
}
1766-
ItemKind::Module(..) => {}
1767-
}
1768-
}
1769-
1770-
fn contains_parent(ctx: &BindgenContext,
1771-
types: &ItemSet,
1772-
id: ItemId)
1773-
-> bool {
1774-
let item = ctx.resolve_item(id);
1775-
let mut last = id;
1776-
let mut current = item.parent_id();
1777-
1778-
while last != current {
1779-
if types.contains(&current) {
1780-
return true;
1781-
}
1782-
last = current;
1783-
current = ctx.resolve_item(current).parent_id();
1784-
}
1722+
let whitelisted_items: ItemSet = context.whitelisted_items().collect();
17851723

1786-
false
1787-
}
1724+
for &id in whitelisted_items.iter() {
1725+
let item = context.resolve_item(id);
17881726

1789-
for item_id in items.iter() {
1790-
let item = context.resolve_item(*item_id);
1791-
if item.is_toplevel(context) ||
1792-
!contains_parent(context, &items, *item_id) {
1793-
item.codegen(context, &mut result, &());
1794-
}
1727+
// Non-toplevel items' parents are responsible one for generating
1728+
// their children. However, if we find an orphaned reference to a
1729+
// non-toplevel item whose parent is not in our whitelisted set, we
1730+
// need to take responsibility for generating it.
1731+
if item.is_toplevel(context) ||
1732+
!ancestor_is_whitelisted(context, &whitelisted_items, id) {
1733+
item.codegen(context, &mut result, &());
17951734
}
17961735
}
1736+
17971737
let saw_union = result.saw_union;
17981738
let mut result = result.items;
17991739
if saw_union && !context.options().unstable_rust {

src/ir/context.rs

+116
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use super::item::{Item, ItemCanonicalName, ItemId};
1212
use super::item_kind::ItemKind;
1313
use super::module::Module;
1414
use super::ty::{FloatKind, Type, TypeKind};
15+
use super::type_collector::{ItemSet, TypeCollector};
1516
use syntax::ast::Ident;
1617
use syntax::codemap::{DUMMY_SP, Span};
1718
use syntax::ext::base::ExtCtxt;
@@ -814,4 +815,119 @@ impl<'ctx> BindgenContext<'ctx> {
814815

815816
self.current_module = previous_id;
816817
}
818+
819+
/// Iterate over all (explicitly or transitively) whitelisted items.
820+
///
821+
/// If no items are explicitly whitelisted, then all items are considered
822+
/// whitelisted.
823+
pub fn whitelisted_items<'me>(&'me self)
824+
-> WhitelistedItemsIter<'me, 'ctx> {
825+
assert!(self.in_codegen_phase());
826+
assert!(self.current_module == self.root_module);
827+
828+
let roots = self.items()
829+
.filter(|&(_, item)| {
830+
// If nothing is explicitly whitelisted, then everything is fair
831+
// game.
832+
if self.options().whitelisted_types.is_empty() &&
833+
self.options().whitelisted_functions.is_empty() &&
834+
self.options().whitelisted_vars.is_empty() {
835+
return true;
836+
}
837+
838+
let name = item.canonical_name(self);
839+
match *item.kind() {
840+
ItemKind::Module(..) => false,
841+
ItemKind::Function(_) => {
842+
self.options().whitelisted_functions.matches(&name)
843+
}
844+
ItemKind::Var(_) => {
845+
self.options().whitelisted_vars.matches(&name)
846+
}
847+
ItemKind::Type(ref ty) => {
848+
if self.options().whitelisted_types.matches(&name) {
849+
return true;
850+
}
851+
852+
// Unnamed top-level enums are special and we whitelist
853+
// them via the `whitelisted_vars` filter, since they're
854+
// effectively top-level constants, and there's no way
855+
// for them to be referenced consistently.
856+
if let TypeKind::Enum(ref enum_) = *ty.kind() {
857+
if ty.name().is_none() &&
858+
enum_.variants().iter().any(|variant| {
859+
self.options()
860+
.whitelisted_vars
861+
.matches(&variant.name())
862+
}) {
863+
return true;
864+
}
865+
}
866+
867+
false
868+
}
869+
}
870+
})
871+
.map(|(&id, _)| id);
872+
873+
let seen: ItemSet = roots.collect();
874+
875+
// The .rev() preserves the expected ordering traversal, resulting in
876+
// more stable-ish bindgen-generated names for anonymous types (like
877+
// unions).
878+
let to_iterate = seen.iter().cloned().rev().collect();
879+
880+
WhitelistedItemsIter {
881+
ctx: self,
882+
seen: seen,
883+
to_iterate: to_iterate,
884+
}
885+
}
886+
}
887+
888+
/// An iterator over whitelisted items.
889+
///
890+
/// See `BindgenContext::whitelisted_items` for more information.
891+
pub struct WhitelistedItemsIter<'ctx, 'gen>
892+
where 'gen: 'ctx,
893+
{
894+
ctx: &'ctx BindgenContext<'gen>,
895+
896+
// The set of whitelisted items we have seen. If you think of traversing
897+
// whitelisted items like GC tracing, this is the mark bits, and contains
898+
// both black and gray items.
899+
seen: ItemSet,
900+
901+
// The set of whitelisted items that we have seen but have yet to iterate
902+
// over and collect transitive references from. To return to the GC analogy,
903+
// this is the mark stack, containing the set of gray items which we have
904+
// not finished tracing yet.
905+
to_iterate: Vec<ItemId>,
906+
}
907+
908+
impl<'ctx, 'gen> Iterator for WhitelistedItemsIter<'ctx, 'gen>
909+
where 'gen: 'ctx,
910+
{
911+
type Item = ItemId;
912+
913+
fn next(&mut self) -> Option<Self::Item> {
914+
let id = match self.to_iterate.pop() {
915+
None => return None,
916+
Some(id) => id,
917+
};
918+
919+
debug_assert!(self.seen.contains(&id));
920+
921+
let mut sub_types = ItemSet::new();
922+
id.collect_types(self.ctx, &mut sub_types, &());
923+
924+
for id in sub_types {
925+
if !self.seen.contains(&id) {
926+
self.to_iterate.push(id);
927+
self.seen.insert(id);
928+
}
929+
}
930+
931+
Some(id)
932+
}
817933
}

tests/expectations/type_alias_empty.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,4 @@
44
#![allow(non_snake_case)]
55

66

7-
7+
pub type bool_constant = ();

tests/tools/run-bindgen.py

+1
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ def parse_args():
6969
def make_bindgen_env():
7070
"""Build the environment to run bindgen in."""
7171
env = os.environ.copy()
72+
env["RUST_BACKTRACE"] = "1"
7273

7374
# El Capitan likes to unset dyld variables
7475
# https://forums.developer.apple.com/thread/9233

0 commit comments

Comments
 (0)