1 //! Objective C types 2 3 use super::context::{BindgenContext, ItemId}; 4 use super::function::FunctionSig; 5 use super::item::Item; 6 use super::traversal::{Trace, Tracer}; 7 use super::ty::TypeKind; 8 use crate::clang; 9 use clang_sys::CXChildVisit_Continue; 10 use clang_sys::CXCursor_ObjCCategoryDecl; 11 use clang_sys::CXCursor_ObjCClassMethodDecl; 12 use clang_sys::CXCursor_ObjCClassRef; 13 use clang_sys::CXCursor_ObjCInstanceMethodDecl; 14 use clang_sys::CXCursor_ObjCProtocolDecl; 15 use clang_sys::CXCursor_ObjCProtocolRef; 16 use clang_sys::CXCursor_ObjCSuperClassRef; 17 use clang_sys::CXCursor_TemplateTypeParameter; 18 use proc_macro2::{Ident, Span, TokenStream}; 19 20 /// Objective C interface as used in TypeKind 21 /// 22 /// Also protocols and categories are parsed as this type 23 #[derive(Debug)] 24 pub(crate) struct ObjCInterface { 25 /// The name 26 /// like, NSObject 27 name: String, 28 29 category: Option<String>, 30 31 is_protocol: bool, 32 33 /// The list of template names almost always, ObjectType or KeyType 34 pub(crate) template_names: Vec<String>, 35 36 /// The list of protocols that this interface conforms to. 37 pub(crate) conforms_to: Vec<ItemId>, 38 39 /// The direct parent for this interface. 40 pub(crate) parent_class: Option<ItemId>, 41 42 /// List of the methods defined in this interfae 43 methods: Vec<ObjCMethod>, 44 45 class_methods: Vec<ObjCMethod>, 46 } 47 48 /// The objective c methods 49 #[derive(Debug)] 50 pub(crate) struct ObjCMethod { 51 /// The original method selector name 52 /// like, dataWithBytes:length: 53 name: String, 54 55 /// Method name as converted to rust 56 /// like, dataWithBytes_length_ 57 rust_name: String, 58 59 signature: FunctionSig, 60 61 /// Is class method? 62 is_class_method: bool, 63 } 64 65 impl ObjCInterface { new(name: &str) -> ObjCInterface66 fn new(name: &str) -> ObjCInterface { 67 ObjCInterface { 68 name: name.to_owned(), 69 category: None, 70 is_protocol: false, 71 template_names: Vec::new(), 72 parent_class: None, 73 conforms_to: Vec::new(), 74 methods: Vec::new(), 75 class_methods: Vec::new(), 76 } 77 } 78 79 /// The name 80 /// like, NSObject name(&self) -> &str81 pub(crate) fn name(&self) -> &str { 82 self.name.as_ref() 83 } 84 85 /// Formats the name for rust 86 /// Can be like NSObject, but with categories might be like NSObject_NSCoderMethods 87 /// and protocols are like PNSObject rust_name(&self) -> String88 pub(crate) fn rust_name(&self) -> String { 89 if let Some(ref cat) = self.category { 90 format!("{}_{}", self.name(), cat) 91 } else if self.is_protocol { 92 format!("P{}", self.name()) 93 } else { 94 format!("I{}", self.name().to_owned()) 95 } 96 } 97 98 /// Is this a template interface? is_template(&self) -> bool99 pub(crate) fn is_template(&self) -> bool { 100 !self.template_names.is_empty() 101 } 102 103 /// List of the methods defined in this interface methods(&self) -> &Vec<ObjCMethod>104 pub(crate) fn methods(&self) -> &Vec<ObjCMethod> { 105 &self.methods 106 } 107 108 /// Is this a protocol? is_protocol(&self) -> bool109 pub(crate) fn is_protocol(&self) -> bool { 110 self.is_protocol 111 } 112 113 /// Is this a category? is_category(&self) -> bool114 pub(crate) fn is_category(&self) -> bool { 115 self.category.is_some() 116 } 117 118 /// List of the class methods defined in this interface class_methods(&self) -> &Vec<ObjCMethod>119 pub(crate) fn class_methods(&self) -> &Vec<ObjCMethod> { 120 &self.class_methods 121 } 122 123 /// Parses the Objective C interface from the cursor from_ty( cursor: &clang::Cursor, ctx: &mut BindgenContext, ) -> Option<Self>124 pub(crate) fn from_ty( 125 cursor: &clang::Cursor, 126 ctx: &mut BindgenContext, 127 ) -> Option<Self> { 128 let name = cursor.spelling(); 129 let mut interface = Self::new(&name); 130 131 if cursor.kind() == CXCursor_ObjCProtocolDecl { 132 interface.is_protocol = true; 133 } 134 135 cursor.visit(|c| { 136 match c.kind() { 137 CXCursor_ObjCClassRef => { 138 if cursor.kind() == CXCursor_ObjCCategoryDecl { 139 // We are actually a category extension, and we found the reference 140 // to the original interface, so name this interface approriately 141 interface.name = c.spelling(); 142 interface.category = Some(cursor.spelling()); 143 } 144 } 145 CXCursor_ObjCProtocolRef => { 146 // Gather protocols this interface conforms to 147 let needle = format!("P{}", c.spelling()); 148 let items_map = ctx.items(); 149 debug!( 150 "Interface {} conforms to {}, find the item", 151 interface.name, needle 152 ); 153 154 for (id, item) in items_map { 155 if let Some(ty) = item.as_type() { 156 if let TypeKind::ObjCInterface(ref protocol) = 157 *ty.kind() 158 { 159 if protocol.is_protocol { 160 debug!( 161 "Checking protocol {}, ty.name {:?}", 162 protocol.name, 163 ty.name() 164 ); 165 if Some(needle.as_ref()) == ty.name() { 166 debug!( 167 "Found conforming protocol {:?}", 168 item 169 ); 170 interface.conforms_to.push(id); 171 break; 172 } 173 } 174 } 175 } 176 } 177 } 178 CXCursor_ObjCInstanceMethodDecl | 179 CXCursor_ObjCClassMethodDecl => { 180 let name = c.spelling(); 181 let signature = 182 FunctionSig::from_ty(&c.cur_type(), &c, ctx) 183 .expect("Invalid function sig"); 184 let is_class_method = 185 c.kind() == CXCursor_ObjCClassMethodDecl; 186 let method = 187 ObjCMethod::new(&name, signature, is_class_method); 188 interface.add_method(method); 189 } 190 CXCursor_TemplateTypeParameter => { 191 let name = c.spelling(); 192 interface.template_names.push(name); 193 } 194 CXCursor_ObjCSuperClassRef => { 195 let item = Item::from_ty_or_ref(c.cur_type(), c, None, ctx); 196 interface.parent_class = Some(item.into()); 197 } 198 _ => {} 199 } 200 CXChildVisit_Continue 201 }); 202 Some(interface) 203 } 204 add_method(&mut self, method: ObjCMethod)205 fn add_method(&mut self, method: ObjCMethod) { 206 if method.is_class_method { 207 self.class_methods.push(method); 208 } else { 209 self.methods.push(method); 210 } 211 } 212 } 213 214 impl ObjCMethod { new( name: &str, signature: FunctionSig, is_class_method: bool, ) -> ObjCMethod215 fn new( 216 name: &str, 217 signature: FunctionSig, 218 is_class_method: bool, 219 ) -> ObjCMethod { 220 let split_name: Vec<&str> = name.split(':').collect(); 221 222 let rust_name = split_name.join("_"); 223 224 ObjCMethod { 225 name: name.to_owned(), 226 rust_name, 227 signature, 228 is_class_method, 229 } 230 } 231 232 /// Method name as converted to rust 233 /// like, dataWithBytes_length_ rust_name(&self) -> &str234 pub(crate) fn rust_name(&self) -> &str { 235 self.rust_name.as_ref() 236 } 237 238 /// Returns the methods signature as FunctionSig signature(&self) -> &FunctionSig239 pub(crate) fn signature(&self) -> &FunctionSig { 240 &self.signature 241 } 242 243 /// Is this a class method? is_class_method(&self) -> bool244 pub(crate) fn is_class_method(&self) -> bool { 245 self.is_class_method 246 } 247 248 /// Formats the method call format_method_call( &self, args: &[TokenStream], ) -> TokenStream249 pub(crate) fn format_method_call( 250 &self, 251 args: &[TokenStream], 252 ) -> TokenStream { 253 let split_name: Vec<Option<Ident>> = self 254 .name 255 .split(':') 256 .enumerate() 257 .map(|(idx, name)| { 258 if name.is_empty() { 259 None 260 } else if idx == 0 { 261 // Try to parse the method name as an identifier. Having a keyword is ok 262 // unless it is `crate`, `self`, `super` or `Self`, so we try to add the `_` 263 // suffix to it and parse it. 264 if ["crate", "self", "super", "Self"].contains(&name) { 265 Some(Ident::new( 266 &format!("{}_", name), 267 Span::call_site(), 268 )) 269 } else { 270 Some(Ident::new(name, Span::call_site())) 271 } 272 } else { 273 // Try to parse the current joining name as an identifier. This might fail if the name 274 // is a keyword, so we try to "r#" to it and parse again, this could also fail 275 // if the name is `crate`, `self`, `super` or `Self`, so we try to add the `_` 276 // suffix to it and parse again. If this also fails, we panic with the first 277 // error. 278 Some( 279 syn::parse_str::<Ident>(name) 280 .or_else(|err| { 281 syn::parse_str::<Ident>(&format!("r#{}", name)) 282 .map_err(|_| err) 283 }) 284 .or_else(|err| { 285 syn::parse_str::<Ident>(&format!("{}_", name)) 286 .map_err(|_| err) 287 }) 288 .expect("Invalid identifier"), 289 ) 290 } 291 }) 292 .collect(); 293 294 // No arguments 295 if args.is_empty() && split_name.len() == 1 { 296 let name = &split_name[0]; 297 return quote! { 298 #name 299 }; 300 } 301 302 // Check right amount of arguments 303 assert!( 304 args.len() == split_name.len() - 1, 305 "Incorrect method name or arguments for objc method, {:?} vs {:?}", 306 args, 307 split_name 308 ); 309 310 // Get arguments without type signatures to pass to `msg_send!` 311 let mut args_without_types = vec![]; 312 for arg in args.iter() { 313 let arg = arg.to_string(); 314 let name_and_sig: Vec<&str> = arg.split(' ').collect(); 315 let name = name_and_sig[0]; 316 args_without_types.push(Ident::new(name, Span::call_site())) 317 } 318 319 let args = split_name.into_iter().zip(args_without_types).map( 320 |(arg, arg_val)| { 321 if let Some(arg) = arg { 322 quote! { #arg: #arg_val } 323 } else { 324 quote! { #arg_val: #arg_val } 325 } 326 }, 327 ); 328 329 quote! { 330 #( #args )* 331 } 332 } 333 } 334 335 impl Trace for ObjCInterface { 336 type Extra = (); 337 trace<T>(&self, context: &BindgenContext, tracer: &mut T, _: &()) where T: Tracer,338 fn trace<T>(&self, context: &BindgenContext, tracer: &mut T, _: &()) 339 where 340 T: Tracer, 341 { 342 for method in &self.methods { 343 method.signature.trace(context, tracer, &()); 344 } 345 346 for class_method in &self.class_methods { 347 class_method.signature.trace(context, tracer, &()); 348 } 349 350 for protocol in &self.conforms_to { 351 tracer.visit(*protocol); 352 } 353 } 354 } 355