diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/bin/bindgen.rs | 48 | ||||
-rw-r--r-- | src/clang.rs | 170 | ||||
-rw-r--r-- | src/clangll.rs | 26 | ||||
-rw-r--r-- | src/gen.rs | 1573 | ||||
-rw-r--r-- | src/lib.rs | 57 | ||||
-rw-r--r-- | src/parser.rs | 694 | ||||
-rw-r--r-- | src/types.rs | 154 |
7 files changed, 2044 insertions, 678 deletions
diff --git a/src/bin/bindgen.rs b/src/bin/bindgen.rs index c049f9a1..b0a3c8e8 100644 --- a/src/bin/bindgen.rs +++ b/src/bin/bindgen.rs @@ -2,7 +2,8 @@ #![crate_type = "bin"] extern crate bindgen; -#[macro_use] extern crate log; +#[macro_use] +extern crate log; use bindgen::{Bindings, BindgenOptions, LinkType, Logger}; use std::io; @@ -16,17 +17,17 @@ struct StdLogger; impl Logger for StdLogger { fn error(&self, msg: &str) { - error!("{}", msg); + println!("{}", msg); } fn warn(&self, msg: &str) { - warn!("{}", msg); + println!("{}", msg); } } enum ParseResult { CmdUsage, - ParseOk(BindgenOptions, Box<io::Write+'static>), + ParseOk(BindgenOptions, Box<io::Write>), ParseErr(String) } @@ -34,7 +35,6 @@ fn parse_args(args: &[String]) -> ParseResult { let args_len = args.len(); let mut options: BindgenOptions = Default::default(); - options.derive_debug = false; let mut out = Box::new(io::BufWriter::new(io::stdout())) as Box<io::Write>; if args_len == 0 { @@ -47,7 +47,7 @@ fn parse_args(args: &[String]) -> ParseResult { options.links.push((args[ix][2..].to_string(), LinkType::Default)); ix += 1; } else { - match &args[ix][..] { + match &*args[ix] { "--help" | "-h" => { return ParseResult::CmdUsage; } @@ -98,12 +98,8 @@ fn parse_args(args: &[String]) -> ParseResult { options.builtins = true; ix += 1; } - "-no-rust-enums" => { - options.rust_enums = false; - ix += 1; - } - "-derive-debug" => { - options.derive_debug = true; + "-ignore-functions" => { + options.ignore_functions = true; ix += 1; } "-allow-unknown-types" => { @@ -117,6 +113,14 @@ fn parse_args(args: &[String]) -> ParseResult { options.override_enum_ty = args[ix + 1].clone(); ix += 2; } + "-enable-cxx-namespaces" => { + options.enable_cxx_namespaces = true; + ix += 1; + } + "-no-type-renaming" => { + options.rename_types = false; + ix += 1; + } _ => { options.clang_args.push(args[ix].clone()); ix += 1; @@ -129,7 +133,7 @@ fn parse_args(args: &[String]) -> ParseResult { } fn print_usage(bin: String) { - let mut s = format!("Usage: {} [options] input.h", &bin[..]); + let mut s = format!("Usage: {} [options] input.h", &bin); s.push_str( " Options: @@ -145,6 +149,10 @@ Options: matching any rule are bound to. -builtins Output bindings for builtin definitions (for example __builtin_va_list) + -ignore-functions Don't generate bindings for functions and methods. + This is useful when you only care about struct layouts. + -enable-cxx-namespaces Enable support for C++ namespaces. + -no-type-renaming Don't rename types. -allow-unknown-types Don't fail if we encounter types we do not support, instead treat them as void -emit-clang-ast Output the ast (for debugging purposes) @@ -163,14 +171,22 @@ Options: Options other than stated above are passed to clang. " ); - print!("{}", &s[..]); + println!("{}", &s); } pub fn main() { let mut bind_args: Vec<_> = env::args().collect(); let bin = bind_args.remove(0); - match parse_args(&bind_args[..]) { + match bindgen::get_include_dir() { + Some(path) => { + bind_args.push("-I".to_owned()); + bind_args.push(path); + } + None => (), + } + + match parse_args(&bind_args) { ParseResult::ParseErr(e) => panic!(e), ParseResult::CmdUsage => print_usage(bin), ParseResult::ParseOk(options, out) => { @@ -179,7 +195,7 @@ pub fn main() { Ok(bindings) => match bindings.write(out) { Ok(()) => (), Err(e) => { - logger.error(&format!("Unable to write bindings to file. {}", e)[..]); + logger.error(&format!("Unable to write bindings to file. {}", e)); exit(-1); } }, diff --git a/src/clang.rs b/src/clang.rs index a8c4aace..be07a0e3 100644 --- a/src/clang.rs +++ b/src/clang.rs @@ -3,11 +3,9 @@ use std::os::raw::{c_uint, c_char, c_int, c_ulong}; use std::{mem, ptr}; use std::fmt; -use std::str; -use std::ffi::CStr; use std::hash::Hash; use std::hash::Hasher; -use std::ffi::CString; +use std::ffi::{CString, CStr}; use clangll::*; @@ -27,12 +25,40 @@ impl Cursor { } } + pub fn mangling(&self) -> String { + let mut mangling = unsafe { + String_ { x: clang_Cursor_getMangling(self.x) }.to_string() + }; + + // Try to undo backend mangling + if cfg!(target_os = "macos") || cfg!(target_os = "windows") { + mangling.remove(0); + } + mangling + } + + pub fn semantic_parent(&self) -> Cursor { + unsafe { + Cursor { x: clang_getCursorSemanticParent(self.x) } + } + } + pub fn kind(&self) -> Enum_CXCursorKind { unsafe { clang_getCursorKind(self.x) } } + pub fn is_template(&self) -> bool { + self.specialized().is_valid() + } + + pub fn is_valid(&self) -> bool { + unsafe { + clang_isInvalid(self.kind()) == 0 + } + } + pub fn location(&self) -> SourceLocation { unsafe { SourceLocation { x: clang_getCursorLocation(self.x) } @@ -45,6 +71,18 @@ impl Cursor { } } + pub fn raw_comment(&self) -> String { + unsafe { + String_ { x: clang_Cursor_getRawCommentText(self.x) }.to_string() + } + } + + pub fn comment(&self) -> Comment { + unsafe { + Comment { x: clang_Cursor_getParsedComment(self.x) } + } + } + pub fn cur_type(&self) -> Type { unsafe { Type { x: clang_getCursorType(self.x) } @@ -63,6 +101,12 @@ impl Cursor { } } + pub fn specialized(&self) -> Cursor { + unsafe { + Cursor { x: clang_getSpecializedCursorTemplate(self.x) } + } + } + pub fn visit<F>(&self, func:F) where F: for<'a, 'b> FnMut(&'a Cursor, &'b Cursor) -> Enum_CXChildVisitResult { @@ -112,6 +156,12 @@ impl Cursor { } } + pub fn visibility(&self) -> Enum_CXVisibilityKind { + unsafe { + clang_getCursorVisibility(self.x) + } + } + // function pub fn args(&self) -> Vec<Cursor> { unsafe { @@ -135,6 +185,33 @@ impl Cursor { clang_Cursor_getNumArguments(self.x) } } + + // CXX member + pub fn access_specifier(&self) -> Enum_CX_CXXAccessSpecifier { + unsafe { + clang_getCXXAccessSpecifier(self.x) + } + } + + // CXX method + pub fn method_is_static(&self) -> bool { + unsafe { + clang_CXXMethod_isStatic(self.x) != 0 + } + } + + pub fn method_is_virtual(&self) -> bool { + unsafe { + clang_CXXMethod_isVirtual(self.x) != 0 + } + } + + // CXX base + pub fn is_virtual_base(&self) -> bool { + unsafe { + clang_isVirtualBase(self.x) != 0 + } + } } extern fn visit_children(cur: CXCursor, parent: CXCursor, @@ -184,6 +261,12 @@ impl Type { } } + pub fn spelling(&self) -> String { + unsafe { + String_ { x: clang_getTypeSpelling(self.x) }.to_string() + } + } + pub fn is_const(&self) -> bool { unsafe { clang_isConstQualifiedType(self.x) == 1 @@ -204,6 +287,18 @@ impl Type { } } + pub fn num_template_args(&self) -> c_int { + unsafe { + clang_Type_getNumTemplateArguments(self.x) + } + } + + pub fn template_arg_type(&self, i: c_int) -> Type { + unsafe { + Type { x: clang_Type_getTemplateArgumentAsType(self.x, i) } + } + } + // pointer pub fn pointee_type(&self) -> Type { unsafe { @@ -291,6 +386,56 @@ impl fmt::Display for SourceLocation { } } +// Comment +pub struct Comment { + x: CXComment +} + +impl Comment { + pub fn kind(&self) -> Enum_CXCommentKind { + unsafe { + clang_Comment_getKind(self.x) + } + } + + pub fn num_children(&self) -> c_uint { + unsafe { + clang_Comment_getNumChildren(self.x) + } + } + + pub fn get_child(&self, idx: c_uint) -> Comment { + unsafe { + Comment { x: clang_Comment_getChild(self.x, idx) } + } + } + + // HTML + pub fn get_tag_name(&self) -> String { + unsafe { + String_ { x: clang_HTMLTagComment_getTagName(self.x) }.to_string() + } + } + + pub fn get_num_tag_attrs(&self) -> c_uint { + unsafe { + clang_HTMLStartTag_getNumAttrs(self.x) + } + } + + pub fn get_tag_attr_name(&self, idx: c_uint) -> String { + unsafe { + String_ { x: clang_HTMLStartTag_getAttrName(self.x, idx) }.to_string() + } + } + + pub fn get_tag_attr_value(&self, idx: c_uint) -> String { + unsafe { + String_ { x: clang_HTMLStartTag_getAttrValue(self.x, idx) }.to_string() + } + } +} + // File pub struct File { x: CXFile @@ -320,7 +465,7 @@ impl fmt::Display for String_ { unsafe { let c_str = clang_getCString(self.x) as *const c_char; let p = c_str as *const _; - str::from_utf8(CStr::from_ptr(p).to_bytes()).unwrap().to_owned().fmt(f) + f.write_str(&String::from_utf8_lossy(CStr::from_ptr(p).to_bytes())) } } } @@ -361,14 +506,13 @@ pub struct TranslationUnit { impl TranslationUnit { pub fn parse(ix: &Index, file: &str, cmd_args: &[String], - unsaved: &[UnsavedFile], opts: c_uint) -> TranslationUnit { - let fname = CString::new(file.as_bytes()).unwrap(); - let fname = fname.as_ptr(); - let c_args: Vec<CString> = cmd_args.iter().map(|s| CString::new(s.as_bytes()).unwrap()).collect(); - let c_args: Vec<*const c_char> = c_args.iter().map(|s| s.as_ptr()).collect(); + unsaved: &[UnsavedFile], opts: ::libc::c_uint) -> TranslationUnit { + let fname = CString::new(file).unwrap(); + let _c_args: Vec<CString> = cmd_args.iter().map(|s| CString::new(s.clone()).unwrap()).collect(); + let c_args: Vec<*const c_char> = _c_args.iter().map(|s| s.as_ptr()).collect(); let mut c_unsaved: Vec<Struct_CXUnsavedFile> = unsaved.iter().map(|f| f.x).collect(); let tu = unsafe { - clang_parseTranslationUnit(ix.x, fname, + clang_parseTranslationUnit(ix.x, fname.as_ptr(), c_args.as_ptr(), c_args.len() as c_int, c_unsaved.as_mut_ptr(), @@ -478,8 +622,8 @@ pub struct UnsavedFile { impl UnsavedFile { pub fn new(name: &str, contents: &str) -> UnsavedFile { - let name = CString::new(name.as_bytes()).unwrap(); - let contents = CString::new(contents.as_bytes()).unwrap(); + let name = CString::new(name).unwrap(); + let contents = CString::new(contents).unwrap(); let x = Struct_CXUnsavedFile { Filename: name.as_ptr(), Contents: contents.as_ptr(), @@ -729,7 +873,7 @@ pub fn ast_dump(c: &Cursor, depth: isize)-> Enum_CXVisitorResult { print_indent(depth, &format!("({} {} {}", kind_to_str(c.kind()), c.spelling(), - type_to_str(ct))[..] + type_to_str(ct)) ); c.visit(| s, _: &Cursor| { ast_dump(s, depth + 1) diff --git a/src/clangll.rs b/src/clangll.rs index 208df235..bd1652f0 100644 --- a/src/clangll.rs +++ b/src/clangll.rs @@ -328,12 +328,17 @@ pub struct CXComment { pub ASTNode: *const c_void, pub TranslationUnit: CXTranslationUnit, } -pub type Enum_CXLinkageKind = c_uint; -pub const CXLinkage_Invalid: c_uint = 0; -pub const CXLinkage_NoLinkage: c_uint = 1; -pub const CXLinkage_Internal: c_uint = 2; -pub const CXLinkage_UniqueExternal: c_uint = 3; -pub const CXLinkage_External: c_uint = 4; +pub type Enum_CXLinkageKind = ::libc::c_uint; +pub const CXLinkage_Invalid: ::libc::c_uint = 0; +pub const CXLinkage_NoLinkage: ::libc::c_uint = 1; +pub const CXLinkage_Internal: ::libc::c_uint = 2; +pub const CXLinkage_UniqueExternal: ::libc::c_uint = 3; +pub const CXLinkage_External: ::libc::c_uint = 4; +pub type Enum_CXVisibilityKind = ::libc::c_uint; +pub const CXVisibility_Invalid: ::libc::c_uint = 0; +pub const CXVisibility_Hidden: ::libc::c_uint = 1; +pub const CXVisibility_Protected: ::libc::c_uint = 2; +pub const CXVisibility_Default: ::libc::c_uint = 3; #[repr(C)] #[derive(Copy, Clone)] pub struct Struct_CXPlatformAvailability { @@ -1005,6 +1010,7 @@ extern "C" { pub fn clang_isPreprocessing(arg1: Enum_CXCursorKind) -> c_uint; pub fn clang_isUnexposed(arg1: Enum_CXCursorKind) -> c_uint; pub fn clang_getCursorLinkage(cursor: CXCursor) -> Enum_CXLinkageKind; + pub fn clang_getCursorVisibility(cursor: CXCursor) -> Enum_CXVisibilityKind; pub fn clang_getCursorAvailability(cursor: CXCursor) -> Enum_CXAvailabilityKind; pub fn clang_getCursorPlatformAvailability(cursor: CXCursor, @@ -1081,8 +1087,11 @@ extern "C" { c_longlong; pub fn clang_Type_getCXXRefQualifier(T: CXType) -> Enum_CXRefQualifierKind; - pub fn clang_Cursor_isBitField(C: CXCursor) -> c_uint; - pub fn clang_isVirtualBase(arg1: CXCursor) -> c_uint; + pub fn clang_Type_getNumTemplateArguments(T: CXType) -> ::libc::c_int; + pub fn clang_Type_getTemplateArgumentAsType(T: CXType, i: ::libc::c_int) -> + CXType; + pub fn clang_Cursor_isBitField(C: CXCursor) -> ::libc::c_uint; + pub fn clang_isVirtualBase(arg1: CXCursor) -> ::libc::c_uint; pub fn clang_getCXXAccessSpecifier(arg1: CXCursor) -> Enum_CX_CXXAccessSpecifier; pub fn clang_getNumOverloadedDecls(cursor: CXCursor) -> c_uint; @@ -1130,6 +1139,7 @@ extern "C" { pub fn clang_Cursor_getCommentRange(C: CXCursor) -> CXSourceRange; pub fn clang_Cursor_getRawCommentText(C: CXCursor) -> CXString; pub fn clang_Cursor_getBriefCommentText(C: CXCursor) -> CXString; + pub fn clang_Cursor_getMangling(C: CXCursor) -> CXString; pub fn clang_Cursor_getParsedComment(C: CXCursor) -> CXComment; pub fn clang_Cursor_getModule(C: CXCursor) -> CXModule; pub fn clang_Module_getASTFile(Module: CXModule) -> CXFile; @@ -3,9 +3,7 @@ use std::cell::RefCell; use std::vec::Vec; use std::rc::Rc; use std::collections::HashMap; -use std::collections::hash_map::Entry; - -use syntax::abi; +use syntax::abi::Abi; use syntax::ast; use syntax::codemap::{Span, Spanned, respan, ExpnInfo, NameAndSpan, MacroBang}; use syntax::ext::base; @@ -15,18 +13,45 @@ use syntax::ext::quote::rt::ToTokens; use syntax::feature_gate::Features; use syntax::owned_slice::OwnedSlice; use syntax::parse; -use syntax::parse::token::InternedString; +use syntax::parse::token::{InternedString, intern}; use syntax::attr::mk_attr_id; use syntax::ptr::P; use syntax::print::pprust::tts_to_string; -use super::{BindgenOptions, LinkType}; +use super::BindgenOptions; +use super::LinkType; use types::*; struct GenCtx<'r> { ext_cx: base::ExtCtxt<'r>, - unnamed_ty: usize, - span: Span + options: BindgenOptions, + span: Span, + module_map: ModuleMap, + current_module_id: ModuleId, +} + +impl<'r> GenCtx<'r> { + fn full_path_for_module(&self, id: ModuleId) -> Vec<String> { + if !self.options.enable_cxx_namespaces { + return vec![]; + } + + let mut ret = vec![]; + + let mut current_id = Some(id); + while let Some(current) = current_id { + let module = &self.module_map.get(¤t).unwrap(); + ret.push(module.name.clone()); + current_id = module.parent_id; + } + + if self.current_module_id == ROOT_MODULE_ID { + ret.pop(); // The root module doens'n need a root:: in the pattern + } + + ret.reverse(); + ret + } } fn first<A, B>((val, _): (A, B)) -> A { @@ -37,11 +62,6 @@ fn ref_eq<T>(thing: &T, other: &T) -> bool { (thing as *const T) == (other as *const T) } -fn to_intern_str(ctx: &mut GenCtx, s: String) -> parse::token::InternedString { - let id = ctx.ext_cx.ident_of(&s[..]); - id.name.as_str() -} - fn empty_generics() -> ast::Generics { ast::Generics { lifetimes: Vec::new(), @@ -53,73 +73,191 @@ fn empty_generics() -> ast::Generics { } } -fn rust_id(ctx: &mut GenCtx, name: String) -> (String, bool) { - let token = parse::token::Ident(ctx.ext_cx.ident_of(&name[..]), parse::token::Plain); - if token.is_any_keyword() || "bool" == &name[..] { - let mut s = "_".to_owned(); - s.push_str(&name[..]); +fn rust_id(ctx: &mut GenCtx, name: &str) -> (String, bool) { + let token = parse::token::Ident(ctx.ext_cx.ident_of(name), parse::token::Plain); + if token.is_any_keyword() || "bool" == name { + let mut s = "_".to_string(); + s.push_str(name); (s, true) } else { - (name, false) + (name.to_owned(), false) } } -fn rust_type_id(ctx: &mut GenCtx, name: String) -> String { - if "bool" == &name[..] || - "uint" == &name[..] || - "u8" == &name[..] || - "u16" == &name[..] || - "u32" == &name[..] || - "f32" == &name[..] || - "f64" == &name[..] || - "i8" == &name[..] || - "i16" == &name[..] || - "i32" == &name[..] || - "i64" == &name[..] || - "Self" == &name[..] || - "str" == &name[..] { - let mut s = "_".to_owned(); - s.push_str(&name[..]); - s - } else { - let (n, _) = rust_id(ctx, name); - n +fn rust_type_id(ctx: &mut GenCtx, name: &str) -> String { + match name { + "bool" | "uint" | "u8" | "u16" | + "u32" | "f32" | "f64" | "i8" | + "i16" | "i32" | "i64" | "Self" | + "str" => { + let mut s = "_".to_string(); + s.push_str(name); + s + } + _ => first(rust_id(ctx, name)) } } -fn unnamed_name(ctx: &mut GenCtx, name: String) -> String { - if name.is_empty() { - ctx.unnamed_ty += 1; - format!("Unnamed{}", ctx.unnamed_ty) - } else { - name +fn comp_name(ctx: &GenCtx, kind: CompKind, name: &str) -> String { + match kind { + CompKind::Struct => struct_name(ctx, name), + CompKind::Union => union_name(ctx, name), } } -fn comp_name(kind: CompKind, name: &str) -> String { - match kind { - CompKind::Struct => struct_name(name), - CompKind::Union => union_name(name), +fn struct_name(ctx: &GenCtx, name: &str) -> String { + if ctx.options.rename_types { + format!("Struct_{}", name) + } else { + name.to_owned() } } -fn struct_name(name: &str) -> String { - format!("Struct_{}", name) +fn union_name(ctx: &GenCtx, name: &str) -> String { + if ctx.options.rename_types { + format!("Union_{}", name) + } else { + name.to_owned() + } } -fn union_name(name: &str) -> String { - format!("Union_{}", name) +fn enum_name(ctx: &GenCtx, name: &str) -> String { + if ctx.options.rename_types { + format!("Enum_{}", name) + } else { + name.to_owned() + } } -fn enum_name(name: &str) -> String { - format!("Enum_{}", name) +fn gen_unmangle_method(ctx: &mut GenCtx, + v: &VarInfo, + counts: &mut HashMap<String, isize>, + self_kind: ast::SelfKind) + -> ast::ImplItem { + let fndecl; + let mut args = vec!(); + + match self_kind { + ast::SelfKind::Static => (), + ast::SelfKind::Region(_, mutable, _) => { + let selfexpr = match mutable { + ast::Mutability::Immutable => quote_expr!(&ctx.ext_cx, &*self), + ast::Mutability::Mutable => quote_expr!(&ctx.ext_cx, &mut *self), + }; + args.push(selfexpr); + }, + _ => unreachable!() + } + + match v.ty { + TFuncPtr(ref sig) => { + fndecl = cfuncty_to_rs(ctx, + &*sig.ret_ty, sig.args.as_slice(), + false); + let mut unnamed: usize = 0; + let iter = if args.len() > 0 { + sig.args[1..].iter() + } else { + sig.args.iter() + }; + for arg in iter { + let (ref n, _) = *arg; + let argname = if n.is_empty() { + unnamed += 1; + format!("arg{}", unnamed) + } else { + first(rust_id(ctx, &n)) + }; + let expr = ast::Expr { + id: ast::DUMMY_NODE_ID, + node: ast::ExprKind::Path(None, ast::Path { + span: ctx.span, + global: false, + segments: vec!(ast::PathSegment { + identifier: ctx.ext_cx.ident_of(&argname), + parameters: ast::PathParameters::none() + }) + }), + span: ctx.span, + attrs: None, + }; + args.push(P(expr)); + } + }, + _ => unreachable!() + }; + + let sig = ast::MethodSig { + unsafety: ast::Unsafety::Unsafe, + abi: Abi::Rust, + decl: P(fndecl), + generics: empty_generics(), + explicit_self: respan(ctx.span, self_kind), + constness: ast::Constness::NotConst, + }; + + let block = ast::Block { + stmts: vec!(), + expr: Some(P(ast::Expr { + id: ast::DUMMY_NODE_ID, + node: ast::ExprKind::Call( + P(ast::Expr { + id: ast::DUMMY_NODE_ID, + node: ast::ExprKind::Path(None, ast::Path { + span: ctx.span, + global: false, + segments: vec!(ast::PathSegment { + identifier: ctx.ext_cx.ident_of(&v.mangled), + parameters: ast::PathParameters::none() + }) + }), + span: ctx.span, + attrs: None, + }), + args + ), + span: ctx.span, + attrs: None, + })), + id: ast::DUMMY_NODE_ID, + rules: ast::BlockCheckMode::Default, + span: ctx.span + }; + + let mut name = v.name.clone(); + let mut count = 0; + match counts.get(&v.name) { + Some(x) => { + count = *x; + name.push_str(&x.to_string()); + }, + None => () + } + count += 1; + counts.insert(v.name.clone(), count); + + let mut attrs = mk_doc_attr(ctx, &v.comment); + attrs.push(respan(ctx.span, ast::Attribute_ { + id: mk_attr_id(), + style: ast::AttrStyle::Outer, + value: P(respan(ctx.span, ast::MetaItemKind::Word(InternedString::new("inline")))), + is_sugared_doc: false + })); + + ast::ImplItem { + id: ast::DUMMY_NODE_ID, + ident: ctx.ext_cx.ident_of(&name), + vis: ast::Visibility::Public, + attrs: attrs, + node: ast::ImplItemKind::Method(sig, P(block)), + span: ctx.span + } } -pub fn gen_mod( - options: &BindgenOptions, - globs: Vec<Global>, - span: Span) - -> Vec<P<ast::Item>> { +pub fn gen_mods(links: &[(String, LinkType)], + map: ModuleMap, + options: BindgenOptions, + span: Span) -> Vec<P<ast::Item>> { // Create a dummy ExtCtxt. We only need this for string interning and that uses TLS. let mut features = Features::new(); features.allow_quote = true; @@ -130,25 +268,132 @@ pub fn gen_mod( trace_mac: false, }; let sess = &parse::ParseSess::new(); - let mut feature_gated_cfgs = Vec::new(); + let mut feature_gated_cfgs = vec![]; let mut ctx = GenCtx { - ext_cx: base::ExtCtxt::new( - sess, - Vec::new(), - cfg, - &mut feature_gated_cfgs, - ), - unnamed_ty: 0, - span: span + ext_cx: base::ExtCtxt::new(sess, Vec::new(), cfg, &mut feature_gated_cfgs), + options: options, + span: span, + module_map: map, + current_module_id: ROOT_MODULE_ID, }; + ctx.ext_cx.bt_push(ExpnInfo { call_site: ctx.span, callee: NameAndSpan { - format: MacroBang(parse::token::intern("")), + format: MacroBang(intern("")), allow_internal_unstable: false, span: None } }); + + if let Some(root_mod) = gen_mod(&mut ctx, ROOT_MODULE_ID, links, span) { + if !ctx.options.enable_cxx_namespaces { + match root_mod.node { + // XXX This clone might be really expensive, but doing: + // ast::ItemMod(ref mut root) => { + // return ::std::mem::replace(&mut root.items, vec![]); + // } + // fails with "error: cannot borrow immutable anonymous field as mutable". + // So... + ast::ItemKind::Mod(ref root) => { + return root.items.clone() + } + _ => unreachable!(), + } + } + + let root_export = P(ast::Item { + ident: ctx.ext_cx.ident_of(""), + attrs: vec![], + id: ast::DUMMY_NODE_ID, + node: ast::ItemKind::Use(P( + Spanned { + node: ast::ViewPathGlob(ast::Path { + span: span.clone(), + global: false, + segments: vec![ast::PathSegment { + identifier: root_mod.ident, + parameters: ast::PathParameters::none(), + }] + }), + span: span.clone(), + })), + vis: ast::Visibility::Public, + span: span.clone(), + }); + + vec![root_export, root_mod] + } else { + vec![] + } +} + +fn gen_mod(mut ctx: &mut GenCtx, + module_id: ModuleId, + links: &[(String, LinkType)], + span: Span) -> Option<P<ast::Item>> { + + // XXX avoid this clone + let module = ctx.module_map.get(&module_id).unwrap().clone(); + + // Import just the root to minimise name conflicts + let mut globals = if module_id != ROOT_MODULE_ID { + // XXX Pass this previously instead of looking it up always? + let root = ctx.ext_cx.ident_of(&ctx.module_map.get(&ROOT_MODULE_ID).unwrap().name); + vec![P(ast::Item { + ident: ctx.ext_cx.ident_of(""), + attrs: vec![], + id: ast::DUMMY_NODE_ID, + node: ast::ItemKind::Use(P( + Spanned { + node: ast::ViewPathSimple(root.clone(), + ast::Path { + span: span.clone(), + global: false, + segments: vec![ast::PathSegment { + identifier: root, + parameters: ast::PathParameters::none(), + }] + }), + span: span.clone(), + })), + vis: ast::Visibility::Public, + span: span.clone(), + })] + } else { + vec![] + }; + + ctx.current_module_id = module_id; + + globals.extend(gen_globals(&mut ctx, links, &module.globals).into_iter()); + + globals.extend(module.children_ids.iter().filter_map(|id| { + gen_mod(ctx, *id, links, span.clone()) + })); + + if !globals.is_empty() { + Some(P(ast::Item { + ident: ctx.ext_cx.ident_of(&module.name), + attrs: vec![], + id: ast::DUMMY_NODE_ID, + node: ast::ItemKind::Mod(ast::Mod { + inner: span, + items: globals, + }), + vis: ast::Visibility::Public, + span: span.clone(), + })) + } else { + None + } +} + + + +fn gen_globals(mut ctx: &mut GenCtx, + links: &[(String, LinkType)], + globs: &[Global]) -> Vec<P<ast::Item>> { let uniq_globs = tag_dup_decl(globs); let mut fs = vec!(); @@ -184,70 +429,51 @@ pub fn gen_mod( for g in gs.into_iter() { match g { GType(ti) => { - let t = ti.borrow(); - defs.extend(ctypedef_to_rs( - &mut ctx, - options.rust_enums, - options.derive_debug, - t.name.clone(), &t.ty)) + let t = ti.borrow().clone(); + defs.push(ctypedef_to_rs(&mut ctx, t)) }, GCompDecl(ci) => { - { - let mut c = ci.borrow_mut(); - c.name = unnamed_name(&mut ctx, c.name.clone()); - } let c = ci.borrow().clone(); - defs.push(opaque_to_rs(&mut ctx, comp_name(c.kind, &c.name))); + let name = comp_name(&ctx, c.kind, &c.name); + + defs.push(opaque_to_rs(&mut ctx, &name)); }, GComp(ci) => { - { - let mut c = ci.borrow_mut(); - c.name = unnamed_name(&mut ctx, c.name.clone()); - } let c = ci.borrow().clone(); - defs.extend(comp_to_rs(&mut ctx, c.kind, comp_name(c.kind, &c.name), - options.derive_debug, - c.layout, c.members).into_iter()) + let name = comp_name(&ctx, c.kind, &c.name); + defs.extend(comp_to_rs(&mut ctx, &name, c).into_iter()) }, GEnumDecl(ei) => { - { - let mut e = ei.borrow_mut(); - e.name = unnamed_name(&mut ctx, e.name.clone()); - } let e = ei.borrow().clone(); - defs.push(opaque_to_rs(&mut ctx, enum_name(&e.name))); + let name = enum_name(&ctx, &e.name); + + defs.push(opaque_to_rs(&mut ctx, &name)); }, GEnum(ei) => { - { - let mut e = ei.borrow_mut(); - e.name = unnamed_name(&mut ctx, e.name.clone()); - } - let e = ei.borrow(); - defs.extend(cenum_to_rs( - &mut ctx, - options.rust_enums, - options.derive_debug, - enum_name(&e.name), e.kind, e.layout, &e.items)); + let e = ei.borrow().clone(); + let name = enum_name(&ctx, &e.name); + defs.extend(cenum_to_rs(&mut ctx, name, e.kind, e.comment, &e.items, e.layout).into_iter()) }, GVar(vi) => { let v = vi.borrow(); - let ty = cty_to_rs(&mut ctx, &v.ty); + let ty = cty_to_rs(&mut ctx, &v.ty, v.is_const, true); defs.push(const_to_rs(&mut ctx, v.name.clone(), v.val.unwrap(), ty)); }, _ => { } } } - let vars = vs.into_iter().map(|v| { + let vars: Vec<_> = vs.into_iter().map(|v| { match v { GVar(vi) => { let v = vi.borrow(); - cvar_to_rs(&mut ctx, v.name.clone(), &v.ty, v.is_const) + cvar_to_rs(&mut ctx, v.name.clone(), v.mangled.clone(), &v.ty, v.is_const) }, _ => unreachable!() } }).collect(); + let mut unmangle_count: HashMap<String, isize> = HashMap::new(); let funcs = { let func_list = fs.into_iter().map(|f| { match f { @@ -255,9 +481,21 @@ pub fn gen_mod( let v = vi.borrow(); match v.ty { TFuncPtr(ref sig) => { - let decl = cfunc_to_rs(&mut ctx, v.name.clone(), + let mut name = v.name.clone(); + let mut count = 0; + match unmangle_count.get(&v.name) { + Some(x) => { + count = *x; + name.push_str(&x.to_string()); + }, + None => () + } + count += 1; + unmangle_count.insert(v.name.clone(), count); + + let decl = cfunc_to_rs(&mut ctx, name, v.mangled.clone(), v.comment.clone(), &*sig.ret_ty, &sig.args[..], - sig.is_variadic); + sig.is_variadic, ast::Visibility::Public); (sig.abi, decl) } _ => unreachable!() @@ -267,26 +505,19 @@ pub fn gen_mod( } }); - let mut map: HashMap<abi::Abi, Vec<_>> = HashMap::new(); + let mut map: HashMap<Abi, Vec<_>> = HashMap::new(); for (abi, func) in func_list { - match map.entry(abi) { - Entry::Occupied(mut occ) => { - occ.get_mut().push(func); - } - Entry::Vacant(vac) => { - vac.insert(vec!(func)); - } - } + map.entry(abi).or_insert(vec![]).push(func); } map }; - if !Vec::is_empty(&vars) { - defs.push(mk_extern(&mut ctx, &options.links, vars, abi::Abi::C)); + if !vars.is_empty() { + defs.push(mk_extern(&mut ctx, links, vars, Abi::C)); } for (abi, funcs) in funcs.into_iter() { - defs.push(mk_extern(&mut ctx, &options.links, funcs, abi)); + defs.push(mk_extern(&mut ctx, &links, funcs, abi)); } //let attrs = vec!(mk_attr_list(&mut ctx, "allow", ["dead_code", "non_camel_case_types", "uppercase_variables"])); @@ -296,44 +527,34 @@ pub fn gen_mod( fn mk_extern(ctx: &mut GenCtx, links: &[(String, LinkType)], foreign_items: Vec<ast::ForeignItem>, - abi: abi::Abi) -> P<ast::Item> { - let attrs = if links.is_empty() { - Vec::new() - } else { - links.iter().map(|&(ref l, ref k)| { - let k = match *k { - LinkType::Default => None, - LinkType::Static => Some("static"), - LinkType::Framework => Some("framework") - }; - let link_name = P(respan(ctx.span, ast::MetaItemKind::NameValue( - to_intern_str(ctx, "name".to_owned()), - respan(ctx.span, ast::LitKind::Str( - to_intern_str(ctx, l.to_owned()), - ast::StrStyle::Cooked - )) - ))); - let link_args = match k { - None => vec!(link_name), - Some(ref k) => vec!(link_name, P(respan(ctx.span, ast::MetaItemKind::NameValue( - to_intern_str(ctx, "kind".to_owned()), - respan(ctx.span, ast::LitKind::Str( - to_intern_str(ctx, (*k).to_owned()), - ast::StrStyle::Cooked - )) - )))) - }; - respan(ctx.span, ast::Attribute_ { - id: mk_attr_id(), - style: ast::AttrStyle::Outer, - value: P(respan(ctx.span, ast::MetaItemKind::List( - to_intern_str(ctx, "link".to_owned()), - link_args) - )), - is_sugared_doc: false - }) - }).collect() - }; + abi: Abi) -> P<ast::Item> { + let attrs: Vec<_> = links.iter().map(|&(ref l, ref k)| { + let k = match *k { + LinkType::Default => None, + LinkType::Static => Some("static"), + LinkType::Framework => Some("framework") + }; + let link_name = P(respan(ctx.span, ast::MetaItemKind::NameValue( + InternedString::new("name"), + respan(ctx.span, ast::LitKind::Str(intern(l).as_str(), ast::StrStyle::Cooked)) + ))); + let link_args = match k { + None => vec!(link_name), + Some(ref k) => vec!(link_name, P(respan(ctx.span, ast::MetaItemKind::NameValue( + InternedString::new("kind"), + respan(ctx.span, ast::LitKind::Str(intern(k).as_str(), ast::StrStyle::Cooked)) + )))) + }; + respan(ctx.span, ast::Attribute_ { + id: mk_attr_id(), + style: ast::AttrStyle::Outer, + value: P(respan(ctx.span, ast::MetaItemKind::List( + InternedString::new("link"), + link_args) + )), + is_sugared_doc: false + }) + }).collect(); let mut items = Vec::new(); items.extend(foreign_items.into_iter()); @@ -352,6 +573,28 @@ fn mk_extern(ctx: &mut GenCtx, links: &[(String, LinkType)], }) } +fn mk_impl(ctx: &mut GenCtx, ty: P<ast::Ty>, + items: Vec<ast::ImplItem>) + -> P<ast::Item> { + let ext = ast::ItemKind::Impl( + ast::Unsafety::Normal, + ast::ImplPolarity::Positive, + empty_generics(), + None, + ty, + items + ); + + P(ast::Item { + ident: ctx.ext_cx.ident_of(""), + attrs: vec!(), + id: ast::DUMMY_NODE_ID, + node: ext, + vis: ast::Visibility::Inherited, + span: ctx.span + }) +} + fn remove_redundant_decl(gs: Vec<Global>) -> Vec<Global> { fn check_decl(a: &Global, ty: &Type) -> bool { match *a { @@ -383,7 +626,7 @@ fn remove_redundant_decl(gs: Vec<Global>) -> Vec<Global> { ).collect() } -fn tag_dup_decl(gs: Vec<Global>) -> Vec<Global> { +fn tag_dup_decl(gs: &[Global]) -> Vec<Global> { fn check(name1: &str, name2: &str) -> bool { !name1.is_empty() && name1 == name2 } @@ -393,75 +636,118 @@ fn tag_dup_decl(gs: Vec<Global>) -> Vec<Global> { (>ype(ref ti1), >ype(ref ti2)) => { let a = ti1.borrow(); let b = ti2.borrow(); - check(&a.name[..], &b.name[..]) + check(&a.name, &b.name) }, (&GComp(ref ci1), &GComp(ref ci2)) => { let a = ci1.borrow(); let b = ci2.borrow(); - check(&a.name[..], &b.name[..]) + check(&a.name, &b.name) }, (&GCompDecl(ref ci1), &GCompDecl(ref ci2)) => { let a = ci1.borrow(); let b = ci2.borrow(); - check(&a.name[..], &b.name[..]) + check(&a.name, &b.name) }, (&GEnum(ref ei1), &GEnum(ref ei2)) => { let a = ei1.borrow(); let b = ei2.borrow(); - check(&a.name[..], &b.name[..]) + check(&a.name, &b.name) }, (&GEnumDecl(ref ei1), &GEnumDecl(ref ei2)) => { let a = ei1.borrow(); let b = ei2.borrow(); - check(&a.name[..], &b.name[..]) + check(&a.name, &b.name) }, (&GVar(ref vi1), &GVar(ref vi2)) => { let a = vi1.borrow(); let b = vi2.borrow(); - check(&a.name[..], &b.name[..]) + check(&a.name, &b.name) && + check(&a.mangled, &b.mangled) }, (&GFunc(ref vi1), &GFunc(ref vi2)) => { let a = vi1.borrow(); let b = vi2.borrow(); - check(&a.name[..], &b.name[..]) + check(&a.name, &b.name) && + check(&a.mangled, &b.mangled) }, _ => false } } + fn check_opaque_dup(g1: &Global, g2: &Global) -> bool { + match (g1, g2) { + (&GCompDecl(ref ci1), &GComp(ref ci2)) => { + let a = ci1.borrow(); + let b = ci2.borrow(); + check(&a.name, &b.name) + }, + (&GEnumDecl(ref ei1), &GEnum(ref ei2)) => { + let a = ei1.borrow(); + let b = ei2.borrow(); + check(&a.name, &b.name) + }, + _ => false, + } + } + if gs.is_empty() { - return gs; + return vec![]; } - let mut res: Vec<Global> = vec!(); - res.push(gs[0].clone()); + let mut step: Vec<Global> = vec!(); + step.push(gs[0].clone()); - for (i, gsi) in gs.iter().enumerate().skip(1) { + for (i, _gsi) in gs.iter().enumerate().skip(1) { let mut dup = false; - for gsj in gs.iter().take(i) { - if check_dup(&gsi, &gsj) { + for j in 0..i { + if i == j { + continue; + } + if check_dup(&gs[i], &gs[j]) { dup = true; break; } } if !dup { - res.push(gsi.clone()); + step.push(gs[i].clone()); + } + } + + let len = step.len(); + let mut res: Vec<Global> = vec!(); + for i in 0..len { + let mut dup = false; + match &step[i] { + &GCompDecl(_) | &GEnumDecl(_) => { + for j in 0..len { + if i == j { + continue; + } + if check_opaque_dup(&step[i], &step[j]) { + dup = true; + break; + } + } + }, + _ => (), + } + + if !dup { + res.push(step[i].clone()); } } res } -fn ctypedef_to_rs( - ctx: &mut GenCtx, - rust_enums: bool, - derive_debug: bool, - name: String, - ty: &Type) - -> Vec<P<ast::Item>> { - fn mk_item(ctx: &mut GenCtx, name: String, ty: &Type) -> P<ast::Item> { +fn ctypedef_to_rs(ctx: &mut GenCtx, ty: TypeInfo) -> P<ast::Item> { + fn mk_item(ctx: &mut GenCtx, name: &str, comment: &str, ty: &Type) -> P<ast::Item> { let rust_name = rust_type_id(ctx, name); - let rust_ty = cty_to_rs(ctx, ty); + let rust_ty = if cty_is_translatable(ty) { + cty_to_rs(ctx, ty, true, true) + } else { + cty_to_rs(ctx, &TVoid, true, true) + }; let base = ast::ItemKind::Ty( P(ast::Ty { id: ast::DUMMY_NODE_ID, @@ -472,8 +758,8 @@ fn ctypedef_to_rs( ); P(ast::Item { - ident: ctx.ext_cx.ident_of(&rust_name[..]), - attrs: Vec::new(), + ident: ctx.ext_cx.ident_of(&rust_name), + attrs: mk_doc_attr(ctx, comment), id: ast::DUMMY_NODE_ID, node: base, vis: ast::Visibility::Public, @@ -481,43 +767,32 @@ fn ctypedef_to_rs( }) } - match *ty { + match ty.ty { TComp(ref ci) => { - let is_empty = ci.borrow().name.is_empty(); - if is_empty { - ci.borrow_mut().name = name.clone(); - let c = ci.borrow().clone(); - comp_to_rs(ctx, c.kind, name, derive_debug, c.layout, c.members) - } else { - vec!(mk_item(ctx, name, ty)) - } + assert!(!ci.borrow().name.is_empty()); + mk_item(ctx, &ty.name, &ty.comment, &ty.ty) }, TEnum(ref ei) => { - let is_empty = ei.borrow().name.is_empty(); - if is_empty { - ei.borrow_mut().name = name.clone(); - let e = ei.borrow(); - cenum_to_rs(ctx, rust_enums, derive_debug, name, e.kind, e.layout, &e.items) - } else { - vec!(mk_item(ctx, name, ty)) - } + assert!(!ei.borrow().name.is_empty()); + mk_item(ctx, &ty.name, &ty.comment, &ty.ty) }, - _ => vec!(mk_item(ctx, name, ty)) + _ => mk_item(ctx, &ty.name, &ty.comment, &ty.ty), } } -fn comp_to_rs(ctx: &mut GenCtx, kind: CompKind, name: String, - derive_debug: bool, - layout: Layout, members: Vec<CompMember>) -> Vec<P<ast::Item>> { - match kind { - CompKind::Struct => cstruct_to_rs(ctx, name, derive_debug, layout, members), - CompKind::Union => cunion_to_rs(ctx, name, derive_debug, layout, members), +fn comp_to_rs(ctx: &mut GenCtx, name: &str, ci: CompInfo) + -> Vec<P<ast::Item>> { + match ci.kind { + CompKind::Struct => cstruct_to_rs(ctx, name, ci), + CompKind::Union => cunion_to_rs(ctx, name, ci.layout, ci.members), } } -fn cstruct_to_rs(ctx: &mut GenCtx, name: String, - derive_debug: bool, - layout: Layout, members: Vec<CompMember>) -> Vec<P<ast::Item>> { +fn cstruct_to_rs(ctx: &mut GenCtx, name: &str, ci: CompInfo) -> Vec<P<ast::Item>> { + let layout = ci.layout; + let members = &ci.members; + let template_args = &ci.args; + let methodlist = &ci.methods; let mut fields = vec!(); let mut methods = vec!(); // Nested composites may need to emit declarations and implementations as @@ -527,40 +802,198 @@ fn cstruct_to_rs(ctx: &mut GenCtx, name: String, let mut unnamed: u32 = 0; let mut bitfields: u32 = 0; - // Debug is only defined on little arrays - let mut can_derive_debug = derive_debug; + if ci.hide || + template_args.iter().any(|f| f == &TVoid) { + return vec!(); + } - for m in &members { - let (opt_rc_c, opt_f) = match *m { - CompMember::Field(ref f) => { (None, Some(f)) } - CompMember::Comp(ref rc_c) => { (Some(rc_c), None) } - CompMember::CompField(ref rc_c, ref f) => { (Some(rc_c), Some(f)) } + let id = rust_type_id(ctx, name); + let id_ty = P(mk_ty(ctx, false, &[id.clone()])); + + if ci.has_vtable { + let mut vffields = vec!(); + let base_vftable = if !members.is_empty() { + if let CompMember::Field(ref fi) = members[0] { + match fi.ty { + TComp(ref ci2) => { + let ci2 = ci2.borrow(); + if ci2.has_vtable { + Some(format!("_vftable_{}", ci2.name)) + } else { + None + } + }, + _ => None + } + } else { + None + } + } else { + None }; + if let Some(ref base) = base_vftable { + let field = ast::StructField_ { + kind: ast::NamedField(ctx.ext_cx.ident_of("_base"), ast::Visibility::Public), + id: ast::DUMMY_NODE_ID, + ty: P(mk_ty(ctx, false, &[base.clone()])), + attrs: vec!(), + }; + vffields.push(respan(ctx.span, field)); + } + + for vm in ci.vmethods.iter() { + let ty = match vm.ty { + TFuncPtr(ref sig) => { + let decl = cfuncty_to_rs(ctx, &*sig.ret_ty, sig.args.as_slice(), sig.is_variadic); + mk_fn_proto_ty(ctx, &decl, sig.abi) + }, + _ => unreachable!() + }; + let field = ast::StructField_ { + kind: ast::NamedField(ctx.ext_cx.ident_of(&vm.name), ast::Visibility::Public), + id: ast::DUMMY_NODE_ID, + ty: P(ty), + attrs: vec!(), + }; + vffields.push(respan(ctx.span, field)); + } + + let vf_name = format!("_vftable_{}", name); + let item = P(ast::Item { + ident: ctx.ext_cx.ident_of(&vf_name), + attrs: vec!(mk_repr_attr(ctx, layout)), + id: ast::DUMMY_NODE_ID, + node: ast::ItemKind::Struct( + ast::VariantData::Struct(vffields, ast::DUMMY_NODE_ID), + empty_generics() + ), + vis: ast::Visibility::Public, + span: ctx.span, + }); + extra.push(item); + + if base_vftable.is_none() { + let vf_type = mk_ty(ctx, false, &[vf_name]); + fields.push(respan(ctx.span, ast::StructField_ { + kind: ast::NamedField(ctx.ext_cx.ident_of("_vftable"), ast::Visibility::Public), + id: ast::DUMMY_NODE_ID, + ty: P(mk_ptrty(ctx, &vf_type, true)), + attrs: Vec::new() + })); + } + } + + if members.is_empty() { + let mut phantom_count = 0; + for arg in template_args { + let f_name = format!("_phantom{}", phantom_count); + phantom_count += 1; + let inner_type = P(cty_to_rs(ctx, &arg, true, false)); + fields.push(respan(ctx.span, ast::StructField_ { + kind: ast::NamedField( + ctx.ext_cx.ident_of(&f_name), + ast::Visibility::Public, + ), + id: ast::DUMMY_NODE_ID, + ty: quote_ty!(&ctx.ext_cx, ::std::marker::PhantomData<$inner_type>), + attrs: vec!(), + })); + } + } + + let mut anon_enum_count = 0; + let mut setters = vec!(); + let mut has_destructor = ci.has_destructor; + for m in members.iter() { + if let &CompMember::Enum(ref ei) = m { + let e = ei.borrow().clone(); + let ename = if e.name.is_empty() { + let ename = format!("{}_enum{}", name, anon_enum_count); + anon_enum_count += 1; + ename + } else { + e.name.clone() + }; + extra.extend(cenum_to_rs(ctx, ename, e.kind, e.comment, &e.items, e.layout).into_iter()); + continue; + } + + fn comp_fields(m: &CompMember) + -> (Option<Rc<RefCell<CompInfo>>>, Option<FieldInfo>) { + match m { + &CompMember::Field(ref f) => { (None, Some(f.clone())) } + &CompMember::Comp(ref rc_c) => { + let c = rc_c.borrow(); + if c.members.len() == 1 { + comp_fields(&c.members[0]) + } else { + (Some(rc_c.clone()), None) + } + } + &CompMember::CompField(ref rc_c, ref f) => { (Some(rc_c.clone()), Some(f.clone())) } + _ => unreachable!() + } + } + + let (opt_rc_c, opt_f) = comp_fields(m); + if let Some(f) = opt_f { + if cty_has_destructor(&f.ty) { + has_destructor = true; + } + if !cty_is_translatable(&f.ty) { + continue; + } let f_name = match f.bitfields { Some(_) => { bitfields += 1; - format!("_bindgen_bitfield_{}_", bitfields) + format!("_bitfield_{}", bitfields) } - None => rust_type_id(ctx, f.name.clone()) + None => rust_type_id(ctx, &f.name) }; - if !f.ty.can_derive_debug() { - can_derive_debug = false; + let mut offset: u32 = 0; + if let Some(ref bitfields) = f.bitfields { + for &(ref bf_name, bf_size) in bitfields.iter() { + setters.push(gen_bitfield_method(ctx, &f_name, bf_name, &f.ty, offset as usize, bf_size)); + offset += bf_size; + } + setters.push(gen_fullbitfield_method(ctx, &f_name, &f.ty, bitfields)) } - let f_ty = P(cty_to_rs(ctx, &f.ty)); + let mut bypass = false; + let f_ty = if let Some(ref rc_c) = opt_rc_c { + if rc_c.borrow().members.len() == 1 { + if let CompMember::Field(ref inner_f) = rc_c.borrow().members[0] { + bypass = true; + inner_f.ty.clone() + } else { + f.ty.clone() + } + } else { + f.ty.clone() + } + } else { + f.ty.clone() + }; + + // If the member is not a template argument, it needs the full path. + let needs_full_path = !template_args.iter().any(|arg| *arg == f_ty); + let f_ty = P(cty_to_rs(ctx, &f_ty, f.bitfields.is_none(), needs_full_path)); fields.push(respan(ctx.span, ast::StructField_ { kind: ast::NamedField( - ctx.ext_cx.ident_of(&f_name[..]), + ctx.ext_cx.ident_of(&f_name), ast::Visibility::Public, ), id: ast::DUMMY_NODE_ID, ty: f_ty, - attrs: Vec::new() + attrs: mk_doc_attr(ctx, &f.comment) })); + if bypass { + continue; + } } if let Some(rc_c) = opt_rc_c { @@ -568,47 +1001,92 @@ fn cstruct_to_rs(ctx: &mut GenCtx, name: String, if c.name.is_empty() { unnamed += 1; let field_name = format!("_bindgen_data_{}_", unnamed); - fields.push(mk_blob_field(ctx, &field_name[..], c.layout)); - methods.extend(gen_comp_methods(ctx, &field_name[..], 0, c.kind, &c.members, &mut extra, derive_debug).into_iter()); + fields.push(mk_blob_field(ctx, &field_name, c.layout)); + methods.extend(gen_comp_methods(ctx, &field_name, 0, c.kind, &c.members, &mut extra).into_iter()); } else { - extra.extend(comp_to_rs(ctx, c.kind, comp_name(c.kind, &c.name), - derive_debug, - c.layout, c.members.clone()).into_iter()); + let name = comp_name(&ctx, c.kind, &c.name); + extra.extend(comp_to_rs(ctx, &name, c.clone()).into_iter()); } } } + if !setters.is_empty() { + extra.push(P(ast::Item { + ident: ctx.ext_cx.ident_of(""), + attrs: vec!(), + id: ast::DUMMY_NODE_ID, + node: ast::ItemKind::Impl( + ast::Unsafety::Normal, + ast::ImplPolarity::Positive, + empty_generics(), + None, + id_ty.clone(), + setters + ), + vis: ast::Visibility::Inherited, + span: ctx.span + })); + } + + let variant_data = if fields.is_empty() { + ast::VariantData::Unit(ast::DUMMY_NODE_ID) + } else { + ast::VariantData::Struct(fields, ast::DUMMY_NODE_ID) + }; + let ty_params = template_args.iter().map(|gt| { + let name = match gt { + &TNamed(ref ti) => { + ctx.ext_cx.ident_of(&ti.borrow().name) + }, + _ => ctx.ext_cx.ident_of("") + }; + ast::TyParam { + ident: name, + id: ast::DUMMY_NODE_ID, + bounds: OwnedSlice::empty(), + default: None, + span: ctx.span + } + }).collect(); let def = ast::ItemKind::Struct( - ast::VariantData::Struct(fields, ast::DUMMY_NODE_ID), - empty_generics() + variant_data, + ast::Generics { + lifetimes: vec!(), + ty_params: OwnedSlice::from_vec(ty_params), + where_clause: ast::WhereClause { + id: ast::DUMMY_NODE_ID, + predicates: vec!() + } + } ); - let id = rust_type_id(ctx, name.clone()); - let mut attrs = vec!(mk_repr_attr(ctx, layout), mk_deriving_copy_attr(ctx, false)); - if can_derive_debug { - attrs.push(mk_deriving_debug_attr(ctx)); + let mut attrs = mk_doc_attr(ctx, &ci.comment); + attrs.push(mk_repr_attr(ctx, layout)); + if !has_destructor { + attrs.push(mk_deriving_copy_attr(ctx)); } - let struct_def = P(ast::Item { ident: ctx.ext_cx.ident_of(&id[..]), + let struct_def = ast::Item { + ident: ctx.ext_cx.ident_of(&id), attrs: attrs, id: ast::DUMMY_NODE_ID, node: def, vis: ast::Visibility::Public, span: ctx.span - }); + }; - let mut items = vec!(struct_def); + let mut items = vec!(P(struct_def)); if !methods.is_empty() { let impl_ = ast::ItemKind::Impl( ast::Unsafety::Normal, ast::ImplPolarity::Positive, empty_generics(), None, - P(mk_ty(ctx, false, vec!(id))), + id_ty.clone(), methods ); items.push( P(ast::Item { - ident: ctx.ext_cx.ident_of(&name[..]), + ident: ctx.ext_cx.ident_of(&name), attrs: vec!(), id: ast::DUMMY_NODE_ID, node: impl_, @@ -616,13 +1094,39 @@ fn cstruct_to_rs(ctx: &mut GenCtx, name: String, span: ctx.span})); } - items.push(mk_clone_impl(ctx, &name[..])); - items.push(mk_default_impl(ctx, &name[..])); items.extend(extra.into_iter()); + + let mut mangledlist = vec!(); + let mut unmangledlist = vec!(); + let mut unmangle_count: HashMap<String, isize> = HashMap::new(); + for v in methodlist { + let v = v.clone(); + match v.ty { + TFuncPtr(ref sig) => { + let name = v.mangled.clone(); + let explicit_self = if v.is_static { + ast::SelfKind::Static + } else if v.is_const { + ast::SelfKind::Region(None, ast::Mutability::Immutable, ctx.ext_cx.ident_of("self")) + } else { + ast::SelfKind::Region(None, ast::Mutability::Mutable, ctx.ext_cx.ident_of("self")) + }; + unmangledlist.push(gen_unmangle_method(ctx, &v, &mut unmangle_count, explicit_self)); + mangledlist.push(cfunc_to_rs(ctx, name, String::new(), String::new(), + &*sig.ret_ty, sig.args.as_slice(), + sig.is_variadic, ast::Visibility::Inherited)); + } + _ => unreachable!() + } + } + if mangledlist.len() > 0 { + items.push(mk_extern(ctx, &vec!(), mangledlist, Abi::C)); + items.push(mk_impl(ctx, id_ty, unmangledlist)); + } items } -fn opaque_to_rs(ctx: &mut GenCtx, name: String) -> P<ast::Item> { +fn opaque_to_rs(ctx: &mut GenCtx, name: &str) -> P<ast::Item> { let def = ast::ItemKind::Enum( ast::EnumDef { variants: vec!() @@ -632,7 +1136,7 @@ fn opaque_to_rs(ctx: &mut GenCtx, name: String) -> P<ast::Item> { let id = rust_type_id(ctx, name); P(ast::Item { - ident: ctx.ext_cx.ident_of(&id[..]), + ident: ctx.ext_cx.ident_of(&id), attrs: Vec::new(), id: ast::DUMMY_NODE_ID, node: def, @@ -641,11 +1145,11 @@ fn opaque_to_rs(ctx: &mut GenCtx, name: String) -> P<ast::Item> { }) } -fn cunion_to_rs(ctx: &mut GenCtx, name: String, derive_debug: bool, layout: Layout, members: Vec<CompMember>) -> Vec<P<ast::Item>> { +fn cunion_to_rs(ctx: &mut GenCtx, name: &str, layout: Layout, members: Vec<CompMember>) -> Vec<P<ast::Item>> { fn mk_item(ctx: &mut GenCtx, name: String, item: ast::ItemKind, vis: ast::Visibility, attrs: Vec<ast::Attribute>) -> P<ast::Item> { P(ast::Item { - ident: ctx.ext_cx.ident_of(&name[..]), + ident: ctx.ext_cx.ident_of(&name), attrs: attrs, id: ast::DUMMY_NODE_ID, node: item, @@ -654,8 +1158,9 @@ fn cunion_to_rs(ctx: &mut GenCtx, name: String, derive_debug: bool, layout: Layo }) } - let ci = Rc::new(RefCell::new(CompInfo::new(name.clone(), CompKind::Union, members.clone(), layout))); - let union = TNamed(Rc::new(RefCell::new(TypeInfo::new(name.clone(), TComp(ci))))); + // XXX what module id is correct? + let ci = Rc::new(RefCell::new(CompInfo::new(name.to_owned(), ROOT_MODULE_ID, name.to_owned(), "".to_owned(), CompKind::Union, members.clone(), layout))); + let union = TNamed(Rc::new(RefCell::new(TypeInfo::new(name.to_owned(), ROOT_MODULE_ID, TComp(ci), layout)))); // Nested composites may need to emit declarations and implementations as // they are encountered. The declarations end up in 'extra' and are emitted @@ -666,27 +1171,22 @@ fn cunion_to_rs(ctx: &mut GenCtx, name: String, derive_debug: bool, layout: Layo let data_field = mk_blob_field(ctx, data_field_name, layout); let def = ast::ItemKind::Struct( - ast::VariantData::Struct( - vec!(data_field), - ast::DUMMY_NODE_ID), + ast::VariantData::Struct(vec![data_field], ast::DUMMY_NODE_ID), empty_generics() ); - let union_id = rust_type_id(ctx, name.clone()); - let union_attrs = { - let mut attrs = vec!(mk_repr_attr(ctx, layout), mk_deriving_copy_attr(ctx, false)); - if derive_debug { - let can_derive_debug = members.iter() - .all(|member| match *member { - CompMember::Field(ref f) | - CompMember::CompField(_, ref f) => f.ty.can_derive_debug(), - _ => true - }); - if can_derive_debug { - attrs.push(mk_deriving_debug_attr(ctx)) - } - } - attrs - }; + let union_id = rust_type_id(ctx, name); + let mut union_attrs = vec!(mk_repr_attr(ctx, layout)); + let can_derive_debug = members.iter() + .all(|member| match *member { + CompMember::Field(ref f) | + CompMember::CompField(_, ref f) => f.ty.can_derive_debug(), + _ => true + }); + union_attrs.push(if can_derive_debug { + mk_deriving_copy_and_maybe_debug_attr(ctx) + } else { + mk_deriving_copy_attr(ctx) + }); let union_def = mk_item(ctx, union_id, def, ast::Visibility::Public, union_attrs); @@ -695,8 +1195,8 @@ fn cunion_to_rs(ctx: &mut GenCtx, name: String, derive_debug: bool, layout: Layo ast::ImplPolarity::Positive, empty_generics(), None, - P(cty_to_rs(ctx, &union)), - gen_comp_methods(ctx, data_field_name, 0, CompKind::Union, &members, &mut extra, derive_debug), + P(cty_to_rs(ctx, &union, true, true)), + gen_comp_methods(ctx, data_field_name, 0, CompKind::Union, &members, &mut extra), ); let mut items = vec!( @@ -704,8 +1204,6 @@ fn cunion_to_rs(ctx: &mut GenCtx, name: String, derive_debug: bool, layout: Layo mk_item(ctx, "".to_owned(), union_impl, ast::Visibility::Inherited, Vec::new()) ); - items.push(mk_clone_impl(ctx, &name[..])); - items.push(mk_default_impl(ctx, &name[..])); items.extend(extra.into_iter()); items } @@ -726,7 +1224,7 @@ fn const_to_rs(ctx: &mut GenCtx, name: String, val: i64, val_ty: ast::Ty) -> P<a value ); - let id = first(rust_id(ctx, name.clone())); + let id = first(rust_id(ctx, &name)); P(ast::Item { ident: ctx.ext_cx.ident_of(&id[..]), attrs: Vec::new(), @@ -737,6 +1235,16 @@ fn const_to_rs(ctx: &mut GenCtx, name: String, val: i64, val_ty: ast::Ty) -> P<a }) } +fn enum_size_to_unsigned_max_value(size: usize) -> u64 { + match size { + 1 => std::u8::MAX as u64, + 2 => std::u16::MAX as u64, + 4 => std::u32::MAX as u64, + 8 => std::u64::MAX, + _ => unreachable!("invalid enum size: {}", size) + } +} + fn enum_size_to_rust_type_name(signed: bool, size: usize) -> &'static str { match (signed, size) { (true, 1) => "i8", @@ -751,81 +1259,60 @@ fn enum_size_to_rust_type_name(signed: bool, size: usize) -> &'static str { } } -fn enum_size_to_unsigned_max_value(size: usize) -> u64 { - match size { - 1 => std::u8::MAX as u64, - 2 => std::u16::MAX as u64, - 4 => std::u32::MAX as u64, - 8 => std::u64::MAX, - _ => unreachable!("invalid enum size: {}", size) - } -} - -fn cenum_value_to_int_lit( - ctx: &mut GenCtx, - enum_is_signed: bool, - size: usize, - value: i64) - -> P<ast::Expr> { +fn cenum_value_to_int_lit(ctx: &mut GenCtx, + enum_is_signed: bool, + size: usize, + value: i64) -> P<ast::Expr> { if enum_is_signed { - let int_lit = - ast::LitKind::Int(value.abs() as u64, ast::LitIntType::Unsuffixed); + let int_lit = ast::LitKind::Int(value.abs() as u64, ast::LitIntType::Unsuffixed); let expr = ctx.ext_cx.expr_lit(ctx.span, int_lit); + if value < 0 { - ctx.ext_cx.expr( - ctx.span, ast::ExprKind::Unary(ast::UnOp::Neg, expr)) + ctx.ext_cx.expr(ctx.span, ast::ExprKind::Unary(ast::UnOp::Neg, expr)) } else { expr } } else { - let u64_value = - value as u64 & enum_size_to_unsigned_max_value(size); - let int_lit = - ast::LitKind::Int(u64_value, ast::LitIntType::Unsuffixed); + let u64_value = value as u64 & enum_size_to_unsigned_max_value(size); + let int_lit = ast::LitKind::Int(u64_value, ast::LitIntType::Unsuffixed); ctx.ext_cx.expr_lit(ctx.span, int_lit) } } -fn cenum_to_rs( - ctx: &mut GenCtx, - rust_enums: bool, - derive_debug: bool, - name: String, - kind: IKind, - layout: Layout, - enum_items: &[EnumItem]) - -> Vec<P<ast::Item>> { +fn cenum_to_rs(ctx: &mut GenCtx, + name: String, + kind: IKind, + comment: String, + enum_items: &[EnumItem], + layout: Layout) -> Vec<P<ast::Item>> { let enum_name = ctx.ext_cx.ident_of(&name); let enum_ty = ctx.ext_cx.ty_ident(ctx.span, enum_name); let enum_is_signed = kind.is_signed(); let enum_repr = enum_size_to_rust_type_name(enum_is_signed, layout.size); + + // Rust is not happy with univariant enums + // if items.len() < 2 { + // return vec!(); + // } + // let mut items = vec![]; - if !rust_enums { - items.push(ctx.ext_cx.item_ty( - ctx.span, - enum_name, - ctx.ext_cx.ty_ident( - ctx.span, - ctx.ext_cx.ident_of(enum_repr)))); + if !ctx.options.rust_enums { + items.push(ctx.ext_cx.item_ty(ctx.span, + enum_name, + ctx.ext_cx.ty_ident(ctx.span, + ctx.ext_cx.ident_of(enum_repr)))); for item in enum_items { - let value = cenum_value_to_int_lit( - ctx, enum_is_signed, layout.size, item.val); - items.push(ctx.ext_cx.item_const( - ctx.span, - ctx.ext_cx.ident_of(&item.name), - enum_ty.clone(), - value)); + let value = cenum_value_to_int_lit(ctx, enum_is_signed, layout.size, item.val); + items.push(ctx.ext_cx.item_const(ctx.span, ctx.ext_cx.ident_of(&item.name), enum_ty.clone(), value)); } return items; } let mut variants = vec![]; let mut found_values = HashMap::new(); - for item in enum_items { let name = ctx.ext_cx.ident_of(&item.name); - if let Some(orig) = found_values.get(&item.val) { let value = ctx.ext_cx.expr_path( ctx.ext_cx.path(ctx.span, vec![enum_name, *orig])); @@ -841,7 +1328,6 @@ fn cenum_to_rs( } found_values.insert(item.val, name); - let value = cenum_value_to_int_lit( ctx, enum_is_signed, layout.size, item.val); @@ -857,20 +1343,16 @@ fn cenum_to_rs( let repr_arg = ctx.ext_cx.meta_word(ctx.span, enum_repr); let repr_list = ctx.ext_cx.meta_list(ctx.span, InternedString::new("repr"), vec![repr_arg]); - let repr_attr = respan(ctx.span, ast::Attribute_ { + + let mut attrs = mk_doc_attr(ctx, &comment); + attrs.push(respan(ctx.span, ast::Attribute_ { id: mk_attr_id(), style: ast::AttrStyle::Outer, value: repr_list, is_sugared_doc: false, - }); + })); - let attrs = { - let mut v = vec![mk_deriving_copy_attr(ctx, true), repr_attr]; - if derive_debug { - v.push(mk_deriving_debug_attr(ctx)); - } - v - }; + attrs.push(mk_deriving_copy_and_maybe_debug_attr(ctx)); items.push(P(ast::Item { ident: enum_name, @@ -890,15 +1372,14 @@ fn cenum_to_rs( /// These are emitted into `extra`. fn gen_comp_methods(ctx: &mut GenCtx, data_field: &str, data_offset: usize, kind: CompKind, members: &[CompMember], - extra: &mut Vec<P<ast::Item>>, - derive_debug: bool) -> Vec<ast::ImplItem> { + extra: &mut Vec<P<ast::Item>>) -> Vec<ast::ImplItem> { let mk_field_method = |ctx: &mut GenCtx, f: &FieldInfo, offset: usize| { // TODO: Implement bitfield accessors if f.bitfields.is_some() { return None; } - let (f_name, _) = rust_id(ctx, f.name.clone()); - let ret_ty = P(cty_to_rs(ctx, &TPtr(Box::new(f.ty.clone()), false, Layout::zero()))); + let f_name = first(rust_id(ctx, &f.name)); + let ret_ty = P(cty_to_rs(ctx, &TPtr(Box::new(f.ty.clone()), false, false, Layout::zero()), true, true)); // When the offset is zero, generate slightly prettier code. let method = { @@ -912,7 +1393,7 @@ fn gen_comp_methods(ctx: &mut GenCtx, data_field: &str, data_offset: usize, ", f_name, tts_to_string(&ret_ty.to_tokens(&ctx.ext_cx)[..]), data_field, offset); parse::new_parser_from_source_str(ctx.ext_cx.parse_sess(), - ctx.ext_cx.cfg(), "".to_owned(), impl_str).parse_item().unwrap().unwrap() + ctx.ext_cx.cfg(), "".to_string(), impl_str).parse_item().unwrap().unwrap() }; method.and_then(|i| { @@ -936,18 +1417,18 @@ fn gen_comp_methods(ctx: &mut GenCtx, data_field: &str, data_offset: usize, CompMember::Comp(ref rc_c) => { let c = &rc_c.borrow(); methods.extend(gen_comp_methods(ctx, data_field, offset, c.kind, - &c.members, extra, derive_debug).into_iter()); + &c.members, extra).into_iter()); c.layout.size } CompMember::CompField(ref rc_c, ref f) => { methods.extend(mk_field_method(ctx, f, offset).into_iter()); let c = rc_c.borrow(); - extra.extend(comp_to_rs(ctx, c.kind, comp_name(c.kind, &c.name), - derive_debug, - c.layout, c.members.clone()).into_iter()); + let name = comp_name(&ctx, c.kind, &c.name); + extra.extend(comp_to_rs(ctx, &name, c.clone()).into_iter()); f.ty.size() } + CompMember::Enum(_) => 0 }; match kind { CompKind::Struct => { offset += advance_by; } @@ -957,28 +1438,121 @@ fn gen_comp_methods(ctx: &mut GenCtx, data_field: &str, data_offset: usize, methods } -// Implements std::default::Default using std::mem::zeroed. -fn mk_default_impl(ctx: &GenCtx, ty_name: &str) -> P<ast::Item> { - let impl_str = format!(r" - impl ::std::default::Default for {} {{ - fn default() -> Self {{ unsafe {{ ::std::mem::zeroed() }} }} - }} - ", ty_name); +fn type_for_bitfield_width(ctx: &mut GenCtx, width: u32) -> ast::Ty { + let input_type = if width > 16 { + "u32" + } else if width > 8 { + "u16" + } else if width > 1 { + "u8" + } else { + "bool" + }; + mk_ty(ctx, false, &[input_type.to_owned()]) +} - parse::new_parser_from_source_str(ctx.ext_cx.parse_sess(), - ctx.ext_cx.cfg(), "".to_owned(), impl_str).parse_item().unwrap().unwrap() +fn gen_bitfield_method(ctx: &mut GenCtx, bindgen_name: &String, + field_name: &String, field_type: &Type, + offset: usize, width: u32) -> ast::ImplItem { + let input_type = type_for_bitfield_width(ctx, width); + let field_type = cty_to_rs(ctx, &field_type, false, true); + let setter_name = ctx.ext_cx.ident_of(&format!("set_{}", field_name)); + let bindgen_ident = ctx.ext_cx.ident_of(&*bindgen_name); + + let node = "e_item!(&ctx.ext_cx, + impl X { + pub fn $setter_name(&mut self, val: $input_type) { + self.$bindgen_ident &= !(((1 << $width) - 1) << $offset); + self.$bindgen_ident |= (val as $field_type) << $offset; + } + } + ).unwrap().node; + match node { + &ast::ItemKind::Impl(_, _, _, _, _, ref items) => items[0].clone(), + _ => unreachable!() + } } -// Implements std::clone::Clone using dereferencing -fn mk_clone_impl(ctx: &GenCtx, ty_name: &str) -> P<ast::Item> { - let impl_str = format!(r" - impl ::std::clone::Clone for {} {{ - fn clone(&self) -> Self {{ *self }} - }} - ", ty_name); +fn gen_fullbitfield_method(ctx: &mut GenCtx, bindgen_name: &String, + field_type: &Type, bitfields: &[(String, u32)]) -> ast::ImplItem { + let field_type = cty_to_rs(ctx, field_type, false, true); + let mut args = vec!(); + let mut unnamed: usize = 0; + for &(ref name, width) in bitfields.iter() { + let ident = if name.is_empty() { + unnamed += 1; + let dummy = format!("unnamed_bitfield{}", unnamed); + ctx.ext_cx.ident_of(&dummy) + } else { + ctx.ext_cx.ident_of(name) + }; + args.push(ast::Arg { + ty: P(type_for_bitfield_width(ctx, width)), + pat: P(ast::Pat { + id: ast::DUMMY_NODE_ID, + node: ast::PatKind::Ident( + ast::BindingMode::ByValue(ast::Mutability::Immutable), + respan(ctx.span, ident), + None + ), + span: ctx.span + }), + id: ast::DUMMY_NODE_ID, + }); + } + + let fndecl = ast::FnDecl { + inputs: args, + output: ast::FunctionRetTy::Ty(P(field_type.clone())), + variadic: false + }; + + let stmts = Vec::with_capacity(bitfields.len() + 1); - parse::new_parser_from_source_str(ctx.ext_cx.parse_sess(), - ctx.ext_cx.cfg(), "".to_owned(), impl_str).parse_item().unwrap().unwrap() + let mut offset = 0; + let mut exprs = quote_expr!(&ctx.ext_cx, 0); + let mut unnamed: usize = 0; + for &(ref name, width) in bitfields.iter() { + let name_ident = if name.is_empty() { + unnamed += 1; + let dummy = format!("unnamed_bitfield{}", unnamed); + ctx.ext_cx.ident_of(&dummy) + } else { + ctx.ext_cx.ident_of(name) + }; + exprs = quote_expr!(&ctx.ext_cx, + $exprs | (($name_ident as $field_type) << $offset) + ); + offset += width; + } + + let block = ast::Block { + stmts: stmts, + expr: Some(exprs), + id: ast::DUMMY_NODE_ID, + rules: ast::BlockCheckMode::Default, + span: ctx.span + }; + + let node = ast::ImplItemKind::Method( + ast::MethodSig { + unsafety: ast::Unsafety::Normal, + abi: Abi::Rust, + decl: P(fndecl), + generics: empty_generics(), + explicit_self: respan(ctx.span, ast::SelfKind::Static), + constness: ast::Constness::Const, + }, P(block) + ); + + ast::ImplItem { + id: ast::DUMMY_NODE_ID, + ident: ctx.ext_cx.ident_of(&format!("new{}", bindgen_name)), + vis: ast::Visibility::Public, + attrs: vec!(), + node: node, + span: ctx.span, + } } fn mk_blob_field(ctx: &GenCtx, name: &str, layout: Layout) -> Spanned<ast::StructField_> { @@ -989,8 +1563,13 @@ fn mk_blob_field(ctx: &GenCtx, name: &str, layout: Layout) -> Spanned<ast::Struc 1 | _ => "u8", }; let data_len = if ty_name == "u8" { layout.size } else { layout.size / layout.align }; - let base_ty = mk_ty(ctx, false, vec!(ty_name.to_owned())); - let data_ty = P(mk_arrty(ctx, &base_ty, data_len)); + + let base_ty = mk_ty(ctx, false, &[ty_name.to_owned()]); + let data_ty = if data_len == 1 { + P(base_ty) + } else { + P(mk_arrty(ctx, &base_ty, data_len)) + }; respan(ctx.span, ast::StructField_ { kind: ast::NamedField( ctx.ext_cx.ident_of(name), @@ -1003,12 +1582,9 @@ fn mk_blob_field(ctx: &GenCtx, name: &str, layout: Layout) -> Spanned<ast::Struc } fn mk_link_name_attr(ctx: &mut GenCtx, name: String) -> ast::Attribute { - let lit = respan(ctx.span, ast::LitKind::Str( - to_intern_str(ctx, name), - ast::StrStyle::Cooked - )); + let lit = respan(ctx.span, ast::LitKind::Str(intern(&name).as_str(), ast::StrStyle::Cooked)); let attr_val = P(respan(ctx.span, ast::MetaItemKind::NameValue( - to_intern_str(ctx, "link_name".to_owned()), lit + InternedString::new("link_name"), lit ))); let attr = ast::Attribute_ { id: mk_attr_id(), @@ -1020,12 +1596,12 @@ fn mk_link_name_attr(ctx: &mut GenCtx, name: String) -> ast::Attribute { } fn mk_repr_attr(ctx: &mut GenCtx, layout: Layout) -> ast::Attribute { - let mut values = vec!(P(respan(ctx.span, ast::MetaItemKind::Word(to_intern_str(ctx, "C".to_owned()))))); + let mut values = vec!(P(respan(ctx.span, ast::MetaItemKind::Word(InternedString::new("C"))))); if layout.packed { - values.push(P(respan(ctx.span, ast::MetaItemKind::Word(to_intern_str(ctx, "packed".to_owned()))))); + values.push(P(respan(ctx.span, ast::MetaItemKind::Word(InternedString::new("packed"))))); } let attr_val = P(respan(ctx.span, ast::MetaItemKind::List( - to_intern_str(ctx, "repr".to_owned()), + InternedString::new("repr"), values ))); @@ -1037,14 +1613,25 @@ fn mk_repr_attr(ctx: &mut GenCtx, layout: Layout) -> ast::Attribute { }) } -fn mk_deriving_copy_attr(ctx: &mut GenCtx, clone: bool) -> ast::Attribute { - let mut words = vec!(); - if clone { - words.push(ctx.ext_cx.meta_word(ctx.span, InternedString::new("Clone"))); +fn mk_deriving_copy_attr(ctx: &mut GenCtx) -> ast::Attribute { + mk_deriving_attr(ctx, &["Copy", "Clone"]) +} + +fn mk_deriving_copy_and_maybe_debug_attr(ctx: &mut GenCtx) -> ast::Attribute { + if ctx.options.derive_debug { + mk_deriving_attr(ctx, &["Copy", "Clone", "Debug"]) + } else { + mk_deriving_copy_attr(ctx) } - words.push(ctx.ext_cx.meta_word(ctx.span, InternedString::new("Copy"))); +} - let attr_val = ctx.ext_cx.meta_list(ctx.span, InternedString::new("derive"), words); +fn mk_deriving_attr(ctx: &mut GenCtx, attrs: &[&'static str]) -> ast::Attribute { + let attr_val = P(respan(ctx.span, ast::MetaItemKind::List( + InternedString::new("derive"), + attrs.iter().map(|attr| { + P(respan(ctx.span, ast::MetaItemKind::Word(InternedString::new(attr)))) + }).collect() + ))); respan(ctx.span, ast::Attribute_ { id: mk_attr_id(), @@ -1054,33 +1641,40 @@ fn mk_deriving_copy_attr(ctx: &mut GenCtx, clone: bool) -> ast::Attribute { }) } -fn mk_deriving_debug_attr(ctx: &mut GenCtx) -> ast::Attribute { - let words = vec!(ctx.ext_cx.meta_word(ctx.span, InternedString::new("Debug"))); +fn mk_doc_attr(ctx: &mut GenCtx, doc: &str) -> Vec<ast::Attribute> { + if doc.is_empty() { + return vec!(); + } - let attr_val = ctx.ext_cx.meta_list(ctx.span, InternedString::new("derive"), words); + let attr_val = P(respan(ctx.span, ast::MetaItemKind::NameValue( + InternedString::new("doc"), + respan(ctx.span, ast::LitKind::Str(intern(doc).as_str(), ast::StrStyle::Cooked)) + ))); - respan(ctx.span, ast::Attribute_ { + vec!(respan(ctx.span, ast::Attribute_ { id: mk_attr_id(), style: ast::AttrStyle::Outer, value: attr_val, - is_sugared_doc: false - }) + is_sugared_doc: true + })) } fn cvar_to_rs(ctx: &mut GenCtx, name: String, - ty: &Type, - is_const: bool) -> ast::ForeignItem { - let (rust_name, was_mangled) = rust_id(ctx, name.clone()); + mangled: String, ty: &Type, + is_const: bool) -> ast::ForeignItem { + let (rust_name, was_mangled) = rust_id(ctx, &name); let mut attrs = Vec::new(); - if was_mangled { + if !mangled.is_empty() { + attrs.push(mk_link_name_attr(ctx, mangled)); + } else if was_mangled { attrs.push(mk_link_name_attr(ctx, name)); } - let val_ty = P(cty_to_rs(ctx, ty)); + let val_ty = P(cty_to_rs(ctx, ty, true, true)); ast::ForeignItem { - ident: ctx.ext_cx.ident_of(&rust_name[..]), + ident: ctx.ext_cx.ident_of(&rust_name), attrs: attrs, node: ast::ForeignItemKind::Static(val_ty, !is_const), id: ast::DUMMY_NODE_ID, @@ -1096,7 +1690,10 @@ fn cfuncty_to_rs(ctx: &mut GenCtx, let ret = match *rty { TVoid => ast::FunctionRetTy::Default(ctx.span), - _ => ast::FunctionRetTy::Ty(P(cty_to_rs(ctx, rty))) + // Disable references in returns for now + TPtr(ref t, is_const, _, ref layout) => + ast::FunctionRetTy::Ty(P(cty_to_rs(ctx, &TPtr(t.clone(), is_const, false, layout.clone()), true, true))), + _ => ast::FunctionRetTy::Ty(P(cty_to_rs(ctx, rty, true, true))) }; let mut unnamed: usize = 0; @@ -1107,7 +1704,7 @@ fn cfuncty_to_rs(ctx: &mut GenCtx, unnamed += 1; format!("arg{}", unnamed) } else { - first(rust_id(ctx, n.clone())) + first(rust_id(ctx, &n)) }; // From the C90 standard (http://c0x.coding-guidelines.com/6.7.5.3.html) @@ -1116,8 +1713,8 @@ fn cfuncty_to_rs(ctx: &mut GenCtx, // (if any) are those specified within the [ and ] of the array type // derivation. let arg_ty = P(match *t { - TArray(ref typ, _, l) => cty_to_rs(ctx, &TPtr(typ.clone(), false, l)), - _ => cty_to_rs(ctx, t), + TArray(ref typ, _, l) => cty_to_rs(ctx, &TPtr(typ.clone(), false, false, l), true, true), + _ => cty_to_rs(ctx, t, true, true), }); ast::Arg { @@ -1126,7 +1723,7 @@ fn cfuncty_to_rs(ctx: &mut GenCtx, id: ast::DUMMY_NODE_ID, node: ast::PatKind::Ident( ast::BindingMode::ByValue(ast::Mutability::Immutable), - respan(ctx.span, ctx.ext_cx.ident_of(&arg_name[..])), + respan(ctx.span, ctx.ext_cx.ident_of(&arg_name)), None ), span: ctx.span @@ -1143,33 +1740,40 @@ fn cfuncty_to_rs(ctx: &mut GenCtx, } } -fn cfunc_to_rs(ctx: &mut GenCtx, name: String, rty: &Type, +fn cfunc_to_rs(ctx: &mut GenCtx, + name: String, + mangled: String, + comment: String, + rty: &Type, aty: &[(String, Type)], - var: bool) -> ast::ForeignItem { + var: bool, + vis: ast::Visibility) -> ast::ForeignItem { let var = !aty.is_empty() && var; let decl = ast::ForeignItemKind::Fn( P(cfuncty_to_rs(ctx, rty, aty, var)), empty_generics() ); - let (rust_name, was_mangled) = rust_id(ctx, name.clone()); + let (rust_name, was_mangled) = rust_id(ctx, &name); - let mut attrs = Vec::new(); - if was_mangled { + let mut attrs = mk_doc_attr(ctx, &comment); + if !mangled.is_empty() { + attrs.push(mk_link_name_attr(ctx, mangled)); + } else if was_mangled { attrs.push(mk_link_name_attr(ctx, name)); } ast::ForeignItem { - ident: ctx.ext_cx.ident_of(&rust_name[..]), + ident: ctx.ext_cx.ident_of(&rust_name), attrs: attrs, node: decl, id: ast::DUMMY_NODE_ID, span: ctx.span, - vis: ast::Visibility::Public, + vis: vis, } } -fn cty_to_rs(ctx: &mut GenCtx, ty: &Type) -> ast::Ty { +fn cty_to_rs(ctx: &mut GenCtx, ty: &Type, allow_bool: bool, use_full_path: bool) -> ast::Ty { let prefix = vec!["std".to_owned(), "os".to_owned(), "raw".to_owned()]; let raw = |fragment: &str| { let mut path = prefix.clone(); @@ -1178,79 +1782,147 @@ fn cty_to_rs(ctx: &mut GenCtx, ty: &Type) -> ast::Ty { }; match *ty { - TVoid => mk_ty(ctx, true, raw("c_void")), + TVoid => mk_ty(ctx, true, &raw("c_void")), TInt(i, ref layout) => match i { IBool => { let ty_name = match layout.size { - 8 => "u64", - 4 => "u32", + 1 if allow_bool => "bool", 2 => "u16", - 1 | _ => "u8", + 4 => "u32", + 8 => "u64", + _ => "u8", }; - mk_ty(ctx, false, vec!(ty_name.to_owned())) + mk_ty(ctx, false, &[ty_name.to_owned()]) }, - ISChar => mk_ty(ctx, true, raw("c_char")), - IUChar => mk_ty(ctx, true, raw("c_uchar")), - IInt => mk_ty(ctx, true, raw("c_int")), - IUInt => mk_ty(ctx, true, raw("c_uint")), - IShort => mk_ty(ctx, true, raw("c_short")), - IUShort => mk_ty(ctx, true, raw("c_ushort")), - ILong => mk_ty(ctx, true, raw("c_long")), - IULong => mk_ty(ctx, true, raw("c_ulong")), - ILongLong => mk_ty(ctx, true, raw("c_longlong")), - IULongLong => mk_ty(ctx, true, raw("c_ulonglong")) + ISChar => mk_ty(ctx, true, &raw("c_char")), + IUChar => mk_ty(ctx, true, &raw("c_uchar")), + IInt => mk_ty(ctx, true, &raw("c_int")), + IUInt => mk_ty(ctx, true, &raw("c_uint")), + IShort => mk_ty(ctx, true, &raw("c_short")), + IUShort => mk_ty(ctx, true, &raw("c_ushort")), + ILong => mk_ty(ctx, true, &raw("c_long")), + IULong => mk_ty(ctx, true, &raw("c_ulong")), + ILongLong => mk_ty(ctx, true, &raw("c_longlong")), + IULongLong => mk_ty(ctx, true, &raw("c_ulonglong")) }, TFloat(f, _) => match f { - FFloat => mk_ty(ctx, true, raw("c_float")), - FDouble => mk_ty(ctx, true, raw("c_double")) + FFloat => mk_ty(ctx, false, &["f32".to_owned()]), + FDouble => mk_ty(ctx, false, &["f64".to_owned()]) }, - TPtr(ref t, is_const, _) => { - let id = cty_to_rs(ctx, &**t); + TPtr(ref t, is_const, _is_ref, _) => { + let id = cty_to_rs(ctx, &**t, allow_bool, use_full_path); mk_ptrty(ctx, &id, is_const) }, TArray(ref t, s, _) => { - let ty = cty_to_rs(ctx, &**t); + let ty = cty_to_rs(ctx, &**t, allow_bool, use_full_path); mk_arrty(ctx, &ty, s) }, TFuncPtr(ref sig) => { let decl = cfuncty_to_rs(ctx, &*sig.ret_ty, &sig.args[..], sig.is_variadic); - let unsafety = if sig.is_safe { ast::Unsafety::Normal } else { ast::Unsafety::Unsafe }; - mk_fnty(ctx, &decl, unsafety, sig.abi) + mk_fnty(ctx, &decl, sig.abi) }, TFuncProto(ref sig) => { let decl = cfuncty_to_rs(ctx, &*sig.ret_ty, &sig.args[..], sig.is_variadic); - let unsafety = if sig.is_safe { ast::Unsafety::Normal } else { ast::Unsafety::Unsafe }; - mk_fn_proto_ty(ctx, &decl, unsafety, sig.abi) + mk_fn_proto_ty(ctx, &decl, sig.abi) }, TNamed(ref ti) => { - let id = rust_type_id(ctx, ti.borrow().name.clone()); - mk_ty(ctx, false, vec!(id)) + let id = rust_type_id(ctx, &ti.borrow().name); + + if use_full_path { + let mut path = ctx.full_path_for_module(ti.borrow().module_id); + path.push(id); + mk_ty(ctx, false, &path) + } else { + mk_ty(ctx, false, &[id]) + } }, TComp(ref ci) => { - let mut c = ci.borrow_mut(); - c.name = unnamed_name(ctx, c.name.clone()); - mk_ty(ctx, false, vec!(comp_name(c.kind, &c.name))) + let c = ci.borrow(); + let id = comp_name(&ctx, c.kind, &c.name); + + let args = c.args.iter().map(|gt| { + P(cty_to_rs(ctx, gt, allow_bool, false)) + }).collect(); + + if use_full_path { + let mut path = ctx.full_path_for_module(c.module_id); + path.push(id); + mk_ty_args(ctx, false, &path, args) + } else { + mk_ty_args(ctx, false, &[id], args) + } + }, TEnum(ref ei) => { - let mut e = ei.borrow_mut(); - e.name = unnamed_name(ctx, e.name.clone()); - mk_ty(ctx, false, vec!(enum_name(&e.name))) + let e = ei.borrow(); + let id = enum_name(&ctx, &e.name); + + if use_full_path { + let mut path = ctx.full_path_for_module(e.module_id); + path.push(id); + mk_ty(ctx, false, &path) + } else { + mk_ty(ctx, false, &[id]) + } + } + } +} + +fn cty_is_translatable(ty: &Type) -> bool { + match ty { + &TVoid => false, + &TArray(ref t, _, _) => { + cty_is_translatable(&**t) + }, + &TComp(ref ci) => { + let c = ci.borrow(); + !c.args.iter().any(|gt| gt == &TVoid) + }, + _ => true, + } +} + +fn cty_has_destructor(ty: &Type) -> bool { + match ty { + &TArray(ref t, _, _) => { + cty_has_destructor(&**t) } + &TComp(ref ci) => { + let c = ci.borrow(); + if c.has_destructor || c.members.iter().any(|f| match f { + &CompMember::Field(ref f) | + &CompMember::CompField(_, ref f) => + cty_has_destructor(&f.ty), + _ => false, + }) { + return true; + } + c.ref_template.is_some() + }, + &TNamed(ref ti) => { + cty_has_destructor(&ti.borrow().ty) + }, + _ => false, } } -fn mk_ty(ctx: &GenCtx, global: bool, segments: Vec<String>) -> ast::Ty { +fn mk_ty(ctx: &GenCtx, global: bool, segments: &[String]) -> ast::Ty { + mk_ty_args(ctx, global, segments, vec!()) +} + +fn mk_ty_args(ctx: &GenCtx, global: bool, segments: &[String], args: Vec<P<ast::Ty>>) -> ast::Ty { + let segment_count = segments.len(); let ty = ast::TyKind::Path( None, ast::Path { span: ctx.span, global: global, - segments: segments.iter().map(|s| { + segments: segments.iter().enumerate().map(|(i, s)| { ast::PathSegment { - identifier: ctx.ext_cx.ident_of(&s[..]), + identifier: ctx.ext_cx.ident_of(s), parameters: ast::PathParameters::AngleBracketed(ast::AngleBracketedParameterData { - lifetimes: Vec::new(), - types: OwnedSlice::empty(), + lifetimes: vec!(), + types: OwnedSlice::from_vec(if i == segment_count - 1 { args.clone() } else { vec![] }), bindings: OwnedSlice::empty(), }), } @@ -1278,6 +1950,23 @@ fn mk_ptrty(ctx: &mut GenCtx, base: &ast::Ty, is_const: bool) -> ast::Ty { } } +#[allow(dead_code)] +fn mk_refty(ctx: &mut GenCtx, base: &ast::Ty, is_const: bool) -> ast::Ty { + let ty = ast::TyKind::Rptr( + None, + ast::MutTy { + ty: P(base.clone()), + mutbl: if is_const { ast::Mutability::Immutable } else { ast::Mutability::Mutable } + } + ); + + ast::Ty { + id: ast::DUMMY_NODE_ID, + node: ty, + span: ctx.span + } +} + fn mk_arrty(ctx: &GenCtx, base: &ast::Ty, n: usize) -> ast::Ty { let int_lit = ast::LitKind::Int(n as u64, ast::LitIntType::Unsigned(ast::UintTy::Us)); let sz = ast::ExprKind::Lit(P(respan(ctx.span, int_lit))); @@ -1300,10 +1989,9 @@ fn mk_arrty(ctx: &GenCtx, base: &ast::Ty, n: usize) -> ast::Ty { fn mk_fn_proto_ty(ctx: &mut GenCtx, decl: &ast::FnDecl, - unsafety: ast::Unsafety, - abi: abi::Abi) -> ast::Ty { + abi: Abi) -> ast::Ty { let fnty = ast::TyKind::BareFn(P(ast::BareFnTy { - unsafety: unsafety, + unsafety: ast::Unsafety::Unsafe, abi: abi, lifetimes: Vec::new(), decl: P(decl.clone()) @@ -1316,12 +2004,9 @@ fn mk_fn_proto_ty(ctx: &mut GenCtx, } } -fn mk_fnty(ctx: &mut GenCtx, - decl: &ast::FnDecl, - unsafety: ast::Unsafety, - abi: abi::Abi) -> ast::Ty { +fn mk_fnty(ctx: &mut GenCtx, decl: &ast::FnDecl, abi: Abi) -> ast::Ty { let fnty = ast::TyKind::BareFn(P(ast::BareFnTy { - unsafety: unsafety, + unsafety: ast::Unsafety::Unsafe, abi: abi, lifetimes: Vec::new(), decl: P(decl.clone()) @@ -1,12 +1,14 @@ #![crate_name = "bindgen"] #![crate_type = "dylib"] +#![feature(quote)] #![cfg_attr(feature = "clippy", feature(plugin))] #![cfg_attr(feature = "clippy", plugin(clippy))] extern crate syntex_syntax as syntax; extern crate libc; -#[macro_use] extern crate log; +#[macro_use] +extern crate log; use std::collections::HashSet; use std::default::Default; @@ -21,7 +23,7 @@ use syntax::print::pprust; use syntax::print::pp::eof; use syntax::ptr::P; -use types::Global; +use types::ModuleMap; mod types; mod clangll; @@ -84,6 +86,11 @@ impl<'a> Builder<'a> { self } + pub fn rename_types(&mut self, value: bool) -> &mut Self { + self.options.rename_types = value; + self + } + pub fn log(&mut self, logger: &'a Logger) -> &mut Self { self.logger = Some(logger); self @@ -111,10 +118,13 @@ pub struct BindgenOptions { pub rust_enums: bool, pub links: Vec<(String, LinkType)>, pub emit_ast: bool, + pub ignore_functions: bool, pub fail_on_unknown_type: bool, + pub enable_cxx_namespaces: bool, + pub rename_types: bool, + pub derive_debug: bool, pub override_enum_ty: String, pub clang_args: Vec<String>, - pub derive_debug: bool, } impl Default for BindgenOptions { @@ -125,18 +135,18 @@ impl Default for BindgenOptions { rust_enums: true, links: Vec::new(), emit_ast: false, - fail_on_unknown_type: false, - override_enum_ty: "".to_owned(), - clang_args: match get_include_dir() { - Some(path) => vec!("-idirafter".to_owned(), path), - None => Vec::new() - }, - derive_debug: true + ignore_functions: false, + fail_on_unknown_type: true, + rename_types: true, + derive_debug: true, + enable_cxx_namespaces: false, + override_enum_ty: "".to_string(), + clang_args: Vec::new() } } } -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] pub enum LinkType { Default, Static, @@ -148,7 +158,7 @@ pub trait Logger { fn warn(&self, msg: &str); } -#[derive(Clone)] +#[derive(Debug, Clone)] pub struct Bindings { module: ast::Mod } @@ -157,21 +167,18 @@ impl Bindings { /// Deprecated - use a `Builder` instead pub fn generate(options: &BindgenOptions, logger: Option<&Logger>, span: Option<Span>) -> Result<Bindings, ()> { let l = DummyLogger; - let logger = match logger { - Some(l) => l, - None => &l as &Logger - }; + let logger = logger.unwrap_or(&l as &Logger); - let span = match span { - Some(s) => s, - None => DUMMY_SP - }; + let span = span.unwrap_or(DUMMY_SP); - let globals = try!(parse_headers(options, logger)); + let module_map = try!(parse_headers(options, logger)); let module = ast::Mod { inner: span, - items: gen::gen_mod(options, globals, span) + items: gen::gen_mods(&options.links[..], + module_map, + options.clone(), + span) }; Ok(Bindings { @@ -217,7 +224,7 @@ impl Logger for DummyLogger { fn warn(&self, _msg: &str) { } } -fn parse_headers(options: &BindgenOptions, logger: &Logger) -> Result<Vec<Global>, ()> { +fn parse_headers(options: &BindgenOptions, logger: &Logger) -> Result<ModuleMap, ()> { fn str_to_ikind(s: &str) -> Option<types::IKind> { match s { "uchar" => Some(types::IUChar), @@ -239,8 +246,10 @@ fn parse_headers(options: &BindgenOptions, logger: &Logger) -> Result<Vec<Global builtins: options.builtins, match_pat: options.match_pat.clone(), emit_ast: options.emit_ast, + ignore_functions: options.ignore_functions, fail_on_unknown_type: options.fail_on_unknown_type, - override_enum_ty: str_to_ikind(&options.override_enum_ty[..]), + enable_cxx_namespaces: options.enable_cxx_namespaces, + override_enum_ty: str_to_ikind(&options.override_enum_ty), clang_args: options.clang_args.clone(), }; diff --git a/src/parser.rs b/src/parser.rs index 7895aa68..c6fc174f 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1,27 +1,30 @@ #![allow(non_upper_case_globals)] use std::collections::{HashMap, HashSet}; -use std::collections::hash_map; use std::cell::RefCell; use std::ops::Deref; use std::rc::Rc; +use std::path::Path; use syntax::abi; use types as il; use types::*; use clang as cx; -use clang::{ast_dump, Cursor, Diagnostic, TranslationUnit, type_to_str}; +use clang::{ast_dump, Comment, Cursor, Diagnostic, TranslationUnit, type_to_str, kind_to_str}; use clangll::*; use super::Logger; +#[derive(Clone)] pub struct ClangParserOptions { pub builtin_names: HashSet<String>, pub builtins: bool, pub match_pat: Vec<String>, pub emit_ast: bool, pub fail_on_unknown_type: bool, + pub ignore_functions: bool, + pub enable_cxx_namespaces: bool, pub override_enum_ty: Option<il::IKind>, pub clang_args: Vec<String>, } @@ -29,10 +32,26 @@ pub struct ClangParserOptions { struct ClangParserCtx<'a> { options: ClangParserOptions, name: HashMap<Cursor, Global>, - globals: Vec<Global>, builtin_defs: Vec<Cursor>, + module_map: ModuleMap, + current_module_id: ModuleId, logger: &'a (Logger+'a), - err_count: i32 + err_count: i32, + anonymous_modules_found: usize, +} + +impl<'a> ClangParserCtx<'a> { + fn module(&self, id: &ModuleId) -> &Module { + self.module_map.get(id).expect("Module not found!") + } + + fn current_module(&self) -> &Module { + self.module(&self.current_module_id) + } + + fn current_module_mut(&mut self) -> &mut Module { + self.module_map.get_mut(&self.current_module_id).expect("Module not found!") + } } fn match_pattern(ctx: &mut ClangParserCtx, cursor: &Cursor) -> bool { @@ -47,76 +66,119 @@ fn match_pattern(ctx: &mut ClangParserCtx, cursor: &Cursor) -> bool { return true; } - let mut found = false; - ctx.options.match_pat.iter().all(|pat| { - if (&name[..]).contains(pat) { - found = true; - } - true - }); - - found + let name = file.name(); + ctx.options.match_pat.iter().any(|pat| name.contains(pat)) } fn decl_name(ctx: &mut ClangParserCtx, cursor: &Cursor) -> Global { let cursor = cursor.canonical(); - let mut new_decl = false; let override_enum_ty = ctx.options.override_enum_ty; - let decl = match ctx.name.entry(cursor) { - hash_map::Entry::Occupied(ref e) => e.get().clone(), - hash_map::Entry::Vacant(e) => { - new_decl = true; - let spelling = cursor.spelling(); - let ty = cursor.cur_type(); - let layout = Layout::new(ty.size(), ty.align()); + let new_decl = !ctx.name.contains_key(&cursor); - let glob_decl = match cursor.kind() { - CXCursor_StructDecl => { - let ci = Rc::new(RefCell::new(CompInfo::new(spelling, CompKind::Struct, vec!(), layout))); - GCompDecl(ci) - } - CXCursor_UnionDecl => { - let ci = Rc::new(RefCell::new(CompInfo::new(spelling, CompKind::Union, vec!(), layout))); - GCompDecl(ci) - } - CXCursor_EnumDecl => { - let kind = match override_enum_ty { - Some(t) => t, - None => match cursor.enum_type().kind() { - CXType_SChar | CXType_Char_S => ISChar, - CXType_UChar | CXType_Char_U => IUChar, - CXType_UShort => IUShort, - CXType_UInt => IUInt, - CXType_ULong => IULong, - CXType_ULongLong => IULongLong, - CXType_Short => IShort, - CXType_Int => IInt, - CXType_Long => ILong, - CXType_LongLong => ILongLong, - _ => IInt, + let decl = if new_decl { + let spelling = cursor.spelling(); + let comment = cursor.raw_comment(); + let (file, _, _, _) = cursor.location().location(); + let ty = cursor.cur_type(); + let layout = Layout::new(ty.size(), ty.align()); + let filename = match Path::new(&file.name()).file_name() { + Some(name) => name.to_string_lossy().replace(".", "_"), + _ => "".to_string() + }; + let glob_decl = match cursor.kind() { + CXCursor_StructDecl => { + let ci = Rc::new(RefCell::new(CompInfo::new(spelling, ctx.current_module_id, filename, comment, CompKind::Struct, vec!(), layout))); + GCompDecl(ci) + } + CXCursor_UnionDecl => { + let ci = Rc::new(RefCell::new(CompInfo::new(spelling, ctx.current_module_id, filename, comment, CompKind::Union, vec!(), layout))); + GCompDecl(ci) + } + CXCursor_EnumDecl => { + let kind = match override_enum_ty { + Some(t) => t, + None => match cursor.enum_type().kind() { + CXType_SChar | CXType_Char_S => ISChar, + CXType_UChar | CXType_Char_U => IUChar, + CXType_UShort => IUShort, + CXType_UInt => IUInt, + CXType_ULong => IULong, + CXType_ULongLong => IULongLong, + CXType_Short => IShort, + CXType_Int => IInt, + CXType_Long => ILong, + CXType_LongLong => ILongLong, + _ => IInt, + } + }; + + let ei = Rc::new(RefCell::new(EnumInfo::new(spelling, ctx.current_module_id, filename, kind, vec!(), layout))); + GEnumDecl(ei) + } + CXCursor_ClassTemplate => { + let ci = Rc::new(RefCell::new(CompInfo::new(spelling, ctx.current_module_id, filename, comment, CompKind::Struct, vec!(), layout))); + GCompDecl(ci) + } + CXCursor_ClassDecl => { + let args = match ty.num_template_args() { + -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); + list.push(conv_ty(ctx, &arg_type, &cursor)); } - }; - let ei = Rc::new(RefCell::new(EnumInfo::new(spelling, kind, vec!(), layout))); - GEnumDecl(ei) - } - CXCursor_TypedefDecl => { - let ti = Rc::new(RefCell::new(TypeInfo::new(spelling, TVoid))); - GType(ti) - } - CXCursor_VarDecl => { - let vi = Rc::new(RefCell::new(VarInfo::new(spelling, TVoid))); - GVar(vi) - } - CXCursor_FunctionDecl => { - let vi = Rc::new(RefCell::new(VarInfo::new(spelling, TVoid))); - GFunc(vi) - } - _ => GOther, - }; + list + } + }; - e.insert(glob_decl.clone()); - glob_decl - }, + let module_id = if args.is_empty() { + ctx.current_module_id + } else { + // it's an instantiation of another template, + // find the canonical declaration to find the module it belongs to. + let parent = cursor.specialized(); + ctx.name.get(&parent).and_then(|global| { + if let GCompDecl(ref ci) = *global { + Some(ci.borrow().module_id) + } else { + None + } + }).unwrap_or_else(|| { + ctx.logger.warn("Template class wasn't declared when parsing specialisation!"); + ctx.current_module_id + }) + }; + + let ci = Rc::new(RefCell::new(CompInfo::new(spelling, module_id, filename, comment, CompKind::Struct, vec!(), layout))); + ci.borrow_mut().args = args; + GCompDecl(ci) + } + CXCursor_TypedefDecl => { + let ti = Rc::new(RefCell::new(TypeInfo::new(spelling, ctx.current_module_id, TVoid, layout))); + GType(ti) + } + CXCursor_VarDecl => { + let mangled = cursor.mangling(); + let vi = Rc::new(RefCell::new(VarInfo::new(spelling, mangled, comment, TVoid))); + GVar(vi) + } + CXCursor_MacroDefinition => { + let vi = Rc::new(RefCell::new(VarInfo::new(spelling, String::new(), comment, TVoid))); + GVar(vi) + } + CXCursor_FunctionDecl => { + let mangled = cursor.mangling(); + let vi = Rc::new(RefCell::new(VarInfo::new(spelling, mangled, comment, TVoid))); + GFunc(vi) + } + _ => GOther, + }; + + ctx.name.insert(cursor, glob_decl.clone()); + glob_decl + } else { + ctx.name.get(&cursor).unwrap().clone() }; if new_decl && ctx.options.builtin_names.contains(&cursor.spelling()) { @@ -128,7 +190,7 @@ fn decl_name(ctx: &mut ClangParserCtx, cursor: &Cursor) -> Global { fn opaque_decl(ctx: &mut ClangParserCtx, decl: &Cursor) { let name = decl_name(ctx, decl); - ctx.globals.push(name); + ctx.current_module_mut().globals.push(name); } fn fwd_decl<F:FnOnce(&mut ClangParserCtx)->()>(ctx: &mut ClangParserCtx, cursor: &Cursor, f: F) { @@ -153,45 +215,42 @@ fn get_abi(cc: Enum_CXCallingConv) -> abi::Abi { } } -fn conv_ptr_ty(ctx: &mut ClangParserCtx, ty: &cx::Type, cursor: &Cursor, layout: Layout) -> il::Type { +fn conv_ptr_ty(ctx: &mut ClangParserCtx, ty: &cx::Type, cursor: &Cursor, is_ref: bool, layout: Layout) -> il::Type { let is_const = ty.is_const(); match ty.kind() { CXType_Void => { - TPtr(Box::new(TVoid), is_const, layout) + return TPtr(Box::new(TVoid), is_const, is_ref, layout) } CXType_Unexposed | CXType_FunctionProto | CXType_FunctionNoProto => { let ret_ty = ty.ret_type(); - let decl = ty.declaration(); - if ret_ty.kind() != CXType_Invalid { + return if ret_ty.kind() != CXType_Invalid { TFuncPtr(mk_fn_sig(ctx, ty, cursor)) - } else if decl.kind() != CXCursor_NoDeclFound { - TPtr(Box::new(conv_decl_ty(ctx, &decl)), ty.is_const(), layout) } else if cursor.kind() == CXCursor_VarDecl { let can_ty = ty.canonical_type(); conv_ty(ctx, &can_ty, cursor) } else { - TPtr(Box::new(TVoid), ty.is_const(), layout) - } + TPtr(Box::new(conv_decl_ty(ctx, ty, cursor)), ty.is_const(), is_ref, layout) + }; } CXType_Typedef => { let decl = ty.declaration(); let def_ty = decl.typedef_type(); if def_ty.kind() == CXType_FunctionProto || def_ty.kind() == CXType_FunctionNoProto { - TPtr(Box::new(conv_ptr_ty(ctx, &def_ty, cursor, layout)), is_const, layout) + return TPtr(Box::new(conv_ptr_ty(ctx, &def_ty, cursor, is_ref, layout)), is_const, is_ref, layout); } else { - TPtr(Box::new(conv_ty(ctx, ty, cursor)), is_const, layout) + return TPtr(Box::new(conv_ty(ctx, ty, cursor)), is_const, is_ref, layout); } } - _ => TPtr(Box::new(conv_ty(ctx, ty, cursor)), is_const, layout), + _ => return TPtr(Box::new(conv_ty(ctx, ty, cursor)), is_const, is_ref, layout), } } fn mk_fn_sig(ctx: &mut ClangParserCtx, ty: &cx::Type, cursor: &Cursor) -> il::FuncSig { let args_lst: Vec<(String, il::Type)> = match cursor.kind() { - CXCursor_FunctionDecl => { + CXCursor_FunctionDecl | CXCursor_CXXMethod => { // For CXCursor_FunctionDecl, cursor.args() is the reliable way to // get parameter names and types. cursor.args().iter().map(|arg| { @@ -218,7 +277,7 @@ fn mk_fn_sig(ctx: &mut ClangParserCtx, ty: &cx::Type, cursor: &Cursor) -> il::Fu // Function is presumed unsafe if it takes a pointer argument. let is_unsafe = args_lst.iter().any(|arg| match arg.1 { - TPtr(_, _, _) => true, + TPtr(..) => true, _ => false }); @@ -231,34 +290,67 @@ fn mk_fn_sig(ctx: &mut ClangParserCtx, ty: &cx::Type, cursor: &Cursor) -> il::Fu } } -fn conv_decl_ty(ctx: &mut ClangParserCtx, cursor: &Cursor) -> il::Type { - match cursor.kind() { - CXCursor_StructDecl => { - let decl = decl_name(ctx, cursor); - let ci = decl.compinfo(); - TComp(ci) - } - CXCursor_UnionDecl => { - let decl = decl_name(ctx, cursor); +fn conv_decl_ty(ctx: &mut ClangParserCtx, ty: &cx::Type, cursor: &Cursor) -> il::Type { + let ty_decl = &ty.declaration(); + return match ty_decl.kind() { + CXCursor_StructDecl | + CXCursor_UnionDecl | + CXCursor_ClassTemplate | + CXCursor_ClassDecl => { + let decl = decl_name(ctx, ty_decl); + let args = match ty.num_template_args() { + -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); + list.push(conv_ty(ctx, &arg_type, &cursor)); + } + list + } + }; let ci = decl.compinfo(); + if !args.is_empty() { + ci.borrow_mut().args = args; + cursor.visit(|c, _: &Cursor| { + if c.kind() != CXCursor_TemplateRef { + return CXChildVisit_Continue; + } + let cref = c.definition(); + ci.borrow_mut().ref_template = Some(conv_decl_ty(ctx, &cref.cur_type(), &cref)); + CXChildVisit_Continue + }); + } TComp(ci) } CXCursor_EnumDecl => { - let decl = decl_name(ctx, cursor); + let decl = decl_name(ctx, ty_decl); let ei = decl.enuminfo(); TEnum(ei) } CXCursor_TypedefDecl => { - let decl = decl_name(ctx, cursor); + let decl = decl_name(ctx, ty_decl); let ti = decl.typeinfo(); TNamed(ti) } - _ => TVoid - } + CXCursor_NoDeclFound | CXCursor_TypeAliasDecl => { + let layout = Layout::new(ty.size(), ty.align()); + TNamed(Rc::new(RefCell::new(TypeInfo::new(ty.spelling().replace("const ", ""), ctx.current_module_id, TVoid, layout)))) + } + _ => { + let fail = ctx.options.fail_on_unknown_type; + log_err_warn(ctx, + &format!("unsupported decl `{}` ({})", + kind_to_str(ty_decl.kind()), ty_decl.location() + ), + fail + ); + TVoid + } + }; } fn conv_ty(ctx: &mut ClangParserCtx, ty: &cx::Type, cursor: &Cursor) -> il::Type { - debug!("conv_ty: ty=`{}` sp=`{}` loc=`{}`", type_to_str(ty.kind()), cursor.spelling(), cursor.location()); let layout = Layout::new(ty.size(), ty.align()); match ty.kind() { CXType_Void | CXType_Invalid => TVoid, @@ -267,6 +359,8 @@ fn conv_ty(ctx: &mut ClangParserCtx, ty: &cx::Type, cursor: &Cursor) -> il::Type CXType_Char_S => TInt(ISChar, layout), CXType_UChar | CXType_Char_U => TInt(IUChar, layout), + CXType_WChar => TInt(IShort, layout), + CXType_Char16 | CXType_UShort => TInt(IUShort, layout), CXType_UInt => TInt(IUInt, layout), CXType_ULong => TInt(IULong, layout), @@ -278,22 +372,23 @@ fn conv_ty(ctx: &mut ClangParserCtx, ty: &cx::Type, cursor: &Cursor) -> il::Type CXType_Float => TFloat(FFloat, layout), CXType_Double => TFloat(FDouble, layout), CXType_LongDouble => TFloat(FDouble, layout), - CXType_Pointer => conv_ptr_ty(ctx, &ty.pointee_type(), cursor, layout), + CXType_Pointer => conv_ptr_ty(ctx, &ty.pointee_type(), cursor, false, layout), + CXType_LValueReference => conv_ptr_ty(ctx, &ty.pointee_type(), cursor, true, layout), CXType_VariableArray | CXType_DependentSizedArray | CXType_IncompleteArray => { - conv_ptr_ty(ctx, &ty.elem_type(), cursor, layout) + conv_ptr_ty(ctx, &ty.elem_type(), cursor, false, layout) } CXType_FunctionProto => TFuncProto(mk_fn_sig(ctx, ty, cursor)), CXType_Record | CXType_Typedef | CXType_Unexposed | - CXType_Enum => conv_decl_ty(ctx, &ty.declaration()), + CXType_Enum => conv_decl_ty(ctx, ty, cursor), CXType_ConstantArray => TArray(Box::new(conv_ty(ctx, &ty.elem_type(), cursor)), ty.array_size(), layout), _ => { let fail = ctx.options.fail_on_unknown_type; log_err_warn(ctx, &format!("unsupported type `{}` ({})", type_to_str(ty.kind()), cursor.location() - )[..], + ), fail ); TVoid @@ -312,17 +407,64 @@ fn opaque_ty(ctx: &mut ClangParserCtx, ty: &cx::Type) { } } +struct Annotations { + opaque: bool, + hide: bool, +} + +impl Annotations { + fn new(cursor: &Cursor) -> Annotations { + let mut anno = Annotations { + opaque: false, + hide: false, + }; + + anno.parse(&cursor.comment()); + anno + } + + fn parse(&mut self, comment: &Comment) { + if comment.kind() == CXComment_HTMLStartTag && + comment.get_tag_name() == "div" && + comment.get_num_tag_attrs() > 1 && + comment.get_tag_attr_name(0) == "rustbindgen" { + for i in 0..comment.get_num_tag_attrs() { + let name = comment.get_tag_attr_name(i); + + match name.as_str() { + "opaque" => self.opaque = true, + "hide" => self.hide = true, + _ => (), + } + } + } + + for i in 0..comment.num_children() { + self.parse(&comment.get_child(i)); + } + } +} + /// Recursively visits a cursor that represents a composite (struct or union) /// type and fills members with CompMember instances representing the fields and /// nested composites that make up the visited composite. fn visit_composite(cursor: &Cursor, parent: &Cursor, ctx: &mut ClangParserCtx, +<<<<<<< 6f1904e52612db3a2517727c053e7cbc84601b2a compinfo: &mut CompInfo) -> Enum_CXVisitorResult { +======= + ci: &mut CompInfo) -> Enum_CXVisitorResult { + +>>>>>>> SM hacks squash fn is_bitfield_continuation(field: &il::FieldInfo, ty: &il::Type, width: u32) -> bool { match (&field.bitfields, ty) { (&Some(ref bitfields), &il::TInt(_, layout)) if *ty == field.ty => { bitfields.iter().map(|&(_, w)| w).fold(0u32, |acc, w| acc + w) + width <= (layout.size * 8) as u32 }, + (&Some(ref bitfields), &il::TNamed(ref info)) if *ty == field.ty => { + let info = info.borrow(); + bitfields.iter().map(|&(_, w)| w).fold(0u32, |acc, w| acc + w) + width <= (info.layout.size * 8) as u32 + }, _ => false } } @@ -342,9 +484,14 @@ fn visit_composite(cursor: &Cursor, parent: &Cursor, match cursor.kind() { CXCursor_FieldDecl => { + let anno = Annotations::new(cursor); + if anno.hide { + return CXChildVisit_Continue; + } let ty = conv_ty(ctx, &cursor.cur_type(), cursor); + let comment = cursor.raw_comment(); - let (name, bitfields) = match (cursor.bit_width(), members.last_mut()) { + let (name, bitfields) = match (cursor.bit_width(), ci.members.last_mut()) { // The field is a continuation of an exising bitfield (Some(width), Some(&mut il::CompMember::Field(ref mut field))) if is_bitfield_continuation(field, &ty, width) => { @@ -363,7 +510,7 @@ fn visit_composite(cursor: &Cursor, parent: &Cursor, _ => { let msg = format!("Enums in bitfields are not supported ({}.{}).", cursor.spelling(), parent.spelling()); - ctx.logger.warn(&msg[..]); + ctx.logger.warn(&msg); } } ("".to_owned(), Some(vec!((cursor.spelling(), width)))) @@ -400,22 +547,37 @@ fn visit_composite(cursor: &Cursor, parent: &Cursor, // }; // +<<<<<<< 6f1904e52612db3a2517727c053e7cbc84601b2a let is_composite = match (inner_composite(&ty), members.last()) { +======= + fn inner_composite(mut ty: &il::Type) -> Option<&Rc<RefCell<CompInfo>>> { + loop { + match ty { + &TComp(ref comp_ty) => return Some(comp_ty), + &TPtr(ref ptr_ty, _, _, _) => ty = &**ptr_ty, + &TArray(ref array_ty, _, _) => ty = &**array_ty, + _ => return None + } + } + } + + let is_composite = match (inner_composite(&ty), ci.members.last()) { +>>>>>>> SM hacks squash (Some(ty_compinfo), Some(&CompMember::Comp(ref c))) => { c.borrow().deref() as *const _ == ty_compinfo.borrow().deref() as *const _ }, _ => false }; - let field = FieldInfo::new(name, ty.clone(), bitfields); + let field = FieldInfo::new(name, ty.clone(), comment, bitfields); if is_composite { - if let Some(CompMember::Comp(c)) = members.pop() { - members.push(CompMember::CompField(c, field)); + if let Some(CompMember::Comp(c)) = ci.members.pop() { + ci.members.push(CompMember::CompField(c, field)); } else { unreachable!(); // Checks in is_composite make this unreachable. } } else { - members.push(CompMember::Field(field)); + ci.members.push(CompMember::Field(field)); } } CXCursor_StructDecl | CXCursor_UnionDecl => { @@ -424,16 +586,150 @@ fn visit_composite(cursor: &Cursor, parent: &Cursor, // cannot be used elsewhere and so does not need to be added // to globals otherwise it will be declared later and a global. let decl = decl_name(ctx_, cursor); - let ci = decl.compinfo(); + let ci2 = decl.compinfo(); cursor.visit(|c, p| { - let mut ci_ = ci.borrow_mut(); + let mut ci_ = ci2.borrow_mut(); visit_composite(c, p, ctx_, &mut ci_) }); - members.push(CompMember::Comp(decl.compinfo())); + ci.members.push(CompMember::Comp(decl.compinfo())); }); } CXCursor_PackedAttr => { - compinfo.layout.packed = true; + ci.layout.packed = true; + } + CXCursor_TemplateTypeParameter => { + let ty = conv_ty(ctx, &cursor.cur_type(), cursor); + let layout = Layout::new(ty.size(), ty.align()); + ci.args.push(TNamed(Rc::new(RefCell::new(TypeInfo::new(cursor.spelling(), ctx.current_module_id, TVoid, layout))))); + } + CXCursor_EnumDecl => { + let anno = Annotations::new(cursor); + fwd_decl(ctx, cursor, |ctx_| { + let decl = decl_name(ctx_, cursor); + let ei = decl.enuminfo(); + ei.borrow_mut().comment = cursor.raw_comment(); + cursor.visit(|c, _: &Cursor| { + let mut ei_ = ei.borrow_mut(); + visit_enum(c, &mut ei_.items) + }); + if anno.opaque { + ei.borrow_mut().items = vec!(); + } + ci.members.push(CompMember::Enum(ei)); + }); + } + CXCursor_CXXBaseSpecifier => { + let ty = conv_ty(ctx, &cursor.cur_type(), cursor); + let fieldname = if ci.members.len() > 0 { + format!("_base{}", ci.members.len()) + } else { + "_base".to_string() + }; + let found_virtual_base = if ci.members.is_empty() { + false + } else if let CompMember::Field(ref fi) = ci.members[0] { + if let TComp(ref ci2) = fi.ty { + ci2.borrow().has_vtable + } else { + false + } + } else { + false + }; + let field = FieldInfo::new(fieldname, ty.clone(), "".to_owned(), None); + if !found_virtual_base && cursor.is_virtual_base() { + ci.members.insert(0, CompMember::Field(field)); + ci.has_vtable = true; + } else { + ci.members.push(CompMember::Field(field)); + } + ci.base_members += 1; + } + CXCursor_CXXMethod => { + if ctx.options.ignore_functions { + return CXChildVisit_Continue; + } + + let linkage = cursor.linkage(); + if linkage != CXLinkage_External { + return CXChildVisit_Continue; + } + + let visibility = cursor.visibility(); + if visibility != CXVisibility_Default { + return CXChildVisit_Continue; + } + + if ci.args.len() > 0 { + return CXChildVisit_Continue; + } + + if cursor.access_specifier() == CX_CXXPrivate { + return CXChildVisit_Continue; + } + + let spelling = cursor.spelling(); + if spelling.len() > 8 && + &(spelling)[..8] == "operator" { + return CXChildVisit_Continue; + } + + fn is_override(ci: &CompInfo, sig: &Type, name: &str) -> bool { + for vm in ci.vmethods.iter() { + if vm.name == name && &vm.ty == sig { + return true; + } + } + for base in ci.members[..ci.base_members].iter() { + let base = match base { + &CompMember::Field(ref fi) => { + match fi.ty { + TComp(ref ci) => ci.clone(), + _ => continue, + } + }, + _ => unreachable!() + }; + if is_override(&*base.borrow(), sig, name) { + return true; + } + } + return false; + } + + if cursor.method_is_virtual() { + ci.has_vtable = true; + } + + let mut sig = mk_fn_sig(ctx, &cursor.cur_type(), cursor); + if !cursor.method_is_static() { + // XXX what have i done + if cursor.method_is_virtual() { + sig.args.insert(0, ("this".to_string(),TPtr(Box::new(TVoid), cursor.cur_type().is_const(), false, Layout::zero()))); + } else { + sig.args.insert(0, ("this".to_string(), + TPtr(Box::new(TNamed(Rc::new(RefCell::new(TypeInfo::new(ci.name.clone(), ctx.current_module_id, TVoid, Layout::zero()))))), cursor.cur_type().is_const(), false, Layout::zero()))); + } + } + + // XXX with final classes we can optimize a bit + let sig = TFuncPtr(sig); + if is_override(ci, &sig, &spelling) { + return CXChildVisit_Continue; + } + + let mut vi = VarInfo::new(spelling, cursor.mangling(), cursor.raw_comment(), sig); + vi.is_static = cursor.method_is_static(); + vi.is_const = cursor.cur_type().is_const(); + + if cursor.method_is_virtual() { + ci.vmethods.push(vi); + } else { + ci.methods.push(vi); + } + } + CXCursor_Destructor => { + ci.has_destructor = true; } _ => { // XXX: Some kind of warning would be nice, but this produces far @@ -453,45 +749,48 @@ fn visit_enum(cursor: &Cursor, items: &mut Vec<EnumItem>) -> Enum_CXVisitorResult { if cursor.kind() == CXCursor_EnumConstantDecl { let name = cursor.spelling(); + let comment = cursor.raw_comment(); let val = cursor.enum_val(); - let item = EnumItem::new(name, val); + let item = EnumItem::new(name, comment, val); items.push(item); } CXChildVisit_Continue } -fn visit_literal(cursor: &Cursor, unit: &TranslationUnit) -> Option<i64> { - if cursor.kind() == CXCursor_IntegerLiteral { - match unit.tokens(cursor) { - None => None, - Some(tokens) => { - if tokens.is_empty() || tokens[0].kind != CXToken_Literal { - None - } else { - let s = &tokens[0].spelling; - let parsed = { - //TODO: try to preserve hex literals? - if s.starts_with("0x") { - i64::from_str_radix(&s[2..], 16) - } else { - s.parse() - } - }; - match parsed { - Ok(i) => Some(i), - Err(_) => None, +fn parse_int_literal_tokens(cursor: &Cursor, unit: &TranslationUnit, which: usize) -> Option<i64> { + match unit.tokens(cursor) { + None => None, + Some(tokens) => { + if tokens.len() <= which || tokens[which].kind != CXToken_Literal { + None + } else { + let ref s = tokens[which].spelling; + let parsed = { + //TODO: try to preserve hex literals? + if s.starts_with("0x") { + i64::from_str_radix(&s[2..], 16) + } else { + s.parse() } + }; + match parsed { + Ok(i) => Some(i), + Err(_) => None, } } } } - else { - None +} + +fn visit_literal(cursor: &Cursor, unit: &TranslationUnit) -> Option<i64> { + if cursor.kind() == CXCursor_IntegerLiteral { + return parse_int_literal_tokens(cursor, unit, 0); } + return None; } fn visit_top(cursor: &Cursor, - ctx: &mut ClangParserCtx, + mut ctx: &mut ClangParserCtx, unit: &TranslationUnit) -> Enum_CXVisitorResult { if !match_pattern(ctx, cursor) { return CXChildVisit_Continue; @@ -499,9 +798,10 @@ fn visit_top(cursor: &Cursor, match cursor.kind() { CXCursor_UnexposedDecl => { - CXChildVisit_Recurse + return CXChildVisit_Recurse; } - CXCursor_StructDecl | CXCursor_UnionDecl => { + CXCursor_StructDecl | CXCursor_UnionDecl | CXCursor_ClassDecl | CXCursor_ClassTemplate => { + let anno = Annotations::new(cursor); fwd_decl(ctx, cursor, |ctx_| { let decl = decl_name(ctx_, cursor); let ci = decl.compinfo(); @@ -509,7 +809,13 @@ fn visit_top(cursor: &Cursor, let mut ci_ = ci.borrow_mut(); visit_composite(c, p, ctx_, &mut ci_) }); - ctx_.globals.push(GComp(ci)); + if anno.opaque { + ci.borrow_mut().members = vec!(); + } + if anno.hide { + ci.borrow_mut().hide = true; + } + ctx_.current_module_mut().globals.push(GComp(ci)); }); CXChildVisit_Continue } @@ -517,11 +823,12 @@ fn visit_top(cursor: &Cursor, fwd_decl(ctx, cursor, |ctx_| { let decl = decl_name(ctx_, cursor); let ei = decl.enuminfo(); + ei.borrow_mut().comment = cursor.raw_comment(); cursor.visit(|c, _: &Cursor| { let mut ei_ = ei.borrow_mut(); visit_enum(c, &mut ei_.items) }); - ctx_.globals.push(GEnum(ei)); + ctx_.current_module_mut().globals.push(GEnum(ei)); }); CXChildVisit_Continue } @@ -531,12 +838,23 @@ fn visit_top(cursor: &Cursor, return CXChildVisit_Continue; } + let visibility = cursor.visibility(); + if visibility != CXVisibility_Default { + return CXChildVisit_Continue; + } + + let spelling = cursor.spelling(); + if spelling.len() > 8 && + &(spelling)[..8] == "operator" { + return CXChildVisit_Continue; + } + let func = decl_name(ctx, cursor); let vi = func.varinfo(); let mut vi = vi.borrow_mut(); vi.ty = TFuncPtr(mk_fn_sig(ctx, &cursor.cur_type(), cursor)); - ctx.globals.push(func); + ctx.current_module_mut().globals.push(func); CXChildVisit_Continue } @@ -546,6 +864,10 @@ fn visit_top(cursor: &Cursor, return CXChildVisit_Continue; } + let visibility = cursor.visibility(); + if visibility != CXVisibility_Default { + return CXChildVisit_Continue; + } let ty = conv_ty(ctx, &cursor.cur_type(), cursor); let var = decl_name(ctx, cursor); let vi = var.varinfo(); @@ -556,7 +878,7 @@ fn visit_top(cursor: &Cursor, vi.val = visit_literal(c, unit); CXChildVisit_Continue }); - ctx.globals.push(var); + ctx.current_module_mut().globals.push(var); CXChildVisit_Continue } @@ -566,12 +888,17 @@ fn visit_top(cursor: &Cursor, under_ty = under_ty.canonical_type(); } + if cursor.spelling() == + cursor.typedef_type().declaration().spelling() { + return CXChildVisit_Continue; + } let ty = conv_ty(ctx, &under_ty, cursor); let typedef = decl_name(ctx, cursor); let ti = typedef.typeinfo(); let mut ti = ti.borrow_mut(); ti.ty = ty.clone(); - ctx.globals.push(typedef); + ti.comment = cursor.raw_comment(); + ctx.current_module_mut().globals.push(typedef); opaque_ty(ctx, &under_ty); @@ -580,36 +907,107 @@ fn visit_top(cursor: &Cursor, CXCursor_FieldDecl => { CXChildVisit_Continue } - _ => CXChildVisit_Continue, + CXCursor_Namespace => { + if !ctx.options.enable_cxx_namespaces { + return CXChildVisit_Recurse; + } + + let namespace_name = match unit.tokens(cursor) { + None => None, + Some(tokens) => { + if tokens.len() <= 1 { + None + } else { + match &*tokens[1].spelling { + "{" => None, + s => Some(s.to_owned()), + } + } + } + }.unwrap_or_else(|| { + ctx.anonymous_modules_found += 1; + format!("__anonymous{}", ctx.anonymous_modules_found) + }); + + // Find an existing namespace children of the current one + let mod_id = ctx.current_module() + .children_ids.iter() + .find(|id| ctx.module_map.get(id).unwrap().name == namespace_name) + .map(|id| *id); + + let mod_id = match mod_id { + Some(id) => id, + None => { + let parent_id = ctx.current_module_id; + let id = ModuleId::next(); + ctx.module_map.get_mut(&parent_id).unwrap().children_ids.push(id); + ctx.module_map.insert(id, Module::new(namespace_name, Some(parent_id))); + id + } + }; + + let previous_id = ctx.current_module_id; + + ctx.current_module_id = mod_id; + cursor.visit(|cur, _: &Cursor| visit_top(cur, &mut ctx, &unit)); + ctx.current_module_id = previous_id; + + return CXChildVisit_Continue; + } + CXCursor_MacroDefinition => { + let val = parse_int_literal_tokens(cursor, unit, 1); + if val.is_none() { + // Not an integer literal. + return CXChildVisit_Continue; + } + let var = decl_name(ctx, cursor); + let vi = var.varinfo(); + let mut vi = vi.borrow_mut(); + vi.ty = match val { + None => TVoid, + Some(v) if v.abs() > u32::max_value() as i64 => TInt(IULongLong, Layout::new(8, 8)), + _ => TInt(IUInt, Layout::new(4, 4)), + }; + vi.is_const = true; + vi.val = val; + ctx.current_module_mut().globals.push(var); + + return CXChildVisit_Continue; + } + _ => return CXChildVisit_Continue, } } fn log_err_warn(ctx: &mut ClangParserCtx, msg: &str, is_err: bool) { if is_err { ctx.err_count += 1; - ctx.logger.error(msg) + ctx.logger.error(msg); } else { - ctx.logger.warn(msg) + ctx.logger.warn(msg); } } -pub fn parse(options: ClangParserOptions, logger: &Logger) -> Result<Vec<Global>, ()> { +pub fn parse(options: ClangParserOptions, logger: &Logger) -> Result<ModuleMap, ()> { let mut ctx = ClangParserCtx { options: options, name: HashMap::new(), builtin_defs: vec!(), - globals: vec!(), + module_map: ModuleMap::new(), + current_module_id: ROOT_MODULE_ID, logger: logger, - err_count: 0 + err_count: 0, + anonymous_modules_found: 0, }; + ctx.module_map.insert(ROOT_MODULE_ID, Module::new("root".to_owned(), None)); + let ix = cx::Index::create(false, true); if ix.is_null() { ctx.logger.error("Clang failed to create index"); return Err(()) } - let unit = TranslationUnit::parse(&ix, "", &ctx.options.clang_args[..], &[], 0); + let unit = TranslationUnit::parse(&ix, "", &ctx.options.clang_args[..], &[], CXTranslationUnit_DetailedPreprocessingRecord); if unit.is_null() { ctx.logger.error("No input files given"); return Err(()) @@ -619,7 +1017,7 @@ pub fn parse(options: ClangParserOptions, logger: &Logger) -> Result<Vec<Global> for d in &diags { let msg = d.format(Diagnostic::default_opts()); let is_err = d.severity() >= CXDiagnostic_Error; - log_err_warn(&mut ctx, &msg[..], is_err); + log_err_warn(&mut ctx, &msg, is_err); } if ctx.err_count > 0 { @@ -646,5 +1044,5 @@ pub fn parse(options: ClangParserOptions, logger: &Logger) -> Result<Vec<Global> return Err(()) } - Ok(ctx.globals) + Ok(ctx.module_map) } diff --git a/src/types.rs b/src/types.rs index d36b3835..7846c7c9 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,6 +1,8 @@ use std::cell::RefCell; use std::fmt; use std::rc::Rc; +use std::collections::HashMap; +use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering}; use syntax::abi; @@ -9,6 +11,45 @@ pub use self::Type::*; pub use self::IKind::*; pub use self::FKind::*; +static NEXT_MODULE_ID: AtomicUsize = ATOMIC_USIZE_INIT; + +#[derive(Clone, Copy, Eq, PartialEq, Hash, Debug)] +pub struct ModuleId(usize); +pub static ROOT_MODULE_ID: ModuleId = ModuleId(0); + +impl ModuleId { + pub fn next() -> ModuleId { + ModuleId(NEXT_MODULE_ID.fetch_add(1, Ordering::SeqCst) + 1) + } +} + +pub type ModuleMap = HashMap<ModuleId, Module>; + +#[derive(Clone)] +pub struct Module { + pub name: String, + pub globals: Vec<Global>, + pub parent_id: Option<ModuleId>, + // Just for convenience + pub children_ids: Vec<ModuleId>, +} + +impl Module { + pub fn new(name: String, parent_id: Option<ModuleId>) -> Self { + Module { + name: name, + globals: vec![], + parent_id: parent_id, + children_ids: vec![], + } + } + + #[allow(dead_code)] + pub fn add_global(&mut self, g: Global) { + self.globals.push(g) + } +} + #[derive(Clone)] pub enum Global { GType(Rc<RefCell<TypeInfo>>), @@ -83,7 +124,7 @@ pub enum Type { TVoid, TInt(IKind, Layout), TFloat(FKind, Layout), - TPtr(Box<Type>, bool, Layout), + TPtr(Box<Type>, bool, bool, Layout), TArray(Box<Type>, usize, Layout), TFuncProto(FuncSig), TFuncPtr(FuncSig), @@ -95,26 +136,26 @@ pub enum Type { impl Type { pub fn size(&self) -> usize { match *self { - TInt(_, l) - | TFloat(_, l) - | TPtr(_, _, l) - | TArray(_, _, l) => l.size, + TInt(_, l) => l.size, + TFloat(_, l) => l.size, + TPtr(_, _, _, l) => l.size, + TArray(_, _, l) => l.size, TNamed(ref ti) => ti.borrow().ty.size(), TComp(ref ci) => ci.borrow().layout.size, TEnum(ref ei) => ei.borrow().layout.size, - TVoid - | TFuncProto(..) - | TFuncPtr(..) => 0, + TVoid => 0, + TFuncProto(..) => 0, + TFuncPtr(..) => 0, } } #[allow(dead_code)] pub fn align(&self) -> usize { match *self { - TInt(_, l) - | TFloat(_, l) - | TPtr(_, _, l) - | TArray(_, _, l) => l.align, + TInt(_, l) => l.align, + TFloat(_, l) => l.align, + TPtr(_, _, _, l) => l.align, + TArray(_, _, l) => l.align, TNamed(ref ti) => ti.borrow().ty.align(), TComp(ref ci) => ci.borrow().layout.align, TEnum(ref ei) => ei.borrow().layout.align, @@ -124,6 +165,7 @@ impl Type { } } + #[allow(dead_code)] pub fn can_derive_debug(&self) -> bool { match *self { TArray(_, size, _) => size <= 32, @@ -175,6 +217,7 @@ pub enum IKind { } impl IKind { + #[allow(dead_code)] pub fn is_signed(self) -> bool { match self { IBool => false, @@ -203,6 +246,7 @@ pub enum CompMember { Field(FieldInfo), Comp(Rc<RefCell<CompInfo>>), CompField(Rc<RefCell<CompInfo>>, FieldInfo), + Enum(Rc<RefCell<EnumInfo>>), } #[derive(Copy, Clone, PartialEq)] @@ -215,16 +259,49 @@ pub enum CompKind { pub struct CompInfo { pub kind: CompKind, pub name: String, + pub module_id: ModuleId, + pub filename: String, + pub comment: String, pub members: Vec<CompMember>, + pub args: Vec<Type>, + pub methods: Vec<VarInfo>, + pub vmethods: Vec<VarInfo>, + pub ref_template: Option<Type>, + pub has_vtable: bool, + pub has_destructor: bool, + pub hide: bool, + pub base_members: usize, pub layout: Layout, } +static mut UNNAMED_COUNTER: u32 = 0; + +fn unnamed_name(name: String, filename: &String) -> String { + return if name.is_empty() { + let n = unsafe { UNNAMED_COUNTER += 1; UNNAMED_COUNTER }; + format!("{}_unnamed_{}", filename, n) + } else { + name + }; +} + impl CompInfo { - pub fn new(name: String, kind: CompKind, members: Vec<CompMember>, layout: Layout) -> CompInfo { + pub fn new(name: String, module_id: ModuleId, filename: String, comment: String, kind: CompKind, members: Vec<CompMember>, layout: Layout) -> CompInfo { CompInfo { kind: kind, - name: name, + module_id: module_id, + name: unnamed_name(name, &filename), + filename: filename, + comment: comment, members: members, + args: vec!(), + methods: vec!(), + vmethods: vec!(), + ref_template: None, + has_vtable: false, + has_destructor: false, + hide: false, + base_members: 0, layout: layout, } } @@ -240,14 +317,16 @@ impl fmt::Debug for CompInfo { pub struct FieldInfo { pub name: String, pub ty: Type, + pub comment: String, pub bitfields: Option<Vec<(String, u32)>>, } impl FieldInfo { - pub fn new(name: String, ty: Type, bitfields: Option<Vec<(String, u32)>>) -> FieldInfo { + pub fn new(name: String, ty: Type, comment: String, bitfields: Option<Vec<(String, u32)>>) -> FieldInfo { FieldInfo { name: name, ty: ty, + comment: comment, bitfields: bitfields, } } @@ -256,15 +335,21 @@ impl FieldInfo { #[derive(Clone, PartialEq)] pub struct EnumInfo { pub name: String, + pub module_id: ModuleId, + pub comment: String, + pub filename: String, pub items: Vec<EnumItem>, pub kind: IKind, pub layout: Layout, } impl EnumInfo { - pub fn new(name: String, kind: IKind, items: Vec<EnumItem>, layout: Layout) -> EnumInfo { + pub fn new(name: String, module_id: ModuleId, filename: String, kind: IKind, items: Vec<EnumItem>, layout: Layout) -> EnumInfo { EnumInfo { - name: name, + name: unnamed_name(name, &filename), + module_id: module_id, + comment: String::new(), + filename: filename, items: items, kind: kind, layout: layout, @@ -281,13 +366,15 @@ impl fmt::Debug for EnumInfo { #[derive(Clone, PartialEq)] pub struct EnumItem { pub name: String, + pub comment: String, pub val: i64 } impl EnumItem { - pub fn new(name: String, val: i64) -> EnumItem { + pub fn new(name: String, comment: String, val: i64) -> EnumItem { EnumItem { name: name, + comment: comment, val: val } } @@ -296,14 +383,20 @@ impl EnumItem { #[derive(Clone, PartialEq)] pub struct TypeInfo { pub name: String, - pub ty: Type + pub module_id: ModuleId, + pub comment: String, + pub ty: Type, + pub layout: Layout, } impl TypeInfo { - pub fn new(name: String, ty: Type) -> TypeInfo { + pub fn new(name: String, module_id: ModuleId, ty: Type, layout: Layout) -> TypeInfo { TypeInfo { name: name, - ty: ty + module_id: module_id, + comment: String::new(), + ty: ty, + layout: layout, } } } @@ -314,22 +407,33 @@ impl fmt::Debug for TypeInfo { } } -#[derive(Clone)] +#[derive(Clone, PartialEq)] pub struct VarInfo { pub name: String, + pub mangled: String, + pub comment: String, pub ty: Type, //TODO: support non-integer constants pub val: Option<i64>, - pub is_const: bool + pub is_const: bool, + pub is_static: bool, } impl VarInfo { - pub fn new(name: String, ty: Type) -> VarInfo { + pub fn new(name: String, mangled: String, comment: String, ty: Type) -> VarInfo { + let mangled = if name == mangled { + String::new() + } else { + mangled + }; VarInfo { name: name, + mangled: mangled, + comment: comment, ty: ty, val: None, - is_const: false + is_const: false, + is_static: false, } } } |