Skip to content

Commit c1aaa6a

Browse files
author
bors-servo
authored
Auto merge of #443 - scoopr:objc, r=emilio
Add initial Objective C support Hi! I made an attempt to support parsing objective c headers. This is very much incomplete, and likely not quite yet ready for merging, but I thought I'd share the progress so far. Comments appreciated, I'm still very much a newbie in just about everything related to this change (rust as a language, rust ast, libclang, rust-bindgen), and there were many parts of code I wasn't quite sure about. Commit message: It parses interfaces and protocol but ignores base classes, methods that don’t have arguments, the arguments are 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.
2 parents e277c7e + 4736263 commit c1aaa6a

15 files changed

+394
-13
lines changed

src/clang.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -696,7 +696,8 @@ impl Type {
696696
CXType_Pointer |
697697
CXType_RValueReference |
698698
CXType_LValueReference |
699-
CXType_MemberPointer => {
699+
CXType_MemberPointer |
700+
CXType_ObjCObjectPointer => {
700701
let ret = Type {
701702
x: unsafe { clang_getPointeeType(self.x) },
702703
};

src/codegen/mod.rs

Lines changed: 140 additions & 4 deletions
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() {
2148-
t.to_rust_ty(ctx).to_ptr(arg_ty.is_const(), ctx.span())
2149-
} else {
2150-
arg_item.to_rust_ty(ctx)
2166+
let arg_ty = match *arg_ty.canonical_type(ctx).kind() {
2167+
TypeKind::Array(t, _) => {
2168+
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+
quote_ty!(ctx.ext_cx(), id)
2175+
} else {
2176+
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)