1//! Objective C types
2
3use super::context::{BindgenContext, ItemId};
4use super::function::FunctionSig;
5use super::item::Item;
6use super::traversal::{Trace, Tracer};
7use super::ty::TypeKind;
8use crate::clang;
9use clang_sys::CXChildVisit_Continue;
10use clang_sys::CXCursor_ObjCCategoryDecl;
11use clang_sys::CXCursor_ObjCClassMethodDecl;
12use clang_sys::CXCursor_ObjCClassRef;
13use clang_sys::CXCursor_ObjCInstanceMethodDecl;
14use clang_sys::CXCursor_ObjCProtocolDecl;
15use clang_sys::CXCursor_ObjCProtocolRef;
16use clang_sys::CXCursor_ObjCSuperClassRef;
17use clang_sys::CXCursor_TemplateTypeParameter;
18use 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)]
24pub(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)]
50pub(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
65impl 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
214impl 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
335impl 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