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