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 { |
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 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 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 fn is_template(&self) -> bool { |
100 | !self.template_names.is_empty() |
101 | } |
102 | |
103 | /// List of the methods defined in this interface |
104 | pub fn methods(&self) -> &Vec<ObjCMethod> { |
105 | &self.methods |
106 | } |
107 | |
108 | /// Is this a protocol? |
109 | pub fn is_protocol(&self) -> bool { |
110 | self.is_protocol |
111 | } |
112 | |
113 | /// Is this a category? |
114 | pub fn is_category(&self) -> bool { |
115 | self.category.is_some() |
116 | } |
117 | |
118 | /// List of the class methods defined in this interface |
119 | pub fn class_methods(&self) -> &Vec<ObjCMethod> { |
120 | &self.class_methods |
121 | } |
122 | |
123 | /// Parses the Objective C interface from the cursor |
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 | |
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 | /// The original method selector name |
233 | /// like, dataWithBytes:length: |
234 | pub fn name(&self) -> &str { |
235 | self.name.as_ref() |
236 | } |
237 | |
238 | /// Method name as converted to rust |
239 | /// like, dataWithBytes_length_ |
240 | pub fn rust_name(&self) -> &str { |
241 | self.rust_name.as_ref() |
242 | } |
243 | |
244 | /// Returns the methods signature as FunctionSig |
245 | pub fn signature(&self) -> &FunctionSig { |
246 | &self.signature |
247 | } |
248 | |
249 | /// Is this a class method? |
250 | pub fn is_class_method(&self) -> bool { |
251 | self.is_class_method |
252 | } |
253 | |
254 | /// Formats the method call |
255 | 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 | |
322 | fn trace<T>(&self, context: &BindgenContext, tracer: &mut T, _: &()) |
323 | where |
324 | T: Tracer, |
325 | { |
326 | for method: &ObjCMethod in &self.methods { |
327 | method.signature.trace(context, tracer, &()); |
328 | } |
329 | |
330 | for class_method: &ObjCMethod in &self.class_methods { |
331 | class_method.signature.trace(context, tracer, &()); |
332 | } |
333 | |
334 | for protocol: &ItemId in &self.conforms_to { |
335 | tracer.visit(*protocol); |
336 | } |
337 | } |
338 | } |
339 | |