1 //! Objective C types
2 
3 use super::context::{BindgenContext, ItemId};
4 use super::function::FunctionSig;
5 use super::traversal::{Trace, Tracer};
6 use super::ty::TypeKind;
7 use clang;
8 use clang_sys::CXChildVisit_Continue;
9 use clang_sys::CXCursor_ObjCCategoryDecl;
10 use clang_sys::CXCursor_ObjCClassMethodDecl;
11 use clang_sys::CXCursor_ObjCClassRef;
12 use clang_sys::CXCursor_ObjCInstanceMethodDecl;
13 use clang_sys::CXCursor_ObjCProtocolDecl;
14 use clang_sys::CXCursor_ObjCProtocolRef;
15 use proc_macro2::{Ident, Span, TokenStream};
16 
17 /// Objective C interface as used in TypeKind
18 ///
19 /// Also protocols and categories are parsed as this type
20 #[derive(Debug)]
21 pub struct ObjCInterface {
22     /// The name
23     /// like, NSObject
24     name: String,
25 
26     category: Option<String>,
27 
28     is_protocol: bool,
29 
30     conforms_to: Vec<ItemId>,
31 
32     /// List of the methods defined in this interfae
33     methods: Vec<ObjCMethod>,
34 
35     class_methods: Vec<ObjCMethod>,
36 }
37 
38 /// The objective c methods
39 #[derive(Debug)]
40 pub struct ObjCMethod {
41     /// The original method selector name
42     /// like, dataWithBytes:length:
43     name: String,
44 
45     /// Method name as converted to rust
46     /// like, dataWithBytes_length_
47     rust_name: String,
48 
49     signature: FunctionSig,
50 
51     /// Is class method?
52     is_class_method: bool,
53 }
54 
55 impl ObjCInterface {
new(name: &str) -> ObjCInterface56     fn new(name: &str) -> ObjCInterface {
57         ObjCInterface {
58             name: name.to_owned(),
59             category: None,
60             is_protocol: false,
61             conforms_to: Vec::new(),
62             methods: Vec::new(),
63             class_methods: Vec::new(),
64         }
65     }
66 
67     /// The name
68     /// like, NSObject
name(&self) -> &str69     pub fn name(&self) -> &str {
70         self.name.as_ref()
71     }
72 
73     /// Formats the name for rust
74     /// Can be like NSObject, but with categories might be like NSObject_NSCoderMethods
75     /// and protocols are like protocol_NSObject
rust_name(&self) -> String76     pub fn rust_name(&self) -> String {
77         if let Some(ref cat) = self.category {
78             format!("{}_{}", self.name(), cat)
79         } else {
80             if self.is_protocol {
81                 format!("protocol_{}", self.name())
82             } else {
83                 self.name().to_owned()
84             }
85         }
86     }
87 
88     /// List of the methods defined in this interface
methods(&self) -> &Vec<ObjCMethod>89     pub fn methods(&self) -> &Vec<ObjCMethod> {
90         &self.methods
91     }
92 
93     /// List of the class methods defined in this interface
class_methods(&self) -> &Vec<ObjCMethod>94     pub fn class_methods(&self) -> &Vec<ObjCMethod> {
95         &self.class_methods
96     }
97 
98     /// Parses the Objective C interface from the cursor
from_ty( cursor: &clang::Cursor, ctx: &mut BindgenContext, ) -> Option<Self>99     pub fn from_ty(
100         cursor: &clang::Cursor,
101         ctx: &mut BindgenContext,
102     ) -> Option<Self> {
103         let name = cursor.spelling();
104         let mut interface = Self::new(&name);
105 
106         if cursor.kind() == CXCursor_ObjCProtocolDecl {
107             interface.is_protocol = true;
108         }
109 
110         cursor.visit(|c| {
111             match c.kind() {
112                 CXCursor_ObjCClassRef => {
113                     if cursor.kind() == CXCursor_ObjCCategoryDecl {
114                         // We are actually a category extension, and we found the reference
115                         // to the original interface, so name this interface approriately
116                         interface.name = c.spelling();
117                         interface.category = Some(cursor.spelling());
118                     }
119                 }
120                 CXCursor_ObjCProtocolRef => {
121                     // Gather protocols this interface conforms to
122                     let needle = format!("protocol_{}", c.spelling());
123                     let items_map = ctx.items();
124                     debug!("Interface {} conforms to {}, find the item", interface.name, needle);
125 
126                     for (id, item) in items_map
127                     {
128                        if let Some(ty) = item.as_type() {
129                             match *ty.kind() {
130                                 TypeKind::ObjCInterface(ref protocol) => {
131                                     if protocol.is_protocol
132                                     {
133                                         debug!("Checking protocol {}, ty.name {:?}", protocol.name, ty.name());
134                                         if Some(needle.as_ref()) == ty.name() {
135                                             debug!("Found conforming protocol {:?}", item);
136                                             interface.conforms_to.push(id);
137                                             break;
138                                         }
139                                     }
140                                 }
141                                 _ => {}
142                             }
143                         }
144                     }
145 
146                 }
147                 CXCursor_ObjCInstanceMethodDecl |
148                 CXCursor_ObjCClassMethodDecl => {
149                     let name = c.spelling();
150                     let signature =
151                         FunctionSig::from_ty(&c.cur_type(), &c, ctx)
152                             .expect("Invalid function sig");
153                     let is_class_method = c.kind() == CXCursor_ObjCClassMethodDecl;
154                     let method = ObjCMethod::new(&name, signature, is_class_method);
155                     interface.add_method(method);
156                 }
157                 _ => {}
158             }
159             CXChildVisit_Continue
160         });
161         Some(interface)
162     }
163 
add_method(&mut self, method: ObjCMethod)164     fn add_method(&mut self, method: ObjCMethod) {
165         if method.is_class_method {
166             self.class_methods.push(method);
167         } else {
168             self.methods.push(method);
169         }
170     }
171 }
172 
173 impl ObjCMethod {
new( name: &str, signature: FunctionSig, is_class_method: bool, ) -> ObjCMethod174     fn new(
175         name: &str,
176         signature: FunctionSig,
177         is_class_method: bool,
178     ) -> ObjCMethod {
179         let split_name: Vec<&str> = name.split(':').collect();
180 
181         let rust_name = split_name.join("_");
182 
183         ObjCMethod {
184             name: name.to_owned(),
185             rust_name: rust_name.to_owned(),
186             signature: signature,
187             is_class_method: is_class_method,
188         }
189     }
190 
191     /// The original method selector name
192     /// like, dataWithBytes:length:
name(&self) -> &str193     pub fn name(&self) -> &str {
194         self.name.as_ref()
195     }
196 
197     /// Method name as converted to rust
198     /// like, dataWithBytes_length_
rust_name(&self) -> &str199     pub fn rust_name(&self) -> &str {
200         self.rust_name.as_ref()
201     }
202 
203     /// Returns the methods signature as FunctionSig
signature(&self) -> &FunctionSig204     pub fn signature(&self) -> &FunctionSig {
205         &self.signature
206     }
207 
208     /// Is this a class method?
is_class_method(&self) -> bool209     pub fn is_class_method(&self) -> bool {
210         self.is_class_method
211     }
212 
213     /// Formats the method call
format_method_call(&self, args: &[TokenStream]) -> TokenStream214     pub fn format_method_call(&self, args: &[TokenStream]) -> TokenStream {
215         let split_name: Vec<_> = self
216             .name
217             .split(':')
218             .filter(|p| !p.is_empty())
219             .map(|name| Ident::new(name, Span::call_site()))
220             .collect();
221 
222         // No arguments
223         if args.len() == 0 && split_name.len() == 1 {
224             let name = &split_name[0];
225             return quote! {
226                 #name
227             };
228         }
229 
230         // Check right amount of arguments
231         if args.len() != split_name.len() {
232             panic!(
233                 "Incorrect method name or arguments for objc method, {:?} vs {:?}",
234                 args,
235                 split_name
236             );
237         }
238 
239         // Get arguments without type signatures to pass to `msg_send!`
240         let mut args_without_types = vec![];
241         for arg in args.iter() {
242             let arg = arg.to_string();
243             let name_and_sig: Vec<&str> = arg.split(' ').collect();
244             let name = name_and_sig[0];
245             args_without_types.push(Ident::new(name, Span::call_site()))
246         }
247 
248         let args = split_name
249             .into_iter()
250             .zip(args_without_types)
251             .map(|(arg, arg_val)| quote! { #arg : #arg_val });
252 
253         quote! {
254             #( #args )*
255         }
256     }
257 }
258 
259 impl Trace for ObjCInterface {
260     type Extra = ();
261 
trace<T>(&self, context: &BindgenContext, tracer: &mut T, _: &()) where T: Tracer,262     fn trace<T>(&self, context: &BindgenContext, tracer: &mut T, _: &())
263     where
264         T: Tracer,
265     {
266         for method in &self.methods {
267             method.signature.trace(context, tracer, &());
268         }
269 
270         for class_method in &self.class_methods {
271             class_method.signature.trace(context, tracer, &());
272         }
273 
274         for protocol in &self.conforms_to {
275             tracer.visit(*protocol);
276         }
277     }
278 }
279