use super::context::{BindgenContext, ItemId};
use super::dot::DotAttributes;
use super::item::Item;
use super::traversal::{EdgeKind, Trace, Tracer};
use super::ty::TypeKind;
use clang;
use clang_sys::CXCallingConv;
use ir::derive::CanDeriveDebug;
use parse::{ClangItemParser, ClangSubItemParser, ParseError, ParseResult};
use std::io;
use syntax::abi;
#[derive(Debug)]
pub struct Function {
name: String,
mangled_name: Option<String>,
signature: ItemId,
comment: Option<String>,
}
impl Function {
pub fn new(name: String,
mangled_name: Option<String>,
sig: ItemId,
comment: Option<String>)
-> Self {
Function {
name: name,
mangled_name: mangled_name,
signature: sig,
comment: comment,
}
}
pub fn name(&self) -> &str {
&self.name
}
pub fn mangled_name(&self) -> Option<&str> {
self.mangled_name.as_ref().map(|n| &**n)
}
pub fn signature(&self) -> ItemId {
self.signature
}
}
impl DotAttributes for Function {
fn dot_attributes<W>(&self,
_ctx: &BindgenContext,
out: &mut W)
-> io::Result<()>
where W: io::Write,
{
if let Some(ref mangled) = self.mangled_name {
try!(writeln!(out,
"<tr><td>mangled name</td><td>{}</td></tr>",
mangled));
}
Ok(())
}
}
#[derive(Debug)]
pub struct FunctionSig {
return_type: ItemId,
argument_types: Vec<(Option<String>, ItemId)>,
is_variadic: bool,
abi: Option<abi::Abi>,
}
fn get_abi(cc: CXCallingConv) -> Option<abi::Abi> {
use clang_sys::*;
match cc {
CXCallingConv_Default => Some(abi::Abi::C),
CXCallingConv_C => Some(abi::Abi::C),
CXCallingConv_X86StdCall => Some(abi::Abi::Stdcall),
CXCallingConv_X86FastCall => Some(abi::Abi::Fastcall),
CXCallingConv_AAPCS => Some(abi::Abi::Aapcs),
CXCallingConv_X86_64Win64 => Some(abi::Abi::Win64),
_ => None,
}
}
fn macos_mangling(symbol: &mut String) {
if cfg!(target_os = "macos") && symbol.starts_with("_") {
symbol.remove(0);
}
}
pub fn cursor_mangling(ctx: &BindgenContext,
cursor: &clang::Cursor)
-> Option<String> {
use clang_sys;
if !ctx.options().enable_mangling {
return None;
}
if cursor.is_in_non_fully_specialized_template() {
return None;
}
if let Ok(mut manglings) = cursor.cxx_manglings() {
if let Some(mut m) = manglings.pop() {
macos_mangling(&mut m);
return Some(m);
}
}
let mut mangling = cursor.mangling();
if mangling.is_empty() {
return None;
}
macos_mangling(&mut mangling);
if cursor.kind() == clang_sys::CXCursor_Destructor {
if mangling.ends_with("D0Ev") {
let new_len = mangling.len() - 4;
mangling.truncate(new_len);
mangling.push_str("D1Ev");
}
}
Some(mangling)
}
impl FunctionSig {
pub fn new(return_type: ItemId,
arguments: Vec<(Option<String>, ItemId)>,
is_variadic: bool,
abi: Option<abi::Abi>)
-> Self {
FunctionSig {
return_type: return_type,
argument_types: arguments,
is_variadic: is_variadic,
abi: abi,
}
}
pub fn from_ty(ty: &clang::Type,
cursor: &clang::Cursor,
ctx: &mut BindgenContext)
-> Result<Self, ParseError> {
use clang_sys::*;
debug!("FunctionSig::from_ty {:?} {:?}", ty, cursor);
if cursor.kind() == CXCursor_FunctionTemplate {
return Err(ParseError::Continue);
}
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 => {
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()
}
_ => {
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() {
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 class = Item::parse(cursor.semantic_parent(), None, ctx)
.expect("Expected to parse the class");
let ptr =
Item::builtin_type(TypeKind::Pointer(class), is_const, ctx);
args.insert(0, (Some("this".into()), ptr));
} else if is_virtual {
let void = Item::builtin_type(TypeKind::Void, false, ctx);
let ptr =
Item::builtin_type(TypeKind::Pointer(void), false, ctx);
args.insert(0, (Some("this".into()), ptr));
}
}
let ty_ret_type = if cursor.kind() == CXCursor_ObjCInstanceMethodDecl ||
cursor.kind() == CXCursor_ObjCClassMethodDecl {
try!(ty.ret_type()
.or_else(|| cursor.ret_type())
.ok_or(ParseError::Continue))
} else {
try!(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_none() {
warn!("Unknown calling convention: {:?}", call_conv);
}
Ok(Self::new(ret, args, ty.is_variadic(), abi))
}
pub fn return_type(&self) -> ItemId {
self.return_type
}
pub fn argument_types(&self) -> &[(Option<String>, ItemId)] {
&self.argument_types
}
pub fn abi(&self) -> Option<abi::Abi> {
self.abi
}
pub fn is_variadic(&self) -> bool {
self.is_variadic && !self.argument_types.is_empty()
}
}
impl ClangSubItemParser for Function {
fn parse(cursor: clang::Cursor,
context: &mut BindgenContext)
-> Result<ParseResult<Self>, ParseError> {
use clang_sys::*;
match cursor.kind() {
CXCursor_FunctionDecl |
CXCursor_Constructor |
CXCursor_Destructor |
CXCursor_CXXMethod => {}
_ => return Err(ParseError::Continue),
};
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();
if linkage != CXLinkage_External &&
linkage != CXLinkage_UniqueExternal {
return Err(ParseError::Continue);
}
let sig =
try!(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 {
if name.starts_with('~') {
name.remove(0);
}
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);
Ok(ParseResult::New(function, Some(cursor)))
}
}
impl Trace for FunctionSig {
type Extra = ();
fn trace<T>(&self, _: &BindgenContext, tracer: &mut T, _: &())
where T: Tracer,
{
tracer.visit_kind(self.return_type(), EdgeKind::FunctionReturn);
for &(_, ty) in self.argument_types() {
tracer.visit_kind(ty, EdgeKind::FunctionParameter);
}
}
}
impl CanDeriveDebug for FunctionSig {
type Extra = ();
fn can_derive_debug(&self, _ctx: &BindgenContext, _: ()) -> bool {
const RUST_DERIVE_FUNPTR_LIMIT: usize = 12;
if self.argument_types.len() > RUST_DERIVE_FUNPTR_LIMIT {
return false;
}
match self.abi {
Some(abi::Abi::C) |
None => true,
_ => false,
}
}
}