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 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 template_names: Vec<String>, 35 36 /// The list of protocols that this interface conforms to. 37 pub conforms_to: Vec<ItemId>, 38 39 /// The direct parent for this interface. 40 pub 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 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 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 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 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 fn methods(&self) -> &Vec<ObjCMethod> { 105 &self.methods 106 } 107 108 /// Is this a protocol? is_protocol(&self) -> bool109 pub fn is_protocol(&self) -> bool { 110 self.is_protocol 111 } 112 113 /// Is this a category? is_category(&self) -> bool114 pub 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 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 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 /// The original method selector name 233 /// like, dataWithBytes:length: name(&self) -> &str234 pub fn name(&self) -> &str { 235 self.name.as_ref() 236 } 237 238 /// Method name as converted to rust 239 /// like, dataWithBytes_length_ rust_name(&self) -> &str240 pub fn rust_name(&self) -> &str { 241 self.rust_name.as_ref() 242 } 243 244 /// Returns the methods signature as FunctionSig signature(&self) -> &FunctionSig245 pub fn signature(&self) -> &FunctionSig { 246 &self.signature 247 } 248 249 /// Is this a class method? is_class_method(&self) -> bool250 pub fn is_class_method(&self) -> bool { 251 self.is_class_method 252 } 253 254 /// Formats the method call format_method_call(&self, args: &[TokenStream]) -> TokenStream255 pub fn format_method_call(&self, args: &[TokenStream]) -> TokenStream { 256 let split_name: Vec<Option<Ident>> = self 257 .name 258 .split(':') 259 .map(|name| { 260 if name.is_empty() { 261 None 262 } else { 263 // Try to parse the current name as an identifier. This might fail if the 264 // name is a keyword so we try to prepend "r#" to it and parse again. If 265 // this also fails, we panic with the first error. 266 Some( 267 syn::parse_str::<Ident>(name) 268 .or_else(|err| { 269 syn::parse_str::<Ident>(&format!("r#{}", name)) 270 .map_err(|_| err) 271 }) 272 .expect("Invalid identifier"), 273 ) 274 } 275 }) 276 .collect(); 277 278 // No arguments 279 if args.is_empty() && split_name.len() == 1 { 280 let name = &split_name[0]; 281 return quote! { 282 #name 283 }; 284 } 285 286 // Check right amount of arguments 287 assert!( 288 args.len() == split_name.len() - 1, 289 "Incorrect method name or arguments for objc method, {:?} vs {:?}", 290 args, 291 split_name 292 ); 293 294 // Get arguments without type signatures to pass to `msg_send!` 295 let mut args_without_types = vec![]; 296 for arg in args.iter() { 297 let arg = arg.to_string(); 298 let name_and_sig: Vec<&str> = arg.split(' ').collect(); 299 let name = name_and_sig[0]; 300 args_without_types.push(Ident::new(name, Span::call_site())) 301 } 302 303 let args = split_name.into_iter().zip(args_without_types).map( 304 |(arg, arg_val)| { 305 if let Some(arg) = arg { 306 quote! { #arg: #arg_val } 307 } else { 308 quote! { #arg_val: #arg_val } 309 } 310 }, 311 ); 312 313 quote! { 314 #( #args )* 315 } 316 } 317 } 318 319 impl Trace for ObjCInterface { 320 type Extra = (); 321 trace<T>(&self, context: &BindgenContext, tracer: &mut T, _: &()) where T: Tracer,322 fn trace<T>(&self, context: &BindgenContext, tracer: &mut T, _: &()) 323 where 324 T: Tracer, 325 { 326 for method in &self.methods { 327 method.signature.trace(context, tracer, &()); 328 } 329 330 for class_method in &self.class_methods { 331 class_method.signature.trace(context, tracer, &()); 332 } 333 334 for protocol in &self.conforms_to { 335 tracer.visit(*protocol); 336 } 337 } 338 } 339