Skip to content

Commit

Permalink
objc: Implement class methods
Browse files Browse the repository at this point in the history
  • Loading branch information
scoopr committed Mar 5, 2017
1 parent 6320ed2 commit e14f1bd
Show file tree
Hide file tree
Showing 14 changed files with 223 additions and 30 deletions.
82 changes: 67 additions & 15 deletions src/codegen/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::ty::{Type, TypeKind};
use ir::var::Var;

Expand Down Expand Up @@ -2400,28 +2400,33 @@ impl CodeGenerator for Function {
}
}

impl CodeGenerator for ObjCInterface {
type Extra = Item;
fn codegen<'a>(&self,
ctx: &BindgenContext,
result: &mut CodegenResult<'a>,
_whitelisted_items: &ItemSet,
_: &Item) {
let mut impl_items = vec![];
let mut trait_items = vec![];

for method in self.methods() {
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 = aster::AstBuilder::new()
let sig;

if method.is_class() {
sig = aster::AstBuilder::new()
.method_sig()
.unsafe_()
.fn_decl()
.with_args(fn_args.clone())
.build(fn_ret);
} else {
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()
Expand All @@ -2437,9 +2442,19 @@ impl CodeGenerator for ObjCInterface {

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])
let body;

if method.is_class() {
let class_name =
class_name.expect("Generating a class method without class name?")
.to_owned();
body = quote_stmt!(ctx.ext_cx(),
msg_send![class($class_name), $methods_and_args])
.unwrap();
} else {
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,
Expand Down Expand Up @@ -2467,10 +2482,33 @@ impl CodeGenerator for ObjCInterface {
span: ctx.span(),
};

(impl_item, trait_item)
}

impl CodeGenerator for ObjCInterface {
type Extra = Item;

fn codegen<'a>(&self,
ctx: &BindgenContext,
result: &mut CodegenResult<'a>,
_whitelisted_items: &ItemSet,
_: &Item) {
let mut impl_items = vec![];
let mut trait_items = vec![];

for method in self.methods() {
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();

Expand Down Expand Up @@ -2562,7 +2600,21 @@ mod utils {
)
.unwrap();

let items = vec![use_objc, id_type];
let class_ty = quote_item!(ctx.ext_cx(),
type Class = *mut objc::runtime::Class;
)
.unwrap();
let class_fn = quote_item!(ctx.ext_cx(),
#[inline]
fn class(name: &str) -> Class {
unsafe {
std::mem::transmute(objc::runtime::Class::get(name))
}
}
)
.unwrap();

let items = vec![use_objc, id_type, class_ty, class_fn];
let old_items = mem::replace(result, items);
result.extend(old_items.into_iter());
}
Expand Down
10 changes: 6 additions & 4 deletions src/ir/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,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()
Expand Down Expand Up @@ -241,7 +242,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))
Expand All @@ -250,8 +252,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")
}

Expand Down
49 changes: 38 additions & 11 deletions src/ir/objc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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,
Expand All @@ -43,6 +46,9 @@ pub struct ObjCInstanceMethod {
rust_name: String,

signature: FunctionSig,

/// Is class method?
is_class: bool,
}

impl ObjCInterface {
Expand All @@ -53,6 +59,7 @@ impl ObjCInterface {
is_protocol: false,
conforms_to: Vec::new(),
methods: Vec::new(),
class_methods: Vec::new(),
}
}

Expand All @@ -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)
Expand Down Expand Up @@ -131,14 +143,19 @@ 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);
if c.kind() == CXCursor_ObjCInstanceMethodDecl {
let method = ObjCMethod::new(&name, signature, false);
interface.methods.push(method);
} else {
let method = ObjCMethod::new(&name, signature, true);
interface.class_methods.push(method);
}
}
_ => {}
}
Expand All @@ -148,16 +165,17 @@ impl ObjCInterface {
}
}

impl ObjCInstanceMethod {
fn new(name: &str, signature: FunctionSig) -> ObjCInstanceMethod {
impl ObjCMethod {
fn new(name: &str, signature: FunctionSig, is_class: 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: is_class,
}
}

Expand All @@ -178,6 +196,11 @@ impl ObjCInstanceMethod {
&self.signature
}

/// Is this a class method?
pub fn is_class(&self) -> bool {
self.is_class
}

/// Formats the method call
pub fn format_method_call(&self, args: &[String]) -> String {
let split_name: Vec<&str> =
Expand Down Expand Up @@ -213,6 +236,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);
}
Expand Down
5 changes: 5 additions & 0 deletions tests/expectations/tests/objc_category.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@
extern crate objc;
#[allow(non_camel_case_types)]
pub type id = *mut objc::runtime::Object;
type Class = *mut objc::runtime::Class;
#[inline]
fn class(name: &str) -> Class {
unsafe { std::mem::transmute(objc::runtime::Class::get(name)) }
}
pub trait Foo {
unsafe fn method(self);
}
Expand Down
5 changes: 5 additions & 0 deletions tests/expectations/tests/objc_class.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@
extern crate objc;
#[allow(non_camel_case_types)]
pub type id = *mut objc::runtime::Object;
type Class = *mut objc::runtime::Class;
#[inline]
fn class(name: &str) -> Class {
unsafe { std::mem::transmute(objc::runtime::Class::get(name)) }
}
pub trait Foo {
unsafe fn method(self);
}
Expand Down
51 changes: 51 additions & 0 deletions tests/expectations/tests/objc_class_method.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/* 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;
type Class = *mut objc::runtime::Class;
#[inline]
fn class(name: &str) -> Class {
unsafe { std::mem::transmute(objc::runtime::Class::get(name)) }
}
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_char,
floatvalue: f32);
}
impl Foo for id {
unsafe fn method() { msg_send!(class ( "Foo" ) , method) }
unsafe fn methodWithInt_(foo: ::std::os::raw::c_int) {
msg_send!(class ( "Foo" ) , methodWithInt:foo )
}
unsafe fn methodWithFoo_(foo: id) {
msg_send!(class ( "Foo" ) , methodWithFoo:foo )
}
unsafe fn methodReturningInt() -> ::std::os::raw::c_int {
msg_send!(class ( "Foo" ) , methodReturningInt)
}
unsafe fn methodReturningFoo() -> *mut id {
msg_send!(class ( "Foo" ) , methodReturningFoo)
}
unsafe fn methodWithArg1_andArg2_andArg3_(intvalue: ::std::os::raw::c_int,
ptr:
*mut ::std::os::raw::c_char,
floatvalue: f32) {
msg_send!(class ( "Foo" ) ,
methodWithArg1:intvalue andArg2:ptr andArg3:floatvalue )
}
}
5 changes: 5 additions & 0 deletions tests/expectations/tests/objc_interface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@
extern crate objc;
#[allow(non_camel_case_types)]
pub type id = *mut objc::runtime::Object;
type Class = *mut objc::runtime::Class;
#[inline]
fn class(name: &str) -> Class {
unsafe { std::mem::transmute(objc::runtime::Class::get(name)) }
}
pub trait Foo { }
impl Foo for id { }
pub trait protocol_bar { }
Expand Down
5 changes: 5 additions & 0 deletions tests/expectations/tests/objc_interface_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@
extern crate objc;
#[allow(non_camel_case_types)]
pub type id = *mut objc::runtime::Object;
type Class = *mut objc::runtime::Class;
#[inline]
fn class(name: &str) -> Class {
unsafe { std::mem::transmute(objc::runtime::Class::get(name)) }
}
pub trait Foo { }
impl Foo for id { }
#[repr(C)]
Expand Down
Loading

0 comments on commit e14f1bd

Please sign in to comment.