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(crate) 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(crate) template_names: Vec<String>, |
35 | |
36 | /// The list of protocols that this interface conforms to. |
37 | pub(crate) conforms_to: Vec<ItemId>, |
38 | |
39 | /// The direct parent for this interface. |
40 | pub(crate) 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(crate) 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 { |
66 | 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 |
81 | pub(crate) 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 |
88 | pub(crate) 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? |
99 | pub(crate) fn is_template(&self) -> bool { |
100 | !self.template_names.is_empty() |
101 | } |
102 | |
103 | /// List of the methods defined in this interface |
104 | pub(crate) fn methods(&self) -> &Vec<ObjCMethod> { |
105 | &self.methods |
106 | } |
107 | |
108 | /// Is this a protocol? |
109 | pub(crate) fn is_protocol(&self) -> bool { |
110 | self.is_protocol |
111 | } |
112 | |
113 | /// Is this a category? |
114 | pub(crate) fn is_category(&self) -> bool { |
115 | self.category.is_some() |
116 | } |
117 | |
118 | /// List of the class methods defined in this interface |
119 | pub(crate) fn class_methods(&self) -> &Vec<ObjCMethod> { |
120 | &self.class_methods |
121 | } |
122 | |
123 | /// Parses the Objective C interface from the cursor |
124 | pub(crate) 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 | |
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 { |
215 | 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 | /// Method name as converted to rust |
233 | /// like, dataWithBytes_length_ |
234 | pub(crate) fn rust_name(&self) -> &str { |
235 | self.rust_name.as_ref() |
236 | } |
237 | |
238 | /// Returns the methods signature as FunctionSig |
239 | pub(crate) fn signature(&self) -> &FunctionSig { |
240 | &self.signature |
241 | } |
242 | |
243 | /// Is this a class method? |
244 | pub(crate) fn is_class_method(&self) -> bool { |
245 | self.is_class_method |
246 | } |
247 | |
248 | /// Formats the method call |
249 | pub(crate) fn format_method_call( |
250 | &self, |
251 | args: &[TokenStream], |
252 | ) -> TokenStream { |
253 | let split_name: Vec<Option<Ident>> = self |
254 | .name |
255 | .split(':' ) |
256 | .enumerate() |
257 | .map(|(idx, name)| { |
258 | if name.is_empty() { |
259 | None |
260 | } else if idx == 0 { |
261 | // Try to parse the method name as an identifier. Having a keyword is ok |
262 | // unless it is `crate`, `self`, `super` or `Self`, so we try to add the `_` |
263 | // suffix to it and parse it. |
264 | if ["crate" , "self" , "super" , "Self" ].contains(&name) { |
265 | Some(Ident::new( |
266 | &format!(" {}_" , name), |
267 | Span::call_site(), |
268 | )) |
269 | } else { |
270 | Some(Ident::new(name, Span::call_site())) |
271 | } |
272 | } else { |
273 | // Try to parse the current joining name as an identifier. This might fail if the name |
274 | // is a keyword, so we try to "r#" to it and parse again, this could also fail |
275 | // if the name is `crate`, `self`, `super` or `Self`, so we try to add the `_` |
276 | // suffix to it and parse again. If this also fails, we panic with the first |
277 | // error. |
278 | Some( |
279 | syn::parse_str::<Ident>(name) |
280 | .or_else(|err| { |
281 | syn::parse_str::<Ident>(&format!("r# {}" , name)) |
282 | .map_err(|_| err) |
283 | }) |
284 | .or_else(|err| { |
285 | syn::parse_str::<Ident>(&format!(" {}_" , name)) |
286 | .map_err(|_| err) |
287 | }) |
288 | .expect("Invalid identifier" ), |
289 | ) |
290 | } |
291 | }) |
292 | .collect(); |
293 | |
294 | // No arguments |
295 | if args.is_empty() && split_name.len() == 1 { |
296 | let name = &split_name[0]; |
297 | return quote! { |
298 | #name |
299 | }; |
300 | } |
301 | |
302 | // Check right amount of arguments |
303 | assert!( |
304 | args.len() == split_name.len() - 1, |
305 | "Incorrect method name or arguments for objc method, {:?} vs {:?}" , |
306 | args, |
307 | split_name |
308 | ); |
309 | |
310 | // Get arguments without type signatures to pass to `msg_send!` |
311 | let mut args_without_types = vec![]; |
312 | for arg in args.iter() { |
313 | let arg = arg.to_string(); |
314 | let name_and_sig: Vec<&str> = arg.split(' ' ).collect(); |
315 | let name = name_and_sig[0]; |
316 | args_without_types.push(Ident::new(name, Span::call_site())) |
317 | } |
318 | |
319 | let args = split_name.into_iter().zip(args_without_types).map( |
320 | |(arg, arg_val)| { |
321 | if let Some(arg) = arg { |
322 | quote! { #arg: #arg_val } |
323 | } else { |
324 | quote! { #arg_val: #arg_val } |
325 | } |
326 | }, |
327 | ); |
328 | |
329 | quote! { |
330 | #( #args )* |
331 | } |
332 | } |
333 | } |
334 | |
335 | impl Trace for ObjCInterface { |
336 | type Extra = (); |
337 | |
338 | fn trace<T>(&self, context: &BindgenContext, tracer: &mut T, _: &()) |
339 | where |
340 | T: Tracer, |
341 | { |
342 | for method: &ObjCMethod in &self.methods { |
343 | method.signature.trace(context, tracer, &()); |
344 | } |
345 | |
346 | for class_method: &ObjCMethod in &self.class_methods { |
347 | class_method.signature.trace(context, tracer, &()); |
348 | } |
349 | |
350 | for protocol: &ItemId in &self.conforms_to { |
351 | tracer.visit(*protocol); |
352 | } |
353 | } |
354 | } |
355 | |