diff --git a/src/codegen/mod.rs b/src/codegen/mod.rs index 7b7c31a8bd..15163a36f1 100644 --- a/src/codegen/mod.rs +++ b/src/codegen/mod.rs @@ -19,7 +19,7 @@ use ir::item::{Item, ItemAncestors, ItemCanonicalName, ItemCanonicalPath, use ir::item_kind::ItemKind; use ir::layout::Layout; use ir::module::Module; -use ir::objc::ObjCInterface; +use ir::objc::{ObjCInterface, ObjCMethod}; use ir::template::{AsNamed, TemplateInstantiation}; use ir::ty::{TemplateDeclaration, Type, TypeKind}; use ir::var::Var; @@ -2434,8 +2434,92 @@ impl CodeGenerator for Function { } } + +fn objc_method_codegen(ctx: &BindgenContext, + method: &ObjCMethod, + class_name: Option<&str>) + -> (ast::ImplItem, ast::TraitItem) { + let signature = method.signature(); + let fn_args = utils::fnsig_arguments(ctx, signature); + let fn_ret = utils::fnsig_return_ty(ctx, signature); + + let sig = if method.is_class_method() { + aster::AstBuilder::new() + .method_sig() + .unsafe_() + .fn_decl() + .with_args(fn_args.clone()) + .build(fn_ret) + } else { + 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 methods_and_args = + ctx.rust_ident(&method.format_method_call(&arg_names)); + + let body = if method.is_class_method() { + let class_name = + class_name.expect("Generating a class method without class name?") + .to_owned(); + let expect_msg = format!("Couldn't find {}", class_name); + quote_stmt!(ctx.ext_cx(), + msg_send![objc::runtime::Class::get($class_name).expect($expect_msg), $methods_and_args]) + .unwrap() + } else { + quote_stmt!(ctx.ext_cx(), msg_send![self, $methods_and_args]).unwrap() + }; + let block = ast::Block { + stmts: vec![body], + id: ast::DUMMY_NODE_ID, + rules: ast::BlockCheckMode::Default, + span: ctx.span(), + }; + + let attrs = vec![]; + + let impl_item = ast::ImplItem { + id: ast::DUMMY_NODE_ID, + ident: ctx.rust_ident(method.rust_name()), + vis: ast::Visibility::Inherited, // Public, + attrs: attrs.clone(), + node: ast::ImplItemKind::Method(sig.clone(), P(block)), + defaultness: ast::Defaultness::Final, + span: ctx.span(), + }; + + let trait_item = ast::TraitItem { + id: ast::DUMMY_NODE_ID, + ident: ctx.rust_ident(method.rust_name()), + attrs: attrs, + node: ast::TraitItemKind::Method(sig, None), + span: ctx.span(), + }; + + (impl_item, trait_item) +} + impl CodeGenerator for ObjCInterface { type Extra = Item; + fn codegen<'a>(&self, ctx: &BindgenContext, result: &mut CodegenResult<'a>, @@ -2445,66 +2529,18 @@ impl CodeGenerator for ObjCInterface { let mut trait_items = vec![]; for method in self.methods() { - 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 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], - id: ast::DUMMY_NODE_ID, - rules: ast::BlockCheckMode::Default, - span: ctx.span(), - }; - - let attrs = vec![]; - - let impl_item = ast::ImplItem { - id: ast::DUMMY_NODE_ID, - ident: ctx.rust_ident(method.rust_name()), - vis: ast::Visibility::Inherited, // Public, - attrs: attrs.clone(), - node: ast::ImplItemKind::Method(sig.clone(), P(block)), - defaultness: ast::Defaultness::Final, - span: ctx.span(), - }; - - let trait_item = ast::TraitItem { - id: ast::DUMMY_NODE_ID, - ident: ctx.rust_ident(method.rust_name()), - attrs: attrs, - node: ast::TraitItemKind::Method(sig, None), - span: ctx.span(), - }; - + let (impl_item, trait_item) = + objc_method_codegen(ctx, method, None); impl_items.push(impl_item); trait_items.push(trait_item) } + for class_method in self.class_methods() { + let (impl_item, trait_item) = + objc_method_codegen(ctx, class_method, Some(self.name())); + impl_items.push(impl_item); + trait_items.push(trait_item) + } let trait_name = self.rust_name(); diff --git a/src/ir/function.rs b/src/ir/function.rs index e82ba83cf2..941694ffb4 100644 --- a/src/ir/function.rs +++ b/src/ir/function.rs @@ -182,7 +182,8 @@ impl FunctionSig { CXCursor_FunctionDecl | CXCursor_Constructor | CXCursor_CXXMethod | - CXCursor_ObjCInstanceMethodDecl => { + CXCursor_ObjCInstanceMethodDecl | + CXCursor_ObjCClassMethodDecl => { // For CXCursor_FunctionDecl, cursor.args() is the reliable way // to get parameter names and types. cursor.args() @@ -243,7 +244,8 @@ impl FunctionSig { } } - let ty_ret_type = if cursor.kind() == CXCursor_ObjCInstanceMethodDecl { + let ty_ret_type = if cursor.kind() == CXCursor_ObjCInstanceMethodDecl || + cursor.kind() == CXCursor_ObjCClassMethodDecl { try!(cursor.ret_type().ok_or(ParseError::Continue)) } else { try!(ty.ret_type().ok_or(ParseError::Continue)) @@ -252,8 +254,8 @@ impl FunctionSig { let abi = get_abi(ty.call_conv()); if abi.is_none() { - assert_eq!(cursor.kind(), - CXCursor_ObjCInstanceMethodDecl, + assert!(cursor.kind() == CXCursor_ObjCInstanceMethodDecl || + cursor.kind() == CXCursor_ObjCClassMethodDecl, "Invalid ABI for function signature") } diff --git a/src/ir/objc.rs b/src/ir/objc.rs index 485bda8a56..3a88eef8ab 100644 --- a/src/ir/objc.rs +++ b/src/ir/objc.rs @@ -7,6 +7,7 @@ use super::ty::TypeKind; use clang; use clang_sys::CXChildVisit_Continue; use clang_sys::CXCursor_ObjCCategoryDecl; +use clang_sys::CXCursor_ObjCClassMethodDecl; use clang_sys::CXCursor_ObjCClassRef; use clang_sys::CXCursor_ObjCInstanceMethodDecl; use clang_sys::CXCursor_ObjCProtocolDecl; @@ -28,12 +29,14 @@ pub struct ObjCInterface { conforms_to: Vec, /// List of the methods defined in this interfae - methods: Vec, + methods: Vec, + + class_methods: Vec, } /// The objective c methods #[derive(Debug)] -pub struct ObjCInstanceMethod { +pub struct ObjCMethod { /// The original method selector name /// like, dataWithBytes:length: name: String, @@ -43,6 +46,9 @@ pub struct ObjCInstanceMethod { rust_name: String, signature: FunctionSig, + + /// Is class method? + is_class_method: bool, } impl ObjCInterface { @@ -53,6 +59,7 @@ impl ObjCInterface { is_protocol: false, conforms_to: Vec::new(), methods: Vec::new(), + class_methods: Vec::new(), } } @@ -77,11 +84,16 @@ impl ObjCInterface { } } - /// List of the methods defined in this interfae - pub fn methods(&self) -> &Vec { + /// List of the methods defined in this interface + pub fn methods(&self) -> &Vec { &self.methods } + /// List of the class methods defined in this interface + pub fn class_methods(&self) -> &Vec { + &self.class_methods + } + /// Parses the Objective C interface from the cursor pub fn from_ty(cursor: &clang::Cursor, ctx: &mut BindgenContext) @@ -131,14 +143,15 @@ impl ObjCInterface { } } - CXCursor_ObjCInstanceMethodDecl => { + CXCursor_ObjCInstanceMethodDecl | + CXCursor_ObjCClassMethodDecl => { let name = c.spelling(); let signature = FunctionSig::from_ty(&c.cur_type(), &c, ctx) .expect("Invalid function sig"); - let method = ObjCInstanceMethod::new(&name, signature); - - interface.methods.push(method); + let is_class_method = c.kind() == CXCursor_ObjCClassMethodDecl; + let method = ObjCMethod::new(&name, signature, is_class_method); + interface.add_method(method); } _ => {} } @@ -146,18 +159,30 @@ impl ObjCInterface { }); Some(interface) } + + fn add_method(&mut self, method: ObjCMethod) { + if method.is_class_method { + self.class_methods.push(method); + } else { + self.methods.push(method); + } + } } -impl ObjCInstanceMethod { - fn new(name: &str, signature: FunctionSig) -> ObjCInstanceMethod { +impl ObjCMethod { + fn new(name: &str, + signature: FunctionSig, + is_class_method: bool) + -> ObjCMethod { let split_name: Vec<&str> = name.split(':').collect(); let rust_name = split_name.join("_"); - ObjCInstanceMethod { + ObjCMethod { name: name.to_owned(), rust_name: rust_name.to_owned(), signature: signature, + is_class_method: is_class_method, } } @@ -178,6 +203,11 @@ impl ObjCInstanceMethod { &self.signature } + /// Is this a class method? + pub fn is_class_method(&self) -> bool { + self.is_class_method + } + /// Formats the method call pub fn format_method_call(&self, args: &[String]) -> String { let split_name: Vec<&str> = @@ -213,6 +243,10 @@ impl Trace for ObjCInterface { method.signature.trace(context, tracer, &()); } + for class_method in &self.class_methods { + class_method.signature.trace(context, tracer, &()); + } + for protocol in &self.conforms_to { tracer.visit(*protocol); } diff --git a/tests/expectations/tests/objc_class_method.rs b/tests/expectations/tests/objc_class_method.rs new file mode 100644 index 0000000000..768abe1070 --- /dev/null +++ b/tests/expectations/tests/objc_class_method.rs @@ -0,0 +1,54 @@ +/* automatically generated by rust-bindgen */ + + +#![allow(non_snake_case)] + +#![cfg(target_os="macos")] + +#[macro_use] +extern crate objc; +#[allow(non_camel_case_types)] +pub type id = *mut objc::runtime::Object; +pub trait Foo { + unsafe fn method(); + unsafe fn methodWithInt_(foo: ::std::os::raw::c_int); + unsafe fn methodWithFoo_(foo: id); + unsafe fn methodReturningInt() + -> ::std::os::raw::c_int; + unsafe fn methodReturningFoo() + -> *mut id; + unsafe fn methodWithArg1_andArg2_andArg3_(intvalue: ::std::os::raw::c_int, + ptr: + *mut ::std::os::raw::c_schar, + floatvalue: f32); +} +impl Foo for id { + unsafe fn method() { + msg_send!(objc :: runtime :: Class :: get ( "Foo" ) . expect ( + "Couldn\'t find Foo" ) , method) + } + unsafe fn methodWithInt_(foo: ::std::os::raw::c_int) { + msg_send!(objc :: runtime :: Class :: get ( "Foo" ) . expect ( + "Couldn\'t find Foo" ) , methodWithInt:foo ) + } + unsafe fn methodWithFoo_(foo: id) { + msg_send!(objc :: runtime :: Class :: get ( "Foo" ) . expect ( + "Couldn\'t find Foo" ) , methodWithFoo:foo ) + } + unsafe fn methodReturningInt() -> ::std::os::raw::c_int { + msg_send!(objc :: runtime :: Class :: get ( "Foo" ) . expect ( + "Couldn\'t find Foo" ) , methodReturningInt) + } + unsafe fn methodReturningFoo() -> *mut id { + msg_send!(objc :: runtime :: Class :: get ( "Foo" ) . expect ( + "Couldn\'t find Foo" ) , methodReturningFoo) + } + unsafe fn methodWithArg1_andArg2_andArg3_(intvalue: ::std::os::raw::c_int, + ptr: + *mut ::std::os::raw::c_schar, + floatvalue: f32) { + msg_send!(objc :: runtime :: Class :: get ( "Foo" ) . expect ( + "Couldn\'t find Foo" ) , + methodWithArg1:intvalue andArg2:ptr andArg3:floatvalue ) + } +} diff --git a/tests/expectations/tests/objc_whitelist.rs b/tests/expectations/tests/objc_whitelist.rs index 332453f1e0..b0418f38d1 100644 --- a/tests/expectations/tests/objc_whitelist.rs +++ b/tests/expectations/tests/objc_whitelist.rs @@ -11,15 +11,25 @@ extern crate objc; pub type id = *mut objc::runtime::Object; pub trait protocol_SomeProtocol { unsafe fn protocolMethod(self); + unsafe fn protocolClassMethod(); } impl protocol_SomeProtocol for id { unsafe fn protocolMethod(self) { msg_send!(self , protocolMethod) } + unsafe fn protocolClassMethod() { + msg_send!(objc :: runtime :: Class :: get ( "SomeProtocol" ) . expect + ( "Couldn\'t find SomeProtocol" ) , protocolClassMethod) + } } pub trait WhitelistMe { unsafe fn method(self); + unsafe fn classMethod(); } impl WhitelistMe for id { unsafe fn method(self) { msg_send!(self , method) } + unsafe fn classMethod() { + msg_send!(objc :: runtime :: Class :: get ( "WhitelistMe" ) . expect ( + "Couldn\'t find WhitelistMe" ) , classMethod) + } } pub trait WhitelistMe_InterestingCategory { } impl WhitelistMe_InterestingCategory for id { } diff --git a/tests/headers/objc_class_method.h b/tests/headers/objc_class_method.h new file mode 100644 index 0000000000..ddda742e8a --- /dev/null +++ b/tests/headers/objc_class_method.h @@ -0,0 +1,11 @@ +// bindgen-flags: --objc-extern-crate -- -x objective-c +// bindgen-osx-only + +@interface Foo ++ (void)method; ++ (void)methodWithInt:(int)foo; ++ (void)methodWithFoo:(Foo*)foo; ++ (int)methodReturningInt; ++ (Foo*)methodReturningFoo; ++ (void)methodWithArg1:(int)intvalue andArg2:(char*)ptr andArg3:(float)floatvalue; +@end diff --git a/tests/headers/objc_whitelist.h b/tests/headers/objc_whitelist.h index 7cbe43d696..8a3bb86917 100644 --- a/tests/headers/objc_whitelist.h +++ b/tests/headers/objc_whitelist.h @@ -5,11 +5,13 @@ // Protocol should be included, since it is used by the WhitelistMe @protocol SomeProtocol -(void)protocolMethod; ++(void)protocolClassMethod; @end // The whitelisted item @interface WhitelistMe -(void)method; ++(void)classMethod; @end // This was also explicitly whitelisted