Skip to content

Add initial Objective C support #443

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 1 commit into from
Feb 2, 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
3 changes: 2 additions & 1 deletion src/clang.rs
Original file line number Diff line number Diff line change
Expand Up @@ -688,7 +688,8 @@ impl Type {
CXType_Pointer |
CXType_RValueReference |
CXType_LValueReference |
CXType_MemberPointer => {
CXType_MemberPointer |
CXType_ObjCObjectPointer => {
let ret = Type {
x: unsafe { clang_getPointeeType(self.x) },
};
Expand Down
144 changes: 140 additions & 4 deletions src/codegen/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,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::ty::{Type, TypeKind};
use ir::type_collector::ItemSet;
use ir::var::Var;
Expand Down Expand Up @@ -87,6 +88,9 @@ struct CodegenResult<'a> {
/// Whether an incomplete array has been generated at least once.
saw_incomplete_array: bool,

/// Whether Objective C types have been seen at least once.
saw_objc: bool,

items_seen: HashSet<ItemId>,
/// The set of generated function/var names, needed because in C/C++ is
/// legal to do something like:
Expand Down Expand Up @@ -119,6 +123,7 @@ impl<'a> CodegenResult<'a> {
items: vec![],
saw_union: false,
saw_incomplete_array: false,
saw_objc: false,
codegen_id: codegen_id,
items_seen: Default::default(),
functions_seen: Default::default(),
Expand All @@ -140,6 +145,10 @@ impl<'a> CodegenResult<'a> {
self.saw_incomplete_array = true;
}

fn saw_objc(&mut self) {
self.saw_objc = true;
}

fn seen(&self, item: ItemId) -> bool {
self.items_seen.contains(&item)
}
Expand Down Expand Up @@ -184,6 +193,7 @@ impl<'a> CodegenResult<'a> {

self.saw_union |= new.saw_union;
self.saw_incomplete_array |= new.saw_incomplete_array;
self.saw_objc |= new.saw_objc;

new.items
}
Expand Down Expand Up @@ -359,6 +369,9 @@ impl CodeGenerator for Module {
if ctx.need_bindegen_complex_type() {
utils::prepend_complex_type(ctx, &mut *result);
}
if result.saw_objc {
utils::prepend_objc_header(ctx, &mut *result);
}
}
};

Expand Down Expand Up @@ -623,6 +636,9 @@ impl CodeGenerator for Type {
TypeKind::Enum(ref ei) => {
ei.codegen(ctx, result, whitelisted_items, item)
}
TypeKind::ObjCInterface(ref interface) => {
interface.codegen(ctx, result, whitelisted_items, item)
}
ref u @ TypeKind::UnresolvedTypeRef(..) => {
unreachable!("Should have been resolved after parsing {:?}!", u)
}
Expand Down Expand Up @@ -2111,6 +2127,9 @@ impl ToRustTy for Type {
let ident = ctx.rust_ident(&name);
quote_ty!(ctx.ext_cx(), $ident)
}
TypeKind::ObjCInterface(..) => {
quote_ty!(ctx.ext_cx(), id)
},
ref u @ TypeKind::UnresolvedTypeRef(..) => {
unreachable!("Should have been resolved after parsing {:?}!", u)
}
Expand Down Expand Up @@ -2144,10 +2163,22 @@ impl ToRustTy for FunctionSig {
// the array type derivation.
//
// [1]: http://c0x.coding-guidelines.com/6.7.5.3.html
let arg_ty = if let TypeKind::Array(t, _) = *arg_ty.canonical_type(ctx).kind() {
t.to_rust_ty(ctx).to_ptr(arg_ty.is_const(), ctx.span())
} else {
arg_item.to_rust_ty(ctx)
let arg_ty = match *arg_ty.canonical_type(ctx).kind() {
Copy link
Contributor

Choose a reason for hiding this comment

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

please fix the alignment in this block.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Damnit, I fixed this once already, but somehow rustfmt got really confused in this block :(

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 {
Expand Down Expand Up @@ -2263,6 +2294,85 @@ 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() {
let method_name = ctx.rust_ident(method.name());

let body = quote_stmt!(ctx.ext_cx(), msg_send![self, $method_name])
.unwrap();
let block = ast::Block {
stmts: vec![body],
id: ast::DUMMY_NODE_ID,
rules: ast::BlockCheckMode::Default,
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 {
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_items.push(impl_item);
trait_items.push(trait_item)
}


let trait_block = aster::AstBuilder::new()
.item()
.pub_()
.trait_(self.name())
.with_items(trait_items)
.build();

let ty_for_impl = quote_ty!(ctx.ext_cx(), id);
let impl_block = aster::AstBuilder::new()
.item()
.impl_()
.trait_()
.id(self.name())
.build()
.with_items(impl_items)
.build_ty(ty_for_impl);

result.push(trait_block);
result.push(impl_block);
result.saw_objc();
}
}



pub fn codegen(context: &mut BindgenContext) -> Vec<P<ast::Item>> {
context.gen(|context| {
let counter = Cell::new(0);
Expand Down Expand Up @@ -2296,6 +2406,32 @@ mod utils {
use syntax::ast;
use syntax::ptr::P;


pub fn prepend_objc_header(ctx: &BindgenContext,
result: &mut Vec<P<ast::Item>>) {
let use_objc = if ctx.options().objc_extern_crate {
quote_item!(ctx.ext_cx(),
use objc;
).unwrap()
} else {
quote_item!(ctx.ext_cx(),
#[macro_use]
extern crate objc;
).unwrap()
};


let id_type = quote_item!(ctx.ext_cx(),
#[allow(non_camel_case_types)]
pub type id = *mut objc::runtime::Object;
)
.unwrap();

let items = vec![use_objc, id_type];
let old_items = mem::replace(result, items);
result.extend(old_items.into_iter());
}

pub fn prepend_union_types(ctx: &BindgenContext,
result: &mut Vec<P<ast::Item>>) {
let prefix = ctx.trait_prefix();
Expand Down
1 change: 1 addition & 0 deletions src/ir/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@ pub mod module;
pub mod ty;
pub mod type_collector;
pub mod var;
pub mod objc;
99 changes: 99 additions & 0 deletions src/ir/objc.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
//! Objective C types

use clang;
use clang_sys::CXChildVisit_Continue;
use clang_sys::CXCursor_ObjCInstanceMethodDecl;
// use clang_sys::CXCursor_ObjCSuperClassRef;
use super::context::BindgenContext;

/// Objective C interface as used in TypeKind
///
/// Also protocols are parsed as this type
#[derive(Debug)]
pub struct ObjCInterface {
/// The name
/// like, NSObject
name: String,

/// List of the methods defined in this interfae
methods: Vec<ObjCInstanceMethod>,
}

/// The objective c methods
#[derive(Debug)]
pub struct ObjCInstanceMethod {
/// The original method selector name
/// like, dataWithBytes:length:
name: String,

/// Method name as converted to rust
/// like, dataWithBytes_length_
rust_name: String,
}

impl ObjCInterface {
fn new(name: &str) -> ObjCInterface {
ObjCInterface {
name: name.to_owned(),
methods: Vec::new(),
}
}

/// The name
/// like, NSObject
pub fn name(&self) -> &str {
self.name.as_ref()
}

/// List of the methods defined in this interfae
pub fn methods(&self) -> &Vec<ObjCInstanceMethod> {
&self.methods
}

/// Parses the Objective C interface from the cursor
pub fn from_ty(cursor: &clang::Cursor,
_ctx: &mut BindgenContext)
-> Option<Self> {
let name = cursor.spelling();
let mut interface = Self::new(&name);

cursor.visit(|cursor| {
match cursor.kind() {
CXCursor_ObjCInstanceMethodDecl => {
let name = cursor.spelling();
let method = ObjCInstanceMethod::new(&name);

interface.methods.push(method);
}
_ => {}
}
CXChildVisit_Continue
});
Some(interface)
}
}

impl ObjCInstanceMethod {
fn new(name: &str) -> ObjCInstanceMethod {
let split_name: Vec<&str> = name.split(':').collect();

Copy link
Contributor

Choose a reason for hiding this comment

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

This may also go away if you want :)

let rust_name = split_name.join("_");

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

/// The original method selector name
/// like, dataWithBytes:length:
pub fn name(&self) -> &str {
self.name.as_ref()
}

/// Method name as converted to rust
/// like, dataWithBytes_length_
pub fn rust_name(&self) -> &str {
self.rust_name.as_ref()
}
}
Loading