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 { 93 if self.is_protocol { 94 format!("P{}", self.name()) 95 } else { 96 format!("I{}", self.name().to_owned()) 97 } 98 } 99 } 100 101 /// Is this a template interface? is_template(&self) -> bool102 pub fn is_template(&self) -> bool { 103 !self.template_names.is_empty() 104 } 105 106 /// List of the methods defined in this interface methods(&self) -> &Vec<ObjCMethod>107 pub fn methods(&self) -> &Vec<ObjCMethod> { 108 &self.methods 109 } 110 111 /// Is this a protocol? is_protocol(&self) -> bool112 pub fn is_protocol(&self) -> bool { 113 self.is_protocol 114 } 115 116 /// Is this a category? is_category(&self) -> bool117 pub fn is_category(&self) -> bool { 118 self.category.is_some() 119 } 120 121 /// List of the class methods defined in this interface class_methods(&self) -> &Vec<ObjCMethod>122 pub fn class_methods(&self) -> &Vec<ObjCMethod> { 123 &self.class_methods 124 } 125 126 /// Parses the Objective C interface from the cursor from_ty( cursor: &clang::Cursor, ctx: &mut BindgenContext, ) -> Option<Self>127 pub fn from_ty( 128 cursor: &clang::Cursor, 129 ctx: &mut BindgenContext, 130 ) -> Option<Self> { 131 let name = cursor.spelling(); 132 let mut interface = Self::new(&name); 133 134 if cursor.kind() == CXCursor_ObjCProtocolDecl { 135 interface.is_protocol = true; 136 } 137 138 cursor.visit(|c| { 139 match c.kind() { 140 CXCursor_ObjCClassRef => { 141 if cursor.kind() == CXCursor_ObjCCategoryDecl { 142 // We are actually a category extension, and we found the reference 143 // to the original interface, so name this interface approriately 144 interface.name = c.spelling(); 145 interface.category = Some(cursor.spelling()); 146 } 147 } 148 CXCursor_ObjCProtocolRef => { 149 // Gather protocols this interface conforms to 150 let needle = format!("P{}", c.spelling()); 151 let items_map = ctx.items(); 152 debug!("Interface {} conforms to {}, find the item", interface.name, needle); 153 154 for (id, item) in items_map 155 { 156 if let Some(ty) = item.as_type() { 157 match *ty.kind() { 158 TypeKind::ObjCInterface(ref protocol) => { 159 if protocol.is_protocol 160 { 161 debug!("Checking protocol {}, ty.name {:?}", protocol.name, ty.name()); 162 if Some(needle.as_ref()) == ty.name() { 163 debug!("Found conforming protocol {:?}", item); 164 interface.conforms_to.push(id); 165 break; 166 } 167 } 168 } 169 _ => {} 170 } 171 } 172 } 173 174 } 175 CXCursor_ObjCInstanceMethodDecl | 176 CXCursor_ObjCClassMethodDecl => { 177 let name = c.spelling(); 178 let signature = 179 FunctionSig::from_ty(&c.cur_type(), &c, ctx) 180 .expect("Invalid function sig"); 181 let is_class_method = c.kind() == CXCursor_ObjCClassMethodDecl; 182 let method = ObjCMethod::new(&name, signature, is_class_method); 183 interface.add_method(method); 184 } 185 CXCursor_TemplateTypeParameter => { 186 let name = c.spelling(); 187 interface.template_names.push(name); 188 } 189 CXCursor_ObjCSuperClassRef => { 190 let item = Item::from_ty_or_ref(c.cur_type(), c, None, ctx); 191 interface.parent_class = Some(item.into()); 192 }, 193 _ => {} 194 } 195 CXChildVisit_Continue 196 }); 197 Some(interface) 198 } 199 add_method(&mut self, method: ObjCMethod)200 fn add_method(&mut self, method: ObjCMethod) { 201 if method.is_class_method { 202 self.class_methods.push(method); 203 } else { 204 self.methods.push(method); 205 } 206 } 207 } 208 209 impl ObjCMethod { new( name: &str, signature: FunctionSig, is_class_method: bool, ) -> ObjCMethod210 fn new( 211 name: &str, 212 signature: FunctionSig, 213 is_class_method: bool, 214 ) -> ObjCMethod { 215 let split_name: Vec<&str> = name.split(':').collect(); 216 217 let rust_name = split_name.join("_"); 218 219 ObjCMethod { 220 name: name.to_owned(), 221 rust_name: rust_name.to_owned(), 222 signature, 223 is_class_method, 224 } 225 } 226 227 /// The original method selector name 228 /// like, dataWithBytes:length: name(&self) -> &str229 pub fn name(&self) -> &str { 230 self.name.as_ref() 231 } 232 233 /// Method name as converted to rust 234 /// like, dataWithBytes_length_ rust_name(&self) -> &str235 pub fn rust_name(&self) -> &str { 236 self.rust_name.as_ref() 237 } 238 239 /// Returns the methods signature as FunctionSig signature(&self) -> &FunctionSig240 pub fn signature(&self) -> &FunctionSig { 241 &self.signature 242 } 243 244 /// Is this a class method? is_class_method(&self) -> bool245 pub fn is_class_method(&self) -> bool { 246 self.is_class_method 247 } 248 249 /// Formats the method call format_method_call(&self, args: &[TokenStream]) -> TokenStream250 pub fn format_method_call(&self, args: &[TokenStream]) -> TokenStream { 251 let split_name: Vec<Option<Ident>> = self 252 .name 253 .split(':') 254 .map(|name| { 255 if name.is_empty() { 256 None 257 } else { 258 Some(Ident::new(name, Span::call_site())) 259 } 260 }) 261 .collect(); 262 263 // No arguments 264 if args.len() == 0 && split_name.len() == 1 { 265 let name = &split_name[0]; 266 return quote! { 267 #name 268 }; 269 } 270 271 // Check right amount of arguments 272 if args.len() != split_name.len() - 1 { 273 panic!( 274 "Incorrect method name or arguments for objc method, {:?} vs {:?}", 275 args, 276 split_name, 277 ); 278 } 279 280 // Get arguments without type signatures to pass to `msg_send!` 281 let mut args_without_types = vec![]; 282 for arg in args.iter() { 283 let arg = arg.to_string(); 284 let name_and_sig: Vec<&str> = arg.split(' ').collect(); 285 let name = name_and_sig[0]; 286 args_without_types.push(Ident::new(name, Span::call_site())) 287 } 288 289 let args = split_name.into_iter().zip(args_without_types).map( 290 |(arg, arg_val)| { 291 if let Some(arg) = arg { 292 quote! { #arg: #arg_val } 293 } else { 294 quote! { #arg_val: #arg_val } 295 } 296 }, 297 ); 298 299 quote! { 300 #( #args )* 301 } 302 } 303 } 304 305 impl Trace for ObjCInterface { 306 type Extra = (); 307 trace<T>(&self, context: &BindgenContext, tracer: &mut T, _: &()) where T: Tracer,308 fn trace<T>(&self, context: &BindgenContext, tracer: &mut T, _: &()) 309 where 310 T: Tracer, 311 { 312 for method in &self.methods { 313 method.signature.trace(context, tracer, &()); 314 } 315 316 for class_method in &self.class_methods { 317 class_method.signature.trace(context, tracer, &()); 318 } 319 320 for protocol in &self.conforms_to { 321 tracer.visit(*protocol); 322 } 323 } 324 } 325