//! Intermediate representation for C/C++ functions and methods. use super::comp::MethodKind; use super::context::{BindgenContext, TypeId}; use super::dot::DotAttributes; use super::item::Item; use super::traversal::{EdgeKind, Trace, Tracer}; use super::ty::TypeKind; use clang; use clang_sys::{self, CXCallingConv}; use ir::derive::{CanTriviallyDeriveDebug, CanTriviallyDeriveHash, CanTriviallyDerivePartialEqOrPartialOrd, CanDerive}; use parse::{ClangItemParser, ClangSubItemParser, ParseError, ParseResult}; use quote; use std::io; const RUST_DERIVE_FUNPTR_LIMIT: usize = 12; /// What kind of a function are we looking at? #[derive(Debug, Copy, Clone, PartialEq)] pub enum FunctionKind { /// A plain, free function. Function, /// A method of some kind. Method(MethodKind), } impl FunctionKind { fn from_cursor(cursor: &clang::Cursor) -> Option { // FIXME(emilio): Deduplicate logic with `ir::comp`. Some(match cursor.kind() { clang_sys::CXCursor_FunctionDecl => FunctionKind::Function, clang_sys::CXCursor_Constructor => FunctionKind::Method( MethodKind::Constructor, ), clang_sys::CXCursor_Destructor => FunctionKind::Method( if cursor.method_is_virtual() { MethodKind::VirtualDestructor { pure_virtual: cursor.method_is_pure_virtual(), } } else { MethodKind::Destructor } ), clang_sys::CXCursor_CXXMethod => { if cursor.method_is_virtual() { FunctionKind::Method(MethodKind::Virtual { pure_virtual: cursor.method_is_pure_virtual(), }) } else if cursor.method_is_static() { FunctionKind::Method(MethodKind::Static) } else { FunctionKind::Method(MethodKind::Normal) } } _ => return None, }) } } /// The style of linkage #[derive(Debug, Clone, Copy)] pub enum Linkage { /// Externally visible and can be linked against External, /// Not exposed externally. 'static inline' functions will have this kind of linkage Internal } /// A function declaration, with a signature, arguments, and argument names. /// /// The argument names vector must be the same length as the ones in the /// signature. #[derive(Debug)] pub struct Function { /// The name of this function. name: String, /// The mangled name, that is, the symbol. mangled_name: Option, /// The id pointing to the current function signature. signature: TypeId, /// The doc comment on the function, if any. comment: Option, /// The kind of function this is. kind: FunctionKind, /// The linkage of the function. linkage: Linkage, } impl Function { /// Construct a new function. pub fn new( name: String, mangled_name: Option, signature: TypeId, comment: Option, kind: FunctionKind, linkage: Linkage ) -> Self { Function { name, mangled_name, signature, comment, kind, linkage, } } /// Get this function's name. pub fn name(&self) -> &str { &self.name } /// Get this function's name. pub fn mangled_name(&self) -> Option<&str> { self.mangled_name.as_ref().map(|n| &**n) } /// Get this function's signature type. pub fn signature(&self) -> TypeId { self.signature } /// Get this function's kind. pub fn kind(&self) -> FunctionKind { self.kind } /// Get this function's linkage. pub fn linkage(&self) -> Linkage { self.linkage } } impl DotAttributes for Function { fn dot_attributes( &self, _ctx: &BindgenContext, out: &mut W, ) -> io::Result<()> where W: io::Write, { if let Some(ref mangled) = self.mangled_name { let mangled: String = mangled.chars().flat_map(|c| c.escape_default()).collect(); writeln!( out, "mangled name{}", mangled )?; } Ok(()) } } /// An ABI extracted from a clang cursor. #[derive(Debug, Copy, Clone)] pub enum Abi { /// The default C ABI. C, /// The "stdcall" ABI. Stdcall, /// The "fastcall" ABI. Fastcall, /// The "thiscall" ABI. ThisCall, /// The "aapcs" ABI. Aapcs, /// The "win64" ABI. Win64, /// An unknown or invalid ABI. Unknown(CXCallingConv), } impl Abi { /// Returns whether this Abi is known or not. fn is_unknown(&self) -> bool { match *self { Abi::Unknown(..) => true, _ => false, } } } impl quote::ToTokens for Abi { fn to_tokens(&self, tokens: &mut quote::Tokens) { tokens.append_all(match *self { Abi::C => quote! { "C" }, Abi::Stdcall => quote! { "stdcall" }, Abi::Fastcall => quote! { "fastcall" }, Abi::ThisCall => quote! { "thiscall" }, Abi::Aapcs => quote! { "aapcs" }, Abi::Win64 => quote! { "win64" }, Abi::Unknown(cc) => panic!( "Cannot turn unknown calling convention to tokens: {:?}", cc ), }); } } /// A function signature. #[derive(Debug)] pub struct FunctionSig { /// The return type of the function. return_type: TypeId, /// The type of the arguments, optionally with the name of the argument when /// declared. argument_types: Vec<(Option, TypeId)>, /// Whether this function is variadic. is_variadic: bool, /// The ABI of this function. abi: Abi, } fn get_abi(cc: CXCallingConv) -> Abi { use clang_sys::*; match cc { CXCallingConv_Default => Abi::C, CXCallingConv_C => Abi::C, CXCallingConv_X86StdCall => Abi::Stdcall, CXCallingConv_X86FastCall => Abi::Fastcall, CXCallingConv_X86ThisCall => Abi::ThisCall, CXCallingConv_AAPCS => Abi::Aapcs, CXCallingConv_X86_64Win64 => Abi::Win64, other => Abi::Unknown(other), } } /// Get the mangled name for the cursor's referent. pub fn cursor_mangling( ctx: &BindgenContext, cursor: &clang::Cursor, ) -> Option { use clang_sys; if !ctx.options().enable_mangling { return None; } // We early return here because libclang may crash in some case // if we pass in a variable inside a partial specialized template. // See rust-lang-nursery/rust-bindgen#67, and rust-lang-nursery/rust-bindgen#462. if cursor.is_in_non_fully_specialized_template() { return None; } let is_destructor = cursor.kind() == clang_sys::CXCursor_Destructor; if let Ok(mut manglings) = cursor.cxx_manglings() { while let Some(m) = manglings.pop() { // Only generate the destructor group 1, see below. if is_destructor && !m.ends_with("D1Ev") { continue; } return Some(m); } } let mut mangling = cursor.mangling(); if mangling.is_empty() { return None; } if is_destructor { // With old (3.8-) libclang versions, and the Itanium ABI, clang returns // the "destructor group 0" symbol, which means that it'll try to free // memory, which definitely isn't what we want. // // Explicitly force the destructor group 1 symbol. // // See http://refspecs.linuxbase.org/cxxabi-1.83.html#mangling-special // for the reference, and http://stackoverflow.com/a/6614369/1091587 for // a more friendly explanation. // // We don't need to do this for constructors since clang seems to always // have returned the C1 constructor. // // FIXME(emilio): Can a legit symbol in other ABIs end with this string? // I don't think so, but if it can this would become a linker error // anyway, not an invalid free at runtime. // // TODO(emilio, #611): Use cpp_demangle if this becomes nastier with // time. if mangling.ends_with("D0Ev") { let new_len = mangling.len() - 4; mangling.truncate(new_len); mangling.push_str("D1Ev"); } } Some(mangling) } impl FunctionSig { /// Construct a new function signature. pub fn new( return_type: TypeId, arguments: Vec<(Option, TypeId)>, is_variadic: bool, abi: Abi, ) -> Self { FunctionSig { return_type: return_type, argument_types: arguments, is_variadic: is_variadic, abi: abi, } } /// Construct a new function signature from the given Clang type. pub fn from_ty( ty: &clang::Type, cursor: &clang::Cursor, ctx: &mut BindgenContext, ) -> Result { use clang_sys::*; debug!("FunctionSig::from_ty {:?} {:?}", ty, cursor); // Skip function templates if cursor.kind() == CXCursor_FunctionTemplate { return Err(ParseError::Continue); } // Don't parse operatorxx functions in C++ let spelling = cursor.spelling(); if spelling.starts_with("operator") { return Err(ParseError::Continue); } let cursor = if cursor.is_valid() { *cursor } else { ty.declaration() }; let mut args: Vec<_> = match cursor.kind() { CXCursor_FunctionDecl | CXCursor_Constructor | CXCursor_CXXMethod | CXCursor_ObjCInstanceMethodDecl | CXCursor_ObjCClassMethodDecl => { // For CXCursor_FunctionDecl, cursor.args() is the reliable way // to get parameter names and types. cursor .args() .unwrap() .iter() .map(|arg| { let arg_ty = arg.cur_type(); let name = arg.spelling(); let name = if name.is_empty() { None } else { Some(name) }; let ty = Item::from_ty_or_ref(arg_ty, *arg, None, ctx); (name, ty) }) .collect() } _ => { // For non-CXCursor_FunctionDecl, visiting the cursor's children // is the only reliable way to get parameter names. let mut args = vec![]; cursor.visit(|c| { if c.kind() == CXCursor_ParmDecl { let ty = Item::from_ty_or_ref(c.cur_type(), c, None, ctx); let name = c.spelling(); let name = if name.is_empty() { None } else { Some(name) }; args.push((name, ty)); } CXChildVisit_Continue }); args } }; let is_method = cursor.kind() == CXCursor_CXXMethod; let is_constructor = cursor.kind() == CXCursor_Constructor; let is_destructor = cursor.kind() == CXCursor_Destructor; if (is_constructor || is_destructor || is_method) && cursor.lexical_parent() != cursor.semantic_parent() { // Only parse constructors once. return Err(ParseError::Continue); } if is_method || is_constructor || is_destructor { let is_const = is_method && cursor.method_is_const(); let is_virtual = is_method && cursor.method_is_virtual(); let is_static = is_method && cursor.method_is_static(); if !is_static && !is_virtual { let parent = cursor.semantic_parent(); let class = Item::parse(parent, None, ctx) .expect("Expected to parse the class"); // The `class` most likely is not finished parsing yet, so use // the unchecked variant. let class = class.as_type_id_unchecked(); let class = if is_const { let const_class_id = ctx.next_item_id(); ctx.build_const_wrapper( const_class_id, class, None, &parent.cur_type(), ) } else { class }; let ptr = Item::builtin_type(TypeKind::Pointer(class), false, ctx); args.insert(0, (Some("this".into()), ptr)); } else if is_virtual { let void = Item::builtin_type(TypeKind::Void, false, ctx); let ptr = Item::builtin_type(TypeKind::Pointer(void), false, ctx); args.insert(0, (Some("this".into()), ptr)); } } let ty_ret_type = if cursor.kind() == CXCursor_ObjCInstanceMethodDecl || cursor.kind() == CXCursor_ObjCClassMethodDecl { ty.ret_type().or_else(|| cursor.ret_type()).ok_or( ParseError::Continue, )? } else { ty.ret_type().ok_or(ParseError::Continue)? }; let ret = Item::from_ty_or_ref(ty_ret_type, cursor, None, ctx); let call_conv = ty.call_conv(); let abi = get_abi(call_conv); if abi.is_unknown() { warn!("Unknown calling convention: {:?}", call_conv); } Ok(Self::new(ret.into(), args, ty.is_variadic(), abi)) } /// Get this function signature's return type. pub fn return_type(&self) -> TypeId { self.return_type } /// Get this function signature's argument (name, type) pairs. pub fn argument_types(&self) -> &[(Option, TypeId)] { &self.argument_types } /// Get this function signature's ABI. pub fn abi(&self) -> Abi { self.abi } /// Is this function signature variadic? pub fn is_variadic(&self) -> bool { // Clang reports some functions as variadic when they *might* be // variadic. We do the argument check because rust doesn't codegen well // variadic functions without an initial argument. self.is_variadic && !self.argument_types.is_empty() } /// Are function pointers with this signature able to derive Rust traits? /// Rust only supports deriving traits for function pointers with a limited /// number of parameters and a couple ABIs. /// /// For more details, see: /// /// * https://github.com/rust-lang-nursery/rust-bindgen/issues/547, /// * https://github.com/rust-lang/rust/issues/38848, /// * and https://github.com/rust-lang/rust/issues/40158 pub fn function_pointers_can_derive(&self) -> bool { if self.argument_types.len() > RUST_DERIVE_FUNPTR_LIMIT { return false; } match self.abi { Abi::C | Abi::Unknown(..) => true, _ => false, } } } impl ClangSubItemParser for Function { fn parse( cursor: clang::Cursor, context: &mut BindgenContext, ) -> Result, ParseError> { use clang_sys::*; let kind = match FunctionKind::from_cursor(&cursor) { None => return Err(ParseError::Continue), Some(k) => k, }; debug!("Function::parse({:?}, {:?})", cursor, cursor.cur_type()); let visibility = cursor.visibility(); if visibility != CXVisibility_Default { return Err(ParseError::Continue); } if cursor.access_specifier() == CX_CXXPrivate { return Err(ParseError::Continue); } if !context.options().generate_inline_functions && cursor.is_inlined_function() { return Err(ParseError::Continue); } let linkage = cursor.linkage(); let linkage = match linkage { CXLinkage_External | CXLinkage_UniqueExternal => Linkage::External, CXLinkage_Internal => Linkage::Internal, _ => return Err(ParseError::Continue) }; // Grab the signature using Item::from_ty. let sig = Item::from_ty(&cursor.cur_type(), cursor, None, context)?; let mut name = cursor.spelling(); assert!(!name.is_empty(), "Empty function name?"); if cursor.kind() == CXCursor_Destructor { // Remove the leading `~`. The alternative to this is special-casing // code-generation for destructor functions, which seems less than // ideal. if name.starts_with('~') { name.remove(0); } // Add a suffix to avoid colliding with constructors. This would be // technically fine (since we handle duplicated functions/methods), // but seems easy enough to handle it here. name.push_str("_destructor"); } let mut mangled_name = cursor_mangling(context, &cursor); if mangled_name.as_ref() == Some(&name) { mangled_name = None; } let comment = cursor.raw_comment(); let function = Self::new(name, mangled_name, sig, comment, kind, linkage); Ok(ParseResult::New(function, Some(cursor))) } } impl Trace for FunctionSig { type Extra = (); fn trace(&self, _: &BindgenContext, tracer: &mut T, _: &()) where T: Tracer, { tracer.visit_kind(self.return_type().into(), EdgeKind::FunctionReturn); for &(_, ty) in self.argument_types() { tracer.visit_kind(ty.into(), EdgeKind::FunctionParameter); } } } impl CanTriviallyDeriveDebug for FunctionSig { fn can_trivially_derive_debug(&self) -> bool { self.function_pointers_can_derive() } } impl CanTriviallyDeriveHash for FunctionSig { fn can_trivially_derive_hash(&self) -> bool { self.function_pointers_can_derive() } } impl CanTriviallyDerivePartialEqOrPartialOrd for FunctionSig { fn can_trivially_derive_partialeq_or_partialord(&self) -> CanDerive { if self.argument_types.len() > RUST_DERIVE_FUNPTR_LIMIT { return CanDerive::No; } match self.abi { Abi::C | Abi::Unknown(..) => CanDerive::Yes, _ => CanDerive::No, } } }