diff --git a/src/clang.rs b/src/clang.rs index 9cf51436df..f0c5124c3b 100644 --- a/src/clang.rs +++ b/src/clang.rs @@ -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) }, }; diff --git a/src/codegen/mod.rs b/src/codegen/mod.rs index 436c2bd7d9..9dd3b6b9bf 100644 --- a/src/codegen/mod.rs +++ b/src/codegen/mod.rs @@ -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; @@ -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, /// The set of generated function/var names, needed because in C/C++ is /// legal to do something like: @@ -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(), @@ -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) } @@ -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 } @@ -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); + } } }; @@ -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) } @@ -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) } @@ -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() { + 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 { @@ -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> { context.gen(|context| { let counter = Cell::new(0); @@ -2296,6 +2406,32 @@ mod utils { use syntax::ast; use syntax::ptr::P; + + pub fn prepend_objc_header(ctx: &BindgenContext, + result: &mut Vec>) { + 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>) { let prefix = ctx.trait_prefix(); diff --git a/src/ir/mod.rs b/src/ir/mod.rs index 73793b167f..d424fcdfaa 100644 --- a/src/ir/mod.rs +++ b/src/ir/mod.rs @@ -17,3 +17,4 @@ pub mod module; pub mod ty; pub mod type_collector; pub mod var; +pub mod objc; diff --git a/src/ir/objc.rs b/src/ir/objc.rs new file mode 100644 index 0000000000..b9fe280b0d --- /dev/null +++ b/src/ir/objc.rs @@ -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, +} + +/// 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 { + &self.methods + } + + /// Parses the Objective C interface from the cursor + pub fn from_ty(cursor: &clang::Cursor, + _ctx: &mut BindgenContext) + -> Option { + 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(); + + 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() + } +} diff --git a/src/ir/ty.rs b/src/ir/ty.rs index d0de72a08b..8d01d905b5 100644 --- a/src/ir/ty.rs +++ b/src/ir/ty.rs @@ -10,6 +10,7 @@ use super::function::FunctionSig; use super::int::IntKind; use super::item::Item; use super::layout::Layout; +use super::objc::ObjCInterface; use super::type_collector::{ItemSet, TypeCollector}; /// The base representation of a type in bindgen. @@ -179,8 +180,12 @@ impl Type { /// Is this a incomplete array type? pub fn is_incomplete_array(&self, ctx: &BindgenContext) -> Option { match self.kind { - TypeKind::Array(item, len) => if len == 0 { Some(item) } else { None }, - TypeKind::ResolvedTypeRef(inner) => ctx.resolve_type(inner).is_incomplete_array(ctx), + TypeKind::Array(item, len) => { + if len == 0 { Some(item) } else { None } + } + TypeKind::ResolvedTypeRef(inner) => { + ctx.resolve_type(inner).is_incomplete_array(ctx) + } _ => None, } } @@ -287,7 +292,7 @@ impl Type { let mut remaining = chars; let valid = (first.is_alphabetic() || first == '_') && - remaining.all(|c| c.is_alphanumeric() || c == '_'); + remaining.all(|c| c.is_alphanumeric() || c == '_'); !valid } @@ -324,7 +329,8 @@ impl Type { TypeKind::Void | TypeKind::NullPtr | TypeKind::BlockPointer | - TypeKind::Pointer(..) => Some(self), + TypeKind::Pointer(..) | + TypeKind::ObjCInterface(..) => Some(self), TypeKind::ResolvedTypeRef(inner) | TypeKind::Alias(inner) | @@ -492,6 +498,9 @@ pub enum TypeKind { /// A named type, that is, a template parameter. Named, + + /// Objective C interface. Always referenced through a pointer + ObjCInterface(ObjCInterface), } impl Type { @@ -525,6 +534,8 @@ impl Type { TypeKind::BlockPointer | TypeKind::Pointer(..) => false, + TypeKind::ObjCInterface(..) => true, // dunno? + TypeKind::UnresolvedTypeRef(..) => { unreachable!("Should have been resolved after parsing!"); } @@ -566,7 +577,16 @@ impl Type { debug!("currently_parsed_types: {:?}", ctx.currently_parsed_types); let canonical_ty = ty.canonical_type(); - let kind = match ty.kind() { + + // Parse objc protocols as if they were interfaces + let mut ty_kind = ty.kind(); + if let Some(loc) = location { + if loc.kind() == CXCursor_ObjCProtocolDecl { + ty_kind = CXType_ObjCInterface; + } + } + + let kind = match ty_kind { CXType_Unexposed if *ty != canonical_ty && canonical_ty.kind() != CXType_Invalid => { debug!("Looking for canonical type: {:?}", canonical_ty); @@ -707,8 +727,7 @@ impl Type { } }; - TypeKind::TemplateAlias(inner_type, - args) + TypeKind::TemplateAlias(inner_type, args) } CXCursor_TemplateRef => { let referenced = location.referenced().unwrap(); @@ -806,6 +825,7 @@ impl Type { // // We might need to, though, if the context is already in the // process of resolving them. + CXType_ObjCObjectPointer | CXType_MemberPointer | CXType_Pointer => { let inner = Item::from_ty_or_ref(ty.pointee_type().unwrap(), @@ -887,6 +907,11 @@ impl Type { parent_id, ctx); } + CXType_ObjCInterface => { + let interface = ObjCInterface::from_ty(&location.unwrap(), ctx) + .expect("Not a valid objc interface?"); + TypeKind::ObjCInterface(interface) + } _ => { error!("unsupported type: kind = {:?}; ty = {:?}; at {:?}", ty.kind(), @@ -941,6 +966,10 @@ impl TypeCollector for Type { types.insert(id); } + TypeKind::ObjCInterface(_) => { + // TODO: + } + // None of these variants have edges to other items and types. TypeKind::UnresolvedTypeRef(_, _, None) | TypeKind::Named | diff --git a/src/lib.rs b/src/lib.rs index dabab15289..d6f3c66ea4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -198,6 +198,13 @@ impl Builder { self } + /// Generate '#[macro_use] extern crate objc;' instead of 'use objc;' + /// in the prologue of the files generated from objective-c files + pub fn objc_extern_crate(mut self, doit: bool) -> Self { + self.options.objc_extern_crate = doit; + self + } + /// Generate a C/C++ file that includes the header and has dummy uses of /// every type defined in the header. pub fn dummy_uses>(mut self, dummy_uses: T) -> Builder { @@ -542,6 +549,10 @@ pub struct BindgenOptions { /// Wether to whitelist types recursively. Defaults to true. pub whitelist_recursively: bool, + + /// Intead of emitting 'use objc;' to files generated from objective c files, + /// generate '#[macro_use] extern crate objc;' + pub objc_extern_crate: bool, } impl BindgenOptions { @@ -588,6 +599,7 @@ impl Default for BindgenOptions { conservative_inline_namespaces: false, generate_comments: true, whitelist_recursively: true, + objc_extern_crate: false, } } } diff --git a/src/options.rs b/src/options.rs index 307ea6b073..535eac4bb0 100644 --- a/src/options.rs +++ b/src/options.rs @@ -49,6 +49,9 @@ pub fn builder_from_flags(args: I) Arg::with_name("no-recursive-whitelist") .long("no-recursive-whitelist") .help("Avoid whitelisting types recursively"), + Arg::with_name("objc-extern-crate") + .long("objc-extern-crate") + .help("Use extern crate instead of use for objc"), Arg::with_name("builtins") .long("builtins") .help("Output bindings for builtin definitions, e.g. \ diff --git a/tests/expectations/Cargo.toml b/tests/expectations/Cargo.toml index 034aa14151..e0da6d5a96 100644 --- a/tests/expectations/Cargo.toml +++ b/tests/expectations/Cargo.toml @@ -9,3 +9,4 @@ authors = [ ] [dependencies] +objc = "0.2" diff --git a/tests/expectations/tests/objc_interface.rs b/tests/expectations/tests/objc_interface.rs new file mode 100644 index 0000000000..027cf57e4f --- /dev/null +++ b/tests/expectations/tests/objc_interface.rs @@ -0,0 +1,15 @@ +/* automatically generated by rust-bindgen */ + + +#![allow(non_snake_case)] + +#![cfg(target_os="macos")] + +#[macro_use] +extern crate objc; +#[allow(non_camel_case_types)] +pub type id = *mut objc::runtime::Object; +pub trait Foo { } +impl Foo for id { } +pub trait bar { } +impl bar for id { } diff --git a/tests/expectations/tests/objc_interface_type.rs b/tests/expectations/tests/objc_interface_type.rs new file mode 100644 index 0000000000..2d68ee5078 --- /dev/null +++ b/tests/expectations/tests/objc_interface_type.rs @@ -0,0 +1,33 @@ +/* automatically generated by rust-bindgen */ + + +#![allow(non_snake_case)] + +#![cfg(target_os="macos")] + +#[macro_use] +extern crate objc; +#[allow(non_camel_case_types)] +pub type id = *mut objc::runtime::Object; +pub trait Foo { } +impl Foo for id { } +#[repr(C)] +#[derive(Debug, Copy)] +pub struct FooStruct { + pub foo: *mut id, +} +#[test] +fn bindgen_test_layout_FooStruct() { + assert_eq!(::std::mem::size_of::() , 8usize); + assert_eq!(::std::mem::align_of::() , 8usize); +} +impl Clone for FooStruct { + fn clone(&self) -> Self { *self } +} +extern "C" { + pub fn fooFunc(foo: id); +} +extern "C" { + #[link_name = "kFoo"] + pub static mut kFoo: *const id; +} diff --git a/tests/expectations/tests/objc_method.rs b/tests/expectations/tests/objc_method.rs new file mode 100644 index 0000000000..e2db24f69d --- /dev/null +++ b/tests/expectations/tests/objc_method.rs @@ -0,0 +1,17 @@ +/* automatically generated by rust-bindgen */ + + +#![allow(non_snake_case)] + +#![cfg(target_os="macos")] + +#[macro_use] +extern crate objc; +#[allow(non_camel_case_types)] +pub type id = *mut objc::runtime::Object; +pub trait Foo { + unsafe fn method(self); +} +impl Foo for id { + unsafe fn method(self) { msg_send!(self , method) } +} diff --git a/tests/headers/objc_interface.h b/tests/headers/objc_interface.h new file mode 100644 index 0000000000..af84bf925a --- /dev/null +++ b/tests/headers/objc_interface.h @@ -0,0 +1,8 @@ +// bindgen-flags: --objc-extern-crate -- -x objective-c +// bindgen-osx-only + +@interface Foo +@end + +@protocol bar +@end diff --git a/tests/headers/objc_interface_type.h b/tests/headers/objc_interface_type.h new file mode 100644 index 0000000000..31d33664c1 --- /dev/null +++ b/tests/headers/objc_interface_type.h @@ -0,0 +1,13 @@ +// bindgen-flags: --objc-extern-crate -- -x objective-c +// bindgen-osx-only + +@interface Foo +@end + +struct FooStruct { + Foo *foo; +}; + +void fooFunc(Foo *foo); + +static const Foo *kFoo; diff --git a/tests/headers/objc_method.h b/tests/headers/objc_method.h new file mode 100644 index 0000000000..91645f16d1 --- /dev/null +++ b/tests/headers/objc_method.h @@ -0,0 +1,7 @@ +// bindgen-flags: --objc-extern-crate -- -x objective-c +// bindgen-osx-only + +@interface Foo +- (void)method; +// TODO: argument methods +@end diff --git a/tests/tests.rs b/tests/tests.rs index 05c8ad2c73..2886f8db5b 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -76,7 +76,7 @@ fn create_bindgen_builder(header: &PathBuf) // Scoop up bindgen-flags from test header let mut flags = Vec::with_capacity(2); - for line in reader.lines().take(2) { + for line in reader.lines().take(3) { let line = try!(line); if line.contains("bindgen-flags: ") { let extra_flags = line.split("bindgen-flags: ") @@ -87,6 +87,12 @@ fn create_bindgen_builder(header: &PathBuf) } else if line.contains("bindgen-unstable") && cfg!(feature = "llvm_stable") { return Ok(None); + } else if line.contains("bindgen-osx-only") { + let prepend_flags = ["--raw-line", "#![cfg(target_os=\"macos\")]"]; + flags = prepend_flags.into_iter() + .map(ToString::to_string) + .chain(flags) + .collect(); } }