1 //! Objective C types 2 3 use super::context::{BindgenContext, ItemId}; 4 use super::function::FunctionSig; 5 use super::traversal::{Trace, Tracer}; 6 use super::ty::TypeKind; 7 use clang; 8 use clang_sys::CXChildVisit_Continue; 9 use clang_sys::CXCursor_ObjCCategoryDecl; 10 use clang_sys::CXCursor_ObjCClassMethodDecl; 11 use clang_sys::CXCursor_ObjCClassRef; 12 use clang_sys::CXCursor_ObjCInstanceMethodDecl; 13 use clang_sys::CXCursor_ObjCProtocolDecl; 14 use clang_sys::CXCursor_ObjCProtocolRef; 15 use proc_macro2::{Ident, Span, TokenStream}; 16 17 /// Objective C interface as used in TypeKind 18 /// 19 /// Also protocols and categories are parsed as this type 20 #[derive(Debug)] 21 pub struct ObjCInterface { 22 /// The name 23 /// like, NSObject 24 name: String, 25 26 category: Option<String>, 27 28 is_protocol: bool, 29 30 conforms_to: Vec<ItemId>, 31 32 /// List of the methods defined in this interfae 33 methods: Vec<ObjCMethod>, 34 35 class_methods: Vec<ObjCMethod>, 36 } 37 38 /// The objective c methods 39 #[derive(Debug)] 40 pub struct ObjCMethod { 41 /// The original method selector name 42 /// like, dataWithBytes:length: 43 name: String, 44 45 /// Method name as converted to rust 46 /// like, dataWithBytes_length_ 47 rust_name: String, 48 49 signature: FunctionSig, 50 51 /// Is class method? 52 is_class_method: bool, 53 } 54 55 impl ObjCInterface { new(name: &str) -> ObjCInterface56 fn new(name: &str) -> ObjCInterface { 57 ObjCInterface { 58 name: name.to_owned(), 59 category: None, 60 is_protocol: false, 61 conforms_to: Vec::new(), 62 methods: Vec::new(), 63 class_methods: Vec::new(), 64 } 65 } 66 67 /// The name 68 /// like, NSObject name(&self) -> &str69 pub fn name(&self) -> &str { 70 self.name.as_ref() 71 } 72 73 /// Formats the name for rust 74 /// Can be like NSObject, but with categories might be like NSObject_NSCoderMethods 75 /// and protocols are like protocol_NSObject rust_name(&self) -> String76 pub fn rust_name(&self) -> String { 77 if let Some(ref cat) = self.category { 78 format!("{}_{}", self.name(), cat) 79 } else { 80 if self.is_protocol { 81 format!("protocol_{}", self.name()) 82 } else { 83 self.name().to_owned() 84 } 85 } 86 } 87 88 /// List of the methods defined in this interface methods(&self) -> &Vec<ObjCMethod>89 pub fn methods(&self) -> &Vec<ObjCMethod> { 90 &self.methods 91 } 92 93 /// List of the class methods defined in this interface class_methods(&self) -> &Vec<ObjCMethod>94 pub fn class_methods(&self) -> &Vec<ObjCMethod> { 95 &self.class_methods 96 } 97 98 /// Parses the Objective C interface from the cursor from_ty( cursor: &clang::Cursor, ctx: &mut BindgenContext, ) -> Option<Self>99 pub fn from_ty( 100 cursor: &clang::Cursor, 101 ctx: &mut BindgenContext, 102 ) -> Option<Self> { 103 let name = cursor.spelling(); 104 let mut interface = Self::new(&name); 105 106 if cursor.kind() == CXCursor_ObjCProtocolDecl { 107 interface.is_protocol = true; 108 } 109 110 cursor.visit(|c| { 111 match c.kind() { 112 CXCursor_ObjCClassRef => { 113 if cursor.kind() == CXCursor_ObjCCategoryDecl { 114 // We are actually a category extension, and we found the reference 115 // to the original interface, so name this interface approriately 116 interface.name = c.spelling(); 117 interface.category = Some(cursor.spelling()); 118 } 119 } 120 CXCursor_ObjCProtocolRef => { 121 // Gather protocols this interface conforms to 122 let needle = format!("protocol_{}", c.spelling()); 123 let items_map = ctx.items(); 124 debug!("Interface {} conforms to {}, find the item", interface.name, needle); 125 126 for (id, item) in items_map 127 { 128 if let Some(ty) = item.as_type() { 129 match *ty.kind() { 130 TypeKind::ObjCInterface(ref protocol) => { 131 if protocol.is_protocol 132 { 133 debug!("Checking protocol {}, ty.name {:?}", protocol.name, ty.name()); 134 if Some(needle.as_ref()) == ty.name() { 135 debug!("Found conforming protocol {:?}", item); 136 interface.conforms_to.push(id); 137 break; 138 } 139 } 140 } 141 _ => {} 142 } 143 } 144 } 145 146 } 147 CXCursor_ObjCInstanceMethodDecl | 148 CXCursor_ObjCClassMethodDecl => { 149 let name = c.spelling(); 150 let signature = 151 FunctionSig::from_ty(&c.cur_type(), &c, ctx) 152 .expect("Invalid function sig"); 153 let is_class_method = c.kind() == CXCursor_ObjCClassMethodDecl; 154 let method = ObjCMethod::new(&name, signature, is_class_method); 155 interface.add_method(method); 156 } 157 _ => {} 158 } 159 CXChildVisit_Continue 160 }); 161 Some(interface) 162 } 163 add_method(&mut self, method: ObjCMethod)164 fn add_method(&mut self, method: ObjCMethod) { 165 if method.is_class_method { 166 self.class_methods.push(method); 167 } else { 168 self.methods.push(method); 169 } 170 } 171 } 172 173 impl ObjCMethod { new( name: &str, signature: FunctionSig, is_class_method: bool, ) -> ObjCMethod174 fn new( 175 name: &str, 176 signature: FunctionSig, 177 is_class_method: bool, 178 ) -> ObjCMethod { 179 let split_name: Vec<&str> = name.split(':').collect(); 180 181 let rust_name = split_name.join("_"); 182 183 ObjCMethod { 184 name: name.to_owned(), 185 rust_name: rust_name.to_owned(), 186 signature: signature, 187 is_class_method: is_class_method, 188 } 189 } 190 191 /// The original method selector name 192 /// like, dataWithBytes:length: name(&self) -> &str193 pub fn name(&self) -> &str { 194 self.name.as_ref() 195 } 196 197 /// Method name as converted to rust 198 /// like, dataWithBytes_length_ rust_name(&self) -> &str199 pub fn rust_name(&self) -> &str { 200 self.rust_name.as_ref() 201 } 202 203 /// Returns the methods signature as FunctionSig signature(&self) -> &FunctionSig204 pub fn signature(&self) -> &FunctionSig { 205 &self.signature 206 } 207 208 /// Is this a class method? is_class_method(&self) -> bool209 pub fn is_class_method(&self) -> bool { 210 self.is_class_method 211 } 212 213 /// Formats the method call format_method_call(&self, args: &[TokenStream]) -> TokenStream214 pub fn format_method_call(&self, args: &[TokenStream]) -> TokenStream { 215 let split_name: Vec<_> = self 216 .name 217 .split(':') 218 .filter(|p| !p.is_empty()) 219 .map(|name| Ident::new(name, Span::call_site())) 220 .collect(); 221 222 // No arguments 223 if args.len() == 0 && split_name.len() == 1 { 224 let name = &split_name[0]; 225 return quote! { 226 #name 227 }; 228 } 229 230 // Check right amount of arguments 231 if args.len() != split_name.len() { 232 panic!( 233 "Incorrect method name or arguments for objc method, {:?} vs {:?}", 234 args, 235 split_name 236 ); 237 } 238 239 // Get arguments without type signatures to pass to `msg_send!` 240 let mut args_without_types = vec![]; 241 for arg in args.iter() { 242 let arg = arg.to_string(); 243 let name_and_sig: Vec<&str> = arg.split(' ').collect(); 244 let name = name_and_sig[0]; 245 args_without_types.push(Ident::new(name, Span::call_site())) 246 } 247 248 let args = split_name 249 .into_iter() 250 .zip(args_without_types) 251 .map(|(arg, arg_val)| quote! { #arg : #arg_val }); 252 253 quote! { 254 #( #args )* 255 } 256 } 257 } 258 259 impl Trace for ObjCInterface { 260 type Extra = (); 261 trace<T>(&self, context: &BindgenContext, tracer: &mut T, _: &()) where T: Tracer,262 fn trace<T>(&self, context: &BindgenContext, tracer: &mut T, _: &()) 263 where 264 T: Tracer, 265 { 266 for method in &self.methods { 267 method.signature.trace(context, tracer, &()); 268 } 269 270 for class_method in &self.class_methods { 271 class_method.signature.trace(context, tracer, &()); 272 } 273 274 for protocol in &self.conforms_to { 275 tracer.visit(*protocol); 276 } 277 } 278 } 279