• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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