summaryrefslogtreecommitdiffstats
path: root/third_party/rust/bindgen/ir/objc.rs
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/bindgen/ir/objc.rs')
-rw-r--r--third_party/rust/bindgen/ir/objc.rs354
1 files changed, 354 insertions, 0 deletions
diff --git a/third_party/rust/bindgen/ir/objc.rs b/third_party/rust/bindgen/ir/objc.rs
new file mode 100644
index 0000000000..93d8b3bfdf
--- /dev/null
+++ b/third_party/rust/bindgen/ir/objc.rs
@@ -0,0 +1,354 @@
+//! Objective C types
+
+use super::context::{BindgenContext, ItemId};
+use super::function::FunctionSig;
+use super::item::Item;
+use super::traversal::{Trace, Tracer};
+use super::ty::TypeKind;
+use crate::clang;
+use clang_sys::CXChildVisit_Continue;
+use clang_sys::CXCursor_ObjCCategoryDecl;
+use clang_sys::CXCursor_ObjCClassMethodDecl;
+use clang_sys::CXCursor_ObjCClassRef;
+use clang_sys::CXCursor_ObjCInstanceMethodDecl;
+use clang_sys::CXCursor_ObjCProtocolDecl;
+use clang_sys::CXCursor_ObjCProtocolRef;
+use clang_sys::CXCursor_ObjCSuperClassRef;
+use clang_sys::CXCursor_TemplateTypeParameter;
+use proc_macro2::{Ident, Span, TokenStream};
+
+/// Objective C interface as used in TypeKind
+///
+/// Also protocols and categories are parsed as this type
+#[derive(Debug)]
+pub(crate) struct ObjCInterface {
+ /// The name
+ /// like, NSObject
+ name: String,
+
+ category: Option<String>,
+
+ is_protocol: bool,
+
+ /// The list of template names almost always, ObjectType or KeyType
+ pub(crate) template_names: Vec<String>,
+
+ /// The list of protocols that this interface conforms to.
+ pub(crate) conforms_to: Vec<ItemId>,
+
+ /// The direct parent for this interface.
+ pub(crate) parent_class: Option<ItemId>,
+
+ /// List of the methods defined in this interfae
+ methods: Vec<ObjCMethod>,
+
+ class_methods: Vec<ObjCMethod>,
+}
+
+/// The objective c methods
+#[derive(Debug)]
+pub(crate) struct ObjCMethod {
+ /// The original method selector name
+ /// like, dataWithBytes:length:
+ name: String,
+
+ /// Method name as converted to rust
+ /// like, dataWithBytes_length_
+ rust_name: String,
+
+ signature: FunctionSig,
+
+ /// Is class method?
+ is_class_method: bool,
+}
+
+impl ObjCInterface {
+ fn new(name: &str) -> ObjCInterface {
+ ObjCInterface {
+ name: name.to_owned(),
+ category: None,
+ is_protocol: false,
+ template_names: Vec::new(),
+ parent_class: None,
+ conforms_to: Vec::new(),
+ methods: Vec::new(),
+ class_methods: Vec::new(),
+ }
+ }
+
+ /// The name
+ /// like, NSObject
+ pub(crate) fn name(&self) -> &str {
+ self.name.as_ref()
+ }
+
+ /// Formats the name for rust
+ /// Can be like NSObject, but with categories might be like NSObject_NSCoderMethods
+ /// and protocols are like PNSObject
+ pub(crate) fn rust_name(&self) -> String {
+ if let Some(ref cat) = self.category {
+ format!("{}_{}", self.name(), cat)
+ } else if self.is_protocol {
+ format!("P{}", self.name())
+ } else {
+ format!("I{}", self.name().to_owned())
+ }
+ }
+
+ /// Is this a template interface?
+ pub(crate) fn is_template(&self) -> bool {
+ !self.template_names.is_empty()
+ }
+
+ /// List of the methods defined in this interface
+ pub(crate) fn methods(&self) -> &Vec<ObjCMethod> {
+ &self.methods
+ }
+
+ /// Is this a protocol?
+ pub(crate) fn is_protocol(&self) -> bool {
+ self.is_protocol
+ }
+
+ /// Is this a category?
+ pub(crate) fn is_category(&self) -> bool {
+ self.category.is_some()
+ }
+
+ /// List of the class methods defined in this interface
+ pub(crate) fn class_methods(&self) -> &Vec<ObjCMethod> {
+ &self.class_methods
+ }
+
+ /// Parses the Objective C interface from the cursor
+ pub(crate) fn from_ty(
+ cursor: &clang::Cursor,
+ ctx: &mut BindgenContext,
+ ) -> Option<Self> {
+ let name = cursor.spelling();
+ let mut interface = Self::new(&name);
+
+ if cursor.kind() == CXCursor_ObjCProtocolDecl {
+ interface.is_protocol = true;
+ }
+
+ cursor.visit(|c| {
+ match c.kind() {
+ CXCursor_ObjCClassRef => {
+ if cursor.kind() == CXCursor_ObjCCategoryDecl {
+ // We are actually a category extension, and we found the reference
+ // to the original interface, so name this interface approriately
+ interface.name = c.spelling();
+ interface.category = Some(cursor.spelling());
+ }
+ }
+ CXCursor_ObjCProtocolRef => {
+ // Gather protocols this interface conforms to
+ let needle = format!("P{}", c.spelling());
+ let items_map = ctx.items();
+ debug!(
+ "Interface {} conforms to {}, find the item",
+ interface.name, needle
+ );
+
+ for (id, item) in items_map {
+ if let Some(ty) = item.as_type() {
+ if let TypeKind::ObjCInterface(ref protocol) =
+ *ty.kind()
+ {
+ if protocol.is_protocol {
+ debug!(
+ "Checking protocol {}, ty.name {:?}",
+ protocol.name,
+ ty.name()
+ );
+ if Some(needle.as_ref()) == ty.name() {
+ debug!(
+ "Found conforming protocol {:?}",
+ item
+ );
+ interface.conforms_to.push(id);
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ CXCursor_ObjCInstanceMethodDecl |
+ CXCursor_ObjCClassMethodDecl => {
+ let name = c.spelling();
+ let signature =
+ FunctionSig::from_ty(&c.cur_type(), &c, ctx)
+ .expect("Invalid function sig");
+ let is_class_method =
+ c.kind() == CXCursor_ObjCClassMethodDecl;
+ let method =
+ ObjCMethod::new(&name, signature, is_class_method);
+ interface.add_method(method);
+ }
+ CXCursor_TemplateTypeParameter => {
+ let name = c.spelling();
+ interface.template_names.push(name);
+ }
+ CXCursor_ObjCSuperClassRef => {
+ let item = Item::from_ty_or_ref(c.cur_type(), c, None, ctx);
+ interface.parent_class = Some(item.into());
+ }
+ _ => {}
+ }
+ CXChildVisit_Continue
+ });
+ Some(interface)
+ }
+
+ fn add_method(&mut self, method: ObjCMethod) {
+ if method.is_class_method {
+ self.class_methods.push(method);
+ } else {
+ self.methods.push(method);
+ }
+ }
+}
+
+impl ObjCMethod {
+ fn new(
+ name: &str,
+ signature: FunctionSig,
+ is_class_method: bool,
+ ) -> ObjCMethod {
+ let split_name: Vec<&str> = name.split(':').collect();
+
+ let rust_name = split_name.join("_");
+
+ ObjCMethod {
+ name: name.to_owned(),
+ rust_name,
+ signature,
+ is_class_method,
+ }
+ }
+
+ /// Method name as converted to rust
+ /// like, dataWithBytes_length_
+ pub(crate) fn rust_name(&self) -> &str {
+ self.rust_name.as_ref()
+ }
+
+ /// Returns the methods signature as FunctionSig
+ pub(crate) fn signature(&self) -> &FunctionSig {
+ &self.signature
+ }
+
+ /// Is this a class method?
+ pub(crate) fn is_class_method(&self) -> bool {
+ self.is_class_method
+ }
+
+ /// Formats the method call
+ pub(crate) fn format_method_call(
+ &self,
+ args: &[TokenStream],
+ ) -> TokenStream {
+ let split_name: Vec<Option<Ident>> = self
+ .name
+ .split(':')
+ .enumerate()
+ .map(|(idx, name)| {
+ if name.is_empty() {
+ None
+ } else if idx == 0 {
+ // Try to parse the method name as an identifier. Having a keyword is ok
+ // unless it is `crate`, `self`, `super` or `Self`, so we try to add the `_`
+ // suffix to it and parse it.
+ if ["crate", "self", "super", "Self"].contains(&name) {
+ Some(Ident::new(
+ &format!("{}_", name),
+ Span::call_site(),
+ ))
+ } else {
+ Some(Ident::new(name, Span::call_site()))
+ }
+ } else {
+ // Try to parse the current joining name as an identifier. This might fail if the name
+ // is a keyword, so we try to "r#" to it and parse again, this could also fail
+ // if the name is `crate`, `self`, `super` or `Self`, so we try to add the `_`
+ // suffix to it and parse again. If this also fails, we panic with the first
+ // error.
+ Some(
+ syn::parse_str::<Ident>(name)
+ .or_else(|err| {
+ syn::parse_str::<Ident>(&format!("r#{}", name))
+ .map_err(|_| err)
+ })
+ .or_else(|err| {
+ syn::parse_str::<Ident>(&format!("{}_", name))
+ .map_err(|_| err)
+ })
+ .expect("Invalid identifier"),
+ )
+ }
+ })
+ .collect();
+
+ // No arguments
+ if args.is_empty() && split_name.len() == 1 {
+ let name = &split_name[0];
+ return quote! {
+ #name
+ };
+ }
+
+ // Check right amount of arguments
+ assert!(
+ args.len() == split_name.len() - 1,
+ "Incorrect method name or arguments for objc method, {:?} vs {:?}",
+ args,
+ split_name
+ );
+
+ // Get arguments without type signatures to pass to `msg_send!`
+ let mut args_without_types = vec![];
+ for arg in args.iter() {
+ let arg = arg.to_string();
+ let name_and_sig: Vec<&str> = arg.split(' ').collect();
+ let name = name_and_sig[0];
+ args_without_types.push(Ident::new(name, Span::call_site()))
+ }
+
+ let args = split_name.into_iter().zip(args_without_types).map(
+ |(arg, arg_val)| {
+ if let Some(arg) = arg {
+ quote! { #arg: #arg_val }
+ } else {
+ quote! { #arg_val: #arg_val }
+ }
+ },
+ );
+
+ quote! {
+ #( #args )*
+ }
+ }
+}
+
+impl Trace for ObjCInterface {
+ type Extra = ();
+
+ fn trace<T>(&self, context: &BindgenContext, tracer: &mut T, _: &())
+ where
+ T: Tracer,
+ {
+ for method in &self.methods {
+ method.signature.trace(context, tracer, &());
+ }
+
+ for class_method in &self.class_methods {
+ class_method.signature.trace(context, tracer, &());
+ }
+
+ for protocol in &self.conforms_to {
+ tracer.visit(*protocol);
+ }
+ }
+}