summaryrefslogtreecommitdiff
path: root/src/ir/function.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/ir/function.rs')
-rw-r--r--src/ir/function.rs318
1 files changed, 318 insertions, 0 deletions
diff --git a/src/ir/function.rs b/src/ir/function.rs
new file mode 100644
index 00000000..50c442db
--- /dev/null
+++ b/src/ir/function.rs
@@ -0,0 +1,318 @@
+//! Intermediate representation for C/C++ functions and methods.
+
+use clang;
+use clang_sys::CXCallingConv;
+use parse::{ClangItemParser, ClangSubItemParser, ParseError, ParseResult};
+use super::context::{BindgenContext, ItemId};
+use super::item::Item;
+use super::ty::TypeKind;
+use super::type_collector::{ItemSet, TypeCollector};
+use syntax::abi;
+
+/// 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<String>,
+
+ /// The id pointing to the current function signature.
+ signature: ItemId,
+
+ /// The doc comment on the function, if any.
+ comment: Option<String>,
+}
+
+impl Function {
+ /// Construct a new 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,
+ }
+ }
+
+ /// 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.
+ pub fn signature(&self) -> ItemId {
+ self.signature
+ }
+}
+
+/// A function signature.
+#[derive(Debug)]
+pub struct FunctionSig {
+ /// The return type of the function.
+ return_type: ItemId,
+
+ /// The type of the arguments, optionally with the name of the argument when
+ /// declared.
+ argument_types: Vec<(Option<String>, ItemId)>,
+
+ /// Whether this function is variadic.
+ is_variadic: bool,
+
+ /// The ABI of this function.
+ abi: abi::Abi,
+}
+
+fn get_abi(cc: CXCallingConv) -> abi::Abi {
+ use clang_sys::*;
+ match cc {
+ CXCallingConv_Default => abi::Abi::C,
+ CXCallingConv_C => abi::Abi::C,
+ CXCallingConv_X86StdCall => abi::Abi::Stdcall,
+ CXCallingConv_X86FastCall => abi::Abi::Fastcall,
+ CXCallingConv_AAPCS => abi::Abi::Aapcs,
+ CXCallingConv_X86_64Win64 => abi::Abi::Win64,
+ other => panic!("unsupported calling convention: {:?}", other),
+ }
+}
+
+/// Get the mangled name for the cursor's referent.
+pub fn cursor_mangling(cursor: &clang::Cursor) -> Option<String> {
+ // We early return here because libclang may crash in some case
+ // if we pass in a variable inside a partial specialized template.
+ // See servo/rust-bindgen#67.
+ if cursor.is_in_non_fully_specialized_template() {
+ return None;
+ }
+
+ let mut mangling = cursor.mangling();
+ if mangling.is_empty() {
+ return None;
+ }
+
+ // Try to undo backend linkage munging (prepended _, generally)
+ if cfg!(target_os = "macos") {
+ mangling.remove(0);
+ }
+
+ Some(mangling)
+}
+
+impl FunctionSig {
+ /// Construct a new function signature.
+ pub fn new(return_type: ItemId,
+ arguments: Vec<(Option<String>, ItemId)>,
+ is_variadic: bool,
+ abi: abi::Abi)
+ -> Self {
+ FunctionSig {
+ return_type: return_type,
+ argument_types: arguments,
+ is_variadic: is_variadic,
+ abi: abi,
+ }
+ }
+
+ /// Construct a new function signature from the given Clang type.
+ 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);
+
+ // 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 => {
+ // 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(&arg_ty, Some(*arg), None, ctx)
+ .expect("Argument?");
+ (name, ty)
+ })
+ .collect()
+ }
+ _ => {
+ // For non-CXCursor_FunctionDecl, visiting the cursor's children
+ // is the only reliable way to get parameter names.
+ let mut args = vec![];
+ cursor.visit(|c| {
+ if c.kind() == CXCursor_ParmDecl {
+ let ty =
+ Item::from_ty(&c.cur_type(), Some(c), None, ctx)
+ .expect("ParmDecl?");
+ let name = c.spelling();
+ let name =
+ if name.is_empty() { None } else { Some(name) };
+ args.push((name, ty));
+ }
+ CXChildVisit_Continue
+ });
+ args
+ }
+ };
+
+ let is_method = cursor.kind() == CXCursor_CXXMethod;
+ let is_constructor = cursor.kind() == CXCursor_Constructor;
+ if (is_constructor || is_method) &&
+ cursor.lexical_parent() != cursor.semantic_parent() {
+ // Only parse constructors once.
+ return Err(ParseError::Continue);
+ }
+
+ if is_method || is_constructor {
+ 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 = try!(ty.ret_type().ok_or(ParseError::Continue));
+ let ret = try!(Item::from_ty(&ty_ret_type, None, None, ctx));
+ let abi = get_abi(ty.call_conv());
+
+ Ok(Self::new(ret, args, ty.is_variadic(), abi))
+ }
+
+ /// Get this function signature's return type.
+ pub fn return_type(&self) -> ItemId {
+ self.return_type
+ }
+
+ /// Get this function signature's argument (name, type) pairs.
+ pub fn argument_types(&self) -> &[(Option<String>, ItemId)] {
+ &self.argument_types
+ }
+
+ /// Get this function signature's ABI.
+ pub fn abi(&self) -> abi::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()
+ }
+}
+
+impl ClangSubItemParser for Function {
+ fn parse(cursor: clang::Cursor,
+ context: &mut BindgenContext)
+ -> Result<ParseResult<Self>, ParseError> {
+ use clang_sys::*;
+ match cursor.kind() {
+ // FIXME(emilio): Generate destructors properly.
+ CXCursor_FunctionDecl |
+ CXCursor_Constructor |
+ 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 cursor.is_inlined_function() {
+ return Err(ParseError::Continue);
+ }
+
+ let linkage = cursor.linkage();
+ if linkage != CXLinkage_External && linkage != CXLinkage_UniqueExternal {
+ return Err(ParseError::Continue);
+ }
+
+ // Grab the signature using Item::from_ty.
+ let sig = try!(Item::from_ty(&cursor.cur_type(),
+ Some(cursor),
+ None,
+ context));
+
+ let name = cursor.spelling();
+ assert!(!name.is_empty(), "Empty function name?");
+
+ let mut mangled_name = cursor_mangling(&cursor);
+ if mangled_name.as_ref() == Some(&name) {
+ mangled_name = None;
+ }
+
+ let comment = cursor.raw_comment();
+
+ let function = Self::new(name, mangled_name, sig, comment);
+ Ok(ParseResult::New(function, Some(cursor)))
+ }
+}
+
+impl TypeCollector for FunctionSig {
+ type Extra = Item;
+
+ fn collect_types(&self,
+ _context: &BindgenContext,
+ types: &mut ItemSet,
+ _item: &Item) {
+ types.insert(self.return_type());
+
+ for &(_, ty) in self.argument_types() {
+ types.insert(ty);
+ }
+ }
+}