Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

objc: Implement class methods #558

Merged
merged 1 commit into from
Mar 7, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
150 changes: 93 additions & 57 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::template::{AsNamed, TemplateInstantiation};
use ir::ty::{TemplateDeclaration, Type, TypeKind};
use ir::var::Var;
Expand Down Expand Up @@ -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>,
Expand All @@ -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();

Expand Down
10 changes: 6 additions & 4 deletions src/ir/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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))
Expand All @@ -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")
}

Expand Down
56 changes: 45 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_method: 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,33 +143,46 @@ 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);
}
_ => {}
}
CXChildVisit_Continue
});
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,
}
}

Expand All @@ -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> =
Expand Down Expand Up @@ -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);
}
Expand Down
Loading