bindgen/ir/
objc.rs
1use 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#[derive(Debug)]
24pub(crate) struct ObjCInterface {
25 name: String,
28
29 category: Option<String>,
30
31 is_protocol: bool,
32
33 pub(crate) template_names: Vec<String>,
35
36 pub(crate) conforms_to: Vec<ItemId>,
38
39 pub(crate) parent_class: Option<ItemId>,
41
42 methods: Vec<ObjCMethod>,
44
45 class_methods: Vec<ObjCMethod>,
46}
47
48#[derive(Debug)]
50pub(crate) struct ObjCMethod {
51 name: String,
54
55 rust_name: String,
58
59 signature: FunctionSig,
60
61 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 pub(crate) fn name(&self) -> &str {
82 self.name.as_ref()
83 }
84
85 pub(crate) fn rust_name(&self) -> String {
89 if let Some(ref cat) = self.category {
90 format!("{}_{cat}", self.name())
91 } else if self.is_protocol {
92 format!("P{}", self.name())
93 } else {
94 format!("I{}", self.name().to_owned())
95 }
96 }
97
98 pub(crate) fn is_template(&self) -> bool {
100 !self.template_names.is_empty()
101 }
102
103 pub(crate) fn methods(&self) -> &Vec<ObjCMethod> {
105 &self.methods
106 }
107
108 pub(crate) fn is_protocol(&self) -> bool {
110 self.is_protocol
111 }
112
113 pub(crate) fn is_category(&self) -> bool {
115 self.category.is_some()
116 }
117
118 pub(crate) fn class_methods(&self) -> &Vec<ObjCMethod> {
120 &self.class_methods
121 }
122
123 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 interface.name = c.spelling();
142 interface.category = Some(cursor.spelling());
143 }
144 }
145 CXCursor_ObjCProtocolRef => {
146 let needle = format!("P{}", c.spelling());
148 let items_map = ctx.items();
149 debug!(
150 "Interface {} conforms to {needle}, find the item",
151 interface.name,
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!("Found conforming protocol {item:?}");
167 interface.conforms_to.push(id);
168 break;
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 =
182 c.kind() == CXCursor_ObjCClassMethodDecl;
183 let method =
184 ObjCMethod::new(&name, signature, is_class_method);
185 interface.add_method(method);
186 }
187 CXCursor_TemplateTypeParameter => {
188 let name = c.spelling();
189 interface.template_names.push(name);
190 }
191 CXCursor_ObjCSuperClassRef => {
192 let item = Item::from_ty_or_ref(c.cur_type(), c, None, ctx);
193 interface.parent_class = Some(item.into());
194 }
195 _ => {}
196 }
197 CXChildVisit_Continue
198 });
199 Some(interface)
200 }
201
202 fn add_method(&mut self, method: ObjCMethod) {
203 if method.is_class_method {
204 self.class_methods.push(method);
205 } else {
206 self.methods.push(method);
207 }
208 }
209}
210
211impl ObjCMethod {
212 fn new(
213 name: &str,
214 signature: FunctionSig,
215 is_class_method: bool,
216 ) -> ObjCMethod {
217 let split_name: Vec<&str> = name.split(':').collect();
218
219 let rust_name = split_name.join("_");
220
221 ObjCMethod {
222 name: name.to_owned(),
223 rust_name,
224 signature,
225 is_class_method,
226 }
227 }
228
229 pub(crate) fn rust_name(&self) -> &str {
232 self.rust_name.as_ref()
233 }
234
235 pub(crate) fn signature(&self) -> &FunctionSig {
237 &self.signature
238 }
239
240 pub(crate) fn is_class_method(&self) -> bool {
242 self.is_class_method
243 }
244
245 pub(crate) fn format_method_call(
247 &self,
248 args: &[TokenStream],
249 ) -> TokenStream {
250 let split_name: Vec<Option<Ident>> = self
251 .name
252 .split(':')
253 .enumerate()
254 .map(|(idx, name)| {
255 if name.is_empty() {
256 None
257 } else if idx == 0 {
258 if ["crate", "self", "super", "Self"].contains(&name) {
262 Some(Ident::new(&format!("{name}_"), Span::call_site()))
263 } else {
264 Some(Ident::new(name, Span::call_site()))
265 }
266 } else {
267 Some(
273 syn::parse_str::<Ident>(name)
274 .or_else(|err| {
275 syn::parse_str::<Ident>(&format!("r#{name}"))
276 .map_err(|_| err)
277 })
278 .or_else(|err| {
279 syn::parse_str::<Ident>(&format!("{name}_"))
280 .map_err(|_| err)
281 })
282 .expect("Invalid identifier"),
283 )
284 }
285 })
286 .collect();
287
288 if args.is_empty() && split_name.len() == 1 {
290 let name = &split_name[0];
291 return quote! {
292 #name
293 };
294 }
295
296 assert!(
298 args.len() == split_name.len() - 1,
299 "Incorrect method name or arguments for objc method, {args:?} vs {split_name:?}"
300 );
301
302 let mut args_without_types = vec![];
304 for arg in args {
305 let arg = arg.to_string();
306 let name_and_sig: Vec<&str> = arg.split(' ').collect();
307 let name = name_and_sig[0];
308 args_without_types.push(Ident::new(name, Span::call_site()));
309 }
310
311 let args = split_name.into_iter().zip(args_without_types).map(
312 |(arg, arg_val)| {
313 if let Some(arg) = arg {
314 quote! { #arg: #arg_val }
315 } else {
316 quote! { #arg_val: #arg_val }
317 }
318 },
319 );
320
321 quote! {
322 #( #args )*
323 }
324 }
325}
326
327impl Trace for ObjCInterface {
328 type Extra = ();
329
330 fn trace<T>(&self, context: &BindgenContext, tracer: &mut T, _: &())
331 where
332 T: Tracer,
333 {
334 for method in &self.methods {
335 method.signature.trace(context, tracer, &());
336 }
337
338 for class_method in &self.class_methods {
339 class_method.signature.trace(context, tracer, &());
340 }
341
342 for protocol in &self.conforms_to {
343 tracer.visit(*protocol);
344 }
345 }
346}