• 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 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