diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/clang.rs | 13 | ||||
-rw-r--r-- | src/codegen/mod.rs | 14 | ||||
-rw-r--r-- | src/codegen/struct_layout.rs | 6 | ||||
-rw-r--r-- | src/ir/comp.rs | 31 | ||||
-rw-r--r-- | src/ir/context.rs | 41 | ||||
-rw-r--r-- | src/ir/dot.rs | 53 | ||||
-rw-r--r-- | src/ir/function.rs | 30 | ||||
-rw-r--r-- | src/ir/item.rs | 45 | ||||
-rw-r--r-- | src/ir/item_kind.rs | 20 | ||||
-rw-r--r-- | src/ir/layout.rs | 2 | ||||
-rw-r--r-- | src/ir/mod.rs | 1 | ||||
-rw-r--r-- | src/ir/module.rs | 12 | ||||
-rw-r--r-- | src/ir/named.rs | 236 | ||||
-rw-r--r-- | src/ir/objc.rs | 46 | ||||
-rw-r--r-- | src/ir/traversal.rs | 124 | ||||
-rw-r--r-- | src/ir/ty.rs | 190 | ||||
-rw-r--r-- | src/ir/var.rs | 20 | ||||
-rw-r--r-- | src/lib.rs | 22 | ||||
-rw-r--r-- | src/options.rs | 3 |
19 files changed, 744 insertions, 165 deletions
diff --git a/src/clang.rs b/src/clang.rs index 613e08e8..1a45eefa 100644 --- a/src/clang.rs +++ b/src/clang.rs @@ -875,7 +875,11 @@ impl Type { pub fn named(&self) -> Type { unsafe { Type { - x: clang_Type_getNamedType(self.x), + x: if clang_Type_getNamedType::is_loaded() { + clang_Type_getNamedType(self.x) + } else { + self.x + }, } } } @@ -1498,6 +1502,13 @@ pub fn ast_dump(c: &Cursor, depth: isize) -> CXChildVisitResult { &specialized); } } + + if let Some(parent) = c.fallible_semantic_parent() { + println!(""); + print_cursor(depth, + String::from(prefix) + "semantic-parent.", + &parent); + } } fn print_type<S: AsRef<str>>(depth: isize, prefix: S, ty: &Type) { diff --git a/src/codegen/mod.rs b/src/codegen/mod.rs index 77f654e6..46b0a3e7 100644 --- a/src/codegen/mod.rs +++ b/src/codegen/mod.rs @@ -10,6 +10,7 @@ use ir::annotations::FieldAccessorKind; use ir::comp::{Base, CompInfo, CompKind, Field, Method, MethodKind}; use ir::context::{BindgenContext, ItemId}; use ir::derive::{CanDeriveCopy, CanDeriveDebug, CanDeriveDefault}; +use ir::dot; use ir::enum_ty::{Enum, EnumVariant, EnumVariantValue}; use ir::function::{Function, FunctionSig}; use ir::int::IntKind; @@ -646,6 +647,9 @@ impl CodeGenerator for Type { TypeKind::Enum(ref ei) => { ei.codegen(ctx, result, whitelisted_items, item) } + TypeKind::ObjCId | TypeKind::ObjCSel => { + result.saw_objc(); + } TypeKind::ObjCInterface(ref interface) => { interface.codegen(ctx, result, whitelisted_items, item) } @@ -2275,6 +2279,8 @@ impl ToRustTy for Type { let ident = ctx.rust_ident(&name); quote_ty!(ctx.ext_cx(), $ident) } + TypeKind::ObjCSel => quote_ty!(ctx.ext_cx(), objc::runtime::Sel), + TypeKind::ObjCId | TypeKind::ObjCInterface(..) => quote_ty!(ctx.ext_cx(), id), ref u @ TypeKind::UnresolvedTypeRef(..) => { unreachable!("Should have been resolved after parsing {:?}!", u) @@ -2460,10 +2466,12 @@ impl CodeGenerator for ObjCInterface { } + let trait_name = self.rust_name(); + let trait_block = aster::AstBuilder::new() .item() .pub_() - .trait_(self.name()) + .trait_(&trait_name) .with_items(trait_items) .build(); @@ -2472,7 +2480,7 @@ impl CodeGenerator for ObjCInterface { .item() .impl_() .trait_() - .id(self.name()) + .id(&trait_name) .build() .with_items(impl_items) .build_ty(ty_for_impl); @@ -2502,7 +2510,7 @@ pub fn codegen(context: &mut BindgenContext) -> Vec<P<ast::Item>> { } if let Some(path) = context.options().emit_ir_graphviz.as_ref() { - match context.emit_ir_graphviz(path.clone()) { + match dot::write_dot_file(context, path) { Ok(()) => info!("Your dot file was generated successfully into: {}", path), Err(e) => error!("{}", e), } diff --git a/src/codegen/struct_layout.rs b/src/codegen/struct_layout.rs index 24938c16..724bef98 100644 --- a/src/codegen/struct_layout.rs +++ b/src/codegen/struct_layout.rs @@ -197,7 +197,7 @@ impl<'a, 'ctx> StructLayoutTracker<'a, 'ctx> { }; // Otherwise the padding is useless. - let need_padding = padding_bytes >= field_layout.align; + let need_padding = padding_bytes >= field_layout.align || field_layout.align > mem::size_of::<*mut ()>(); self.latest_offset += padding_bytes; @@ -213,7 +213,7 @@ impl<'a, 'ctx> StructLayoutTracker<'a, 'ctx> { field_layout); if need_padding && padding_bytes != 0 { - Some(Layout::new(padding_bytes, field_layout.align)) + Some(Layout::new(padding_bytes, cmp::min(field_layout.align, mem::size_of::<*mut ()>()))) } else { None } @@ -262,6 +262,8 @@ impl<'a, 'ctx> StructLayoutTracker<'a, 'ctx> { Layout::new(padding_bytes, layout.align) }; + debug!("pad bytes to struct {}, {:?}", name, layout); + Some(self.padding_field(layout)) } else { None diff --git a/src/ir/comp.rs b/src/ir/comp.rs index ce6ec25d..b97879f7 100644 --- a/src/ir/comp.rs +++ b/src/ir/comp.rs @@ -625,8 +625,19 @@ impl CompInfo { // StructDecl to note incomplete structs that hasn't been // forward-declared before, see: // + // Also, clang seems to scope struct definitions inside + // unions to the whole translation unit. Since those are + // anonymous, let's just assume that if the cursor we've + // found is a definition it's a valid inner type. + // + // Note that doing this could be always ok, but let's just + // keep the union check for now. + // // https://github.com/servo/rust-bindgen/issues/482 - if cur.semantic_parent() != cursor { + let is_inner_struct = cur.semantic_parent() == cursor || + (kind == CompKind::Union && + cur.is_definition()); + if !is_inner_struct { return CXChildVisit_Continue; } @@ -871,7 +882,7 @@ impl CompInfo { } impl TemplateDeclaration for CompInfo { - fn template_params(&self, _ctx: &BindgenContext) -> Option<Vec<ItemId>> { + fn self_template_params(&self, _ctx: &BindgenContext) -> Option<Vec<ItemId>> { if self.template_args.is_empty() { None } else { @@ -1040,10 +1051,10 @@ impl Trace for CompInfo { if let Some(template) = self.specialized_template() { // This is an instantiation of a template declaration with concrete // template type arguments. - tracer.visit(template); + tracer.visit_kind(template, EdgeKind::TemplateDeclaration); let args = item.applicable_template_args(context); for a in args { - tracer.visit(a); + tracer.visit_kind(a, EdgeKind::TemplateArgument); } } else { let params = item.applicable_template_args(context); @@ -1055,27 +1066,27 @@ impl Trace for CompInfo { } for base in self.base_members() { - tracer.visit(base.ty); + tracer.visit_kind(base.ty, EdgeKind::BaseMember); } for field in self.fields() { - tracer.visit(field.ty()); + tracer.visit_kind(field.ty(), EdgeKind::Field); } for &ty in self.inner_types() { - tracer.visit(ty); + tracer.visit_kind(ty, EdgeKind::InnerType); } for &var in self.inner_vars() { - tracer.visit(var); + tracer.visit_kind(var, EdgeKind::InnerVar); } for method in self.methods() { - tracer.visit(method.signature); + tracer.visit_kind(method.signature, EdgeKind::Method); } for &ctor in self.constructors() { - tracer.visit(ctor); + tracer.visit_kind(ctor, EdgeKind::Constructor); } } } diff --git a/src/ir/context.rs b/src/ir/context.rs index 7383c09a..27a43f20 100644 --- a/src/ir/context.rs +++ b/src/ir/context.rs @@ -5,7 +5,7 @@ use super::int::IntKind; use super::item::{Item, ItemCanonicalPath, ItemSet}; use super::item_kind::ItemKind; use super::module::{Module, ModuleKind}; -use super::traversal::{self, Edge, ItemTraversal, Trace}; +use super::traversal::{self, Edge, ItemTraversal}; use super::ty::{FloatKind, TemplateDeclaration, Type, TypeKind}; use BindgenOptions; use cexpr; @@ -18,8 +18,6 @@ use std::cell::Cell; use std::collections::{HashMap, hash_map}; use std::collections::btree_map::{self, BTreeMap}; use std::fmt; -use std::fs::File; -use std::io::{self, Write}; use std::iter::IntoIterator; use syntax::ast::Ident; use syntax::codemap::{DUMMY_SP, Span}; @@ -636,7 +634,7 @@ impl<'ctx> BindgenContext<'ctx> { .and_then(|canon_decl| { self.get_resolved_type(&canon_decl) .and_then(|template_decl_id| { - template_decl_id.num_template_params(self) + template_decl_id.num_self_template_params(self) .map(|num_params| { (*canon_decl.cursor(), template_decl_id, @@ -660,7 +658,7 @@ impl<'ctx> BindgenContext<'ctx> { .cloned() }) .and_then(|template_decl| { - template_decl.num_template_params(self) + template_decl.num_self_template_params(self) .map(|num_template_params| { (*template_decl.decl(), template_decl.id(), @@ -708,7 +706,7 @@ impl<'ctx> BindgenContext<'ctx> { use clang_sys; let num_expected_args = match self.resolve_type(template) - .num_template_params(self) { + .num_self_template_params(self) { Some(n) => n, None => { warn!("Tried to instantiate a template for which we could not \ @@ -1111,33 +1109,6 @@ impl<'ctx> BindgenContext<'ctx> { &self.options } - /// Output graphviz dot file. - pub fn emit_ir_graphviz(&self, path: String) -> io::Result<()> { - let file = try!(File::create(path)); - let mut dot_file = io::BufWriter::new(file); - writeln!(&mut dot_file, "digraph {{")?; - - let mut err: Option<io::Result<_>> = None; - - for (id, item) in self.items() { - writeln!(&mut dot_file, "{} {};", id.0, item.dot_attributes(self))?; - - item.trace(self, &mut |sub_id: ItemId, _edge_kind| { - match writeln!(&mut dot_file, "{} -> {};", id.0, sub_id.as_usize()) { - Ok(_) => {}, - Err(e) => err = Some(Err(e)), - } - }, &()); - - if err.is_some() { - return err.unwrap(); - } - } - - writeln!(&mut dot_file, "}}")?; - Ok(()) - } - /// Tokenizes a namespace cursor in order to get the name and kind of the /// namespace, fn tokenize_namespace(&self, @@ -1360,13 +1331,13 @@ impl PartialType { } impl TemplateDeclaration for PartialType { - fn template_params(&self, _ctx: &BindgenContext) -> Option<Vec<ItemId>> { + fn self_template_params(&self, _ctx: &BindgenContext) -> Option<Vec<ItemId>> { // Maybe at some point we will eagerly parse named types, but for now we // don't and this information is unavailable. None } - fn num_template_params(&self, _ctx: &BindgenContext) -> Option<usize> { + fn num_self_template_params(&self, _ctx: &BindgenContext) -> Option<usize> { // Wouldn't it be nice if libclang would reliably give us this // information‽ match self.decl().kind() { diff --git a/src/ir/dot.rs b/src/ir/dot.rs new file mode 100644 index 00000000..b7a117bb --- /dev/null +++ b/src/ir/dot.rs @@ -0,0 +1,53 @@ +//! Generating Graphviz `dot` files from our IR. + +use std::fs::File; +use std::io::{self, Write}; +use std::path::Path; +use super::context::{BindgenContext, ItemId}; +use super::traversal::Trace; + +/// A trait for anything that can write attributes as `<table>` rows to a dot +/// file. +pub trait DotAttributes { + /// Write this thing's attributes to the given output. Each attribute must + /// be its own `<tr>...</tr>`. + fn dot_attributes<W>(&self, ctx: &BindgenContext, out: &mut W) -> io::Result<()> + where W: io::Write; +} + +/// Write a graphviz dot file containing our IR. +pub fn write_dot_file<P>(ctx: &BindgenContext, path: P) -> io::Result<()> + where P: AsRef<Path> +{ + let file = try!(File::create(path)); + let mut dot_file = io::BufWriter::new(file); + try!(writeln!(&mut dot_file, "digraph {{")); + + let mut err: Option<io::Result<_>> = None; + + for (id, item) in ctx.items() { + try!(writeln!(&mut dot_file, + r#"{} [fontname="courier", label=< <table border="0">"#, + id.as_usize())); + try!(item.dot_attributes(ctx, &mut dot_file)); + try!(writeln!(&mut dot_file, r#"</table> >];"#)); + + item.trace(ctx, &mut |sub_id: ItemId, _edge_kind| { + if err.is_some() { + return; + } + + match writeln!(&mut dot_file, "{} -> {};", id.as_usize(), sub_id.as_usize()) { + Ok(_) => {}, + Err(e) => err = Some(Err(e)), + } + }, &()); + + if let Some(err) = err { + return err; + } + } + + try!(writeln!(&mut dot_file, "}}")); + Ok(()) +} diff --git a/src/ir/function.rs b/src/ir/function.rs index 22b9c9b0..5864bbf8 100644 --- a/src/ir/function.rs +++ b/src/ir/function.rs @@ -1,12 +1,14 @@ //! Intermediate representation for C/C++ functions and methods. use super::context::{BindgenContext, ItemId}; +use super::dot::DotAttributes; use super::item::Item; -use super::traversal::{Trace, Tracer}; +use super::traversal::{EdgeKind, Trace, Tracer}; use super::ty::TypeKind; use clang; use clang_sys::CXCallingConv; use parse::{ClangItemParser, ClangSubItemParser, ParseError, ParseResult}; +use std::io; use syntax::abi; /// A function declaration, with a signature, arguments, and argument names. @@ -59,6 +61,18 @@ impl Function { } } +impl DotAttributes for Function { + fn dot_attributes<W>(&self, _ctx: &BindgenContext, out: &mut W) -> io::Result<()> + where W: io::Write + { + if let Some(ref mangled) = self.mangled_name { + try!(writeln!(out, "<tr><td>mangled name</td><td>{}</td></tr>", mangled)); + } + + Ok(()) + } +} + /// A function signature. #[derive(Debug)] pub struct FunctionSig { @@ -91,7 +105,13 @@ fn get_abi(cc: CXCallingConv) -> Option<abi::Abi> { } /// Get the mangled name for the cursor's referent. -pub fn cursor_mangling(cursor: &clang::Cursor) -> Option<String> { +pub fn cursor_mangling(ctx: &BindgenContext, + cursor: &clang::Cursor) + -> Option<String> { + if !ctx.options().enable_mangling { + return None; + } + // We early return here because libclang may crash in some case // if we pass in a variable inside a partial specialized template. // See servo/rust-bindgen#67, and servo/rust-bindgen#462. @@ -304,7 +324,7 @@ impl ClangSubItemParser for Function { let name = cursor.spelling(); assert!(!name.is_empty(), "Empty function name?"); - let mut mangled_name = cursor_mangling(&cursor); + let mut mangled_name = cursor_mangling(context, &cursor); if mangled_name.as_ref() == Some(&name) { mangled_name = None; } @@ -322,10 +342,10 @@ impl Trace for FunctionSig { fn trace<T>(&self, _: &BindgenContext, tracer: &mut T, _: &()) where T: Tracer, { - tracer.visit(self.return_type()); + tracer.visit_kind(self.return_type(), EdgeKind::FunctionReturn); for &(_, ty) in self.argument_types() { - tracer.visit(ty); + tracer.visit_kind(ty, EdgeKind::FunctionParameter); } } } diff --git a/src/ir/item.rs b/src/ir/item.rs index 8f16a96f..21b27f07 100644 --- a/src/ir/item.rs +++ b/src/ir/item.rs @@ -3,10 +3,11 @@ use super::annotations::Annotations; use super::context::{BindgenContext, ItemId, PartialType}; use super::derive::{CanDeriveCopy, CanDeriveDebug, CanDeriveDefault}; +use super::dot::{DotAttributes}; use super::function::Function; use super::item_kind::ItemKind; use super::module::Module; -use super::traversal::{Trace, Tracer}; +use super::traversal::{EdgeKind, Trace, Tracer}; use super::ty::{TemplateDeclaration, Type, TypeKind}; use clang; use clang_sys; @@ -15,6 +16,7 @@ use std::cell::{Cell, RefCell}; use std::collections::BTreeSet; use std::fmt::Write; use std::iter; +use std::io; /// A trait to get the canonical name from an item. /// @@ -203,7 +205,7 @@ impl Trace for Item { tracer.visit(fun.signature()); } ItemKind::Var(ref var) => { - tracer.visit(var.ty()); + tracer.visit_kind(var.ty(), EdgeKind::VarType); } ItemKind::Module(_) => { // Module -> children edges are "weak", and we do not want to @@ -372,20 +374,6 @@ impl Item { self.id } - /// Get this `Item`'s dot attributes. - pub fn dot_attributes(&self, ctx: &BindgenContext) -> String { - format!("[fontname=\"courier\", label=< \ - <table border=\"0\"> \ - <tr><td>ItemId({})</td></tr> \ - <tr><td>name</td><td>{}</td></tr> \ - <tr><td>kind</td><td>{}</td></tr> \ - </table> \ - >]", - self.id.as_usize(), - self.name(ctx).get(), - self.kind.kind_name()) - } - /// Get this `Item`'s parent's identifier. /// /// For the root module, the parent's ID is its own ID. @@ -928,23 +916,36 @@ impl Item { /// A set of items. pub type ItemSet = BTreeSet<ItemId>; +impl DotAttributes for Item { + fn dot_attributes<W>(&self, ctx: &BindgenContext, out: &mut W) -> io::Result<()> + where W: io::Write + { + try!(writeln!(out, + "<tr><td>{:?}</td></tr> + <tr><td>name</td><td>{}</td></tr>", + self.id, + self.name(ctx).get())); + self.kind.dot_attributes(ctx, out) + } +} + impl TemplateDeclaration for ItemId { - fn template_params(&self, ctx: &BindgenContext) -> Option<Vec<ItemId>> { + fn self_template_params(&self, ctx: &BindgenContext) -> Option<Vec<ItemId>> { ctx.resolve_item_fallible(*self) - .and_then(|item| item.template_params(ctx)) + .and_then(|item| item.self_template_params(ctx)) } } impl TemplateDeclaration for Item { - fn template_params(&self, ctx: &BindgenContext) -> Option<Vec<ItemId>> { - self.kind.template_params(ctx) + fn self_template_params(&self, ctx: &BindgenContext) -> Option<Vec<ItemId>> { + self.kind.self_template_params(ctx) } } impl TemplateDeclaration for ItemKind { - fn template_params(&self, ctx: &BindgenContext) -> Option<Vec<ItemId>> { + fn self_template_params(&self, ctx: &BindgenContext) -> Option<Vec<ItemId>> { match *self { - ItemKind::Type(ref ty) => ty.template_params(ctx), + ItemKind::Type(ref ty) => ty.self_template_params(ctx), // If we start emitting bindings to explicitly instantiated // functions, then we'll need to check ItemKind::Function for // template params. diff --git a/src/ir/item_kind.rs b/src/ir/item_kind.rs index 3ff06731..6dfd6764 100644 --- a/src/ir/item_kind.rs +++ b/src/ir/item_kind.rs @@ -1,5 +1,8 @@ //! Different variants of an `Item` in our intermediate representation. +use std::io; +use super::context::BindgenContext; +use super::dot::DotAttributes; use super::function::Function; use super::module::Module; use super::ty::Type; @@ -39,7 +42,7 @@ impl ItemKind { ItemKind::Type(..) => "Type", ItemKind::Function(..) => "Function", ItemKind::Var(..) => "Var" - } + } } /// Is this a module? @@ -122,3 +125,18 @@ impl ItemKind { self.as_var().expect("Not a var") } } + +impl DotAttributes for ItemKind { + fn dot_attributes<W>(&self, ctx: &BindgenContext, out: &mut W) -> io::Result<()> + where W: io::Write + { + try!(writeln!(out, "<tr><td>kind</td><td>{}</td></tr>", self.kind_name())); + + match *self { + ItemKind::Module(ref module) => module.dot_attributes(ctx, out), + ItemKind::Type(ref ty) => ty.dot_attributes(ctx, out), + ItemKind::Function(ref func) => func.dot_attributes(ctx, out), + ItemKind::Var(ref var) => var.dot_attributes(ctx, out), + } + } +} diff --git a/src/ir/layout.rs b/src/ir/layout.rs index 38379261..f21a501c 100644 --- a/src/ir/layout.rs +++ b/src/ir/layout.rs @@ -38,7 +38,7 @@ impl Layout { /// alignment possible. pub fn for_size(size: usize) -> Self { let mut next_align = 2; - while size % next_align == 0 && next_align <= 2 * mem::size_of::<*mut ()>() { + while size % next_align == 0 && next_align <= mem::size_of::<*mut ()>() { next_align *= 2; } Layout { diff --git a/src/ir/mod.rs b/src/ir/mod.rs index e624e46b..ba549c51 100644 --- a/src/ir/mod.rs +++ b/src/ir/mod.rs @@ -7,6 +7,7 @@ pub mod annotations; pub mod comp; pub mod context; pub mod derive; +pub mod dot; pub mod enum_ty; pub mod function; pub mod int; diff --git a/src/ir/module.rs b/src/ir/module.rs index 6b6c535b..6787e3f9 100644 --- a/src/ir/module.rs +++ b/src/ir/module.rs @@ -1,6 +1,8 @@ //! Intermediate representation for modules (AKA C++ namespaces). +use std::io; use super::context::{BindgenContext, ItemId}; +use super::dot::DotAttributes; use clang; use parse::{ClangSubItemParser, ParseError, ParseResult}; use parse_one; @@ -56,6 +58,16 @@ impl Module { } } +impl DotAttributes for Module { + fn dot_attributes<W>(&self, _ctx: &BindgenContext, out: &mut W) -> io::Result<()> + where W: io::Write + { + writeln!(out, + "<tr><td>ModuleKind</td><td>{:?}</td></tr>", + self.kind) + } +} + impl ClangSubItemParser for Module { fn parse(cursor: clang::Cursor, ctx: &mut BindgenContext) diff --git a/src/ir/named.rs b/src/ir/named.rs index 7a6c597c..3c676662 100644 --- a/src/ir/named.rs +++ b/src/ir/named.rs @@ -76,11 +76,50 @@ //! fixed-point. //! //! We use the "monotone framework" for this fix-point analysis where our -//! lattice is the powerset of the template parameters that appear in the input -//! C++ header, our join function is set union, and we use the -//! `ir::traversal::Trace` trait to implement the work-list optimization so we -//! don't have to revisit every node in the graph when for every iteration -//! towards the fix-point. +//! lattice is the mapping from each IR item to the powerset of the template +//! parameters that appear in the input C++ header, our join function is set +//! union, and we use the `ir::traversal::Trace` trait to implement the +//! work-list optimization so we don't have to revisit every node in the graph +//! when for every iteration towards the fix-point. +//! +//! A lattice is a set with a partial ordering between elements, where there is +//! a single least upper bound and a single greatest least bound for every +//! subset. We are dealing with finite lattices, which means that it has a +//! finite number of elements, and it follows that there exists a single top and +//! a single bottom member of the lattice. For example, the power set of a +//! finite set forms a finite lattice where partial ordering is defined by set +//! inclusion, that is `a <= b` if `a` is a subset of `b`. Here is the finite +//! lattice constructed from the set {0,1,2}: +//! +//! ```text +//! .----- Top = {0,1,2} -----. +//! / | \ +//! / | \ +//! / | \ +//! {0,1} -------. {0,2} .--------- {1,2} +//! | \ / \ / | +//! | / \ | +//! | / \ / \ | +//! {0} --------' {1} `---------- {2} +//! \ | / +//! \ | / +//! \ | / +//! `------ Bottom = {} ------' +//! ``` +//! +//! A monotone function `f` is a function where if `x <= y`, then it holds that +//! `f(x) <= f(y)`. It should be clear that running a monotone function to a +//! fix-point on a finite lattice will always terminate: `f` can only "move" +//! along the lattice in a single direction, and therefore can only either find +//! a fix-point in the middle of the lattice or continue to the top or bottom +//! depending if it is ascending or descending the lattice respectively. +//! +//! For our analysis, we are collecting the set of template parameters used by +//! any given IR node. The set of template parameters appearing in the program +//! is finite. Our lattice is their powerset. We start at the bottom element, +//! the empty set. Our analysis only adds members to the set of used template +//! parameters, never removes them, so it is monotone, and therefore iteration +//! to a fix-point will terminate. //! //! For a deeper introduction to the general form of this kind of analysis, see //! [Static Program Analysis by Anders Møller and Michael I. Schwartzbach][spa]. @@ -173,42 +212,123 @@ pub fn analyze<Analysis>(extra: Analysis::Extra) -> Analysis::Output analysis.into() } -/// An analysis that finds the set of template parameters that actually end up -/// used in our generated bindings. +/// An analysis that finds for each IR item its set of template parameters that +/// it uses. +/// +/// We use the following monotone constraint function: +/// +/// ```ignore +/// template_param_usage(v) = +/// self_template_param_usage(v) union +/// template_param_usage(w_0) union +/// template_param_usage(w_1) union +/// ... +/// template_param_usage(w_n) +/// ``` +/// +/// Where `v` has direct edges in the IR graph to each of `w_0`, `w_1`, +/// ..., `w_n` (for example, if `v` were a struct type and `x` and `y` +/// were the types of two of `v`'s fields). We ignore certain edges, such +/// as edges from a template declaration to its template parameters' +/// definitions for this analysis. If we didn't, then we would mistakenly +/// determine that ever template parameter is always used. +/// +/// Finally, `self_template_param_usage` is defined with the following cases: +/// +/// * If `T` is a template parameter: +/// +/// ```ignore +/// self_template_param_usage(T) = { T } +/// ``` +/// +/// * If `inst` is a template instantiation, `inst.args` are the template +/// instantiation's template arguments, and `inst.decl` is the template +/// declaration being instantiated: +/// +/// ```ignore +/// self_template_param_usage(inst) = +/// { T: for T in inst.args, if T in template_param_usage(inst.decl) } +/// ``` +/// +/// * And for all other IR items, the result is the empty set: +/// +/// ```ignore +/// self_template_param_usage(_) = { } +/// ``` #[derive(Debug, Clone)] pub struct UsedTemplateParameters<'a> { ctx: &'a BindgenContext<'a>, - used: ItemSet, + used: HashMap<ItemId, ItemSet>, dependencies: HashMap<ItemId, Vec<ItemId>>, } +impl<'a> UsedTemplateParameters<'a> { + fn consider_edge(kind: EdgeKind) -> bool { + match kind { + // For each of these kinds of edges, if the referent uses a template + // parameter, then it should be considered that the origin of the + // edge also uses the template parameter. + EdgeKind::TemplateArgument | + EdgeKind::BaseMember | + EdgeKind::Field | + EdgeKind::InnerType | + EdgeKind::InnerVar | + EdgeKind::Constructor | + EdgeKind::VarType | + EdgeKind::TypeReference => true, + + // We can't emit machine code for new instantiations of function + // templates and class templates' methods (and don't detect explicit + // instantiations) so we must ignore template parameters that are + // only used by functions. + EdgeKind::Method | + EdgeKind::FunctionReturn | + EdgeKind::FunctionParameter => false, + + // If we considered these edges, we would end up mistakenly claiming + // that every template parameter always used. + EdgeKind::TemplateDeclaration | + EdgeKind::TemplateParameterDefinition => false, + + // Since we have to be careful about which edges we consider for + // this analysis to be correct, we ignore generic edges. We also + // avoid a `_` wild card to force authors of new edge kinds to + // determine whether they need to be considered by this analysis. + EdgeKind::Generic => false, + } + } +} + impl<'a> MonotoneFramework for UsedTemplateParameters<'a> { type Node = ItemId; type Extra = &'a BindgenContext<'a>; - type Output = ItemSet; + type Output = HashMap<ItemId, ItemSet>; fn new(ctx: &'a BindgenContext<'a>) -> UsedTemplateParameters<'a> { + let mut used = HashMap::new(); let mut dependencies = HashMap::new(); for item in ctx.whitelisted_items() { + dependencies.entry(item).or_insert(vec![]); + used.insert(item, ItemSet::new()); + { // We reverse our natural IR graph edges to find dependencies // between nodes. - let mut add_reverse_edge = |sub_item, _| { + item.trace(ctx, &mut |sub_item, _| { dependencies.entry(sub_item).or_insert(vec![]).push(item); - }; - item.trace(ctx, &mut add_reverse_edge, &()); + }, &()); } // Additionally, whether a template instantiation's template // arguments are used depends on whether the template declaration's // generic template parameters are used. - ctx.resolve_item_fallible(item) - .and_then(|item| item.as_type()) + ctx.resolve_item(item) + .as_type() .map(|ty| match ty.kind() { &TypeKind::TemplateInstantiation(decl, ref args) => { let decl = ctx.resolve_type(decl); - let params = decl.template_params(ctx) + let params = decl.self_template_params(ctx) .expect("a template instantiation's referenced \ template declaration should have template \ parameters"); @@ -222,57 +342,65 @@ impl<'a> MonotoneFramework for UsedTemplateParameters<'a> { UsedTemplateParameters { ctx: ctx, - used: ItemSet::new(), + used: used, dependencies: dependencies, } } - fn initial_worklist(&self) -> Vec<Self::Node> { + fn initial_worklist(&self) -> Vec<ItemId> { self.ctx.whitelisted_items().collect() } - fn constrain(&mut self, item: ItemId) -> bool { - let original_size = self.used.len(); + fn constrain(&mut self, id: ItemId) -> bool { + let original_len = self.used[&id].len(); - item.trace(self.ctx, &mut |item, edge_kind| { - if edge_kind == EdgeKind::TemplateParameterDefinition { - // The definition of a template parameter is not considered a - // use of said template parameter. Ignore this edge. - return; + // First, add this item's self template parameter usage. + let item = self.ctx.resolve_item(id); + let ty_kind = item.as_type().map(|ty| ty.kind()); + match ty_kind { + Some(&TypeKind::Named) => { + // This is a trivial use of the template type parameter. + self.used.get_mut(&id).unwrap().insert(id); } - - let ty_kind = self.ctx.resolve_item(item) - .as_type() - .map(|ty| ty.kind()); - - match ty_kind { - Some(&TypeKind::Named) => { - // This is a "trivial" use of the template type parameter. - self.used.insert(item); - }, - Some(&TypeKind::TemplateInstantiation(decl, ref args)) => { - // A template instantiation's concrete template argument is - // only used if the template declaration uses the - // corresponding template parameter. - let decl = self.ctx.resolve_type(decl); - let params = decl.template_params(self.ctx) - .expect("a template instantiation's referenced \ - template declaration should have template \ - parameters"); - for (arg, param) in args.iter().zip(params.iter()) { - if self.used.contains(param) { - if self.ctx.resolve_item(*arg).is_named() { - self.used.insert(*arg); - } + Some(&TypeKind::TemplateInstantiation(decl, ref args)) => { + // A template instantiation's concrete template argument is + // only used if the template declaration uses the + // corresponding template parameter. + let params = decl.self_template_params(self.ctx) + .expect("a template instantiation's referenced \ + template declaration should have template \ + parameters"); + for (arg, param) in args.iter().zip(params.iter()) { + if self.used[&decl].contains(param) { + if self.ctx.resolve_item(*arg).is_named() { + self.used.get_mut(&id).unwrap().insert(*arg); } } - }, - _ => return, + } } + _ => {} + } + + // Second, add the union of each of its referent item's template + // parameter usage. + item.trace(self.ctx, &mut |sub_id, edge_kind| { + if sub_id == id || !Self::consider_edge(edge_kind) { + return; + } + + // This clone is unfortunate because we are potentially thrashing + // malloc. We could investigate replacing the ItemSet values with + // Rc<RefCell<ItemSet>> to make the borrow checker happy, but it + // isn't clear that the added indirection wouldn't outweigh the cost + // of malloc'ing a new ItemSet here. Ideally, `HashMap` would have a + // `split_entries` method analogous to `slice::split_at_mut`... + let to_add = self.used[&sub_id].clone(); + self.used.get_mut(&id).unwrap().extend(to_add); }, &()); - let new_size = self.used.len(); - new_size != original_size + let new_len = self.used[&id].len(); + assert!(new_len >= original_len); + new_len != original_len } fn each_depending_on<F>(&self, item: ItemId, mut f: F) @@ -286,8 +414,8 @@ impl<'a> MonotoneFramework for UsedTemplateParameters<'a> { } } -impl<'a> From<UsedTemplateParameters<'a>> for ItemSet { - fn from(used_templ_params: UsedTemplateParameters) -> ItemSet { +impl<'a> From<UsedTemplateParameters<'a>> for HashMap<ItemId, ItemSet> { + fn from(used_templ_params: UsedTemplateParameters) -> Self { used_templ_params.used } } diff --git a/src/ir/objc.rs b/src/ir/objc.rs index b3c3688b..963c8e20 100644 --- a/src/ir/objc.rs +++ b/src/ir/objc.rs @@ -4,17 +4,24 @@ use super::context::BindgenContext; use super::function::FunctionSig; use clang; use clang_sys::CXChildVisit_Continue; +use clang_sys::CXCursor_ObjCCategoryDecl; +use clang_sys::CXCursor_ObjCClassRef; use clang_sys::CXCursor_ObjCInstanceMethodDecl; +use clang_sys::CXCursor_ObjCProtocolDecl; /// Objective C interface as used in TypeKind /// -/// Also protocols are parsed as this type +/// Also protocols and categories are parsed as this type #[derive(Debug)] pub struct ObjCInterface { /// The name /// like, NSObject name: String, + category: Option<String>, + + is_protocol: bool, + /// List of the methods defined in this interfae methods: Vec<ObjCInstanceMethod>, } @@ -37,6 +44,8 @@ impl ObjCInterface { fn new(name: &str) -> ObjCInterface { ObjCInterface { name: name.to_owned(), + category: None, + is_protocol: false, methods: Vec::new(), } } @@ -47,6 +56,21 @@ impl ObjCInterface { self.name.as_ref() } + /// Formats the name for rust + /// Can be like NSObject, but with categories might be like NSObject_NSCoderMethods + /// and protocols are like protocol_NSObject + pub fn rust_name(&self) -> String { + if let Some(ref cat) = self.category { + format!("{}_{}", self.name(), cat) + } else { + if self.is_protocol { + format!("protocol_{}", self.name()) + } else { + self.name().to_owned() + } + } + } + /// List of the methods defined in this interfae pub fn methods(&self) -> &Vec<ObjCInstanceMethod> { &self.methods @@ -59,12 +83,24 @@ impl ObjCInterface { let name = cursor.spelling(); let mut interface = Self::new(&name); - cursor.visit(|cursor| { - match cursor.kind() { + if cursor.kind() == CXCursor_ObjCProtocolDecl { + interface.is_protocol = true; + } + + cursor.visit(|c| { + match c.kind() { + CXCursor_ObjCClassRef => { + if cursor.kind() == CXCursor_ObjCCategoryDecl { + // We are actually a category extension, and we found the reference + // to the original interface, so name this interface approriately + interface.name = c.spelling(); + interface.category = Some(cursor.spelling()); + } + } CXCursor_ObjCInstanceMethodDecl => { - let name = cursor.spelling(); + let name = c.spelling(); let signature = - FunctionSig::from_ty(&cursor.cur_type(), &cursor, ctx) + FunctionSig::from_ty(&c.cur_type(), &c, ctx) .expect("Invalid function sig"); let method = ObjCInstanceMethod::new(&name, signature); diff --git a/src/ir/traversal.rs b/src/ir/traversal.rs index 8c5e32cf..30772aad 100644 --- a/src/ir/traversal.rs +++ b/src/ir/traversal.rs @@ -44,22 +44,137 @@ impl Into<ItemId> for Edge { } /// The kind of edge reference. This is useful when we wish to only consider -/// certain kinds of edges for a particular traversal. +/// certain kinds of edges for a particular traversal or analysis. #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum EdgeKind { /// A generic, catch-all edge. Generic, /// An edge from a template declaration, to the definition of a named type - /// parameter. For example, the edge Foo -> T in the following snippet: + /// parameter. For example, the edge from `Foo<T>` to `T` in the following + /// snippet: /// /// ```C++ /// template<typename T> + /// class Foo { }; + /// ``` + TemplateParameterDefinition, + + /// An edge from a template instantiation to the template declaration that + /// is being instantiated. For example, the edge from `Foo<int>` to + /// to `Foo<T>`: + /// + /// ```C++ + /// template<typename T> + /// class Foo { }; + /// + /// using Bar = Foo<int>; + /// ``` + TemplateDeclaration, + + /// An edge from a template instantiation to its template argument. For + /// example, `Foo<Bar>` to `Bar`: + /// + /// ```C++ + /// template<typename T> + /// class Foo { }; + /// + /// class Bar { }; + /// + /// using FooBar = Foo<Bar>; + /// ``` + TemplateArgument, + + /// An edge from a compound type to one of its base member types. For + /// example, the edge from `Bar` to `Foo`: + /// + /// ```C++ + /// class Foo { }; + /// + /// class Bar : public Foo { }; + /// ``` + BaseMember, + + /// An edge from a compound type to the types of one of its fields. For + /// example, the edge from `Foo` to `int`: + /// + /// ```C++ /// class Foo { /// int x; /// }; /// ``` - TemplateParameterDefinition, + Field, + + /// An edge from an class or struct type to an inner type member. For + /// example, the edge from `Foo` to `Foo::Bar` here: + /// + /// ```C++ + /// class Foo { + /// struct Bar { }; + /// }; + /// ``` + InnerType, + + /// An edge from an class or struct type to an inner static variable. For + /// example, the edge from `Foo` to `Foo::BAR` here: + /// + /// ```C++ + /// class Foo { + /// static const char* BAR; + /// }; + /// ``` + InnerVar, + + /// An edge from a class or struct type to one of its method functions. For + /// example, the edge from `Foo` to `Foo::bar`: + /// + /// ```C++ + /// class Foo { + /// bool bar(int x, int y); + /// }; + /// ``` + Method, + + /// An edge from a class or struct type to one of its constructor + /// functions. For example, the edge from `Foo` to `Foo::Foo(int x, int y)`: + /// + /// ```C++ + /// class Foo { + /// int my_x; + /// int my_y; + /// + /// public: + /// Foo(int x, int y); + /// }; + /// ``` + Constructor, + + /// An edge from a function declaration to its return type. For example, the + /// edge from `foo` to `int`: + /// + /// ```C++ + /// int foo(char* string); + /// ``` + FunctionReturn, + + /// An edge from a function declaration to one of its parameter types. For + /// example, the edge from `foo` to `char*`: + /// + /// ```C++ + /// int foo(char* string); + /// ``` + FunctionParameter, + + /// An edge from a static variable to its type. For example, the edge from + /// `FOO` to `const char*`: + /// + /// ```C++ + /// static const char* FOO; + /// ``` + VarType, + + /// An edge from a non-templated alias or typedef to the referenced type. + TypeReference, } /// A predicate to allow visiting only sub-sets of the whole IR graph by @@ -211,7 +326,8 @@ impl<F> Tracer for F } /// Trace all of the outgoing edges to other items. Implementations should call -/// `tracer.visit(edge)` for each of their outgoing edges. +/// one of `tracer.visit(edge)` or `tracer.visit_kind(edge, EdgeKind::Whatever)` +/// for each of their outgoing edges. pub trait Trace { /// If a particular type needs extra information beyond what it has in /// `self` and `context` to find its referenced items, its implementation diff --git a/src/ir/ty.rs b/src/ir/ty.rs index c3ec4039..ce42a171 100644 --- a/src/ir/ty.rs +++ b/src/ir/ty.rs @@ -3,27 +3,63 @@ use super::comp::CompInfo; use super::context::{BindgenContext, ItemId}; use super::derive::{CanDeriveCopy, CanDeriveDebug, CanDeriveDefault}; +use super::dot::DotAttributes; use super::enum_ty::Enum; use super::function::FunctionSig; use super::int::IntKind; -use super::item::Item; +use super::item::{Item, ItemAncestors}; use super::layout::Layout; use super::objc::ObjCInterface; -use super::traversal::{Trace, Tracer}; +use super::traversal::{EdgeKind, Trace, Tracer}; use clang::{self, Cursor}; use parse::{ClangItemParser, ParseError, ParseResult}; +use std::io; use std::mem; -/// Template declaration related methods. +/// Template declaration (and such declaration's template parameters) related +/// methods. +/// +/// Consider this example: +/// +/// ```c++ +/// template <typename T, typename U> +/// class Foo { +/// template <typename V> +/// using Bar = V*; +/// +/// class Inner { +/// T x; +/// U y; +/// Bar<int> z; +/// }; +/// }; +/// +/// class Qux { +/// int y; +/// }; +/// ``` +/// +/// The following table depicts the results of each trait method when invoked on +/// `Foo`, `Bar`, and `Qux`. +/// +/// +------+----------------------+--------------------------+------------------------+ +/// |Decl. | self_template_params | num_self_template_params | all_template_parameters| +/// +------+----------------------+--------------------------+------------------------+ +/// |Foo | Some([T, U]) | Some(2) | Some([T, U]) | +/// |Bar | Some([V]) | Some(1) | Some([T, U, V]) | +/// |Inner | None | None | Some([T, U]) | +/// |Qux | None | None | None | +/// +------+----------------------+--------------------------+------------------------+ pub trait TemplateDeclaration { /// Get the set of `ItemId`s that make up this template declaration's free /// template parameters. /// /// Note that these might *not* all be named types: C++ allows - /// constant-value template parameters. Of course, Rust does not allow - /// generic parameters to be anything but types, so we must treat them as - /// opaque, and avoid instantiating them. - fn template_params(&self, ctx: &BindgenContext) -> Option<Vec<ItemId>>; + /// constant-value template parameters as well as template-template + /// parameters. Of course, Rust does not allow generic parameters to be + /// anything but types, so we must treat them as opaque, and avoid + /// instantiating them. + fn self_template_params(&self, ctx: &BindgenContext) -> Option<Vec<ItemId>>; /// Get the number of free template parameters this template declaration /// has. @@ -32,8 +68,38 @@ pub trait TemplateDeclaration { /// `template_params` returns `None`. This is useful when we only have /// partial information about the template declaration, such as when we are /// in the middle of parsing it. - fn num_template_params(&self, ctx: &BindgenContext) -> Option<usize> { - self.template_params(ctx).map(|params| params.len()) + fn num_self_template_params(&self, ctx: &BindgenContext) -> Option<usize> { + self.self_template_params(ctx).map(|params| params.len()) + } + + /// Get the complete set of template parameters that can affect this + /// declaration. + /// + /// Note that this item doesn't need to be a template declaration itself for + /// `Some` to be returned here (in contrast to `self_template_params`). If + /// this item is a member of a template declaration, then the parent's + /// template parameters are included here. + /// + /// In the example above, `Inner` depends on both of the `T` and `U` type + /// parameters, even though it is not itself a template declaration and + /// therefore has no type parameters itself. Perhaps it helps to think about + /// how we would fully reference such a member type in C++: + /// `Foo<int,char>::Inner`. `Foo` *must* be instantiated with template + /// arguments before we can gain access to the `Inner` member type. + fn all_template_params(&self, ctx: &BindgenContext) -> Option<Vec<ItemId>> + where Self: ItemAncestors + { + let each_self_params: Vec<Vec<_>> = self.ancestors(ctx) + .filter_map(|id| id.self_template_params(ctx)) + .collect(); + if each_self_params.is_empty() { + None + } else { + Some(each_self_params.into_iter() + .rev() + .flat_map(|params| params) + .collect()) + } } } @@ -356,6 +422,8 @@ impl Type { TypeKind::NullPtr | TypeKind::BlockPointer | TypeKind::Pointer(..) | + TypeKind::ObjCId | + TypeKind::ObjCSel | TypeKind::ObjCInterface(..) => Some(self), TypeKind::ResolvedTypeRef(inner) | @@ -384,6 +452,63 @@ impl Type { } } +impl DotAttributes for Type { + fn dot_attributes<W>(&self, ctx: &BindgenContext, out: &mut W) -> io::Result<()> + where W: io::Write + { + if let Some(ref layout) = self.layout { + try!(writeln!(out, + "<tr><td>size</td><td>{}</td></tr> + <tr><td>align</td><td>{}</td></tr>", + layout.size, + layout.align)); + if layout.packed { + try!(writeln!(out, "<tr><td>packed</td><td>true</td></tr>")); + } + } + + if self.is_const { + try!(writeln!(out, "<tr><td>const</td><td>true</td></tr>")); + } + + self.kind.dot_attributes(ctx, out) + } +} + +impl DotAttributes for TypeKind { + fn dot_attributes<W>(&self, _ctx: &BindgenContext, out: &mut W) -> io::Result<()> + where W: io::Write + { + write!(out, + "<tr><td>TypeKind</td><td>{}</td></tr>", + match *self { + TypeKind::Void => "Void", + TypeKind::NullPtr => "NullPtr", + TypeKind::Comp(..) => "Comp", + TypeKind::Int(..) => "Int", + TypeKind::Float(..) => "Float", + TypeKind::Complex(..) => "Complex", + TypeKind::Alias(..) => "Alias", + TypeKind::TemplateAlias(..) => "TemplateAlias", + TypeKind::Array(..) => "Array", + TypeKind::Function(..) => "Function", + TypeKind::Enum(..) => "Enum", + TypeKind::Pointer(..) => "Pointer", + TypeKind::BlockPointer => "BlockPointer", + TypeKind::Reference(..) => "Reference", + TypeKind::TemplateInstantiation(..) => "TemplateInstantiation", + TypeKind::ResolvedTypeRef(..) => "ResolvedTypeRef", + TypeKind::Named => "Named", + TypeKind::ObjCId => "ObjCId", + TypeKind::ObjCSel => "ObjCSel", + TypeKind::ObjCInterface(..) => "ObjCInterface", + TypeKind::UnresolvedTypeRef(..) => { + unreachable!("there shouldn't be any more of these anymore") + } + }) + } +} + #[test] fn is_invalid_named_type_valid() { let ty = Type::new(Some("foo".into()), None, TypeKind::Named, false); @@ -430,18 +555,18 @@ fn is_invalid_named_type_empty_name() { impl TemplateDeclaration for Type { - fn template_params(&self, ctx: &BindgenContext) -> Option<Vec<ItemId>> { - self.kind.template_params(ctx) + fn self_template_params(&self, ctx: &BindgenContext) -> Option<Vec<ItemId>> { + self.kind.self_template_params(ctx) } } impl TemplateDeclaration for TypeKind { - fn template_params(&self, ctx: &BindgenContext) -> Option<Vec<ItemId>> { + fn self_template_params(&self, ctx: &BindgenContext) -> Option<Vec<ItemId>> { match *self { TypeKind::ResolvedTypeRef(id) => { - ctx.resolve_type(id).template_params(ctx) + ctx.resolve_type(id).self_template_params(ctx) } - TypeKind::Comp(ref comp) => comp.template_params(ctx), + TypeKind::Comp(ref comp) => comp.self_template_params(ctx), TypeKind::TemplateAlias(_, ref args) => Some(args.clone()), TypeKind::TemplateInstantiation(..) | @@ -459,6 +584,8 @@ impl TemplateDeclaration for TypeKind { TypeKind::UnresolvedTypeRef(..) | TypeKind::Named | TypeKind::Alias(_) | + TypeKind::ObjCId | + TypeKind::ObjCSel | TypeKind::ObjCInterface(_) => None, } } @@ -505,6 +632,8 @@ impl CanDeriveDefault for Type { TypeKind::NullPtr | TypeKind::Pointer(..) | TypeKind::BlockPointer | + TypeKind::ObjCId | + TypeKind::ObjCSel | TypeKind::ObjCInterface(..) | TypeKind::Enum(..) => false, TypeKind::Function(..) | @@ -650,6 +779,12 @@ pub enum TypeKind { /// Objective C interface. Always referenced through a pointer ObjCInterface(ObjCInterface), + + /// Objective C 'id' type, points to any object + ObjCId, + + /// Objective C selector type + ObjCSel, } impl Type { @@ -681,6 +816,8 @@ impl Type { TypeKind::Reference(..) | TypeKind::NullPtr | TypeKind::BlockPointer | + TypeKind::ObjCId | + TypeKind::ObjCSel | TypeKind::Pointer(..) => false, TypeKind::ObjCInterface(..) => true, // dunno? @@ -730,8 +867,10 @@ impl Type { // Parse objc protocols as if they were interfaces let mut ty_kind = ty.kind(); if let Some(loc) = location { - if loc.kind() == CXCursor_ObjCProtocolDecl { - ty_kind = CXType_ObjCInterface; + match loc.kind() { + CXCursor_ObjCProtocolDecl | + CXCursor_ObjCCategoryDecl => ty_kind = CXType_ObjCInterface, + _ => {} } } @@ -1089,6 +1228,9 @@ impl Type { parent_id, ctx); } + CXType_ObjCId => TypeKind::ObjCId, + CXType_ObjCSel => TypeKind::ObjCSel, + CXType_ObjCClass | CXType_ObjCInterface => { let interface = ObjCInterface::from_ty(&location.unwrap(), ctx) .expect("Not a valid objc interface?"); @@ -1124,14 +1266,18 @@ impl Trace for Type { TypeKind::Array(inner, _) | TypeKind::Alias(inner) | TypeKind::ResolvedTypeRef(inner) => { - tracer.visit(inner); + tracer.visit_kind(inner, EdgeKind::TypeReference); + } + TypeKind::TemplateAlias(inner, ref template_params) => { + tracer.visit_kind(inner, EdgeKind::TypeReference); + for &item in template_params { + tracer.visit_kind(item, EdgeKind::TemplateParameterDefinition); + } } - - TypeKind::TemplateAlias(inner, ref template_args) | TypeKind::TemplateInstantiation(inner, ref template_args) => { - tracer.visit(inner); + tracer.visit_kind(inner, EdgeKind::TemplateDeclaration); for &item in template_args { - tracer.visit(item); + tracer.visit_kind(item, EdgeKind::TemplateArgument); } } TypeKind::Comp(ref ci) => ci.trace(context, tracer, item), @@ -1157,6 +1303,8 @@ impl Trace for Type { TypeKind::Int(_) | TypeKind::Float(_) | TypeKind::Complex(_) | + TypeKind::ObjCId | + TypeKind::ObjCSel | TypeKind::BlockPointer => {} } } diff --git a/src/ir/var.rs b/src/ir/var.rs index 6cfcdae7..7b610da4 100644 --- a/src/ir/var.rs +++ b/src/ir/var.rs @@ -1,6 +1,7 @@ //! Intermediate representation of variables. use super::context::{BindgenContext, ItemId}; +use super::dot::DotAttributes; use super::function::cursor_mangling; use super::int::IntKind; use super::item::Item; @@ -8,6 +9,7 @@ use super::ty::{FloatKind, TypeKind}; use cexpr; use clang; use parse::{ClangItemParser, ClangSubItemParser, ParseError, ParseResult}; +use std::io; use std::num::Wrapping; /// The type for a constant variable. @@ -84,6 +86,22 @@ impl Var { } } +impl DotAttributes for Var { + fn dot_attributes<W>(&self, _ctx: &BindgenContext, out: &mut W) -> io::Result<()> + where W: io::Write + { + if self.is_const { + try!(writeln!(out, "<tr><td>const</td><td>true</td></tr>")); + } + + if let Some(ref mangled) = self.mangled_name { + try!(writeln!(out, "<tr><td>mangled name</td><td>{}</td></tr>", mangled)); + } + + Ok(()) + } +} + impl ClangSubItemParser for Var { fn parse(cursor: clang::Cursor, ctx: &mut BindgenContext) @@ -238,7 +256,7 @@ impl ClangSubItemParser for Var { .map(VarType::String) }; - let mangling = cursor_mangling(&cursor); + let mangling = cursor_mangling(ctx, &cursor); let var = Var::new(name, mangling, ty, value, is_const); Ok(ParseResult::New(var, Some(cursor))) @@ -212,6 +212,18 @@ impl Builder { self } + /// Whether to use the clang-provided name mangling. This is true and + /// probably needed for C++ features. + /// + /// However, some old libclang versions seem to return incorrect results in + /// some cases for non-mangled functions, see [1], so we allow disabling it. + /// + /// [1]: https://github.com/servo/rust-bindgen/issues/528 + pub fn trust_clang_mangling(mut self, doit: bool) -> Self { + self.options.enable_mangling = doit; + self + } + /// Generate a C/C++ file that includes the header and has dummy uses of /// every type defined in the header. pub fn dummy_uses<T: Into<String>>(mut self, dummy_uses: T) -> Builder { @@ -572,6 +584,15 @@ pub struct BindgenOptions { /// Intead of emitting 'use objc;' to files generated from objective c files, /// generate '#[macro_use] extern crate objc;' pub objc_extern_crate: bool, + + /// Whether to use the clang-provided name mangling. This is true and + /// probably needed for C++ features. + /// + /// However, some old libclang versions seem to return incorrect results in + /// some cases for non-mangled functions, see [1], so we allow disabling it. + /// + /// [1]: https://github.com/servo/rust-bindgen/issues/528 + pub enable_mangling: bool, } /// TODO(emilio): This is sort of a lie (see the error message that results from @@ -626,6 +647,7 @@ impl Default for BindgenOptions { generate_comments: true, whitelist_recursively: true, objc_extern_crate: false, + enable_mangling: true, } } } diff --git a/src/options.rs b/src/options.rs index e54ee012..a62aa73d 100644 --- a/src/options.rs +++ b/src/options.rs @@ -60,6 +60,9 @@ pub fn builder_from_flags<I> Arg::with_name("objc-extern-crate") .long("objc-extern-crate") .help("Use extern crate instead of use for objc"), + Arg::with_name("distrust-clang-mangling") + .long("distrust-clang-mangling") + .help("Do not trust the libclang-provided mangling"), Arg::with_name("builtins") .long("builtins") .help("Output bindings for builtin definitions, e.g. \ |