From d85a7101959be97eaaf342452954ce99cb9c0cdb Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Sun, 8 Jan 2023 01:44:09 +0100 Subject: [PATCH 1/4] Improve support for other frameworks Like CoreGraphics and UIKit --- crates/header-translator/src/rust_type.rs | 18 ++++++--- crates/header-translator/src/stmt.rs | 38 +++++++++++++------ .../header-translator/src/unexposed_macro.rs | 24 ++++++++---- .../header-translator/translation-config.toml | 4 ++ 4 files changed, 59 insertions(+), 25 deletions(-) diff --git a/crates/header-translator/src/rust_type.rs b/crates/header-translator/src/rust_type.rs index cdf3068c4..ca21eed42 100644 --- a/crates/header-translator/src/rust_type.rs +++ b/crates/header-translator/src/rust_type.rs @@ -793,14 +793,14 @@ impl RustType { /// This is sound to output in (almost, c_void is not a valid return type) any /// context. `Ty` is then used to change these types into something nicer when -/// requires. +/// required. impl fmt::Display for RustType { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { use RustType::*; match self { // Primitives Void => write!(f, "c_void"), - C99Bool => panic!("C99's bool is unsupported"), // write!(f, "bool") + C99Bool => write!(f, "bool"), Char => write!(f, "c_char"), SChar => write!(f, "c_schar"), UChar => write!(f, "c_uchar"), @@ -1011,7 +1011,7 @@ impl Ty { this } - pub fn parse_typedef(ty: Type<'_>, context: &Context<'_>) -> Option { + pub fn parse_typedef(ty: Type<'_>, typedef_name: &str, context: &Context<'_>) -> Option { let mut ty = RustType::parse(ty, false, Nullability::Unspecified, false, context); ty.visit_lifetime(|lifetime| { @@ -1024,9 +1024,9 @@ impl Ty { // Handled by Stmt::EnumDecl RustType::Enum { .. } => None, // Handled above and in Stmt::StructDecl - // The rest is only `NSZone` - RustType::Struct { name } => { - assert_eq!(name, "_NSZone", "invalid struct in typedef"); + RustType::Struct { name } if name == typedef_name => None, + RustType::Struct { name } if name == typedef_name => { + warn!(name, "invalid struct in typedef"); None } // Opaque structs @@ -1354,6 +1354,9 @@ impl fmt::Display for Ty { write!(f, "Option<&'static Class>") } } + RustType::C99Bool => { + panic!("C99's bool as Objective-C method return is unsupported") + } RustType::ObjcBool => write!(f, "bool"), ty => write!(f, "{ty}"), } @@ -1449,6 +1452,9 @@ impl fmt::Display for Ty { write!(f, "Option<&Class>") } } + RustType::C99Bool if self.kind == TyKind::MethodArgument => { + panic!("C99's bool as Objective-C method argument is unsupported") + } RustType::ObjcBool if self.kind == TyKind::MethodArgument => write!(f, "bool"), ty @ RustType::Pointer { nullability, diff --git a/crates/header-translator/src/stmt.rs b/crates/header-translator/src/stmt.rs index d157f6567..5e313a92a 100644 --- a/crates/header-translator/src/stmt.rs +++ b/crates/header-translator/src/stmt.rs @@ -295,7 +295,7 @@ pub enum Stmt { }, } -fn parse_struct(entity: &Entity<'_>, name: String, context: &Context<'_>) -> Stmt { +fn parse_struct(entity: &Entity<'_>, context: &Context<'_>) -> (bool, Vec<(String, Ty)>) { let mut boxable = false; let mut fields = Vec::new(); @@ -325,11 +325,7 @@ fn parse_struct(entity: &Entity<'_>, name: String, context: &Context<'_>) -> Stm _ => warn!("unknown"), }); - Stmt::StructDecl { - name, - boxable, - fields, - } + (boxable, fields) } impl Stmt { @@ -533,7 +529,7 @@ impl Stmt { // If this struct doesn't have a name, or the // name is private, let's parse it with the // typedef name. - struct_ = Some(parse_struct(&entity, name.clone(), context)) + struct_ = Some(parse_struct(&entity, context)) } else { skip_struct = true; } @@ -545,9 +541,13 @@ impl Stmt { _ => warn!("unknown"), }); - if let Some(struct_) = struct_ { - assert_eq!(kind, None, "should not have parsed anything"); - return vec![struct_]; + if let Some((boxable, fields)) = struct_ { + assert_eq!(kind, None, "should not have parsed a kind"); + return vec![Stmt::StructDecl { + name, + boxable, + fields, + }]; } if skip_struct { @@ -566,7 +566,7 @@ impl Stmt { let ty = entity .get_typedef_underlying_type() .expect("typedef underlying type"); - if let Some(ty) = Ty::parse_typedef(ty, context) { + if let Some(ty) = Ty::parse_typedef(ty, &name, context) { vec![Self::AliasDecl { name, ty, kind }] } else { vec![] @@ -582,8 +582,19 @@ impl Stmt { { return vec![]; } + + // See https://github.com/rust-lang/rust-bindgen/blob/95fd17b874910184cc0fcd33b287fa4e205d9d7a/bindgen/ir/comp.rs#L1392-L1408 + if !entity.is_definition() { + return vec![]; + } + if !name.starts_with('_') { - return vec![parse_struct(entity, name, context)]; + let (boxable, fields) = parse_struct(entity, context); + return vec![Stmt::StructDecl { + name, + boxable, + fields, + }]; } } vec![] @@ -760,6 +771,9 @@ impl Stmt { let ty = Ty::parse_function_argument(ty, context); arguments.push((name, ty)) } + EntityKind::VisibilityAttr => { + // CG_EXTERN or UIKIT_EXTERN + } _ => warn!("unknown"), }); diff --git a/crates/header-translator/src/unexposed_macro.rs b/crates/header-translator/src/unexposed_macro.rs index fc19ca644..0cdf2b14f 100644 --- a/crates/header-translator/src/unexposed_macro.rs +++ b/crates/header-translator/src/unexposed_macro.rs @@ -14,18 +14,24 @@ pub enum UnexposedMacro { TypedEnum, TypedExtensibleEnum, BridgedTypedef, + Bridged, + BridgedMutable, } impl UnexposedMacro { fn from_name(s: &str) -> Option { match s { - "NS_ENUM" => Some(Self::Enum), - "NS_OPTIONS" => Some(Self::Options), - "NS_CLOSED_ENUM" => Some(Self::ClosedEnum), + "NS_ENUM" | "CF_ENUM" => Some(Self::Enum), + "NS_OPTIONS" | "CF_OPTIONS" => Some(Self::Options), + "NS_CLOSED_ENUM" | "CF_CLOSED_ENUM" => Some(Self::ClosedEnum), "NS_ERROR_ENUM" => Some(Self::ErrorEnum), - "NS_TYPED_ENUM" => Some(Self::TypedEnum), - "NS_TYPED_EXTENSIBLE_ENUM" => Some(Self::TypedExtensibleEnum), - "NS_SWIFT_BRIDGED_TYPEDEF" => Some(Self::BridgedTypedef), + "NS_TYPED_ENUM" | "CF_TYPED_ENUM" => Some(Self::TypedEnum), + "NS_TYPED_EXTENSIBLE_ENUM" | "CF_TYPED_EXTENSIBLE_ENUM" => { + Some(Self::TypedExtensibleEnum) + } + "NS_SWIFT_BRIDGED_TYPEDEF" | "CF_SWIFT_BRIDGED_TYPEDEF" => Some(Self::BridgedTypedef), + "CF_BRIDGED_TYPE" => Some(Self::Bridged), + "CF_BRIDGED_MUTABLE_TYPE" => Some(Self::BridgedMutable), // TODO "NS_FORMAT_FUNCTION" => None, "NS_FORMAT_ARGUMENT" => None, @@ -41,12 +47,16 @@ impl UnexposedMacro { | "NS_OPENGL_ENUM_DEPRECATED" | "OBJC_AVAILABLE" | "OBJC_DEPRECATED" - | "APPKIT_API_UNAVAILABLE_BEGIN_MACCATALYST" => None, + | "APPKIT_API_UNAVAILABLE_BEGIN_MACCATALYST" + | "CG_AVAILABLE_STARTING" + | "CG_AVAILABLE_BUT_DEPRECATED" => None, // Might be interesting in the future "NS_SWIFT_NAME" + | "CF_SWIFT_NAME" | "NS_SWIFT_ASYNC_NAME" | "NS_SWIFT_ASYNC_THROWS_ON_FALSE" | "NS_SWIFT_UNAVAILABLE" + | "CF_SWIFT_UNAVAILABLE" | "OBJC_SWIFT_UNAVAILABLE" => None, name => { warn!(name, "unknown unexposed macro"); diff --git a/crates/header-translator/translation-config.toml b/crates/header-translator/translation-config.toml index 793571fd0..4b277f017 100644 --- a/crates/header-translator/translation-config.toml +++ b/crates/header-translator/translation-config.toml @@ -102,6 +102,10 @@ skipped = true [protocol.NSObject] skipped = true +# Defined in `objc2` instead +[typedef.NSZone] +skipped = true + # Contains bitfields [struct.NSDecimal] skipped = true From 1d6d126b3fecb1d32692462c4259d8f0aebfb848 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Sun, 8 Jan 2023 10:49:41 +0100 Subject: [PATCH 2/4] Add CoreAnimation / QuartzCore --- crates/header-translator/framework-includes.h | 2 + .../src/data/CoreAnimation.rs | 2 + crates/header-translator/src/data/mod.rs | 1 + crates/header-translator/src/rust_type.rs | 8 +++ .../header-translator/translation-config.toml | 64 +++++++++++++++++++ crates/icrate/CHANGELOG.md | 3 + crates/icrate/Cargo.toml | 5 +- crates/icrate/src/CoreAnimation/fixes/mod.rs | 3 + crates/icrate/src/CoreAnimation/mod.rs | 15 +++++ crates/icrate/src/generated | 2 +- crates/icrate/src/lib.rs | 2 + 11 files changed, 104 insertions(+), 3 deletions(-) create mode 100644 crates/header-translator/src/data/CoreAnimation.rs create mode 100644 crates/icrate/src/CoreAnimation/fixes/mod.rs create mode 100644 crates/icrate/src/CoreAnimation/mod.rs diff --git a/crates/header-translator/framework-includes.h b/crates/header-translator/framework-includes.h index eb29fad17..a5476ca9b 100644 --- a/crates/header-translator/framework-includes.h +++ b/crates/header-translator/framework-includes.h @@ -8,6 +8,8 @@ #import +#import + #import #if TARGET_OS_OSX diff --git a/crates/header-translator/src/data/CoreAnimation.rs b/crates/header-translator/src/data/CoreAnimation.rs new file mode 100644 index 000000000..89a871f29 --- /dev/null +++ b/crates/header-translator/src/data/CoreAnimation.rs @@ -0,0 +1,2 @@ +data! { +} diff --git a/crates/header-translator/src/data/mod.rs b/crates/header-translator/src/data/mod.rs index 41e10a5d5..04cf77946 100644 --- a/crates/header-translator/src/data/mod.rs +++ b/crates/header-translator/src/data/mod.rs @@ -6,6 +6,7 @@ mod macros; data! { mod AppKit; mod AuthenticationServices; + mod CoreAnimation; mod CoreData; mod Foundation; } diff --git a/crates/header-translator/src/rust_type.rs b/crates/header-translator/src/rust_type.rs index ca21eed42..b0c507f80 100644 --- a/crates/header-translator/src/rust_type.rs +++ b/crates/header-translator/src/rust_type.rs @@ -475,6 +475,8 @@ enum RustType { U32, I64, U64, + ISize, + USize, // Objective-C Id { @@ -639,6 +641,8 @@ impl RustType { "uint32_t" => Self::U32, "int64_t" => Self::I64, "uint64_t" => Self::U64, + "ssize_t" => Self::ISize, + "size_t" => Self::USize, // MacTypes.h "UInt8" => Self::U8, @@ -824,6 +828,10 @@ impl fmt::Display for RustType { U32 => write!(f, "u32"), I64 => write!(f, "i64"), U64 => write!(f, "u64"), + // TODO: Use core::ffi::c_ssize_t + ISize => write!(f, "isize"), + // TODO: Use core::ffi::c_size_t + USize => write!(f, "usize"), // Objective-C Id { diff --git a/crates/header-translator/translation-config.toml b/crates/header-translator/translation-config.toml index 4b277f017..46236d154 100644 --- a/crates/header-translator/translation-config.toml +++ b/crates/header-translator/translation-config.toml @@ -3,6 +3,9 @@ [library.Foundation] imports = ["Foundation"] +[library.QuartzCore] +imports = ["CoreAnimation", "Foundation"] + [library.CoreData] imports = ["CoreData", "Foundation"] @@ -542,6 +545,60 @@ databaseScope = { skipped = true } setDatabaseScope = { skipped = true } [protocol.NSFetchedResultsControllerDelegate.methods] controller_didChangeContentWithSnapshot = { skipped = true } +[class.CAKeyframeAnimation.methods] +path = { skipped = true } +setPath = { skipped = true } +[class.CAShapeLayer.methods] +path = { skipped = true } +setPath = { skipped = true } +fillColor = { skipped = true } +setFillColor = { skipped = true } +strokeColor = { skipped = true } +setStrokeColor = { skipped = true } +[class.CAEmitterCell.methods] +color = { skipped = true } +setColor = { skipped = true } +[class.CALayer.methods] +affineTransform = { skipped = true } +setAffineTransform = { skipped = true } +drawInContext = { skipped = true } +renderInContext = { skipped = true } +backgroundColor = { skipped = true } +setBackgroundColor = { skipped = true } +borderColor = { skipped = true } +setBorderColor = { skipped = true } +shadowColor = { skipped = true } +setShadowColor = { skipped = true } +shadowPath = { skipped = true } +setShadowPath = { skipped = true } +[protocol.CALayerDelegate.methods] +drawLayer_inContext = { skipped = true } +[protocol.CAMetalDrawable] +skipped = true +[class.CAMetalLayer] +skipped = true +[class.CAOpenGLLayer] +skipped = true +[class.CARenderer.methods] +rendererWithMTLTexture_options = { skipped = true } +setDestination = { skipped = true } +beginFrameAtTime_timeStamp = { skipped = true } +[class.CARemoteLayerClient.methods] +initWithServerPort = { skipped = true } +[class.CARemoteLayerServer.methods] +serverPort = { skipped = true } +[class.CAReplicatorLayer.methods] +instanceColor = { skipped = true } +setInstanceColor = { skipped = true } +[class.CATextLayer.methods] +font = { skipped = true } +setFont = { skipped = true } +foregroundColor = { skipped = true } +setForegroundColor = { skipped = true } +[fn.CATransform3DMakeAffineTransform] +skipped = true +[fn.CATransform3DGetAffineTransform] +skipped = true # Uses a pointer to SEL, which doesn't implement Encode yet [protocol.NSMenuDelegate.methods] @@ -590,6 +647,8 @@ effectiveAppearance = { skipped = true } skipped = true [class.NSView.methods.getRectsExposedDuringLiveResize_count] skipped = true +[class.CAMediaTimingFunction.methods.getControlPointAtIndex_values] +skipped = true # Overridden fmt::Debug because we're missing https://github.com/madsmtm/objc2/issues/267 # See fixes/debug.rs @@ -679,3 +738,8 @@ skipped = true # Wrong type on GNUStep [class.NSMutableData.methods.mutableBytes] skipped = true + +# Selector contains multiple colons after each other, like `::::` +[class.CAMediaTimingFunction.methods] +functionWithControlPoints = { skipped = true } +initWithControlPoints = { skipped = true } diff --git a/crates/icrate/CHANGELOG.md b/crates/icrate/CHANGELOG.md index f1983a1db..bba837eb3 100644 --- a/crates/icrate/CHANGELOG.md +++ b/crates/icrate/CHANGELOG.md @@ -10,6 +10,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ## icrate Unreleased - YYYY-MM-DD +### Added +* Added the `CoreAnimation` framework. + ## icrate 0.0.1 - 2022-12-24 diff --git a/crates/icrate/Cargo.toml b/crates/icrate/Cargo.toml index 26c82cb4a..36d3afefc 100644 --- a/crates/icrate/Cargo.toml +++ b/crates/icrate/Cargo.toml @@ -90,6 +90,7 @@ unstable-docsrs = [] AppKit = ["Foundation", "CoreData"] AuthenticationServices = ["Foundation"] +CoreAnimation = ["Foundation"] CoreData = ["Foundation"] Foundation = ["objective-c", "block"] @@ -97,8 +98,8 @@ Foundation = ["objective-c", "block"] unstable-frameworks-all = ["AppKit", "AuthenticationServices", "CoreData", "Foundation"] unstable-frameworks-gnustep = ["AppKit", "Foundation"] unstable-frameworks-gnustep-32bit = ["Foundation"] -unstable-frameworks-ios = ["AuthenticationServices", "CoreData", "Foundation"] -unstable-frameworks-macos-10-7 = ["AppKit", "CoreData", "Foundation"] +unstable-frameworks-ios = ["AuthenticationServices", "CoreData", "CoreAnimation", "Foundation"] +unstable-frameworks-macos-10-7 = ["AppKit", "CoreAnimation", "CoreData", "Foundation"] unstable-frameworks-macos-10-13 = ["unstable-frameworks-macos-10-7"] unstable-frameworks-macos-11 = ["unstable-frameworks-macos-10-13", "AuthenticationServices"] unstable-frameworks-macos-12 = ["unstable-frameworks-macos-11"] diff --git a/crates/icrate/src/CoreAnimation/fixes/mod.rs b/crates/icrate/src/CoreAnimation/fixes/mod.rs new file mode 100644 index 000000000..724f0e3e4 --- /dev/null +++ b/crates/icrate/src/CoreAnimation/fixes/mod.rs @@ -0,0 +1,3 @@ +use std::os::raw::c_double; + +pub type CFTimeInterval = c_double; diff --git a/crates/icrate/src/CoreAnimation/mod.rs b/crates/icrate/src/CoreAnimation/mod.rs new file mode 100644 index 000000000..671137f0e --- /dev/null +++ b/crates/icrate/src/CoreAnimation/mod.rs @@ -0,0 +1,15 @@ +//! # Core Animation +//! +//! This actually lives in the `QuartzCore` framework, but `CoreAnimation` is +//! the name that people use to refer to it. +#![doc(alias = "QuartzCore")] + +mod fixes; +#[path = "../generated/QuartzCore/mod.rs"] +mod generated; + +pub use self::fixes::*; +pub use self::generated::*; + +#[link(name = "QuartzCore", kind = "framework")] +extern "C" {} diff --git a/crates/icrate/src/generated b/crates/icrate/src/generated index bd2baf84a..b6aab8586 160000 --- a/crates/icrate/src/generated +++ b/crates/icrate/src/generated @@ -1 +1 @@ -Subproject commit bd2baf84af4bd336f1a53c951abe3b00a8326b17 +Subproject commit b6aab858673b3bfb07883e8e93b3cbe1ea728917 diff --git a/crates/icrate/src/lib.rs b/crates/icrate/src/lib.rs index ddf84d8f0..b2ef2b31e 100644 --- a/crates/icrate/src/lib.rs +++ b/crates/icrate/src/lib.rs @@ -35,6 +35,8 @@ mod macros; pub mod AppKit; #[cfg(feature = "AuthenticationServices")] pub mod AuthenticationServices; +#[cfg(feature = "CoreAnimation")] +pub mod CoreAnimation; #[cfg(feature = "CoreData")] pub mod CoreData; #[cfg(feature = "Foundation")] From d97b53828c01871f32e7dcda28da27571709fa30 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Sun, 8 Jan 2023 05:58:02 +0100 Subject: [PATCH 3/4] Allow enriching the safety of functions --- crates/header-translator/src/config.rs | 20 +++++++++++++++++++- crates/header-translator/src/data/README.md | 3 +++ crates/header-translator/src/data/macros.rs | 17 +++++++++++++++++ crates/header-translator/src/stmt.rs | 21 +++++++++++++-------- crates/icrate/src/macros.rs | 13 +++++++++++++ 5 files changed, 65 insertions(+), 9 deletions(-) diff --git a/crates/header-translator/src/config.rs b/crates/header-translator/src/config.rs index 6b97da35a..94d0d009c 100644 --- a/crates/header-translator/src/config.rs +++ b/crates/header-translator/src/config.rs @@ -99,8 +99,26 @@ pub struct MethodData { pub mutating: bool, } +#[derive(Deserialize, Debug, Clone, PartialEq, Eq)] +#[serde(deny_unknown_fields)] +pub struct FnData { + #[serde(default)] + pub skipped: bool, + #[serde(rename = "unsafe")] + #[serde(default = "unsafe_default")] + pub unsafe_: bool, +} + +impl Default for FnData { + fn default() -> Self { + Self { + skipped: skipped_default(), + unsafe_: unsafe_default(), + } + } +} + // TODO -pub type FnData = StructData; pub type StaticData = StructData; pub type TypedefData = StructData; diff --git a/crates/header-translator/src/data/README.md b/crates/header-translator/src/data/README.md index 0d0db2617..f1baf927e 100644 --- a/crates/header-translator/src/data/README.md +++ b/crates/header-translator/src/data/README.md @@ -26,6 +26,9 @@ data! { // The method `mutateUnchecked` takes &mut self, but is not safe. mut -mutateUnchecked; } + + // Declare the function "foo" as safe + unsafe fn foo; } ``` diff --git a/crates/header-translator/src/data/macros.rs b/crates/header-translator/src/data/macros.rs index f1afe8f7d..cec2fd679 100644 --- a/crates/header-translator/src/data/macros.rs +++ b/crates/header-translator/src/data/macros.rs @@ -97,6 +97,23 @@ macro_rules! __data_inner { $($rest)* } }; + // Function + ( + @($config:expr) + + unsafe fn $function:ident; + + $($rest:tt)* + ) => { + let mut data = $config.fns.entry(stringify!($function).to_string()).or_default(); + + data.unsafe_ = false; + + __data_inner! { + @($config) + $($rest)* + } + } } macro_rules! __data_methods { diff --git a/crates/header-translator/src/stmt.rs b/crates/header-translator/src/stmt.rs index 5e313a92a..a6d3fed12 100644 --- a/crates/header-translator/src/stmt.rs +++ b/crates/header-translator/src/stmt.rs @@ -286,6 +286,7 @@ pub enum Stmt { result_type: Ty, // Some -> inline function. body: Option<()>, + safe: bool, }, /// typedef Type TypedefName; AliasDecl { @@ -735,12 +736,9 @@ impl Stmt { EntityKind::FunctionDecl => { let name = entity.get_name().expect("function name"); - if context - .fns - .get(&name) - .map(|data| data.skipped) - .unwrap_or_default() - { + let data = context.fns.get(&name).cloned().unwrap_or_default(); + + if data.skipped { return vec![]; } @@ -788,6 +786,7 @@ impl Stmt { arguments, result_type, body, + safe: !data.unsafe_, }] } EntityKind::UnionDecl => { @@ -1062,9 +1061,12 @@ impl fmt::Display for Stmt { arguments, result_type, body: None, + safe, } => { + let unsafe_ = if *safe { "" } else { " unsafe" }; + writeln!(f, "extern_fn!(")?; - write!(f, " pub unsafe fn {name}(")?; + write!(f, " pub{unsafe_} fn {name}(")?; for (param, arg_ty) in arguments { write!(f, "{}: {arg_ty},", handle_reserved(param))?; } @@ -1076,9 +1078,12 @@ impl fmt::Display for Stmt { arguments, result_type, body: Some(_body), + safe, } => { + let unsafe_ = if *safe { "" } else { " unsafe" }; + writeln!(f, "inline_fn!(")?; - write!(f, " pub unsafe fn {name}(")?; + write!(f, " pub{unsafe_} fn {name}(")?; for (param, arg_ty) in arguments { write!(f, "{}: {arg_ty},", handle_reserved(param))?; } diff --git a/crates/icrate/src/macros.rs b/crates/icrate/src/macros.rs index 058e3dd9c..b8782b37c 100644 --- a/crates/icrate/src/macros.rs +++ b/crates/icrate/src/macros.rs @@ -233,6 +233,19 @@ macro_rules! extern_fn { $v fn $name($($args)*) $(-> $res)?; } }; + ( + $v:vis fn $name:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $res:ty)?; + ) => { + #[inline] + $v extern "C" fn $name($($arg: $arg_ty),*) $(-> $res)? { + extern "C" { + fn $name($($arg: $arg_ty),*) $(-> $res)?; + } + unsafe { + $name($($arg),*) + } + } + }; } macro_rules! inline_fn { From 9a4c02be33e29af457fadb7fa12a1138bb0f0c35 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Sun, 8 Jan 2023 05:44:38 +0100 Subject: [PATCH 4/4] Improve the safety of a bit of CoreAnimation functionality For feature-parity with the `cocoa` crate's `quartzcore` module --- .../src/data/CoreAnimation.rs | 231 ++++++++++++++++++ crates/header-translator/src/lib.rs | 1 + crates/icrate/src/generated | 2 +- 3 files changed, 233 insertions(+), 1 deletion(-) diff --git a/crates/header-translator/src/data/CoreAnimation.rs b/crates/header-translator/src/data/CoreAnimation.rs index 89a871f29..411d38d04 100644 --- a/crates/header-translator/src/data/CoreAnimation.rs +++ b/crates/header-translator/src/data/CoreAnimation.rs @@ -1,2 +1,233 @@ +// We probably technically had the choice of making these classes mutating +// methods require `&mut`, but as with so many things in Cocoa, that would +// make it difficult to use in a larger context (e.g. even after assigning a +// layer to a view you'd often still want to modify the layer). + data! { + class CALayer { + unsafe +layer; + unsafe -init; + + // -initWithLayer must be called in a specific context + + // -presentationLayer's return value may not be safe to modify + // -modelLayer must be called in a specific context + + unsafe -bounds; + unsafe -setBounds; + unsafe -position; + unsafe -setPosition; + unsafe -zPosition; + unsafe -setZPosition; + unsafe -anchorPoint; + unsafe -setAnchorPoint; + unsafe -anchorPointZ; + unsafe -setAnchorPointZ; + unsafe -transform; + unsafe -setTransform; + unsafe -affineTransform; + unsafe -setAffineTransform; + unsafe -frame; + unsafe -setFrame; + unsafe -isHidden; + unsafe -setHidden; + unsafe -isDoubleSided; + unsafe -setDoubleSided; + unsafe -isGeometryFlipped; + unsafe -setGeometryFlipped; + + unsafe -contentsAreFlipped; + + unsafe -superlayer; + unsafe -removeFromSuperlayer; + + // -sublayers is not safe, since the returned array is not guaranteed + // to retain its elements. + + // -setSublayers not safe since it requires all layers to have a + // `nil` superlayer. + + // If the layer already has a superlayer, it will be changed + // appropriately by these methods (effectively, removeFromSuperlayer + // is called on the given layer inside these). + unsafe -addSublayer; + unsafe -insertSublayer_atIndex; + unsafe -insertSublayer_below; + unsafe -insertSublayer_above; + + // -replaceSublayer is not safe since it requires `oldlayer` to exist + // in the current layer. + + unsafe -sublayerTransform; + unsafe -setSublayerTransform; + unsafe -mask; + // -setMask's argument must have a `nil` superlayer + unsafe -masksToBounds; + unsafe -setMasksToBounds; + + unsafe -convertPoint_fromLayer; + unsafe -convertPoint_toLayer; + unsafe -convertRect_fromLayer; + unsafe -convertRect_toLayer; + unsafe -convertTime_fromLayer; + unsafe -convertTime_toLayer; + unsafe -hitTest; + unsafe -containsPoint; + + // No type set: + // -contents; + // -setContents; + unsafe -contentsRect; + unsafe -setContentsRect; + unsafe -contentsGravity; + unsafe -setContentsGravity; + unsafe -contentsScale; + unsafe -setContentsScale; + unsafe -contentsCenter; + unsafe -setContentsCenter; + unsafe -contentsFormat; + unsafe -setContentsFormat; + unsafe -minificationFilter; + unsafe -setMinificationFilter; + unsafe -magnificationFilter; + unsafe -setMagnificationFilter; + unsafe -minificationFilterBias; + unsafe -setMinificationFilterBias; + unsafe -isOpaque; + unsafe -setOpaque; + + unsafe -display; + unsafe -setNeedsDisplay; + unsafe -setNeedsDisplayInRect; + unsafe -needsDisplay; + unsafe -displayIfNeeded; + unsafe -needsDisplayOnBoundsChange; + unsafe -setNeedsDisplayOnBoundsChange; + + unsafe -drawsAsynchronously; + unsafe -setDrawsAsynchronously; + unsafe -edgeAntialiasingMask; + unsafe -setEdgeAntialiasingMask; + unsafe -allowsEdgeAntialiasing; + unsafe -setAllowsEdgeAntialiasing; + unsafe -cornerRadius; + unsafe -setCornerRadius; + unsafe -maskedCorners; + unsafe -setMaskedCorners; + unsafe -cornerCurve; + unsafe -setCornerCurve; + unsafe -cornerCurveExpansionFactor; + unsafe -borderWidth; + unsafe -setBorderWidth; + unsafe -opacity; + // Gives "undefined results" outside [0; 1], but by this the authors + // very likely didn't mean "triggers language-level UB". + unsafe -setOpacity; + unsafe -allowsGroupOpacity; + unsafe -setAllowsGroupOpacity; + // No type set: + // -compositingFilter; + // -setCompositingFilter; + // -filters; + // -setFilters; + // -backgroundFilters; + // -setBackgroundFilters; + unsafe -shouldRasterize; + unsafe -setShouldRasterize; + unsafe -rasterizationScale; + unsafe -setRasterizationScale; + unsafe -shadowOpacity; + // Gives "undefined results" outside [0; 1], but by this the authors + // very likely didn't mean "triggers language-level UB". + unsafe -setShadowOpacity; + unsafe -shadowOffset; + unsafe -setShadowOffset; + unsafe -shadowRadius; + unsafe -setShadowRadius; + unsafe -autoresizingMask; + unsafe -setAutoresizingMask; + unsafe -layoutManager; + unsafe -setLayoutManager; + + unsafe -preferredFrameSize; + unsafe -setNeedsLayout; + unsafe -needsLayout; + unsafe -layoutIfNeeded; + unsafe -layoutSublayers; + unsafe -resizeSublayersWithOldSize; + unsafe -resizeWithOldSuperlayerSize; + + unsafe -defaultActionForKey; + unsafe -actionForKey; + unsafe -actions; + unsafe -setActions; + + // Copies the animation + unsafe -addAnimation_forKey; + + unsafe -removeAllAnimations; + unsafe -removeAnimationForKey; + unsafe -animationKeys; + + // Not safe since modifying the returned animation is UB. + // -animationForKey; + + unsafe -name; + unsafe -setName; + unsafe -delegate; + unsafe -setDelegate; + // No type set: + // -style; + // -setStyle; + } + + class CARenderer { + unsafe -layer; + unsafe -setLayer; + unsafe -bounds; + unsafe -setBounds; + unsafe -updateBounds; + unsafe -addUpdateRect; + unsafe -render; + unsafe -nextFrameTime; + unsafe -endFrame; + } + + // SAFETY: CATransaction is basically just a way to call methods that + // access thread-local state. + class CATransaction { + unsafe +begin; + unsafe +commit; + unsafe +flush; + + unsafe +animationDuration; + unsafe +setAnimationDuration; + + unsafe +animationTimingFunction; + unsafe +setAnimationTimingFunction; + + unsafe +disableActions; + unsafe +setDisableActions; + + // TODO: + // unsafe +completionBlock; + // unsafe +setCompletionBlock; + } + + unsafe fn CACurrentMediaTime; + + // SAFETY: Basic mathematical functions + unsafe fn CATransform3DIsIdentity; + unsafe fn CATransform3DEqualToTransform; + unsafe fn CATransform3DMakeTranslation; + unsafe fn CATransform3DMakeScale; + unsafe fn CATransform3DMakeRotation; + unsafe fn CATransform3DTranslate; + unsafe fn CATransform3DScale; + unsafe fn CATransform3DRotate; + unsafe fn CATransform3DConcat; + unsafe fn CATransform3DInvert; + unsafe fn CATransform3DMakeAffineTransform; + unsafe fn CATransform3DIsAffine; + unsafe fn CATransform3DGetAffineTransform; } diff --git a/crates/header-translator/src/lib.rs b/crates/header-translator/src/lib.rs index f4c354ad8..91e07c588 100644 --- a/crates/header-translator/src/lib.rs +++ b/crates/header-translator/src/lib.rs @@ -1,4 +1,5 @@ #![cfg(feature = "run")] +#![recursion_limit = "256"] use std::collections::BTreeMap; use std::fmt; use std::path::Path; diff --git a/crates/icrate/src/generated b/crates/icrate/src/generated index b6aab8586..70ed6b12c 160000 --- a/crates/icrate/src/generated +++ b/crates/icrate/src/generated @@ -1 +1 @@ -Subproject commit b6aab858673b3bfb07883e8e93b3cbe1ea728917 +Subproject commit 70ed6b12c11eb9b9a22f546aac251c59b5e25170