summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMikko Lehtonen <scoopr@iki.fi>2017-03-05 00:41:58 +0200
committerMikko Lehtonen <scoopr@iki.fi>2017-03-07 23:08:55 +0200
commitcc6ab475138ce8a27a1aceae53667f226122cac9 (patch)
treee80ee232edafcd0ad73d2fe9cb0d5c5423226394
parentdd1dc9e391f59e864316f145d1d3aac9294fd255 (diff)
objc: Implement class methods
-rw-r--r--src/codegen/mod.rs150
-rw-r--r--src/ir/function.rs10
-rw-r--r--src/ir/objc.rs56
-rw-r--r--tests/expectations/tests/objc_class_method.rs54
-rw-r--r--tests/expectations/tests/objc_whitelist.rs10
-rw-r--r--tests/headers/objc_class_method.h11
-rw-r--r--tests/headers/objc_whitelist.h2
7 files changed, 221 insertions, 72 deletions
diff --git a/src/codegen/mod.rs b/src/codegen/mod.rs
index 7b7c31a8..15163a36 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 e82ba83c..941694ff 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 485bda8a..3a88eef8 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<ItemId>,
/// List of the methods defined in this interfae
- methods: Vec<ObjCInstanceMethod>,
+ methods: Vec<ObjCMethod>,
+
+ class_methods: Vec<ObjCMethod>,
}
/// 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<ObjCInstanceMethod> {
+ /// List of the methods defined in this interface
+ pub fn methods(&self) -> &Vec<ObjCMethod> {
&self.methods
}
+ /// List of the class methods defined in this interface
+ pub fn class_methods(&self) -> &Vec<ObjCMethod> {
+ &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 00000000..768abe10
--- /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 332453f1..b0418f38 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 00000000..ddda742e
--- /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 7cbe43d6..8a3bb869 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 <SomeProtocol>
-(void)method;
++(void)classMethod;
@end
// This was also explicitly whitelisted