Skip to content

Commit ea1f397

Browse files
committed
Add initial Objective C support
It parses interfaces and protocol but ignores base classes, and their methods which don’t have arguments, the method signature is currently ignored. Also you can pass objc class instances to C functions. Next steps are inheritance/base classes, method signatures, properties, categories. Then check with system headers what is missing.
1 parent bdd034b commit ea1f397

15 files changed

+390
-9
lines changed

src/clang.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -688,7 +688,8 @@ impl Type {
688688
CXType_Pointer |
689689
CXType_RValueReference |
690690
CXType_LValueReference |
691-
CXType_MemberPointer => {
691+
CXType_MemberPointer |
692+
CXType_ObjCObjectPointer => {
692693
let ret = Type {
693694
x: unsafe { clang_getPointeeType(self.x) },
694695
};

src/codegen/mod.rs

Lines changed: 137 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ use ir::item::{Item, ItemAncestors, ItemCanonicalName, ItemCanonicalPath};
1313
use ir::item_kind::ItemKind;
1414
use ir::layout::Layout;
1515
use ir::module::Module;
16+
use ir::objc::ObjCInterface;
1617
use ir::ty::{Type, TypeKind};
1718
use ir::type_collector::ItemSet;
1819
use ir::var::Var;
@@ -87,6 +88,9 @@ struct CodegenResult<'a> {
8788
/// Whether an incomplete array has been generated at least once.
8889
saw_incomplete_array: bool,
8990

91+
/// Whether Objective C types have been seen at least once.
92+
saw_objc: bool,
93+
9094
items_seen: HashSet<ItemId>,
9195
/// The set of generated function/var names, needed because in C/C++ is
9296
/// legal to do something like:
@@ -119,6 +123,7 @@ impl<'a> CodegenResult<'a> {
119123
items: vec![],
120124
saw_union: false,
121125
saw_incomplete_array: false,
126+
saw_objc: false,
122127
codegen_id: codegen_id,
123128
items_seen: Default::default(),
124129
functions_seen: Default::default(),
@@ -140,6 +145,10 @@ impl<'a> CodegenResult<'a> {
140145
self.saw_incomplete_array = true;
141146
}
142147

148+
fn saw_objc(&mut self) {
149+
self.saw_objc = true;
150+
}
151+
143152
fn seen(&self, item: ItemId) -> bool {
144153
self.items_seen.contains(&item)
145154
}
@@ -184,6 +193,7 @@ impl<'a> CodegenResult<'a> {
184193

185194
self.saw_union |= new.saw_union;
186195
self.saw_incomplete_array |= new.saw_incomplete_array;
196+
self.saw_objc |= new.saw_objc;
187197

188198
new.items
189199
}
@@ -359,6 +369,9 @@ impl CodeGenerator for Module {
359369
if ctx.need_bindegen_complex_type() {
360370
utils::prepend_complex_type(ctx, &mut *result);
361371
}
372+
if result.saw_objc {
373+
utils::prepend_objc_header(ctx, &mut *result);
374+
}
362375
}
363376
};
364377

@@ -623,6 +636,9 @@ impl CodeGenerator for Type {
623636
TypeKind::Enum(ref ei) => {
624637
ei.codegen(ctx, result, whitelisted_items, item)
625638
}
639+
TypeKind::ObjCInterface(ref interface) => {
640+
interface.codegen(ctx, result, whitelisted_items, item)
641+
}
626642
ref u @ TypeKind::UnresolvedTypeRef(..) => {
627643
unreachable!("Should have been resolved after parsing {:?}!", u)
628644
}
@@ -2111,6 +2127,9 @@ impl ToRustTy for Type {
21112127
let ident = ctx.rust_ident(&name);
21122128
quote_ty!(ctx.ext_cx(), $ident)
21132129
}
2130+
TypeKind::ObjCInterface(..) => {
2131+
quote_ty!(ctx.ext_cx(), id)
2132+
},
21142133
ref u @ TypeKind::UnresolvedTypeRef(..) => {
21152134
unreachable!("Should have been resolved after parsing {:?}!", u)
21162135
}
@@ -2144,10 +2163,22 @@ impl ToRustTy for FunctionSig {
21442163
// the array type derivation.
21452164
//
21462165
// [1]: http://c0x.coding-guidelines.com/6.7.5.3.html
2147-
let arg_ty = if let TypeKind::Array(t, _) = *arg_ty.canonical_type(ctx).kind() {
2166+
let arg_ty = match *arg_ty.canonical_type(ctx).kind() {
2167+
TypeKind::Array(t, _) => {
21482168
t.to_rust_ty(ctx).to_ptr(arg_ty.is_const(), ctx.span())
2169+
},
2170+
TypeKind::Pointer(inner) => {
2171+
let inner = ctx.resolve_item(inner);
2172+
let inner_ty = inner.expect_type();
2173+
if let TypeKind::ObjCInterface(_) = *inner_ty.canonical_type(ctx).kind() {
2174+
aster::ty::TyBuilder::new().id("id")
21492175
} else {
21502176
arg_item.to_rust_ty(ctx)
2177+
}
2178+
},
2179+
_ => {
2180+
arg_item.to_rust_ty(ctx)
2181+
}
21512182
};
21522183

21532184
let arg_name = match *name {
@@ -2263,6 +2294,85 @@ impl CodeGenerator for Function {
22632294
}
22642295
}
22652296

2297+
impl CodeGenerator for ObjCInterface {
2298+
type Extra = Item;
2299+
fn codegen<'a>(&self,
2300+
ctx: &BindgenContext,
2301+
result: &mut CodegenResult<'a>,
2302+
_whitelisted_items: &ItemSet,
2303+
_: &Item) {
2304+
let mut impl_items = vec![];
2305+
let mut trait_items = vec![];
2306+
2307+
for method in self.methods() {
2308+
let method_name = ctx.rust_ident(method.name());
2309+
2310+
let body = quote_stmt!(ctx.ext_cx(), msg_send![self, $method_name])
2311+
.unwrap();
2312+
let block = ast::Block {
2313+
stmts: vec![body],
2314+
id: ast::DUMMY_NODE_ID,
2315+
rules: ast::BlockCheckMode::Default,
2316+
span: ctx.span(),
2317+
};
2318+
2319+
let sig = aster::AstBuilder::new()
2320+
.method_sig()
2321+
.unsafe_()
2322+
.fn_decl()
2323+
.self_()
2324+
.build(ast::SelfKind::Value(ast::Mutability::Immutable))
2325+
.build(ast::FunctionRetTy::Default(ctx.span()));
2326+
let attrs = vec![];
2327+
2328+
let impl_item = ast::ImplItem {
2329+
id: ast::DUMMY_NODE_ID,
2330+
ident: ctx.rust_ident(method.rust_name()),
2331+
vis: ast::Visibility::Inherited, // Public,
2332+
attrs: attrs.clone(),
2333+
node: ast::ImplItemKind::Method(sig.clone(), P(block)),
2334+
defaultness: ast::Defaultness::Final,
2335+
span: ctx.span(),
2336+
};
2337+
2338+
let trait_item = ast::TraitItem {
2339+
id: ast::DUMMY_NODE_ID,
2340+
ident: ctx.rust_ident(method.rust_name()),
2341+
attrs: attrs,
2342+
node: ast::TraitItemKind::Method(sig, None),
2343+
span: ctx.span(),
2344+
};
2345+
2346+
impl_items.push(impl_item);
2347+
trait_items.push(trait_item)
2348+
}
2349+
2350+
2351+
let trait_block = aster::AstBuilder::new()
2352+
.item()
2353+
.pub_()
2354+
.trait_(self.name())
2355+
.with_items(trait_items)
2356+
.build();
2357+
2358+
let ty_for_impl = quote_ty!(ctx.ext_cx(), id);
2359+
let impl_block = aster::AstBuilder::new()
2360+
.item()
2361+
.impl_()
2362+
.trait_()
2363+
.id(self.name())
2364+
.build()
2365+
.with_items(impl_items)
2366+
.build_ty(ty_for_impl);
2367+
2368+
result.push(trait_block);
2369+
result.push(impl_block);
2370+
result.saw_objc();
2371+
}
2372+
}
2373+
2374+
2375+
22662376
pub fn codegen(context: &mut BindgenContext) -> Vec<P<ast::Item>> {
22672377
context.gen(|context| {
22682378
let counter = Cell::new(0);
@@ -2296,6 +2406,32 @@ mod utils {
22962406
use syntax::ast;
22972407
use syntax::ptr::P;
22982408

2409+
2410+
pub fn prepend_objc_header(ctx: &BindgenContext,
2411+
result: &mut Vec<P<ast::Item>>) {
2412+
let use_objc = if ctx.options().objc_extern_crate {
2413+
quote_item!(ctx.ext_cx(),
2414+
use objc;
2415+
).unwrap()
2416+
} else {
2417+
quote_item!(ctx.ext_cx(),
2418+
#[macro_use]
2419+
extern crate objc;
2420+
).unwrap()
2421+
};
2422+
2423+
2424+
let id_type = quote_item!(ctx.ext_cx(),
2425+
#[allow(non_camel_case_types)]
2426+
pub type id = *mut objc::runtime::Object;
2427+
)
2428+
.unwrap();
2429+
2430+
let items = vec![use_objc, id_type];
2431+
let old_items = mem::replace(result, items);
2432+
result.extend(old_items.into_iter());
2433+
}
2434+
22992435
pub fn prepend_union_types(ctx: &BindgenContext,
23002436
result: &mut Vec<P<ast::Item>>) {
23012437
let prefix = ctx.trait_prefix();

src/ir/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,4 @@ pub mod module;
1717
pub mod ty;
1818
pub mod type_collector;
1919
pub mod var;
20+
pub mod objc;

src/ir/objc.rs

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
//! Objective C types
2+
3+
use clang;
4+
use clang_sys::CXChildVisit_Continue;
5+
use clang_sys::CXCursor_ObjCInstanceMethodDecl;
6+
// use clang_sys::CXCursor_ObjCSuperClassRef;
7+
use super::context::BindgenContext;
8+
9+
/// Objective C interface as used in TypeKind
10+
///
11+
/// Also protocols are parsed as this type
12+
#[derive(Debug)]
13+
pub struct ObjCInterface {
14+
/// The name
15+
/// like, NSObject
16+
name: String,
17+
18+
/// List of the methods defined in this interfae
19+
methods: Vec<ObjCInstanceMethod>,
20+
}
21+
22+
/// The objective c methods
23+
#[derive(Debug)]
24+
pub struct ObjCInstanceMethod {
25+
/// The original method selector name
26+
/// like, dataWithBytes:length:
27+
name: String,
28+
29+
/// Method name as converted to rust
30+
/// like, dataWithBytes_length_
31+
rust_name: String,
32+
}
33+
34+
impl ObjCInterface {
35+
fn new(name: &str) -> ObjCInterface {
36+
ObjCInterface {
37+
name: name.to_owned(),
38+
methods: Vec::new(),
39+
}
40+
}
41+
42+
/// The name
43+
/// like, NSObject
44+
pub fn name(&self) -> &str {
45+
self.name.as_ref()
46+
}
47+
48+
/// List of the methods defined in this interfae
49+
pub fn methods(&self) -> &Vec<ObjCInstanceMethod> {
50+
&self.methods
51+
}
52+
53+
/// Parses the Objective C interface from the cursor
54+
pub fn from_ty(cursor: &clang::Cursor,
55+
_ctx: &mut BindgenContext)
56+
-> Option<Self> {
57+
let name = cursor.spelling();
58+
let mut interface = Self::new(&name);
59+
60+
cursor.visit(|cursor| {
61+
match cursor.kind() {
62+
CXCursor_ObjCInstanceMethodDecl => {
63+
let name = cursor.spelling();
64+
let method = ObjCInstanceMethod::new(&name);
65+
66+
interface.methods.push(method);
67+
}
68+
_ => {}
69+
}
70+
CXChildVisit_Continue
71+
});
72+
Some(interface)
73+
}
74+
}
75+
76+
impl ObjCInstanceMethod {
77+
fn new(name: &str) -> ObjCInstanceMethod {
78+
let split_name: Vec<&str> = name.split(':').collect();
79+
80+
let rust_name = split_name.join("_");
81+
82+
ObjCInstanceMethod {
83+
name: name.to_owned(),
84+
rust_name: rust_name.to_owned(),
85+
}
86+
}
87+
88+
/// The original method selector name
89+
/// like, dataWithBytes:length:
90+
pub fn name(&self) -> &str {
91+
self.name.as_ref()
92+
}
93+
94+
/// Method name as converted to rust
95+
/// like, dataWithBytes_length_
96+
pub fn rust_name(&self) -> &str {
97+
self.rust_name.as_ref()
98+
}
99+
}

0 commit comments

Comments
 (0)