diff --git a/crates/header-translator/src/cache.rs b/crates/header-translator/src/cache.rs index 42ca07642..ef052e6cb 100644 --- a/crates/header-translator/src/cache.rs +++ b/crates/header-translator/src/cache.rs @@ -29,7 +29,7 @@ impl ClassCache { fn all_methods_data(&self) -> impl Iterator { self.to_emit .iter() - .flat_map(|cache| cache.methods.iter().map(|m| (m.is_class, &*m.selector))) + .flat_map(|cache| cache.methods.iter().map(|m| m.id())) } } @@ -147,9 +147,7 @@ impl<'a> Cache<'a> { let mut methods: Vec<_> = cache .methods .iter() - .filter(|method| { - !seen_methods.contains(&(method.is_class, &method.selector)) - }) + .filter(|method| !seen_methods.contains(&method.id())) .filter_map(|method| { method.clone().update(ClassData::get_method_data( data, diff --git a/crates/header-translator/src/expr.rs b/crates/header-translator/src/expr.rs index a9dd84720..f908e38c5 100644 --- a/crates/header-translator/src/expr.rs +++ b/crates/header-translator/src/expr.rs @@ -4,8 +4,9 @@ use std::fmt::Write; use clang::token::TokenKind; use clang::{Entity, EntityKind, EntityVisitResult}; +use crate::context::Context; use crate::immediate_children; -use crate::unexposed_macro::UnexposedMacro; +use crate::unexposed_attr::UnexposedAttr; #[derive(Clone, Debug, PartialEq)] pub enum Expr { @@ -36,7 +37,7 @@ impl Expr { } } - pub fn parse_enum_constant(entity: &Entity<'_>) -> Option { + pub fn parse_enum_constant(entity: &Entity<'_>, context: &Context<'_>) -> Option { let mut declaration_references = Vec::new(); entity.visit_children(|entity, _parent| { @@ -51,8 +52,8 @@ impl Expr { immediate_children(entity, |entity, _span| match entity.get_kind() { EntityKind::UnexposedAttr => { - if let Some(macro_) = UnexposedMacro::parse(&entity) { - panic!("parsed macro in expr: {macro_:?}, {entity:?}"); + if let Some(attr) = UnexposedAttr::parse(&entity, context) { + error!(?attr, "unknown attribute"); } } _ => { diff --git a/crates/header-translator/src/lib.rs b/crates/header-translator/src/lib.rs index 4eb5e5c82..ac6b1a01d 100644 --- a/crates/header-translator/src/lib.rs +++ b/crates/header-translator/src/lib.rs @@ -24,10 +24,9 @@ mod library; mod method; mod objc2_utils; mod output; -mod property; mod rust_type; mod stmt; -mod unexposed_macro; +mod unexposed_attr; pub use self::cache::Cache; pub use self::config::Config; diff --git a/crates/header-translator/src/method.rs b/crates/header-translator/src/method.rs index 1f8da481b..bdc834997 100644 --- a/crates/header-translator/src/method.rs +++ b/crates/header-translator/src/method.rs @@ -1,6 +1,6 @@ use std::fmt; -use clang::{Entity, EntityKind, ObjCQualifiers}; +use clang::{Entity, EntityKind, ObjCAttributes, ObjCQualifiers}; use tracing::span::EnteredSpan; use crate::availability::Availability; @@ -9,21 +9,10 @@ use crate::context::Context; use crate::id::ItemIdentifier; use crate::immediate_children; use crate::objc2_utils::in_selector_family; -use crate::property::PartialProperty; -use crate::rust_type::Ty; -use crate::unexposed_macro::UnexposedMacro; +use crate::rust_type::{MethodArgumentQualifier, Ty}; +use crate::unexposed_attr::UnexposedAttr; -#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] -pub enum Qualifier { - In, - Inout, - Out, - Bycopy, - Byref, - Oneway, -} - -impl Qualifier { +impl MethodArgumentQualifier { pub fn parse(qualifiers: ObjCQualifiers) -> Self { match qualifiers { ObjCQualifiers { @@ -50,117 +39,208 @@ impl Qualifier { byref: false, oneway: false, } => Self::Out, - ObjCQualifiers { - in_: false, - inout: false, - out: false, - bycopy: true, - byref: false, - oneway: false, - } => Self::Bycopy, - ObjCQualifiers { - in_: false, - inout: false, - out: false, - bycopy: false, - byref: true, - oneway: false, - } => Self::Byref, - ObjCQualifiers { - in_: false, - inout: false, - out: false, - bycopy: false, - byref: false, - oneway: true, - } => Self::Oneway, - _ => unreachable!("invalid qualifiers: {:?}", qualifiers), + qualifiers => unreachable!("unsupported qualifiers {qualifiers:?}"), } } } +#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy, Default)] +struct MethodModifiers { + returns_inner_pointer: bool, + consumes_self: bool, + returns_retained: bool, + returns_not_retained: bool, + designated_initializer: bool, +} + +impl MethodModifiers { + fn parse(entity: &Entity<'_>, context: &Context<'_>) -> Self { + let mut this = Self::default(); + + immediate_children(entity, |entity, _span| match entity.get_kind() { + EntityKind::UnexposedAttr => { + if let Some(attr) = UnexposedAttr::parse(&entity, context) { + match attr { + UnexposedAttr::ReturnsRetained => { + this.returns_retained = true; + } + UnexposedAttr::ReturnsNotRetained => { + this.returns_not_retained = true; + } + attr => error!(?attr, "unknown attribute"), + } + } + } + EntityKind::ObjCReturnsInnerPointer => { + this.returns_inner_pointer = true; + } + EntityKind::NSConsumesSelf => { + this.consumes_self = true; + } + EntityKind::NSReturnsAutoreleased => { + error!("found NSReturnsAutoreleased, which requires manual handling"); + } + EntityKind::NSReturnsRetained => { + this.returns_retained = true; + } + EntityKind::NSReturnsNotRetained => { + this.returns_not_retained = true; + } + EntityKind::ObjCDesignatedInitializer => { + this.designated_initializer = true; + } + EntityKind::ObjCRequiresSuper => { + // TODO: Can we use this for something? + // + } + EntityKind::WarnUnusedResultAttr => { + // TODO: Emit `#[must_use]` on this + } + EntityKind::ObjCClassRef + | EntityKind::ObjCProtocolRef + | EntityKind::TypeRef + | EntityKind::ParmDecl => { + // Ignore + } + EntityKind::IbActionAttr | EntityKind::IbOutletAttr => { + // TODO: Do we want to do something special here? + } + EntityKind::ObjCInstanceMethodDecl => { + warn!("method inside property/method"); + } + _ => error!("unknown"), + }); + + this + } +} + +/// The retain semantics calling convention for a method. +/// +/// This also encodes the "method family" that a method belongs to. #[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] pub enum MemoryManagement { - /// Consumes self and returns retained pointer - Init, - ReturnsRetained, - ReturnsInnerPointer, + IdCopyOrMutCopy, + IdNew, + IdInit, + IdOther, + // TODO: + // IdReturnsRetained, + // IdReturnsNotRetained, Normal, } impl MemoryManagement { - /// Verifies that the selector and the memory management rules match up - /// in a way that we can just use `msg_send_id!`. - fn verify_sel(self, sel: &str) { - let bytes = sel.as_bytes(); - if in_selector_family(bytes, b"init") { - assert!(self == Self::Init, "{self:?} did not match {sel}"); - } else if in_selector_family(bytes, b"new") - || in_selector_family(bytes, b"alloc") - || in_selector_family(bytes, b"copy") - || in_selector_family(bytes, b"mutableCopy") - { - assert!( - self == Self::ReturnsRetained, - "{self:?} did not match {sel}" - ); - } else { - if self == Self::ReturnsInnerPointer { - return; - } - assert!(self == Self::Normal, "{self:?} did not match {sel}"); - } - } - - /// Matches `objc2::__macro_helpers::retain_semantics`. - fn get_memory_management_name(sel: &str) -> &'static str { - let bytes = sel.as_bytes(); - match ( - in_selector_family(bytes, b"new"), + /// The calling convention depends solely on these arguments. + /// + /// See + fn new(is_class: bool, selector: &str, result_type: &Ty, modifiers: MethodModifiers) -> Self { + // The method has been checked already to not have a + // `objc_method_family` attribute. + + // If: + // > its selector falls into the corresponding selector family + let bytes = selector.as_bytes(); + let id_type = match ( in_selector_family(bytes, b"alloc"), - in_selector_family(bytes, b"init"), in_selector_family(bytes, b"copy"), in_selector_family(bytes, b"mutableCopy"), + in_selector_family(bytes, b"new"), + in_selector_family(bytes, b"init"), ) { - (true, false, false, false, false) => "New", - (false, true, false, false, false) => "Alloc", - (false, false, true, false, false) => "Init", - (false, false, false, true, false) => "CopyOrMutCopy", - (false, false, false, false, true) => "CopyOrMutCopy", - (false, false, false, false, false) => "Other", + (true, false, false, false, false) => { + // It's not really worth the effort to support these, since + // they're only defined on `NSObject` and `NSProxy`, and we + // have it in `ClassType` anyhow. + error!("the `alloc` method-family requires manual handling"); + Self::IdOther + } + (false, true, false, false, false) => Self::IdCopyOrMutCopy, + (false, false, true, false, false) => Self::IdCopyOrMutCopy, + (false, false, false, true, false) => Self::IdNew, + (false, false, false, false, true) => Self::IdInit, + (false, false, false, false, false) => Self::IdOther, _ => unreachable!(), - } - } - - pub fn is_init(sel: &str) -> bool { - in_selector_family(sel.as_bytes(), b"init") - } - - pub fn is_alloc(sel: &str) -> bool { - in_selector_family(sel.as_bytes(), b"alloc") - } + }; - pub fn is_new(sel: &str) -> bool { - in_selector_family(sel.as_bytes(), b"new") + // And if: + // > its signature obeys the added restrictions of the method family. + // + // Which is just: + // > must return a retainable object pointer + if result_type.is_id() { + // We also check that the correct modifier flags were set for the + // given method family. + match ( + modifiers.returns_inner_pointer, + modifiers.consumes_self, + modifiers.returns_retained, + modifiers.returns_not_retained, + modifiers.designated_initializer, + id_type, + ) { + (false, false, true, false, false, Self::IdCopyOrMutCopy) => Self::IdCopyOrMutCopy, + (false, false, true, false, false, Self::IdNew) => Self::IdNew, + // For the `init` family there's another restriction: + // > must be instance methods + // + // Note: There's some extra restrictions about a program being + // "ill-formed" if it contains certain selectors with `init` + // methods that are not correct super/subclasses, but we don't + // need to handle that since the header would fail to compile + // in `clang` if that was the case. + (false, true, true, false, _, Self::IdInit) => { + if is_class { + Self::IdOther + } else { + Self::IdInit + } + } + (false, false, false, false, false, Self::IdOther) => Self::IdOther, + data => { + error!(?data, "invalid MemoryManagement id attributes"); + Self::IdOther + } + } + } else if let MethodModifiers { + designated_initializer: false, + // TODO: Maybe we can use this to emit things with lifetime of: + // `'self + 'autoreleasepool` + returns_inner_pointer: _, + consumes_self: false, + returns_retained: false, + returns_not_retained: false, + } = modifiers + { + Self::Normal + } else { + error!(?modifiers, "invalid MemoryManagement attributes"); + Self::Normal + } } } #[allow(dead_code)] #[derive(Debug, Clone, PartialEq)] pub struct Method { - pub selector: String, + selector: String, pub fn_name: String, - pub availability: Availability, + availability: Availability, pub is_class: bool, - pub is_optional_protocol: bool, - pub memory_management: MemoryManagement, - pub arguments: Vec<(String, Option, Ty)>, + is_optional_protocol: bool, + memory_management: MemoryManagement, + arguments: Vec<(String, Ty)>, pub result_type: Ty, - pub safe: bool, - pub mutating: bool, + safe: bool, + mutating: bool, } impl Method { + /// Value that uniquely identifies the method in a class. + pub fn id(&self) -> (bool, &str) { + (self.is_class, &self.selector) + } + /// Takes one of `EntityKind::ObjCInstanceMethodDecl` or /// `EntityKind::ObjCClassMethodDecl`. pub fn partial(entity: Entity<'_>) -> PartialMethod<'_> { @@ -221,7 +301,7 @@ impl Method { } pub fn visit_required_types(&self, mut f: impl FnMut(&ItemIdentifier)) { - for (_, _, arg) in &self.arguments { + for (_, arg) in &self.arguments { arg.visit_required_types(&mut f); } @@ -263,6 +343,8 @@ impl<'tu> PartialMethod<'tu> { .expect("method availability"), ); + let modifiers = MethodModifiers::parse(&entity, context); + let mut arguments: Vec<_> = entity .get_arguments() .expect("method arguments") @@ -270,8 +352,9 @@ impl<'tu> PartialMethod<'tu> { .map(|entity| { let name = entity.get_name().expect("arg display name"); let _span = debug_span!("method argument", name).entered(); - let qualifier = entity.get_objc_qualifiers().map(Qualifier::parse); - let mut is_consumed = false; + let qualifier = entity + .get_objc_qualifiers() + .map(MethodArgumentQualifier::parse); immediate_children(&entity, |entity, _span| match entity.get_kind() { EntityKind::ObjCClassRef @@ -281,14 +364,11 @@ impl<'tu> PartialMethod<'tu> { // Ignore } EntityKind::NSConsumed => { - if is_consumed { - error!("got NSConsumed twice"); - } - is_consumed = true; + error!("found NSConsumed, which requires manual handling"); } EntityKind::UnexposedAttr => { - if let Some(macro_) = UnexposedMacro::parse(&entity) { - warn!(?macro_, "unknown macro"); + if let Some(attr) = UnexposedAttr::parse(&entity, context) { + error!(?attr, "unknown attribute"); } } // For some reason we recurse into array types @@ -297,13 +377,13 @@ impl<'tu> PartialMethod<'tu> { }); let ty = entity.get_type().expect("argument type"); - let ty = Ty::parse_method_argument(ty, is_consumed, context); + let ty = Ty::parse_method_argument(ty, qualifier, context); - (name, qualifier, ty) + (name, ty) }) .collect(); - let is_error = if let Some((_, _, ty)) = arguments.last() { + let is_error = if let Some((_, ty)) = arguments.last() { ty.argument_is_error_out() } else { false @@ -319,94 +399,32 @@ impl<'tu> PartialMethod<'tu> { } if let Some(qualifiers) = entity.get_objc_qualifiers() { - let qualifier = Qualifier::parse(qualifiers); - error!(?qualifier, "unexpected qualifier on return type"); + error!(?qualifiers, "unsupported qualifiers on return type"); } let result_type = entity.get_result_type().expect("method return type"); let mut result_type = Ty::parse_method_return(result_type, context); - result_type.fix_related_result_type(is_class, &selector); + let memory_management = MemoryManagement::new(is_class, &selector, &result_type, modifiers); + + // Related result types. + // + let is_related = if is_class { + matches!(memory_management, MemoryManagement::IdNew) + } else { + matches!(memory_management, MemoryManagement::IdInit) || selector == "self" + }; - if is_class && MemoryManagement::is_alloc(&selector) { - result_type.set_is_alloc(); + if is_related { + result_type.try_fix_related_result_type(); } if is_error { result_type.set_is_error(); } - let mut designated_initializer = false; - let mut consumes_self = false; - let mut memory_management = MemoryManagement::Normal; - - immediate_children(&entity, |entity, _span| match entity.get_kind() { - EntityKind::ObjCClassRef - | EntityKind::ObjCProtocolRef - | EntityKind::TypeRef - | EntityKind::ParmDecl => { - // Ignore - } - EntityKind::ObjCDesignatedInitializer => { - if designated_initializer { - error!("encountered ObjCDesignatedInitializer twice"); - } - designated_initializer = true; - } - EntityKind::NSConsumesSelf => { - consumes_self = true; - } - EntityKind::NSReturnsRetained => { - if memory_management != MemoryManagement::Normal { - error!("got unexpected NSReturnsRetained") - } - memory_management = MemoryManagement::ReturnsRetained; - } - EntityKind::ObjCReturnsInnerPointer => { - if memory_management != MemoryManagement::Normal { - error!("got unexpected ObjCReturnsInnerPointer") - } - memory_management = MemoryManagement::ReturnsInnerPointer; - } - EntityKind::NSConsumed => { - // Handled inside arguments - } - EntityKind::IbActionAttr => { - // TODO: What is this? - } - EntityKind::ObjCRequiresSuper => { - // TODO: Can we use this for something? - // - } - EntityKind::WarnUnusedResultAttr => { - // TODO: Emit `#[must_use]` on this - } - EntityKind::UnexposedAttr => { - if let Some(macro_) = UnexposedMacro::parse(&entity) { - warn!(?macro_, "unknown macro"); - } - } - _ => error!("unknown"), - }); - - if consumes_self { - if memory_management != MemoryManagement::ReturnsRetained { - error!("got NSConsumesSelf without NSReturnsRetained"); - } - memory_management = MemoryManagement::Init; - } - - // Verify that memory management is as expected - if result_type.is_id() { - memory_management.verify_sel(&selector); - } - - if data.mutating && (is_class || MemoryManagement::is_init(&selector)) { - error!("invalid mutating method"); - } - Some(( - designated_initializer, + modifiers.designated_initializer, Method { selector, fn_name, @@ -423,6 +441,113 @@ impl<'tu> PartialMethod<'tu> { } } +#[derive(Debug)] +pub struct PartialProperty<'tu> { + pub entity: Entity<'tu>, + pub name: String, + pub getter_name: String, + pub setter_name: Option, + pub is_class: bool, + pub attributes: Option, + pub _span: EnteredSpan, +} + +impl PartialProperty<'_> { + pub fn parse( + self, + getter_data: MethodData, + setter_data: Option, + context: &Context<'_>, + ) -> (Option, Option) { + let Self { + entity, + name, + getter_name, + setter_name, + is_class, + attributes, + _span, + } = self; + + // Early return if both getter and setter are skipped + // + // To reduce warnings. + if getter_data.skipped && setter_data.map(|data| data.skipped).unwrap_or(true) { + return (None, None); + } + + let availability = Availability::parse( + entity + .get_platform_availability() + .expect("method availability"), + ); + + let modifiers = MethodModifiers::parse(&entity, context); + + let is_copy = attributes.map(|a| a.copy).unwrap_or(false); + + if let Some(qualifiers) = entity.get_objc_qualifiers() { + error!(?qualifiers, "properties do not support qualifiers"); + } + + let getter = if !getter_data.skipped { + let ty = Ty::parse_property_return( + entity.get_type().expect("property type"), + is_copy, + context, + ); + + let memory_management = MemoryManagement::new(is_class, &getter_name, &ty, modifiers); + + Some(Method { + selector: getter_name.clone(), + fn_name: getter_name, + availability: availability.clone(), + is_class, + is_optional_protocol: entity.is_objc_optional(), + memory_management, + arguments: Vec::new(), + result_type: ty, + safe: !getter_data.unsafe_, + mutating: getter_data.mutating, + }) + } else { + None + }; + + let setter = if let Some(setter_name) = setter_name { + let setter_data = setter_data.expect("setter_data must be present if setter_name was"); + if !setter_data.skipped { + let ty = + Ty::parse_property(entity.get_type().expect("property type"), is_copy, context); + + let selector = setter_name.clone() + ":"; + let memory_management = + MemoryManagement::new(is_class, &selector, &Ty::VOID_RESULT, modifiers); + + Some(Method { + selector, + fn_name: setter_name, + availability, + is_class, + is_optional_protocol: entity.is_objc_optional(), + memory_management, + arguments: vec![(name, ty)], + result_type: Ty::VOID_RESULT, + safe: !setter_data.unsafe_, + mutating: setter_data.mutating, + }) + } else { + None + } + } else { + None + }; + + (getter, setter) + } +} + impl Method { pub(crate) fn emit_on_subclasses(&self) -> bool { if !self.result_type.is_instancetype() { @@ -431,7 +556,7 @@ impl Method { if self.is_class { !matches!(&*self.selector, "new" | "supportsSecureCoding") } else { - self.memory_management == MemoryManagement::Init + self.memory_management == MemoryManagement::IdInit && !matches!(&*self.selector, "init" | "initWithCoder:") } } @@ -441,43 +566,63 @@ impl fmt::Display for Method { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let _span = debug_span!("method", self.fn_name).entered(); + // + // Attributes + // + if self.is_optional_protocol { writeln!(f, " #[optional]")?; } + let id_mm_name = match &self.memory_management { + MemoryManagement::IdCopyOrMutCopy => Some("CopyOrMutCopy"), + MemoryManagement::IdNew => Some("New"), + MemoryManagement::IdInit => Some("Init"), + MemoryManagement::IdOther => Some("Other"), + MemoryManagement::Normal => None, + }; + if let Some(id_mm_name) = id_mm_name { + write!(f, " #[method_id(@__retain_semantics {id_mm_name} ")?; + } else { + write!(f, " #[method(")?; + } let error_trailing = if self.result_type.is_error() { "_" } else { "" }; + writeln!(f, "{}{})]", self.selector, error_trailing)?; - if self.result_type.is_id() { - writeln!( - f, - " #[method_id(@__retain_semantics {} {}{})]", - MemoryManagement::get_memory_management_name(&self.selector), - self.selector, - error_trailing, - )?; - } else { - writeln!(f, " #[method({}{})]", self.selector, error_trailing)?; - }; + // + // Signature + // write!(f, " pub ")?; if !self.safe { write!(f, "unsafe ")?; } write!(f, "fn {}(", handle_reserved(&self.fn_name))?; - if !self.is_class { - if MemoryManagement::is_init(&self.selector) { - write!(f, "this: Option>, ")?; - } else if self.mutating { - write!(f, "&mut self, ")?; - } else { - write!(f, "&self, ")?; + + // Receiver + if let MemoryManagement::IdInit = self.memory_management { + if self.mutating { + error!("invalid mutating method"); + } + write!(f, "this: Option>, ")?; + } else if self.is_class { + if self.mutating { + error!("invalid mutating method"); } + // Insert nothing; a class method is assumed + } else if self.mutating { + write!(f, "&mut self, ")?; + } else { + write!(f, "&self, ")?; } - for (param, _qualifier, arg_ty) in &self.arguments { + + // Arguments + for (param, arg_ty) in &self.arguments { write!(f, "{}: {arg_ty},", handle_reserved(param))?; } write!(f, ")")?; + // Result writeln!(f, "{};", self.result_type)?; Ok(()) diff --git a/crates/header-translator/src/property.rs b/crates/header-translator/src/property.rs deleted file mode 100644 index f2611960b..000000000 --- a/crates/header-translator/src/property.rs +++ /dev/null @@ -1,139 +0,0 @@ -use clang::{Entity, EntityKind, ObjCAttributes}; -use tracing::span::EnteredSpan; - -use crate::availability::Availability; -use crate::config::MethodData; -use crate::context::Context; -use crate::immediate_children; -use crate::method::{MemoryManagement, Method, Qualifier}; -use crate::rust_type::Ty; -use crate::unexposed_macro::UnexposedMacro; - -#[derive(Debug)] -pub struct PartialProperty<'tu> { - pub entity: Entity<'tu>, - pub name: String, - pub getter_name: String, - pub setter_name: Option, - pub is_class: bool, - pub attributes: Option, - pub _span: EnteredSpan, -} - -impl PartialProperty<'_> { - pub fn parse( - self, - getter_data: MethodData, - setter_data: Option, - context: &Context<'_>, - ) -> (Option, Option) { - let Self { - entity, - name, - getter_name, - setter_name, - is_class, - attributes, - _span, - } = self; - - // Early return if both getter and setter are skipped - // - // To reduce warnings. - if getter_data.skipped && setter_data.map(|data| data.skipped).unwrap_or(true) { - return (None, None); - } - - let availability = Availability::parse( - entity - .get_platform_availability() - .expect("method availability"), - ); - - let is_copy = attributes.map(|a| a.copy).unwrap_or(false); - - let mut memory_management = MemoryManagement::Normal; - - immediate_children(&entity, |entity, _span| match entity.get_kind() { - EntityKind::ObjCClassRef - | EntityKind::ObjCProtocolRef - | EntityKind::TypeRef - | EntityKind::ParmDecl => { - // Ignore - } - EntityKind::ObjCReturnsInnerPointer => { - if memory_management != MemoryManagement::Normal { - error!(?memory_management, "unexpected ObjCReturnsInnerPointer") - } - memory_management = MemoryManagement::ReturnsInnerPointer; - } - EntityKind::ObjCInstanceMethodDecl => { - warn!("method in property"); - } - EntityKind::IbOutletAttr => { - // TODO: What is this? - } - EntityKind::UnexposedAttr => { - if let Some(macro_) = UnexposedMacro::parse(&entity) { - warn!(?macro_, "unknown macro"); - } - } - _ => error!("unknown"), - }); - - let qualifier = entity.get_objc_qualifiers().map(Qualifier::parse); - if qualifier.is_some() { - error!("properties do not support qualifiers"); - } - - let getter = if !getter_data.skipped { - let ty = Ty::parse_property_return( - entity.get_type().expect("property type"), - is_copy, - context, - ); - - Some(Method { - selector: getter_name.clone(), - fn_name: getter_name, - availability: availability.clone(), - is_class, - is_optional_protocol: entity.is_objc_optional(), - memory_management, - arguments: Vec::new(), - result_type: ty, - safe: !getter_data.unsafe_, - mutating: getter_data.mutating, - }) - } else { - None - }; - - let setter = if let Some(setter_name) = setter_name { - let setter_data = setter_data.expect("setter_data must be present if setter_name was"); - if !setter_data.skipped { - let ty = - Ty::parse_property(entity.get_type().expect("property type"), is_copy, context); - - Some(Method { - selector: setter_name.clone() + ":", - fn_name: setter_name, - availability, - is_class, - is_optional_protocol: entity.is_objc_optional(), - memory_management, - arguments: vec![(name, None, ty)], - result_type: Ty::VOID_RESULT, - safe: !setter_data.unsafe_, - mutating: setter_data.mutating, - }) - } else { - None - } - } else { - None - }; - - (getter, setter) - } -} diff --git a/crates/header-translator/src/rust_type.rs b/crates/header-translator/src/rust_type.rs index 5c3e0d7a6..7da9659cb 100644 --- a/crates/header-translator/src/rust_type.rs +++ b/crates/header-translator/src/rust_type.rs @@ -7,7 +7,6 @@ use serde::Deserialize; use crate::context::Context; use crate::id::ItemIdentifier; -use crate::method::MemoryManagement; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] enum ParsePosition { @@ -207,7 +206,6 @@ enum IdType { AnyClass { protocols: Vec, }, - Allocated, Self_ { ownership: Option, }, @@ -422,7 +420,6 @@ impl fmt::Display for IdType { Self::AnyProtocol => write!(f, "Protocol"), // TODO: Handle this better Self::AnyClass { .. } => write!(f, "TodoClass"), - Self::Allocated => write!(f, "Allocated"), Self::Self_ { .. } => write!(f, "Self"), } } @@ -1200,13 +1197,20 @@ impl fmt::Display for Inner { } } +#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] +pub enum MethodArgumentQualifier { + In, + Inout, + Out, +} + #[derive(Debug, Clone, PartialEq, Eq, Hash)] enum TyKind { MethodReturn { with_error: bool }, FnReturn, Static, Typedef, - MethodArgument { is_consumed: bool }, + MethodArgument, FnArgument, Struct, Enum, @@ -1224,7 +1228,11 @@ impl Ty { kind: TyKind::MethodReturn { with_error: false }, }; - pub fn parse_method_argument(ty: Type<'_>, is_consumed: bool, context: &Context<'_>) -> Self { + pub fn parse_method_argument( + ty: Type<'_>, + _qualifier: Option, + context: &Context<'_>, + ) -> Self { let ty = Inner::parse(ty, Lifetime::Unspecified, context); match &ty { @@ -1245,9 +1253,11 @@ impl Ty { }), } + // TODO: Is the qualifier useful for anything? + Self { ty, - kind: TyKind::MethodArgument { is_consumed }, + kind: TyKind::MethodArgument, } } @@ -1267,7 +1277,7 @@ impl Ty { } pub fn parse_function_argument(ty: Type<'_>, context: &Context<'_>) -> Self { - let mut this = Self::parse_method_argument(ty, false, context); + let mut this = Self::parse_method_argument(ty, None, context); this.kind = TyKind::FnArgument; this } @@ -1330,7 +1340,7 @@ impl Ty { Self { ty, - kind: TyKind::MethodArgument { is_consumed: false }, + kind: TyKind::MethodArgument, } } @@ -1500,20 +1510,6 @@ impl Ty { matches!(self.ty, Inner::Id { .. }) } - pub fn set_is_alloc(&mut self) { - match &mut self.ty { - Inner::Id { - ty: ty @ IdType::Self_ { ownership: None }, - lifetime: Lifetime::Unspecified, - is_const: false, - nullability: _, - } => { - *ty = IdType::Allocated; - } - _ => error!(?self, "invalid alloc return type"), - } - } - pub fn set_is_error(&mut self) { if let TyKind::MethodReturn { with_error } = &mut self.kind { *with_error = true; @@ -1544,30 +1540,20 @@ impl Ty { matches!(&self.ty, Inner::TypeDef { id } if id.name == s) } - /// Related result types - /// - pub fn fix_related_result_type(&mut self, is_class: bool, selector: &str) { - if let Inner::Id { - ty: ty @ IdType::AnyObject { .. }, - .. - } = &mut self.ty - { - let is_related = if is_class { - MemoryManagement::is_new(selector) || MemoryManagement::is_alloc(selector) - } else { - MemoryManagement::is_init(selector) || selector == "self" - }; - - if is_related { - if let IdType::AnyObject { protocols } = &ty { - if !protocols.is_empty() { - warn!(?ty, "related result type with protocols"); - return; - } + pub fn try_fix_related_result_type(&mut self) { + if let Inner::Id { ty, .. } = &mut self.ty { + if let IdType::AnyObject { protocols } = &ty { + if !protocols.is_empty() { + warn!(?ty, "related result type with protocols"); + return; } *ty = IdType::Self_ { ownership: None }; + } else { + // Only fix if the type is `id` } + } else { + panic!("tried to fix related result type on non-id type") } } @@ -1701,7 +1687,7 @@ impl fmt::Display for Ty { } ty => write!(f, "{ty}"), }, - TyKind::MethodArgument { is_consumed: _ } | TyKind::FnArgument => match &self.ty { + TyKind::MethodArgument | TyKind::FnArgument => match &self.ty { Inner::Id { ty, is_const: false, @@ -1721,10 +1707,10 @@ impl fmt::Display for Ty { write!(f, "Option<&Class>") } } - Inner::C99Bool if self.kind == TyKind::MethodArgument { is_consumed: false } => { + Inner::C99Bool if self.kind == TyKind::MethodArgument => { panic!("C99's bool as Objective-C method argument is unsupported") } - Inner::ObjcBool if self.kind == TyKind::MethodArgument { is_consumed: false } => { + Inner::ObjcBool if self.kind == TyKind::MethodArgument => { write!(f, "bool") } ty @ Inner::Pointer { diff --git a/crates/header-translator/src/stmt.rs b/crates/header-translator/src/stmt.rs index 94eb84992..140e0882e 100644 --- a/crates/header-translator/src/stmt.rs +++ b/crates/header-translator/src/stmt.rs @@ -15,7 +15,7 @@ use crate::id::ItemIdentifier; use crate::immediate_children; use crate::method::{handle_reserved, Method}; use crate::rust_type::{Ownership, Ty}; -use crate::unexposed_macro::UnexposedMacro; +use crate::unexposed_attr::UnexposedAttr; #[derive(serde::Deserialize, Debug, Clone, PartialEq, Eq)] pub struct Derives(Cow<'static, str>); @@ -153,8 +153,8 @@ fn parse_objc_decl( // Maybe useful for knowing when to implement `Error` for the type } EntityKind::UnexposedAttr => { - if let Some(macro_) = UnexposedMacro::parse(&entity) { - warn!(?macro_, "unknown macro"); + if let Some(attr) = UnexposedAttr::parse(&entity, context) { + error!(?attr, "unknown attribute"); } } _ => error!("unknown"), @@ -249,7 +249,7 @@ pub enum Stmt { EnumDecl { id: ItemIdentifier>, ty: Ty, - kind: Option, + kind: Option, variants: Vec<(String, Expr)>, }, /// static const ty name = expr; @@ -276,7 +276,7 @@ pub enum Stmt { AliasDecl { id: ItemIdentifier, ty: Ty, - kind: Option, + kind: Option, }, } @@ -286,8 +286,8 @@ fn parse_struct(entity: &Entity<'_>, context: &Context<'_>) -> (bool, Vec<(Strin immediate_children(entity, |entity, span| match entity.get_kind() { EntityKind::UnexposedAttr => { - if let Some(macro_) = UnexposedMacro::parse(&entity) { - warn!(?macro_, "unknown macro"); + if let Some(attr) = UnexposedAttr::parse(&entity, context) { + error!(?attr, "unknown attribute"); } } EntityKind::FieldDecl => { @@ -314,6 +314,21 @@ fn parse_struct(entity: &Entity<'_>, context: &Context<'_>) -> (bool, Vec<(Strin (boxable, fields) } +fn parse_fn_param_children(entity: &Entity<'_>, context: &Context<'_>) { + immediate_children(entity, |entity, _span| match entity.get_kind() { + EntityKind::UnexposedAttr => { + if let Some(attr) = UnexposedAttr::parse(&entity, context) { + error!(?attr, "unknown attribute"); + } + } + EntityKind::ObjCClassRef | EntityKind::TypeRef | EntityKind::ObjCProtocolRef => {} + EntityKind::NSConsumed => { + error!("found NSConsumed, which requires manual handling"); + } + kind => error!(?kind, "unknown"), + }); +} + impl Stmt { pub fn parse(entity: &Entity<'_>, context: &Context<'_>) -> Vec { let _span = debug_span!( @@ -495,11 +510,11 @@ impl Stmt { immediate_children(entity, |entity, _span| match entity.get_kind() { EntityKind::UnexposedAttr => { - if let Some(macro_) = UnexposedMacro::parse_plus_macros(&entity, context) { + if let Some(attr) = UnexposedAttr::parse(&entity, context) { if kind.is_some() { - panic!("got multiple unexposed macros {kind:?}, {macro_:?}"); + panic!("got multiple unexposed attributes {kind:?}, {attr:?}"); } - kind = Some(macro_); + kind = Some(attr); } } EntityKind::StructDecl => { @@ -643,21 +658,21 @@ impl Stmt { let expr = if data.use_value { val } else { - Expr::parse_enum_constant(&entity).unwrap_or(val) + Expr::parse_enum_constant(&entity, context).unwrap_or(val) }; variants.push((name, expr)); } EntityKind::UnexposedAttr => { - if let Some(macro_) = UnexposedMacro::parse(&entity) { + if let Some(attr) = UnexposedAttr::parse(&entity, context) { if let Some(kind) = &kind { - assert_eq!(kind, ¯o_, "got differing enum kinds in {id:?}"); + assert_eq!(kind, &attr, "got differing enum kinds in {id:?}"); } else { - kind = Some(macro_); + kind = Some(attr); } } } EntityKind::FlagEnum => { - let macro_ = UnexposedMacro::Options; + let macro_ = UnexposedAttr::Options; if let Some(kind) = &kind { assert_eq!(kind, ¯o_, "got differing enum kinds in {id:?}"); } else { @@ -699,8 +714,8 @@ impl Stmt { immediate_children(entity, |entity, _span| match entity.get_kind() { EntityKind::UnexposedAttr => { - if let Some(macro_) = UnexposedMacro::parse(&entity) { - panic!("unexpected attribute: {macro_:?}"); + if let Some(attr) = UnexposedAttr::parse(&entity, context) { + error!(?attr, "unknown attribute"); } } EntityKind::VisibilityAttr => {} @@ -751,14 +766,15 @@ impl Stmt { immediate_children(entity, |entity, _span| match entity.get_kind() { EntityKind::UnexposedAttr => { - if let Some(macro_) = UnexposedMacro::parse(&entity) { - warn!(?macro_, "unknown macro"); + if let Some(attr) = UnexposedAttr::parse(&entity, context) { + error!(?attr, "unknown attribute"); } } EntityKind::ObjCClassRef | EntityKind::TypeRef | EntityKind::ObjCProtocolRef => {} EntityKind::ParmDecl => { + parse_fn_param_children(&entity, context); // Could also be retrieved via. `get_arguments` let name = entity.get_name().unwrap_or_else(|| "_".into()); let ty = entity.get_type().expect("function argument type"); @@ -1154,10 +1170,10 @@ impl fmt::Display for Stmt { } => { let macro_name = match kind { None => "extern_enum", - Some(UnexposedMacro::Enum) => "ns_enum", - Some(UnexposedMacro::Options) => "ns_options", - Some(UnexposedMacro::ClosedEnum) => "ns_closed_enum", - Some(UnexposedMacro::ErrorEnum) => "ns_error_enum", + Some(UnexposedAttr::Enum) => "ns_enum", + Some(UnexposedAttr::Options) => "ns_options", + Some(UnexposedAttr::ClosedEnum) => "ns_closed_enum", + Some(UnexposedAttr::ErrorEnum) => "ns_error_enum", _ => panic!("invalid enum kind"), }; writeln!(f, "{macro_name}!(")?; @@ -1247,13 +1263,13 @@ impl fmt::Display for Stmt { } Self::AliasDecl { id, ty, kind } => { match kind { - Some(UnexposedMacro::TypedEnum) => { + Some(UnexposedAttr::TypedEnum) => { writeln!(f, "typed_enum!(pub type {} = {ty};);", id.name)?; } - Some(UnexposedMacro::TypedExtensibleEnum) => { + Some(UnexposedAttr::TypedExtensibleEnum) => { writeln!(f, "typed_extensible_enum!(pub type {} = {ty};);", id.name)?; } - None | Some(UnexposedMacro::BridgedTypedef) => { + None | Some(UnexposedAttr::BridgedTypedef) => { // "bridged" typedefs should just use a normal type // alias. writeln!(f, "pub type {} = {ty};", id.name)?; diff --git a/crates/header-translator/src/unexposed_macro.rs b/crates/header-translator/src/unexposed_attr.rs similarity index 69% rename from crates/header-translator/src/unexposed_macro.rs rename to crates/header-translator/src/unexposed_attr.rs index 2077810cc..34498ad0a 100644 --- a/crates/header-translator/src/unexposed_macro.rs +++ b/crates/header-translator/src/unexposed_attr.rs @@ -5,19 +5,23 @@ use crate::context::Context; /// Parts of `EntityKind::UnexposedAttr` that we can easily parse. #[derive(Clone, Debug, PartialEq, Eq, Hash)] -pub enum UnexposedMacro { +pub enum UnexposedAttr { Enum, Options, ClosedEnum, ErrorEnum, TypedEnum, TypedExtensibleEnum, + BridgedTypedef, Bridged, BridgedMutable, + + ReturnsRetained, + ReturnsNotRetained, } -impl UnexposedMacro { +impl UnexposedAttr { fn from_name(s: &str) -> Option { match s { "NS_ENUM" | "CF_ENUM" => Some(Self::Enum), @@ -31,9 +35,21 @@ impl UnexposedMacro { "NS_SWIFT_BRIDGED_TYPEDEF" | "CF_SWIFT_BRIDGED_TYPEDEF" => Some(Self::BridgedTypedef), "CF_BRIDGED_TYPE" => Some(Self::Bridged), "CF_BRIDGED_MUTABLE_TYPE" => Some(Self::BridgedMutable), + "NS_RETURNS_RETAINED" | "CF_RETURNS_RETAINED" => Some(Self::ReturnsRetained), + "NS_RETURNS_NOT_RETAINED" | "CF_RETURNS_NOT_RETAINED" => Some(Self::ReturnsNotRetained), // TODO - "NS_FORMAT_FUNCTION" => None, - "NS_FORMAT_ARGUMENT" => None, + "NS_FORMAT_FUNCTION" | "NS_FORMAT_ARGUMENT" => None, + // TODO + "NS_NOESCAPE" => None, + // TODO: We could potentially automatically elide this argument + // from the method call, though it's rare enough that it's + // probably not really worth the effort. + "__unused" => None, + // We assume that a function is inline if it has a body, so not + // interesting. + "NS_INLINE" => None, + // We don't synthethize properties, so irrelevant for us. + "NS_REQUIRES_PROPERTY_DEFINITIONS" => None, // Availability attributes - their data is already exposed. "API_AVAILABLE" | "API_UNAVAILABLE" @@ -41,11 +57,15 @@ impl UnexposedMacro { | "API_DEPRECATED_WITH_REPLACEMENT" | "NS_CLASS_AVAILABLE_MAC" | "NS_AVAILABLE" + | "NS_UNAVAILABLE" + | "NS_AUTOMATED_REFCOUNT_UNAVAILABLE" + | "NS_AUTOMATED_REFCOUNT_WEAK_UNAVAILABLE" | "NS_OPENGL_DEPRECATED" | "NS_OPENGL_CLASS_DEPRECATED" | "NS_OPENGL_ENUM_DEPRECATED" | "OBJC_AVAILABLE" | "OBJC_DEPRECATED" + | "UNAVAILABLE_ATTRIBUTE" | "APPKIT_API_UNAVAILABLE_BEGIN_MACCATALYST" | "CG_AVAILABLE_STARTING" | "CG_AVAILABLE_BUT_DEPRECATED" => None, @@ -53,23 +73,21 @@ impl UnexposedMacro { "NS_SWIFT_NAME" | "CF_SWIFT_NAME" | "NS_SWIFT_ASYNC_NAME" + | "NS_SWIFT_NOTHROW" | "NS_SWIFT_ASYNC_THROWS_ON_FALSE" + | "NS_REFINED_FOR_SWIFT" + | "CF_REFINED_FOR_SWIFT" | "NS_SWIFT_UNAVAILABLE" | "CF_SWIFT_UNAVAILABLE" | "OBJC_SWIFT_UNAVAILABLE" => None, name => { - warn!(name, "unknown unexposed macro"); + error!(name, "unknown unexposed macro"); None } } } - pub fn parse(entity: &Entity<'_>) -> Option { - let location = entity.get_location().expect("unexposed attr location"); - Self::parse_location(location) - } - - pub fn parse_plus_macros(entity: &Entity<'_>, context: &Context<'_>) -> Option { + pub fn parse(entity: &Entity<'_>, context: &Context<'_>) -> Option { let location = entity.get_location().expect("unexposed attr location"); if let Some(macro_name) = context diff --git a/crates/header-translator/translation-config.toml b/crates/header-translator/translation-config.toml index a9df2ecb6..e3b43a846 100644 --- a/crates/header-translator/translation-config.toml +++ b/crates/header-translator/translation-config.toml @@ -1,4 +1,8 @@ -# Various fixes that we need to do because our translation is imperfect. +### Various fixes that we need to do because our translation is imperfect. + +### +### Libraries +### [library.Foundation] imports = ["Foundation"] @@ -19,13 +23,123 @@ imports = ["AppKit", "CoreData", "Foundation"] [library.AuthenticationServices] imports = ["AuthenticationServices", "Foundation"] -# Uses NS_REPLACES_RECEIVER +### +### Attributes that change a function/method's calling convention. +### +### The following should be handled automatically: +### - `ns_returns_not_retained` / `cf_returns_not_retained` / `os_returns_not_retained` +### - `ns_returns_retained` / `cf_returns_retained` / `os_returns_retained` +### +### The rest are only very rarely used in Apple's frameworks, so while we +### _could_ handle them too, I think it's easier to just do it manually. +### +### See https://clang.llvm.org/docs/AttributeReference.html +### + +# `ns_consumed`, `cf_consumed` and `os_consumed` +[fn.CFAutorelease] +skipped = true +[fn.CFBridgingRelease] +skipped = true +[fn.NSMakeCollectable] +skipped = true +[fn.NSFreeMapTable] +skipped = true +[fn.IOServiceGetMatchingService] +skipped = true +[fn.IOServiceGetMatchingServices] +skipped = true +[fn.IOServiceAddMatchingNotification] +skipped = true +[fn.CVOpenGLBufferRelease] +skipped = true +[fn.CVDisplayLinkRelease] +skipped = true +[fn.CVOpenGLBufferPoolRelease] +skipped = true +[fn.CVOpenGLTextureRelease] +skipped = true +[fn.CVPixelBufferPoolRelease] +skipped = true +[fn.CVPixelBufferRelease] +skipped = true +[fn.CVOpenGLTextureCacheRelease] +skipped = true +[fn.CVBufferRelease] +skipped = true +[protocol.NSKeyedUnarchiverDelegate.methods.unarchiver_didDecodeObject] +skipped = true +# + a few methods from DriverKit.framework and Kernel.framework, but those +# are written in C++, so we're not going to ever handle them anyhow. + +# `ns_consumes_self` and `os_consumes_this` [class.NSObject.methods.awakeAfterUsingCoder] skipped = true -[protocol.NSKeyedUnarchiverDelegate.methods] -# Uses NS_RELEASES_ARGUMENT and NS_RETURNS_RETAINED -unarchiver_didDecodeObject = { skipped = true } +# `ns_returns_autoreleased` +# Not ever used + +# `objc_method_family` +[class.ABNewPersonViewController.methods.newPersonViewDelegate] +skipped = true + +# `objc_ownership` in .apinotes +[class.NSBundle.methods.loadNibNamed_owner_topLevelObjects] +skipped = true +[class.NSNib.methods.instantiateWithOwner_topLevelObjects] +skipped = true + +# Return type `oneway void` +[class.NSObject.methods.release] +skipped = true +[class.NSPasteboard.methods.releaseGlobally] +skipped = true +[class.NSView.methods.releaseGState] +skipped = true +# + some more in IMServicePlugIn.framework + +# `alloc`-family methods +[class.NSObject.methods.alloc] +skipped = true +[class.NSObject.methods.allocWithZone] +skipped = true +[class.NSProxy.methods.alloc] +skipped = true +[class.NSProxy.methods.allocWithZone] +skipped = true + +### +### Lifetime annotations that we don't handle yet. +### + +# Uses `SomeObject * __strong *`, which is unsupported +[class.NSNetService.methods.getInputStream_outputStream] +skipped = true +[class.NSPropertyListSerialization.methods] +dataFromPropertyList_format_errorDescription = { skipped = true } +propertyListFromData_mutabilityOption_format_errorDescription = { skipped = true } +[fn.MTLCopyAllDevicesWithObserver] +skipped = true + +# Uses `__autoreleasing` in a typedef, which I'm unsure how to handle +[typedef.MTLAutoreleasedArgument] +skipped = true +[protocol.MTLFunction.methods.newArgumentEncoderWithBufferIndex_reflection] +skipped = true +[typedef.MTLAutoreleasedRenderPipelineReflection] +skipped = true +[typedef.MTLAutoreleasedComputePipelineReflection] +skipped = true +[protocol.MTLDevice.methods] +newRenderPipelineStateWithDescriptor_options_reflection_error = { skipped = true } +newComputePipelineStateWithFunction_options_reflection_error = { skipped = true } +newComputePipelineStateWithDescriptor_options_reflection_error = { skipped = true } +newRenderPipelineStateWithTileDescriptor_options_reflection_error = { skipped = true } +newRenderPipelineStateWithMeshDescriptor_options_reflection_error = { skipped = true } + +### +### Others +### [class.NSBlockOperation.methods] # Uses `NSArray`, which is difficult to handle @@ -37,15 +151,6 @@ registerObjectOfClass_visibility_loadHandler = { skipped = true } canLoadObjectOfClass = { skipped = true } loadObjectOfClass_completionHandler = { skipped = true } -# These use `SomeObject * __strong *`, which is unsupported -[class.NSNetService.methods] -getInputStream_outputStream = { skipped = true } -[class.NSPropertyListSerialization.methods] -dataFromPropertyList_format_errorDescription = { skipped = true } -propertyListFromData_mutabilityOption_format_errorDescription = { skipped = true } -[fn.MTLCopyAllDevicesWithObserver] -skipped = true - # Has `error:` parameter, but returns NSInteger (where 0 means error) [class.NSJSONSerialization.methods.writeJSONObject_toStream_options_error] skipped = true @@ -101,9 +206,6 @@ skipped = true # Root class [class.NSProxy] definition-skipped = true -[class.NSProxy.methods] -alloc = { skipped = true } -allocWithZone = { skipped = true } # Ignore categories on NSObject for now [class.NSObject] @@ -366,12 +468,6 @@ skipped = true [static.NSRollbackMergePolicy] skipped = true -# These return `oneway void`, which is a bit tricky to handle. -[class.NSPasteboard.methods.releaseGlobally] -skipped = true -[class.NSView.methods.releaseGState] -skipped = true - # Typedef that uses a generic from a class [typedef.NSCollectionViewDiffableDataSourceItemProvider] skipped = true @@ -420,6 +516,8 @@ skipped = true skipped = true # Uses stuff from different frameworks / system libraries +[fn.CFBridgingRetain] +skipped = true [class.NSAnimationContext.methods.timingFunction] skipped = true [class.NSAnimationContext.methods.setTimingFunction] @@ -774,22 +872,6 @@ skipped = true [class.NSMutableData.methods.mutableBytes] skipped = true -# Uses __autoreleasing in a typedef, which I'm unsure how to handle -[typedef.MTLAutoreleasedArgument] -skipped = true -[protocol.MTLFunction.methods.newArgumentEncoderWithBufferIndex_reflection] -skipped = true -[typedef.MTLAutoreleasedRenderPipelineReflection] -skipped = true -[typedef.MTLAutoreleasedComputePipelineReflection] -skipped = true -[protocol.MTLDevice.methods] -newRenderPipelineStateWithDescriptor_options_reflection_error = { skipped = true } -newComputePipelineStateWithFunction_options_reflection_error = { skipped = true } -newComputePipelineStateWithDescriptor_options_reflection_error = { skipped = true } -newRenderPipelineStateWithTileDescriptor_options_reflection_error = { skipped = true } -newRenderPipelineStateWithMeshDescriptor_options_reflection_error = { skipped = true } - # Uses unions internally [struct.MTLPackedFloat3] skipped = true diff --git a/crates/icrate/src/Foundation/fixes/mod.rs b/crates/icrate/src/Foundation/fixes/mod.rs index a0efa16c5..5a708f28f 100644 --- a/crates/icrate/src/Foundation/fixes/mod.rs +++ b/crates/icrate/src/Foundation/fixes/mod.rs @@ -10,10 +10,13 @@ mod copy; mod debug; mod generic_return; mod gnustep; +mod ns_consumed; pub use self::__NSDecimal::NSDecimal; pub use self::__NSNotFound::NSNotFound; #[cfg(feature = "Foundation_NSProxy")] pub use self::__NSProxy::NSProxy; +#[cfg(feature = "Foundation_NSMapTable")] +pub use self::ns_consumed::NSFreeMapTable; #[cfg(feature = "Foundation_NSUUID")] pub(crate) use self::NSUUID::UuidBytes; diff --git a/crates/icrate/src/Foundation/fixes/ns_consumed.rs b/crates/icrate/src/Foundation/fixes/ns_consumed.rs new file mode 100644 index 000000000..bdc8ade57 --- /dev/null +++ b/crates/icrate/src/Foundation/fixes/ns_consumed.rs @@ -0,0 +1,6 @@ +extern "C" { + #[cfg(feature = "Foundation_NSMapTable")] + pub fn NSFreeMapTable(table: *mut crate::Foundation::NSMapTable); +} + +// TODO: Add `-[NSKeyedUnarchiverDelegate unarchiver:didDecodeObject:]` diff --git a/crates/icrate/src/generated b/crates/icrate/src/generated index 48fb96a2d..8c106dd1d 160000 --- a/crates/icrate/src/generated +++ b/crates/icrate/src/generated @@ -1 +1 @@ -Subproject commit 48fb96a2dc408d17f2a298520c132516004398e2 +Subproject commit 8c106dd1dac31ae63508b3f933db61d9e765e7fb