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