Skip to content

objc: Support method arguments #475

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

Merged
merged 2 commits into from
Feb 6, 2017
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
11 changes: 11 additions & 0 deletions src/clang.rs
Original file line number Diff line number Diff line change
Expand Up @@ -523,6 +523,14 @@ impl Cursor {
pub fn evaluate(&self) -> Option<EvalResult> {
EvalResult::new(*self)
}

/// Return the result type for this cursor
pub fn ret_type(&self) -> Option<Type> {
let rt = Type {
x: unsafe { clang_getCursorResultType(self.x) },
};
if rt.is_valid() { Some(rt) } else { None }
}
}

extern "C" fn visit_children<Visitor>(cur: CXCursor,
Expand Down Expand Up @@ -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 {
Expand Down
160 changes: 96 additions & 64 deletions src/codegen/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2153,61 +2153,8 @@ impl ToRustTy for FunctionSig {

fn to_rust_ty(&self, ctx: &BindgenContext, _item: &Item) -> P<ast::Ty> {
// 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::<Vec<_>>();
let ret = utils::fnsig_return_ty(ctx, &self);
let arguments = utils::fnsig_arguments(ctx, &self);

let decl = P(ast::FnDecl {
inputs: arguments,
Expand Down Expand Up @@ -2316,9 +2263,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],
Expand All @@ -2327,13 +2301,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 {
Expand Down Expand Up @@ -2697,4 +2664,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<ast::Arg> {
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::<Vec<_>>()
}

}
10 changes: 8 additions & 2 deletions src/ir/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ fn get_abi(cc: CXCallingConv) -> abi::Abi {
CXCallingConv_X86FastCall => abi::Abi::Fastcall,
CXCallingConv_AAPCS => abi::Abi::Aapcs,
CXCallingConv_X86_64Win64 => abi::Abi::Win64,
CXCallingConv_Invalid => abi::Abi::C, // TODO:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of this, could we make this function return Option<abi::Abi>, commenting that it should only return None for objective C stuff, and asserting that in the caller?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Like this? (as separate commit)

other => panic!("unsupported calling convention: {:?}", other),
}
}
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -218,7 +220,11 @@ 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());

Expand Down
46 changes: 40 additions & 6 deletions src/ir/objc.rs
Original file line number Diff line number Diff line change
@@ -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
///
Expand All @@ -30,6 +29,8 @@ pub struct ObjCInstanceMethod {
/// Method name as converted to rust
/// like, dataWithBytes_length_
rust_name: String,

signature: FunctionSig,
}

impl ObjCInterface {
Expand All @@ -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<Self> {
let name = cursor.spelling();
let mut interface = Self::new(&name);
Expand All @@ -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);
}
Expand All @@ -75,14 +79,15 @@ 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("_");

ObjCInstanceMethod {
name: name.to_owned(),
rust_name: rust_name.to_owned(),
signature: signature,
}
}

Expand All @@ -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::<Vec<_>>()
.join("")
}
}
31 changes: 31 additions & 0 deletions tests/expectations/tests/objc_method.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 )
}
}
6 changes: 5 additions & 1 deletion tests/headers/objc_method.h
Original file line number Diff line number Diff line change
Expand Up @@ -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