diff options
Diffstat (limited to 'src/ir')
-rw-r--r-- | src/ir/annotations.rs | 141 | ||||
-rw-r--r-- | src/ir/comp.rs | 718 | ||||
-rw-r--r-- | src/ir/context.rs | 685 | ||||
-rw-r--r-- | src/ir/enum_ty.rs | 110 | ||||
-rw-r--r-- | src/ir/function.rs | 220 | ||||
-rw-r--r-- | src/ir/int.rs | 30 | ||||
-rw-r--r-- | src/ir/item.rs | 681 | ||||
-rw-r--r-- | src/ir/item_kind.rs | 89 | ||||
-rw-r--r-- | src/ir/layout.rs | 26 | ||||
-rw-r--r-- | src/ir/mod.rs | 12 | ||||
-rw-r--r-- | src/ir/module.rs | 52 | ||||
-rw-r--r-- | src/ir/ty.rs | 537 | ||||
-rw-r--r-- | src/ir/var.rs | 160 |
13 files changed, 3461 insertions, 0 deletions
diff --git a/src/ir/annotations.rs b/src/ir/annotations.rs new file mode 100644 index 00000000..cc61dbfd --- /dev/null +++ b/src/ir/annotations.rs @@ -0,0 +1,141 @@ +use clang; + +#[derive(Copy, PartialEq, Clone, Debug)] +pub enum FieldAccessorKind { + None, + Regular, + Unsafe, + Immutable, +} + +/// Annotations for a given item, or a field. +#[derive(Clone, PartialEq, Debug)] +pub struct Annotations { + /// Whether this item is marked as opaque. Only applies to types. + opaque: bool, + /// Whether this item should be hidden from the output. Only applies to + /// types. + hide: bool, + /// Whether this type should be replaced by another. The name must be the + /// canonical name that that type would get. + use_instead_of: Option<String>, + /// Manually disable deriving copy/clone on this type. Only applies to + /// struct or union types. + disallow_copy: bool, + /// Whether fields should be marked as private or not. You can set this on + /// structs (it will apply to all the fields), or individual fields. + private_fields: Option<bool>, + /// The kind of accessor this field will have. Also can be applied to + /// structs so all the fields inside share it by default. + accessor_kind: Option<FieldAccessorKind>, +} + +fn parse_accessor(s: &str) -> FieldAccessorKind { + match s { + "false" => FieldAccessorKind::None, + "unsafe" => FieldAccessorKind::Unsafe, + "immutable" => FieldAccessorKind::Immutable, + _ => FieldAccessorKind::Regular, + } +} + +impl Default for Annotations { + fn default() -> Self { + Annotations { + opaque: false, + hide: false, + use_instead_of: None, + disallow_copy: false, + private_fields: None, + accessor_kind: None + } + } +} + +impl Annotations { + pub fn new(cursor: &clang::Cursor) -> Option<Annotations> { + let mut anno = Annotations::default(); + let mut matched_one = false; + anno.parse(&cursor.comment(), &mut matched_one); + + if matched_one { + Some(anno) + } else { + None + } + } + + pub fn hide(&self) -> bool { + self.hide + } + + pub fn opaque(&self) -> bool { + self.opaque + } + + /// For a given type, indicates the type it should replace. + /// + /// For example, in the following code: + /// + /// ```cpp + /// + /// /** <div rustbindgen replaces="Bar"></div> */ + /// struct Foo { int x; }; + /// + /// struct Bar { char foo; }; + /// ``` + /// + /// the generated code would look something like: + /// + /// ```rust + /// /** <div rustbindgen replaces="Bar"></div> */ + /// struct Bar { + /// int x; + /// }; + /// ``` + /// + /// That is, code for `Foo` is used to generate `Bar`. + pub fn use_instead_of(&self) -> Option<&str> { + self.use_instead_of.as_ref().map(|s| &**s) + } + + pub fn disallow_copy(&self) -> bool { + self.disallow_copy + } + + pub fn private_fields(&self) -> Option<bool> { + self.private_fields + } + + pub fn accessor_kind(&self) -> Option<FieldAccessorKind> { + self.accessor_kind + } + + fn parse(&mut self, comment: &clang::Comment, matched: &mut bool) { + use clangll::CXComment_HTMLStartTag; + if comment.kind() == CXComment_HTMLStartTag && + comment.get_tag_name() == "div" && + comment.get_num_tag_attrs() > 1 && + comment.get_tag_attr_name(0) == "rustbindgen" { + *matched = true; + for i in 0..comment.get_num_tag_attrs() { + let value = comment.get_tag_attr_value(i); + let name = comment.get_tag_attr_name(i); + match name.as_str() { + "opaque" => self.opaque = true, + "hide" => self.hide = true, + "nocopy" => self.disallow_copy = true, + "replaces" => self.use_instead_of = Some(value), + "private" => self.private_fields = Some(value != "false"), + "accessor" + => self.accessor_kind = Some(parse_accessor(&value)), + _ => {}, + } + } + } + + for i in 0..comment.num_children() { + self.parse(&comment.get_child(i), matched); + } + } +} diff --git a/src/ir/comp.rs b/src/ir/comp.rs new file mode 100644 index 00000000..1e6bf555 --- /dev/null +++ b/src/ir/comp.rs @@ -0,0 +1,718 @@ +use super::annotations::Annotations; +use super::context::BindgenContext; +use super::context::TypeResolver; +use super::layout::Layout; +use super::item::{Item, ItemId}; +use super::ty::{Type, RUST_DERIVE_IN_ARRAY_LIMIT}; +use std::cell::Cell; +use std::cmp; +use parse::{ClangItemParser, ParseError}; +use clang; + +#[derive(Debug, Copy, Clone, PartialEq)] +pub enum CompKind { + Struct, + Union, +} + +#[derive(Debug, Copy, Clone, PartialEq)] +pub enum MethodKind { + Static, + Normal, + Virtual, +} + +/// A struct representing a C++ method, either static, normal, or virtual. +#[derive(Debug)] +pub struct Method { + kind: MethodKind, + /// The signature of the method. Take into account this is not a `Type` + /// item, but a `Function` one. + /// + /// This is tricky and probably this field should be renamed. + signature: ItemId, + is_const: bool, +} + +impl Method { + fn new(kind: MethodKind, signature: ItemId, is_const: bool) -> Self { + Method { + kind: kind, + signature: signature, + is_const: is_const, + } + } + + pub fn kind(&self) -> MethodKind { + self.kind + } + + pub fn is_virtual(&self) -> bool { + self.kind == MethodKind::Virtual + } + + pub fn is_static(&self) -> bool { + self.kind == MethodKind::Static + } + + pub fn signature(&self) -> ItemId { + self.signature + } + + pub fn is_const(&self) -> bool { + self.is_const + } +} + +/// A struct representing a C++ field. +#[derive(Clone, Debug)] +pub struct Field { + /// The name of the field, empty if it's an unnamed bitfield width. + name: Option<String>, + /// The inner type. + ty: ItemId, + /// The doc comment on the field if any. + comment: Option<String>, + /// Annotations for this field, or the default. + annotations: Annotations, + /// If this field is a bitfield, and how many bits does it contain if it is. + bitfield: Option<u32>, + /// If the C++ field is marked as `mutable` + mutable: bool, +} + +impl Field { + pub fn new(name: Option<String>, + ty: ItemId, + comment: Option<String>, + annotations: Option<Annotations>, + bitfield: Option<u32>, + mutable: bool) -> Field { + Field { + name: name, + ty: ty, + comment: comment, + annotations: annotations.unwrap_or_default(), + bitfield: bitfield, + mutable: mutable, + } + } + + pub fn name(&self) -> Option<&str> { + self.name.as_ref().map(|n| &**n) + } + + pub fn ty(&self) -> ItemId { + self.ty + } + + pub fn comment(&self) -> Option<&str> { + self.comment.as_ref().map(|c| &**c) + } + + pub fn bitfield(&self) -> Option<u32> { + self.bitfield + } + + pub fn is_mutable(&self) -> bool { + self.mutable + } + + pub fn annotations(&self) -> &Annotations { + &self.annotations + } +} + + +#[derive(Debug)] +pub struct CompInfo { + /// Whether this is a struct or a union. + kind: CompKind, + /// The members of this struct or union. + fields: Vec<Field>, + /// The template parameters of this class. These are non-concrete, and + /// should always be a Type(TypeKind::Named(name)), but still they need to + /// be registered with an unique type id in the context. + template_args: Vec<ItemId>, + /// The method declarations inside this class, if in C++ mode. + methods: Vec<Method>, + /// Vector of classes this one inherits from. + base_members: Vec<ItemId>, + /// The parent reference template if any. + ref_template: Option<ItemId>, + /// The inner types that were declared inside this class, in something like: + /// + /// class Foo { + /// typedef int FooTy; + /// struct Bar { + /// int baz; + /// }; + /// } + /// + /// static Foo::Bar const = {3}; + inner_types: Vec<ItemId>, + /// Set of static constants declared inside this class. + inner_vars: Vec<ItemId>, + /// Whether this type should generate an vtable (TODO: Should be able to + /// look at the virtual methods and ditch this field). + has_vtable: bool, + /// Whether this type has destructor. + has_destructor: bool, + /// Whether this type has a base type with more than one member. + /// + /// TODO: We should be able to compute this. + has_nonempty_base: bool, + /// If this type has a template parameter which is not a type (e.g.: a size_t) + has_non_type_template_params: bool, + /// Whether this struct layout is packed. + packed: bool, + /// Whether this struct is anonymous. + is_anonymous: bool, + /// Used to know if we've found an opaque attribute that could cause us to + /// generate a type with invalid layout. This is explicitly used to avoid us + /// generating bad alignments when parsing types like max_align_t. + /// + /// It's not clear what the behavior should be here, if generating the item + /// and pray, or behave as an opaque type. + found_unknown_attr: bool, + /// Used to detect if we've run in a can_derive_debug cycle while cycling + /// around the template arguments. + detect_derive_debug_cycle: Cell<bool>, + /// Used to detect if we've run in a has_destructor cycle while cycling + /// around the template arguments. + detect_has_destructor_cycle: Cell<bool>, +} + +impl CompInfo { + pub fn new(kind: CompKind) -> Self { + CompInfo { + kind: kind, + fields: vec![], + template_args: vec![], + methods: vec![], + base_members: vec![], + ref_template: None, + inner_types: vec![], + inner_vars: vec![], + has_vtable: false, + has_destructor: false, + has_nonempty_base: false, + has_non_type_template_params: false, + packed: false, + is_anonymous: false, + found_unknown_attr: false, + detect_derive_debug_cycle: Cell::new(false), + detect_has_destructor_cycle: Cell::new(false), + } + } + + pub fn can_derive_debug(&self, type_resolver: &TypeResolver, layout: Option<Layout>) -> bool { + // We can reach here recursively via template parameters of a member, + // for example. + if self.detect_derive_debug_cycle.get() { + warn!("Derive debug cycle detected!"); + return true; + } + + if self.kind == CompKind::Union { + let layout = layout.unwrap_or_else(Layout::zero); + let size_divisor = cmp::max(1, layout.align); + return layout.size / size_divisor <= RUST_DERIVE_IN_ARRAY_LIMIT; + } + + self.detect_derive_debug_cycle.set(true); + + let can_derive_debug = + self.template_args.iter().all(|ty| { + type_resolver.resolve_type(*ty) + .can_derive_debug(type_resolver) + }) && + self.fields.iter().all(|field| { + type_resolver.resolve_type(field.ty) + .can_derive_debug(type_resolver) + }); + + self.detect_derive_debug_cycle.set(false); + + can_derive_debug + } + + pub fn is_unsized(&self, type_resolver: &TypeResolver) -> bool { + !self.has_vtable(type_resolver) && self.fields.is_empty() && + self.base_members.iter().all(|base| { + type_resolver + .resolve_type(*base) + .canonical_type(type_resolver) + .is_unsized(type_resolver) + }) && + self.ref_template.map_or(true, |template| { + type_resolver.resolve_type(template).is_unsized(type_resolver) + }) + } + + pub fn has_destructor(&self, type_resolver: &TypeResolver) -> bool { + if self.detect_has_destructor_cycle.get() { + warn!("Cycle detected looking for destructors"); + // Assume no destructor, since we don't have an explicit one. + return false; + } + + self.detect_has_destructor_cycle.set(true); + + let has_destructor = self.has_destructor || match self.kind { + CompKind::Union => false, + CompKind::Struct => { + // NB: We can't rely on a type with type parameters + // not having destructor. + // + // This is unfortunate, but... + self.ref_template.as_ref().map_or(false, |t| { + type_resolver.resolve_type(*t).has_destructor(type_resolver) + }) || + self.template_args.iter().any(|t| { + type_resolver.resolve_type(*t).has_destructor(type_resolver) + }) || + self.base_members.iter().any(|t| { + type_resolver.resolve_type(*t).has_destructor(type_resolver) + }) || + self.fields.iter().any(|field| { + type_resolver.resolve_type(field.ty) + .has_destructor(type_resolver) + }) + } + }; + + self.detect_has_destructor_cycle.set(false); + + has_destructor + } + + pub fn can_derive_copy(&self, type_resolver: &TypeResolver) -> bool { + // NOTE: Take into account that while unions in C and C++ are copied by + // default, the may have an explicit destructor in C++, so we can't + // defer this check just for the union case. + if self.has_destructor(type_resolver) { + return false; + } + + match self.kind { + CompKind::Union => true, + CompKind::Struct => { + // With template args, use a safe subset of the types, + // since copyability depends on the types itself. + self.ref_template.as_ref().map_or(true, |t| { + type_resolver.resolve_type(*t).can_derive_copy(type_resolver) + }) && + self.base_members.iter().all(|t| { + type_resolver.resolve_type(*t).can_derive_copy(type_resolver) + }) && + self.fields.iter().all(|field| { + type_resolver.resolve_type(field.ty) + .can_derive_copy(type_resolver) + }) + } + } + } + + pub fn is_template_specialization(&self) -> bool { + self.ref_template.is_some() + } + + pub fn specialized_template(&self) -> Option<ItemId> { + self.ref_template + } + + // Computes the layout of this type. + // + // This is called as a fallback under some circumstances where LLVM doesn't + // give us the correct layout. + // If we're a union without known layout, we try to compute it from our + // members. This is not ideal, but clang fails to report the size for + // these kind of unions, see test/headers/template_union.hpp + pub fn layout(&self, type_resolver: &TypeResolver) -> Option<Layout> { + use std::cmp; + + // We can't do better than clang here, sorry. + if self.kind == CompKind::Struct { + return None; + } + + let mut max_size = 0; + let mut max_align = 0; + for field in &self.fields { + let field_layout = type_resolver.resolve_type(field.ty) + .layout(type_resolver); + + if let Some(layout) = field_layout { + max_size = cmp::max(max_size, layout.size); + max_align = cmp::max(max_align, layout.align); + } + } + + Some(Layout::new(max_size, max_align)) + } + + pub fn fields(&self) -> &[Field] { + &self.fields + } + + pub fn template_args(&self) -> &[ItemId] { + &self.template_args + } + + pub fn has_non_type_template_params(&self) -> bool { + self.has_non_type_template_params + } + + pub fn has_vtable(&self, type_resolver: &TypeResolver) -> bool { + self.has_vtable || self.base_members().iter().any(|base| { + type_resolver + .resolve_type(*base) + .has_vtable(type_resolver) + }) || self.ref_template.map_or(false, |template| { + type_resolver.resolve_type(template).has_vtable(type_resolver) + }) + } + + pub fn methods(&self) -> &[Method] { + &self.methods + } + + pub fn kind(&self) -> CompKind { + self.kind + } + + pub fn base_members(&self) -> &[ItemId] { + &self.base_members + } + + pub fn from_ty(potential_id: ItemId, + ty: &clang::Type, + location: Option<clang::Cursor>, + ctx: &mut BindgenContext) -> Result<Self, ParseError> { + use clangll::*; + // Sigh... For class templates we want the location, for + // specialisations, we want the declaration... So just try both. + // + // TODO: Yeah, this code reads really bad. + let mut cursor = ty.declaration(); + let mut kind = Self::kind_from_cursor(&cursor); + if kind.is_err() { + if let Some(location) = location { + kind = Self::kind_from_cursor(&location); + cursor = location; + } + } + + let kind = try!(kind); + + debug!("CompInfo::from_ty({:?}, {:?})", kind, cursor); + + + let mut ci = CompInfo::new(kind); + ci.is_anonymous = cursor.is_anonymous(); + ci.template_args = match ty.num_template_args() { + // In forward declarations and not specializations, etc, they are in + // the ast, we'll meet them in CXCursor_TemplateTypeParameter + -1 => vec![], + len => { + let mut list = Vec::with_capacity(len as usize); + for i in 0..len { + let arg_type = ty.template_arg_type(i); + if arg_type.kind() != CXType_Invalid { + let type_id = + Item::from_ty_or_ref(arg_type, None, None, ctx); + + list.push(type_id); + } else { + ci.has_non_type_template_params = true; + warn!("warning: Template parameter is not a type"); + } + } + + list + } + }; + + ci.ref_template = Item::parse(cursor.specialized(), None, ctx).ok(); + + let mut maybe_anonymous_struct_field = None; + cursor.visit(|cur, _other| { + if cur.kind() != CXCursor_FieldDecl { + if let Some((ty, ref _clang_ty)) = maybe_anonymous_struct_field { + let field = Field::new(None, ty, None, None, None, false); + ci.fields.push(field); + } + maybe_anonymous_struct_field = None; + } + + match cur.kind() { + CXCursor_FieldDecl => { + match maybe_anonymous_struct_field.take() { + Some((ty, clang_ty)) => { + let mut used = false; + cur.visit(|child, _| { + if child.cur_type() == clang_ty { + used = true; + } + CXChildVisit_Continue + }); + if !used { + let field = Field::new(None, ty, None, None, None, false); + ci.fields.push(field); + } + }, + None => {} + } + + let bit_width = cur.bit_width(); + let field_type = + Item::from_ty_or_ref(cur.cur_type(), Some(*cur), Some(potential_id), ctx); + let comment = cur.raw_comment(); + let annotations = Annotations::new(cur); + let name = cur.spelling(); + let is_mutable = cursor.is_mutable_field(); + + // Name can be empty if there are bitfields, for example, + // see tests/headers/struct_with_bitfields.h + assert!(!name.is_empty() || bit_width.is_some(), + "Empty field name?"); + + let name = if name.is_empty() { None } else { Some(name) }; + + let field = Field::new(name, field_type, comment, + annotations, bit_width, is_mutable); + ci.fields.push(field); + + // No we look for things like attributes and stuff. + cur.visit(|cur, _| { + if cur.kind() == CXCursor_UnexposedAttr { + ci.found_unknown_attr = true; + } + CXChildVisit_Continue + }); + + } + CXCursor_UnexposedAttr => { + ci.found_unknown_attr = true; + } + CXCursor_EnumDecl | + CXCursor_TypeAliasDecl | + CXCursor_TypedefDecl | + CXCursor_StructDecl | + CXCursor_UnionDecl | + CXCursor_ClassTemplate | + CXCursor_ClassDecl => { + let inner = Item::parse(*cur, Some(potential_id), ctx) + .expect("Inner ClassDecl"); + if !ci.inner_types.contains(&inner) { + ci.inner_types.push(inner); + } + // A declaration of an union or a struct without name could + // also be an unnamed field, unfortunately. + if cur.spelling().is_empty() && cur.kind() != CXCursor_EnumDecl { + maybe_anonymous_struct_field = Some((inner, cur.cur_type())); + } + } + CXCursor_PackedAttr => { + ci.packed = true; + } + CXCursor_TemplateTypeParameter => { + // Yes! You can arrive here with an empty template parameter + // name! Awesome, isn't it? + // + // see tests/headers/empty_template_param_name.hpp + if cur.spelling().is_empty() { + return CXChildVisit_Continue; + } + + let default_type = + Item::from_ty(&cur.cur_type(), Some(*cur), Some(potential_id), ctx).ok(); + + let param = Item::named_type(cur.spelling(), default_type, potential_id, ctx); + ci.template_args.push(param); + } + CXCursor_CXXBaseSpecifier => { + if !ci.has_vtable { + ci.has_vtable = cur.is_virtual_base(); + } + let type_id = Item::from_ty(&cur.cur_type(), None, None, ctx) + .expect("BaseSpecifier"); + ci.base_members.push(type_id); + } + CXCursor_CXXMethod => { + let is_virtual = cur.method_is_virtual(); + let is_static = cur.method_is_static(); + debug_assert!(!(is_static && is_virtual), "How?"); + + if !ci.has_vtable { + ci.has_vtable = is_virtual; + } + + let linkage = cur.linkage(); + if linkage != CXLinkage_External { + return CXChildVisit_Continue; + } + + if cur.access_specifier() == CX_CXXPrivate { + return CXChildVisit_Continue; + } + + let visibility = cur.visibility(); + if visibility != CXVisibility_Default { + return CXChildVisit_Continue; + } + + if cur.is_inlined_function() { + return CXChildVisit_Continue; + } + + let spelling = cur.spelling(); + if spelling.starts_with("operator") { + return CXChildVisit_Continue; + } + + // This used to not be here, but then I tried generating + // stylo bindings with this (without path filters), and + // cried a lot with a method in gfx/Point.h + // (ToUnknownPoint), that somehow was causing the same type + // to be inserted in the map two times. + // + // I couldn't make a reduced test case, but anyway... + // Methods of template functions not only use to be inlined, + // but also instantiated, and we wouldn't be able to call + // them, so just bail out. + if !ci.template_args.is_empty() { + return CXChildVisit_Continue; + } + + // NB: This gets us an owned `Function`, not a `FunctionSig`. + let method_signature = Item::parse(*cur, Some(potential_id), ctx) + .expect("CXXMethod"); + let is_const = cur.method_is_const(); + let method_kind = if is_static { + MethodKind::Static + } else if is_virtual { + MethodKind::Virtual + } else { + MethodKind::Normal + }; + ci.methods.push(Method::new(method_kind, method_signature, is_const)); + } + CXCursor_Destructor => { + if cur.method_is_virtual() { + // FIXME: Push to the method list? + ci.has_vtable = true; + } + ci.has_destructor = true; + } + CXCursor_NonTypeTemplateParameter => { + ci.has_non_type_template_params = true; + } + CXCursor_VarDecl => { + let linkage = cur.linkage(); + if linkage != CXLinkage_External && linkage != CXLinkage_UniqueExternal { + return CXChildVisit_Continue; + } + + let visibility = cur.visibility(); + if visibility != CXVisibility_Default { + return CXChildVisit_Continue; + } + + let item = Item::parse(*cur, Some(potential_id), ctx) + .expect("VarDecl"); + ci.inner_vars.push(item); + } + // Intentionally not handled + CXCursor_CXXAccessSpecifier | + CXCursor_CXXFinalAttr | + CXCursor_Constructor | + CXCursor_FunctionTemplate | + CXCursor_ConversionFunction => {} + _ => { + warn!("unhandled composite member `{}` (kind {}) in `{}` ({})", + cur.spelling(), cur.kind(), cursor.spelling(), + cur.location()); + } + } + CXChildVisit_Continue + }); + + if let Some((ty, _)) = maybe_anonymous_struct_field { + let field = Field::new(None, ty, None, None, None, false); + ci.fields.push(field); + } + + Ok(ci) + } + + fn kind_from_cursor(cursor: &clang::Cursor) -> Result<CompKind, ParseError> { + use clangll::*; + Ok(match cursor.kind() { + CXCursor_UnionDecl => CompKind::Union, + CXCursor_ClassDecl | + CXCursor_StructDecl => CompKind::Struct, + CXCursor_ClassTemplatePartialSpecialization | + CXCursor_ClassTemplate => { + match cursor.template_kind() { + CXCursor_UnionDecl => CompKind::Union, + _ => CompKind::Struct, + } + } + _ => { + warn!("Unknown kind for comp type: {:?}", cursor); + return Err(ParseError::Continue); + } + }) + } + + pub fn signature_contains_named_type(&self, + type_resolver: &TypeResolver, + ty: &Type) -> bool { + // We don't generate these, so rather don't make the codegen step to + // think we got it covered. + if self.has_non_type_template_params() { + return false; + } + self.template_args.iter().any(|arg| { + type_resolver.resolve_type(*arg) + .signature_contains_named_type(type_resolver, ty) + }) + } + + pub fn inner_types(&self) -> &[ItemId] { + &self.inner_types + } + + pub fn inner_vars(&self) -> &[ItemId] { + &self.inner_vars + } + + pub fn found_unknown_attr(&self) -> bool { + self.found_unknown_attr + } + + pub fn packed(&self) -> bool { + self.packed + } + + /// Returns whether this type needs an explicit vtable because it has + /// virtual methods and none of its base classes has already a vtable. + pub fn needs_explicit_vtable(&self, type_resolver: &TypeResolver) -> bool { + self.has_vtable(type_resolver) && !self.base_members.iter().any(|base| { + // NB: Ideally, we could rely in all these types being `comp`, and + // life would be beautiful. + // + // Unfortunately, given the way we implement --match-pat, and also + // that you can inherit from templated types, we need to handle + // other cases here too. + type_resolver + .resolve_type(*base) + .canonical_type(type_resolver) + .as_comp().map_or(false, |ci| { + ci.has_vtable(type_resolver) + }) + }) + } +} diff --git a/src/ir/context.rs b/src/ir/context.rs new file mode 100644 index 00000000..5ddedda0 --- /dev/null +++ b/src/ir/context.rs @@ -0,0 +1,685 @@ +use super::ty::{Type, TypeKind, FloatKind}; +use super::item::{Item, ItemCanonicalName, ItemId}; +use super::item_kind::ItemKind; +use super::int::IntKind; +use super::module::Module; +use clang::{self, Cursor}; +use std::borrow::{Cow, Borrow}; +use std::collections::btree_map::{self, BTreeMap}; +use std::collections::{HashSet, HashMap}; +use std::fmt; +use syntax::ast::Ident; +use syntax::codemap::{DUMMY_SP, Span}; +use syntax::ext::base::ExtCtxt; +use parse::ClangItemParser; +use BindgenOptions; + +// This is just convenience to avoid creating a manual debug impl for the +// context. +struct GenContext<'ctx>(ExtCtxt<'ctx>); + +impl<'ctx> fmt::Debug for GenContext <'ctx> { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + write!(fmt, "GenContext {{ ... }}") + } +} + +/// A context used during parsing and generation of structs. +#[derive(Debug)] +pub struct BindgenContext<'ctx> { + /// The map of all the items parsed so far. + /// + /// It's a BTreeMap because we want the keys to be sorted to have consistent + /// output. + items: BTreeMap<ItemId, Item>, + + /// Clang cursor to type map. This is needed to be able to associate types + /// with item ids during parsing. + /// + /// The cursor used for storage is the definition cursor. + types: HashMap<Cursor, ItemId>, + + /// A cursor to module map. Similar reason than above. + modules: HashMap<Cursor, ItemId>, + + /// The root module, this is guaranteed to be an item of kind Module. + root_module: ItemId, + + /// Current module being traversed. + current_module: ItemId, + + /// A stack with the current type declarations and types we're parsing. This + /// is needed to avoid infinite recursion when parsing a type like: + /// + /// struct c { struct c* next; }; + /// + /// This means effectively, that a type has a potential ID before knowing if + /// it's a correct type. But that's not important in practice. + /// + /// We could also use the `types` HashMap, but my intention with it is that + /// only valid types and declarations end up there, and this could + /// potentially break that assumption. + /// + /// FIXME: Should not be public, though... meh. + pub currently_parsed_types: Vec<(Cursor, ItemId)>, + + /// A HashSet with all the already parsed macro names. This is done to avoid + /// hard errors while parsing duplicated macros. + parsed_macros: HashSet<String>, + + /// The active replacements collected from replaces="xxx" annotations. + replacements: HashMap<String, ItemId>, + + collected_typerefs: bool, + + /// Dummy structures for code generation. + gen_ctx: Option<&'ctx GenContext<'ctx>>, + span: Span, + + /// The clang index for parsing. + index: clang::Index, + + /// The translation unit for parsing. + translation_unit: clang::TranslationUnit, + + /// The options given by the user via cli or other medium. + options: BindgenOptions, +} + +impl<'ctx> BindgenContext<'ctx> { + pub fn new(options: BindgenOptions) -> Self { + use clangll; + + let index = clang::Index::new(false, true); + + let translation_unit = + clang::TranslationUnit::parse(&index, "", &options.clang_args, &[], + clangll::CXTranslationUnit_DetailedPreprocessingRecord); + + let root_module = Self::build_root_module(); + let mut me = BindgenContext { + items: Default::default(), + types: Default::default(), + modules: Default::default(), + root_module: root_module.id(), + current_module: root_module.id(), + currently_parsed_types: vec![], + parsed_macros: Default::default(), + replacements: Default::default(), + collected_typerefs: false, + gen_ctx: None, + span: DUMMY_SP, + index: index, + translation_unit: translation_unit, + options: options, + }; + + me.add_item(root_module, None, None); + + me + } + + pub fn add_item(&mut self, + item: Item, + declaration: Option<Cursor>, + location: Option<Cursor>) { + use clangll::{CXCursor_ClassTemplate, CXCursor_ClassTemplatePartialSpecialization}; + debug!("BindgenContext::add_item({:?}, declaration: {:?}, loc: {:?}", item, declaration, location); + debug_assert!(declaration.is_some() || !item.kind().is_type() || + item.kind().expect_type().is_builtin_or_named(), + "Adding a type without declaration?"); + + let id = item.id(); + let is_type = item.kind().is_type(); + let old_item = self.items.insert(id, item); + assert!(old_item.is_none(), "Inserted type twice?"); + + if is_type && declaration.is_some() { + let declaration = declaration.unwrap(); + debug_assert_eq!(declaration, declaration.canonical()); + if declaration.is_valid() { + let old = self.types.insert(declaration, id); + debug_assert_eq!(old, None); + } else if location.is_some() && + (location.unwrap().kind() == CXCursor_ClassTemplate || + location.unwrap().kind() == CXCursor_ClassTemplatePartialSpecialization) { + let old = self.types.insert(location.unwrap().canonical(), id); + debug_assert_eq!(old, None); + } else { + // This could happen, for example, with types like `int*` or + // similar. + // + // Fortunately, we don't care about those types being + // duplicated, so we can just ignore them. + debug!("Invalid declaration {:?} found for type {:?}", + declaration, self.items.get(&id).unwrap().kind().expect_type()); + } + } + } + + // TODO: Move all this syntax crap to other part of the code. + pub fn ext_cx(&self) -> &ExtCtxt<'ctx> { + &self.gen_ctx.expect("Not in gen phase").0 + } + + pub fn span(&self) -> Span { + self.span + } + + /// Mangles a name so it doesn't conflict with any keyword. + pub fn rust_mangle<'a>(&self, name: &'a str) -> Cow<'a, str> { + use syntax::parse::token; + let ident = self.rust_ident_raw(&name); + let token = token::Ident(ident); + if token.is_any_keyword() || + name.contains("@") || + name.contains("?") || + name.contains("$") || + "bool" == name + { + let mut s = name.to_owned(); + s = s.replace("@", "_"); + s = s.replace("?", "_"); + s = s.replace("$", "_"); + s.push_str("_"); + return Cow::Owned(s) + } + Cow::Borrowed(name) + } + + /// Returns a mangled name as a rust identifier. + pub fn rust_ident(&self, name: &str) -> Ident { + self.rust_ident_raw(&self.rust_mangle(name)) + } + + pub fn rust_ident_raw<S>(&self, name: &S) -> Ident + where S: Borrow<str>, + { + self.ext_cx().ident_of(name.borrow()) + } + + pub fn items<'a>(&'a self) -> btree_map::Iter<'a, ItemId, Item> { + self.items.iter() + } + + pub fn collected_typerefs(&self) -> bool { + self.collected_typerefs + } + + fn collect_typerefs(&mut self) -> Vec<(ItemId, clang::Type, Option<clang::Cursor>)> { + debug_assert!(!self.collected_typerefs); + self.collected_typerefs = true; + let mut typerefs = vec![]; + for (id, ref mut item) in &mut self.items { + let kind = item.kind(); + let ty = match kind.as_type() { + Some(ty) => ty, + None => continue, + }; + + match *ty.kind() { + TypeKind::UnresolvedTypeRef(ref ty, loc) => { + typerefs.push((*id, ty.clone(), loc)); + } + _ => {}, + }; + } + typerefs + } + + fn resolve_typerefs(&mut self) { + let typerefs = self.collect_typerefs(); + + for (id, ty, loc) in typerefs { + let _resolved = { + let resolved = Item::from_ty(&ty, loc, None, self) + .expect("What happened?"); + let mut item = self.items.get_mut(&id).unwrap(); + + *item.kind_mut().as_type_mut().unwrap().kind_mut() = + TypeKind::ResolvedTypeRef(resolved); + resolved + }; + + // Something in the STL is trolling me. I don't need this assertion + // right now, but worth investigating properly once this lands. + // + // debug_assert!(self.items.get(&resolved).is_some(), "How?"); + } + } + + fn process_replacements(&mut self) { + if self.replacements.is_empty() { + return; + } + + // FIXME: This is linear, but the replaces="xxx" annotation was already + // there, and for better or worse it's useful, sigh... + // + // We leverage the ResolvedTypeRef thing, though, which is cool :P. + + let mut replacements = vec![]; + + for (id, item) in self.items.iter() { + let ty = match item.kind().as_type() { + Some(ty) => ty, + None => continue, + }; + + // canonical_name calls are expensive. + let ci = match ty.as_comp() { + Some(ci) => ci, + None => continue, + }; + + if ci.is_template_specialization() { + continue; + } + + if let Some(replacement) = self.replacements.get(&item.canonical_name(self)) { + if replacement != id { + // We set this just after parsing the annotation. It's + // very unlikely, but this can happen. + if self.items.get(replacement).is_some() { + replacements.push((*id, *replacement)); + } + } + } + } + + for (id, replacement) in replacements { + let mut item = self.items.get_mut(&id).unwrap(); + *item.kind_mut().as_type_mut().unwrap().kind_mut() = + TypeKind::ResolvedTypeRef(replacement); + } + } + + // Enters in the generation phase. + pub fn gen<F, Out>(&mut self, cb: F) -> Out + where F: FnOnce(&Self) -> Out + { + use syntax::ext::expand::ExpansionConfig; + use syntax::codemap::{ExpnInfo, MacroBang, NameAndSpan}; + use syntax::ext::base; + use syntax::parse; + use std::mem; + + let cfg = ExpansionConfig::default("xxx".to_owned()); + let sess = parse::ParseSess::new(); + let mut loader = base::DummyMacroLoader; + let mut ctx = + GenContext(base::ExtCtxt::new(&sess, vec![], cfg, &mut loader)); + + ctx.0.bt_push(ExpnInfo { + call_site: self.span, + callee: NameAndSpan { + format: MacroBang(parse::token::intern("")), + allow_internal_unstable: false, + span: None + } + }); + + // FIXME: This is evil, we should move code generation to use a wrapper + // of BindgenContext instead, I guess. Even though we know it's fine + // because we remove it before the end of this function. + self.gen_ctx = Some(unsafe { mem::transmute(&ctx) }); + + self.resolve_typerefs(); + self.process_replacements(); + + let ret = cb(self); + self.gen_ctx = None; + ret + } + + // This deserves a comment. Builtin types don't get a valid declaration, so + // we can't add it to the cursor->type map. + // + // That being said, they're not generated anyway, and are few, so the + // duplication and special-casing is fine. + // + // If at some point we care about the memory here, probably a map TypeKind + // -> builtin type ItemId would be the best to improve that. + fn add_builtin_item(&mut self, item: Item) { + debug_assert!(item.kind().is_type()); + let id = item.id(); + let old_item = self.items.insert(id, item); + assert!(old_item.is_none(), "Inserted type twice?"); + } + + fn build_root_module() -> Item { + let module = Module::new(Some("root".into())); + let id = ItemId::next(); + Item::new(id, None, None, id, ItemKind::Module(module)) + } + + pub fn root_module(&self) -> ItemId { + self.root_module + } + + pub fn resolve_type(&self, type_id: ItemId) -> &Type { + self.items.get(&type_id).unwrap().kind().expect_type() + } + + pub fn safe_resolve_type(&self, type_id: ItemId) -> Option<&Type> { + self.items.get(&type_id).map(|t| t.kind().expect_type()) + } + + pub fn resolve_item_fallible(&self, item_id: ItemId) -> Option<&Item> { + self.items.get(&item_id) + } + + pub fn resolve_item(&self, item_id: ItemId) -> &Item { + match self.items.get(&item_id) { + Some(item) => item, + None => panic!("Not an item: {:?}", item_id), + } + } + + pub fn current_module(&self) -> ItemId { + self.current_module + } + + /// This is one of the hackiest methods in all the parsing code. This method + /// is used to allow having templates with another argument names instead of + /// the canonical ones. + /// + /// This is surprisingly difficult to do with libclang, due to the fact that + /// partial template specializations don't provide explicit template + /// argument information. + /// + /// The only way to do this as far as I know, is inspecting manually the + /// AST, looking for TypeRefs inside. This, unfortunately, doesn't work for + /// more complex cases, see the comment on the assertion below. + /// + /// To see an example of what this handles: + /// + /// ``` + /// template<typename T> + /// class Incomplete { + /// T p; + /// }; + /// + /// template<typename U> + /// class Foo { + /// Incomplete<U> bar; + /// }; + /// ``` + fn build_template_wrapper(&mut self, + wrapping: ItemId, + parent_id: ItemId, + ty: &clang::Type, + location: clang::Cursor) -> ItemId { + use clangll::*; + let mut args = vec![]; + let mut found_invalid_template_ref = false; + let self_id = ItemId::next(); + location.visit(|c, _| { + if c.kind() == CXCursor_TemplateRef && + c.cur_type().kind() == CXType_Invalid { + found_invalid_template_ref = true; + } + if c.kind() == CXCursor_TypeRef { + let new_ty = + Item::from_ty_or_ref(c.cur_type(), Some(*c), Some(self_id), self); + args.push(new_ty); + } + CXChildVisit_Continue + }); + + let item = { + let wrapping_type = self.resolve_type(wrapping); + let old_args = match *wrapping_type.kind() { + TypeKind::Comp(ref ci) => ci.template_args(), + _ => panic!("how?"), + }; + // The following assertion actually fails with partial template + // specialization. But as far as I know there's no way at all to + // grab the specialized types from neither the AST or libclang. + // + // This flaw was already on the old parser, but I now think it has + // no clear solution. + // + // For an easy example in which there's no way at all of getting the + // `int` type, except manually parsing the spelling: + // + // template<typename T, typename U> + // class Incomplete { + // T d; + // U p; + // }; + // + // template<typename U> + // class Foo { + // Incomplete<U, int> bar; + // }; + // + // debug_assert_eq!(old_args.len(), args.len()); + // + // That being said, this is not so common, so just error! and hope + // for the best, returning the previous type, who knows. + if old_args.len() != args.len() { + error!("Found partial template specialization, expect dragons!"); + return wrapping; + } + + let type_kind = TypeKind::TemplateRef(wrapping, args); + let name = ty.spelling(); + let name = if name.is_empty() { None } else { Some(name) }; + let ty = Type::new(name, ty.fallible_layout().ok(), type_kind, ty.is_const()); + Item::new(self_id, None, None, parent_id, ItemKind::Type(ty)) + }; + + // Bypass all the validations in add_item explicitly. + self.items.insert(self_id, item); + self_id + } + + /// Looks up for an already resolved type, either because it's builtin, or + /// because we already have it in the map. + pub fn builtin_or_resolved_ty(&mut self, + parent_id: Option<ItemId>, + ty: &clang::Type, + location: Option<clang::Cursor>) -> Option<ItemId> { + use clangll::{CXCursor_ClassTemplate, CXCursor_ClassTemplatePartialSpecialization}; + debug!("builtin_or_resolved_ty: {:?}, {:?}, {:?}", ty, location, parent_id); + let mut declaration = ty.declaration(); + if !declaration.is_valid() { + if let Some(location) = location { + if location.kind() == CXCursor_ClassTemplate || + location.kind() == CXCursor_ClassTemplatePartialSpecialization { + declaration = location; + } + } + } + let canonical_declaration = declaration.canonical(); + if canonical_declaration.is_valid() { + // First lookup to see if we already have it resolved. + let id = self.types.get(&canonical_declaration).map(|id| *id); + if let Some(id) = id { + debug!("Already resolved ty {:?}, {:?}, {:?} {:?}", + id, declaration, ty, location); + // If the declaration existed, we *might* be done, but it's not + // the case for class templates, where the template arguments + // may vary. + // + // In this case, we create a TemplateRef with the new template + // arguments, pointing to the canonical template. + // + // Note that we only do it if parent_id is some, and we have a + // location for building the new arguments, the template + // argument names don't matter in the global context. + if (declaration.kind() == CXCursor_ClassTemplate || + declaration.kind() == CXCursor_ClassTemplatePartialSpecialization) && + *ty != canonical_declaration.cur_type() && + location.is_some() && parent_id.is_some() { + return Some( + self.build_template_wrapper(id, parent_id.unwrap(), ty, + location.unwrap())); + } + + return Some(self.build_ty_wrapper(id, parent_id, ty)); + } + } + + debug!("Not resolved, maybe builtin?"); + + // Else, build it. + self.build_builtin_ty(ty, declaration) + } + + // This is unfortunately a lot of bloat, but is needed to properly track + // constness et. al. + // + // We should probably make the constness tracking separate, so it doesn't + // bloat that much, but hey, we already bloat the heck out of builtin types. + fn build_ty_wrapper(&mut self, + wrapped_id: ItemId, + parent_id: Option<ItemId>, + ty: &clang::Type) -> ItemId { + let id = ItemId::next(); + let spelling = ty.spelling(); + let is_const = ty.is_const(); + let layout = ty.fallible_layout().ok(); + let type_kind = TypeKind::ResolvedTypeRef(wrapped_id); + let ty = Type::new(Some(spelling), layout, type_kind, is_const); + let item = Item::new(id, None, None, + parent_id.unwrap_or(self.current_module), ItemKind::Type(ty)); + self.add_builtin_item(item); + id + } + + fn build_builtin_ty(&mut self, + ty: &clang::Type, + _declaration: Cursor) -> Option<ItemId> { + use clangll::*; + let type_kind = match ty.kind() { + CXType_NullPtr => TypeKind::NullPtr, + CXType_Void => TypeKind::Void, + CXType_Bool => TypeKind::Int(IntKind::Bool), + CXType_Int => TypeKind::Int(IntKind::Int), + CXType_UInt => TypeKind::Int(IntKind::UInt), + CXType_SChar | + CXType_Char_S => TypeKind::Int(IntKind::Char), + CXType_UChar | + CXType_Char_U => TypeKind::Int(IntKind::UChar), + CXType_Short => TypeKind::Int(IntKind::Short), + CXType_UShort => TypeKind::Int(IntKind::UShort), + CXType_WChar | + CXType_Char16 => TypeKind::Int(IntKind::U16), + CXType_Char32 => TypeKind::Int(IntKind::U32), + CXType_Long => TypeKind::Int(IntKind::Long), + CXType_ULong => TypeKind::Int(IntKind::ULong), + CXType_LongLong => TypeKind::Int(IntKind::LongLong), + CXType_ULongLong => TypeKind::Int(IntKind::ULongLong), + CXType_Float => TypeKind::Float(FloatKind::Float), + CXType_Double => TypeKind::Float(FloatKind::Double), + CXType_LongDouble => TypeKind::Float(FloatKind::LongDouble), + _ => return None, + }; + + let spelling = ty.spelling(); + let is_const = ty.is_const(); + let layout = ty.fallible_layout().ok(); + let ty = Type::new(Some(spelling), layout, type_kind, is_const); + let id = ItemId::next(); + let item = Item::new(id, None, None, self.root_module, ItemKind::Type(ty)); + self.add_builtin_item(item); + Some(id) + } + + pub fn translation_unit(&self) -> &clang::TranslationUnit { + &self.translation_unit + } + + pub fn parsed_macro(&self, macro_name: &str) -> bool { + self.parsed_macros.contains(macro_name) + } + + pub fn note_parsed_macro(&mut self, macro_name: String) { + debug_assert!(!self.parsed_macros.contains(¯o_name)); + self.parsed_macros.insert(macro_name); + } + + pub fn in_codegen_phase(&self) -> bool { + self.gen_ctx.is_some() + } + + /// This is a bit of a hack, but it's done so using the replaces="xxx" + /// annotation implies hide in the other type. + pub fn replace(&mut self, name: &str, potential_ty: ItemId) { + self.replacements.insert(name.into(), potential_ty); + } + + pub fn hidden_by_name(&self, name: &str) -> bool { + debug_assert!(self.in_codegen_phase(), + "You're not supposed to call this yet"); + self.options.hidden_types.contains(name) + } + + pub fn opaque_by_name(&self, name: &str) -> bool { + debug_assert!(self.in_codegen_phase(), + "You're not supposed to call this yet"); + self.options.opaque_types.contains(name) + } + + pub fn options(&self) -> &BindgenOptions { + &self.options + } + + /// Given a CXCursor_Namespace cursor, return the item id of the + /// corresponding module, or create one on the fly. + pub fn module(&mut self, cursor: clang::Cursor) -> ItemId { + use clangll::*; + assert!(cursor.kind() == CXCursor_Namespace, "Be a nice person"); + let cursor = cursor.canonical(); + let module_id = match self.modules.get(&cursor) { + Some(id) => return *id, + None => ItemId::next(), + }; + + let module_name = self.translation_unit + .tokens(&cursor).and_then(|tokens| { + if tokens.len() <= 1 { + None + } else { + match &*tokens[1].spelling { + "{" => None, + s => Some(s.to_owned()), + } + } + }); + + let module = Module::new(module_name); + let module = Item::new(module_id, None, None, self.current_module, + ItemKind::Module(module)); + + self.add_item(module, None, None); + + module_id + } + + pub fn with_module<F>(&mut self, module_id: ItemId, cb: F) + where F: FnOnce(&mut Self, &mut Vec<ItemId>) + { + debug_assert!(self.resolve_item(module_id).kind().is_module(), "Wat"); + + let previous_id = self.current_module; + self.current_module = module_id; + + let mut children = vec![]; + cb(self, &mut children); + + self.items.get_mut(&module_id).unwrap() + .as_module_mut().expect("Not a module?") + .children_mut().extend(children.into_iter()); + + self.current_module = previous_id; + } +} + +/// This was originally a type that only exposes the resolve_type operation to +/// its consumers. +/// +/// Later a made resolve_type public, so... meh. It should go away soon. +pub type TypeResolver<'ctx> = BindgenContext<'ctx>; diff --git a/src/ir/enum_ty.rs b/src/ir/enum_ty.rs new file mode 100644 index 00000000..c85ee07a --- /dev/null +++ b/src/ir/enum_ty.rs @@ -0,0 +1,110 @@ +use super::item::{Item, ItemId}; +use super::ty::TypeKind; +use super::context::BindgenContext; +use parse::{ClangItemParser, ParseError}; +use clang; + +#[derive(Debug)] +pub struct Enum { + /// The representation used for this enum. + /// Should be an IntKind type. + /// + /// It's None if the enum is a forward declaration and isn't defined + /// anywhere else, see tests/headers/func_ptr_in_struct.h + repr: Option<ItemId>, + /// The different variants, with explicit values. + variants: Vec<EnumVariant>, +} + +impl Enum { + pub fn new(repr: Option<ItemId>, variants: Vec<EnumVariant>) -> Self { + Enum { + repr: repr, + variants: variants, + } + } + + pub fn repr(&self) -> Option<ItemId> { + self.repr + } + + pub fn variants(&self) -> &[EnumVariant] { + &self.variants + } + + pub fn from_ty(ty: &clang::Type, + ctx: &mut BindgenContext) -> Result<Self, ParseError> { + use clangll::*; + if ty.kind() != CXType_Enum { + return Err(ParseError::Continue); + } + + let declaration = ty.declaration().canonical(); + let repr = Item::from_ty(&declaration.enum_type(), None, None, ctx).ok(); + let mut variants = vec![]; + + let is_signed = match repr { + Some(repr) => { + let repr_type = ctx.resolve_type(repr); + match *repr_type.canonical_type(ctx).kind() { + TypeKind::Int(ref int_kind) => int_kind.is_signed(), + ref other => panic!("Since when enums can be non-integers? {:?}", other), + } + } + // Assume signedness since the default type by the C standard is an + // int. + None => true, + }; + + declaration.visit(|cursor, _| { + if cursor.kind() == CXCursor_EnumConstantDecl { + let name = cursor.spelling(); + let comment = cursor.raw_comment(); + let val = if is_signed { + EnumVariantValue::Signed(cursor.enum_val_signed()) + } else { + EnumVariantValue::Unsigned(cursor.enum_val_unsigned()) + }; + variants.push(EnumVariant::new(name, comment, val)); + } + CXChildVisit_Continue + }); + + Ok(Enum::new(repr, variants)) + } +} + +/// A single enum variant, to be contained only in an enum. +#[derive(Debug)] +pub struct EnumVariant { + /// The name of the variant. + name: String, + /// An optional doc comment. + comment: Option<String>, + /// The integer value of the variant. + val: EnumVariantValue, +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum EnumVariantValue { + Signed(i64), + Unsigned(u64), +} + +impl EnumVariant { + pub fn new(name: String, comment: Option<String>, val: EnumVariantValue) -> Self { + EnumVariant { + name: name, + comment: comment, + val: val, + } + } + + pub fn name(&self) -> &str { + &self.name + } + + pub fn val(&self) -> EnumVariantValue { + self.val + } +} diff --git a/src/ir/function.rs b/src/ir/function.rs new file mode 100644 index 00000000..b95ac57b --- /dev/null +++ b/src/ir/function.rs @@ -0,0 +1,220 @@ +use super::item::{Item, ItemId}; +use super::ty::TypeKind; +use super::context::BindgenContext; +use syntax::abi; +use clang; +use clangll::Enum_CXCallingConv; +use parse::{ClangItemParser, ClangSubItemParser, ParseError, ParseResult}; + +/// A function declaration , with a signature, arguments, and argument names. +/// +/// The argument names vector must be the same length as the ones in the +/// signature. +#[derive(Debug)] +pub struct Function { + name: String, + /// The mangled name, that is, the symbol. + mangled_name: Option<String>, + /// The id pointing to the current function signature. + signature: ItemId, + /// The doc comment on the function, if any. + comment: Option<String>, +} + +impl Function { + pub fn new(name: String, + mangled_name: Option<String>, + sig: ItemId, + comment: Option<String>) -> Self { + Function { + name: name, + mangled_name: mangled_name, + signature: sig, + comment: comment, + } + } + + pub fn name(&self) -> &str { + &self.name + } + + pub fn mangled_name(&self) -> Option<&str> { + self.mangled_name.as_ref().map(|n| &**n) + } + + pub fn signature(&self) -> ItemId { + self.signature + } +} + +/// A function signature. +#[derive(Debug)] +pub struct FunctionSig { + /// The return type of the function. + return_type: ItemId, + /// The type of the arguments, optionally with the name of the argument when + /// declared. + argument_types: Vec<(Option<String>, ItemId)>, + /// Whether this function is variadic. + is_variadic: bool, + /// The abi of this function. + abi: abi::Abi, +} + +fn get_abi(cc: Enum_CXCallingConv) -> abi::Abi { + use clangll::*; + match cc { + CXCallingConv_Default => abi::Abi::C, + CXCallingConv_C => abi::Abi::C, + CXCallingConv_X86StdCall => abi::Abi::Stdcall, + CXCallingConv_X86FastCall => abi::Abi::Fastcall, + CXCallingConv_AAPCS => abi::Abi::Aapcs, + CXCallingConv_X86_64Win64 => abi::Abi::Win64, + other => panic!("unsupported calling convention: {}", other), + } +} + +pub fn cursor_mangling(cursor: &clang::Cursor) -> Option<String> { + let mut mangling = cursor.mangling(); + + // Try to undo backend linkage munging (prepended _, generally) + if cfg!(target_os = "macos") { + mangling.remove(0); + } + + if mangling.is_empty() { None } else { Some(mangling) } +} + +impl FunctionSig { + pub fn new(return_type: ItemId, + arguments: Vec<(Option<String>, ItemId)>, + is_variadic: bool, + abi: abi::Abi) -> Self { + FunctionSig { + return_type: return_type, + argument_types: arguments, + is_variadic: is_variadic, + abi: abi, + } + } + + pub fn from_ty(ty: &clang::Type, + cursor: &clang::Cursor, + ctx: &mut BindgenContext) -> Result<Self, ParseError> { + use clangll::*; + debug!("FunctionSig::from_ty {:?} {:?}", ty, cursor); + + // Don't parse operatorxx functions in C++ + let spelling = cursor.spelling(); + if spelling.starts_with("operator") { + return Err(ParseError::Continue); + } + + let cursor = if cursor.is_valid() { + *cursor + } else { + ty.declaration() + }; + let mut args: Vec<_> = match cursor.kind() { + CXCursor_FunctionDecl | + CXCursor_CXXMethod => { + // For CXCursor_FunctionDecl, cursor.args() is the reliable way + // to get parameter names and types. + cursor.args().iter().map(|arg| { + let arg_ty = arg.cur_type(); + let name = arg.spelling(); + let name = if name.is_empty() { None } else { Some(name) }; + let ty = Item::from_ty(&arg_ty, Some(*arg), None, ctx) + .expect("Argument?"); + (name, ty) + }).collect() + } + _ => { + // For non-CXCursor_FunctionDecl, visiting the cursor's children + // is the only reliable way to get parameter names. + let mut args = vec![]; + cursor.visit(|c, _| { + if c.kind() == CXCursor_ParmDecl { + let ty = Item::from_ty(&c.cur_type(), Some(*c), None, ctx) + .expect("ParmDecl?"); + let name = c.spelling(); + let name = if name.is_empty() { None } else { Some(name) }; + args.push((name, ty)); + } + CXChildVisit_Continue + }); + args + } + }; + + if cursor.kind() == CXCursor_CXXMethod { + let is_const = cursor.method_is_const(); + let is_virtual = cursor.method_is_virtual(); + let is_static = cursor.method_is_static(); + if !is_static && !is_virtual { + let class = Item::parse(cursor.semantic_parent(), None, ctx) + .expect("Expected to parse the class"); + let ptr = Item::builtin_type(TypeKind::Pointer(class), is_const, ctx); + args.insert(0, (Some("this".into()), ptr)); + } else if is_virtual { + let void = Item::builtin_type(TypeKind::Void, false, ctx); + let ptr = Item::builtin_type(TypeKind::Pointer(void), false, ctx); + args.insert(0, (Some("this".into()), ptr)); + } + } + + let ret = try!(Item::from_ty(&ty.ret_type(), None, None, ctx)); + let abi = get_abi(ty.call_conv()); + + Ok(Self::new(ret, args, ty.is_variadic(), abi)) + } + + pub fn return_type(&self) -> ItemId { + self.return_type + } + + pub fn argument_types(&self) -> &[(Option<String>, ItemId)] { + &self.argument_types + } + + pub fn abi(&self) -> abi::Abi { + self.abi + } + + pub fn is_variadic(&self) -> bool { + // Clang reports some functions as variadic when they *might* be + // variadic. We do the argument check because rust doesn't codegen well + // variadic functions without an initial argument. + self.is_variadic && !self.argument_types.is_empty() + } +} + +impl ClangSubItemParser for Function { + fn parse(cursor: clang::Cursor, + context: &mut BindgenContext) -> Result<ParseResult<Self>, ParseError> { + use clangll::*; + match cursor.kind() { + CXCursor_FunctionDecl | + CXCursor_CXXMethod => {}, + _ => return Err(ParseError::Continue), + }; + + debug!("Function::parse({:?}, {:?})", cursor, cursor.cur_type()); + + // Grab the signature using Item::from_ty. + let sig = try!(Item::from_ty(&cursor.cur_type(), Some(cursor), None, context)); + + let name = cursor.spelling(); + assert!(!name.is_empty(), "Empty function name?"); + + let mut mangled_name = cursor_mangling(&cursor); + if mangled_name.as_ref() == Some(&name) { + mangled_name = None; + } + + let comment = cursor.raw_comment(); + + let function = Self::new(name, mangled_name, sig, comment); + Ok(ParseResult::New(function, Some(cursor))) + } +} diff --git a/src/ir/int.rs b/src/ir/int.rs new file mode 100644 index 00000000..d2769b77 --- /dev/null +++ b/src/ir/int.rs @@ -0,0 +1,30 @@ +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub enum IntKind { + Bool, + Char, + UChar, + Short, + UShort, + Int, + UInt, + Long, + ULong, + LongLong, + ULongLong, + U16, // For Char16 and Wchar + U32, // For Char32 + // Though now we're at it we could add equivalents for the rust types... +} + +impl IntKind { + pub fn is_signed(&self) -> bool { + use self::IntKind::*; + match *self { + Bool | UChar | UShort | + UInt | ULong | ULongLong | U16 | U32 => false, + + Char | Short | Int | + Long | LongLong => true, + } + } +} diff --git a/src/ir/item.rs b/src/ir/item.rs new file mode 100644 index 00000000..c9ac71a4 --- /dev/null +++ b/src/ir/item.rs @@ -0,0 +1,681 @@ +use super::context::BindgenContext; +use super::item_kind::ItemKind; +use super::ty::{Type, TypeKind}; +use super::function::Function; +use super::module::Module; +use super::annotations::Annotations; +use std::fmt; +use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering}; +use parse::{ClangItemParser, ClangSubItemParser, ParseError, ParseResult}; +use clang; +use clangll; + +/// A trait to get the canonical name from an item. +/// +/// This is the trait that will eventually isolate all the logic related to name +/// mangling and that kind of stuff. +/// +/// This assumes no nested paths, at some point I'll have to make it a more +/// complex thing. +/// +/// This name is required to be safe for Rust, that is, is not expected to +/// return any rust keyword from here. +pub trait ItemCanonicalName { + fn canonical_name(&self, ctx: &BindgenContext) -> String; +} + +/// The same, but specifies the path that needs to be followed to reach an item. +/// +/// To contrast with canonical_name, here's an example: +/// +/// ``` +/// namespace foo { +/// const BAR = 3; +/// } +/// ``` +/// +/// For bar, the canonical path is foo::BAR, while the canonical name is just +/// BAR. +pub trait ItemCanonicalPath { + fn canonical_path(&self, ctx: &BindgenContext) -> Vec<String>; +} + +/// A single identifier for an item. +/// +/// TODO: Build stronger abstractions on top of this, like TypeId(ItemId), ... +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct ItemId(usize); + +impl fmt::Display for ItemId { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + try!(write!(fmt, "_bindgen_id_")); + self.0.fmt(fmt) + } +} + +pub static NEXT_ITEM_ID: AtomicUsize = ATOMIC_USIZE_INIT; + +impl ItemId { + pub fn next() -> Self { + ItemId(NEXT_ITEM_ID.fetch_add(1, Ordering::Relaxed)) + } +} + +// Pure convenience +impl ItemCanonicalName for ItemId { + fn canonical_name(&self, ctx: &BindgenContext) -> String { + debug_assert!(ctx.in_codegen_phase(), + "You're not supposed to call this yet"); + ctx.resolve_item(*self).canonical_name(ctx) + } +} + +impl ItemCanonicalPath for ItemId { + fn canonical_path(&self, ctx: &BindgenContext) -> Vec<String> { + debug_assert!(ctx.in_codegen_phase(), + "You're not supposed to call this yet"); + ctx.resolve_item(*self).canonical_path(ctx) + } +} + +#[derive(Debug)] +pub struct Item { + /// This item's id. + id: ItemId, + /// A doc comment over the item, if any. + comment: Option<String>, + /// Annotations extracted from the doc comment, or the default ones + /// otherwise. + annotations: Annotations, + /// An item's parent id. This will most likely be a class where this item + /// was declared, or a module, etc. + /// + /// All the items have a parent, except the root module, in which case the + /// parent id is its own id. + parent_id: ItemId, + /// The item kind. + kind: ItemKind, +} + +impl Item { + pub fn new(id: ItemId, + comment: Option<String>, + annotations: Option<Annotations>, + parent_id: ItemId, + kind: ItemKind) -> Self { + debug_assert!(id != parent_id || kind.is_module()); + Item { + id: id, + parent_id: parent_id, + comment: comment, + annotations: annotations.unwrap_or_default(), + kind: kind, + } + } + + pub fn id(&self) -> ItemId { + self.id + } + + pub fn parent_id(&self) -> ItemId { + self.parent_id + } + + pub fn comment(&self) -> Option<&str> { + self.comment.as_ref().map(|c| &**c) + } + + pub fn kind(&self) -> &ItemKind { + &self.kind + } + + pub fn kind_mut(&mut self) -> &mut ItemKind { + &mut self.kind + } + + pub fn is_toplevel(&self, ctx: &BindgenContext) -> bool { + // FIXME: Workaround for some types falling behind when parsing weird + // stl classes, for example. + if ctx.options().enable_cxx_namespaces && + self.kind().is_module() && + self.id() != ctx.root_module() { + return false; + } + + let mut parent = self.parent_id; + loop { + let parent_item = match ctx.resolve_item_fallible(parent) { + Some(item) => item, + None => return false, + }; + + if parent_item.id() == ctx.root_module() { + return true; + } else if ctx.options().enable_cxx_namespaces || !parent_item.kind().is_module() { + return false; + } + + parent = parent_item.parent_id(); + } + } + + pub fn expect_type(&self) -> &Type { + self.kind().expect_type() + } + + pub fn expect_function(&self) -> &Function { + self.kind().expect_function() + } + + pub fn applicable_template_args(&self, ctx: &BindgenContext) -> Vec<ItemId> { + let ty = match *self.kind() { + ItemKind::Type(ref ty) => ty, + _ => return vec![], + }; + + fn parent_contains(ctx: &BindgenContext, + parent_template_args: &[ItemId], + item: ItemId) -> bool { + let item_ty = ctx.resolve_type(item); + parent_template_args.iter().any(|parent_item| { + let parent_ty = ctx.resolve_type(*parent_item); + match (parent_ty.kind(), item_ty.kind()) { + (&TypeKind::Named(ref n, _), &TypeKind::Named(ref i, _)) => n == i, + _ => false, + } + }) + } + + match *ty.kind() { + TypeKind::Named(..) => vec![self.id()], + TypeKind::Array(inner, _) | + TypeKind::Pointer(inner) | + TypeKind::Reference(inner) | + TypeKind::Alias(_, inner) | + TypeKind::ResolvedTypeRef(inner) => { + ctx.resolve_item(inner).applicable_template_args(ctx) + } + // XXX Is this completely correct? Partial template specialization + // is hard anyways, sigh... + TypeKind::TemplateRef(_, ref args) => { + args.clone() + } + // In a template specialization we've got all we want. + TypeKind::Comp(ref ci) if ci.is_template_specialization() => { + ci.template_args().iter().cloned().collect() + } + TypeKind::Comp(ref ci) => { + let mut parent_template_args = + ctx.resolve_item(self.parent_id()) + .applicable_template_args(ctx); + + for ty in ci.template_args() { + if !parent_contains(ctx, &parent_template_args, *ty) { + parent_template_args.push(*ty); + } + } + + parent_template_args + } + _ => vec![], + } + } + + fn is_module(&self) -> bool { + match self.kind { + ItemKind::Module(..) => true, + _ => false, + } + } + + pub fn annotations(&self) -> &Annotations { + &self.annotations + } + + /// Whether this item should be hidden, either due to annotations, or due to + /// other kind of configuration. + pub fn is_hidden(&self, ctx: &BindgenContext) -> bool { + debug_assert!(ctx.in_codegen_phase(), + "You're not supposed to call this yet"); + self.annotations.hide() || + ctx.hidden_by_name(&self.real_canonical_name(ctx, false)) + } + + pub fn is_opaque(&self, ctx: &BindgenContext) -> bool { + debug_assert!(ctx.in_codegen_phase(), + "You're not supposed to call this yet"); + self.annotations.opaque() || + ctx.opaque_by_name(&self.real_canonical_name(ctx, false)) + } + + /// Get the canonical name without taking into account the replaces + /// annotation. + fn real_canonical_name(&self, ctx: &BindgenContext, count_namespaces: bool) -> String { + let base_name = match *self.kind() { + ItemKind::Type(ref ty) => { + match *ty.kind() { + // If we're a template specialization, our name is our parent's + TypeKind::Comp(ref ci) if ci.is_template_specialization() => { + return ci.specialized_template().unwrap().canonical_name(ctx); + }, + // Same as above + TypeKind::ResolvedTypeRef(inner) | + TypeKind::TemplateRef(inner, _) => { + return inner.canonical_name(ctx); + } + // If we're a named type, we don't need to mangle it, and we + // should be able to assert we're not top level. + TypeKind::Named(ref name, _) => { + return name.to_owned(); + } + _ => {} + } + + ty.name().map(ToOwned::to_owned) + .unwrap_or_else(|| format!("_bindgen_ty{}", self.id())) + } + ItemKind::Function(ref fun) => { + let mut base = fun.name().to_owned(); + + // We might need to deduplicate if we're a method. + let parent = ctx.resolve_item(self.parent_id()); + if let ItemKind::Type(ref ty) = *parent.kind() { + if let TypeKind::Comp(ref ci) = *ty.kind() { + let mut count = 0; + let mut found = false; + for method in ci.methods() { + if method.signature() == self.id() { + found = true; + break; + } + let fun = ctx.resolve_item(method.signature()) + .expect_function(); + if fun.name() == base { + count += 1; + } + } + + assert!(found, "Method not found?"); + if count != 0 { + base.push_str(&count.to_string()); + } + } + } + base + } + ItemKind::Var(ref var) => { + var.name().to_owned() + } + ItemKind::Module(ref module) => { + module.name().map(ToOwned::to_owned) + .unwrap_or_else(|| format!("_bindgen_mod{}", self.id())) + } + }; + + let parent = ctx.resolve_item(self.parent_id()); + let parent_is_namespace = parent.is_module(); + if self.is_toplevel(ctx) || (parent_is_namespace && count_namespaces) { + return ctx.rust_mangle(&base_name).into_owned(); + } + + // TODO: allow modification of the mangling functions, maybe even per + // item type? + format!("{}_{}", parent.canonical_name(ctx), base_name) + } + + pub fn as_module_mut(&mut self) -> Option<&mut Module> { + match self.kind { + ItemKind::Module(ref mut module) => Some(module), + _ => None, + } + } +} + +impl ClangItemParser for Item { + fn builtin_type(kind: TypeKind, is_const: bool, ctx: &mut BindgenContext) -> ItemId { + // Feel free to add more here, I'm just lazy. + match kind { + TypeKind::Void | + TypeKind::Int(..) | + TypeKind::Pointer(..) | + TypeKind::Float(..) => {}, + _ => panic!("Unsupported builtin type"), + } + + let ty = Type::new(None, None, kind, is_const); + let id = ItemId::next(); + let module = ctx.root_module(); + ctx.add_item(Item::new(id, None, None, module, ItemKind::Type(ty)), + None, None); + id + } + + + fn parse(cursor: clang::Cursor, + parent_id: Option<ItemId>, + context: &mut BindgenContext) -> Result<ItemId, ParseError> { + use ir::function::Function; + use ir::module::Module; + use ir::var::Var; + + if !cursor.is_valid() { + return Err(ParseError::Continue); + } + + let comment = cursor.raw_comment(); + let annotations = Annotations::new(&cursor); + + // FIXME: The current_module logic is not really accurate. We should be + // able to index modules by their Cursor, and locate the proper module + // for a given item. + // + // We don't support modules properly though, so there's no rush for + // this. + let current_module = context.current_module(); + macro_rules! try_parse { + ($what:ident) => { + match $what::parse(cursor, context) { + Ok(ParseResult::New(item, declaration)) => { + let id = ItemId::next(); + context.add_item(Item::new(id, comment, annotations, + parent_id.unwrap_or(current_module), + ItemKind::$what(item)), + declaration, + Some(cursor)); + return Ok(id); + } + Ok(ParseResult::AlreadyResolved(id)) => { + return Ok(id); + } + Err(ParseError::Recurse) => return Err(ParseError::Recurse), + Err(ParseError::Continue) => {}, + } + } + } + + try_parse!(Module); + + // NOTE: Is extremely important to parse functions and vars **before** + // types. Otherwise we can parse a function declaration as a type + // (which is legal), and lose functions to generate. + // + // In general, I'm not totally confident this split between + // ItemKind::Function and TypeKind::FunctionSig is totally worth it, but + // I guess we can try. + try_parse!(Function); + try_parse!(Var); + + // Types are sort of special, so to avoid parsing template classes + // twice, handle them separately. + { + let definition = cursor.definition(); + let applicable_cursor = if definition.is_valid() { + definition + } else { + cursor + }; + match Self::from_ty(&applicable_cursor.cur_type(), + Some(applicable_cursor), parent_id, context) + { + Ok(ty) => return Ok(ty), + Err(ParseError::Recurse) => return Err(ParseError::Recurse), + Err(ParseError::Continue) => {}, + } + } + + // Guess how does clang treat extern "C" blocks? + if cursor.kind() == clangll::CXCursor_UnexposedDecl { + Err(ParseError::Recurse) + } else { + error!("Unhandled cursor kind: {}", ::clang::kind_to_str(cursor.kind())); + Err(ParseError::Continue) + } + } + + fn from_ty_or_ref(ty: clang::Type, + location: Option<clang::Cursor>, + parent_id: Option<ItemId>, + context: &mut BindgenContext) -> ItemId { + debug!("from_ty_or_ref: {:?}, {:?}, {:?}", ty, location, parent_id); + + if context.collected_typerefs() { + debug!("refs already collected, resolving directly"); + return Self::from_ty(&ty, location, parent_id, context) + .expect("Unable to resolve type"); + } + + if let Some(ty) = context.builtin_or_resolved_ty(parent_id, &ty, location) { + debug!("{:?} already resolved: {:?}", ty, location); + return ty; + } + + debug!("New unresolved type reference: {:?}, {:?}", ty, location); + + let is_const = ty.is_const(); + let kind = TypeKind::UnresolvedTypeRef(ty, location); + let id = ItemId::next(); + let current_module = context.current_module(); + context.add_item(Item::new(id, None, None, + parent_id.unwrap_or(current_module), + ItemKind::Type(Type::new(None, None, kind, is_const))), + Some(clang::Cursor::null()), + None); + id + } + + + fn from_ty(ty: &clang::Type, + location: Option<clang::Cursor>, + parent_id: Option<ItemId>, + context: &mut BindgenContext) -> Result<ItemId, ParseError> { + Self::from_ty_with_id(ItemId::next(), ty, location, parent_id, context) + } + + fn from_ty_with_id(id: ItemId, + ty: &clang::Type, + location: Option<clang::Cursor>, + parent_id: Option<ItemId>, + context: &mut BindgenContext) -> Result<ItemId, ParseError> { + use clangll::*; + + let decl = { + let decl = ty.declaration(); + let definition = decl.definition(); + if definition.is_valid() { + definition + } else { + decl + } + }; + + let comment = + decl.raw_comment() + .or_else(|| location.as_ref().and_then(|l| l.raw_comment())); + let annotations = + Annotations::new(&decl) + .or_else(|| location.as_ref().and_then(|l| Annotations::new(l))); + + if let Some(ref replaced) = annotations.as_ref().and_then(|a| a.use_instead_of()) { + context.replace(replaced, id); + } + + if let Some(ty) = context.builtin_or_resolved_ty(parent_id, ty, location) { + return Ok(ty); + } + + // First, check we're not recursing. + let mut valid_decl = decl.kind() != CXCursor_NoDeclFound; + let declaration_to_look_for = if valid_decl { + decl.canonical() + } else if location.is_some() && location.unwrap().kind() == CXCursor_ClassTemplate { + valid_decl = true; + location.unwrap() + } else { + decl + }; + + if valid_decl { + if let Some(&(_, item_id)) = context.currently_parsed_types.iter().find(|&&(d, _)| d == declaration_to_look_for) { + debug!("Avoiding recursion parsing type: {:?}", ty); + return Ok(item_id); + } + } + + let current_module = context.current_module(); + if valid_decl { + context.currently_parsed_types.push((declaration_to_look_for, id)); + } + + let result = Type::from_clang_ty(id, ty, location, parent_id, context); + let ret = match result { + Ok(ParseResult::AlreadyResolved(ty)) => Ok(ty), + Ok(ParseResult::New(item, declaration)) => { + context.add_item(Item::new(id, comment, annotations, + parent_id.unwrap_or(current_module), + ItemKind::Type(item)), + declaration, + location); + Ok(id) + } + Err(ParseError::Continue) => Err(ParseError::Continue), + Err(ParseError::Recurse) => { + debug!("Item::from_ty recursing in the ast"); + let mut result = Err(ParseError::Recurse); + if let Some(ref location) = location { + // Need to pop here, otherwise we'll get stuck. + // + // TODO: Find a nicer interface, really. Also, the + // declaration_to_look_for suspiciously shares a lot of + // logic with ir::context, so we should refactor that. + if valid_decl { + let (popped_decl, _) = context.currently_parsed_types.pop().unwrap(); + assert_eq!(popped_decl, declaration_to_look_for); + } + + location.visit(|cur, _other| { + use clangll::*; + result = Item::from_ty_with_id(id, ty, Some(*cur), parent_id, context); + match result { + Ok(..) => CXChildVisit_Break, + Err(ParseError::Recurse) => CXChildVisit_Recurse, + Err(ParseError::Continue) => CXChildVisit_Continue, + } + }); + + if valid_decl { + context.currently_parsed_types.push((declaration_to_look_for, id)); + } + } + // If we have recursed into the AST all we know, and we still + // haven't found what we've got, let's + // just make a named type. + // + // This is what happens with some template members, for example. + // + // FIXME: Maybe we should restrict this to things with parent? + // It's harmless, but if we restrict that, then + // tests/headers/nsStyleAutoArray.hpp crashes. + if let Err(ParseError::Recurse) = result { + Ok(Self::named_type_with_id(id, ty.spelling(), + None, + parent_id.unwrap_or(context.current_module()), + context)) + } else { + result + } + } + }; + + if valid_decl { + let (popped_decl, _) = context.currently_parsed_types.pop().unwrap(); + assert_eq!(popped_decl, declaration_to_look_for); + } + + ret + } + + /// A named type is a template parameter, e.g., the "T" in Foo<T>. They're + /// always local so it's the only exception when there's no declaration for + /// a type. + /// + /// It must have an id, and must not be the current module id. Ideally we + /// could assert the parent id is a Comp(..) type, but that info isn't + /// available yet. + fn named_type_with_id<S>(id: ItemId, + name: S, + default: Option<ItemId>, + parent_id: ItemId, + context: &mut BindgenContext) -> ItemId + where S: Into<String> + { + // see tests/headers/const_tparam.hpp + // and tests/headers/variadic_tname.hpp + let name = name.into().replace("const ", "").replace(".", ""); + + context.add_item(Item::new(id, None, None, parent_id, + ItemKind::Type(Type::named(name, default))), + None, + None); + + id + } + + fn named_type<S>(name: S, + default: Option<ItemId>, + parent_id: ItemId, + context: &mut BindgenContext) -> ItemId + where S: Into<String> + { + Self::named_type_with_id(ItemId::next(), name, default, parent_id, context) + } +} + +impl ItemCanonicalName for Item { + fn canonical_name(&self, ctx: &BindgenContext) -> String { + debug_assert!(ctx.in_codegen_phase(), + "You're not supposed to call this yet"); + if let Some(other_canon_type) = self.annotations.use_instead_of() { + return other_canon_type.to_owned(); + } + self.real_canonical_name(ctx, ctx.options().enable_cxx_namespaces) + } +} + +impl ItemCanonicalPath for Item { + fn canonical_path(&self, ctx: &BindgenContext) -> Vec<String> { + if !ctx.options().enable_cxx_namespaces { + return vec![self.canonical_name(ctx)]; + } + + if self.id() == ctx.root_module() { + match self.kind { + ItemKind::Module(ref module) => { + return vec![module.name().unwrap().into()] + } + _ => panic!("Something has wrong horribly wrong"), + } + } + + // TODO: This duplicates too much logic with real_canonical_name. + if let ItemKind::Type(ref ty) = *self.kind() { + match *ty.kind() { + TypeKind::Comp(ref ci) if ci.is_template_specialization() => { + return ci.specialized_template().unwrap().canonical_path(ctx); + }, + TypeKind::ResolvedTypeRef(inner) | + TypeKind::TemplateRef(inner, _) => { + return inner.canonical_path(ctx); + } + TypeKind::Named(ref name, _) => { + return vec![name.clone()]; + } + _ => {} + } + } + + let mut parent_path = self.parent_id().canonical_path(&ctx); + parent_path.push(self.real_canonical_name(ctx, true)); + + parent_path + } +} diff --git a/src/ir/item_kind.rs b/src/ir/item_kind.rs new file mode 100644 index 00000000..b6f317a7 --- /dev/null +++ b/src/ir/item_kind.rs @@ -0,0 +1,89 @@ +use super::function::Function; +use super::module::Module; +use super::ty::Type; +use super::var::Var; + +/// A item we parse and translate. +#[derive(Debug)] +pub enum ItemKind { + /// A module, created implicitly once (the root module), or via C++ + /// namespaces. + Module(Module), + + /// A type declared in any of the multiple ways it can be declared. + Type(Type), + + /// A function or method declaration. + Function(Function), + /// A variable declaration, most likely a static. + Var(Var), +} + +impl ItemKind { + pub fn as_module(&self) -> Option<&Module> { + match *self { + ItemKind::Module(ref module) => Some(module), + _ => None, + } + } + + pub fn is_module(&self) -> bool { + self.as_module().is_some() + } + + pub fn expect_module(&self) -> &Module { + self.as_module().expect("Not a module") + } + + pub fn as_function(&self) -> Option<&Function> { + match *self { + ItemKind::Function(ref func) => Some(func), + _ => None, + } + } + + pub fn is_function(&self) -> bool { + self.as_function().is_some() + } + + pub fn expect_function(&self) -> &Function { + self.as_function().expect("Not a function") + } + + pub fn as_type(&self) -> Option<&Type> { + match *self { + ItemKind::Type(ref ty) => Some(ty), + _ => None, + } + } + + pub fn as_type_mut(&mut self) -> Option<&mut Type> { + match *self { + ItemKind::Type(ref mut ty) => Some(ty), + _ => None, + } + } + + pub fn is_type(&self) -> bool { + self.as_type().is_some() + } + + pub fn expect_type(&self) -> &Type { + self.as_type().expect("Not a type") + } + + pub fn as_var(&self) -> Option<&Var> { + match *self { + ItemKind::Var(ref v) => Some(v), + _ => None, + } + } + + pub fn is_var(&self) -> bool { + self.as_var().is_some() + } + + pub fn expect_var(&self) -> &Var { + self.as_var().expect("Not a var") + } +} diff --git a/src/ir/layout.rs b/src/ir/layout.rs new file mode 100644 index 00000000..d672ebea --- /dev/null +++ b/src/ir/layout.rs @@ -0,0 +1,26 @@ + +/// A type that represents the struct layout of a type. +#[derive(Debug, Clone, Copy)] +pub struct Layout { + pub size: usize, + pub align: usize, + pub packed: bool, +} + +impl Layout { + pub fn new(size: usize, align: usize) -> Self { + Layout { + size: size, + align: align, + packed: false, + } + } + + pub fn is_zero(&self) -> bool { + self.size == 0 && self.align == 0 + } + + pub fn zero() -> Self { + Self::new(0, 0) + } +} diff --git a/src/ir/mod.rs b/src/ir/mod.rs new file mode 100644 index 00000000..07ac3059 --- /dev/null +++ b/src/ir/mod.rs @@ -0,0 +1,12 @@ +pub mod annotations; +pub mod comp; +pub mod context; +pub mod enum_ty; +pub mod function; +pub mod int; +pub mod item; +pub mod item_kind; +pub mod layout; +pub mod module; +pub mod ty; +pub mod var; diff --git a/src/ir/module.rs b/src/ir/module.rs new file mode 100644 index 00000000..77fee5ef --- /dev/null +++ b/src/ir/module.rs @@ -0,0 +1,52 @@ +use super::context::BindgenContext; +use super::item::ItemId; +use clang; +use parse::{ClangSubItemParser, ParseError, ParseResult}; +use parse_one; + +/// A module, as in, a C++ namespace. +#[derive(Clone, Debug)] +pub struct Module { + /// The name of the module, or none if it's anonymous. + name: Option<String>, + /// The children of this module, just here for convenience. + children_ids: Vec<ItemId>, +} + +impl Module { + pub fn new(name: Option<String>) -> Self { + Module { + name: name, + children_ids: vec![], + } + } + + pub fn name(&self) -> Option<&str> { + self.name.as_ref().map(|n| &**n) + } + + pub fn children_mut(&mut self) -> &mut Vec<ItemId> { + &mut self.children_ids + } + + pub fn children(&self) -> &[ItemId] { + &self.children_ids + } +} + +impl ClangSubItemParser for Module { + fn parse(cursor: clang::Cursor, ctx: &mut BindgenContext) -> Result<ParseResult<Self>, ParseError> { + use clangll::*; + match cursor.kind() { + CXCursor_Namespace => { + let module_id = ctx.module(cursor); + ctx.with_module(module_id, |ctx, children| { + cursor.visit(|cursor, _| parse_one(ctx, *cursor, Some(module_id), children)) + }); + + Ok(ParseResult::AlreadyResolved(module_id)) + } + _ => Err(ParseError::Continue) + } + } +} diff --git a/src/ir/ty.rs b/src/ir/ty.rs new file mode 100644 index 00000000..b0448437 --- /dev/null +++ b/src/ir/ty.rs @@ -0,0 +1,537 @@ +use super::comp::CompInfo; +use super::enum_ty::Enum; +use super::function::FunctionSig; +use super::item::{Item, ItemId}; +use super::int::IntKind; +use super::layout::Layout; +use super::context::BindgenContext; +use super::context::TypeResolver; +use parse::{ClangItemParser, ParseResult, ParseError}; +use clang::{self, Cursor}; + +#[derive(Debug)] +pub struct Type { + /// The name of the type, or None if it was an unnamed struct or union. + name: Option<String>, + /// The layout of the type, if known. + layout: Option<Layout>, + /// Whether this type is marked as opaque. + opaque: bool, + /// Whether this type is marked as hidden. + hide: bool, + /// The inner kind of the type + kind: TypeKind, + /// Whether this type is const-qualified. + is_const: bool, +} + +pub const RUST_DERIVE_IN_ARRAY_LIMIT: usize = 32usize; + +impl Type { + pub fn as_comp(&self) -> Option<&CompInfo> { + match self.kind { + TypeKind::Comp(ref ci) => Some(ci), + _ => None, + } + } + + pub fn new(name: Option<String>, + layout: Option<Layout>, + kind: TypeKind, + is_const: bool) -> Self { + Type { + name: name, + layout: layout, + opaque: false, + hide: false, + kind: kind, + is_const: is_const, + } + } + + pub fn kind(&self) -> &TypeKind { + &self.kind + } + + pub fn kind_mut(&mut self) -> &mut TypeKind { + &mut self.kind + } + + pub fn name(&self) -> Option<&str> { + self.name.as_ref().map(|name| &**name) + } + + pub fn is_comp(&self) -> bool { + match self.kind { + TypeKind::Comp(..) => true, + _ => false, + } + } + + pub fn is_named(&self) -> bool { + match self.kind { + TypeKind::Named(..) => true, + _ => false, + } + } + + pub fn is_builtin_or_named(&self) -> bool { + match self.kind { + TypeKind::Void | + TypeKind::NullPtr | + TypeKind::Function(..) | + TypeKind::Array(..) | + TypeKind::Reference(..) | + TypeKind::Pointer(..) | + TypeKind::Int(..) | + TypeKind::Float(..) | + TypeKind::Named(..) => true, + _ => false, + } + } + + /// Creates a new named type, with name `name`. + pub fn named(name: String, default: Option<ItemId>) -> Self { + assert!(!name.is_empty()); + // TODO: stop duplicating the name, it's stupid. + let kind = TypeKind::Named(name.clone(), default); + Self::new(Some(name), None, kind, false) + } + + pub fn is_integer_literal(&self) -> bool { + match *self.kind() { + TypeKind::Int(..) => true, + _ => false, + } + } + + pub fn is_const(&self) -> bool { + self.is_const + } + + pub fn layout(&self, type_resolver: &TypeResolver) -> Option<Layout> { + use std::mem; + + self.layout.or_else(|| { + match self.kind { + TypeKind::Comp(ref ci) + => ci.layout(type_resolver), + // FIXME(emilio): This is a hack for anonymous union templates. + // Use the actual pointer size! + TypeKind::Pointer(..) + => Some(Layout::new(mem::size_of::<*mut ()>(), mem::align_of::<*mut ()>())), + TypeKind::ResolvedTypeRef(inner) + => type_resolver.resolve_type(inner).layout(type_resolver), + _ => None, + } + }) + } + + pub fn is_opaque(&self, _type_resolver: &TypeResolver) -> bool { + self.opaque + } + + pub fn can_derive_debug(&self, type_resolver: &TypeResolver) -> bool { + !self.is_opaque(type_resolver) && match self.kind { + TypeKind::Array(t, len) => { + len <= RUST_DERIVE_IN_ARRAY_LIMIT && + type_resolver.resolve_type(t).can_derive_debug(type_resolver) + } + TypeKind::ResolvedTypeRef(t) | + TypeKind::Alias(_, t) => { + type_resolver.resolve_type(t).can_derive_debug(type_resolver) + } + TypeKind::Comp(ref info) => { + info.can_derive_debug(type_resolver, self.layout(type_resolver)) + } + _ => true, + } + } + + // For some reason, deriving copies of an array of a type that is not known + // to be copy is a compile error. e.g.: + // + // #[derive(Copy)] + // struct A<T> { + // member: T, + // } + // + // is fine, while: + // + // #[derive(Copy)] + // struct A<T> { + // member: [T; 1], + // } + // + // is an error. + // + // That's the point of the existence of can_derive_copy_in_array(). + pub fn can_derive_copy_in_array(&self, type_resolver: &TypeResolver) -> bool { + match self.kind { + TypeKind::ResolvedTypeRef(t) | + TypeKind::Alias(_, t) | + TypeKind::Array(t, _) => { + type_resolver.resolve_type(t) + .can_derive_copy_in_array(type_resolver) + } + TypeKind::Named(..) => false, + _ => self.can_derive_copy(type_resolver), + } + } + + pub fn can_derive_copy(&self, type_resolver: &TypeResolver) -> bool { + !self.is_opaque(type_resolver) && match self.kind { + TypeKind::Array(t, len) => { + len <= RUST_DERIVE_IN_ARRAY_LIMIT && + type_resolver.resolve_type(t).can_derive_copy_in_array(type_resolver) + } + TypeKind::ResolvedTypeRef(t) | + TypeKind::TemplateRef(t, _) | + TypeKind::Alias(_, t) => { + type_resolver.resolve_type(t).can_derive_copy(type_resolver) + } + TypeKind::Comp(ref info) => { + info.can_derive_copy(type_resolver) + } + _ => true, + } + } + + pub fn has_vtable(&self, type_resolver: &TypeResolver) -> bool { + // FIXME: Can we do something about template parameters? Huh... + match self.kind { + TypeKind::TemplateRef(t, _) | + TypeKind::Alias(_, t) | + TypeKind::ResolvedTypeRef(t) | + TypeKind::Array(t, _) => { + type_resolver.resolve_type(t).has_vtable(type_resolver) + } + TypeKind::Comp(ref info) => { + info.has_vtable(type_resolver) + } + _ => false, + } + + } + + pub fn has_destructor(&self, type_resolver: &TypeResolver) -> bool { + self.is_opaque(type_resolver) || match self.kind { + TypeKind::TemplateRef(t, _) | + TypeKind::Alias(_, t) | + TypeKind::ResolvedTypeRef(t) | + TypeKind::Array(t, _) => { + type_resolver.resolve_type(t).has_destructor(type_resolver) + } + TypeKind::Comp(ref info) => { + info.has_destructor(type_resolver) + } + _ => false, + } + } + + pub fn signature_contains_named_type(&self, + type_resolver: &TypeResolver, + ty: &Type) -> bool { + debug_assert!(ty.is_named()); + let name = match *ty.kind() { + TypeKind::Named(ref name, _) => name, + _ => unreachable!(), + }; + + match self.kind { + TypeKind::Named(ref this_name, _) + => this_name == name, + TypeKind::ResolvedTypeRef(t) | + TypeKind::Array(t, _) | + TypeKind::Pointer(t) + => type_resolver.resolve_type(t) + .signature_contains_named_type(type_resolver, ty), + TypeKind::TemplateRef(_inner, ref template_args) => { + template_args.iter().any(|arg| { + type_resolver.resolve_type(*arg) + .signature_contains_named_type(type_resolver, ty) + }) + } + TypeKind::Comp(ref ci) + => ci.signature_contains_named_type(type_resolver, ty), + _ => false, + } + } + + pub fn canonical_type<'tr>(&'tr self, type_resolver: &'tr TypeResolver) -> &'tr Type { + match self.kind { + TypeKind::Named(..) | + TypeKind::Array(..) | + TypeKind::Comp(..) | + TypeKind::Int(..) | + TypeKind::Float(..) | + TypeKind::Function(..) | + TypeKind::Enum(..) | + TypeKind::Reference(..) | + TypeKind::Void | + TypeKind::NullPtr | + TypeKind::Pointer(..) => self, + + TypeKind::ResolvedTypeRef(inner) | + TypeKind::Alias(_, inner) | + TypeKind::TemplateRef(inner, _) + => type_resolver.resolve_type(inner).canonical_type(type_resolver), + + TypeKind::UnresolvedTypeRef(..) + => unreachable!("Should have been resolved after parsing!"), + } + } +} + +#[derive(Debug, Copy, Clone, PartialEq)] +pub enum FloatKind { + Float, + Double, + LongDouble, +} + +/// The different kinds of types that we can parse. +/// +/// TODO: The name in the Alias and Named kinds is a bit unsound, should be in +/// type.name? +#[derive(Debug)] +pub enum TypeKind { + /// The void type. + Void, + /// The nullptr_t type. + NullPtr, + /// A compound type, that is, a class, struct, or union. + Comp(CompInfo), + /// An integer type, of a given kind. `bool` and `char` are also considered + /// integers. + Int(IntKind), + /// A floating point type. + Float(FloatKind), + /// A type alias, with a name, that points to another type. + Alias(String, ItemId), + /// An array of a type and a lenght. + Array(ItemId, usize), + /// A function type, with a given signature. + Function(FunctionSig), + /// An enum type. + Enum(Enum), + /// A pointer to a type. The bool field represents whether it's const or + /// not. + Pointer(ItemId), + /// A reference to a type, as in: int& foo(). + Reference(ItemId), + /// A reference to a template, with different template parameter names. To + /// see why this is needed, check out the creation of this variant in + /// `Type::from_clang_ty`. + TemplateRef(ItemId, Vec<ItemId>), + + /// A reference to a yet-to-resolve type. This stores the clang cursor + /// itself, and postpones its resolution. + /// + /// These are gone in a phase after parsing where these are mapped to + /// already known types, and are converted to ResolvedTypeRef. + /// + /// see tests/headers/typeref.hpp to see somewhere where this is a problem. + UnresolvedTypeRef(clang::Type, Option<clang::Cursor>), + ResolvedTypeRef(ItemId), + + /// A named type, that is, a template parameter, with an optional default + /// type. + Named(String, Option<ItemId>), +} + +impl Type { + pub fn is_unsized(&self, type_resolver: &TypeResolver) -> bool { + match self.kind { + TypeKind::Void => true, + TypeKind::Comp(ref ci) => ci.is_unsized(type_resolver), + TypeKind::Array(inner, size) => { + size == 0 || + type_resolver.resolve_type(inner).is_unsized(type_resolver) + } + TypeKind::ResolvedTypeRef(inner) | + TypeKind::Alias(_, inner) | + TypeKind::TemplateRef(inner, _) + => type_resolver.resolve_type(inner).is_unsized(type_resolver), + TypeKind::Named(..) | + TypeKind::Int(..) | + TypeKind::Float(..) | + TypeKind::Function(..) | + TypeKind::Enum(..) | + TypeKind::Reference(..) | + TypeKind::NullPtr | + TypeKind::Pointer(..) => false, + + TypeKind::UnresolvedTypeRef(..) + => unreachable!("Should have been resolved after parsing!"), + } + } + + pub fn from_clang_ty(potential_id: ItemId, + ty: &clang::Type, + location: Option<Cursor>, + parent_id: Option<ItemId>, + ctx: &mut BindgenContext) -> Result<ParseResult<Self>, ParseError> { + use clangll::*; + if let Some(ty) = ctx.builtin_or_resolved_ty(parent_id, ty, location) { + debug!("{:?} already resolved: {:?}", ty, location); + return Ok(ParseResult::AlreadyResolved(ty)); + } + + let layout = ty.fallible_layout().ok(); + let cursor = ty.declaration(); + let mut name = cursor.spelling(); + + debug!("from_clang_ty: {:?}, ty: {:?}, loc: {:?}", potential_id, ty, location); + debug!("currently_parsed_types: {:?}", ctx.currently_parsed_types); + + let canonical_ty = ty.canonical_type(); + let kind = match ty.kind() { + CXType_Unexposed if *ty != canonical_ty && + canonical_ty.kind() != CXType_Invalid => { + debug!("Looking for canonical type: {:?}", canonical_ty); + return Self::from_clang_ty(potential_id, &canonical_ty, + location, parent_id, ctx); + } + CXType_Unexposed | + CXType_Invalid => { + // For some reason Clang doesn't give us any hint + // in some situations where we should generate a + // function pointer (see + // tests/headers/func_ptr_in_struct.h), so we do a + // guess here trying to see if it has a valid return + // type. + if ty.ret_type().kind() != CXType_Invalid { + let signature = + try!(FunctionSig::from_ty(ty, &location.unwrap_or(cursor), ctx)); + TypeKind::Function(signature) + // Same here, with template specialisations we can safely assume + // this is a Comp(..) + } else if ty.num_template_args() > 0 { + debug!("Template specialization: {:?}", ty); + let complex = + CompInfo::from_ty(potential_id, ty, location, ctx) + .expect("C'mon"); + TypeKind::Comp(complex) + } else if let Some(location) = location { + match location.kind() { + CXCursor_ClassTemplatePartialSpecialization | + CXCursor_ClassTemplate => { + name = location.spelling(); + let complex = + CompInfo::from_ty(potential_id, ty, Some(location), ctx) + .expect("C'mon"); + TypeKind::Comp(complex) + } + CXCursor_TemplateRef => { + let referenced = location.referenced(); + return Self::from_clang_ty(potential_id, + &referenced.cur_type(), + Some(referenced), + parent_id, + ctx); + } + CXCursor_TypeRef => { + let referenced = location.referenced(); + // FIXME: use potential id? + return Ok(ParseResult::AlreadyResolved( + Item::from_ty_or_ref(referenced.cur_type(), + Some(referenced), + parent_id, + ctx))); + } + _ => { + if ty.kind() == CXType_Unexposed { + warn!("Unexposed type {:?}, recursing inside, loc: {:?}", ty, location); + return Err(ParseError::Recurse); + } + + error!("invalid type {:?}", ty); + return Err(ParseError::Continue); + } + } + } else { + // TODO: Don't duplicate this! + if ty.kind() == CXType_Unexposed { + warn!("Unexposed type {:?}, recursing inside", ty); + return Err(ParseError::Recurse); + } + + error!("invalid type `{}`", ty.spelling()); + return Err(ParseError::Continue); + } + } + // NOTE: We don't resolve pointers eagerly because the pointee type + // might not have been parsed, and if it contains templates or + // something else we might get confused, see the comment inside + // TypeRef. + // + // We might need to, though, if the context is already in the + // process of resolving them. + CXType_MemberPointer | + CXType_Pointer => { + let inner = + Item::from_ty_or_ref(ty.pointee_type(), Some(ty.pointee_type().declaration()), parent_id, ctx); + TypeKind::Pointer(inner) + } + // XXX: RValueReference is most likely wrong, but I don't think we + // can even add bindings for that, so huh. + CXType_RValueReference | + CXType_LValueReference => { + let inner = + Item::from_ty_or_ref(ty.pointee_type(), Some(ty.pointee_type().declaration()), parent_id, ctx); + TypeKind::Reference(inner) + } + // XXX DependentSizedArray is wrong + CXType_VariableArray | + CXType_DependentSizedArray | + CXType_IncompleteArray => { + let inner = Item::from_ty(&ty.elem_type(), location, parent_id, ctx) + .expect("Not able to resolve array element?"); + TypeKind::Pointer(inner) + } + CXType_FunctionNoProto | + CXType_FunctionProto => { + let signature = try!(FunctionSig::from_ty(ty, &location.unwrap_or(cursor), ctx)); + TypeKind::Function(signature) + } + CXType_Typedef => { + let inner = cursor.typedef_type(); + let inner = + Item::from_ty_or_ref(inner, location, parent_id, ctx); + TypeKind::Alias(ty.spelling(), inner) + } + CXType_Enum => { + let enum_ = Enum::from_ty(ty, ctx) + .expect("Not an enum?"); + TypeKind::Enum(enum_) + } + CXType_Record => { + let complex = CompInfo::from_ty(potential_id, ty, location, ctx) + .expect("Not a complex type?"); + TypeKind::Comp(complex) + } + CXType_ConstantArray => { + let inner = Item::from_ty(&ty.elem_type(), location, parent_id, ctx) + .expect("Not able to resolve array element?"); + TypeKind::Array(inner, ty.array_size()) + } + #[cfg(not(feature="llvm_stable"))] + CXType_Elaborated => { + return Self::from_clang_ty(potential_id, &ty.named(), + location, parent_id, ctx); + } + _ => { + error!("unsupported type {:?} at {:?}", ty, location); + return Err(ParseError::Continue); + } + }; + + let name = if name.is_empty() { None } else { Some(name) }; + let is_const = ty.is_const(); + + let ty = Type::new(name, layout, kind, is_const); + // TODO: maybe declaration.canonical()? + Ok(ParseResult::New(ty, Some(cursor.canonical()))) + } +} diff --git a/src/ir/var.rs b/src/ir/var.rs new file mode 100644 index 00000000..ac59973b --- /dev/null +++ b/src/ir/var.rs @@ -0,0 +1,160 @@ +use super::item::{Item, ItemId}; +use super::context::BindgenContext; +use super::ty::TypeKind; +use super::int::IntKind; +use super::function::cursor_mangling; +use parse::{ClangItemParser, ClangSubItemParser, ParseResult, ParseError}; +use clang; + +#[derive(Debug)] +pub struct Var { + /// The name of the variable. + name: String, + /// The mangled name of the variable. + mangled_name: Option<String>, + /// The type of the variable. + ty: ItemId, + /// TODO: support non-integer constants? + /// The integer value of the variable. + val: Option<i64>, + /// Whether this variable is const. + is_const: bool, +} + +impl Var { + pub fn new(name: String, + mangled: Option<String>, + ty: ItemId, + val: Option<i64>, + is_const: bool) -> Var { + assert!(!name.is_empty()); + Var { + name: name, + mangled_name: mangled, + ty: ty, + val: val, + is_const: is_const, + } + } + + pub fn is_const(&self) -> bool { + self.is_const + } + + pub fn val(&self) -> Option<i64> { + self.val + } + + pub fn ty(&self) -> ItemId { + self.ty + } + + pub fn name(&self) -> &str { + &self.name + } + + pub fn mangled_name(&self) -> Option<&str> { + self.mangled_name.as_ref().map(|n| &**n) + } +} + +impl ClangSubItemParser for Var { + fn parse(cursor: clang::Cursor, + context: &mut BindgenContext) -> Result<ParseResult<Self>, ParseError> { + use clangll::*; + match cursor.kind() { + CXCursor_MacroDefinition => { + let value = match parse_int_literal_tokens(&cursor, context.translation_unit(), 1) { + None => return Err(ParseError::Continue), + Some(v) => v, + }; + + let name = cursor.spelling(); + if name.is_empty() { + warn!("Empty macro name?"); + return Err(ParseError::Continue); + } + + if context.parsed_macro(&name) { + warn!("Duplicated macro definition: {}", name); + return Err(ParseError::Continue); + } + context.note_parsed_macro(name.clone()); + + let ty = if value.abs() > u32::max_value() as i64 { + Item::builtin_type(TypeKind::Int(IntKind::ULongLong), true, context) + } else { + Item::builtin_type(TypeKind::Int(IntKind::UInt), true, context) + }; + + Ok(ParseResult::New(Var::new(name, None, ty, Some(value), true), Some(cursor))) + } + CXCursor_VarDecl => { + let name = cursor.spelling(); + if name.is_empty() { + warn!("Empty constant name?"); + return Err(ParseError::Continue); + } + + let ty = cursor.cur_type(); + + // XXX this is redundant, remove! + let is_const = ty.is_const(); + + let ty = Item::from_ty(&ty, Some(cursor), None, context) + .expect("Unable to resolve constant type?"); + + let mut value = None; + + // Note: Ty might not be totally resolved yet, see + // tests/headers/inner_const.hpp + // + // That's fine because in that case we know it's not a literal. + if context.safe_resolve_type(ty).map_or(false, |t| t.is_integer_literal()) { + // Try to parse a literal token value + cursor.visit(|c, _| { + if c.kind() == CXCursor_IntegerLiteral { + value = + parse_int_literal_tokens(&c, + context.translation_unit(), + 0); + } + CXChildVisit_Continue + }); + } + + let mangling = cursor_mangling(&cursor); + + let var = Var::new(name, mangling, ty, value, is_const); + Ok(ParseResult::New(var, Some(cursor))) + + } + _ => { + /* TODO */ + Err(ParseError::Continue) + } + } + } +} + +fn parse_int_literal_tokens(cursor: &clang::Cursor, + unit: &clang::TranslationUnit, + which: usize) -> Option<i64> { + use clangll::CXToken_Literal; + let tokens = match unit.tokens(cursor) { + None => return None, + Some(tokens) => tokens, + }; + + if tokens.len() <= which || tokens[which].kind != CXToken_Literal { + return None; + } + + let s = &tokens[which].spelling; + // TODO: try to preserve hex literals? + if s.starts_with("0x") { + i64::from_str_radix(&s[2..], 16).ok() + } else { + s.parse().ok() + } +} |