diff --git a/src/clang.rs b/src/clang.rs index 8741663d85..4a46b694e3 100644 --- a/src/clang.rs +++ b/src/clang.rs @@ -523,6 +523,14 @@ impl Cursor { pub fn evaluate(&self) -> Option { EvalResult::new(*self) } + + /// Return the result type for this cursor + pub fn ret_type(&self) -> Option { + let rt = Type { + x: unsafe { clang_getCursorResultType(self.x) }, + }; + if rt.is_valid() { Some(rt) } else { None } + } } extern "C" fn visit_children(cur: CXCursor, @@ -1363,6 +1371,9 @@ pub fn ast_dump(c: &Cursor, depth: isize) -> CXChildVisitResult { prefix, type_to_str(ty.kind()))); } + if let Some(ty) = c.ret_type() { + print_indent(depth, format!(" {}ret-type = {}", prefix, type_to_str(ty.kind()))); + } if let Some(refd) = c.referenced() { if refd != *c { diff --git a/src/codegen/mod.rs b/src/codegen/mod.rs index 9c71e99343..3b0882afcb 100644 --- a/src/codegen/mod.rs +++ b/src/codegen/mod.rs @@ -2153,61 +2153,8 @@ impl ToRustTy for FunctionSig { fn to_rust_ty(&self, ctx: &BindgenContext, _item: &Item) -> P { // TODO: we might want to consider ignoring the reference return value. - let return_item = ctx.resolve_item(self.return_type()); - let ret = - if let TypeKind::Void = *return_item.kind().expect_type().kind() { - ast::FunctionRetTy::Default(ctx.span()) - } else { - ast::FunctionRetTy::Ty(return_item.to_rust_ty(ctx)) - }; - - let mut unnamed_arguments = 0; - let arguments = self.argument_types().iter().map(|&(ref name, ty)| { - let arg_item = ctx.resolve_item(ty); - let arg_ty = arg_item.kind().expect_type(); - - // From the C90 standard[1]: - // - // A declaration of a parameter as "array of type" shall be - // adjusted to "qualified pointer to type", where the type - // qualifiers (if any) are those specified within the [ and ] of - // the array type derivation. - // - // [1]: http://c0x.coding-guidelines.com/6.7.5.3.html - let arg_ty = match *arg_ty.canonical_type(ctx).kind() { - TypeKind::Array(t, _) => { - t.to_rust_ty(ctx).to_ptr(arg_ty.is_const(), ctx.span()) - }, - TypeKind::Pointer(inner) => { - let inner = ctx.resolve_item(inner); - let inner_ty = inner.expect_type(); - if let TypeKind::ObjCInterface(_) = *inner_ty.canonical_type(ctx).kind() { - quote_ty!(ctx.ext_cx(), id) - } else { - arg_item.to_rust_ty(ctx) - } - }, - _ => { - arg_item.to_rust_ty(ctx) - } - }; - - let arg_name = match *name { - Some(ref name) => ctx.rust_mangle(name).into_owned(), - None => { - unnamed_arguments += 1; - format!("arg{}", unnamed_arguments) - } - }; - - assert!(!arg_name.is_empty()); - - ast::Arg { - ty: arg_ty, - pat: aster::AstBuilder::new().pat().id(arg_name), - id: ast::DUMMY_NODE_ID, - } - }).collect::>(); + let ret = utils::fnsig_return_ty(ctx, &self); + let arguments = utils::fnsig_arguments(ctx, &self); let decl = P(ast::FnDecl { inputs: arguments, @@ -2217,7 +2164,7 @@ impl ToRustTy for FunctionSig { let fnty = ast::TyKind::BareFn(P(ast::BareFnTy { unsafety: ast::Unsafety::Unsafe, - abi: self.abi(), + abi: self.abi().expect("Invalid abi for function!"), lifetimes: vec![], decl: decl, })); @@ -2297,7 +2244,8 @@ impl CodeGenerator for Function { vis: ast::Visibility::Public, }; - let item = ForeignModBuilder::new(signature.abi()) + let item = ForeignModBuilder::new(signature.abi() + .expect("Invalid abi for function!")) .with_foreign_item(foreign_item) .build(ctx); @@ -2316,9 +2264,36 @@ impl CodeGenerator for ObjCInterface { let mut trait_items = vec![]; for method in self.methods() { - let method_name = ctx.rust_ident(method.name()); + let signature = method.signature(); + let fn_args = utils::fnsig_arguments(ctx, signature); + let fn_ret = utils::fnsig_return_ty(ctx, signature); + let sig = aster::AstBuilder::new() + .method_sig() + .unsafe_() + .fn_decl() + .self_() + .build(ast::SelfKind::Value(ast::Mutability::Immutable)) + .with_args(fn_args.clone()) + .build(fn_ret); + + // Collect the actual used argument names + let arg_names: Vec<_> = fn_args.iter() + .map(|ref arg| { + match arg.pat.node { + ast::PatKind::Ident(_, ref spanning, _) => { + spanning.node.name.as_str().to_string() + } + _ => { + panic!("odd argument!"); + } + } + }) + .collect(); - let body = quote_stmt!(ctx.ext_cx(), msg_send![self, $method_name]) + let methods_and_args = + ctx.rust_ident(&method.format_method_call(&arg_names)); + let body = + quote_stmt!(ctx.ext_cx(), msg_send![self, $methods_and_args]) .unwrap(); let block = ast::Block { stmts: vec![body], @@ -2327,13 +2302,6 @@ impl CodeGenerator for ObjCInterface { span: ctx.span(), }; - let sig = aster::AstBuilder::new() - .method_sig() - .unsafe_() - .fn_decl() - .self_() - .build(ast::SelfKind::Value(ast::Mutability::Immutable)) - .build(ast::FunctionRetTy::Default(ctx.span())); let attrs = vec![]; let impl_item = ast::ImplItem { @@ -2697,4 +2665,69 @@ mod utils { _ => panic!("How did this happen exactly?"), } } + + pub fn fnsig_return_ty(ctx: &BindgenContext, + sig: &super::FunctionSig) + -> ast::FunctionRetTy { + let return_item = ctx.resolve_item(sig.return_type()); + if let TypeKind::Void = *return_item.kind().expect_type().kind() { + ast::FunctionRetTy::Default(ctx.span()) + } else { + ast::FunctionRetTy::Ty(return_item.to_rust_ty(ctx)) + } + } + + pub fn fnsig_arguments(ctx: &BindgenContext, + sig: &super::FunctionSig) + -> Vec { + use super::ToPtr; + let mut unnamed_arguments = 0; + sig.argument_types().iter().map(|&(ref name, ty)| { + let arg_item = ctx.resolve_item(ty); + let arg_ty = arg_item.kind().expect_type(); + + // From the C90 standard[1]: + // + // A declaration of a parameter as "array of type" shall be + // adjusted to "qualified pointer to type", where the type + // qualifiers (if any) are those specified within the [ and ] of + // the array type derivation. + // + // [1]: http://c0x.coding-guidelines.com/6.7.5.3.html + let arg_ty = match *arg_ty.canonical_type(ctx).kind() { + TypeKind::Array(t, _) => { + t.to_rust_ty(ctx).to_ptr(arg_ty.is_const(), ctx.span()) + }, + TypeKind::Pointer(inner) => { + let inner = ctx.resolve_item(inner); + let inner_ty = inner.expect_type(); + if let TypeKind::ObjCInterface(_) = *inner_ty.canonical_type(ctx).kind() { + quote_ty!(ctx.ext_cx(), id) + } else { + arg_item.to_rust_ty(ctx) + } + }, + _ => { + arg_item.to_rust_ty(ctx) + } + }; + + let arg_name = match *name { + Some(ref name) => ctx.rust_mangle(name).into_owned(), + None => { + unnamed_arguments += 1; + format!("arg{}", unnamed_arguments) + } + }; + + assert!(!arg_name.is_empty()); + + ast::Arg { + ty: arg_ty, + pat: aster::AstBuilder::new().pat().id(arg_name), + id: ast::DUMMY_NODE_ID, + } + }).collect::>() + } + } diff --git a/src/ir/function.rs b/src/ir/function.rs index cc31867939..9ef9c3a73c 100644 --- a/src/ir/function.rs +++ b/src/ir/function.rs @@ -73,18 +73,19 @@ pub struct FunctionSig { is_variadic: bool, /// The ABI of this function. - abi: abi::Abi, + abi: Option, } -fn get_abi(cc: CXCallingConv) -> abi::Abi { +fn get_abi(cc: CXCallingConv) -> Option { 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, + 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), + CXCallingConv_Invalid => None, other => panic!("unsupported calling convention: {:?}", other), } } @@ -116,7 +117,7 @@ impl FunctionSig { pub fn new(return_type: ItemId, arguments: Vec<(Option, ItemId)>, is_variadic: bool, - abi: abi::Abi) + abi: Option) -> Self { FunctionSig { return_type: return_type, @@ -154,7 +155,8 @@ impl FunctionSig { let mut args: Vec<_> = match cursor.kind() { CXCursor_FunctionDecl | CXCursor_Constructor | - CXCursor_CXXMethod => { + CXCursor_CXXMethod | + CXCursor_ObjCInstanceMethodDecl => { // For CXCursor_FunctionDecl, cursor.args() is the reliable way // to get parameter names and types. cursor.args() @@ -218,10 +220,19 @@ impl FunctionSig { } } - let ty_ret_type = try!(ty.ret_type().ok_or(ParseError::Continue)); + let ty_ret_type = if cursor.kind() == CXCursor_ObjCInstanceMethodDecl { + try!(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, None, None, ctx); let abi = get_abi(ty.call_conv()); + if abi.is_none() { + assert_eq!(cursor.kind(), CXCursor_ObjCInstanceMethodDecl, + "Invalid ABI for function signature") + } + Ok(Self::new(ret, args, ty.is_variadic(), abi)) } @@ -236,7 +247,7 @@ impl FunctionSig { } /// Get this function signature's ABI. - pub fn abi(&self) -> abi::Abi { + pub fn abi(&self) -> Option { self.abi } diff --git a/src/ir/objc.rs b/src/ir/objc.rs index 08cab1cce7..ea2cc0c887 100644 --- a/src/ir/objc.rs +++ b/src/ir/objc.rs @@ -1,11 +1,10 @@ //! Objective C types -// use clang_sys::CXCursor_ObjCSuperClassRef; - -use super::context::BindgenContext; use clang; use clang_sys::CXChildVisit_Continue; use clang_sys::CXCursor_ObjCInstanceMethodDecl; +use super::context::BindgenContext; +use super::function::FunctionSig; /// Objective C interface as used in TypeKind /// @@ -30,6 +29,8 @@ pub struct ObjCInstanceMethod { /// Method name as converted to rust /// like, dataWithBytes_length_ rust_name: String, + + signature: FunctionSig, } impl ObjCInterface { @@ -53,7 +54,7 @@ impl ObjCInterface { /// Parses the Objective C interface from the cursor pub fn from_ty(cursor: &clang::Cursor, - _ctx: &mut BindgenContext) + ctx: &mut BindgenContext) -> Option { let name = cursor.spelling(); let mut interface = Self::new(&name); @@ -62,7 +63,10 @@ impl ObjCInterface { match cursor.kind() { CXCursor_ObjCInstanceMethodDecl => { let name = cursor.spelling(); - let method = ObjCInstanceMethod::new(&name); + let signature = + FunctionSig::from_ty(&cursor.cur_type(), &cursor, ctx) + .expect("Invalid function sig"); + let method = ObjCInstanceMethod::new(&name, signature); interface.methods.push(method); } @@ -75,7 +79,7 @@ impl ObjCInterface { } impl ObjCInstanceMethod { - fn new(name: &str) -> ObjCInstanceMethod { + fn new(name: &str, signature: FunctionSig) -> ObjCInstanceMethod { let split_name: Vec<&str> = name.split(':').collect(); let rust_name = split_name.join("_"); @@ -83,6 +87,7 @@ impl ObjCInstanceMethod { ObjCInstanceMethod { name: name.to_owned(), rust_name: rust_name.to_owned(), + signature: signature, } } @@ -97,4 +102,33 @@ impl ObjCInstanceMethod { pub fn rust_name(&self) -> &str { self.rust_name.as_ref() } + + /// Returns the methods signature as FunctionSig + pub fn signature(&self) -> &FunctionSig { + &self.signature + } + + /// Formats the method call + pub fn format_method_call(&self, args: &[String]) -> String { + let split_name: Vec<&str> = + self.name.split(':').filter(|p| !p.is_empty()).collect(); + + // No arguments + if args.len() == 0 && split_name.len() == 1 { + return split_name[0].to_string(); + } + + // Check right amount of arguments + if args.len() != split_name.len() { + panic!("Incorrect method name or arguments for objc method, {:?} vs {:?}", + args, + split_name); + } + + split_name.iter() + .zip(args.iter()) + .map(|parts| format!("{}:{} ", parts.0, parts.1)) + .collect::>() + .join("") + } } diff --git a/tests/expectations/tests/objc_method.rs b/tests/expectations/tests/objc_method.rs index e2db24f69d..d0342a2129 100644 --- a/tests/expectations/tests/objc_method.rs +++ b/tests/expectations/tests/objc_method.rs @@ -11,7 +11,38 @@ extern crate objc; pub type id = *mut objc::runtime::Object; pub trait Foo { unsafe fn method(self); + unsafe fn methodWithInt_(self, foo: ::std::os::raw::c_int); + unsafe fn methodWithFoo_(self, foo: id); + unsafe fn methodReturningInt(self) + -> ::std::os::raw::c_int; + unsafe fn methodReturningFoo(self) + -> *mut id; + unsafe fn methodWithArg1_andArg2_andArg3_(self, + intvalue: ::std::os::raw::c_int, + ptr: + *mut ::std::os::raw::c_char, + floatvalue: f32); } impl Foo for id { unsafe fn method(self) { msg_send!(self , method) } + unsafe fn methodWithInt_(self, foo: ::std::os::raw::c_int) { + msg_send!(self , methodWithInt:foo ) + } + unsafe fn methodWithFoo_(self, foo: id) { + msg_send!(self , methodWithFoo:foo ) + } + unsafe fn methodReturningInt(self) -> ::std::os::raw::c_int { + msg_send!(self , methodReturningInt) + } + unsafe fn methodReturningFoo(self) -> *mut id { + msg_send!(self , methodReturningFoo) + } + unsafe fn methodWithArg1_andArg2_andArg3_(self, + intvalue: ::std::os::raw::c_int, + ptr: + *mut ::std::os::raw::c_char, + floatvalue: f32) { + msg_send!(self , + methodWithArg1:intvalue andArg2:ptr andArg3:floatvalue ) + } } diff --git a/tests/headers/objc_method.h b/tests/headers/objc_method.h index 91645f16d1..7a22e8e44e 100644 --- a/tests/headers/objc_method.h +++ b/tests/headers/objc_method.h @@ -3,5 +3,9 @@ @interface Foo - (void)method; -// TODO: argument methods +- (void)methodWithInt:(int)foo; +- (void)methodWithFoo:(Foo*)foo; +- (int)methodReturningInt; +- (Foo*)methodReturningFoo; +- (void)methodWithArg1:(int)intvalue andArg2:(char*)ptr andArg3:(float)floatvalue; @end